/*
* Copyright (C) 2012 Dr. John Lindsay <jlindsay@uoguelph.ca>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package whitebox.geospatialfiles.shapefile;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import whitebox.structures.BoundingBox;
/**
*
* @author Dr. John Lindsay email: jlindsay@uoguelph.ca
*/
public class PolyLine implements Geometry {
private int numParts;
private int numPoints;
private int[] parts;
private double[][] points;
private BoundingBox bb;
private double maxExtent;
//private ByteBuffer buf;
//constructors
/**
* This constructor is used when the PolyLineM is being created from data
* that is read directly from a file.
* @param rawData A byte array containing all of the raw data needed to create
* the PolyLineM, starting with the bounding box, i.e. leaving out the
* ShapeType data.
*/
public PolyLine(byte[] rawData) {
try {
ByteBuffer buf = ByteBuffer.wrap(rawData);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.rewind();
bb = new BoundingBox(buf.getDouble(0), buf.getDouble(8),
buf.getDouble(16), buf.getDouble(24));
maxExtent = bb.getMaxExtent();
numParts = buf.getInt(32);
numPoints = buf.getInt(36);
parts = new int[numParts];
for (int i = 0; i < numParts; i++) {
parts[i] = buf.getInt(40 + i * 4);
}
int pos = 40 + numParts * 4;
points = new double[numPoints][2];
for (int i = 0; i < numPoints; i++) {
points[i][0] = buf.getDouble(pos + i * 16); // x value
points[i][1] = buf.getDouble(pos + i * 16 + 8); // y value
}
buf.clear();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
/**
* This is the constructor that is used when creating a new polyline.
* @param parts an int array that indicates the zero-base starting byte for
* each part.
* @param points a double[][] array containing the point data. The first
* dimension of the array is the total number of points in the polyline.
*/
public PolyLine (int[] parts, double[][] points) {
numParts = parts.length;
numPoints = points.length;
this.parts = (int[])parts.clone();
this.points = new double[numPoints][2];
for (int i = 0; i < numPoints; i++) {
this.points[i][0] = points[i][0];
this.points[i][1] = points[i][1];
}
double minX = Float.POSITIVE_INFINITY;
double minY = Float.POSITIVE_INFINITY;
double maxX = Float.NEGATIVE_INFINITY;
double maxY = Float.NEGATIVE_INFINITY;
for (int i = 0; i < numPoints; i++) {
if (points[i][0] < minX) { minX = points[i][0]; }
if (points[i][0] > maxX) { maxX = points[i][0]; }
if (points[i][1] < minY) { minY = points[i][1]; }
if (points[i][1] > maxY) { maxY = points[i][1]; }
}
bb = new BoundingBox(minX, minY, maxX, maxY);
maxExtent = bb.getMaxExtent();
}
// properties
@Override
public BoundingBox getBox() {
return bb;
}
public double getXMin() {
return bb.getMinX();
}
public double getYMin() {
return bb.getMinY();
}
public double getXMax() {
return bb.getMaxX();
}
public double getYMax() {
return bb.getMaxY();
}
public int getNumPoints() {
return numPoints;
}
@Override
public double[][] getPoints() {
return points;
}
public int getNumParts() {
return numParts;
}
@Override
public int[] getParts() {
return parts;
}
@Override
public int getLength() {
return 32 + 8 + numParts * 4 + numPoints * 16;
}
@Override
public ByteBuffer toByteBuffer() {
ByteBuffer buf = ByteBuffer.allocate(getLength());
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.rewind();
// put the bounding box data in.
buf.putDouble(bb.getMinX());
buf.putDouble(bb.getMinY());
buf.putDouble(bb.getMaxX());
buf.putDouble(bb.getMaxY());
// put the numParts and numPoints in.
buf.putInt(numParts);
buf.putInt(numPoints);
// put the part data in.
for (int i = 0; i < numParts; i++) {
buf.putInt(parts[i]);
}
// put the point data in.
for (int i = 0; i < numPoints; i++) {
buf.putDouble(points[i][0]);
buf.putDouble(points[i][1]);
}
return buf;
}
@Override
public ShapeType getShapeType() {
return ShapeType.POLYLINE;
}
@Override
public boolean isMappable(BoundingBox box, double minSize) {
return box.overlaps(bb) && maxExtent > minSize;
}
@Override
public boolean needsClipping(BoundingBox box) {
return (!bb.entirelyContainedWithin(box)) && (bb.overlaps(box));
}
@Override
public com.vividsolutions.jts.geom.Geometry[] getJTSGeometries() {
GeometryFactory factory = new GeometryFactory();
int part;
int j, i;
int startingPointInPart, endingPointInPart;
int numPointsInPart;
CoordinateArraySequence coordArray;
com.vividsolutions.jts.geom.LineString[] polyArray = new com.vividsolutions.jts.geom.LineString[numParts];
for (part = 0; part < numParts; part++) {
startingPointInPart = parts[part];
if (part < numParts - 1) {
endingPointInPart = parts[part + 1];
} else {
endingPointInPart = numPoints;
}
numPointsInPart = endingPointInPart - startingPointInPart;
coordArray = new CoordinateArraySequence(numPointsInPart);
j = 0;
for (i = startingPointInPart; i < endingPointInPart; i++) {
coordArray.setOrdinate(j, 0, points[i][0]);
coordArray.setOrdinate(j, 1, points[i][1]);
j++;
}
polyArray[part] = factory.createLineString(coordArray);
}
return polyArray;
}
}