geo icon indicating copy to clipboard operation
geo copied to clipboard

point types

Open calvinmetcalf opened this issue 9 years ago • 10 comments

how do we want to handle the difference between x,y, x,y,z, x,y,z,w (someone could conceivably want a 4th digit for something like time). Possibilities

  • WKT style: use templates or something to have distinct PointXY, PoingXYZ, types a draw back is that if things assume 2d points having a 3d point could break stuff
  • GeoJSON style: make it a vector functions would have to double check that that enough dimensions where present and the type system wouldn't be able to help

calvinmetcalf avatar Jan 25 '15 14:01 calvinmetcalf

How about make our Coordinate struct like

pub struct Coordinate {
  pub x: f64,
  pub y: f64,
  pub z: Option<f64>,
  pub m: Option<f64>
}

And also operators like distance are 2d by default. If any operation involves z, it must declare this nature on name like distance3d.

sunng87 avatar Jan 25 '15 14:01 sunng87

I think initially just having two coordinates (x, y) should be be an okay starting point, and if we want to expand we can do that in the future. Though, if we do think having more than two coordinates is important right now, I like @sunng87 's suggestion

frewsxcv avatar Jan 25 '15 17:01 frewsxcv

We can use traits and generics to write code that handles several coordinate types. It's more efficient than Option because the type of coordinates is known at compile time. But, for now, I think we should stick to simple 2D coordinates, and postpone this decision until we get a better feel for the code.

mgax avatar Jan 25 '15 21:01 mgax

I'm looking into picking back up launchbadge/sqlx#166 for a personal project of mine, however I require 3D points, otherwise there's no point in my doing any of this. This seems like the place to start with this since I will really be needing the features of postgis instead of just three floats. I'm not sure I'll be needing the M or W parts for the supported "4D" points, so I'm tempted to ignore that for the first iteration of this.

With that said, I'm quite confident that I can generalize the code in coordinate.rs by either using a macro, or something more clever. However, I'm not sure how much I'd be allowed to break the interface of the existing Coordinate and Point. Something like struct Coordinate<T, const N: usize>([T; N]) could work very well in theory, but it would require some larger refactoring to the crate.

Moving into the "4D" case, at least 4D in the nomenclature of the WKT/WKB I feel like we're outside this crate's notion of a Point. I'm not sure if we can even define Add for a POINT ZM for example, since the M value may be used to encode some additional information like a "mile marker" on a highway. Again, I'm personally not in need of this (yet), so generalizing the euclidean points first seems like the first step, then supporting the full WKT/WKB types.

Let me know if I'm going in the wrong direction here, and how I should proceed if you can.

EDIT: I should also add a question about how geozero fits into this?

nixpulvis avatar Feb 25 '21 00:02 nixpulvis

Wow! A very old issue!

My guess is that the majority of the work due here will be plotting forward a course that keeps things optimal for the vast majority of users who are not doing anything with 3D/4D points.

Are you familiar with how other geometry libraries deal with 3d/4d, like geos/JTS? We don't have to copy them, but it's a good jumping off point for discussion at least.

One aside, you mentioned postgis, but it seems like https://github.com/launchbadge/sqlx/issues/166 is talking about postgres's built in geometric types, which, as I understand it, are not the same as the geometry types in postgis.

see e.g. https://gis.stackexchange.com/a/223490

michaelkirk avatar Feb 25 '21 00:02 michaelkirk

EDIT: I should also add a question about how geozero fits into this?

/cc @pka the geozero author, who is likely to have better perspective on this than me.

michaelkirk avatar Feb 25 '21 00:02 michaelkirk

The geozero API does support converting points with XYZM + t/tm dimensions, but has currently only complete 3D/4D implementations for FlatGeobuf, WKB and WKT. To read 3D points from SQLx an implementation of GeomProcessor for a point struct with z-support is required. Opened a meta-issue in https://github.com/georust/geozero/issues/7.

pka avatar Feb 25 '21 07:02 pka

I would also like to upvote for the support for 3D points. I was using Shapely a lot for my projects, which is basically a Python wrapper of GEOS. AFAIK, shapely treat the z-value just as an associate attribute to be carried around without participating in the algorithms. Quoted from its docs:

Shapely is a planar geometry library and z, the height above or below the plane, is ignored in geometric analysis. There is a potential pitfall for users here: coordinate tuples that differ only in z are not distinguished from each other and their application can result in suprisingly invalid geometry objects. For example, LineString([(0, 0, 0), (0, 0, 1)]) does not return a vertical line of unit length, but an invalid line in the plane with zero length. Similarly, Polygon([(0, 0, 0), (0, 0, 1), (1, 1, 1)]) is not bounded by a closed ring and is invalid.

I personally like the proposal of

pub struct Coordinate {
  pub x: f64,
  pub y: f64,
  pub z: Option<f64>,
  pub m: Option<f64>
}

as proposed at the very beginning. Just having the slot for saving z value will already makes a huge difference, allowing other crates to take advantages of this API.

Furthermore, if we can support some basic operation on the z-value (just using interpolation between coordinates) will be an even huge improvement. (This is not supported by GEOS, but we can make our rust version better than them right 😄 )

cmpute avatar Nov 25 '21 03:11 cmpute

I tried to hack a multi-dimensional geo types, first using traits, but later switched to constant generics as they seemed far easier and less surprising. WIP implementation.

Coordinates are stored with three generics -- T: CoordNum (as before), a metadata generic M: Metadata (could be empty ()), and a const D: usize representing coordinate dimensions (2D, 3D, ...).

struct Coordinate<T: CoordNum, M: Metadata, const D: usize> {
    coord: [T; D],
    meta: M,
}

Fixed length types (point, line, box, triangle) are stored with another param const P: usize representing the number of points:

struct SizedMultipoint<T: CoordNum, M: Metadata, const D: usize, const P: usize> {
    coords: [Coordinate<T, M, D>; P],
}

Variable-length types use vectors:

struct Multipoint<T: CoordNum, M: Metadata, const D: usize> {
    coords: Vec<Coordinate<T, M, D>>,
}

The actual types would become:

type Point<T, M, const D: usize> = SizedMultipoint<T, M, D, 1>;
type Point2D<T> = Point<T, (), 2>;
type Point2DM<T,M> = Point<T, M, 2>;
type Point3D<T> = Point<T, (), 3>;
type Point3DM<T, M> = Point<T, M, 3>;

type Line<T, M, const D: usize> = SizedMultipoint<T, M, D, 2>;
type Line2D<T> = Line<T, (), 2>;
type Line2DM<T, M> = Line<T, M, 2>;
type Line3D<T> = Line<T, (), 3>;
type Line3DM<T, M> = Line<T, M, 3>;

type Triangle<T, M, const D: usize> = SizedMultipoint<T, M, D, 3>;
type Triangle2D<T> = Triangle<T, (), 2>;
type Triangle2DM<T, M> = Triangle<T, M, 2>;
type Triangle3D<T> = Triangle<T, (), 3>;
type Triangle3DM<T, M> = Triangle<T, M, 3>;
  • See the current version of the above code here
  • See also an alternative single array variant (no generic M param): here

nyurik avatar Feb 21 '22 16:02 nyurik

Very rough draft implementation of this is being done in https://github.com/georust/geo/pull/742/files Basic idea is to treat Coordinate as an alias type (to reduce refactoring). Things like .x and .y will have to become functions .x() and .y() and corresponding setters. Surprisingly cargo test already passes when ran from the geo-types/ dir.

nyurik avatar Feb 22 '22 06:02 nyurik