diff --git a/packages/graphic-walker/src/components/leafletRenderer/ChoroplethRenderer.tsx b/packages/graphic-walker/src/components/leafletRenderer/ChoroplethRenderer.tsx index bc936bb5..66e0767a 100644 --- a/packages/graphic-walker/src/components/leafletRenderer/ChoroplethRenderer.tsx +++ b/packages/graphic-walker/src/components/leafletRenderer/ChoroplethRenderer.tsx @@ -1,4 +1,4 @@ -import React, { Fragment, forwardRef, useEffect, useMemo, useRef } from "react"; +import React, { Fragment, forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from "react"; import { CircleMarker, MapContainer, Polygon, Marker, TileLayer, Tooltip } from "react-leaflet"; import { type Map, divIcon } from "leaflet"; import type { DeepReadonly, IRow, IViewField, VegaGlobalConfig } from "../../interfaces"; @@ -21,6 +21,7 @@ export interface IChoroplethRendererProps { text: DeepReadonly | undefined; details: readonly DeepReadonly[]; vegaConfig: VegaGlobalConfig; + scaleIncludeUnmatchedChoropleth: boolean; } export interface IChoroplethRendererRef {} @@ -85,7 +86,9 @@ const resolveCenter = (coordinates: [lat: number, lng: number][]): [lng: number, }; const ChoroplethRenderer = forwardRef(function ChoroplethRenderer (props, ref) { - const { data, allFields, features, geoKey, defaultAggregated, geoId, color, opacity, text, details, vegaConfig } = props; + const { data, allFields, features, geoKey, defaultAggregated, geoId, color, opacity, text, details, vegaConfig, scaleIncludeUnmatchedChoropleth } = props; + + useImperativeHandle(ref, () => ({})); const geoIndices = useMemo(() => { if (geoId) { @@ -94,14 +97,19 @@ const ChoroplethRenderer = forwardRef { + const [indices, geoShapes] = useMemo<[indices: number[], geoShapes: (FeatureCollection['features'][number] | undefined)[]]>(() => { if (geoIndices.length && geoKey && features) { - return geoIndices.map(id => { + const indices: number[] = []; + const shapes = geoIndices.map((id, i) => { const feature = id ? features.features.find(f => f.properties?.[geoKey] === id) : undefined; + if (feature) { + indices.push(i); + } return feature; }); + return [indices, shapes]; } - return []; + return [[], []]; }, [geoIndices, features, geoKey]); useEffect(() => { @@ -149,8 +157,15 @@ const ChoroplethRenderer = forwardRef { + if (scaleIncludeUnmatchedChoropleth) { + return data; + } + return indices.map(i => data[i]); + }, [data, indices, scaleIncludeUnmatchedChoropleth]); + + const opacityScale = useOpacityScale(distribution, opacity, defaultAggregated); + const colorScale = useColorScale(distribution, color, defaultAggregated, vegaConfig); const tooltipFields = useMemo(() => { return details.concat( diff --git a/packages/graphic-walker/src/components/leafletRenderer/index.tsx b/packages/graphic-walker/src/components/leafletRenderer/index.tsx index 09ec0411..2bb4ba78 100644 --- a/packages/graphic-walker/src/components/leafletRenderer/index.tsx +++ b/packages/graphic-walker/src/components/leafletRenderer/index.tsx @@ -19,7 +19,7 @@ export const LEAFLET_DEFAULT_HEIGHT = 600; const LeafletRenderer = forwardRef(function LeafletRenderer (props, ref) { const { draggableFieldState, data, visualConfig, vegaConfig = {} } = props; const { latitude: [lat], longitude: [lng], geoId: [geoId], dimensions, measures, size: [size], color: [color], opacity: [opacity], text: [text], details } = draggableFieldState; - const { defaultAggregated, geoms: [markType], geojson, geoKey = '' } = visualConfig; + const { defaultAggregated, geoms: [markType], geojson, geoKey = '', scaleIncludeUnmatchedChoropleth = false } = visualConfig; const allFields = useMemo(() => [...dimensions, ...measures], [dimensions, measures]); const latField = useMemo(() => allFields.find((f) => f.geoRole === 'latitude'), [allFields]); const lngField = useMemo(() => allFields.find((f) => f.geoRole === 'longitude'), [allFields]); @@ -55,6 +55,7 @@ const LeafletRenderer = forwardRef(f text={text} details={details} vegaConfig={vegaConfig} + scaleIncludeUnmatchedChoropleth={scaleIncludeUnmatchedChoropleth} /> ); } diff --git a/packages/graphic-walker/src/components/visualConfig/index.tsx b/packages/graphic-walker/src/components/visualConfig/index.tsx index b24ac553..9402bd89 100644 --- a/packages/graphic-walker/src/components/visualConfig/index.tsx +++ b/packages/graphic-walker/src/components/visualConfig/index.tsx @@ -13,6 +13,8 @@ const VisualConfigPanel: React.FC = (props) => { const { commonStore, vizStore } = useGlobalStore(); const { showVisualConfigPanel } = commonStore; const { visualConfig } = vizStore; + const { coordSystem, geoms: [markType] } = visualConfig; + const isChoropleth = coordSystem === 'geographic' && markType === 'choropleth'; const { t } = useTranslation(); const formatConfigList: (keyof IVisualConfig['format'])[] = [ 'numberFormat', @@ -25,6 +27,7 @@ const VisualConfigPanel: React.FC = (props) => { normalizedNumberFormat: visualConfig.format.normalizedNumberFormat, }); const [zeroScale, setZeroScale] = useState(visualConfig.zeroScale); + const [scaleIncludeUnmatchedChoropleth, setScaleIncludeUnmatchedChoropleth] = useState(visualConfig.scaleIncludeUnmatchedChoropleth ?? false); return ( { }} /> + {isChoropleth && ( +
+ { + setScaleIncludeUnmatchedChoropleth(en); + }} + /> +
+ )}
{ runInAction(() => { vizStore.setVisualConfig('format', format); vizStore.setVisualConfig('zeroScale', zeroScale); + vizStore.setVisualConfig('scaleIncludeUnmatchedChoropleth', scaleIncludeUnmatchedChoropleth); commonStore.setShowVisualConfigPanel(false); }) }} diff --git a/packages/graphic-walker/src/interfaces.ts b/packages/graphic-walker/src/interfaces.ts index 0419e379..22af0504 100644 --- a/packages/graphic-walker/src/interfaces.ts +++ b/packages/graphic-walker/src/interfaces.ts @@ -208,6 +208,8 @@ export interface IVisualConfig { interactiveScale: boolean; sorted: 'none' | 'ascending' | 'descending'; zeroScale: boolean; + /** @default false */ + scaleIncludeUnmatchedChoropleth?: boolean; format: { numberFormat?: string; timeFormat?: string; diff --git a/packages/graphic-walker/src/store/visualSpecStore.ts b/packages/graphic-walker/src/store/visualSpecStore.ts index 96c861f2..ee49e2bb 100644 --- a/packages/graphic-walker/src/store/visualSpecStore.ts +++ b/packages/graphic-walker/src/store/visualSpecStore.ts @@ -77,6 +77,7 @@ export function initVisualConfig(): IVisualConfig { interactiveScale: false, sorted: "none", zeroScale: true, + scaleIncludeUnmatchedChoropleth: false, size: { mode: "auto", width: 320, @@ -373,7 +374,7 @@ export class VizSpecStore { public setVisualConfig(configKey: K, value: IVisualConfig[K]) { this.useMutable(({ config }) => { switch (true) { - case ["defaultAggregated", "defaultStack", "showActions", "interactiveScale"].includes(configKey): { + case ["defaultAggregated", "defaultStack", "showActions", "interactiveScale", "scaleIncludeUnmatchedChoropleth"].includes(configKey): { return ((config as unknown as { [k: string]: boolean })[configKey] = Boolean(value)); } case configKey === "geoms" && Array.isArray(value):