【问题标题】:Google App Engine GeohashingGoogle App Engine 地理哈希
【发布时间】:2011-01-04 20:05:25
【问题描述】:

我正在使用 GWT 和 App Engine 编写一个网络应用程序。我的应用程序需要根据纬度、经度发布和查询项目。

由于 google 的分布式数据库设计,您不能简单地查询一组不等式。相反,他们建议进行地理哈希处理。该方法在此页面上进行了描述。

http://code.google.com/appengine/articles/geosearch.html

本质上,您预先计算了一个边界框,以便您可以查询已使用该边界框标记的项目。

我不明白这个过程的一部分。 “切片”属性是什么意思?

感谢您的帮助!

【问题讨论】:

  • 您不认为您的问题可以更具体一点吗?您能否让我找到您正在谈论的 slice 实例,还是我必须阅读整个大页面?
  • 我所指的切片实例首先在“输入位置”一节中进行了描述,“除了分辨率,我们还指定了一个‘切片’。一个切片就是分割的精细程度地理框中的每个分辨率级别。”

标签: google-app-engine map geolocation geocoding geohashing


【解决方案1】:

有关 Geomodel 的完整 java 移植,请参阅http://code.google.com/p/javageomodel/

有一个demo类来教你如何使用它。

【讨论】:

    【解决方案2】:

    您可能对在 Google App Engine 上实现类似 geohash 的系统的 GeoModel 开源项目感兴趣,而不是自己实现 geohash。您无需了解所有细节,只需导入此库并调用proximity_fetch()bounding_box_fetch()

    This more recent article 描述了它的工作原理并提供了一个使用它的示例。

    【讨论】:

    • 你知道Java版本吗?我打算使用 App Engine 的 Java API。
    • 我不知道有一个。但是您可以自己移植它;)我敢打赌,维护 Python GeoModel 的 Google 人员会愿意提供一点帮助。
    【解决方案3】:

    您可以使用框的西北角坐标和两个参数:分辨率和切片来定义边界框,而不是使用 4 个坐标(最小和最大纬度,最小和最大经度)来定义边界框。 分辨率定义了盒子的比例,它被实现为小数点以下的位数。 切片是框的宽度和高度,以最低有效数字为单位。

    geobox.py 中的 cmets 更详细地解释了这一点,并提供了很好的示例:

    要查询边界框的成员,我们从一些输入坐标开始 像 lat=37.78452 long=-122.39532 (分辨率均为 5)。然后我们围绕这些 上下坐标到最近的“切片”以生成地理框。一片” 是在 geobox 中划分每个分辨率级别的精细程度。最小值 切片大小为 1,最大值没有限制,因为更大的切片将 只是溢出到较低的分辨率(希望示例能够解释)。

    一些例子:

    分辨率=5,切片=2,纬度=37.78452 长=-122.39532: "37.78452|-122.39532|37.78450|-122.39530"

    分辨率=5,切片=10,纬度=37.78452 长=-122.39532: "37.78460|-122.39540|37.78450|-122.39530"

    分辨率=5,切片=25,纬度=37.78452 长=-122.39532: "37.78475|-122.39550|37.78450|-122.39525"

    【讨论】:

    • 在我看来,降低分辨率和使用更大的切片都会增加边界框的大小。如何决定这些参数?不同分辨率和切片大小的权衡是什么?
    【解决方案4】:

    我正在研究 GWT/GAE project 并且遇到了同样的问题。我的解决方案是使用一个Geohash 类,我稍微修改了一下使其对GWT 友好。这非常适合我的邻近搜索需求。

    如果您从未见过 Geohashes 的实际应用,请查看 Dave Troy's JS 演示页面。

    【讨论】:

      【解决方案5】:

      在 App Engine 中进行地理空间搜索的替代方法是 Search Api。您无需担心 geohashing 或实施细节,您将能够搜索地理点附近的元素。

      https://developers.google.com/appengine/docs/python/search/overview#Performing_Location-Based_Searches

      【讨论】:

        【解决方案6】:

        我正在从事一个使用 geohashing 的 GAE 项目,这个 python 库为我解决了问题:http://mappinghacks.com/code/geohash.py.txt

        【讨论】:

          【解决方案7】:

          我还需要 Java 版本的 GeoModel。我之前使用过 Geohash,它允许我在给定的边界框中获取位置。但是在排序方面有相当大的限制:为了让 BigTable 接受像geohash > '" + bottomLeft + "' && geohash < '" + topRight + "'" 这样的过滤器,您还必须按geohash 对列表进行排序,这使得无法按照其他标准对其进行排序(特别是如果你想使用分页)。同时,除了Java代码之外,我只是想不出一个解决方案来按距离(从给定的用户位置,即边界框的中心)对结果进行排序。同样,如果您需要分页,这将不起作用。

          由于这些问题,我不得不使用不同的方法,而 GeoModel/Geoboxes 似乎就是这样。所以,我将 Python 代码移植到 Java 中,它运行良好!结果如下:

          public class Geobox {
          
              private static double roundSlicedown(double coord, double slice) {
                  double remainder = coord % slice;
                  if (remainder == Double.NaN) {
                      return coord;
                  }
                  if (coord > 0) {
                      return coord - remainder + slice;
                  } else {
                      return coord - remainder;
                  }
              }
          
              private static double[] computeTuple(double lat, double lng,
                      int resolution, double slice) {
                  slice = slice * Math.pow(10, -resolution);
                  double adjustedLat = roundSlicedown(lat, slice);
                  double adjustedLng = roundSlicedown(lng, slice);
                  return new double[] { adjustedLat, adjustedLng - slice,
                          adjustedLat - slice, adjustedLng };
              }
          
              private static String formatTuple(double[] values, int resolution) {
                  StringBuffer s = new StringBuffer();
                  String format = String.format("%%.%df", resolution);
                  for (int i = 0; i < values.length; i++) {
                      s.append(String.format(format, values[i]).replace(',','.'));
                      if (i < values.length - 1) {
                          s.append("|");
                      }
                  }
                  return s.toString();
              }
          
              public static String compute(double lat, double lng, int resolution,
                      int slice) {
                  return formatTuple(computeTuple(lat, lng, resolution, slice),
                          resolution);
              }
          
              public static List<String> computeSet(double lat, double lng,
                      int resolution, double slice) {
                  double[] primaryBox = computeTuple(lat, lng, resolution, slice);
                  slice = slice * Math.pow(10, -resolution);
                  List<String> set = new ArrayList<String>();
                  for (int i = -1; i < 2; i++) {
                      double latDelta = slice * i;
                      for (int j = -1; j < 2; j++) {
                          double lngDelta = slice * j;
                          double[] adjustedBox = new double[] { primaryBox[0] + latDelta,
                                  primaryBox[1] + lngDelta, primaryBox[2] + latDelta,
                                  primaryBox[3] + lngDelta };
                          set.add(formatTuple(adjustedBox, resolution));
                      }
                  }
                  return set;
              }
          }
          

          【讨论】:

          【解决方案8】:

          很抱歉回复晚了,但我已经有一段时间没有返回此页面了。使用 Geobox 方法的 GeoDao 实现可能如下所示:

          public class GeoDaoImpl extends DaoImpl<T extends GeoModel> {
          
              // geobox configs are: resolution, slice, use set (1 = true)
              private final static int[][] GEOBOX_CONFIGS = 
                  { { 4, 5, 1 },
                    { 3, 2, 1 },
                    { 3, 8, 0 },
                    { 3, 16, 0 },
                    { 2, 5, 0 } };
          
              public GeoDaoImpl(Class<T> persistentClass) {
                  super(persistentClass);
              }
          
              public List<T> findInGeobox(double lat, double lng, int predefinedBox, String filter, String ordering, int offset, int limit) {
                  return findInGeobox(lat, lng, GEOBOX_CONFIGS[predefinedBox][0], GEOBOX_CONFIGS[predefinedBox][1], filter, ordering, offset, limit);     
              }
          
              public List<T> findInGeobox(double lat, double lng, int resolution, int slice, String filter, String ordering, int offset, int limit) {
                  String box = Geobox.compute(lat, lng, resolution, slice);
                  if (filter == null) {
                      filter = "";
                  } else {
                      filter += " && ";
                  }
                  filter += "geoboxes=='" + box + "'";        
                  return super.find(persistentClass, filter, ordering, offset, limit);
              }
          
              public List<T> findNearest(final double lat, final double lng, String filter, String ordering, int offset, int limit) {
                  LinkedHashMap<String, T> uniqueList = new LinkedHashMap<String, T>();
                  int length = offset + limit;
                  for (int i = 0; i < GEOBOX_CONFIGS.length; i++) {       
                      List<T> subList = findInGeobox(lat, lng, i, filter, ordering, 0, limit);
                      for (T model : subList) {
                          uniqueList.put(model.getId(), model);
                      }
                      if (uniqueList.size() >= length) {
                          break;
                      }
                  }
          
                  List<T> list = new ArrayList<T>();
                  int i = 0;
                  for (String key : uniqueList.keySet()) {
                      if (i >= offset && i <= length) {
                          list.add(uniqueList.get(key));
                      }
                      i++;
                  }
          
                  Collections.sort(list, new Comparator<T>() {
                      public int compare(T model1, T model2) {                
                          double distance1 = Geoutils.distFrom(model1.getLatitude(), model1.getLongitude(), lat, lng);
                          double distance2 = Geoutils.distFrom(model2.getLatitude(), model2.getLongitude(), lat, lng);
                          return Double.compare(distance1, distance2);
                      }
                  });
          
                  return list;
              }
          
              @Override
              public void save(T model) {
                  preStore(model);
                  super.save(model);
              }
          
              private void preStore(T model) {
                  // geoboxes are needed to find the nearest entities and sort them by distance
                  List<String> geoboxes = new ArrayList<String>();
                  for (int[] geobox : GEOBOX_CONFIGS) {
                       // use set
                       if (geobox[2] == 1) {
                           geoboxes.addAll(Geobox.computeSet(model.getLatitude(), model.getLongitude(), geobox[0], geobox[1]));
                       } else {
                           geoboxes.add(Geobox.compute(model.getLatitude(), model.getLongitude(), geobox[0], geobox[1]));
                       }
                  }
                  model.setGeoboxes(geoboxes);
              }
          
          }
          

          【讨论】:

            猜你喜欢
            • 2013-03-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-16
            • 1970-01-01
            • 2013-07-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多