背景
我正在编写一个在邮政编码的某个半径范围内查找事件的应用程序。您可以将其视为票务管理员,您可以在其中输入您的邮政编码,并显示半径为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搜索邮政编码范围内的事件?
答案 0 :(得分:21)
所以,根据菲尔的建议,我用空间重新写了很多这个。这很好,你只需要.NET4.5,EF5 +和sql server(我相信2008年及以上)。我正在使用EF6 + Sql Server 2012。
第一步是在我的数据库Geography
表中添加EventListings
列(右键单击它 - >设计)。我点了我的位置:
接下来,由于我首先使用EDM和数据库,我不得不更新我的模型以使用我创建的新字段。然后我收到一个关于无法将Geography转换为double的错误,所以我要做的就是在实体中选择Location属性,转到它的属性并将其类型从double更改为Geography
:
然后你所要做的就是查询你的实体集合:
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 + ")");
我如何获得距离扩展方法?
要获取扩展方法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]