blob: 58a22e5fb3d7327ec05df2478fbe3961ccf8aa43 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.referencing.internal;
import java.util.Locale;
import java.util.Collection;
import javax.measure.Unit;
import org.opengis.util.GenericName;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.cs.AxisDirection;
import org.apache.sis.util.Characters;
import org.apache.sis.util.CharSequences;
import org.apache.sis.measure.Units;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import org.opengis.referencing.datum.RealizationMethod;
/**
* Extensions to the standard set of {@link RealizationEpoch}.
* Some of those constants are derived from a legacy {@code VerticalDatumType} code list.
* Those constants are not in public API because they were intentionally omitted from ISO 19111,
* and the ISO experts said that they should really not be public.
*
* @author Martin Desruisseaux (IRD, Geomatys)
*/
public final class VerticalDatumTypes {
/**
* A pseudo-realization method for ellipsoidal heights that are measured along
* the normal to the ellipsoid used in the definition of horizontal datum.
* <strong>The use of this method is deprecated</strong> as ellipsoidal height
* should never be separated from the horizontal components according ISO 19111.
*
* <h4>Legacy</h4>
* This type was associated to code 2000 in the {@code Vert_Datum} element of the legacy WKT 1 format.
* The UML identifier was {@code CS_DatumType.CS_VD_Ellipsoidal}.
*
* @see org.apache.sis.referencing.CommonCRS.Vertical#ELLIPSOIDAL
*/
static final String ELLIPSOIDAL = "ELLIPSOIDAL";
/**
* A vertical datum type for orthometric heights that are measured along the plumb line.
*
* <h4>Legacy</h4>
* This type was associated to code 2001 in the {@code Vert_Datum} element of the legacy WKT 1 format.
* The UML identifier was {@code CS_DatumType.CS_VD_Orthometric}.
*/
private static final String ORTHOMETRIC = "ORTHOMETRIC";
/**
* A vertical datum type for origin of the vertical axis based on atmospheric pressure.
*
* <h4>Legacy</h4>
* This type was associated to code 2003 in the {@code Vert_Datum} element of the legacy WKT 1 format.
* The UML identifier was {@code CS_DatumType.CS_VD_AltitudeBarometric}.
*
* @see org.apache.sis.referencing.CommonCRS.Vertical#BAROMETRIC
*/
static final String BAROMETRIC = "BAROMETRIC";
/**
* Do not allow instantiation of this class.
*/
private VerticalDatumTypes() {
}
/**
* Returns a pseudo-realization method for ellipsoidal heights.
* <strong>The use of this method is deprecated</strong> as ellipsoidal height
* should never be separated from the horizontal components according ISO 19111.
*
* <h4>Maintenance note</h4>
* If the implementation of this method is modified, search for {@code RealizationMethod.valueOf}
* at least in {@link org.apache.sis.referencing.CommonCRS.Vertical#datum()} and make sure that
* the code is equivalent.
*
* @return the ellipsoidal pseudo-realization method.
*/
public static RealizationMethod ellipsoidal() {
return RealizationMethod.valueOf(ELLIPSOIDAL);
}
/**
* Returns {@code true} if the given value is the ellipsoidal pseudo-realization method.
*
* @param method the method to test, or {@code null}.
* @return whether the given method is the ellipsoidal pseudo-realization method.
*/
public static boolean ellipsoidal(final RealizationMethod method) {
return (method != null) && ELLIPSOIDAL.equalsIgnoreCase(method.name());
}
/**
* Returns the vertical datum type from a legacy code. The legacy codes were defined in
* OGC 01-009 (<cite>Coordinate Transformation Services)</cite>, which also defined the version 1
* of <cite>Well Known Text</cite></a> format (WKT 1). This method is used for WKT 1 parsing.
*
* @param code the legacy vertical datum code.
* @return the vertical datum type, or {@code null} if none.
*/
public static RealizationMethod fromLegacy(final int code) {
switch (code) {
// case 2000: return null; // CS_VD_Other
case 2001: return RealizationMethod.valueOf(ORTHOMETRIC); // CS_VD_Orthometric
case 2002: return ellipsoidal(); // CS_VD_Ellipsoidal
case 2003: return RealizationMethod.valueOf(BAROMETRIC); // CS_VD_AltitudeBarometric
case 2005: return RealizationMethod.GEOID; // CS_VD_GeoidModelDerived
case 2006: return RealizationMethod.TIDAL; // CS_VD_Depth
default: return null;
}
}
/**
* Returns the legacy code for the datum type, or 2000 (other surface) if unknown.
* This method is used for WKT 1 formatting.
*
* @param method the realization method, or {@code null} if unknown.
* @return the legacy code for the given datum type, or 0 if unknown.
*/
public static int toLegacy(final RealizationMethod method) {
if (method != null) {
switch (method.name().toUpperCase(Locale.US)) {
case ORTHOMETRIC: return 2001; // CS_VD_Orthometric
case ELLIPSOIDAL: return 2002; // CS_VD_Ellipsoidal
case BAROMETRIC: return 2003; // CS_VD_AltitudeBarometric
case "GEOID": return 2005; // CS_VD_GeoidModelDerived
case "TIDAL": return 2006; // CS_VD_Depth
}
}
return 2000;
}
/**
* Returns the vertical datum type from a realization method.
* If the given method cannot be mapped to a legacy type, then this method returns "other surface".
* This is because the vertical datum type was a mandatory property in legacy OGC/ISO standards.
* This method is used for writing GML documents older than GML 3.2.
*
* @param method the realization method, or {@code null}.
* @return the vertical datum type name (never null).
*/
public static String toName(final RealizationMethod method) {
if (method == RealizationMethod.GEOID) return "geoidal";
if (method == RealizationMethod.TIDAL) return "depth";
if (method != null) {
return method.name().toLowerCase(Locale.US);
}
return "other surface";
}
/**
* Returns the realization method from a vertical datum type.
* This method is used for reading GML documents older than GML 3.2.
*
* @param type the vertical datum type, or {@code null}.
* @return the realization method, or {@code null} if none.
*/
public static RealizationMethod fromName(final String type) {
if ("GEOIDAL" .equalsIgnoreCase(type)) return RealizationMethod.GEOID;
if ("DEPTH" .equalsIgnoreCase(type)) return RealizationMethod.TIDAL;
if (BAROMETRIC .equalsIgnoreCase(type)) return RealizationMethod.valueOf(BAROMETRIC);
if (ORTHOMETRIC.equalsIgnoreCase(type)) return RealizationMethod.valueOf(ORTHOMETRIC);
if (ELLIPSOIDAL.equalsIgnoreCase(type)) return ellipsoidal();
return null;
}
/**
* Guesses the realization method of a datum from its name, aliases or a given vertical axis.
* This is sometimes needed after XML unmarshalling or WKT parsing, because GML 3.2 and ISO 19162
* do not contain any attribute for the datum type.
*
* <p>This method uses heuristic rules and may be changed in any future SIS version.</p>
*
* @param name the name of the datum for which to guess a type, or {@code null} if unknown.
* @param aliases the aliases of the datum for which to guess a type, or {@code null} if unknown.
* @param axis the vertical axis for which to guess a type, or {@code null} if unknown.
* @return a datum type, or {@code null} if none can be guessed.
*/
public static RealizationMethod guess(final String name, final Collection<? extends GenericName> aliases,
final CoordinateSystemAxis axis)
{
RealizationMethod method = guess(name);
if (method != null) {
return method;
}
if (aliases != null) {
for (final GenericName alias : aliases) {
method = guess(alias.tip().toString());
if (method != null) {
return method;
}
}
}
if (axis != null) {
final Unit<?> unit = axis.getUnit();
if (Units.isLinear(unit)) {
final String abbreviation = axis.getAbbreviation();
if (abbreviation.length() == 1) {
AxisDirection dir = AxisDirection.UP; // Expected direction for accepting the type.
switch (abbreviation.charAt(0)) {
case 'h': method = ellipsoidal(); break;
case 'H': method = RealizationMethod.GEOID; break;
case 'D': method = RealizationMethod.TIDAL; dir = AxisDirection.DOWN; break;
default: return null;
}
if (dir.equals(axis.getDirection())) {
return method;
}
}
} else if (Units.isPressure(unit)) {
return RealizationMethod.valueOf(BAROMETRIC);
}
}
return null;
}
/**
* Guesses the realization method of a datum of the given name. This method attempts to guess only if
* the given name contains at least one letter. If the type cannot be determined, returns {@code null}.
*
* @param name name of the datum for which to guess a realization method, or {@code null}.
* @return a realization method, or {@code null} if none can be guessed.
*/
private static RealizationMethod guess(final String name) {
if (name != null) {
if (CharSequences.equalsFiltered("Mean Sea Level", name, Characters.Filter.LETTERS_AND_DIGITS, true)) {
return RealizationMethod.TIDAL;
}
if (name.contains("geoid")) {
return RealizationMethod.GEOID;
}
}
return null;
}
}