/**
*
*/
package com.browseengine.bobo.facets.filter;
import java.io.IOException;
import org.apache.lucene.search.DocIdSetIterator;
import com.browseengine.bobo.api.BoboSegmentReader;
import com.browseengine.bobo.docidset.RandomAccessDocIdSet;
import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.impl.GeoFacetHandler.GeoFacetData;
import com.browseengine.bobo.util.BigFloatArray;
import com.browseengine.bobo.util.GeoMatchUtil;
public class GeoFacetFilter extends RandomAccessFilter {
private final FacetHandler<GeoFacetData> _handler;
private final float _lat;
private final float _lon;
private final float _rad;
// variable to specify if the geo distance calculations are in miles. Default is miles
private final boolean _miles;
/**
* @param facetHandler The Geo Facet Handler for this instance
* @param lat latitude value of the user's point of interest
* @param lon longitude value of the user's point of interest
* @param radius Radius from the point of interest
* @param miles variable to specify if the geo distance calculations are in miles. False indicates distance calculation is in kilometers
*/
public GeoFacetFilter(FacetHandler<GeoFacetData> facetHandler, float lat, float lon,
float radius, boolean miles) {
_handler = facetHandler;
_lat = lat;
_lon = lon;
_rad = radius;
_miles = miles;
}
/*
* (non-Javadoc)
* @see
* com.browseengine.bobo.facets.filter.RandomAccessFilter#getRandomAccessDocIdSet(com.browseengine
* .bobo.api.BoboIndexReader)
*/
@Override
public RandomAccessDocIdSet getRandomAccessDocIdSet(BoboSegmentReader reader) throws IOException {
int maxDoc = reader.maxDoc();
final GeoFacetData dataCache = _handler.getFacetData(reader);
return new GeoDocIdSet(dataCache.get_xValArray(), dataCache.get_yValArray(),
dataCache.get_zValArray(), _lat, _lon, _rad, maxDoc, _miles);
}
private static final class GeoDocIdSet extends RandomAccessDocIdSet {
private final BigFloatArray _xvals;
private final BigFloatArray _yvals;
private final BigFloatArray _zvals;
private final float _radius;
private final float _targetX;
private final float _targetY;
private final float _targetZ;
private final float _delta;
private final int _maxDoc;
// variable to specify if the geo distance calculations are in miles. Default is miles
private final boolean _miles;
/**
*
* @param xvals array of x coordinate values for docid
* @param yvals array of y coordinate values for docid
* @param zvals array of z coordinate values for docid
* @param lat target latitude
* @param lon target longitude
* @param radius target radius
* @param maxdoc max doc in the docid set
* @param miles variable to specify if the geo distance calculations are in miles. False indicates distance calculation is in kilometers
*/
GeoDocIdSet(final BigFloatArray xvals, final BigFloatArray yvals, final BigFloatArray zvals,
final float lat, final float lon, final float radius, final int maxdoc, boolean miles) {
_xvals = xvals;
_yvals = yvals;
_zvals = zvals;
_miles = miles;
if (_miles) _radius = GeoMatchUtil.getMilesRadiusCosine(radius);
else _radius = GeoMatchUtil.getKMRadiusCosine(radius);
float[] coords = GeoMatchUtil.geoMatchCoordsFromDegrees(lat, lon);
_targetX = coords[0];
_targetY = coords[1];
_targetZ = coords[2];
if (_miles) _delta = (radius / GeoMatchUtil.EARTH_RADIUS_MILES);
else _delta = (radius / GeoMatchUtil.EARTH_RADIUS_KM);
_maxDoc = maxdoc;
}
@Override
public boolean get(int docid) {
float docX = _xvals.get(docid);
float docY = _yvals.get(docid);
float docZ = _zvals.get(docid);
return inCircle(docX, docY, docZ, _targetX, _targetY, _targetZ, _radius);
}
@Override
public DocIdSetIterator iterator() {
return new GeoDocIdSetIterator(_xvals, _yvals, _zvals, _targetX, _targetY, _targetZ, _delta,
_radius, _maxDoc);
}
}
private static class GeoDocIdSetIterator extends DocIdSetIterator {
private final BigFloatArray _xvals;
private final BigFloatArray _yvals;
private final BigFloatArray _zvals;
private final float _radius;
private final float _targetX;
private final float _targetY;
private final float _targetZ;
private final float _delta;
private final int _maxDoc;
private int _doc;
GeoDocIdSetIterator(BigFloatArray xvals, BigFloatArray yvals, BigFloatArray zvals,
float targetX, float targetY, float targetZ, float delta, float radiusCosine, int maxdoc) {
_xvals = xvals;
_yvals = yvals;
_zvals = zvals;
_targetX = targetX;
_targetY = targetY;
_targetZ = targetZ;
_delta = delta;
_radius = radiusCosine;
_maxDoc = maxdoc;
_doc = -1;
}
@Override
final public int docID() {
return _doc;
}
@Override
final public int nextDoc() throws IOException {
final float x = _targetX;
final float xu = x + _delta;
final float xl = x - _delta;
final float y = _targetY;
final float yu = y + _delta;
final float yl = y - _delta;
final float z = _targetZ;
final float zu = z + _delta;
final float zl = z - _delta;
int docid = _doc;
while (++docid < _maxDoc) {
float docX = _xvals.get(docid);
if (docX > xu || docX < xl) continue;
float docY = _yvals.get(docid);
if (docY > yu || docY < yl) continue;
float docZ = _zvals.get(docid);
if (docZ > zu || docZ < zl) continue;
if (GeoFacetFilter.inCircle(docX, docY, docZ, _targetX, _targetY, _targetZ, _radius)) {
_doc = docid;
return _doc;
}
}
_doc = DocIdSetIterator.NO_MORE_DOCS;
return _doc;
}
@Override
final public int advance(int targetId) throws IOException {
if (_doc < targetId){
_doc = targetId - 1;
}
final float x = _targetX;
final float xu = x + _delta;
final float xl = x - _delta;
final float y = _targetY;
final float yu = y + _delta;
final float yl = y - _delta;
final float z = _targetZ;
final float zu = z + _delta;
final float zl = z - _delta;
int docid = _doc;
while (++docid < _maxDoc) {
float docX = _xvals.get(docid);
if (docX > xu || docX < xl) continue;
float docY = _yvals.get(docid);
if (docY > yu || docY < yl) continue;
float docZ = _zvals.get(docid);
if (docZ > zu || docZ < zl) continue;
if (GeoFacetFilter.inCircle(docX, docY, docZ, _targetX, _targetY, _targetZ, _radius)) {
_doc = docid;
return _doc;
}
}
_doc = DocIdSetIterator.NO_MORE_DOCS;
return _doc;
}
@Override
public long cost() {
// TODO Auto-generated method stub
return 0;
}
}
public static boolean inCircle(float docX, float docY, float docZ, float targetX, float targetY,
float targetZ, float radCosine) {
if (docX == -1.0f && docY == -1.0f && docZ == -1.0f) return false;
float dotProductCosine = (docX * targetX) + (docY * targetY) + (docZ * targetZ);
return (radCosine <= dotProductCosine);
}
}