package com.revolsys.record.io.format.shp; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; 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.EndianInput; import com.revolsys.io.endian.EndianOutput; import com.revolsys.util.JavaBeanUtil; import com.revolsys.util.MathUtil; public final class ShapefileGeometryUtil { public static final Map<String, Method> GEOMETRY_TYPE_READ_METHOD_MAP = new LinkedHashMap<>(); public static final Map<String, Method> GEOMETRY_TYPE_WRITE_METHOD_MAP = new LinkedHashMap<>(); public static final ShapefileGeometryUtil SHP_INSTANCE = new ShapefileGeometryUtil(true); static { addReadWriteMethods("Point"); addReadWriteMethods("Polygon"); addReadWriteMethods("Polyline"); addReadWriteMethods("Multipoint"); for (final boolean z : Arrays.asList(false, true)) { for (final boolean m : Arrays.asList(false, true)) { final String hasZ = String.valueOf(z).toUpperCase(); final String hasM = String.valueOf(m).toUpperCase(); GEOMETRY_TYPE_READ_METHOD_MAP.put("LINESTRING" + hasZ + hasM, GEOMETRY_TYPE_READ_METHOD_MAP.get("POLYLINE" + hasZ + hasM)); GEOMETRY_TYPE_WRITE_METHOD_MAP.put("LINESTRING" + hasZ + hasM, GEOMETRY_TYPE_WRITE_METHOD_MAP.get("POLYLINE" + hasZ + hasM)); GEOMETRY_TYPE_READ_METHOD_MAP.put("MULTILINESTRING" + hasZ + hasM, GEOMETRY_TYPE_READ_METHOD_MAP.get("POLYLINE" + hasZ + hasM)); GEOMETRY_TYPE_WRITE_METHOD_MAP.put("MULTILINESTRING" + hasZ + hasM, GEOMETRY_TYPE_WRITE_METHOD_MAP.get("POLYLINE" + hasZ + hasM)); GEOMETRY_TYPE_READ_METHOD_MAP.put("MULTIPOLYGON" + hasZ + hasM, GEOMETRY_TYPE_READ_METHOD_MAP.get("POLYGON" + hasZ + hasM)); GEOMETRY_TYPE_WRITE_METHOD_MAP.put("MULTIPOLYGON" + hasZ + hasM, GEOMETRY_TYPE_WRITE_METHOD_MAP.get("POLYGON" + hasZ + hasM)); } } } private static void addMethod(final String action, final Map<String, Method> methodMap, final String geometryType, final boolean hasZ, final boolean hasM, final Class<?>... parameterTypes) { final String geometryTypeKey = (geometryType + hasZ + hasM).toUpperCase(); String methodName = action + geometryType; if (hasZ) { methodName += "Z"; } if (hasM) { methodName += "M"; } final Method method = JavaBeanUtil.getMethod(ShapefileGeometryUtil.class, methodName, parameterTypes); methodMap.put(geometryTypeKey, method); } private static void addReadWriteMethods(final String geometryType) { addMethod("read", GEOMETRY_TYPE_READ_METHOD_MAP, geometryType, false, false, GeometryFactory.class, EndianInput.class, Integer.TYPE); addMethod("read", GEOMETRY_TYPE_READ_METHOD_MAP, geometryType, true, false, GeometryFactory.class, EndianInput.class, Integer.TYPE); addMethod("read", GEOMETRY_TYPE_READ_METHOD_MAP, geometryType, false, true, GeometryFactory.class, EndianInput.class, Integer.TYPE); addMethod("read", GEOMETRY_TYPE_READ_METHOD_MAP, geometryType, true, true, GeometryFactory.class, EndianInput.class, Integer.TYPE); addMethod("write", GEOMETRY_TYPE_WRITE_METHOD_MAP, geometryType, false, false, EndianOutput.class, Geometry.class); addMethod("write", GEOMETRY_TYPE_WRITE_METHOD_MAP, geometryType, true, false, EndianOutput.class, Geometry.class); addMethod("write", GEOMETRY_TYPE_WRITE_METHOD_MAP, geometryType, false, true, EndianOutput.class, Geometry.class); addMethod("write", GEOMETRY_TYPE_WRITE_METHOD_MAP, geometryType, true, true, EndianOutput.class, Geometry.class); } public static Method getReadMethod(String geometryTypeKey) { geometryTypeKey = geometryTypeKey.toUpperCase(); final Method method = GEOMETRY_TYPE_READ_METHOD_MAP.get(geometryTypeKey); if (method == null) { throw new IllegalArgumentException("Cannot get Shape Reader for: " + geometryTypeKey); } return method; } public static Method getWriteMethod(final GeometryFactory geometryFactory, final DataType dataType) { final int axisCount = geometryFactory.getAxisCount(); final boolean hasZ = axisCount > 2; final boolean hasM = axisCount > 3; final String geometryType = dataType.toString(); final String geometryTypeKey = geometryType.toUpperCase() + hasZ + hasM; return getWriteMethod(geometryTypeKey); } public static Method getWriteMethod(String geometryTypeKey) { geometryTypeKey = geometryTypeKey.toUpperCase(); final Method method = GEOMETRY_TYPE_WRITE_METHOD_MAP.get(geometryTypeKey); if (method == null) { throw new IllegalArgumentException("Cannot get Shape Writer for: " + geometryTypeKey); } return method; } private final boolean shpFile; private final boolean writeLength; public ShapefileGeometryUtil(final boolean shpFile) { this.shpFile = shpFile; this.writeLength = shpFile; } 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 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); } } @SuppressWarnings("unchecked") public <V extends Geometry> V read(final Method method, final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) { return (V)JavaBeanUtil.method(method, this, geometryFactory, in, recordLength); } public void readCoordinates(final EndianInput in, final int vertexCount, final int axisCount, final double[] coordinates, final int axisIndex) throws IOException { for (int j = 0; j < vertexCount; j++) { double value = in.readLEDouble(); if (value == -Double.MAX_VALUE) { value = Double.NaN; } coordinates[j * axisCount + axisIndex] = value; } } public void readCoordinates(final EndianInput in, final int[] partIndex, final List<double[]> coordinateLists, final int axisIndex, final int axisCount) throws IOException { in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); for (int i = 0; i < partIndex.length; i++) { final double[] coordinates = coordinateLists.get(i); final int vertexCount = coordinates.length / axisCount; readCoordinates(in, vertexCount, axisCount, coordinates, axisIndex); } } public int[] readIntArray(final EndianInput in, final int count) throws IOException { final int[] values = new int[count]; for (int i = 0; i < count; i++) { final int value = in.readLEInt(); values[i] = value; } return values; } public Punctual readMultipoint(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int vertexCount = in.readLEInt(); final double[] coordinates = readXYCoordinates(in, vertexCount, 2); return geometryFactory.punctual(new LineStringDouble(2, coordinates)); } public Punctual readMultipointM(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int vertexCount = in.readLEInt(); final int axisCount = 4; final double[] coordinates = readXYCoordinates(in, vertexCount, axisCount); in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); setCoordinatesNaN(coordinates, vertexCount, axisCount, 2); readCoordinates(in, vertexCount, axisCount, coordinates, 3); return geometryFactory.punctual(new LineStringDouble(axisCount, coordinates)); } public Punctual readMultipointZ(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int vertexCount = in.readLEInt(); final double[] coordinates = readXYCoordinates(in, vertexCount, 3); in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); readCoordinates(in, vertexCount, 3, coordinates, 2); return geometryFactory.punctual(new LineStringDouble(3, coordinates)); } public Punctual readMultipointZM(GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int vertexCount = in.readLEInt(); int axisCount; if (recordLength == 20 + 12 * vertexCount) { geometryFactory = geometryFactory.convertAxisCount(3); axisCount = 3; } else { axisCount = 4; } final double[] coordinates = readXYCoordinates(in, vertexCount, axisCount); in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); readCoordinates(in, vertexCount, axisCount, coordinates, 2); if (axisCount == 4) { in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); readCoordinates(in, vertexCount, axisCount, coordinates, 3); } return geometryFactory.punctual(axisCount, coordinates); } public int[] readPartIndex(final EndianInput in, final int numParts, final int vertexCount) throws IOException { final int[] partIndex = new int[numParts]; if (numParts > 0) { int startIndex = in.readLEInt(); for (int i = 1; i < partIndex.length; i++) { final int index = in.readLEInt(); partIndex[i - 1] = index - startIndex; startIndex = index; } partIndex[partIndex.length - 1] = vertexCount - startIndex; } return partIndex; } public Point readPoint(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { final double[] coordinates = readXYCoordinates(in, 1, 2); return geometryFactory.point(coordinates); } public Point readPointM(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { final double x = in.readLEDouble(); final double y = in.readLEDouble(); final double z = 0; final double m = in.readLEDouble(); return geometryFactory.point(x, y, z, m); } public void readPoints(final EndianInput in, final int[] partIndex, final List<double[]> coordinateLists, final int axisCount) throws IOException { for (int i = 0; i < partIndex.length; i++) { final int count = partIndex[i]; final double[] coordinates = coordinateLists.get(i); readXYCoordinates(in, axisCount, count, coordinates); } } public Point readPointZ(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { final double x = in.readLEDouble(); final double y = in.readLEDouble(); final double z = in.readLEDouble(); return geometryFactory.point(x, y, z); } public Point readPointZM(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { final double x = in.readLEDouble(); final double y = in.readLEDouble(); final double z = in.readLEDouble(); if (recordLength == 14) { return geometryFactory.convertAxisCount(3).point(x, y, z); } else { final double m = in.readLEDouble(); return geometryFactory.point(x, y, z, m); } } public Geometry readPolygon(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int numParts = in.readLEInt(); final int vertexCount = in.readLEInt(); final int[] partIndex = readPartIndex(in, numParts, vertexCount); final List<double[]> parts = newCoordinatesLists(partIndex, 2); readPoints(in, partIndex, parts, 2); return newPolygonGeometryFromParts(geometryFactory, parts, 2); } public Geometry readPolygonM(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int partCount = in.readLEInt(); final int vertexCount = in.readLEInt(); final int axisCount = 4; final int[] partIndex = readPartIndex(in, partCount, vertexCount); final List<double[]> parts = newCoordinatesLists(partIndex, axisCount); readPoints(in, partIndex, parts, axisCount); readCoordinates(in, partIndex, parts, 3, axisCount); return newPolygonGeometryFromParts(geometryFactory, parts, axisCount); } public Geometry readPolygonZ(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int numParts = in.readLEInt(); final int vertexCount = in.readLEInt(); final int axisCount = 3; final int[] partIndex = readPartIndex(in, numParts, vertexCount); final List<double[]> parts = newCoordinatesLists(partIndex, axisCount); readPoints(in, partIndex, parts, axisCount); readCoordinates(in, partIndex, parts, 2, axisCount); return newPolygonGeometryFromParts(geometryFactory, parts, axisCount); } public Geometry readPolygonZM(GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int numParts = in.readLEInt(); final int vertexCount = in.readLEInt(); final int[] partIndex = readPartIndex(in, 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(in, partIndex, parts, axisCount); readCoordinates(in, partIndex, parts, 2, axisCount); if (axisCount == 4) { readCoordinates(in, partIndex, parts, 3, axisCount); } return newPolygonGeometryFromParts(geometryFactory, parts, axisCount); } public Geometry readPolyline(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int numParts = in.readLEInt(); final int vertexCount = in.readLEInt(); final int axisCount = 2; if (numParts == 1) { in.readLEInt(); final double[] coordinates = readXYCoordinates(in, 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] = in.readLEInt(); } 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(in, numCoords, axisCount); lines.add(geometryFactory.lineString(2, coordinates)); } return geometryFactory.lineal(lines); } } public Geometry readPolylineM(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int partCount = in.readLEInt(); final int allVertexCount = in.readLEInt(); final int axisCount = 4; if (partCount == 1) { in.readLEInt(); final double[] coordinates = readXYCoordinates(in, allVertexCount, axisCount); in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); setCoordinatesNaN(coordinates, allVertexCount, axisCount, 2); readCoordinates(in, 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] = in.readLEInt(); } 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(in, vertexCount, axisCount); coordinatesList.add(coordinates); } in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); for (int i = 0; i < partIndex.length - 1; i++) { final double[] coordinates = coordinatesList.get(i); final int vertexCount = coordinates.length / axisCount; readCoordinates(in, vertexCount, axisCount, coordinates, 3); } return geometryFactory.lineal(axisCount, coordinatesList.toArray(new double[coordinatesList.size()][])); } } public Geometry readPolylineZ(final GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int numParts = in.readLEInt(); final int vertexCount = in.readLEInt(); final int axisCount = 3; return readPolylineZ(geometryFactory, in, numParts, vertexCount, axisCount); } public Geometry readPolylineZ(final GeometryFactory geometryFactory, final EndianInput in, final int partCount, final int allVertexCount, final int axisCount) throws IOException { if (partCount == 1) { in.readLEInt(); final double[] coordinates = readXYCoordinates(in, allVertexCount, axisCount); in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); readCoordinates(in, 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] = in.readLEInt(); } 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(in, vertexCount, axisCount); } in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); for (int i = 0; i < partCount; i++) { final double[] coordinates = linesCoordinates[i]; final int vertexCount = coordinates.length / axisCount; readCoordinates(in, vertexCount, axisCount, coordinates, 2); } return geometryFactory.lineal(axisCount, linesCoordinates); } } public Geometry readPolylineZM(GeometryFactory geometryFactory, final EndianInput in, final int recordLength) throws IOException { in.skipBytes(4 * MathUtil.BYTES_IN_DOUBLE); final int geometryCount = in.readLEInt(); final int vertexCount = in.readLEInt(); if (22 + geometryCount * 2 + vertexCount * 12 == recordLength) { geometryFactory = geometryFactory.convertAxisCount(3); return readPolylineZ(geometryFactory, in, geometryCount, vertexCount, 3); } else { final int axisCount = 4; if (geometryCount == 1) { in.readLEInt(); final double[] coordinates = readXYCoordinates(in, vertexCount, axisCount); in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); readCoordinates(in, vertexCount, axisCount, coordinates, 2); in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); readCoordinates(in, 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] = in.readLEInt(); } 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(in, numCoords, axisCount); coordinatesList.add(coordinates); } in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); for (int i = 0; i < partIndex.length - 1; i++) { final double[] coordinates = coordinatesList.get(i); readCoordinates(in, coordinates.length / 4, axisCount, coordinates, 2); } in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); for (int i = 0; i < partIndex.length - 1; i++) { final double[] coordinates = coordinatesList.get(i); readCoordinates(in, 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 EndianInput in, final int vertexCount, final int axisCount) throws IOException { final double[] coordinates = new double[vertexCount * axisCount]; readXYCoordinates(in, axisCount, vertexCount, coordinates); return coordinates; } public void readXYCoordinates(final EndianInput in, final int axisCount, final int vertexCount, final double[] coordinates) throws IOException { for (int j = 0; j < vertexCount; j++) { final double x = in.readLEDouble(); final double y = in.readLEDouble(); coordinates[j * axisCount] = x; coordinates[j * axisCount + 1] = y; } } 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 setCoordinatesNaN(final EndianInput in, final int[] partIndex, final List<double[]> coordinateLists, final int axisIndex, final int axisCount) throws IOException { in.skipBytes(2 * MathUtil.BYTES_IN_DOUBLE); 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 write(final Method method, final EndianOutput out, final Geometry geometry) { JavaBeanUtil.method(method, this, out, geometry); } public void writeEnvelope(final EndianOutput out, final BoundingBox envelope) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { writeMCoordinatesRange(out, coordinatesList); for (final LineString coordinates : coordinatesList) { writeMCoordinates(out, coordinates); } } public void writeMCoordinatesRange(final EndianOutput out, final Geometry geometry) throws IOException { 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) throws IOException { 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) throws IOException { writeMultipoint(out, geometry, ShapefileConstants.MULTI_POINT_SHAPE, 8); } private void writeMultipoint(final EndianOutput out, final Geometry geometry, final int shapeType, final int wordsPerPoint) throws IOException { 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) throws IOException { writeMultipoint(out, geometry, ShapefileConstants.MULTI_POINT_M_SHAPE, 12); writeMCoordinates(out, geometry); } public void writeMultipointZ(final EndianOutput out, final Geometry geometry) throws IOException { 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) throws IOException { writeMultipoint(out, geometry, ShapefileConstants.MULTI_POINT_ZM_SHAPE, 16); writeZCoordinates(out, geometry); writeMCoordinates(out, geometry); } public void writePoint(final EndianOutput out, final Geometry geometry) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { writePolyline(out, geometry, ShapefileConstants.POLYLINE_SHAPE, 8); } private void writePolyline(final EndianOutput out, final Geometry geometry, final int shapeType, final int wordsPerPoint) throws IOException { 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) throws IOException { writePolyline(out, geometry, ShapefileConstants.POLYLINE_M_SHAPE, 12); writeMCoordinates(out, geometry); } public void writePolylinePartIndexes(final EndianOutput out, final Geometry geometry) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { 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) throws IOException { writeXy(out, coordinates.getX(index), 'X'); writeXy(out, coordinates.getY(index), 'Y'); } private void writeXy(final EndianOutput out, final Point point) throws IOException { 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) throws IOException { for (final Vertex vertex : geometry.vertices()) { writeXy(out, vertex); } } public void writeXYCoordinates(final EndianOutput out, final LineString coordinates) throws IOException { for (int i = 0; i < coordinates.getVertexCount(); i++) { writeXy(out, coordinates, i); } } public void writeZCoordinates(final EndianOutput out, final Geometry geometry) throws IOException { 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) throws IOException { 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) throws IOException { writeZCoordinatesRange(out, coordinatesList); for (final LineString coordinates : coordinatesList) { writeZCoordinates(out, coordinates); } } public void writeZCoordinatesRange(final EndianOutput out, final Geometry geometry) throws IOException { 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) throws IOException { 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); } } }