Package org.geotools.referencing.factory.epsg

Source Code of org.geotools.referencing.factory.epsg.FactoryUsingWKT

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2005-2008, 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.epsg;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
import java.util.Map;
import java.util.TreeSet;
import java.util.TreeMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.net.MalformedURLException;
import java.net.URL;

import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import org.geotools.factory.Hints;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.factory.AbstractAuthorityFactory;
import org.geotools.referencing.factory.DeferredAuthorityFactory;
import org.geotools.referencing.factory.FactoryNotFoundException;
import org.geotools.referencing.factory.PropertyAuthorityFactory;
import org.geotools.referencing.factory.ReferencingFactoryContainer;
import org.geotools.metadata.iso.citation.Citations;
import org.geotools.metadata.iso.citation.CitationImpl;
import org.geotools.io.TableWriter;
import org.geotools.io.IndentedLineWriter;
import org.geotools.resources.Arguments;
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Loggings;
import org.geotools.resources.i18n.LoggingKeys;
import org.geotools.resources.i18n.Vocabulary;
import org.geotools.resources.i18n.VocabularyKeys;
import org.geotools.util.logging.Logging;


/**
* Authority factory for {@linkplain CoordinateReferenceSystem Coordinate Reference Systems}
* beyong the one defined in the EPSG database. This factory is used as a fallback when a
* requested code is not found in the EPSG database, or when there is no connection at all
* to the EPSG database. The additional CRS are defined as <cite>Well Known Text</cite> in
* a property file located by default in the {@code org.geotools.referencing.factory.epsg}
* package, and whose name should be {@value #FILENAME}. If no property file is found, the
* factory won't be activated. The property file can also be located in a custom directory;
* See {@link #getDefinitionsURL()} for more details.
* <p>
* This factory can also be used to provide custom extensions or overrides to a main EPSG factory.
* In order to provide a custom extension file, override the {@link #getDefinitionsURL()} method.
* In order to make the factory be an override, change the default priority by using the
* two arguments constructor (this factory defaults to {@link ThreadedEpsgFactory#PRIORITY} - 10,
* so it's used as an extension).
*
* @since 2.1
*
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux
* @author Jody Garnett
* @author Rueben Schulz
* @author Andrea Aime
*/
public class FactoryUsingWKT extends DeferredAuthorityFactory implements CRSAuthorityFactory {
    /**
     * The authority. Will be created only when first needed.
     *
     * @see #getAuthority
     */
    private Citation authority;

    /**
     * The default filename to read. The default {@code FactoryUsingWKT} implementation will
     * search for the first occurence of this file in the following places:
     * <p>
     * <ul>
     *   <li>In the directory specified by the
     *       {@value org.geotools.factory.GeoTools#CRS_DIRECTORY_KEY} system property.</li>
     *   <li>In every {@code org/geotools/referencing/factory/espg} directories found on the
     *       classpath.</li>
     * </ul>
     * <p>
     * The filename part before the extension ({@code "epsg"}) denotes the authority namespace
     * where to register the content of this file. The user-directory given by the system property
     * may contains other property files for other authorities, like {@code "esri.properties"},
     * but those additional authorities are not handled by the default {@code FactoryUsingWKT}
     * class.
     *
     * @see #getDefinitionsURL
     */
    public static final String FILENAME = "epsg.properties";

    /**
     * The factories to be given to the backing store.
     */
    private final ReferencingFactoryContainer factories;

    /**
     * Default priority for this factory.
     *
     * @since 2.4
     * @deprecated We will try to replace the priority mechanism by a better
     *             one in a future Geotools version.
     */
    protected static final int DEFAULT_PRIORITY = ThreadedEpsgFactory.PRIORITY - 10;

    /**
     * Directory scanned for extra definitions.
     */
    private final File directory;

    /**
     * Constructs an authority factory using the default set of factories.
     */
    public FactoryUsingWKT() {
        this(null);
    }

    /**
     * Constructs an authority factory using a set of factories created from the specified hints.
     * This constructor recognizes the {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS},
     * {@link Hints#DATUM_FACTORY DATUM} and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM}
     * {@code FACTORY} hints.
     */
    public FactoryUsingWKT(final Hints userHints) {
        this(userHints, DEFAULT_PRIORITY);
    }

    /**
     * Constructs an authority factory using the specified hints and priority.
     */
    protected FactoryUsingWKT(final Hints userHints, final int priority) {
        super(userHints, priority);
        factories = ReferencingFactoryContainer.instance(userHints);
        Object hint = null;
        if (userHints != null) {
            hint = userHints.get(Hints.CRS_AUTHORITY_EXTRA_DIRECTORY);
        }
        if (hint instanceof File) {
            directory = (File) hint;
        } else if (hint instanceof String) {
            directory = new File((String) hint);
        } else {
            directory = null;
        }
        hints.put(Hints.CRS_AUTHORITY_EXTRA_DIRECTORY, directory);
        // Disposes the cached property file after at least 15 minutes of inactivity.
        setTimeout(15 * 60 * 1000L);
    }

    /**
     * Returns the authority. The default implementation returns the first citation returned
     * by {@link #getAuthorities()}, with the addition of identifiers from all additional
     * authorities returned by the above method.
     *
     * @see #getAuthorities
     */
    @Override
    public synchronized Citation getAuthority() {
        // No need to synchronize; this is not a big deal if we create this object twice.
        if (authority == null) {
            final Citation[] authorities = getAuthorities();
            switch (authorities.length) {
                case 0: authority = Citations.EPSG; break;
                case 1: authority = authorities[0]; break;
                default: {
                    final CitationImpl c = new CitationImpl(authorities[0]);
                    final Collection<Identifier> identifiers = c.getIdentifiers();
                    for (int i=1; i<authorities.length; i++) {
                        identifiers.addAll(authorities[i].getIdentifiers());
                    }
                    c.freeze();
                    authority = c;
                    break;
                }
            }
        }
        return authority;
    }

    /**
     * Returns the set of authorities to use as {@linkplain CoordinateReferenceSystem#getIdentifiers
     * identifiers} for the CRS to be created. This set is given to the
     * {@linkplain PropertyAuthorityFactory#PropertyAuthorityFactory(ReferencingFactoryContainer,
     * Citation[], URL) properties-backed factory constructor}.
     * <p>
     * The default implementation returns a singleton containing only {@linkplain Citations#EPSG
     * EPSG}. Subclasses should override this method in order to enumerate all relevant authorities,
     * with {@linkplain Citations#EPSG EPSG} in last position. For example {@link EsriExtension}
     * returns {{@linkplain Citations#ESRI ESRI}, {@linkplain Citations#EPSG EPSG}}.
     *
     * @since 2.4
     */
    protected Citation[] getAuthorities() {
        return new Citation[] {
            Citations.EPSG
        };
    }

    /**
     * Returns the URL to the property file that contains CRS definitions.
     * The default implementation performs the following search path:
     * <ul>
     *   <li>If a value is set for the {@value #CRS_DIRECTORY_KEY} system property key,
     *       then the {@value #FILENAME} file will be searched in this directory.</li>
     *   <li>If no value is set for the above-cited system property, or if no {@value #FILENAME}
     *       file was found in that directory, then the first {@value #FILENAME} file found in
     *       any {@code org/geotools/referencing/factory/epsg} directory on the classpath will
     *       be used.</li>
     *   <li>If no file was found on the classpath neither, then this factory will be disabled.</li>
     * </ul>
     *
     * @return The URL, or {@code null} if none.
     */
    protected URL getDefinitionsURL() {
        try {
            if (directory != null) {
                final File file = new File(directory, FILENAME);
                if (file.isFile()) {
                    return file.toURI().toURL();
                }
            }
        } catch (SecurityException exception) {
            Logging.unexpectedException(LOGGER, exception);
        } catch (MalformedURLException exception) {
            Logging.unexpectedException(LOGGER, exception);
        }
        return FactoryUsingWKT.class.getResource(FILENAME);
    }

    /**
     * Creates the backing store authority factory.
     *
     * @return The backing store to uses in {@code createXXX(...)} methods.
     * @throws FactoryNotFoundException if the no {@code epsg.properties} file has been found.
     * @throws FactoryException if the constructor failed to find or read the file.
     *         This exception usually has an {@link IOException} as its cause.
     */
    protected AbstractAuthorityFactory createBackingStore() throws FactoryException {
        try {
            URL url = getDefinitionsURL();
            if (url == null) {
                throw new FactoryNotFoundException(Errors.format(
                        ErrorKeys.FILE_DOES_NOT_EXIST_$1, FILENAME));
            }
            final Iterator<? extends Identifier> ids = getAuthority().getIdentifiers().iterator();
            final String authority = ids.hasNext() ? ids.next().getCode() : "EPSG";
            final LogRecord record = Loggings.format(Level.CONFIG,
                    LoggingKeys.USING_FILE_AS_FACTORY_$2, url.getPath(), authority);
            record.setLoggerName(LOGGER.getName());
            LOGGER.log(record);
            return new PropertyAuthorityFactory(factories, getAuthorities(), url);
        } catch (IOException exception) {
            throw new FactoryException(Errors.format(ErrorKeys.CANT_READ_$1, FILENAME), exception);
        }
    }

    /**
     * Returns a factory of the given type.
     */
    private static final <T extends AbstractAuthorityFactory> T getFactory(final Class<T> type) {
        return type.cast(ReferencingFactoryFinder.getCRSAuthorityFactory("EPSG",
                new Hints(Hints.CRS_AUTHORITY_FACTORY, type)));
    }

    /**
     * Prints a list of codes that duplicate the ones provided by {@link ThreadedEpsgFactory}.
     * This is used for implementation of {@linkplain #main main method} in order to check
     * the content of the {@value #FILENAME} file (or whatever property file used as backing
     * store for this factory) from the command line.
     *
     * @param  out The writer where to print the report.
     * @return The set of duplicated codes.
     * @throws FactoryException if an error occured.
     *
     * @since 2.4
     */
    protected Set reportDuplicatedCodes(final PrintWriter out) throws FactoryException {
        final AbstractAuthorityFactory sqlFactory = getFactory(ThreadedEpsgFactory.class);
        final Vocabulary resources = Vocabulary.getResources(null);
        out.println(resources.getLabel(VocabularyKeys.COMPARE_WITH));
        try {
            final IndentedLineWriter w = new IndentedLineWriter(out);
            w.setIndentation(4);
            w.write(sqlFactory.getBackingStoreDescription());
            w.flush();
        } catch (IOException e) {
            // Should never happen, since we are writting to a PrintWriter.
            throw new AssertionError(e);
        }
        out.println();
        final Set<String> wktCodes   = this.      getAuthorityCodes(IdentifiedObject.class);
        final Set<String> sqlCodes   = sqlFactory.getAuthorityCodes(IdentifiedObject.class);
        final Set<String> duplicated = new TreeSet<String>();
        for (String code : wktCodes) {
            code = code.trim();
            if (sqlCodes.contains(code)) {
                duplicated.add(code);
                /*
                 * Note: we don't use wktCodes.retainsAll(sqlCode) because the Set implementations
                 *       are usually not the standard ones, but rather some implementations backed
                 *       by a connection to the resources of the underlying factory. We also close
                 *       the connection after this loop for the same reason.  In addition, we take
                 *       this opportunity for sorting the codes.
                 */
            }
        }
        if (duplicated.isEmpty()) {
            out.println(resources.getString(VocabularyKeys.NO_DUPLICATION_FOUND));
        } else {
            for (final String code : duplicated) {
                out.print(resources.getLabel(VocabularyKeys.DUPLICATED_VALUE));
                out.println(code);
            }
        }
        return duplicated;
    }

    /**
     * Prints a list of CRS that can't be instantiated. This is used for implementation of
     * {@linkplain #main main method} in order to check the content of the {@value #FILENAME}
     * file (or whatever property file used as backing store for this factory) from the command
     * line.
     *
     * @param  out The writer where to print the report.
     * @return The set of codes that can't be instantiated.
     * @throws FactoryException if an error occured while
     *         {@linkplain #getAuthorityCodes fetching authority codes}.
     *
     * @since 2.4
     */
    protected Set reportInstantiationFailures(final PrintWriter out) throws FactoryException {
        final Set<String> codes = getAuthorityCodes(CoordinateReferenceSystem.class);
        final Map<String,String> failures = new TreeMap<String,String>();
        for (final String code : codes) {
            try {
                createCoordinateReferenceSystem(code);
            } catch (FactoryException exception) {
                failures.put(code, exception.getLocalizedMessage());
            }
        }
        if (!failures.isEmpty()) {
            final TableWriter writer = new TableWriter(out, " ");
            for (final Map.Entry<String,String> entry : failures.entrySet()) {
                writer.write(entry.getKey());
                writer.write(':');
                writer.nextColumn();
                writer.write(entry.getValue());
                writer.nextLine();
            }
            try {
                writer.flush();
            } catch (IOException e) {
                // Should not happen, since we are writting to a PrintWriter
                throw new AssertionError(e);
            }
        }
        return failures.keySet();
    }

    /**
     * Prints a list of codes that duplicate the ones provided in the {@link ThreadedEpsgFactory}.
     * The factory tested is the one registered in {@link ReferencingFactoryFinder}. By default,
     * this is this {@code FactoryUsingWKT} class backed by the {@value #FILENAME} property file.
     * This method can be invoked from the command line in order to check the content of the
     * property file. Valid arguments are:
     * <p>
     * <table>
     *   <tr><td>{@code -test}</td><td>Try to instantiate all CRS and reports any failure
     *       to do so.</td></tr>
     *   <tr><td>{@code -duplicated}</td><td>List all codes from the WKT factory that are
     *       duplicating a code from the SQL factory.</td></tr>
     * </table>
     *
     * @param  args Command line arguments.
     * @throws FactoryException if an error occured.
     *
     * @since 2.4
     */
    public static void main(final String[] args) throws FactoryException {
        main(args, FactoryUsingWKT.class);
    }

    /**
     * Implementation of the {@link #main} method, shared by subclasses.
     */
    static void main(String[] args, final Class<? extends FactoryUsingWKT> type)
            throws FactoryException
    {
        final Arguments arguments = new Arguments(args);
        Locale.setDefault(arguments.locale);
        final boolean duplicated  = arguments.getFlag("-duplicated");
        final boolean instantiate = arguments.getFlag("-test");
        args = arguments.getRemainingArguments(0);
        final FactoryUsingWKT factory = getFactory(type);
        if (duplicated) {
            factory.reportDuplicatedCodes(arguments.out);
        }
        if (instantiate) {
            factory.reportInstantiationFailures(arguments.out);
        }
        factory.dispose();
    }
}
TOP

Related Classes of org.geotools.referencing.factory.epsg.FactoryUsingWKT

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.