如何使用空格搜索邮政编码的半径?

背景

我正在编写一个应用程序,它可以在邮政编码的某个半径范围内查找事件。 您可以将其视为票务管理员,您可以在其中输入您的邮政编码,并显示半径为x的所有音乐会。

我有一个数据库表,其中包含邮政编码,每个邮政编码都有“纬度和经度”。 我还有一个’EventListings’表,其中每个’Event’都有一个ZipCode字段。

问题

目前,我在服务层的Linq-to-Entities查询中使用Haversine公式来查找哪些事件在半径范围内。 现在,我将它用作where子句中的filter。 我也想把它放在select子句中,所以我可以在网站上显示“这距离4.6英里”,等等。

我无法将此代码移动到单独的C#方法中,因为Linq-to-Entities会抱怨它无法将其转换为sql,因此我也会在select语句中复制整个公式。 这非常难看。 我试着解决它。

我试过的

我编辑了Entity,并添加了一个特殊的标量属性“DistanceFromOrigin”。 然后,我创建了一个存储过程,它返回了所有实体数据,以及新字段“DistanceFromOrigin”的硬编码值(用于测试目的)。

然后我开始意识到我不能告诉entity framework使用我的sproc在EventListings实体上的select语句…… Phil提出了spatials,这就是我的意思。

如何使用Spatials搜索邮政编码范围内的事件?

所以,使用菲尔的建议,我用空间重新写了很多这个。 这很好,你只需要.NET4.5,EF5 +和sql server(我相信2008年及以上)。 我正在使用EF6 + Sql Server 2012。

建立

第一步是将Geography列添加到我的数据库EventListings表(右键单击它 – > Design)。 我命名我的位置:

在此处输入图像描述

接下来,由于我首先使用EDM和数据库,我不得不更新我的模型以使用我创建的新字段。 然后我收到一个关于无法将Geography转换为double的错误,所以我要解决的是选择实体中的Location属性,转到其属性并将其类型从double更改为Geography

在此处输入图像描述

在半径内查询并使用位置添加事件

然后你要做的就是查询你的实体集合,如下所示:

  var events = EventRepository.EventListings .Where(x => x.Location.Distance(originCoordinates) * 0.00062 <= radiusParam); 

距离扩展方法获取从当前对象到您传入的“其他”对象的距离。此另一个对象的类型为DbGeography 。 你只需要调用静态方法,然后创建其中一只小狗,然后将经度和纬度作为一个点:

 DBGeography originCoordinates = DBGeography.fromText("Point(" + originLongitude + " " + originLatitude + ")"); 

这不是我创建originCoordinates的方式。 我下载了一个单独的数据库,其中包含所有邮政编码及其纬度和经度的列表。 我也添加了一个Geography类型的列。 我将在这个答案的最后给出。 然后,我查询了zipcode上下文,以从ZipCode表中的Location字段获取DbGeography对象。

如果用户需要比邮政编码更具体的来源,我会调用Google Maps API(更具体的GeoCode)并通过Web服务获取用户特定地址的纬度和经度,并从中创建DBGeography对象响应中的纬度和经度值。

我也在创建活动时使用谷歌API。 在将实体添加到EF之前,我只是设置了这样的位置变量:

 someEventEntity.Location = DBGeography.fromText("Point(" + longitudeFromGoogle+ " " + latitudeFromGoogle + ")"); 

其他提示与技巧和疑难解答

我是如何获得距离扩展方法的?

要获取扩展方法Distance ,必须添加对项目的引用: System.Data.Entity执行此操作后,必须添加using: using System.Data.Entity.Spatial; 到你的class级。

Distance扩展方法以米单位返回距离(您可以更改它,但这是默认值)。 这里,我的半径参数是英里,所以我做了一些数学转换。

注意System.Data.Spatial有一个DBGeography类。 这是错误的 ,它对我不起作用。 我在互联网上找到的很多例子都使用了这个例子。

如何将Lat / Long转换为Geography Column

所以,如果你像我一样下载了一个包含所有LatitudeLongitude列的邮政编码数据库,然后意识到它没有地理列......这可能会有所帮助:

1)在表格中添加“位置”列。 将类型设置为Geography。

2)执行以下sql

 UPDATE [ZipCodes] SET Location = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' + CAST([Latitude] AS VARCHAR(20)) + ')', 4326) 

如何查看地理项目的详细信息

因此,当您在插入一些DbGeography项后查询Sql Server Management Studio中的EventListings表时,您将看到Location列包含hex值,如:0x1234513462346。 当您想确保插入正确的值时,这不是很有用。

要实际查看此字段的纬度和经度,您必须像这样查询:

 SELECT Location.Lat Latitude, Location.Long Longitude FROM [EventListings]