我正在开展一个项目,我的数据库中存储了纬度和经度坐标。我使用Google地图将这些地点绘制为地图上的标记。我不想将任何“不可见”标记(当前视口/边界框外的标记)绘制到地图上。因此,我按照Googles advice regarding viewport marker management。
我有一个可行的解决方案,每当我的地图视口发生变化时,我都会使用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
发送到存储过程的参数只是视口/边界框的西南角和东北角的纬度和经度。然后我将这些点与我的数据库中存储的经度和纬度值进行比较,以查看当前视口/边界框中的哪些位置。
这很好用!但是我读到如果经度为负值(Prime Meridian以西),你会遇到问题,如果它是负数,那么简单的解决办法就是将360加到经度上。
我有两个问题:
如何更改我的存储过程(上图)以考虑否定 经度?
我应该考虑对此进行任何其他修改 存储过程使其万无一失?
如果您想知道从VarChar到Decimal的转换,我发现在客户端使用简单的字符串(javascript)更容易,然后在我需要进行计算时将它们转换为我的存储过程中的十进制数字
提前致谢!
答案 0 :(得分: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))
)
)
P.S。:请注意,您应该使用类似PostGIS的GIS,这样才能真正有效:)我即将解决这个问题,但我仍然推迟“直到我有一个GIS”。你激励我,也许我也会选择这个解决方案。
P.S。:我不确定您的数据库引擎是如何优化的,但我想如果您将纬度和经度定义为索引,那么可能帮助提高效率,以便它可以更快地解决问题。但我完全不确定。
答案 1 :(得分:1)
实际上我找到了更好的解决方案。 SQL Server具有用于处理空间数据的内置数据类型。所以我在表格中添加了一个新值,并将其称为 place_geo ,其数据类型为地理位置。然后我用地理点填充它(根据我在数据库中已经拥有的纬度和经度创建)。当我将点存储为数据库中的地理点时,存储过程的语法变得更加简单,我认为它的执行速度会更快。
这是我的存储过程:
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)
那它是做什么用的? 首先,我得到经度和纬度作为十进制格式的输入参数。我声明了一个新变量(@my_point),其数据类型为“geography”。然后,我使用经度和纬度输入参数为刚刚创建的变量指定一个新的地理点。
然后我要求我的数据库表(place_geo)中最接近@my_point的点。 (STDistance返回两种地理类型之间的最短线。)除以1000就是得到以公里为单位的距离。
如果您希望它返回多个结果,只需更改选择:SELECT TOP 5或SELECT TOP 10。
目前我没有足够的测试数据来测试这是否比使用旧方法更快,但也许其他人已经做过一些测试?我猜想使用地理数据类型要比旧方法快得多。
使用我有限的测试数据,旧的存储过程和这个新的存储过程会返回相同的结果。
希望您发现此后续有用!