Skip to content

Commit

Permalink
Merge pull request #341 from interactivethings/mutations
Browse files Browse the repository at this point in the history
Swiss municipalities mutations audit
  • Loading branch information
ptbrowne authored Sep 9, 2024
2 parents ebf2495 + c39b6d9 commit 7988312
Show file tree
Hide file tree
Showing 13 changed files with 2,543 additions and 87 deletions.
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"tabWidth": 2,
"useTabs": false
}
3 changes: 2 additions & 1 deletion website/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ declare module "@deck.gl/layers" {
export const GeoJsonLayer: $FixMe;
export const LineLayer: $FixMe;
export const PathLayer: $FixMe;
export const ScatterplotLayer: $FixMe;
}

declare module "@deck.gl/react" {
export const DeckGL: $FixMe;
export default DeckGL;
}
}
6 changes: 5 additions & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
"@deck.gl/layers": "^8.9.4",
"@deck.gl/react": "^8.9.4",
"@material-ui/core": "^4.12.4",
"@turf/turf": "^7.1.0",
"@types/cors": "^2.8.17",
"@types/d3": "^7.4.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^20.11.30",
"@types/react": "^18.2.67",
"@types/topojson": "^3.2.6",
Expand All @@ -22,6 +24,7 @@
"fp-ts": "^2.16.0",
"immer": "^9.0.21",
"io-ts": "^2.2.20",
"jsdom": "^25.0.0",
"jss": "^10.10.0",
"mapshaper": "^0.6.25",
"next": "^13.2.4",
Expand All @@ -32,7 +35,8 @@
"swiss-maps": "^4.5.0",
"topojson": "^3.0.2",
"typescript": "^5.4.3",
"use-immer": "^0.10.0"
"use-immer": "^0.10.0",
"zod": "^3.23.8"
},
"devDependencies": {}
}
Binary file added website/public/screenshot-mutations.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions website/src/components/Examples/Examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ function Examples(props: Props, ref: any) {
/>
</MUI.CardMedia>
</MUI.Card>
<MUI.Card className={classes.card} elevation={4}>
<img
src="/screenshot-mutations.jpg"
width="100%"
alt="Screenshot of mutations tool"
/>
<MUI.Box className={classes.cardFooter}>
<MUI.Typography className={classes.cardAuthor}>
InteractiveThings
</MUI.Typography>
<MUI.Link className={classes.cardTitle} href="/mutations">
Municipalities change audit
</MUI.Link>
</MUI.Box>
</MUI.Card>
</div>
</Root>
);
Expand All @@ -80,6 +95,30 @@ const useStyles = MUI.makeStyles(
card: {
width: 424,
},

cardFooter: {
backgroundColor: "white",
borderTop: "1px solid #f0f0f0",
padding: theme.spacing(1, 2),
color: "black",
},

cardAuthor: {
fontSize: "13px",
fontWeight: "bold",
display: "inline",
marginRight: "0.375rem",
},

cardTitle: {
fontSize: "13px",
display: "inline",
fontWeight: "normal",

"&:active, &:visited": {
color: "black",
},
},
}),
{ name: "XuiExamples" }
);
Expand Down
116 changes: 35 additions & 81 deletions website/src/components/Generator/internal/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ import DeckGL from "@deck.gl/react";
import * as MUI from "@material-ui/core";
import clsx from "clsx";
import * as d3 from "d3";
import cityData from "public/swiss-city-topo.json";
import * as React from "react";
import { useQuery } from "react-query";
import { COLOR_SCHEMA_MAP } from "src/domain/color-schema";
import { previewSourceUrl } from "src/shared";
import * as topojson from "topojson";
import { useImmer } from "use-immer";
import { useContext } from "../context";
import { useContext, Value } from "../context";
import { CH_BBOX, constrainZoom, LINE_COLOR } from "../domain/deck-gl";
import { useGeoData } from "src/domain/geodata";

interface Props {}

Expand All @@ -33,58 +30,11 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
const { options } = ctx.state;

const [state, mutate] = useImmer({
fetching: false,
viewState: INITIAL_VIEW_STATE,
geoData: {
country: undefined as any,
cantons: undefined as any,
neighbors: undefined as Array<number[]> | undefined,
municipalities: undefined as any,
lakes: undefined as any,
city: undefined as any,
},
});

const { data: json, isFetching } = useQuery(
["preview", options.year, options.simplify, ...options.shapes],
() => fetch(previewSourceUrl(options, "v0")).then((res) => res.json())
);

React.useEffect(() => {
if (!json) {
return;
}
mutate((draft) => {
if (cityData) {
draft.geoData.city = topojson.feature(
cityData as any,
cityData.objects["swiss-city"] as any
);
}

if (json.objects?.country) {
draft.geoData.country = topojson.feature(json, json.objects.country);
}

if (json.objects?.cantons) {
draft.geoData.cantons = topojson.feature(json, json.objects.cantons);
draft.geoData.neighbors = topojson.neighbors(
json.objects.cantons.geometries
);
}

if (json.objects?.municipalities) {
draft.geoData.municipalities = topojson.feature(
json,
json.objects.municipalities
);
}

if (json.objects?.lakes) {
draft.geoData.lakes = topojson.feature(json, json.objects.lakes);
}
});
}, [json]);
const query = useGeoData(options);
const { data: geoData, isFetching } = query;

/*
const onViewStateChange = React.useCallback(
Expand Down Expand Up @@ -118,11 +68,11 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
* See https://observablehq.com/@mbostock/map-coloring
* */
const colorIndex = (() => {
const { cantons, neighbors } = state.geoData;
const { cantons, neighbors } = geoData;
if (!neighbors) {
return undefined;
}
const index = new Int32Array(cantons.features.length);
const index = new Int32Array(cantons ? cantons.features.length : 0);
for (let i = 0; i < index.length; ++i) {
index[i] = ((d3.max(neighbors[i], (j) => index[j]) as number) + 1) | 0;
}
Expand All @@ -139,10 +89,15 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
// domain is decided by coloring item size
// currently only support cantons
// if not exist, a random number 30 is assigned
.domain(["1", state.geoData?.cantons?.length ?? "30"])
.domain([
"1",
geoData.cantons?.features?.length
? `${geoData.cantons.features.length}`
: "30",
])
.range(color)
);
}, [options.color, state.geoData.cantons]);
}, [options.color, geoData.cantons]);

return (
<div className={clsx(classes.root)}>
Expand All @@ -165,7 +120,7 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
{options.shapes.has("country") && (
<GeoJsonLayer
id="country"
data={state.geoData?.country}
data={geoData?.country}
pickable={false}
stroked={true}
filled={false}
Expand All @@ -181,7 +136,7 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
{options.shapes.has("cantons") && (
<GeoJsonLayer
id="cantons"
data={state.geoData.cantons}
data={geoData.cantons}
pickable={false}
stroked={true}
filled={true}
Expand All @@ -204,28 +159,27 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
/>
)}

{state.geoData.municipalities &&
options.shapes.has("municipalities") && (
<GeoJsonLayer
id="municipalities"
data={state.geoData.municipalities}
pickable={false}
stroked={true}
filled={false}
getFillColor={[230, 230, 230]}
extruded={false}
lineWidthMinPixels={0.5}
lineWidthMaxPixels={1}
getLineWidth={200}
lineMiterLimit={1}
getLineColor={LINE_COLOR}
/>
)}
{geoData.municipalities && options.shapes.has("municipalities") && (
<GeoJsonLayer
id="municipalities"
data={geoData.municipalities}
pickable={false}
stroked={true}
filled={false}
getFillColor={[230, 230, 230]}
extruded={false}
lineWidthMinPixels={0.5}
lineWidthMaxPixels={1}
getLineWidth={200}
lineMiterLimit={1}
getLineColor={[255, 0, 0, 255]}
/>
)}

{options.shapes.has("lakes") && (
<GeoJsonLayer
id="lakes"
data={state.geoData.lakes}
data={geoData.lakes}
pickable={false}
stroked={true}
filled={true}
Expand All @@ -242,7 +196,7 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
{options.withName && (
<GeoJsonLayer
id="city"
data={state.geoData?.city}
data={geoData?.city}
pickable={false}
stroked={true}
filled={false}
Expand All @@ -265,8 +219,8 @@ export const Preview = React.forwardRef(({}: Props, deckRef: any) => {
{ctx.state.highlightedShape &&
options.shapes.has(ctx.state.highlightedShape) &&
(() => {
const data = state.geoData[
ctx.state.highlightedShape as keyof typeof state.geoData
const data = geoData[
ctx.state.highlightedShape as keyof typeof geoData
] as $FixMe;

if (ctx.state.highlightedShape === "lakes") {
Expand Down
62 changes: 62 additions & 0 deletions website/src/components/Mutations/Map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { GeoJsonLayer } from "@deck.gl/layers";
import DeckGL from "@deck.gl/react";
import React, { ComponentProps } from "react";
import { useGeoData } from "src/domain/geodata";
import { MapController } from "@deck.gl/core";

export const LINE_COLOR = [100, 100, 100, 127] as const;

const MutationsMap = ({
highlightedMunicipalities,
geoData,
...props
}: {
highlightedMunicipalities: {
added: number[];
removed: number[];
};
geoData: ReturnType<typeof useGeoData>["data"];
} & ComponentProps<typeof DeckGL>) => {
return (
<DeckGL
controller={MapController}
getTooltip={({
object,
}: {
object: { properties: { name: string } };
}) => {
if (!object) {
return;
}
return "Municipality: " + object.properties.name;
}}
{...props}
>
{geoData.municipalities && (
<GeoJsonLayer
id="municipalities"
data={geoData.municipalities}
pickable={true}
stroked={true}
filled={true}
extruded={false}
lineWidthMinPixels={0.5}
lineWidthMaxPixels={1}
getLineWidth={200}
lineMiterLimit={1}
updateTriggers={{ getFillColor: highlightedMunicipalities }}
getLineColor={[30, 30, 30]}
getFillColor={(d: { properties: { id: number } }) => {
return highlightedMunicipalities.added.includes(d.properties.id)
? [0, 255, 0, 100]
: highlightedMunicipalities.removed.includes(d.properties.id)
? [255, 0, 0, 100]
: [255, 255, 255];
}}
/>
)}
</DeckGL>
);
};

export default MutationsMap;
Loading

0 comments on commit 7988312

Please sign in to comment.