Changeset 9118 in josm


Ignore:
Timestamp:
2015-12-14T14:14:23+01:00 (4 years ago)
Author:
bastiK
Message:

more stable zoom&move for extreme scales and unusual world bounds

Location:
trunk/src/org/openstreetmap/josm
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/projection/AbstractProjection.java

    r9105 r9118  
    22package org.openstreetmap.josm.data.projection;
    33
     4import org.openstreetmap.josm.data.Bounds;
     5import org.openstreetmap.josm.data.ProjectionBounds;
    46import org.openstreetmap.josm.data.coor.EastNorth;
    57import org.openstreetmap.josm.data.coor.LatLon;
     
    3133    protected double pm;       /* prime meridian */
    3234    protected double k0 = 1.0; /* general scale factor */
     35
     36    private volatile ProjectionBounds projectionBoundsBox;
    3337
    3438    public final Ellipsoid getEllipsoid() {
     
    105109        return degree + (minute/60.0) + (second/3600.0);
    106110    }
     111
     112    @Override
     113    public final ProjectionBounds getWorldBoundsBoxEastNorth() {
     114        ProjectionBounds result = projectionBoundsBox;
     115        if (result == null) {
     116            synchronized (this) {
     117                result = projectionBoundsBox;
     118                if (result == null) {
     119                    Bounds b = getWorldBoundsLatLon();
     120                    // add 4 corners
     121                    result = new ProjectionBounds(latlon2eastNorth(b.getMin()));
     122                    result.extend(latlon2eastNorth(b.getMax()));
     123                    result.extend(latlon2eastNorth(new LatLon(b.getMinLat(), b.getMaxLon())));
     124                    result.extend(latlon2eastNorth(new LatLon(b.getMaxLat(), b.getMinLon())));
     125                    // and trace along the outline
     126                    double dLon = (b.getMaxLon() - b.getMinLon()) / 1000;
     127                    double dLat = (b.getMaxLat() - b.getMinLat()) / 1000;
     128                    for (double lon=b.getMinLon(); lon<b.getMaxLon(); lon += dLon) {
     129                        result.extend(latlon2eastNorth(new LatLon(b.getMinLat(), lon)));
     130                        result.extend(latlon2eastNorth(new LatLon(b.getMaxLat(), lon)));
     131                    }
     132                    for (double lat=b.getMinLat(); lat<b.getMaxLat(); lat += dLat) {
     133                        result.extend(latlon2eastNorth(new LatLon(lat, b.getMinLon())));
     134                        result.extend(latlon2eastNorth(new LatLon(lat, b.getMaxLat())));
     135                    }
     136                    projectionBoundsBox = result;
     137                }
     138            }
     139        }
     140        return projectionBoundsBox;
     141    }
    107142}
  • trunk/src/org/openstreetmap/josm/data/projection/Projection.java

    r8584 r9118  
    33
    44import org.openstreetmap.josm.data.Bounds;
     5import org.openstreetmap.josm.data.ProjectionBounds;
    56import org.openstreetmap.josm.data.coor.EastNorth;
    67import org.openstreetmap.josm.data.coor.LatLon;
     
    7071
    7172    /**
     73     * Get an approximate EastNorth box around the lat/lon world bounds.
     74     *
     75     * Note: The projection is only valid within the bounds returned by
     76     * {@link #getWorldBoundsLatLon()}. The lat/lon bounds need not be a
     77     * rectangular shape in east/north space. This method returns a box that
     78     * contains this shape.
     79     *
     80     * @return EastNorth box around the lat/lon world bounds
     81     */
     82    ProjectionBounds getWorldBoundsBoxEastNorth();
     83
     84    /**
    7285     * Get the number of meters per unit of this projection. This more
    7386     * defines the scale of the map, than real conversion of unit to meters
  • trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java

    r9078 r9118  
    391391    public void zoomTo(EastNorth newCenter, double newScale, boolean initial) {
    392392        Bounds b = getProjection().getWorldBoundsLatLon();
    393         LatLon cl = Projections.inverseProject(newCenter);
    394         boolean changed = false;
    395         double lat = cl.lat();
    396         double lon = cl.lon();
    397         if (lat < b.getMinLat()) {
    398             changed = true;
    399             lat = b.getMinLat();
    400         } else if (lat > b.getMaxLat()) {
    401             changed = true;
    402             lat = b.getMaxLat();
    403         }
    404         if (lon < b.getMinLon()) {
    405             changed = true;
    406             lon = b.getMinLon();
    407         } else if (lon > b.getMaxLon()) {
    408             changed = true;
    409             lon = b.getMaxLon();
    410         }
    411         if (changed) {
    412             newCenter = Projections.project(new LatLon(lat, lon));
    413         }
    414         int width = getWidth()/2;
    415         int height = getHeight()/2;
    416         LatLon l1 = new LatLon(b.getMinLat(), lon);
    417         LatLon l2 = new LatLon(b.getMaxLat(), lon);
    418         EastNorth e1 = getProjection().latlon2eastNorth(l1);
    419         EastNorth e2 = getProjection().latlon2eastNorth(l2);
    420         double d = e2.north() - e1.north();
    421         if (height > 0 && d < height*newScale) {
    422             double newScaleH = d/height;
    423             e1 = getProjection().latlon2eastNorth(new LatLon(lat, b.getMinLon()));
    424             e2 = getProjection().latlon2eastNorth(new LatLon(lat, b.getMaxLon()));
    425             d = e2.east() - e1.east();
    426             if (width > 0 && d < width*newScale) {
    427                 newScale = Math.max(newScaleH, d/width);
    428             }
    429         } else if (height > 0) {
    430             d = d/(l1.greatCircleDistance(l2)*height*10);
    431             if (newScale < d) {
    432                 newScale = d;
     393        ProjectionBounds pb = getProjection().getWorldBoundsBoxEastNorth();
     394        int width = getWidth();
     395        int height = getHeight();
     396
     397        // make sure, the center of the screen is within projection bounds
     398        double east = newCenter.east();
     399        double north = newCenter.north();
     400        east = Math.max(east, pb.minEast);
     401        east = Math.min(east, pb.maxEast);
     402        north = Math.max(north, pb.minNorth);
     403        north = Math.min(north, pb.maxNorth);
     404        newCenter = new EastNorth(east, north);
     405
     406        // don't zoom out too much, the world bounds should be at least
     407        // half the size of the screen
     408        double pbHeight = pb.maxNorth - pb.minNorth;
     409        if (height > 0 && 2 * pbHeight < height * newScale) {
     410            double newScaleH = 2 * pbHeight / height;
     411            double pbWidth = pb.maxEast - pb.minEast;
     412            if (width > 0 && 2 * pbWidth < width * newScale) {
     413                double newScaleW = 2 * pbWidth / width;
     414                newScale = Math.max(newScaleH, newScaleW);
     415            }
     416        }
     417
     418        // don't zoom in too much, minimum: 100 px = 1 cm
     419        LatLon ll1 = getLatLon(width / 2 - 50, height / 2);
     420        LatLon ll2 = getLatLon(width / 2 + 50, height / 2);
     421        if (ll1.isValid() && ll1.isValid() && b.contains(ll1) && b.contains(ll2)) {
     422            double d_m = ll1.greatCircleDistance(ll2);
     423            double d_en = 100 * scale;
     424            double scaleMin = 0.01 * d_en / d_m / 100;
     425            if (newScale < scaleMin) {
     426                newScale = scaleMin;
    433427            }
    434428        }
Note: See TracChangeset for help on using the changeset viewer.