12

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

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

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

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

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

谢谢你的帮助!

4

8 回答 8

7

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

有一个演示类向您解释如何使用它。

于 2010-02-26T16:21:27.243 回答
6

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

这篇更新的文章描述了它的工作原理并提供了一个使用它的示例。

于 2010-01-15T09:32:11.803 回答
3

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

geobox.py中的注释更详细地解释了这一点,并附有很好的例子:

为了查询边界框的成员,我们从一些输入坐标开始,例如 lat=37.78452 long=-122.39532(分辨率均为 5)。然后我们将这些坐标上下四舍五入到最近的“切片”以生成地理框。“切片”是在地理框中划分每个分辨率级别的精细程度。最小切片大小为 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”

于 2010-01-14T08:06:49.227 回答
3

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

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

于 2010-01-23T03:20:57.937 回答
1

在 App Engine 中进行地理空间搜索的另一种方法是 Search Api。您无需担心地理散列或实施细节,您将能够搜索靠近地理点的元素。

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

于 2013-05-20T05:31:31.650 回答
0

I was working on a GAE project with geohashing and this python library did the trick for me: http://mappinghacks.com/code/geohash.py.txt

于 2010-02-01T03:00:09.673 回答
0

我还需要一个 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;
    }
}
于 2010-02-01T02:17:21.663 回答
-1

很抱歉回答迟了,但我有一段时间没有返回此页面。使用 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);
    }

}
于 2010-02-24T22:12:55.013 回答