Leaflet.js: передать свойство объекта конструктору класса - PullRequest
0 голосов
/ 31 августа 2018

У меня есть довольно много используемых определенных маркеров svg (глифов) (большое спасибо пользователю SO с именем rioV8 за его помощь в этом - и не только в этом -...), и в идеале я хотел бы, чтобы эти глифы получить их форму из структуры свойств объекта.

            //create feature properties
            var p = {
                "id": i,
                "popup": "Dot_" + i,
                "year": parseInt(data[i].year),
                "glyphName": "square",
                "size": 500 // Fixed size circle radius=~13
            };

Эти определяемые пользователем глифы расширяются L.circleMarker, и для простоты скажем, что их формы могут быть квадратными или ромбовидными. В настоящее время я расширяю L.Class и передаю glyphName в конструкторе: (не стесняйтесь критиковать это, если вам это не нравится)

        var glyph = L.Class.extend({
        initialize: function(glyphName) {
             glyphName === "square"? this.type = MarkerSquare:
             glyphName === "diamond"? this.type = MarkerDiamond:
             this.type = L.circleMarker; 
            },
        });

и когда мне нужно построить глифы, у меня появляется что-то вроде:

L.geoJson(myDots[i], {
    pointToLayer: function(feature, latlng) {
        var p = latlng;
        var myGlyph = new glyph('diamond')
        return new myGlyph.type(p, style(feature));
       },
       onEachFeature: onEachDot
   }).addTo(map);

Могу ли я получить форму, определяемую свойствами объекта, пожалуйста? В конце концов я пытаюсь объединить эти две строки

var myGlyph = new glyph('diamond')
return new myGlyph.type(p, style(feature));

к чему-то вроде

return new myGlyph.type(p, style(feature));

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

Спасибо! (Полный код ниже)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">

    <title>Chart</title>
    <style>
        html,
        body {
            height: 100%;
            margin: 0;
        }

        #map {
            width: 600px;
            height: 600px;
        }
    </style>


    <script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.3/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.3.3/dist/leaflet.js"></script>
</head>

<body>
    <div id="map"></div>
    <script>
        L.Canvas.include({
            _updateMarkerDiamond: function(layer) {
                if (!this._drawing || layer._empty()) {
                    return;
                }

                var p = layer._point,
                    ctx = this._ctx,
                    r = Math.max(Math.round(layer._radius), 6);

                this._drawnLayers[layer._leaflet_id] = layer;

                ctx.beginPath();
                ctx.moveTo(p.x - r, p.y);
                ctx.lineTo(p.x, p.y - r);
                ctx.lineTo(p.x + r, p.y);
                ctx.lineTo(p.x, p.y + r);
                ctx.lineTo(p.x - r, p.y);
                ctx.closePath();
                this._fillStroke(ctx, layer);
            }
        });
        var MarkerDiamond = L.CircleMarker.extend({
            _updatePath: function() {
                this._renderer._updateMarkerDiamond(this);
            }
        });


        L.Canvas.include({
            _updateMarkerSquare: function(layer) {
                if (!this._drawing || layer._empty()) {
                    return;
                }

                var p = layer._point,
                    ctx = this._ctx,
                    r = Math.max(Math.round(layer._radius), 5);

                this._drawnLayers[layer._leaflet_id] = layer;

                ctx.beginPath();
                ctx.moveTo(p.x - r, p.y - r);
                ctx.lineTo(p.x + r, p.y - r);
                ctx.lineTo(p.x + r, p.y + r);
                ctx.lineTo(p.x - r, p.y + r);
                ctx.lineTo(p.x - r, p.y - r);
                ctx.closePath();
                this._fillStroke(ctx, layer);
            }
        });
        var MarkerSquare = L.CircleMarker.extend({
            _updatePath: function() {
                this._renderer._updateMarkerSquare(this);
            }
        });

        var glyph = L.Class.extend({
            initialize: function(glyphName) {
                glyphName === "square"? this.type = MarkerSquare:
                glyphName === "diamond"? this.type = MarkerDiamond:
                this.type = L.circleMarker;
            },
        });


        var data = [];
        var NumOfPoints = 100;
        for (let i = 0; i < NumOfPoints; i++) {
            data.push({
                num: i,
                x: Math.random() * 60,
                y: Math.random() * 60,
                year: Math.floor(100 * Math.random())
            })
        }

        renderChart(data);

        function make_dots(data) {
            var arr = [];
            var nest = d3.nest()
                .key(function(d) {
                    return Math.floor(d.year / 10);
                })
                .entries(data);

            for (var k = 0; k < nest.length; ++k) {
                arr[k] = helper(nest[k].values);
            }
            return arr;
        }

        function helper(data) {
            dots = {
                type: "FeatureCollection",
                features: []
            };
            for (var i = 0; i < data.length; ++i) {
                x = data[i].x;
                y = data[i].y;
                var g = {
                    "type": "Point",
                    "coordinates": [x, y]
                };

                //create feature properties
                var p = {
                    "id": i,
                    "popup": "Dot_" + i,
                    "year": parseInt(data[i].year),
                    //"glyphName": "square",
                    "size": 500 // Fixed size circle radius=~13
                };

                //create features with proper geojson structure
                dots.features.push({
                    "geometry": g,
                    "type": "Feature",
                    "properties": p
                });
            }
            return dots;
        }


        //create color ramp
        function getColor(y) {
            return y > 90 ? '#6068F0' :
                y > 80 ? '#6B64DC' :
                y > 70 ? '#7660C9' :
                y > 60 ? '#815CB6' :
                y > 50 ? '#8C58A3' :
                y > 40 ? '#985490' :
                y > 30 ? '#A3507C' :
                y > 20 ? '#AE4C69' :
                y > 10 ? '#B94856' :
                y > 0 ? '#C44443' :
                '#D04030';
        }

        //calculate radius so that resulting circles will be proportional by area
        function getRadius(y) {
            r = Math.sqrt(y / Math.PI)
            return r;
        }

        var myRenderer;

        //create style, with fillColor picked from color ramp
        function style(feature) {
            return {
                radius: getRadius(feature.properties.size),
                fillColor: getColor(feature.properties.year),
                color: "#000",
                weight: 0,
                opacity: 1,
                fillOpacity: 0.9,
                renderer: myRenderer
            };
        }

        //create highlight style, with darker color and larger radius
        function highlightStyle(feature) {
            return {
                radius: getRadius(feature.properties.size) + 1.5,
                fillColor: "#FFCE00",
                color: "#FFCE00",
                weight: 1,
                opacity: 1,
                fillOpacity: 0.9,
            };
        }

        //attach styles and popups to the marker layer
        function highlightDot(e) {
            var layer = e.target;
            dotStyleHighlight = highlightStyle(layer.feature);
            layer.setStyle(dotStyleHighlight);
            if (!L.Browser.ie && !L.Browser.opera) {
                layer.bringToFront();
            }
        }

        function resetDotHighlight(e) {
            var layer = e.target;
            dotStyleDefault = style(layer.feature);
            layer.setStyle(dotStyleDefault);
        }

        function onEachDot(feature, layer) {
            layer.on({
                mouseover: highlightDot,
                mouseout: resetDotHighlight
            });
            var popup = '<table style="width:110px"><tbody><tr><td><div><b>Marker:</b></div></td><td><div>' + feature.properties.popup +
                '</div></td></tr><tr class><td><div><b>Group:</b></div></td><td><div>' + feature.properties.glyphName +
                '</div></td></tr><tr><td><div><b>X:</b></div></td><td><div>' + feature.geometry.coordinates[0] +
                '</div></td></tr><tr><td><div><b>Y:</b></div></td><td><div>' + feature.geometry.coordinates[1] +
                '</div></td></tr></tbody></table>'

            layer.bindPopup(popup);
        }



        function renderChart(data) {


            var myDots = make_dots(data);

            var minZoom = 0,
                maxZoom = 15;

            var map = L.map('map', {
                minZoom: minZoom,
                maxZoom: maxZoom
            }).setView([30, 30], 3);

            L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
                continuousWorld: false,
                minZoom: 0,
                noWrap: true
            }).addTo(map);

            myRenderer = L.canvas({
                padding: 0.5
            });

            // Define an array to keep layerGroups
            var dotlayer = [];

            //create marker layer and display it on the map
            for (var i = 0; i < myDots.length; i += 1) {
                dotlayer[i] = L.geoJson(myDots[i], {
                    pointToLayer: function(feature, latlng) {
                        var p = latlng;
                        
                        var myGlyph = new glyph('diamond')
                        return new myGlyph.type(p, style(feature));
                    },
                    onEachFeature: onEachDot
                }).addTo(map);
            }
            var cl = L.control.layers(null, {}).addTo(map);
            for (j = 0; j < dotlayer.length; j += 1) {
                var name = "Group " + j + "0-" + j + "9";
                cl.addOverlay(dotlayer[j], name);
            }
        }
    </script>
</body>

</html>

1 Ответ

0 голосов
/ 31 августа 2018

Вам необходимо сделать shape маркера свойством маркера и объединить детали рендеринга MarkerDiamond и MarkerSquare в другой маркер и решить, какую часть рендера следует рисовать с помощью if внутри метод _updateMarkerXX на основе свойства shape.

layer.options.shape содержит фигуру внутри процедуры рендеринга.

Или сделайте это в Marker рутине

var Marker = L.CircleMarker.extend({
     _updatePath: function() {
         if (this.options.shape === "square")
             this._renderer._updateMarkerSquare(this);
         if (this.options.shape === "diamond")
             this._renderer._updateMarkerDiamond(this);
     }
 });

    function style(feature) {
        return {
            radius: getRadius(feature.properties.size),
            shape: feature.properties.shape,
            fillColor: getColor(feature.properties.year),
            color: "#000",
            weight: 0,
            opacity: 1,
            fillOpacity: 0.9,
            renderer: myRenderer
        };
    }

Редактировать

Может быть полезно определить время использования магических чисел (Enums) вместо строк, потому что сравнение числа дешевле, чем сравнение строк. А у Aenaon есть около 300 000 маркеров, но это может быть незначительным.

...