Skip to content
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

Handling CRS ids from different sources when encoding/decoding external data and managing in-memory objets #226

Open
navispatial opened this issue Apr 10, 2024 · 0 comments
Labels
enhancement New feature or request 🌐 geobase Related to the code package "geobase"

Comments

@navispatial
Copy link
Member

navispatial commented Apr 10, 2024

This is not a trivial task to implement as a common solution.

Geospatial data formats

GeoJSON

GeoJSON (RFC 7946)

  • data expected to be WGS84 longitude / latitude
  • However, where all involved parties have a prior arrangement, alternative coordinate reference systems can be used without risk of data being misinterpreted
  • anyway no mechanisms to announce CRS identifiers

JSON-FG

See #53

  • when a geometry objects has "place" element, then an optional "coordRefSys" field can specify an alternative coordinate system (other than WGS84 longitude / latitude)
  • there are multiple ways to specify such an CRS identifier (or not just id but also properties of such a reference system), alternatives listed below, copied from the JSON-FG editorial draft

A reference system can be specified in a JSON-FG document using a "coordRefSys" member...:

  • URI: "http://www.opengis.net/def/crs/EPSG/0/3857"
  • Safe CURIE: "[EPSG:3857]"
  • A reference system value by reference (URI) and with an epoch:
     {
    "type": "Reference",
    "href": "http://www.opengis.net/def/crs/EPSG/0/4979",
    "epoch": 2017.23
    }
    
  • A ad hoc compound reference system value (using URIs):
    [
    {
      "type": "Reference",
      "href": "http://www.opengis.net/def/crs/EPSG/0/4258",
      "epoch": 2016.47
    },
    "http://www.opengis.net/def/crs/EPSG/0/7837"
    ]
    

WKT

Well-known text representation of geometry (WKT)

  • no mechanisms to announce CRS identifiers

WKB

Well-known binary representation of geometry (WKB)

  • no mechanisms to announce CRS identifiers

EWKT

CRS identifier as an integer (assuming an EPSG code here) just like wikipedia page describes:

SRID=4326;POINT(-44.3 60.1)

See PostGIS: ST_GeomFromEWKT for more examples.

EWKB

See

SRID integer can be encoded after wkbType in binary encoding.

Geobase 1.0.x implementation

CoordRefSys class

CoordRefSys

Related class to provide logic, can be customized by extending and registering: CoordRefSysResolver.

Feature and Geometry classes

Feature and geometry objects are specified by the vector_data sub package:

  • FeatureObject
    • FeatureCollection
    • Feature
  • Geometry
    • SimpleGeometry
      • Point
      • LineString
      • Polygon
      • MultiPoint
      • MultiLineString
      • MultiPolygon
    • GeometryCollection

These classes do not have members for CRS information. It's assumed that a CRS applied is known by an application context, or stored outside feature and geometry objects. This should work as long as all feature and geometry objects share a same CRS.

Some encoding / decoding methods has a parameter allowing passing such CRS information to a encoder / decoder.

For example Point:

/// Parses a point geometry from text conforming to format.
Point.parse(String text, {TextReaderFormat<SimpleGeometryContent> format = GeoJSON.geometry, CoordRefSys? crs, Map<String, dynamic>? options});

/// The string representation of this geometry object, with format applied.
String toText({TextWriterFormat<SimpleGeometryContent> format = GeoJSON.geometry, int? decimals, CoordRefSys? crs, Map<String, dynamic>? options});

Text format

Such a crs parameter is passed to a text format object that has following definition:

TextWriterFormat

/// Returns a text format decoder that decodes text as Content to builder.
ContentDecoder decoder(Content builder, {CoordRefSys? crs, Map<String, dynamic>? options});

TextWriterFormat

/// Returns a text format encoder for Content.
ContentEncoder<Content> encoder({StringSink? buffer, int? decimals, CoordRefSys? crs, Map<String, dynamic>? options}); 

An optional CoordRefSys? crs is then passed to actual decoder or encoder implementation, that can use crs information for example to handle axis order logic for all geospatial features and geometries applied.

Content interfaces

Decoders, encoders and object builder also use "content interfaces" to send decoded or encoded elements, for example SimpleGeometryContent:

/// Writes a point geometry with position.
void point(Position position, {String? name});

/// Writes a line string geometry with a chain of positions.
void lineString(PositionSeries chain, {String? name, Box? bounds});

/// ....

Currently methods on these content interfaces do not have a parameter for a geometry or feature instance specific CRS information (only decoder / encoder specific common value is available for some formats).

Issues with current implementation

No issues when decoding/encoding GeoJSON, WKT or WKB data.

However supporting EWKT, EWKB and JSON-FG properly might require a way to pass geometry or feature object specific CRS identifier between decoders, encoders and object builders.

And supporting instantiating an object tree parsed from a JSON-FG document might require an optional CRS member for each feature and geometry objects. So that when null either a default CRS (like WGS 84) or a CRS from a parent object would be assumed.

Geobase 1.1.x proposal

See #165 both decoding and encoding of EWKB can be implemented without major changes.

Geobase 2.0.x proposal

CoordRefSys class

Should be ensured that integer ids (from EWKB or EWKT) and different type of URI, CURIE, lists of URIs / CURIEs, URIs or CURIEs with epoch data (that are going to be used by JSON-FG) etc. can be parsed and used.

Add some basic logic to CoordRefSysResolver to cover most common reference systems.

Feature and Geometry classes

Add the CoordRefSys? crs member to feature and geometry objects with backward compatibility in mind. Such information would be totally optional, so by default null value would be stored.

New constructors / static factories might be needed that allow storing an instance of CoordRefSys to crs member.

Content interfaces

Changes to signatures of all methods building a geometry or a feature would be needed. For example:

/// Writes a point geometry with position.
void point(Position position, {String? name, CoordRefSys? crs});

/// Writes a line string geometry with a chain of positions.
void lineString(PositionSeries chain, {String? name, Box? bounds, CoordRefSys? crs});

/// ....

Decoding

Decoders / parsers could use changed methods to pass a geometry or feature specific CRS information to consumers.

As a consumer for example object builders (like GeometryBuilder and FeatureBuilder) would then use any non-null crs data when instantiating geometries or features.

Encoding

Similarly when encoding / writing a geometry or feature object to external data format.

Currently Point has methods to write as text format:

  @override
  void writeTo(SimpleGeometryContent writer, {String? name}) =>
      isEmptyByGeometry
          ? writer.emptyGeometry(Geom.point, name: name)
          : writer.point(position, name: name);

  @override
  String toText({
    TextWriterFormat<SimpleGeometryContent> format = GeoJSON.geometry,
    int? decimals,
    CoordRefSys? crs,
    Map<String, dynamic>? options,
  }) {
    final encoder =
        format.encoder(decimals: decimals, crs: crs, options: options);
    writeTo(encoder.writer);
    return encoder.toText();
  }

This should be changed to something like this:

  @override
  void writeTo(SimpleGeometryContent writer, {String? name}) =>
      isEmptyByGeometry
          ? writer.emptyGeometry(Geom.point, name: name)
          : writer.point(position, name: name, crs: this.crs);

  @override
  String toText({
    TextWriterFormat<SimpleGeometryContent> format = GeoJSON.geometry,
    int? decimals,
    CoordRefSys? crs,
    Map<String, dynamic>? options,
  }) {
    final encoder =
        format.encoder(decimals: decimals, crs: this.crs ?? crs, options: options);
    writeTo(encoder.writer);
    return encoder.toText();
  }

Target version

Changes described above can be implemented with backward compatibility - at least for package user, however if someone has extended classes mentioned above, then there might be some risks.

As package API changes are needed to multiple sub packages it would be most safe to target geobase 2.0.0 version with changes described.

Also #29, #53, #165 should be associated to these changes too.

@navispatial navispatial added enhancement New feature or request 🌐 geobase Related to the code package "geobase" labels Apr 10, 2024
@navispatial navispatial added this to the geobase 2.0.0 milestone Apr 10, 2024
@navispatial navispatial modified the milestones: v2.0 on dart 3.3+, v3.0 on dart 3.3+ Apr 16, 2024
@navispatial navispatial removed this from the v3.0 on dart 3.3+ milestone May 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request 🌐 geobase Related to the code package "geobase"
Projects
None yet
Development

No branches or pull requests

1 participant