From be65b093a1b11713b25e19bdd58d6ff3d9184032 Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Sun, 19 Apr 2020 21:58:35 +0200 Subject: [PATCH] Include Leaflet.streetlabels.js in repository --- htdocs/index.html | 2 +- htdocs/leafletjs/Leaflet.streetlabels.js | 196 +++++++++++++++++++++++ 2 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 htdocs/leafletjs/Leaflet.streetlabels.js diff --git a/htdocs/index.html b/htdocs/index.html index 27b0f9a..d9cff65 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -12,7 +12,7 @@ - + diff --git a/htdocs/leafletjs/Leaflet.streetlabels.js b/htdocs/leafletjs/Leaflet.streetlabels.js new file mode 100644 index 0000000..8685ac9 --- /dev/null +++ b/htdocs/leafletjs/Leaflet.streetlabels.js @@ -0,0 +1,196 @@ +/* + * + * +*/ +L.StreetLabels = L.LabelTextCollision + .extend({ + + options: { + /** + * Default property name to display the tooltip + */ + propertyName: 'name', + showLabelIf: null, + fontStyle: { + dynamicFontSize: false, + fontSize: 10, + fontSizeUnit: "px", + lineWidth: 4.0, + fillStyle: "black", + strokeStyle: "white", + }, + }, + + initialize: function (options) { + L.LabelTextCollision.prototype.initialize.call(this, options); + L.Util.stamp(this); + this._layers = this._layers || {}; + }, + + _initContainer: function (options) { + L.LabelTextCollision.prototype._initContainer.call(this, options); + + //Register the add/remove layers event to update the annotations accordingly + if (this._map) { + var handleLayerChanges = function () { + this._reset(); + this._redraw(); + }.bind(this); + this._map.on("layerremove", L.Util.throttle(handleLayerChanges, 32, this)); + } + }, + + _text: function (ctx, layer) { + + if (layer && layer.feature && layer.feature.properties && layer.feature.properties[this.options.propertyName] !== 'undefined') { + + if (this.options.showLabelIf) { + if (this.options.showLabelIf.call(this, layer.feature) === false) { + return; + } + } + + var layerText = layer.feature.properties[this.options.propertyName]; + ctx.globalAlpha = 1; + var p; + + // polygon or polyline + if (layer._parts.length === 0 || layer._parts[0].length === 0) { + return; + } + + if (layer instanceof L.Polygon && this._map.hasLayer(layer)) { + p = this._getCentroid(layer); + } + else { + p = this._getCenter(layer._parts[0]); + } + + if (!p) { + return; + } + + // label bounds offset + var offsetX = 0; + var offsetY = 0; + + /** + * TODO setting for custom font + */ + ctx.lineWidth = this.options.fontStyle.lineWidth; + + var fontSize = this.options.fontStyle.fontSize; + + if (this._map && this.options.fontStyle.dynamicFontSize === true) { + fontSize = this._getDynamicFontSize(); + + } + + ctx.font = fontSize + this.options.fontStyle.fontSizeUnit + " 'Helvetica Neue',Helvetica,Arial,sans-serif"; + + // Collision detection + var textWidth = (ctx.measureText(layerText).width) + p.x;// + offsetX; + + var textHeight = p.y + offsetY + 20; + + var bounds = L.bounds( + L.point(p.x + offsetX, p.y + offsetY), L.point( + textWidth, textHeight)); + + if (this.options.collisionFlg) { + + for (var index in this._textList) { + var pointBounds = this._textList[index]; + if (pointBounds.intersects(bounds)) { + return; + } + } + } + + this._textList.push(bounds); + + + ctx.fillStyle = this.options.fontStyle.fillStyle; + ctx.strokeStyle = this.options.fontStyle.strokeStyle; + + if (layer instanceof L.Polygon || layer instanceof L.CircleMarker) { + var textLength = ctx.measureText(layerText).width; + ctx.strokeText(layerText, p.x + offsetX - textLength / 2, p.y + offsetY); + ctx.fillText(layerText, p.x + offsetX - textLength / 2, p.y + offsetY); + } + if (layer instanceof L.Polyline) { + /** + * Render text alongside the polyline + * **/ + var startCoords = layer.getLatLngs()[0]; + var stopCoords = layer.getLatLngs()[layer.getLatLngs().length - 1]; + + //Flip lineString if bearing is negative + if (this._getBearing(startCoords, stopCoords) < 0) + layer = this._getLineStringReverse(layer); + + if (layer._parts) { + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.lineWidth = 3; + layer._parts.forEach(function (part) { + //Build the points list for the first part + var pathPoints = []; + for (var i = 0; i < part.length; i++) { + var linePart = part[i]; + pathPoints.push(linePart.x); + pathPoints.push(linePart.y); + } + + ctx.textPath(layerText, pathPoints); + }); + } + } + } + }, + + /*** + * Returns the bearing in degrees clockwise from north (0 degrees) + from the first L.LatLng to the second, at the first LatLng + @param {L.LatLng} latlng1: origin point of the bearing + @param {L.LatLng} latlng2: destination point of the bearing + @returns {float} degrees clockwise from north. + Source: https://makinacorpus.github.io/Leaflet.GeometryUtil/leaflet.geometryutil.js.html + */ + _getBearing: function (startCoords, stopCoords) { + var rad = Math.PI / 180, + lat1 = startCoords.lat * rad, + lat2 = stopCoords.lat * rad, + lon1 = startCoords.lng * rad, + lon2 = stopCoords.lng * rad, + y = Math.sin(lon2 - lon1) * Math.cos(lat2), + x = Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1); + var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360; + return bearing >= 180 ? bearing - 360 : bearing; + }, + + /** + Returns a clone with reversed coordinates. + @param {L.PolyLine} polyline polyline to reverse + @returns {L.PolyLine} polyline reversed + Source: https://makinacorpus.github.io/Leaflet.GeometryUtil/leaflet.geometryutil.js.html + */ + _getLineStringReverse: function (polyline) { + var latLngs = polyline.getLatLngs().slice(0).reverse(); + polyline.setLatLngs(latLngs); + return polyline; + }, + + _getDynamicFontSize: function () { + return parseInt(this._map.getZoom()); + }, + + _getCentroid: function (layer) { + if (layer && layer.getCenter && this._map) { + var latlngCenter = layer.getCenter(); + var containerPoint = this._map.latLngToContainerPoint(latlngCenter); + return this._map.containerPointToLayerPoint(containerPoint); + } + }, + });