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

Improve typing and change StyleLayer query call interface #5276

Merged
merged 8 commits into from
Dec 31, 2024
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### ✨ Features and improvements
- Allows setting the desired WebGL version to use ([#5236](https://github.com/maplibre/maplibre-gl-js/pull/5236)). You can now use `contextType` inside `canvasContextAttributes` to choose which WebGL version to use
- ⚠️ `StyleLayer`'s `queryIntersectsFeature` method parameters were moved to `QueryIntersectsFeatureParams`. To overcome it simply wrap the method parameters with `{}` ([#5276](https://github.com/maplibre/maplibre-gl-js/pull/5276))
- _...Add new stuff here..._

### 🐞 Bug fixes
Expand Down
37 changes: 25 additions & 12 deletions src/data/feature_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@
};
};

export type QueryResults = {
[_: string]: QueryResultsItem[];
};

export type QueryResultsItem = {
featureIndex: number;
feature: GeoJSONFeature;
intersectionZ?: boolean | number;
};

/**
* An in memory index class to allow fast interaction with features
*/
Expand Down Expand Up @@ -110,7 +120,7 @@
styleLayers: {[_: string]: StyleLayer},
serializedLayers: {[_: string]: any},
sourceFeatureState: SourceFeatureState
): {[_: string]: Array<{featureIndex: number; feature: GeoJSONFeature}>} {
): QueryResults {

Check warning on line 123 in src/data/feature_index.ts

View check run for this annotation

Codecov / codecov/patch

src/data/feature_index.ts#L123

Added line #L123 was not covered by tests
this.loadVTLayers();

const params = args.params;
Expand All @@ -136,7 +146,7 @@

matching.sort(topDownFeatureComparator);

const result = {};
const result: QueryResults = {};

Check warning on line 149 in src/data/feature_index.ts

View check run for this annotation

Codecov / codecov/patch

src/data/feature_index.ts#L149

Added line #L149 was not covered by tests
let previousIndex;
for (let k = 0; k < matching.length; k++) {
const index = matching[k];
Expand All @@ -163,7 +173,16 @@
featureGeometry = loadGeometry(feature);
}

return styleLayer.queryIntersectsFeature(queryGeometry, feature, featureState, featureGeometry, this.z, args.transform, pixelsToTileUnits, args.pixelPosMatrix);
return styleLayer.queryIntersectsFeature({
queryGeometry,
feature,
featureState,
geometry: featureGeometry,
zoom: this.z,
transform: args.transform,
pixelsToTileUnits,
pixelPosMatrix: args.pixelPosMatrix
});

Check warning on line 185 in src/data/feature_index.ts

View check run for this annotation

Codecov / codecov/patch

src/data/feature_index.ts#L176-L185

Added lines #L176 - L185 were not covered by tests
}
);
}
Expand All @@ -172,13 +191,7 @@
}

loadMatchingFeature(
result: {
[_: string]: Array<{
featureIndex: number;
feature: GeoJSONFeature;
intersectionZ?: boolean | number;
}>;
},
result: QueryResults,

Check warning on line 194 in src/data/feature_index.ts

View check run for this annotation

Codecov / codecov/patch

src/data/feature_index.ts#L194

Added line #L194 was not covered by tests
bucketIndex: number,
sourceLayerIndex: number,
featureIndex: number,
Expand Down Expand Up @@ -261,8 +274,8 @@
filterSpec: FilterSpecification,
filterLayerIDs: Set<string> | null,
availableImages: Array<string>,
styleLayers: {[_: string]: StyleLayer}) {
const result = {};
styleLayers: {[_: string]: StyleLayer}): QueryResults {
const result: QueryResults = {};

Check warning on line 278 in src/data/feature_index.ts

View check run for this annotation

Codecov / codecov/patch

src/data/feature_index.ts#L277-L278

Added lines #L277 - L278 were not covered by tests
this.loadVTLayers();

const filter = featureFilter(filterSpec);
Expand Down
101 changes: 60 additions & 41 deletions src/source/query_features.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import {mat4} from 'gl-matrix';
import type Point from '@mapbox/point-geometry';
import type {SourceCache} from './source_cache';
import type {StyleLayer} from '../style/style_layer';
import type {CollisionIndex} from '../symbol/collision_index';
import type {IReadonlyTransform} from '../geo/transform_interface';
import type {RetainedQueryData} from '../symbol/placement';
import type {FilterSpecification} from '@maplibre/maplibre-gl-style-spec';
import type {MapGeoJSONFeature} from '../util/vectortile_to_geojson';
import type Point from '@mapbox/point-geometry';
import {mat4} from 'gl-matrix';
import type {GeoJSONFeature, MapGeoJSONFeature} from '../util/vectortile_to_geojson';
import type {QueryResults, QueryResultsItem} from '../data/feature_index';
import type {OverscaledTileID} from './tile_id';

type RenderedFeatureLayer = {
wrappedTileID: string;
queryResults: QueryResults;
};

/**
* Options to pass to query the map for the rendered features
Expand Down Expand Up @@ -55,10 +62,16 @@
validate?: boolean;
};

export type QueryRenderedFeaturesResults = {
[key: string]: QueryRenderedFeaturesResultsItem[];
};

export type QueryRenderedFeaturesResultsItem = QueryResultsItem & { feature: MapGeoJSONFeature };

/*
* Returns a matrix that can be used to convert from tile coordinates to viewport pixel coordinates.
*/
function getPixelPosMatrix(transform, tileID) {
function getPixelPosMatrix(transform, tileID: OverscaledTileID) {
const t = mat4.create();
mat4.translate(t, t, [1, 1, 0]);
mat4.scale(t, t, [transform.width * 0.5, transform.height * 0.5, 1]);
Expand Down Expand Up @@ -95,14 +108,14 @@
queryGeometry: Array<Point>,
params: QueryRenderedFeaturesOptionsStrict | undefined,
transform: IReadonlyTransform
): { [key: string]: Array<{featureIndex: number; feature: MapGeoJSONFeature}> } {
): QueryRenderedFeaturesResults {

const has3DLayer = queryIncludes3DLayer(params?.layers ?? null, styleLayers, sourceCache.id);
const maxPitchScaleFactor = transform.maxPitchScaleFactor();
const tilesIn = sourceCache.tilesIn(queryGeometry, maxPitchScaleFactor, has3DLayer);

tilesIn.sort(sortTilesIn);
const renderedFeatureLayers = [];
const renderedFeatureLayers: RenderedFeatureLayer[] = [];
for (const tileIn of tilesIn) {
renderedFeatureLayers.push({
wrappedTileID: tileIn.tileID.wrapped().key,
Expand All @@ -122,19 +135,7 @@

const result = mergeRenderedFeatureLayers(renderedFeatureLayers);

// Merge state from SourceCache into the results
for (const layerID in result) {
result[layerID].forEach((featureWrapper) => {
const feature = featureWrapper.feature as MapGeoJSONFeature;
const state = sourceCache.getFeatureState(feature.layer['source-layer'], feature.id);
feature.source = feature.layer.source;
if (feature.layer['source-layer']) {
feature.sourceLayer = feature.layer['source-layer'];
}
feature.state = state;
});
}
return result;
return convertFeaturesToMapFeatures(result, sourceCache);
HarelM marked this conversation as resolved.
Show resolved Hide resolved
}

export function queryRenderedSymbols(styleLayers: {[_: string]: StyleLayer},
Expand All @@ -145,8 +146,8 @@
collisionIndex: CollisionIndex,
retainedQueryData: {
[_: number]: RetainedQueryData;
}) {
const result = {};
}): QueryRenderedFeaturesResults {
const result: QueryResults = {};
const renderedSymbols = collisionIndex.queryRenderedSymbols(queryGeometry);
const bucketQueryData: RetainedQueryData[] = [];
for (const bucketInstanceId of Object.keys(renderedSymbols).map(Number)) {
Expand Down Expand Up @@ -192,29 +193,15 @@
}
}

// Merge state from SourceCache into the results
for (const layerName in result) {
result[layerName].forEach((featureWrapper) => {
const feature = featureWrapper.feature;
const layer = styleLayers[layerName];
const sourceCache = sourceCaches[layer.source];
const state = sourceCache.getFeatureState(feature.layer['source-layer'], feature.id);
feature.source = feature.layer.source;
if (feature.layer['source-layer']) {
feature.sourceLayer = feature.layer['source-layer'];
}
feature.state = state;
});
}
return result;
return convertFeaturesToMapFeaturesMultiple(result, styleLayers, sourceCaches);
}

export function querySourceFeatures(sourceCache: SourceCache, params: QuerySourceFeatureOptions | undefined) {
export function querySourceFeatures(sourceCache: SourceCache, params: QuerySourceFeatureOptions | undefined): GeoJSONFeature[] {
const tiles = sourceCache.getRenderableIds().map((id) => {
return sourceCache.getTileByID(id);
});

const result = [];
const result: GeoJSONFeature[] = [];

const dataTiles = {};
for (let i = 0; i < tiles.length; i++) {
Expand All @@ -229,16 +216,16 @@
return result;
}

function sortTilesIn(a, b) {
function sortTilesIn(a: {tileID: OverscaledTileID}, b: {tileID: OverscaledTileID}) {

Check warning on line 219 in src/source/query_features.ts

View check run for this annotation

Codecov / codecov/patch

src/source/query_features.ts#L219

Added line #L219 was not covered by tests
const idA = a.tileID;
const idB = b.tileID;
return (idA.overscaledZ - idB.overscaledZ) || (idA.canonical.y - idB.canonical.y) || (idA.wrap - idB.wrap) || (idA.canonical.x - idB.canonical.x);
}

function mergeRenderedFeatureLayers(tiles) {
function mergeRenderedFeatureLayers(tiles: RenderedFeatureLayer[]): QueryResults {
// Merge results from all tiles, but if two tiles share the same
// wrapped ID, don't duplicate features between the two tiles
const result = {};
const result: QueryResults = {};
const wrappedIDLayerMap = {};
for (const tile of tiles) {
const queryResults = tile.queryResults;
Expand All @@ -258,3 +245,35 @@
}
return result;
}

function convertFeaturesToMapFeatures(result: QueryResults, sourceCache: SourceCache): QueryRenderedFeaturesResults {
// Merge state from SourceCache into the results
for (const layerID in result) {
for (const featureWrapper of result[layerID]) {
convertFeatureToMapFeature(featureWrapper, sourceCache);
};
}
return result as QueryRenderedFeaturesResults;
}

function convertFeaturesToMapFeaturesMultiple(result: QueryResults, styleLayers: {[_: string]: StyleLayer}, sourceCaches: {[_: string]: SourceCache}): QueryRenderedFeaturesResults {
// Merge state from SourceCache into the results
for (const layerName in result) {
for (const featureWrapper of result[layerName]) {
const layer = styleLayers[layerName];
const sourceCache = sourceCaches[layer.source];
convertFeatureToMapFeature(featureWrapper, sourceCache);
};
}

Check warning on line 267 in src/source/query_features.ts

View check run for this annotation

Codecov / codecov/patch

src/source/query_features.ts#L262-L267

Added lines #L262 - L267 were not covered by tests
return result as QueryRenderedFeaturesResults;
}

function convertFeatureToMapFeature(featureWrapper: QueryResultsItem, sourceCache: SourceCache) {
const feature = featureWrapper.feature as MapGeoJSONFeature;
const state = sourceCache.getFeatureState(feature.layer['source-layer'], feature.id);
feature.source = feature.layer.source;
if (feature.layer['source-layer']) {
feature.sourceLayer = feature.layer['source-layer'];
}
feature.state = state;
}
2 changes: 1 addition & 1 deletion src/source/source_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ export class SourceCache extends Evented {
* @param pointQueryGeometry - coordinates of the corners of bounding rectangle
* @returns result items have `{tile, minX, maxX, minY, maxY}`, where min/max bounding values are the given bounds transformed in into the coordinate space of this tile.
*/
tilesIn(pointQueryGeometry: Array<Point>, maxPitchScaleFactor: number, has3DLayer: boolean) {
tilesIn(pointQueryGeometry: Array<Point>, maxPitchScaleFactor: number, has3DLayer: boolean): TileResult[] {
const tileResults: TileResult[] = [];

const transform = this.transform;
Expand Down
11 changes: 5 additions & 6 deletions src/source/tile.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {uniqueId, parseCacheControl} from '../util/util';
import {deserialize as deserializeBucket} from '../data/bucket';
import '../data/feature_index';
import type {FeatureIndex} from '../data/feature_index';
import {GeoJSONFeature} from '../util/vectortile_to_geojson';
import {featureFilter} from '@maplibre/maplibre-gl-style-spec';
import {SymbolBucket} from '../data/bucket/symbol_bucket';
Expand Down Expand Up @@ -30,11 +29,11 @@
import type {LayerFeatureStates} from './source_state';
import type {FilterSpecification} from '@maplibre/maplibre-gl-style-spec';
import type Point from '@mapbox/point-geometry';
import {type mat4} from 'gl-matrix';
import type {mat4} from 'gl-matrix';
import type {VectorTileLayer} from '@mapbox/vector-tile';
import {type ExpiryData} from '../util/ajax';
import {type QueryRenderedFeaturesOptionsStrict} from './query_features';

import type {ExpiryData} from '../util/ajax';
import type {QueryRenderedFeaturesOptionsStrict} from './query_features';
import type {FeatureIndex, QueryResults} from '../data/feature_index';
/**
* The tile's state, can be:
*
Expand Down Expand Up @@ -290,7 +289,7 @@
transform: IReadonlyTransform,
maxPitchScaleFactor: number,
pixelPosMatrix: mat4
): {[_: string]: Array<{featureIndex: number; feature: GeoJSONFeature}>} {
): QueryResults {

Check warning on line 292 in src/source/tile.ts

View check run for this annotation

Codecov / codecov/patch

src/source/tile.ts#L292

Added line #L292 was not covered by tests
if (!this.latestFeatureIndex || !this.latestFeatureIndex.rawTileData)
return {};

Expand Down
14 changes: 12 additions & 2 deletions src/style/query_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,21 @@
return Math.sqrt(translate[0] * translate[0] + translate[1] * translate[1]);
}

/**
* @internal
* Translates a geometry by a certain pixels in tile coordinates
* @param queryGeometry - The geometry to translate in tile coordinates
* @param translate - The translation in pixels
* @param translateAnchor - The anchor of the translation
* @param bearing - The bearing of the map
* @param pixelsToTileUnits - The scale factor from pixels to tile units
* @returns the translated geometry in tile coordinates
*/
export function translate(queryGeometry: Array<Point>,
translate: [number, number],
translateAnchor: 'viewport' | 'map',
bearing: number,
pixelsToTileUnits: number) {
pixelsToTileUnits: number): Point[] {

Check warning on line 39 in src/style/query_utils.ts

View check run for this annotation

Codecov / codecov/patch

src/style/query_utils.ts#L39

Added line #L39 was not covered by tests
if (!translate[0] && !translate[1]) {
return queryGeometry;
}
Expand All @@ -36,7 +46,7 @@
pt._rotate(-bearing);
}

const translated = [];
const translated: Point[] = [];

Check warning on line 49 in src/style/query_utils.ts

View check run for this annotation

Codecov / codecov/patch

src/style/query_utils.ts#L49

Added line #L49 was not covered by tests
for (let i = 0; i < queryGeometry.length; i++) {
const point = queryGeometry[i];
translated.push(point.sub(pt));
Expand Down
Loading
Loading