-
Notifications
You must be signed in to change notification settings - Fork 62
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
Improved ergonomics with ToGeoJson and TryFromGeoJson traits #184
Comments
Thinking about this a bit more - it seems like a common use case would be to build a feature by adding properties to a geo-types geometry. I think a better approach might be: trait ToGeoJsonGeometry {
// Implementor must construct a geojson::Geometry - we might consider a blanket
// implementation for all `G: Into<geojson::Geometry>`
fn geojson_geometry(&self) -> geojson::Geometry<T>;
// the rest of the methods can have a default impl
fn geojson_geometry_string(&self) -> String {
self.geojson_geometry().to_string()
}
fn geojson_feature_with_props(&self, props: Option<JsonObject>) -> geojson::Feature<T> {
let mut feature = geojson::Feature::from(self.geojson_geometry());
feature.props = props
return feature
}
fn geojson_feature_with_props_string(&self, props: Option<GeoJsonProperties>) -> String {
self. geojson_feature_with_props(props).to_string()
}
}
// anything that can be converted Into a geojson::Geometry can do all
// the things in `ToGeoJsonGeomtry`
impl<G: Into<geojson::Geometry>> ToGeoJsonGeometry for G {
fn geojson_geometry(self) {
self.into()
}
}
// If your own type is composed of a geometry and its properties, you might want an impl like this
trait ToGeoJsonFeature {
fn geojson_feature(&self) -> geojson::Feature<T>;
fn geojson_feature_string(&self) -> String;
}
// Example 1 - I have my own type that combines geometry and feature data
// so I impl ToGeoJsonFeature.
struct MyPointOfInterest {
point: geo_types::Point<f32>
name: String
}
impl ToGeoJsonFeature for MyType {
fn geojson_feature(&self) -> geojson::Feature<f32> {
self.point
.geojson_feature_with_props(Some(json!({"name": self.name.clone()})))
}
}
let garys_place = MyPointOfInterest { point: geo_types::point!(x: 1., y: 2.), name: "Gary's house" };
assert_eq!(garys_place.geojson_feature_string(), r#"{ "type": "Feature", "geometry": {"coordinates":[1.0,2.0],"type":"Point"}, "properties": {"name": "Gary's house" }}"#)
// Example 2 - Working with geometry only (no feature data)
let point = geo_types::point!(x: 0, y: 1);
// a bare geometry
assert_eq!(point.geojson_geometry_string(), r#"{"coordinates":[1.0,2.0],"type":"Point"}"#)
// a feature with no properties
assert_eq!(point.geojson_feature_with_properties_string(None), r#"{ "type": "Feature", "geometry": {"coordinates":[1.0,2.0],"type":"Point"}}"#)
// Example 3 - Ad hoc feature properties
assert_eq!(point.geojson_feature_with_properties_string(Some(json!({"foo": 1 }), r#"{ "type": "Feature", "geometry": {"coordinates":[1.0,2.0],"type":"Point"}, "properties": {"foo": 1 }}"#) |
Indeed the process of adding properties was something I would miss in your original proposal, really like this additional suggestion! On the function name, why |
You're right to bring it up - it feels redundant and verbose. My thinking was that there was value in having different names for methods across traits:
Granted it would work (in that it'd compile) to have them share the same name:
But the ambiguity could produce some error messages that might be confusing in practice - especially if a type implements both traits:
I haven't prototyped any of this outside of GH comments yet - maybe it won't be so bad. |
If you've been following the recent changes I made in the WKT crate (georust/wkt#89, georust/wkt#95), I'm interested in doing a similar thing here in the geojson crate: avoid exposing the end user to the intermediate GeoJson struct, and instead let them go directly from their type (e.g. geo-types) to the serialized strings (and back again). This isn't a performance change - it's only a way to expose the user to shorter code with less concepts.
Specifically, I'd like to add two new traits (one for serialization, the other for deserialization), and conform all the geo-types.
For the deserialization, something like:
Which would be used like (equivalent usage with the current API in red):
And for serialization:
ToGeoJson
is more complicated because of the geometry/feature/feature collection distinction. This is my best guess as to what might be good. I'm interested in feedback if you have any.Note that in the case of the geo-types integration, no
properties
would be written. If the user wanted to serialize properties, I guess they'd callgeojson_feature()
then mutate it. Or they could continue to do whatever they're doing now, without using these API's.The "before" implementation here is actually already pretty concise, but the new code removes the concept of instantiating the intermediate
GeoJson
type, and also I think it's less surprising that it's returning a geojson geometry string (as opposed to a possibly a feature string).The text was updated successfully, but these errors were encountered: