5

我需要能够显示从用户选择的特定位置到n个城市/城镇的距离。就像点击地图,得到100英里内的所有目的地,只是它不会是地图,而是网页上的链接。

我需要选择一种解决方案,该解决方案可以从一个州内扩展到一个国家,甚至可能扩展到全球——这意味着从数千个位置扩展到十万个位置。

我虽然将 CITY1_ID、CITY2_ID 和 DISTANCE 存储在关系 DB 表中,但我怀疑它是否能很好地扩展到 Web 应用程序(数百万行)。

使用 NoSQL Database 或 Graph DB 可以更有效地完成这项工作吗?或者 RDBMS 是否足以通过适当的设计解决这个问题?

补充:如果我不存储在 DB 中,那么我将如何获得类似:让我在圣何塞 100 英里内的所有城市?

4

7 回答 7

4

您应该city_id, latitude, longitude为每个城市存储一个 - 然后根据运行时输入计算距离。

于 2012-10-02T20:20:09.160 回答
2

不是计算 2 个城市之间的距离,而是计算 100 英里的边界框,然后您有 4 个浮点变量可以插入数据库 - 浮点比较比数据库中的距离计算快得多。缺点是你在角落里有更多的距离。

PHP函数计算边界框

函数 getBoundingBox($lat_degrees,$lon_degrees,$distance_in_miles)
{
       $半径=3963.1;// 以英里为单位的地球

        // 轴承
        $due_north = 0;
        $due_south = 180;
        $due_east = 90;
        $due_west = 270;

        // 将纬度和经度转换为弧度
        $lat_r = deg2rad($lat_degrees);
        $lon_r = deg2rad($lon_degrees);

        // 找到最北端、最南端、最东端和最西端的角 $distance_in_miles
        //原始公式来自
        // http://www.movable-type.co.uk/scripts/latlong.html

        $northmost = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_north));
        $southmost = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_south));

        $eastmost = $lon_r + atan2(sin($due_east)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r)) ;
        $westmost = $lon_r + atan2(sin($due_west)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r)) ;

        $northmost = rad2deg($northmost);
        $southmost = rad2deg($southmost);
        $eastmost = rad2deg($eastmost);
        $westmost = rad2deg($westmost);

        //返回2个点NW角和SE角
        返回数组($northmost,$westmost,$southmost,$eastmost);
}

那么你的 SQL 是

SELECT * FROM table WHERE latitude <= $northmost AND longitude >= $westmost AND latitude >= $southmost AND longitude <= $eastmost

于 2012-10-06T07:09:42.670 回答
1

我多次使用过的一个简单some_distance_function解决方案(但不是使用 mysql)是创建一个用户定义的函数,它有四个参数latitude1, longitude1, latitude2longitude2它返回距离,然后针对该距离函数测试所有内容并查看每个项目,无论是否距离小于或等于给定值。如果您只有几千个位置,这是非常好的和有效的。

如果您需要针对数百万条记录运行此查询,您可能希望查看哪些 GIS(地理信息系统)扩展可用于您选择的数据库,因为有更好的(至少在搜索能力方面)持久数据结构用于搜索大量位置。

编辑:举一个微软如何做的例子,见http://technet.microsoft.com/en-us/library/bb964712(v=sql.105).aspx

看起来 MySQL 通常支持空间扩展:

http://dev.mysql.com/doc/refman/5.0/en/gis-introduction.html
http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html

编辑二:

看起来这个问题也可能有帮助。

在 MYSQL 中求两点之间的距离。(使用点数据类型)

于 2012-10-02T20:54:11.247 回答
1

这是使用 RDBMS 的解决方案。保留两张桌子

  • CityByLat { latitude, city_id } 在纬度上具有聚集索引和
  • CityByLng { logitude, city_id } 在经度上具有聚集索引

当您需要从给定的纬度和经度查找一定半径内的城市时,您可以对两个表进行有效的范围查询,以获取一定纬度和经度范围内的城市。然后,您可以仅计算与检索到的城市的实际距离。

于 2012-10-06T06:49:26.260 回答
0

正如其他人所指出的,您可以存储每个条目的纬度/经度坐标,并在运行时使用类似于以下内容的方法计算距离,它提供公里/英里距离输出:

function distance($lat1, $lng1, $lat2, $lng2, $miles = true)
{
        $pi80 = M_PI / 180;
        $lat1 *= $pi80;
        $lng1 *= $pi80;
        $lat2 *= $pi80;
        $lng2 *= $pi80;

        $r = 6372.797; // mean radius of Earth in km
        $dlat = $lat2 - $lat1;
        $dlng = $lng2 - $lng1;
        $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
        $km = $r * $c;

        return ($miles ? ($km * 0.621371192) : $km);
}

编辑:这不适用于半径搜索中的n 个匹配项。鉴于给定半径内城镇/城市的密度,最好将距离计算移到 SQL 中,因为它的速度要快得多,并且您可以与x公里/英里内的那些进行匹配。

于 2012-10-02T20:46:08.560 回答
0

不要存储它,用经度和纬度计算它的运行时间。与节省城市之间的所有距离相反,可扩展性极强。

您有一个参考点(圣何塞)并遍历所有城市记录并计算它的运行时间(如果有很多记录,请由客户端完成此计算,可能使用 javascript 或其他东西,因为如果您让服务器执行此操作,它很快就会付出代价)。JavaScript 可能看起来像这样:

var R = 6371; // Radius of the earth in km
var dLat = (lat2-lat1).toRad();  // Javascript functions in radians
var dLon = (lon2-lon1).toRad(); 
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) * 
        Math.sin(dLon/2) * Math.sin(dLon/2); 
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
var d = R * c; // Distance in km

上面的代码来自这里

注意:它以公里为单位,因为我是荷兰人,因此使用公制

于 2012-10-02T20:23:31.810 回答
0

我正在将Neo4J用于类似的东西,它可以很好地扩展任何可以表示为图表的数据类型。

于 2012-10-02T20:34:08.727 回答