/* * 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. * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.metadata.iso.citation; import java.util.Collection; import java.util.Iterator; import org.opengis.metadata.Identifier; import org.opengis.metadata.citation.Citation; import org.opengis.metadata.citation.OnLineFunction; import org.opengis.metadata.citation.PresentationForm; import org.opengis.metadata.citation.ResponsibleParty; import org.opengis.metadata.citation.Role; import org.opengis.util.InternationalString; import org.geotools.util.SimpleInternationalString; /** * A set of pre-defined constants and static methods working on {@linkplain Citation citations}. * Pre-defined metadata constants are usually declared in implementation classes like * {@link ResponsiblePartyImpl}. But citations are an exception since they are extensively * referenced in the Geotools library, and handling citations requires some convenience methods. * They are factored out in this {@code Citations} class for clarity. * <p> * Citations may be about an <cite>organisation</cite> (e.g. {@linkplain #OPEN_GIS OpenGIS}), * a <cite>specification</cite> (e.g. {@linkplain #WMS}) or an <cite>authority</cite> that * maintains definitions of codes (e.g. {@linkplain #EPSG}). In the later case, the citation * contains an {@linkplain Citation#getIdentifiers identifier} which is the namespace of the * codes maintained by the authority. For example the identifier for the {@link #EPSG} citation * is {@code "EPSG"}, and EPSG codes look like {@code "EPSG:4326"}. * * @since 2.2 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * @author Jody Garnett * * @todo Classify the pre-defined constants using the javadoc {@code @category} tag * once it will be available (targeted for J2SE 1.6). */ public final class Citations { /** * Do not allows instantiation of this class. */ private Citations() { } /////////////////////////////////////////////////////////////////////// //////// //////// //////// O R G A N I S A T I O N S //////// //////// //////// /////////////////////////////////////////////////////////////////////// /** * The <A HREF="http://www.opengeospatial.org">Open Geospatial consortium</A> organisation. * "Open Geospatial consortium" is the new name for "OpenGIS consortium". * An {@linkplain Citation#getAlternateTitles alternate title} for this citation is "OGC" * (according ISO 19115, alternate titles often contain abreviations). * * @see ResponsiblePartyImpl#OGC * @see #OPEN_GIS */ public static final Citation OGC; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.OGC); c.getAlternateTitles().add(new SimpleInternationalString("OGC")); // NOTE: all OGC alternate titles will be copied in OPEN_GIS as well. c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); c.freeze(); OGC = c; } /** * The <A HREF="http://www.opengis.org">OpenGIS consortium</A> organisation. * "OpenGIS consortium" is the old name for "Open Geospatial consortium". * {@linkplain Citation#getAlternateTitles Alternate titles} for this citation are * "OpenGIS" and "OGC" (according ISO 19115, alternate titles often contain abreviations). * * @see ResponsiblePartyImpl#OPEN_GIS * @see #OGC */ public static final Citation OPEN_GIS; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.OPEN_GIS); final Collection<InternationalString> alt = c.getAlternateTitles(); alt.add(new SimpleInternationalString("OpenGIS")); alt.addAll(OGC.getAlternateTitles()); c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); c.freeze(); OPEN_GIS = c; } /** * The <A HREF="http://www.esri.com">ESRI</A> organisation. * * @see ResponsiblePartyImpl#ESRI */ public static final Citation ESRI; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.ESRI); c.addAuthority("ESRI", true); c.freeze(); ESRI = c; } /** * The <A HREF="http://www.oracle.com">Oracle</A> organisation. * * @see ResponsiblePartyImpl#ORACLE */ public static final Citation ORACLE; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.ORACLE); c.freeze(); ORACLE = c; } /** * The <A HREF="http://postgis.refractions.net">PostGIS</A> project. * * @see ResponsiblePartyImpl#POSTGIS * * @since 2.4 */ public static final Citation POSTGIS; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.POSTGIS); c.freeze(); POSTGIS = c; } /** * The <A HREF="http://www.geotools.org">Geotools</A> project. * * @see ResponsiblePartyImpl#GEOTOOLS */ public static final Citation GEOTOOLS; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.GEOTOOLS); c.freeze(); GEOTOOLS = c; } /////////////////////////////////////////////////////////////////////// //////// //////// //////// S P E C I F I C A T I O N S //////// //////// //////// /////////////////////////////////////////////////////////////////////// // Do not put the ...files/?artifact... link in the head sentence: it break javadoc formatting. /** * The Web Map Service specification. {@linkplain Citation#getAlternateTitles Alternate titles} * for this citation are "WMS", "WMS 1.3.0", "OGC 04-024" and "ISO 19128". Note that the * version numbers may be upgrated in future Geotools versions. * * @see <A HREF="http://www.opengeospatial.org/">Open Geospatial Consortium</A> * @see <A HREF="http://www.opengis.org/docs/01-068r3.pdf">WMS 1.1.1 specification</A> * @see <A HREF="http://portal.opengis.org/files/?artifact_id=5316">WMS 1.3.0 specification</A> * @see ResponsiblePartyImpl#OGC * @see OnLineResourceImpl#WMS */ public static final Citation WMS; static { final CitationImpl c = new CitationImpl("Web Map Service"); final Collection<InternationalString> titles = c.getAlternateTitles(); titles.add(new SimpleInternationalString("WMS")); titles.add(new SimpleInternationalString("WMS 1.3.0")); titles.add(new SimpleInternationalString("OGC 04-024")); titles.add(new SimpleInternationalString("ISO 19128")); final Collection<ResponsibleParty> parties = c.getCitedResponsibleParties(); parties.add(ResponsiblePartyImpl.OGC); parties.add(ResponsiblePartyImpl.OGC(Role.PUBLISHER, OnLineResourceImpl.WMS)); /* * The WMS specification is a model in a programming point of view, but this is not * the purpose of ISO 19115 PresentationForm.MODEL_DIGITAL in my understanding. The * later rather looks like the output of a numerical model (e.g. meteorological model). * The WMS specification is distributed as a PDF document. */ c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); c.freeze(); WMS = c; } /** * The <A HREF="http://www.remotesensing.org/geotiff/geotiff.html">GeoTIFF</A> specification. * * @see ResponsiblePartyImpl#GEOTIFF */ public static final Citation GEOTIFF; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.GEOTIFF); c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); c.freeze(); GEOTIFF = c; } /** * The <A HREF="http://java.sun.com/products/java-media/jai">Java Advanced Imaging</A> library. * An {@linkplain Citation#getAlternateTitles alternate title} for this citation is "JAI" * (according ISO 19115, alternate titles often contain abreviations). * * @see ResponsiblePartyImpl#SUN_MICROSYSTEMS */ public static final Citation JAI; static { final CitationImpl c = new CitationImpl("Java Advanced Imaging"); c.getAlternateTitles().add(new SimpleInternationalString("JAI")); c.getCitedResponsibleParties().add(ResponsiblePartyImpl.SUN_MICROSYSTEMS); c.freeze(); JAI = c; } /////////////////////////////////////////////////////////////////////// //////// //////// //////// C R S A U T H O R I T I E S //////// //////// //////// /////////////////////////////////////////////////////////////////////// /** * The <A HREF="http://www.epsg.org">European Petroleum Survey Group</A> authority. * An {@linkplain Citation#getAlternateTitles alternate title} for this citation is * "EPSG" (according ISO 19115, alternate titles often contain abreviations). In * addition, this citation contains the "EPSG" {@linkplain Citation#getIdentifiers identifier} * for the "Authority name" {@linkplain Citation#getIdentifierTypes identifier type}. * <p> * This citation is used as an authority for * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system} * identifiers. When searching an {@linkplain org.opengis.referencing.crs.CRSAuthorityFactory CRS * authority factory} on EPSG data, Geotools compares the {@code "EPSG"} string against the * {@linkplain Citation#getIdentifiers identifiers} (or against the {@linkplain Citation#getTitle * title} and {@linkplain Citation#getAlternateTitles alternate titles} if there is no identifier) * using the {@link #identifierMatches(Citation,String) identifierMatches} method. * * @see ResponsiblePartyImpl#EPSG * @see #AUTO * @see #AUTO2 * @see #CRS */ public static final Citation EPSG; static { final CitationImpl c = new CitationImpl(ResponsiblePartyImpl.EPSG); c.addAuthority("EPSG", true); c.getPresentationForm().add(PresentationForm.TABLE_DIGITAL); c.freeze(); EPSG = c; } /** * The <A HREF="http://www.opengis.org/docs/01-068r3.pdf">WMS 1.1.1</A> "Automatic Projections" * authority. An {@linkplain Citation#getAlternateTitles alternate title} for this citation is * "AUTO" (according ISO 19115, alternate titles often contain abreviations). In addition, this * citation contains the "AUTO" {@linkplain Citation#getIdentifiers identifier} for the * "Authority name" {@linkplain Citation#getIdentifierTypes identifier type}. * <p> * <strong>Warning:</strong> {@code AUTO} is different from {@link #AUTO2} used for WMS 1.3.0. * <p> * This citation is used as an authority for * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system} * identifiers. When searching an {@linkplain org.opengis.referencing.crs.CRSAuthorityFactory CRS * authority factory} on AUTO data, Geotools compares the {@code "AUTO"} string against the * {@linkplain Citation#getIdentifiers identifiers} (or against the {@linkplain Citation#getTitle * title} and {@linkplain Citation#getAlternateTitles alternate titles} if there is no identifier) * using the {@link #identifierMatches(Citation,String) identifierMatches} method. * * @see <A HREF="http://www.opengeospatial.org/">Open Geospatial Consortium</A> * @see <A HREF="http://www.opengis.org/docs/01-068r3.pdf">WMS 1.1.1 specification</A> * @see #WMS * @see #AUTO2 * @see #CRS * @see #EPSG */ public static final Citation AUTO; static { // Sanity check ensure that all @see tags are actually available in the metadata final CitationImpl c = new CitationImpl("Automatic Projections"); c.addAuthority("AUTO", false); /* * Do not put "WMS 1.1.1" and "OGC 01-068r3" as alternative titles. They are alternative * titles for the WMS specification (see the WMS constant in this class), not for the * AUTO authority name. */ final Collection<ResponsibleParty> parties = c.getCitedResponsibleParties(); parties.add(ResponsiblePartyImpl.OGC); parties.add(ResponsiblePartyImpl.OGC(Role.PUBLISHER, OnLineFunction.DOWNLOAD, "http://www.opengis.org/docs/01-068r3.pdf")); c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); // See comment in WMS. c.freeze(); AUTO = c; } // Do not put the ...files/?artifact... link in the head sentence: it break javadoc formatting. /** * The WMS 1.3.0 "Automatic Projections" authority. An {@linkplain Citation#getAlternateTitles * alternate title} for this citation is "AUTO2" (according ISO 19115, alternate titles often * contain abreviations). In addition, this citation contains the "AUTO2" * {@linkplain Citation#getIdentifiers identifier} for the "Authority name" * {@linkplain Citation#getIdentifierTypes identifier type}. * <p> * <strong>Warning:</strong> {@code AUTO2} is different from {@link #AUTO} used for WMS 1.1.1 * and earlier. * <p> * This citation is used as an authority for * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system} * identifiers. When searching an {@linkplain org.opengis.referencing.crs.CRSAuthorityFactory CRS * authority factory} on AUTO2 data, Geotools compares the {@code "AUTO2"} string against the * {@linkplain Citation#getIdentifiers identifiers} (or against the {@linkplain Citation#getTitle * title} and {@linkplain Citation#getAlternateTitles alternate titles} if there is no identifier) * using the {@link #identifierMatches(Citation,String) identifierMatches} method. * * @see <A HREF="http://www.opengeospatial.org/">Open Geospatial Consortium</A> * @see <A HREF="http://portal.opengis.org/files/?artifact_id=5316">WMS 1.3.0 specification</A> * @see #WMS * @see #AUTO * @see #CRS * @see #EPSG */ public static final Citation AUTO2; static { final CitationImpl c = new CitationImpl("Automatic Projections"); c.addAuthority("AUTO2", false); /* * Do not put "WMS 1.3.0" and "OGC 04-024" as alternative titles. They are alternative * titles for the WMS specification (see the WMS constant in this class), not for the * AUTO2 authority name. */ final Collection<ResponsibleParty> parties = c.getCitedResponsibleParties(); parties.add(ResponsiblePartyImpl.OGC); parties.add(ResponsiblePartyImpl.OGC(Role.PUBLISHER, OnLineResourceImpl.WMS)); c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); // See comment in WMS. c.freeze(); AUTO2 = c; } // Do not put the ...files/?artifact... link in the head sentence: it break javadoc formatting. /** * The WMS 1.3.0 "CRS" authority. This is defined in the same document than {@link #AUTO2}. * * @see #WMS * @see #AUTO * @see #AUTO2 * @see #CRS * @see #EPSG */ public static final Citation CRS; static { final CitationImpl c = new CitationImpl("Web Map Service CRS"); c.addAuthority("CRS", false); c.getCitedResponsibleParties().addAll(AUTO2.getCitedResponsibleParties()); c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); // See comment in WMS. c.freeze(); CRS = c; } /** * URN in the OGC namespace. This citation contains the {@code "urn:ogc:def"} and * {@code "urn:x-ogc:def"} {@linkplain Citation#getIdentifiers identifiers} for the * "Authority name" {@linkplain Citation#getIdentifierTypes identifier type}. * * @since 2.4 */ public static final Citation URN_OGC; static { final CitationImpl c = new CitationImpl("URN in OGC namespace"); c.addAuthority("urn:ogc:def", false); c.addAuthority("urn:x-ogc:def", false); c.getCitedResponsibleParties().add(ResponsiblePartyImpl.OGC); c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); c.freeze(); URN_OGC = c; } /** * URL in the OGC namespace. This citation contains the * {@code "http://www.opengis.net"} {@linkplain Citation#getIdentifiers identifiers} * for the "Authority name" {@linkplain Citation#getIdentifierTypes identifier type}. * * @since 2.4 */ public static final Citation HTTP_OGC; static { final CitationImpl c = new CitationImpl("URL in OGC namespace"); c.addAuthority("http://www.opengis.net", false); c.getCitedResponsibleParties().add(ResponsiblePartyImpl.OGC); c.getPresentationForm().add(PresentationForm.DOCUMENT_DIGITAL); c.freeze(); HTTP_OGC = c; } /////////////////////////////////////////////////////////////////////// //////// //////// //////// End of constants declarations //////// //////// //////// /////////////////////////////////////////////////////////////////////// /** * List of citations declared in this class. */ private static final Citation[] AUTHORITIES = { OGC, OPEN_GIS, ESRI, ORACLE, GEOTOOLS, WMS, GEOTIFF, JAI, EPSG, AUTO, AUTO2, CRS }; /** * Returns a citation of the given name. If the given name matches a {@linkplain * Citation#getTitle title} or an {@linkplain Citation#getAlternateTitles alternate titles} * of one of the pre-defined constants ({@link #EPSG}, {@link #GEOTIFF}, <cite>etc.</cite>), * then this constant is returned. Otherwise, a new citation is created with the specified * name as the title. * * @param title The citation title (or alternate title). * @return A citation using the specified name */ public static Citation fromName(final String title) { for (int i=0; i<AUTHORITIES.length; i++) { final Citation citation = AUTHORITIES[i]; if (titleMatches(citation, title)) { return citation; } } return new CitationImpl(title); } /** * Returns {@code true} if at least one {@linkplain Citation#getTitle title} or * {@linkplain Citation#getAlternateTitles alternate title} in {@code c1} is equals to a title * or alternate title in {@code c2}. The comparaison is case-insensitive and ignores leading * and trailing spaces. The titles ordering is not significant. * * @param c1 The first citation to compare. * @param c2 the second citation to compare. * @return {@code true} if at least one title or alternate title matches. */ public static boolean titleMatches(final Citation c1, final Citation c2) { InternationalString candidate = c2.getTitle(); Iterator<? extends InternationalString> iterator = null; do { // The "null" locale argument is required for getting the unlocalized version. final String asString = candidate.toString(null); if (titleMatches(c1, asString)) { return true; } final String asLocalized = candidate.toString(); if (asLocalized!=asString && titleMatches(c1, asLocalized)) { return true; } if (iterator == null) { final Collection<? extends InternationalString> titles = c2.getAlternateTitles(); if (titles == null) { break; } iterator = titles.iterator(); } if (!iterator.hasNext()) { break; } candidate = iterator.next(); } while (true); return false; } /** * Returns {@code true} if the {@linkplain Citation#getTitle title} or any * {@linkplain Citation#getAlternateTitles alternate title} in the given citation * matches the given string. The comparaison is case-insensitive and ignores leading * and trailing spaces. * * @param citation The citation to check for. * @param title The title or alternate title to compare. * @return {@code true} if the title or alternate title matches the given string. */ public static boolean titleMatches(final Citation citation, String title) { title = title.trim(); InternationalString candidate = citation.getTitle(); Iterator<? extends InternationalString> iterator = null; do { // The "null" locale argument is required for getting the unlocalized version. final String asString = candidate.toString(null); if (asString.trim().equalsIgnoreCase(title)) { return true; } final String asLocalized = candidate.toString(); if (asLocalized!=asString && asLocalized.trim().equalsIgnoreCase(title)) { return true; } if (iterator == null) { final Collection<? extends InternationalString> titles = citation.getAlternateTitles(); if (titles == null) { break; } iterator = titles.iterator(); } if (!iterator.hasNext()) { break; } candidate = iterator.next(); } while (true); return false; } /** * Returns {@code true} if at least one {@linkplain Citation#getIdentifiers identifier} in * {@code c1} is equals to an identifier in {@code c2}. The comparaison is case-insensitive * and ignores leading and trailing spaces. The identifier ordering is not significant. * <p> * If (and <em>only</em> if) the citations do not contains any identifier, then this method * fallback on titles comparaison using the {@link #titleMatches(Citation,Citation) * titleMatches} method. This fallback exists for compatibility with client codes using * citation {@linkplain Citation#getTitle titles} without identifiers. * * @param c1 The first citation to compare. * @param c2 the second citation to compare. * @return {@code true} if at least one identifier, title or alternate title matches. */ public static boolean identifierMatches(Citation c1, Citation c2) { /* * If there is no identifier in both citations, fallback on title comparaisons. If there is * identifiers in only one citation, make sure that this citation is the second one (c2) in * order to allow at least one call to 'identifierMatches(c1, String)'. */ Iterator<? extends Identifier> iterator = c2.getIdentifiers().iterator(); if (!iterator.hasNext()) { iterator = c1.getIdentifiers().iterator(); if (!iterator.hasNext()) { return titleMatches(c1, c2); } c1 = c2; c2 = null; // Just for make sure that we don't use it by accident. } do { final String id = iterator.next().getCode().trim(); if (identifierMatches(c1, id)) { return true; } } while (iterator.hasNext()); return false; } /** * Returns {@code true} if any {@linkplain Citation#getIdentifiers identifiers} in the given * citation matches the given string. The comparaison is case-insensitive and ignores leading * and trailing spaces. If (and <em>only</em> if) the citation do not contains any identifier, * then this method fallback on titles comparaison using the {@link #titleMatches(Citation, * String) titleMatches} method. This fallback exists for compatibility with client codes using * citation {@linkplain Citation#getTitle titles} without identifiers. * * @param citation The citation to check for. * @param identifier The identifier to compare. * @return {@code true} if the title or alternate title matches the given string. */ public static boolean identifierMatches(final Citation citation, String identifier) { identifier = identifier.trim(); final Collection<? extends Identifier> identifiers = citation.getIdentifiers(); for (final Identifier id : identifiers) { final String code = id.getCode().trim(); if (identifier.equalsIgnoreCase(code)) { return true; } } if (identifiers.isEmpty()) { return titleMatches(citation, identifier); } else { return false; } } /** * Returns the shortest identifier for the specified citation, or the title if there is * no identifier. This method is useful for extracting the namespace from an authority, * for example {@code "EPSG"}. * * @param citation The citation for which to get the identifier. * @return The shortest identifier of the given citation. * * @since 2.4 */ public static String getIdentifier(final Citation citation) { String identifier = null; for (final Identifier id : citation.getIdentifiers()) { final String candidate = id.getCode().trim(); final int length = candidate.length(); if (length != 0) { if (identifier == null || length < identifier.length()) { identifier = candidate; } } } if (identifier == null) { identifier = String.valueOf(citation.getTitle()); } return identifier; } }