1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { countryData } from "https://esm.town/v/jdan/countryData";
import { blob } from "https://esm.town/v/std/blob?v=10";
export async function svgMapOfCountry(country: string) {
const mapUrl = (await countryData())
.find((value) => value.countryLabel === country)
.mapLabel
// Don't love this
.replaceAll("+", "_");
if (!mapUrl) {
throw new Error(`Unable to find map for country: ${country}`);
}
const cacheKey = `cache-jdan/svgMapOfCountry/${country}`;
const cached = await blob.getJSON(cacheKey);
if (cached) {
return cached;
}
const res = await fetch(mapUrl);
if (res.status !== 200) {
throw new Error(`[${res.status}] Error fetching ${mapUrl}`);
}
const mapData = await res.json();
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
// Calculate bounds and include logic for holes
mapData.data.features.forEach(feature => {
const processRing = (ring) => {
ring.forEach(([x, y_]) => {
// flip y
const y = 90 - y_;
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
});
};
const geometry = feature.geometry;
if (geometry.type === "Polygon" || geometry.type === "MultiPolygon") {
const polygons = geometry.type === "Polygon" ? [geometry.coordinates] : geometry.coordinates;
polygons.forEach(polygon => {
polygon.forEach(ring => processRing(ring));
});
}
});
const width = maxX - minX;
const height = maxY - minY;
const strokeWidth = Math.min(width, height) * 0.004;
const style = `fill: none; stroke: black; stroke-width: ${strokeWidth}px`;
// Define the viewBox to fit all coordinates
const viewBox = `${minX} ${minY} ${width} ${height}`;
// Start SVG string without width and height, only viewBox
let svgString = `<svg viewBox="${viewBox}" xmlns="http://www.w3.org/2000/svg">\n`;
// Function to convert a ring to a path data string
const ringToPathD = (ring) => {
let d = `M ${ring[0][0]} ${90 - ring[0][1]} `;
ring.slice(1).forEach(coord => {
d += `L ${coord[0]} ${90 - coord[1]} `;
});
return d + "Z"; // Close the path
};
// Iterate through features to create path elements for polygons and multipolygons
mapData.data.features.forEach(feature => {
const geometry = feature.geometry;
if (geometry.type === "Polygon" || geometry.type === "MultiPolygon") {
const polygons = geometry.type === "Polygon" ? [geometry.coordinates] : geometry.coordinates;
polygons.forEach(polygon => {
let d = "";
polygon.forEach((ring, index) => {
d += ringToPathD(ring); // Outer ring and holes
});
svgString += `<path d="${d}" style="${style}"></path>\n`;
});
}
});
svgString += "</svg>";
return svgString;
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
February 4, 2024