Description
Discussed in #6273
Originally posted by glennsl May 28, 2023
I've been trying to model GeoJSON using rescript@11 types, just to see if I can, and am stumbling on just a few issues that I think should be feasible (though not easy) to fix since all the necessary features do exist in various forms. So I figured I'd document this as a prominent use case.
See the full TypeScript type definitions for reference.
The big problem is that a GeoJSON document can be either a single geometry object (of which there are several kinds), a "feature" which contains geometry, or a feature collection. These all use a type
tag, but because geometry objects are included in several different types, modeling this requires structural typing. And unfortunately @tag
only works on nominal variants, not structural (polymorphic) ones. This is also the case with inline records, which is necessary for @tag
to be useful, so that's another hurdle.
Essentially, I'd like to define the types like this:
@tag("type")
type rec geometry = [
| #Point({coordinates: pos})
| #MultiPoint({coordinates: array<pos>})
| #LineString({coordinates: array<pos>})
| #MultiLineString({coordinates: array<array<pos>>})
| #Polygon({coordinates: array<array<pos>>})
| #MultiPolygon({coordinates: array<array<array<pos>>>})
| #GeometryCollection({coordinates: array<geometry>})
]
@tag("type")
type feature = [
| #Feature({geometry: geometry})
]
@tag("type")
type t = [
| geometry
| feature
| #FeatureCollection({features: array<feature>})
]
Another minor problem is that positions and bounding boxes are arrays of different arities. These could be modeled with an unboxed tagged variant containing tuples of different arities, and possibly a constructor an array to cover the remaining arities. E.g.:
@unboxed
type pos =
| LatLng((float, float))
| LatLngAlt((float, float, float))
| M(array<float>)
Lots of edge cases to consider though. A further improvement could be to (optionally) model inline records in unboxed variants as arrays, so that it could be:
@unboxed
type pos =
| LatLng({lat: float, lng: float})
| LatLngAlt({lat: float, lng: float, alt: float})
| M(array<float>)
```</div>