// **********************************************************************
//
// <copyright>
//
// BBN Technologies, a Verizon Company
// 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/ESRIPolygonRecord.java,v $
// $RCSfile: ESRIPolygonRecord.java,v $
// $Revision: 1.8 $
// $Date: 2009/01/21 01:24:42 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.layer.shape;
import java.io.IOException;
import com.bbn.openmap.dataAccess.shape.ShapeConstants;
import com.bbn.openmap.omGraphics.DrawingAttributes;
import com.bbn.openmap.omGraphics.OMAreaList;
import com.bbn.openmap.omGraphics.OMGeometry;
import com.bbn.openmap.omGraphics.OMGeometryList;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMList;
import com.bbn.openmap.omGraphics.OMPoly;
import com.bbn.openmap.omGraphics.geom.PolygonGeometry;
import com.bbn.openmap.omGraphics.geom.PolylineGeometry;
import com.bbn.openmap.proj.ProjMath;
/**
* The Polygon record type. This class implements the ESRI Shapefile polygon AND
* arc/polyline record types.
*
* @author Ray Tomlinson
* @author Tom Mitchell
* @author HACK-author blame it on aculline
* @version $Revision: 1.8 $ $Date: 2009/01/21 01:24:42 $
*/
public class ESRIPolygonRecord extends ESRIRecord {
/** Polygon or arc/polyline?. */
protected int shapeType = SHAPE_TYPE_POLYGON;
/** The bounding box. */
public ESRIBoundingBox bounds;
/** An array of polygons. */
public ESRIPoly[] polygons;
public ESRIPolygonRecord() {
bounds = new ESRIBoundingBox();
polygons = new ESRIPoly[0];
}
/**
* Initialize a polygon record from the given buffer.
*
* @param b
* the buffer
* @param off
* the offset into the buffer where the data starts
*/
public ESRIPolygonRecord(byte b[], int off) throws IOException {
super(b, off);
int ptr = off + 8;
shapeType = readLEInt(b, ptr);
ptr += 4;
if ((shapeType != SHAPE_TYPE_POLYGON) && (shapeType != SHAPE_TYPE_ARC)) {
throw new IOException("Invalid polygon record. Expected shape "
+ "type " + SHAPE_TYPE_POLYGON + " or type "
+ SHAPE_TYPE_ARC + ", but found " + shapeType);
}
boolean ispolyg = isPolygon();
bounds = readBox(b, ptr);
ptr += 32; // A box is 4 doubles (4 x 8bytes)
int numParts = readLEInt(b, ptr);
ptr += 4;
int numPoints = readLEInt(b, ptr);
ptr += 4;
if (numParts <= 0) {
polygons = new ESRIPoly[0];
return;
}
polygons = new ESRIPoly[numParts];
int origin = 0;
int _len;
for (int i = 0; i < numParts; i++) {
int nextOrigin = readLEInt(b, ptr);
ptr += 4;
if (i > 0) {
_len = nextOrigin - origin;
if (ispolyg)
++_len;// connect pairs
polygons[i - 1] = new ESRIPoly.ESRIFloatPoly(_len);
}
origin = nextOrigin;
}
_len = numPoints - origin;
if (ispolyg)
++_len;// connect pairs
polygons[numParts - 1] = new ESRIPoly.ESRIFloatPoly(_len);
for (int i = 0; i < numParts; i++) {
ptr += polygons[i].read(b, ptr, ispolyg);
}
}
/**
* Is this a polygon or a arc/polyline?
*
* @return boolean
*/
public boolean isPolygon() {
return shapeType == SHAPE_TYPE_POLYGON;
}
/**
* Set the poly type (polygon or arc/polyline).
*/
public void setPolygon(boolean isPolygon) {
shapeType = isPolygon ? SHAPE_TYPE_POLYGON : SHAPE_TYPE_ARC;
}
/**
* Add a poly to the record.
*
* @param radians
* coordinates: y,x,y,x,... (lat,lon) order in RADIANS!
*/
public void add(double radians[]) {
ESRIPoly newPoly = new ESRIPoly.ESRIFloatPoly(radians);
int numParts = polygons.length;
ESRIPoly oldPolys[] = polygons;
polygons = new ESRIPoly[numParts + 1];
System.arraycopy(oldPolys, 0, polygons, 0, numParts);
polygons[numParts] = newPoly;
int len = radians.length;
for (int i = 0; i < len; i += 2) {
// REMEMBER: switch to x,y order
bounds.addPoint(ProjMath.radToDeg(radians[i + 1]),// x
// (lon)
ProjMath.radToDeg(radians[i]));// y (lat)
}
}
/**
* Generates 2D OMGraphics and adds them to the given list. If you are using
* jdk1.1.X, you'll have to comment out this method, because jdk1.1.X
* doesn't know about the java.awt.Stroke and java.awt.Paint interfaces.
*
* @param list
* the graphics list
* @param drawingAttributes
* the drawingAttributes to paint the poly.
*/
public void addOMGraphics(OMGraphicList list,
DrawingAttributes drawingAttributes) {
int nPolys = polygons.length;
if (nPolys <= 0)
return;
OMPoly p = null;
double[] pts;
boolean ispolyg = isPolygon();
/*
* modifications in the next 4 lines marked with as: allow to treat
* ESRIPolygonRecord with holes correctly (ESRIPolys with
* counterclockwise order of vertices)
*/
OMList sublist = null;
if (nPolys > 1) {
// Only want the OMAreaList if the shape type is for
// polygons
if (false && ispolyg) {
// There's a problem here with OMPolys that wrap
// around the earth, putting them in OMAreaLists makes
// them draw lines through the map. Need to sort that
// out before enabling this code by removing 'false'
// in if statement.
sublist = new OMAreaList();
((OMAreaList) sublist).setConnectParts(false);
} else {
sublist = new OMGraphicList();
}
if (drawingAttributes != null) {
drawingAttributes.setTo(sublist);
}
sublist.setVague(true); // Treat list as one object.
list.add(sublist);
sublist.putAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE, new Integer(getRecordNumber()));
}
for (int i = 0; i < nPolys; i++) {
// these points are already in RADIAN lat,lon order!...
pts = ((ESRIPoly.ESRIFloatPoly) polygons[i]).getRadians();
p = new OMPoly(pts, OMGraphic.RADIANS, OMGraphic.LINETYPE_STRAIGHT);
if (drawingAttributes != null) {
drawingAttributes.setTo(p);
}
if (!ispolyg) {
p.setIsPolygon(false);
}
if (sublist != null) {
sublist.add(p);
} else {
// There should be only one.
p.putAttribute(ShapeConstants.SHAPE_INDEX_ATTRIBUTE, new Integer(getRecordNumber()));
list.add(p);
}
}
}
/**
* Generates OMGeometry and adds them to the given list.
*
* @param list
* the geometry list
*/
public OMGeometry addOMGeometry(OMGeometryList list) {
int nPolys = polygons.length;
if (nPolys <= 0) {
return null;
}
double[] pts;
boolean ispolyg = isPolygon();
OMGeometry geom = null;
for (int i = 0; i < nPolys; i++) {
// these points are already in RADIAN lat,lon order!...
pts = ((ESRIPoly.ESRIFloatPoly) polygons[i]).getRadians();
if (ispolyg) {
geom = new PolygonGeometry.LL(pts, OMGraphic.RADIANS,
OMGraphic.LINETYPE_STRAIGHT);
} else {
geom = new PolylineGeometry.LL(pts, OMGraphic.RADIANS,
OMGraphic.LINETYPE_STRAIGHT);
}
list.add(geom);
}
return geom;
}
/**
* Gets this record's bounding box.
*
* @return a bounding box
*/
public ESRIBoundingBox getBoundingBox() {
return bounds;
}
/**
* Gets this record's shape type as an int. Shape types are enumerated on
* the ShapeUtils class.
*
* @return the shape type as an int (either SHAPE_TYPE_POLYGON or
* SHAPE_TYPE_ARC)
*/
public int getShapeType() {
return shapeType;
}
/**
* Yields the length of this record's data portion.
* <p>
* (44 + (numParts * 4) + (numPoints * 16)) <br>
* 3 Integers + 4 doubles == 3 * 4bytes + 4 * 8bytes == 12 + 32 == 44.
*
* @return number of bytes equal to the size of this record's data
*/
public int getRecordLength() {
int numParts = polygons.length;
int numPoints = 0;
for (int i = 0; i < numParts; i++) {
numPoints += polygons[i].nPoints;
}
return (44 + (numParts * 4) + (numPoints * 16));
}
/**
* Writes this polygon to the given buffer at the given offset.
*
* @param b
* the buffer
* @param off
* the offset
* @return the number of bytes written
*/
public int write(byte[] b, int off) {
int nBytes = super.write(b, off);
nBytes += writeLEInt(b, off + nBytes, shapeType);
// bounds
nBytes += writeBox(b, off + nBytes, bounds);
// numparts
int numParts = polygons.length;
nBytes += writeLEInt(b, off + nBytes, numParts);
// numpoints
int numPoints = 0;
for (int i = 0; i < numParts; i++) {
numPoints += polygons[i].nPoints;
}
nBytes += writeLEInt(b, off + nBytes, numPoints);
// parts
int ptr = 0;
for (int i = 0; i < numParts; i++) {
nBytes += writeLEInt(b, off + nBytes, ptr);
ptr += polygons[i].nPoints;
}
// points
for (int i = 0; i < numParts; i++) {
// REMEMBER: stored internally as y,x order (lat,lon
// order)
double[] pts = ((ESRIPoly.ESRIFloatPoly) polygons[i]).getRadians();
int nPts = pts.length;
for (int j = 0; j < nPts; j += 2) {
nBytes += writeLEDouble(b, off + nBytes, (double) ProjMath
.radToDeg(pts[j + 1]));// x
// (lon)
nBytes += writeLEDouble(b, off + nBytes, (double) ProjMath
.radToDeg(pts[j]));// y (lat)
}
}
// return number of bytes written
return nBytes;
}
}