2

我正在做一个项目,我的数据库中存储了纬度和经度坐标的地方。我使用谷歌地图将这些地方绘制为地图上的标记。我不想在地图上绘制任何“不可见”标记(当前视口/边界框之外的标记)。因此,我遵循Google 关于视口标记管理的建议

我有一个可行的解决方案,只要我的地图视口发生变化,我就使用 AJAX 查询 ASP.NET Web 服务。此 Web 服务调用我的 MSSQL 数据库中的存储过程,以获取坐标位于当前视口/边界框内的所有位置。存储过程如下所示:

ALTER PROCEDURE dbo.GetPlacesInsideBoundingBox

    (
    @swLat VarChar(11), /* south-west point latitude */
    @swLng VarChar(11), /* south-west point longitude */
    @neLat VarChar(11), /* north-east point latitude */
    @neLng VarChar(11) /* north-east point longitude */
    )

AS
    /* SET NOCOUNT ON */

    SELECT * FROM dbo.Place WHERE 
    place_lat >= CONVERT(Decimal(11,8), @swLat) 
    AND place_lat <= CONVERT(Decimal(11,8), @neLat) 
    AND place_lng >= CONVERT(Decimal(11,8), @swLng)  
    AND place_lng <= CONVERT(Decimal(11,8), @neLng)
    RETURN

发送到存储过程的参数只是视口/边界框西南角和东北角的纬度和经度。然后我将这些点与存储在我的数据库中的经度和纬度值进行比较,以查看当前视口/边界框内的位置。

这很好用!但是我读到,如果经度(本初子午线以西)为负值,则可能会遇到问题,并且简单的解决方案是在经度为负时将 360 添加到经度上。

我有两个问题:

  1. 如何更改我的存储过程(上图)以考虑负经度?

  2. 我应该考虑对这个存储过程进行任何其他修改以使其万无一失吗?

如果您想知道从 VarChar 到 Decimal 的转换,我发现在客户端 (javascript) 上使用简单的字符串会更容易,然后在我需要进行计算时在我的存储过程中将它们转换为十进制数字。

提前致谢!

4

2 回答 2

1

其实我找到了更好的解决方案。SQL Server 具有用于处理空间数据的内置数据类型。因此,我在表中添加了一个新值,并将其命名为place_geo,数据类型为 geography。然后我用地理点(根据数据库中已有的纬度和经度创建)填充它。当我将点作为地理点存储在我的数据库中时,存储过程的语法变得更加简单,我也认为它会执行得更快。

这是我的存储过程:

ALTER PROCEDURE dbo.GetPlaceClosestToMe
    (
    @my_lat Decimal(11,8),
    @my_lng Decimal(11,8)
    )
AS
    DECLARE @my_point geography;
    SET @my_point = geography::Point(@my_lat, @my_lng , 4326); /* SRID 4326 */
    SET NOCOUNT ON

    SELECT TOP 1 
        place_id, 
        place_name,
        @my_point.STDistance(place_geo)/1000 [Distance in km]
FROM Places
ORDER BY @my_point.STDistance(place_geo)

那么它有什么作用呢?首先,我得到一个经度和一个纬度作为十进制格式的输入参数。我声明了一个数据类型为“geography”的新变量 (@my_point)。然后,我使用经度和纬度输入参数为刚刚创建的变量分配一个新的地理点。

然后我询问我的数据库表 (place_geo) 中最接近 @my_point 的点。(STDistance 返回两种地理类型之间的最短线。)除以 1000 得到以公里表示的距离。

如果您希望它返回多个结果,只需更改选择:SELECT TOP 5 或 SELECT TOP 10。

目前我没有足够的测试数据来测试这是否比使用旧方法更快,但也许其他人已经做了一些测试?我猜想使用地理数据类型比旧方法快得多。

使用我有限的测试数据,旧存储过程和这个新存储过程返回相同的结果。

希望你发现这个后续有用!

于 2011-12-05T16:21:09.553 回答
1

1)经度正常应该在-180到180之间,所以只需检查来自谷歌的坐标是否满足这个条件。大概是。如果不是,只需通过加或减 360 将它们规范化到此区间内。在调用存储过程之前对它们进行规范化。

2) 通常情况下@swLng <= @neLng,但您也必须处理此案@swLng > @neLng。因此,在您的存储过程中,您的经度条件如下所示:

AND 
(
    (CONVERT(Decimal(11,8), @swLng) <= CONVERT(Decimal(11,8), @neLng)
        AND place_lng >= CONVERT(Decimal(11,8), @swLng) 
        AND place_lng <= CONVERT(Decimal(11,8), @neLng)
    ) 
    OR 
    (CONVERT(Decimal(11,8), @swLng) > CONVERT(Decimal(11,8), @neLng)
        AND (place_lng >= CONVERT(Decimal(11,8), @swLng) 
             OR place_lng <= CONVERT(Decimal(11,8), @neLng))
    )
)

PS:请注意,您可能应该使用像 PostGIS 这样的 GIS 才能真正有效:) 我正要解决这个问题,但我仍然推迟了它“直到我有一个 GIS”。你启发了我,也许我也会寻求这个解决方案。

PS:我不确定您的数据库引擎如何优化,但我想如果您将纬度和经度定义为索引可能会有所帮助 - 这样它就可以更快地解决条件。但我完全不确定。

于 2011-11-29T13:29:29.687 回答