Ignore:
Timestamp:
2012-01-26T04:59:08+01:00 (13 years ago)
Author:
joshdoe
Message:

utilsplugin2: Fix possible bugs with "Replace Geometry", including those in #7295

More aggressive at preventing the action to be cautious.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/dumbutils/ReplaceGeometryAction.java

    r27564 r27623  
    11package utilsplugin2.dumbutils;
    22
     3import java.awt.event.ActionEvent;
     4import java.awt.event.KeyEvent;
     5import java.awt.geom.Area;
    36import java.awt.geom.Point2D;
    4 import java.awt.geom.Area;
     7import java.util.*;
     8import javax.swing.JOptionPane;
     9import org.openstreetmap.josm.Main;
     10import org.openstreetmap.josm.actions.JosmAction;
     11import org.openstreetmap.josm.command.*;
     12import org.openstreetmap.josm.data.coor.LatLon;
    513import org.openstreetmap.josm.data.osm.Node;
    6 import java.util.*;
    7 import org.openstreetmap.josm.command.*;
    8 import org.openstreetmap.josm.Main;
    9 import javax.swing.JOptionPane;
     14import org.openstreetmap.josm.data.osm.OsmPrimitive;
     15import org.openstreetmap.josm.data.osm.Relation;
    1016import org.openstreetmap.josm.data.osm.Way;
    11 import java.awt.event.KeyEvent;
    12 import org.openstreetmap.josm.tools.Shortcut;
    13 import java.awt.event.ActionEvent;
    14 import org.openstreetmap.josm.actions.JosmAction;
    15 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1617import org.openstreetmap.josm.gui.DefaultNameFormatter;
    1718import static org.openstreetmap.josm.tools.I18n.tr;
     19import org.openstreetmap.josm.tools.Shortcut;
    1820
    1921/**
     
    2426public class ReplaceGeometryAction extends JosmAction {
    2527    private static final String TITLE = tr("Replace Geometry");
    26     private static final double MAX_NODE_REPLACEMENT_DISTANCE = 3e-4;
    2728
    2829    public ReplaceGeometryAction() {
     
    3940
    4041        // There must be two ways selected: one with id > 0 and one new.
    41         List<OsmPrimitive> selection = new ArrayList(getCurrentDataSet().getSelected());
     42        List<OsmPrimitive> selection = new ArrayList<OsmPrimitive>(getCurrentDataSet().getSelected());
    4243        if (selection.size() != 2) {
    4344            JOptionPane.showMessageDialog(Main.parent,
     
    7576            Collection<Node> nodePool = getUnimportantNodes(way);
    7677            nodeToReplace = findNearestNode(node, nodePool);
    77 
    78             if (nodeToReplace == null && !nodePool.isEmpty()) {
    79                 // findNearestNode failed, just pick the first unimportant node
    80                 nodeToReplace = nodePool.iterator().next();
    81             }
    8278        }
    8379
     
    124120   
    125121    public void replaceWayWithWay(List<Way> selection) {
     122        // determine which way will be replaced and which will provide the geometry
    126123        boolean overrideNewCheck = false;
    127124        int idxNew = selection.get(0).isNew() ? 0 : 1;
    128 
    129125        if( selection.get(1-idxNew).isNew() ) {
    130126            // if both are new, select the one with all the DB nodes
     
    145141        Way geometry = selection.get(idxNew);
    146142        Way way = selection.get(1 - idxNew);
     143       
    147144        if( !overrideNewCheck && (way.isNew() || !geometry.isNew()) ) {
    148145            JOptionPane.showMessageDialog(Main.parent,
    149146                    tr("Please select one way that exists in the database and one new way with correct geometry."),
     147                    TITLE, JOptionPane.WARNING_MESSAGE);
     148            return;
     149        }
     150
     151        Area a = getCurrentDataSet().getDataSourceArea();
     152        if (!isInArea(way, a) || !isInArea(geometry, a)) {
     153            JOptionPane.showMessageDialog(Main.parent,
     154                    tr("The ways must be entirely within the downloaded area."),
     155                    TITLE, JOptionPane.WARNING_MESSAGE);
     156            return;
     157        }
     158       
     159        if (hasImportantNode(way)) {
     160            JOptionPane.showMessageDialog(Main.parent,
     161                    tr("The way to be replaced cannot have any nodes with properties or relation memberships."),
    150162                    TITLE, JOptionPane.WARNING_MESSAGE);
    151163            return;
     
    160172            List<OsmPrimitive> referrers = node.getReferrers();
    161173            if( node.isNew() && !node.isDeleted() && referrers.size() == 1
    162                     && referrers.get(0).equals(geometry) && !way.containsNode(node) )
     174                    && referrers.get(0).equals(geometry) && !way.containsNode(node)
     175                    && !hasInterestingKey(node))
    163176                geometryPool.add(node);
    164177        }
     
    221234    protected Collection<Node> getUnimportantNodes(Way way) {
    222235        Set<Node> nodePool = new HashSet<Node>();
    223         Area a = getCurrentDataSet().getDataSourceArea();
    224236        for (Node n : way.getNodes()) {
    225237            List<OsmPrimitive> referrers = n.getReferrers();
    226238            if (!n.isDeleted() && referrers.size() == 1 && referrers.get(0).equals(way)
    227                     && (n.isNewOrUndeleted() || a == null || a.contains(n.getCoor()))) {
     239                    && !hasInterestingKey(n)) {
    228240                nodePool.add(n);
    229241            }
    230242        }
    231243        return nodePool;
     244    }
     245   
     246    /**
     247     * Checks if a way has at least one important node (e.g. interesting tag,
     248     * role membership), and thus cannot be safely modified.
     249     *
     250     * @param way
     251     * @return
     252     */
     253    protected boolean hasImportantNode(Way way) {
     254        for (Node n : way.getNodes()) {
     255            //TODO: if way is connected to other ways, warn or disallow?
     256            for (OsmPrimitive o : n.getReferrers()) {
     257                if (o instanceof Relation) {
     258                    return true;
     259                }
     260            }
     261            if (hasInterestingKey(n)) {
     262                return true;
     263            }
     264        }
     265        return false;
     266    }
     267   
     268    protected boolean hasInterestingKey(OsmPrimitive object) {
     269        for (String key : object.getKeys().keySet()) {
     270            if (!OsmPrimitive.isUninterestingKey(key)) {
     271                return true;
     272            }
     273        }
     274        return false;
     275    }
     276
     277    protected static boolean isInArea(Node node, Area area) {
     278        if (node.isNewOrUndeleted() || area == null || area.contains(node.getCoor())) {
     279            return true;
     280        }
     281        return false;
     282    }
     283   
     284    protected static boolean isInArea(Way way, Area area) {
     285        if (area == null) {
     286            return true;
     287        }
     288
     289        for (Node n : way.getNodes()) {
     290            if (!isInArea(n, area)) {
     291                return false;
     292            }
     293        }
     294
     295        return true;
    232296    }
    233297   
     
    241305       
    242306        Node nearest = null;
    243         double distance = MAX_NODE_REPLACEMENT_DISTANCE;
     307        // TODO: use meters instead of degrees, but do it fast
     308        double distance = Double.parseDouble(Main.pref.get("utilsplugin2.replace-geometry.max-distance", "1"));
    244309        Point2D coor = node.getCoor();
     310
    245311        for( Node n : nodes ) {
    246312            double d = n.getCoor().distance(coor);
Note: See TracChangeset for help on using the changeset viewer.