/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2012, Geomatys * * 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; either * version 2.1 of the License, or (at your option) any later version. * * 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.geotoolkit.wps.converters; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Polygon; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; import org.apache.sis.feature.FeatureExt; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.coverage.grid.GridCoverageBuilder; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.referencing.CommonCRS; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.ArgumentChecks; import org.geotoolkit.data.FeatureCollection; import org.geotoolkit.data.FeatureIterator; import org.geotoolkit.data.geojson.binding.GeoJSONFeature; import org.geotoolkit.data.geojson.binding.GeoJSONGeometry; import org.geotoolkit.data.geojson.binding.GeoJSONObject; import org.geotoolkit.data.geojson.utils.GeoJSONParser; import org.geotoolkit.nio.IOUtilities; import static org.geotoolkit.wps.converters.WPSObjectConverter.TMP_DIR_PATH; import static org.geotoolkit.wps.converters.WPSObjectConverter.TMP_DIR_URL; import org.geotoolkit.wps.io.WPSIO; import org.geotoolkit.wps.xml.Reference; import org.geotoolkit.wps.xml.WPSXmlFactory; import org.geotoolkit.wps.xml.v100.ComplexDataType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.opengis.feature.Feature; import org.opengis.feature.Property; import org.opengis.util.FactoryException; /** * * @author Quentin Boileau (Geoamtys) */ public final class ConvertersTestUtils { public static final double DELTA = 1E-12; private ConvertersTestUtils() { } public static RenderedImage makeRendredImage() { final BufferedImage img = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB); final Graphics2D g2d = img.createGraphics(); final GradientPaint gp = new GradientPaint(0, 0, Color.BLACK, 500, 500, Color.WHITE, true); g2d.setPaint(gp); g2d.fillRect(0, 0, 500, 500); g2d.dispose(); return img; } public static GridCoverage2D makeCoverage() { final BufferedImage img = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB); final Graphics2D g2d = img.createGraphics(); final GradientPaint gp = new GradientPaint(0, 0, Color.RED, 500, 500, Color.BLUE, true); g2d.setPaint(gp); g2d.fillRect(0, 0, 500, 500); g2d.dispose(); //set it's envelope final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic()); env.setRange(0, 0, 100); env.setRange(1, 0, 100); //create the coverage final GridCoverageBuilder gcb = new GridCoverageBuilder(); gcb.setEnvelope(env); gcb.setRenderedImage(img); return gcb.getGridCoverage2D(); } /** * Helper method to initialize a converter and run with its parameters. * * The converters are the ones that convert Complex or reference to Geometry * or Feature types. * * The method is in charge of setting up the parameters map, the resource * (either a java URL or String containg json datas) and calling conversion * method. * * @param <SourceType> type of the input data in the conversion * @param <TargetType> type of the output data in the conversion * @param sourceClass class of the input data in the conversion * @param targetClass class of the output dat in the conversion * @param resourcePath url to the resource file to read * @param mimeType mime type of the input data * @param encoding encoding of the input data * @param schema schema for the input data (only arguments that can be null) * @return an instance of TargetType which is the result of a call to the converter * @throws IOException on reading the test resource * @throws IllegalArgumentException when one of the String arguments is null * @throws NullArgumentException when one of the arguments is null */ public static <SourceType, TargetType> TargetType initAndRunInputConversion( Class sourceClass, Class targetClass, String resourcePath, String mimeType, String encoding, String schema) throws IOException { ArgumentChecks.ensureNonNull("sourceClass", sourceClass); ArgumentChecks.ensureNonNull("targetClass", targetClass); ArgumentChecks.ensureNonEmpty("resourcePath", resourcePath); ArgumentChecks.ensureNonEmpty("mimeType", mimeType); ArgumentChecks.ensureNonEmpty("encoding", encoding); // Get the converter to test final WPSObjectConverter<SourceType, TargetType> converter = WPSConverterRegistry.getInstance().getConverter(sourceClass, targetClass); // Setup parameters map final Map<String, Object> param = ConvertersTestUtils.createParameters( mimeType, encoding); // Setup the resource and run conversion if (sourceClass.equals(ComplexDataType.class)) { final String resource = ConvertersTestUtils.getTestResource(ConvertersTestUtils.class, resourcePath); final ComplexDataType complex = ConvertersTestUtils.createComplex(mimeType, encoding, schema, resource); return converter.convert((SourceType) complex, param); } else if (sourceClass.equals(Reference.class)) { final URL resource = ConvertersTestUtils.class.getResource(resourcePath); final Reference reference = ConvertersTestUtils.createReference("1.0.0", mimeType, encoding, schema, resource); return converter.convert((SourceType) reference, param); } else { fail(); return null; } } /** * Load a test resource. * * The resourcePath argument have to be one of the following : * - "/inputs/geometry.json" * - "/inputs/geometrycollection.json" * - "/inputs/feature.json" * - "/inputs/featurecollection.json" * * @param resourcePath path to the resource to load * @return an Object that can be casted to one of the following types : * Geometry, Geometry[], Feature, FeatureCollection * * @throws org.apache.sis.storage.DataStoreException when reading errors occur * @throws java.net.URISyntaxException when reading errors occur * @throws java.io.IOException when reading errors occur * @throws org.opengis.util.FactoryException when reading errors occur * @throws NullArgumentException when resourcePath is null * @throws IllegalArgumentException when resourcePath is empty */ public static Object loadTestResource(String resourcePath) throws DataStoreException, URISyntaxException, IOException, FactoryException { ArgumentChecks.ensureNonEmpty("resourcePath", resourcePath); Object testResource = null; if ("/inputs/geometry.json".equals(resourcePath)) { final Feature feature = WPSConvertersUtils.readFeatureFromJson(ConvertersTestUtils.class.getResource(resourcePath).toURI()); final Object geometryValue = FeatureExt.getDefaultGeometryAttributeValue(feature); if (!(geometryValue instanceof Geometry)) fail(); testResource = geometryValue; } else if ("/inputs/geometrycollection.json".equals(resourcePath)) testResource = ConvertersTestUtils.getGeometryArrayFromTestFolder(resourcePath); else if ("/inputs/feature.json".equals(resourcePath)) testResource = WPSConvertersUtils.readFeatureFromJson(ConvertersTestUtils.class.getResource(resourcePath).toURI()); else if ("/inputs/featurecollection.json".equals(resourcePath)) testResource = WPSConvertersUtils.readFeatureCollectionFromJson(ConvertersTestUtils.class.getResource(resourcePath).toURI()); else fail("Unknown test resource : " + resourcePath); return testResource; } /** * Helper method to initialize a converter and run with its parameters. * * The converters are the one that convert Geometry or Feature types to * Complex or Reference. * * The method is in charge of setting up the parameters map, the resource * (either a java URL or String containg json datas) and calling conversion * method. * * @param <SourceType> type of the input data in the conversion * @param <TargetType> type of the output data in the conversion * @param sourceClass class of the input data in the conversion * @param targetClass class of the output dat in the conversion * @param resource resource to give as input to the converter (must be of the * same type as SourceType) * @param mimeType mime type of the input data * @param encoding encoding of the input data * * @return an instance of TargetType which is the result of a call to the converter * * @throws IOException on reading the test resource * @throws IllegalArgumentException when mimeType or encoding is empty * @throws NullArgumentException when an argument is null */ public static <SourceType, TargetType> TargetType initAndRunOutputConversion( Class sourceClass, Class targetClass, Object resource, String mimeType, String encoding) throws IOException { ArgumentChecks.ensureNonNull("sourceClass", sourceClass); ArgumentChecks.ensureNonNull("targetClass", targetClass); ArgumentChecks.ensureNonNull("resource", resource); ArgumentChecks.ensureNonEmpty("mimeType", mimeType); ArgumentChecks.ensureNonEmpty("encoding", encoding); // Get converter final WPSObjectConverter<SourceType, TargetType> converter = WPSConverterRegistry.getInstance().getConverter(sourceClass, targetClass); // Setup the parameters map Path tmpDirPath; Map<String, Object> parametersMap = null; if (targetClass.equals(ComplexDataType.class)) parametersMap = ConvertersTestUtils.createParameters(mimeType, encoding); else if (targetClass.equals(Reference.class)) { tmpDirPath = Files.createTempDirectory(UUID.randomUUID().toString()); parametersMap = ConvertersTestUtils.createParameters(mimeType, encoding, tmpDirPath.toString(), WPSIO.IOType.OUTPUT.toString()); } else fail(); return converter.convert((SourceType) resource, parametersMap); } /** * Helper method that creates an output parameter map * @param mimeType mime-type property to define in the map * @param encoding encoding property to define in the map * @return the map the two above property */ public static final Map<String, Object> createParameters(String mimeType, String encoding) { final HashMap<String, Object> map = new HashMap<>(); map.put(WPSObjectConverter.MIME, mimeType); map.put(WPSObjectConverter.ENCODING, encoding); return map; } /** * Helper method that creates an output parameter map * @param mimeType mime-type property to define in the map * @param encoding encoding property to define in the map * @param tmpDirPath url to the temporary directory where to write the content of a reference * @param ioType ioType that helps to know which type of reference instanciate * @return the map the two above property */ public static final Map<String, Object> createParameters(final String mimeType, final String encoding, final String tmpDirPath, final String ioType) { final Map<String, Object> map = createParameters(mimeType, encoding); map.put(TMP_DIR_PATH, tmpDirPath); map.put(TMP_DIR_URL, "File:" + tmpDirPath); map.put(WPSObjectConverter.IOTYPE, ioType); return map; } /** * Helper method that creates a complex with all its field filled * @param mimeType can be null * @param encoding can be null * @param schema can be null * @param content can be null * @return the complex with its field filled using the above parameters */ public static final ComplexDataType createComplex(String mimeType, String encoding, String schema, String content) { final ComplexDataType complex = new ComplexDataType(); complex.setMimeType(mimeType); complex.setEncoding(encoding); complex.setSchema(schema); if (content != null) complex.getContent().add(content); return complex; } /** * Helper method that creates a ReferenceType with all its field filled * @param mimeType can be null * @param encoding can be null * @param schema can be null * @param url must be set * @return the reference with its field filled using the above parameters */ public static final Reference createReference(String version, String mimeType, String encoding, String schema, URL url) { final Reference reference = WPSXmlFactory.buildInOutReference(version, WPSIO.IOType.INPUT); reference.setMimeType(mimeType); reference.setEncoding(encoding); reference.setSchema(schema); reference.setHref(url.toString()); return reference; } /** * Helper method that creates a String containg all the content of a given * test resource * * @param clazz class that wants to get the resource * @param url url to the resource (relative to the class) * @return a String containing the content of the file * @throws IOException */ public static final String getTestResource(Class clazz, String url) throws IOException { ArgumentChecks.ensureNonNull("class", clazz); ArgumentChecks.ensureNonNull("url", url); final InputStream inputStream = clazz.getResourceAsStream(url); assertNotNull(inputStream); try { final String content = IOUtilities.toString(inputStream); return content; } finally { inputStream.close(); } } /** * Helper method that loads a geometry array from a json file * @param jsonURL url to the json file (the root is considered to be src/test/resources * so you can reference items in this folder by using /inputs for example * @return a geometry array * @throws IOException * @throws FactoryException */ public static final Geometry[] getGeometryArrayFromTestFolder(final String jsonURL) throws IOException, FactoryException { return getGeometryArrayFromInputStream(ConvertersTestUtils.class.getResourceAsStream(jsonURL)); } /** * Helper method that loads a geometry array from a json file * @param inputStream inputStream on the json file * @return a geometry array * @throws IOException * @throws FactoryException */ public static Geometry[] getGeometryArrayFromInputStream(final InputStream inputStream) throws IOException, FactoryException { final GeoJSONObject geoJsonObject = GeoJSONParser.parse(inputStream); if (!(geoJsonObject instanceof GeoJSONGeometry.GeoJSONGeometryCollection)) fail(); final GeoJSONGeometry.GeoJSONGeometryCollection geometryCollection = (GeoJSONGeometry.GeoJSONGeometryCollection) geoJsonObject; final Geometry parentGeometry = WPSConvertersUtils.convertGeoJSONGeometryToGeometry(geometryCollection); final Geometry[] geometryArray = new Geometry[parentGeometry.getNumGeometries()]; for (int i = 0; i < geometryArray.length; i++) geometryArray[i] = parentGeometry.getGeometryN(i); return geometryArray; } /** * Helper method that read a geometry in a json file * @param path path of the file containing the geometry * @return the read geometry * @throws IOException on IO errors when reading the file * @throws FactoryException when the crs of the geometry cannot be retrieved */ public static Geometry getGeometryFromGeoJsonContent(final String path) throws IOException, FactoryException { final GeoJSONObject geoJsonObject = GeoJSONParser.parse(Paths.get(path)); GeoJSONGeometry geoJsonGeometry = null; if (geoJsonObject instanceof GeoJSONFeature) { GeoJSONFeature jsonFeature = (GeoJSONFeature) geoJsonObject; geoJsonGeometry = jsonFeature.getGeometry(); } else if (geoJsonObject instanceof GeoJSONGeometry) geoJsonGeometry = (GeoJSONGeometry) geoJsonObject; else fail(); return WPSConvertersUtils.convertGeoJSONGeometryToGeometry(geoJsonGeometry); } /** * Helper method to test that a given feature collection as the same values * as in the test file featurecollection.json * * @param featureCollection the FeatureCollection to test */ public static void assertFeatureCollectionIsValid(final FeatureCollection featureCollection) { assertNotNull(featureCollection); // Ensure the feature collection contains 7 features assertEquals(featureCollection.size(), 7); try (FeatureIterator iterator = featureCollection.iterator()) { boolean[] isDefinedProperties = new boolean[7]; for (int i = 0; i < isDefinedProperties.length; i++) isDefinedProperties[i] = false; // Check that each property defined in the geojson file is in the // feature collection while (iterator.hasNext()) { final Feature feature = iterator.next(); final Object geometryValue = FeatureExt.getDefaultGeometryAttributeValue(feature); assertTrue(geometryValue instanceof Geometry); final Geometry currentGeometry = (Geometry) geometryValue; final String propertyValue = ((String)feature.getPropertyValue("name")); final Coordinate pointCoordinates = currentGeometry.getCoordinate(); final Coordinate[] polygonCoordinates = currentGeometry.getCoordinates(); if (propertyValue.equals("ABBOTT NEIGHBORHOOD PARK")) { isDefinedProperties[0] = !isDefinedProperties[0]; assertEquals(-80.87088507656375, pointCoordinates.x, DELTA); assertEquals(35.21515162500578, pointCoordinates.y, DELTA); } else if (propertyValue.equals("DOUBLE OAKS CENTER")) { isDefinedProperties[1] = !isDefinedProperties[1]; assertEquals(-80.83775386582222, pointCoordinates.x, DELTA); assertEquals(35.24980190252168, pointCoordinates.y, DELTA); } else if (propertyValue.equals("DOUBLE OAKS NEIGHBORHOOD PARK")) { isDefinedProperties[2] = !isDefinedProperties[2]; assertEquals(-80.83827000459532, pointCoordinates.x, DELTA); assertEquals(35.25674709224663, pointCoordinates.y, DELTA); } else if (propertyValue.equals("DOUBLE OAKS POOL")) { isDefinedProperties[3] = !isDefinedProperties[3]; assertEquals(-80.83697759172735, pointCoordinates.x, DELTA); assertEquals(35.25751734669229, pointCoordinates.y, DELTA); } else if (propertyValue.equals("DAVID B. WAYMER FLYING REGIONAL PARK")) { isDefinedProperties[4] = !isDefinedProperties[4]; assertEquals(-80.81647652154736, pointCoordinates.x, DELTA); assertEquals(35.40148708491418, pointCoordinates.y, DELTA); } else if (propertyValue.equals("DAVID B. WAYMER COMMUNITY PARK")) { isDefinedProperties[5] = !isDefinedProperties[5]; assertEquals(-80.83556459443902, pointCoordinates.x, DELTA); assertEquals(35.39917224760999, pointCoordinates.y, DELTA); } else if (propertyValue.equals("Plaza Road Park")) { isDefinedProperties[6] = !isDefinedProperties[6]; assertEquals(-80.72487831115721, polygonCoordinates[0].x, DELTA); assertEquals(35.26545403190955, polygonCoordinates[0].y, DELTA); assertEquals(-80.72135925292969, polygonCoordinates[1].x, DELTA); assertEquals(35.26727607954368, polygonCoordinates[1].y, DELTA); assertEquals(-80.71517944335938, polygonCoordinates[2].x, DELTA); assertEquals(35.26769654625573, polygonCoordinates[2].y, DELTA); } else fail(); } for (boolean isDefined : isDefinedProperties) assertTrue(isDefined); } } /** * Helper method to test that a given feature as the same values * as in the test file feature.json * @param feature the feature to test */ public static void assertFeatureIsValid(final Feature feature) { final String propertyValue = ((String)feature.getPropertyValue("name")); assertEquals(propertyValue, "Plaza Road Park"); Object value = FeatureExt.getDefaultGeometryAttributeValue(feature); assertTrue(value instanceof Polygon); Polygon polygon = (Polygon) value; Coordinate[] coordinates = polygon.getCoordinates(); // Test the value of the first three coordinates of the polygon assertEquals(-80.72487831115721, coordinates[0].x, DELTA); assertEquals(35.26545403190955, coordinates[0].y, DELTA); assertEquals(-80.72135925292969, coordinates[1].x, DELTA); assertEquals(35.26727607954368, coordinates[1].y, DELTA); assertEquals(-80.71517944335938, coordinates[2].x, DELTA); assertEquals(35.26769654625573, coordinates[2].y, DELTA); } /** * Helper method to test that a given geometry array as the same values as in * the test file geometrycollection.jso, * @param geometryArray the geometry array to test */ public static void assertGeometryArrayIsValid(Geometry[] geometryArray) { assertNotNull(geometryArray); assertEquals(3, geometryArray.length); for (Geometry geometry : geometryArray) { if (geometry.getGeometryType().equals("Point")) { assertEquals(-80.66080570220947, geometry.getCoordinate().x, DELTA); assertEquals(35.04939206472683, geometry.getCoordinate().y, DELTA); } else if (geometry.getGeometryType().equals("Polygon")) { assertEquals(-80.66458225250244, geometry.getCoordinates()[0].x, DELTA); assertEquals(35.04496519190309, geometry.getCoordinates()[0].y, DELTA); assertEquals(-80.66344499588013, geometry.getCoordinates()[1].x, DELTA); assertEquals(35.04603679820616, geometry.getCoordinates()[1].y, DELTA); } else if (geometry.getGeometryType().equals("LineString")) { assertEquals(-80.66237211227417, geometry.getCoordinates()[0].x, DELTA); assertEquals(35.05950973022538, geometry.getCoordinates()[0].y, DELTA); assertEquals(-80.66269397735596, geometry.getCoordinates()[1].x, DELTA); assertEquals(35.0592638296087, geometry.getCoordinates()[1].y, DELTA); } else // Fail, the geometrycollection.json file contains only 3 geometry // of the above types. fail(); } } /** * Helper method to test that a given Geometry as the same values as in the * test file geometry.json. * * @param geometry the geometry to test */ public static void assertGeometryIsValid(final Geometry geometry) { assertNotNull(geometry); assertEquals(-80.87088507656375, geometry.getCoordinate().x, DELTA); assertEquals(35.21515162500578, geometry.getCoordinate().y, DELTA); } }