// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/shape/BufferedShapeLayer.java,v $
// $RCSfile: BufferedShapeLayer.java,v $
// $Revision: 1.11 $
// $Date: 2008/10/16 03:26:50 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.layer.shape;
import java.awt.event.ActionEvent;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;
import java.util.logging.Level;
import com.bbn.openmap.io.FormatException;
import com.bbn.openmap.layer.shape.SpatialIndex.Entry;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.proj.ProjMath;
import com.bbn.openmap.proj.Projection;
/**
* An OpenMap Layer that displays shape files. This loads the data up front and
* then just reprojects/repaints when needed. Note that the ESRIRecords have
* been updated so that the OMGraphics that get created from them are loaded
* with an Integer object that notes the number of the record as it was read
* from the .shp file. This lets you align the object with the correct attribute
* data in the .dbf file.
*/
public class BufferedShapeLayer extends ShapeLayer {
private static final long serialVersionUID = 1L;
protected OMGraphicList bufferedList = null;
/**
* Initializes an empty shape layer.
*/
public BufferedShapeLayer() {
super();
setProjectionChangePolicy(new com.bbn.openmap.layer.policy.StandardPCPolicy(this));
}
/**
* Creates a ShapeLayer that reads the give shape file.
*
* @param pathToShpFile
*/
public BufferedShapeLayer(String pathToShpFile) {
super(pathToShpFile);
setProjectionChangePolicy(new com.bbn.openmap.layer.policy.StandardPCPolicy(this));
}
/**
* Get the graphics for the entire planet.
*/
protected OMGraphicList getWholePlanet() throws IOException, FormatException {
spatialIndex.readIndexFile(null, coordTransform);
return spatialIndex.getAllOMGraphics((OMGraphicList) null, drawingAttributes, (Projection) null, coordTransform);
}
/**
* This overridden method checks to see if the buffered OMGraphicList is
* created, and then returns a subset of OMGraphics that are actually on the
* map. If the buffered OMGraphicList hasn't been created yet, it gets
* created here.
*/
public synchronized OMGraphicList prepare() {
OMGraphicList list = getList();
if (list != null) {
list.clear();
} else {
list = new OMGraphicList();
}
if (spatialIndex == null)
return list;
try {
if (bufferedList == null) {
bufferedList = getWholePlanet();
}
} catch (FormatException fe) {
if (logger.isLoggable(Level.FINE)) {
logger.fine(fe.getMessage());
}
return list;
} catch (IOException ioe) {
if (logger.isLoggable(Level.FINE)) {
logger.fine(ioe.getMessage());
}
return list;
}
// grab local
Projection proj = getProjection();
Point2D ul = proj.getUpperLeft();
Point2D lr = proj.getLowerRight();
double ulLat = ul.getY();
double ulLon = ul.getX();
double lrLat = lr.getY();
double lrLon = lr.getX();
// check for dateline anomaly on the screen. we check for
// ulLon >= lrLon, but we need to be careful of the check for
// equality because of floating point arguments...
try {
if (ProjMath.isCrossingDateline(ulLon, lrLon, proj.getScale())) {
if (logger.isLoggable(Level.FINE)) {
logger.fine(getName() + ": Dateline is on screen");
}
double ymin = Math.min(ulLat, lrLat);
double ymax = Math.max(ulLat, lrLat);
checkSpatialIndexEntries(ulLon, ymin, 180.0d, ymax, list, proj);
checkSpatialIndexEntries(-180.0d, ymin, lrLon, ymax, list, proj);
} else {
double xmin = Math.min(ulLon, lrLon);
double xmax = Math.max(ulLon, lrLon);
double ymin = Math.min(ulLat, lrLat);
double ymax = Math.max(ulLat, lrLat);
checkSpatialIndexEntries(xmin, ymin, xmax, ymax, list, proj);
}
} catch (FormatException fe) {
if (logger.isLoggable(Level.FINE)) {
logger.fine(fe.getMessage());
}
} catch (IOException ioe) {
if (logger.isLoggable(Level.FINE)) {
logger.fine(ioe.getMessage());
}
}
return list;
}
protected void checkSpatialIndexEntries(double xmin, double ymin, double xmax, double ymax,
OMGraphicList retList, Projection proj)
throws IOException, FormatException {
// There should be the same number of objects in both iterators.
Iterator<?> entryIt = spatialIndex.entryIterator();
Iterator<?> omgIt = bufferedList.iterator();
OMGraphicList labels = null;
if (spatialIndex.getDbf() != null) {
labels = new OMGraphicList();
retList.add(labels);
}
while (entryIt.hasNext() && omgIt.hasNext()) {
Entry entry = (Entry) entryIt.next();
OMGraphic omg = (OMGraphic) omgIt.next();
if (entry.intersects(xmin, ymin, xmax, ymax)) {
// We want to set attributes before the evaluate method is
// called, since there might be special attributes set on the
// omg based on dbf contents.
drawingAttributes.setTo(omg);
omg = spatialIndex.evaluate(omg, labels, proj);
// omg can be null from the evaluate method, if the omg doesn't
// pass proj and rule tests.
if (omg != null) {
omg.generate(proj);
retList.add(omg);
}
}
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd == RedrawCmd) {
setList(null);
}
super.actionPerformed(e);
}
/**
* This method gets called from setProperties.
*
* @param realPrefix This prefix has already been scoped, which means it is
* an empty string if setProperties was called with a null prefix, or
* it's a String ending with a period if it was defined with
* characters.
* @param props Properties containing information about files and the layer.
*/
protected void setFileProperties(String realPrefix, Properties props) {
bufferedList = null;
super.setFileProperties(realPrefix, props);
}
public void setSpatialIndex(SpatialIndex si) {
bufferedList = null;
super.setSpatialIndex(si);
}
}