Невозможно вставить текст в карту листовки с помощью JavaScript и листовки.Плагин textPath

#javascript #leaflet

Вопрос:

В настоящее время я пытаюсь нарисовать несколько линий, кругов и полигонов на карте листовки с помощью JavaScript.

Я смог вставлять линии, круги и полигоны, а с помощью TooolTip текст вставляется в полилинии на карте листовки. Я использую это для встраивания этой карты в графический интерфейс Pyqt5.

Тип текста, которого я хочу добиться, такой же, как на рисунке 1, поэтому всплывающая подсказка не подходит, поскольку я могу получить текст только в виде рисунка 2.

Пробовали до сих пор

Я искал другие решения и нашел листовку.Плагин textPath. Я пытался использовать его в этом контексте, но у меня нет опыта работы с JavaScript, и я не смог заставить его работать.

Ниже приведены 3 файла, map.html , map.js и leaflet.textpath.js . map.html файл включает веб-канал Qt, карту листовки, сценарий JS и файл leaflet.textpath из GitHub https://github.com/makinacorpus/Leaflet .textPath.

Проблема

Ошибка возникает, когда я пытаюсь установить текст в качестве примера полилинии, так что это попытка использовать плагин. Я считаю, что ошибка в map.html файл, но я недостаточно знаком с JS или HTML, чтобы уловить ошибку. Я пытался использовать другие плагины в качестве повернутого маркера листовки, но этот сработал.

Ошибка

js: неперехваченная ошибка типа: не удается прочитать свойство ‘setAttribute’ неопределенного

Я был бы признателен за любые рекомендации.

введите описание изображения здесь

(1)

введите описание изображения здесь

(2)

Файлы

map.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>
 

map.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: amp;copy; <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]});

       }
 

leaflet.textpath.js скопировано с GitHub

 /*
 * 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 amp;amp; this._textNode amp;amp; 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 amp;amp; 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;
    }
});



})();
 

Ответ №1:

Для тех, кто начинает с JS, как я, и поскольку у меня недостаточно знаний JS, я опубликую, как я решил проблему, но я не совсем понимаю, почему это работает.

Проблема заключалась в опции preferCanvas: true для карты, я изменил способ добавления карты и плитки, и это решило проблему.

 // ADD TILE
                var tile_opt = {attribution: 'Map data: amp;copy; <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@189amp;gl=cnamp;x={x}amp;y={y}amp;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);
 

Кроме того, текстовый путь не был подходящим решением для тысячи вставок текста, поскольку панорамирование и масштабирование стали очень медленными. В итоге я использовал всплывающую подсказку, поскольку она может обрабатывать тысячи текстов, когда она не является постоянной.