|
| 1 | +/* |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | + * or more contributor license agreements. See the NOTICE file |
| 4 | + * distributed with this work for additional information |
| 5 | + * regarding copyright ownership. The ASF licenses this file |
| 6 | + * to you under the Apache License, Version 2.0 (the |
| 7 | + * "License"); you may not use this file except in compliance |
| 8 | + * with the License. You may obtain a copy of the License at |
| 9 | + * |
| 10 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | + * |
| 12 | + * Unless required by applicable law or agreed to in writing, |
| 13 | + * software distributed under the License is distributed on an |
| 14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | + * KIND, either express or implied. See the License for the |
| 16 | + * specific language governing permissions and limitations |
| 17 | + * under the License. |
| 18 | + */ |
| 19 | +package org.apache.sedona.common.Geography; |
| 20 | + |
| 21 | +import static org.junit.Assert.assertEquals; |
| 22 | +import static org.junit.Assert.assertTrue; |
| 23 | + |
| 24 | +import com.google.common.geometry.S2LatLng; |
| 25 | +import com.google.common.geometry.S2LatLngRect; |
| 26 | +import com.google.common.geometry.S2Loop; |
| 27 | +import com.google.common.geometry.S2Point; |
| 28 | +import org.apache.sedona.common.S2Geography.Geography; |
| 29 | +import org.apache.sedona.common.S2Geography.PolygonGeography; |
| 30 | +import org.apache.sedona.common.geography.Constructors; |
| 31 | +import org.apache.sedona.common.geography.Functions; |
| 32 | +import org.junit.Test; |
| 33 | +import org.locationtech.jts.io.ParseException; |
| 34 | + |
| 35 | +public class FunctionTest { |
| 36 | + private static final double EPS = 1e-9; |
| 37 | + |
| 38 | + private static void assertDegAlmostEqual(double a, double b) { |
| 39 | + assertTrue("exp=" + b + ", got=" + a, Math.abs(a - b) <= EPS); |
| 40 | + } |
| 41 | + |
| 42 | + private static void assertLatLng(S2Point p, double expLatDeg, double expLngDeg) { |
| 43 | + S2LatLng ll = new S2LatLng(p).normalized(); |
| 44 | + assertDegAlmostEqual(ll.latDegrees(), expLatDeg); |
| 45 | + assertDegAlmostEqual(ll.lngDegrees(), expLngDeg); |
| 46 | + } |
| 47 | + |
| 48 | + /** Assert a *single* rectangular envelope polygon has these 4 corners in SW→SE→NE→NW order. */ |
| 49 | + private static void assertRectLoopVertices( |
| 50 | + S2Loop loop, double latLo, double lngLo, double latHi, double lngHi) { |
| 51 | + assertEquals("rect must have 4 vertices", 4, loop.numVertices()); |
| 52 | + // SW |
| 53 | + assertLatLng(loop.vertex(0), latLo, lngLo); |
| 54 | + // SE |
| 55 | + assertLatLng(loop.vertex(1), latLo, lngHi); |
| 56 | + // NE |
| 57 | + assertLatLng(loop.vertex(2), latHi, lngHi); |
| 58 | + // NW |
| 59 | + assertLatLng(loop.vertex(3), latHi, lngLo); |
| 60 | + } |
| 61 | + |
| 62 | + @Test |
| 63 | + public void envelope_noSplit_antimeridian() throws Exception { |
| 64 | + String wkt = "MULTIPOINT ((-179 0), (179 1), (-180 10))"; |
| 65 | + Geography g = Constructors.geogFromWKT(wkt, 4326); |
| 66 | + PolygonGeography env = (PolygonGeography) Functions.getEnvelope(g, /*split*/ false); |
| 67 | + |
| 68 | + S2LatLngRect r = g.region().getRectBound(); |
| 69 | + assertTrue(r.lng().isInverted()); |
| 70 | + assertDegAlmostEqual(r.latLo().degrees(), 0.0); |
| 71 | + assertDegAlmostEqual(r.latHi().degrees(), 10.0); |
| 72 | + assertDegAlmostEqual(r.lngLo().degrees(), 179.0); |
| 73 | + assertDegAlmostEqual(r.lngHi().degrees(), -179.0); |
| 74 | + |
| 75 | + S2Loop loop = env.polygon.getLoops().get(0); |
| 76 | + assertRectLoopVertices(loop, /*latLo*/ 0, /*lngLo*/ 179, /*latHi*/ 10, /*lngHi*/ -179); |
| 77 | + } |
| 78 | + |
| 79 | + @Test |
| 80 | + public void envelope_netherlands_perVertex() throws Exception { |
| 81 | + String nl = |
| 82 | + "POLYGON ((3.314971 50.80372, 7.092053 50.80372, 7.092053 53.5104, 3.314971 53.5104, 3.314971 50.80372))"; |
| 83 | + Geography g = Constructors.geogFromWKT(nl, 4326); |
| 84 | + Geography env = Functions.getEnvelope(g, true); |
| 85 | + String expectedWKT = "SRID=4326; POLYGON ((3.3 50.8, 7.1 50.8, 7.1 53.5, 3.3 53.5, 3.3 50.8))"; |
| 86 | + assertEquals(expectedWKT, env.toString()); |
| 87 | + } |
| 88 | + |
| 89 | + @Test |
| 90 | + public void envelope_fiji_split_perVertex() throws Exception { |
| 91 | + // <-------------------- WESTERN HEMISPHERE | EASTERN HEMISPHERE --------------------> |
| 92 | + // |
| 93 | + // Longitude: ... -179.8° -180°| 180° 177.3° ... |
| 94 | + // ----------------------------------+-------------------------------------------- |
| 95 | + // | |
| 96 | + // Latitude | |
| 97 | + // -16° +------------------------+ +------------------------+ |
| 98 | + // | | | | |
| 99 | + // | POLYGON 2 | | POLYGON 1 | |
| 100 | + // | | | | |
| 101 | + // -18.3° +------------------------+ +------------------------+ |
| 102 | + // | |
| 103 | + // | |
| 104 | + // ^ |
| 105 | + // | |
| 106 | + // Antimeridian |
| 107 | + // (The map's seam at 180°) |
| 108 | + String fiji = |
| 109 | + "MULTIPOLYGON (" |
| 110 | + + "((177.285 -18.28799, 180 -18.28799, 180 -16.02088, 177.285 -16.02088, 177.285 -18.28799))," |
| 111 | + + "((-180 -18.28799, -179.7933 -18.28799, -179.7933 -16.02088, -180 -16.02088, -180 -18.28799))" |
| 112 | + + ")"; |
| 113 | + Geography g = Constructors.geogFromWKT(fiji, 4326); |
| 114 | + Geography env = Functions.getEnvelope(g, /*split*/ true); |
| 115 | + String expectedWKT = |
| 116 | + "SRID=4326; MULTIPOLYGON (((177.3 -18.3, 180 -18.3, 180 -16, 177.3 -16, 177.3 -18.3)), " |
| 117 | + + "((-180 -18.3, -179.8 -18.3, -179.8 -16, -180 -16, -180 -18.3)))"; |
| 118 | + assertEquals(expectedWKT, env.toString()); |
| 119 | + |
| 120 | + expectedWKT = |
| 121 | + "SRID=4326; POLYGON ((177.3 -18.3, -179.8 -18.3, -179.8 -16, 177.3 -16, 177.3 -18.3))"; |
| 122 | + env = Functions.getEnvelope(g, /*split*/ false); |
| 123 | + assertEquals(expectedWKT, env.toString()); |
| 124 | + } |
| 125 | + |
| 126 | + @Test |
| 127 | + public void getEnvelopePoint() throws ParseException { |
| 128 | + String wkt = "POINT (-180 10)"; |
| 129 | + Geography geography = Constructors.geogFromWKT(wkt, 0); |
| 130 | + Geography envelope = Functions.getEnvelope(geography, false); |
| 131 | + assertEquals("POINT (180 10)", envelope.toString()); |
| 132 | + } |
| 133 | + |
| 134 | + @Test |
| 135 | + public void testEnvelopeWKTCompare() throws Exception { |
| 136 | + String antarctica = "POLYGON ((-180 -90, -180 -63.27066, 180 -63.27066, 180 -90, -180 -90))"; |
| 137 | + Geography g = Constructors.geogFromWKT(antarctica, 4326); |
| 138 | + Geography env = Functions.getEnvelope(g, true); |
| 139 | + |
| 140 | + String expectedWKT = |
| 141 | + "SRID=4326; POLYGON ((-180 -63.3, 180 -63.3, 180 -90, -180 -90, -180 -63.3))"; |
| 142 | + assertEquals((expectedWKT), (env.toString())); |
| 143 | + |
| 144 | + String multiCountry = |
| 145 | + "MULTIPOLYGON (((-180 -90, -180 -63.27066, 180 -63.27066, 180 -90, -180 -90))," |
| 146 | + + "((3.314971 50.80372, 7.092053 50.80372, 7.092053 53.5104, 3.314971 53.5104, 3.314971 50.80372)))"; |
| 147 | + g = Constructors.geogFromWKT(multiCountry, 4326); |
| 148 | + env = Functions.getEnvelope(g, true); |
| 149 | + |
| 150 | + String expectedWKT2 = |
| 151 | + "SRID=4326; POLYGON ((-180 53.5, 180 53.5, 180 -90, -180 -90, -180 53.5))"; |
| 152 | + assertEquals((expectedWKT2), (env.toString())); |
| 153 | + } |
| 154 | +} |
0 commit comments