/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2005-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-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; * 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.geotoolkit.referencing.factory.epsg; import java.util.Set; import java.util.Map; import java.util.TreeSet; import java.util.TreeMap; import java.io.PrintWriter; import java.io.IOException; import org.opengis.util.FactoryException; import org.opengis.metadata.citation.Citation; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.geotoolkit.factory.Hints; import org.apache.sis.referencing.factory.GeodeticAuthorityFactory; import org.geotoolkit.referencing.factory.wkt.PropertyAuthorityFactory; import org.geotoolkit.metadata.Citations; import org.geotoolkit.io.TableWriter; import org.geotoolkit.io.IndentedLineWriter; import org.geotoolkit.resources.Vocabulary; /** * 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 CRS are defined as <cite>Well Known Text</cite> in a property * file named {@value #FILENAME}. The search path is as below, in that order: * * <ol> * <li><p>If a value for {@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY} exists in the hints map * given at construction time, then that value will be used as the directory where to * search for the {@value #FILENAME} file. Reminder: such hint can be * {@linkplain Hints#putSystemDefault defined system-wide} for convenience.</p></li> * <li><p>The {@value #FILENAME} files found in all {@code org/geotoolkit/referencing/factory/epsg} * directories on the classpath are merged with the values found in previous step, if any. * If the same value is defined twice, the value of previous step have precedence.</p></li> * </ol> * * This factory can also be used to provide custom extensions or overrides to a main EPSG factory: * * <ul> * <li><p>In order to provide a custom extension, you can create a subclass that invoke the * {@link #PropertyEpsgFactory(Hints, String, Citation[])} constructor with different constants. * You can also subclass {@link PropertyAuthorityFactory} directly for getting more control.</p></li> * * <li><p>In order to make the factory be an override, override the * {@link #setOrdering setOrdering(...)} method as below: * * {@preformat java * protected void setOrdering(Organizer organizer) { * organizer.before(ThreadedEpsgFactory.class); * } * }</p></li> * </ul> * * {@section Caching of CRS objects} * This factory doesn't cache any result. Any call to a {@code createFoo} method * will trig a new WKT parsing. For adding caching service, this factory should * be wrapped in {@link org.geotoolkit.referencing.factory.CachingAuthorityFactory}. * Note that this is done automatically when this factory is used through the * {@link org.geotoolkit.referencing.CRS} static methods. * * {@section Troubleshotting} * If the {@value #FILENAME} file is on the classpath but seems to be ignored, * the following actions may provide some useful informations: * * <ul> * <li><p>Print the list of every registered factories using the code snippet documented * in the {@link org.geotoolkit.referencing.factory.FactoryDependencies} class and verify * that {@code PropertyEpsgFactory} is presents.</p></li> * * <li><p>Set the logging level for the {@code org.geotoolkit} loggers to {@code CONFIG}. * See <a href="http://java.sun.com/javase/6/docs/technotes/guides/logging/overview.html">Java * Logging Overview</a> for the standard way, or use the following Geotk convenience method: * * {@preformat java * Logging.GEOTOOLKIT.forceMonolineConsoleOutput(Level.CONFIG); * } * </p></li> * * <li><p>Force the system to ignore any factory other than {@code PropertyEpsgFactory}: * * {@preformat java * Hints.putSystemDefault(Hints.CRS_AUTHORITY_FACTORY, PropertyEpsgFactory.class); * } * </p></li> * </ul> * * @author Martin Desruisseaux (IRD) * @author Jody Garnett (Refractions) * @author Rueben Schulz (UBC) * @author Andrea Aime (TOPP) * @version 3.00 * * @since 2.1 * @module */ public class PropertyEpsgFactory extends PropertyAuthorityFactory implements CRSAuthorityFactory { /** * The default filename to read, which is {@value}. The default * {@code PropertyEpsgFactory} implementation will search for every occurrences of * {@code org/geotoolkit/referencing/factory/espg/epsg.properties} on the classpath. * However a different directory for this filename can be specified using * {@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY}. */ public static final String FILENAME = "epsg.properties"; /** * Constructs a default authority factory. * * @throws IOException If an error occurred while reading the definition files. * Note that do exception is thrown if there is no file - in this case * the factory is only considered not {@linkplain #availability available}. */ public PropertyEpsgFactory() throws IOException { this(null); } /** * Constructs an authority factory from the given hints. * Hints of special interest are: * <p> * <ul> * <li>{@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY}</li> * <li>{@link Hints#FORCE_LONGITUDE_FIRST_AXIS_ORDER}</li> * </ul> * <p> * This constructor recognizes also {@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. * * @param userHints An optional set of hints, or {@code null} if none. * @throws IOException If an error occurred while reading the definition files. * Note that do exception is thrown if there is no file - in this case * the factory is only considered not {@linkplain #availability available}. */ public PropertyEpsgFactory(final Hints userHints) throws IOException { this(userHints, FILENAME, Citations.EPSG); } /** * Constructs an authority factory for the given authorities. This constructor recognizes * the same hints than {@link #PropertyEpsgFactory(Hints)}. The {@code authorities} argument * should 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}}. * * @param userHints * An optional set of hints, or {@code null} if none. * @param filename * The name of the file to look in the {@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY} * directory and in every {@code org/geotoolkit/referencing/factory/espg} directories * found on the classpath. * @param authorities * The organizations or parties responsible for definition and maintenance of the * database. Should contains at least {@link Citations#EPSG}. * @throws IOException If an error occurred while reading the definition files. * Note that do exception is thrown if there is no file - in this case * the factory is only considered not {@linkplain #availability available}. * * @since 3.00 */ public PropertyEpsgFactory(final Hints userHints, final String filename, final Citation... authorities) throws IOException { super(userHints, Hints.CRS_AUTHORITY_EXTRA_DIRECTORY, PropertyEpsgFactory.class, filename, authorities); } /** * Prints a list of codes that duplicate the ones provided by {@link ThreadedEpsgFactory}. * This is used 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. * * {@preformat text * java -jar geotk-referencing.jar test duplicates * } * * @param out The writer where to print the report. * @return The set of duplicated codes. * @throws FactoryException if an error occurred. * * @see org.geotoolkit.console.ReferencingCommands#test * * @since 2.4 */ public Set<String> reportDuplicates(final PrintWriter out) throws FactoryException { final GeodeticAuthorityFactory sqlFactory = (GeodeticAuthorityFactory) org.apache.sis.referencing.CRS.getAuthorityFactory("EPSG"); final Vocabulary resources = Vocabulary.getResources(null); out.println(resources.getLabel(Vocabulary.Keys.CompareWith)); try { final IndentedLineWriter w = new IndentedLineWriter(out); w.setIndentation(4); w.write(sqlFactory.getAuthority().getTitle().toString()); w.flush(); } catch (IOException e) { // Should never happen, since we are writing 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<>(); 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(Vocabulary.Keys.NoDuplicationFound)); } else { for (final String code : duplicated) { out.print(resources.getLabel(Vocabulary.Keys.DuplicatedValue)); out.print(' '); out.println(code); } } return duplicated; } /** * Prints a list of CRS that can't be instantiated. This is used 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. To lauch from the command line, use the following: * * {@preformat text * java -jar geotk-referencing.jar test creates * } * * @param out The writer where to print the report. * @return The set of codes that can't be instantiated. * @throws FactoryException if an error occurred while * {@linkplain #getAuthorityCodes fetching authority codes}. * * @see org.geotoolkit.console.ReferencingCommands#test * * @since 2.4 */ public Set<String> reportInstantiationFailures(final PrintWriter out) throws FactoryException { final Set<String> codes = getAuthorityCodes(CoordinateReferenceSystem.class); final Map<String,String> failures = new TreeMap<>(); 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 writing to a PrintWriter throw new AssertionError(e); } } return failures.keySet(); } }