Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swiss municipalities mutations audit #341

Merged
merged 11 commits into from
Sep 9, 2024
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
ptbrowne marked this conversation as resolved.
Show resolved Hide resolved
? `${geoData?.cantons?.features?.length}`
ptbrowne marked this conversation as resolved.
Show resolved Hide resolved
: "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