javascript - 无法使用 JavaScript 和 Leaflet.TextPath 插件将文本插入到传单地图中
问题描述
我目前正在尝试使用 JavaScript 将一些线、圆和多边形绘制到传单地图中。
我已经能够插入线、圆和多边形,并通过使用 ToolTip 将文本插入到 Leaflet 地图中的折线中。我正在使用它将此地图嵌入到 Pyqt5 GUI。
我想要实现的文本类型如图 1 所示,因此 ToolTip 不适合,因为我只能获取图 2 中的文本。
到目前为止已经尝试过
我已经寻找其他解决方案并找到了 Leaflet.TextPath 插件。我尝试在这种情况下使用它,但我没有使用 JavaScript 的经验,也无法让它工作。
下面有3个文件,map.html、map.js和leaflet.textpath.js。map.html 文件包括来自 GitHub https://github.com/makinacorpus/Leaflet.TextPath的 Qt WebChannel、LeafLet 地图、JS 脚本和 leaflet.textpath 文件。
问题
当我尝试将文本设置为示例折线时发生错误,因此这是尝试使用插件。我相信错误在 map.html 文件中,但我对 JS 或 HTML 不够熟悉,无法捕捉到错误。我尝试使用其他插件作为 Leaflet Rotated Marker,但这一个确实有效。
错误
js:未捕获的类型错误:无法读取未定义的属性“setAttribute”
我将不胜感激任何指导。
(1)
(2)
文件
地图.html
<!DOCTYPE html>
<html>
<head>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" />
<script type="text/javascript" src="map.js"></script>
<script src="leaflet.textpath.js"></script>
<style>
body { padding: 0; margin: 0; }
html, body, #map { height: 100%; width=100%}
</style>
</head>
<body onload="initialize()">
<div id="map"></div>
</body>
</html>
地图.js
var map;
var coords = [[[-2.9116417837562043, -79.03574879233575], [-2.911946317883794, -79.03491894398161], [-2.912403030611765, -79.03362471656573]], [[-2.9113915485759168, -79.0344148954767], [-2.9115095650671665, -79.03409894066323], [-2.9117645016593343, -79.03334145899967]]];
var arrows = [[[[-2.911800140520451, -79.03531727419156], [-2.911768021089238, -79.03535268947631], [-2.911788113588835, -79.03535004729815], [-2.911801770657676, -79.03536494151672]], [[-2.9121805563780936, -79.03425516236084], [-2.912148881780239, -79.03429097175606], [-2.9121689397158708, -79.03428808152826], [-2.9121827817497454, -79.03430280585212]]], [[[-2.9114567419946438, -79.03424035911497], [-2.9114244189203617, -79.03427559058679], [-2.9114445263105413, -79.0342730630545], [-2.911458097271369, -79.0342880349087]], [[-2.911642671195043, -79.03370344867531], [-2.911611523070874, -79.03373971200101], [-2.9116315366609524, -79.03373653221718], [-2.911645592328774, -79.03375105493932]]]];
function initialize(){
// ADD MAP
map = L.map('map',{zoomSnap: 0, zoomControl: false, preferCanvas: true, inertia: false, doubleClickZoom: false, wheelPxPerZoomLevel: 58.35});
//GET TILE
L.tileLayer('http://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
attribution: 'Map data: © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://cloudmade.com">CloudMade</a>' ,
maxZoom: 10000, reuseTiles: true,
}
).addTo(map);
// ADD LINES
var style = {color: "#F44336", weight: 5, opacity: 1 };
for (let i of coords) {L.polyline(i, style).addTo(map);}
// ADD CIRCLES
for (let i = 0; i < coords.length; i++) {
for (let j = 0; j < coords[i].length; j++){L.circle([coords[i][j][0],coords[i][j][1]], {color: '#00B4FF',fillColor: '#00B4FF',fillOpacity: 0.5,radius: 1.9}).addTo(map);}
}
// ADD ARROWS
for (let i = 0; i < arrows.length; i++) {
for (let j = 0; j < arrows[i].length; j++){L.polygon(arrows[i][j], {color: '#00B4FF',fillColor: '#00B4FF',fillOpacity: 0.5,radius: 5}).addTo(map);}
}
// TRY ADDING TEXT TO POLILYNE
var plane = L.polyline([[-2.9116417837562043, -79.03574879233575], [-2.911946317883794, -79.03491894398161], [-2.912403030611765, -79.03362471656573]], {weight: 1, color: 'black', dashArray: '2, 2'}).addTo(map);
plane.setText('TEXT', {repeat: true,offset: 8, attributes: {'font-weight': 'bold','font-size': '24'}}); //THIS LINES CAUSES AN ERROR
// ADD SCALE
var scale = L.control.scale();
scale.addTo(map);
//SET BOUNDS
var southWest = new L.LatLng(-2.9125375550722334,-79.03576792871915),
northEast = new L.LatLng(-2.9112634693443318,-79.03332005718264),
bounds = new L.LatLngBounds(southWest, northEast);
map.fitBounds(bounds, {padding: [0, 0]});
}
从 GitHub 复制的leaflet.textpath.js
/*
* 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;
}
});
})();
解决方案
对于像我这样从 JS 开始的人,由于我没有足够的 JS 知识,我将发布我是如何解决问题的,但我真的不明白为什么会这样。
问题是选项 preferCanvas: true 的地图,我改变了添加地图和瓷砖的方式,这解决了问题。
// ADD TILE
var tile_opt = {attribution: 'Map data: © <a href="https://developers.google.com/maps"> Google</a>, <a href="https://developers.google.com/maps/terms"> Terms of Use. </a>' , maxZoom: 10000, reuseTiles: true, noWrap: true }
var _tile = L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', tile_opt);
// ADD MAP
var map_opt = {zoomSnap: 0, zoomControl: false, inertia: false, doubleClickZoom: false, wheelPxPerZoomLevel: 58.35}
var map = L.map('map', map_opt).fitWorld().addLayer(_tile);
另外,文本路径不是插入数千个文本的合适解决方案,因为平移和缩放变得非常慢。我最终使用了 ToolTip,因为当它不是永久性的时,它可以处理数千个文本。
推荐阅读
- java - Spring Boot 嵌入式 Apache Derby
- kubernetes - 如何经营一次性工作?
- python - 如何在 Pandas 中找到方法的源代码?
- android - RenderScript 删除位图上带有 alpha 的像素
- c++ - 在 C++ 中寻找排列和组合
- laravel - 如何在登录时自定义 laravel 护照错误?
- javascript - 如何使用 Javascript dom 编辑 html 元素?
- javascript - 如何在 Native File System API 中读取文件内容
- linux - 如何在终端中使输入和输出颜色不同?
- javascript - 如何通过连接所有字符串来折叠数组,但保留对象