Ticket #13303: patch-mapview-hatched-stay-in-place.patch

File patch-mapview-hatched-stay-in-place.patch, 12.3 KB (added by michael2402, 10 years ago)
  • src/org/openstreetmap/josm/data/Bounds.java

    diff --git a/src/org/openstreetmap/josm/data/Bounds.java b/src/org/openstreetmap/josm/data/Bounds.java
    index 067a9c3..8ce17fd 100644
    a b import java.awt.geom.Rectangle2D;  
    77import java.text.DecimalFormat;
    88import java.text.MessageFormat;
    99import java.util.Objects;
     10import java.util.function.Consumer;
    1011
    1112import org.openstreetmap.josm.data.coor.LatLon;
    1213import org.openstreetmap.josm.data.osm.BBox;
     14import org.openstreetmap.josm.data.projection.Projection;
    1315import org.openstreetmap.josm.tools.CheckParameterUtil;
    1416
    1517/**
    public class Bounds {  
    398400     * @return the bounding box to Rectangle2D.Double
    399401     */
    400402    public Rectangle2D.Double asRect() {
    401         double w = maxLon-minLon + (crosses180thMeridian() ? 360.0 : 0.0);
     403        double w = getWidth();
    402404        return new Rectangle2D.Double(minLon, minLat, w, maxLat-minLat);
    403405    }
    404406
     407    private double getWidth() {
     408        return maxLon-minLon + (crosses180thMeridian() ? 360.0 : 0.0);
     409    }
     410
    405411    public double getArea() {
    406         double w = maxLon-minLon + (crosses180thMeridian() ? 360.0 : 0.0);
     412        double w = getWidth();
    407413        return w * (maxLat - minLat);
    408414    }
    409415
    public class Bounds {  
    441447        maxLon = LatLon.toIntervalLon(maxLon);
    442448    }
    443449
     450    /**
     451     * Visit points along the edge of this bounds instance.
     452     * @param projection The projection that should be used to determine how often the edge should be split along a given corner.
     453     * @param visitor A function to call for the points on the edge.
     454     */
     455    public void visitEdge(Projection projection, Consumer<LatLon> visitor) {
     456        double width = getWidth();
     457        double height = maxLat - minLat;
     458        //TODO: Use projection to see if there is any need for doing this along each axis.
     459        int splitX = Math.max((int) width / 10, 10);
     460        int splitY = Math.max((int) height / 10, 10);
     461
     462        for (int step = 0; step < splitX; step++) {
     463            visitor.accept(new LatLon(minLat, minLon + width * step / splitX));
     464        }
     465        for (int step = 0; step < splitY; step++) {
     466            visitor.accept(new LatLon(minLat + height * step / splitY, maxLon));
     467        }
     468        for (int step = 0; step < splitX; step++) {
     469            visitor.accept(new LatLon(maxLat, maxLon - width * step / splitX));
     470        }
     471        for (int step = 0; step < splitY; step++) {
     472            visitor.accept(new LatLon(maxLat - height * step / splitY, minLon));
     473        }
     474    }
     475
    444476    @Override
    445477    public int hashCode() {
    446478        return Objects.hash(minLat, minLon, maxLat, maxLon);
  • src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java

    diff --git a/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java b/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
    index b8f290c..553e85b 100644
    a b public class BoundingXYVisitor extends AbstractVisitor {  
    5353     */
    5454    public void visit(Bounds b) {
    5555        if (b != null) {
    56             visit(b.getMin());
    57             visit(b.getMax());
     56            b.visitEdge(Main.getProjection(), this::visit);
    5857        }
    5958    }
    6059
  • src/org/openstreetmap/josm/gui/MapView.java

    diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
    index 63e4bb4..cdb1da9 100644
    a b import java.awt.event.MouseAdapter;  
    1717import java.awt.event.MouseEvent;
    1818import java.awt.event.MouseMotionListener;
    1919import java.awt.geom.Area;
    20 import java.awt.geom.GeneralPath;
    2120import java.awt.image.BufferedImage;
    2221import java.beans.PropertyChangeEvent;
    2322import java.beans.PropertyChangeListener;
    import org.openstreetmap.josm.data.ProjectionBounds;  
    4544import org.openstreetmap.josm.data.SelectionChangedListener;
    4645import org.openstreetmap.josm.data.ViewportData;
    4746import org.openstreetmap.josm.data.coor.EastNorth;
    48 import org.openstreetmap.josm.data.coor.LatLon;
    4947import org.openstreetmap.josm.data.imagery.ImageryInfo;
    5048import org.openstreetmap.josm.data.osm.DataSet;
    5149import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    973971    private void drawWorldBorders(Graphics2D tempG) {
    974972        tempG.setColor(Color.WHITE);
    975973        Bounds b = getProjection().getWorldBoundsLatLon();
    976         double lat = b.getMinLat();
    977         double lon = b.getMinLon();
    978 
    979         Point p = getPoint(b.getMin());
    980 
    981         GeneralPath path = new GeneralPath();
    982 
    983         double d = 1.0;
    984         path.moveTo(p.x, p.y);
    985         double max = b.getMax().lat();
    986         for (; lat <= max; lat += d) {
    987             p = getPoint(new LatLon(lat >= max ? max : lat, lon));
    988             path.lineTo(p.x, p.y);
    989         }
    990         lat = max; max = b.getMax().lon();
    991         for (; lon <= max; lon += d) {
    992             p = getPoint(new LatLon(lat, lon >= max ? max : lon));
    993             path.lineTo(p.x, p.y);
    994         }
    995         lon = max; max = b.getMinLat();
    996         for (; lat >= max; lat -= d) {
    997             p = getPoint(new LatLon(lat <= max ? max : lat, lon));
    998             path.lineTo(p.x, p.y);
    999         }
    1000         lat = max; max = b.getMinLon();
    1001         for (; lon >= max; lon -= d) {
    1002             p = getPoint(new LatLon(lat, lon <= max ? max : lon));
    1003             path.lineTo(p.x, p.y);
    1004         }
    1005974
    1006975        int w = getWidth();
    1007976        int h = getHeight();
    1008977
    1009978        // Work around OpenJDK having problems when drawing out of bounds
    1010         final Area border = new Area(path);
     979        final Area border = getState().getArea(b).getViewArea();
    1011980        // Make the viewport 1px larger in every direction to prevent an
    1012981        // additional 1px border when zooming in
    1013982        final Area viewport = new Area(new Rectangle(-1, -1, w + 2, h + 2));
  • src/org/openstreetmap/josm/gui/MapViewState.java

    diff --git a/src/org/openstreetmap/josm/gui/MapViewState.java b/src/org/openstreetmap/josm/gui/MapViewState.java
    index f7dcd8d..fc08ac9 100644
    a b import java.awt.Container;  
    55import java.awt.Point;
    66import java.awt.Rectangle;
    77import java.awt.geom.AffineTransform;
     8import java.awt.geom.Area;
     9import java.awt.geom.Path2D;
    810import java.awt.geom.Point2D;
    911import java.awt.geom.Point2D.Double;
    1012import java.awt.geom.Rectangle2D;
    public final class MapViewState {  
    213215                topLeft.north() / scale);
    214216    }
    215217
     218    public LatLonRectangle getArea(Bounds bounds) {
     219        return new LatLonRectangle(bounds);
     220    }
     221
    216222    /**
    217223     * Creates a new state that is the same as the current state except for that it is using a new center.
    218224     * @param newCenter The new center coordinate.
    public final class MapViewState {  
    490496        }
    491497    }
    492498
     499    /**
     500     * This is a rectangle in lat/lon space.
     501     * @author Michael Zangl
     502     * @since xxx
     503     */
     504    public class LatLonRectangle {
     505
     506        private Bounds bounds;
     507
     508        LatLonRectangle(Bounds bounds) {
     509            this.bounds = new Bounds(bounds);
     510        }
     511
     512        /**
     513         * Gets the approximate area this shape covers in view space.
     514         * @return The area in view space
     515         */
     516        public Area getViewArea() {
     517            Path2D area = new Path2D.Double();
     518            bounds.visitEdge(getProjection(), latlon -> {
     519                MapViewPoint point = getPointFor(latlon);
     520                if (area.getCurrentPoint() == null) {
     521                    area.moveTo(point.getInViewX(), point.getInViewY());
     522                } else {
     523                    area.lineTo(point.getInViewX(), point.getInViewY());
     524                }
     525            });
     526            area.closePath();
     527            return new Area(area);
     528        }
     529    }
     530
    493531}
  • src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java b/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
    index 7bdfd1a..cef09f8 100644
    a b import java.awt.Composite;  
    1212import java.awt.Graphics2D;
    1313import java.awt.GraphicsEnvironment;
    1414import java.awt.GridBagLayout;
    15 import java.awt.Point;
    1615import java.awt.Rectangle;
    1716import java.awt.TexturePaint;
    1817import java.awt.event.ActionEvent;
    1918import java.awt.geom.Area;
     19import java.awt.geom.Rectangle2D;
    2020import java.awt.image.BufferedImage;
    2121import java.io.File;
    2222import java.util.ArrayList;
    import org.openstreetmap.josm.data.ProjectionBounds;  
    5151import org.openstreetmap.josm.data.SelectionChangedListener;
    5252import org.openstreetmap.josm.data.conflict.Conflict;
    5353import org.openstreetmap.josm.data.conflict.ConflictCollection;
     54import org.openstreetmap.josm.data.coor.EastNorth;
    5455import org.openstreetmap.josm.data.coor.LatLon;
    5556import org.openstreetmap.josm.data.gpx.GpxConstants;
    5657import org.openstreetmap.josm.data.gpx.GpxData;
    import org.openstreetmap.josm.data.projection.Projection;  
    8182import org.openstreetmap.josm.data.validation.TestError;
    8283import org.openstreetmap.josm.gui.ExtendedDialog;
    8384import org.openstreetmap.josm.gui.MapView;
     85import org.openstreetmap.josm.gui.MapViewState.MapViewPoint;
    8486import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    8587import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
    8688import org.openstreetmap.josm.gui.io.AbstractIOTask;
    import org.openstreetmap.josm.tools.date.DateUtils;  
    110112 * @since 17
    111113 */
    112114public class OsmDataLayer extends AbstractModifiableLayer implements Listener, SelectionChangedListener {
     115    private static final int HATCHED_SIZE = 15;
    113116    /** Property used to know if this layer has to be saved on disk */
    114117    public static final String REQUIRES_SAVE_TO_DISK_PROP = OsmDataLayer.class.getName() + ".requiresSaveToDisk";
    115118    /** Property used to know if this layer has to be uploaded */
    public class OsmDataLayer extends AbstractModifiableLayer implements Listener, S  
    303306    private final ConflictCollection conflicts;
    304307
    305308    /**
    306      * a paint texture for non-downloaded area
     309     * a texture for non-downloaded area
    307310     */
    308     private static volatile TexturePaint hatched;
     311    private static volatile BufferedImage hatched;
    309312
    310313    static {
    311314        createHatchTexture();
    public class OsmDataLayer extends AbstractModifiableLayer implements Listener, S  
    331334     * Initialize the hatch pattern used to paint the non-downloaded area
    332335     */
    333336    public static void createHatchTexture() {
    334         BufferedImage bi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_ARGB);
     337        BufferedImage bi = new BufferedImage(HATCHED_SIZE, HATCHED_SIZE, BufferedImage.TYPE_INT_ARGB);
    335338        Graphics2D big = bi.createGraphics();
    336339        big.setColor(getBackgroundColor());
    337340        Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
    338341        big.setComposite(comp);
    339         big.fillRect(0, 0, 15, 15);
     342        big.fillRect(0, 0, HATCHED_SIZE, HATCHED_SIZE);
    340343        big.setColor(getOutsideColor());
    341344        big.drawLine(-1, 6, 6, -1);
    342345        big.drawLine(4, 16, 16, 4);
    343         Rectangle r = new Rectangle(0, 0, 15, 15);
    344         hatched = new TexturePaint(bi, r);
     346        hatched = bi;
    345347    }
    346348
    347349    /**
    public class OsmDataLayer extends AbstractModifiableLayer implements Listener, S  
    407409                if (bounds.isCollapsed()) {
    408410                    continue;
    409411                }
    410                 Point p1 = mv.getPoint(bounds.getMin());
    411                 Point p2 = mv.getPoint(bounds.getMax());
    412                 Rectangle r = new Rectangle(Math.min(p1.x, p2.x), Math.min(p1.y, p2.y), Math.abs(p2.x-p1.x), Math.abs(p2.y-p1.y));
    413                 a.subtract(new Area(r));
     412
     413                a.subtract(mv.getState().getArea(bounds).getViewArea());
    414414            }
    415415
    416416            // paint remainder
    417             g.setPaint(hatched);
     417            MapViewPoint anchor = mv.getState().getPointFor(new EastNorth(0, 0));
     418            Rectangle2D anchorRect = new Rectangle2D.Double(anchor.getInView().getX() % HATCHED_SIZE,
     419                    anchor.getInView().getY() % HATCHED_SIZE, HATCHED_SIZE, HATCHED_SIZE);
     420            g.setPaint(new TexturePaint(hatched, anchorRect));
    418421            g.fill(a);
    419422        }
    420423