【问题标题】:Given a latitude and longitude, find users interested in that position给定纬度和经度,找到对该位置感兴趣的用户
【发布时间】:2009-11-06 10:24:57
【问题描述】:

给定一张桌子,

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

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

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

例如使用 Point(7,5) 过滤也应该返回用户 A,如 367-360=7。

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

【问题讨论】:

  • Point(4,5) 是一个更好的例子,因为 7 在 6..367 范围内(不考虑 a==b mod 360)。 4 在范围之外但在 a 的区域内。此外,每个点都在该区域内(6..367、46..407);也许 (50..367, 46..407) 是一个更好的测试区域,因为经度范围不包括某些值,但纬度不包括。另外,端点是否包含在区域中?术语“min”和“max”暗示它们是,但最好是明确的。

标签: mysql geolocation geocoding geospatial spatial


【解决方案1】:

我建议将存储在表中的纬度和经度值限制在 [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)
;

【讨论】:

  • 我喜欢你的解决方案。我对此的一个疑虑是您正在丢失信息。从最初的问题很难判断最终意图是什么,但似乎修改这些值可能会删除有价值的信息。我想保留原始数据并存储修改后的数据以加快搜索速度很容易,但会增加存储空间。干得好!
  • 关于数据其他用途的问题中的(缺乏)要求为更改存储表示留下了一些余地。关于存储这两种形式作为时间/空间权衡的观点很好。
【解决方案2】:

为了回答您的部分问题,如果您说 367 和 7 相同,我同样假设 727 也是。

因此,您希望使用 360 的模数。例如除以 360 的余数。

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

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

看起来像 SELECT b MOD 360 from table; 这样的东西是你想要的。

【讨论】:

    【解决方案3】:

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

    假设:

    • 0 &lt;= x &lt; 360
    • 0 &lt;= y &lt; 360
    • max_lat - min_lat &lt;= 361
    • max_lng - min_lng &lt;= 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 = 0 则 x 必须介于 min 和 max 之间,或者必须介于0 和最大 - 360。

    为清楚起见,呈现上述格式。如果将 360 的加法和减法移动到 x 和 y 参数,则比较会变得恒定,并且可能会显着加快查询速度。

    【讨论】:

      【解决方案4】:

      使用模数确保所有值都在 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 函数会阻止查询利用这些列上的任何索引。

      【讨论】:

      • 如果 min = 350 and max = 370 and point = 2 那么比较变成“WHERE 2 BETWEEN 350 AND 10”,我认为这不会匹配,但应该匹配。
      • 现在你有: mod(2,360) 介于 mod(350,360) 和 mod(370,360)... 或 mod(2,360) 介于 mod(370,360) 和 mod(350,360) 之间,在 350 和10 或 2 在 10 和 350 之间仍然不满足条件。
      猜你喜欢
      • 2019-06-15
      • 1970-01-01
      • 2011-09-09
      • 2017-11-10
      • 1970-01-01
      • 1970-01-01
      • 2016-11-16
      • 2013-06-06
      • 2018-01-08
      相关资源
      最近更新 更多