Skip to content

Commit

Permalink
tmp: Vector tiles experimentations
Browse files Browse the repository at this point in the history
  • Loading branch information
wmai committed Aug 23, 2022
1 parent 20727d3 commit ec2bd3b
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 47 deletions.
122 changes: 96 additions & 26 deletions packages/visualizations-react/stories/Map/StudioChoropleth.stories.tsx
@@ -1,7 +1,12 @@
import React from 'react';
import { Meta } from '@storybook/react';
import { Choropleth, Props } from '../../src';
import { ChoroplethOptions, DataFrame } from '@opendatasoft/visualizations';
import {
DataFrame,
ChoroplethOptions,
ChoroplethShapeGeoJsonValue,
ChoroplethShapeVectorTilesValue,
} from '@opendatasoft/visualizations';
import { shapes, multiPolygonShapes } from './shapes';
import { IMAGES } from '../utils';

Expand Down Expand Up @@ -42,9 +47,11 @@ const StudioChoroplethArgs: Props<DataFrame, ChoroplethOptions> = {
],
},
options: {
style: {},
parameters: {},
shapes,
shapes: shapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
tooltip: {
label: () => 'Test',
},
aspectRatio: 1,
},
};
Expand All @@ -60,9 +67,11 @@ const StudioChoroplethMultiPolygonArgs: Props<DataFrame, ChoroplethOptions> = {
],
},
options: {
style: {},
parameters: {},
shapes: multiPolygonShapes,
shapes: multiPolygonShapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
tooltip: {
label: () => 'Test',
},
aspectRatio: 1,
},
};
Expand All @@ -78,10 +87,11 @@ const StudioChoroplethEmptyValueArgs: Props<DataFrame, ChoroplethOptions> = {
],
},
options: {
style: {},
parameters: {},
shapes,
emptyValueColor: '#f29d9d',
shapes: shapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
tooltip: {
label: () => 'Test',
},
aspectRatio: 1,
},
};
Expand All @@ -98,9 +108,11 @@ const StudioChoroplethGradientArgs: Props<DataFrame, ChoroplethOptions> = {
],
},
options: {
style: {},
parameters: {},
shapes,
shapes: shapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
tooltip: {
label: () => 'Test',
},
colorsScale: {
type: 'gradient',
colors: {
Expand All @@ -127,9 +139,11 @@ const StudioChoroplethPaletteArgs: Props<DataFrame, ChoroplethOptions> = {
],
},
options: {
style: {},
parameters: {},
shapes,
shapes: shapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
tooltip: {
label: () => 'Test',
},
colorsScale: {
type: 'palette',
colors: ['#bcf5f9', '#89c5fd', '#3a80ec', '#0229bf'],
Expand All @@ -149,9 +163,8 @@ const StudioChoroplethCustomTooltipArgs: Props<DataFrame, ChoroplethOptions> = {
value: df,
},
options: {
style: {},
parameters: {},
shapes,
shapes: shapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
colorsScale: {
type: 'palette',
colors: ['#bcf5f9', '#89c5fd', '#3a80ec', '#0229bf'],
Expand Down Expand Up @@ -179,9 +192,8 @@ const StudioChoroplethComplexTooltipArgs: Props<DataFrame, ChoroplethOptions> =
value: df,
},
options: {
style: {},
parameters: {},
shapes,
shapes: shapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
colorsScale: {
type: 'palette',
colors: ['#bcf5f9', '#89c5fd', '#3a80ec', '#0229bf'],
Expand Down Expand Up @@ -216,9 +228,11 @@ const StudioChoroplethLongLabelsArgs: Props<DataFrame, ChoroplethOptions> = {
],
},
options: {
style: {},
parameters: {},
shapes,
shapes: shapes as ChoroplethShapeGeoJsonValue,
emptyValueColor: 'red',
tooltip: {
label: () => 'Test',
},
colorsScale: {
type: 'palette',
colors: ['#bcf5f9', '#89c5fd', '#3a80ec', '#1e03fd', '#0229bf'],
Expand All @@ -230,3 +244,59 @@ const StudioChoroplethLongLabelsArgs: Props<DataFrame, ChoroplethOptions> = {
},
};
StudioChoroplethLongLabels.args = StudioChoroplethLongLabelsArgs;


export const StudioVectorTilesChoropleth = Template.bind({});
const StudioVectorTilesChoroplethArgs: Props<DataFrame, ChoroplethOptions> = {
data: {
loading: false,
value: [
{ x: '01', y: 10 },
{ x: '02', y: 2 },
{ x: '03', y: 30 },
{ x: '04', y: 4 },
{ x: '05', y: 50 },
{ x: '06', y: 6 },
{ x: '11', y: 100 },
{ x: '24', y: 8 },
{ x: '27', y: 90 },
{ x: '28', y: 10 },
{ x: '32', y: 110 },
{ x: '44', y: 12 },
{ x: '52', y: 130 },
{ x: '53', y: 14 },
{ x: '75', y: 150 },
{ x: '76', y: 16 },
{ x: '84', y: 170 },
{ x: '93', y: 18 },
{ x: '94', y: 190 },
{ x: '975', y: 20 },
{ x: '977', y: 210 },
{ x: '978', y: 22 },
{ x: '984', y: 230 },
{ x: '986', y: 24 },
{ x: '987', y: 250 },
{ x: '988', y: 26 },
{ x: '989', y: 270 },
],
},
options: {
shapes: {
type: 'vtiles',
url: 'https://static.opendatasoft.com/vector-tiles/fr_40_region_2021/{z}/{x}/{y}.pbf',
} as ChoroplethShapeVectorTilesValue,
emptyValueColor: 'red',
tooltip: {
label: (feature) => `value: ${JSON.stringify(feature)}`,
},
colorsScale: {
type: 'palette',
colors: ['#bcf5f9', '#89c5fd', '#3a80ec', '#1e03fd', '#0229bf'],
},
aspectRatio: 1,
legend: {
title: 'I Am Legend',
},
},
};
StudioVectorTilesChoropleth.args = StudioVectorTilesChoroplethArgs;
54 changes: 43 additions & 11 deletions packages/visualizations/src/components/Map/Choropleth.svelte
Expand Up @@ -8,7 +8,8 @@
import type { ColorsScale, DataBounds, Color } from '../types';
import MapRender from './MapRender.svelte';
import { BLANK } from './mapStyles';
import { colorShapes, LIGHT_GREY, DARK_GREY } from './utils';
import { colorShapes, mapKeyToColor } from './utils';
import { DefaultColors } from './constants';
import type {
ChoroplethDataValue,
ChoroplethLayer,
Expand All @@ -28,13 +29,11 @@
const defaultColorsScale: ColorsScale = {
type: 'gradient',
colors: {
start: LIGHT_GREY,
end: DARK_GREY,
start: DefaultColors.LightGrey,
end: DefaultColors.DarkGrey,
},
};
const defaultEmptyValueColor = '#cccccc';
let aspectRatio: number;
let renderTooltip: MapRenderTooltipFunction;
let bbox: BBox;
Expand All @@ -53,7 +52,7 @@
aspectRatio,
activeShapes,
interactive = defaultInteractive,
emptyValueColor = defaultEmptyValueColor,
emptyValueColor = DefaultColors.EmptyShape,
} = options);
// Choropleth is always display over a blank map, for readability purposes
Expand All @@ -67,7 +66,11 @@
newShapes: ChoroplethShapeValue,
newColorScale: ColorsScale
) {
if (newShapes.type === 'geojson' && !newShapes.geoJson) {
if (
(newShapes.type === 'geojson' && !newShapes.geoJson)
|| (newShapes.type === 'vtiles' && !newShapes.url)
|| !values
) {
// We don't have everything we need yet
return;
}
Expand All @@ -93,14 +96,43 @@
paint: {
'fill-color': ['get', 'color'],
'fill-opacity': 0.8,
'fill-outline-color': '#fff',
'fill-outline-color': DefaultColors.ShapeOutline,
},
};
bbox = turfBbox(newShapes.geoJson);
} else {
// eslint-disable-next-line no-console
console.error('Unknown shapes type', newShapes.type);
} else if (newShapes.type === 'vtiles') {
dataBounds = { min: 1, max: 27 }; // FIXME: don't hardcode this
source = {
type: 'vector',
tiles: [newShapes.url],
};
const keyToColor = mapKeyToColor(values, newColorScale);
const matchExpression = [
'match',
['get', 'reg_code'], // FIXME: Don't hardcode the key
];
Object.entries(keyToColor).forEach(e => matchExpression.push(...e));
matchExpression.push(DefaultColors.EmptyShape); // Default fallback color
layer = {
'type': 'fill',
'source-layer': 'fr_40_region_2021', // FIXME: Don't hardcode the source layer
'layout': {},
'paint': {
'fill-color': matchExpression,
'fill-outline-color': DefaultColors.ShapeOutline,
'fill-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false],
1,
0.5,
],
}
};
}
}
Expand Down
24 changes: 18 additions & 6 deletions packages/visualizations/src/components/Map/MapRender.svelte
Expand Up @@ -2,7 +2,7 @@

<script lang="ts">
import maplibregl, {
Map,
Map as MapType,
SourceSpecification,
StyleSpecification,
NavigationControl,
Expand Down Expand Up @@ -57,7 +57,7 @@
$: cssVarStyles = `--aspect-ratio:${aspectRatio};`;
let container: HTMLElement;
let map: Map;
let map: MapType;
// Used to add navigation control to map
let nav: NavigationControl;
Expand All @@ -76,6 +76,13 @@
const sourceId = `shape-source-${mapId}`;
const layerId = `shape-layer-${mapId}`;
$: console.log(`--> MapRender [${mapId}]:`, { // TOREMOVE
source,
layer,
style,
bbox,
});
function fitMapToBbox(newBbox: BBox) {
// Cancel saved max bounds to properly fitBounds
map.setMaxBounds(null);
Expand Down Expand Up @@ -129,10 +136,15 @@
function sourceLoadingCallback(e: MapSourceDataEvent) {
// sourceDataType can be "visibility" or "metadata", in which case it's not about the data itself
if (e.isSourceLoaded && e.sourceId === sourceId && !e.sourceDataType) {
// The type forces you to pass a filter parameter in the option, but it's not required by the real code
// https://github.com/maplibre/maplibre-gl-js/issues/1393
// @ts-ignore
const renderedFeatures = map.querySourceFeatures(sourceId, { sourceLayer: layerId });
const renderedFeatures = map.querySourceFeatures(
sourceId,
// The type forces you to pass a filter parameter in the option, but it's not required by the real code
// https://github.com/maplibre/maplibre-gl-js/issues/1393
// @ts-ignore
{
sourceLayer: layer['source-layer'] || layerId, // FIXME: This may not the best way to do it
},
);
if (renderedFeatures.length) {
// Restrict zoom max
Expand Down
8 changes: 8 additions & 0 deletions packages/visualizations/src/components/Map/constants.ts
@@ -0,0 +1,8 @@
/* eslint-disable import/prefer-default-export */

export enum DefaultColors {
ShapeOutline = '#FFFFFF',
EmptyShape = '#CCCCCC',
LightGrey = '#CBD2DB',
DarkGrey = '#515457',
}

0 comments on commit ec2bd3b

Please sign in to comment.