nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

commit 57215a908ab74e3e9f5640bac2add2ff23a64c96
parent 813dca4b05e11924d7399279f1633919dd3b745b
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Fri,  3 Mar 2017 01:23:14 +0300

[canvas markers] refactored code, split big function drawTile

Diffstat:
Msrc/lib/leaflet.layer.canvasMarkers/index.js | 251+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 131 insertions(+), 120 deletions(-)

diff --git a/src/lib/leaflet.layer.canvasMarkers/index.js b/src/lib/leaflet.layer.canvasMarkers/index.js @@ -152,41 +152,37 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ canvas.width = this.options.tileSize; canvas.height = this.options.tileSize; L.Util.requestAnimFrame(() => { - this.drawTile(canvas, coords); - done(null, canvas); - }); + this.drawTile(canvas, coords).then(() => done(null, canvas)); + } + ); return canvas; }, - drawTile: function(canvas, coords) { - const - zoom = coords.z, - tileSize = this.options.tileSize, - tileN = coords.y * tileSize, - tileW = coords.x * tileSize, - tileS = tileN + tileSize, - tileE = tileW + tileSize; + selectMarkersForDraw: function({tileN, tileS, tileE, tileW}, zoom, withPaddings) { + // FIXME: padding should depend on options.iconScale and fontSize + if (!this._map) { + return {}; + } const - iconsHorPad = 520, - iconsVertPad = 50, - labelsHorPad = 256, - labelsVertPad = 20; + iconsHorPad = withPaddings ? 520 : 0, + iconsVertPad = withPaddings ? 50 : 0, + labelsHorPad = withPaddings ? 256 : 0, + labelsVertPad = withPaddings ? 20 : 0; const iconsBounds = L.latLngBounds( - this._map.unproject([tileW - iconsHorPad, tileS + iconsHorPad], zoom), - this._map.unproject([tileE + iconsHorPad, tileN - iconsVertPad], zoom) + this._map.unproject(L.point(tileW - iconsHorPad, tileS + iconsHorPad), zoom), + this._map.unproject(L.point(tileE + iconsHorPad, tileN - iconsVertPad), zoom) ), labelsBounds = L.latLngBounds( - this._map.unproject([tileW - labelsHorPad, tileS + labelsHorPad], zoom), - this._map.unproject([tileE + labelsHorPad, tileN - labelsVertPad], zoom) + this._map.unproject(L.point(tileW - labelsHorPad, tileS + labelsHorPad), zoom), + this._map.unproject(L.point(tileE + labelsHorPad, tileN - labelsVertPad), zoom) ); - const iconUrls = [], markerJobs = {}; - const pointsForMarkers = this.rtree.search( - { + // used only to preload icons + const pointsForMarkers = this.rtree.search({ minX: iconsBounds.getWest(), minY: iconsBounds.getSouth(), maxX: iconsBounds.getEast(), @@ -194,13 +190,13 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ } ); + // used to place labels const pointsForLabels = this.rtree.search({ minX: labelsBounds.getWest(), minY: labelsBounds.getSouth(), maxX: labelsBounds.getEast(), maxY: labelsBounds.getNorth() } ); - for (let marker of pointsForMarkers) { const p = this._map.project(marker.latlng, zoom); let icon = marker.icon; @@ -211,101 +207,116 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ let markerId = L.stamp(marker); markerJobs[markerId] = {marker: marker, icon: icon, projectedXY: p}; } - this.preloadIcons(iconUrls).then(() => { - if (!this._map) { - return; - } - const textHeight = this.options.labelFontSize; - if (this._labelPositionsZoom !== zoom) { - this._labelPositionsZoom = zoom; - this.resetLabels(); - } - const ctx = canvas.getContext('2d'); - ctx.font = L.Util.template('bold {size}px {name}', - {'name': this.options.labelFontName, 'size': this.options.labelFontSize} - ); - for (let [markerId, job] of Object.entries(markerJobs)) { - let img = this._images[job.icon.url]; - job.img = img; - const imgW = Math.round(img.width * this.options.iconScale); - const imgH = Math.round(img.height * this.options.iconScale); - if (!(markerId in this._iconPositions)) { - let x = job.projectedXY.x - job.icon.center[0] * this.options.iconScale; - let y = job.projectedXY.y - job.icon.center[1] * this.options.iconScale; - x = Math.round(x); - y = Math.round(y); - this._iconPositions[markerId] = [x, y]; - this._regions.insert({ - minX: x, minY: y, maxX: x + imgW, maxY: y + imgH, - marker: job.marker, isLabel: false - } - ); + return {iconUrls, markerJobs, pointsForLabels}; + }, + + drawSelectedMarkers: function(canvas, pixelExtents, markerJobs, pointsForLabels, zoom) { + const {tileN, tileW, tileS, tileE} = pixelExtents; + const textHeight = this.options.labelFontSize; + if (this._labelPositionsZoom !== zoom) { + this._labelPositionsZoom = zoom; + this.resetLabels(); + } + const ctx = canvas.getContext('2d'); + ctx.font = L.Util.template('bold {size}px {name}', + {'name': this.options.labelFontName, 'size': this.options.labelFontSize * this.options.iconScale} + ); + for (let [markerId, job] of Object.entries(markerJobs)) { + let img = this._images[job.icon.url]; + job.img = img; + const imgW = Math.round(img.width * this.options.iconScale); + const imgH = Math.round(img.height * this.options.iconScale); + if (!(markerId in this._iconPositions)) { + let x = job.projectedXY.x - job.icon.center[0] * this.options.iconScale; + let y = job.projectedXY.y - job.icon.center[1] * this.options.iconScale; + x = Math.round(x); + y = Math.round(y); + this._iconPositions[markerId] = [x, y]; + this._regions.insert({ + minX: x, minY: y, maxX: x + imgW, maxY: y + imgH, + marker: job.marker, isLabel: false } - let [x, y] = this._iconPositions[markerId]; - job.iconCenter = [x + imgW / 2, y + imgH / 2]; - job.iconSize = [imgW, imgH]; + ); + } + let [x, y] = this._iconPositions[markerId]; + job.iconCenter = [x + imgW / 2, y + imgH / 2]; + job.iconSize = [imgW, imgH]; + } + for (let marker of pointsForLabels) { + const markerId = L.stamp(marker); + const job = markerJobs[markerId]; + let label = job.marker.label; + if (label) { + if (typeof label === 'function') { + label = label(job.marker); } - for (let marker of pointsForLabels) { - const markerId = L.stamp(marker); - const job = markerJobs[markerId]; - let label = job.marker.label; - if (label) { - if (typeof label === 'function') { - label = label(job.marker); + job.label = label; + if (!(markerId in this._labelPositions)) { + const textWidth = ctx.measureText(label).width; + const p = this.findLabelPosition(job.iconCenter, job.iconSize, textWidth, textHeight); + this._labelPositions[markerId] = p; + let [x, y] = p; + this._regions.insert({ + minX: x, minY: y, maxX: x + textWidth, maxY: y + textHeight, + marker: job.marker, isLabel: true } - job.label = label; - if (!(markerId in this._labelPositions)) { - const textWidth = ctx.measureText(label).width; - const p = this.findLabelPosition(job.iconCenter, job.iconSize, textWidth, textHeight); - this._labelPositions[markerId] = p; - let [x, y] = p; - this._regions.insert({ - minX: x, minY: y, maxX: x + textWidth, maxY: y + textHeight, - marker: job.marker, isLabel: true - } - ); + ); - } - } else { - this._labelPositions[markerId] = null; - } } + } else { + this._labelPositions[markerId] = null; + } + } - const regionsInTile = this._regions.search({minX: tileW, minY: tileN, maxX: tileE, maxY: tileS}); - // draw labels - for (let region of regionsInTile) { - if (region.isLabel) { - //TODO: set font name ant size in options - const markerId = L.stamp(region.marker); - const job = markerJobs[markerId]; - const p = this._labelPositions[markerId]; - const x = p[0] - tileW; - const y = p[1] - tileN; - ctx.textBaseline = 'bottom'; - ctx.shadowColor = '#fff'; - ctx.strokeStyle = '#fff'; - ctx.fillStyle = '#000'; - ctx.lineWidth = 1; - ctx.shadowBlur = 2; - ctx.strokeText(job.label, x, y + textHeight); - ctx.shadowBlur = 0; - ctx.fillText(job.label, x, y + textHeight); - } - } - // draw icons - for (let region of regionsInTile) { - if (!region.isLabel) { - const markerId = L.stamp(region.marker); - const job = markerJobs[markerId]; - const p = this._iconPositions[markerId]; - const x = p[0] - tileW; - const y = p[1] - tileN; - ctx.drawImage(job.img, x, y, job.iconSize[0], job.iconSize[1]); - } - } + const regionsInTile = this._regions.search({minX: tileW, minY: tileN, maxX: tileE, maxY: tileS}); + // draw labels + for (let region of regionsInTile) { + if (region.isLabel) { + //TODO: set font name ant size in options + const markerId = L.stamp(region.marker); + const job = markerJobs[markerId]; + const p = this._labelPositions[markerId]; + const x = p[0] - tileW; + const y = p[1] - tileN; + ctx.textBaseline = 'bottom'; + ctx.shadowColor = '#fff'; + ctx.strokeStyle = '#fff'; + ctx.fillStyle = '#000'; + ctx.lineWidth = 1; + ctx.shadowBlur = 2; + ctx.strokeText(job.label, x, y + textHeight); + ctx.shadowBlur = 0; + ctx.fillText(job.label, x, y + textHeight); } - ); - return this; + } + // draw icons + for (let region of regionsInTile) { + if (!region.isLabel) { + const markerId = L.stamp(region.marker); + const job = markerJobs[markerId]; + const p = this._iconPositions[markerId]; + const x = p[0] - tileW; + const y = p[1] - tileN; + ctx.drawImage(job.img, x, y, job.iconSize[0], job.iconSize[1]); + } + } + }, + + drawTile: async function(canvas, coords) { + const + zoom = coords.z, + tileSize = this.options.tileSize, + tileN = coords.y * tileSize, + tileW = coords.x * tileSize, + tileS = tileN + tileSize, + tileE = tileW + tileSize; + const pixelExtents = {tileN, tileS, tileE, tileW}; + const {iconUrls, markerJobs, pointsForLabels} = this.selectMarkersForDraw(pixelExtents, zoom, true); + if (!markerJobs) { + return; + } + await this.preloadIcons(iconUrls); + this.drawSelectedMarkers(canvas, pixelExtents, markerJobs, pointsForLabels, zoom); }, resetLabels: function() { @@ -318,9 +329,10 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ if (!e.latlng) { return; } - var p = this._map.project(e.latlng), - region = this._regions.search({minX: p.x, minY: p.y, maxX: p.x, maxY: p.y})[0], - marker; + const + p = this._map.project(e.latlng), + region = this._regions.search({minX: p.x, minY: p.y, maxX: p.x, maxY: p.y})[0]; + let marker; if (region) { marker = region.marker; } else { @@ -330,7 +342,7 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ }, onMouseMove: function(e) { - var marker = this.findMarkerFromMouseEvent(e); + const marker = this.findMarkerFromMouseEvent(e); if (this._hoverMarker !== marker) { if (this._hoverMarker) { this.fire('markerleave', {marker: this._hoverMarker}); @@ -343,11 +355,10 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ }, showTooltip: function(e) { - var text; if (!e.marker.tooltip) { return; } - text = e.marker.tooltip; + let text = e.marker.tooltip; if (typeof text === 'function') { text = text(e.marker); if (!e.marker.tooltip) { @@ -355,7 +366,7 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ } } this.toolTip.innerHTML = text; - var p = this._map.latLngToLayerPoint(e.marker.latlng); + const p = this._map.latLngToLayerPoint(e.marker.latlng); L.DomUtil.setPosition(this.toolTip, p); L.DomUtil.addClass(this.toolTip, 'canvas-marker-tooltip-on'); }, @@ -379,7 +390,7 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ }, onClick: function(e) { - var marker = this.findMarkerFromMouseEvent(e); + const marker = this.findMarkerFromMouseEvent(e); if (marker) { L.extend(e, {marker: marker}); this.fire('markerclick', e); @@ -387,7 +398,7 @@ L.Layer.CanvasMarkers = L.GridLayer.extend({ }, onRightClick: function(e) { - var marker = this.findMarkerFromMouseEvent(e); + const marker = this.findMarkerFromMouseEvent(e); if (marker) { L.extend(e, {marker: marker}); this.fire('markercontextmenu', e);