123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- define(["leaflet", "rbush"],
- function (L, rbush) {
- var labelLocations = [["left", "middle", 0 / 8],
- ["center", "top", 6 / 8],
- ["right", "middle", 4 / 8],
- ["left", "top", 7 / 8],
- ["left", "ideographic", 1 / 8],
- ["right", "top", 5 / 8],
- ["center", "ideographic", 2 / 8],
- ["right", "ideographic", 3 / 8]]
- var fontFamily = "Roboto"
- var nodeRadius = 4
- var ctx = document.createElement("canvas").getContext("2d")
- function measureText(font, text) {
- ctx.font = font
- return ctx.measureText(text)
- }
- function mapRTree(d) {
- var o = [d.position.lat, d.position.lng, d.position.lat, d.position.lng]
- o.label = d
- return o
- }
- function prepareLabel(fillStyle, fontSize, offset, stroke, minZoom) {
- return function (d) {
- var font = fontSize + "px " + fontFamily
- return { position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude),
- label: d.nodeinfo.hostname,
- offset: offset,
- fillStyle: fillStyle,
- height: fontSize * 1.2,
- font: font,
- stroke: stroke,
- minZoom: minZoom,
- width: measureText(font, d.nodeinfo.hostname).width
- }
- }
- }
- function calcOffset(offset, loc) {
- return [ offset * Math.cos(loc[2] * 2 * Math.PI),
- -offset * Math.sin(loc[2] * 2 * Math.PI)]
- }
- function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) {
- var margin = 1 + 1.41 * (1 - (z - minZoom) / (maxZoom - minZoom))
- var width = label.width * margin
- var height = label.height * margin
- var dx = { left: 0,
- right: -width,
- center: -width / 2
- }
- var dy = { top: 0,
- ideographic: -height,
- middle: -height / 2
- }
- var x = p.x + offset[0] + dx[anchor[0]]
- var y = p.y + offset[1] + dy[anchor[1]]
- return [x, y, x + width, y + height]
- }
- var c = L.TileLayer.Canvas.extend({
- onAdd: function (map) {
- L.TileLayer.Canvas.prototype.onAdd.call(this, map)
- if (this.data)
- this.prepareLabels()
- },
- setData: function (d) {
- this.data = d
- if (this._map)
- this.prepareLabels()
- },
- prepareLabels: function () {
- var d = this.data
- // label:
- // - position (WGS84 coords)
- // - offset (2D vector in pixels)
- // - anchor (tuple, textAlignment, textBaseline)
- // - minZoom (inclusive)
- // - label (string)
- // - color (string)
- var labelsOnline = d.online.map(prepareLabel("rgba(0, 0, 0, 0.9)", 10, 8, true, 13))
- var labelsOffline = d.offline.map(prepareLabel("rgba(212, 62, 42, 0.9)", 9, 5, false, 16))
- var labelsNew = d.new.map(prepareLabel("rgba(48, 99, 20, 0.9)", 11, 8, true, 0))
- var labelsLost = d.lost.map(prepareLabel("rgba(212, 62, 42, 0.9)", 11, 8, true, 0))
- var labels = []
- .concat(labelsNew)
- .concat(labelsLost)
- .concat(labelsOnline)
- .concat(labelsOffline)
- var minZoom = this.options.minZoom
- var maxZoom = this.options.maxZoom
- var trees = []
- var map = this._map
- function nodeToRect(z) {
- return function (d) {
- var p = map.project(d.position, z)
- return [p.x - nodeRadius, p.y - nodeRadius,
- p.x + nodeRadius, p.y + nodeRadius]
- }
- }
- for (var z = minZoom; z <= maxZoom; z++) {
- trees[z] = rbush(9)
- trees[z].load(labels.map(nodeToRect(z)))
- }
- labels = labels.map(function (d) {
- var best = labelLocations.map(function (loc) {
- var offset = calcOffset(d.offset, loc)
- var z
- for (z = maxZoom; z >= d.minZoom; z--) {
- var p = map.project(d.position, z)
- var rect = labelRect(p, offset, loc, d, minZoom, maxZoom, z)
- var candidates = trees[z].search(rect)
- if (candidates.length > 0)
- break
- }
- return {loc: loc, z: z + 1}
- }).filter(function (d) {
- return d.z <= maxZoom
- }).sort(function (a, b) {
- return a.z - b.z
- })[0]
- if (best !== undefined) {
- d.offset = calcOffset(d.offset, best.loc)
- d.minZoom = best.z
- d.anchor = best.loc
- for (var z = maxZoom; z >= best.z; z--) {
- var p = map.project(d.position, z)
- var rect = labelRect(p, d.offset, best.loc, d, minZoom, maxZoom, z)
- trees[z].insert(rect)
- }
- return d
- } else
- return undefined
- }).filter(function (d) { return d !== undefined })
- this.margin = 16
- if (labels.length > 0)
- this.margin += labels.map(function (d) {
- return d.width
- }).sort().reverse()[0]
- this.labels = rbush(9)
- this.labels.load(labels.map(mapRTree))
- this.redraw()
- },
- drawTile: function (canvas, tilePoint, zoom) {
- 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.labels)
- return
- var tileSize = this.options.tileSize
- var s = tilePoint.multiplyBy(tileSize)
- var map = this._map
- function projectNodes(d) {
- var p = map.project(d.label.position)
- p.x -= s.x
- p.y -= s.y
- return {p: p, label: d.label}
- }
- var bbox = getTileBBox(s, map, tileSize, this.margin)
- var labels = this.labels.search(bbox).map(projectNodes)
- var ctx = canvas.getContext("2d")
- ctx.lineWidth = 5
- ctx.strokeStyle = "rgba(255, 255, 255, 0.8)"
- ctx.miterLimit = 2
- function drawLabel(d) {
- ctx.font = d.label.font
- ctx.textAlign = d.label.anchor[0]
- ctx.textBaseline = d.label.anchor[1]
- ctx.fillStyle = d.label.fillStyle
- if (d.label.stroke)
- ctx.strokeText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1])
- ctx.fillText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1])
- }
- labels.filter(function (d) {
- return zoom >= d.label.minZoom
- }).forEach(drawLabel)
- }
- })
- return c
- })
|