package com.revolsys.record.io.format.shp;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import com.revolsys.datatype.DataType;
import com.revolsys.datatype.DataTypes;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Lineal;
import com.revolsys.geometry.model.LinearRing;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.Polygon;
import com.revolsys.geometry.model.Punctual;
import com.revolsys.geometry.model.impl.LineStringDouble;
import com.revolsys.geometry.model.vertex.Vertex;
import com.revolsys.io.endian.EndianOutput;
import com.revolsys.util.function.Function3;
public final class ShapefileGeometryHandler {
public static final ShapefileGeometryHandler SHP_INSTANCE = new ShapefileGeometryHandler(true);
public final Map<String, Function3<GeometryFactory, ByteBuffer, Integer, Geometry>> readFunctionByGeometryType = new LinkedHashMap<>();
public final Map<String, BiConsumer<EndianOutput, Geometry>> writeFunctionByGeometryType = new LinkedHashMap<>();
private final boolean shpFile;
private final boolean writeLength;
public ShapefileGeometryHandler(final boolean shpFile) {
this.shpFile = shpFile;
this.writeLength = shpFile;
{
final String typeName = "POINT";
this.readFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::readPoint);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::readPointZ);
this.readFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::readPointM);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::readPointZM);
}
{
final String typeName = "MULTIPOINT";
this.readFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::readMultipoint);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::readMultipointZ);
this.readFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::readMultipointM);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::readMultipointZM);
}
for (final String typeName : Arrays.asList("LINESTRING", "MULTILINESTRING")) {
this.readFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::readPolyline);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::readPolylineZ);
this.readFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::readPolylineM);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::readPolylineZM);
}
for (final String typeName : Arrays.asList("POLYGON", "MULTIPOLYGON")) {
this.readFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::readPolygon);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::readPolygonZ);
this.readFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::readPolygonM);
this.readFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::readPolygonZM);
}
{
final String typeName = "POINT";
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::writePoint);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::writePointZ);
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::writePointM);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::writePointZM);
}
{
final String typeName = "MULTIPOINT";
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::writeMultipoint);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::writeMultipointZ);
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::writeMultipointM);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::writeMultipointZM);
}
for (final String typeName : Arrays.asList("LINESTRING", "MULTILINESTRING")) {
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::writePolyline);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::writePolylineZ);
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::writePolylineM);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::writePolylineZM);
}
for (final String typeName : Arrays.asList("POLYGON", "MULTIPOLYGON")) {
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "FALSE", this::writePolygon);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "FALSE", this::writePolygonZ);
this.writeFunctionByGeometryType.put(typeName + "FALSE" + "TRUE", this::writePolygonM);
this.writeFunctionByGeometryType.put(typeName + "TRUE" + "TRUE", this::writePolygonZM);
}
}
public Function3<GeometryFactory, ByteBuffer, Integer, Geometry> getReadFunction(
String geometryTypeKey) {
geometryTypeKey = geometryTypeKey.toUpperCase();
final Function3<GeometryFactory, ByteBuffer, Integer, Geometry> function = this.readFunctionByGeometryType
.get(geometryTypeKey);
if (function == null) {
throw new IllegalArgumentException("Cannot get Shape Reader for: " + geometryTypeKey);
}
return function;
}
public int getShapeType(final Geometry geometry) {
if (geometry != null) {
final GeometryFactory geometryFactory = geometry.getGeometryFactory();
final DataType dataType = geometry.getDataType();
return getShapeType(geometryFactory, dataType);
}
return ShapefileConstants.NULL_SHAPE;
}
public int getShapeType(final GeometryFactory geometryFactory, final DataType dataType) {
final int axisCount = geometryFactory.getAxisCount();
final boolean hasZ = axisCount > 2;
final boolean hasM = axisCount > 3;
if (DataTypes.POINT.equals(dataType)) {
if (hasM) {
return ShapefileConstants.POINT_ZM_SHAPE;
} else if (hasZ) {
if (this.shpFile) {
return ShapefileConstants.POINT_ZM_SHAPE;
} else {
return ShapefileConstants.POINT_Z_SHAPE;
}
} else {
return ShapefileConstants.POINT_SHAPE;
}
} else if (DataTypes.MULTI_POINT.equals(dataType)) {
if (hasM) {
return ShapefileConstants.MULTI_POINT_ZM_SHAPE;
} else if (hasZ) {
if (this.shpFile) {
return ShapefileConstants.MULTI_POINT_ZM_SHAPE;
} else {
return ShapefileConstants.MULTI_POINT_Z_SHAPE;
}
} else {
return ShapefileConstants.MULTI_POINT_SHAPE;
}
} else if (DataTypes.LINEAR_RING.equals(dataType) || DataTypes.LINE_STRING.equals(dataType)
|| DataTypes.MULTI_LINE_STRING.equals(dataType)) {
if (hasM) {
return ShapefileConstants.POLYLINE_ZM_SHAPE;
} else if (hasZ) {
if (this.shpFile) {
return ShapefileConstants.POLYLINE_ZM_SHAPE;
} else {
return ShapefileConstants.POLYLINE_Z_SHAPE;
}
} else {
return ShapefileConstants.POLYLINE_SHAPE;
}
} else if (DataTypes.POLYGON.equals(dataType) || DataTypes.MULTI_POLYGON.equals(dataType)) {
if (hasM) {
return ShapefileConstants.POLYGON_ZM_SHAPE;
} else if (hasZ) {
if (this.shpFile) {
return ShapefileConstants.POLYGON_ZM_SHAPE;
} else {
return ShapefileConstants.POLYGON_Z_SHAPE;
}
} else {
return ShapefileConstants.POLYGON_SHAPE;
}
} else {
throw new IllegalArgumentException("Unsupported geometry type: " + dataType);
}
}
public BiConsumer<EndianOutput, Geometry> getWriteFunction(String geometryTypeKey) {
geometryTypeKey = geometryTypeKey.toUpperCase();
final BiConsumer<EndianOutput, Geometry> function = this.writeFunctionByGeometryType
.get(geometryTypeKey);
if (function == null) {
throw new IllegalArgumentException("Cannot get Shape Reader for: " + geometryTypeKey);
}
return function;
}
public boolean isShpFile() {
return this.shpFile;
}
public List<double[]> newCoordinatesLists(final int[] partIndex, final int axisCount) {
final List<double[]> parts = new ArrayList<>(partIndex.length);
for (final int partNumPoints : partIndex) {
final double[] coordinates = new double[partNumPoints * axisCount];
parts.add(coordinates);
}
return parts;
}
public Geometry newPolygonGeometryFromParts(final GeometryFactory geometryFactory,
final List<double[]> parts, final int axisCount) {
final List<Polygon> polygons = new ArrayList<>();
final List<LinearRing> currentParts = new ArrayList<>();
for (final double[] coordinates : parts) {
final LinearRing ring = geometryFactory.linearRing(axisCount, coordinates);
final boolean ringClockwise = ring.isClockwise();
if (ringClockwise) {
if (!currentParts.isEmpty()) {
final Polygon polygon = geometryFactory.polygon(currentParts);
polygons.add(polygon);
currentParts.clear();
}
}
currentParts.add(ring);
}
if (!currentParts.isEmpty()) {
final Polygon polygon = geometryFactory.polygon(currentParts);
polygons.add(polygon);
}
if (polygons.size() == 1) {
return polygons.get(0);
} else {
return geometryFactory.polygonal(polygons);
}
}
public void readCoordinates(final ByteBuffer buffer, final int vertexCount, final int axisCount,
final double[] coordinates, final int axisIndex) {
for (int j = 0; j < vertexCount; j++) {
double value = buffer.getDouble();
;
if (value == -Double.MAX_VALUE) {
value = Double.NaN;
}
coordinates[j * axisCount + axisIndex] = value;
}
}
public void readCoordinates(final ByteBuffer buffer, final int[] partIndex,
final List<double[]> coordinateLists, final int axisIndex, final int axisCount) {
buffer.getDouble();
buffer.getDouble();
;
for (int i = 0; i < partIndex.length; i++) {
final double[] coordinates = coordinateLists.get(i);
final int vertexCount = coordinates.length / axisCount;
readCoordinates(buffer, vertexCount, axisCount, coordinates, axisIndex);
}
}
public int[] readIntArray(final ByteBuffer buffer, final int count) {
final int[] values = new int[count];
for (int i = 0; i < count; i++) {
final int value = buffer.getInt();
values[i] = value;
}
return values;
}
public Punctual readMultipoint(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int vertexCount = buffer.getInt();
final double[] coordinates = readXYCoordinates(buffer, vertexCount, 2);
return geometryFactory.punctual(new LineStringDouble(2, coordinates));
}
public Punctual readMultipointM(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int vertexCount = buffer.getInt();
final int axisCount = 4;
final double[] coordinates = readXYCoordinates(buffer, vertexCount, axisCount);
buffer.getDouble();
buffer.getDouble();
setCoordinatesNaN(coordinates, vertexCount, axisCount, 2);
readCoordinates(buffer, vertexCount, axisCount, coordinates, 3);
return geometryFactory.punctual(new LineStringDouble(axisCount, coordinates));
}
public Punctual readMultipointZ(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int vertexCount = buffer.getInt();
final double[] coordinates = readXYCoordinates(buffer, vertexCount, 3);
buffer.getDouble();
buffer.getDouble();
readCoordinates(buffer, vertexCount, 3, coordinates, 2);
return geometryFactory.punctual(new LineStringDouble(3, coordinates));
}
public Punctual readMultipointZM(GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int vertexCount = buffer.getInt();
int axisCount;
if (recordLength == 20 + 12 * vertexCount) {
geometryFactory = geometryFactory.convertAxisCount(3);
axisCount = 3;
} else {
axisCount = 4;
}
final double[] coordinates = readXYCoordinates(buffer, vertexCount, axisCount);
buffer.getDouble();
buffer.getDouble();
;
readCoordinates(buffer, vertexCount, axisCount, coordinates, 2);
if (axisCount == 4) {
buffer.getDouble();
buffer.getDouble();
;
readCoordinates(buffer, vertexCount, axisCount, coordinates, 3);
}
return geometryFactory.punctual(axisCount, coordinates);
}
public int[] readPartIndex(final ByteBuffer buffer, final int numParts, final int vertexCount) {
final int[] partIndex = new int[numParts];
if (numParts > 0) {
int startIndex = buffer.getInt();
for (int i = 1; i < partIndex.length; i++) {
final int index = buffer.getInt();
partIndex[i - 1] = index - startIndex;
startIndex = index;
}
partIndex[partIndex.length - 1] = vertexCount - startIndex;
}
return partIndex;
}
public Point readPoint(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
final double[] coordinates = readXYCoordinates(buffer, 1, 2);
return geometryFactory.point(coordinates);
}
public Point readPointM(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
final double x = buffer.getDouble();
final double y = buffer.getDouble();
final double z = 0;
final double m = buffer.getDouble();
return geometryFactory.point(x, y, z, m);
}
public void readPoints(final ByteBuffer buffer, final int[] partIndex,
final List<double[]> coordinateLists, final int axisCount) {
for (int i = 0; i < partIndex.length; i++) {
final int count = partIndex[i];
final double[] coordinates = coordinateLists.get(i);
readXYCoordinates(buffer, axisCount, count, coordinates);
}
}
public Point readPointZ(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
final double x = buffer.getDouble();
final double y = buffer.getDouble();
final double z = buffer.getDouble();
return geometryFactory.point(x, y, z);
}
public Point readPointZM(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
final double x = buffer.getDouble();
final double y = buffer.getDouble();
final double z = buffer.getDouble();
if (recordLength == 14) {
return geometryFactory.convertAxisCount(3).point(x, y, z);
} else {
final double m = buffer.getDouble();
return geometryFactory.point(x, y, z, m);
}
}
public Geometry readPolygon(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int numParts = buffer.getInt();
final int vertexCount = buffer.getInt();
final int[] partIndex = readPartIndex(buffer, numParts, vertexCount);
final List<double[]> parts = newCoordinatesLists(partIndex, 2);
readPoints(buffer, partIndex, parts, 2);
return newPolygonGeometryFromParts(geometryFactory, parts, 2);
}
public Geometry readPolygonM(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int partCount = buffer.getInt();
final int vertexCount = buffer.getInt();
final int axisCount = 4;
final int[] partIndex = readPartIndex(buffer, partCount, vertexCount);
final List<double[]> parts = newCoordinatesLists(partIndex, axisCount);
readPoints(buffer, partIndex, parts, axisCount);
readCoordinates(buffer, partIndex, parts, 3, axisCount);
return newPolygonGeometryFromParts(geometryFactory, parts, axisCount);
}
public Geometry readPolygonZ(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int numParts = buffer.getInt();
final int vertexCount = buffer.getInt();
final int axisCount = 3;
final int[] partIndex = readPartIndex(buffer, numParts, vertexCount);
final List<double[]> parts = newCoordinatesLists(partIndex, axisCount);
readPoints(buffer, partIndex, parts, axisCount);
readCoordinates(buffer, partIndex, parts, 2, axisCount);
return newPolygonGeometryFromParts(geometryFactory, parts, axisCount);
}
public Geometry readPolygonZM(GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int numParts = buffer.getInt();
final int vertexCount = buffer.getInt();
final int[] partIndex = readPartIndex(buffer, numParts, vertexCount);
final int axisCount;
if (recordLength == 22 + 8 + 2 * numParts + 12 * vertexCount) {
axisCount = 3;
geometryFactory = geometryFactory.convertAxisCount(3);
} else {
axisCount = 4;
}
final List<double[]> parts = newCoordinatesLists(partIndex, axisCount);
readPoints(buffer, partIndex, parts, axisCount);
readCoordinates(buffer, partIndex, parts, 2, axisCount);
if (axisCount == 4) {
readCoordinates(buffer, partIndex, parts, 3, axisCount);
}
return newPolygonGeometryFromParts(geometryFactory, parts, axisCount);
}
public Geometry readPolyline(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int numParts = buffer.getInt();
final int vertexCount = buffer.getInt();
final int axisCount = 2;
if (numParts == 1) {
buffer.getInt();
final double[] coordinates = readXYCoordinates(buffer, vertexCount, axisCount);
return geometryFactory.lineString(2, coordinates);
} else {
final int[] partIndex = new int[numParts + 1];
partIndex[numParts] = vertexCount;
for (int i = 0; i < partIndex.length - 1; i++) {
partIndex[i] = buffer.getInt();
}
final List<LineString> lines = new ArrayList<>();
for (int i = 0; i < partIndex.length - 1; i++) {
final int startIndex = partIndex[i];
final int endIndex = partIndex[i + 1];
final int numCoords = endIndex - startIndex;
final double[] coordinates = readXYCoordinates(buffer, numCoords, axisCount);
lines.add(geometryFactory.lineString(2, coordinates));
}
return geometryFactory.lineal(lines);
}
}
public Geometry readPolylineM(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int partCount = buffer.getInt();
final int allVertexCount = buffer.getInt();
final int axisCount = 4;
if (partCount == 1) {
buffer.getInt();
final double[] coordinates = readXYCoordinates(buffer, allVertexCount, axisCount);
buffer.getDouble();
buffer.getDouble();
setCoordinatesNaN(coordinates, allVertexCount, axisCount, 2);
readCoordinates(buffer, allVertexCount, axisCount, coordinates, 3);
return geometryFactory.lineString(axisCount, coordinates);
} else {
final int[] partIndex = new int[partCount + 1];
partIndex[partCount] = allVertexCount;
for (int i = 0; i < partIndex.length - 1; i++) {
partIndex[i] = buffer.getInt();
}
final List<double[]> coordinatesList = new ArrayList<>();
for (int i = 0; i < partIndex.length - 1; i++) {
final int startIndex = partIndex[i];
final int endIndex = partIndex[i + 1];
final int vertexCount = endIndex - startIndex;
final double[] coordinates = readXYCoordinates(buffer, vertexCount, axisCount);
coordinatesList.add(coordinates);
}
buffer.getDouble();
buffer.getDouble();
for (int i = 0; i < partIndex.length - 1; i++) {
final double[] coordinates = coordinatesList.get(i);
final int vertexCount = coordinates.length / axisCount;
readCoordinates(buffer, vertexCount, axisCount, coordinates, 3);
}
return geometryFactory.lineal(axisCount,
coordinatesList.toArray(new double[coordinatesList.size()][]));
}
}
public Geometry readPolylineZ(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int numParts = buffer.getInt();
final int vertexCount = buffer.getInt();
return readPolylineZ(geometryFactory, buffer, numParts, vertexCount);
}
public Geometry readPolylineZ(final GeometryFactory geometryFactory, final ByteBuffer buffer,
final int partCount, final int allVertexCount) {
final int axisCount = 3;
if (partCount == 1) {
buffer.getInt();
final double[] coordinates = readXYCoordinates(buffer, allVertexCount, axisCount);
buffer.getDouble();
buffer.getDouble();
readCoordinates(buffer, allVertexCount, axisCount, coordinates, 2);
return geometryFactory.lineString(axisCount, coordinates);
} else {
final int[] partIndex = new int[partCount + 1];
partIndex[partCount] = allVertexCount;
for (int i = 0; i < partCount; i++) {
partIndex[i] = buffer.getInt();
}
final double[][] linesCoordinates = new double[partCount][];
for (int i = 0; i < partCount; i++) {
final int startIndex = partIndex[i];
final int endIndex = partIndex[i + 1];
final int vertexCount = endIndex - startIndex;
linesCoordinates[i] = readXYCoordinates(buffer, vertexCount, axisCount);
}
buffer.getDouble();
buffer.getDouble();
for (int i = 0; i < partCount; i++) {
final double[] coordinates = linesCoordinates[i];
final int vertexCount = coordinates.length / axisCount;
readCoordinates(buffer, vertexCount, axisCount, coordinates, 2);
}
return geometryFactory.lineal(axisCount, linesCoordinates);
}
}
public Geometry readPolylineZM(GeometryFactory geometryFactory, final ByteBuffer buffer,
final int recordLength) {
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
buffer.getDouble();
final int geometryCount = buffer.getInt();
final int vertexCount = buffer.getInt();
if (22 + geometryCount * 2 + vertexCount * 12 == recordLength) {
geometryFactory = geometryFactory.convertAxisCount(3);
return readPolylineZ(geometryFactory, buffer, geometryCount, vertexCount);
} else {
final int axisCount = 4;
if (geometryCount == 1) {
buffer.getInt();
final double[] coordinates = readXYCoordinates(buffer, vertexCount, axisCount);
buffer.getDouble();
buffer.getDouble();
readCoordinates(buffer, vertexCount, axisCount, coordinates, 2);
buffer.getDouble();
buffer.getDouble();
readCoordinates(buffer, vertexCount, axisCount, coordinates, 3);
return geometryFactory.lineString(axisCount, coordinates);
} else {
final int[] partIndex = new int[geometryCount + 1];
partIndex[geometryCount] = vertexCount;
for (int i = 0; i < partIndex.length - 1; i++) {
partIndex[i] = buffer.getInt();
}
final List<double[]> coordinatesList = new ArrayList<>();
for (int i = 0; i < partIndex.length - 1; i++) {
final int startIndex = partIndex[i];
final int endIndex = partIndex[i + 1];
final int numCoords = endIndex - startIndex;
final double[] coordinates = readXYCoordinates(buffer, numCoords, axisCount);
coordinatesList.add(coordinates);
}
buffer.getDouble();
buffer.getDouble();
for (int i = 0; i < partIndex.length - 1; i++) {
final double[] coordinates = coordinatesList.get(i);
readCoordinates(buffer, coordinates.length / 4, axisCount, coordinates, 2);
}
buffer.getDouble();
buffer.getDouble();
for (int i = 0; i < partIndex.length - 1; i++) {
final double[] coordinates = coordinatesList.get(i);
readCoordinates(buffer, coordinates.length / 4, axisCount, coordinates, 3);
}
final List<LineString> lines = new ArrayList<>();
for (final double[] coordinates : coordinatesList) {
lines.add(geometryFactory.lineString(axisCount, coordinates));
}
return geometryFactory.lineal(lines);
}
}
}
public double[] readXYCoordinates(final ByteBuffer buffer, final int vertexCount,
final int axisCount) {
final double[] coordinates = new double[vertexCount * axisCount];
readXYCoordinates(buffer, axisCount, vertexCount, coordinates);
return coordinates;
}
public void readXYCoordinates(final ByteBuffer buffer, final int axisCount, final int vertexCount,
final double[] coordinates) {
for (int j = 0; j < vertexCount; j++) {
final double x = buffer.getDouble();
final double y = buffer.getDouble();
coordinates[j * axisCount] = x;
coordinates[j * axisCount + 1] = y;
}
}
public void setCoordinatesNaN(final ByteBuffer buffer, final int[] partIndex,
final List<double[]> coordinateLists, final int axisIndex, final int axisCount) {
buffer.getDouble();
buffer.getDouble();
for (int i = 0; i < partIndex.length; i++) {
final double[] coordinates = coordinateLists.get(i);
final int vertexCount = coordinates.length / axisCount;
setCoordinatesNaN(coordinates, vertexCount, axisCount, axisIndex);
}
}
public void setCoordinatesNaN(final double[] coordinates, final int vertexCount,
final int axisCount, final int axisIndex) {
for (int j = 0; j < vertexCount; j++) {
coordinates[j * axisCount + axisIndex] = Double.NaN;
}
}
public void writeEnvelope(final EndianOutput out, final BoundingBox envelope) {
out.writeLEDouble(envelope.getMinX());
out.writeLEDouble(envelope.getMinY());
out.writeLEDouble(envelope.getMaxX());
out.writeLEDouble(envelope.getMaxY());
}
public void writeMCoordinates(final EndianOutput out, final Geometry geometry) {
writeMCoordinatesRange(out, geometry);
if (geometry.getAxisCount() >= 4) {
for (final Vertex vertex : geometry.vertices()) {
final double m = vertex.getM();
if (Double.isNaN(m)) {
out.writeLEDouble(0);
} else {
out.writeLEDouble(m);
}
}
} else {
for (int i = 0; i < geometry.getVertexCount(); i++) {
out.writeLEDouble(0);
}
}
}
public void writeMCoordinates(final EndianOutput out, final LineString coordinates) {
if (coordinates.getAxisCount() >= 4) {
for (int i = 0; i < coordinates.getVertexCount(); i++) {
final double m = coordinates.getM(i);
if (!Double.isNaN(m)) {
out.writeLEDouble(m);
} else {
out.writeLEDouble(0);
}
}
} else {
for (int i = 0; i < coordinates.getVertexCount(); i++) {
out.writeLEDouble(0);
}
}
}
public void writeMCoordinates(final EndianOutput out, final List<LineString> coordinatesList) {
writeMCoordinatesRange(out, coordinatesList);
for (final LineString coordinates : coordinatesList) {
writeMCoordinates(out, coordinates);
}
}
public void writeMCoordinatesRange(final EndianOutput out, final Geometry geometry) {
final BoundingBox boundingBox = geometry.getBoundingBox();
final double minM = boundingBox.getMin(3);
final double maxM = boundingBox.getMax(3);
if (Double.isNaN(minM) || Double.isNaN(maxM)) {
out.writeLEDouble(0);
out.writeLEDouble(0);
} else {
out.writeLEDouble(minM);
out.writeLEDouble(maxM);
}
}
public void writeMCoordinatesRange(final EndianOutput out,
final List<LineString> coordinatesList) {
double minM = Double.MAX_VALUE;
double maxM = -Double.MAX_VALUE;
for (final LineString ring : coordinatesList) {
for (int i = 0; i < ring.getVertexCount(); i++) {
double m = ring.getCoordinate(i, 2);
if (Double.isNaN(m)) {
m = 0;
}
minM = Math.min(m, minM);
maxM = Math.max(m, maxM);
}
}
if (minM == Double.MAX_VALUE && maxM == -Double.MAX_VALUE) {
out.writeLEDouble(0);
out.writeLEDouble(0);
} else {
out.writeLEDouble(minM);
out.writeLEDouble(maxM);
}
}
public void writeMultipoint(final EndianOutput out, final Geometry geometry) {
writeMultipoint(out, geometry, ShapefileConstants.MULTI_POINT_SHAPE, 8);
}
private void writeMultipoint(final EndianOutput out, final Geometry geometry, final int shapeType,
final int wordsPerPoint) {
if (geometry instanceof Punctual) {
final int vertexCount = geometry.getVertexCount();
if (this.writeLength) {
final int recordLength = 20 + wordsPerPoint * vertexCount;
// (BYTES_IN_INT + 4 * BYTES_IN_DOUBLE + BYTES_IN_INT +
// (vertexCount * 2 * BYTES_IN_DOUBLE)) / BYTES_IN_SHORT;
out.writeInt(recordLength);
}
out.writeLEInt(shapeType);
final BoundingBox envelope = geometry.getBoundingBox();
writeEnvelope(out, envelope);
out.writeLEInt(vertexCount);
writeXYCoordinates(out, geometry);
} else {
throw new IllegalArgumentException(
"Expecting Punctual geometry got " + geometry.getGeometryType());
}
}
public void writeMultipointM(final EndianOutput out, final Geometry geometry) {
writeMultipoint(out, geometry, ShapefileConstants.MULTI_POINT_M_SHAPE, 12);
writeMCoordinates(out, geometry);
}
public void writeMultipointZ(final EndianOutput out, final Geometry geometry) {
int shapeType;
if (this.shpFile) {
shapeType = ShapefileConstants.MULTI_POINT_ZM_SHAPE;
} else {
shapeType = ShapefileConstants.MULTI_POINT_Z_SHAPE;
}
writeMultipoint(out, geometry, shapeType, 12);
writeZCoordinates(out, geometry);
}
public void writeMultipointZM(final EndianOutput out, final Geometry geometry) {
writeMultipoint(out, geometry, ShapefileConstants.MULTI_POINT_ZM_SHAPE, 16);
writeZCoordinates(out, geometry);
writeMCoordinates(out, geometry);
}
public void writePoint(final EndianOutput out, final Geometry geometry) {
if (geometry instanceof Point) {
final Point point = (Point)geometry;
if (this.writeLength) {
final int recordLength = 10;
// (BYTES_IN_INT + 2 * BYTES_IN_DOUBLE) / BYTES_IN_SHORT;
out.writeInt(recordLength);
}
out.writeLEInt(ShapefileConstants.POINT_SHAPE);
writeXy(out, point);
} else {
throw new IllegalArgumentException(
"Expecting " + Point.class + " geometry got " + geometry.getClass());
}
}
public void writePointM(final EndianOutput out, final Geometry geometry) {
if (geometry instanceof Point) {
final Point point = (Point)geometry;
if (this.writeLength) {
final int recordLength = 14;
// (BYTES_IN_INT + 3 * BYTES_IN_DOUBLE) / BYTES_IN_SHORT;
out.writeInt(recordLength);
}
out.writeLEInt(ShapefileConstants.POINT_M_SHAPE);
writeXy(out, point);
final double m = point.getM();
if (Double.isNaN(m)) {
out.writeLEDouble(0);
} else {
out.writeLEDouble(m);
}
} else {
throw new IllegalArgumentException(
"Expecting " + Point.class + " geometry got " + geometry.getClass());
}
}
public void writePointZ(final EndianOutput out, final Geometry geometry) {
if (geometry instanceof Point) {
final Point point = (Point)geometry;
if (this.writeLength) {
final int recordLength = 14;
// (BYTES_IN_INT + 3 * BYTES_IN_DOUBLE) / BYTES_IN_SHORT;
out.writeInt(recordLength);
}
if (this.shpFile) {
out.writeLEInt(ShapefileConstants.POINT_ZM_SHAPE);
} else {
out.writeLEInt(ShapefileConstants.POINT_Z_SHAPE);
}
writeXy(out, point);
final double z = point.getZ();
if (Double.isNaN(z)) {
out.writeLEDouble(0);
} else {
out.writeLEDouble(z);
}
} else {
throw new IllegalArgumentException(
"Expecting " + Point.class + " geometry got " + geometry.getClass());
}
}
public void writePointZM(final EndianOutput out, final Geometry geometry) {
if (geometry instanceof Point) {
final Point point = (Point)geometry;
if (this.writeLength) {
final int recordLength = 18;
// (BYTES_IN_INT + 4 * BYTES_IN_DOUBLE) / BYTES_IN_SHORT;
out.writeInt(recordLength);
}
out.writeLEInt(ShapefileConstants.POINT_ZM_SHAPE);
writeXy(out, point);
final double z = point.getZ();
if (Double.isNaN(z)) {
out.writeLEDouble(0);
} else {
out.writeLEDouble(z);
}
final double m = point.getM();
if (Double.isNaN(m)) {
out.writeLEDouble(0);
} else {
out.writeLEDouble(m);
}
} else {
throw new IllegalArgumentException(
"Expecting " + Point.class + " geometry got " + geometry.getClass());
}
}
public void writePolygon(final EndianOutput out, final Geometry geometry) {
writePolygon(out, geometry, ShapefileConstants.POLYGON_SHAPE, 0, 8);
}
private List<LineString> writePolygon(final EndianOutput out, final Geometry geometry,
final int shapeType, final int headerOverhead, final int wordsPerPoint) {
int vertexCount = 0;
final List<LineString> rings = new ArrayList<>();
for (int i = 0; i < geometry.getGeometryCount(); i++) {
final Geometry part = geometry.getGeometry(i);
if (part instanceof Polygon) {
final Polygon polygon = (Polygon)part;
LineString shell = polygon.getShell();
shell = shell.toClockwise();
rings.add(shell);
vertexCount += shell.getVertexCount();
final int numHoles = polygon.getHoleCount();
for (int j = 0; j < numHoles; j++) {
LineString hole = polygon.getHole(j);
hole = hole.toCounterClockwise();
rings.add(hole);
vertexCount += hole.getVertexCount();
}
} else {
throw new IllegalArgumentException(
"Expecting " + Polygon.class + " geometry got " + part.getClass());
}
}
final int numParts = rings.size();
if (this.writeLength) {
final int recordLength = 22 + headerOverhead + 2 * numParts + wordsPerPoint * vertexCount;
out.writeInt(recordLength);
}
out.writeLEInt(shapeType);
final BoundingBox envelope = geometry.getBoundingBox();
writeEnvelope(out, envelope);
out.writeLEInt(numParts);
out.writeLEInt(vertexCount);
int partIndex = 0;
for (final LineString ring : rings) {
out.writeLEInt(partIndex);
partIndex += ring.getVertexCount();
}
for (final LineString ring : rings) {
writeXYCoordinates(out, ring);
}
return rings;
}
public void writePolygonM(final EndianOutput out, final Geometry geometry) {
final List<LineString> rings = writePolygon(out, geometry, ShapefileConstants.POLYGON_M_SHAPE,
8, 12);
writeMCoordinates(out, rings);
}
public void writePolygonZ(final EndianOutput out, final Geometry geometry) {
int shapeType;
if (this.shpFile) {
shapeType = ShapefileConstants.POLYGON_ZM_SHAPE;
} else {
shapeType = ShapefileConstants.POLYGON_Z_SHAPE;
}
final List<LineString> rings = writePolygon(out, geometry, shapeType, 8, 12);
writeZCoordinates(out, rings);
}
public void writePolygonZM(final EndianOutput out, final Geometry geometry) {
final List<LineString> rings = writePolygon(out, geometry, ShapefileConstants.POLYGON_ZM_SHAPE,
16, 16);
writeZCoordinates(out, rings);
writeMCoordinates(out, rings);
}
public void writePolyline(final EndianOutput out, final Geometry geometry) {
writePolyline(out, geometry, ShapefileConstants.POLYLINE_SHAPE, 8);
}
private void writePolyline(final EndianOutput out, final Geometry geometry, final int shapeType,
final int wordsPerPoint) {
if (geometry instanceof Lineal) {
final int numCoordinates = geometry.getVertexCount();
final int numGeometries = geometry.getGeometryCount();
final BoundingBox envelope = geometry.getBoundingBox();
if (this.writeLength) {
// final int recordLength = ((3 + numGeometries) * BYTES_IN_INT + (4 + 2
// * numCoordinates)
// * BYTES_IN_DOUBLE) / 2;
final int recordLength = 22 + numGeometries * 2 + numCoordinates * wordsPerPoint;
out.writeInt(recordLength);
}
out.writeLEInt(shapeType);
writeEnvelope(out, envelope);
out.writeLEInt(numGeometries);
out.writeLEInt(numCoordinates);
writePolylinePartIndexes(out, geometry);
writeXYCoordinates(out, geometry);
} else {
throw new IllegalArgumentException(
"Expecting Lineal geometry got " + geometry.getGeometryType() + "\n" + geometry);
}
}
public void writePolylineM(final EndianOutput out, final Geometry geometry) {
writePolyline(out, geometry, ShapefileConstants.POLYLINE_M_SHAPE, 12);
writeMCoordinates(out, geometry);
}
public void writePolylinePartIndexes(final EndianOutput out, final Geometry geometry) {
int partIndex = 0;
for (int i = 0; i < geometry.getGeometryCount(); i++) {
final LineString line = (LineString)geometry.getGeometry(i);
out.writeLEInt(partIndex);
partIndex += line.getVertexCount();
}
}
public void writePolylineZ(final EndianOutput out, final Geometry geometry) {
int shapeType;
if (this.shpFile) {
shapeType = ShapefileConstants.POLYLINE_ZM_SHAPE;
} else {
shapeType = ShapefileConstants.POLYLINE_Z_SHAPE;
}
writePolyline(out, geometry, shapeType, 12);
writeZCoordinates(out, geometry);
}
public void writePolylineZM(final EndianOutput out, final Geometry geometry) {
writePolyline(out, geometry, ShapefileConstants.POLYLINE_ZM_SHAPE, 16);
writeZCoordinates(out, geometry);
writeMCoordinates(out, geometry);
}
public void writeXy(final EndianOutput out, final double value, final char axisName) {
if (Double.isNaN(value)) {
throw new IllegalArgumentException(axisName + " coordinate value cannot be NaN");
} else if (Double.isInfinite(value)) {
throw new IllegalArgumentException(axisName + " coordinate cannot be infinite");
} else {
out.writeLEDouble(value);
}
}
private void writeXy(final EndianOutput out, final LineString coordinates, final int index) {
writeXy(out, coordinates.getX(index), 'X');
writeXy(out, coordinates.getY(index), 'Y');
}
private void writeXy(final EndianOutput out, final Point point) {
final double x = point.getX();
final double y = point.getY();
writeXy(out, x, 'X');
writeXy(out, y, 'Y');
}
public void writeXYCoordinates(final EndianOutput out, final Geometry geometry) {
for (final Vertex vertex : geometry.vertices()) {
writeXy(out, vertex);
}
}
public void writeXYCoordinates(final EndianOutput out, final LineString coordinates) {
for (int i = 0; i < coordinates.getVertexCount(); i++) {
writeXy(out, coordinates, i);
}
}
public void writeZCoordinates(final EndianOutput out, final Geometry geometry) {
writeZCoordinatesRange(out, geometry);
if (geometry.getAxisCount() >= 3) {
for (final Vertex vertex : geometry.vertices()) {
final double z = vertex.getZ();
if (Double.isNaN(z)) {
out.writeLEDouble(0);
} else {
out.writeLEDouble(z);
}
}
} else {
for (int i = 0; i < geometry.getVertexCount(); i++) {
out.writeLEDouble(0);
}
}
}
public void writeZCoordinates(final EndianOutput out, final LineString coordinates) {
if (coordinates.getAxisCount() >= 3) {
for (int i = 0; i < coordinates.getVertexCount(); i++) {
final double z = coordinates.getZ(i);
if (Double.isNaN(z)) {
out.writeLEDouble(0);
} else {
out.writeLEDouble(z);
}
}
} else {
for (int i = 0; i < coordinates.getVertexCount(); i++) {
out.writeLEDouble(0);
}
}
}
public void writeZCoordinates(final EndianOutput out, final List<LineString> coordinatesList) {
writeZCoordinatesRange(out, coordinatesList);
for (final LineString coordinates : coordinatesList) {
writeZCoordinates(out, coordinates);
}
}
public void writeZCoordinatesRange(final EndianOutput out, final Geometry geometry) {
final BoundingBox boundingBox = geometry.getBoundingBox();
final double min = boundingBox.getMin(2);
final double max = boundingBox.getMax(2);
if (Double.isNaN(min) || Double.isNaN(max)) {
out.writeLEDouble(0);
out.writeLEDouble(0);
} else {
out.writeLEDouble(min);
out.writeLEDouble(max);
}
}
public void writeZCoordinatesRange(final EndianOutput out,
final List<LineString> coordinatesList) {
double minZ = Double.MAX_VALUE;
double maxZ = -Double.MAX_VALUE;
for (final LineString ring : coordinatesList) {
for (int i = 0; i < ring.getVertexCount(); i++) {
double z = ring.getCoordinate(i, 2);
if (Double.isNaN(z)) {
z = 0;
}
minZ = Math.min(z, minZ);
maxZ = Math.max(z, maxZ);
}
}
if (minZ == Double.MAX_VALUE || maxZ == -Double.MAX_VALUE) {
out.writeLEDouble(0);
out.writeLEDouble(0);
} else {
out.writeLEDouble(minZ);
out.writeLEDouble(maxZ);
}
}
}