/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-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.referencing.datum; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.opengis.metadata.extent.Extent; import org.opengis.referencing.datum.Datum; import org.opengis.util.InternationalString; import org.geotools.referencing.AbstractIdentifiedObject; import org.geotools.referencing.wkt.Formatter; import org.geotools.util.Utilities; import org.geotools.resources.Classes; import org.geotools.resources.i18n.Vocabulary; /** * Specifies the relationship of a coordinate system to the earth, thus creating a {@linkplain * org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system}. A datum * uses a parameter or set of parameters that determine the location of the origin of the coordinate * reference system. Each datum subtype can be associated with only specific types of * {@linkplain org.opengis.referencing.cs.AbstractCS coordinate systems}. * <p> * A datum can be defined as a set of real points on the earth that have coordinates. * The definition of the datum may also include the temporal behavior (such as the * rate of change of the orientation of the coordinate axes). * <p> * This class is conceptually <cite>abstract</cite>, even if it is technically possible to * instantiate it. Typical applications should create instances of the most specific subclass with * {@code Default} prefix instead. An exception to this rule may occurs when it is not possible to * identify the exact type. * * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * * @since 2.1 * * @see org.geotools.referencing.cs.AbstractCS * @see org.geotools.referencing.crs.AbstractCRS */ public class AbstractDatum extends AbstractIdentifiedObject implements Datum { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -4894180465652474930L; /** * List of localizable properties. To be given to * {@link AbstractIdentifiedObject} constructor. */ private static final String[] LOCALIZABLES = {ANCHOR_POINT_KEY, SCOPE_KEY}; /** * Description, possibly including coordinates, of the point or points used to anchor the datum * to the Earth. Also known as the "origin", especially for Engineering and Image Datums. */ private final InternationalString anchorPoint; /** * The time after which this datum definition is valid. This time may be precise * (e.g. 1997 for IRTF97) or merely a year (e.g. 1983 for NAD83). If the time is * not defined, then the value is {@link Long#MIN_VALUE}. */ private final long realizationEpoch; /** * Area or region in which this datum object is valid. */ private final Extent domainOfValidity; /** * Description of domain of usage, or limitations of usage, for which this * datum object is valid. */ private final InternationalString scope; /** * Constructs a new datum with the same values than the specified one. * This copy constructor provides a way to wrap an arbitrary implementation into a * Geotools one or a user-defined one (as a subclass), usually in order to leverage * some implementation-specific API. This constructor performs a shallow copy, * i.e. the properties are not cloned. * * @param datum The datum to copy. * * @since 2.2 */ public AbstractDatum(final Datum datum) { super(datum); final Date epoch = datum.getRealizationEpoch(); realizationEpoch = (epoch!=null) ? epoch.getTime() : Long.MIN_VALUE; domainOfValidity = datum.getDomainOfValidity(); scope = datum.getScope(); anchorPoint = datum.getAnchorPoint(); } /** * Constructs a datum from a set of properties. The properties given in argument follow * the same rules than for the {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) * super-class constructor}. Additionally, the following properties are understood by this * construtor: * <br><br> * <table border='1'> * <tr bgcolor="#CCCCFF" class="TableHeadingColor"> * <th nowrap>Property name</th> * <th nowrap>Value type</th> * <th nowrap>Value given to</th> * </tr> * <tr> * <td nowrap> {@link #ANCHOR_POINT_KEY "anchorPoint"} </td> * <td nowrap> {@link InternationalString} or {@link String} </td> * <td nowrap> {@link #getAnchorPoint}</td> * </tr> * <tr> * <td nowrap> {@link #REALIZATION_EPOCH_KEY "realizationEpoch"} </td> * <td nowrap> {@link Date} </td> * <td nowrap> {@link #getRealizationEpoch}</td> * </tr> * <tr> * <td nowrap> {@link #DOMAIN_OF_VALIDITY_KEY "domainOfValidity"} </td> * <td nowrap> {@link Extent} </td> * <td nowrap> {@link #getDomainOfValidity}</td> * </tr> * <tr> * <td nowrap> {@link #SCOPE_KEY "scope"} </td> * <td nowrap> {@link InternationalString} or {@link String} </td> * <td nowrap> {@link #getScope}</td> * </tr> * </table> * * @param properties The properties to be given to the identified object. */ public AbstractDatum(final Map<String,?> properties) { this(properties, new HashMap<String,Object>()); } /** * Work around for RFE #4093999 in Sun's bug database * ("Relax constraint on placement of this()/super() call in constructors"). */ private AbstractDatum(final Map<String,?> properties, final Map<String,Object> subProperties) { super(properties, subProperties, LOCALIZABLES); final Date realizationEpoch; anchorPoint = (InternationalString) subProperties.get(ANCHOR_POINT_KEY ); realizationEpoch = (Date) subProperties.get(REALIZATION_EPOCH_KEY ); domainOfValidity = (Extent) subProperties.get(DOMAIN_OF_VALIDITY_KEY); scope = (InternationalString) subProperties.get(SCOPE_KEY ); this.realizationEpoch = (realizationEpoch != null) ? realizationEpoch.getTime() : Long.MIN_VALUE; } /** * Same convenience method than {@link org.geotools.cs.AbstractCS#name} except that we get * the unlocalized name (usually in English locale), because the name is part of the elements * compared by the {@link #equals} method. */ static Map<String,Object> name(final int key) { final Map<String,Object> properties = new HashMap<String,Object>(4); final InternationalString name = Vocabulary.formatInternational(key); properties.put(NAME_KEY, name.toString(null)); // "null" required for unlocalized version. properties.put(ALIAS_KEY, name); return properties; } /** * Description, possibly including coordinates, of the point or points used to anchor the datum * to the Earth. Also known as the "origin", especially for Engineering and Image Datums. * * <ul> * <li>For a geodetic datum, this point is also known as the fundamental point, which is * traditionally the point where the relationship between geoid and ellipsoid is defined. * In some cases, the "fundamental point" may consist of a number of points. In those * cases, the parameters defining the geoid/ellipsoid relationship have then been averaged * for these points, and the averages adopted as the datum definition.</li> * * <li>For an engineering datum, the anchor point may be a physical point, or it may be a * point with defined coordinates in another CRS.</li> * * <li>For an image datum, the anchor point is usually either the centre of the image or the * corner of the image.</li> * * <li>For a temporal datum, this attribute is not defined. Instead of the anchor point, * a temporal datum carries a separate time origin of type {@link Date}.</li> * </ul> */ public InternationalString getAnchorPoint() { return anchorPoint; } /** * The time after which this datum definition is valid. This time may be precise (e.g. 1997 * for IRTF97) or merely a year (e.g. 1983 for NAD83). In the latter case, the epoch usually * refers to the year in which a major recalculation of the geodetic control network, underlying * the datum, was executed or initiated. An old datum can remain valid after a new datum is * defined. Alternatively, a datum may be superseded by a later datum, in which case the * realization epoch for the new datum defines the upper limit for the validity of the * superseded datum. */ public Date getRealizationEpoch() { return (realizationEpoch!=Long.MIN_VALUE) ? new Date(realizationEpoch) : null; } /** * Area or region or timeframe in which this datum is valid. * * @since 2.4 */ public Extent getDomainOfValidity() { return domainOfValidity; } /** * Area or region in which this datum object is valid. * * @deprecated Renamed {@link #getDomainOfValidity}. */ public Extent getValidArea() { return domainOfValidity; } /** * Description of domain of usage, or limitations of usage, for which this * datum object is valid. */ public InternationalString getScope() { return scope; } /** * Gets the type of the datum as an enumerated code. Datum type was provided * for all kind of datum in the legacy OGC 01-009 specification. In the new * OGC 03-73 (ISO 19111) specification, datum type is provided only for * vertical datum. Nevertheless, we keep this method around since it is * needed for WKT formatting. Note that we returns the datum type ordinal * value, not the code list object. */ int getLegacyDatumType() { return 0; } /** * Compares the specified object with this datum for equality. * * @param object The object to compare to {@code this}. * @param compareMetadata {@code true} for performing a strict comparaison, or * {@code false} for comparing only properties relevant to transformations. * @return {@code true} if both objects are equal. */ @Override public boolean equals(final AbstractIdentifiedObject object, final boolean compareMetadata) { if (super.equals(object, compareMetadata)) { if (!compareMetadata) { /* * Tests for name, since datum with different name have completly * different meaning. We don't perform this comparaison if the user * asked for metadata comparaison, because in such case the names * have already been compared by the subclass. */ return nameMatches(object.getName().getCode()) || object.nameMatches(getName().getCode()); } final AbstractDatum that = (AbstractDatum) object; return this.realizationEpoch == that.realizationEpoch && Utilities.equals(this.domainOfValidity, that.domainOfValidity) && Utilities.equals(this.anchorPoint, that.anchorPoint) && Utilities.equals(this.scope, that.scope); } return false; } /** * Format the inner part of a * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well * Known Text</cite> (WKT)</A> element. * * Note: All subclasses will override this method, but only {@link DefaultGeodeticDatum} will * <strong>not</strong> invokes this parent method, because horizontal datum do not write * the datum type. * * @param formatter The formatter to use. * @return The WKT element name. */ @Override protected String formatWKT(final Formatter formatter) { formatter.append(getLegacyDatumType()); return Classes.getShortClassName(this); } }