Package org.geotools.referencing.factory

Source Code of org.geotools.referencing.factory.PropertyCoordinateOperationAuthorityFactory

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2002-2012, 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.referencing.factory;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.DefaultMathTransformFactory;
import org.geotools.referencing.operation.DefaultOperation;
import org.geotools.referencing.operation.DefaultOperationMethod;
import org.geotools.referencing.operation.transform.AbstractMathTransform;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.util.SimpleInternationalString;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.OperationMethod;
import org.opengis.util.InternationalString;

/**
* A {@link CoordinateOperationAuthorityFactory} backed by a properties file.
* Allows custom transform definitions across two CRSs, expressed as WKT math transforms.
* Entries in the properties file take this format:
* <pre>
* [source crs code],[target crs code]=[WKT math transform]
* </pre>
* Examples:
* <pre>
* 4230,4258=PARAM_MT["NTv2", PARAMETER["Latitude and longitude difference file", "100800401.gsb"]]
* 23031,25831=PARAM_MT["Similarity transformation", \
*   PARAMETER["Ordinate 1 of evaluation point in target CRS", -129.549], \
*   PARAMETER["Ordinate 2 of evaluation point in target CRS", -208.185], \
*   PARAMETER["Scale difference", 1.0000015504], \
*   PARAMETER["Rotation angle of source coordinate reference system axes", 1.56504]]
* </pre>
* For more compact definitions, parameter names can be replaced by their corresponding EPSG codes.
* Following examples are the same as former ones:
* <pre>
* 4230,4258=PARAM_MT["9615", PARAMETER["8656", "100800401.gsb"]]
* 23031,25831=PARAM_MT["9621", \
*   PARAMETER["8621", -129.549], \
*   PARAMETER["8622", -208.185], \
*   PARAMETER["8611", 1.0000015504], \
*   PARAMETER["8614", 1.56504]]
* </pre>
* References:
* <p>
* See <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">
* <cite>Well-Known Text format</cite></a> for math transform syntax.
* Visit the <a href="http://www.epsg-registry.org/"> <cite>EPSG Geodetic Parameter Registry</cite>
* </a> for EPSG parameter codes and values.
* <p>
* Note that invertible transforms will be used in both directions.
* <p>
* This factory doesn't cache any result. Any call to a {@code createFoo} method will trig a new
* WKT parsing. For caching, this factory should be wrapped in some buffered factory like
* {@link BufferedAuthorityFactory}.
*
* @source $URL$
* @version $Id$
* @author Oscar Fonts
*/
public class PropertyCoordinateOperationAuthorityFactory extends
        DirectAuthorityFactory implements CoordinateOperationAuthorityFactory {

    /**
     * The authority for this factory.
     */
    private final Citation authority;
   
    /**
     * The properties object for our properties file. Keys are CRS code pairs
     * separated by a comma. The associated value is a WKT string for the Math
     * Transform. See {@link PropertyCoordinateOperationAuthorityFactory}.
     */
    private final Properties definitions = new Properties();
   
    /**
     * An unmodifiable view of the authority keys. This view is always up to date
     * even if entries are added or removed in the {@linkplain #definitions} map.
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private final Set<String> codes = Collections.unmodifiableSet((Set) definitions.keySet());
   
    /**
     * Creates a factory for the specified authority from the specified file.
     *
     * @param  factories   The underlying factories used for objects creation.
     * @param  authority   The organization or party responsible for definition and maintenance of
     *                     the database.
     * @param  definitions URL to the definition file.
     * @throws IOException if the definitions can't be read.
     */
    public PropertyCoordinateOperationAuthorityFactory(
            final ReferencingFactoryContainer  factories,
            final Citation                     authority,
            final URL                          definitions)
        throws IOException
    {
        // Set priority low
        super(factories, MINIMUM_PRIORITY + 10);
       
        // Set authority
        this.authority = authority;
        ensureNonNull("authority", authority);
       
        // Load properties
        final InputStream in = definitions.openStream();
        this.definitions.load(in);
        in.close();
    }
   
    /**
     * Creates an operation from a single operation code.
     *
     * @param  code Coded value for operation.
     * @return The operation for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     */
    @Override   
    public CoordinateOperation createCoordinateOperation(String code)
            throws NoSuchAuthorityCodeException, FactoryException {
        String[] crsPair = trimAuthority(code).split(",");
        if (crsPair.length == 2) {
            Set<CoordinateOperation> coordopset = createFromCoordinateReferenceSystemCodes(
                    trimAuthority(crsPair[0]), trimAuthority(crsPair[1]));
            if (!coordopset.isEmpty()) {
                return coordopset.iterator().next();
            }
        }
        return null;
    }

    /**
     * Creates a {@link CoordinateOperation} from
     * {@linkplain CoordinateReferenceSystem coordinate reference system} codes.
     * This method returns a single operation from the properties file.
     * If operation is invertible, will check also for the inverse one.
     * If operation not found, it will return an empty set.
     *
     * @param  sourceCRS   Coded value of source coordinate reference system.
     * @param  targetCRS   Coded value of target coordinate reference system.
     * @return The operation from {@code sourceCRS} to {@code targetCRS} (one single element).
     * @throws NoSuchAuthorityCodeException if a specified code was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     */
    @Override
    public Set<CoordinateOperation> createFromCoordinateReferenceSystemCodes(
            String sourceCRS, String targetCRS)
                    throws NoSuchAuthorityCodeException, FactoryException {

        Set<CoordinateOperation> coordops = new HashSet<CoordinateOperation>(1);

        CoordinateOperation coordop = createFromCoordinateReferenceSystemCodes(sourceCRS,
                targetCRS, false);
        if (coordop == null) {
            // Not found. Try to create from the inverse.
            coordop = createFromCoordinateReferenceSystemCodes(targetCRS, sourceCRS, true);
        }
        if (coordop != null) {
            // Add to set if found.
            coordops.add(coordop);
        }
        return coordops;
    }
   
   
    /**
     * Seeks for a WKT definition in the properties file from a CRS pair, parses it,
     * and creates the corresponding CoordinateOperation. Returns {@code null}
     * if something went wrong.
     * <p>
     * Will log a WARNING message if a parsing error occurred.
     *
     * @param  sourceCRS   Coded value of source coordinate reference system.
     * @param  targetCRS   Coded value of target coordinate reference system.
     * @param  inverse     {@code true} to create operation from the inverse definition.
     * @return The operation from {@code sourceCRS} to {@code targetCRS},
     *         or {@code null} if not found.
     * @throws NoSuchAuthorityCodeException if a specified code was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     */
    CoordinateOperation createFromCoordinateReferenceSystemCodes(
            String sourceCRS, String targetCRS, boolean inverse)
                    throws NoSuchAuthorityCodeException, FactoryException {

        // Get WKT definition from properties
        sourceCRS = trimAuthority(sourceCRS);
        targetCRS = trimAuthority(targetCRS);
        String id = sourceCRS+","+targetCRS;
        String WKT = definitions.getProperty(id);       
        if(WKT == null) {
            // No definition found.
            return null;
        }
      
        // Create MathTransform from WKT
        MathTransform mt = null;
        try {
            mt = factories.getMathTransformFactory().createFromWKT(WKT);
        } catch (FactoryException e) {
            // Probably malformed WKT.
            LOGGER.warning("Error creating transformation: " + WKT);
            return null;
        }

        // Create the CRS definitions
        String s = this.authority.getIdentifiers().iterator().next().getCode();
        CoordinateReferenceSystem source = CRS.decode(s+":"+sourceCRS);
        CoordinateReferenceSystem target = CRS.decode(s+":"+targetCRS);
       
        // Need to create a derived MathTransform that will handle axis order and units
        // as defined in CRS. Had to cast to DefaultMathTransformFactory because
        // createBaseToDerived is not defined in MathTransformFactory interface (GeoAPI).
        DefaultMathTransformFactory mtf = (DefaultMathTransformFactory)factories.
                getMathTransformFactory();
        MathTransform mt2 = mtf.createBaseToDerived(source, mt, target.getCoordinateSystem());
       
        // Extract name from the transform, if possible, or use class name.
        String methodName;
        try {
            if (mt instanceof AbstractMathTransform) {
                methodName = ((AbstractMathTransform)mt).getParameterValues().getDescriptor().getName().getCode();
            } else if (mt instanceof AffineTransform2D) {
                methodName = ((AffineTransform2D)mt).getParameterValues().getDescriptor().getName().getCode();
            } else {
                methodName = mt.getClass().getSimpleName();
            }
        } catch (NullPointerException e) {
            methodName = mt.getClass().getSimpleName();
        }
        Map<String, String> props = new HashMap<String, String>();
        props.put("name", methodName);
       
        // Create the OperationMethod
        OperationMethod method = new DefaultOperationMethod(props,
                mt2.getSourceDimensions(), mt2.getTargetDimensions(), null);
       
        // Finally create CoordinateOperation
        CoordinateOperation coordop = null;
        if (!inverse) { // Direct operation
            props.put("name", sourceCRS + " \u21E8 " + targetCRS);
            coordop = DefaultOperation.create(props, source, target,
                    mt2, method, CoordinateOperation.class);
        } else { // Inverse operation
            try {
                props.put("name", targetCRS + " \u21E8 " + sourceCRS);
                coordop = DefaultOperation.create(props, target, source,
                    mt2.inverse(), method, CoordinateOperation.class);
            } catch (NoninvertibleTransformException e) {
                return null;
            }       
        }
        return coordop;
    }   

    /**
     * Returns the set of authority codes of the given type.
     * Only CoordinateOperation.class is accepted as type.
     *
     * This factory will not filter codes for its subclasses.
     *
     * @param  type The CoordinateOperation type (or null, same effect).
     * @return All of available authority codes, or an empty set.
     */
    @Override
    public Set<String> getAuthorityCodes(Class<? extends IdentifiedObject> type) {
        if (type==null || type.isAssignableFrom(CoordinateOperation.class)) {
            return codes;
        } else {
            return Collections.emptySet();
        }
    }

    /**
     * Gets a description of the object corresponding to a code.
     *
     * @param  code Value allocated by authority.
     * @return A description of the object, or {@code null} if the object
     *         corresponding to the specified {@code code} has no description.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the query failed for some other reason.
     */
    @Override
    public InternationalString getDescriptionText(String code)
            throws NoSuchAuthorityCodeException, FactoryException {
       
        final String wkt = definitions.getProperty(trimAuthority(code));
       
        if (wkt == null) {
            throw noSuchAuthorityCode(IdentifiedObject.class, code);
        }
       
        // The first string literal in WKT will be considered the description text.
        int start = wkt.indexOf('"');
        if (start >= 0) {
            final int end = wkt.indexOf('"', ++start);
            if (end >= 0) {
                return new SimpleInternationalString(wkt.substring(start, end).trim());
            }
        }
        return null;
    }

    /**
     * Returns the organization or party responsible for definition and maintenance of the
     * database.
     */
    @Override
    public Citation getAuthority() {
        return authority;
    }
}
TOP

Related Classes of org.geotools.referencing.factory.PropertyCoordinateOperationAuthorityFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.