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';}//计算半径,使所得圆按面积成比例函数 getRadius(y) {r = Math.sqrt(y/Math.PI)返回 r;}//这是非常重要的!使用画布,否则图表对于浏览器来说太重//点数太高,在这种情况下,我们有大约 300K 点要绘制var myRenderer;//= L.canvas({//填充:0.5//});//创建样式,从色带中选取fillColor功能风格(功能){返回 {半径:getRadius(feature.properties.size),fillColor: getColor(feature.properties.year),颜色:#000",权重:0,不透明度:1,填充不透明度:0.9,渲染器:myRenderer};}//创建高亮样式,颜色更深,半径更大功能亮点风格(功能){返回 {半径:getRadius(feature.properties.size) + 1.5,填充颜色:#FFCE00",颜色:#FFCE00",重量:1,不透明度:1,填充不透明度:0.9};}//将样式和弹出窗口附加到标记层功能亮点点(e){var 层 = e.target;dotStyleHighlight = highlightStyle(layer.feature);layer.setStyle(dotStyleHighlight);如果 (!L.Browser.ie && !L.Browser.opera) {layer.bringToFront();}}函数 resetDotHighlight(e) {var 层 = e.target;dotStyleDefault = style(layer.feature);layer.setStyle(dotStyleDefault);}函数 onEachDot(特征,层){层上({鼠标悬停:highlightDot,mouseout:resetDotHighlight});var popup = '<table style="width:110px"><tbody><tr><td><div><b>标记:</b></div></td>'+ 特性.properties.popup +'</div></td></tr><tr类><td><div><b>组:</b></div></td><td><div>'+ 特性.properties.year +'</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(弹出);}功能makeIcon(名称,颜色){如果(名称==钻石"){//这是标记的 SVGvar icon = "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='20' height='20'> " +"<path stroke=" + "'" + 颜色 + "'" + " stroke-width='3' fill='none' " +" d='M10,1 5,10 10,19, 15,10Z'/</svg>";}//基于 http://www.smiffysplace.com/stars.htmlif (name == "6-pointed-star") {//这是标记的 SVGvar icon = "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='28' height='28'> " +"<path stroke=" + "'" + 颜色 + "'" + " stroke-width='3' fill='none' " +" d='m13 13m0 5l5 3.66l-0.67 -6.16l5.67 -2.5l-5.67 -2.5l0.67 -6.16l-5 3.66l-5 -3.66l0.67 6.16l-5.65.672l.67-0.67 6.16z'/></svg>";}//这里是技巧,base64 编码 URLvar svgURL = "data:image/svg+xml;base64," + btoa(icon);//创建图标var svgIcon = L.icon({图标网址:svgURL,图标大小:[20, 20],阴影大小:[12, 10],iconAnchor: [5, 5],popupAnchor: [5, -5]});返回 svgIcon}函数渲染图表(数据){var myDots = make_dots(data);var minZoom = 0,maxZoom = 15;var map = L.map('地图', {minZoom: minZoom,最大变焦:最大变焦}).setView([0.5, 0.5], 5);L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {连续世界:假,最小缩放:0,noWrap: 真}).addTo(地图);myRenderer = L.canvas({ padding: 0.5 });//定义一个数组来保存 layerGroupsvar dotlayer = [];//创建标记层并将其显示在地图上for (var i = 0; i < myDots.length; i += 1) {dotlayer[i] = L.geoJson(myDots[i], {pointToLayer:函数(特征,纬度){var p = latlng;//返回 L.circleMarker(p, style(feature));//console.log("起始标记.")//返回 L.marker(p, {//渲染器: myRenderer,//icon: makeIcon('6-pointed-star', style(feature).color),//});return new Marker6Point(p, style(feature));},onEachFeature:onEachDot}).addTo(地图);}var cl = L.control.layers(null, {}).addTo(map);for (j = 0; j </html>如果这还不够快,您可以随时设置自己的图块服务器并将标记烘焙到不同缩放级别的图块中(在透明的 PNG 文件中).并将其用作地形图块顶部的单独图块层.您没有简单的弹出窗口,但 300K 标记的渲染速度非常快.您可以为要显示的每个图层/组制作图块叠加.所有服务都来自同一个磁贴服务器. I am trying to use some user defined svg icons for markers on leaflet but I think the whole task gets too heavy for my browser.
Until now I was using L.circleMarker but I now have to use markers like asterisks, arrows, stars etc instead so I decided to do them as svg path and then plug them in instead of my circleMarkers. To make things more complicated I have more than 300K points. With the circleMarkers I was able to make a workable chart, not lightning fast but quite acceptable especially when a fairly deep zoom was used to be able to distinguish individual points (otherwise everything was like a big blob and useless to study).
With the svg markers however the chart becomes so computationally heavy that the browser just hangs. I have played around with 100, 1000 and 10000 points and even with 1000 points the difference becomes apparent. Is there any solution to this please, has anyone used svg markers with lots of data points? I think canvas is properly employed in my code, especially for the circleMarkers but I might be mistaken. Any help highly appreciated. Code in the snippet, comment/uncomment the few lines towards the bottom: return L.circleMarker(p, style(feature));
or console.log("Starting markers.")
return L.marker(p, {
renderer: myRenderer,
icon: makeIcon('6-pointed-star', style(feature).color),
});
to switch from the circleMarkers to svg markers.
Many thanks!
PS. With svg markers the code breaks with the highlight event but I have quite understood whats wrong..it works fine with circleMarkers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Chart</title>
<style>
#tooltip {
position:absolute;
background-color: #2B292E;
color: white;
font-family: sans-serif;
font-size: 15px;
pointer-events: none; /*dont trigger events on the tooltip*/
padding: 15px 20px 10px 20px;
text-align: center;
opacity: 0;
border-radius: 4px;
}
html, body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 600px;
}
</style>
<!-- Reference style.css -->
<!-- <link rel="stylesheet" type="text/css" href="style.css">-->
<!-- Reference minified version of D3 -->
<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.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
</head>
<body>
<div id="map"></div>
<script>
var data = [];
var NumOfPoints = 100
for (let i = 0; i < NumOfPoints; i++) {
data.push({
num: i,
x: Math.random(),
y: Math.random(),
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),
"size": 30 // Fixed size
};
//create features with proper geojson structure
dots.features.push({
"geometry": g,
"type": "Feature",
"properties": p
});
}
return dots;
}
//////////////////////////////////////////////////////////////////////////////////////////////
//styling and displaying the data as circle markers//
//////////////////////////////////////////////////////////////////////////////////////////////
//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;
}
// This is very important! Use a canvas otherwise the chart is too heavy for the browser when
// the number of points is too high, as in this case where we have around 300K points to plot
var myRenderer = L.canvas({
padding: 0.5
});
//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.year +
'</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 makeIcon(name, color) {
if (name == "diamond") {
// here's the SVG for the marker
var icon = "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='20' height='20'> " +
"<path stroke=" + "'" + color + "'" + " stroke-width='3' fill='none' " +
" d='M10,1 5,10 10,19, 15,10Z'/></svg>";
}
// Based on http://www.smiffysplace.com/stars.html
if (name == "6-pointed-star") {
// here's the SVG for the marker
var icon = "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='28' height='28'> " +
"<path stroke=" + "'" + color + "'" + " stroke-width='3' fill='none' " +
" d='m13 13m0 5l5 3.6599999999999966l-0.6700000000000017 -6.159999999999997l5.670000000000002 -2.5l-5.670000000000002 -2.5l0.6700000000000017 -6.159999999999997l-5 3.6599999999999966l-5 -3.6599999999999966l0.6700000000000017 6.159999999999997l-5.670000000000002 2.5l5.670000000000002 2.5l-0.6700000000000017 6.159999999999997z'/></svg>";
}
// here's the trick, base64 encode the URL
var svgURL = "data:image/svg+xml;base64," + btoa(icon);
// create icon
var svgIcon = L.icon({
iconUrl: svgURL,
iconSize: [20, 20],
shadowSize: [12, 10],
iconAnchor: [5, 5],
popupAnchor: [5, -5]
});
return svgIcon
}
function renderChart(data) {
var myDots = make_dots(data);
var minZoom = 0,
maxZoom = 15;
var map = L.map('map', {
minZoom: minZoom,
maxZoom: maxZoom
}).setView([0.5, 0.5], 10);
L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
continuousWorld: false,
minZoom: 0,
noWrap: true
}).addTo(map);
var 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;
// return L.circleMarker(p, style(feature));
console.log("Starting markers.")
return L.marker(p, {
renderer: myRenderer,
icon: makeIcon('6-pointed-star', style(feature).color),
});
},
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>
解决方案 Your svg 6-point markers are not rendered by the Canvas. Have a look with the DevTools and you see they are img tags with a base-64 encoded svg as source. If you have a large number of markers this will slow down the HTML renderer.
The CircleMarkers are rendered on the canvas.
By creating a new L.Path subclass you can draw any marker you want on the canvas and let leaflet do what it does best. Make these leaflet modifications before any other JS code otherwise it will complain that it is not a constructor. L.Canvas.include({
_updateMarker6Point: function (layer) {
if (!this._drawing || layer._empty()) { return; }
var p = layer._point,
ctx = this._ctx,
r = Math.max(Math.round(layer._radius), 1);
this._drawnLayers[layer._leaflet_id] = layer;
ctx.beginPath();
ctx.moveTo(p.x + r , p.y );
ctx.lineTo(p.x + 0.43*r, p.y + 0.25 * r);
ctx.lineTo(p.x + 0.50*r, p.y + 0.87 * r);
ctx.lineTo(p.x , p.y + 0.50 * r);
ctx.lineTo(p.x - 0.50*r, p.y + 0.87 * r);
ctx.lineTo(p.x - 0.43*r, p.y + 0.25 * r);
ctx.lineTo(p.x - r, p.y );
ctx.lineTo(p.x - 0.43*r, p.y - 0.25 * r);
ctx.lineTo(p.x - 0.50*r, p.y - 0.87 * r);
ctx.lineTo(p.x , p.y - 0.50 * r);
ctx.lineTo(p.x + 0.50*r, p.y - 0.87 * r);
ctx.lineTo(p.x + 0.43*r, p.y - 0.25 * r);
ctx.closePath();
this._fillStroke(ctx, layer);
}
});
var Marker6Point = L.CircleMarker.extend({
_updatePath: function () {
this._renderer._updateMarker6Point(this);
}
});
You use it the same as for the circleMarker return new Marker6Point(p, style(feature));
In the code there are 2 L.canvas instances and there are 2 variables myRenderer . I have kept the global variable but assign it only when there is an L.map() constructed in the renderChart() function.
For the demo I have used a larger area with markers and used 10000 markers. My browser has no problems with that. I have increased the size in the properties to 500 so we get a marker with radius 13 pixels, so you can see the star clearly.
I have used the latest leaflet version 1.3.3.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Chart</title>
<style>
#tooltip {
position:absolute;
background-color: #2B292E;
color: white;
font-family: sans-serif;
font-size: 15px;
pointer-events: none; /*dont trigger events on the tooltip*/
padding: 15px 20px 10px 20px;
text-align: center;
opacity: 0;
border-radius: 4px;
}
html, body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 600px;
}
</style>
<!-- Reference style.css -->
<!-- <link rel="stylesheet" type="text/css" href="style.css">-->
<!-- Reference minified version of D3 -->
<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({
_updateMarker6Point: function (layer) {
if (!this._drawing || layer._empty()) { return; }
var p = layer._point,
ctx = this._ctx,
r = Math.max(Math.round(layer._radius), 1);
this._drawnLayers[layer._leaflet_id] = layer;
ctx.beginPath();
ctx.moveTo(p.x + r , p.y );
ctx.lineTo(p.x + 0.43*r, p.y + 0.25 * r);
ctx.lineTo(p.x + 0.50*r, p.y + 0.87 * r);
ctx.lineTo(p.x , p.y + 0.50 * r);
ctx.lineTo(p.x - 0.50*r, p.y + 0.87 * r);
ctx.lineTo(p.x - 0.43*r, p.y + 0.25 * r);
ctx.lineTo(p.x - r, p.y );
ctx.lineTo(p.x - 0.43*r, p.y - 0.25 * r);
ctx.lineTo(p.x - 0.50*r, p.y - 0.87 * r);
ctx.lineTo(p.x , p.y - 0.50 * r);
ctx.lineTo(p.x + 0.50*r, p.y - 0.87 * r);
ctx.lineTo(p.x + 0.43*r, p.y - 0.25 * r);
ctx.closePath();
this._fillStroke(ctx, layer);
}
});
var Marker6Point = L.CircleMarker.extend({
_updatePath: function () {
this._renderer._updateMarker6Point(this);
}
});
var data = [];
var NumOfPoints = 10000;
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),
"size": 500 // Fixed size circle radius=~13
};
//create features with proper geojson structure
dots.features.push({
"geometry": g,
"type": "Feature",
"properties": p
});
}
return dots;
}
//////////////////////////////////////////////////////////////////////////////////////////////
//styling and displaying the data as circle markers//
//////////////////////////////////////////////////////////////////////////////////////////////
//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;
}
// This is very important! Use a canvas otherwise the chart is too heavy for the browser when
// the number of points is too high, as in this case where we have around 300K points to plot
var myRenderer;
// = L.canvas({
// padding: 0.5
// });
//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.year +
'</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 makeIcon(name, color) {
if (name == "diamond") {
// here's the SVG for the marker
var icon = "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='20' height='20'> " +
"<path stroke=" + "'" + color + "'" + " stroke-width='3' fill='none' " +
" d='M10,1 5,10 10,19, 15,10Z'/></svg>";
}
// Based on http://www.smiffysplace.com/stars.html
if (name == "6-pointed-star") {
// here's the SVG for the marker
var icon = "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='28' height='28'> " +
"<path stroke=" + "'" + color + "'" + " stroke-width='3' fill='none' " +
" d='m13 13m0 5l5 3.66l-0.67 -6.16l5.67 -2.5l-5.67 -2.5l0.67 -6.16l-5 3.66l-5 -3.66l0.67 6.16l-5.67 2.5l5.67 2.5l-0.67 6.16z'/></svg>";
}
// here's the trick, base64 encode the URL
var svgURL = "data:image/svg+xml;base64," + btoa(icon);
// create icon
var svgIcon = L.icon({
iconUrl: svgURL,
iconSize: [20, 20],
shadowSize: [12, 10],
iconAnchor: [5, 5],
popupAnchor: [5, -5]
});
return svgIcon
}
function renderChart(data) {
var myDots = make_dots(data);
var minZoom = 0,
maxZoom = 15;
var map = L.map('map', {
minZoom: minZoom,
maxZoom: maxZoom
}).setView([0.5, 0.5], 5);
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;
// return L.circleMarker(p, style(feature));
// console.log("Starting markers.")
// return L.marker(p, {
// renderer: myRenderer,
// icon: makeIcon('6-pointed-star', style(feature).color),
// });
return new Marker6Point(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>
If this is not fast enough you can always set up your own tile server and bake the markers into the tiles at the different zoom levels (in transparent PNG files). And use this as a separate tile layer on top of the terrain tiles. You don't have easy popups but the rendering of 300K markers is pretty fast. You can make a tile overlay for each layer/group you want to show. All serviced from the same tile server.
这篇关于Leaflet.js:使用自定义 svg 标记太慢(还有很多要点)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文
|