Third example of a map drawn with Svelte and the d3 projections. Transitions are made now when the mouse is over a region and when the projection is changed.
Check this blog post from Geoexamples for more explanations.
To test it, clone the standard svelte template by
npx degit sveltejs/template svelte-app cd svelte-app
And copy the App.svelte and Feature.svelte files into the src directory.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="stylesheet" href="global.css" />
<link rel="stylesheet" href="bundle.css" />
<script defer src="bundle.js"></script>
</head>
<body></body>
</html>
<script>
import { geoAlbers, geoPath, geoProjection } from "d3-geo";
import { geoAlbersUk } from "d3-composite-projections";
import { scaleLinear } from "d3-scale";
import { extent } from "d3-array";
import { onMount } from "svelte";
import { feature } from "topojson";
import { tweened } from "svelte/motion";
import { interpolate } from "d3-interpolate";
import Feature from "./Feature.svelte";
let data = [];
let colorScale = () => {};
const width = "960";
const height = "500";
const projectionAlbers = geoAlbers()
.rotate([4.4, 0.8])
.center([0, 55.4])
.parallels([50, 60])
.scale(3800)
.translate([width / 2, (1.8 * height) / 2]);
const projectionAlbersUk = geoAlbersUk()
.translate([width / 2, (1.85 * height) / 2])
.scale(5200);
const projectionTween = (projection0, projection1) => {
return function(t) {
function project(λ, φ) {
(λ *= 180 / Math.PI), (φ *= 180 / Math.PI);
var p0 = projection0([λ, φ]),
p1 = projection1([λ, φ]);
if (!p0 || !p1) return [0, 0];
return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]];
}
return geoProjection(project)
.scale(1)
.translate([0, 0]);
};
};
const currentProj = tweened(projectionAlbers, {
duration: 1000,
interpolate: projectionTween
});
$: path = geoPath().projection($currentProj);
const opacity = tweened(0, {
duration: 1000
});
onMount(async function() {
const response = await fetch(
"https://gist.githubusercontent.com/rveciana/27272a581e975835aaa321ddf816d726/raw/c40062a328843322208b8e98c2104dc8f6ad5301/uk-counties.json"
);
const json = await response.json();
const topoData = feature(json, json.objects.UK);
const land = {
...topoData,
features: topoData.features.filter(
d => d.properties.NAME_1 === "Scotland"
)
};
const namesExtent = extent(land.features, d => d.properties.NAME_2.length);
colorScale = scaleLinear()
.domain(namesExtent)
.range(["#feedde", "#fd8d3c"]);
data = land.features;
});
</script>
<style>
svg {
width: 960px;
height: 500px;
background-color: "#eeeeee";
}
.borders {
fill: #ddd;
}
</style>
<button
on:click={() => {
currentProj.set($currentProj === projectionAlbers ? projectionAlbersUk : projectionAlbers);
opacity.set($currentProj === projectionAlbers ? 1 : 0);
}}>
Change
</button>
<svg width="960" height="500">
<path
class="borders"
d={projectionAlbersUk.getCompositionBorders()}
style="opacity: {$opacity}" />
{#each data as feature}
<Feature
featurePath={path(feature)}
initialColor={colorScale(feature.properties.NAME_2.length)} />
{/each}
</svg>
<script>
import { tweened } from "svelte/motion";
import { interpolateLab } from "d3-interpolate";
import { rgb } from "d3-color";
export let featurePath;
export let initialColor;
const color = tweened(initialColor, {
duration: 300,
interpolate: interpolateLab
});
</script>
<style>
.provinceShape {
stroke: #444444;
stroke-width: 0.5;
}
</style>
<path
d={featurePath}
class="provinceShape"
fill={$color}
on:mouseover={() => {
color.set(rgb(initialColor).brighter(0.3));
}}
on:mouseout={() => {
color.set(initialColor);
}} />
svg.svelte-1lltho4{width:960px;height:500px;background-color:"#eeeeee"}.borders.svelte-1lltho4{fill:#ddd}
.provinceShape.svelte-116txg3{stroke:#444444;stroke-width:0.5}
/*# sourceMappingURL=bundle.css.map */