/** * File : SpatialIndex.java * Created on : 16 Apr 2008 */ package iamrescue.belief.spatial; import iamrescue.belief.IAMWorldModel; import iamrescue.util.SpatialUtils; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import javolution.util.FastMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import rescuecore2.standard.entities.Area; import rescuecore2.standard.entities.Building; import rescuecore2.standard.entities.Human; import rescuecore2.standard.entities.Road; import rescuecore2.standard.entities.StandardEntity; import rescuecore2.standard.entities.StandardPropertyURN; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.EntityID; import rescuecore2.worldmodel.EntityListener; import rescuecore2.worldmodel.Property; import rescuecore2.worldmodel.WorldModel; import rescuecore2.worldmodel.WorldModelListener; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.index.quadtree.Quadtree; /** * @author ss2 * */ public class SlowSpatialQuadtreeIndex implements ISpatialIndex, EntityListener, WorldModelListener<StandardEntity> { /** * Currently uses three quad trees for different types of objects */ private Quadtree movingTree; private Quadtree roadsAndNodesTree; private Quadtree buildingTree; // private final static GeometryFactory factory = new GeometryFactory(); private Map<StandardEntity, Envelope> insertedEnvelopes; private static Log log = LogFactory.getLog(SlowSpatialQuadtreeIndex.class); private IAMWorldModel worldModel = null; private Map<EntityID, Geometry> geometries; public SlowSpatialQuadtreeIndex(IAMWorldModel worldModel) { this.worldModel = worldModel; movingTree = new Quadtree(); roadsAndNodesTree = new Quadtree(); buildingTree = new Quadtree(); insertedEnvelopes = new FastMap<StandardEntity, Envelope>(); geometries = new FastMap<EntityID, Geometry>(); Collection<StandardEntity> allObjects = worldModel.getAllEntities(); for (StandardEntity object : allObjects) { addObject(object); if (object instanceof Area) { geometries.put(object.getID(), SpatialUtils.createGeometry( object, worldModel)); } } worldModel.addWorldModelListener(this); } private void addObject(StandardEntity object) { if (SpatialUtils.isSpatialObject(object)) { Envelope env = SpatialUtils.createBoundingEnvelope(object, worldModel); if (env != null) { selectQuadtree(object).insert(env, object); assert !insertedEnvelopes.containsKey(object); insertedEnvelopes.put(object, env); } // Also register, even if position is null, so that updates are // received. object.addEntityListener(this); /* * Collection<RescueObjectProperty> properties = object * .getSupportedProperties(); for (RescueObjectProperty prop : * spatialProperties) { if (properties.contains(prop)) { try { * * } catch (PropertyNotSupportedException e) { * log.error("This exception should not have occurred", e); } } } */ } } private void removeObject(StandardEntity object) { if (SpatialUtils.isSpatialObject(object)) { Envelope env = insertedEnvelopes.remove(object); geometries.remove(object.getID()); if (env != null) { selectQuadtree(object).remove(env, object); } // Also remove listener(s)! object.removeEntityListener(this); } } private Quadtree selectQuadtree(StandardEntity object) { if (object instanceof Road) { return roadsAndNodesTree; } else if (object instanceof Building) { return buildingTree; } else if (object instanceof Human) { return movingTree; } else { log.error("Cannot find quadtree for object " + object); return null; } } public Collection<StandardEntity> query(SpatialQuery query) { Geometry geometry = query.getGeometry(); Geometry envelope = geometry.getEnvelope(); int distance = query.getDistance(); Envelope rectangle; if (envelope instanceof Polygon) { Polygon polyRectangle = (Polygon) envelope; rectangle = new Envelope(polyRectangle.getCoordinates()[0].x - distance, polyRectangle.getCoordinates()[1].x + distance, polyRectangle.getCoordinates()[1].y - distance, polyRectangle.getCoordinates()[2].y + distance); } else if (envelope instanceof Point) { Point pointRectangle = (Point) envelope; if (pointRectangle.isEmpty()) { throw new IllegalArgumentException("Invalid shape: " + geometry + ". Must represent either a point or an area."); } rectangle = new Envelope(pointRectangle.getX() - distance, pointRectangle.getX() + distance, pointRectangle.getY() - distance, pointRectangle.getY() + distance); } else if (envelope instanceof LineString) { LineString line = (LineString) envelope; double minX = line.getStartPoint().getX(); double maxX = line.getStartPoint().getX(); double minY = line.getStartPoint().getY(); double maxY = line.getStartPoint().getY(); for (int i = 1; i < line.getNumPoints(); i++) { Point p = line.getPointN(i); if (p.getX() < minX) { minX = p.getX(); } if (p.getX() > maxX) { maxX = p.getX(); } if (p.getY() < minY) { minY = p.getY(); } if (p.getY() > maxY) { maxY = p.getY(); } } rectangle = new Envelope(minX - distance, maxX + distance, minY - distance, maxY + distance); } else { throw new IllegalArgumentException("Invalid shape: " + geometry + ". Must represent either a point or an area."); } ArrayList<StandardEntity> list = new ArrayList<StandardEntity>(); // Now query all relevant trees Class<? extends StandardEntity> queryClass = query.getQueryClass(); SpatialObjectVisitor visitor = new SpatialObjectVisitor(query, list, geometries, worldModel); if (queryClass.isAssignableFrom(Road.class) || Road.class.isAssignableFrom(queryClass)) { roadsAndNodesTree.query(rectangle, visitor); } if (queryClass.isAssignableFrom(Human.class) || Human.class.isAssignableFrom(queryClass)) { movingTree.query(rectangle, visitor); } if (queryClass.isAssignableFrom(Building.class) || Building.class.isAssignableFrom(queryClass)) { buildingTree.query(rectangle, visitor); } return list; } public void propertyChanged(Entity e, Property p, Object oldValue, Object newValue) { if (p.getURN().equals(StandardPropertyURN.POSITION.toString()) || p.getURN().equals(StandardPropertyURN.X.toString()) || p.getURN().equals(StandardPropertyURN.Y.toString())) { removeObject((StandardEntity) e); addObject((StandardEntity) e); } } public void entityAdded(WorldModel<? extends StandardEntity> model, StandardEntity e) { addObject(e); } public void entityRemoved(WorldModel<? extends StandardEntity> model, StandardEntity e) { removeObject(e); } }