在Leaflet中,如何在EPSG3857中转换传单视口边界框,以便我可以检索存储在EPSG4326中的数据

时间:2017-11-29 22:59:33

标签: leaflet gis

我的政府提供的地图数据存储在我们的数据库中,带有投影EPSG4326。由于所有数据都存储为经度和纬度,因此即使传单本身默认为投影EPSG3857,也可以成功正确地加载和显示传单。这样做是可以的,因为没有地方将传单转换到EPSG4326,因为没有地图提供商提供为EPSG4326投影的地图图块,我不想自己生成它们。

有很多数据,所以我只想检索可见视口内的数据。 Leaflet为我提供了getBounds()方法来检索可见视口的经度和纬度。不幸的是,它提供给我的坐标是EPSG3857坐标,这意味着检索到的数据在边界周围扭曲 - 4326和3857之间的差异意味着曲线是错误的。

在下图中,我将边界框的经度和纬度传递给数据库,并将其与数据相交。我绘制了绿线,以显示使用传单提供给我的EPSG3857坐标的问题。如果坐标是EPSG4326,那么它会给我更宽的坐标,这些坐标将检索绿线上方和视口内的数据。

我目前有一个黑客,我在边界框的经度和纬度上加了几度,但我希望有一个更好的解决方案。有没有更好的方法来实现这一目标?我可以以某种方式将边界框转换为EPSG4326而无需将整个过程切换到EPSG4326,从而丢失地图图块吗?

Distortion Image

更新

以下是我在后端SQL Server中使用的代码:

CREATE PROC [core].[GetGeoData]
    @Zoom int,
    @BoundingBox_N decimal(18,14),
    @BoundingBox_S decimal(18,14),
    @BoundingBox_E decimal(18,14),
    @BoundingBox_W decimal(18,14)
AS
BEGIN

    DECLARE @BoundingBox varchar(200)
    DECLARE @BoundingBoxGeo geography
    SET @BoundingBox = 'POLYGON((' + CAST(@BoundingBox_E as varchar(20)) + ' ' + CAST(@BoundingBox_S as varchar(20)) + ',' + CAST(@BoundingBox_E as varchar(20)) + ' ' + CAST(@BoundingBox_N as varchar(20)) + ',' + CAST(@BoundingBox_W as varchar(20)) + ' ' + CAST(@BoundingBox_N as varchar(20)) + ',' + CAST(@BoundingBox_W as varchar(20)) + ' ' + CAST(@BoundingBox_S as varchar(20)) + ',' + CAST(@BoundingBox_E as varchar(20)) + ' ' + CAST(@BoundingBox_S as varchar(20)) + '))'
    SET @BoundingBoxGeo = geography::STGeomFromText(@BoundingBox, 4326)

    SELECT PostalCode, geo.STIntersection(@BoundingBoxGeo) AS geo
    FROM [geo].[AustralianGeoData]  
    WHERE  zoom=@Zoom AND geo.STIntersects(@BoundingBoxGeo)=1

END

在前端,我正在调用我的代码:

const mapBounds = map.getBounds();
const zoom = this.map.getZoom();
const boundingBoxN = mapBounds.getNorth();
const boundingBoxS = mapBounds.getSouth();
const boundingBoxE = mapBounds.getEast();
const boundingBoxW = mapBounds.getWest();
this.mapService.getGeoData(zoom, boundingBoxN, boundingBoxS, boundingBoxE, boundingBoxW)
            .subscribe(
            response => { 
   const geoJson = JSON.parse(row.geoJson);
   this.layers.push(geoJson);
});

将边界框直接传递给后端。 map是一个L.map实例。

1 个答案:

答案 0 :(得分:0)

好的,我在执行交集时已将查询更改为使用几何类型而不是地理类型。这将检索到正确的数据 - 当我移动地图时,我可以看到数据仅在视口中加载,因为在检索卸载区域和渲染新图层时会有一个小延迟。

我的存储过程现在看起来像这样:

ALTER PROC [core].[GetGeoData]
    @Zoom int,
    @BoundingBox_N decimal(18,14),
    @BoundingBox_S decimal(18,14),
    @BoundingBox_E decimal(18,14),
    @BoundingBox_W decimal(18,14)
AS
BEGIN

    DECLARE @BoundingBox varchar(200)
    DECLARE @BoundingBoxGeo geometry
    SET @BoundingBox = 'POLYGON((' + CAST(@BoundingBox_E as varchar(20)) + ' ' + CAST(@BoundingBox_S as varchar(20)) + ',' + CAST(@BoundingBox_E as varchar(20)) + ' ' + CAST(@BoundingBox_N as varchar(20)) + ',' + CAST(@BoundingBox_W as varchar(20)) + ' ' + CAST(@BoundingBox_N as varchar(20)) + ',' + CAST(@BoundingBox_W as varchar(20)) + ' ' + CAST(@BoundingBox_S as varchar(20)) + ',' + CAST(@BoundingBox_E as varchar(20)) + ' ' + CAST(@BoundingBox_S as varchar(20)) + '))'
    SET @BoundingBoxGeo = geometry::STGeomFromText(@BoundingBox, 4326)
    SELECT PostalCode, geography::STGeomFromText(geometry::STGeomFromText(geo.AsTextZM(),4326).STIntersection(@BoundingBoxGeo).AsTextZM(),4326) AS geo
    FROM [geo].[AustralianGeoData]   
    WHERE  zoom=@Zoom AND geometry::STGeomFromText(geo.AsTextZM(),4326).STIntersects(@BoundingBoxGeo)=1
    AND geo.STGeometryType() IN ('Polygon','MultiPolygon','GeometryCollection')
END