Skip to content

Commit

Permalink
District numbers and outlines (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
nofurtherinformation authored Dec 23, 2024
1 parent eae910e commit a260a8b
Show file tree
Hide file tree
Showing 11 changed files with 731 additions and 32 deletions.
323 changes: 323 additions & 0 deletions app/package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@
"@sentry/nextjs": "^8.26.0",
"@stitches/react": "^1.2.8",
"@tanstack/react-query": "^5.51.11",
"@turf/area": "^7.1.0",
"@turf/bbox": "^7.1.0",
"@turf/bbox-clip": "^7.1.0",
"@turf/center-of-mass": "^7.1.0",
"@turf/dissolve": "^7.1.0",
"@turf/helpers": "^7.1.0",
"@turf/point-on-feature": "^7.1.0",
"@turf/points-within-polygon": "^7.1.0",
"@visx/axis": "^3.12.0",
"@visx/brush": "^3.12.0",
"@visx/gradient": "^3.12.0",
Expand All @@ -30,6 +36,7 @@
"@visx/scale": "^3.12.0",
"@visx/shape": "^3.12.0",
"axios": "^1.7.2",
"comlink": "^4.4.2",
"d3-scale-chromatic": "^3.1.0",
"idb-keyval": "^6.2.1",
"lodash": "^4.17.21",
Expand Down
7 changes: 5 additions & 2 deletions app/src/app/components/sidebar/Layers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default function Layers() {
name="districts"
value={[
visibleLayerIds.includes(BLOCK_LAYER_ID) ? '1' : '',
mapOptions.showZoneNumbers ? '2' : '',
parentsAreBroken && mapOptions.showBrokenDistricts ? '3' : '',
mapOptions.lockPaintedAreas === true ? '4' : '',
mapOptions.higlightUnassigned === true ? 'higlightUnassigned' : '',
Expand All @@ -63,8 +64,10 @@ export default function Layers() {
>
Show painted districts
</CheckboxGroup.Item>
<CheckboxGroup.Item value="2" disabled>
Show numbering for painted districts
<CheckboxGroup.Item value="2" onClick={() => setMapOptions({
showZoneNumbers: !mapOptions.showZoneNumbers
})}>
Show numbering for painted districts <i>(experimental)</i>
</CheckboxGroup.Item>
<CheckboxGroup.Item
value="3"
Expand Down
157 changes: 144 additions & 13 deletions app/src/app/constants/layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import {getBlocksSource} from './sources';
import {DocumentObject} from '../utils/api/apiHandlers';
import {MapStore, useMapStore} from '../store/mapStore';
import {colorScheme} from './colors';
import {throttle} from 'lodash';
import GeometryWorker from '../utils/GeometryWorker';


export const BLOCK_SOURCE_ID = 'blocks';
export const BLOCK_LAYER_ID = 'blocks';
Expand All @@ -20,9 +23,9 @@ export const BLOCK_HOVER_LAYER_ID = `${BLOCK_LAYER_ID}-hover`;
export const BLOCK_HOVER_LAYER_ID_CHILD = `${BLOCK_LAYER_ID_CHILD}-hover`;

export const INTERACTIVE_LAYERS = [BLOCK_HOVER_LAYER_ID, BLOCK_HOVER_LAYER_ID_CHILD];
export const LINE_LAYERS = [BLOCK_LAYER_ID, BLOCK_LAYER_ID_CHILD] as const

export const PARENT_LAYERS = [BLOCK_LAYER_ID, BLOCK_HOVER_LAYER_ID];
export const LINE_LAYERS = [BLOCK_LAYER_ID, BLOCK_LAYER_ID_CHILD] as const;
export const ZONE_LABEL_LAYERS = ['ZONE_OUTLINE', 'ZONE_LABEL', 'ZONE_LABEL_BG'];
export const PARENT_LAYERS = [BLOCK_LAYER_ID, BLOCK_HOVER_LAYER_ID];
export const COUNTY_LAYERS = ['counties_fill', 'counties_boundary','counties_labels']

export const CHILD_LAYERS = [
Expand Down Expand Up @@ -54,10 +57,23 @@ ZONE_ASSIGNMENT_STYLE_DYNAMIC.push('#cecece');
// @ts-ignore
export const ZONE_ASSIGNMENT_STYLE: ExpressionSpecification = ZONE_ASSIGNMENT_STYLE_DYNAMIC;

export const ZONE_LABEL_STYLE_DYNAMIC = colorScheme.reduce(
(val, color, i) => {
val.push(['==', ['get', 'zone'], i + 1], color); // 1-indexed per mapStore.ts
return val;
},
['case'] as any
);
ZONE_LABEL_STYLE_DYNAMIC.push('#cecece');

// cast the above as an ExpressionSpecification
// @ts-ignore
export const ZONE_LABEL_STYLE: ExpressionSpecification = ZONE_LABEL_STYLE_DYNAMIC;

const LAYER_LINE_WIDTHS = {
[BLOCK_LAYER_ID]: 2,
[BLOCK_LAYER_ID_CHILD]: 1
}
[BLOCK_LAYER_ID_CHILD]: 1,
};

export function getLayerFilter(layerId: string, _shatterIds?: MapStore['shatterIds']) {
const shatterIds = _shatterIds || useMapStore.getState().shatterIds;
Expand All @@ -77,7 +93,7 @@ export function getLayerFill(
captiveIds?: Set<string>,
shatterIds?: Set<string>
): DataDrivenPropertyValueSpecification<number> {
const innerFillSpec = ([
const innerFillSpec = [
'case',
// is broken parent
['boolean', ['feature-state', 'broken'], false],
Expand Down Expand Up @@ -119,7 +135,7 @@ export function getLayerFill(
['boolean', ['feature-state', 'hover'], false],
0.6,
0.2,
] as unknown) as DataDrivenPropertyValueSpecification<number>;
] as unknown as DataDrivenPropertyValueSpecification<number>;
if (captiveIds?.size) {
return [
'case',
Expand Down Expand Up @@ -188,12 +204,11 @@ export function getHighlightLayerSpecification(
};
}


export function getBlocksLayerSpecification(
sourceLayer: string,
layerId: typeof LINE_LAYERS[number]
layerId: (typeof LINE_LAYERS)[number]
): LayerSpecification {
const lineWidth = LAYER_LINE_WIDTHS[layerId]
const lineWidth = LAYER_LINE_WIDTHS[layerId];

const layerSpec: LayerSpecification = {
id: layerId,
Expand All @@ -206,8 +221,28 @@ export function getBlocksLayerSpecification(
paint: {
'line-opacity': 0.8,
// 'line-color': '#aaaaaa', // Default color
'line-color': ['interpolate', ['exponential', 1.6], ['zoom'], 6, '#aaa', 9, '#777', 14, '#333'],
'line-width': ['interpolate', ['exponential', 1.6], ['zoom'], 6, lineWidth*.125, 9, lineWidth*.35, 14, lineWidth],
'line-color': [
'interpolate',
['exponential', 1.6],
['zoom'],
6,
'#aaa',
9,
'#777',
14,
'#333',
],
'line-width': [
'interpolate',
['exponential', 1.6],
['zoom'],
6,
lineWidth * 0.125,
9,
lineWidth * 0.35,
14,
lineWidth,
],
},
};
if (CHILD_LAYERS.includes(layerId)) {
Expand Down Expand Up @@ -295,4 +330,100 @@ export function removeBlockLayers(map: Map | null) {
});
}

export {addBlockLayers};
const getDissolved = async () => {
const {getMapRef} = useMapStore.getState();
const mapRef = getMapRef();
if (!mapRef || !GeometryWorker) return;
const currentView = mapRef.getBounds();
const { centroids, dissolved} = await GeometryWorker.getCentroidsFromView(
currentView.getWest(),
currentView.getSouth(),
currentView.getEast(),
currentView.getNorth()
);
return {centroids, dissolved};
};

const removeZoneMetaLayers = () => {
const {getMapRef} = useMapStore.getState();
const mapRef = getMapRef();
if (!mapRef) return;
ZONE_LABEL_LAYERS.forEach(id => {
mapRef.getLayer(id) && mapRef.removeLayer(id);
});
ZONE_LABEL_LAYERS.forEach(id => {
mapRef.getSource(id) && mapRef.removeSource(id);
});
};

const addZoneMetaLayers = async ({
centroids,
dissolved,
}: {
centroids?: GeoJSON.FeatureCollection;
dissolved?: GeoJSON.FeatureCollection;
}) => {
const geoms =
centroids && dissolved
? {
centroids,
dissolved,
}
: await getDissolved();
const {getMapRef} = useMapStore.getState();
const mapRef = getMapRef();
if (!mapRef || !geoms) return;
const zoneLabelSource = mapRef.getSource('ZONE_LABEL');
if (!zoneLabelSource) {
mapRef.addSource('ZONE_LABEL', {
type: 'geojson',
data: geoms.centroids,
});
mapRef.addLayer({
id: 'ZONE_LABEL_BG',
type: 'circle',
source: 'ZONE_LABEL',
paint: {
'circle-color': '#fff',
'circle-radius': 15,
'circle-opacity': 0.8,
'circle-stroke-color': ZONE_LABEL_STYLE || '#000',
'circle-stroke-width': 2,
},

filter: ['==', ['get', 'zone'], ['get', 'zone']],
});
mapRef.addLayer({
id: 'ZONE_LABEL',
type: 'symbol',
source: 'ZONE_LABEL',
layout: {
'text-field': ['get', 'zone'],
'text-font': ['Barlow Bold'],
'text-size': 18,
'text-anchor': 'center',
'text-offset': [0, 0],
},
paint: {
'text-color': '#000',
},
});
} else {
// @ts-ignore behavior is correct, typing on `source` is wrong
zoneLabelSource.setData(geoms.centroids);
}
};

const debouncedAddZoneMetaLayers = throttle(
addZoneMetaLayers,
1000,
{ leading: true, trailing: true }
);

export {
addBlockLayers,
removeZoneMetaLayers,
addZoneMetaLayers,
getDissolved,
debouncedAddZoneMetaLayers,
};
15 changes: 15 additions & 0 deletions app/src/app/store/mapRenderSubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
BLOCK_LAYER_ID_HIGHLIGHT,
getHighlightLayerSpecification,
BLOCK_LAYER_ID_HIGHLIGHT_CHILD,
removeZoneMetaLayers,
debouncedAddZoneMetaLayers,
COUNTY_LAYERS,
} from '../constants/layers';
import {
Expand All @@ -22,6 +24,7 @@ import {
} from '../utils/helpers';
import {useMapStore as _useMapStore, MapStore} from '@store/mapStore';
import {getFeatureUnderCursor} from '@utils/helpers';
import GeometryWorker from '../utils/GeometryWorker';
import { useHoverStore as _useHoverStore } from '@store/mapStore';

const BBOX_TOLERANCE_DEG = 0.02;
Expand Down Expand Up @@ -89,17 +92,29 @@ export const getRenderSubscriptions = (useMapStore: typeof _useMapStore, useHove
state.appLoadingState,
state.mapRenderingState,
state.mapOptions.lockPaintedAreas,
state.mapOptions.showZoneNumbers
],
(curr, prev) => {
colorZoneAssignments(curr, prev);

const {
captiveIds,
shatterIds,
getMapRef,
setLockedFeatures,
lockedFeatures,
mapRenderingState,
mapOptions
} = useMapStore.getState();
if (mapOptions.showZoneNumbers){
GeometryWorker?.updateProps(
Array.from(curr[0].entries())
).then(() => {
debouncedAddZoneMetaLayers({})
})
} else {
removeZoneMetaLayers()
}
const mapRef = getMapRef();
if (!mapRef || mapRenderingState !== 'loaded') return;
[...PARENT_LAYERS, ...CHILD_LAYERS].forEach(layerId => {
Expand Down
1 change: 1 addition & 0 deletions app/src/app/store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type DistrictrMapOptions = {
higlightUnassigned?: boolean;
lockPaintedAreas: boolean | Array<NullableZone>;
mode: 'default' | 'break';
showZoneNumbers?: boolean
paintByCounty?: boolean;
currentStateFp?: string;
showPopulationTooltip?: boolean;
Expand Down
Loading

0 comments on commit a260a8b

Please sign in to comment.