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 cmuelle8, 11 years ago)

josm-add-length-calculation-based-on-curvature_enhanceWayInfo.patch

  • src/org/openstreetmap/josm/data/coor/LatLon.java

     
    88import static java.lang.Math.sin;
    99import static java.lang.Math.sqrt;
    1010import static java.lang.Math.toRadians;
     11import static org.openstreetmap.josm.tools.I18n.tr;
    1112import static org.openstreetmap.josm.tools.I18n.trc;
    1213
    1314import java.awt.geom.Area;
     
    2021import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
    2122import org.openstreetmap.josm.Main;
    2223import org.openstreetmap.josm.data.Bounds;
     24import org.openstreetmap.josm.data.projection.Ellipsoid;
    2325import org.openstreetmap.josm.tools.Utils;
    2426
    2527/**
     
    6163    public static final LatLon NORTH_POLE = new LatLon(90, 0);
    6264    public static final LatLon SOUTH_POLE = new LatLon(-90, 0);
    6365
     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     */
    6487    private static DecimalFormat cDmsMinuteFormatter = new DecimalFormat("00");
    6588    private static DecimalFormat cDmsSecondFormatter = new DecimalFormat("00.0");
    6689    private static DecimalFormat cDmMinuteFormatter = new DecimalFormat("00.000");
     90
    6791    public static final DecimalFormat cDdFormatter;
    6892    public static final DecimalFormat cDdHighPecisionFormatter;
    6993    static {
     
    303327    }
    304328
    305329    /**
     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    /**
    306360     * Computes the distance between this lat/lon and another point on the earth.
    307      * Uses Haversine formular.
     361     * Uses Haversine formula.
    308362     * @param other the other point.
    309      * @return distance in metres.
     363     * @return the (shortest) great circle distance to the other point in metres.
    310364     */
    311365    public double greatCircleDistance(LatLon other) {
    312         double R = 6378135;
     366        double R = Main.getProjection().getEllipsoid().getMeanRadius();
    313367        double sinHalfLat = sin(toRadians(other.lat() - this.lat()) / 2);
    314368        double sinHalfLon = sin(toRadians(other.lon() - this.lon()) / 2);
    315369        double d = 2 * R * asin(
     
    326380    }
    327381
    328382    /**
     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    /**
    329429     * Returns the heading, in radians, that you have to use to get from this lat/lon to another.
    330430     *
    331431     * (I don't know the original source of this formula, but see
     
    400500        return super.distanceSq(ll);
    401501    }
    402502
     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
    403534    @Override
    404535    public String toString() {
    405536        return "LatLon[lat="+lat()+",lon="+lon()+']';
  • src/org/openstreetmap/josm/data/osm/Node.java

     
    7272        return new LatLon(lat, lon);
    7373    }
    7474
     75    public LatLon getLatLon() {
     76        return getCoor();
     77    }
     78
    7579    /**
    7680     * <p>Replies the projected east/north coordinates.</p>
    7781     *
  • src/org/openstreetmap/josm/data/projection/Ellipsoid.java

     
    360360
    361361        return xyz;
    362362    }
     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    }
    363386}
  • src/org/openstreetmap/josm/data/projection/Projection.java

     
    55import org.openstreetmap.josm.data.ProjectionBounds;
    66import org.openstreetmap.josm.data.coor.EastNorth;
    77import org.openstreetmap.josm.data.coor.LatLon;
     8import org.openstreetmap.josm.data.projection.datum.Datum;
    89
    910/**
    1011 * A projection, i.e.&nbsp;a class that supports conversion from lat/lon
     
    1516 */
    1617public interface Projection {
    1718    /**
     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    /**
    1831     * The default scale factor in east/north units per pixel
    1932     * ({@link org.openstreetmap.josm.gui.NavigatableComponent#scale})).
    2033     * FIXME: misnomer
  • src/org/openstreetmap/josm/gui/MapStatus.java

     
    9494
    9595    private static final DecimalFormat ONE_DECIMAL_PLACE = new DecimalFormat(
    9696            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");
    9798    private static final double DISTANCE_THRESHOLD = Main.pref.getDouble("statusbar.distance-threshold", 0.01);
    9899
    99100    /**
     
    10381039     */
    10391040    public void setDist(double dist) {
    10401041        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));
    10421044    }
    10431045
    10441046    /**
  • src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java

     
    3838import org.openstreetmap.josm.gui.NavigatableComponent;
    3939import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    4040import org.openstreetmap.josm.gui.mappaint.Cascade;
    41 import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
    4241import org.openstreetmap.josm.gui.mappaint.ElemStyles;
    4342import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
    4443import org.openstreetmap.josm.gui.mappaint.MultiCascade;
     
    4645import org.openstreetmap.josm.gui.mappaint.StyleElementList;
    4746import org.openstreetmap.josm.gui.mappaint.StyleSource;
    4847import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
     48import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
    4949import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
    5050import org.openstreetmap.josm.gui.util.GuiHelper;
    5151import org.openstreetmap.josm.gui.widgets.JosmTextArea;
     
    274274        }
    275275
    276276        void addWayNodes(Way w) {
     277            Node last = null;
    277278            add(tr("{0} Nodes: ", w.getNodesCount()));
    278279            for (Node n : w.getNodes()) {
     280                if (last!=null) {
     281                    s.append(n.getCoor().getDistanceSummary(last.getCoor()));
     282                }
    279283                s.append(INDENT).append(INDENT);
    280284                addNameAndId(n);
    281285                s.append(NL);
     286                last = n;
    282287            }
    283288        }
    284289