简短版本:如何将SVG路径添加到Leaflet地图,以便在地图坐标更改时路径将更新(例如,在缩放更改或幻灯片上)?
长版本:您好,我有一个包含建筑大纲的地形image。在对图像进行地理校正后,我使用Photoshop将栅格数据转换为SVG。我知道描述SVG边界的边界框的地理坐标,并知道SVG路径元素的内部坐标。我想知道现在将SVG的路径元素中描述的建筑物添加到Leaflet地图的最佳方法。
这是一个小提示,显示红色的SVG图像的边界框和蓝色的建筑物:http://jsfiddle.net/duhaime/4vL925Lj/正如您所看到的,建筑物相对于边界框尚未正确定向。
我最初计划对齐建筑物是使用一次性脚本将路径元素从SVG坐标系转换为lat,long坐标,然后使用我用来绘制的多边形函数在地图上绘制建筑物边界框:
var polyline = L.polyline(
[upperLeft, upperRight, lowerRight, lowerLeft, upperLeft],
{color: 'red', className: 'bounding-box', weight: 2}
).addTo(map);
这种方法的问题在于Leaflet折线无法绘制Bezier曲线,这些曲线存在于上面的SVG路径元素中。作为一种解决方法,我认为我可以对贝塞尔曲线使用线性近似,尽管这可能会成为一个相当大的工作量。
最终我意识到上面小提琴中的边界框的SVG使用了Bezier曲线,这让我想到我可能会使用矩阵变换将构建SVG的坐标空间转换到Leaflet坐标空间。上面的小提琴使用样本矩阵变换css规则来转换建筑层。
在深入了解这个兔子洞之前,我想问一下:其他人认为将上述SVG中描述建筑物的路径添加到小提琴中的Leaflet地图的最佳方法是什么?我非常感谢其他人可以就此问题提出的建议!
进展:我决定简化这个问题并弄清楚如何使用矩阵变换将一个div(“A”)转换为另一个div(“B”)的宽高比。在这样做时,我做了一个小的Python script,它将输入div的像素坐标和所需的输出div B的像素坐标作为输入。该脚本生成变换矩阵X,使得AX = B.该脚本在内部记录,并附带fiddle。
我还制作了一个gist,它导出变换矩阵,将SVG空间中的点投影到正确的lat,lng坐标。最糟糕的情况是,我可以对SVG路径元素进行分区,使用变换矩阵获取每个点的点积,并使用传单绘制折线以绘制建筑物。那会失去Bezier曲线......
答案 0 :(得分:2)
这需要相当多的思考,但我找到了解决方案。
在读完之后,我意识到可以通过识别变换矩阵,然后通过乘以每个点,将点从一个坐标空间(例如SVG坐标空间)转换到另一个(例如,纬度/长坐标空间)。输入空间(SVG)由该变换矩阵组成。此操作会将给定点转置到地图中的适当位置。
我写了this script来计算所需的变换矩阵。该脚本将SVG的边界框坐标和从中提取SVG元素的地理定位地理坐标的边界框坐标作为参数。该脚本生成变换矩阵,并显示如何通过矩阵将SVG空间中的点相乘以找到适当的纬度/经度坐标。
有一个问题 - 人们需要在没有任何CSS转换的情况下表示SVG中的点。为了直观地表示SVG中SVG点的位置,我使用this tool将SVG中的路径元素转换为多边形元素,source是公开可用的。
如果其他人需要完成类似的任务,这里是我使用的完整工作流程:
<rect>
或其他几何形状,请将它们转换为路径并重新保存。gdalinfo {your-geotiff-file.tif}
<polygon>
元素数组,并为每个元素将多边形拆分为一个点数组。将每个点乘以变换矩阵以找到该点的纬度/经度位置。对于它的价值,我用于生成矩阵变换以及将<polygon>
元素点转换为纬度/长度空间的脚本是here。请注意,脚本中的某些路径需要根据您的情况进行更新 - 例如。该脚本将输出geojson推送到我实验室管理的S3存储桶:)
我希望这可以帮助那些发现自己面临这项任务的人!坦白地说,我花了很多精力,我很确定必须有一个更优雅的工作流程......
答案 1 :(得分:1)
我已将此功能添加到我之前在Leaflet Maps上完成的工作中。这可能适用于您的申请。 请参阅:www.svgdiscovery.com/K/K04A.htm
这使用了Leaflet Map和导入的SVG路径共有的两个关键点。
答案 2 :(得分:0)
以下示例使用折线值来演示在Leaflet贴图的缩放/平移期间应用于svg路径和其他形状的方法。 基本上创建SVG层并且在其中添加所有svg元素。
<head>
<title>Untitled</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.js" ></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.css" />
<style type="text/css">
<!--
#map {
width: 500px;
height: 500px;
}
-->
</style>
</head>
<body>
<div id="map"></div>
</body>
<script>
// create the map object itself
centerCoordinates = new L.LatLng(41.307, -72.928);
var map = new L.Map("map", {
center: centerCoordinates,
zoom: 14,
zoomControl: false
});
// position the zoom controls in the bottom right hand corner
L.control.zoom({
position: 'bottomright',
zoom: 14,
maxZoom: 20,
minZoom: 12,
}).addTo(map);
map.addLayer(new L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> © <a href="http://cartodb.com/attributions">CartoDB</a>',
subdomains: 'abcd',
maxZoom: 19
}));
// specify the coordinates of the overlay's bounding box
var upperLeft = L.latLng(41.329785, -72.927220);
var lowerLeft = L.latLng(41.304414, -72.945686);
var upperRight = L.latLng(41.319186, -72.903268);
var lowerRight = L.latLng(41.293816, -72.921718);
/*
// create a red polyline from an array of LatLng points
var polyline = L.polyline(
[upperLeft, upperRight, lowerRight, lowerLeft, upperLeft], {
color: 'red',
className: 'bounding-box',
weight: 2
}
).addTo(map);
*/
//---CREATE SVG---
map._initPathRoot() //---creates an svg layer---
var MySVG=document.querySelector("svg") //---access svg element---
var NS="http://www.w3.org/2000/svg"
//---place svg elems in here---
var SvgElemG=document.createElementNS(NS,"g")
MySVG.appendChild(SvgElemG)
//---zooming the map's SVG elements---
map.on("viewreset", adjustSVGElements);
//---add svg polygon---
var polygon=document.createElementNS(NS,"polyline")
polygon.setAttribute("stroke-width",1)
polygon.setAttribute("fill","none")
polygon.setAttribute("stroke","red")
//---convert latLng to x,y---
var xyUL=map.latLngToLayerPoint(upperLeft)
var xyLL=map.latLngToLayerPoint(lowerLeft)
var xyLR=map.latLngToLayerPoint(lowerRight)
var xyUR=map.latLngToLayerPoint(upperRight)
var points=[xyUL.x,xyUL.y,xyLL.x,xyLL.y,xyLR.x,xyLR.y,xyUR.x,xyUR.y,xyUL.x,xyUL.y].toString()
polygon.setAttribute('points',points)
//--required for zoom---
var svgPnt=L.point(0,0) //--reference for translate--
var latLng=map.layerPointToLatLng(svgPnt)
var lat=latLng.lat
var lng=latLng.lng
polygon.setAttribute("lat",lat)
polygon.setAttribute("lng",lng)
//---retain the zoom level at its creation--
polygon.setAttribute('initZoom',map.getZoom())
SvgElemG.appendChild(polygon)
//--- on map zoom - fired via map event: viewreset---
function adjustSVGElements()
{
var mapZoom=map.getZoom()
var svgElems=SvgElemG.childNodes
for(var k=0;k<svgElems.length;k++)
{
var svgElem=svgElems.item(k)
var lat=parseFloat(svgElem.getAttribute("lat"))
var lng=parseFloat(svgElem.getAttribute("lng"))
var latLng= new L.latLng(lat, lng)
var transX=map.latLngToLayerPoint(latLng).x
var transY=map.latLngToLayerPoint(latLng).y
//---trash previous transform---
svgElem.setAttribute("transform","") //---required for IE
svgElem.removeAttribute("transform")
var transformRequestObj=MySVG.createSVGTransform()
var animTransformList=svgElem.transform
//---get baseVal to access/place object transforms
var transformList=animTransformList.baseVal
//---translate----
transformRequestObj.setTranslate( transX, transY)
transformList.appendItem(transformRequestObj)
transformList.consolidate()
//---scale---
var initZoom=parseFloat(svgElem.getAttribute("initZoom"))
var scale = (Math.pow(2, mapZoom)/2)/(Math.pow(2, initZoom)/2);
transformRequestObj.setScale(scale,scale)
transformList.appendItem(transformRequestObj)
transformList.consolidate()
}
}
</script>
&#13;