/*
* <copyright>
* Copyright 2011 BBN Technologies
* </copyright>
*/
package com.bbn.openmap.dataAccess.shape;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import com.bbn.openmap.geo.ExtentIndex;
import com.bbn.openmap.geo.ExtentIndex.ArrayListExtentIndexImpl;
import com.bbn.openmap.geo.GeoExtent;
import com.bbn.openmap.geo.GeoPath;
import com.bbn.openmap.geo.GeoPoint;
import com.bbn.openmap.geo.GeoRegion;
import com.bbn.openmap.geo.Intersection;
import com.bbn.openmap.geo.MatchCollector;
import com.bbn.openmap.geo.MatchFilter;
import com.bbn.openmap.geo.MatchParameters;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMPoint;
import com.bbn.openmap.omGraphics.OMPoly;
import com.bbn.openmap.proj.coords.GeoCoordTransformation;
/**
* A GeoExtentIndex that knows how to work with Shape files. Plots the shape
* file contents in memory in lat/lon space, can then be used with the
* Intersection class to do spatial analysis on the shape file contents.
*
* @author ddietrick
*/
public class ShapeGeoIndex
extends ArrayListExtentIndexImpl {
protected HashMap<Object, OMGraphic> omgraphics;
private ShapeGeoIndex(Builder builder) {
super(builder.numberOfBuckets, builder.margin);
omgraphics = new HashMap<Object, OMGraphic>();
EsriGraphicList graphicList = EsriGraphicList.getEsriGraphicList(builder.shapeFile, null,
builder.geoCoordTransform);
if (graphicList != null) {
load(graphicList);
}
}
/**
* After you test for intersections with some GeoExtent, you get an iterator of GeoExtents. You can get the ID
* from each of those, which will in turn allow you to ask for the OMGraphic representing the shape from the given
* file. The ID is an integer index into the shape file and attribute dbf file, btw.
*
* @param id The id provided by the extent in the intersection test.
* @return OMGraphic or null (if not found).
*/
public OMGraphic getForID(Object id) {
if (omgraphics != null) {
return omgraphics.get(id);
}
return null;
}
public void load(EsriGraphicList list) {
if (list != null) {
int type = list.getType();
switch (type) {
case ShapeConstants.SHAPE_TYPE_POINT:
case ShapeConstants.SHAPE_TYPE_POINTM:
case ShapeConstants.SHAPE_TYPE_POINTZ:
loadPoints(list);
break;
case ShapeConstants.SHAPE_TYPE_POLYGON:
case ShapeConstants.SHAPE_TYPE_POLYGONM:
case ShapeConstants.SHAPE_TYPE_POLYGONZ:
loadPolygons(list);
break;
case ShapeConstants.SHAPE_TYPE_POLYLINE:
case ShapeConstants.SHAPE_TYPE_POLYLINEM:
case ShapeConstants.SHAPE_TYPE_POLYLINEZ:
loadPolylines(list);
break;
default:
// case ShapeConstants.SHAPE_TYPE_NULL:
}
}
}
/**
* @param list assuming list is not null, and has two levels at most -
* points, or lists of points.
*/
private void loadPoints(EsriGraphicList list) {
for (OMGraphic omg : list) {
if (omg instanceof OMGraphicList) {
Object recNum = ((OMGraphicList) omg).getAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE);
for (OMGraphic pnt : (OMGraphicList) omg) {
addPoint((OMPoint) pnt, recNum);
}
} else {
addPoint((OMPoint) omg, omg.getAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE));
}
}
}
private void addPoint(OMPoint omp, Object id) {
double latitude = omp.getLat();
double longitude = omp.getLon();
GeoPoint.Impl geoPoint = new GeoPoint.Impl(latitude, longitude);
geoPoint.setID(id);
addExtent(geoPoint);
omgraphics.put(id, omp);
}
/**
* @param list assuming list is not null
*/
private void loadPolygons(EsriGraphicList list) {
for (OMGraphic omg : list) {
if (omg instanceof OMGraphicList) {
Object recNum = ((OMGraphicList) omg).getAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE);
for (OMGraphic poly : (OMGraphicList) omg) {
addPolygon((OMPoly) poly, recNum);
}
} else {
addPolygon((OMPoly) omg, omg.getAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE));
}
}
}
private void addPolygon(OMPoly omp, Object id) {
double[] latlonArray = omp.getLatLonArray();
GeoRegion.Impl region = new GeoRegion.Impl(latlonArray, false);
region.setID(id);
addExtent(region);
omgraphics.put(id, omp);
}
/**
* @param list assuming list is not null
*/
private void loadPolylines(EsriGraphicList list) {
for (OMGraphic omg : list) {
if (omg instanceof OMGraphicList) {
Object recNum = ((OMGraphicList) omg).getAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE);
for (OMGraphic pnt : (OMGraphicList) omg) {
addPolyline((OMPoly) pnt, recNum);
}
} else {
addPolyline((OMPoly) omg, omg.getAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE));
}
}
}
private void addPolyline(OMPoly omp, Object id) {
double[] latlonArray = omp.getLatLonArray();
GeoPath.Impl region = new GeoPath.Impl(latlonArray);
region.setID(id);
addExtent(region);
omgraphics.put(id, omp);
}
/**
* Get an iterator with all of the objects in this ShapeGeoIndex that
* intersect with the given extent.
*
* @param extent GeoExtent (GeoPoint, GeoPath, GeoRegion) to test against.
* @return Iterator over intersecting shape objects.
*/
public Iterator getIntersections(GeoExtent extent) {
return getIntersections(extent, new MatchFilter.MatchParametersMF(MatchParameters.STRICT),
new MatchCollector.SetMatchCollector());
}
/**
* Get an iterator with all of the objects in this ShapeGeoIndex that
* intersect with the given extent.
*
* @param extent GeoExtext (GeoPoint, GeoPath, GeoRegion) to test against.
* @param filter MatchFilter a MatchFilter can eliminate
* @param collector
* @return Iterator over intersecting shape objects.
*/
public Iterator getIntersections(GeoExtent extent, MatchFilter filter, MatchCollector collector) {
Intersection intersection = Intersection.intersector();
intersection.consider(extent, this);
collector = intersection.getCollector();
return collector.iterator();
}
/**
* Use this class to create a ShapeGeoIndex.
*
* @author ddietrick
*/
public static class Builder {
private URL shapeFile;
private GeoCoordTransformation geoCoordTransform;
private int numberOfBuckets = ExtentIndex.AbstractExtentIndex.D_NBUCKETS;
private double margin = ExtentIndex.AbstractExtentIndex.D_MARGIN;
public Builder(URL shape) {
this.shapeFile = shape;
}
public Builder setGeoCoordTransformation(GeoCoordTransformation geoCoordTransformation) {
this.geoCoordTransform = geoCoordTransformation;
return this;
}
public Builder setNumberOfBuckets(int nBuckets) {
this.numberOfBuckets = nBuckets;
return this;
}
public Builder setMargin(double mar) {
this.margin = mar;
return this;
}
public ShapeGeoIndex create() {
return new ShapeGeoIndex(this);
}
}
}