Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### ✨ Features and improvements
- Add Etag unmodified support to optimize vector tile reloading ([#7074](https://github.com/maplibre/maplibre-gl-js/pull/7074)) (by [@rivkamatan](https://github.com/rivkamatan and [@wayofthefuture](https://github.com/wayofthefuture))
- ⚠️ Support geojson nested objects ([#6992](https://github.com/maplibre/maplibre-gl-js/pull/6992)) (by [HarelM](https://github.com/HarelM))

- _...Add new stuff here..._

### 🐞 Bug fixes
Expand Down
20 changes: 13 additions & 7 deletions src/data/feature_index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type Point from '@mapbox/point-geometry';
import {type VectorTileFeatureLike, type VectorTileLayerLike, GEOJSON_TILE_LAYER_NAME} from '@maplibre/vt-pbf';
import {loadGeometry} from './load_geometry';
import {toEvaluationFeature} from './evaluation_feature';
import {EXTENT} from './extent';
Expand All @@ -13,18 +14,18 @@ import {EvaluationParameters} from '../style/evaluation_parameters';
import {polygonIntersectsBox} from '../util/intersection_tests';
import {PossiblyEvaluated} from '../style/properties';
import {FeatureIndexArray} from './array_types.g';

import {MLTVectorTile} from '../source/vector_tile_mlt';
import {Bounds} from '../geo/bounds';
import {VectorTile} from '@mapbox/vector-tile';

import type {OverscaledTileID} from '../tile/tile_id';
import type {SourceFeatureState} from '../source/source_state';
import type {mat4} from 'gl-matrix';
import type {MapGeoJSONFeature} from '../util/vectortile_to_geojson';
import type {StyleLayer} from '../style/style_layer';
import type {FeatureFilter, FeatureState, FilterSpecification, PromoteIdSpecification} from '@maplibre/maplibre-gl-style-spec';
import type {IReadonlyTransform} from '../geo/transform_interface';
import {type VectorTileFeatureLike, type VectorTileLayerLike, GEOJSON_TILE_LAYER_NAME} from '@maplibre/vt-pbf';
import {VectorTile} from '@mapbox/vector-tile';
import type {TileEncoding} from '../source/worker_source';

export {GEOJSON_TILE_LAYER_NAME};

Expand Down Expand Up @@ -67,7 +68,7 @@ export class FeatureIndex {
grid3D: TransferableGridIndex;
featureIndexArray: FeatureIndexArray;
promoteId?: PromoteIdSpecification;
encoding: string;
encoding: TileEncoding;
rawTileData: ArrayBuffer;
bucketLayerIDs: Array<Array<string>>;

Expand Down Expand Up @@ -114,9 +115,14 @@ export class FeatureIndex {

loadVTLayers(): {[_: string]: VectorTileLayerLike} {
if (!this.vtLayers) {
this.vtLayers = this.encoding !== 'mlt'
? new VectorTile(new Protobuf(this.rawTileData)).layers
: new MLTVectorTile(this.rawTileData).layers;
switch (this.encoding) {
case 'mlt':
this.vtLayers = new MLTVectorTile(this.rawTileData).layers;
break;
case 'mvt':
default:
this.vtLayers = new VectorTile(new Protobuf(this.rawTileData)).layers;
}
this.sourceLayerCoder = new DictionaryCoder(this.vtLayers ? Object.keys(this.vtLayers).sort() : [GEOJSON_TILE_LAYER_NAME]);
}
return this.vtLayers;
Expand Down
1 change: 1 addition & 0 deletions src/source/geojson_worker_source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ describe('reloadTile', () => {
let data = await source.reloadTile(tileParams as any as WorkerTileParameters) as WorkerTileWithData;
expect('rawTileData' in data).toBeFalsy();
data.rawTileData = firstData.rawTileData;
data.encoding = 'mvt';
expect(data).toEqual(firstData);

// also shouldn't call loadVectorData again
Expand Down
13 changes: 8 additions & 5 deletions src/source/geojson_worker_source.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import {getJSON} from '../util/ajax';
import {RequestPerformance} from '../util/performance';
import rewind from '@mapbox/geojson-rewind';
import {GeoJSONWrapper} from '@maplibre/vt-pbf';
import {fromVectorTileJs, GeoJSONWrapper} from '@maplibre/vt-pbf';
import {EXTENT} from '../data/extent';
import Supercluster, {type Options as SuperclusterOptions, type ClusterProperties} from 'supercluster';
import geojsonvt, {type GeoJSONVTOptions, type GeoJSONVT} from '@maplibre/geojson-vt';
import {createExpression} from '@maplibre/maplibre-gl-style-spec';
import {isAbortError} from '../util/abort_error';
import {toVirtualVectorTile} from './vector_tile_overzoomed';
import {type GeoJSONSourceDiff, applySourceDiff, toUpdateable, type GeoJSONFeatureId} from './geojson_source_diff';
import {WorkerTile} from './worker_tile';
import {WorkerTileState, type ParsingState} from './worker_tile_state';
import {extend} from '../util/util';
import {extend, JSON_PREFIX} from '../util/util';

import type {WorkerSource, WorkerTileParameters, TileParameters, WorkerTileResult} from './worker_source';
import type {LoadVectorTileResult} from './vector_tile_worker_source';
Expand Down Expand Up @@ -99,7 +98,11 @@ export class GeoJSONWorkerSource implements WorkerSource {
if (!geoJSONTile) return null;

const geojsonWrapper = new GeoJSONWrapper(geoJSONTile.features, {version: 2, extent: EXTENT});
return toVirtualVectorTile(geojsonWrapper);
return {
vectorTile: geojsonWrapper,
rawData: fromVectorTileJs(geojsonWrapper, JSON_PREFIX).buffer
};

}

/**
Expand Down Expand Up @@ -159,7 +162,7 @@ export class GeoJSONWorkerSource implements WorkerSource {
if (parseState) {
const {rawData} = parseState;
// Transferring a copy of rawTileData because the worker needs to retain its copy.
result = extend({rawTileData: rawData.slice(0)}, result);
result = extend({rawTileData: rawData.slice(0), encoding: 'mvt'}, result);
}

return result;
Expand Down
20 changes: 1 addition & 19 deletions src/source/vector_tile_overzoomed.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import Point from '@mapbox/point-geometry';
import {type VectorTileFeatureLike, type VectorTileLayerLike, type VectorTileLike, fromVectorTileJs} from '@maplibre/vt-pbf';
import {clipGeometry} from '../symbol/clip_line';
import type {LoadVectorTileResult} from './vector_tile_worker_source';
import type {CanonicalTileID} from '../tile/tile_id';
import type {VectorTileFeatureLike, VectorTileLayerLike, VectorTileLike} from '@maplibre/vt-pbf';

class VectorTileFeatureOverzoomed implements VectorTileFeatureLike {
pointsArray: Point[][];
Expand Down Expand Up @@ -60,23 +59,6 @@ export class VectorTileOverzoomed implements VectorTileLike {
}
}

/**
* Encodes the virtual tile into binary vector tile form.
* This is a convenience that allows `FeatureIndex` to operate the same way across `VectorTileSource` and `GeoJSONSource` data.
* @param virtualVectorTile - a syntetically created vector tile, this tile should have the relevant layer and features already added to it.
* @returns - the encoded vector tile along with the original virtual tile binary data.
*/
export function toVirtualVectorTile(virtualVectorTile: VectorTileLike): LoadVectorTileResult {
let pbf: Uint8Array = fromVectorTileJs(virtualVectorTile);
if (pbf.byteOffset !== 0 || pbf.byteLength !== pbf.buffer.byteLength) {
pbf = new Uint8Array(pbf); // Compatibility with node Buffer (https://github.com/mapbox/pbf/issues/35)
}
return {
vectorTile: virtualVectorTile,
rawData: pbf.buffer
};
}

/**
* This function slices a source tile layer into an overzoomed tile layer for a target tile ID.
* @param sourceLayer - the source tile layer to slice
Expand Down
4 changes: 2 additions & 2 deletions src/source/vector_tile_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {Map} from '../ui/map';
import type {Dispatcher} from '../util/dispatcher';
import type {Tile} from '../tile/tile';
import type {VectorSourceSpecification, PromoteIdSpecification} from '@maplibre/maplibre-gl-style-spec';
import type {WorkerTileParameters, OverzoomParameters, WorkerTileResult} from './worker_source';
import type {WorkerTileParameters, OverzoomParameters, WorkerTileResult, TileEncoding} from './worker_source';

export type VectorTileSourceOptions = VectorSourceSpecification & {
collectResourceTiming?: boolean;
Expand Down Expand Up @@ -69,7 +69,7 @@ export class VectorTileSource extends Evented implements Source {
maxzoom: number;
url: string;
scheme: string;
encoding: string;
encoding: TileEncoding;
tileSize: number;
promoteId: PromoteIdSpecification;

Expand Down
9 changes: 6 additions & 3 deletions src/source/vector_tile_worker_source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ describe('vector tile worker source', () => {
const loadVectorData = (_params, _rawData) => {
return {
vectorTile: new VectorTile(new Protobuf(rawTileData)),
rawData: rawTileData
rawData: rawTileData,
encoding: 'mvt'
};
};

Expand Down Expand Up @@ -343,7 +344,8 @@ describe('vector tile worker source', () => {
vectorTile: new VectorTile(new Protobuf(rawTileData)),
rawData: rawTileData,
cacheControl: null,
expires: null
expires: null,
encoding: 'mvt'
};
};

Expand Down Expand Up @@ -406,7 +408,8 @@ describe('vector tile worker source', () => {
vectorTile: new VectorTile(new Protobuf(rawTileData)),
rawData: rawTileData,
cacheControl: null,
expires: null
expires: null,
encoding: 'mvt'
};
};

Expand Down
9 changes: 6 additions & 3 deletions src/source/vector_tile_worker_source.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import Protobuf from 'pbf';
import {VectorTile} from '@mapbox/vector-tile';
import {fromVectorTileJs, type VectorTileLayerLike, type VectorTileLike} from '@maplibre/vt-pbf';
import {type ExpiryData, getArrayBuffer} from '../util/ajax';
import {WorkerTile} from './worker_tile';
import {WorkerTileState, type ParsingState} from './worker_tile_state';
import {BoundedLRUCache} from '../tile/tile_cache';
import {extend} from '../util/util';
import {RequestPerformance} from '../util/performance';
import {VectorTileOverzoomed, sliceVectorTileLayer, toVirtualVectorTile} from './vector_tile_overzoomed';
import {VectorTileOverzoomed, sliceVectorTileLayer} from './vector_tile_overzoomed';
import {MLTVectorTile} from './vector_tile_mlt';
import type {
WorkerSource,
Expand All @@ -17,7 +18,6 @@
import type {IActor} from '../util/actor';
import type {StyleLayer} from '../style/style_layer';
import type {StyleLayerIndex} from '../style/style_layer_index';
import type {VectorTileLayerLike, VectorTileLike} from '@maplibre/vt-pbf';

export type LoadVectorTileResult = {
vectorTile: VectorTileLike;
Expand Down Expand Up @@ -193,7 +193,10 @@
overzoomedVectorTile.addLayer(slicedTileLayer);
}
}
const overzoomedVectorTileResult = toVirtualVectorTile(overzoomedVectorTile);
const overzoomedVectorTileResult = {

Check warning on line 196 in src/source/vector_tile_worker_source.ts

View workflow job for this annotation

GitHub Actions / Annotate

src/source/vector_tile_worker_source.ts#L196

This line is not covered by a test
vectorTile: overzoomedVectorTile,
rawData: fromVectorTileJs(overzoomedVectorTile).buffer
};
this.overzoomedTileResultCache.set(cacheKey, overzoomedVectorTileResult);

return overzoomedVectorTileResult;
Expand Down
6 changes: 4 additions & 2 deletions src/source/worker_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import type {StyleLayerIndex} from '../style/style_layer_index';
import type {SubdivisionGranularitySetting} from '../render/subdivision_granularity_settings';
import type {DashEntry} from '../render/line_atlas';

export type TileEncoding = 'mlt' | 'mvt';

/**
* Parameters to identify a tile
*/
Expand All @@ -40,7 +42,7 @@ export type WorkerTileParameters = TileParameters & {
collectResourceTiming?: boolean;
returnDependencies?: boolean;
subdivisionGranularity: SubdivisionGranularitySetting;
encoding?: string;
encoding?: TileEncoding;
/**
* Provide this property when the requested tile has a higher canonical Z than source maxzoom.
* This allows the worker to know that it needs to overzoom from a source tile.
Expand Down Expand Up @@ -80,7 +82,7 @@ export type WorkerTileWithData = ExpiryData & {
featureIndex: FeatureIndex;
collisionBoxArray: CollisionBoxArray;
rawTileData?: ArrayBuffer;
encoding?: string;
encoding?: TileEncoding;
resourceTiming?: Array<PerformanceResourceTiming>;
// Only used for benchmarking:
glyphMap?: {
Expand Down
9 changes: 5 additions & 4 deletions src/tile/tile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ import {toEvaluationFeature} from '../data/evaluation_feature';
import {EvaluationParameters} from '../style/evaluation_parameters';
import {rtlMainThreadPluginFactory} from '../source/rtl_text_plugin_main_thread';

const CLOCK_SKEW_RETRY_TIMEOUT = 30000;

import type {SourceFeatureState} from '../source/source_state';
import type {Bucket} from '../data/bucket';
import type {StyleLayer} from '../style/style_layer';
import type {WorkerTileResult} from '../source/worker_source';
import type {TileEncoding, WorkerTileResult} from '../source/worker_source';
import type {Actor} from '../util/actor';
import type {DEMData} from '../data/dem_data';
import type {AlphaImage} from '../util/image';
Expand All @@ -34,6 +32,9 @@ import type {QueryRenderedFeaturesOptionsStrict, QuerySourceFeatureOptionsStrict
import type {DashEntry} from '../render/line_atlas';
import type {VectorTileLayerLike} from '@maplibre/vt-pbf';
import type {Painter} from '../render/painter';

const CLOCK_SKEW_RETRY_TIMEOUT = 30000;

/**
* The tile's state, can be:
*
Expand Down Expand Up @@ -73,7 +74,7 @@ export class Tile {
buckets: {[_: string]: Bucket};
latestFeatureIndex: FeatureIndex | null;
latestRawTileData: ArrayBuffer;
latestEncoding: string;
latestEncoding: TileEncoding;
imageAtlas: ImageAtlas;
imageAtlasTexture: Texture;
dashPositions: {[_: string]: DashEntry};
Expand Down
2 changes: 2 additions & 0 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {pixelsToTileUnits} from '../source/pixels_to_tile_units';
import {type OverscaledTileID} from '../tile/tile_id';
import type {Event} from './evented';

export const JSON_PREFIX = '__$json__:';

/**
* Returns a new 64 bit float vec4 of zeroes.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/util/vectortile_to_geojson.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type Point from '@mapbox/point-geometry';
import {classifyRings} from '@mapbox/vector-tile';
import {JSON_PREFIX} from './util';
import type {LayerSpecification} from '@maplibre/maplibre-gl-style-spec';
import type {VectorTileFeatureLike} from '@maplibre/vt-pbf';

Expand Down Expand Up @@ -45,6 +46,13 @@ export class GeoJSONFeature {
this._y = y;
this._z = z;

for (const key in vectorTileFeature.properties) {
if (typeof vectorTileFeature.properties[key] !== 'string' || !vectorTileFeature.properties[key].startsWith(JSON_PREFIX)) {
Copy link
Copy Markdown
Collaborator

@wayofthefuture wayofthefuture Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blows my version away! consider const value = vectorTileFeature.properties[key]... I'm not sure though I guess this is a style preference or something i don't know

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to avoid allocation as much as possible...

continue;
}
// JSON parsing the special case of a json prefix that is serialized in geojson worker source.
vectorTileFeature.properties[key] = JSON.parse(vectorTileFeature.properties[key].slice(JSON_PREFIX.length));
}
this.properties = vectorTileFeature.properties;
this.id = id;
}
Expand Down
2 changes: 1 addition & 1 deletion test/build/bundle_size.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1023690
1024823
16 changes: 8 additions & 8 deletions test/examples/measure-distances.html
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,16 @@

map.getSource('geojson').setData(geojson);
});
});

map.on('mousemove', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['measure-points']
map.on('mousemove', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['measure-points']
});
// UI indicator for clicking/hovering a point on the map
map.getCanvas().style.cursor = features.length ?
'pointer' :
'crosshair';
});
// UI indicator for clicking/hovering a point on the map
map.getCanvas().style.cursor = features.length ?
'pointer' :
'crosshair';
});
</script>
</body>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
{
"geometry": {
"type": "Point",
"coordinates": [
0,
0
]
},
"type": "Feature",
"properties": {
"nested": {
"nested2": {
"num": 1
}
}
},
"source": "geojson",
"state": {}
}
]
Loading