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

时间:2014-03-23 12:41:18

标签: c# entity-framework sql-server-2012 entity-framework-6

背景

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

我有一个包含邮政编码的数据库表,每个邮政编码都包含一个邮政编码。纬度和经度。我还有一个' EventListings'表格中的每个事件'有一个ZipCode字段。

问题

目前,我在服务层的Linq-to-Entities查询中使用Haversine公式来查找哪些事件在半径范围内。现在,我在where子句中使用它作为过滤器。我也想把它放在select子句中,所以在我可以展示的网站上#34;这是4.6英里远的地方"等等。

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

我尝试过的事情

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

然后我意识到我无法告诉实体框架使用我的sproc在EventListings实体上的select语句...... Phil提出了spatials,这就是我的用途。

问题

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

1 个答案:

答案 0 :(得分:21)

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

设置-向上

第一步是在我的数据库Geography表中添加EventListings列(右键单击它 - >设计)。我点了我的位置:

enter image description here

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

enter image description here

在半径范围内查询&使用位置添加事件

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

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

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

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

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

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

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

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

其他提示&amp;诀窍&amp;故障排除

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

要获取扩展方法Distance,您必须添加对项目的引用:System.Data.Entity 完成后,您必须将使用:using System.Data.Entity.Spatial;添加到您的班级。

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

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

如何将Lat / Long转换为地理列

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

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列包含十六进制值,如:0x1234513462346。当您想确保插入正确的值时,这不是很有用。

实际查看纬度&amp;经度偏离此字段,您必须像这样查询:

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