/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.renderer.shape.shapehandler.simple;
import java.awt.geom.Point2D;
import java.nio.ByteBuffer;
import org.geotools.data.shapefile.shp.ShapeHandler;
import org.geotools.data.shapefile.shp.ShapeType;
import org.geotools.renderer.shape.GeometryHandlerUtilities;
import org.geotools.renderer.shape.ShapefileRenderer;
import org.geotools.renderer.shape.SimpleGeometry;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import com.vividsolutions.jts.geom.Envelope;
/**
* A ShapeHandler that reads PointHandler objects from a file. It returns a SimpleGeometry and decimates all points that
* map to the same screen location.
*
* @author jeichar
* @since 2.1.x
* @source $URL$
*/
public class PolygonHandler implements ShapeHandler {
private ShapeType type;
private Envelope bbox;
double spanx, spany;
private MathTransform mt;
/**
* Create new instance
*
* @param type
* the type of shape.
* @param env
* the area that is visible. If shape is not in area then skip.
* @param mt
* the transform to go from data to the envelope (and that should
* be used to transform the shape coords)
* @param hasOpacity
*/
public PolygonHandler(ShapeType type, Envelope env, MathTransform mt, boolean hasOpacity)
throws TransformException {
if (mt == null) {
throw new NullPointerException();
}
this.type = type;
this.bbox = env;
this.mt = mt;
Point2D span = GeometryHandlerUtilities.calculateSpan(mt,0,0);
this.spanx = span.getX() ;
this.spany = span.getY() ;
}
/**
* @see org.geotools.data.shapefile.shp.ShapeHandler#getShapeType()
*/
public ShapeType getShapeType() {
return type;
}
public Object read(ByteBuffer buffer, ShapeType type) {
if (type == ShapeType.NULL) {
return null;
}
Envelope geomBBox = GeometryHandlerUtilities.readBounds(buffer);
if (!bbox.intersects(geomBBox)) {
return null;
}
boolean bboxdecimate = geomBBox.getWidth() <= spanx
&& geomBBox.getHeight() <= spany;
int numParts = buffer.getInt();
int numPoints = buffer.getInt(); // total number of points
int[] partOffsets = new int[numParts];
// points = new Coordinate[numPoints];
for (int i = 0; i < numParts; i++) {
partOffsets[i] = buffer.getInt();
}
double[][] coords = new double[numParts][];
double[][] transformed = new double[numParts][];
// if needed in future otherwise all references to a z are commented
// out.
// if( dimensions==3 )
// z=new double[numParts][];
int finish, start = 0;
int length = 0;
if (bboxdecimate) {
coords = new double[1][];
coords[0] = new double[4];
transformed = new double[1][];
transformed[0] = new double[8];
coords[0][0] = buffer.getDouble();
coords[0][1] = buffer.getDouble();
try {
mt.transform(coords[0], 0, transformed[0], 0, 1);
} catch (Exception e) {
ShapefileRenderer.LOGGER.severe("could not transform coordinates "
+ e.getLocalizedMessage());
transformed[0] = coords[0];
}
transformed[0][2] = transformed[0][0];
transformed[0][3] = transformed[0][1] + 0.1;
transformed[0][4] = transformed[0][0] + 0.1;
transformed[0][5] = transformed[0][1] + 0.1;
transformed[0][6] = transformed[0][0];
transformed[0][7] = transformed[0][1];
} else {
Envelope partEnvelope = new Envelope();
int partsInBBox = 0;
for (int part = 0; part < numParts; part++) {
start = partOffsets[part];
partEnvelope.init();
if (part == (numParts - 1)) {
finish = numPoints;
} else {
finish = partOffsets[part + 1];
}
length = finish - start;
int totalDoubles = length * 2;
coords[part] = new double[totalDoubles];
int readDoubles = 0;
int currentDoubles = 0;
for (; currentDoubles < totalDoubles;) {
try {
coords[part][readDoubles] = buffer.getDouble();
readDoubles++;
currentDoubles++;
coords[part][readDoubles] = buffer.getDouble();
readDoubles++;
currentDoubles++;
} catch (Exception e) {
e.printStackTrace();
}
if (collapsePoints(coords, part, totalDoubles, readDoubles,
currentDoubles)) {
readDoubles -= 2;
} else {
partEnvelope.expandToInclude(
coords[part][readDoubles - 2],
coords[part][readDoubles - 1]);
}
}
if (!partEnvelope.intersects(bbox)) {
continue;
}
if( readDoubles<8 )
transformed[partsInBBox] = new double[8];
else
transformed[partsInBBox] = new double[readDoubles];
if (!mt.isIdentity()) {
try {
GeometryHandlerUtilities.transform(type, mt, coords[part], transformed[partsInBBox], readDoubles / 2);
// mt.transform(coords[part], 0, transformed[partsInBBox],
// 0, readDoubles / 2);
} catch (Exception e) {
ShapefileRenderer.LOGGER
.severe("could not transform coordinates "
+ e.getLocalizedMessage());
transformed[partsInBBox] = coords[part];
}
} else {
System.arraycopy(coords[part], 0, transformed[partsInBBox],
0, readDoubles / 2);
}
if( readDoubles<8 ){
for(int i=readDoubles; i<transformed[partsInBBox].length;i++){
transformed[partsInBBox][i]=transformed[partsInBBox][i-2];
}
}
partsInBBox++;
}
if (partsInBBox == 0)
return null;
if (partsInBBox != numParts) {
double[][] tmp = new double[partsInBBox][];
System.arraycopy(transformed, 0, tmp, 0, partsInBBox);
transformed = tmp;
}
}
return createGeometry(type, geomBBox, transformed);
}
protected Object createGeometry(ShapeType type, Envelope geomBBox, double[][] transformed) {
return new SimpleGeometry(type, transformed, geomBBox);
}
/**
* Return true if the current point and the last point should be collapsed
* into a single point. The first and last point must be the same so if the
* current doubles is the 1-2 second double or if one of the last two the it
* will return false. Otherwise it will return true if the distance in the y
* axis is less than the distance of one pixel... similar comparison is done
* along the x-axis.
*
* @return true if the current point and the last point should be collapsed
* into a single point.
*/
private boolean collapsePoints(double[][] coords, int part,
int totalDoubles, int readDoubles, int currentDoubles) {
return currentDoubles > 3
&& currentDoubles < totalDoubles - 1
&& Math.abs(coords[part][readDoubles - 4]
- coords[part][readDoubles - 2]) <= spanx
&& Math.abs(coords[part][readDoubles - 3]
- coords[part][readDoubles - 1]) <= spany;
}
/**
* @see org.geotools.data.shapefile.shp.ShapeHandler#write(java.nio.ByteBuffer,
* java.lang.Object)
*/
public void write(ByteBuffer buffer, Object geometry) {
// This handler doesnt write
throw new UnsupportedOperationException(
"This handler is only for reading");
}
/**
* @see org.geotools.data.shapefile.shp.ShapeHandler#getLength(java.lang.Object)
*/
public int getLength(Object geometry) {
// TODO Auto-generated method stub
return 0;
}
}