001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    Copyright © 2008-2024 Open Geospatial Consortium, Inc.
004 *    http://www.geoapi.org
005 *
006 *    Licensed under the Apache License, Version 2.0 (the "License");
007 *    you may not use this file except in compliance with the License.
008 *    You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *    Unless required by applicable law or agreed to in writing, software
013 *    distributed under the License is distributed on an "AS IS" BASIS,
014 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 *    See the License for the specific language governing permissions and
016 *    limitations under the License.
017 */
018package org.opengis.test.metadata;
019
020import org.opengis.metadata.*;
021import org.opengis.metadata.extent.*;
022import org.opengis.geometry.Geometry;
023import org.opengis.test.ValidatorContainer;
024
025import static org.junit.jupiter.api.Assertions.*;
026import static org.opengis.test.Assertions.assertBetween;
027
028
029/**
030 * Validates {@link Extent} and related objects from the {@code org.opengis.metadata.extent} package.
031 *
032 * <p>This class is provided for users wanting to override the validation methods. When the default
033 * behavior is sufficient, the {@link org.opengis.test.Validators} static methods provide a more
034 * convenient way to validate various kinds of objects.</p>
035 *
036 * @author  Martin Desruisseaux (Geomatys)
037 * @version 3.1
038 * @since   2.2
039 */
040public class ExtentValidator extends MetadataValidator {
041    /**
042     * Creates a new validator instance.
043     *
044     * @param container  the set of validators to use for validating other kinds of objects
045     *                   (see {@linkplain #container field javadoc}).
046     */
047    public ExtentValidator(final ValidatorContainer container) {
048        super(container, "org.opengis.metadata.extent");
049    }
050
051    /**
052     * For each interface implemented by the given object, invokes the corresponding
053     * {@code validate(…)} method defined in this class (if any).
054     *
055     * @param  object  the object to dispatch to {@code validate(…)} methods, or {@code null}.
056     * @return number of {@code validate(…)} methods invoked in this class for the given object.
057     */
058    public int dispatch(final GeographicExtent object) {
059        int n = 0;
060        if (object != null) {
061            if (object instanceof GeographicDescription) {validate((GeographicDescription) object); n++;}
062            if (object instanceof GeographicBoundingBox) {validate((GeographicBoundingBox) object); n++;}
063            if (object instanceof BoundingPolygon)       {validate((BoundingPolygon)       object); n++;}
064        }
065        return n;
066    }
067
068    /**
069     * Validates the geographic description.
070     *
071     * @param  object  the object to validate, or {@code null}.
072     */
073    public void validate(final GeographicDescription object) {
074        if (object == null) {
075            return;
076        }
077        final Identifier identifier = object.getGeographicIdentifier();
078        mandatory(identifier, "GeographicDescription: must have an identifier.");
079    }
080
081    /**
082     * Validates the bounding polygon.
083     *
084     * @param  object  the object to validate, or {@code null}.
085     *
086     * @todo Not yet implemented.
087     */
088    public void validate(final BoundingPolygon object) {
089        if (object == null) {
090            return;
091        }
092        for (final Geometry e : toArray(Geometry.class, object.getPolygons())) {
093            // TODO
094        }
095    }
096
097    /**
098     * Validates the geographic bounding box.
099     *
100     * @param  object  the object to validate, or {@code null}.
101     */
102    public void validate(final GeographicBoundingBox object) {
103        if (object == null) {
104            return;
105        }
106        final double west  = object.getWestBoundLongitude();
107        final double east  = object.getEastBoundLongitude();
108        final double south = object.getSouthBoundLatitude();
109        final double north = object.getNorthBoundLatitude();
110        assertBetween(-180, +180, west, "GeographicBoundingBox: illegal west bound.");
111        assertBetween(-180, +180, east, "GeographicBoundingBox: illegal east bound.");
112        assertBetween(-90,   +90, south, "GeographicBoundingBox: illegal south bound.");
113        assertBetween(-90,   +90, north, "GeographicBoundingBox: illegal north bound.");
114        assertFalse(south > north, "GeographicBoundingBox: invalid range of latitudes.");       // Accept NaN.
115        /*
116         * Do not require west <= east, as this condition is not specified in ISO 19115.
117         * Some implementations may use west > east for box spanning the anti-meridian.
118         */
119    }
120
121    /**
122     * Validates the vertical extent.
123     *
124     * @param  object  the object to validate, or {@code null}.
125     */
126    public void validate(final VerticalExtent object) {
127        if (object == null) {
128            return;
129        }
130        final Double minimum = object.getMinimumValue();
131        final Double maximum = object.getMaximumValue();
132        mandatory(minimum, "VerticalExtent: must have a minimum value.");
133        mandatory(maximum, "VerticalExtent: must have a maximum value.");
134        if (minimum != null && maximum != null) {
135            assertTrue(minimum <= maximum, "VerticalExtent: invalid range.");
136        }
137        container.validate(object.getVerticalCRS());
138    }
139
140    /**
141     * Validates the temporal extent.
142     *
143     * @param  object  the object to validate, or {@code null}.
144     *
145     * @todo Validation of temporal primitives not yet implemented.
146     */
147    public void validate(final TemporalExtent object) {
148        if (object == null) {
149            return;
150        }
151        if (object instanceof SpatialTemporalExtent) {
152            for (final GeographicExtent e : toArray(GeographicExtent.class, ((SpatialTemporalExtent) object).getSpatialExtent())) {
153                dispatch(e);
154            }
155        }
156    }
157
158    /**
159     * Validates the given extent.
160     *
161     * @param  object  the object to validate, or {@code null}.
162     */
163    public void validate(final Extent object) {
164        if (object == null) {
165            return;
166        }
167        validateOptional(object.getDescription());
168        for (GeographicExtent e : toArray(GeographicExtent.class, object.getGeographicElements())) dispatch(e);
169        for (VerticalExtent   e : toArray(VerticalExtent  .class, object.getVerticalElements  ())) validate(e);
170        for (TemporalExtent   e : toArray(TemporalExtent  .class, object.getTemporalElements  ())) validate(e);
171    }
172}