矩阵变换:将SVG路径坐标转换为Leaflet坐标系

duh*_*ime 9 javascript svg matrix linear-algebra leaflet

简短版本:如何将SVG路径添加到Leaflet地图,以便在地图坐标更改时路径将更新(例如,在缩放更改或幻灯片上)?

长版:你好,我有一个包含建筑轮廓的地形.在对图像进行地理校正后,我使用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); 
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于Leaflet折线无法绘制Bezier曲线,这些曲线存在于上面的SVG路径元素中.作为一种解决方法,我认为我可以对贝塞尔曲线使用线性近似,尽管这可能会成为一个相当大的工作量.

最后我意识到上面小提琴中的边界框的SVG使用贝塞尔曲线,这让我想到我可能会使用矩阵变换将构建SVG的坐标空间转换到Leaflet坐标空间.上面的小提琴使用样本矩阵变换css规则来变换建筑物层.

在深入了解这个兔子洞之前,我想问一下:其他人认为最好的方法是将上述SVG中的建筑物的路径添加到小提琴中的Leaflet地图中?我将非常感谢其他人可以就此问题提供的任何建议!

进展:我决定简化这个问题并弄清楚如何使用矩阵变换将一个div("A")转换为另一个div("B")的纵横比.在这样做的过程中,我制作了一个小的Python脚本,它将输入div的像素坐标和所需的输出div B的像素坐标作为输入.该脚本生成变换矩阵X,使得AX = B. 该脚本在内部记录,并伴随着小提琴.

我还提出了一个要点,即推导出变换矩阵,将SVG空间中的点投射到适当的lat,lng coords中.最糟糕的情况是,我可以对SVG路径元素进行分区,使用变换矩阵获取每个点的点积,并使用传单绘制折线以绘制建筑物.那将失去Bezier曲线......

Fra*_*her 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: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <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>
Run Code Online (Sandbox Code Playgroud)