Skip to content

Instantly share code, notes, and snippets.

@nautilytics
Last active January 12, 2021 17:05
Show Gist options
  • Save nautilytics/47c1e69b4c9d3bb849af6d28c78c0475 to your computer and use it in GitHub Desktop.
Save nautilytics/47c1e69b4c9d3bb849af6d28c78c0475 to your computer and use it in GitHub Desktop.
A GeoJSON to PNG converter using D3 for Node
const {createConverter} = require("convert-svg-to-png");
const path = require("path");
const sharp = require('sharp');
const D3Node = require("d3-node");
const {d3} = require("d3-node");
// Set up global variables which can be changed
const width = 400;
const height = 400;
const fileName = "./UK.json";
const activeFillColor = "#149E9C";
const getRegion = feature => feature.properties.region; // helper func to grab the region of each feature
const getId = feature => feature.properties.PCON11CD; // helper func to grab the unique feature ID of each feature
const convertSvgFiles = async () => {
// Entry point into main convert GeoJSON to PNG function
const converter = createConverter();
try {
// Load the GeoJSON file which can be located locally (or via a link)
const f = path.join(__dirname, fileName);
const json = require(f);
// Find all the unique regions in our data set, while keeping the features attached to the nested data
const regions = d3.nest()
.key(d => getRegion(d))
.entries(json.features);
// Go through each region and create a generic FeatureCollection for the polygons within that region
for (const region of regions) {
// Using the FeatureCollection, center the region with the selected geographic projection
const projection = d3.geoMercator()
.fitSize([width, height], {
"type": "FeatureCollection",
"name": "UK_2",
"crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:OGC:1.3:CRS84"}},
"features": region.values
});
const p = d3.geoPath(projection);
// Go through each feature, or constituency, within the region,
// and render it as SVG with the feature highlighted
const features = region.values;
for (let feature of features) {
const renderedSVG = await renderSVG(features, feature, p);
try {
// Using the `sharp` library, take the rendered SVG string and generate a PNG
await sharp(Buffer.from(renderedSVG.svgString))
.extract({
left: 0,
top: renderedSVG.y1,
width: width,
height: renderedSVG.y2 - renderedSVG.y1
})
.png()
.toFile(`./PNGS/${getId(feature)}.png`);
} catch (err) {
console.error(err);
}
}
}
} finally {
await converter.destroy();
process.exit();
}
};
const renderSVG = async (features, feature, p) => {
// Use D3 on the back-end to create an SVG of the FeatureCollection
const d3N = new D3Node();
const svg = d3N.createSVG(width, height);
svg
.selectAll("path")
.data(features)
.enter()
.append("path")
.style("stroke", "black")
.style("fill", d => getId(d) === getId(feature) ? activeFillColor : "white")
.style("shape-rendering", "crispEdges")
.style("stroke-width", "1px")
.attr("d", p);
// Use the bounds of the feature to make sure our images don't have any extra white space around them
let y1, y2;
features.forEach(feature => {
const bound = p.bounds(feature);
if (!y1 || bound[0][1] < y1) y1 = bound[0][1];
if (!y2 || bound[1][1] > y2) y2 = bound[1][1];
});
const svgString = d3N.svgString();
return {
svgString,
y1: Math.floor(Math.max(y1, 0)),
y2: Math.floor(y2)
};
};
// Run the application to convert the SVG files
convertSvgFiles()
.then();
{
"name": "geojson-to-png-converter",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"convert-svg-to-png": "^0.5.0",
"d3-node": "^2.2.0",
"sharp": "^0.23.3"
}
}
Display the source blob
Display the rendered blob
Raw
{
"type": "FeatureCollection",
"name": "UK",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [
{
"type": "Feature",
"properties": {
"PCON11NM": "Amber Valley",
"PCON11CD": "R120",
"region": "East Midlands"
},
"geometry": {
"type": "Polygon",
"coordinates": [ [ [ -1.3316, 53.081 ], [ -1.3341, 53.0789 ], [ -1.3347, 53.0776 ], [ -1.3358, 53.0771 ], [ -1.3366, 53.0757 ], [ -1.3358, 53.0746 ], [ -1.3383, 53.0727 ], [ -1.3422, 53.0709 ], [ -1.344, 53.0683 ], [ -1.3435, 53.0668 ], [ -1.3444, 53.0655 ], [ -1.3446, 53.0633 ], [ -1.3419, 53.0605 ], [ -1.3402, 53.0597 ], [ -1.3375, 53.0558 ], [ -1.3374, 53.0547 ], [ -1.3359, 53.0529 ], [ -1.3364, 53.0517 ], [ -1.3357, 53.0507 ], [ -1.3344, 53.0502 ], [ -1.3332, 53.0481 ], [ -1.3346, 53.0473 ], [ -1.333, 53.0446 ], [ -1.3338, 53.0429 ], [ -1.3336, 53.0417 ], [ -1.3346, 53.0406 ], [ -1.3332, 53.0397 ], [ -1.3326, 53.0383 ], [ -1.3326, 53.0364 ], [ -1.3332, 53.0356 ], [ -1.3313, 53.0318 ], [ -1.3318, 53.0309 ], [ -1.3291, 53.0307 ], [ -1.3278, 53.0284 ], [ -1.3259, 53.0274 ], [ -1.3246, 53.0261 ], [ -1.3221, 53.0202 ], [ -1.3186, 53.0161 ], [ -1.3202, 53.0155 ], [ -1.3183, 53.0138 ], [ -1.3196, 53.0117 ], [ -1.3192, 53.0102 ], [ -1.3158, 53.0086 ], [ -1.3136, 53.0067 ], [ -1.3134, 53.0046 ], [ -1.3123, 53.0046 ], [ -1.31, 53.0034 ], [ -1.308, 53.0039 ], [ -1.3074, 53.0036 ], [ -1.3098, 53.002 ], [ -1.314, 53.0013 ], [ -1.3169, 52.9992 ], [ -1.3183, 52.9967 ], [ -1.3224, 52.9939 ], [ -1.3242, 52.9937 ], [ -1.3227, 52.9921 ], [ -1.3231, 52.9898 ], [ -1.3246, 52.9895 ], [ -1.3238, 52.9883 ], [ -1.3262, 52.9886 ], [ -1.328, 52.9873 ], [ -1.3315, 52.987 ], [ -1.3285, 52.9843 ], [ -1.3281, 52.9816 ], [ -1.3304, 52.9809 ], [ -1.3328, 52.9781 ], [ -1.3362, 52.9779 ], [ -1.3372, 52.9785 ], [ -1.3399, 52.9782 ], [ -1.3423, 52.9789 ], [ -1.3477, 52.978 ], [ -1.3503, 52.9782 ], [ -1.352, 52.9778 ], [ -1.3554, 52.978 ], [ -1.3585, 52.9777 ], [ -1.362, 52.9767 ], [ -1.3672, 52.9773 ], [ -1.3693, 52.9759 ], [ -1.3701, 52.9736 ], [ -1.371, 52.9731 ], [ -1.3752, 52.9742 ], [ -1.3783, 52.9766 ], [ -1.386, 52.9791 ], [ -1.3859, 52.9802 ], [ -1.3842, 52.9816 ], [ -1.3937, 52.9817 ], [ -1.3955, 52.9807 ], [ -1.4027, 52.9796 ], [ -1.4093, 52.977 ], [ -1.4098, 52.9796 ], [ -1.4156, 52.9806 ], [ -1.4186, 52.9801 ], [ -1.4205, 52.981 ], [ -1.4244, 52.9814 ], [ -1.4299, 52.9801 ], [ -1.432, 52.9793 ], [ -1.4447, 52.9788 ], [ -1.4496, 52.9807 ], [ -1.4514, 52.9823 ], [ -1.4537, 52.9809 ], [ -1.4559, 52.9826 ], [ -1.4579, 52.9824 ], [ -1.4602, 52.9839 ], [ -1.4634, 52.9829 ], [ -1.4641, 52.9857 ], [ -1.4635, 52.9868 ], [ -1.4645, 52.9899 ], [ -1.464, 52.9919 ], [ -1.4667, 52.9917 ], [ -1.4671, 52.994 ], [ -1.4707, 52.9935 ], [ -1.4715, 52.9959 ], [ -1.4702, 52.9972 ], [ -1.4679, 52.9974 ], [ -1.4653, 52.9983 ], [ -1.465, 52.9992 ], [ -1.4688, 52.9987 ], [ -1.4696, 52.9999 ], [ -1.4696, 53.001 ], [ -1.4714, 53.0022 ], [ -1.4711, 53.0047 ], [ -1.4686, 53.0052 ], [ -1.4666, 53.0036 ], [ -1.4638, 53.0037 ], [ -1.4629, 53.0078 ], [ -1.4638, 53.011 ], [ -1.462, 53.0117 ], [ -1.4604, 53.0107 ], [ -1.4599, 53.0126 ], [ -1.4615, 53.013 ], [ -1.4615, 53.0149 ], [ -1.461, 53.016 ], [ -1.4584, 53.0181 ], [ -1.4572, 53.0186 ], [ -1.4555, 53.0205 ], [ -1.4506, 53.0223 ], [ -1.4491, 53.0238 ], [ -1.4476, 53.0264 ], [ -1.4475, 53.0281 ], [ -1.4452, 53.0329 ], [ -1.4453, 53.0336 ], [ -1.443, 53.0366 ], [ -1.4468, 53.0372 ], [ -1.4504, 53.0364 ], [ -1.4497, 53.0377 ], [ -1.4509, 53.0392 ], [ -1.4534, 53.0405 ], [ -1.4564, 53.0407 ], [ -1.4815, 53.0463 ], [ -1.4817, 53.0475 ], [ -1.4805, 53.0487 ], [ -1.4804, 53.0497 ], [ -1.4815, 53.0526 ], [ -1.4831, 53.0552 ], [ -1.4839, 53.0595 ], [ -1.4884, 53.0637 ], [ -1.4871, 53.0642 ], [ -1.4838, 53.0644 ], [ -1.4815, 53.0658 ], [ -1.4803, 53.0678 ], [ -1.4779, 53.0676 ], [ -1.477, 53.0684 ], [ -1.4773, 53.0699 ], [ -1.4793, 53.0733 ], [ -1.476, 53.0747 ], [ -1.47, 53.0758 ], [ -1.4694, 53.0785 ], [ -1.4681, 53.0787 ], [ -1.4656, 53.0816 ], [ -1.4657, 53.0826 ], [ -1.4608, 53.0832 ], [ -1.4592, 53.0827 ], [ -1.4581, 53.0834 ], [ -1.4612, 53.0864 ], [ -1.4632, 53.0898 ], [ -1.4654, 53.0917 ], [ -1.4653, 53.0924 ], [ -1.469, 53.0959 ], [ -1.47, 53.0965 ], [ -1.4716, 53.0992 ], [ -1.4685, 53.1003 ], [ -1.4693, 53.1019 ], [ -1.4687, 53.1062 ], [ -1.4676, 53.107 ], [ -1.4654, 53.1108 ], [ -1.4615, 53.1102 ], [ -1.46, 53.1083 ], [ -1.4573, 53.1088 ], [ -1.4555, 53.1096 ], [ -1.4524, 53.1064 ], [ -1.4505, 53.1055 ], [ -1.4503, 53.1045 ], [ -1.4482, 53.1036 ], [ -1.4434, 53.1045 ], [ -1.4373, 53.1049 ], [ -1.4325, 53.1059 ], [ -1.4301, 53.1053 ], [ -1.4274, 53.1055 ], [ -1.4254, 53.1039 ], [ -1.4237, 53.1033 ], [ -1.4223, 53.1045 ], [ -1.4203, 53.1055 ], [ -1.4197, 53.1065 ], [ -1.4173, 53.1056 ], [ -1.4138, 53.1055 ], [ -1.4099, 53.1064 ], [ -1.41, 53.1074 ], [ -1.408, 53.1075 ], [ -1.4072, 53.1069 ], [ -1.4029, 53.1073 ], [ -1.4005, 53.1062 ], [ -1.3949, 53.1047 ], [ -1.3925, 53.1053 ], [ -1.3912, 53.1052 ], [ -1.3891, 53.1064 ], [ -1.384, 53.107 ], [ -1.379, 53.1088 ], [ -1.377, 53.1086 ], [ -1.3732, 53.1077 ], [ -1.3727, 53.1055 ], [ -1.3728, 53.1038 ], [ -1.3703, 53.1024 ], [ -1.3689, 53.1001 ], [ -1.3696, 53.099 ], [ -1.3669, 53.0977 ], [ -1.366, 53.0967 ], [ -1.3641, 53.0959 ], [ -1.364, 53.0945 ], [ -1.365, 53.0932 ], [ -1.3623, 53.0918 ], [ -1.3607, 53.0904 ], [ -1.359, 53.0898 ], [ -1.357, 53.0883 ], [ -1.3531, 53.0862 ], [ -1.3509, 53.0866 ], [ -1.3448, 53.0862 ], [ -1.3437, 53.0866 ], [ -1.3417, 53.0856 ], [ -1.3398, 53.0842 ], [ -1.3316, 53.081 ] ] ]
}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment