Skip to content

Commit

Permalink
fix(carto): Support picking with MVT tile-relative coordinates (#8926)
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy committed Jun 3, 2024
1 parent 303d1da commit 12e837f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 119 deletions.
6 changes: 6 additions & 0 deletions modules/carto/src/layers/vector-tile-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ export default class VectorTileLayer<
getPickingInfo(params) {
const info = super.getPickingInfo(params);

// MVT tiles use tile-relative coordinates, handled by MVTLayer#getPickingInfo.
if (this.state.mvt) {
return info;
}

// CARTO tiles use [lat, lng], so overwrite `info.object`.
if (this.state.binary && info.index !== -1) {
const {data} = params.sourceLayer!.props;
info.object = binaryToGeojson(data as BinaryFeatureCollection, {
Expand Down
84 changes: 0 additions & 84 deletions test/modules/carto/layers/carto-tile-layer.spec.ts

This file was deleted.

129 changes: 94 additions & 35 deletions test/modules/carto/layers/vector-tile-layer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,11 @@ import {testPickingLayer} from '../../layers/test-picking-layer';
import {WebMercatorViewport} from '@deck.gl/core';
import {withMockFetchMapsV3} from '../mock-fetch';

const geoJSONData = [
{
id: 1,
type: 'Feature',
geometry: {
type: 'Point',
// Unlike MVT, coordinates are not relative to the tile, but [lng, lat]
coordinates: [-123, 45]
},
properties: {
cartodb_id: 148
}
}
];

const geoJSONBinaryData = geojsonToBinary(JSON.parse(JSON.stringify(geoJSONData)));

test(`VectorTileLayer#picking`, async t => {
await withMockFetchMapsV3(async () => {
class TestVectorTileLayer extends VectorTileLayer {
static readonly layerName = 'TestVectorTileLayer';
async getTileData() {
return geoJSONBinaryData;
}
}

await testPickingLayer({
layer: new TestVectorTileLayer({
id: 'mvt',
binary: true,
data: vectorTableSource({
accessToken: 'XXX',
connectionName: 'carto_dw',
tableName: 'carto-demo-data.demo_tables.test'
}),
uniqueIdProperty: 'cartodb_id',
autoHighlight: true
}),
// CARTO binary tile coordinates are [lat, lng], not tile-relative like MVT.
layer: createTestVectorTileLayer([-123, 45], 'binary'),
viewport: new WebMercatorViewport({
latitude: 0,
longitude: 0,
Expand Down Expand Up @@ -88,3 +55,95 @@ test(`VectorTileLayer#picking`, async t => {

t.end();
});

test(`VectorTileLayer#pickingMVT`, async t => {
await withMockFetchMapsV3(async () => {
await testPickingLayer({
// MVT tile coordinates are tile-relative.
layer: createTestVectorTileLayer([0.2, 0.5], 'mvt'),
viewport: new WebMercatorViewport({
latitude: 0,
longitude: 0,
zoom: 1
}),
testCases: [
{
pickedColor: new Uint8Array([1, 0, 0, 0]),
pickedLayerId: 'mvt-0-0-1-points-circle',
mode: 'hover',
onAfterUpdate: ({layer, subLayers, info}) => {
t.comment('hover over polygon');
t.ok(info.object, 'info.object is populated');
t.ok(info.object.properties, 'info.object.properties is populated');
t.ok(info.object.geometry, 'info.object.geometry is populated');
t.deepEqual(
info.object.geometry.coordinates.map(Math.round),
[-144, 67],
'picked coordinates are correct'
);
t.ok(
subLayers.every(l => l.props.highlightedObjectIndex === 0),
'set sub layers highlightedObjectIndex'
);
}
},
{
pickedColor: new Uint8Array([0, 0, 0, 0]),
pickedLayerId: '',
mode: 'hover',
onAfterUpdate: ({layer, subLayers, info}) => {
t.comment('pointer leave');
t.notOk(info.object, 'info.object is not populated');
t.ok(
subLayers.every(l => l.props.highlightedObjectIndex === -1),
'cleared sub layers highlightedObjectIndex'
);
}
}
]
});
}).catch(t.fail);

t.end();
});

function createTestVectorTileLayer(
coordinates: [number, number],
format: 'binary' | 'mvt'
): VectorTileLayer {
const geoJSONData = [
{
id: 1,
type: 'Feature',
geometry: {type: 'Point', coordinates},
properties: {cartodb_id: 148}
}
];

const geoJSONBinaryData = geojsonToBinary(JSON.parse(JSON.stringify(geoJSONData)));

class TestVectorTileLayer extends VectorTileLayer {
static readonly layerName = 'TestVectorTileLayer';
async getTileData() {
return geoJSONBinaryData;
}
}

// Override ?formatTiles to test both MVT and binary tiles.
const data = vectorTableSource({
accessToken: 'XXX',
connectionName: 'carto_dw',
tableName: 'carto-demo-data.demo_tables.test'
}).then(({tiles, ...rest}) => ({
...rest,
tiles: tiles.map(url => url.replace(/formatTiles=\w+/, `formatTiles=${format}`))
}));

return new TestVectorTileLayer({
id: 'mvt',
binary: true,
data,
uniqueIdProperty: 'cartodb_id',
autoHighlight: true
});
}

0 comments on commit 12e837f

Please sign in to comment.