Published
Edited
Nov 22, 2020
Importers
Insert cell
md`# TopoJSON Utils`
Insert cell
{
const topojsonData = await getTopoJSONData(REGION_TYPE.DISTRICT);

const C_TO_COLOR = {
K: 'red',
M: 'blue',
A: 'green',
B: 'orange',
P: 'purple',
G: 'cyan'
// N: 'red',
// S: 'blue'
};

function funcDataToColor(data) {
if (C_TO_COLOR[data]) {
return C_TO_COLOR[data];
}
return 'white';
}

return renderInfographic({
funcDrawInner: function(svg) {
drawMap(svg, topojsonData, {
funcDataToColor: function(d) {
const c = d.properties.name.substring(0, 1);
return funcDataToColor(c);
}
});
},
funcDrawLegend: function(svg) {
drawLegend(svg, Object.keys(C_TO_COLOR), { funcDataToColor });
},
kicker: 'Sri Lanka',
title: 'Districts',
subTitle: 'With names beginning with...',
showGrid: true
});
}
Insert cell
Insert cell
topojson = require('topojson')
Insert cell
Insert cell
Insert cell
Insert cell
import {
DEFAULT_STYLE,
getSVG,
drawText,
drawCircle,
drawPolygon
} from '@nuuuwan/svg-utils'
Insert cell
import { renderInfographic } from '@nuuuwan/infographic-utils'
Insert cell
import { drawLegend } from '@nuuuwan/legend-utils'
Insert cell
function convertTopoJSONToGeoJSON(topojsonData, objectsKey = 'data') {
return topojson.feature(topojsonData, topojsonData.objects[objectsKey])
.features;
}
Insert cell
function getCompleteGeoJSONMap(topojsonData) {
return topojson.merge(topojsonData, [topojsonData.objects.data]);
}
Insert cell
async function getTopoJSONData(regionType) {
const TOPOJSON_DATA_ROOT =
'https://raw.githubusercontent.com/nuuuwan/sl-topojson/master';
const VERSION = '2020-11-20-10:40';
return await d3.json(`${TOPOJSON_DATA_ROOT}/${regionType}.${VERSION}.json`);
}
Insert cell
function optionsDefault(options = {}) {
options = addDefaults(options, {
padding: 72,
height: 900,
width: 1600,
projection: d3.geoNaturalEarth(),

stroke: 'black',
strokeWidth: 0.2,
pathFillOpacity: 0.4,

nSimulationTicks: 0,
labelColor: 'black',
maxValueRadius: 50,

funcDataToColor(data) {
return 'white';
},

funcDataToRadius(data) {
return 50;
}
});

options = addDefaults(options, {
funcDrawLabel: function(geojsonDatum, svg, [x, y], options) {
drawText(
svg,
[x, y - DEFAULT_STYLE.fontSize / 3],
geojsonDatum.properties.name,
{
fill: options.labelColor,
fontSize: DEFAULT_STYLE.fontSize / 2
}
);
drawText(
svg,
[x, y + DEFAULT_STYLE.fontSize / 3],
geojsonDatum.properties.id,
{
fill: options.labelColor,
fontSize: DEFAULT_STYLE.fontSize / 2
}
);
}
});
return options;
}
Insert cell
function optionsCartogram(options = {}) {
options = optionsDefault(options);

const funcDataToColorOriginal = options.funcDataToColor;
const funcDrawLabelOriginal = options.funcDrawLabel;

options.funcDrawLabel = function(data, svg, [x, y], options) {
const r = options.funcDataToRadius(data);
drawCircle(svg, [x, y], r, {
fill: funcDataToColorOriginal(data),
stroke: 'darkgray'
});
funcDrawLabelOriginal(data, svg, [x, y], options);
};

options.stroke = '#404040';
options.pathFillOpacity = 0.1;
options.nSimulationTicks = 200;

return options;
}
Insert cell
function drawMap(svg, geoData, options = {}) {
options = optionsDefault(options);

const completeMap = getCompleteGeoJSONMap(geoData);
const geojsonData = convertTopoJSONToGeoJSON(geoData);
const projection = options.projection.fitExtent(
[
[options.padding, options.padding],
[options.width - options.padding, options.height - options.padding]
],
completeMap
);
const funcGetPathD = d3.geoPath(projection);

const isSimulation = options.nSimulationTicks > 0;
const initXY = d => projection(d3.geoCentroid(d));
if (isSimulation) {
const simulation = d3
.forceSimulation(geojsonData)
.force("x", d3.forceX(d => initXY(d)[0]))
.force("y", d3.forceY(d => initXY(d)[1]))
.force("collide", d3.forceCollide(d => 0.5 + options.funcDataToRadius(d)))
.stop();

for (let i = 0; i < options.nSimulationTicks; i++) {
simulation.tick();
}
}

geojsonData.forEach(function(geojsonDatum, i) {
svg
.append("path")
.attr("fill", options.funcDataToColor(geojsonDatum))
.attr('fill-opacity', options.pathFillOpacity)
.attr('stroke', options.stroke)
.attr('stroke-width', options.strokeWidth)
.attr("d", funcGetPathD(geojsonDatum));
});

geojsonData.forEach(function(geojsonDatum, i) {
const [x, y] = isSimulation
? [geojsonDatum.x, geojsonDatum.y]
: initXY(geojsonDatum);
options.funcDrawLabel(geojsonDatum, svg, [x, y], options);
});
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more