| /* |
| * 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); |
| } |
| } |
| } |
| } |