package com.revolsys.record.io.test; import java.io.File; import java.util.Arrays; import java.util.List; import org.junit.Assert; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import com.revolsys.datatype.DataType; import com.revolsys.datatype.DataTypes; import com.revolsys.geometry.io.GeometryReader; import com.revolsys.geometry.model.ClockDirection; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.Polygonal; import com.revolsys.geometry.test.model.GeometryTestUtil; import com.revolsys.geometry.test.model.TestUtil; import com.revolsys.io.IoConstants; import com.revolsys.io.IoFactory; import com.revolsys.io.PathName; import com.revolsys.io.Writer; import com.revolsys.junit.RunnableTestCase; import com.revolsys.logging.Logs; import com.revolsys.record.ArrayRecord; import com.revolsys.record.Record; import com.revolsys.record.io.GeometryRecordReader; import com.revolsys.record.io.RecordReader; import com.revolsys.record.io.RecordReaderFactory; import com.revolsys.record.io.RecordWriter; import com.revolsys.record.io.RecordWriterFactory; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.record.schema.RecordDefinitionImpl; import com.revolsys.spring.resource.FileSystemResource; import junit.framework.TestCase; import junit.framework.TestSuite; @RunWith(Suite.class) @SuiteClasses({ GeoJsonIoTest.class, GmlIoTest.class, KmlIoTest.class, ShapefileIoTest.class, WktlIoTest.class, XBaseIoTest.class }) public class RecordIoTestSuite { public static void addGeometryTestSuites(final TestSuite suite, final String namePrefix, final GeometryTestFunction<GeometryFactory, Geometry, DataType> testFunction) { final List<DataType> geometryDataTypes = Arrays.asList(DataTypes.POINT, DataTypes.LINE_STRING, DataTypes.POLYGON, DataTypes.MULTI_POINT, DataTypes.MULTI_LINE_STRING, DataTypes.MULTI_POLYGON); for (final DataType dataType : geometryDataTypes) { final TestSuite dataTypeSuite = new TestSuite(namePrefix + " " + dataType.toString()); suite.addTest(dataTypeSuite); for (int axisCount = 2; axisCount < 5; axisCount++) { int maxGeometryCount = 1; if (dataType.toString().startsWith("Multi")) { maxGeometryCount = 3; } for (int geometryCount = 0; geometryCount <= maxGeometryCount; geometryCount++) { int maxVertexCount = 2; if (dataType.toString().contains("Line")) { maxVertexCount = 4; } for (int vertexCount = 2; vertexCount <= maxVertexCount; vertexCount++) { int maxRingCount = 0; if (dataType.toString().contains("Polygon")) { maxRingCount = 3; } if (geometryCount == 0) { maxRingCount = 0; } for (int ringCount = 0; ringCount <= maxRingCount; ringCount++) { final GeometryFactory geometryFactory = GeometryFactory.floating(4326, axisCount); double delta = 1.0; if (geometryFactory.isProjected()) { delta = 1000.0; } final Geometry geometry = GeometryTestUtil.geometry(geometryFactory, dataType, geometryCount, ringCount, vertexCount, delta); String name = namePrefix + " " + dataType + " A=" + axisCount + " G=" + geometryCount; if (maxVertexCount > 2) { name += " V=" + vertexCount; } if (maxRingCount > 2) { name += " R=" + ringCount; } final TestCase testCase = new RunnableTestCase(name, () -> testFunction.apply(geometryFactory, geometry, dataType)); dataTypeSuite.addTest(testCase); } } } } } } public static void addWriteReadTest(final TestSuite suite, final String prefix, final String fileExtension) { addGeometryTestSuites(suite, prefix, (geometryFactory, geometry, geometryDataType) -> { final String geometryTypeString = geometryDataType.toString(); final File directory = new File("/tmp/revolsystest/io/" + fileExtension); final File file = new File(directory, geometryTypeString + "_" + geometryFactory.getAxisCount() + "_" + geometry.getVertexCount() + "." + fileExtension); file.delete(); file.getParentFile().mkdirs(); final FileSystemResource resource = new FileSystemResource(file); final RecordWriterFactory recordWriterFactory = IoFactory.factory(RecordWriterFactory.class, resource); final RecordDefinitionImpl recordDefinition = new RecordDefinitionImpl( PathName.newPathName(geometryTypeString)); if (recordWriterFactory.isCustomFieldsSupported()) { recordDefinition.addField("BOOLEAN", DataTypes.BOOLEAN, true); recordDefinition.addField("BYTE", DataTypes.BYTE, true); recordDefinition.addField("SHORT", DataTypes.SHORT, true); recordDefinition.addField("INT", DataTypes.INT, true); recordDefinition.addField("LONG", DataTypes.LONG, true); recordDefinition.addField("FLOAT", DataTypes.FLOAT, 4, 3, true); recordDefinition.addField("DOUBLE", DataTypes.DOUBLE, 10, 9, true); recordDefinition.addField("STRING", DataTypes.STRING, true); } if (recordWriterFactory.isGeometrySupported()) { recordDefinition.addField("GEOMETRY", geometryDataType, true); } recordDefinition.setGeometryFactory(geometryFactory); final ArrayRecord record = new ArrayRecord(recordDefinition); record.setValue("BOOLEAN", true); record.setValue("BYTE", Byte.MAX_VALUE); record.setValue("SHORT", Short.MAX_VALUE); record.setValue("INT", Integer.MAX_VALUE); record.setValue("LONG", 999999999999999999L); record.setValue("FLOAT", 6.789); record.setValue("DOUBLE", 1.234567890); record.setValue("STRING", "test"); record.setGeometryValue(geometry); doRecordWriteTest(resource, record); doRecordReadTest(resource, record); doGeometryReadTest(resource, record); }); } private static void assertGeometry(final ClockDirection polygonRingDirection, final Geometry expectedGeometry, final Geometry actualGeometry) { if (expectedGeometry instanceof Polygonal) { Polygonal expectedPolygonal = (Polygonal)expectedGeometry; expectedPolygonal = expectedPolygonal.toClockDirection(polygonRingDirection); assertGeometry(expectedPolygonal, actualGeometry); } else { assertGeometry(expectedGeometry, actualGeometry); } } private static void assertGeometry(final Geometry expectedGeometry, final Geometry actualGeometry) { Assert.assertEquals("Empty", expectedGeometry.isEmpty(), actualGeometry.isEmpty()); if (!expectedGeometry.isEmpty()) { final GeometryFactory expectedGeometryFactory = expectedGeometry.getGeometryFactory(); final GeometryFactory actualGeometryFactory = actualGeometry.getGeometryFactory(); Assert.assertEquals("Geometry Factory", expectedGeometryFactory, actualGeometryFactory); final int axisCount = expectedGeometry.getAxisCount(); Assert.assertEquals("Axis Count", axisCount, actualGeometry.getAxisCount()); if (!actualGeometry.equals(axisCount, expectedGeometry)) { // Allow for conversion of multi part to single part if (expectedGeometry.getGeometryCount() != 1 || !actualGeometry.equals(axisCount, expectedGeometry.getGeometry(0))) { TestUtil.failNotEquals("Geometry Equal Exact", expectedGeometry, actualGeometry); } } } } private static void doGeometryReadTest(final FileSystemResource resource, final ArrayRecord record) { if (GeometryReader.isReadable(resource)) { try ( GeometryReader geometryReader = GeometryReader.newGeometryReader(resource)) { final ClockDirection polygonRingDirection = geometryReader.getPolygonRingDirection(); final List<Geometry> geometries = geometryReader.toList(); Assert.assertEquals("Geometry Count", 1, geometries.size()); final Geometry expectedGeometry = record.getGeometry(); final Geometry actualGeometry = geometries.get(0); assertGeometry(polygonRingDirection, expectedGeometry, actualGeometry); } } else { Logs.debug(RecordIoTestSuite.class, "Reading geometry not supported for: " + resource.getFileNameExtension()); } } private static void doRecordReadTest(final FileSystemResource resource, final ArrayRecord record) { if (RecordReader.isReadable(resource)) { final RecordReaderFactory recordReaderFactory = IoFactory.factory(RecordReaderFactory.class, resource); try ( RecordReader recordReader = RecordReader.newRecordReader(resource)) { final ClockDirection polygonRingDirection = recordReader.getPolygonRingDirection(); final List<Record> records = recordReader.toList(); Assert.assertEquals("Record Count", 1, records.size()); final Record actualRecord = records.get(0); if (recordReaderFactory.isCustomFieldsSupported() && !(recordReader instanceof GeometryRecordReader)) { for (final String fieldName : record.getRecordDefinition().getFieldNames()) { if (!fieldName.equals("GEOMETRY")) { final Object expectedValue = record.getValue(fieldName); final Object actualValue = actualRecord.getValue(fieldName); final boolean equals = DataType.equal(expectedValue, actualValue); com.revolsys.geometry.util.Assert.equals(fieldName, equals, expectedValue, actualValue); } } } if (recordReaderFactory.isGeometrySupported()) { final Geometry expectedGeometry = record.getGeometry(); final Geometry actualGeometry = actualRecord.getGeometry(); assertGeometry(polygonRingDirection, expectedGeometry, actualGeometry); } } } else { Logs.debug(RecordIoTestSuite.class, "Reading geometry not supported for: " + resource.getFileNameExtension()); } } private static void doRecordWriteTest(final FileSystemResource resource, final Record record) { final RecordDefinition recordDefinition = record.getRecordDefinition(); final GeometryFactory geometryFactory = recordDefinition.getGeometryFactory(); try ( Writer<Record> writer = RecordWriter.newRecordWriter(record, resource)) { writer.setProperty(IoConstants.GEOMETRY_FACTORY, geometryFactory); final FieldDefinition geometryField = recordDefinition.getGeometryField(); if (geometryField != null) { final DataType geometryDataType = geometryField.getDataType(); writer.setProperty(IoConstants.GEOMETRY_TYPE, geometryDataType); } writer.write(record); } } }