001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    http://www.geoapi.org
004 *
005 *    Copyright (C) 2008-2019 Open Geospatial Consortium, Inc.
006 *    All Rights Reserved. http://www.opengeospatial.org/ogc/legal
007 *
008 *    Permission to use, copy, and modify this software and its documentation, with
009 *    or without modification, for any purpose and without fee or royalty is hereby
010 *    granted, provided that you include the following on ALL copies of the software
011 *    and documentation or portions thereof, including modifications, that you make:
012 *
013 *    1. The full text of this NOTICE in a location viewable to users of the
014 *       redistributed or derivative work.
015 *    2. Notice of any changes or modifications to the OGC files, including the
016 *       date changes were made.
017 *
018 *    THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE
019 *    NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
020 *    TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
021 *    THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
022 *    PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
023 *
024 *    COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
025 *    CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
026 *
027 *    The name and trademarks of copyright holders may NOT be used in advertising or
028 *    publicity pertaining to the software without specific, written prior permission.
029 *    Title to copyright in this software and any associated documentation will at all
030 *    times remain with copyright holders.
031 */
032package org.opengis.test.referencing;
033
034import java.util.Date;
035import javax.measure.Unit;
036import javax.measure.quantity.Angle;
037import javax.measure.quantity.Length;
038
039import org.opengis.referencing.datum.*;
040import org.opengis.test.ValidatorContainer;
041import static org.opengis.test.Assert.*;
042
043
044/**
045 * Validates {@link Datum} and related objects from the {@code org.opengis.datum} package.
046 *
047 * <p>This class is provided for users wanting to override the validation methods. When the default
048 * behavior is sufficient, the {@link org.opengis.test.Validators} static methods provide a more
049 * convenient way to validate various kinds of objects.</p>
050 *
051 * @author  Martin Desruisseaux (Geomatys)
052 * @version 3.1
053 * @since   2.2
054 */
055public class DatumValidator extends ReferencingValidator {
056    /**
057     * Creates a new validator instance.
058     *
059     * @param container  the set of validators to use for validating other kinds of objects
060     *                   (see {@linkplain #container field javadoc}).
061     */
062    public DatumValidator(ValidatorContainer container) {
063        super(container, "org.opengis.referencing.datum");
064    }
065
066    /**
067     * For each interface implemented by the given object, invokes the corresponding
068     * {@code validate(…)} method defined in this class (if any).
069     *
070     * @param  object  the object to dispatch to {@code validate(…)} methods, or {@code null}.
071     * @return number of {@code validate(…)} methods invoked in this class for the given object.
072     */
073    public int dispatch(final Datum object) {
074        int n = 0;
075        if (object != null) {
076            if (object instanceof GeodeticDatum)    {validate((GeodeticDatum)    object); n++;}
077            if (object instanceof VerticalDatum)    {validate((VerticalDatum)    object); n++;}
078            if (object instanceof TemporalDatum)    {validate((TemporalDatum)    object); n++;}
079            if (object instanceof ImageDatum)       {validate((ImageDatum)       object); n++;}
080            if (object instanceof EngineeringDatum) {validate((EngineeringDatum) object); n++;}
081            if (n == 0) {
082                validateIdentifiedObject(object);
083            }
084        }
085        return n;
086    }
087
088    /**
089     * Validates the given prime meridian.
090     *
091     * @param  object  the object to validate, or {@code null}.
092     */
093    public void validate(final PrimeMeridian object) {
094        if (object == null) {
095            return;
096        }
097        validateIdentifiedObject(object);
098        final Unit<Angle> unit = object.getAngularUnit();
099        mandatory("PrimeMeridian: shall have a unit of measurement.", unit);
100        double longitude = object.getGreenwichLongitude();
101        if (unit != null) {
102            final Unit<Angle> degree = units.degree();
103            assertTrue("PrimeMeridian: unit must be compatible with degrees.", unit.isCompatible(degree));
104            longitude = unit.getConverterTo(degree).convert(longitude);
105        }
106        assertBetween("PrimeMeridian: expected longitude in [-180 … +180]° range.", -180, +180, longitude);
107    }
108
109    /**
110     * Validates the given ellipsoid.
111     * This method checks the following conditions:
112     *
113     * <ul>
114     *   <li>{@linkplain Ellipsoid#getAxisUnit() Axis unit} is defined and is linear.</li>
115     *   <li>{@linkplain Ellipsoid#getSemiMinorAxis() semi-minor} &lt;= {@linkplain Ellipsoid#getSemiMajorAxis() semi-major}.</li>
116     *   <li>{@linkplain Ellipsoid#getInverseFlattening() inverse flattening} &gt; 0.</li>
117     *   <li>Consistency of semi-minor axis length with inverse flattening factor.</li>
118     * </ul>
119     *
120     * @param  object  the object to validate, or {@code null}.
121     */
122    public void validate(final Ellipsoid object) {
123        if (object == null) {
124            return;
125        }
126        validateIdentifiedObject(object);
127        final Unit<Length> unit = object.getAxisUnit();
128        mandatory("Ellipsoid: shall have a unit of measurement.", unit);
129        if (unit != null) {
130            assertTrue("Ellipsoid: unit must be compatible with metres.", unit.isCompatible(units.metre()));
131        }
132        final double semiMajor         = object.getSemiMajorAxis();
133        final double semiMinor         = object.getSemiMinorAxis();
134        final double inverseFlattening = object.getInverseFlattening();
135        assertTrue("Ellipsoid: expected semi-major axis length > 0.", semiMajor > 0);
136        assertTrue("Ellipsoid: expected semi-minor axis length > 0.", semiMinor > 0);
137        assertTrue("Ellipsoid: expected semi-minor <= semi-major axis length.", semiMinor <= semiMajor);
138        assertTrue("Ellipsoid: expected inverse flattening > 0.", inverseFlattening > 0);
139        if (!object.isSphere()) {
140            assertEquals("Ellipsoid: inconsistent semi-major axis length.",
141                    semiMajor - semiMajor/inverseFlattening, semiMinor, semiMinor*DEFAULT_TOLERANCE);
142            assertEquals("Ellipsoid: inconsistent inverse flattening.",
143                    semiMajor / (semiMajor-semiMinor), inverseFlattening, inverseFlattening*DEFAULT_TOLERANCE);
144        }
145    }
146
147    /**
148     * Validates the given datum.
149     *
150     * @param  object  the object to validate, or {@code null}.
151     */
152    public void validate(final GeodeticDatum object) {
153        if (object == null) {
154            return;
155        }
156        validateIdentifiedObject(object);
157        final PrimeMeridian meridian = object.getPrimeMeridian();
158        mandatory("GeodeticDatum: shall have a prime meridian.", meridian);
159        validate(meridian);
160
161        final Ellipsoid ellipsoid = object.getEllipsoid();
162        mandatory("GeodeticDatum: shall have an ellipsoid.", ellipsoid);
163        validate(ellipsoid);
164    }
165
166    /**
167     * Validates the given datum.
168     *
169     * @param  object  the object to validate, or {@code null}.
170     */
171    public void validate(final VerticalDatum object) {
172        if (object == null) {
173            return;
174        }
175        validateIdentifiedObject(object);
176        final VerticalDatumType type = object.getVerticalDatumType();
177        mandatory("VerticalDatum: shall have a datum type.", type);
178    }
179
180    /**
181     * Validates the given datum.
182     *
183     * @param  object  the object to validate, or {@code null}.
184     */
185    public void validate(final TemporalDatum object) {
186        if (object == null) {
187            return;
188        }
189        validateIdentifiedObject(object);
190        final Date origin = object.getOrigin();
191        mandatory("TemporalDatum: expected an origin.", origin);
192        forbidden("TemporalDatum: should not have anchor point.", object.getAnchorPoint());
193        forbidden("TemporalDatum: should not have realization epoch.", object.getRealizationEpoch());
194    }
195
196    /**
197     * Validates the given datum.
198     *
199     * @param  object  the object to validate, or {@code null}.
200     */
201    public void validate(final ImageDatum object) {
202        if (object == null) {
203            return;
204        }
205        validateIdentifiedObject(object);
206        final PixelInCell pc = object.getPixelInCell();
207        mandatory("ImageDatum: shall specify PixelInCell.", pc);
208    }
209
210    /**
211     * Validates the given datum.
212     *
213     * @param  object  the object to validate, or {@code null}.
214     */
215    public void validate(final EngineeringDatum object) {
216        if (object == null) {
217            return;
218        }
219        validateIdentifiedObject(object);
220    }
221}