Git Product home page Git Product logo

wkt's Introduction

wkt

Rust read/write support for well-known text (WKT).

Crate API Documentation

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

wkt's People

Contributors

antoine-de avatar audunska avatar blackrez avatar bors[bot] avatar categulario avatar crocme10 avatar frewsxcv avatar groteworld avatar lnicola avatar michaelkirk avatar nyurik avatar osialr avatar pjsier avatar rmanoka avatar softdevca avatar urschrei avatar woshilapin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wkt's Issues

Release new version

rust-wkt 0.0.3 depends on rust-geo 0.0.2 which does not compile on Rust 1.1.0 (stable):

.../.cargo/registry/src/github.com-1ecc6299db9ec823/geo-0.0.2/src/lib.rs:1:1: 1:18 error: unstable feature
.../.cargo/registry/src/github.com-1ecc6299db9ec823/geo-0.0.2/src/lib.rs:1 #![feature(core)]

Support deserializing integer geometries

#101 added support for serializing integer geometries to WKT, but I hit an issue while implementing deserialization for integer types.

My WIP is here:
https://github.com/georust/wkt/tree/mkirk/try-from-non-floats

The issue is that numbers with decimal points will not parse as integers. For example i32::from_str("1.9") will Err.

Arguably that's expected. But I can also imagine people wanting us to "just make it work" either by rounding or truncating the portion past the decimal.

In any case, the current error message is confusing:

value: InvalidWKT("Missing closing parenthesis for type")

I'd at least expect something like "failed to parse numeric type"

I've punted on this now by simply not adding support for parsing non-float types yet.

Should we support LINEARRING type?

e.g. LINEARRING(0 0, 0 5, 5 5, 5 0, 0 0)

JTS uses this - but it's unclear to me if this is a standard thing that we would benefit from supporting.

Next release (0.9.2)

We've got a handful of nice things we should release:

* Approximately 40% faster according to `cargo bench`.
* Some "numeric" characters like `¾` and `①` were being treated as digits.
* Support `POINT EMPTY` in conversion to `geo_types`.
  Converts to `MultiPoint([])`.
  * <https://github.com/georust/wkt/pull/64>
* Minimal support for JTS extension: `LINEARRING` by parsing it as a `LINESTRING`.

diff: 0.9.1...master

I don't think any of them are breaking changes, so it'd just be a minor release.

Is there anything else we should be waiting for? Otherwise, unless anyone objects, I'll plan to release 0.9.2 in ~24 hours.

Move base types to a standalone library as foundation of georust libraries

(Greetings, I just joined georust on @frewsxcv 's invitation)

Since we have a clear road map for geo-spatial libraries in rust, I suggest that the first step is to move all standard types into a standalone library. We can call it geo-types. It will contain all types described in OGC Simple Feature standard. Our I/O libraries like wkt and geojson will depend on it, and our future libraries will also use types defined in this library. This will make it consistent from user.

Add Support For EWKT

Extended WKT is a simple variant used in PostGIS and related software, in particular, the SRID. Presently trying to parse something like SRID=4326; throws InvalidWKT("Invalid type encountered") as the error.

It appears there already is an issue opened for the up to 4 ordinate values part of the spec.

The BNF spec provided by PostGIS would be a helpful guide.

release 0.11.0

I'd like to get the next release out.

If possible, I'd like to include #103 since it's the logical compliment to the already merged but unreleased #101

Can someone take a look?

And anything else we should wait for?

make Wkt<T> display

I was trying to find out how to get a Wkt to actually convert to a string. Is there a reason why it doesn't implement Display? It seems, instead i have to do ẁkt.item.to_string() - which I find counter-intuitive.

WKT Release: proposed minor release 0.9.1

There are a couple of changes I'd like to see published, so I plan to proceed with a minor release in a ~48 hours unless I hear of something we want to wait for.

My specific reason is the tests in georust/geo#629 which depend on the new deserialize support via the (also unreleased) jts-test-runner.

Current Changes

  • Add serde::Deserialize for Wkt and Geometry.
  • Add helper functions for deserializing from WKT format into
    geo_types::Geometry and geo_types::Point

Reconsider design of core structures

Right now, it's possible to model this with the current design:

LineString(vec![
    Coord { x: 1, y: 1, None, None },
    Coord { x: 1, y: 1, Some(1), None },
])

...which translates to something like:

LINESTRING ( 1 1, 1 1 1 )

...which doesn't make any sense since the first item has two dimensions but the second item has three dimensions.

We can change the core structures to a different design to enforce all items having the same dimensions:

(Keep in mind, this design is not final and is just a rought draft)

pub trait Dimension {
    fn from_quad(x: f64, y: f64, z: Option<f64>, m: Option<f64>) -> Self;

    fn to_quad(&self) -> (f64, f64, Option<f64>, Option<f64>);
}

pub struct Xy(f64, f64);
impl Dimension for Xy {
    fn from_quad(x: f64, y: f64, _: Option<f64>, _: Option<f64>) -> Self {
        Xy(x, y)
    }

    fn to_quad(&self) -> (f64, f64, Option<f64>, Option<f64>) {
        (self.0, self.1, None, None)
    }
}

pub struct Xyz(f64, f64, f64);
impl Dimension for Xyz {
    fn from_quad(x: f64, y: f64, z: Option<f64>, _: Option<f64>) -> Self {
        Xyz(x, y, z.unwrap())
    }

    fn to_quad(&self) -> (f64, f64, Option<f64>, Option<f64>) {
        (self.0, self.1, Some(self.2), None)
    }
}

pub struct Xym(f64, f64, f64);
impl Dimension for Xym {
    fn from_quad(x: f64, y: f64, _: Option<f64>, m: Option<f64>) -> Self {
        Xym(x, y, m.unwrap())
    }

    fn to_quad(&self) -> (f64, f64, Option<f64>, Option<f64>) {
        (self.0, self.1, None, Some(self.2))
    }
}

pub struct Xyzm(f64, f64, f64, f64);
impl Dimension for Xyzm {
    fn from_quad(x: f64, y: f64, z: Option<f64>, m: Option<f64>) -> Self {
        Xyzm(x, y, z.unwrap(), m.unwrap())
    }

    fn to_quad(&self) -> (f64, f64, Option<f64>, Option<f64>) {
        (self.0, self.1, Some(self.2), Some(self.3))
    }
}

// Concrete types

pub enum Point<D: Dimension> {
    Empty,
    Value(D),
}

pub enum LineString<D: Dimension> {
    Empty,
    Value(Vec<D>),
}

pub enum Geometry<D: Dimension> {
    Empty,
    Point(Point<D>),
    LineString(LineString<D>),
    // ...
    GeometryCollection(Vec<Geometry<D>>),
}

playpen

Better way to serialize to wkt?

I was trying to write a geo-type as WKT to file, and this is what I came up with:

    let points = include!("../src/algorithm/test_fixtures/louisiana.rs");
    let geo_linestring: geo::LineString<f32> = points.into();
    use wkt::ToWkt;
    let wkt: wkt::Wkt<_> = geo::Geometry::from(geo_linestring).to_wkt();
    let wkt_linestring = wkt.items.first().unwrap();
    let serialized = format!("{}", wkt_linestring);
    println!("{}", &serialized);

Is there already better way?

I was hoping for something like:

    let points = include!("../src/algorithm/test_fixtures/louisiana.rs");
    let geo_linestring: geo::LineString<f32> = points.into();
    use wkt::ToWkt;
    let wkt: wkt::Wkt<_> = geo::Geometry::from(geo_linestring).to_wkt();
-   let wkt_linestring = wkt.items.first().unwrap();
-   let serialized = format!("{}", wkt_linestring);
+   let serialized = wkt.to_string()
    println!("{}", &serialized);

Or like serde_jsons::to_writer

    let points = include!("../src/algorithm/test_fixtures/louisiana.rs");
    let geo_linestring: geo::LineString<f32> = points.into();
    use wkt::ToWkt;
    let wkt: wkt::Wkt<_> = geo::Geometry::from(geo_linestring).to_wkt();
-   let wkt_linestring = wkt.items.first().unwrap();
-   let serialized = format!("{}", wkt_linestring);
+   let file = File::create("/tmp/foo").unwrap();
+   let mut writer = BufWriter::new(file);
+   wkt::to_writer(&mut writer, wkt);
    println!("{}", &serialized);

Any thoughts?

`POINT EMPTY` vis-à-vis `geo_types`

One point of friction when mapping WKT to geo_types is POINT EMPTY.

Similar EMPTY types present no such problem: GEOMETRYCOLLECTION EMPTY, LINESTRING EMPTY, POLYGON EMPTY all have intuitive corollaries in geo_types. (GeometryCollection(vec![]), LineString(vec![]), Polygon { exterior: vec![], interiors: vec![] }

You might say:

Obviously POINT EMPTY maps to Option::<geo_types::Point>::None!

To which I say:

GEOMETRYCOLLECTION(POINT EMPTY)
or
GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POINT EMPTY))
GEOMETRYCOLLECTION(POINT 1 1, GEOMETRYCOLLECTION(POINT EMPTY))

The conversion of which is not obvious to me.

A few options:

  1. Do nothing. Whenever converting to geo-types, continue to error whenever we encounter a POINT EMPTY, force the caller to reconcile any confusion. This is great because we wash our hands of the problem, but I think we'd be doing a disservice to, as policy, force every user to re-invent this essential wheel from scratch.

  2. add a deserialize_point -> Option<geo_types::Point> method to complement deserialize_geometry.

#[cfg(feature = "geo-types")]
pub fn deserialize_point<'de, D, T>(deserializer: D) -> Result<Option<geo_types::Point<T>>, D::Error>
    where
        D: Deserializer<'de>,
        T: FromStr + Default + WktFloat,
{
    use serde::Deserialize;
    Wkt::deserialize(deserializer).and_then(|wkt: Wkt<T>| {
        use std::convert::TryFrom;
        geo_types::Point::try_from(wkt).map(|p| Some(p))
            .or_else(|e| {
            if let crate::conversion::Error::PointConversionError = e {
                // map a WKT: 'POINT EMPTY' to an `Option<geo_types::Point>::None`
                return Ok(None);
            }

            Err(D::Error::custom(e))
        })
    })
}

This would only solve the top level POINT EMPTY case, and would continue to error on GEOMETRYCOLLECTION(POINT EMPTY) case. Would this half measure be worthwhile or only amplify the confusion?

  1. deserialize POINT EMPTY to GEOMETRYCOLLECTION EMPTY. This is at first a confusing alternative, but neatly makes a bunch of stuff "Just work". That is, parsing can continue, and, as far as I know, there are no operations for which a GEOMETRYCOLLECTION EMPTY would behave differently from a POINT EMPTY. It just "feels weird" to lose that type information. FWIW, I think this is the approach postgis takes. You can read some rationale from https://gis.stackexchange.com/a/106942:

It's not a bug, more like a strategic laziness. 1.X versions of PostGIS only supported GEOMETRYCOLLECTION EMPTY, not other forms of empty. For 2.X I (perhaps foolishly) embraced the full variety of nothings. The result is a not-entirely-complete support of varieties of nothing, made slightly ill-formed by the fact that support libraries like GEOS have their own concepts of what kinds of nothing are worth preserving, and that standards like WKB cannot even represent some forms of it (POINT EMPTY is not representable in WKB).

Anyhow, a bunch of nothing is still nothing. Do you you need to retain fidelity of your collections of nothing?

UPDATE

Looking at the PostGIS code, I'm pretty sure you're seeing an effect of the "is empty" function. Your input is in fact being parsed into an internal representation that reflects the input, a collection of empty things. But on output to WKB, the first test is "is this thing empty? if so emit an empty representation". So then we get this situation: is a geometry collection of empty things itself empty? Philosophy 101. In terms of most things we might do with it (calculate area or length, intersect it with things, really any operation at all) a collection of empty is just the same as a single empty. Mucking with the definition of "is empty" has a lot of knock-on effects all over the code base, so we haven't touched it much.

  1. maybe some combination of 2 and 3 above: have a deserialize_point -> Option<Point> method, but when encountering an internal Point, as in GEOMETRYCOLLETION(POINT 1 1, POINT EMPTY) convert it to GEOMETRYCOLLETION(POINT 1 1, GEOMETRYCOLLETION EMPTY). That seems preferable to discarding it since GEOMETRYCOLLECTION(POINT 1 1) would have an observably different member count.

Remove ToWkt?

Does this get used anywhere? If so seems like we could replace usages with Into<ToWkt>

Use new TZM geo-types instead the custom WKT ones

Once (if) the new geo-types with 3dM support is merged, I think wkt can migrate to use them directly, instead of having its own duplicate set of types. This should improve performance (no extra transformation), and simplify code - one set of types for everything.

// current WKT type
pub struct Coord<T: WktFloat> {
    pub x: T,
    pub y: T,
    pub z: Option<T>,
    pub m: Option<T>,
}

// corresponding geo type
pub struct Coordinate<T: CoordNum, Z: ZCoord, M: Measure> {
    pub x: T,
    pub y: T,
    pub z: Z,
    pub m: M,
}

The 3dM types require proper generics (either concrete float type or a NoValue empty struct). We could represent it with a new Wkt enum instead of the existing pub struct Wkt<T> { pub item: Geometry<T> }. NoValue might need to be updated to support all CoordNum requirements.

// Usage:
match Wkt::from_str("LINESTRING (10 -20, -0 -0.5)").unwrap() {
  Wkt(g) => ..., // 2D value
  WktM(g) => ..., // 2D+M value
  Wkt3D(g) => ..., // 3D value
  Wkt3DM(g) => ..., // 3D+M value
  WktEmpty(g) => ..., // Empty value, e.g. "POINT EMPTY"
}

// Note that all T, Z, and M types of the 3dM geo-types in WKT are used as the same `T` generic type, except for the empty type
pub enum Wkt {
    Wkt(Geometry<T>),
    WktM(GeometryM<T>),
    Wkt3D(Geometry3D<T>),
    Wkt3DM(Geometry3DM<T>),
    WktEmpty(Geometry<NoValue>),
}

Avoid intermediate `Wkt` allocation for `ToWkt::wkt_string` for geo-types

Currently geo-types uses the default implementation of wkt_string

fn wkt_string(&self) -> String {
    self
        .to_wkt() // <-- this allocates a Wkt struct
        .to_string()
}

Instead we could write an individual implementation for each geometry type that doesn't rely on first converting to a Wkt struct. e.g.

impl ToWkt for geo_types::Point {
    ...
    fn wkt_string(&self) -> String {
        format!("POINT({} {})", self.x, self.y)
    } 
}

There might be better and worse ways to do this WRT code re-use of the existing serialization methods on Wkt.

crates publishing team

👋 Hi @frewsxcv and @mgax

Following up on georust/meta#21, I created a new wkt-publishers team for publishing to crates.io with you two as maintainers (actually since @frewsxcv is an organization owner, GH won't let me mark him as a maintainer, but it'd be redundant permissions anyway 🤷).

Could you please update the crate owners? Specifically:


  • update wkt crate owners on crates.io:

You two are the only "user owners" of that crate, so only you two can edit that crate's owners:

cd wkt
# 💥 remove large publishing group
cargo owner --remove github:georust:core
# 🔐 add small publishing group
cargo owner --add github:georust:wkt-publishers

# 🚨 consider removing mgax, since they expressed no not lately being active on the project
cargo owner --remove mgax

# 🚨 and from https://github.com/orgs/georust/teams/wkt-publishers

# make sure everything looks good 👀 
cargo owner --list

And if there's anyone else you'd like to be able to publish the crates, feel free to add them to your publishing team, or as a user-owner. Consider that user-owners, as opposed to owners-via-team, can themselves edit the crate's owners, which could be desirable, or not, depending.

Let me know if you have any questions, or you can review georust/meta#21.

Make geo-types a non-optional dependency

As part of #71 I've written some examples. However, the most simple "practical" example uses geo-types, which is enabled by default, but optional, which means we can't run --no-default-features in tests (The use of unstable nightly features notwithstanding).

Is there a reason to keep geo-types as an optional feature? Are there existing uses cases which don't make use of geo-types? Is it likely that potential users of the crate will be put off by a dependency on geo-types?

Output `TRIANGLE` type?

I'm not sure if we should support this, but I wanted to at least gather some facts for documentation.

postgis supports it:

SELECT ST_AsText('TRIANGLE((0 0,0 1,1 1,0 0 ))');
          st_astext          
-----------------------------
 TRIANGLE((0 0,0 1,1 1,0 0))
(1 row)

JTS does not support it

reader.read("TRIANGLE((0 0,0 1,1 1,0 0 ))");
> org.locationtech.jts.io.ParseException: Unknown geometry type: TRIANGLE (line 1)

Nor does GEOS

     message: `ParseException: Unknown type: 'TRIANGLE'`

Add MIT license file

This repo is licensed Apache/MIT, but we only host the Apache license file in the repo. Copy the LICENSE-MIT file from georust/geo

Relax ToWkt impls to work with CoordNum

Currently the ToWkt impls. need T: CoordFloat + Display on the geometry. Can this be relaxed to T: CoordNum + Display? Or, is this a big structural change? Ideally, even FromWkt should rather use CoordNum + FromStr than CoordFloat if that's possible.

Non-geospatial type support

  • CircularString
  • CompoundCurve
  • CurvePolygon
  • MultiCurve
  • MultiSurface
  • Curve
  • Surface
  • PolyhedralSurface
  • TIN
  • Triangle

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.