package eu.esdihumboldt.cst.functions.geometric; import java.util.List; import java.util.Map; import net.jcip.annotations.Immutable; import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import com.google.common.collect.ListMultimap; import com.vividsolutions.jts.geom.Geometry; import eu.esdihumboldt.hale.common.align.model.impl.PropertyEntityDefinition; import eu.esdihumboldt.hale.common.align.transformation.engine.TransformationEngine; import eu.esdihumboldt.hale.common.align.transformation.function.PropertyValue; import eu.esdihumboldt.hale.common.align.transformation.function.TransformationException; import eu.esdihumboldt.hale.common.align.transformation.function.impl.AbstractSingleTargetPropertyTransformation; import eu.esdihumboldt.hale.common.align.transformation.function.impl.NoResultException; import eu.esdihumboldt.hale.common.align.transformation.report.TransformationLog; import eu.esdihumboldt.hale.common.instance.geometry.CRSDefinitionManager; import eu.esdihumboldt.hale.common.instance.geometry.DefaultGeometryProperty; import eu.esdihumboldt.hale.common.instance.geometry.GeometryFinder; import eu.esdihumboldt.hale.common.instance.geometry.impl.CodeDefinition; import eu.esdihumboldt.hale.common.instance.helper.DepthFirstInstanceTraverser; import eu.esdihumboldt.hale.common.instance.helper.InstanceTraverser; import eu.esdihumboldt.hale.common.schema.geometry.CRSDefinition; import eu.esdihumboldt.hale.common.schema.geometry.GeometryProperty; /** * Reproject geometry function. * * @author Sandro Salari * @author Stefano Costa, GeoSolutions */ @Immutable public class ReprojectGeometry extends AbstractSingleTargetPropertyTransformation<TransformationEngine> implements ReprojectGeometryFunction { @Override protected Object evaluate(String transformationIdentifier, TransformationEngine engine, ListMultimap<String, PropertyValue> variables, String resultName, PropertyEntityDefinition resultProperty, Map<String, String> executionParameters, TransformationLog log) throws TransformationException, NoResultException { // Get input geometry PropertyValue input = variables.get("source").get(0); Object inputValue = input.getValue(); InstanceTraverser traverser = new DepthFirstInstanceTraverser(true); GeometryFinder geoFind = new GeometryFinder(null); traverser.traverse(inputValue, geoFind); List<GeometryProperty<?>> geoms = geoFind.getGeometries(); CoordinateReferenceSystem sourceCRS = geoms.get(0).getCRSDefinition().getCRS(); Geometry sourceGeometry = geoms.get(0).getGeometry(); Geometry resultGeometry = sourceGeometry; CoordinateReferenceSystem targetCRS = sourceCRS; // Get input parameter String srs = getParameterChecked(PARAMETER_REFERENCE_SYSTEM).as(String.class); if (srs != null) { try { targetCRS = parseReferenceSystemParamter(srs); } catch (Exception e) { throw new TransformationException( "Error determining destination Cordinate Reference System.", e); } // Retrieve transformation from cell context, or create a new // instance Map<Object, Object> cellContext = getExecutionContext().getCellContext(); MathTransform transform = getOrCreateMathTransform(sourceCRS, targetCRS, cellContext); // Apply transformation try { resultGeometry = JTS.transform(sourceGeometry, transform); } catch (MismatchedDimensionException | TransformException e) { throw new TransformationException("Problem on execute transformation from: " + sourceCRS + " to " + targetCRS, e); } } return new DefaultGeometryProperty<Geometry>(new CodeDefinition(CRS.toSRS(targetCRS), targetCRS), resultGeometry); } /** * Construct a {@link CoordinateReferenceSystem} instance by parsing the * input string via a {@link CRSDefinitionManager} instance. * * @param crs string representation of the CRS * @return the {@link CoordinateReferenceSystem} instance */ private CoordinateReferenceSystem parseReferenceSystemParamter(String crs) { CoordinateReferenceSystem parsedCrs = null; if (crs != null) { CRSDefinition crsDef = CRSDefinitionManager.getInstance().parse(crs); if (crsDef != null) { parsedCrs = crsDef.getCRS(); } else { throw new IllegalArgumentException("Could not parse CRS definition."); } } return parsedCrs; } /** * Attempt to find a math transform between the specified Coordinate * Reference Systems. * <p> * The method first tries to look up a relevant MathTransform instance from * the provided {@code context} object; then, if none was found, it creates * a new one and stores it in the context, to allow its reuse by following * reproject transformations. * </p> * * @param sourceCRS The source CRS. * @param targetCRS The target CRS. * @param context The context. * @return The math transform from {@code sourceCRS} to {@code targetCRS}. * @throws TransformationException if no math transform could be found */ private MathTransform getOrCreateMathTransform(CoordinateReferenceSystem sourceCRS, CoordinateReferenceSystem targetCRS, Map<Object, Object> context) throws TransformationException { MathTransform transform = null; String key = sourceCRS.getName().hashCode() + " --> " + targetCRS.getName().hashCode(); synchronized (context) { transform = (MathTransform) context.get(key); if (transform == null) { transform = createMathTransform(sourceCRS, targetCRS); context.put(key, transform); } } return transform; } /** * Attempt to find a math transform between the specified Coordinate * Reference Systems. * * @param sourceCRS The source CRS. * @param targetCRS The target CRS. * @return The math transform from {@code sourceCRS} to {@code targetCRS}. * @throws TransformationException if no math transform could be found */ private MathTransform createMathTransform(CoordinateReferenceSystem sourceCRS, CoordinateReferenceSystem targetCRS) throws TransformationException { MathTransform transform = null; try { transform = CRS.findMathTransform(sourceCRS, targetCRS, false); // Transformation cannot be found because either the sourceCRS or // the targetCRS is missing bursa-wolf parameters } catch (FactoryException ex1) { try { Integer sourceEpsgCode = CRS.lookupEpsgCode(sourceCRS, true); Integer targetEpsgCode = CRS.lookupEpsgCode(targetCRS, true); if (sourceEpsgCode != null && targetEpsgCode != null) { transform = CRS.findMathTransform(CRS.decode("EPSG:" + sourceEpsgCode, true), CRS.decode("EPSG:" + targetEpsgCode, true)); } else { throw new TransformationException( "Unable to find requested transformation from: " + sourceCRS + " to " + targetCRS); } } catch (FactoryException ex2) { throw new TransformationException("Problem on execute transformation from: " + sourceCRS + " to " + targetCRS, ex2); } } return transform; } }