-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement 3D and Measure support for geo types #742
Conversation
Wow! It's astonishing that such a major extension is not more intrusive. For geo-types users, the major changes are probably that they have to use One point which should be prototyped probably, is how to implement 2D functions keeping additional dimensions and meta data. Naming:
|
Yeah, this looks great. But I wonder how it affects the IDE support? rust-analyzer knows almost nothing about generics. |
I haven't had any issues with it in IntelliJ - seems to work well -- e.g. if there is a |
Naming is hard. @pka I renamed Coordinate3D to
SRID could (later?) be introduced as a separate generic field, with some other trait magic that implements a default. The tag data could be called something else: |
Disclaimer that I've only given this a cursory look. There's a lot of noise in the diff from the upstream commits. re: the naming of A couple of things I don't understand yet:
|
Sure, any name is fine here -
No, metadata (to be renamed per above) is not required for 3D, but seems to be requested enough (and common enough in the industry) to introduce at the same time. This reduces code churn because adding
The intention is to add |
Thanks for your consideration of breaking changes. It is indeed nice to consolidate breakage. Is it clear to you what the behavior of a simple algorithm like
It seems kind of at odds with it's utility. e.g. if I'm holding my SRID in meta, maybe I expect that to be preserved in the meta of the output geometry? You don't have to respond right now, I'm sure you're still thinking about the design. I just hope to avoid a situation where, in the effort to accommodate two new things, it becomes too challenging and then we end up with neither. Spend your time as you like, of course. |
@michaelkirk actually see #742 (comment) -- i think SRID won't be part of the (what is now known as metadata, but shall be renamed). SRID affects algorithms. "meta" does not, so it is more like an extra/tag thing. And no, no idea of how it will be used -- need to look at how postgis deals with the measurement. One example is ST_AddMeasure. In that doc, note the important comment:
So in PostGIS every function capable of understanding z-index is marked as such. |
Looking further at various M-related PostGIS functions like ST_FilterByM, they all work with M value that can be compared, extrapolated, etc. That implies that |
After a lot of experimentation, it seems the constant-generic-based array would be too difficult and pointless to implement. Instead, per a wonderfully quick stackoverflow suggestion, I'm introducing a new P.S. SRID should be done as one value per feature (point/line/...), not one per coordinate, so removing it for now. |
Update: stabilizing coordinates and points. Most algorithms will not work if the point/coord has z or m values because algorithms are accepting/returning Alternatively we can keep the existing To simplify migration, some of these changes were moved to #775 /// Test that actually works but only if all types match
assert!(point!(x: 1, y: 2, z: 3, m: 4).contains(&point!(x: 1, y: 2, z: 3, m: 4)));
/// Coordinate storage
pub struct CoordTZM<T: CoordNum, Z: ZCoord, M: Measure> {
pub x: T,
pub y: T,
pub z: Z,
pub m: M,
}
pub type Coordinate<T> = CoordTZM<T, NoValue, NoValue>;
pub type CoordinateM<T, M> = CoordTZM<T, NoValue, M>;
pub type CoordinateZ<T> = CoordTZM<T, T, NoValue>;
pub type CoordinateZM<T, M> = CoordTZM<T, T, M>;
// points
pub struct PointTZM<T: CoordNum, Z: ZCoord, M: Measure>(pub CoordTZM<T, Z, M>);
pub type Point<T> = PointTZM<T, NoValue, NoValue>;
pub type PointM<T, M> = PointTZM<T, NoValue, M>;
pub type PointZ<T> = PointTZM<T, T, NoValue>;
pub type PointZM<T, M> = PointTZM<T, T, M>; |
Rust does not allow tuple type aliases to be instantiated, so `Point(c)` must always be a real struct, and cannot be a type alias like `type Point<T> = GenericPoint<T,Z,M>`. To make the migration transparent, this PR adds support for a single-expression `point!` macro. Now it can be used in 3 ways: ``` point! { x: <number>, y: <number> } point!(<x_number>, <y_number>) point!(<coordinate>) ``` See also georust#742 (comment) * Replace `Point(c)` with `point!(c)` * Replace a few `format!("{}", v)` with `v.to_string()` * Use `Self` or `Self::Output` instead of a concrete type when possible.
Rust does not allow tuple type aliases to be instantiated, so `Point(c)` must always be a real struct, and cannot be a type alias like `type Point<T> = GenericPoint<T,Z,M>`. To make the migration transparent, this PR adds support for a single-expression `point!` macro. Now it can be used in 3 ways: ``` point! { x: <number>, y: <number> } point!(<x_number>, <y_number>) point!(<coordinate>) ``` See also georust#742 (comment) * Replace `Point(c)` with `point!(c)` * Replace a few `format!("{}", v)` with `v.to_string()` * Use `Self` or `Self::Output` instead of a concrete type when possible.
aef15ab
to
d92621d
Compare
Update: TZM support has been implemented on all geo types, and many simple implementations for those types. Some open questions:
|
30618a2
to
c1e99d2
Compare
This PR is ready for review. I updated the top comment with lots of details and examples. Reviewing should become much easier after related non-breaking PRs are merged (see list at the top) |
@michaelkirk sure, this works, thanks! With WKT -- yes, those changes will have to be made soon anyway, so yes, this will be simpler. Would everyone be ok to merge code that uses reference to the unpublished WKT? This goes a little against the principle that the I'm ok to just merge your changes into my branch, and will eventually squash it right before the release to keep the history cleaner (unless we can configure bors to auto-squash on merge) |
I think we shouldn't merge either until they are both approved. In the meanwhile, do as I've done in #795, and update the patch section of your geo branch to reference your wkt branch, and update the patch section of your wkt branch to reference your geo branch. |
@michaelkirk thanks, applied your changes, seem to pass. The WKT workaround is still bothering me. I believe any code in the main branch should be "release-ready" - you can just publish whatever is in the |
## geo-types * BREAKING: Remove deprecated functions on the `Geometry<T>`: * `into_point` - Switch to `std::convert::TryInto<Point>` * `into_line_string` - Switch to `std::convert::TryInto<LineString>` * `into_line` - Switch to `std::convert::TryInto<Line>` * `into_polygon` - Switch to `std::convert::TryInto<Polygon>` * `into_multi_point` - Switch to `std::convert::TryInto<MultiPoint>` * `into_multi_line_string` - Switch to `std::convert::TryInto<MultiLineString>` * `into_multi_polygon` - Switch to `std::convert::TryInto<MultiPolygon>` * BREAKING: Remove deprecated `CoordinateType` trait. Use `CoordFloat` or `CoordNum` instead. * BREAKING: Remove deprecated functions from `LineString<T>` * Remove `points_iter()` -- use `points()` instead. * Remove `num_coords()` -- use `geo::algorithm::coords_iter::CoordsIter::coords_count` instead. * BREAKING: Remove deprecated functions from `Point<T>` * Remove `lng()` -- use `x()` instead. * Remove `set_lng()` -- use `set_x()` instead. * Remove `lat()` -- use `y()` instead. * Remove `set_lat()` -- use `set_y()` instead. * BREAKING: Remove deprecated `Polygon<T>::is_convex()` -- use `geo::is_convex` on `poly.exterior()` instead. * BREAKING: Remove deprecated `Rect<T>::try_new()` -- use `Rect::new` instead, since `Rect::try_new` will never Error. Also removes corresponding `InvalidRectCoordinatesError`. ## geo * BREAKING: Remove deprecated `rotate()` from the `Rotate` trait: > Equivalent to `rotate_around_centroid` except for `Polygon<T>`, where it is equivalent to rotating around the polygon's outer ring. Call that instead, or `rotate_around_center` if you'd like to rotate around the geometry's bounding box center. * BREAKING: Remove `ToGeo` trait. Switch to `std::convert::Into<Geo>` or `std::convert::TryInto<Geo>`.
This PR focuses on `geo-types` only, making it possible to merge without conflicts because it no longer uses local relative paths. This is a part of georust#742. It also bumps geo-types requirements to 0.7.4. This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change. Many other pending PRs are included here - most of them can be merged without affecting existing users, and will make reviewing this PR easier. See the list below. ## geo-types restructuring All geo type structs have been renamed from `Foo<T>(...)` to `FooTZM<T,Z,M>(...)`, and several type aliases were added: ```rust // old pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>); // new pub struct LineStringTZM<T: CoordNum, Z: ZCoord, M: Measure>(pub Vec<CoordTZM<T, Z, M>>); pub type LineString<T> = LineStringTZM<T, NoValue, NoValue>; pub type LineStringM<T, M> = LineStringTZM<T, NoValue, M>; pub type LineStringZ<T> = LineStringTZM<T, T, NoValue>; pub type LineStringZM<T, M> = LineStringTZM<T, T, M>; ``` ## NoValue magic `NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue: ```rust pub struct NoValue; impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for CoordTZM<T, Z, M> { type Output = Self; fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z, m: self.m - rhs.m, } } } ``` ## Variant algorithm implementations Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure (Z is set to `NoValue` by `LineM` type alias. ```rust impl<T: CoordNum, M: Measure> LineM<T, M> { /// Calculate the slope (Δy/Δx). pub fn slope(&self) -> T { self.dy() / self.dx() } } ``` ## Breaking changes ### Tuple constructors It will not be possible to use implicit type constructor for tuples because type aliases do not support them. Most geo types will have a `new(...)` fn if they didn't have one before. ```rust // old MultiPoint(vec![...]); ...iter().map(MultiPoint); // new MultiPoint::new(vec![...]); ...iter().map(MultiPoint::new); ``` ### Type destructors Destructuring assignments seem to require real type rather than an alias. ```rust // old let Point(c) = p; // new let PointTZM(c) = p; ```
This PR focuses on `geo-types` only, making it possible to merge without conflicts because it no longer uses local relative paths. This is a part of georust#742. It also bumps geo-types requirements to 0.7.4. This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change. Many other pending PRs are included here - most of them can be merged without affecting existing users, and will make reviewing this PR easier. See the list below. All geo type structs have been renamed from `Foo<T>(...)` to `FooTZM<T,Z,M>(...)`, and several type aliases were added: ```rust // old pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>); // new pub struct LineStringTZM<T: CoordNum, Z: ZCoord, M: Measure>(pub Vec<CoordTZM<T, Z, M>>); pub type LineString<T> = LineStringTZM<T, NoValue, NoValue>; pub type LineStringM<T, M> = LineStringTZM<T, NoValue, M>; pub type LineStringZ<T> = LineStringTZM<T, T, NoValue>; pub type LineStringZM<T, M> = LineStringTZM<T, T, M>; ``` `NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue: ```rust pub struct NoValue; impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for CoordTZM<T, Z, M> { type Output = Self; fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z, m: self.m - rhs.m, } } } ``` Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure (Z is set to `NoValue` by `LineM` type alias. ```rust impl<T: CoordNum, M: Measure> LineM<T, M> { /// Calculate the slope (Δy/Δx). pub fn slope(&self) -> T { self.dy() / self.dx() } } ``` It will not be possible to use implicit type constructor for tuples because type aliases do not support them. Most geo types will have a `new(...)` fn if they didn't have one before. ```rust // old MultiPoint(vec![...]); ...iter().map(MultiPoint); // new MultiPoint::new(vec![...]); ...iter().map(MultiPoint::new); ``` Destructuring assignments seem to require real type rather than an alias. ```rust // old let Point(c) = p; // new let PointTZM(c) = p; ```
After many attempts to make a clean switch, it seems the only real way to do this is as was suggested by @frewsxcv -- update just the |
This PR focuses on `geo-types` only, making it possible to merge without conflicts because it no longer uses local relative paths. This is a part of georust#742. It also bumps geo-types requirements to 0.7.4. This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change. Many other pending PRs are included here - most of them can be merged without affecting existing users, and will make reviewing this PR easier. See the list below. All geo type structs have been renamed from `Foo<T>(...)` to `FooTZM<T,Z,M>(...)`, and several type aliases were added: ```rust // old pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>); // new pub struct LineStringTZM<T: CoordNum, Z: ZCoord, M: Measure>(pub Vec<CoordTZM<T, Z, M>>); pub type LineString<T> = LineStringTZM<T, NoValue, NoValue>; pub type LineStringM<T, M> = LineStringTZM<T, NoValue, M>; pub type LineStringZ<T> = LineStringTZM<T, T, NoValue>; pub type LineStringZM<T, M> = LineStringTZM<T, T, M>; ``` `NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue: ```rust pub struct NoValue; impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for CoordTZM<T, Z, M> { type Output = Self; fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z, m: self.m - rhs.m, } } } ``` Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure (Z is set to `NoValue` by `LineM` type alias. ```rust impl<T: CoordNum, M: Measure> LineM<T, M> { /// Calculate the slope (Δy/Δx). pub fn slope(&self) -> T { self.dy() / self.dx() } } ``` It will not be possible to use implicit type constructor for tuples because type aliases do not support them. Most geo types will have a `new(...)` fn if they didn't have one before. ```rust // old MultiPoint(vec![...]); ...iter().map(MultiPoint); // new MultiPoint::new(vec![...]); ...iter().map(MultiPoint::new); ``` Destructuring assignments seem to require real type rather than an alias. ```rust // old let Point(c) = p; // new let PointTZM(c) = p; ```
This PR includes georust#772 and georust#812 --- This PR focuses on `geo-types` only. This is a part of georust#742. This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change. ## geo-types restructuring All geo type structs have been renamed from `Foo<T>(...)` to `Foo<T, Z=NoValue, M=NoValue>(...)`, and several type aliases were added: ```rust // old pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>); // new pub struct LineString<T: CoordNum, Z: ZCoord=NoValue, M: Measure=NoValue>(pub Vec<Coordinate<T, Z, M>>); pub type LineStringM<T, M=T> = LineString<T, NoValue, M>; pub type LineString3D<T> = LineString<T, T, NoValue>; pub type LineString3DM<T, M=T> = LineString<T, T, M>; ``` ## NoValue magic `NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue: ```rust pub struct NoValue; impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for Coordinate<T, Z, M> { type Output = Self; fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z, m: self.m - rhs.m, } } } ``` ## Variant algorithm implementations Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure. ```rust impl<T: CoordNum, M: Measure> Line<T, NoValue, M> { /// Calculate the slope (Δy/Δx). pub fn slope(&self) -> T { self.dy() / self.dx() } } ``` ## Breaking changes * It is no longer possible to create Coordinate with just `x` and `y` values using implicit tuple constructor. Use `coord!` instead.
This PR includes georust#772 and georust#812 --- This PR focuses on `geo-types` only. This is a part of georust#742. This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change. ## geo-types restructuring All geo type structs have been renamed from `Foo<T>(...)` to `Foo<T, Z=NoValue, M=NoValue>(...)`, and several type aliases were added: ```rust // old pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>); // new pub struct LineString<T: CoordNum, Z: ZCoord=NoValue, M: Measure=NoValue>(pub Vec<Coordinate<T, Z, M>>); pub type LineStringM<T, M=T> = LineString<T, NoValue, M>; pub type LineString3D<T> = LineString<T, T, NoValue>; pub type LineString3DM<T, M=T> = LineString<T, T, M>; ``` ## NoValue magic `NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue: ```rust pub struct NoValue; impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for Coordinate<T, Z, M> { type Output = Self; fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z, m: self.m - rhs.m, } } } ``` ## Variant algorithm implementations Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure. ```rust impl<T: CoordNum, M: Measure> Line<T, NoValue, M> { /// Calculate the slope (Δy/Δx). pub fn slope(&self) -> T { self.dy() / self.dx() } } ``` ## Breaking changes * It is no longer possible to create Coordinate with just `x` and `y` values using implicit tuple constructor. Use `coord!` instead.
This PR includes #772 --- This PR focuses on `geo-types` only. This is a part of #742. This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change. All geo type structs have been renamed from `Foo<T>(...)` to `Foo<T, Z=NoValue, M=NoValue>(...)`, and several type aliases were added: ```rust // old pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>); // new pub struct LineString<T: CoordNum, Z: ZCoord=NoValue, M: Measure=NoValue>(pub Vec<Coordinate<T, Z, M>>); pub type LineStringM<T, M=T> = LineString<T, NoValue, M>; pub type LineString3D<T> = LineString<T, T, NoValue>; pub type LineString3DM<T, M=T> = LineString<T, T, M>; ``` `NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue: ```rust pub struct NoValue; impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for Coordinate<T, Z, M> { type Output = Self; fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z, m: self.m - rhs.m, } } } ``` Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure. ```rust impl<T: CoordNum, M: Measure> Line<T, NoValue, M> { /// Calculate the slope (Δy/Δx). pub fn slope(&self) -> T { self.dy() / self.dx() } } ``` * It is no longer possible to create Coordinate with just `x` and `y` values using implicit tuple constructor. Use `coord!` instead.
This PR includes #772 --- This PR focuses on `geo-types` only. This is a part of #742. This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change. All geo type structs have been renamed from `Foo<T>(...)` to `Foo<T, Z=NoValue, M=NoValue>(...)`, and several type aliases were added: ```rust // old pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>); // new pub struct LineString<T: CoordNum, Z: ZCoord=NoValue, M: Measure=NoValue>(pub Vec<Coordinate<T, Z, M>>); pub type LineStringM<T, M=T> = LineString<T, NoValue, M>; pub type LineString3D<T> = LineString<T, T, NoValue>; pub type LineString3DM<T, M=T> = LineString<T, T, M>; ``` `NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue: ```rust pub struct NoValue; impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for Coordinate<T, Z, M> { type Output = Self; fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, z: self.z - rhs.z, m: self.m - rhs.m, } } } ``` Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure. ```rust impl<T: CoordNum, M: Measure> Line<T, NoValue, M> { /// Calculate the slope (Δy/Δx). pub fn slope(&self) -> T { self.dy() / self.dx() } } ``` * It is no longer possible to create Coordinate with just `x` and `y` values using implicit tuple constructor. Use `coord!` instead.
Closing for inactivity. Feel free to reopen if you resume work on this. |
UPDATE: most of this pr has been moved to #797 (all of
geo-types
changes). 797 should be merged and published first. This PR will need to be updated once 797 is published. I will mark this as a draft in the mean time.This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change.
Many other pending PRs are included here - most of them can be merged without affecting existing users, and will make reviewing this PR easier. See the list below.
geo-types restructuring
All geo type structs have been renamed from
Foo<T>(...)
toFooTZM<T,Z,M>(...)
, and several type aliases were added:NoValue magic
NoValue
is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that aZ
orM
value can be manipulated without checking if it is actually there. This code works for Z and M being either a number or a NoValue:Variant algorithm implementations
Function implementations can keep just the original 2D
<T>
variant, or add support for 3D<Z>
and/or the Measure<M>
. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure (Z is set toNoValue
byLineM
type alias.Breaking changes
Tuple constructors
It will not be possible to use implicit type constructor for tuples because type aliases do not support them. Most geo types will have a
new(...)
fn if they didn't have one before.Type destructors
Destructuring assignments seem to require real type rather than an alias.
Related PRs
This PR includes #778, #782, #783, #784, #786, #787, #788, #775, #772, #785 which can be easily merged without breaking existing code.
TODO