package com.revolsys.record.io.format.esri.gdb.xml; import java.io.Writer; import java.sql.Timestamp; import java.util.UUID; import com.revolsys.datatype.DataType; import com.revolsys.datatype.DataTypes; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.cs.ProjectedCoordinateSystem; import com.revolsys.geometry.cs.esri.EsriCoordinateSystems; import com.revolsys.geometry.cs.esri.EsriCsWktWriter; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.Lineal; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygon; import com.revolsys.geometry.model.Punctual; import com.revolsys.io.AbstractRecordWriter; import com.revolsys.io.PathUtil; import com.revolsys.logging.Logs; import com.revolsys.record.Record; import com.revolsys.record.io.format.esri.gdb.xml.type.EsriGeodatabaseXmlFieldType; import com.revolsys.record.io.format.esri.gdb.xml.type.EsriGeodatabaseXmlFieldTypeRegistry; import com.revolsys.record.io.format.xml.XmlConstants; import com.revolsys.record.io.format.xml.XmlWriter; import com.revolsys.record.io.format.xml.XsiConstants; import com.revolsys.record.property.FieldProperties; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.util.Dates; import com.revolsys.util.number.Doubles; public class EsriGeodatabaseXmlRecordWriter extends AbstractRecordWriter implements EsriGeodatabaseXmlConstants { private int datasetId = 3; private String datasetType; private final EsriGeodatabaseXmlFieldTypeRegistry fieldTypes = EsriGeodatabaseXmlFieldTypeRegistry.INSTANCE; private String geometryType; private int objectId = 1; private boolean opened; private final XmlWriter out; private final RecordDefinition recordDefinition; public EsriGeodatabaseXmlRecordWriter(final RecordDefinition recordDefinition, final Writer out) { this.recordDefinition = recordDefinition; this.out = new XmlWriter(out); } @Override public void close() { if (!this.opened) { writeHeader(null); } writeFooter(); this.out.close(); } @Override public void flush() { this.out.flush(); } @Override public void write(final Record record) { if (!this.opened) { writeHeader(record.getGeometry()); writeWorkspaceDataHeader(); } this.out.startTag(RECORD); this.out.attribute(XsiConstants.TYPE, RECORD_TYPE); this.out.startTag(VALUES); this.out.attribute(XsiConstants.TYPE, VALUES_TYPE); for (final FieldDefinition field : this.recordDefinition.getFields()) { final String fieldName = field.getName(); final Object value; if (isWriteCodeValues()) { value = record.getValue(fieldName); } else { value = record.getCodeValue(fieldName); } final DataType type = field.getDataType(); final EsriGeodatabaseXmlFieldType fieldType = this.fieldTypes.getFieldType(type); if (fieldType != null) { fieldType.writeValue(this.out, value); } } if (this.recordDefinition.getField("OBJECTID") == null) { final EsriGeodatabaseXmlFieldType fieldType = this.fieldTypes.getFieldType(DataTypes.INTEGER); fieldType.writeValue(this.out, this.objectId++); } this.out.endTag(VALUES); this.out.endTag(RECORD); } private void writeDataElement(final RecordDefinition recordDefinition, final Geometry geometry) { final String dataElementType; final FieldDefinition geometryField = recordDefinition.getGeometryField(); boolean hasGeometry = false; DataType geometryDataType = null; if (geometryField != null) { geometryDataType = geometryField.getDataType(); if (this.fieldTypes.getFieldType(geometryDataType) != null) { hasGeometry = true; if (geometryDataType.equals(DataTypes.POINT)) { this.geometryType = GEOMETRY_TYPE_POINT; } else if (geometryDataType.equals(DataTypes.MULTI_POINT)) { this.geometryType = GEOMETRY_TYPE_MULTI_POINT; } else if (geometryDataType.equals(DataTypes.LINE_STRING)) { this.geometryType = GEOMETRY_TYPE_POLYLINE; } else if (geometryDataType.equals(DataTypes.MULTI_LINE_STRING)) { this.geometryType = GEOMETRY_TYPE_POLYLINE; } else if (geometryDataType.equals(DataTypes.POLYGON)) { this.geometryType = GEOMETRY_TYPE_POLYGON; } else { if (geometry instanceof Point) { this.geometryType = GEOMETRY_TYPE_POINT; } else if (geometry instanceof Punctual) { this.geometryType = GEOMETRY_TYPE_MULTI_POINT; } else if (geometry instanceof Lineal) { this.geometryType = GEOMETRY_TYPE_POLYLINE; } else if (geometry instanceof Polygon) { this.geometryType = GEOMETRY_TYPE_POLYGON; } else { hasGeometry = false; } } } } if (hasGeometry) { dataElementType = DATA_ELEMENT_FEATURE_CLASS; this.datasetType = DATASET_TYPE_FEATURE_CLASS; } else { dataElementType = DATA_ELEMENT_TABLE; this.datasetType = DATASET_TYPE_TABLE; } this.out.startTag(DATA_ELEMENT); this.out.attribute(XsiConstants.TYPE, dataElementType); final String path = recordDefinition.getPath(); final String localName = PathUtil.getName(path); this.out.element(CATALOG_PATH, "/FC=" + localName); this.out.element(NAME, localName); this.out.element(METADATA_RETRIEVED, true); this.out.startTag(METADATA); this.out.attribute(XsiConstants.TYPE, XML_PROPERTY_SET_TYPE); this.out.startTag(XML_DOC); this.out.text("<?xml version=\"1.0\"?>"); this.out.text("<metadata xml:lang=\"en\">"); this.out.text("<Esri>"); this.out.text("<MetaID>{"); this.out.text(UUID.randomUUID().toString().toUpperCase()); this.out.text("}</MetaID>"); this.out.text("<CreaDate>"); final Timestamp date = new Timestamp(System.currentTimeMillis()); this.out.text(Dates.format("yyyyMMdd", date)); this.out.text("</CreaDate>"); this.out.text("<CreaTime>"); this.out.text(Dates.format("HHmmssSS", date)); this.out.text("</CreaTime>"); this.out.text("<SyncOnce>TRUE</SyncOnce>"); this.out.text("</Esri>"); this.out.text("</metadata>"); this.out.endTag(XML_DOC); this.out.endTag(METADATA); this.out.element(DATASET_TYPE, this.datasetType); this.out.element(DSID, this.datasetId++); this.out.element(VERSIONED, false); this.out.element(CAN_VERSION, false); this.out.element(HAS_OID, true); this.out.element(OBJECT_ID_FIELD_NAME, "OBJECTID"); writeFields(recordDefinition); this.out.element(CLSID, "{52353152-891A-11D0-BEC6-00805F7C4268}"); this.out.emptyTag(EXTCLSID); this.out.startTag(RELATIONSHIP_CLASS_NAMES); this.out.attribute(XsiConstants.TYPE, NAMES_TYPE); this.out.endTag(RELATIONSHIP_CLASS_NAMES); this.out.element(ALIAS_NAME, localName); this.out.emptyTag(MODEL_NAME); this.out.element(HAS_GLOBAL_ID, false); this.out.emptyTag(GLOBAL_ID_FIELD_NAME); this.out.emptyTag(RASTER_FIELD_NAME); this.out.startTag(EXTENSION_PROPERTIES); this.out.attribute(XsiConstants.TYPE, PROPERTY_SET_TYPE); this.out.startTag(PROPERTY_ARRAY); this.out.attribute(XsiConstants.TYPE, PROPERTY_ARRAY_TYPE); this.out.endTag(PROPERTY_ARRAY); this.out.endTag(EXTENSION_PROPERTIES); this.out.startTag(CONTROLLER_MEMBERSHIPS); this.out.attribute(XsiConstants.TYPE, CONTROLLER_MEMBERSHIPS_TYPE); this.out.endTag(CONTROLLER_MEMBERSHIPS); if (hasGeometry) { this.out.element(FEATURE_TYPE, FEATURE_TYPE_SIMPLE); this.out.element(SHAPE_TYPE, this.geometryType); this.out.element(SHAPE_FIELD_NAME, geometryField.getName()); final GeometryFactory geometryFactory = geometryField .getProperty(FieldProperties.GEOMETRY_FACTORY); this.out.element(HAS_M, false); this.out.element(HAS_Z, geometryFactory.hasZ()); this.out.element(HAS_SPATIAL_INDEX, false); this.out.emptyTag(AREA_FIELD_NAME); this.out.emptyTag(LENGTH_FIELD_NAME); writeExtent(geometryFactory); writeSpatialReference(geometryFactory); } this.out.endTag(DATA_ELEMENT); } public void writeExtent(final GeometryFactory geometryFactory) { if (geometryFactory != null) { final CoordinateSystem coordinateSystem = geometryFactory.getCoordinateSystem(); if (coordinateSystem != null) { final BoundingBox boundingBox = coordinateSystem.getAreaBoundingBox(); this.out.startTag(EXTENT); this.out.attribute(XsiConstants.TYPE, ENVELOPE_N_TYPE); this.out.element(X_MIN, boundingBox.getMinX()); this.out.element(Y_MIN, boundingBox.getMinY()); this.out.element(X_MAX, boundingBox.getMaxX()); this.out.element(Y_MAX, boundingBox.getMaxY()); this.out.element(Z_MIN, boundingBox.getMin(2)); this.out.element(Z_MAX, boundingBox.getMax(2)); writeSpatialReference(geometryFactory); this.out.endTag(EXTENT); } } } private void writeField(final FieldDefinition attribute) { final String fieldName = attribute.getName(); if (fieldName.equals("OBJECTID")) { writeOidField(); } else { final DataType dataType = attribute.getDataType(); final EsriGeodatabaseXmlFieldType fieldType = this.fieldTypes.getFieldType(dataType); if (fieldType == null) { Logs.error(this, "Data type not supported " + dataType); } else { this.out.startTag(FIELD); this.out.attribute(XsiConstants.TYPE, FIELD_TYPE); this.out.element(NAME, fieldName); this.out.element(TYPE, fieldType.getEsriFieldType()); this.out.element(IS_NULLABLE, !attribute.isRequired()); int length = fieldType.getFixedLength(); if (length < 0) { length = attribute.getLength(); } this.out.element(LENGTH, length); final int precision; if (fieldType.isUsePrecision()) { precision = attribute.getLength(); } else { precision = 0; } this.out.element(PRECISION, precision); this.out.element(SCALE, attribute.getScale()); final GeometryFactory geometryFactory = attribute .getProperty(FieldProperties.GEOMETRY_FACTORY); if (geometryFactory != null) { this.out.startTag(GEOMETRY_DEF); this.out.attribute(XsiConstants.TYPE, GEOMETRY_DEF_TYPE); this.out.element(AVG_NUM_POINTS, 0); this.out.element(GEOMETRY_TYPE, this.geometryType); this.out.element(HAS_M, false); this.out.element(HAS_Z, geometryFactory.hasZ()); writeSpatialReference(geometryFactory); this.out.endTag(); } this.out.endTag(FIELD); } } } private void writeFields(final RecordDefinition recordDefinition) { this.out.startTag(FIELDS); this.out.attribute(XsiConstants.TYPE, FIELDS_TYPE); this.out.startTag(FIELD_ARRAY); this.out.attribute(XsiConstants.TYPE, FIELD_ARRAY_TYPE); for (final FieldDefinition attribute : recordDefinition.getFields()) { writeField(attribute); } if (recordDefinition.getField("OBJECTID") == null) { writeOidField(); } this.out.endTag(FIELD_ARRAY); this.out.endTag(FIELDS); } public void writeFooter() { this.out.endDocument(); } private void writeHeader(final Geometry geometry) { this.opened = true; this.out.startDocument("UTF-8", "1.0"); this.out.startTag(WORKSPACE); this.out.setPrefix(XsiConstants.PREFIX, XsiConstants.NAMESPACE_URI); this.out.setPrefix(XmlConstants.XML_SCHEMA_NAMESPACE_PREFIX, XmlConstants.XML_SCHEMA_NAMESPACE_URI); this.out.startTag(WORKSPACE_DEFINITION); this.out.attribute(XsiConstants.TYPE, WORKSPACE_DEFINITION_TYPE); this.out.element(WORKSPACE_TYPE, "esriLocalDatabaseWorkspace"); this.out.element(VERSION, ""); this.out.startTag(DOMAINS); this.out.attribute(XsiConstants.TYPE, DOMAINS_TYPE); this.out.endTag(DOMAINS); this.out.startTag(DATASET_DEFINITIONS); this.out.attribute(XsiConstants.TYPE, DATASET_DEFINITIONS_TYPE); writeDataElement(this.recordDefinition, geometry); this.out.endTag(DATASET_DEFINITIONS); this.out.endTag(WORKSPACE_DEFINITION); } private void writeOidField() { this.out.startTag(FIELD); this.out.attribute(XsiConstants.TYPE, FIELD_TYPE); this.out.element(NAME, "OBJECTID"); this.out.element(TYPE, FIELD_TYPE_OBJECT_ID); this.out.element(IS_NULLABLE, false); this.out.element(LENGTH, 4); this.out.element(PRECISION, 10); this.out.element(SCALE, 0); this.out.element(REQUIRED, true); this.out.element(EDIATBLE, false); this.out.endTag(FIELD); } public void writeSpatialReference(final GeometryFactory geometryFactory) { if (geometryFactory != null) { final CoordinateSystem coordinateSystem = geometryFactory.getCoordinateSystem(); if (coordinateSystem != null) { final CoordinateSystem esriCoordinateSystem = EsriCoordinateSystems .getCoordinateSystem(coordinateSystem); if (esriCoordinateSystem != null) { this.out.startTag(SPATIAL_REFERENCE); if (esriCoordinateSystem instanceof ProjectedCoordinateSystem) { this.out.attribute(XsiConstants.TYPE, PROJECTED_COORDINATE_SYSTEM_TYPE); } else { this.out.attribute(XsiConstants.TYPE, GEOGRAPHIC_COORDINATE_SYSTEM_TYPE); } this.out.element(WKT, EsriCsWktWriter.toWkt(esriCoordinateSystem)); this.out.element(X_ORIGIN, 0); this.out.element(Y_ORIGIN, 0); final double scaleXy = geometryFactory.getScaleXY(); this.out.element(XY_SCALE, (int)scaleXy); this.out.element(Z_ORIGIN, 0); final double scaleZ = geometryFactory.getScaleZ(); this.out.element(Z_SCALE, (int)scaleZ); this.out.element(M_ORIGIN, 0); this.out.element(M_SCALE, 1); this.out.element(XY_TOLERANCE, Doubles.toString(1.0 / scaleXy * 2.0)); this.out.element(Z_TOLERANCE, Doubles.toString(1.0 / scaleZ * 2.0)); this.out.element(M_TOLERANCE, 1); this.out.element(HIGH_PRECISION, true); this.out.element(WKID, coordinateSystem.getCoordinateSystemId()); this.out.endTag(SPATIAL_REFERENCE); } } } } public void writeWorkspaceDataHeader() { this.out.startTag(WORKSPACE_DATA); this.out.attribute(XsiConstants.TYPE, WORKSPACE_DATA_TYPE); this.out.startTag(DATASET_DATA); this.out.attribute(XsiConstants.TYPE, DATASET_DATA_TABLE_DATA); this.out.element(DATASET_NAME, this.recordDefinition.getName()); this.out.element(DATASET_TYPE, this.datasetType); this.out.startTag(DATA); this.out.attribute(XsiConstants.TYPE, DATA_RECORD_SET); writeFields(this.recordDefinition); this.out.startTag(RECORDS); this.out.attribute(XsiConstants.TYPE, RECORDS_TYPE); } }