From 22b49c1a556cf1241b2c07e832143b7bc640f00e Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Sun, 19 Apr 2015 12:22:09 +0200 Subject: [PATCH] map: draw only labels present on map using rtrees --- app.js | 1 + bower.json | 3 +- lib/map.js | 35 ++++++++++++++---- lib/map/clientlayer.js | 82 +++++++++++++++++++++++------------------- lib/map/labelslayer.js | 46 ++++++++++++------------ 5 files changed, 100 insertions(+), 67 deletions(-) diff --git a/app.js b/app.js index 47c9741..50e61c6 100644 --- a/app.js +++ b/app.js @@ -12,6 +12,7 @@ require.config({ "numeral": "../bower_components/numeraljs/min/numeral.min", "numeral-intl": "../bower_components/numeraljs/min/languages.min", "virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom", + "rbush": "../bower_components/rbush/rbush", "helper": "../helper" }, shim: { diff --git a/bower.json b/bower.json index 95793ff..20bf6fe 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,8 @@ "numeraljs": "~1.5.3", "roboto-fontface": "~0.3.0", "virtual-dom": "~2.0.1", - "leaflet-providers": "~1.0.27" + "leaflet-providers": "~1.0.27", + "rbush": "https://github.com/mourner/rbush.git#~1.3.5" }, "authors": [ "Nils Schneider " diff --git a/lib/map.js b/lib/map.js index a6fe0b4..e9572c5 100644 --- a/lib/map.js +++ b/lib/map.js @@ -1,7 +1,7 @@ define(["map/clientlayer", "map/labelslayer", - "d3", "leaflet", "moment", "locationmarker", + "d3", "leaflet", "moment", "locationmarker", "rbush", "leaflet.label", "leaflet.providers"], - function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker) { + function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush) { var options = { worldCopyJump: true, zoomControl: false } @@ -300,6 +300,15 @@ define(["map/clientlayer", "map/labelslayer", return L.circle(barycenter, r * config.mapSigmaScale) } + function mapRTree(d) { + var o = [ d.nodeinfo.location.latitude, d.nodeinfo.location.longitude, + d.nodeinfo.location.latitude, d.nodeinfo.location.longitude] + + o.node = d + + return o + } + self.setData = function (data) { nodeDict = {} linkDict = {} @@ -349,11 +358,23 @@ define(["map/clientlayer", "map/labelslayer", groupNew = L.featureGroup(markersNew).addTo(map) groupLost = L.featureGroup(markersLost).addTo(map) - clientLayer.setData(data.nodes.all.filter(online).filter(has_location)) - labelsLayer.setData({online: nodesOnline.filter(has_location), - offline: nodesOffline.filter(has_location), - new: data.nodes.new.filter(has_location), - lost: data.nodes.lost.filter(has_location) + var rtreeOnlineAll = rbush(9) + var rtreeOnline = rbush(9) + var rtreeOffline = rbush(9) + var rtreeNew = rbush(9) + var rtreeLost = rbush(9) + + rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree)) + rtreeOnline.load(nodesOnline.filter(has_location).map(mapRTree)) + rtreeOffline.load(nodesOffline.filter(has_location).map(mapRTree)) + rtreeNew.load(data.nodes.new.filter(has_location).map(mapRTree)) + rtreeLost.load(data.nodes.lost.filter(has_location).map(mapRTree)) + + clientLayer.setData(rtreeOnlineAll) + labelsLayer.setData({online: rtreeOnline, + offline: rtreeOffline, + new: rtreeNew, + lost: rtreeLost }) updateView(true) diff --git a/lib/map/clientlayer.js b/lib/map/clientlayer.js index e1a7dce..a22d30e 100644 --- a/lib/map/clientlayer.js +++ b/lib/map/clientlayer.js @@ -6,6 +6,13 @@ define(["leaflet"], this.redraw() }, drawTile: function (canvas, tilePoint) { + function getTileBBox(s, map, tileSize, margin) { + var tl = map.unproject([s.x - margin, s.y - margin]) + var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]) + + return [br.lat, tl.lng, tl.lat, br.lng] + } + if (!this.data) return @@ -13,51 +20,54 @@ define(["leaflet"], var s = tilePoint.multiplyBy(tileSize) var map = this._map - function project(coords) { - var p = map.project(new L.LatLng(coords[0], coords[1])) - return {x: p.x - s.x, y: p.y - s.y} - } - - var nodes = this.data - var ctx = canvas.getContext("2d") var margin = 50 + var bbox = getTileBBox(s, map, tileSize, margin) + + var nodes = this.data.search(bbox) + + if (nodes.length === 0) + return + + var ctx = canvas.getContext("2d") + + var distance = 12 + var radius = 3 + var a = 1.2 + var startAngle = Math.PI ctx.beginPath() nodes.forEach(function (d) { - var p = project([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude]) - if (p.x + margin < 0 || p.y + margin < 0 || p.x - tileSize - margin > 0 || p.y - tileSize - margin > 0) - return + var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]) + var clients = d.node.statistics.clients - var clients = d.statistics.clients - if (d.clients === 0) - return + if (clients === 0) + return - var distance = 12 - var radius = 3 - var a = 1.2 - var startAngle = Math.PI - var angle = startAngle + p.x -= s.x + p.y -= s.y - for (var i = 0; i < clients; i++) { - if ((angle - startAngle) > 2 * Math.PI) { - angle = startAngle - distance += 2 * radius * a - } + var angle = startAngle - var x = p.x + distance * Math.cos(angle) - var y = p.y + distance * Math.sin(angle) - - ctx.moveTo(x, y) - ctx.arc(x, y, radius, 0, 2 * Math.PI) - - var n = Math.floor((Math.PI * distance) / (a * radius)) - var angleDelta = 2 * Math.PI / n - angle += angleDelta + for (var i = 0; i < clients; i++) { + if ((angle - startAngle) > 2 * Math.PI) { + angle = startAngle + distance += 2 * radius * a } - }) - ctx.fillStyle = "rgba(153, 118, 16, 0.5)" - ctx.fill() - } + var x = p.x + distance * Math.cos(angle) + var y = p.y + distance * Math.sin(angle) + + ctx.moveTo(x, y) + ctx.arc(x, y, radius, 0, 2 * Math.PI) + + var n = Math.floor((Math.PI * distance) / (a * radius)) + var angleDelta = 2 * Math.PI / n + angle += angleDelta + } + }) + + ctx.fillStyle = "rgba(153, 118, 16, 0.5)" + ctx.fill() + } }) }) diff --git a/lib/map/labelslayer.js b/lib/map/labelslayer.js index 500ccba..9585d50 100644 --- a/lib/map/labelslayer.js +++ b/lib/map/labelslayer.js @@ -6,6 +6,13 @@ define(["leaflet"], this.redraw() }, drawTile: function (canvas, tilePoint) { + function getTileBBox(s, map, tileSize, margin) { + var tl = map.unproject([s.x - margin, s.y - margin]) + var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]) + + return [br.lat, tl.lng, tl.lat, br.lng] + } + if (!this.data) return @@ -13,41 +20,34 @@ define(["leaflet"], var s = tilePoint.multiplyBy(tileSize) var map = this._map - function project(coords) { - var p = map.project(new L.LatLng(coords[0], coords[1])) - return {x: p.x - s.x, y: p.y - s.y} - } - function projectNodes(d) { - return { p: project([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude]), - o: d - } + var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]) + + p.x -= s.x + p.y -= s.y + + return {p: p, o: d.node} } - var margin = 150 - function onTile(d) { - return d.p.x + margin > 0 || - d.p.y + margin > 0 || - d.p.x - tileSize - margin < 0 || - d.p.y - tileSize - margin < 0 - } + var margin = 256 + var bbox = getTileBBox(s, map, tileSize, margin) + + var nodesOnline = this.data.online.search(bbox).map(projectNodes) + var nodesOffline = this.data.offline.search(bbox).map(projectNodes) + var nodesNew = this.data.new.search(bbox).map(projectNodes) + var nodesLost = this.data.lost.search(bbox).map(projectNodes) var ctx = canvas.getContext("2d") - var nodesOnline = this.data.online.map(projectNodes).filter(onTile) - var nodesOffline = this.data.offline.map(projectNodes).filter(onTile) - var nodesNew = this.data.new.map(projectNodes).filter(onTile) - var nodesLost = this.data.lost.map(projectNodes).filter(onTile) - - var distance = 10 ctx.font = "12px Roboto" ctx.textBaseline = "middle" ctx.textAlign = "left" ctx.lineWidth = 2.5 + var distance = 10 function drawLabel(d) { - ctx.strokeText(d.o.nodeinfo.hostname, d.p.x + distance, d.p.y) - ctx.fillText(d.o.nodeinfo.hostname, d.p.x + distance, d.p.y) + ctx.strokeText(d.o.nodeinfo.hostname, d.p.x + distance, d.p.y) + ctx.fillText(d.o.nodeinfo.hostname, d.p.x + distance, d.p.y) } ctx.fillStyle = "rgba(212, 62, 42, 0.6)"