从地理存储过程中获取里程

Posted

技术标签:

【中文标题】从地理存储过程中获取里程【英文标题】:Getting miles from Geography Stored Procedure 【发布时间】:2017-01-28 10:25:58 【问题描述】:

我是使用 Geography 数据类型的新手,但我已经知道如何使用它从一个点获取邮政编码列表(在存储过程中,我只是将点作为 varchar 并提供给它“经度纬度”),太棒了。

但是,接下来我需要展示的是距该点大约几英里的距离。我看了几个答案,没有找到任何东西。

这是我的存储过程:

ALTER PROCEDURE [dbo].[GetZipCodesByDistance]
@point varchar(500),
@distance int
AS
BEGIN
SET NOCOUNT ON;

declare @geoPoint geography
set @geoPoint = geography::STGeomFromText('POINT (' + @point + ')', 4326)
                           .STBuffer(@distance / 0.0006213712)
SELECT   zc.zip_id AS ZipId
        ,zc.zip_code AS ZipCodeId
        ,zc.zip_type AS ZipType
        ,zc.zip_city AS ZipCity
        ,zc.zip_utc AS ZipUtc
        ,zc.zip_dst AS ZipDst
        ,zc.zip_latitude AS ZipLatitude
        ,zc.zip_longitude AS ZipLongitude,
        (Geo.MakeValid()).STDistance(@geoPoint)
FROM zip_code zc
WHERE zc.Geo.STIntersects(@geoPoint) = 1

所以,我的问题是......我如何让它从该点返回英里?我尝试添加列“(Geo.MakeValid()).STDistance(@geoPoint)”,但它返回全零。

感谢您的帮助!

AJ

【问题讨论】:

那么你的问题是什么?请阅读How-to-Ask 这里是START 了解如何提高问题质量并获得更好答案的好地方。 对不起,我没有明确表示,我已经编辑了我的帖子,以便在其中包含问题。谢谢! 【参考方案1】:

试试这个

SELECT geography::STGeomFromText('POINT(' + zc.zip_latitude
                                    + ' ' + zc.zip_longitude + ')'
                                 , 4326).STDistance(@geoPoint); 

【讨论】:

这让我非常接近,我必须将 zip_latitude 和 zip_longitude 转换为 VARCHAR(50),现在距离列显示在“13489758.0063597”我尝试将其除以 1609.344,但距离又回来了作为 8382.31166576187 这是不正确的样式。有什么想法吗? Damien_The_Unbeliever 让我更接近,但由于某种原因,存储过程中的距离值显示英里。但是,它不会转移到 Web 应用程序。有什么想法吗? 我不明白Geo.MakeValid()这个函数是Geo一个表字段?【参考方案2】:

我仍然不明白您的实际要求是什么。我们有类似的要求来查找邮政编码之间的距离。查看完成工作的示例函数

CREATE FUNCTION Wrk.ZipDistance
(
    @Zip1 VARCHAR(20)
    ,@Zip2 VARCHAR(20)
)
RETURNS FLOAT
AS
BEGIN
    DECLARE @LatLong1 GEOGRAPHY
    DECLARE @LatLong2 GEOGRAPHY
    DECLARE @Result FLOAT
    SELECT
        @LatLong1 = LatLong
    FROM
        PostalCode
    WHERE
        PostalCode = @Zip1

    SELECT
        @LatLong2 = LatLong
    FROM
        PostalCode
    WHERE
        PostalCode = @Zip2

    SELECT @Result = @Lat1.STDistance(@Lat2) 

    RETURN @Result/1000
END

希望对你有帮助

【讨论】:

【参考方案3】:

我建议引入一个单独的变量来保存 point 与您的缓冲区:

ALTER PROCEDURE [dbo].[GetZipCodesByDistance]
@point varchar(500),
@distance int
AS
BEGIN
SET NOCOUNT ON;

declare @geoPoint geography
set @geoPoint = geography::STGeomFromText('POINT (' + @point + ')', 4326);

declare @searchArea geography
set @searchArea = @geoPoint.STBuffer(@distance / 0.0006213712)

select  zc.zip_id AS ZipId
        ,zc.zip_code AS ZipCodeId
        ,zc.zip_type AS ZipType
        ,zc.zip_city AS ZipCity
        ,zc.zip_utc AS ZipUtc
        ,zc.zip_dst AS ZipDst
        ,zc.zip_latitude AS ZipLatitude
        ,zc.zip_longitude AS ZipLongitude,
        (Geo.MakeValid()).STDistance(@geoPoint)
        FROM zip_code zc
where zc.Geo.STIntersects(@searchArea) = 1

我尝试添加列“(Geo.MakeValid()).STDistance(@geoPoint)”,但它返回全零。

之前您正在计算找到的点与搜索区域之间的距离——这当然是 0,因为我们已经(通过STIntersects)确定它们在该区域内。所以我改变了一些事情,以便STDistance 调用使用的是点而不是区域。

(当然,如果可能的话,我也建议更改存储过程的参数,以便传递一个已经构造的geography 而不是字符串。但这可能并不总是可能的)

【讨论】:

这在 SQL 中完美运行。但是,当我从 MVC 应用程序调用存储过程时,它返回 0。在我的模型中,我有: [NotMapped] public double Distance get;放; 。我更改了 SQL 查询,以便最后一个“列”作为距离返回。知道我做错了什么吗? 你给我看的第一段代码是属性NotMapped 发现了一些有趣的东西。在 DbContext 中,我有以下内容: var query = this.Database.SqlQuery("GetZipCodesByDistance @point, @distance", new SqlParameter("point", point), new SqlParameter("distance", distance) ) ;返回查询;其中,在调试中运行它时,我收到错误:SqlParameter 已包含在另一个 SqlParameterCollection 中。 完整的 Facility 类可以在这里找到:gist.github.com/ajtatum/88ff2c3bd426e9c0f58fa290f3cf99ad【参考方案4】:

好的,想出了一个方法来完成这项工作。

存储过程如下:

ALTER PROCEDURE [dbo].[GetZipCodesByDistance] @point VARCHAR(500),
@distance INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @geoPoint GEOGRAPHY
SET @geoPoint = GEOGRAPHY::STGeomFromText('POINT (' + @point + ')', 4326);

DECLARE @searchArea GEOGRAPHY
SET @searchArea = @geoPoint.STBuffer(@distance / 0.0006213712)

DECLARE @ZipDistance TABLE(
ZipCode VARCHAR(5),
ZipLatitude DECIMAL(9,6),
ZipLongitude DECIMAL(9,6),
Distance DECIMAL(9,2)
)

INSERT INTO @ZipDistance
SELECT
        zc.zip_code AS ZipCode
        ,zc.zip_latitude AS ZipLatitude
        ,zc.zip_longitude AS ZipLongitude
        ,CAST((((Geo.MakeValid()).STDistance(@geoPoint))/1609.34) AS DECIMAL(9,2)) AS Distance
FROM zip_code zc
WHERE zc.Geo.STIntersects(@searchArea) = 1

SELECT * FROM @ZipDistance
END

然后我创建了一个模型:

public class ZipCodeWithDistance

    public string ZipCode  get; set; 
    public decimal ZipLatitude  get; set; 
    public decimal ZipLongitude  get; set; 
    public decimal Distance  get; set; 

在 DbContext 中,我有以下内容:

public List<ZipCodeWithDistance> GetZipCodes(string point, int distance)

    var zipCodes = this.Database.SqlQuery<ZipCodeWithDistance>(
    "GetZipCodesByDistance @point, @distance",
    new SqlParameter("point", point),
    new SqlParameter("distance", distance)
    ).ToList();

    return zipCodes;

在 FacilityModel 中,我有:

[NotMapped]
public decimal Distance  get; set; 

最后,在服务中,我有以下内容:

public IQueryable<Facility>GetFacilitiesByZipCodes(List<ZipCodeWithDistance> zipCodes)

    var zipCodeName = zipCodes.Select(x => x.ZipCode).ToList();
    var facilities = oandpDbContext.Facilities.Where(x => zipCodeName.Contains(x.ZipCode.Substring(0, 5))).ToList();
    facilities.ForEach(x =>
    
        var zip = zipCodes.FirstOrDefault(y => y.ZipCode == x.ZipCode.Substring(0, 5));
        if (zip != null)
        
            x.Distance = zip.Distance;
        
    );
return facilities.AsQueryable();

不确定这是否是完成这项工作的最佳/最有效的方法,但它确实有效。欢迎任何想法或cmets!谢谢你,AJ

【讨论】:

以上是关于从地理存储过程中获取里程的主要内容,如果未能解决你的问题,请参考以下文章

使用存储过程从视图中检索或过滤数据是不是比使用存储过程从表中获取或过滤数据更快?

如何从 JDBC 中的存储过程中获取输出

如何使用 SQL 查询将值传递给具有空间/地理数据类型的存储过程

无法使用 SqlQuery 从存储过程中获取数据

从存储过程中获取返回值

oracle如何从存储过程中获取数据