Skip to content

Instantly share code, notes, and snippets.

@anisotropi4
Last active April 19, 2017 16:35
Show Gist options
  • Save anisotropi4/baa54b185b821e4aa1ec10482067c099 to your computer and use it in GitHub Desktop.
Save anisotropi4/baa54b185b821e4aa1ec10482067c099 to your computer and use it in GitHub Desktop.
North Yorkshire Railway OSM with scale filter
license: gpl-3.0
data: Open Data Commons Open Database License (ODbL) http://www.openstreetmap.org/copyright

A more sophisticated d3 (https://d3js.org) and leaflet (http://leafletjs.com) mash-up displays North Yorkshire OSM Railway Points of Interest and uses the quadtree function to quickly find nodes to display.

As rendering large datasets becomes problematic (North Yorkshire railway data has about 21k points) this uses a random-logarithm heuristic filter to select few points at larger scale.

The data visualised is extracted from OpenStreetMap (http://www.openstreetmap.org) uses Overpass API (http://www.overpass-api.de) plus:
Arangodb (https://www.arangodb.com)
jq (https://stedolan.github.io/jq)

The data is used under the OpenStreetMap Creative Commons Attribution (http://www.openstreetmap.org/copyright)

<!DOCTYPE html>
<html>
<head>
<title>d3.js with leaflet.js rendering OSM points-of-interest</title>
<link rel="stylesheet" href="https://npmcdn.com/leaflet@1.0.3/dist/leaflet.css">
<script src="https://npmcdn.com/leaflet@1.0.3/dist/leaflet.js"></script>
<script src="http://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div id="map" style="width: 1340px; height: 635px"></div>
<script type="text/javascript">
var radius = 4;
var log2 = Math.log(2.0);
var minZoom = 3;
var maxZoom = 18;
// var map = L.map('map').setView([51.53, -0.124], 14); //KGX
var map = L.map('map').setView([53.96, -1.08], 14); //YRK
mapLink = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; ' + mapLink + ' Contributors',
minZoom: minZoom,
maxZoom: maxZoom
}).addTo(map);
L.svg().addTo(map);
var svg = d3.select("#map").select("svg")
, g = svg.selectAll("g");
var collections = [];
var colours15 = {0: "GreenYellow", 13: "DeepSkyBlue", 3: "Gold", 5: "Red", 4: "Purple", 6: "Orange", 7: "DeepPink", 8: "Magenta", 9: "DarkSlateBlue", 10: "GreenYellow", 11: "Lime", 12: "Cyan", 1: "Blue", 14: "DarkBlue", 15: "DarkOrange", 16: "DarkOrchid"};
var colourscale = function(n) {
return colours15[n];
}
// mapply works for big data sets while Math.{min,max}.apply() fails
function mapply(a, fn) {
var m = a[0];
for (i=1; i < a.length; i++) {
if (fn(a[i], m) != m) {
m = a[i];
}
}
return m;
}
var arange;
d3.json("raildata.json", function(err, d) {
if (err)
return console.warn(err);
var a = [];
for (var i = 1; i <= d.length; i++) {
a.push(Math.ceil(Math.log(i)/log2));
}
a = d3.shuffle(a);
arange = [mapply(a, Math.min), mapply(a, Math.max)];
var ascale = d3.scaleLinear().domain([arange[0], arange[1]]).range([maxZoom-2, minZoom]);
d.forEach(function(v, i) {
v.type = "output";
v.colour = colourscale(5);
v.LatLng = new L.LatLng(v.lat,v.lon);
v.lrange = ascale(a[i]);
});
collections = collections.concat(d);
render();
});
function render() {
var tree = d3.quadtree().x(function(d) {
return d.lat;
}).y(function(d) {
return d.lon;
}).addAll(collections);
var zscale = d3.scaleLinear().domain([minZoom, maxZoom]).range([arange[1], arange[0]]);
function visiblenodes() {
var nodes = [];
var z = map.getZoom();
console.log("zoom:", z, " zoomu:", zscale(z));
var bounds = map.getBounds();
var p0 = bounds._southWest
, p3 = bounds._northEast;
tree.visit(function(node, x1, y1, x2, y2) {
if (node.data && node.data.lrange >= zscale(map.getZoom())) {
nodes.push(node.data);
}
return x1 >= p3.lat || y1 >= p3.lon || x2 < p0.lat || y2 < p0.lon;
});
console.log("# nodes selected", nodes.length);
return nodes;
}
map.on("viewreset", update);
map.on("moveend", update);
update();
function update() {
g.selectAll("circle").remove();
g.selectAll("text").remove();
g.selectAll("rect").remove();
var nodes = visiblenodes()
, i = 0;
var feature = g.selectAll("circle").data(nodes).enter().append("g").style("fill", function(d, i) {
return d.colour;
}).append("svg:circle").style("stroke", "black").style("opacity", function(d, i) {
return 0.6;
}).attr("r", radius).attr("transform", function(d) {
var p = map.latLngToLayerPoint(d.LatLng);
d.x = p.x;
d.y = p.y;
return "translate(" + d.x + "," + d.y + ")";
});
}
}
</script>
</body>
</html>
This file has been truncated, but you can view the full file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment