给定纬度和经度,找到对该位置感兴趣的用户

时间:2009-11-06 10:24:58

标签: mysql geolocation geocoding geospatial spatial

给出一张表,

+-----+---------+---------+---------+---------+---------+
| user| min_lat | max_lat | min_lng | max_lng |
+-----+---------+---------+---------+---------+---------+
| a   |    46   |     407 |       6 |     367 | 
| b   |   226   |     227 |     186 |     188 | 

点(x,y)查找点在用户的最小和最大纬度和经度范围内的用户(其中​​min和max long和lat =当前位置减去或加上半径)。

最小值可以小于0,最大值可以大于360,查询需要考虑这些。

E.g。使用Point(7,5)进行过滤也应该返回用户A,如367-360 = 7。

不确定我是否正确,但希望有人可以给我一些见解。

4 个答案:

答案 0 :(得分:2)

我建议限制纬度和范围存储在表中的经度值到[0,360]范围。在插入和更新之前创建触发器以强制执行mod 360等效,并且如果max-min> 360,将最小值和最大值分别设置为0和360。例如:

delimiter ;;

CREATE TRIGGER normalize_inserted_ranges BEFORE INSERT 
  ON table
  FOR EACH ROW BEGIN
    IF NEW.max_lat - NEW.min_lat >= 360 THEN
        SET NEW.min_lat=0;
        SET NEW.max_lat=360;
    ELSE
        SET NEW.min_lat = NEW.min_lat % 360;
        SET NEW.max_lat = NEW.max_lat % 360;
    END IF;
    IF NEW.max_lng - NEW.min_lng >= 360 THEN
        SET NEW.min_lng=0;
        SET NEW.max_lng=360;
    ELSE
        SET NEW.min_lng = NEW.min_lng % 360;
        SET NEW.max_lng = NEW.max_lng % 360;
    END IF;
  END
;;
delimiter ;

然后您可以使用以下查询:

SELECT user FROM table
  WHERE
      IF(min_lng <= max_lng, 
         @x BETWEEN min_lng AND max_lng, 
         @x <= max_lng OR min_lng <= @x)
    AND
      IF(min_lat <= max_lat,
         @y BETWEEN min_lat AND max_lat, 
         @y <= max_lat OR min_lat <= @y)
;

答案 1 :(得分:0)

要回答查询的一部分,如果你说367和7是相同的,我同样假设727也是。

因此,您希望使用360的模数。剩下的除以360。

e.g。 (其中%是C#和C ++语法,您可能会找到一种SQL方法来执行此操作)

7 % 360 = 7
367 % 360 = 7
727 % 360 = 7

看起来像SELECT b MOD 360 from table;之类的东西就是你想要的东西。

答案 2 :(得分:0)

我怀疑有一个更优雅的答案,但我认为SQL where子句的限制使这有点困难。

假设:

  • 0 <= x < 360
  • 0 <= y < 360
  • max_lat - min_lat <= 361
  • max_lng - min_lng <= 361
SELECT user FROM user_location WHERE  
((min_lat <  0 AND ((0 <= y AND y <= max_lat) OR (min_lat + 360 <= y AND y < 360))) OR  
 (min_lat >= 0 AND ((min_lat <= y AND y <= max_lat) OR (0 <= y AND y <= max_lat - 360))))  
AND  
((min_lng <  0 AND ((0 <= x AND x <= max_lng) OR (min_lng + 360 <= x AND x <= 360))) OR  
 (min_lng >= 0 AND ((min_lng <= x AND x <= max_lng) OR (0 <= x AND x <= max_lng - 360))))

在一个方面,如果min&lt; 0然后x必须落在0和max之间,或者x必须落在min + 360和360之间。类似地,如果min> = 0则x必须落在min和max之间,或者它必须落在0到max - 360之间。

上面的格式是为了清楚起见。如果将360的加法和减法移动到x和y参数,则比较将变为常量,并可能显着加快查询速度。

答案 3 :(得分:0)

使用模数来确保所有值都在0到360之间,并且查询变得非常简单。假设{pointLat}和{pointLng}是要过滤的点的坐标。

SELECT *
FROM table
WHERE IF(min_lat < max_lat,
        MOD({pointLat}, 360) BETWEEN MOD(min_lat, 360) AND MOD(max_lat, 360)),
        MOD({pointLat}, 360) NOT BETWEEN MOD(min_lat, 360) AND MOD(max_lat, 360)))
    AND IF(min_lng < max_lng,
        MOD({pointLng}, 360) BETWEEN MOD(min_lng, 360) AND MOD(max_lng, 360)),
        MOD({pointLng}, 360) NOT BETWEEN MOD(min_lng, 360) AND MOD(max_lng, 360)));

虽然这可行,但我强烈建议在SQL查询之外执行MOD计算或添加带有规范化值的额外列。使用此代码中的MOD函数将阻止查询利用这些列上的任何索引。