From c83c708950131a5d601d44c258e820dc48b82c82 Mon Sep 17 00:00:00 2001 From: Ib Green Date: Fri, 19 Apr 2024 20:29:54 -0400 Subject: [PATCH] feat(mvt): TableTileSource refactor, improved typing chore: tile recator --- modules/mvt/src/index.ts | 2 +- .../geojsonvt/{clip.ts => clip-features.ts} | 16 +-- .../{convert.ts => convert-feature.ts} | 39 +++--- modules/mvt/src/lib/geojsonvt/feature.ts | 47 ------- .../mvt/src/lib/geojsonvt/proto-feature.ts | 64 +++++++++ .../lib/geojsonvt/{tile.ts => proto-tile.ts} | 125 +++++++++++------- .../mvt/src/lib/geojsonvt/transform-tile.ts | 57 ++++++++ modules/mvt/src/lib/geojsonvt/transform.ts | 57 -------- .../geojsonvt/{wrap.ts => wrap-features.ts} | 22 +-- modules/mvt/src/lib/parse-tilejson.ts | 2 +- modules/mvt/src/table-tile-source.ts | 60 +++++---- modules/mvt/test/lib/geojsonvt/clip.spec.ts | 18 +-- 12 files changed, 278 insertions(+), 231 deletions(-) rename modules/mvt/src/lib/geojsonvt/{clip.ts => clip-features.ts} (94%) rename modules/mvt/src/lib/geojsonvt/{convert.ts => convert-feature.ts} (89%) delete mode 100644 modules/mvt/src/lib/geojsonvt/feature.ts create mode 100644 modules/mvt/src/lib/geojsonvt/proto-feature.ts rename modules/mvt/src/lib/geojsonvt/{tile.ts => proto-tile.ts} (53%) create mode 100644 modules/mvt/src/lib/geojsonvt/transform-tile.ts delete mode 100644 modules/mvt/src/lib/geojsonvt/transform.ts rename modules/mvt/src/lib/geojsonvt/{wrap.ts => wrap-features.ts} (74%) diff --git a/modules/mvt/src/index.ts b/modules/mvt/src/index.ts index 1328de1799..50ea86d134 100644 --- a/modules/mvt/src/index.ts +++ b/modules/mvt/src/index.ts @@ -11,7 +11,7 @@ export {TileJSONLoader} from './tilejson-loader'; export {MVTSource} from './mvt-source'; -// TableTileSource +// ProtoTileSource export type {TableTileSourceProps} from './table-tile-source'; export {TableTileSource} from './table-tile-source'; diff --git a/modules/mvt/src/lib/geojsonvt/clip.ts b/modules/mvt/src/lib/geojsonvt/clip-features.ts similarity index 94% rename from modules/mvt/src/lib/geojsonvt/clip.ts rename to modules/mvt/src/lib/geojsonvt/clip-features.ts index 6b0ae2dbf8..8b35a06f29 100644 --- a/modules/mvt/src/lib/geojsonvt/clip.ts +++ b/modules/mvt/src/lib/geojsonvt/clip-features.ts @@ -3,8 +3,8 @@ // Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license -import type {TableTileFeature} from './tile'; -import {createFeature} from './feature'; +import type {ProtoFeature} from './proto-tile'; +import {createProtoFeature} from './proto-feature'; /* eslint-disable no-continue */ @@ -20,8 +20,8 @@ import {createFeature} from './feature'; * @param minAll and maxAll: minimum and maximum coordinate value for all features */ // eslint-disable-next-line max-params, complexity, max-statements -export function clip( - features: TableTileFeature[], +export function clipFeatures( + features: ProtoFeature[], scale: number, k1: number, k2: number, @@ -29,7 +29,7 @@ export function clip( minAll: number, maxAll: number, options: {lineMetrics: boolean} -): TableTileFeature[] | null { +): ProtoFeature[] | null { k1 /= scale; k2 /= scale; @@ -41,7 +41,7 @@ export function clip( return null; // trivial reject } - const clipped: TableTileFeature[] = []; + const clipped: ProtoFeature[] = []; for (const feature of features) { const geometry = feature.geometry; @@ -82,7 +82,7 @@ export function clip( if (newGeometry.length) { if (options.lineMetrics && type === 'LineString') { for (const line of newGeometry) { - clipped.push(createFeature(feature.id, type, line, feature.tags)); + clipped.push(createProtoFeature(feature.id, type, line, feature.tags)); } continue; } @@ -100,7 +100,7 @@ export function clip( type = newGeometry.length === 3 ? 'Point' : 'MultiPoint'; } - clipped.push(createFeature(feature.id, type, newGeometry, feature.tags)); + clipped.push(createProtoFeature(feature.id, type, newGeometry, feature.tags)); } } diff --git a/modules/mvt/src/lib/geojsonvt/convert.ts b/modules/mvt/src/lib/geojsonvt/convert-feature.ts similarity index 89% rename from modules/mvt/src/lib/geojsonvt/convert.ts rename to modules/mvt/src/lib/geojsonvt/convert-feature.ts index 45f33f9af9..7371065b87 100644 --- a/modules/mvt/src/lib/geojsonvt/convert.ts +++ b/modules/mvt/src/lib/geojsonvt/convert-feature.ts @@ -7,16 +7,29 @@ // @ts-nocheck import type {Feature, FeatureCollection} from '@loaders.gl/schema'; -import type {TableTileFeature} from './tile'; - +import type {ProtoFeature} from './proto-tile'; +import {createProtoFeature} from './proto-feature'; import {simplify} from './simplify'; -import {createFeature} from './feature'; + +export type ConvertFeatureOptions = { + /** max zoom to preserve detail on */ + maxZoom?: number; + /** simplification tolerance (higher means simpler) */ + tolerance?: number; + /** tile extent */ + extent?: number; + /** whether to calculate line metrics */ + lineMetrics?: boolean; +}; /** * converts a GeoJSON feature into an intermediate projected JSON vector format * with simplification data */ -export function convert(data: Feature | FeatureCollection, options): TableTileFeature[] { +export function convertFeatureToProtoFeature( + data: Feature | FeatureCollection, + options: ConvertFeatureOptions +): ProtoFeature[] { const features = []; if (data.type === 'FeatureCollection') { for (let i = 0; i < data.features.length; i++) { @@ -32,27 +45,17 @@ export function convert(data: Feature | FeatureCollection, options): TableTileFe return features; } -export type ConvertFeatureOptions = { - /** max zoom to preserve detail on */ - maxZoom?: number; - /** simplification tolerance (higher means simpler) */ - tolerance?: number; - /** tile extent */ - extent?: number; - /** whether to calculate line metrics */ - lineMetrics?: boolean; -}; - /** * converts a GeoJSON feature into an intermediate projected JSON vector format * with simplification data */ function convertFeature( - features: TableTileFeature[], geojson: Feature, + features: ProtoFeature[], options: ConvertFeatureOptions, index: number ): void { + // GeoJSON geometries can be null, but no vector tile will include them. if (!geojson.geometry) { return; } @@ -81,7 +84,7 @@ function convertFeature( for (const line of coords) { geometry = []; convertLine(line, geometry, tolerance, false); - features.push(createFeature(id, 'LineString', geometry, geojson.properties)); + features.push(createProtoFeature(id, 'LineString', geometry, geojson.properties)); } return; } else { @@ -113,7 +116,7 @@ function convertFeature( throw new Error('Input data is not a valid GeoJSON object.'); } - features.push(createFeature(id, type, geometry, geojson.properties)); + features.push(createProtoFeature(id, type, geometry, geojson.properties)); } function convertPoint(coords, out): void { diff --git a/modules/mvt/src/lib/geojsonvt/feature.ts b/modules/mvt/src/lib/geojsonvt/feature.ts deleted file mode 100644 index 7183b40440..0000000000 --- a/modules/mvt/src/lib/geojsonvt/feature.ts +++ /dev/null @@ -1,47 +0,0 @@ -// loaders.gl -// SPDX-License-Identifier: MIT -// Copyright (c) vis.gl contributors -// Forked from https://github.com/mapbox/geojson-vt under compatible ISC license - -import {TableTileFeature} from './tile'; - -export function createFeature(id, type, geom, tags): TableTileFeature { - const feature: TableTileFeature = { - // eslint-disable-next-line - id: id == null ? null : id, - type, - geometry: geom, - tags, - minX: Infinity, - minY: Infinity, - maxX: -Infinity, - maxY: -Infinity - }; - - if (type === 'Point' || type === 'MultiPoint' || type === 'LineString') { - calcLineBBox(feature, geom); - } else if (type === 'Polygon') { - // the outer ring (ie [0]) contains all inner rings - calcLineBBox(feature, geom[0]); - } else if (type === 'MultiLineString') { - for (const line of geom) { - calcLineBBox(feature, line); - } - } else if (type === 'MultiPolygon') { - for (const polygon of geom) { - // the outer ring (ie [0]) contains all inner rings - calcLineBBox(feature, polygon[0]); - } - } - - return feature; -} - -function calcLineBBox(feature, geom) { - for (let i = 0; i < geom.length; i += 3) { - feature.minX = Math.min(feature.minX, geom[i]); - feature.minY = Math.min(feature.minY, geom[i + 1]); - feature.maxX = Math.max(feature.maxX, geom[i]); - feature.maxY = Math.max(feature.maxY, geom[i + 1]); - } -} diff --git a/modules/mvt/src/lib/geojsonvt/proto-feature.ts b/modules/mvt/src/lib/geojsonvt/proto-feature.ts new file mode 100644 index 0000000000..97e44bd1e7 --- /dev/null +++ b/modules/mvt/src/lib/geojsonvt/proto-feature.ts @@ -0,0 +1,64 @@ +// loaders.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors +// Forked from https://github.com/mapbox/geojson-vt under compatible ISC license + +import {ProtoFeature} from './proto-tile'; + +export function createProtoFeature( + id: any, + type: 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon', + geometry: any[], + tags +): ProtoFeature { + const feature: ProtoFeature = { + // eslint-disable-next-line + id: id == null ? null : id, + type, + simplifiedType: undefined!, // TODO + geometry, + tags, + minX: Infinity, + minY: Infinity, + maxX: -Infinity, + maxY: -Infinity + }; + + // TODO break out into separate function + switch (type) { + case 'Point': + case 'MultiPoint': + case 'LineString': + calcLineBBox(feature, geometry); + break; + + case 'MultiLineString': + for (const line of geometry) { + calcLineBBox(feature, line); + } + break; + + case 'Polygon': + // the outer ring (ie [0]) contains all inner rings + calcLineBBox(feature, geometry[0]); + break; + + case 'MultiPolygon': + for (const polygon of geometry) { + // the outer ring (ie [0]) contains all inner rings + calcLineBBox(feature, polygon[0]); + } + break; + } + + return feature; +} + +function calcLineBBox(feature, geometry) { + for (let i = 0; i < geometry.length; i += 3) { + feature.minX = Math.min(feature.minX, geometry[i]); + feature.minY = Math.min(feature.minY, geometry[i + 1]); + feature.maxX = Math.max(feature.maxX, geometry[i]); + feature.maxY = Math.max(feature.maxY, geometry[i + 1]); + } +} diff --git a/modules/mvt/src/lib/geojsonvt/tile.ts b/modules/mvt/src/lib/geojsonvt/proto-tile.ts similarity index 53% rename from modules/mvt/src/lib/geojsonvt/tile.ts rename to modules/mvt/src/lib/geojsonvt/proto-tile.ts index d51e7028c6..9271cfe534 100644 --- a/modules/mvt/src/lib/geojsonvt/tile.ts +++ b/modules/mvt/src/lib/geojsonvt/proto-tile.ts @@ -3,25 +3,27 @@ // Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license -// import type {Feature} from '@loaders.gl/schema'; - -export type TableTileFeature = { - type: any; - geometry: any; +export type ProtoFeature = { + type: 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon'; + simplifiedType: 1 | 2 | 3; + geometry: any[]; // book keeping id?: string; - tags?: string[]; + tags?: Record; - // spatial extents + /** spatial extents */ minX: number; + /** spatial extents */ maxX: number; + /** spatial extents */ minY: number; + /** spatial extents */ maxY: number; }; -export type TableTile = { - features: TableTileFeature[]; // Feature[]; Doesn't seem JSON compatible?? +export type ProtoTile = { + protoFeatures: ProtoFeature[]; // Feature[]; Doesn't seem JSON compatible?? type?: number; tags?: Record; @@ -53,10 +55,10 @@ export type CreateTileOptions = { /** * Create a tile from features and tile index */ -export function createTile(features: any[], z, tx, ty, options: CreateTileOptions): TableTile { +export function createProtoTile(features: any[], z, tx, ty, options: CreateTileOptions): ProtoTile { const tolerance = z === options.maxZoom ? 0 : options.tolerance / ((1 << z) * options.extent); - const tile: TableTile = { - features: [], + const tile: ProtoTile = { + protoFeatures: [], numPoints: 0, numSimplified: 0, numFeatures: features.length, @@ -71,78 +73,101 @@ export function createTile(features: any[], z, tx, ty, options: CreateTileOption maxY: 0 }; for (const feature of features) { - addFeature(tile, feature, tolerance, options); + addProtoFeature(tile, feature, tolerance, options); } return tile; } // eslint-disable-next-line complexity, max-statements -function addFeature(tile: TableTile, feature, tolerance: number, options: CreateTileOptions) { +function addProtoFeature(tile: ProtoTile, feature: ProtoFeature, tolerance: number, options: CreateTileOptions) { const geom = feature.geometry; const type = feature.type; - const simplified: number[] = []; + const simplifiedGeometry: number[] = []; tile.minX = Math.min(tile.minX, feature.minX); tile.minY = Math.min(tile.minY, feature.minY); tile.maxX = Math.max(tile.maxX, feature.maxX); tile.maxY = Math.max(tile.maxY, feature.maxY); - if (type === 'Point' || type === 'MultiPoint') { - for (let i = 0; i < geom.length; i += 3) { - simplified.push(geom[i], geom[i + 1]); - tile.numPoints++; - tile.numSimplified++; - } - } else if (type === 'LineString') { - addLine(simplified, geom, tile, tolerance, false, false); - } else if (type === 'MultiLineString' || type === 'Polygon') { - for (let i = 0; i < geom.length; i++) { - addLine(simplified, geom[i], tile, tolerance, type === 'Polygon', i === 0); - } - } else if (type === 'MultiPolygon') { - for (let k = 0; k < geom.length; k++) { - const polygon = geom[k]; - for (let i = 0; i < polygon.length; i++) { - addLine(simplified, polygon[i], tile, tolerance, true, i === 0); + let simplifiedType: 1 | 2 | 3; + switch (type) { + case 'Point': + case 'MultiPoint': + simplifiedType = 1; + for (let i = 0; i < geom.length; i += 3) { + simplifiedGeometry.push(geom[i], geom[i + 1]); + tile.numPoints++; + tile.numSimplified++; } - } + break; + + case 'LineString': + simplifiedType = 2; + addProtoLine(simplifiedGeometry, geom, tile, tolerance, false, false); + break; + + case 'MultiLineString': + simplifiedType = 2; + for (let i = 0; i < geom.length; i++) { + addProtoLine(simplifiedGeometry, geom[i], tile, tolerance, false, i === 0); + } + break; + + case 'Polygon': + simplifiedType = 3; + for (let i = 0; i < geom.length; i++) { + addProtoLine(simplifiedGeometry, geom[i], tile, tolerance, true, i === 0); + } + break; + + case 'MultiPolygon': + simplifiedType = 3; + for (let k = 0; k < geom.length; k++) { + const polygon = geom[k]; + for (let i = 0; i < polygon.length; i++) { + addProtoLine(simplifiedGeometry, polygon[i], tile, tolerance, true, i === 0); + } + } + break; + + default: + throw new Error(`Unknown geometry type: ${type}`); } - if (simplified.length) { - let tags = feature.tags || null; + if (simplifiedGeometry.length) { + let tags: Record | null = feature.tags || null; if (type === 'LineString' && options.lineMetrics) { tags = {}; - for (const key in feature.tags) tags[key] = feature.tags[key]; + for (const key in feature.tags) { + tags[key] = feature.tags[key]; + } + // @ts-expect-error adding fields to arrays // eslint-disable-next-line camelcase tags.mapbox_clip_start = geom.start / geom.size; + // @ts-expect-error adding fields to arrays // eslint-disable-next-line camelcase tags.mapbox_clip_end = geom.end / geom.size; } - // @ts-expect-error TODO - create sub type? - const tileFeature: TableTileFeature = { - geometry: simplified, - type: - type === 'Polygon' || type === 'MultiPolygon' - ? 3 - : type === 'LineString' || type === 'MultiLineString' - ? 2 - : 1, + const tileFeature: ProtoFeature = { + geometry: simplifiedGeometry, + simplifiedType, + // @ts-expect-error tags }; if (feature.id !== null) { tileFeature.id = feature.id; } - tile.features.push(tileFeature); + tile.protoFeatures.push(tileFeature); } } // eslint-disable-next-line max-params, max-statements -function addLine( - result, - geom, - tile: TableTile, +function addProtoLine( + result: any[], + geom: any, + tile: ProtoTile, tolerance: number, isPolygon: boolean, isOuter: boolean diff --git a/modules/mvt/src/lib/geojsonvt/transform-tile.ts b/modules/mvt/src/lib/geojsonvt/transform-tile.ts new file mode 100644 index 0000000000..63426f72ed --- /dev/null +++ b/modules/mvt/src/lib/geojsonvt/transform-tile.ts @@ -0,0 +1,57 @@ +// loaders.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors +// Forked from https://github.com/mapbox/geojson-vt under compatible ISC license + +import type {ProtoTile} from './proto-tile'; + +/** + * Transforms the coordinates of each protoFeature in the given protoTile from + * mercator-projected space into (extent x extent) protoTile space. + */ +export function transformTile(protoTile: ProtoTile, extent: number): ProtoTile { + if (protoTile.transformed) { + return protoTile; + } + + const z2 = 1 << protoTile.z; + const tx = protoTile.x; + const ty = protoTile.y; + + for (const protoFeature of protoTile.protoFeatures) { + const geom = protoFeature.geometry; + const simplifiedType = protoFeature.simplifiedType; + + protoFeature.geometry = []; + + if (simplifiedType === 1) { + for (let j = 0; j < geom.length; j += 2) { + protoFeature.geometry.push(transformPoint(geom[j], geom[j + 1], extent, z2, tx, ty)); + } + } else { + for (let j = 0; j < geom.length; j++) { + const ring: number[][] = []; + for (let k = 0; k < geom[j].length; k += 2) { + ring.push(transformPoint(geom[j][k], geom[j][k + 1], extent, z2, tx, ty)); + } + protoFeature.geometry.push(ring); + } + } + } + + protoTile.transformed = true; + + return protoTile; +} + +// eslint-disable-next-line max-params +function transformPoint( + x: number, + y: number, + extent: number, + z2: number, + tx: number, + ty: number +): number[] { + return [Math.round(extent * (x * z2 - tx)), Math.round(extent * (y * z2 - ty))]; +} diff --git a/modules/mvt/src/lib/geojsonvt/transform.ts b/modules/mvt/src/lib/geojsonvt/transform.ts deleted file mode 100644 index 4bb64ddc9a..0000000000 --- a/modules/mvt/src/lib/geojsonvt/transform.ts +++ /dev/null @@ -1,57 +0,0 @@ -// loaders.gl -// SPDX-License-Identifier: MIT -// Copyright (c) vis.gl contributors -// Forked from https://github.com/mapbox/geojson-vt under compatible ISC license - -import type {TableTile} from './tile'; - -/** - * Transforms the coordinates of each feature in the given tile from - * mercator-projected space into (extent x extent) tile space. - */ -export function transformTile(tile: TableTile, extent: number): TableTile { - if (tile.transformed) { - return tile; - } - - const z2 = 1 << tile.z; - const tx = tile.x; - const ty = tile.y; - - for (const feature of tile.features) { - const geom = feature.geometry; - const type = feature.type; - - feature.geometry = []; - - if (type === 1) { - for (let j = 0; j < geom.length; j += 2) { - feature.geometry.push(transformPoint(geom[j], geom[j + 1], extent, z2, tx, ty)); - } - } else { - for (let j = 0; j < geom.length; j++) { - const ring: number[][] = []; - for (let k = 0; k < geom[j].length; k += 2) { - ring.push(transformPoint(geom[j][k], geom[j][k + 1], extent, z2, tx, ty)); - } - feature.geometry.push(ring); - } - } - } - - tile.transformed = true; - - return tile; -} - -// eslint-disable-next-line max-params -function transformPoint( - x: number, - y: number, - extent: number, - z2: number, - tx: number, - ty: number -): number[] { - return [Math.round(extent * (x * z2 - tx)), Math.round(extent * (y * z2 - ty))]; -} diff --git a/modules/mvt/src/lib/geojsonvt/wrap.ts b/modules/mvt/src/lib/geojsonvt/wrap-features.ts similarity index 74% rename from modules/mvt/src/lib/geojsonvt/wrap.ts rename to modules/mvt/src/lib/geojsonvt/wrap-features.ts index 797bfdbc23..209970437d 100644 --- a/modules/mvt/src/lib/geojsonvt/wrap.ts +++ b/modules/mvt/src/lib/geojsonvt/wrap-features.ts @@ -3,9 +3,9 @@ // Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license -import type {TableTileFeature} from './tile'; -import {clip} from './clip'; -import {createFeature} from './feature'; +import type {ProtoFeature} from './proto-tile'; +import {createProtoFeature} from './proto-feature'; +import {clipFeatures} from './clip-features'; /** * Options for wrap() @@ -22,14 +22,14 @@ export type WrapOptions = { * @param options buffer and extent * @returns */ -export function wrap(features: TableTileFeature[], options: WrapOptions) { +export function wrapFeatures(features: ProtoFeature[], options: WrapOptions): ProtoFeature[] { const buffer = options.buffer / options.extent; - let merged: TableTileFeature[] = features; - const left = clip(features, 1, -1 - buffer, buffer, 0, -1, 2, options); // left world copy - const right = clip(features, 1, 1 - buffer, 2 + buffer, 0, -1, 2, options); // right world copy + let merged: ProtoFeature[] = features; + const left = clipFeatures(features, 1, -1 - buffer, buffer, 0, -1, 2, options); // left world copy + const right = clipFeatures(features, 1, 1 - buffer, 2 + buffer, 0, -1, 2, options); // right world copy if (left || right) { - merged = clip(features, 1, -buffer, 1 + buffer, 0, -1, 2, options) || []; // center world copy + merged = clipFeatures(features, 1, -buffer, 1 + buffer, 0, -1, 2, options) || []; // center world copy if (left) { merged = shiftFeatureCoords(left, 1).concat(merged); // merge left into center @@ -48,8 +48,8 @@ export function wrap(features: TableTileFeature[], options: WrapOptions) { * @param offset * @returns */ -function shiftFeatureCoords(features: TableTileFeature[], offset: number): TableTileFeature[] { - const newFeatures: TableTileFeature[] = []; +function shiftFeatureCoords(features: ProtoFeature[], offset: number): ProtoFeature[] { + const newFeatures: ProtoFeature[] = []; for (let i = 0; i < features.length; i++) { const feature = features[i]; @@ -76,7 +76,7 @@ function shiftFeatureCoords(features: TableTileFeature[], offset: number): Table } } - newFeatures.push(createFeature(feature.id, type, newGeometry, feature.tags)); + newFeatures.push(createProtoFeature(feature.id, type, newGeometry, feature.tags)); } return newFeatures; diff --git a/modules/mvt/src/lib/parse-tilejson.ts b/modules/mvt/src/lib/parse-tilejson.ts index d128ad772f..7c5e549366 100644 --- a/modules/mvt/src/lib/parse-tilejson.ts +++ b/modules/mvt/src/lib/parse-tilejson.ts @@ -419,7 +419,7 @@ function attributeTypeToFieldType(aType: string): {type: string} { const type = aType.toLowerCase(); if (!type || !attrTypeMap[type]) { // console.warn( - // `cannot convert attribute type ${type} to loaders.gl data type, use string by default` + // `cannot convertFeatureToProtoFeature attribute type ${type} to loaders.gl data type, use string by default` // ); } return attrTypeMap[type] || {type: 'string'}; diff --git a/modules/mvt/src/table-tile-source.ts b/modules/mvt/src/table-tile-source.ts index 07820595ef..b6c5af93a5 100644 --- a/modules/mvt/src/table-tile-source.ts +++ b/modules/mvt/src/table-tile-source.ts @@ -8,12 +8,14 @@ import {VectorTileSource, log} from '@loaders.gl/loader-utils'; import {Schema, deduceTableSchema, Feature, GeoJSONTable} from '@loaders.gl/schema'; import {Stats, Stat} from '@probe.gl/stats'; -import type {TableTile, TableTileFeature} from './lib/geojsonvt/tile'; -import {convert} from './lib/geojsonvt/convert'; // GeoJSON conversion and preprocessing -import {clip} from './lib/geojsonvt/clip'; // stripe clipping algorithm -import {wrap} from './lib/geojsonvt/wrap'; // date line processing -import {transformTile} from './lib/geojsonvt/transform'; // coordinate transformation -import {createTile} from './lib/geojsonvt/tile'; // final simplified tile generation +import type {ProtoTile, ProtoFeature} from './lib/geojsonvt/proto-tile'; +// final simplified tile generation +import {createProtoTile} from './lib/geojsonvt/proto-tile'; +// GeoJSON conversion and preprocessing +import {convertFeatureToProtoFeature} from './lib/geojsonvt/convert-feature'; +import {clipFeatures} from './lib/geojsonvt/clip-features'; // stripe clipping algorithm +import {wrapFeatures} from './lib/geojsonvt/wrap-features'; // date line processing +import {transformTile} from './lib/geojsonvt/transform-tile'; // coordinate transformation import {projectToLngLat} from './lib/utils/geometry-utils'; import {projectToLocalCoordinates} from './lib/utils/geometry-utils'; @@ -74,7 +76,7 @@ export class TableTileSource implements VectorTileSource { debug: 0 // logging level (0, 1 or 2) }; - /** Global stats for all TableTileSources */ + /** Global stats for all ProtoTileSources */ static stats = new Stats({ id: 'table-tile-source-all', stats: [new Stat('count', 'tiles'), new Stat('count', 'features')] @@ -96,7 +98,7 @@ export class TableTileSource implements VectorTileSource { schema: Schema | null = null; /** Map of generated tiles, indexed by stringified tile coordinates */ - tiles: Record = {}; + tiles: Record = {}; /** Array of tile coordinates */ tileCoords: {x: number; y: number; z: number}[] = []; @@ -180,13 +182,13 @@ export class TableTileSource implements VectorTileSource { // projects and adds simplification info log.time(1, 'preprocess table')(); - let features = convert(table, this.props); + let features = convertFeatureToProtoFeature(table, this.props); log.timeEnd(1, 'preprocess table')(); // wraps features (ie extreme west and extreme east) log.time(1, 'generate tiles')(); - features = wrap(features, this.props); + features = wrapFeatures(features, this.props); // start slicing from the top tile down if (features.length === 0) { @@ -208,7 +210,7 @@ export class TableTileSource implements VectorTileSource { * @note Application must await `source.ready` before calling sync methods. */ // eslint-disable-next-line complexity, max-statements - getRawTile(tileIndex: {z: number; x: number; y: number}): TableTile | null { + getRawTile(tileIndex: {z: number; x: number; y: number}): ProtoTile | null { const {z, y} = tileIndex; let {x} = tileIndex; // z = +z; @@ -222,7 +224,7 @@ export class TableTileSource implements VectorTileSource { } const z2 = 1 << z; - x = (x + z2) & (z2 - 1); // wrap tile x coordinate + x = (x + z2) & (z2 - 1); // wrapFeatures tile x coordinate const id = toID(z, x, y); if (this.tiles[id]) { @@ -268,7 +270,7 @@ export class TableTileSource implements VectorTileSource { */ // eslint-disable-next-line max-params, max-statements, complexity splitTile( - features: TableTileFeature[], + features: ProtoFeature[], z: number, x: number, y: number, @@ -292,7 +294,7 @@ export class TableTileSource implements VectorTileSource { if (!tile) { log.time(2, 'tile creation')(); - tile = this.tiles[id] = createTile(features, z, x, y, this.props); + tile = this.tiles[id] = createProtoTile(features, z, x, y, this.props); this.tileCoords.push({z, x, y}); const key = `z${z}`; @@ -324,7 +326,7 @@ export class TableTileSource implements VectorTileSource { // save reference to original geometry in tile so that we can drill down later if we stop now tile.source = features; - /** eslint-disable no-continue */ + /* eslint-disable no-continue */ // if it's the first-pass tiling if (cz === undefined) { @@ -358,26 +360,26 @@ export class TableTileSource implements VectorTileSource { const k3 = 0.5 + k1; const k4 = 1 + k1; - let tl: TableTileFeature[] | null = null; - let bl: TableTileFeature[] | null = null; - let tr: TableTileFeature[] | null = null; - let br: TableTileFeature[] | null = null; + let tl: ProtoFeature[] | null = null; + let bl: ProtoFeature[] | null = null; + let tr: ProtoFeature[] | null = null; + let br: ProtoFeature[] | null = null; - let left = clip(features, z2, x - k1, x + k3, 0, tile.minX, tile.maxX, this.props); - let right = clip(features, z2, x + k2, x + k4, 0, tile.minX, tile.maxX, this.props); + let left = clipFeatures(features, z2, x - k1, x + k3, 0, tile.minX, tile.maxX, this.props); + let right = clipFeatures(features, z2, x + k2, x + k4, 0, tile.minX, tile.maxX, this.props); // @ts-expect-error - unclear why this is needed? features = null; if (left) { - tl = clip(left, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, this.props); - bl = clip(left, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, this.props); + tl = clipFeatures(left, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, this.props); + bl = clipFeatures(left, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, this.props); left = null; } if (right) { - tr = clip(right, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, this.props); - br = clip(right, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, this.props); + tr = clipFeatures(right, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, this.props); + br = clipFeatures(right, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, this.props); right = null; } @@ -397,7 +399,7 @@ function toID(z, x, y): number { // eslint-disable-next-line max-statements, complexity function convertToGeoJSONTable( - vtTile: TableTile, + protoTile: ProtoTile, props: { coordinates: 'wgs84' | 'local'; tileIndex: {x: number; y: number; z: number}; @@ -405,7 +407,7 @@ function convertToGeoJSONTable( } ): GeoJSONTable | null { const features: Feature[] = []; - for (const rawFeature of vtTile.features) { + for (const rawFeature of protoTile.protoFeatures) { if (!rawFeature || !rawFeature.geometry) { continue; } @@ -421,7 +423,7 @@ function convertToGeoJSONTable( let coordinates: any; // raw geometry - switch (rawFeature.type) { + switch (rawFeature.simplifiedType) { case 1: if (rawFeature.geometry.length === 1) { type = 'Point'; @@ -450,7 +452,7 @@ function convertToGeoJSONTable( } break; default: - continue; + throw new Error(rawFeature.simplifiedType +'is not a valid simplified type'); } switch (props.coordinates) { diff --git a/modules/mvt/test/lib/geojsonvt/clip.spec.ts b/modules/mvt/test/lib/geojsonvt/clip.spec.ts index f86ff4f888..bc8547cfe9 100644 --- a/modules/mvt/test/lib/geojsonvt/clip.spec.ts +++ b/modules/mvt/test/lib/geojsonvt/clip.spec.ts @@ -5,7 +5,7 @@ import test from 'tape-promise/tape'; // @ts-ignore-error -import {clip} from '@loaders.gl/mvt/lib/geojsonvt/clip'; +import {clipFeatures} from '@loaders.gl/mvt/lib/geojsonvt/clip-features'; /* eslint comma-spacing:0*/ @@ -15,8 +15,8 @@ const geom1 = [ ]; const geom2 = [0, 0, 0, 50, 0, 0, 50, 10, 0, 0, 10, 0]; -test('GeoJSONVT#clip#clips polylines', (t) => { - const clipped = clip( +test('GeoJSONVT#clipFeatures#clips polylines', (t) => { + const clipped = clipFeatures( [ {geometry: geom1, type: 'LineString', tags: 1, minX: 0, minY: 0, maxX: 50, maxY: 60}, {geometry: geom2, type: 'LineString', tags: 2, minX: 0, minY: 0, maxX: 50, maxY: 10} @@ -66,7 +66,7 @@ test('GeoJSONVT#clip#clips polylines', (t) => { t.end(); }); -test('GeoJSONVT#clip#clips lines with line metrics on', (t) => { +test('GeoJSONVT#clipFeatures#clips lines with line metrics on', (t) => { const geom = geom1.slice(); // @ts-expect-error geom.size = 0; @@ -81,7 +81,7 @@ test('GeoJSONVT#clip#clips lines with line metrics on', (t) => { // @ts-expect-error geom.end = geom.size; - const clipped = clip( + const clipped = clipFeatures( [{geometry: geom, type: 'LineString', minX: 0, minY: 0, maxX: 50, maxY: 60}], 1, 10, @@ -109,8 +109,8 @@ function closed(geometry) { return [geometry.concat(geometry.slice(0, 3))]; } -test('GeoJSONVT#clip#clips polygons', (t) => { - const clipped = clip( +test('GeoJSONVT#clipFeatures#clips polygons', (t) => { + const clipped = clipFeatures( [ {geometry: closed(geom1), type: 'Polygon', tags: 1, minX: 0, minY: 0, maxX: 50, maxY: 60}, {geometry: closed(geom2), type: 'Polygon', tags: 2, minX: 0, minY: 0, maxX: 50, maxY: 10} @@ -157,8 +157,8 @@ test('GeoJSONVT#clip#clips polygons', (t) => { t.end(); }); -test('GeoJSONVT#clip#clips points', (t) => { - const clipped = clip( +test('GeoJSONVT#clipFeatures#clips points', (t) => { + const clipped = clipFeatures( [ {geometry: geom1, type: 'MultiPoint', tags: 1, minX: 0, minY: 0, maxX: 50, maxY: 60}, {geometry: geom2, type: 'MultiPoint', tags: 2, minX: 0, minY: 0, maxX: 50, maxY: 10}