UNPKG

leaflet-textpath

Version:
176 lines (143 loc) 5.77 kB
/* * Leaflet.TextPath - Shows text along a polyline * Inspired by Tom Mac Wright article : * http://mapbox.com/osmdev/2012/11/20/getting-serious-about-svg/ */ (function () { var __onAdd = L.Polyline.prototype.onAdd, __onRemove = L.Polyline.prototype.onRemove, __updatePath = L.Polyline.prototype._updatePath, __bringToFront = L.Polyline.prototype.bringToFront; var PolylineTextPath = { onAdd: function (map) { __onAdd.call(this, map); this._textRedraw(); }, onRemove: function (map) { map = map || this._map; if (map && this._textNode && map._renderer._container) map._renderer._container.removeChild(this._textNode); __onRemove.call(this, map); }, bringToFront: function () { __bringToFront.call(this); this._textRedraw(); }, _updatePath: function () { __updatePath.call(this); this._textRedraw(); }, _textRedraw: function () { var text = this._text, options = this._textOptions; if (text) { this.setText(null).setText(text, options); } }, setText: function (text, options) { this._text = text; this._textOptions = options; /* If not in SVG mode or Polyline not added to map yet return */ /* setText will be called by onAdd, using value stored in this._text */ if (!L.Browser.svg || typeof this._map === 'undefined') { return this; } var defaults = { repeat: false, fillColor: 'black', attributes: {}, below: false, }; options = L.Util.extend(defaults, options); /* If empty text, hide */ if (!text) { if (this._textNode && this._textNode.parentNode) { this._map._renderer._container.removeChild(this._textNode); /* delete the node, so it will not be removed a 2nd time if the layer is later removed from the map */ delete this._textNode; } return this; } text = text.replace(/ /g, '\u00A0'); // Non breakable spaces var id = 'pathdef-' + L.Util.stamp(this); var svg = this._map._renderer._container; this._path.setAttribute('id', id); if (options.repeat) { /* Compute single pattern length */ var pattern = L.SVG.create('text'); for (var attr in options.attributes) pattern.setAttribute(attr, options.attributes[attr]); pattern.appendChild(document.createTextNode(text)); svg.appendChild(pattern); var alength = pattern.getComputedTextLength(); svg.removeChild(pattern); /* Create string as long as path */ text = new Array(Math.ceil(isNaN(this._path.getTotalLength() / alength) ? 0 : this._path.getTotalLength() / alength)).join(text); } /* Put it along the path using textPath */ var textNode = L.SVG.create('text'), textPath = L.SVG.create('textPath'); var dy = options.offset || this._path.getAttribute('stroke-width'); textPath.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", '#'+id); textNode.setAttribute('dy', dy); for (var attr in options.attributes) textNode.setAttribute(attr, options.attributes[attr]); textPath.appendChild(document.createTextNode(text)); textNode.appendChild(textPath); this._textNode = textNode; if (options.below) { svg.insertBefore(textNode, svg.firstChild); } else { svg.appendChild(textNode); } /* Center text according to the path's bounding box */ if (options.center) { var textLength = textNode.getComputedTextLength(); var pathLength = this._path.getTotalLength(); /* Set the position for the left side of the textNode */ textNode.setAttribute('dx', ((pathLength / 2) - (textLength / 2))); } /* Change label rotation (if required) */ if (options.orientation) { var rotateAngle = 0; switch (options.orientation) { case 'flip': rotateAngle = 180; break; case 'perpendicular': rotateAngle = 90; break; default: rotateAngle = options.orientation; } var rotatecenterX = (textNode.getBBox().x + textNode.getBBox().width / 2); var rotatecenterY = (textNode.getBBox().y + textNode.getBBox().height / 2); textNode.setAttribute('transform','rotate(' + rotateAngle + ' ' + rotatecenterX + ' ' + rotatecenterY + ')'); } /* Initialize mouse events for the additional nodes */ if (this.options.interactive) { if (L.Browser.svg || !L.Browser.vml) { textPath.setAttribute('class', 'leaflet-interactive'); } var events = ['click', 'dblclick', 'mousedown', 'mouseover', 'mouseout', 'mousemove', 'contextmenu']; for (var i = 0; i < events.length; i++) { L.DomEvent.on(textNode, events[i], this.fire, this); } } return this; } }; L.Polyline.include(PolylineTextPath); L.LayerGroup.include({ setText: function(text, options) { for (var layer in this._layers) { if (typeof this._layers[layer].setText === 'function') { this._layers[layer].setText(text, options); } } return this; } }); })();