Skip to content

Commit

Permalink
Merge pull request #302 from visualize-admin/feat/maps-polishing
Browse files Browse the repository at this point in the history
Polishing the maps
  • Loading branch information
bprusinowski authored Feb 1, 2022
2 parents 2cedc34 + 7733eb1 commit ba4b7c5
Show file tree
Hide file tree
Showing 30 changed files with 1,454 additions and 1,099 deletions.
4 changes: 2 additions & 2 deletions app/charts/chart-config-ui-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
encodings: [
{
field: "baseLayer",
optional: true,
optional: false,
values: [],
filters: false,
},
Expand All @@ -289,7 +289,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
},
{
field: "symbolLayer",
optional: true,
optional: false,
values: ["Measure"],
filters: false,
},
Expand Down
62 changes: 53 additions & 9 deletions app/charts/map/chart-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import {
import {
DimensionMetaDataFragment,
useDataCubeObservationsQuery,
useGeoCoordinatesByDimensionIriQuery,
useGeoShapesByDimensionIriQuery,
} from "../../graphql/query-hooks";
import { useLocale } from "../../locales/use-locale";
import { GeoCoordinates } from "../../rdf/query-geo-coordinates";
import { QueryFilters } from "../shared/chart-helpers";
import { ChartContainer } from "../shared/containers";
import { MapComponent } from "./map";
Expand Down Expand Up @@ -55,7 +56,10 @@ export const ChartMapVisualization = ({
variables: {
locale,
iri: dataSetIri,
measures: [areaDimensionIri, symbolDimensionIri],
measures: [
chartConfig.fields.areaLayer.measureIri,
chartConfig.fields.symbolLayer.measureIri,
],
filters: queryFilters,
},
});
Expand All @@ -66,11 +70,40 @@ export const ChartMapVisualization = ({
| Observation[]
| undefined;

const [{ data: fetchedGeoCoordinates }] =
useGeoCoordinatesByDimensionIriQuery({
variables: {
dataCubeIri: dataSetIri,
dimensionIri: symbolDimensionIri,
locale,
},
});

const geoCoordinates =
fetchedGeoCoordinates?.dataCubeByIri?.dimensionByIri?.__typename ===
"GeoCoordinatesDimension"
? fetchedGeoCoordinates.dataCubeByIri.dimensionByIri.geoCoordinates
: undefined;

const [{ data: fetchedGeoShapes }] = useGeoShapesByDimensionIriQuery({
variables: {
dataCubeIri: dataSetIri,
dimensionIri: areaDimensionIri,
locale,
},
});

const geoShapes =
fetchedGeoShapes?.dataCubeByIri?.dimensionByIri?.__typename ===
"GeoShapesDimension"
? (fetchedGeoShapes.dataCubeByIri.dimensionByIri.geoShapes as GeoShapes)
: undefined;

const areaLayer: AreaLayer | undefined = useMemo(() => {
const dimension = dimensions?.find((d) => d.iri === areaDimensionIri);

if (isGeoShapesDimension(dimension) && observations) {
const { topology } = dimension.geoShapes as GeoShapes;
if (isGeoShapesDimension(dimension) && geoShapes && observations) {
const { topology } = geoShapes;

const topojson = topojsonFeature(
topology,
Expand All @@ -91,20 +124,24 @@ export const ChartMapVisualization = ({
mesh: topojsonMesh(topology, topology.objects.shapes),
};
}
}, [areaDimensionIri, dimensions, observations]);
}, [areaDimensionIri, dimensions, observations, geoShapes]);

const symbolLayer: SymbolLayer | undefined = useMemo(() => {
const dimension = dimensions?.find((d) => d.iri === symbolDimensionIri);

if (isGeoCoordinatesDimension(dimension)) {
const points = (dimension.geoCoordinates as GeoCoordinates[]).map(
if (
isGeoCoordinatesDimension(dimension) &&
geoCoordinates &&
observations
) {
const points = geoCoordinates.map(
(d) =>
({
coordinates: [d.longitude, d.latitude],
properties: {
iri: d.iri,
label: d.label,
observation: observations?.find(
observation: observations.find(
(o) => o[symbolDimensionIri] === d.label
),
},
Expand All @@ -122,7 +159,7 @@ export const ChartMapVisualization = ({
return { points };
}
}
}, [areaLayer, dimensions, observations, symbolDimensionIri]);
}, [areaLayer, dimensions, observations, symbolDimensionIri, geoCoordinates]);

useEffect(() => {
const loadLakes = async () => {
Expand Down Expand Up @@ -152,6 +189,7 @@ export const ChartMapVisualization = ({
measures={measures}
dimensions={dimensions}
baseLayer={chartConfig.baseLayer}
geoShapes={geoShapes}
/>
);
} else if (geoData.state === "fetching" || fetching) {
Expand All @@ -170,13 +208,15 @@ export const ChartMapPrototype = ({
measures,
dimensions,
baseLayer,
geoShapes,
}: {
observations: Observation[];
features: GeoData;
fields: MapFields;
measures: DimensionMetaDataFragment[];
dimensions: DimensionMetaDataFragment[];
baseLayer: BaseLayer;
geoShapes?: GeoShapes;
}) => {
return (
<Box sx={{ m: 4, bg: "#fff" }}>
Expand All @@ -187,6 +227,7 @@ export const ChartMapPrototype = ({
measures={measures}
dimensions={dimensions}
baseLayer={baseLayer}
geoShapes={geoShapes}
/>
</Box>
);
Expand All @@ -200,13 +241,15 @@ export const ChartMap = memo(
measures,
dimensions,
baseLayer,
geoShapes,
}: {
features: GeoData;
observations: Observation[];
measures: DimensionMetaDataFragment[];
dimensions: DimensionMetaDataFragment[];
fields: MapFields;
baseLayer: BaseLayer;
geoShapes?: GeoShapes;
}) => {
return (
<MapChart
Expand All @@ -216,6 +259,7 @@ export const ChartMap = memo(
measures={measures}
dimensions={dimensions}
baseLayer={baseLayer}
geoShapes={geoShapes}
>
<ChartContainer>
<MapComponent />
Expand Down
9 changes: 1 addition & 8 deletions app/charts/map/map-legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,7 @@ export const MapLegend = () => {
const { areaLayer, symbolLayer } = useChartState() as MapState;

return (
<Flex
sx={{
minHeight: 100,
borderTop: "1px solid",
borderTopColor: "monochrome200",
flexWrap: "wrap",
}}
>
<Flex sx={{ minHeight: 100, flexWrap: "wrap" }}>
{areaLayer.show && (
<Box sx={{ p: 4 }}>
{areaLayer.measureLabel && (
Expand Down
16 changes: 12 additions & 4 deletions app/charts/map/map-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import {
GeoData,
GeoFeature,
GeoShapes,
isGeoShapesDimension,
Observation,
} from "../../domain/data";
Expand Down Expand Up @@ -142,10 +143,12 @@ const useMapState = ({
measures,
dimensions,
baseLayer,
geoShapes,
}: Pick<ChartProps, "data" | "measures" | "dimensions"> & {
features: GeoData;
fields: MapFields;
baseLayer: BaseLayer;
geoShapes?: GeoShapes;
}): MapState => {
const width = useWidth();
const { areaLayer, symbolLayer } = fields;
Expand All @@ -169,10 +172,9 @@ const useMapState = ({
const dimension = dimensions.find((d) => d.iri === geoDimensionIri);

// Right now hierarchies are only created for geoShapes
if (isGeoShapesDimension(dimension)) {
if (isGeoShapesDimension(dimension) && geoShapes) {
const hierarchyLabels = (
(dimension.geoShapes as any).topology.objects.shapes
.geometries as GeoFeature[]
(geoShapes as any).topology.objects.shapes.geometries as GeoFeature[]
)
.filter((d) => d.properties.hierarchyLevel === hierarchyLevel)
.map((d) => d.properties.label);
Expand All @@ -182,7 +184,7 @@ const useMapState = ({

return data;
},
[data, dimensions]
[data, dimensions, geoShapes]
);

const areaData = useMemo(
Expand Down Expand Up @@ -313,12 +315,14 @@ const MapChartProvider = ({
measures,
dimensions,
baseLayer,
geoShapes,
children,
}: Pick<ChartProps, "data" | "measures" | "dimensions"> & {
features: GeoData;
children: ReactNode;
fields: MapFields;
baseLayer: BaseLayer;
geoShapes?: GeoShapes;
}) => {
const state = useMapState({
data,
Expand All @@ -327,6 +331,7 @@ const MapChartProvider = ({
measures,
dimensions,
baseLayer,
geoShapes,
});
return (
<ChartContext.Provider value={state}>{children}</ChartContext.Provider>
Expand All @@ -340,11 +345,13 @@ export const MapChart = ({
measures,
dimensions,
baseLayer,
geoShapes,
children,
}: Pick<ChartProps, "data" | "measures" | "dimensions"> & {
features: GeoData;
fields: MapFields;
baseLayer: BaseLayer;
geoShapes?: GeoShapes;
children: ReactNode;
}) => {
return (
Expand All @@ -357,6 +364,7 @@ export const MapChart = ({
fields={fields}
measures={measures}
dimensions={dimensions}
geoShapes={geoShapes}
baseLayer={baseLayer}
>
{children}
Expand Down
75 changes: 43 additions & 32 deletions app/charts/map/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ const INITIAL_VIEW_STATE = {
type BBox = [[number, number], [number, number]];

const CH_BBOX: BBox = [
[5.956800664952974, 45.81912371940225],
[10.493446773955753, 47.80741209797084],
[6.02260949059, 45.7769477403],
[10.4427014502, 47.8308275417],
];

/**
Expand All @@ -55,35 +55,40 @@ const constrainZoom = (

const { width, height, zoom, longitude, latitude } = viewState;

const [x, y] = vp.project([longitude, latitude]);
const [x0, y1] = vp.project(bbox[0]);
const [x1, y0] = vp.project(bbox[1]);
// Make sure the map is rendered before trying to project & fitBounds
if (vp.width > 1 && vp.height > 1) {
const [x, y] = vp.project([longitude, latitude]);
const [x0, y1] = vp.project(bbox[0]);
const [x1, y0] = vp.project(bbox[1]);

const fitted = vp.fitBounds(bbox, { padding });
const fitted = vp.fitBounds(bbox, { padding });

const [cx, cy] = vp.project([fitted.longitude, fitted.latitude]);
const [cx, cy] = vp.project([fitted.longitude, fitted.latitude]);

const h = height - padding * 2;
const w = width - padding * 2;
const h = height - padding * 2;
const w = width - padding * 2;

const h2 = h / 2;
const w2 = w / 2;
const h2 = h / 2;
const w2 = w / 2;

const y2 =
y1 - y0 < h ? cy : y - h2 < y0 ? y0 + h2 : y + h2 > y1 ? y1 - h2 : y;
const x2 =
x1 - x0 < w ? cx : x - w2 < x0 ? x0 + w2 : x + w2 > x1 ? x1 - w2 : x;
const y2 =
y1 - y0 < h ? cy : y - h2 < y0 ? y0 + h2 : y + h2 > y1 ? y1 - h2 : y;
const x2 =
x1 - x0 < w ? cx : x - w2 < x0 ? x0 + w2 : x + w2 > x1 ? x1 - w2 : x;

const p = vp.unproject([x2, y2]);
const p = vp.unproject([x2, y2]);

return {
...viewState,
transitionDuration: 0,
transitionInterpolator: null,
zoom: Math.max(zoom, fitted.zoom),
longitude: p[0],
latitude: p[1],
};
return {
...viewState,
transitionDuration: 0,
transitionInterpolator: null,
zoom: Math.max(zoom, fitted.zoom),
longitude: p[0],
latitude: p[1],
};
} else {
return viewState;
}
};

export const MapComponent = () => {
Expand Down Expand Up @@ -181,7 +186,7 @@ export const MapComponent = () => {
pickable={false}
minZoom={2}
maxZoom={16}
tileSize={256}
maxCacheSize={512}
renderSubLayers={(props: { tile: TileData; data: $FixMe }) => {
const {
bbox: { west, south, east, north },
Expand Down Expand Up @@ -237,13 +242,19 @@ export const MapComponent = () => {
updateTriggers={{
getFillColor: [areaLayer.getValue, areaLayer.getColor],
}}
getFillColor={(d: GeoFeature) =>
d.properties.observation
? areaLayer.getColor(
areaLayer.getValue(d.properties.observation)
)
: [33, 33, 33, 33]
}
getFillColor={(d: GeoFeature) => {
const { observation } = d.properties;

if (observation) {
const value = areaLayer.getValue(observation);

if (value) {
return areaLayer.getColor(value);
}
}

return [222, 222, 222, 255];
}}
/>
<GeoJsonLayer
id="shapes-mesh"
Expand Down
Loading

0 comments on commit ba4b7c5

Please sign in to comment.