From fd657d5c0dc5bb10f8c8d34f72746f63fdf032ba Mon Sep 17 00:00:00 2001 From: Ib Green Date: Fri, 4 Oct 2024 10:48:24 -0400 Subject: [PATCH] chore(gis): Move WKT parsing into gis module (#3117) --- modules/gis/src/index.ts | 24 + .../lib/tables/convert-table-to-geojson.ts | 22 +- .../src/lib/utils/base64-encoder.ts | 0 .../src/lib/utils/binary-reader.ts | 0 .../src/lib/utils/binary-writer.ts | 0 .../{wkt => gis}/src/lib/utils/hex-encoder.ts | 0 .../src/lib/utils/hex-transcoder.ts | 0 .../lib => gis/src/lib/wkt}/encode-twkb.ts | 2 +- .../src/lib => gis/src/lib/wkt}/encode-wkb.ts | 2 +- .../lib => gis/src/lib/wkt}/encode-wkt-crs.ts | 0 .../src/lib => gis/src/lib/wkt}/encode-wkt.ts | 0 .../lib => gis/src/lib/wkt}/parse-hex-wkb.ts | 0 .../src/lib => gis/src/lib/wkt}/parse-twkb.ts | 4 +- .../src/lib/wkt}/parse-wkb-header.ts | 0 .../src/lib => gis/src/lib/wkt}/parse-wkb.ts | 16 +- .../lib => gis/src/lib/wkt}/parse-wkt-crs.ts | 0 .../src/lib => gis/src/lib/wkt}/parse-wkt.ts | 0 modules/{wkt => gis}/test/data/README.md | 0 .../test/data/wkt}/geometrycollection.geojson | 0 .../test/data/wkt}/geometrycollection.wkt | 0 .../test/data/wkt}/parse-test-cases.ts | 0 .../test/data/wkt}/wkb-testdata2d-nan.json | 0 .../test/data/wkt}/wkb-testdata2d.json | 0 .../test/data/wkt}/wkb-testdataZ-nan.json | 0 .../test/data/wkt}/wkb-testdataZ.json | 0 modules/gis/test/index.ts | 22 + .../test}/utils/hex-transcoder.spec.ts | 0 modules/gis/test/wkt/encode-twkb.spec.ts | 67 +++ modules/gis/test/wkt/encode-wkb.spec.ts | 65 +++ modules/gis/test/wkt/encode-wkt.spec.ts | 49 ++ modules/gis/test/wkt/parse-hex-twkb.spec.ts | 61 +++ modules/gis/test/wkt/parse-hex-wkb.spec.ts | 65 +++ modules/gis/test/wkt/parse-wkb.spec.ts | 65 +++ modules/gis/test/wkt/parse-wkt-crs.spec.ts | 265 +++++++++ modules/gis/test/wkt/parse-wkt.spec.ts | 511 ++++++++++++++++++ .../src/lib/parsers/parse-geoparquet.ts | 11 +- modules/wkt/src/hex-wkb-loader.ts | 16 +- modules/wkt/src/index.ts | 10 - modules/wkt/src/lib/{utils => }/version.ts | 0 modules/wkt/src/twkb-loader.ts | 8 +- modules/wkt/src/twkb-writer.ts | 4 +- modules/wkt/src/wkb-loader.ts | 11 +- modules/wkt/src/wkb-writer.ts | 4 +- modules/wkt/src/wkt-crs-loader.ts | 6 +- modules/wkt/src/wkt-crs-writer.ts | 8 +- modules/wkt/src/wkt-loader.ts | 4 +- modules/wkt/src/wkt-writer.ts | 4 +- modules/wkt/test/hex-wkb-loader.spec.ts | 6 +- modules/wkt/test/index.ts | 2 - modules/wkt/test/twkb-loader.spec.ts | 9 +- modules/wkt/test/twkb-writer.spec.ts | 18 +- modules/wkt/test/wkb-loader.spec.ts | 9 +- modules/wkt/test/wkb-writer.spec.ts | 10 +- modules/wkt/test/wkt-loader.spec.ts | 4 +- 54 files changed, 1271 insertions(+), 113 deletions(-) rename modules/{wkt => gis}/src/lib/utils/base64-encoder.ts (100%) rename modules/{wkt => gis}/src/lib/utils/binary-reader.ts (100%) rename modules/{wkt => gis}/src/lib/utils/binary-writer.ts (100%) rename modules/{wkt => gis}/src/lib/utils/hex-encoder.ts (100%) rename modules/{wkt => gis}/src/lib/utils/hex-transcoder.ts (100%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/encode-twkb.ts (99%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/encode-wkb.ts (99%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/encode-wkt-crs.ts (100%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/encode-wkt.ts (100%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/parse-hex-wkb.ts (100%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/parse-twkb.ts (98%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/parse-wkb-header.ts (100%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/parse-wkb.ts (95%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/parse-wkt-crs.ts (100%) rename modules/{wkt/src/lib => gis/src/lib/wkt}/parse-wkt.ts (100%) rename modules/{wkt => gis}/test/data/README.md (100%) rename modules/{wkt/test/data => gis/test/data/wkt}/geometrycollection.geojson (100%) rename modules/{wkt/test/data => gis/test/data/wkt}/geometrycollection.wkt (100%) rename modules/{wkt/test/utils => gis/test/data/wkt}/parse-test-cases.ts (100%) rename modules/{wkt/test/data => gis/test/data/wkt}/wkb-testdata2d-nan.json (100%) rename modules/{wkt/test/data => gis/test/data/wkt}/wkb-testdata2d.json (100%) rename modules/{wkt/test/data => gis/test/data/wkt}/wkb-testdataZ-nan.json (100%) rename modules/{wkt/test/data => gis/test/data/wkt}/wkb-testdataZ.json (100%) rename modules/{wkt/test/lib => gis/test}/utils/hex-transcoder.spec.ts (100%) create mode 100644 modules/gis/test/wkt/encode-twkb.spec.ts create mode 100644 modules/gis/test/wkt/encode-wkb.spec.ts create mode 100644 modules/gis/test/wkt/encode-wkt.spec.ts create mode 100644 modules/gis/test/wkt/parse-hex-twkb.spec.ts create mode 100644 modules/gis/test/wkt/parse-hex-wkb.spec.ts create mode 100644 modules/gis/test/wkt/parse-wkb.spec.ts create mode 100644 modules/gis/test/wkt/parse-wkt-crs.spec.ts create mode 100644 modules/gis/test/wkt/parse-wkt.spec.ts rename modules/wkt/src/lib/{utils => }/version.ts (100%) diff --git a/modules/gis/src/index.ts b/modules/gis/src/index.ts index 6759224be1..b9d998c54b 100644 --- a/modules/gis/src/index.ts +++ b/modules/gis/src/index.ts @@ -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'; diff --git a/modules/gis/src/lib/tables/convert-table-to-geojson.ts b/modules/gis/src/lib/tables/convert-table-to-geojson.ts index 366a7262a0..03e1edb788 100644 --- a/modules/gis/src/lib/tables/convert-table-to-geojson.ts +++ b/modules/gis/src/lib/tables/convert-table-to-geojson.ts @@ -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, @@ -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; @@ -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); @@ -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 diff --git a/modules/wkt/src/lib/utils/base64-encoder.ts b/modules/gis/src/lib/utils/base64-encoder.ts similarity index 100% rename from modules/wkt/src/lib/utils/base64-encoder.ts rename to modules/gis/src/lib/utils/base64-encoder.ts diff --git a/modules/wkt/src/lib/utils/binary-reader.ts b/modules/gis/src/lib/utils/binary-reader.ts similarity index 100% rename from modules/wkt/src/lib/utils/binary-reader.ts rename to modules/gis/src/lib/utils/binary-reader.ts diff --git a/modules/wkt/src/lib/utils/binary-writer.ts b/modules/gis/src/lib/utils/binary-writer.ts similarity index 100% rename from modules/wkt/src/lib/utils/binary-writer.ts rename to modules/gis/src/lib/utils/binary-writer.ts diff --git a/modules/wkt/src/lib/utils/hex-encoder.ts b/modules/gis/src/lib/utils/hex-encoder.ts similarity index 100% rename from modules/wkt/src/lib/utils/hex-encoder.ts rename to modules/gis/src/lib/utils/hex-encoder.ts diff --git a/modules/wkt/src/lib/utils/hex-transcoder.ts b/modules/gis/src/lib/utils/hex-transcoder.ts similarity index 100% rename from modules/wkt/src/lib/utils/hex-transcoder.ts rename to modules/gis/src/lib/utils/hex-transcoder.ts diff --git a/modules/wkt/src/lib/encode-twkb.ts b/modules/gis/src/lib/wkt/encode-twkb.ts similarity index 99% rename from modules/wkt/src/lib/encode-twkb.ts rename to modules/gis/src/lib/wkt/encode-twkb.ts index f647d25d54..70594a9f90 100644 --- a/modules/wkt/src/lib/encode-twkb.ts +++ b/modules/gis/src/lib/wkt/encode-twkb.ts @@ -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 = { diff --git a/modules/wkt/src/lib/encode-wkb.ts b/modules/gis/src/lib/wkt/encode-wkb.ts similarity index 99% rename from modules/wkt/src/lib/encode-wkb.ts rename to modules/gis/src/lib/wkt/encode-wkb.ts index ad464df302..60607cc392 100644 --- a/modules/wkt/src/lib/encode-wkb.ts +++ b/modules/gis/src/lib/wkt/encode-wkb.ts @@ -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 diff --git a/modules/wkt/src/lib/encode-wkt-crs.ts b/modules/gis/src/lib/wkt/encode-wkt-crs.ts similarity index 100% rename from modules/wkt/src/lib/encode-wkt-crs.ts rename to modules/gis/src/lib/wkt/encode-wkt-crs.ts diff --git a/modules/wkt/src/lib/encode-wkt.ts b/modules/gis/src/lib/wkt/encode-wkt.ts similarity index 100% rename from modules/wkt/src/lib/encode-wkt.ts rename to modules/gis/src/lib/wkt/encode-wkt.ts diff --git a/modules/wkt/src/lib/parse-hex-wkb.ts b/modules/gis/src/lib/wkt/parse-hex-wkb.ts similarity index 100% rename from modules/wkt/src/lib/parse-hex-wkb.ts rename to modules/gis/src/lib/wkt/parse-hex-wkb.ts diff --git a/modules/wkt/src/lib/parse-twkb.ts b/modules/gis/src/lib/wkt/parse-twkb.ts similarity index 98% rename from modules/wkt/src/lib/parse-twkb.ts rename to modules/gis/src/lib/wkt/parse-twkb.ts index eee02ab300..fdb80e0263 100644 --- a/modules/wkt/src/lib/parse-twkb.ts +++ b/modules/gis/src/lib/wkt/parse-twkb.ts @@ -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'; /** @@ -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); diff --git a/modules/wkt/src/lib/parse-wkb-header.ts b/modules/gis/src/lib/wkt/parse-wkb-header.ts similarity index 100% rename from modules/wkt/src/lib/parse-wkb-header.ts rename to modules/gis/src/lib/wkt/parse-wkb-header.ts diff --git a/modules/wkt/src/lib/parse-wkb.ts b/modules/gis/src/lib/wkt/parse-wkb.ts similarity index 95% rename from modules/wkt/src/lib/parse-wkb.ts rename to modules/gis/src/lib/wkt/parse-wkb.ts index b54efa8c3b..3aa40695f1 100644 --- a/modules/wkt/src/lib/parse-wkb.ts +++ b/modules/gis/src/lib/wkt/parse-wkb.ts @@ -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); diff --git a/modules/wkt/src/lib/parse-wkt-crs.ts b/modules/gis/src/lib/wkt/parse-wkt-crs.ts similarity index 100% rename from modules/wkt/src/lib/parse-wkt-crs.ts rename to modules/gis/src/lib/wkt/parse-wkt-crs.ts diff --git a/modules/wkt/src/lib/parse-wkt.ts b/modules/gis/src/lib/wkt/parse-wkt.ts similarity index 100% rename from modules/wkt/src/lib/parse-wkt.ts rename to modules/gis/src/lib/wkt/parse-wkt.ts diff --git a/modules/wkt/test/data/README.md b/modules/gis/test/data/README.md similarity index 100% rename from modules/wkt/test/data/README.md rename to modules/gis/test/data/README.md diff --git a/modules/wkt/test/data/geometrycollection.geojson b/modules/gis/test/data/wkt/geometrycollection.geojson similarity index 100% rename from modules/wkt/test/data/geometrycollection.geojson rename to modules/gis/test/data/wkt/geometrycollection.geojson diff --git a/modules/wkt/test/data/geometrycollection.wkt b/modules/gis/test/data/wkt/geometrycollection.wkt similarity index 100% rename from modules/wkt/test/data/geometrycollection.wkt rename to modules/gis/test/data/wkt/geometrycollection.wkt diff --git a/modules/wkt/test/utils/parse-test-cases.ts b/modules/gis/test/data/wkt/parse-test-cases.ts similarity index 100% rename from modules/wkt/test/utils/parse-test-cases.ts rename to modules/gis/test/data/wkt/parse-test-cases.ts diff --git a/modules/wkt/test/data/wkb-testdata2d-nan.json b/modules/gis/test/data/wkt/wkb-testdata2d-nan.json similarity index 100% rename from modules/wkt/test/data/wkb-testdata2d-nan.json rename to modules/gis/test/data/wkt/wkb-testdata2d-nan.json diff --git a/modules/wkt/test/data/wkb-testdata2d.json b/modules/gis/test/data/wkt/wkb-testdata2d.json similarity index 100% rename from modules/wkt/test/data/wkb-testdata2d.json rename to modules/gis/test/data/wkt/wkb-testdata2d.json diff --git a/modules/wkt/test/data/wkb-testdataZ-nan.json b/modules/gis/test/data/wkt/wkb-testdataZ-nan.json similarity index 100% rename from modules/wkt/test/data/wkb-testdataZ-nan.json rename to modules/gis/test/data/wkt/wkb-testdataZ-nan.json diff --git a/modules/wkt/test/data/wkb-testdataZ.json b/modules/gis/test/data/wkt/wkb-testdataZ.json similarity index 100% rename from modules/wkt/test/data/wkb-testdataZ.json rename to modules/gis/test/data/wkt/wkb-testdataZ.json diff --git a/modules/gis/test/index.ts b/modules/gis/test/index.ts index df530b6f61..deaa4ae008 100644 --- a/modules/gis/test/index.ts +++ b/modules/gis/test/index.ts @@ -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'; diff --git a/modules/wkt/test/lib/utils/hex-transcoder.spec.ts b/modules/gis/test/utils/hex-transcoder.spec.ts similarity index 100% rename from modules/wkt/test/lib/utils/hex-transcoder.spec.ts rename to modules/gis/test/utils/hex-transcoder.spec.ts diff --git a/modules/gis/test/wkt/encode-twkb.spec.ts b/modules/gis/test/wkt/encode-twkb.spec.ts new file mode 100644 index 0000000000..1ef011e9b2 --- /dev/null +++ b/modules/gis/test/wkt/encode-twkb.spec.ts @@ -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(); +}); + */ diff --git a/modules/gis/test/wkt/encode-wkb.spec.ts b/modules/gis/test/wkt/encode-wkb.spec.ts new file mode 100644 index 0000000000..436bb37a93 --- /dev/null +++ b/modules/gis/test/wkt/encode-wkb.spec.ts @@ -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(); +}); diff --git a/modules/gis/test/wkt/encode-wkt.spec.ts b/modules/gis/test/wkt/encode-wkt.spec.ts new file mode 100644 index 0000000000..4ad91efd78 --- /dev/null +++ b/modules/gis/test/wkt/encode-wkt.spec.ts @@ -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(); +}); diff --git a/modules/gis/test/wkt/parse-hex-twkb.spec.ts b/modules/gis/test/wkt/parse-hex-twkb.spec.ts new file mode 100644 index 0000000000..c1e2b258f4 --- /dev/null +++ b/modules/gis/test/wkt/parse-hex-twkb.spec.ts @@ -0,0 +1,61 @@ +// loaders.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {fetchFile, parseSync} from '@loaders.gl/core'; +import {isTWKB} from '@loaders.gl/gis'; +import {TWKBLoader} 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_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ.json'; + +test('parseHexTWKB#2D', async (t) => { + const response = await fetchFile(WKB_2D_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const testCase of Object.values(TEST_CASES)) { + if (testCase.geoJSON.type === 'GeometryCollection') { + continue; // eslint-disable-line + } + + // Big endian + if (testCase.twkb && testCase.binary) { + t.ok(isTWKB(testCase.twkb), 'isTWKB(2D)'); + const geometry = {...testCase.geoJSON}; + // TODO - Weird empty geometry case, is that coorrect per spec? + if ( + geometry.coordinates.length === 1 && + // @ts-ignore + geometry.coordinates[0].length === 1 && + // @ts-ignore + geometry.coordinates[0][0].length === 0 + ) { + geometry.coordinates = []; + } + t.deepEqual(parseSync(testCase.twkb, TWKBLoader), geometry); + } + } + + t.end(); +}); + +// test('parseHexTWKB#Z', async (t) => { +// const response = await fetchFile(WKB_Z_TEST_CASES); +// const TEST_CASES = parseTestCases(await response.json()); + +// // TODO parseWKB outputs TypedArrays; testCase contains regular arrays +// for (const testCase of Object.values(TEST_CASES)) { +// if (testCase.geoJSON.type === 'GeometryCollection') { +// continue; +// } + +// if (testCase.wkbXdr && testCase.binary && testCase.geoJSON) { +// t.deepEqual(parseSync(testCase.twkbXdr, TWKBLoader, {wkb: {shape: 'geojson-geometry'}}), testCase.geoJSON); +// } +// } + +// t.end(); +// }); diff --git a/modules/gis/test/wkt/parse-hex-wkb.spec.ts b/modules/gis/test/wkt/parse-hex-wkb.spec.ts new file mode 100644 index 0000000000..cb7b2fe2ba --- /dev/null +++ b/modules/gis/test/wkt/parse-hex-wkb.spec.ts @@ -0,0 +1,65 @@ +// loaders.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {fetchFile, parseSync} from '@loaders.gl/core'; +import {HexWKBLoader} 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_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ.json'; + +test('parseHexWKB#2D', async (t) => { + const response = await fetchFile(WKB_2D_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const testCase of Object.values(TEST_CASES)) { + // Little endian + if (testCase.wkbHex && testCase.binary) { + t.deepEqual( + parseSync(testCase.wkbHex, HexWKBLoader, {wkb: {shape: 'binary-geometry'}}), + testCase.binary + ); + } + + // Big endian + if (testCase.wkbHexXdr && testCase.binary) { + t.deepEqual( + parseSync(testCase.wkbHexXdr, HexWKBLoader, {wkb: {shape: 'binary-geometry'}}), + testCase.binary + ); + } + } + + t.end(); +}); + +test('parseHexWKB#Z', async (t) => { + const response = await fetchFile(WKB_Z_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const testCase of Object.values(TEST_CASES)) { + // Little endian + if (testCase.wkbHex && testCase.binary) { + t.deepEqual( + parseSync(testCase.wkbHex, HexWKBLoader, {wkb: {shape: 'binary-geometry'}}), + testCase.binary, + testCase.wkbHex.slice(0, 60) + ); + } + + // Big endian + if (testCase.wkbHexXdr && testCase.binary) { + t.deepEqual( + parseSync(testCase.wkbHexXdr, HexWKBLoader, {wkb: {shape: 'binary-geometry'}}), + testCase.binary, + testCase.wkbHexXdr.slice(0, 60) + ); + } + } + + t.end(); +}); diff --git a/modules/gis/test/wkt/parse-wkb.spec.ts b/modules/gis/test/wkt/parse-wkb.spec.ts new file mode 100644 index 0000000000..353e55a80b --- /dev/null +++ b/modules/gis/test/wkt/parse-wkb.spec.ts @@ -0,0 +1,65 @@ +// loaders.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {fetchFile, parseSync} from '@loaders.gl/core'; +import {isWKB} from '@loaders.gl/gis'; +import {WKBLoader} 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_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ.json'; + +test('parseWKB#2D', async (t) => { + const response = await fetchFile(WKB_2D_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + const TEST_CASES2 = {multiPolygonWithTwoPolygons: TEST_CASES.multiPolygonWithTwoPolygons}; + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const [title, testCase] of Object.entries(TEST_CASES2)) { + // Little endian + if (testCase.wkb && testCase.binary) { + t.ok(isWKB(testCase.wkb), 'isWKB(2D)'); + const result = parseSync(testCase.wkb, WKBLoader, {wkb: {shape: 'binary-geometry'}}); + t.deepEqual(result, testCase.binary, title); + } + + // Big endian + if (testCase.wkbXdr && testCase.binary) { + t.ok(isWKB(testCase.wkbXdr), 'isWKB(2D)'); + const result = parseSync(testCase.wkbXdr, WKBLoader, {wkb: {shape: 'binary-geometry'}}); + t.deepEqual(result, testCase.binary, title); + } + } + + t.end(); +}); + +test('parseWKB#Z', async (t) => { + const response = await fetchFile(WKB_Z_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const [title, testCase] of Object.entries(TEST_CASES)) { + // Little endian + if (testCase.wkb && testCase.binary) { + t.ok(isWKB(testCase.wkb), 'isWKB(Z)'); + const result = parseSync(testCase.wkb, WKBLoader); + t.deepEqual(result, testCase.binary, title); + } + + // Big endian + if (testCase.wkbXdr && testCase.binary) { + t.ok(isWKB(testCase.wkbXdr), 'isWKB(Z)'); + const result = parseSync(testCase.wkbXdr, WKBLoader); + t.deepEqual(result, testCase.binary, title); + } + + // if (testCase.wkbXdr && testCase.binary && testCase.geoJSON) { + // t.deepEqual(parseSync(testCase.wkbXdr, WKBLoader, {wkb: {shape: 'geometry'}}), testCase.geoJSON); + // } + } + + t.end(); +}); diff --git a/modules/gis/test/wkt/parse-wkt-crs.spec.ts b/modules/gis/test/wkt/parse-wkt-crs.spec.ts new file mode 100644 index 0000000000..c64a778973 --- /dev/null +++ b/modules/gis/test/wkt/parse-wkt-crs.spec.ts @@ -0,0 +1,265 @@ +// loaders.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors +// parse-wkt-crs was forked from https://github.com/DanielJDufour/wkt-crs under Creative Commons CC0 1.0 license. + +import test from 'tape-promise/tape'; +import {parseSync, encodeTextSync} from '@loaders.gl/core'; +import {WKTCRSLoader, WKTCRSWriter} from '@loaders.gl/wkt'; + +const roundtrip = (wkt) => encodeTextSync(parseSync(wkt, WKTCRSLoader, {raw: true}), WKTCRSWriter); + +const condense = (wkt) => wkt.trim().replace(/(?<=[,\[\]])[ \n]+/g, ''); + +test('parseWKTCRS#NAD27 / UTM zone 16N', (t) => { + const wkt = + 'PROJCS["NAD27 / UTM zone 16N",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-87],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","26716"]]'; + const data = parseSync(wkt, WKTCRSLoader, {raw: false, debug: false}); + // console.log(JSON.stringify(data, undefined, 2)); + t.deepEqual(data.length, 1); + t.deepEqual(Object.keys(data), ['0', 'PROJCS']); + t.deepEqual(data.PROJCS.AUTHORITY, ['AUTHORITY', 'EPSG', '26716']); + t.deepEqual(data.PROJCS === data[0], true); + t.deepEqual(data.PROJCS[1] === 'NAD27 / UTM zone 16N', true); + t.deepEqual(data.PROJCS.GEOGCS === data[0][2], true); + + // raw mode + // t.deepEqual(roundtrip(wkt), wkt); + t.end(); +}); + +test('parseWKTCRS#wikipedia example', (t) => { + const wkt = `GEODCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84", 6378137, 298.257223563, LENGTHUNIT["metre", 1]]], + CS[ellipsoidal, 2], + AXIS["Latitude (lat)", north, ORDER[1]], + AXIS["Longitude (lon)", east, ORDER[2]], + ANGLEUNIT["degree", 0.0174532925199433]]`; + const data = parseSync(wkt, WKTCRSLoader, {debug: false}); + t.deepEqual(data.GEODCRS[1], 'WGS 84'); + t.deepEqual(data.GEODCRS.DATUM.ELLIPSOID[3], 298.257223563); + t.deepEqual(data.GEODCRS.CS[1], 'ellipsoidal'); + t.deepEqual(data.GEODCRS.ANGLEUNIT[2], 0.0174532925199433); + // t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test.skip('parseWKTCRS#wikipedia raw', (t) => { + const wkt = `GEODCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84", 6378137, 298.257223563, LENGTHUNIT["metre", 1]]], + CS[ellipsoidal, 2], + AXIS["Latitude (lat)", north, ORDER[1]], + AXIS["Longitude (lon)", east, ORDER[2]], + ANGLEUNIT["degree", 0.0174532925199433]]`; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data.GEODCRS[1], 'WGS 84'); + t.deepEqual(data.GEODCRS.DATUM.ELLIPSOID[3], 'raw:298.257223563'); + t.deepEqual(data.GEODCRS.CS[1], 'raw:ellipsoidal'); + t.deepEqual(data.GEODCRS.ANGLEUNIT[2], 'raw:0.0174532925199433'); + t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test('parseWKTCRS#wikipedia concat', (t) => { + const wkt = ` + CONCAT_MT[ + PARAM_MT["Mercator_2SP", + PARAMETER["semi_major",6370997.0], + PARAMETER["semi_minor",6370997.0], + PARAMETER["central_meridian",180.0], + PARAMETER["false_easting",-500000.0], + PARAMETER["false_northing",-1000000.0], + PARAMETER["standard parallel 1",60.0]], + PARAM_MT["Affine", + PARAMETER["num_row",3], + PARAMETER["num_col",3], + PARAMETER["elt_0_1",1], + PARAMETER["elt_0_2",2], + PARAMETER["elt 1 2",3]]] + `; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data.CONCAT_MT.PARAM_MT, undefined); + t.deepEqual(data.CONCAT_MT.MULTIPLE_PARAM_MT.length, 2); + // t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test.skip('parseWKTCRS#wikipedia datum shift', (t) => { + const wkt = ` + COORDINATEOPERATION["AGD84 to GDA94 Auslig 5m", + SOURCECRS["…full CRS definition required here but omitted for brevity…"], + TARGETCRS["…full CRS definition required here but omitted for brevity…"], + METHOD["Geocentric translations", ID["EPSG", 1031]], + PARAMETER["X-axis translation", -128.5, LENGTHUNIT["metre", 1]], + PARAMETER["Y-axis translation", -53.0, LENGTHUNIT["metre", 1]], + PARAMETER["Z-axis translation", 153.4, LENGTHUNIT["metre", 1]], + OPERATIONACCURACY[5], + AREA["Australia onshore"], + BBOX[-43.7, 112.85, -9.87, 153.68]] + `; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + // stringifying array ignores keys added on + const str = JSON.stringify(data); + t.deepEqual( + str, + '[["COORDINATEOPERATION","AGD84 to GDA94 Auslig 5m",["SOURCECRS","…full CRS definition required here but omitted for brevity…"],["TARGETCRS","…full CRS definition required here but omitted for brevity…"],["METHOD","Geocentric translations",["ID","EPSG","raw:1031"]],["PARAMETER","X-axis translation","raw:-128.5",["LENGTHUNIT","metre","raw:1"]],["PARAMETER","Y-axis translation","raw:-53.0",["LENGTHUNIT","metre","raw:1"]],["PARAMETER","Z-axis translation","raw:153.4",["LENGTHUNIT","metre","raw:1"]],["OPERATIONACCURACY","raw:5"],["AREA","Australia onshore"],["BBOX","raw:-43.7","raw:112.85","raw:-9.87","raw:153.68"]]]' + ); + t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test('parseWKTCRS#proj4js example', (t) => { + const wkt = + 'PROJCS["NAD83 / Massachusetts Mainland",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",42.68333333333333],PARAMETER["standard_parallel_2",41.71666666666667],PARAMETER["latitude_of_origin",41],PARAMETER["central_meridian",-71.5],PARAMETER["false_easting",200000],PARAMETER["false_northing",750000],AUTHORITY["EPSG","26986"],AXIS["X",EAST],AXIS["Y",NORTH]]'; + const data = parseSync(wkt, WKTCRSLoader); + t.deepEqual(data.PROJCS[1], 'NAD83 / Massachusetts Mainland'); + t.end(); +}); + +test('parseWKTCRS#parse attribute that ends in number (TOWGS84)', (t) => { + const wkt = + ' GEOGCS["SAD69",DATUM["South_American_Datum_1969",SPHEROID["GRS 1967 Modified",6378160,298.25,AUTHORITY["EPSG","7050"]],TOWGS84[-57,1,-41,0,0,0,0],AUTHORITY["EPSG","6618"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4618"]]'; + t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test.skip('parseWKTCRS#another parse bug', (t) => { + const wkt = + 'PROJCS["ETRS89 / TM35FIN(E,N)",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",27],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3067"]]'; + const data = parseSync(wkt, WKTCRSLoader, {debug: false}); + t.deepEqual(data.PROJCS[1], 'ETRS89 / TM35FIN(E,N)'); + t.deepEqual(data.PROJCS.MULTIPLE_AXIS[1][2], 'NORTH'); + t.deepEqual(roundtrip(wkt), wkt); + t.end(); +}); + +// Not clear where to find crs.json +// test.skip('parseWKTCRS#try to parse everything in crs.json', (t) => { +// let data = require('./crs.json'); +// data = data.map(({wkt, esriwkt, prettywkt}) => ({ +// raw: { +// wkt: parseSync(wkt, WKTCRSLoader, {raw: true}), +// esriwkt: parseSync(esriwkt, WKTCRSLoader, {raw: true}), +// prettywkt: parseSync(prettywkt, WKTCRSLoader, {raw: true}) +// }, +// dynamic: { +// wkt: parseSync(wkt, WKTCRSLoader, {raw: false}), +// esriwkt: parseSync(esriwkt, WKTCRSLoader, {raw: false}), +// prettywkt: parseSync(prettywkt, WKTCRSLoader, {raw: false}) +// } +// })); + +// // prettywkt and wkt should be equivalent +// // only difference was white space +// data.every(({raw, dynamic}) => { +// t.deepEqual(raw.wkt, raw.prettywkt); +// t.deepEqual(dynamic.wkt, dynamic.prettywkt); +// }); +// }); + +// test("7.5.6.3 Axis unit for ordinal coordinate systems", t => { +// const wkt = `NULL[CS[ordinal,2], +// AXIS["inline (I)",southeast,ORDER[1]], +// AXIS["crossline (J)",northeast,ORDER[2]]]`; +// const data = parseSync(wkt, WKTCRSLoader, { debug: true }); +// t.deepEqual(data.CS[0], "ordinal"); +// t.end(); +// }); + +test.skip('parseWKTCRS#sort parameters', (t) => { + const wkt = + 'PROJCS["WGS_1984_Antarctic_Polar_Stereographic",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Stereographic_South_Pole"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",0.0],PARAMETER["Standard_Parallel_1",-71.0],UNIT["Meter",1.0]]'; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true, sort: true}); + t.deepEqual( + encodeTextSync(data, WKTCRSWriter), + 'PROJCS["WGS_1984_Antarctic_Polar_Stereographic",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Stereographic_South_Pole"],PARAMETER["Central_Meridian",0.0],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Standard_Parallel_1",-71.0],UNIT["Meter",1.0]]' + ); + t.end(); +}); + +// test("sort example", t => { +// const data = ["EXAMPLE", ["AXIS", "Northing", "raw:NORTH"], ["AXIS", "Easting", "raw:EAST"]]; +// wktcrs.sort(data); +// t.deepEqual(data, ["EXAMPLE", ["AXIS", "Easting", "raw:EAST"], ["AXIS", "Northing", "raw:NORTH"]]); +// t.end(); +// }); + +test.skip('parseWKTCRS#sort params', (t) => { + const wkt = + 'PARAMETERS[PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-87],PARAMETER["scale_factor",0.9996]]'; + let data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data[0].MULTIPLE_PARAMETER, [ + ['PARAMETER', 'latitude_of_origin', 'raw:0'], + ['PARAMETER', 'central_meridian', 'raw:-87'], + ['PARAMETER', 'scale_factor', 'raw:0.9996'] + ]); + data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true, sort: true}); + t.deepEqual(data[0].MULTIPLE_PARAMETER, [ + ['PARAMETER', 'central_meridian', 'raw:-87'], + ['PARAMETER', 'latitude_of_origin', 'raw:0'], + ['PARAMETER', 'scale_factor', 'raw:0.9996'] + ]); + t.deepEqual( + encodeTextSync(data, WKTCRSWriter), + 'PARAMETERS[PARAMETER["central_meridian",-87],PARAMETER["latitude_of_origin",0],PARAMETER["scale_factor",0.9996]]' + ); + t.end(); +}); + +test('parseWKTCRS#parse inner parens', (t) => { + const wkt = + 'GEOGCS["GRS 1980(IUGG, 1980)",DATUM["unknown",SPHEROID["GRS80",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["epsg","7686"]]'; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data.GEOGCS[0], 'GEOGCS'); + t.end(); +}); + +test.skip('WKTCRSWriter#authority', (t) => { + const authority = ['AUTHORITY', 'EPSG', '9122']; + const unparsed = encodeTextSync(authority, WKTCRSWriter); + t.deepEqual(unparsed, {data: 'AUTHORITY["EPSG","9122"]'}); + t.end(); +}); + +test.skip('WKTCRSWriter#PRIMEM', (t) => { + const authority = ['PRIMEM', 'Greenwich', 0, ['AUTHORITY', 'EPSG', '8901']]; + const unparsed = encodeTextSync(authority, WKTCRSWriter); + t.deepEqual(unparsed, {data: 'PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]]'}); + t.end(); +}); + +test.skip('WKTCRSWriter#DATUM', (t) => { + const datum = [ + 'DATUM', + 'North_American_Datum_1927', + ['SPHEROID', 'Clarke 1866', 6378206.4, 294.9786982139006, ['AUTHORITY', 'EPSG', '7008']], + ['AUTHORITY', 'EPSG', '6267'] + ]; + const unparsed = encodeTextSync(datum, WKTCRSWriter); + t.deepEqual(unparsed, { + data: 'DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]]' + }); + t.end(); +}); + +test.skip('WKTCRSWriter#GEOGCS', (t) => { + const data = [ + 'GEOGCS', + 'NAD27', + [ + 'DATUM', + 'North_American_Datum_1927', + ['SPHEROID', 'Clarke 1866', 6378206.4, 294.9786982139006, ['AUTHORITY', 'EPSG', '7008']], + ['AUTHORITY', 'EPSG', '6267'] + ], + ['PRIMEM', 'Greenwich', 0, ['AUTHORITY', 'EPSG', '8901']], + ['UNIT', 'degree', 0.0174532925199433, ['AUTHORITY', 'EPSG', '9122']], + ['AUTHORITY', 'EPSG', '4267'] + ]; + const unparsed = encodeTextSync(data, WKTCRSWriter); + t.deepEqual(unparsed, { + data: 'GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]]' + }); +}); diff --git a/modules/gis/test/wkt/parse-wkt.spec.ts b/modules/gis/test/wkt/parse-wkt.spec.ts new file mode 100644 index 0000000000..af70c9736a --- /dev/null +++ b/modules/gis/test/wkt/parse-wkt.spec.ts @@ -0,0 +1,511 @@ +// loaders.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +// Fork of https://github.com/mapbox/wellknown under ISC license (MIT/BSD-2-clause equivalent) + +import test from 'tape-promise/tape'; +import {WKTLoader} from '@loaders.gl/wkt'; +import {setLoaderOptions, fetchFile, parseSync} from '@loaders.gl/core'; + +import fuzzer from 'fuzzer'; + +const GEOMETRYCOLLECTION_WKT_URL = '@loaders.gl/gis/test/data/wkt/geometrycollection.wkt'; +const GEOMETRYCOLLECTION_GEOJSON_URL = '@loaders.gl/gis/test/data/wkt/geometrycollection.geojson'; + +setLoaderOptions({ + _workerType: 'test' +}); + +// eslint-disable-next-line max-statements +test('parseWKT', async (t) => { + let response = await fetchFile(GEOMETRYCOLLECTION_WKT_URL); + const GEOMETRYCOLLECTION_WKT = await response.text(); + + response = await fetchFile(GEOMETRYCOLLECTION_GEOJSON_URL); + const GEOMETRYCOLLECTION_GEOJSON = await response.json(); + + t.deepEqual(parseSync('POINT (0 1)', WKTLoader), { + type: 'Point', + coordinates: [0, 1] + }); + t.deepEqual(parseSync('POINT (1 1)', WKTLoader), { + type: 'Point', + coordinates: [1, 1] + }); + t.deepEqual(parseSync('POINT(1 1)', WKTLoader), { + type: 'Point', + coordinates: [1, 1] + }); + t.deepEqual(parseSync('POINT\n\r(1 1)', WKTLoader), { + type: 'Point', + coordinates: [1, 1] + }); + t.deepEqual(parseSync('POINT(1.1 1.1)', WKTLoader), { + type: 'Point', + coordinates: [1.1, 1.1] + }); + t.deepEqual(parseSync('point(1.1 1.1)', WKTLoader), { + type: 'Point', + coordinates: [1.1, 1.1] + }); + t.deepEqual(parseSync('point(1 2 3)', WKTLoader), { + type: 'Point', + coordinates: [1, 2, 3] + }); + t.deepEqual(parseSync('point(1 2 3 4)', WKTLoader), { + type: 'Point', + coordinates: [1, 2, 3, 4] + }); + t.deepEqual(parseSync('SRID=3857;POINT (1 2 3)', WKTLoader), { + type: 'Point', + coordinates: [1, 2, 3], + crs: { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::3857' + } + } + }); + t.deepEqual(parseSync('LINESTRING (30 10, 10 30, 40 40)', WKTLoader), { + type: 'LineString', + coordinates: [ + [30, 10], + [10, 30], + [40, 40] + ] + }); + t.deepEqual(parseSync('LINESTRING(30 10, 10 30, 40 40)', WKTLoader), { + type: 'LineString', + coordinates: [ + [30, 10], + [10, 30], + [40, 40] + ] + }); + t.deepEqual(parseSync('LineString(30 10, 10 30, 40 40)', WKTLoader), { + type: 'LineString', + coordinates: [ + [30, 10], + [10, 30], + [40, 40] + ] + }); + t.deepEqual(parseSync('LINESTRING (1 2 3, 4 5 6)', WKTLoader), { + type: 'LineString', + coordinates: [ + [1, 2, 3], + [4, 5, 6] + ] + }); + t.deepEqual(parseSync('LINESTRING (1 2 3 4, 5 6 7 8)', WKTLoader), { + type: 'LineString', + coordinates: [ + [1, 2, 3, 4], + [5, 6, 7, 8] + ] + }); + t.deepEqual(parseSync('SRID=3857;LINESTRING (30 10, 10 30, 40 40)', WKTLoader), { + type: 'LineString', + coordinates: [ + [30, 10], + [10, 30], + [40, 40] + ], + crs: { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::3857' + } + } + }); + t.deepEqual(parseSync('POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))', WKTLoader), { + type: 'Polygon', + coordinates: [ + [ + [30, 10], + [10, 20], + [20, 40], + [40, 40], + [30, 10] + ] + ] + }); + t.deepEqual(parseSync('POLYGON((30 10, 10 20, 20 40, 40 40, 30 10))', WKTLoader), { + type: 'Polygon', + coordinates: [ + [ + [30, 10], + [10, 20], + [20, 40], + [40, 40], + [30, 10] + ] + ] + }); + t.deepEqual(parseSync('SRID=3857;POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))', WKTLoader), { + type: 'Polygon', + coordinates: [ + [ + [30, 10], + [10, 20], + [20, 40], + [40, 40], + [30, 10] + ] + ], + crs: { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::3857' + } + } + }); + t.deepEqual( + parseSync( + 'POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10),(20 30, 35 35, 30 20, 20 30))', + WKTLoader + ), + { + type: 'Polygon', + coordinates: [ + [ + [35, 10], + [10, 20], + [15, 40], + [45, 45], + [35, 10] + ], + [ + [20, 30], + [35, 35], + [30, 20], + [20, 30] + ] + ] + } + ); + t.deepEqual(parseSync('MULTIPOINT (0 0, 2 3)', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [0, 0], + [2, 3] + ] + }); + t.deepEqual(parseSync('MULTIPOINT (1 1, 2 3)', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [1, 1], + [2, 3] + ] + }); + t.deepEqual(parseSync('MultiPoint (1 1, 2 3)', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [1, 1], + [2, 3] + ] + }); + t.deepEqual(parseSync('SRID=3857;MULTIPOINT (1 1, 2 3)', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [1, 1], + [2, 3] + ], + crs: { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::3857' + } + } + }); + t.deepEqual(parseSync('MULTIPOINT ((0 0), (2 3))', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [0, 0], + [2, 3] + ] + }); + t.deepEqual(parseSync('MULTIPOINT ((1 1), (2 3))', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [1, 1], + [2, 3] + ] + }); + t.deepEqual(parseSync('MultiPoint ((1 1), (2 3))', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [1, 1], + [2, 3] + ] + }); + t.deepEqual(parseSync('SRID=3857;MULTIPOINT ((1 1), (2 3))', WKTLoader), { + type: 'MultiPoint', + coordinates: [ + [1, 1], + [2, 3] + ], + crs: { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::3857' + } + } + }); + t.deepEqual( + parseSync('MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 10 30, 40 40))', WKTLoader), + { + type: 'MultiLineString', + coordinates: [ + [ + [30, 10], + [10, 30], + [40, 40] + ], + [ + [30, 10], + [10, 30], + [40, 40] + ] + ] + } + ); + t.deepEqual( + parseSync( + 'SRID=3857;MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 10 30, 40 40))', + WKTLoader + ), + { + type: 'MultiLineString', + coordinates: [ + [ + [30, 10], + [10, 30], + [40, 40] + ], + [ + [30, 10], + [10, 30], + [40, 40] + ] + ], + crs: { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::3857' + } + } + } + ); + t.deepEqual( + parseSync( + 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))', + WKTLoader + ), + { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [30, 20], + [10, 40], + [45, 40], + [30, 20] + ] + ], + [ + [ + [15, 5], + [40, 10], + [10, 20], + [5, 10], + [15, 5] + ] + ] + ] + } + ); + t.deepEqual(parseSync('MULTIPOLYGON (((-74.03349399999999 40.688348)))', WKTLoader), { + type: 'MultiPolygon', + coordinates: [[[[-74.03349399999999, 40.688348]]]] + }); + t.deepEqual( + parseSync( + '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)))', + WKTLoader + ), + { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [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] + ] + ] + ] + } + ); + t.deepEqual( + parseSync( + 'SRID=3857;MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))', + WKTLoader + ), + { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [30, 20], + [10, 40], + [45, 40], + [30, 20] + ] + ], + [ + [ + [15, 5], + [40, 10], + [10, 20], + [5, 10], + [15, 5] + ] + ] + ], + crs: { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::3857' + } + } + } + ); + t.deepEqual(parseSync(GEOMETRYCOLLECTION_WKT, WKTLoader), GEOMETRYCOLLECTION_GEOJSON); + t.deepEqual(parseSync('GeometryCollection(POINT(4 6),LINESTRING(4 6,7 10))', WKTLoader), { + type: 'GeometryCollection', + geometries: [ + { + type: 'Point', + coordinates: [4, 6] + }, + { + type: 'LineString', + coordinates: [ + [4, 6], + [7, 10] + ] + } + ] + }); + t.deepEqual(parseSync('GeometryCollection(POINT(4 6),\nLINESTRING(4 6,7 10))', WKTLoader), { + type: 'GeometryCollection', + geometries: [ + { + type: 'Point', + coordinates: [4, 6] + }, + { + type: 'LineString', + coordinates: [ + [4, 6], + [7, 10] + ] + } + ] + }); + t.deepEqual(parseSync('POINT (1e-6 1E+2)', WKTLoader), { + type: 'Point', + coordinates: [1e-6, 1e2] + }); + t.equal(parseSync('POINT(100)', WKTLoader), null); + t.equal(parseSync('POINT(100, 100)', WKTLoader), null); + t.equal(parseSync('POINT()', WKTLoader), null); + t.equal(parseSync('MULTIPOINT()', WKTLoader), null); + t.equal(parseSync('MULTIPOINT(1)', WKTLoader), null); + t.equal(parseSync('MULTIPOINT(1 1, 1)', WKTLoader), null); + + t.deepEqual(parseSync('POINT Z (1 2 3)', WKTLoader), { + type: 'Point', + coordinates: [1, 2, 3] + }); + + t.deepEqual(parseSync('LINESTRING Z (30 10 1, 10 30 2, 40 40 3)', WKTLoader), { + type: 'LineString', + coordinates: [ + [30, 10, 1], + [10, 30, 2], + [40, 40, 3] + ] + }); + + t.deepEqual(parseSync('POLYGON Z ((30 10 1, 10 20 2, 20 40 3, 40 40 4, 30 10 5))', WKTLoader), { + type: 'Polygon', + coordinates: [ + [ + [30, 10, 1], + [10, 20, 2], + [20, 40, 3], + [40, 40, 4], + [30, 10, 5] + ] + ] + }); + + t.end(); +}); + +// NOTE(Kyle): Test disabled for now, to be fixed before 2.2.0 release +// test('WKTWorkerLoader', async t => { +// if (typeof Worker === 'undefined') { +// t.comment('Worker is not usable in non-browser environments'); +// t.end(); +// return; +// } + +// const GEOMETRYCOLLECTION_WKT = await load(GEOMETRYCOLLECTION_WKT_URL, WKTWorkerLoader); + +// const response = await fetchFile(GEOMETRYCOLLECTION_GEOJSON_URL); +// const GEOMETRYCOLLECTION_GEOJSON = await response.json(); + +// t.deepEqual(parseSync(GEOMETRYCOLLECTION_WKT, WKTLoader), GEOMETRYCOLLECTION_GEOJSON); +// t.end(); +// }); + +test('parseWKT#fuzz', (t) => { + fuzzer.seed(0); + const inputs = [ + 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))', + 'POINT(1.1 1.1)', + 'LINESTRING (30 10, 10 30, 40 40)', + 'GeometryCollection(POINT(4 6),\nLINESTRING(4 6,7 10))' + ]; + inputs.forEach(function (str) { + for (let i = 0; i < 10000; i++) { + const input = fuzzer.mutate.string(str); + try { + parseSync(input, WKTLoader); + } catch (e) { + t.fail(`could not parse ${input}, exception ${e}`); + } + } + }); + t.end(); +}); diff --git a/modules/parquet/src/lib/parsers/parse-geoparquet.ts b/modules/parquet/src/lib/parsers/parse-geoparquet.ts index da65e2688e..5d8d1402a7 100644 --- a/modules/parquet/src/lib/parsers/parse-geoparquet.ts +++ b/modules/parquet/src/lib/parsers/parse-geoparquet.ts @@ -10,7 +10,6 @@ import type { ObjectRowTableBatch } from '@loaders.gl/schema'; import {convertWKBTableToGeoJSON} from '@loaders.gl/gis'; -import {WKTLoader, WKBLoader} from '@loaders.gl/wkt'; import type {ParquetLoaderOptions} from '../../parquet-loader'; @@ -47,10 +46,7 @@ function convertTable( case 'geojson-table': try { - return convertWKBTableToGeoJSON(objectRowTable, objectRowTable.schema!, [ - WKTLoader, - WKBLoader - ]); + return convertWKBTableToGeoJSON(objectRowTable, objectRowTable.schema!); } catch (error) { return objectRowTable; } @@ -70,10 +66,7 @@ function convertBatch( case 'geojson-table': try { - const geojsonTable = convertWKBTableToGeoJSON(objectRowBatch, objectRowBatch.schema!, [ - WKTLoader, - WKBLoader - ]); + const geojsonTable = convertWKBTableToGeoJSON(objectRowBatch, objectRowBatch.schema!); return { ...objectRowBatch, ...geojsonTable diff --git a/modules/wkt/src/hex-wkb-loader.ts b/modules/wkt/src/hex-wkb-loader.ts index d7c1cbdcf6..d05fb73cfb 100644 --- a/modules/wkt/src/hex-wkb-loader.ts +++ b/modules/wkt/src/hex-wkb-loader.ts @@ -3,12 +3,12 @@ // Copyright (c) vis.gl contributors import type {LoaderWithParser} from '@loaders.gl/loader-utils'; -import type {BinaryGeometry} from '@loaders.gl/schema'; +import type {Geometry, BinaryGeometry} from '@loaders.gl/schema'; +import {parseWKB, decodeHex} from '@loaders.gl/gis'; import type {WKBLoaderOptions} from './wkb-loader'; import {WKBLoader} from './wkb-loader'; -import {VERSION} from './lib/utils/version'; -import {decodeHex} from './lib/utils/hex-transcoder'; +import {VERSION} from './lib/version'; export type HexWKBLoaderOptions = WKBLoaderOptions; @@ -16,7 +16,7 @@ export type HexWKBLoaderOptions = WKBLoaderOptions; * Worker loader for Hex-encoded WKB (Well-Known Binary) */ export const HexWKBLoader = { - dataType: null as unknown as BinaryGeometry, + dataType: null as unknown as Geometry | BinaryGeometry, batchType: null as never, name: 'Hexadecimal WKB', id: 'wkb', @@ -32,12 +32,11 @@ export const HexWKBLoader = { // TODO - encoding here seems wasteful - extend hex transcoder? parse: async (arrayBuffer: ArrayBuffer) => parseHexWKB(new TextDecoder().decode(arrayBuffer)), parseTextSync: parseHexWKB -} as const satisfies LoaderWithParser; +} as const satisfies LoaderWithParser; -function parseHexWKB(text: string, options?: HexWKBLoaderOptions): BinaryGeometry { +function parseHexWKB(text: string, options?: HexWKBLoaderOptions): Geometry | BinaryGeometry { const uint8Array = decodeHex(text); - const binaryGeometry = WKBLoader.parseSync?.(uint8Array.buffer, options); - // @ts-expect-error + const binaryGeometry = parseWKB(uint8Array.buffer, options?.wkb); return binaryGeometry; } @@ -47,6 +46,7 @@ function parseHexWKB(text: string, options?: HexWKBLoaderOptions): BinaryGeometr * * @param str input string * @returns true if string is a valid WKB in HEX format + * @todo Avoid costly regex check */ export function isHexWKB(string: string | null): boolean { if (!string) { diff --git a/modules/wkt/src/index.ts b/modules/wkt/src/index.ts index 19d719f42f..7327f89d08 100644 --- a/modules/wkt/src/index.ts +++ b/modules/wkt/src/index.ts @@ -15,13 +15,3 @@ export {HexWKBLoader} from './hex-wkb-loader'; export {TWKBLoader} from './twkb-loader'; export {TWKBWriter} from './twkb-writer'; - -// EXPERIMENTAL APIs -export {isWKT} from './lib/parse-wkt'; - -export {isWKB, parseWKBHeader} from './lib/parse-wkb-header'; -export type {WKBHeader} from './lib/parse-wkb-header'; - -export {isTWKB} from './lib/parse-twkb'; - -export {encodeHex, decodeHex} from './lib/utils/hex-transcoder'; diff --git a/modules/wkt/src/lib/utils/version.ts b/modules/wkt/src/lib/version.ts similarity index 100% rename from modules/wkt/src/lib/utils/version.ts rename to modules/wkt/src/lib/version.ts diff --git a/modules/wkt/src/twkb-loader.ts b/modules/wkt/src/twkb-loader.ts index 3b657b6a5f..948d4675a6 100644 --- a/modules/wkt/src/twkb-loader.ts +++ b/modules/wkt/src/twkb-loader.ts @@ -4,8 +4,8 @@ import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import type {BinaryGeometry, Geometry} from '@loaders.gl/schema'; -import {VERSION} from './lib/utils/version'; -import {parseTWKBGeometry, isTWKB} from './lib/parse-twkb'; +import {parseTWKB, isTWKB} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type WKBLoaderOptions = LoaderOptions & { wkb?: { @@ -42,6 +42,6 @@ export const TWKBWorkerLoader = { */ export const TWKBLoader = { ...TWKBWorkerLoader, - parse: async (arrayBuffer: ArrayBuffer) => parseTWKBGeometry(arrayBuffer), - parseSync: parseTWKBGeometry + parse: async (arrayBuffer: ArrayBuffer) => parseTWKB(arrayBuffer), + parseSync: parseTWKB } as const satisfies LoaderWithParser; diff --git a/modules/wkt/src/twkb-writer.ts b/modules/wkt/src/twkb-writer.ts index 2be3606cc1..415d2864be 100644 --- a/modules/wkt/src/twkb-writer.ts +++ b/modules/wkt/src/twkb-writer.ts @@ -4,8 +4,8 @@ import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {Geometry} from '@loaders.gl/schema'; -import {VERSION} from './lib/utils/version'; -import {encodeTWKB} from './lib/encode-twkb'; +import {encodeTWKB} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type TWKBWriterOptions = WriterOptions & { twkb?: { diff --git a/modules/wkt/src/wkb-loader.ts b/modules/wkt/src/wkb-loader.ts index e3f43c9967..cf8b36c5e7 100644 --- a/modules/wkt/src/wkb-loader.ts +++ b/modules/wkt/src/wkb-loader.ts @@ -4,14 +4,13 @@ import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import type {BinaryGeometry, Geometry} from '@loaders.gl/schema'; -import {VERSION} from './lib/utils/version'; -import {parseWKB} from './lib/parse-wkb'; -import {isWKB} from './lib/parse-wkb-header'; +import {parseWKB, isWKB} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type WKBLoaderOptions = LoaderOptions & { wkb?: { /** 'geometry' is deprecated use 'geojson-geometry' */ - shape: 'geojson-geometry' | 'binary-geometry' | 'geometry'; + shape: 'geojson-geometry' | 'binary-geometry'; }; }; @@ -43,6 +42,6 @@ export const WKBWorkerLoader = { */ export const WKBLoader = { ...WKBWorkerLoader, - parse: async (arrayBuffer: ArrayBuffer) => parseWKB(arrayBuffer), - parseSync: parseWKB + parse: async (arrayBuffer: ArrayBuffer, options?) => parseWKB(arrayBuffer, options?.wkb), + parseSync: (arrayBuffer: ArrayBuffer, options?) => parseWKB(arrayBuffer, options?.wkb) } as const satisfies LoaderWithParser; diff --git a/modules/wkt/src/wkb-writer.ts b/modules/wkt/src/wkb-writer.ts index 871ac323b0..c3a7644715 100644 --- a/modules/wkt/src/wkb-writer.ts +++ b/modules/wkt/src/wkb-writer.ts @@ -4,8 +4,8 @@ import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {Geometry, Feature} from '@loaders.gl/schema'; -import {VERSION} from './lib/utils/version'; -import {encodeWKB} from './lib/encode-wkb'; +import {encodeWKB} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type WKBWriterOptions = WriterOptions & { wkb?: { diff --git a/modules/wkt/src/wkt-crs-loader.ts b/modules/wkt/src/wkt-crs-loader.ts index cea54d7eb4..d8a4a61af3 100644 --- a/modules/wkt/src/wkt-crs-loader.ts +++ b/modules/wkt/src/wkt-crs-loader.ts @@ -3,9 +3,9 @@ // Copyright (c) vis.gl contributors import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; -import {VERSION} from './lib/utils/version'; -import type {ParseWKTCRSOptions, WKTCRS} from './lib/parse-wkt-crs'; -import {parseWKTCRS} from './lib/parse-wkt-crs'; +import type {ParseWKTCRSOptions, WKTCRS} from '@loaders.gl/gis'; +import {parseWKTCRS} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type WKTCRSLoaderOptions = LoaderOptions & { 'wkt-crs'?: ParseWKTCRSOptions; diff --git a/modules/wkt/src/wkt-crs-writer.ts b/modules/wkt/src/wkt-crs-writer.ts index e9059032ee..fedb466afb 100644 --- a/modules/wkt/src/wkt-crs-writer.ts +++ b/modules/wkt/src/wkt-crs-writer.ts @@ -3,11 +3,9 @@ // Copyright (c) vis.gl contributors import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; -import {VERSION} from './lib/utils/version'; - -import type {WKTCRS} from './lib/parse-wkt-crs'; -import type {EncodeWKTCRSOptions} from './lib/encode-wkt-crs'; -import {encodeWKTCRS} from './lib/encode-wkt-crs'; +import type {WKTCRS, EncodeWKTCRSOptions} from '@loaders.gl/gis'; +import {encodeWKTCRS} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type WKTCRSWriterOptions = WriterOptions & { 'wkt-crs'?: EncodeWKTCRSOptions; diff --git a/modules/wkt/src/wkt-loader.ts b/modules/wkt/src/wkt-loader.ts index 97ba36a27e..43e128433c 100644 --- a/modules/wkt/src/wkt-loader.ts +++ b/modules/wkt/src/wkt-loader.ts @@ -4,8 +4,8 @@ import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import type {Geometry} from '@loaders.gl/schema'; -import {VERSION} from './lib/utils/version'; -import {parseWKT, isWKT, WKT_MAGIC_STRINGS} from './lib/parse-wkt'; +import {parseWKT, isWKT, WKT_MAGIC_STRINGS} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type WKTLoaderOptions = LoaderOptions & { /** Options for the WKTLoader */ diff --git a/modules/wkt/src/wkt-writer.ts b/modules/wkt/src/wkt-writer.ts index df6d4d8acc..b0e4816cb6 100644 --- a/modules/wkt/src/wkt-writer.ts +++ b/modules/wkt/src/wkt-writer.ts @@ -4,8 +4,8 @@ import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {Geometry} from '@loaders.gl/schema'; -import {VERSION} from './lib/utils/version'; -import {encodeWKT} from './lib/encode-wkt'; +import {encodeWKT} from '@loaders.gl/gis'; +import {VERSION} from './lib/version'; export type WKTWriterOptions = WriterOptions & { wkt?: {}; diff --git a/modules/wkt/test/hex-wkb-loader.spec.ts b/modules/wkt/test/hex-wkb-loader.spec.ts index f85df45663..a37df2d518 100644 --- a/modules/wkt/test/hex-wkb-loader.spec.ts +++ b/modules/wkt/test/hex-wkb-loader.spec.ts @@ -5,10 +5,10 @@ import test from 'tape-promise/tape'; import {fetchFile, parseSync} from '@loaders.gl/core'; import {HexWKBLoader} from '@loaders.gl/wkt'; -import {parseTestCases} from './utils/parse-test-cases'; +import {parseTestCases} from '@loaders.gl/gis/test/data/wkt/parse-test-cases'; -const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; -const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; +const WKB_2D_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdata2d.json'; +const WKB_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ.json'; test('HexWKBLoader#2D', async (t) => { const response = await fetchFile(WKB_2D_TEST_CASES); diff --git a/modules/wkt/test/index.ts b/modules/wkt/test/index.ts index c6ecdef254..bbc92d9233 100644 --- a/modules/wkt/test/index.ts +++ b/modules/wkt/test/index.ts @@ -2,8 +2,6 @@ // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors -import './lib/utils/hex-transcoder.spec'; - import './wkb-loader.spec'; import './wkb-writer.spec'; diff --git a/modules/wkt/test/twkb-loader.spec.ts b/modules/wkt/test/twkb-loader.spec.ts index 1c52853fe7..095d1e4dc5 100644 --- a/modules/wkt/test/twkb-loader.spec.ts +++ b/modules/wkt/test/twkb-loader.spec.ts @@ -4,11 +4,12 @@ import test from 'tape-promise/tape'; import {fetchFile, parseSync} from '@loaders.gl/core'; -import {TWKBLoader, isTWKB} from '@loaders.gl/wkt'; -import {parseTestCases} from './utils/parse-test-cases'; +import {isTWKB} from '@loaders.gl/gis'; +import {TWKBLoader} from '@loaders.gl/wkt'; +import {parseTestCases} from '@loaders.gl/gis/test/data/wkt/parse-test-cases'; -const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; -// const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; +const WKB_2D_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdata2d.json'; +// const WKB_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ.json'; test('TWKBLoader#2D', async (t) => { const response = await fetchFile(WKB_2D_TEST_CASES); diff --git a/modules/wkt/test/twkb-writer.spec.ts b/modules/wkt/test/twkb-writer.spec.ts index ab8632a233..24426a7e04 100644 --- a/modules/wkt/test/twkb-writer.spec.ts +++ b/modules/wkt/test/twkb-writer.spec.ts @@ -6,14 +6,14 @@ import test from 'tape-promise/tape'; import {fetchFile, encodeSync} from '@loaders.gl/core'; import {WKBWriter} from '@loaders.gl/wkt'; -import {parseTestCases} from './utils/parse-test-cases'; +import {parseTestCases} from '@loaders.gl/gis/test/data/wkt/parse-test-cases'; -const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; -const WKB_2D_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d-nan.json'; -const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; -const WKB_Z_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ-nan.json'; +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('WKBWriter#2D', async (t) => { +test('TWKBWriter#2D', async (t) => { const response = await fetchFile(WKB_2D_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -26,7 +26,7 @@ test('WKBWriter#2D', async (t) => { t.end(); }); -test('WKBWriter#2D NaN', async (t) => { +test('TWKBWriter#2D NaN', async (t) => { const response = await fetchFile(WKB_2D_NAN_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -39,7 +39,7 @@ test('WKBWriter#2D NaN', async (t) => { t.end(); }); -test('WKBWriter#Z', async (t) => { +test('TWKBWriter#Z', async (t) => { const response = await fetchFile(WKB_Z_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -52,7 +52,7 @@ test('WKBWriter#Z', async (t) => { t.end(); }); -test('WKBWriter#Z NaN', async (t) => { +test('TWKBWriter#Z NaN', async (t) => { const response = await fetchFile(WKB_Z_NAN_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); diff --git a/modules/wkt/test/wkb-loader.spec.ts b/modules/wkt/test/wkb-loader.spec.ts index 76163a4c3f..0bb7452e0e 100644 --- a/modules/wkt/test/wkb-loader.spec.ts +++ b/modules/wkt/test/wkb-loader.spec.ts @@ -4,11 +4,12 @@ import test from 'tape-promise/tape'; import {fetchFile, parseSync} from '@loaders.gl/core'; -import {WKBLoader, isWKB} from '@loaders.gl/wkt'; -import {parseTestCases} from './utils/parse-test-cases'; +import {isWKB} from '@loaders.gl/gis'; +import {WKBLoader} from '@loaders.gl/wkt'; +import {parseTestCases} from '@loaders.gl/gis/test/data/wkt/parse-test-cases'; -const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; -const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; +const WKB_2D_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdata2d.json'; +const WKB_Z_TEST_CASES = '@loaders.gl/gis/test/data/wkt/wkb-testdataZ.json'; test('WKBLoader#2D', async (t) => { const response = await fetchFile(WKB_2D_TEST_CASES); diff --git a/modules/wkt/test/wkb-writer.spec.ts b/modules/wkt/test/wkb-writer.spec.ts index a219b64d76..5ddb78cb0f 100644 --- a/modules/wkt/test/wkb-writer.spec.ts +++ b/modules/wkt/test/wkb-writer.spec.ts @@ -5,12 +5,12 @@ import test from 'tape-promise/tape'; import {fetchFile, encodeSync} from '@loaders.gl/core'; import {WKBWriter} from '@loaders.gl/wkt'; -import {parseTestCases} from './utils/parse-test-cases'; +import {parseTestCases} from '@loaders.gl/gis/test/data/wkt/parse-test-cases'; -const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; -const WKB_2D_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d-nan.json'; -const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; -const WKB_Z_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ-nan.json'; +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('WKBWriter#2D', async (t) => { const response = await fetchFile(WKB_2D_TEST_CASES); diff --git a/modules/wkt/test/wkt-loader.spec.ts b/modules/wkt/test/wkt-loader.spec.ts index 9c788bec45..402a320ae8 100644 --- a/modules/wkt/test/wkt-loader.spec.ts +++ b/modules/wkt/test/wkt-loader.spec.ts @@ -11,8 +11,8 @@ import {setLoaderOptions, fetchFile, parseSync} from '@loaders.gl/core'; import fuzzer from 'fuzzer'; -const GEOMETRYCOLLECTION_WKT_URL = '@loaders.gl/wkt/test/data/geometrycollection.wkt'; -const GEOMETRYCOLLECTION_GEOJSON_URL = '@loaders.gl/wkt/test/data/geometrycollection.geojson'; +const GEOMETRYCOLLECTION_WKT_URL = '@loaders.gl/gis/test/data/wkt/geometrycollection.wkt'; +const GEOMETRYCOLLECTION_GEOJSON_URL = '@loaders.gl/gis/test/data/wkt/geometrycollection.geojson'; setLoaderOptions({ _workerType: 'test'