package org.geotoolkit.data.geojson; import com.fasterxml.jackson.core.JsonEncoding; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.io.WKTReader; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.iso.SimpleInternationalString; import org.geotoolkit.data.*; import org.geotoolkit.data.query.QueryBuilder; import org.geotoolkit.data.session.Session; import org.apache.sis.referencing.CommonCRS; import org.junit.BeforeClass; import org.junit.Test; import org.opengis.parameter.ParameterValueGroup; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import org.apache.sis.feature.FeatureExt; import org.apache.sis.feature.builder.AttributeRole; import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.apache.sis.internal.feature.AttributeConvention; import static org.geotoolkit.data.AbstractFileFeatureStoreFactory.PATH; import static org.geotoolkit.data.geojson.GeoJSONFeatureStoreFactory.PARAMETERS_DESCRIPTOR; import org.geotoolkit.storage.DataStores; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; import org.opengis.feature.Feature; import org.opengis.feature.FeatureAssociationRole; import org.opengis.feature.FeatureType; import org.opengis.filter.Filter; /** * @author Quentin Boileau (Geomatys) */ public class GeoJSONWriteTest extends org.geotoolkit.test.TestBase { private static final GeometryFactory GF = new GeometryFactory(); private static final WKTReader WKT_READER = new WKTReader(); private static final Properties PROPERTIES = new Properties(); @BeforeClass public static void init() { try { PROPERTIES.load(GeoJSONWriteTest.class.getResourceAsStream("/org/geotoolkit/geojson/geometries.properties")); } catch (IOException e) { e.printStackTrace(); } } @Test public void writeSimpleFTTest() throws Exception { final Path pointFile = Files.createTempFile("point", ".json"); final ParameterValueGroup param = PARAMETERS_DESCRIPTOR.createValue(); param.parameter(PATH.getName().getCode()).setValue(pointFile.toUri()); final FeatureStore store = (FeatureStore) DataStores.open(param); assertNotNull(store); assertEquals(0, store.getNames().size()); final String typeName = pointFile.getFileName().toString().replace(".json", ""); //test creating an unvalid feature type final FeatureType unvalidFeatureType = buildGeometryFeatureType("test", Point.class); try { store.createFeatureType(unvalidFeatureType); fail(); } catch (DataStoreException ex) { //normal exception } assertEquals(0, store.getNames().size()); //test writing and reading a feature final FeatureType validFeatureType = buildGeometryFeatureType(typeName, Point.class); store.createFeatureType(validFeatureType); assertEquals(1, store.getNames().size()); assertTrue(Files.exists(pointFile)); final Point expectedPoint = GF.createPoint(new Coordinate(-105.01621, 39.57422)); try (FeatureWriter fw = store.getFeatureWriter(QueryBuilder.filtered(validFeatureType.getName().toString(),Filter.EXCLUDE))) { final Feature feature = fw.next(); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), expectedPoint); feature.setPropertyValue("type","simple"); fw.write(); } assertTrue(Files.exists(pointFile)); final FeatureReader reader = store.getFeatureReader(QueryBuilder.all(validFeatureType.getName())); assertTrue(reader.hasNext()); final Feature f = reader.next(); assertEquals("simple", f.getPropertyValue("type")); assertEquals(expectedPoint, f.getPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString())); reader.close(); Files.deleteIfExists(pointFile); } @Test public void writeAbstractGeometryTest() throws Exception { Path geomsFile = Files.createTempFile("geoms", ".json"); ParameterValueGroup param = PARAMETERS_DESCRIPTOR.createValue(); param.parameter(PATH.getName().getCode()).setValue(geomsFile.toUri()); FeatureStore store = (FeatureStore) DataStores.open(param); assertNotNull(store); assertEquals(0, store.getNames().size()); String typeName = geomsFile.getFileName().toString().replace(".json", ""); FeatureType validFeatureType = buildGeometryFeatureType(typeName, Geometry.class); assertEquals(0, store.getNames().size()); store.createFeatureType(validFeatureType); assertEquals(1, store.getNames().size()); assertTrue(Files.exists(geomsFile)); Point pt = (Point)WKT_READER.read(PROPERTIES.getProperty("point")); MultiPoint mpt = (MultiPoint)WKT_READER.read(PROPERTIES.getProperty("multipoint")); LineString line = (LineString)WKT_READER.read(PROPERTIES.getProperty("linestring")); MultiLineString mline = (MultiLineString)WKT_READER.read(PROPERTIES.getProperty("multilinestring")); Polygon poly = (Polygon)WKT_READER.read(PROPERTIES.getProperty("polygon")); MultiPolygon mpoly = (MultiPolygon)WKT_READER.read(PROPERTIES.getProperty("multipolygon")); GeometryCollection coll = (GeometryCollection)WKT_READER.read(PROPERTIES.getProperty("geometrycollection")); try (FeatureWriter fw = store.getFeatureWriter(QueryBuilder.filtered(validFeatureType.getName().toString(),Filter.EXCLUDE))) { Feature feature = fw.next(); feature.setPropertyValue("type","Point"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), pt); fw.write(); feature = fw.next(); feature.setPropertyValue("type","MultiPoint"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), mpt); fw.write(); feature = fw.next(); feature.setPropertyValue("type","LineString"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), line); fw.write(); feature = fw.next(); feature.setPropertyValue("type","MultiLineString"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), mline); fw.write(); feature = fw.next(); feature.setPropertyValue("type","Polygon"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), poly); fw.write(); feature = fw.next(); feature.setPropertyValue("type","MultiPolygon"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), mpoly); fw.write(); feature = fw.next(); feature.setPropertyValue("type","GeometryCollection"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), coll); fw.write(); } assertTrue(Files.exists(geomsFile)); Session session = store.createSession(false); FeatureCollection fcoll = session.getFeatureCollection(QueryBuilder.all(validFeatureType.getName())); assertEquals(7, fcoll.size()); try (FeatureIterator ite = fcoll.iterator()) { while (ite.hasNext()) { Feature f = ite.next(); //System.out.println(f); Geometry geom = (Geometry)f.getPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString()); if (geom instanceof Point) { assertTrue(pt.equalsExact(geom, 0.0000001)); } else if (geom instanceof MultiPoint) { assertTrue(mpt.equalsExact(geom, 0.0000001)); } else if (geom instanceof LineString) { assertTrue(line.equalsExact(geom, 0.0000001)); } else if (geom instanceof MultiLineString) { assertTrue(mline.equalsExact(geom, 0.0000001)); } else if (geom instanceof Polygon) { assertTrue(poly.equalsExact(geom, 0.0000001)); } else if (geom instanceof MultiPolygon) { assertTrue(mpoly.equalsExact(geom, 0.0000001)); } else if (geom instanceof GeometryCollection) { assertTrue(coll.equalsExact(geom, 0.0000001)); } } } Files.deleteIfExists(geomsFile); } @Test public void writeComplexFeaturesTest() throws Exception { Path complexFile = Files.createTempFile("complex", ".json"); ParameterValueGroup param = PARAMETERS_DESCRIPTOR.createValue(); param.parameter(PATH.getName().getCode()).setValue(complexFile.toUri()); FeatureStore store = (FeatureStore) DataStores.open(param); assertNotNull(store); assertEquals(0, store.getNames().size()); String typeName = complexFile.getFileName().toString().replace(".json", ""); FeatureType complexFT = buildComplexFeatureType(typeName); assertEquals(0, store.getNames().size()); store.createFeatureType(complexFT); assertEquals(1, store.getNames().size()); assertTrue(Files.exists(complexFile)); Point pt = (Point)WKT_READER.read(PROPERTIES.getProperty("point")); Feature expected = null; try (FeatureWriter fw = store.getFeatureWriter(QueryBuilder.filtered(complexFT.getName().toString(),Filter.EXCLUDE))) { Feature feature = fw.next(); feature.setPropertyValue("longProp",100l); feature.setPropertyValue("stringProp","Some String"); feature.setPropertyValue("integerProp",15); feature.setPropertyValue("booleanProp",true); final FeatureType level1Type = ((FeatureAssociationRole)feature.getType().getProperty("level1")).getValueType(); final Feature level11 = level1Type.newInstance(); level11.setPropertyValue("longProp2",66446l); final FeatureType level2Type = ((FeatureAssociationRole)level11.getType().getProperty("level2")).getValueType(); final Feature level211 = level2Type.newInstance(); level211.setPropertyValue("level2prop","text"); final Feature level212 = level2Type.newInstance(); level212.setPropertyValue("level2prop","text2"); final Feature level213 = level2Type.newInstance(); level213.setPropertyValue("level2prop","text3"); level11.setPropertyValue("level2", Arrays.asList(level211,level212,level213)); Feature level12 = level1Type.newInstance(); level12.setPropertyValue("longProp2",4444444l); final Feature level221 = level2Type.newInstance(); level221.setPropertyValue("level2prop","fish"); final Feature level222 = level2Type.newInstance(); level222.setPropertyValue("level2prop","cat"); final Feature level223 = level2Type.newInstance(); level223.setPropertyValue("level2prop","dog"); level12.setPropertyValue("level2", Arrays.asList(level221,level222,level223)); feature.setPropertyValue("level1",Arrays.asList(level11,level12)); feature.setPropertyValue("geometry", pt); expected = FeatureExt.copy(feature); fw.write(); } catch (Exception ex) { ex.printStackTrace(); fail(ex.getMessage()); } assertTrue(Files.exists(complexFile)); Session session = store.createSession(false); FeatureCollection fcoll = session.getFeatureCollection(QueryBuilder.all(complexFT.getName())); assertEquals(1, fcoll.size()); try (FeatureIterator ite = fcoll.iterator()) { while (ite.hasNext()) { Feature candidate = ite.next(); assertEquals(expected, candidate); } } Files.deleteIfExists(complexFile); } @Test public void writeStreamTest() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); FeatureType validFeatureType = buildGeometryFeatureType("simpleFT", Point.class); Point pt = (Point)WKT_READER.read(PROPERTIES.getProperty("point")); try (FeatureWriter fw = new GeoJSONStreamWriter(baos, validFeatureType, 4)) { Feature feature = fw.next(); feature.setPropertyValue("type","feat1"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), pt); fw.write(); feature = fw.next(); feature.setPropertyValue("type","feat2"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), pt); fw.write(); } String outputJSON = baos.toString("UTF-8"); assertNotNull(outputJSON); assertFalse(outputJSON.isEmpty()); String expected = "{\n" + "\"type\":\"FeatureCollection\"\n" + ",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"urn:ogc:def:crs:OGC:1.3:CRS84\"}}\n" + ",\"features\":[\n" + "{\"type\":\"Feature\",\"id\":\"id-0\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-105.0162,39.5742]},\"properties\":{\"type\":\"feat1\"}}\n" + ",{\"type\":\"Feature\",\"id\":\"id-1\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-105.0162,39.5742]},\"properties\":{\"type\":\"feat2\"}}\n" + "]}"; assertEquals(expected, outputJSON); } @Test public void writeStreamSingleFeatureTest() throws Exception { FeatureType validFeatureType = buildGeometryFeatureType("simpleFT", Point.class); Point pt = (Point)WKT_READER.read(PROPERTIES.getProperty("point")); final String outputJSON; try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { Feature feature = validFeatureType.newInstance(); feature.setPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString(), "id-0"); feature.setPropertyValue("type","feat1"); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), pt); GeoJSONStreamWriter.writeSingleFeature(baos, feature, JsonEncoding.UTF8, 4, false); outputJSON = baos.toString("UTF-8"); } assertNotNull(outputJSON); assertFalse(outputJSON.isEmpty()); String expected = "{\"type\":\"Feature\",\"id\":\"id-0\"," + "\"geometry\":{\"type\":\"Point\",\"coordinates\":[-105.0162,39.5742]}," + "\"properties\":{\"type\":\"feat1\"}}"; assertEquals(expected, outputJSON); } @Test public void writeStreamSingleGeometryTest() throws Exception { FeatureType validFeatureType = buildGeometryFeatureType("simpleFT", Point.class); Point pt = (Point)WKT_READER.read(PROPERTIES.getProperty("point")); final String outputJSON; try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { GeoJSONStreamWriter.writeSingleGeometry(baos, pt, JsonEncoding.UTF8, 4, false); outputJSON = baos.toString("UTF-8"); } assertNotNull(outputJSON); assertFalse(outputJSON.isEmpty()); String expected = "{\"type\":\"Point\",\"coordinates\":[-105.0162,39.5742]}"; assertEquals(expected, outputJSON); } @Test public void writePropertyArrayTest() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); FeatureType validFeatureType = buildPropertyArrayFeatureType("arrayFT", Point.class); Point pt = (Point)WKT_READER.read(PROPERTIES.getProperty("point")); double[][] array1 = new double[5][5]; double[][] array2 = new double[5][5]; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { array1[i][j] = i+j; array2[i][j] = i-j; } } try (FeatureWriter fw = new GeoJSONStreamWriter(baos, validFeatureType, 4)) { Feature feature = fw.next(); feature.setPropertyValue("array",array1); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), pt); fw.write(); feature = fw.next(); feature.setPropertyValue("array",array2); feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), pt); fw.write(); } String outputJSON = baos.toString("UTF-8"); assertNotNull(outputJSON); assertFalse(outputJSON.isEmpty()); String expected = "{\n" + "\"type\":\"FeatureCollection\"\n" + ",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"urn:ogc:def:crs:OGC:1.3:CRS84\"}}\n" + ",\"features\":[\n" + "{\"type\":\"Feature\",\"id\":\"id-0\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-105.0162,39.5742]},\"properties\":{\"array\":[[0.0,1.0,2.0,3.0,4.0],[1.0,2.0,3.0,4.0,5.0],[2.0,3.0,4.0,5.0,6.0],[3.0,4.0,5.0,6.0,7.0],[4.0,5.0,6.0,7.0,8.0]]}}\n" + ",{\"type\":\"Feature\",\"id\":\"id-1\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-105.0162,39.5742]},\"properties\":{\"array\":[[0.0,-1.0,-2.0,-3.0,-4.0],[1.0,0.0,-1.0,-2.0,-3.0],[2.0,1.0,0.0,-1.0,-2.0],[3.0,2.0,1.0,0.0,-1.0],[4.0,3.0,2.0,1.0,0.0]]}}\n" + "]}"; assertEquals(expected, outputJSON); } private FeatureType buildGeometryFeatureType(String name, Class geomClass) { final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName(name); ftb.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY); ftb.addAttribute(String.class).setName("type"); ftb.addAttribute(geomClass).setName("geometry").setCRS(CommonCRS.WGS84.normalizedGeographic()).addRole(AttributeRole.DEFAULT_GEOMETRY); return ftb.build(); } private FeatureType buildPropertyArrayFeatureType(String name, Class geomClass) { final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName(name); ftb.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY); ftb.addAttribute(double[][].class).setName("array"); ftb.addAttribute(geomClass).setName("geometry").setCRS(CommonCRS.WGS84.normalizedGeographic()).addRole(AttributeRole.DEFAULT_GEOMETRY); return ftb.build(); } /** * Build 2 level Feature complex */ private FeatureType buildComplexFeatureType(String name) { FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName("level2"); ftb.addAttribute(String.class).setName("level2prop"); final FeatureType level2 = ftb.build(); ftb = new FeatureTypeBuilder(); ftb.setName("level1"); ftb.addAttribute(Long.class).setName("longProp2"); ftb.addAssociation(level2).setName("level2").setMinimumOccurs(1).setMaximumOccurs(5); final FeatureType level1 = ftb.build(); ftb = new FeatureTypeBuilder(); ftb.setName(name); ftb.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY); ftb.addAttribute(Long.class).setName("longProp"); ftb.addAttribute(String.class).setName("stringProp"); ftb.addAttribute(Integer.class).setName("integerProp"); ftb.addAttribute(Boolean.class).setName("booleanProp"); ftb.addAssociation(level1).setName("level1").setMinimumOccurs(1).setMaximumOccurs(3); ftb.addAttribute(Point.class).setName("geometry").setCRS(CommonCRS.WGS84.normalizedGeographic()).addRole(AttributeRole.DEFAULT_GEOMETRY); ftb.setDescription(new SimpleInternationalString("Description")); return ftb.build(); } }