/** * */ 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); } }