package com.revolsys.oracle.recordstore.esri; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Blob; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import com.revolsys.datatype.DataType; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.ClockDirection; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.LinearRing; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygon; import com.revolsys.jdbc.field.JdbcFieldDefinition; import com.revolsys.record.Record; import com.revolsys.record.property.FieldProperties; import com.revolsys.util.Exceptions; public class ArcSdeStGeometryFieldDefinition extends JdbcFieldDefinition { public static List<List<Geometry>> getParts(final Geometry geometry) { final List<List<Geometry>> partsList = new ArrayList<>(); if (geometry != null) { final ClockDirection expectedRingOrientation = ClockDirection.COUNTER_CLOCKWISE; for (final Geometry part : geometry.geometries()) { if (!part.isEmpty()) { if (part instanceof Point) { final Point point = (Point)part; partsList.add(Collections.<Geometry> singletonList(point)); } else if (part instanceof LineString) { final LineString line = (LineString)part; partsList.add(Collections.<Geometry> singletonList(line)); } else if (part instanceof Polygon) { final Polygon polygon = (Polygon)part; final List<Geometry> ringList = new ArrayList<>(); ClockDirection partExpectedRingOrientation = expectedRingOrientation; for (LinearRing ring : polygon.rings()) { final ClockDirection ringOrientation = ring.getClockDirection(); if (ringOrientation != partExpectedRingOrientation) { ring = ring.reverse(); } ringList.add(ring); if (partExpectedRingOrientation == expectedRingOrientation) { partExpectedRingOrientation = expectedRingOrientation.opposite(); } } partsList.add(ringList); } } } } return partsList; } private final int axisCount; private final GeometryFactory geometryFactory; private final ArcSdeSpatialReference spatialReference; public ArcSdeStGeometryFieldDefinition(final String dbName, final String name, final DataType type, final boolean required, final String description, final Map<String, Object> properties, final ArcSdeSpatialReference spatialReference, final int axisCount) { super(dbName, name, type, -1, 0, 0, required, description, properties); this.spatialReference = spatialReference; final GeometryFactory factory = spatialReference.getGeometryFactory(); if (axisCount == 3) { this.geometryFactory = GeometryFactory.fixed(factory.getCoordinateSystemId(), axisCount, factory.getScaleX(), factory.getScaleY(), factory.getScaleZ()); } else { this.geometryFactory = GeometryFactory.fixed(factory.getCoordinateSystemId(), axisCount, factory.getScaleX(), factory.getScaleY()); } this.axisCount = axisCount; setProperty(FieldProperties.GEOMETRY_FACTORY, this.geometryFactory); } @Override public void addColumnName(final StringBuilder sql, final String tablePrefix) { sql.append(tablePrefix); sql.append(".GEOMETRY.ENTITY, "); sql.append(tablePrefix); sql.append(".GEOMETRY.NUMPTS, "); sql.append(tablePrefix); sql.append(".GEOMETRY.POINTS"); } @Override public void addStatementPlaceHolder(final StringBuilder sql) { sql.append("SDE.ST_GEOMETRY(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); } @Override public ArcSdeStGeometryFieldDefinition clone() { return new ArcSdeStGeometryFieldDefinition(getDbName(), getName(), getDataType(), isRequired(), getDescription(), getProperties(), this.spatialReference, this.axisCount); } @Override public int setFieldValueFromResultSet(final ResultSet resultSet, final int columnIndex, final Record object) throws SQLException { final int geometryType = resultSet.getInt(columnIndex); if (!resultSet.wasNull()) { final int numPoints = resultSet.getInt(columnIndex + 1); final Blob blob = resultSet.getBlob(columnIndex + 2); try ( final InputStream pointsIn = new BufferedInputStream(blob.getBinaryStream(), 32000)) { final Double xOffset = this.spatialReference.getXOffset(); final Double yOffset = this.spatialReference.getYOffset(); final Double xyScale = this.spatialReference.getXyScale(); final Double zScale = this.spatialReference.getZScale(); final Double zOffset = this.spatialReference.getZOffset(); final Double mScale = this.spatialReference.getMScale(); final Double mOffset = this.spatialReference.getMOffset(); final GeometryFactory geometryFactory = this.geometryFactory; final Geometry geometry = PackedCoordinateUtil.getGeometry(pointsIn, geometryFactory, geometryType, numPoints, xOffset, yOffset, xyScale, zOffset, zScale, mOffset, mScale); object.setValue(getIndex(), geometry); } catch (final IOException e) { throw Exceptions.wrap(e); } } return columnIndex + 3; } public void setFloat(final PreparedStatement statement, int index, final Double value, final Number defaultValue) throws SQLException { if (value == null || Double.isInfinite(value) || Double.isNaN(value)) { if (defaultValue == null) { statement.setNull(index++, Types.FLOAT); } else { statement.setFloat(index, defaultValue.floatValue()); } } else { statement.setFloat(index, value.floatValue()); } } @Override public int setPreparedStatementValue(final PreparedStatement statement, final int parameterIndex, Object value) throws SQLException { int index = parameterIndex; if (value instanceof BoundingBox) { final BoundingBox boundingBox = (BoundingBox)value; value = boundingBox.convert(this.geometryFactory).toPolygon(1); } if (value instanceof Geometry) { Geometry geometry = (Geometry)value; geometry = geometry.newGeometry(this.geometryFactory); final int sdeSrid = this.spatialReference.getEsriSrid(); final Double xOffset = this.spatialReference.getXOffset(); final Double yOffset = this.spatialReference.getYOffset(); final Double xyScale = this.spatialReference.getXyScale(); final Double zScale = this.spatialReference.getZScale(); final Double zOffset = this.spatialReference.getZOffset(); final Double mScale = this.spatialReference.getMScale(); final Double mOffset = this.spatialReference.getMOffset(); final BoundingBox envelope = geometry.getBoundingBox(); final double minX = envelope.getMinX(); final double minY = envelope.getMinY(); final double maxX = envelope.getMaxX(); final double maxY = envelope.getMaxY(); final double area = geometry.getArea(); final double length = geometry.getLength(); final boolean hasZ = this.axisCount > 2 && zOffset != null && zScale != null; final boolean hasM = this.axisCount > 3 && mOffset != null && mScale != null; int numPoints = 0; byte[] data; final List<List<Geometry>> parts = getParts(geometry); final int entityType = ArcSdeConstants.getStGeometryType(geometry); numPoints = PackedCoordinateUtil.getNumPoints(parts); data = PackedCoordinateUtil.getPackedBytes(xOffset, yOffset, xyScale, hasZ, zOffset, zScale, hasM, mScale, mOffset, parts); statement.setInt(index++, entityType); statement.setInt(index++, numPoints); setFloat(statement, index++, minX, 0); setFloat(statement, index++, minY, 0); setFloat(statement, index++, maxX, 0); setFloat(statement, index++, maxY, 0); if (hasZ) { final double minZ = envelope.getMin(2); final double maxZ = envelope.getMax(2); setFloat(statement, index++, minZ, 0); setFloat(statement, index++, maxZ, 0); } else { statement.setNull(index++, Types.FLOAT); statement.setNull(index++, Types.FLOAT); } if (hasM) { final double minM = envelope.getMin(3); final double maxM = envelope.getMax(3); setFloat(statement, index++, minM, 0); setFloat(statement, index++, maxM, 0); } else { statement.setNull(index++, Types.FLOAT); statement.setNull(index++, Types.FLOAT); } statement.setFloat(index++, (float)area); statement.setFloat(index++, (float)length); statement.setInt(index++, sdeSrid); statement.setBytes(index++, data); } else { throw new IllegalArgumentException("Geometry cannot be null"); } return index; } }