Browse Source

map: draw only labels present on map using rtrees

Nils Schneider 9 years ago
parent
commit
22b49c1a55
5 changed files with 103 additions and 70 deletions
  1. 1 0
      app.js
  2. 2 1
      bower.json
  3. 28 7
      lib/map.js
  4. 49 39
      lib/map/clientlayer.js
  5. 23 23
      lib/map/labelslayer.js

+ 1 - 0
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: {

+ 2 - 1
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 <nils@nilsschneider.net>"

+ 28 - 7
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)

+ 49 - 39
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 margin = 50
+        var bbox = getTileBBox(s, map, tileSize, margin)
+
+        var nodes = this.data.search(bbox)
+
+        if (nodes.length === 0)
+          return
 
-        var nodes = this.data
         var ctx = canvas.getContext("2d")
-        var margin = 50
+
+        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 clients = d.statistics.clients
-            if (d.clients === 0)
-              return
-
-            var distance = 12
-            var radius = 3
-            var a = 1.2
-            var startAngle = Math.PI
-            var angle = startAngle
-
-            for (var i = 0; i < clients; i++) {
-              if ((angle - startAngle) > 2 * Math.PI) {
-                angle = startAngle
-                distance += 2 * radius * a
-              }
-
-              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
+          var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude])
+          var clients = d.node.statistics.clients
+
+          if (clients === 0)
+            return
+
+          p.x -= s.x
+          p.y -= s.y
+
+          var angle = startAngle
+
+          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()
+      }
     })
 })

+ 23 - 23
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])
 
-        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
+          p.x -= s.x
+          p.y -= s.y
+
+          return {p: p, o: d.node}
         }
 
-        var ctx = canvas.getContext("2d")
+        var margin = 256
+        var bbox = getTileBBox(s, map, tileSize, margin)
 
-        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 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 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)"