package com.revolsys.gis.esri.gdb.file.capi.type; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.BiConsumer; import com.revolsys.datatype.DataType; import com.revolsys.datatype.DataTypes; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.gis.esri.gdb.file.FileGdbRecordStore; import com.revolsys.gis.esri.gdb.file.capi.swig.Row; import com.revolsys.io.endian.EndianOutput; import com.revolsys.io.endian.EndianOutputStream; import com.revolsys.record.Record; import com.revolsys.record.io.format.esri.gdb.xml.model.Field; import com.revolsys.record.io.format.esri.gdb.xml.model.GeometryDef; import com.revolsys.record.io.format.esri.gdb.xml.model.SpatialReference; import com.revolsys.record.io.format.esri.gdb.xml.model.enums.GeometryType; import com.revolsys.record.io.format.shp.ShapefileGeometryHandler; import com.revolsys.record.property.FieldProperties; import com.revolsys.util.Booleans; import com.revolsys.util.Exceptions; import com.revolsys.util.function.Function3; public class GeometryFieldDefinition extends AbstractFileGdbFieldDefinition { public static final Map<GeometryType, DataType> GEOMETRY_TYPE_DATA_TYPE_MAP = new LinkedHashMap<>(); private static final ShapefileGeometryHandler SHAPEFILE_GEOMETRY_HANDLER = new ShapefileGeometryHandler( false); static { GEOMETRY_TYPE_DATA_TYPE_MAP.put(GeometryType.esriGeometryPoint, DataTypes.POINT); GEOMETRY_TYPE_DATA_TYPE_MAP.put(GeometryType.esriGeometryMultipoint, DataTypes.MULTI_POINT); GEOMETRY_TYPE_DATA_TYPE_MAP.put(GeometryType.esriGeometryPolyline, DataTypes.MULTI_LINE_STRING); GEOMETRY_TYPE_DATA_TYPE_MAP.put(GeometryType.esriGeometryPolygon, DataTypes.MULTI_POLYGON); } private static final Integer MINUS1 = -1; private GeometryFactory geometryFactory = GeometryFactory.DEFAULT_3D; private BiConsumer<EndianOutput, Geometry> writeFunction; private Function3<GeometryFactory, ByteBuffer, Integer, Geometry> readFunction; public GeometryFieldDefinition(final Field field) { super(field.getName(), DataTypes.GEOMETRY, Booleans.getBoolean(field.getRequired()) || !field.isIsNullable()); final GeometryDef geometryDef = field.getGeometryDef(); if (geometryDef == null) { throw new IllegalArgumentException("Field definition does not include a geometry definition"); } else { final SpatialReference spatialReference = geometryDef.getSpatialReference(); if (spatialReference == null) { throw new IllegalArgumentException("Field definition does not include a spatial reference"); } else { final GeometryType geometryType = geometryDef.getGeometryType(); final DataType dataType = GEOMETRY_TYPE_DATA_TYPE_MAP.get(geometryType); setType(dataType); this.geometryFactory = spatialReference.getGeometryFactory(); int axisCount = 2; final boolean hasZ = geometryDef.isHasZ(); if (hasZ) { axisCount = 3; } final boolean hasM = geometryDef.isHasM(); if (hasM) { axisCount = 4; } if (axisCount != this.geometryFactory.getAxisCount()) { final int srid = this.geometryFactory.getCoordinateSystemId(); final double scaleX = this.geometryFactory.getScaleX(); final double scaleY = this.geometryFactory.getScaleY(); final double scaleZ = this.geometryFactory.getScaleZ(); this.geometryFactory = GeometryFactory.fixed(srid, axisCount, scaleX, scaleY, scaleZ); } setProperty(FieldProperties.GEOMETRY_FACTORY, this.geometryFactory); final String geometryTypeKey = dataType.toString() + hasZ + hasM; this.readFunction = SHAPEFILE_GEOMETRY_HANDLER.getReadFunction(geometryTypeKey); if (this.readFunction == null) { throw new IllegalArgumentException("No read method for geometry type " + geometryTypeKey); } this.writeFunction = SHAPEFILE_GEOMETRY_HANDLER.getWriteFunction(geometryTypeKey); if (this.writeFunction == null) { throw new IllegalArgumentException( "No write method for geometry type " + geometryTypeKey); } } } } @Override public int getMaxStringLength() { return 40; } @Override public Object getValue(final Row row) { final String name = getName(); final FileGdbRecordStore recordStore = getRecordStore(); if (recordStore.isNull(row, name)) { return null; } else { final byte[] bytes; synchronized (getSync()) { bytes = row.getGeometry(); } final ByteBuffer buffer = ByteBuffer.wrap(bytes); buffer.order(ByteOrder.LITTLE_ENDIAN); final int geometryType = buffer.getInt(); if (geometryType == 0) { final DataType dataType = getDataType(); if (DataTypes.POINT.equals(dataType)) { return this.geometryFactory.point(); } else if (DataTypes.MULTI_POINT.equals(dataType)) { return this.geometryFactory.point(); } else if (DataTypes.LINE_STRING.equals(dataType)) { return this.geometryFactory.lineString(); } else if (DataTypes.MULTI_LINE_STRING.equals(dataType)) { return this.geometryFactory.lineString(); } else if (DataTypes.POLYGON.equals(dataType)) { return this.geometryFactory.polygon(); } else if (DataTypes.MULTI_POLYGON.equals(dataType)) { return this.geometryFactory.polygon(); } else { return null; } } else { final Geometry geometry = this.readFunction.apply(this.geometryFactory, buffer, MINUS1); return geometry; } } } @Override public void setValue(final Record record, final Row row, final Object value) { if (value == null) { setNull(row); } else if (value instanceof Geometry) { final Geometry geometry = (Geometry)value; final Geometry projectedGeometry = geometry.convertGeometry(this.geometryFactory); if (projectedGeometry.isEmpty()) { setNull(row); } else { byte[] bytes; if (geometry.isEmpty()) { bytes = new byte[] { 0, 0, 0, 0 }; } else { try ( final ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); final EndianOutput out = new EndianOutputStream(byteOut)) { this.writeFunction.accept(out, projectedGeometry); bytes = byteOut.toByteArray(); } catch (final IOException e) { throw Exceptions.wrap(e); } } synchronized (getSync()) { row.setGeometry(bytes); } } } else { throw new IllegalArgumentException( "Expecting a " + Geometry.class + " not a " + value.getClass() + "=" + value); } } }