Skip to content

Instantly share code, notes, and snippets.

@enjalot
Last active September 13, 2019 13:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save enjalot/fe2a8ee0ad59a58ce295f035419d9e63 to your computer and use it in GitHub Desktop.
Save enjalot/fe2a8ee0ad59a58ce295f035419d9e63 to your computer and use it in GitHub Desktop.
TopoJSON.io

Inspired by geojson.io this tool lets you quickly load and visualize TopoJSON files.

This should probably be rendering with Canvas overlay instead of SVG to be more performant. I leave that as a challenge to you, dear reader :)

Built with blockbuilder.org

forked from enjalot's block: TopoJSON => GeoJSON tool

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/2.0.2/turf.min.js"></script>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.12.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.12.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
#load {
position: absolute;
width: 45%;
background-color: rgba(255, 255, 255, 0.6);
font-family: Monospace;
margin: 10px;
padding: 5px;
border-radius: 3px;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
line-height: 24px;
}
#meta {
}
#url {
margin-top: 10px;
width: calc(100% - 55px);
}
.example {
font-size: 10px;
font-weight: bold;
margin: 5px;
cursor: pointer;
}
.example:hover {
text-decoration: underline;
}
#map {
width: 100%;
height: 100%;
position: absolute;
}
svg {
width: 100%;
height: 100%;
position: absolute;
}
path {
stroke: #111;
fill: #ff7;
fill-opacity: 0.3;
}
</style>
</head>
<body>
<div id="map">
</div>
<div id="load">
<input id="url">
<button id="loadurl">Load</button>
<br>or <span id="examples"></span>
<br>
<input id="loadfile" type="file">
<br>
<div id="meta">
</div>
</div>
<script>
//Setup mapbox-gl map
mapboxgl.accessToken = 'pk.eyJ1IjoiZW5qYWxvdCIsImEiOiJjaWhtdmxhNTIwb25zdHBsejk0NGdhODJhIn0.2-F2hS_oTZenAWc0BMf_uw'
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/enjalot/cihmvv7kg004v91kn22zjptsc',
center: [0,30],
zoom: 0.8,
})
map.addControl(new mapboxgl.Navigation());
// Setup our svg layer that we can manipulate with d3
var container = map.getCanvasContainer()
var svg = d3.select(container).append("svg")
/*
var bbox = document.body.getBoundingClientRect();
var canvas = d3.select(container).append("canvas").node()
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext('2d');
*/
/*
function project(d) {
return map.project(getLL(d));
}
function getLL(d) {
return new mapboxgl.LngLat(+d.lon, +d.lat)
}*/
function getD3() {
var bbox = document.body.getBoundingClientRect();
var center = map.getCenter();
var zoom = map.getZoom();
// 512 is hardcoded tile size, might need to be 256 or changed to suit your map config
var scale = (512) * 0.5 / Math.PI * Math.pow(2, zoom);
var d3projection = d3.geo.mercator()
.center([center.lng, center.lat])
.translate([bbox.width/2, bbox.height/2])
.scale(scale);
return d3projection;
}
var path = d3.geo.path()
.projection(getD3())
/*
.projection(function(lonlat, i) {
var p = map.project(new mapboxgl.LngLat(lonlat[0], lonlat[1]))
return [p.x, p.y];
})*/
//.context(ctx)
function render() {
path.projection(getD3())
svg.selectAll("path").attr("d", path)
}
// re-render our visualization whenever the view changes
map.on("viewreset", function() {
render()
})
map.on("move", function() {
render()
})
// render our initial visualization
render()
// Prep the data loading UI
var examples = [
{"name": "World countries", url: "http://enjalot.github.io/wwsd/data/world/ne_50m_admin_0_countries.topojson"},
{"name": "California streams", url:"http://enjalot.github.io/wwsd/data/USA/california-streams.topojson"},
{"name": "London tube stations", url: "http://enjalot.github.io/wwsd/data/UK/london_stations.topojson"}
]
d3.select("#examples").selectAll("a.example")
.data(examples)
.enter().append("a").classed("example", true)
.text(function(d) { return d.name})
.on("click", function(d) {
d3.select("#url").node().value = d.url;
loadUrl(d.url)
})
d3.select("#loadurl").on("click", loadUrl);
d3.select("#url").on("keydown", function() {
if(d3.event.code === "Enter") {
loadUrl()
}
})
d3.select("#loadfile").node()
.addEventListener('change', loadFile, false);
var load = d3.select("#load").node()
load.addEventListener("dragover", handleDragOver, false)
load.addEventListener("drop", loadFile, false);
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy';
}
function loadFile(evt) {
evt.stopPropagation();
evt.preventDefault();
d3.select("pre#out").text("LOADING")
var files;
if(evt.dataTransfer) {
files = evt.dataTransfer.files;
} else {
files = evt.target.files;
}
var reader = new FileReader();
reader.onload = function(e) {
convert(JSON.parse(e.target.result))
}
reader.readAsText(files[0])
}
function convert(topo) {
console.log("topojson:", topo)
var keys = Object.keys(topo.objects);
d3.select("#meta").html(keys.length + " objects: " + keys.join("<br>"))
// clear the SVG
svg.selectAll("*").remove();
var boundsList = [];
keys.forEach(function(key) {
var geo = topojson.feature(topo, topo.objects[key])
// https://github.com/d3/d3/wiki/Geo-Paths#bounds
//boundsList.push(d3.geo.bounds(boundsList))
var envelope = turf.envelope(geo).geometry.coordinates[0]
boundsList.push([envelope[0], envelope[2]])
console.log("geojson", key, envelope, geo)
renderJSON(geo)
});
// TODO: calculate the total bounds of all objects
// left,bottom right,top
var bounds = boundsList[0];
console.log("bounds", bounds);
if(bounds[0][1] <= -90) bounds[0][1] = -89;
if(bounds[1][1] <= -90) bounds[1][1] = -89;
map.fitBounds(bounds)
}
function loadUrl(url) {
if(!url) {
url = d3.select("#url").node().value;
}
d3.select("#meta").text("LOADING")
d3.json(url, function(err, topo) {
if(err) {
d3.select("#meta").text("ERROR:" + err.message)
return;
}
convert(topo)
})
}
function renderJSON(geo, pretty) {
svg.append("path")
.datum(geo)
.attr("d", path);
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment