package com.esri;
import com.esri.core.geometry.Envelope2D;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.QuadTree;
import com.esri.core.geometry.SpatialReference;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Create an Esri geom QuadTree from an HBase scan
*/
public class SearchQuadTree extends SearchAbstract
{
private final class QuadFeature
{
Geometry geometry;
Map<byte[], Double> attributes;
}
public static final String KEY_GEOMETRY_TYPE = "com.esri.geometryType";
public static final byte[] GEOM = "geom".getBytes();
public static final byte[] SHAPE = "shape".getBytes();
private Point m_point;
private QuadTree m_quadTree;
private List<QuadFeature> m_featureList;
private QuadTree.QuadTreeIterator m_quadTreeIterator;
private SpatialReference m_spatialReference;
@Override
public void setup(
final Configuration configuration,
final List<ColumnInterface> columnList) throws IOException
{
m_point = new Point();
m_spatialReference = SpatialReference.create(configuration.getInt("com.esri.wkid", 4326));
final Geometry.Type geometryType = configuration.getEnum(KEY_GEOMETRY_TYPE, Geometry.Type.Polygon);
int index = 0;
m_featureList = new ArrayList<QuadFeature>();
final int height = configuration.getInt("com.esri.quadTreeHeight", 8);
m_quadTree = new QuadTree(new Envelope2D(-180, -90, 180, 90), height);
final HTable table = new HTable(configuration, configuration.get(GeoEnrichmentJob.KEY_TABLE));
try
{
final Scan scan = new Scan();
scan.setMaxVersions(1);
scan.setCaching(configuration.getInt(SearchHBase.KEY_SCAN_CACHING, 128));
scan.addColumn(GEOM, SHAPE);
for (final ColumnInterface column : columnList)
{
scan.addColumn(column.getFamilyAsBytes(), column.getQualifierAsBytes());
}
final ResultScanner scanner = table.getScanner(scan);
try
{
for (final Result result : scanner)
{
final byte[] bytes = result.getValue(GEOM, SHAPE);
final Geometry geometry = GeometryEngine.geometryFromEsriShape(bytes, geometryType);
final Envelope2D envelope2D = new Envelope2D();
geometry.queryEnvelope2D(envelope2D);
final QuadFeature quadFeature = new QuadFeature();
quadFeature.geometry = geometry;
quadFeature.attributes = new HashMap<byte[], Double>();
for (final ColumnInterface column : columnList)
{
final byte[] value = result.getValue(column.getFamilyAsBytes(), column.getQualifierAsBytes());
quadFeature.attributes.put(column.getQualifierAsBytes(), column.toDouble(value));
}
m_featureList.add(quadFeature);
m_quadTree.insert(index++, envelope2D);
}
}
finally
{
scanner.close();
}
}
finally
{
table.close();
}
m_quadTreeIterator = m_quadTree.getIterator();
}
@Override
public boolean search(
final double lon,
final double lat,
final List<ColumnInterface> columnList) throws IOException
{
resetColumns(columnList);
m_point.setX(lon);
m_point.setY(lat);
m_quadTreeIterator.resetIterator(m_point, 0);
int elementIndex = m_quadTreeIterator.next();
while (elementIndex >= 0)
{
final int featureIndex = m_quadTree.getElement(elementIndex);
final QuadFeature feature = m_featureList.get(featureIndex);
if (GeometryEngine.contains(feature.geometry, m_point, m_spatialReference))
{
for (final ColumnInterface column : columnList)
{
column.setWeight(feature.attributes.get(column.getQualifierAsBytes()));
}
m_found = true;
break;
}
elementIndex = m_quadTreeIterator.next();
}
return m_found;
}
}