Ticket #12427: josm-add-length-calculation-based-on-curvature_enhanceWayInfo.patch
| File josm-add-length-calculation-based-on-curvature_enhanceWayInfo.patch, 12.9 KB (added by , 11 years ago) |
|---|
-
src/org/openstreetmap/josm/data/coor/LatLon.java
8 8 import static java.lang.Math.sin; 9 9 import static java.lang.Math.sqrt; 10 10 import static java.lang.Math.toRadians; 11 import static org.openstreetmap.josm.tools.I18n.tr; 11 12 import static org.openstreetmap.josm.tools.I18n.trc; 12 13 13 14 import java.awt.geom.Area; … … 20 21 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 21 22 import org.openstreetmap.josm.Main; 22 23 import org.openstreetmap.josm.data.Bounds; 24 import org.openstreetmap.josm.data.projection.Ellipsoid; 23 25 import org.openstreetmap.josm.tools.Utils; 24 26 25 27 /** … … 61 63 public static final LatLon NORTH_POLE = new LatLon(90, 0); 62 64 public static final LatLon SOUTH_POLE = new LatLon(-90, 0); 63 65 66 /** 67 * DistanceTypes 68 */ 69 public enum DistanceType { 70 EUCLIDIAN, 71 GREAT_CIRCLE, 72 CURVATURE, 73 ELLIPTICAL, 74 AUTO, 75 } 76 77 public static final double EPS_MAX = Double.MAX_VALUE; 78 public static final double EPS_10M = 10.0; 79 public static final double EPS_1M = 1.0; 80 public static final double EPS_1DM = 0.1; 81 public static final double EPS_1CM = 0.01; 82 public static final double EPS_MIN = 0.001; 83 84 /** 85 * DecimalFormats 86 */ 64 87 private static DecimalFormat cDmsMinuteFormatter = new DecimalFormat("00"); 65 88 private static DecimalFormat cDmsSecondFormatter = new DecimalFormat("00.0"); 66 89 private static DecimalFormat cDmMinuteFormatter = new DecimalFormat("00.000"); 90 67 91 public static final DecimalFormat cDdFormatter; 68 92 public static final DecimalFormat cDdHighPecisionFormatter; 69 93 static { … … 303 327 } 304 328 305 329 /** 330 * Compute the distance, projection dependant, fixed to a simple projection that maps 331 * a square degree to a square unit in two dimensional euclidian space. 332 * 333 * This will not always find the shortest distance (wrt a sphere), since it does not 334 * wrap at projection bounds. At the poles a projected square degree is a lot larger 335 * than at the equator, this distortion is /not/ accounted for by this function. 336 * 337 * The closer the points are together (wrt euclidian space), 338 * and the less difference they have in longitude, 339 * the smaller the inaccuracy will be. 340 * 341 * @param other the other point. 342 * @return the distance. 343 */ 344 public double euclidianDistance(LatLon other) { 345 double R = Main.getProjection().getEllipsoid().getMeanRadius(); 346 return 2*PI*R * distance(other)/360; 347 } 348 349 /** 350 * Computes the <u>sphere</u> surface area segment given by the minimal bbox around this point and another. 351 * @param other the other point. 352 * @return the sphere surface area in square metres. 353 */ 354 public double greatCircleArea(LatLon other) { 355 //double R = Main.getProjection().getEllipsoid().getAuthalicRadius(); 356 return -1.0; 357 } 358 359 /** 306 360 * Computes the distance between this lat/lon and another point on the earth. 307 * Uses Haversine formula r.361 * Uses Haversine formula. 308 362 * @param other the other point. 309 * @return distancein metres.363 * @return the (shortest) great circle distance to the other point in metres. 310 364 */ 311 365 public double greatCircleDistance(LatLon other) { 312 double R = 6378135;366 double R = Main.getProjection().getEllipsoid().getMeanRadius(); 313 367 double sinHalfLat = sin(toRadians(other.lat() - this.lat()) / 2); 314 368 double sinHalfLon = sin(toRadians(other.lon() - this.lon()) / 2); 315 369 double d = 2 * R * asin( … … 326 380 } 327 381 328 382 /** 383 * Convenience method. 384 * @param other the other point to measure distance to. 385 * @return the (shortest) distance on an approximated "great ellipse" in metres. 386 */ 387 public double curvatureDistance(LatLon other) { 388 /* use EPS_MAX here if there are performance problems */ 389 return curvatureDistance(other, EPS_MIN); 390 } 391 392 /** 393 * Computes the distance between this lat/lon and another point using local radii dependent on lat. 394 * 395 * The approximation is better than the plain great circle distance, because it uses <u>two</u> 396 * perpendicular "great circles" that intersect at the center LatLon coordinate (between this 397 * and the other point)to better emulate ellipsoid curvature: 398 * <ul><li>one circle with "meridional" radius, fitting the ellipsoid's curvature in north-south direction</li> 399 * <li>a second circle, fitting the ellipsoid's curvature in east-west direction</li></ul> 400 * 401 * See <a href="http://clynchg3c.com/Technote/geodesy/radiigeo.pdf">a technote</a> or 402 * <a href="https://en.wikipedia.org/wiki/Earth_radius#Radii_of_curvature">the wiki article</a> 403 * for more details. 404 * 405 * As the curvature varies with latitude the function offers recursive distance calculation 406 * of half of the distances (split using the center LatLon coordinate) until the difference 407 * between the sum of distances of both halves and the full distance is less than <code>eps</code>. 408 * 409 * @param other the other point. 410 * @param eps recurse until error is smaller than epsilon meters. 411 * @return the (shortest) distance on an approximated "great ellipse" in metres. 412 */ 413 public double curvatureDistance(LatLon other, final double eps) { 414 final Ellipsoid e = Main.getProjection().getEllipsoid(); 415 return new Object() { 416 private double r(LatLon a, LatLon b, double _ret) { 417 LatLon m = a.getCenter(b); 418 double mlat = toRadians(m.lat()); 419 double dlat = toRadians(b.lat() - a.lat()) * e.meridionalRadiusOfCurvature(mlat); 420 double dlon = toRadians(b.lon() - a.lon()) * cos(mlat) * e.verticalRadiusOfCurvature(mlat); 421 double ret = sqrt(dlat*dlat + dlon*dlon); 422 423 return eps > Math.abs(_ret/2-ret) ? ret : r(a, m, ret)+r(m, b, ret); 424 } 425 }.r(this, other, 0); 426 } 427 428 /** 329 429 * Returns the heading, in radians, that you have to use to get from this lat/lon to another. 330 430 * 331 431 * (I don't know the original source of this formula, but see … … 400 500 return super.distanceSq(ll); 401 501 } 402 502 503 @SuppressWarnings("javadoc") 504 public double distance(final LatLon other, final DistanceType d) { 505 switch (d) { 506 case CURVATURE: return curvatureDistance(other); 507 case GREAT_CIRCLE: return greatCircleDistance(other); 508 case EUCLIDIAN: return euclidianDistance(other); 509 } 510 return -1.0; 511 } 512 513 /** 514 * Calls every distance measuring method and replies the result 515 * @param other the other coordinate to measure a distance to 516 * @return the formatted output, one line for the output of each distance function 517 */ 518 public String getDistanceSummary(final LatLon other) { 519 DecimalFormat df = new DecimalFormat(Main.pref.get("statusbar.decimal-format", "0.0")); 520 DecimalFormat _df = new DecimalFormat("0.000"); 521 StringBuffer ret = new StringBuffer(0xFF); 522 for (DistanceType d: DistanceType.values()) { 523 double n = distance(other, d); 524 if (!(n < 0)) { 525 ret.append(" "); 526 ret.append(tr(d.name().replace("_", " ").toLowerCase() + " distance: {0} m", 527 n < 100 ? _df.format(n) : df.format(n))); 528 ret.append("\n"); 529 } 530 } 531 return ret.toString(); 532 } 533 403 534 @Override 404 535 public String toString() { 405 536 return "LatLon[lat="+lat()+",lon="+lon()+']'; -
src/org/openstreetmap/josm/data/osm/Node.java
72 72 return new LatLon(lat, lon); 73 73 } 74 74 75 public LatLon getLatLon() { 76 return getCoor(); 77 } 78 75 79 /** 76 80 * <p>Replies the projected east/north coordinates.</p> 77 81 * -
src/org/openstreetmap/josm/data/projection/Ellipsoid.java
360 360 361 361 return xyz; 362 362 } 363 364 /** 365 * get earth's equatorial radius 366 */ 367 public double getEquatorialRadius() { 368 return a; 369 } 370 371 /** 372 * get earth's polar radius 373 */ 374 public double getPolarRadius() { 375 return b; 376 } 377 378 /** 379 * get earth's global mean radius 380 * @return mean radius 381 * @see <a href="https://en.wikipedia.org/wiki/Earth_radius#Mean_radius">mean radius on wikipedia</a> 382 */ 383 public double getMeanRadius() { 384 return (2*a+b)/3; 385 } 363 386 } -
src/org/openstreetmap/josm/data/projection/Projection.java
5 5 import org.openstreetmap.josm.data.ProjectionBounds; 6 6 import org.openstreetmap.josm.data.coor.EastNorth; 7 7 import org.openstreetmap.josm.data.coor.LatLon; 8 import org.openstreetmap.josm.data.projection.datum.Datum; 8 9 9 10 /** 10 11 * A projection, i.e. a class that supports conversion from lat/lon … … 15 16 */ 16 17 public interface Projection { 17 18 /** 19 * Returns the associated {@link Datum} 20 * @return the datum of this projection 21 */ 22 public Datum getDatum(); 23 24 /** 25 * Returns the associated {@link Ellipsoid} 26 * @return the ellipsoid specification 27 */ 28 public Ellipsoid getEllipsoid(); 29 30 /** 18 31 * The default scale factor in east/north units per pixel 19 32 * ({@link org.openstreetmap.josm.gui.NavigatableComponent#scale})). 20 33 * FIXME: misnomer -
src/org/openstreetmap/josm/gui/MapStatus.java
94 94 95 95 private static final DecimalFormat ONE_DECIMAL_PLACE = new DecimalFormat( 96 96 Main.pref.get("statusbar.decimal-format", "0.0")); // change of preference requires restart 97 private static final DecimalFormat TWO_DECIMAL_PLACES = new DecimalFormat("0.00"); 97 98 private static final double DISTANCE_THRESHOLD = Main.pref.getDouble("statusbar.distance-threshold", 0.01); 98 99 99 100 /** … … 1038 1039 */ 1039 1040 public void setDist(double dist) { 1040 1041 distValue = dist; 1041 distText.setText(dist < 0 ? "--" : NavigatableComponent.getDistText(dist, ONE_DECIMAL_PLACE, DISTANCE_THRESHOLD)); 1042 distText.setText(dist < 0 ? "--" : NavigatableComponent.getDistText(dist, dist < 100 1043 ? TWO_DECIMAL_PLACES : ONE_DECIMAL_PLACE, DISTANCE_THRESHOLD)); 1042 1044 } 1043 1045 1044 1046 /** -
src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java
38 38 import org.openstreetmap.josm.gui.NavigatableComponent; 39 39 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 40 40 import org.openstreetmap.josm.gui.mappaint.Cascade; 41 import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;42 41 import org.openstreetmap.josm.gui.mappaint.ElemStyles; 43 42 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 44 43 import org.openstreetmap.josm.gui.mappaint.MultiCascade; … … 46 45 import org.openstreetmap.josm.gui.mappaint.StyleElementList; 47 46 import org.openstreetmap.josm.gui.mappaint.StyleSource; 48 47 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource; 48 import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement; 49 49 import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource; 50 50 import org.openstreetmap.josm.gui.util.GuiHelper; 51 51 import org.openstreetmap.josm.gui.widgets.JosmTextArea; … … 274 274 } 275 275 276 276 void addWayNodes(Way w) { 277 Node last = null; 277 278 add(tr("{0} Nodes: ", w.getNodesCount())); 278 279 for (Node n : w.getNodes()) { 280 if (last!=null) { 281 s.append(n.getCoor().getDistanceSummary(last.getCoor())); 282 } 279 283 s.append(INDENT).append(INDENT); 280 284 addNameAndId(n); 281 285 s.append(NL); 286 last = n; 282 287 } 283 288 } 284 289
