Skip to content

Commit

Permalink
chore(POI Map): improve how features are highlighted
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinFabre-ods committed Jan 11, 2024
1 parent 1f7c971 commit d96cd8f
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 15 deletions.
94 changes: 85 additions & 9 deletions packages/visualizations/src/components/MapPoi/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import maplibregl, {
MapMouseEvent,
MapOptions,
StyleSpecification,
CircleLayerSpecification,
SymbolLayerSpecification,
} from 'maplibre-gl';

import {
Expand Down Expand Up @@ -36,7 +38,26 @@ const CURSOR = {
DRAG: 'move',
};

const POPUP_FEATURE_STATE_KEY = 'popup-feature';
const ACTIVE_FEATURE_RATIO_SIZE = 1.3;

/** Sorts features in a layer by setting a sort key for a specific feature. */
const sortLayerFeatures = (
map: maplibregl.Map,
layer: MapGeoJSONFeature['layer'],
feature: MapGeoJSONFeature
) => {
map.setLayoutProperty(layer.id, `${layer.type}-sort-key`, [
'case',
['==', ['id'], feature.id],
1,
0,
]);
};

/** Restores the original sorting order of features in a layer */
const unsortLayerFeatures = (map: maplibregl.Map, layer: MapGeoJSONFeature['layer']) => {
map.setLayoutProperty(layer.id, `${layer.type}-sort-key`, 0);
};

type MapFunction = (map: maplibregl.Map) => unknown;

Expand Down Expand Up @@ -88,11 +109,66 @@ export default class MapPOI {
this.queuedFunctions = [];
}

private updateFeatureState(feature: ActiveFeatureType, stateKey: string, stateValue: unknown) {
/** Make active feature bigger and sort it on top of other features in the layer */
private highlightFeature(feature: ActiveFeatureType) {
if (!feature) return;
const { layer } = feature;
this.queue((map) => {
sortLayerFeatures(map, layer, feature);
switch (layer.type) {
case 'symbol':
// eslint-disable-next-line no-case-declarations
const iconSize = ((layer as SymbolLayerSpecification).layout?.['icon-size'] ||
1) as number;
map.setLayoutProperty(layer.id, 'icon-size', [
'case',
['==', ['id'], feature.id],
iconSize * ACTIVE_FEATURE_RATIO_SIZE,
iconSize,
]);
break;
case 'circle':
// eslint-disable-next-line no-case-declarations
const circleRadius = (layer as CircleLayerSpecification).paint?.[
'circle-radius'
] as number;
map.setPaintProperty(layer.id, 'circle-radius', [
'case',
['==', ['id'], feature.id],
circleRadius * ACTIVE_FEATURE_RATIO_SIZE,
circleRadius,
]);
break;
default:
break;
}
});
}

/** Reset active feature highlight state */
private unhighlightFeature(feature: ActiveFeatureType) {
if (!feature) return;
const { id, source, sourceLayer } = feature;
const { layer } = feature;
this.queue((map) => {
map.setFeatureState({ id, source, sourceLayer }, { [stateKey]: stateValue });
unsortLayerFeatures(map, layer);
switch (layer.type) {
case 'symbol':
map.setLayoutProperty(
layer.id,
'icon-size',
(layer as SymbolLayerSpecification).layout?.['icon-size'] || 1
);
break;
case 'circle':
map.setPaintProperty(
layer.id,
'circle-radius',
(layer as CircleLayerSpecification).paint?.['circle-radius']
);
break;
default:
break;
}
});
}

Expand Down Expand Up @@ -172,15 +248,15 @@ export default class MapPOI {
}

private navigateToFeature(direction: number) {
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
this.unhighlightFeature(this.activeFeature);
const activeFeatureIndex = this.availableFeaturesOnClick.indexOf(this.activeFeature);
this.activeFeature = this.availableFeaturesOnClick[activeFeatureIndex + direction];
this.updatePopupContent();
this.updatePopupDisplay();
if (this.activeFeature?.geometry.type === 'Point') {
this.popup.setLngLat(this.activeFeature?.geometry.coordinates as LngLatLike);
}
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, true);
this.highlightFeature(this.activeFeature);
}

private renderFeaturesNavigationControls() {
Expand Down Expand Up @@ -307,7 +383,7 @@ export default class MapPOI {
});

// Removing feature state for the obsolete active feature.
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
this.unhighlightFeature(this.activeFeature);
const hasFeatures = !!features.length;
// Current rule: use the first feature to build the popup.
// TO DO: Create a menu to display a list of feature to choose from.
Expand Down Expand Up @@ -340,7 +416,7 @@ export default class MapPOI {

this.updatePopupContent();
this.updatePopupDisplay();
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, true);
this.highlightFeature(this.activeFeature);
}

initialize(
Expand Down Expand Up @@ -520,7 +596,7 @@ export default class MapPOI {

constructor() {
this.popup.on('close', () => {
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
this.unhighlightFeature(this.activeFeature);
this.onPopupDisplayUpdate(this.activePopupDisplay, null);
this.activePopupDisplay = null;
this.activeFeature = null;
Expand Down
8 changes: 2 additions & 6 deletions packages/visualizations/src/components/MapPoi/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,7 @@ const getMapCircleLayer = (layer: CircleLayer): CircleLayerSpecification => {
...getBaseMapLayerConfiguration(layer),
type,
paint: {
'circle-radius': [
'case',
['boolean', ['feature-state', 'popup-feature'], false],
circleRadius * 1.3,
circleRadius,
],
'circle-radius': circleRadius,
...(circleBorderColor && { 'circle-stroke-width': circleStrokeWidth }),
'circle-color': circleColor,
...(circleBorderColor && { 'circle-stroke-color': circleBorderColor }),
Expand Down Expand Up @@ -124,6 +119,7 @@ const getMapSymbolLayer = (layer: SymbolLayer): SymbolLayerSpecification => {
...getBaseMapLayerConfiguration(layer),
type,
layout: {
'icon-size': 1,
'icon-allow-overlap': true,
'icon-image': iconImage,
},
Expand Down

0 comments on commit d96cd8f

Please sign in to comment.