- Timestamp:
- 2021-08-01T22:03:28+02:00 (3 years ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/tools/Geometry.java
r17141 r18109 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.tools; 3 4 import static org.openstreetmap.josm.data.projection.Ellipsoid.WGS84; 3 5 4 6 import java.awt.geom.Area; … … 26 28 import org.openstreetmap.josm.data.coor.EastNorth; 27 29 import org.openstreetmap.josm.data.coor.ILatLon; 30 import org.openstreetmap.josm.data.coor.LatLon; 28 31 import org.openstreetmap.josm.data.osm.BBox; 29 32 import org.openstreetmap.josm.data.osm.DataSet; … … 1557 1560 1558 1561 /** 1562 * Create a new LatLon at a specified distance. Currently uses WGS84, but may change randomly in the future. 1563 * This does not currently attempt to be hugely accurate. The actual location may be off 1564 * depending upon the distance and the elevation, but should be within 0.0002 meters. 1565 * 1566 * @param original The originating point 1567 * @param angle The angle (from true north) in radians 1568 * @param offset The distance to the new point in the current projection's units 1569 * @return The location at the specified angle and distance from the originating point 1570 * @since 18109 1571 */ 1572 public static ILatLon getLatLonFrom(final ILatLon original, final double angle, final double offset) { 1573 final double meterOffset = ProjectionRegistry.getProjection().getMetersPerUnit() * offset; 1574 final double radianLat = Math.toRadians(original.lat()); 1575 final double radianLon = Math.toRadians(original.lon()); 1576 final double angularDistance = meterOffset / WGS84.a; 1577 final double lat = Math.asin(Math.sin(radianLat) * Math.cos(angularDistance) 1578 + Math.cos(radianLat) * Math.sin(angularDistance) * Math.cos(angle)); 1579 final double lon = radianLon + Math.atan2(Math.sin(angle) * Math.sin(angularDistance) * Math.cos(radianLat), 1580 Math.cos(angularDistance) - Math.sin(radianLat) * Math.sin(lat)); 1581 return new LatLon(Math.toDegrees(lat), Math.toDegrees(lon)); 1582 } 1583 1584 /** 1559 1585 * Calculate closest distance between a line segment s1-s2 and a point p 1560 1586 * @param s1 start of segment -
trunk/test/unit/org/openstreetmap/josm/TestUtils.java
r17372 r18109 23 23 import java.time.format.DateTimeFormatter; 24 24 import java.time.temporal.Temporal; 25 import java.util.ArrayList; 25 26 import java.util.Arrays; 26 27 import java.util.Collection; … … 31 32 import java.util.concurrent.ExecutionException; 32 33 import java.util.concurrent.ThreadPoolExecutor; 34 import java.util.concurrent.atomic.AtomicInteger; 33 35 import java.util.function.Function; 34 36 import java.util.stream.Collectors; 37 import java.util.stream.IntStream; 35 38 import java.util.stream.Stream; 36 39 … … 169 172 } 170 173 174 /** 175 * Create a test matrix for parameterized tests. 176 * <br /> 177 * <b>WARNING:</b> This can quickly become <i>very</i> large (this is combinatorial, 178 * so the returned {@link Stream} length is the size of the object collections multiplied by each other. 179 * So if you have three lists of size 3, 4, and 5, the stream size would be {@code 3 * 4 * 5} or 60 elements. 180 * <br /> 181 * Generally speaking, you should avoid putting expected values into the test matrix. 182 * 183 * @param objectCollections The collections of objects. May include/provide {@code null}. 184 * @return The object arrays to be used as arguments. Note: The returned stream might not be thread-safe. 185 */ 186 public static Stream<Object[]> createTestMatrix(List<?>... objectCollections) { 187 // Create the original object arrays 188 final AtomicInteger size = new AtomicInteger(1); 189 Stream.of(objectCollections).mapToInt(Collection::size).forEach(i -> size.set(size.get() * i)); 190 final List<Object[]> testMatrix = new ArrayList<>(size.get()); 191 final int[] indexes = IntStream.range(0, objectCollections.length).map(i -> 0).toArray(); 192 193 // It is important to make a new object array each time (we modify them) 194 return IntStream.range(0, size.get()).mapToObj(index -> new Object[objectCollections.length]).peek(args -> { 195 // Just in case someone tries to make this parallel, synchronize on indexes to avoid most issues. 196 synchronized (indexes) { 197 // Set the args 198 for (int listIndex = 0; listIndex < objectCollections.length; listIndex++) { 199 args[listIndex] = objectCollections[listIndex].get(indexes[listIndex]); 200 } 201 // Increment indexes 202 for (int listIndex = 0; listIndex < objectCollections.length; listIndex++) { 203 indexes[listIndex] = indexes[listIndex] + 1; 204 if (indexes[listIndex] >= objectCollections[listIndex].size()) { 205 indexes[listIndex] = 0; 206 } else { 207 break; 208 } 209 } 210 } 211 }); 212 } 213 171 214 private static <T> String getFailMessage(T o1, T o2, int a, int b) { 172 215 return new StringBuilder("Compared\no1: ").append(o1).append("\no2: ") -
trunk/test/unit/org/openstreetmap/josm/tools/GeometryTest.java
r17275 r18109 12 12 import java.util.ArrayList; 13 13 import java.util.Arrays; 14 import java.util.Collections; 14 15 import java.util.List; 16 import java.util.stream.Stream; 15 17 16 18 import org.junit.Assert; 19 import org.junit.jupiter.api.Test; 17 20 import org.junit.jupiter.api.extension.RegisterExtension; 18 import org.junit.jupiter.api.Test; 21 import org.junit.jupiter.params.ParameterizedTest; 22 import org.junit.jupiter.params.provider.Arguments; 23 import org.junit.jupiter.params.provider.MethodSource; 19 24 import org.openstreetmap.josm.TestUtils; 20 25 import org.openstreetmap.josm.data.coor.EastNorth; … … 27 32 import org.openstreetmap.josm.data.osm.Way; 28 33 import org.openstreetmap.josm.data.osm.search.SearchCompiler; 34 import org.openstreetmap.josm.data.projection.Projection; 35 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 36 import org.openstreetmap.josm.data.projection.Projections; 29 37 import org.openstreetmap.josm.io.OsmReader; 30 38 import org.openstreetmap.josm.testutils.JOSMTestRules; … … 470 478 } 471 479 480 static Stream<Arguments> testGetLatLonFrom() { 481 // The projection can quickly explode the test matrix, so only test WGS84 (EPSG:3857). If other projections have 482 // issues, add them to the first list. 483 return TestUtils.createTestMatrix( 484 // Check specific projections 485 Collections.singletonList(Projections.getProjectionByCode("EPSG:3857")), 486 // Check extreme latitudes (degrees) 487 Arrays.asList(0, 89, -89), 488 // Test extreme longitudes (degrees) 489 Arrays.asList(0, -179, 179), 490 // Test various angles (degrees) 491 // This tests cardinal directions, and then some varying angles. 492 // TBH, the cardinal directions should find any issues uncovered by the varying angles, 493 // but it may not. 494 Arrays.asList(0, 90, 180, 270, 45), 495 // Test various distances (meters) 496 Arrays.asList(1, 10_000) 497 ).map(Arguments::of); 498 } 499 500 @ParameterizedTest(name = "[{index}] {3}° {4}m @ lat = {1} lon = {2} - {0}") 501 @MethodSource 502 void testGetLatLonFrom(final Projection projection, final double lat, final double lon, final double angle, final double offsetInMeters) { 503 ProjectionRegistry.setProjection(projection); 504 final double offset = offsetInMeters / projection.getMetersPerUnit(); 505 final LatLon original = new LatLon(lat, lon); 506 507 final LatLon actual = (LatLon) Geometry.getLatLonFrom(original, Math.toRadians(angle), offset); 508 // Due to degree -> radian -> degree conversion, there is a limit to how precise it can be 509 assertEquals(offsetInMeters, original.greatCircleDistance(actual), 0.000_000_1); 510 // The docs indicate that this should not be highly precise. 511 assertEquals(angle, Math.toDegrees(original.bearing(actual)), 0.000_001); 512 } 472 513 }
Note:
See TracChangeset
for help on using the changeset viewer.