source: josm/trunk/test/unit/org/openstreetmap/josm/tools/GeometryTest.java@ 17360

Last change on this file since 17360 was 17275, checked in by Don-vip, 3 years ago

see #16567 - upgrade almost all tests to JUnit 5, except those depending on WiremockRule

See https://github.com/tomakehurst/wiremock/issues/684

  • Property svn:eol-style set to native
File size: 20.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static org.junit.jupiter.api.Assertions.assertEquals;
5import static org.junit.jupiter.api.Assertions.assertFalse;
6import static org.junit.jupiter.api.Assertions.assertNotEquals;
7import static org.junit.jupiter.api.Assertions.assertTrue;
8
9import java.io.InputStream;
10import java.nio.file.Files;
11import java.nio.file.Paths;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.List;
15
16import org.junit.Assert;
17import org.junit.jupiter.api.extension.RegisterExtension;
18import org.junit.jupiter.api.Test;
19import org.openstreetmap.josm.TestUtils;
20import org.openstreetmap.josm.data.coor.EastNorth;
21import org.openstreetmap.josm.data.coor.LatLon;
22import org.openstreetmap.josm.data.osm.DataSet;
23import org.openstreetmap.josm.data.osm.Node;
24import org.openstreetmap.josm.data.osm.OsmPrimitive;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.RelationMember;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.data.osm.search.SearchCompiler;
29import org.openstreetmap.josm.io.OsmReader;
30import org.openstreetmap.josm.testutils.JOSMTestRules;
31
32import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
33
34/**
35 * Unit tests of {@link Geometry} class.
36 */
37class GeometryTest {
38 /**
39 * Primitives need preferences and projection.
40 */
41 @RegisterExtension
42 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
43 public JOSMTestRules test = new JOSMTestRules().preferences().projection();
44
45 /**
46 * Test of {@link Geometry#getLineLineIntersection} method.
47 */
48 @Test
49 void testLineLineIntersection() {
50 EastNorth p1 = new EastNorth(-9477809.106349014, 1.5392960539974203E7);
51 EastNorth p2 = new EastNorth(-9477813.789091509, 1.5392954297092048E7);
52 EastNorth p3 = new EastNorth(-9477804.974058038, 1.539295490030348E7);
53 EastNorth p4 = new EastNorth(-9477814.628697459, 1.5392962142181376E7);
54
55 EastNorth intersectionPoint = Geometry.getLineLineIntersection(p1, p2, p3, p4);
56
57 EastNorth d1 = p3.subtract(intersectionPoint);
58 EastNorth d2 = p1.subtract(p2);
59 Double crossProduct = d1.east()*d2.north() - d1.north()*d2.east();
60 Double scalarProduct = d1.east()*d2.east() + d1.north()*d2.north();
61 Double len1 = d1.length();
62 Double len2 = d2.length();
63
64 Double angle1 = Geometry.getCornerAngle(p1, p2, intersectionPoint);
65 Double angle2 = Geometry.getCornerAngle(p3, p4, intersectionPoint);
66 Assert.assertTrue("intersection point not on line, angle: " + angle1,
67 Math.abs(angle1) < 1e-10);
68 Assert.assertTrue("intersection point not on line, angle: " + angle2,
69 Math.abs(angle1) < 1e-10);
70
71 Assert.assertTrue("cross product != 1 : " + Math.abs(crossProduct/len1/len2),
72 Math.abs(Math.abs(crossProduct/len1/len2) - 1) < 1e-10);
73 Assert.assertTrue("scalar product != 0 : " + scalarProduct/len1/len2,
74 Math.abs(scalarProduct/len1/len2) < 1e-10);
75 }
76
77 /**
78 * Test of {@link Geometry#closedWayArea(org.openstreetmap.josm.data.osm.Way)} method.
79 *
80 * @throws Exception if an error occurs
81 */
82 @Test
83 void testClosedWayArea() throws Exception {
84 try (InputStream in = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "create_multipolygon.osm"))) {
85 DataSet ds = OsmReader.parseDataSet(in, null);
86 Way closedWay = (Way) SubclassFilteredCollection.filter(ds.allPrimitives(),
87 SearchCompiler.compile("landuse=forest")).iterator().next();
88 Assert.assertEquals(5760015.7353515625, Geometry.closedWayArea(closedWay), 1e-3);
89 Assert.assertEquals(5760015.7353515625, Geometry.computeArea(closedWay), 1e-3);
90 }
91 }
92
93 /**
94 * Test of {@link Geometry#multipolygonArea(Relation)}} method.
95 *
96 * @throws Exception if an error occurs
97 */
98 @Test
99 void testMultipolygonArea() throws Exception {
100 try (InputStream in = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "multipolygon.osm"))) {
101 DataSet ds = OsmReader.parseDataSet(in, null);
102 final Relation r = ds.getRelations().iterator().next();
103 Assert.assertEquals(4401735.20703125, Geometry.multipolygonArea(r), 1e-3);
104 Assert.assertEquals(4401735.20703125, Geometry.computeArea(r), 1e-3);
105 }
106 }
107
108 /**
109 * Test of {@link Geometry#getAreaAndPerimeter(List)} method.
110 *
111 * @throws Exception if an error occurs
112 */
113 @Test
114 void testAreaAndPerimeter() throws Exception {
115 try (InputStream in = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "create_multipolygon.osm"))) {
116 DataSet ds = OsmReader.parseDataSet(in, null);
117 Way closedWay = (Way) SubclassFilteredCollection.filter(ds.allPrimitives(),
118 SearchCompiler.compile("landuse=forest")).iterator().next();
119 Geometry.AreaAndPerimeter areaAndPerimeter = Geometry.getAreaAndPerimeter(closedWay.getNodes());
120 Assert.assertEquals(12495000., areaAndPerimeter.getArea(), 1e-3);
121 Assert.assertEquals(15093.201209424187, areaAndPerimeter.getPerimeter(), 1e-3);
122 }
123 }
124
125 /**
126 * Test of {@link Geometry#getNormalizedAngleInDegrees(double)} method.
127 */
128 @Test
129 void testRightAngle() {
130 Node n1 = new Node(1);
131 Node n2 = new Node(2);
132 Node n3 = new Node(3);
133 n1.setCoor(new LatLon(10.22873540462851, 6.169719398316592));
134 n2.setCoor(new LatLon(10.229332494162811, 6.16978130985785));
135 n3.setCoor(new LatLon(10.22924937004949, 6.17060908367496));
136
137 double angle = Geometry.getNormalizedAngleInDegrees(Geometry.getCornerAngle(n1.getEastNorth(),
138 n2.getEastNorth(), n3.getEastNorth()));
139 assertEquals(90, angle, 1e-8);
140 angle = Geometry.getNormalizedAngleInDegrees(Geometry.getCornerAngle(n1.getEastNorth(),
141 n2.getEastNorth(), n1.getEastNorth()));
142 assertEquals(0, angle, 1e-8);
143
144 n1.setCoor(new LatLon(10.2295011, 6.1693106));
145 n2.setCoor(new LatLon(10.2294958, 6.16930635));
146 n3.setCoor(new LatLon(10.2294895, 6.1693039));
147
148 angle = Geometry.getNormalizedAngleInDegrees(Geometry.getCornerAngle(n1.getEastNorth(),
149 n2.getEastNorth(), n3.getEastNorth()));
150 assertEquals(162.66381817961337, angle, 1e-5);
151
152 angle = Geometry.getNormalizedAngleInDegrees(Geometry.getCornerAngle(n3.getEastNorth(),
153 n2.getEastNorth(), n1.getEastNorth()));
154 assertEquals(162.66381817961337, angle, 1e-5);
155 }
156
157 /**
158 * Test of {@link Geometry#getCentroidEN} method.
159 */
160 @Test
161 void testCentroidEN() {
162 EastNorth en1 = new EastNorth(100, 200);
163 EastNorth en2 = new EastNorth(150, 400);
164 EastNorth en3 = new EastNorth(200, 200);
165 assertEquals(en1, Geometry.getCentroidEN(Arrays.asList(en1)));
166 assertEquals(new EastNorth(125, 300), Geometry.getCentroidEN(Arrays.asList(en1, en2)));
167 assertEquals(new EastNorth(150, 266d + 2d/3d), Geometry.getCentroidEN(Arrays.asList(en1, en2, en3)));
168 }
169
170
171 /**
172 * Test of {@link Geometry#polygonIntersection} method with two triangles.
173 */
174 @Test
175 void testPolygonIntersectionTriangles() {
176 Node node1 = new Node(new LatLon(0.0, 1.0));
177 Node node2 = new Node(new LatLon(0.0, 2.0));
178 Node node3 = new Node(new LatLon(5.0, 1.5));
179 List<Node> poly1 = Arrays.asList(node1, node2, node3, node1);
180 Node node4 = new Node(new LatLon(10.0, 1.0));
181 Node node5 = new Node(new LatLon(10.0, 2.0));
182 Node node6 = new Node(new LatLon(5.000001, 1.5));
183 List<Node> poly2 = Arrays.asList(node4, node5, node6, node4);
184 // no intersection, not even touching
185 assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
186
187 node5.setCoor(new LatLon(5.0, 1.5));
188 // touching in a single point with two different nodes
189 assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
190
191 node5.setCoor(new LatLon(4.99999999, 1.5));
192 // now node5 lies inside way1, intersection is a very small area, in OSM precision nodes are equal
193 assertEquals(node5.getCoor().getRoundedToOsmPrecision(), node3.getCoor().getRoundedToOsmPrecision());
194 assertEquals(Geometry.PolygonIntersection.CROSSING, Geometry.polygonIntersection(poly1, poly2));
195
196 node5.setCoor(new LatLon(4.9999999, 1.5));
197 // intersection area is too big to ignore
198 assertNotEquals(node5.getCoor().getRoundedToOsmPrecision(), node3.getCoor().getRoundedToOsmPrecision());
199 assertEquals(Geometry.PolygonIntersection.CROSSING, Geometry.polygonIntersection(poly1, poly2));
200 }
201
202 /**
203 * Test of {@link Geometry#polygonIntersection} method with two V-shapes
204 */
205 @Test
206 void testPolygonIntersectionVShapes() {
207 Node node1 = new Node(new LatLon(1.0, 1.0));
208 Node node2 = new Node(new LatLon(2.0, 2.0));
209 Node node3 = new Node(new LatLon(0.9, 1.0));
210 Node node4 = new Node(new LatLon(2.0, 0.0));
211 List<Node> poly1 = Arrays.asList(node1, node2, node3, node4, node1);
212 Node node5 = new Node(new LatLon(3.0, 1.0));
213 Node node6 = new Node(new LatLon(2.0, 2.0)); // like node2
214 Node node7 = new Node(new LatLon(3.1, 1.0));
215 Node node8 = new Node(new LatLon(2.0, 0.0)); // like node4
216 List<Node> poly2 = Arrays.asList(node5, node6, node7, node8, node5);
217
218 // touching in two points but not overlapping
219 assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
220
221 // touching in one point, small overlap at the other
222 node6.setCoor(new LatLon(1.9999999, 2.0));
223 assertEquals(Geometry.PolygonIntersection.CROSSING, Geometry.polygonIntersection(poly1, poly2));
224
225 // two small overlaps, but clearly visible because lines are crossing
226 node6.setCoor(new LatLon(1.99999999, 2.0));
227 node8.setCoor(new LatLon(1.99999999, 0.0));
228 assertEquals(Geometry.PolygonIntersection.OUTSIDE, Geometry.polygonIntersection(poly1, poly2));
229 }
230
231 /**
232 * Test of {@link Geometry#isPolygonInsideMultiPolygon}
233 * See #17652. Triangle crosses outer way of multipolygon.
234 */
235 @Test
236 void testIsPolygonInsideMultiPolygon() {
237 Node node1 = new Node(new LatLon(1.01, 1.0));
238 Node node2 = new Node(new LatLon(1.01, 1.1));
239 Node node3 = new Node(new LatLon(1.02, 1.05));
240 Way w1 = new Way();
241 w1.setNodes(Arrays.asList(node1, node2, node3, node1));
242 w1.put("building", "yes");
243
244 Node node4 = new Node(new LatLon(1.0, 1.09));
245 Node node5 = new Node(new LatLon(1.0, 1.12));
246 Node node6 = new Node(new LatLon(1.1, 1.12));
247 Node node7 = new Node(new LatLon(1.1, 1.09));
248 Way outer = new Way();
249 outer.setNodes(Arrays.asList(node4, node5, node6, node7, node4));
250 Node node8 = new Node(new LatLon(1.04, 1.1));
251 Node node9 = new Node(new LatLon(1.04, 1.11));
252 Node node10 = new Node(new LatLon(1.06, 1.105));
253 Way inner = new Way();
254 inner.setNodes(Arrays.asList(node8, node9, node10, node8));
255 Relation mp = new Relation();
256 mp.addMember(new RelationMember("outer", outer));
257 mp.addMember(new RelationMember("inner", inner));
258 mp.put("type", "multipolygon");
259 assertFalse(Geometry.isPolygonInsideMultiPolygon(w1.getNodes(), mp, null));
260
261 node4.setCoor(new LatLon(1.006, 0.99));
262 // now w1 is inside
263 assertTrue(Geometry.isPolygonInsideMultiPolygon(w1.getNodes(), mp, null));
264 }
265
266 /**
267 * Test of {@link Geometry#filterInsideMultipolygon}
268 */
269 @Test
270 void testFilterInsideMultiPolygon() {
271 Node node1 = new Node(new LatLon(1.01, 1.0));
272 Node node2 = new Node(new LatLon(1.01, 1.1));
273 Node node3 = new Node(new LatLon(1.02, 1.05));
274 Way w1 = new Way();
275 w1.setNodes(Arrays.asList(node1, node2, node3, node1));
276 w1.put("building", "yes");
277 Relation mp1 = new Relation();
278 mp1.addMember(new RelationMember("outer", w1));
279 mp1.put("type", "multipolygon");
280
281 Node node4 = new Node(new LatLon(1.0, 1.09));
282 Node node5 = new Node(new LatLon(1.0, 1.12));
283 Node node6 = new Node(new LatLon(1.1, 1.12));
284 Node node7 = new Node(new LatLon(1.1, 1.09));
285 Way outer = new Way();
286 outer.setNodes(Arrays.asList(node4, node5, node6, node7, node4));
287 Node node8 = new Node(new LatLon(1.04, 1.1));
288 Node node9 = new Node(new LatLon(1.04, 1.11));
289 Node node10 = new Node(new LatLon(1.06, 1.105));
290 Way inner = new Way();
291 inner.setNodes(Arrays.asList(node8, node9, node10, node8));
292 Relation mp2 = new Relation();
293 mp2.addMember(new RelationMember("outer", outer));
294 mp2.addMember(new RelationMember("inner", inner));
295 mp2.put("type", "multipolygon");
296 assertFalse(Geometry.isPolygonInsideMultiPolygon(w1.getNodes(), mp2, null));
297 assertFalse(Geometry.filterInsideMultipolygon(Arrays.asList(w1), mp2).contains(w1));
298
299 node4.setCoor(new LatLon(1.006, 0.99));
300 // now w1 is inside
301 assertTrue(Geometry.isPolygonInsideMultiPolygon(w1.getNodes(), mp2, null));
302 assertTrue(Geometry.filterInsideMultipolygon(Arrays.asList(w1), mp2).contains(w1));
303 assertTrue(Geometry.filterInsideMultipolygon(Arrays.asList(mp1), mp2).contains(mp1));
304 assertTrue(Geometry.filterInsideMultipolygon(Arrays.asList(w1, mp1), mp2).contains(w1));
305 assertTrue(Geometry.filterInsideMultipolygon(Arrays.asList(w1, mp1), mp2).contains(mp1));
306 }
307
308 /**
309 * Test of {@link Geometry#getDistance} method.
310 */
311 @Test
312 void testGetDistance() {
313 Node node1 = new Node(new LatLon(0, 0));
314 Node node2 = new Node(new LatLon(0.1, 1));
315 Node node3 = new Node(new LatLon(1.1, 0.1));
316 Node node4 = new Node(new LatLon(1, 1.1));
317 Way way1 = TestUtils.newWay("", node1, node2);
318 Way way2 = TestUtils.newWay("", node3, node4);
319 Relation testRelation1 = new Relation();
320 Relation testRelation2 = new Relation();
321 testRelation1.addMember(new RelationMember("", way1));
322 testRelation1.addMember(new RelationMember("", way2));
323 testRelation2.addMember(new RelationMember("", node1));
324 testRelation2.addMember(new RelationMember("", node2));
325 testRelation2.addMember(new RelationMember("", node3));
326 testRelation2.addMember(new RelationMember("", node4));
327
328 double distance = Geometry.getDistance(null, node3);
329 assertEquals(Double.NaN, distance, 0.1);
330
331 distance = Geometry.getDistance(way1, null);
332 assertEquals(Double.NaN, distance, 0.1);
333
334 distance = Geometry.getDistance(null, null);
335 assertEquals(Double.NaN, distance, 0.1);
336
337 distance = Geometry.getDistance(node1, node2);
338 assertEquals(111874.6474307704, distance, 0.1);
339
340 distance = Geometry.getDistance(way1, node3);
341 assertEquals(120743.55085962385, distance, 0.1);
342
343 distance = Geometry.getDistance(node3, way1);
344 assertEquals(120743.55085962385, distance, 0.1);
345
346 distance = Geometry.getDistance(way1, way2);
347 assertEquals(100803.63714283936, distance, 0.1);
348
349 distance = Geometry.getDistance(testRelation1, new Node(new LatLon(0, 0.5)));
350 assertEquals(5538.354450686605, distance, 0.1);
351
352 distance = Geometry.getDistance(new Node(new LatLon(0, 0.5)), testRelation1);
353 assertEquals(5538.354450686605, distance, 0.1);
354
355 distance = Geometry.getDistance(testRelation1, testRelation2);
356 assertEquals(0.0, distance, 0.1);
357 }
358
359 /**
360 * Test of {@link Geometry#getClosestPrimitive} method
361 */
362 @Test
363 void testGetClosestPrimitive() {
364 Node node1 = new Node(new LatLon(0, 0));
365 Node node2 = new Node(new LatLon(0.1, 1));
366 Node node3 = new Node(new LatLon(1.1, 0.1));
367 Node node4 = new Node(new LatLon(1, 1.1));
368 Way way1 = TestUtils.newWay("", node1, node2);
369 Way way2 = TestUtils.newWay("", node3, node4);
370
371 List<OsmPrimitive> primitives = new ArrayList<>();
372 primitives.add(way1);
373 primitives.add(way2);
374 OsmPrimitive closest = Geometry.getClosestPrimitive(node1, primitives);
375 assertEquals(way1, closest);
376 }
377
378 /**
379 * Test of {@link Geometry#getFurthestPrimitive} method
380 */
381 @Test
382 void testGetFurthestPrimitive() {
383 Node node1 = new Node(new LatLon(0, 0));
384 Node node2 = new Node(new LatLon(0, 1.1));
385 Node node3 = new Node(new LatLon(1, 0.1));
386 Node node4 = new Node(new LatLon(1.1, 1));
387 Way way1 = TestUtils.newWay("", node1, node2);
388 Way way2 = TestUtils.newWay("", node3, node4);
389 Way way3 = TestUtils.newWay("", node2, node4);
390 Way way4 = TestUtils.newWay("", node1, node3);
391
392 List<OsmPrimitive> primitives = new ArrayList<>();
393 primitives.add(way1);
394 OsmPrimitive furthest = Geometry.getFurthestPrimitive(new Node(new LatLon(0, 0.75)), primitives);
395 assertEquals(way1, furthest);
396 primitives.add(way2);
397 primitives.add(way3);
398 primitives.add(way4);
399 furthest = Geometry.getFurthestPrimitive(new Node(new LatLon(0, 0.5)), primitives);
400 assertEquals(way2, furthest);
401 furthest = Geometry.getFurthestPrimitive(new Node(new LatLon(.25, 0.5)), primitives);
402 assertEquals(way2, furthest);
403 }
404
405 /**
406 * Test of {@link Geometry#getClosestWaySegment} method
407 */
408 @Test
409 void testGetClosestWaySegment() {
410 Node node1 = new Node(new LatLon(0, 0));
411 Node node2 = new Node(new LatLon(0, 1));
412 Node node3 = new Node(new LatLon(0.3, 0.5));
413 Node node4 = new Node(new LatLon(0.1, 0));
414 Way way1 = TestUtils.newWay("", node1, node2, node3, node4);
415
416 Way closestSegment = Geometry.getClosestWaySegment(way1, new Node(new LatLon(0, 0.5))).toWay();
417 Assert.assertTrue(closestSegment.containsNode(node1));
418 Assert.assertTrue(closestSegment.containsNode(node2));
419 }
420
421 /**
422 * Test of {@link Geometry#getDistanceSegmentSegment} method
423 */
424 @Test
425 void testGetDistanceSegmentSegment() {
426 Node node1 = new Node(new LatLon(2.0, 2.0));
427 Node node2 = new Node(new LatLon(2.0, 3.0));
428 Node node3 = new Node(new LatLon(2.3, 2.5));
429 Node node4 = new Node(new LatLon(2.1, 2.0));
430
431 // connected segments
432 assertEquals(0.0, Geometry.getDistanceSegmentSegment(node1, node2, node3, node1), 0.000001);
433
434 // distance between node 1 and node4 is the shortest
435 double expected = node1.getEastNorth().distance(node4.getEastNorth());
436 assertEquals(expected, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 0.000001);
437
438 // crossing segments
439 node4.setCoor(new LatLon(1.9998192774806864, 2.0004056993230455));
440 assertEquals(0, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 0.000001);
441
442 // usual case
443 node4.setCoor(new LatLon(2.0002098170882276, 2.0000778643530537));
444 assertEquals(23.4, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 1.0);
445
446 // similar segments, reversed direction
447 node3.setCoor(node2.getCoor());
448 node4.setCoor(node1.getCoor());
449 assertEquals(0.0, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 0.000001);
450
451 // overlapping segments
452 node3.setCoor(new LatLon(2.0, 2.2));
453 node4.setCoor(new LatLon(2.0, 2.3));
454 assertEquals(0.0, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 0.000001);
455
456 // parallel segments, n1 and n3 at same longitude
457 node3.setCoor(new LatLon(2.1, 2.0));
458 node4.setCoor(new LatLon(2.1, 2.3));
459 expected = node1.getEastNorth().distance(node3.getEastNorth());
460 assertEquals(expected, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 0.000001);
461
462 // parallel segments
463 node3.setCoor(new LatLon(2.1, 2.1));
464 assertEquals(expected, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 0.000001);
465
466 // almost parallel segments
467 node3.setCoor(new LatLon(2.09999999, 2.1));
468 assertEquals(expected, Geometry.getDistanceSegmentSegment(node1, node2, node3, node4), 0.01);
469 assertTrue(expected > Geometry.getDistanceSegmentSegment(node1, node2, node3, node4));
470 }
471
472}
Note: See TracBrowser for help on using the repository browser.