/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2010, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.geojson; import java.io.ByteArrayOutputStream; import java.io.StringWriter; import java.util.Iterator; import java.util.List; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geojson.feature.FeatureJSON; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.io.WKTReader; public class FeatureJSONTest extends GeoJSONTestSupport { FeatureJSON fjson = new FeatureJSON(); SimpleFeatureType featureType; SimpleFeatureBuilder fb; @Override protected void setUp() throws Exception { super.setUp(); SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.setName("feature"); tb.setSRS("EPSG:4326"); tb.add("int", Integer.class); tb.add("double", Double.class); tb.add("string", String.class); tb.add("geometry", Geometry.class); featureType = tb.buildFeatureType(); fb = new SimpleFeatureBuilder(featureType); } public void testFeatureWrite() throws Exception { StringWriter writer = new StringWriter(); fjson.writeFeature(feature(1), writer); assertEquals(strip(featureText(1)), writer.toString()); } public void testWriteReadNoProperties() throws Exception { SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.add("geom", Point.class, CRS.decode("EPSG:4326")); tb.add("name", String.class); tb.add("quantity", Integer.class); tb.setName("outbreak"); SimpleFeatureType schema = tb.buildFeatureType(); SimpleFeatureBuilder fb = new SimpleFeatureBuilder(schema); fb.add(new WKTReader().read("POINT(10 20)")); SimpleFeature feature = fb.buildFeature("outbreak.1"); FeatureJSON fj = new FeatureJSON(); ByteArrayOutputStream os = new ByteArrayOutputStream(); fj.writeFeature(feature, os); String json = os.toString(); // here it would break because the written json was incorrect SimpleFeature feature2 = fj.readFeature(json); assertEquals(feature.getID(), feature2.getID()); } public void testFeatureRead() throws Exception { SimpleFeature f1 = feature(1); SimpleFeature f2 = fjson.readFeature(reader(strip(featureText(1)))); assertEqualsLax(f1, f2); } public void testFeatureWithRegularGeometryAttributeRead() throws Exception { SimpleFeature f = fjson.readFeature(reader(strip("{" + " 'type': 'Feature'," + " 'geometry': {" + " 'type': 'Point'," + " 'coordinates': [0.1, 0.1]," + " }," + " 'properties': {" + " 'int': 1," + " 'double': 0.1," + " 'string': 'one'," + " 'otherGeometry': {" + " 'type': 'LineString'," + " 'coordinates': [[1.1, 1.2], [1.3, 1.4]]" + " }"+ " }," + " 'id': 'feature.0'" + " }"))); assertNotNull(f); assertTrue(f.getDefaultGeometry() instanceof Point); Point p = (Point) f.getDefaultGeometry(); assertEquals(0.1, p.getX(), 0.1); assertEquals(0.1, p.getY(), 0.1); assertTrue(f.getAttribute("otherGeometry") instanceof LineString); assertTrue(new GeometryFactory().createLineString(new Coordinate[]{ new Coordinate(1.1, 1.2), new Coordinate(1.3, 1.4)}).equals((LineString)f.getAttribute("otherGeometry"))); assertEquals(1, ((Number)f.getAttribute("int")).intValue()); assertEquals(0.1, ((Number)f.getAttribute("double")).doubleValue()); assertEquals("one", f.getAttribute("string")); } public void testFeatureWithRegularGeometryAttributeNoDefaultGeometryRead() throws Exception { SimpleFeature f = fjson.readFeature(reader(strip("{" + " 'type': 'Feature'," + " 'properties': {" + " 'int': 1," + " 'double': 0.1," + " 'string': 'one'," + " 'otherGeometry': {" + " 'type': 'LineString'," + " 'coordinates': [[1.1, 1.2], [1.3, 1.4]]" + " }"+ " }," + " 'id': 'feature.0'" + " }"))); assertNotNull(f); assertTrue(f.getDefaultGeometry() instanceof LineString); LineString l = (LineString) f.getDefaultGeometry(); assertTrue(new GeometryFactory().createLineString(new Coordinate[]{ new Coordinate(1.1, 1.2), new Coordinate(1.3, 1.4)}).equals(l)); assertTrue(f.getAttribute("otherGeometry") instanceof LineString); assertTrue(new GeometryFactory().createLineString(new Coordinate[]{ new Coordinate(1.1, 1.2), new Coordinate(1.3, 1.4)}).equals((LineString)f.getAttribute("otherGeometry"))); assertEquals(1, ((Number)f.getAttribute("int")).intValue()); assertEquals(0.1, ((Number)f.getAttribute("double")).doubleValue()); assertEquals("one", f.getAttribute("string")); } public void testFeatureWithBoundsWrite() throws Exception { String json = "{" + " 'type': 'Feature'," + " 'bbox': [1.1, 1.1, 1.1, 1.1], " + " 'geometry': {" + " 'type': 'Point'," + " 'coordinates': [1.1, 1.1]" + " }," + " 'properties': {" + " 'int': 1," + " 'double': 1.1," + " 'string': 'one'" + " }," + " 'id': 'feature.1'" + " }"; fjson.setEncodeFeatureBounds(true); assertEquals(strip(json), fjson.toString(feature(1))); } public void testFeatureWithCRSWrite() throws Exception { fjson.setEncodeFeatureCRS(true); assertEquals(strip(featureWithCRSText()), fjson.toString(feature(1))); } public void testFeatureNoGeometryWrite() throws Exception { String json = "{" + " 'type': 'Feature'," + " 'properties': {" + " 'foo': 'FOO'" + " }," + " 'id': 'feature.foo'" + " }"; SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.setName("nogeom"); tb.add("foo", String.class); SimpleFeatureType ft = tb.buildFeatureType(); SimpleFeatureBuilder b = new SimpleFeatureBuilder(ft); b.add("FOO"); SimpleFeature f = b.buildFeature("feature.foo"); assertEquals(strip(json), fjson.toString(f)); } String featureWithCRSText() { String json = "{" + " 'type': 'Feature'," + " 'crs': {" + " 'type': 'name'," + " 'properties': {" + " 'name': 'EPSG:4326'" + " }" + " }, " + " 'geometry': {" + " 'type': 'Point'," + " 'coordinates': [1.1, 1.1]" + " }," + " 'properties': {" + " 'int': 1," + " 'double': 1.1," + " 'string': 'one'" + " }," + " 'id': 'feature.1'" + " }"; return json; } public void testFeatureWithCRSRead() throws Exception { SimpleFeature f = fjson.readFeature(reader(strip(featureWithCRSText()))); assertTrue(CRS.equalsIgnoreMetadata(CRS.decode("EPSG:4326"), f.getFeatureType().getCoordinateReferenceSystem())); } String featureWithBBOXText() { String json = "{" + " 'type': 'Feature'," + " 'bbox': [1.1, 1.1, 1.1, 1.1]," + " 'geometry': {" + " 'type': 'Point'," + " 'coordinates': [1.1, 1.1]" + " }," + " 'properties': {" + " 'int': 1," + " 'double': 1.1," + " 'string': 'one'" + " }," + " 'id': 'feature.1'" + " }"; return json; } public void testFeatureWithBBOXRead() throws Exception { SimpleFeature f = fjson.readFeature(reader(strip(featureWithBBOXText()))); assertEquals(1.1, f.getBounds().getMinX(), 0.1d); assertEquals(1.1, f.getBounds().getMaxX(), 0.1d); assertEquals(1.1, f.getBounds().getMinY(), 0.1d); assertEquals(1.1, f.getBounds().getMaxY(), 0.1d); } String featureWithBoundedByAttributeText() { String json = "{" + " 'type': 'Feature'," + " 'geometry': {" + " 'type': 'Point'," + " 'coordinates': [1.1, 1.1]" + " }," + " 'properties': {" + " 'boundedBy': [-1.2, -1.3, 1.2, 1.3]," + " 'int': 1," + " 'double': 1.1," + " 'string': 'one'" + " }," + " 'id': 'feature.1'" + " }"; return json; } SimpleFeature featureWithBoundedByAttribute() { SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.setName("feature"); tb.add("geometry", Point.class); tb.add("boundedBy", Envelope.class); tb.add("int", Integer.class); tb.add("double", Double.class); tb.add("string", String.class); SimpleFeatureBuilder b = new SimpleFeatureBuilder(tb.buildFeatureType()); b.add(new GeometryFactory().createPoint(new Coordinate(1.1,1.1))); b.add(new Envelope(-1.2, 1.2, -1.3, 1.3)); b.add(1); b.add(1.1); b.add("one"); return b.buildFeature("feature.1"); } public void testFeatureWithBoundedByAttributeRead() throws Exception { SimpleFeature f = fjson.readFeature(reader(strip(featureWithBoundedByAttributeText()))); List l = (List) f.getAttribute("boundedBy"); assertEquals(-1.2, (Double) l.get(0), 0.1d); assertEquals(-1.3, (Double) l.get(1), 0.1d); assertEquals(1.2, (Double) l.get(2), 0.1d); assertEquals(1.3, (Double) l.get(3), 0.1d); } public void testFeatureWithBoundedByAttributeWrite() throws Exception { StringWriter writer = new StringWriter(); fjson.writeFeature(featureWithBoundedByAttribute(), writer); assertEquals(strip(featureWithBoundedByAttributeText()), writer.toString()); } public void testFeatureCollectionWrite() throws Exception { StringWriter writer = new StringWriter(); fjson.writeFeatureCollection(collection(), writer); assertEquals(strip(collectionText()), writer.toString()); } public void testFeatureCollectionRead() throws Exception { FeatureCollection actual = fjson.readFeatureCollection(reader(strip(collectionText()))); assertNotNull(actual); FeatureCollection expected = collection(); assertEquals(expected.size(), actual.size()); Iterator a = actual.iterator(); Iterator e = expected.iterator(); while(e.hasNext()) { assertTrue(a.hasNext()); assertEqualsLax((SimpleFeature)e.next(), (SimpleFeature) a.next()); } actual.close(a); expected.close(e); } public void testFeatureCollectionStreamBasic() throws Exception { testFeatureCollectionStream(false, false); } public void testFeatureCollectionStreamFull() throws Exception { testFeatureCollectionStream(true, true); } void testFeatureCollectionStream(boolean withBounds, boolean withCRS) throws Exception { FeatureIterator<SimpleFeature> features = fjson.streamFeatureCollection(reader(strip(collectionText(withBounds, withCRS)))); FeatureCollection expected = collection(); Iterator e = expected.iterator(); while(e.hasNext()) { features.hasNext(); //ensure that hasNext() does not skip features assertTrue(features.hasNext()); assertEqualsLax((SimpleFeature)e.next(), features.next()); } features.close(); expected.close(e); } public void testFeatureCollectionWithBoundsWrite() throws Exception { fjson.setEncodeFeatureCollectionBounds(true); assertEquals(strip(collectionText(true, false)), fjson.toString(collection())); } public void testFeatureCollectionWithCRSWrite() throws Exception { fjson.setEncodeFeatureCollectionCRS(true); assertEquals(strip(collectionText(false, true)), fjson.toString(collection())); } public void testCRSWrite() throws Exception { CoordinateReferenceSystem crs = CRS.decode("EPSG:4326"); StringWriter writer = new StringWriter(); fjson.writeCRS(crs, writer); assertEquals(strip(crsText()), writer.toString()); } public void testCRSRead() throws Exception { Object crs = fjson.readCRS(reader(strip(crsText()))); assertTrue(CRS.equalsIgnoreMetadata(CRS.decode("epsg:4326"), crs)); } String crsText() { return "{" + " 'type': 'name',"+ " 'properties': {"+ " 'name': 'EPSG:4326'"+ " }"+ "}"; } SimpleFeature feature(int val) { fb.add(val); fb.add(val + 0.1); fb.add(toString(val)); fb.add(new GeometryFactory().createPoint(new Coordinate(val+0.1,val+0.1))); return fb.buildFeature("feature." + val); } String featureText(int val) { String text = "{" + " 'type': 'Feature'," + " 'geometry': {" + " 'type': 'Point'," + " 'coordinates': [" + (val+0.1) + "," + (val+0.1) + "]" + " }, " + "' properties': {" + " 'int': " + val + "," + " 'double': " + (val + 0.1) + "," + " 'string': '" + toString(val) + "'" + " }," + " 'id':'feature." + val + "'" + "}"; return text; } FeatureCollection collection() { DefaultFeatureCollection collection = new DefaultFeatureCollection(null, featureType); for (int i = 0; i < 3; i++) { collection.add(feature(i)); } return collection; } String collectionText() { return collectionText(false,false); } String collectionText(boolean withBounds, boolean withCRS) { StringBuffer sb = new StringBuffer(); sb.append("{'type':'FeatureCollection',"); if (withBounds) { FeatureCollection features = collection(); ReferencedEnvelope bbox = features.getBounds(); sb.append("'bbox': ["); sb.append(bbox.getMinX()).append(",").append(bbox.getMinY()).append(",") .append(bbox.getMaxX()).append(",").append(bbox.getMaxY()); sb.append("],"); } if (withCRS) { sb.append("'crs': {"); sb.append(" 'type': 'name',"); sb.append(" 'properties': {"); sb.append(" 'name': 'EPSG:4326'"); sb.append(" }"); sb.append("},"); } sb.append("'features':["); for (int i = 0; i < 3; i++) { sb.append(featureText(i)).append(","); } sb.setLength(sb.length()-1); sb.append("]}"); return sb.toString(); } }