navibyte / geospatial

Geospatial data structures, tools and utilities for Dart and Flutter.
Other
51 stars 5 forks source link

Add Extended WKB or EWKB support #224

Closed flikkr closed 3 months ago

flikkr commented 3 months ago

I have a WKB as a String stored in my Postgres table. The value is 0101000020E6100000AFCF9CF529765440920F30A9906F3940, which when converted using this online converter returns the correct WKT format: POINT(81.846311 25.4358011). I know I can use PostGIS extension to handle these conversions but I would like to handle the conversion on the frontend for simplicity.

I've tried using Point.decode() to pass in a UInt8List representation of the WKB string, which I encode to hex using the following function:

final bin = '0101000020E6100000AFCF9CF529765440920F30A9906F3940';

List<int> ibytes = [];
for (int i = 0; i < bin.length; i++) {
  String hexDigit = bin.substring(i, i+1);
  ibytes.add(int.parse(hexDigit, radix: 16));
}

final bytes = Uint8List.fromList(ibytes);
final point = Point.decode(b);

I get an expection when trying to decode the point which has the following stack trace. I debugged the error and it seems like the WKB id is 3001, which happens in coords.dart file:

Unhandled exception:
FormatException: Invalid WKB id
#0      Coords.fromWkbId (package:geobase/src/common/codes/coords.dart:170:9)
#1      _WkbGeometryBufferDecoder._buildGeometry (package:geobase/src/vector/formats/wkb/wkb_decoder.dart:70:30)
#2      _WkbGeometryBufferDecoder.buildAll (package:geobase/src/vector/formats/wkb/wkb_decoder.dart:51:7)
#3      _WkbGeometryDecoder.decodeBytes (package:geobase/src/vector/formats/wkb/wkb_decoder.dart:25:7)
#4      GeometryBuilder.decode (package:geobase/src/vector_data/model/geometry/geometry_builder.dart:228:13)
#5      new Point.decode (package:geobase/src/vector_data/model/geometry/point.dart:227:23)
#6      _wkbSample2 (file:///Users/kazymirrabier/Projects/test/geospatial/dart/geobase/example/geobase_example.dart:1067:25)
#7      main (file:///Users/kazymirrabier/Projects/test/geospatial/dart/geobase/example/geobase_example.dart:61:3)
#8      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#9      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
navispatial commented 3 months ago

Currently WKB support for the geobase package is implemented according to specs:

Well-Known binary from GEOS docs describes three flavors of WKB:

So the implemented one is "Standard WKB".

It seems that your sample is "Extended WKB" data that is not currently implemented.

There is also some other needs known to implement support for EWKB (and EWKT) in geobase, so it's could be a candidate for version 1.1.0.

There is also PostGIS specific docs about EWKB:

Changing name of the issue to "Add Extended WKB or EWKB support".

navispatial commented 3 months ago

Original name of the issue was "Decode WKB Point to WKT from String", changed to "Add Extended WKB or EWKB support".

navispatial commented 3 months ago

And checking previous issues.... there was already some feature requests (and sample code) on #165 and also #29 for WKT relates to this. However keeping this issue #224 until implemented even if partially a duplicate with #165.

navispatial commented 3 months ago

Support for decoding EWKB data (however ignoring any SRID) is added to geobase version 1.0.2.

From release notes:

🧩 Features:

A sample to test decoding HEX-EWKB data:

import 'dart:typed_data';

import 'package:geobase/geobase.dart';

void _hexewkbSample() {
  // HEX-EWKB samples
  const hexewkbSamples = [
    // "POINT(10.1 20.2)" with SRID 4326
    '0101000020E610000033333333333324403333333333333440',

    // "POINT Z(10.1 20.2 10.1)" without SRID
    '0101000080333333333333244033333333333334403333333333332440',

    // "LINESTRING(10.1 10.1,20.2 20.2,30.3 30.3)" with SRID 4326
    '0102000020E6100000030000003333333333332440333333333333244033333333333334403333333333333440cdcccccccc4c3e40cdcccccccc4c3e40',
  ];

  // decode each sample and print as WKT string
  for (final hexewkb in hexewkbSamples) {
    // convert hex string to byte data as Uint8List
    final len = hexewkb.length;
    final byteCount = len ~/ 2;
    final bytes = List.filled(byteCount, 0);
    for (var i = 0; i < byteCount; i++) {
      bytes[i] = int.parse(hexewkb.substring(i * 2, i * 2 + 2), radix: 16);
    }
    final ewkbBytes = Uint8List.fromList(bytes);

    // decode as a geometry object
    final geom = GeometryBuilder.decode(ewkbBytes, format: WKB.geometry);

    // print as WKT string
    print(geom.toText(format: WKT.geometry));
  }
}

Full support for EWKB (encode to EWKB bytes or hex, support reading SRIDs, etc) shall be implemented in future (in some version 1.1.0, 1.2.0, .. or 2.0.0), depends what kind of library API changes are needed and what other features shall be implemented.

This support is tracked in the #165 issue.

So maybe closing this issue?