blob: 8c79280e9214ae5718beddd917b8e43c54316d08 [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.geometry;
import java.awt.geom.Rectangle2D;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.DirectPosition;
import org.apache.sis.referencing.crs.DefaultGeographicCRS;
import org.apache.sis.referencing.crs.HardCodedCRS;
import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestCase;
import org.junit.Test;
import static java.lang.Double.NaN;
import static org.opengis.test.Validators.*;
import static org.apache.sis.test.ReferencingAssert.*;
/**
* Tests the methods defined in the {@link AbstractEnvelope} class.
* Various implementations are used for each test.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 0.8
* @since 0.3
* @module
*/
@DependsOn(GeneralDirectPositionTest.class)
public final strictfp class AbstractEnvelopeTest extends TestCase {
/**
* Enumeration of implementations to be tested.
* The {@code LAST} constant is for stopping the loops.
*/
private static final int GENERAL=0, IMMUTABLE=1, RECTANGLE=2, SUBENVELOPE=3, LAST=4;
/**
* The coordinate reference system used for the tests.
*/
static final DefaultGeographicCRS WGS84 = HardCodedCRS.WGS84;
/**
* Creates an envelope of the given type. The type shall be one of the
* {@link #GENERAL}, {@link #IMMUTABLE} or {@link #RECTANGLE} constants.
*/
private static Envelope create(final int type,
final double xmin, final double xmax,
final double ymin, final double ymax)
{
final Envelope envelope;
switch (type) {
case GENERAL: {
final GeneralEnvelope ge = new GeneralEnvelope(2);
ge.setCoordinateReferenceSystem(WGS84);
ge.setRange(0, xmin, xmax);
ge.setRange(1, ymin, ymax);
envelope = ge;
break;
}
case IMMUTABLE: {
envelope = new ImmutableEnvelope(new double[] {xmin, ymin}, new double[] {xmax, ymax}, WGS84);
break;
}
case RECTANGLE: {
envelope = new Envelope2D(WGS84, xmin, ymin, xmax - xmin, ymax - ymin);
break;
}
case SUBENVELOPE: {
GeneralEnvelope ge = new GeneralEnvelope(5);
ge.setRange(1, xmin, xmax);
ge.setRange(2, ymin, ymax);
ge.setRange(0, 2, 3); // Following values will be verified in verifyInvariants(…)
ge.setRange(3, 4, 6);
ge.setRange(4, 8, 9);
ge = ge.subEnvelope(1, 3);
ge.setCoordinateReferenceSystem(WGS84);
envelope = ge;
break;
}
default: throw new IllegalArgumentException(String.valueOf(type));
}
if (PENDING_NEXT_GEOAPI_RELEASE) {
validate(envelope);
}
return envelope;
}
/**
* Verifies some invariants for the given envelope of the given type.
*/
private static void verifyInvariants(final int type, final Envelope envelope) {
assertSame(WGS84, envelope.getCoordinateReferenceSystem());
switch (type) {
case SUBENVELOPE: {
// Asserts that other dimensions in the original envelope has not been modified.
final double[] coordinates = ((SubEnvelope) envelope).coordinates;
assertEquals(2, coordinates[0], STRICT);
assertEquals(3, coordinates[5], STRICT);
assertEquals(4, coordinates[3], STRICT);
assertEquals(6, coordinates[8], STRICT);
assertEquals(8, coordinates[4], STRICT);
assertEquals(9, coordinates[9], STRICT);
break;
}
}
}
/**
* Tests the simple case (no anti-meridian crossing).
*
* {@preformat text
* ┌─────────────┐
* │ ┌───────┐ │
* │ └───────┘ │
* └─────────────┘
* }
*/
@Test
public void testSimpleEnvelope() {
final DirectPosition2D inside = new DirectPosition2D( 3, 32);
final DirectPosition2D outside = new DirectPosition2D(-5, 32);
final Envelope2D contained = (Envelope2D) create(RECTANGLE, -2, 10, 35, 40);
final Envelope2D intersect = (Envelope2D) create(RECTANGLE, -2, 16, 35, 40);
final Envelope2D disjoint = (Envelope2D) create(RECTANGLE, 14, 16, 35, 40);
for (int type=0; type<LAST; type++) {
final String label = "Type " + type;
final Envelope envelope = create(type, -4, 12, 30, 50);
assertEquals(label, 30, envelope.getMinimum(1), STRICT);
assertEquals(label, 50, envelope.getMaximum(1), STRICT);
assertEquals(label, 40, envelope.getMedian (1), STRICT);
assertEquals(label, 20, envelope.getSpan (1), STRICT);
assertEquals(label, -4, envelope.getMinimum(0), STRICT);
assertEquals(label, 12, envelope.getMaximum(0), STRICT);
assertEquals(label, 4, envelope.getMedian (0), STRICT);
assertEquals(label, 16, envelope.getSpan (0), STRICT);
switch (type) {
default: {
final AbstractEnvelope ext = (AbstractEnvelope) envelope;
assertTrue (label, ext.contains (inside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect, false));
assertTrue (label, ext.intersects(intersect, false));
assertDisjoint(ext, disjoint);
assertContains(ext, contained);
break;
}
case RECTANGLE: {
final Rectangle2D ext = (Rectangle2D) envelope;
assertTrue (label, ext.contains (inside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect));
assertTrue (label, ext.intersects(intersect));
assertDisjoint(ext, disjoint);
assertContains(ext, contained);
break;
}
}
verifyInvariants(type, envelope);
}
}
/**
* Tests a case crossing the anti-meridian.
*
* {@preformat text
* ─────┐ ┌───────── ─────┐ ┌─────
* │ │ ┌────┐ or ──┐ │ │ ┌──
* │ │ └────┘ ──┘ │ │ └──
* ─────┘ └───────── ─────┘ └─────
* }
*/
@Test
public void testCrossingAntiMeridian() {
final DirectPosition2D inside = new DirectPosition2D(18, 32);
final DirectPosition2D outside = new DirectPosition2D( 3, 32);
final Envelope2D contained = (Envelope2D) create(RECTANGLE, 14, 16, 35, 40);
final Envelope2D intersect = (Envelope2D) create(RECTANGLE, -2, 16, 35, 40);
final Envelope2D disjoint = (Envelope2D) create(RECTANGLE, -2, 10, 35, 40);
final Envelope2D spanning = (Envelope2D) create(RECTANGLE, 16, -8, 35, 40);
for (int type=0; type<LAST; type++) {
final String label = "Type " + type;
final Envelope envelope = create(type, 12, -4, 30, 50);
final DirectPosition lower = envelope.getLowerCorner();
final DirectPosition upper = envelope.getUpperCorner();
assertEquals(label, 30, envelope.getMinimum (1), STRICT);
assertEquals(label, 50, envelope.getMaximum (1), STRICT);
assertEquals(label, 40, envelope.getMedian (1), STRICT);
assertEquals(label, 20, envelope.getSpan (1), STRICT);
assertEquals(label, 12, lower .getOrdinate(0), STRICT);
assertEquals(label, -180, envelope.getMinimum (0), STRICT);
assertEquals(label, -4, upper .getOrdinate(0), STRICT);
assertEquals(label, +180, envelope.getMaximum (0), STRICT);
assertEquals(label, -176, envelope.getMedian (0), STRICT);
assertEquals(label, 344, envelope.getSpan (0), STRICT); // 360° - testSimpleEnvelope()
switch (type) {
default: {
final AbstractEnvelope ext = (AbstractEnvelope) envelope;
assertTrue (label, ext.contains (inside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect, false));
assertTrue (label, ext.intersects(intersect, false));
assertDisjoint(ext, disjoint);
assertContains(ext, contained);
assertContains(ext, spanning);
break;
}
case RECTANGLE: {
final Rectangle2D ext = (Rectangle2D) envelope;
assertTrue (label, ext.contains (inside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect));
assertTrue (label, ext.intersects(intersect));
assertDisjoint(ext, disjoint);
assertContains(ext, contained);
assertContains(ext, spanning);
break;
}
}
verifyInvariants(type, envelope);
}
}
/**
* Tests a the anti-meridian case with a larger empty space
* on the left side.
*
* {@preformat text
* ───┐ ┌───────── ───┐ ┌─────
* │ │ ┌────┐ or ───┼──┐ │ ┌──
* │ │ └────┘ ───┼──┘ │ └──
* ───┘ └───────── ───┘ └─────
* }
*/
@Test
public void testCrossingAntiMeridianTwice() {
final DirectPosition2D inside = new DirectPosition2D(18, 32);
final DirectPosition2D outside = new DirectPosition2D( 3, 32);
final Envelope2D contained = (Envelope2D) create(RECTANGLE, 14, 16, 35, 40);
final Envelope2D intersect = (Envelope2D) create(RECTANGLE, -2, 16, 35, 40);
final Envelope2D disjoint = (Envelope2D) create(RECTANGLE, -2, 10, 35, 40);
final Envelope2D spanning = (Envelope2D) create(RECTANGLE, 16, -8, 35, 40);
for (int type=0; type<LAST; type++) {
final String label = "Type " + type;
final Envelope envelope = create(type, 12, -364, 30, 50);
final DirectPosition lower = envelope.getLowerCorner();
final DirectPosition upper = envelope.getUpperCorner();
assertEquals(label, 30, envelope.getMinimum (1), STRICT);
assertEquals(label, 50, envelope.getMaximum (1), STRICT);
assertEquals(label, 40, envelope.getMedian (1), STRICT);
assertEquals(label, 20, envelope.getSpan (1), STRICT);
assertEquals(label, 12, lower .getOrdinate(0), STRICT);
assertEquals(label, -180, envelope.getMinimum (0), STRICT);
assertEquals(label, -364, upper .getOrdinate(0), STRICT);
assertEquals(label, +180, envelope.getMaximum (0), STRICT);
assertEquals(label, 4, envelope.getMedian (0), STRICT); // Note the alternance with the previous test methods.
assertEquals(label, NaN, envelope.getSpan (0), STRICT); // testCrossingAntiMeridian() + 360°.
if (envelope instanceof AbstractEnvelope) {
final AbstractEnvelope ext = (AbstractEnvelope) envelope;
assertTrue (label, ext.contains (inside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect, false));
assertTrue (label, ext.intersects(intersect, false));
assertFalse(label, ext.contains (spanning, false));
assertTrue (label, ext.intersects(spanning, false));
assertDisjoint(ext, disjoint);
assertContains(ext, contained);
break;
}
if (envelope instanceof Rectangle2D) {
final Rectangle2D ext = (Rectangle2D) envelope;
assertTrue (label, ext.contains (inside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect));
assertTrue (label, ext.intersects(intersect));
assertFalse(label, ext.contains (spanning));
assertTrue (label, ext.intersects(spanning));
assertDisjoint(ext, disjoint);
assertContains(ext, contained);
break;
}
verifyInvariants(type, envelope);
}
}
/**
* Tests a the anti-meridian case with a larger empty space
* on the left and right sides.
*/
@Test
public void testCrossingAntiMeridianThreeTimes() {
final DirectPosition2D wasInside = new DirectPosition2D(18, 32);
final DirectPosition2D outside = new DirectPosition2D( 3, 32);
final Envelope2D wasContained = (Envelope2D) create(RECTANGLE, 14, 16, 35, 40);
final Envelope2D wasIntersect = (Envelope2D) create(RECTANGLE, -2, 16, 35, 40);
final Envelope2D disjoint = (Envelope2D) create(RECTANGLE, -2, 10, 35, 40);
final Envelope2D spanning = (Envelope2D) create(RECTANGLE, 16, -8, 35, 40);
for (int type=0; type<LAST; type++) {
final String label = "Type " + type;
final Envelope envelope = create(type, 372, -364, 30, 50);
final DirectPosition lower = envelope.getLowerCorner();
final DirectPosition upper = envelope.getUpperCorner();
assertEquals(label, 30, envelope.getMinimum (1), STRICT);
assertEquals(label, 50, envelope.getMaximum (1), STRICT);
assertEquals(label, 40, envelope.getMedian (1), STRICT);
assertEquals(label, 20, envelope.getSpan (1), STRICT);
assertEquals(label, 372, lower .getOrdinate(0), STRICT);
assertEquals(label, -180, envelope.getMinimum (0), STRICT);
assertEquals(label, -364, upper .getOrdinate(0), STRICT);
assertEquals(label, +180, envelope.getMaximum (0), STRICT);
assertEquals(label, -176, envelope.getMedian (0), STRICT); // Note the alternance with the previous test methods.
assertEquals(label, NaN, envelope.getSpan (0), STRICT); // testCrossingAntiMeridianTwice() + 360°.
switch (type) {
default: {
final AbstractEnvelope ext = (AbstractEnvelope) envelope;
assertFalse(label, ext.contains (wasInside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (spanning, false));
assertTrue (label, ext.intersects(spanning, false));
assertDisjoint(ext, wasIntersect);
assertDisjoint(ext, disjoint);
assertDisjoint(ext, wasContained);
break;
}
case RECTANGLE: {
final Rectangle2D ext = (Rectangle2D) envelope;
assertFalse(label, ext.contains (wasInside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (spanning));
assertTrue (label, ext.intersects(spanning));
assertDisjoint(ext, wasIntersect);
assertDisjoint(ext, disjoint);
assertDisjoint(ext, wasContained);
break;
}
}
verifyInvariants(type, envelope);
}
}
/**
* Tests an empty envelope from -0 to 0°
*/
@Test
public void testRange0() {
final DirectPosition2D wasInside = new DirectPosition2D(18, 32);
final DirectPosition2D outside = new DirectPosition2D( 3, 32);
final Envelope2D wasContained = (Envelope2D) create(RECTANGLE, 14, 16, 35, 40);
final Envelope2D intersect = (Envelope2D) create(RECTANGLE, -2, 16, 35, 40);
final Envelope2D spanning = (Envelope2D) create(RECTANGLE, 16, -8, 35, 40);
for (int type=0; type<LAST; type++) {
final String label = "Type " + type;
final Envelope envelope = create(type, -0.0, 0.0, 30, 50);
assertEquals(label, 30, envelope.getMinimum(1), STRICT);
assertEquals(label, 50, envelope.getMaximum(1), STRICT);
assertEquals(label, 40, envelope.getMedian (1), STRICT);
assertEquals(label, 20, envelope.getSpan (1), STRICT);
assertEquals(label, -0.0, envelope.getMinimum(0), STRICT);
assertEquals(label, 0.0, envelope.getMaximum(0), STRICT);
assertEquals(label, 0, envelope.getMedian (0), STRICT);
assertEquals(label, 0, envelope.getSpan (0), STRICT);
switch (type) {
default: {
final AbstractEnvelope ext = (AbstractEnvelope) envelope;
assertFalse(label, ext.contains (wasInside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect, false));
assertTrue (label, ext.intersects(intersect, false));
assertFalse(label, ext.contains (spanning, false));
assertFalse(label, ext.intersects(spanning, false));
assertFalse(label, ext.intersects(spanning, false));
assertDisjoint(ext, wasContained);
break;
}
case RECTANGLE: {
final Rectangle2D ext = (Rectangle2D) envelope;
assertFalse(label, ext.contains (wasInside));
assertFalse(label, ext.contains (outside));
assertFalse(label, ext.contains (intersect));
assertTrue (label, ext.intersects(intersect));
assertFalse(label, ext.contains (spanning));
assertFalse(label, ext.intersects(spanning));
assertFalse(label, ext.intersects(spanning));
assertDisjoint(ext, wasContained);
break;
}
}
verifyInvariants(type, envelope);
}
}
/**
* Tests a case crossing the anti-meridian crossing, from 0° to -0°.
*/
@Test
public void testRange360() {
final DirectPosition2D inside = new DirectPosition2D(18, 32);
final DirectPosition2D wasOutside = new DirectPosition2D( 3, 32);
final Envelope2D contained = (Envelope2D) create(RECTANGLE, 14, 16, 35, 40);
final Envelope2D intersect = (Envelope2D) create(RECTANGLE, -2, 16, 35, 40);
final Envelope2D spanning = (Envelope2D) create(RECTANGLE, 16, -8, 35, 40);
for (int type=0; type<LAST; type++) {
final String label = "Type " + type;
final Envelope envelope = create(type, 0.0, -0.0, 30, 50);
final DirectPosition lower = envelope.getLowerCorner();
final DirectPosition upper = envelope.getUpperCorner();
assertEquals(label, 30, envelope.getMinimum (1), STRICT);
assertEquals(label, 50, envelope.getMaximum (1), STRICT);
assertEquals(label, 40, envelope.getMedian (1), STRICT);
assertEquals(label, 20, envelope.getSpan (1), STRICT);
assertEquals(label, 0.0, lower .getOrdinate(0), STRICT);
assertEquals(label, -180, envelope.getMinimum (0), STRICT);
assertEquals(label, -0.0, upper .getOrdinate(0), STRICT);
assertEquals(label, +180, envelope.getMaximum (0), STRICT);
assertEquals(label, 180, envelope.getMedian (0), STRICT);
assertEquals(label, 360, envelope.getSpan (0), STRICT);
switch (type) {
default: {
final AbstractEnvelope ext = (AbstractEnvelope) envelope;
assertTrue(label, ext.contains(inside));
assertTrue(label, ext.contains(wasOutside));
assertTrue(label, ext.intersects(intersect, false));
assertContains(ext, contained);
assertContains(ext, spanning);
break;
}
case RECTANGLE: {
final Rectangle2D ext = (Rectangle2D) envelope;
assertTrue(label, ext.contains(inside));
assertTrue(label, ext.contains(wasOutside));
assertTrue(label, ext.intersects(intersect));
assertContains(ext, contained);
assertContains(ext, spanning);
break;
}
}
verifyInvariants(type, envelope);
}
}
/**
* Tests {@link AbstractEnvelope#toSimpleEnvelopes()} on an empty envelope.
*
* @since 0.4
*/
@Test
public void testToSimpleEnvelopesOnEmptyEnvelope() {
for (int type=0; type<LAST; type++) {
if (type != RECTANGLE) {
final AbstractEnvelope envelope = (AbstractEnvelope) create(type, 0, 0, 0, 0);
assertEquals(0, envelope.toSimpleEnvelopes().length);
}
}
}
/**
* Tests {@link AbstractEnvelope#toSimpleEnvelopes()} on a simple envelope having no wraparound axis.
*
* @since 0.4
*/
@Test
@DependsOnMethod("testToSimpleEnvelopesOnEmptyEnvelope")
public void testToSimpleEnvelopesOnSimpleEnvelope() {
for (int type=0; type<LAST; type++) {
if (type != RECTANGLE) {
final AbstractEnvelope envelope = (AbstractEnvelope) create(type, -20, 30, -10, 15);
final Envelope[] simples = envelope.toSimpleEnvelopes();
assertEquals(1, simples.length);
assertSame(envelope, simples[0]);
}
}
}
/**
* Tests {@link AbstractEnvelope#toSimpleEnvelopes()} on a simple envelope having no wraparound axis.
*
* @since 0.4
*/
@Test
@DependsOnMethod("testToSimpleEnvelopesOnEmptyEnvelope")
public void testToSimpleEnvelopesOverAntiMeridian() {
for (int type=0; type<LAST; type++) {
if (type != RECTANGLE) {
final AbstractEnvelope envelope = (AbstractEnvelope) create(type, 155, -150, 0, 50);
final Envelope[] simples = envelope.toSimpleEnvelopes();
assertEquals(2, simples.length);
final AbstractEnvelope e0 = (AbstractEnvelope) simples[0];
final AbstractEnvelope e1 = (AbstractEnvelope) simples[1];
assertEquals( 155.0, e0.getLower(0), STRICT);
assertEquals( 0.0, e0.getLower(1), STRICT);
assertEquals( 180.0, e0.getUpper(0), STRICT);
assertEquals( 50.0, e0.getUpper(1), STRICT);
assertEquals( 25.0, e0.getSpan (0), STRICT);
assertEquals( 50.0, e0.getSpan (1), STRICT);
assertEquals(-180.0, e1.getLower(0), STRICT);
assertEquals( 0.0, e1.getLower(1), STRICT);
assertEquals(-150.0, e1.getUpper(0), STRICT);
assertEquals( 50.0, e1.getUpper(1), STRICT);
assertEquals( 30.0, e1.getSpan (0), STRICT);
assertEquals( 50.0, e1.getSpan (1), STRICT);
}
}
}
}