这是我很久以前写的一些东西,可能会让你朝着正确的方向前进。
当您要求使用 VB.Net 时,您真正需要的是一个执行“大圆距离 http://en.wikipedia.org/wiki/Great-circle_distance”计算确定由纬度和经度标识的两点之间的距离。
因此,做出以下假设:
- 您的邮政编码数据位于单个表中。
- 该表具有纬度和经度的属性,它们是邮政编码的近似质心
您可以使用 LINQ to SQL 查询来生成所需的结果集,如下所示
Const EARTH_RADIUS As Single = 3956.0883313286095
Dim radCvtFactor As Single = Math.PI / 180
Dim zipCodeRadius As Integer = <Your Radius Value>
Dim zipQry = From zc In db.ZipCodes
Where zc.Zip = "<Your Zip Code>" _
Select zc.Latitude,
zc.Longitude,
ZipLatRads = RadCvtFactor * zc.Latitude,
ZipLonRads = RadCvtFactor * zc.Longitude
Dim zipRslt = zipQry.SingleOrDefault()
If zipRslt IsNot Nothing Then
Dim zcQry = From zc In db.ZipCodes _
Where zc.Latitude >= (zipRslt.Latitude - 0.5) And zc.Latitude <= (zipRslt.Latitude + 0.5) _
And zc.Longitude >= (zipRslt.Longitude - 0.5) And (zc.Longitude <= zipRslt.Longitude + 0.5) _
And Math.Abs(EARTH_RADIUS * (2 * Math.Atan2(Math.Sqrt(Math.Pow(Math.Sin(((RadCvtFactor * zc.Latitude) - zipRslt.ZipLatRads) / 2), 2) + _
Math.Cos(zipRslt.ZipLatRads) * Math.Cos(RadCvtFactor * zc.Latitude) * _
Math.Pow(Math.Sin(((RadCvtFactor * zc.Longitude) - zipRslt.ZipLonRads) / 2), 2)), _
Math.Sqrt(1 - Math.Pow(Math.Sin(((RadCvtFactor * zc.Latitude) - zipRslt.ZipLatRads) / 2), 2) + _
Math.Cos(zipRslt.ZipLatRads) * Math.Cos(RadCvtFactor * zc.Latitude) * _
Math.Pow(Math.Sin((RadCvtFactor * zc.Longitude) / 2), 2))))) <= zipCodeRadius _
Select zc
End If
它看起来很复杂,因为事实确实如此。 SO 上有更聪明的人可以解释该算法。我只是通过在互联网上找到的一些 SQL 代码实现了这个 - 我不记得从哪里找到的。谷歌搜索应该可以到达那里。
第一个查询 (zipQry) 返回起始邮政编码的纬度和经度(以度数和弧度表示)。然后,这些结果将用于执行第二个查询。
第二个查询中 WHERE 子句的第一部分:
Where zc.Latitude >= (zipRslt.Latitude - 0.5) And zc.Latitude <= (zipRslt.Latitude + 0.5) _
And zc.Longitude >= (zipRslt.Longitude - 0.5) And (zc.Longitude <= zipRslt.Longitude + 0.5) _
只是缩小了要检查的邮政编码列表的范围,使查询运行得更快。它会向纬度和经度添加任意数量,以便您在搜索加利福尼亚州的半径时无需检查俄亥俄州的所有邮政编码。其余的都是前面提到的大圆距离算法的一部分。
为了提高效率,这可能可以在一个查询中完成,但当时我需要这种方式,现在我不知道原因了。