package no.priv.garshol.duke.databases;
import org.apache.lucene.search.Filter;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceUtils;
import no.priv.garshol.duke.Property;
import no.priv.garshol.duke.comparators.GeopositionComparator;
/**
* All spatial Lucene search functionality is located in this class.
* The class serves no natural design function, but moving all the
* dependencies out here ensures that people who don't need the
* geosearch can use Duke without the spatial libraries.
*/
public class GeoProperty {
private Property prop; // the property containing coordinates
private SpatialContext spatialctx;
private SpatialStrategy strategy;
public GeoProperty(Property prop) {
this.prop = prop;
this.spatialctx = SpatialContext.GEO;
int maxlevels = 11; // FIXME: how to compute?
GeohashPrefixTree grid = new GeohashPrefixTree(spatialctx, maxlevels);
this.strategy = new RecursivePrefixTreeStrategy(grid, prop.getName());
}
/**
* Returns the name of the property.
*/
public String getName() {
return prop.getName();
}
/**
* Geoindexes the coordinates.
*/
public IndexableField[] createIndexableFields(String value) {
return strategy.createIndexableFields(parsePoint(value));
}
/**
* Returns a geoquery.
*/
public Filter geoSearch(String value) {
GeopositionComparator comp = (GeopositionComparator) prop.getComparator();
double dist = comp.getMaxDistance();
double degrees = DistanceUtils.dist2Degrees(dist, DistanceUtils.EARTH_MEAN_RADIUS_KM * 1000.0);
Shape circle = spatialctx.makeCircle(parsePoint(value), degrees);
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, circle);
return strategy.makeFilter(args);
}
/**
* Parses coordinates into a Spatial4j point shape.
*/
private Point parsePoint(String point) {
int comma = point.indexOf(',');
if (comma == -1)
return null;
float lat = Float.valueOf(point.substring(0, comma));
float lng = Float.valueOf(point.substring(comma + 1));
return spatialctx.makePoint(lng, lat);
}
}