Skip to content

Commit

Permalink
chore(gis): Move WKT parsing into gis module (#3117)
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen authored Oct 4, 2024
1 parent c806c01 commit fd657d5
Show file tree
Hide file tree
Showing 54 changed files with 1,271 additions and 113 deletions.
24 changes: 24 additions & 0 deletions modules/gis/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,27 @@ export {geojsonToBinary} from './lib/binary-features/geojson-to-binary';
export {geojsonToFlatGeojson} from './lib/binary-features/geojson-to-flat-geojson';
export {binaryToGeojson, binaryToGeometry} from './lib/binary-features/binary-to-geojson';
export {transformBinaryCoords, transformGeoJsonCoords} from './lib/binary-features/transform';

// WKT

export {parseWKT, isWKT, WKT_MAGIC_STRINGS} from './lib/wkt/parse-wkt';

export {encodeWKT} from './lib/wkt/encode-wkt';

export {isWKB} from './lib/wkt/parse-wkb-header';
export {parseWKB} from './lib/wkt/parse-wkb';
export {encodeWKB} from './lib/wkt/encode-wkb';

export {parseTWKB, isTWKB} from './lib/wkt/parse-twkb';
export {encodeTWKB} from './lib/wkt/encode-twkb';

export type {ParseWKTCRSOptions, WKTCRS} from './lib/wkt/parse-wkt-crs';
export {parseWKTCRS} from './lib/wkt/parse-wkt-crs';
export type {EncodeWKTCRSOptions} from './lib/wkt/encode-wkt-crs';
export {encodeWKTCRS} from './lib/wkt/encode-wkt-crs';

// EXPERIMENTAL APIs

export type {WKBHeader} from './lib/wkt/parse-wkb-header';

export {encodeHex, decodeHex} from './lib/utils/hex-transcoder';
22 changes: 7 additions & 15 deletions modules/gis/src/lib/tables/convert-table-to-geojson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import type {LoaderWithParser} from '@loaders.gl/loader-utils';
import type {
ArrayRowTable,
GeoJSONTable,
Expand All @@ -14,12 +13,13 @@ import type {
import {getTableLength, getTableRowAsObject} from '@loaders.gl/schema-utils';

import {GeoColumnMetadata, getGeoMetadata} from '../geo/geoparquet-metadata';
import {parseWKB} from '../wkt/parse-wkb';
import {parseWKT} from '../wkt/parse-wkt';

/** TODO - move to loaders.gl/gis? */
export function convertWKBTableToGeoJSON(
table: ArrayRowTable | ObjectRowTable,
schema: Schema,
loaders: LoaderWithParser[]
schema: Schema
): GeoJSONTable {
const geoMetadata = getGeoMetadata(schema);
const primaryColumn = geoMetadata?.primary_column;
Expand All @@ -33,7 +33,7 @@ export function convertWKBTableToGeoJSON(
const length = getTableLength(table);
for (let rowIndex = 0; rowIndex < length; rowIndex++) {
const row = getTableRowAsObject(table, rowIndex);
const geometry = parseGeometry(row[primaryColumn], columnMetadata, loaders);
const geometry = parseGeometry(row[primaryColumn], columnMetadata);
delete row[primaryColumn];
const feature: Feature = {type: 'Feature', geometry: geometry!, properties: row};
features.push(feature);
Expand All @@ -42,24 +42,16 @@ export function convertWKBTableToGeoJSON(
return {shape: 'geojson-table', schema, type: 'FeatureCollection', features};
}

function parseGeometry(
geometry: unknown,
columnMetadata: GeoColumnMetadata,
loaders: LoaderWithParser[]
): Geometry | null {
function parseGeometry(geometry: unknown, columnMetadata: GeoColumnMetadata): Geometry | null {
switch (columnMetadata.encoding) {
case 'wkt':
const wktLoader = loaders.find((loader) => loader.id === 'wkt');
return wktLoader?.parseTextSync?.(geometry as string) || null;
return parseWKT(geometry as string) || null;
case 'wkb':
default:
const wkbLoader = loaders.find((loader) => loader.id === 'wkb');
const arrayBuffer = ArrayBuffer.isView(geometry)
? geometry.buffer.slice(geometry.byteOffset, geometry.byteOffset + geometry.byteLength)
: (geometry as ArrayBuffer);
const geojson = wkbLoader?.parseSync?.(arrayBuffer, {
wkb: {shape: 'geojson-geometry'}
}) as unknown as Geometry;
const geojson = parseWKB(arrayBuffer, {shape: 'geojson-geometry'}) as unknown as Geometry;
return geojson; // binaryGeometry ? binaryToGeometry(binaryGeometry) : null;
// const binaryGeometry = WKBLoader.parseSync?.(geometry);
// ts-ignore
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {
Geometry
} from '@loaders.gl/schema';

import {BinaryWriter} from './utils/binary-writer';
import {BinaryWriter} from '../utils/binary-writer';
import {WKBGeometryType} from './parse-wkb-header';

type TWKBPrecision = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
GeometryCollection
} from '@loaders.gl/schema';

import {BinaryWriter} from './utils/binary-writer';
import {BinaryWriter} from '../utils/binary-writer';

/**
* Integer code for geometry type
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
MultiLineString,
MultiPolygon
} from '@loaders.gl/schema';
import {BinaryReader} from './utils/binary-reader';
import {BinaryReader} from '../utils/binary-reader';
import {WKBGeometryType} from './parse-wkb-header';

/**
Expand Down Expand Up @@ -57,7 +57,7 @@ type TWKBHeader = {
mPrecisionFactor: number;
};

export function parseTWKBGeometry(arrayBuffer: ArrayBuffer): Geometry {
export function parseTWKB(arrayBuffer: ArrayBuffer): Geometry {
const binaryReader = new BinaryReader(arrayBuffer);

const context = parseTWKBHeader(binaryReader);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,26 @@ import type {
Geometry
} from '@loaders.gl/schema';
import {binaryToGeometry} from '@loaders.gl/gis';
import type {WKBLoaderOptions} from '../wkb-loader';

import {parseWKBHeader, WKBGeometryType} from './parse-wkb-header';

export function parseWKB(
arrayBuffer: ArrayBuffer,
options?: WKBLoaderOptions
options?: {shape?: 'geojson-geometry' | 'binary-geometry'}
): BinaryGeometry | Geometry {
const binaryGeometry = parseWKBToBinary(arrayBuffer, options);
const shape = options?.wkb?.shape || 'binary-geometry';
const binaryGeometry = parseWKBToBinary(arrayBuffer);
const shape = options?.shape || 'binary-geometry';
switch (shape) {
case 'binary-geometry':
return binaryGeometry;
case 'geojson-geometry':
return binaryToGeometry(binaryGeometry);
case 'geometry':
// eslint-disable-next-line no-console
console.error('WKBLoader: "geometry" shape is deprecated, use "binary-geometry" instead');
return binaryToGeometry(binaryGeometry);
default:
throw new Error(shape);
}
}

export function parseWKBToBinary(
arrayBuffer: ArrayBuffer,
options?: WKBLoaderOptions
): BinaryGeometry {
export function parseWKBToBinary(arrayBuffer: ArrayBuffer): BinaryGeometry {
const dataView = new DataView(arrayBuffer);

const wkbHeader = parseWKBHeader(dataView);
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
22 changes: 22 additions & 0 deletions modules/gis/test/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

// binary features
import './binary-features/binary-to-geojson.spec';
import './binary-features/geojson-to-flat-geojson.spec';
import './binary-features/geojson-to-binary.spec';
import './binary-features/transform.spec';

// utils
import './utils/hex-transcoder.spec';

// wkt parsers
import './wkt/parse-wkb.spec';
import './wkt/encode-wkb.spec';

// import './wkt/parse-twkb.spec';
import './wkt/encode-twkb.spec';

import './wkt/parse-hex-wkb.spec';

import './wkt/parse-wkt.spec';
import './wkt/encode-wkt.spec';

import './wkt/parse-wkt-crs.spec';
67 changes: 67 additions & 0 deletions modules/gis/test/wkt/encode-twkb.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

/**
import test from 'tape-promise/tape';
import {fetchFile, encodeSync} from '@loaders.gl/core';
import {WKBWriter} from '@loaders.gl/wkt';
import {parseTestCases} from '@loaders.gl/gis/test/data/wkt/parse-test-cases';
const WKB_2D_TEST_CASES = '@loaders.gl/gis/test/data/wkb-testdata2d.json';
const WKB_2D_NAN_TEST_CASES = '@loaders.gl/gis/test/data/wkb-testdata2d-nan.json';
const WKB_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkb-testdataZ.json';
const WKB_Z_NAN_TEST_CASES = '@loaders.gl/gis/test/data/wkb-testdataZ-nan.json';
test('encodeTWKB#2D', async (t) => {
const response = await fetchFile(WKB_2D_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());
for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: false, hasM: false}});
t.deepEqual(encoded, wkb);
}
t.end();
});
test('encodeTWKB#2D NaN', async (t) => {
const response = await fetchFile(WKB_2D_NAN_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());
for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: false, hasM: false}});
t.deepEqual(encoded, wkb);
}
t.end();
});
test('encodeTWKB#Z', async (t) => {
const response = await fetchFile(WKB_Z_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());
for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: true, hasM: false}});
t.deepEqual(encoded, wkb);
}
t.end();
});
test('encodeTWKB#Z NaN', async (t) => {
const response = await fetchFile(WKB_Z_NAN_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());
for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: true, hasM: false}});
t.deepEqual(encoded, wkb);
}
t.end();
});
*/
65 changes: 65 additions & 0 deletions modules/gis/test/wkt/encode-wkb.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import test from 'tape-promise/tape';
import {fetchFile, encodeSync} from '@loaders.gl/core';
import {WKBWriter} from '@loaders.gl/wkt';
import {parseTestCases} from '@loaders.gl/gis/test/data/wkt/parse-test-cases';

const WKB_2D_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdata2d.json';
const WKB_2D_NAN_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdata2d-nan.json';
const WKB_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ.json';
const WKB_Z_NAN_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ-nan.json';

test('encodeWKB#2D', async (t) => {
const response = await fetchFile(WKB_2D_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());

for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: false, hasM: false}});
t.deepEqual(encoded, wkb);
}

t.end();
});

test('encodeWKB#2D NaN', async (t) => {
const response = await fetchFile(WKB_2D_NAN_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());

for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: false, hasM: false}});
t.deepEqual(encoded, wkb);
}

t.end();
});

test('encodeWKB#Z', async (t) => {
const response = await fetchFile(WKB_Z_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());

for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: true, hasM: false}});
t.deepEqual(encoded, wkb);
}

t.end();
});

test('encodeWKB#Z NaN', async (t) => {
const response = await fetchFile(WKB_Z_NAN_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());

for (const testCase of Object.values(TEST_CASES)) {
const {geoJSON, wkb} = testCase;
const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: true, hasM: false}});
t.deepEqual(encoded, wkb);
}

t.end();
});
49 changes: 49 additions & 0 deletions modules/gis/test/wkt/encode-wkt.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import test from 'tape-promise/tape';

import {encodeTextSync} from '@loaders.gl/core';
import {WKTWriter} from '@loaders.gl/wkt';

test('encodeWKT', (t) => {
t.throws(
() => encodeTextSync({type: 'FeatureCollection'}, WKTWriter),
'does not accept featurecollections'
);

// const fixtures = [
// 'LINESTRING (30 10, 10 30, 40 40)',
// 'POINT (1 1)',
// 'POINT (1 1 1 1)',
// 'LINESTRING (1 2 3, 4 5 6)',
// 'LINESTRING (1 2 3 4, 5 6 7 8)',
// 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))',
// 'POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10), (20 30, 35 35, 30 20, 20 30))',
// 'MULTIPOINT (1 1, 2 3)',
// 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5), (10 10, 15 10, 15 15, 10 10)))',
// 'MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 10 30, 40 40))',
// 'GEOMETRYCOLLECTION (POINT (4 6), LINESTRING (4 6, 7 10))'
// ];

// fixtures.forEach((fix) => t.equal(fix, encodeSync(parse(fix, WKTLoader), WKTWriter), fix));

t.equal(
encodeTextSync(
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [42, 20]
}
},
WKTWriter
),
'POINT (42 20)',
'point equal'
);

t.end();
});
Loading

0 comments on commit fd657d5

Please sign in to comment.