/*
* 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.crs;
import java.util.Map;
import java.util.HashMap;
import javax.measure.unit.Unit;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.util.InternationalString;
import org.geotools.measure.Measure;
import org.geotools.referencing.AbstractIdentifiedObject;
import org.geotools.referencing.AbstractReferenceSystem;
import org.geotools.referencing.cs.AbstractCS;
import org.geotools.referencing.wkt.Formatter;
import org.geotools.resources.CRSUtilities;
import org.geotools.resources.i18n.Vocabulary;
import org.geotools.util.UnsupportedImplementationException;
/**
* Abstract coordinate reference system, usually defined by a coordinate system and a datum.
*
* @since 2.1
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*
* @see AbstractCS
* @see org.geotools.referencing.datum.AbstractDatum
* @tutorial http://docs.codehaus.org/display/GEOTOOLS/Coordinate+Transformation+Services+for+Geotools+2.1
*/
public abstract class AbstractCRS extends AbstractReferenceSystem implements CoordinateReferenceSystem {
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = -7433284548909530047L;
/**
* The coordinate system.
*/
protected final CoordinateSystem coordinateSystem;
/**
* Constructs a new coordinate reference system 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 crs The coordinate reference system to copy.
*
* @since 2.2
*/
public AbstractCRS(final CoordinateReferenceSystem crs) {
super(crs);
coordinateSystem = crs.getCoordinateSystem();
}
/**
* Constructs a coordinate reference system from a set of properties. The properties are given
* unchanged to the {@linkplain AbstractReferenceSystem#AbstractReferenceSystem(Map) super-class
* constructor}.
*
* @param properties Set of properties. Should contains at least {@code "name"}.
* @param cs The coordinate system.
*/
public AbstractCRS(final Map<String,?> properties, final CoordinateSystem cs) {
super(properties);
ensureNonNull("cs", cs);
this.coordinateSystem = cs;
}
/**
* Creates a name for the predefined constants in subclasses. The name is an unlocalized String
* object. However, since this method is used for creation of convenience objects only (not for
* objects created from an "official" database), the "unlocalized" name is actually choosen
* according the user's locale at class initialization time. The same name is also added in
* a localizable form as an alias. Since the {@link #nameMatches} convenience method checks
* the alias, it still possible to consider two objects are equivalent even if their names
* were formatted in different locales.
*/
static Map<String,?> 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());
properties.put(ALIAS_KEY, name);
return properties;
}
/**
* Returns the coordinate system.
*/
public CoordinateSystem getCoordinateSystem() {
return coordinateSystem;
}
/**
* Returns the unit used for all axis. If not all axis uses the same unit,
* then this method returns {@code null}. This method is often used for
* Well Know Text (WKT) formatting.
*/
final Unit<?> getUnit() {
return CRSUtilities.getUnit(coordinateSystem);
}
/**
* Computes the distance between two points. This convenience method delegates the work to
* the underlyling {@linkplain AbstractCS coordinate system}, if possible.
*
* @param coord1 Coordinates of the first point.
* @param coord2 Coordinates of the second point.
* @return The distance between {@code coord1} and {@code coord2}.
* @throws UnsupportedOperationException if this coordinate reference system can't compute
* distances.
* @throws MismatchedDimensionException if a coordinate doesn't have the expected dimension.
*/
public Measure distance(final double[] coord1, final double[] coord2)
throws UnsupportedOperationException, MismatchedDimensionException
{
if (coordinateSystem instanceof AbstractCS) {
return ((AbstractCS) coordinateSystem).distance(coord1, coord2);
}
throw new UnsupportedImplementationException(coordinateSystem.getClass());
}
/**
* Compare this coordinate reference system with the specified object for equality.
* If {@code compareMetadata} is {@code true}, then all available properties are
* compared including {@linkplain #getValidArea valid area} and {@linkplain #getScope scope}.
*
* @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)) {
final AbstractCRS that = (AbstractCRS) object;
return equals(this.coordinateSystem, that.coordinateSystem, compareMetadata);
}
return false;
}
/**
* Returns a hash value for this CRS. {@linkplain #getName Name},
* {@linkplain #getIdentifiers identifiers} and {@linkplain #getRemarks remarks}
* are not taken in account. In other words, two CRS objects will return the same
* hash value if they are equal in the sense of
* <code>{@link #equals(AbstractIdentifiedObject,boolean) equals}(AbstractIdentifiedObject,
* <strong>false</strong>)</code>.
*
* @return The hash code value. This value doesn't need to be the same
* in past or future versions of this class.
*/
@Override
public int hashCode() {
return (int)serialVersionUID ^ coordinateSystem.hashCode();
}
/**
* Formats 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. The default implementation writes the following
* elements:
* <ul>
* <li>The {@linkplain #datum datum}, if any.</li>
* <li>The unit if all axis use the same unit. Otherwise the unit is omitted and
* the WKT format is {@linkplain Formatter#isInvalidWKT flagged as invalid}.</li>
* <li>All {@linkplain #coordinateSystem coordinate system}'s axis.</li>
* </ul>
*
* @param formatter The formatter to use.
* @return The name of the WKT element type (e.g. {@code "GEOGCS"}).
*/
@Override
protected String formatWKT(final Formatter formatter) {
formatDefaultWKT(formatter);
// Will declares the WKT as invalid.
return super.formatWKT(formatter);
}
/**
* Default implementation of {@link #formatWKT}. For {@link DefaultEngineeringCRS}
* and {@link DefaultVerticalCRS} use only.
*/
void formatDefaultWKT(final Formatter formatter) {
final Unit<?> unit = getUnit();
formatter.append(unit);
final int dimension = coordinateSystem.getDimension();
for (int i=0; i<dimension; i++) {
formatter.append(coordinateSystem.getAxis(i));
}
if (unit == null) {
formatter.setInvalidWKT(CoordinateReferenceSystem.class);
}
}
}