define(["leaflet", "jshashes"], function (L, jsHashes) { var MD5 = new jsHashes.MD5() return L.TileLayer.Canvas.extend({ setData: function (d) { this.data = d //pre-calculate start angles this.data.all().forEach(function (d) { var hash = MD5.hex(d.node.nodeinfo.node_id) d.startAngle = (parseInt(hash.substr(0, 2), 16) / 255) * 2 * Math.PI }) 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 var tileSize = this.options.tileSize var s = tilePoint.multiplyBy(tileSize) var map = this._map 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 radius = 3 var a = 1.2 var startDistance = 12 ctx.beginPath() nodes.forEach(function (d) { 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 for (var orbit = 0, i = 0; i < clients; orbit++) { var distance = startDistance + orbit * 2 * radius * a var n = Math.floor((Math.PI * distance) / (a * radius)) var delta = clients - i for (var j = 0; j < Math.min(delta, n); i++, j++) { var angle = 2 * Math.PI / n * j var x = p.x + distance * Math.cos(angle + d.startAngle) var y = p.y + distance * Math.sin(angle + d.startAngle) ctx.moveTo(x, y) ctx.arc(x, y, radius, 0, 2 * Math.PI) } } }) ctx.fillStyle = "rgba(220, 0, 103, 0.7)" ctx.fill() } }) })