Lucene空间,准确

Kev*_*vin 6 java lucene

我正在遵循"Lucene in Action"中的示例,第308-315页,其中描述了Lucene Spatial.我正在使用lucene 2.9.4.我已经使用http://geocoder.us/service/distance端点来计算某些位置之间的距离,然后编写单元测试以验证索引可以找到给定半径内的位置.

我想知道我能指望的是多么准确.例如,如果我给出半径10.0并且我的纬度/经度点之间的距离是9.99英里,那么它能在所有情况下找到这个位置吗?

引发这个问题的是,我发现搜索对于小半径值(例如10.0或更小)非常准确,而对于较大值(例如r = 25.0)则不准确.

有什么我可能做错了吗?搜索者是否可能选择一个不具有给定半径的所有纬度/长度的层?我的理解是它选择了保证半径内所有点的最小层,即层算法只是一个优化.

编辑:我也发现了这个:https://issues.apache.org/jira/browse/LUCENE-2519和明显固定的代码:http://code.google.com/p/spatial-search-lucene/source /browse/trunk/src/main/java/org/apache/lucene/spatial/tier/projection/SinusoidalProjector.java?r=38,但当我修补我的代码以使用固定的SinusoidalProjector时,我的索引在所有情况下返回零广告.

这并没有让我充满信心:

http://www.lucidimagination.com/blog/2010/07/20/update-spatial-search-in-apache-lucene-and-solr/

http://www.lucidimagination.com/search/document/c32e81783642df47/spatial_rethinking_cartesian_tiers_implementation#c32e81783642df47

它似乎表明整个代码中存在黑客攻击并且简单地修补SinusoidalProjector是不够的.

Kev*_*vin 4

我花了一些时间查看源代码,我想我明白出了什么问题。首先,我做了一个错误的假设,即 geocoder.us 计算的距离与 lucene 内部计算的点之间的距离相同。这些值很接近,但并不精确。所以我通过调用 lucene 来计算纬度/经度对之间的距离

double distance = DistanceUtils.getInstance().getDistanceMi(lat1,lon1,lat2,lon2);
Run Code Online (Sandbox Code Playgroud)

接下来,我深入研究了 DistanceQueryBuilder 类http://grepcode.com/file/repo1.maven.org/maven2/org.apache.lucene/lucene-spatial/2.9.4/org/apache/lucene/spatial/tier/DistanceQueryBuilder。 java?av=f,我认为其中有一个错误。

它计算边界框以获取笛卡尔层,如下所示:

CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix);
Filter cartesianFilter = cpf.getBoundingArea(lat, lng, miles);
Run Code Online (Sandbox Code Playgroud)

通过查看 LLRect.createBox http://grepcode.com/file/repo1.maven.org/maven2/org.apache.lucene/lucene-spatial/2.9.4/org/apache/lucene/spatial/就很清楚了Geometry/shape/LLRect.java#LLRect.createBox%28org.apache.lucene.spatial.geometry.LatLng%2Cdouble%2Cdouble%29 getBoudningArea 的第三个参数将被视为边界框的完整宽度/高度。因此传递半径值会导致边界框太小。

修复方法是提供 DistanceQueryBuilder 的替代版本来执行此操作:

Filter cartesianFilter = cpf.getBoundingArea(lat,lng,miles*2);
Run Code Online (Sandbox Code Playgroud)

这似乎有效。我仍然相信 DistanceApproximation http://grepcode.com/file/repo1.maven.org/maven2/org.apache.lucene/lucene-spatial/2.9.4/org/apache/lucene/spatial/geometry/shape /DistanceApproximation.java#DistanceApproximation.getMilesPerLngDeg%28double%29已被破坏,因为似乎以下操作应该是可逆的,但它们不是:

// similar to implementation of DistanceUtils.getBoundary():
double milesPerLng = DistanceApproximation.getMilesPerLngDeg(lat);
double milesPerLat = DistanceApproximation.getMilesperLatDeg();


double lngDelta = radius / milesPerLng;
double latDelta = radius / milesPerLat;

// Now it seems like this should be roughly true:
assertEquals(radius, DistanceUtils.getInstance().getDistanceMi(lat,lng,lat,lng+lngDelta));
assertEquals(radius, DistanceUtils.getInstance().getDistanceMi(lat,lng,lat+latDelta,lng));
Run Code Online (Sandbox Code Playgroud)

但事实并非如此。例如,当上面的代码给出 lat=34、lng=-118 和 radius=25(而不是断言我只是打印结果)时,我得到:

Lng delta: 0.36142327178505024, dist: 20.725929003138496
Lat delta: 0.4359569489852007, dist: 30.155567734407825
Run Code Online (Sandbox Code Playgroud)

我猜测代码之所以有效,只是因为在选择边界框后选择的笛卡尔层将产生比边界框稍大的区域。但我认为这并不能得到保证。

我希望对此有更多了解的人可以发表评论,因为这些只是深入研究了一下午代码后的观察结果。我确实注意到,最近的 lucene 空间代码位于 googlecode 上:http://code.google.com/p/spatial-search-lucene/,而且看起来实现已经发生了很大的变化,但我没有深入研究细节。