Skip to content

Commit 70a129c

Browse files
authored
[feat] vector tile layer - add feature uid selector (#3203)
* [feat] vector tile layer - add feature uid selector Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com> * nit Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com> --------- Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
1 parent 2b8af82 commit 70a129c

3 files changed

Lines changed: 37 additions & 18 deletions

File tree

src/components/src/side-panel/layer-panel/vector-tile-layer-configurator.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {VectorTileLayer} from '@kepler.gl/layers';
99
import {KeplerTable as KeplerDataset} from '@kepler.gl/table';
1010

1111
import SourceDataSelectorFactory from '../common/source-data-selector';
12+
import FieldSelectorFactory from '../../common/field-selector';
1213
import ChannelByValueSelectorFactory from './channel-by-value-selector';
1314
import LayerConfigGroupFactory, {ConfigGroupCollapsibleContent} from './layer-config-group';
1415
import {LayerColorRangeSelectorFactory, LayerColorSelectorFactory} from './layer-color-selector';
@@ -37,7 +38,8 @@ VectorTileLayerConfiguratorFactory.deps = [
3738
LayerConfigGroupFactory,
3839
VisConfigSliderFactory,
3940
VisConfigSwitchFactory,
40-
SourceDataSelectorFactory
41+
SourceDataSelectorFactory,
42+
FieldSelectorFactory
4143
];
4244

4345
function VectorTileLayerConfiguratorFactory(
@@ -46,7 +48,9 @@ function VectorTileLayerConfiguratorFactory(
4648
LayerColorSelector: ReturnType<typeof LayerColorSelectorFactory>,
4749
LayerConfigGroup: ReturnType<typeof LayerConfigGroupFactory>,
4850
VisConfigSlider: ReturnType<typeof VisConfigSliderFactory>,
49-
VisConfigSwitch: ReturnType<typeof VisConfigSwitchFactory>
51+
VisConfigSwitch: ReturnType<typeof VisConfigSwitchFactory>,
52+
_SourceDataSelector: ReturnType<typeof SourceDataSelectorFactory>,
53+
FieldSelector: ReturnType<typeof FieldSelectorFactory>
5054
): React.FC<Props> {
5155
const VectorTileLayerConfigurator = ({
5256
layer,
@@ -190,6 +194,19 @@ function VectorTileLayerConfiguratorFactory(
190194
<VisConfigSwitch {...layer.visConfigSettings.radiusUnits} {...visConfiguratorProps} />
191195
</ConfigGroupCollapsibleContent>
192196
</LayerConfigGroup>
197+
198+
{/* Unique ID Field */}
199+
<LayerConfigGroup {...visConfiguratorProps} label="layer.uniqueIdField">
200+
<FieldSelector
201+
fields={layerChannelConfigProps.fields || []}
202+
value={layer.config.uniqueIdField || null}
203+
onSelect={(val: any) =>
204+
layerConfiguratorProps.onChange?.({uniqueIdField: val?.name || null})
205+
}
206+
placeholder={'placeholder.selectField'}
207+
erasable
208+
/>
209+
</LayerConfigGroup>
193210
</StyledLayerConfigurator>
194211
);
195212
};

src/layers/src/vector-tile/vector-tile-layer.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ export type VectorTileLayerConfig = Merge<
179179
radiusScale?: string;
180180
radiusDomain?: VisualChannelDomain;
181181
radiusRange?: any;
182+
183+
uniqueIdField?: string | null;
182184
}
183185
>;
184186

@@ -335,7 +337,9 @@ export default class VectorTileLayer extends AbstractTileLayer<VectorTile, Featu
335337

336338
radiusField: null,
337339
radiusDomain: [0, 1],
338-
radiusScale: SCALE_TYPES.linear
340+
radiusScale: SCALE_TYPES.linear,
341+
342+
uniqueIdField: null
339343
};
340344
}
341345

@@ -553,15 +557,13 @@ export default class VectorTileLayer extends AbstractTileLayer<VectorTile, Featu
553557
if (data.tileSource) {
554558
const hoveredObject = this.hasHoveredObject(objectHovered);
555559

556-
// Try to infer a stable unique id property from the hovered feature so we can
557-
// highlight the same feature across adjacent tiles. If none is found, rely on
558-
// feature.id when available.
560+
// Resolve unique id property: use configured uniqueIdField if set, otherwise infer
559561
let uniqueIdProperty: string | undefined;
560562
let highlightedFeatureId: string | number | undefined;
561563
if (hoveredObject && hoveredObject.properties) {
562-
uniqueIdProperty = UUID_CANDIDATES.find(
563-
k => hoveredObject.properties && k in hoveredObject.properties
564-
);
564+
uniqueIdProperty =
565+
this.config.uniqueIdField ??
566+
UUID_CANDIDATES.find(k => hoveredObject.properties && k in hoveredObject.properties);
565567
highlightedFeatureId = uniqueIdProperty
566568
? hoveredObject.properties[uniqueIdProperty]
567569
: (hoveredObject as any).id;
@@ -570,7 +572,8 @@ export default class VectorTileLayer extends AbstractTileLayer<VectorTile, Featu
570572
// Build per-tile clipped overlay to draw only the outer stroke of highlighted feature per tile
571573
const perTileOverlays = this._getPerTileOverlays(hoveredObject, {
572574
defaultLayerProps,
573-
visConfig
575+
visConfig,
576+
uniqueIdProperty
574577
});
575578

576579
const layers = [
@@ -705,18 +708,15 @@ export default class VectorTileLayer extends AbstractTileLayer<VectorTile, Featu
705708
*/
706709
_getPerTileOverlays(
707710
hoveredObject: Feature,
708-
options: {defaultLayerProps: any; visConfig: any}
711+
options: {defaultLayerProps: any; visConfig: any; uniqueIdProperty?: string}
709712
): DeckLayer[] {
710713
let perTileOverlays: DeckLayer[] = [];
711714
if (hoveredObject) {
712715
try {
713716
const tiles = this.tileDataset?.getTiles?.() || [];
714717
// Derive hovered id from hoveredObject
715-
const hoveredPid = UUID_CANDIDATES.find(
716-
k => hoveredObject?.properties && k in hoveredObject.properties
717-
);
718-
const hoveredId = hoveredPid
719-
? String(hoveredObject?.properties?.[hoveredPid])
718+
const hoveredId = options.uniqueIdProperty
719+
? String(hoveredObject?.properties?.[options.uniqueIdProperty])
720720
: String((hoveredObject as any)?.id);
721721

722722
// Group matched fragments by tile id
@@ -727,8 +727,9 @@ export default class VectorTileLayer extends AbstractTileLayer<VectorTile, Featu
727727
if (!Array.isArray(features)) continue;
728728
const tileId = (tile as any).id;
729729
for (const f of features) {
730-
const pid = UUID_CANDIDATES.find(k => f.properties && k in f.properties);
731-
const fid = pid ? f.properties?.[pid] : (f as any).id;
730+
const fid = options.uniqueIdProperty
731+
? f.properties?.[options.uniqueIdProperty]
732+
: (f as any).id;
732733
if (fid !== undefined && String(fid) === hoveredId) {
733734
(byTile[tileId] = byTile[tileId] || []).push(f as Feature);
734735
}

src/localization/src/translations/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export default {
9999
service: 'Service',
100100
layer: 'Layer',
101101
appearance: 'Appearance',
102+
uniqueIdField: 'Unique ID Field',
102103
type: {
103104
point: 'point',
104105
arc: 'arc',

0 commit comments

Comments
 (0)