package com.revolsys.record.io.format.geojson; import java.io.BufferedWriter; import java.io.Writer; 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.Lineal; import com.revolsys.geometry.model.LinearRing; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygon; import com.revolsys.geometry.model.Polygonal; import com.revolsys.geometry.model.Punctual; import com.revolsys.io.AbstractRecordWriter; import com.revolsys.io.IoConstants; import com.revolsys.math.Angle; import com.revolsys.record.Record; import com.revolsys.record.io.format.cogojson.CogoJson; import com.revolsys.record.io.format.json.JsonWriter; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.util.MathUtil; public class GeoJsonRecordWriter extends AbstractRecordWriter { private final boolean cogo; boolean initialized = false; /** The writer */ private JsonWriter out; private boolean singleObject; private int srid = -1; private RecordDefinition recordDefinition; public GeoJsonRecordWriter(final Writer out, final boolean cogo) { this.out = new JsonWriter(new BufferedWriter(out)); this.out.setIndent(true); this.cogo = cogo; } public GeoJsonRecordWriter(final Writer out, final RecordDefinition recordDefinition) { this(out, false); this.recordDefinition = recordDefinition; } /** * Closes the underlying reader. */ @Override public void close() { if (this.out != null) { try { writeFooter(); } finally { this.out.close(); this.out = null; } } } private void coordinatePoint(final Point coordinates) { this.out.print('['); for (int axisIndex = 0; axisIndex < coordinates.getAxisCount(); axisIndex++) { if (axisIndex > 0) { this.out.print(','); } final double value = coordinates.getCoordinate(axisIndex); this.out.value(value); } this.out.print(']'); } private void coordinatesLineString(final LineString line) { this.out.startList(false); this.out.indent(); for (int i = 0; i < line.getVertexCount(); i++) { if (i > 0) { this.out.endAttribute(); this.out.indent(); } double x = line.getX(i); double y = line.getY(i); if (this.cogo && i > 0) { final double currentX = x; final double previousX = line.getX(i - 1); final double previousY = line.getY(i - 1); x = MathUtil.distance(previousX, previousY, currentX, y); y = Angle.angleNorthDegrees(previousX, previousY, currentX, y); } this.out.print('['); this.out.value(x); this.out.print(','); this.out.value(y); for (int axisIndex = 2; axisIndex < line.getAxisCount(); axisIndex++) { this.out.print(','); final double value = line.getCoordinate(i, axisIndex); this.out.value(value); } this.out.print(']'); } this.out.endList(); } private void coordinatesPoint(final Point point) { coordinatePoint(point); } private void coordinatesPolygon(final Polygon polygon) { this.out.startList(false); this.out.indent(); final LineString shell = polygon.getShell(); coordinatesLineString(shell.toCounterClockwise()); for (final LinearRing hole : polygon.holes()) { this.out.endAttribute(); this.out.indent(); coordinatesLineString(hole.toClockwise()); } this.out.endList(); } @Override public void flush() { this.out.flush(); } private void geometry(final Geometry geometry) { this.out.startObject(); if (geometry instanceof Point) { final Point point = (Point)geometry; point(point); } else if (geometry instanceof LineString) { final LineString line = (LineString)geometry; line(line); } else if (geometry instanceof Polygon) { final Polygon polygon = (Polygon)geometry; polygon(polygon); } else if (geometry instanceof Punctual) { final Punctual punctual = (Punctual)geometry; multiPoint(punctual); } else if (geometry instanceof Lineal) { final Lineal lineal = (Lineal)geometry; multiLineString(lineal); } else if (geometry instanceof Polygonal) { final Polygonal polygonal = (Polygonal)geometry; multiPolygon(polygonal); } else if (geometry.isGeometryCollection()) { geometryCollection(geometry); } this.out.endObject(); } private void geometryCollection(final Geometry geometryCollection) { type(GeoJson.GEOMETRY_COLLECTION); this.out.endAttribute(); this.out.label(GeoJson.GEOMETRIES); this.out.startList(); final int numGeometries = geometryCollection.getGeometryCount(); if (numGeometries > 0) { geometry(geometryCollection.getGeometry(0)); for (int i = 1; i < numGeometries; i++) { final Geometry geometry = geometryCollection.getGeometry(i); this.out.endAttribute(); geometry(geometry); } } this.out.endList(); } @Override public ClockDirection getPolygonRingDirection() { return ClockDirection.COUNTER_CLOCKWISE; } @Override public RecordDefinition getRecordDefinition() { return this.recordDefinition; } public boolean isCogo() { return this.cogo; } private void line(final LineString line) { if (this.cogo) { type(CogoJson.COGO_LINE_STRING); } else { type(GeoJson.LINE_STRING); } this.out.endAttribute(); this.out.label(GeoJson.COORDINATES); if (line.isEmpty()) { this.out.startList(); this.out.endList(); } else { coordinatesLineString(line); } } private void multiLineString(final Lineal lineal) { if (this.cogo) { type(CogoJson.COGO_LINE_STRING); } else { type(GeoJson.MULTI_LINE_STRING); } this.out.endAttribute(); this.out.label(GeoJson.COORDINATES); this.out.startList(); this.out.indent(); final int numGeometries = lineal.getGeometryCount(); if (numGeometries > 0) { coordinatesLineString((LineString)lineal.getGeometry(0)); for (int i = 1; i < numGeometries; i++) { final LineString lineString = (LineString)lineal.getGeometry(i); this.out.endAttribute(); this.out.indent(); coordinatesLineString(lineString); } } this.out.endList(); } private void multiPoint(final Punctual punctual) { type(GeoJson.MULTI_POINT); this.out.endAttribute(); this.out.label(GeoJson.COORDINATES); this.out.startList(); this.out.indent(); final int numGeometries = punctual.getGeometryCount(); if (numGeometries > 0) { coordinatesPoint(punctual.getPoint(0)); for (int i = 1; i < numGeometries; i++) { final Point point = punctual.getPoint(i); this.out.endAttribute(); this.out.indent(); coordinatesPoint(point); } } this.out.endList(); } private void multiPolygon(final Polygonal polygonal) { if (this.cogo) { type(CogoJson.COGO_MULTI_POLYGON); } else { type(GeoJson.MULTI_POLYGON); } this.out.endAttribute(); this.out.label(GeoJson.COORDINATES); this.out.startList(); this.out.indent(); final int numGeometries = polygonal.getGeometryCount(); if (numGeometries > 0) { coordinatesPolygon((Polygon)polygonal.getGeometry(0)); for (int i = 1; i < numGeometries; i++) { final Polygon polygon = (Polygon)polygonal.getGeometry(i); this.out.endAttribute(); this.out.indent(); coordinatesPolygon(polygon); } } this.out.endList(); } private void point(final Point point) { type(GeoJson.POINT); this.out.endAttribute(); this.out.label(GeoJson.COORDINATES); if (point.isEmpty()) { this.out.startList(); this.out.endList(); } else { coordinatesPoint(point); } } private void polygon(final Polygon polygon) { if (this.cogo) { type(CogoJson.COGO_POLYGON); } else { type(GeoJson.POLYGON); } this.out.endAttribute(); this.out.label(GeoJson.COORDINATES); if (polygon.isEmpty()) { this.out.startList(); this.out.endList(); } else { coordinatesPolygon(polygon); } } private void srid(final int srid) { final String urn = GeoJson.URN_OGC_DEF_CRS_EPSG + srid; this.out.label(GeoJson.CRS); this.out.startObject(); type(GeoJson.NAME); this.out.endAttribute(); this.out.label(GeoJson.PROPERTIES); this.out.startObject(); this.out.label(GeoJson.NAME); this.out.value(urn); this.out.endObject(); this.out.endObject(); } private void type(final String type) { this.out.label(GeoJson.TYPE); this.out.value(type); } @Override public void write(final Record record) { if (this.initialized) { this.out.endAttribute(); } else { writeHeader(); this.initialized = true; } this.out.startObject(); type(GeoJson.FEATURE); Geometry mainGeometry = record.getGeometry(); final GeometryFactory geometryFactory = getProperty(IoConstants.GEOMETRY_FACTORY); if (geometryFactory != null) { mainGeometry = mainGeometry.convertGeometry(geometryFactory); } writeSrid(mainGeometry); final RecordDefinition recordDefinition = record.getRecordDefinition(); final int geometryIndex = recordDefinition.getGeometryFieldIndex(); boolean geometryWritten = false; this.out.endAttribute(); this.out.label(GeoJson.GEOMETRY); if (mainGeometry != null) { geometryWritten = true; geometry(mainGeometry); } if (!geometryWritten) { this.out.value(null); } this.out.endAttribute(); this.out.label(GeoJson.PROPERTIES); this.out.startObject(); boolean hasValue = false; for (final FieldDefinition field : recordDefinition.getFields()) { final int fieldIndex = field.getIndex(); if (fieldIndex != geometryIndex) { final Object value; if (isWriteCodeValues()) { value = record.getCodeValue(fieldIndex); } else { value = record.getValue(fieldIndex); } if (isValueWritable(value)) { if (hasValue) { this.out.endAttribute(); } else { hasValue = true; } final String name = field.getName(); this.out.label(name); if (value instanceof Geometry) { final Geometry geometry = (Geometry)value; geometry(geometry); } else { this.out.value(value); } } } } this.out.endObject(); this.out.endObject(); } private void writeFooter() { if (!this.singleObject) { this.out.endList(); this.out.endObject(); } final String callback = getProperty(IoConstants.JSONP_PROPERTY); if (callback != null) { this.out.print(");"); } } private void writeHeader() { this.out.setIndent(isIndent()); final String callback = getProperty(IoConstants.JSONP_PROPERTY); if (callback != null) { this.out.print(callback); this.out.print('('); } this.singleObject = Boolean.TRUE.equals(getProperty(IoConstants.SINGLE_OBJECT_PROPERTY)); if (!this.singleObject) { this.out.startObject(); type(GeoJson.FEATURE_COLLECTION); this.srid = writeSrid(); this.out.endAttribute(); this.out.label(GeoJson.FEATURES); this.out.startList(); } } private int writeSrid() { final GeometryFactory geometryFactory = getProperty(IoConstants.GEOMETRY_FACTORY); return writeSrid(geometryFactory); } private void writeSrid(final Geometry geometry) { if (geometry != null) { final GeometryFactory geometryFactory = geometry.getGeometryFactory(); writeSrid(geometryFactory); } } protected int writeSrid(final GeometryFactory geometryFactory) { if (geometryFactory != null) { final int srid = geometryFactory.getCoordinateSystemId(); if (srid != 0 && srid != this.srid) { this.out.endAttribute(); srid(srid); return srid; } } return -1; } }