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

chore(twkb): Add TWKBLoader tests #2653

Merged
merged 5 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/parquet/src/lib/geo/decode-geo-column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function parseGeometry(geometry: any, columnMetadata: GeoColumnMetadata): Geomet
case 'wkb':
default:
const binaryGeometry = WKBLoader.parseSync?.(geometry);
// @ts-ignore
return binaryGeometry ? binaryToGeometry(binaryGeometry) : null;
}
}
22 changes: 12 additions & 10 deletions modules/wkt/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
// loaders.gl, MIT license

export {WKTCRSLoader} from './wkt-crs-loader';
export {WKTCRSWriter} from './wkt-crs-writer';

export {WKTLoader, WKTWorkerLoader} from './wkt-loader';
export {WKTWriter} from './wkt-writer';

export {WKBLoader, WKBWorkerLoader} from './wkb-loader';
export {WKBWriter} from './wkb-writer';

export {TWKBLoader} from './twkb-loader';
export {TWKBWriter} from './twkb-writer';

export {HexWKBLoader} from './hex-wkb-loader';

export {WKTLoader, WKTWorkerLoader} from './wkt-loader';
export {WKTWriter} from './wkt-writer';

export {WKTCRSLoader} from './wkt-crs-loader';
export {WKTCRSWriter} from './wkt-crs-writer';
export {TWKBLoader} from './twkb-loader';
export {TWKBWriter} from './twkb-writer';

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

export {isWKB, parseWKBHeader} from './lib/parse-wkb-header';
export type {WKBHeader} from './lib/parse-wkb-header';

export {isWKT} from './lib/parse-wkt';
export {isTWKB} from './lib/parse-twkb';

export {encodeHex, decodeHex} from './lib/utils/hex-transcoder';
28 changes: 9 additions & 19 deletions modules/wkt/src/lib/parse-twkb.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// loaders.gl, MIT license
// Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz

import type {BinaryGeometry, Geometry, GeometryCollection} from '@loaders.gl/schema';
import type {Geometry, GeometryCollection} from '@loaders.gl/schema';
import type {Point, LineString, Polygon} from '@loaders.gl/schema';
import type {MultiPoint, MultiLineString, MultiPolygon} from '@loaders.gl/schema';
import {BinaryReader} from './utils/binary-reader';
Expand All @@ -26,14 +26,6 @@ export function isTWKB(arrayBuffer: ArrayBuffer): boolean {
return true;
}

/**
* Parse a TWKB encoded array buffer
* @param arrayBuffer
*/
export function parseTWKB(arrayBuffer: ArrayBuffer): BinaryGeometry {
throw new Error('not implemented');
}

/** State passed around between parsing functions, extracted from the header */
type ParseTWKBState = {
hasBoundingBox: boolean;
Expand Down Expand Up @@ -158,7 +150,7 @@ function parsePoint(reader: BinaryReader, context: ParseTWKBState): Point {
return {type: 'Point', coordinates: []};
}

return {type: 'Point', coordinates: parsePointCoordinates(reader, context)};
return {type: 'Point', coordinates: readFirstPoint(reader, context)};
}

function parseLineString(reader: BinaryReader, context: ParseTWKBState): LineString {
Expand All @@ -185,14 +177,12 @@ function parsePolygon(reader: BinaryReader, context: ParseTWKBState): Polygon {

const ringCount = reader.readVarInt();

const polygons: number[][][] = [];

const previousPoint = makePreviousPoint(context);

const exteriorRingCount = reader.readVarInt();
const exteriorRingLength = reader.readVarInt();
const exteriorRing: number[][] = [];

for (let i = 0; i < exteriorRingCount; i++) {
for (let i = 0; i < exteriorRingLength; i++) {
exteriorRing.push(parseNextPoint(reader, context, previousPoint));
}

Expand All @@ -208,7 +198,7 @@ function parsePolygon(reader: BinaryReader, context: ParseTWKBState): Polygon {
polygon.push(interiorRing);
}

return {type: 'Polygon', coordinates: polygons};
return {type: 'Polygon', coordinates: polygon};
}

function parseMultiPoint(reader: BinaryReader, context: ParseTWKBState): MultiPoint {
Expand Down Expand Up @@ -269,14 +259,14 @@ function parseMultiPolygon(reader: BinaryReader, context: ParseTWKBState): Multi
exteriorRing.push(parseNextPoint(reader, context, previousPoint));
}

const polygon: number[][][] = [exteriorRing];
const polygon: number[][][] = exteriorRing ? [exteriorRing] : [];

for (let j = 1; j < ringCount; j++) {
const interiorRing: number[][] = [];

const interiorRingCount = reader.readVarInt();
const interiorRingLength = reader.readVarInt();

for (let k = 0; k < interiorRingCount; k++) {
for (let k = 0; k < interiorRingLength; k++) {
interiorRing.push(parseNextPoint(reader, context, previousPoint));
}

Expand Down Expand Up @@ -330,7 +320,7 @@ function makePreviousPoint(context: ParseTWKBState): number[] {
return makePointCoordinates(0, 0, context.hasZ ? 0 : undefined, context.hasM ? 0 : undefined);
}

function parsePointCoordinates(reader: BinaryReader, context: ParseTWKBState): number[] {
function readFirstPoint(reader: BinaryReader, context: ParseTWKBState): number[] {
const x = zigZagDecode(reader.readVarInt()) / context.precisionFactor;
const y = zigZagDecode(reader.readVarInt()) / context.precisionFactor;
const z = context.hasZ ? zigZagDecode(reader.readVarInt()) / context.zPrecisionFactor : undefined;
Expand Down
2 changes: 1 addition & 1 deletion modules/wkt/src/lib/utils/binary-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class BinaryReader {
let nextByte;
do {
// TODO - this needs to be accessed via data view?
nextByte = this.arrayBuffer[this.byteOffset + bytesRead];
nextByte = this.dataView.getUint8(this.byteOffset + bytesRead);
result += (nextByte & 0x7f) << (7 * bytesRead);
bytesRead++;
} while (nextByte >= 0x80);
Expand Down
8 changes: 4 additions & 4 deletions modules/wkt/src/twkb-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
import {BinaryGeometry, Geometry} from '@loaders.gl/schema';
import {VERSION} from './lib/utils/version';
import {parseTWKB, isTWKB} from './lib/parse-twkb';
import {parseTWKBGeometry, isTWKB} from './lib/parse-twkb';

export type WKBLoaderOptions = LoaderOptions & {
wkb?: {
Expand All @@ -14,7 +14,7 @@ export type WKBLoaderOptions = LoaderOptions & {
/**
* Worker loader for WKB (Well-Known Binary)
*/
export const TWKBWorkerLoader: Loader<BinaryGeometry | Geometry, never, WKBLoaderOptions> = {
export const TWKBWorkerLoader: Loader<Geometry, never, WKBLoaderOptions> = {
name: 'TWKB (Tiny Well-Known Binary)',
id: 'twkb',
module: 'wkt',
Expand All @@ -37,6 +37,6 @@ export const TWKBWorkerLoader: Loader<BinaryGeometry | Geometry, never, WKBLoade
*/
export const TWKBLoader: LoaderWithParser<BinaryGeometry | Geometry, never, WKBLoaderOptions> = {
...TWKBWorkerLoader,
parse: async (arrayBuffer: ArrayBuffer) => parseTWKB(arrayBuffer),
parseSync: parseTWKB
parse: async (arrayBuffer: ArrayBuffer) => parseTWKBGeometry(arrayBuffer),
parseSync: parseTWKBGeometry
};
56 changes: 36 additions & 20 deletions modules/wkt/test/twkb-loader.spec.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,56 @@
/**
* import test from 'tape-promise/tape';
import test from 'tape-promise/tape';
import {fetchFile, parseSync} from '@loaders.gl/core';
import {WKBLoader} from '@loaders.gl/wkt';
import {TWKBLoader, isTWKB} from '@loaders.gl/wkt';
import {parseTestCases} from './utils/parse-test-cases';
import {isWKB} from '../src/lib/parse-wkb-header';

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_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json';

test.only('WKBLoader#2D', async (t) => {
test('TWKBLoader#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;
}

// Big endian
if (testCase.twkb && testCase.binary) {
t.ok(isWKB(testCase.twkb), 'isWKB(2D)');
t.deepEqual(parseSync(testCase.twkb, WKBLoader), 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('WKBLoader#Z', async (t) => {
const response = await fetchFile(WKB_Z_TEST_CASES);
const TEST_CASES = parseTestCases(await response.json());
// test('TWKBLoader#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.wkbXdr && testCase.binary && testCase.geoJSON) {
t.deepEqual(parseSync(testCase.wkbXdr, WKBLoader, {wkb: {shape: 'geometry'}}), testCase.geoJSON);
}
}
// // TODO parseWKB outputs TypedArrays; testCase contains regular arrays
// for (const testCase of Object.values(TEST_CASES)) {
// if (testCase.geoJSON.type === 'GeometryCollection') {
// continue;
// }

t.end();
});
*/
// if (testCase.wkbXdr && testCase.binary && testCase.geoJSON) {
// t.deepEqual(parseSync(testCase.twkbXdr, TWKBLoader, {wkb: {shape: 'geometry'}}), testCase.geoJSON);
// }
// }

// t.end();
// });
3 changes: 1 addition & 2 deletions modules/wkt/test/wkb-loader.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import test from 'tape-promise/tape';
import {fetchFile, parseSync} from '@loaders.gl/core';
import {WKBLoader} from '@loaders.gl/wkt';
import {WKBLoader, isWKB} from '@loaders.gl/wkt';
import {parseTestCases} from './utils/parse-test-cases';
import {isWKB} from '../src/lib/parse-wkb-header';

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';
Expand Down