package com.revolsys.gdal.record; import java.util.List; import javax.annotation.PreDestroy; import org.gdal.ogr.DataSource; import org.gdal.ogr.Feature; import org.gdal.ogr.FeatureDefn; import org.gdal.ogr.FieldDefn; import org.gdal.ogr.GeomFieldDefn; import org.gdal.ogr.Layer; import com.revolsys.datatype.DataTypes; 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.geometry.model.vertex.Vertex; import com.revolsys.io.AbstractRecordWriter; import com.revolsys.logging.Logs; import com.revolsys.record.Record; import com.revolsys.record.RecordState; import com.revolsys.record.property.FieldProperties; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.record.schema.RecordStore; import com.revolsys.util.number.Integers; public class OgrRecordWriter extends AbstractRecordWriter { private DataSource dataSource; private OgrRecordStore recordStore; OgrRecordWriter(final OgrRecordStore recordStore) { this.recordStore = recordStore; this.dataSource = recordStore.newDataSource(true); } private void addParts(final org.gdal.ogr.Geometry ogrGeometry, final Geometry geometry, final int geometryType, final int axisCount) { for (final Geometry part : geometry.geometries()) { final org.gdal.ogr.Geometry ogrRing = toOgrGeometry(part, geometryType, axisCount); ogrGeometry.AddGeometry(ogrRing); } } @Override @PreDestroy public synchronized void close() { try { if (this.dataSource != null) { this.dataSource.delete(); } } finally { this.dataSource = null; this.recordStore = null; } } private void delete(final Record record) { final RecordDefinition recordDefinition = record.getRecordDefinition(); final Layer layer = getLayer(recordDefinition); final String driverName = this.recordStore.getDriverName(); if (OgrRecordStore.SQLITE.equals(driverName) || OgrRecordStore.GEO_PAKCAGE.equals(driverName)) { final Integer fid = record.getInteger(OgrRecordStore.ROWID); if (fid != null) { layer.DeleteFeature(fid); } } } protected Layer getLayer(final RecordDefinition recordDefinition) { final String typePath = recordDefinition.getPath(); final String layerName = this.recordStore.getLayerName(typePath); final Layer layer = this.dataSource.GetLayer(layerName); return layer; } private void insert(final Record record) { final RecordDefinition sourceRecordDefinition = record.getRecordDefinition(); final RecordDefinition recordDefinition = this.recordStore .getRecordDefinition(sourceRecordDefinition); final String typePath = sourceRecordDefinition.getPath(); final List<FieldDefinition> attributes = recordDefinition.getFields(); final List<String> idFieldNames = recordDefinition.getIdFieldNames(); for (final FieldDefinition attribute : attributes) { final String name = attribute.getName(); if (!idFieldNames.contains(name)) { if (attribute.isRequired()) { final Object value = record.getValue(name); if (value == null) { throw new IllegalArgumentException( "Atribute " + typePath + "." + name + " is required"); } } } } try { final Layer layer = getLayer(sourceRecordDefinition); final FeatureDefn featureDefinition = layer.GetLayerDefn(); final Feature feature = new Feature(featureDefinition); try { setFieldValues(featureDefinition, record, feature); setGeometries(featureDefinition, record, feature); layer.CreateFeature(feature); final String driverName = this.recordStore.getDriverName(); if (OgrRecordStore.SQLITE.equals(driverName) || OgrRecordStore.GEO_PAKCAGE.equals(driverName)) { record.setValue(OgrRecordStore.ROWID, feature.GetFieldAsInteger(OgrRecordStore.ROWID)); } record.setState(RecordState.PERSISTED); } finally { feature.delete(); this.recordStore.addStatistic("Insert", record); } } catch (final IllegalArgumentException e) { throw new RuntimeException( "Unable to insert row " + e.getMessage() + "\n" + record.toString(), e); } catch (final RuntimeException e) { Logs.debug(OgrRecordWriter.class, "Unable to insert row \n:" + record.toString()); throw new RuntimeException("Unable to insert row", e); } } @SuppressWarnings("deprecation") protected void setFieldValues(final FeatureDefn featureDefinition, final Record record, final Feature feature) { final int fieldCount = featureDefinition.GetFieldCount(); for (int fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { final FieldDefn fieldDefinition = featureDefinition.GetFieldDefn(fieldIndex); final String name = fieldDefinition.GetName(); final Object value = record.getValue(name); if (value != null) { final int fieldType = fieldDefinition.GetFieldType(); switch (fieldType) { case 0: final Integer intValue = Integers.toValid(value); if (intValue != null) { feature.SetField(fieldIndex, intValue); } break; case 1: // value = feature.GetFieldAsIntegerList(fieldIndex); break; case 2: final Double doubleValue = DataTypes.DOUBLE.toObject(value); if (doubleValue != null) { feature.SetField(fieldIndex, doubleValue); } break; case 3: // value = feature.GetFieldAsDoubleList(fieldIndex); break; case 4: case 6: final String string = DataTypes.toString(value); feature.SetField(fieldIndex, string); break; case 5: case 7: // value = feature.GetFieldAsStringList(fieldIndex); break; case 8: // binary break; case 9: case 10: case 11: final java.util.Date date = DataTypes.DATE_TIME.toObject(value); final int year = 1900 + date.getYear(); final int month = date.getMonth(); final int day = date.getDay(); final int hours = date.getHours(); final int minutes = date.getMinutes(); final int seconds = date.getSeconds(); final int timezoneOffset = date.getTimezoneOffset(); feature.SetField(fieldIndex, year, month, day, hours, minutes, seconds, timezoneOffset); break; default: final String string2 = DataTypes.toString(value); feature.SetField(fieldIndex, string2); break; } } } } private void setGeometries(final FeatureDefn featureDefinition, final Record record, final Feature feature) { final RecordDefinition recordDefinition = record.getRecordDefinition(); final int geometryFieldCount = featureDefinition.GetGeomFieldCount(); for (int fieldIndex = 0; fieldIndex < geometryFieldCount; fieldIndex++) { final GeomFieldDefn fieldDefinition = featureDefinition.GetGeomFieldDefn(fieldIndex); final String name = fieldDefinition.GetName(); Geometry geometry = record.getValue(name); if (geometry != null) { final FieldDefinition attribute = recordDefinition.getField(name); final GeometryFactory geometryFactory = attribute .getProperty(FieldProperties.GEOMETRY_FACTORY); geometry = geometry.convertGeometry(geometryFactory); final int geometryType = fieldDefinition.GetFieldType(); final int axisCount = geometryFactory.getAxisCount(); final org.gdal.ogr.Geometry ogrGeometry = toOgrGeometry(geometry, geometryType, axisCount); feature.SetGeomField(fieldIndex, ogrGeometry); } } } protected org.gdal.ogr.Geometry toOgrGeometry(final Geometry geometry, final int geometryType, final int axisCount) { final org.gdal.ogr.Geometry ogrGeometry = new org.gdal.ogr.Geometry(geometryType); if (!geometry.isEmpty()) { switch (geometryType) { case 1: case 0x80000000 + 1: { final Point point = (Point)geometry; final double x = point.getX(); final double y = point.getY(); if (axisCount == 2) { ogrGeometry.AddPoint(x, y); } else { final double z = point.getZ(); ogrGeometry.AddPoint(x, y, z); } } break; case 2: case 0x80000000 + 2: final LineString line = (LineString)geometry; final int vertexCount = line.getVertexCount(); for (int vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) { final double x = line.getX(vertexIndex); final double y = line.getY(vertexIndex); if (axisCount == 2) { ogrGeometry.AddPoint(x, y); } else { final double z = line.getZ(vertexIndex); ogrGeometry.AddPoint(x, y, z); } } break; case 3: case 0x80000000 + 3: for (final LinearRing ring : ((Polygon)geometry).rings()) { final org.gdal.ogr.Geometry ogrRing = toOgrGeometry(ring, 101, axisCount); ogrGeometry.AddGeometry(ogrRing); } break; case 4: addParts(ogrGeometry, geometry, 1, axisCount); break; case 0x80000000 + 4: addParts(ogrGeometry, geometry, 0x80000000 + 1, axisCount); break; case 5: addParts(ogrGeometry, geometry, 2, axisCount); break; case 0x80000000 + 5: addParts(ogrGeometry, geometry, 0x80000000 + 2, axisCount); break; case 6: addParts(ogrGeometry, geometry, 3, axisCount); break; case 0x80000000 + 6: addParts(ogrGeometry, geometry, 0x80000000 + 3, axisCount); break; // case 0x80000000 + 7: // final List<Geometry> parts = new ArrayList<>(); // for (int partIndex = 0; partIndex < geometry. GetGeometryCount(); // partIndex++) { // final org.gdal.ogr.Geometry ogrPart = geometry. // GetGeometryRef(partIndex); // final Geometry part = toGeometry(ogrPart); // parts.add(part); // } // return this.geometryFactory.geometry(parts); case 101: for (final Vertex vertex : geometry.vertices()) { final double x = vertex.getX(); final double y = vertex.getY(); if (axisCount == 2) { ogrGeometry.AddPoint(x, y); } else { final double z = vertex.getZ(); ogrGeometry.AddPoint(x, y, z); } } break; default: return null; } } if (axisCount == 2) { ogrGeometry.FlattenTo2D(); } return ogrGeometry; } private void update(final Record record) { final RecordDefinition recordDefinition = record.getRecordDefinition(); final Layer layer = getLayer(recordDefinition); final String driverName = this.recordStore.getDriverName(); if (OgrRecordStore.SQLITE.equals(driverName) || OgrRecordStore.GEO_PAKCAGE.equals(driverName)) { final Integer fid = record.getInteger(OgrRecordStore.ROWID); if (fid != null) { final Feature feature = layer.GetFeature(fid); if (feature != null) { final FeatureDefn featureDefinition = layer.GetLayerDefn(); setFieldValues(featureDefinition, record, feature); layer.SetFeature(feature); } } } } @Override public synchronized void write(final Record record) { try { final RecordDefinition recordDefinition = record.getRecordDefinition(); final RecordStore recordStore = recordDefinition.getRecordStore(); if (recordStore == this.recordStore) { switch (record.getState()) { case NEW: insert(record); break; case MODIFIED: update(record); break; case PERSISTED: // No action required break; case DELETED: delete(record); break; default: throw new IllegalStateException("State not known"); } } else { insert(record); } } catch (final RuntimeException e) { throw e; } catch (final Error e) { throw e; } catch (final Exception e) { throw new RuntimeException("Unable to write", e); } } }