/* * 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.cs; import java.util.List; import java.util.Arrays; import java.util.Comparator; import java.util.Map; import javax.measure.unit.SI; import javax.measure.unit.Unit; import org.opengis.referencing.cs.*; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; /** * Converts an arbitrary CS into one of the predefined constants provided in the * {@link org.geotools.referencing.cs} package. The main usage for this class is * to reorder the axis in some "standard" order like (<var>x</var>, <var>y</var>, * <var>z</var>) or (<var>longitude</var>, <var>latitude</var>). What "standard" * order means is sometime an arbitrary choice, which explain why this class is * not public at this time. * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) */ final class PredefinedCS implements Comparator<CoordinateSystem> { /** * An instance of {@link PredefinedCS}. Will be created only when first needed. */ private static Comparator<CoordinateSystem> csComparator; /** * Our ordering for coordinate system objects. */ @SuppressWarnings("unchecked") private final Class<? extends CoordinateSystem>[] types = new Class[] { CartesianCS .class, AffineCS .class, EllipsoidalCS.class, SphericalCS .class, CylindricalCS.class, PolarCS .class, VerticalCS .class, TimeCS .class, LinearCS .class, UserDefinedCS.class }; /** * Creates a comparator. */ private PredefinedCS() { } /** * Compares the ordering between two coordinate systems. This comparator is used for sorting * the axis in an user-supplied compound CS in an order closes to some "standard" order. */ public int compare(final CoordinateSystem object1, final CoordinateSystem object2) { final Class<? extends CoordinateSystem> type1 = object1.getClass(); final Class<? extends CoordinateSystem> type2 = object2.getClass(); for (int i=0; i<types.length; i++) { final Class<?> type = types[i]; final boolean a1 = type.isAssignableFrom(type1); final boolean a2 = type.isAssignableFrom(type2); if (a1) return a2 ? 0 : -1; if (a2) return a1 ? 0 : +1; } return 0; } /** * Implementation of the {@link AbstractCS#standard} method. */ static CoordinateSystem standard(final CoordinateSystem cs) throws IllegalArgumentException { final int dimension = cs.getDimension(); if (cs instanceof CartesianCS) { switch (dimension) { case 2: { if (DefaultCartesianCS.PROJECTED.axisColinearWith(cs)) { return DefaultCartesianCS.PROJECTED; } if (DefaultCartesianCS.GRID.axisColinearWith(cs)) { return DefaultCartesianCS.GRID; } if (DefaultCartesianCS.GENERIC_2D.directionColinearWith(cs)) { return DefaultCartesianCS.GENERIC_2D; } return rightHanded((CartesianCS) cs); } case 3: { if (DefaultCartesianCS.GEOCENTRIC.axisColinearWith(cs)) { return DefaultCartesianCS.GEOCENTRIC; } if (DefaultCartesianCS.GENERIC_3D.directionColinearWith(cs)) { return DefaultCartesianCS.GENERIC_3D; } return rightHanded((CartesianCS) cs); } } } if (cs instanceof AffineCS) { return rightHanded((AffineCS) cs); } if (cs instanceof EllipsoidalCS) { switch (dimension) { case 2: return DefaultEllipsoidalCS.GEODETIC_2D; case 3: return DefaultEllipsoidalCS.GEODETIC_3D; } } if (cs instanceof SphericalCS) { switch (dimension) { case 3: return DefaultSphericalCS.GEOCENTRIC; } } if (cs instanceof VerticalCS) { switch (dimension) { case 1: { return DefaultVerticalCS.ELLIPSOIDAL_HEIGHT; } } } if (cs instanceof TimeCS) { switch (dimension) { case 1: return DefaultTimeCS.DAYS; } } if (cs instanceof DefaultCompoundCS) { final List<CoordinateSystem> components = ((DefaultCompoundCS) cs).getCoordinateSystems(); final CoordinateSystem[] user = new CoordinateSystem[components.size()]; final CoordinateSystem[] std = new CoordinateSystem[user.length]; for (int i=0; i<std.length; i++) { std[i] = standard(user[i] = components.get(i)); } if (csComparator == null) { csComparator = new PredefinedCS(); } Arrays.sort(std, csComparator); return Arrays.equals(user, std) ? cs : new DefaultCompoundCS(std); } throw new IllegalArgumentException( Errors.format(ErrorKeys.UNSUPPORTED_COORDINATE_SYSTEM_$1, cs.getName().getCode())); } /** * Reorder the axis in the specified Affine CS in an attempt to get a right-handed system. * Units are standardized to meters in the process. If no axis change is needed, then this * method returns {@code cs} unchanged. */ private static AffineCS rightHanded(final AffineCS cs) { boolean changed = false; final int dimension = cs.getDimension(); final CoordinateSystemAxis[] axis = new CoordinateSystemAxis[dimension]; for (int i=0; i<dimension; i++) { /* * Gets the axis and replaces it by one of the predefined constants declared in * DefaultCoordinateSystemAxis, if possible. The predefined constants use ISO 19111 * names with metres or degrees units, so it is pretty close to the "standard" axis * we are looking for. */ CoordinateSystemAxis axe = axis[i] = cs.getAxis(i); DefaultCoordinateSystemAxis standard = DefaultCoordinateSystemAxis.getPredefined(axe); if (standard != null) { axe = standard; } /* * Changes units to meters. Every units in an affine CS should be linear or * dimensionless (the later is used for grid coordinates). The 'usingUnit' * method will thrown an exception if the unit is incompatible. See * DefaultAffineCS.isCompatibleUnit(Unit). */ final Unit<?> unit = axe.getUnit(); if (!Unit.ONE.equals(unit) && !SI.METER.equals(unit)) { if (!(axe instanceof DefaultCoordinateSystemAxis)) { axe = new DefaultCoordinateSystemAxis(axe); } axe = ((DefaultCoordinateSystemAxis) axe).usingUnit(SI.METER); } changed |= (axe != axis[i]); axis[i] = axe; } /* * Sorts the axis in an attempt to create a right-handed system * and creates a new Coordinate System if at least one axis changed. */ changed |= ComparableAxisWrapper.sort(axis); if (!changed) { return cs; } final Map<String,?> properties = DefaultAffineCS.getProperties(cs, null); if (cs instanceof CartesianCS) { return new DefaultCartesianCS(properties, axis); } return new DefaultAffineCS(properties, axis); } }