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
xxxxxxxxxx
<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: "https://enjalot.github.io/wwsd/data/world/ne_50m_admin_0_countries.topojson"},
{"name": "California streams", url:"https://enjalot.github.io/wwsd/data/USA/california-streams.topojson"},
{"name": "London tube stations", url: "https://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>
Updated missing url https://cdnjs.cloudflare.com/ajax/libs/Turf.js/2.0.2/turf.min.js to https://cdnjs.cloudflare.com/ajax/libs/turf.js/2.0.2/turf.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://d3js.org/topojson.v1.min.js
https://cdnjs.cloudflare.com/ajax/libs/Turf.js/2.0.2/turf.min.js
https://api.tiles.mapbox.com/mapbox-gl-js/v0.12.0/mapbox-gl.js