Changeset 5132 in josm for trunk


Ignore:
Timestamp:
2012-03-29T23:22:07+02:00 (8 years ago)
Author:
simon04
Message:

fix #7513 - Warn non-experts when combining ways with conflicting tags or ways being part of relations

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

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r4982 r5132  
    22package org.openstreetmap.josm.actions;
    33
    4 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.combineTigerTags;
    5 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing;
    6 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing;
    74import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    85import static org.openstreetmap.josm.tools.I18n.tr;
     
    3431import org.openstreetmap.josm.data.osm.Node;
    3532import org.openstreetmap.josm.data.osm.OsmPrimitive;
    36 import org.openstreetmap.josm.data.osm.Relation;
    3733import org.openstreetmap.josm.data.osm.TagCollection;
    3834import org.openstreetmap.josm.data.osm.Way;
     
    9389        }
    9490        return targetWay;
    95     }
    96 
    97     /**
    98      * Replies the set of referring relations
    99      *
    100      * @return the set of referring relations
    101      */
    102     public static Set<Relation> getParentRelations(Collection<Way> ways) {
    103         HashSet<Relation> ret = new HashSet<Relation>();
    104         for (Way w: ways) {
    105             ret.addAll(OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class));
    106         }
    107         return ret;
    10891    }
    10992
     
    194177        modifiedTargetWay.setNodes(path);
    195178
    196         TagCollection completeWayTags = new TagCollection(wayTags);
    197         combineTigerTags(completeWayTags);
    198         normalizeTagCollectionBeforeEditing(completeWayTags, ways);
    199         TagCollection tagsToEdit = new TagCollection(completeWayTags);
    200         completeTagCollectionForEditing(tagsToEdit);
    201 
    202         CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
    203         dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues());
    204         dialog.setTargetPrimitive(targetWay);
    205         Set<Relation> parentRelations = getParentRelations(ways);
    206         dialog.getRelationMemberConflictResolverModel().populate(
    207                 parentRelations,
    208                 ways
    209         );
    210         dialog.prepareDefaultDecisions();
    211 
    212         // resolve tag conflicts if necessary
    213         //
    214         if (!completeWayTags.isApplicableToPrimitive() || !parentRelations.isEmpty()) {
    215             dialog.setVisible(true);
    216             if (dialog.isCanceled())
    217                 throw new UserCancelException();
    218         }
     179        List<Command> resolution = CombinePrimitiveResolverDialog.launchIfNecessary(wayTags, ways, Collections.singleton(targetWay));
    219180
    220181        LinkedList<Command> cmds = new LinkedList<Command>();
     
    223184
    224185        cmds.add(new ChangeCommand(targetWay, modifiedTargetWay));
    225         cmds.addAll(dialog.buildResolutionCommands());
     186        cmds.addAll(resolution);
    226187        cmds.add(new DeleteCommand(deletedWays));
    227188        final SequenceCommand sequenceCommand = new SequenceCommand(tr("Combine {0} ways", ways.size()), cmds);
  • trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java

    r4982 r5132  
    4343import org.openstreetmap.josm.data.osm.Way;
    4444import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog;
    45 import org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil;
    4645import org.openstreetmap.josm.tools.Geometry;
    4746import org.openstreetmap.josm.tools.Pair;
     
    538537        }
    539538
    540         if (ways.size() < 2)
     539        if (ways.size() < 2) {
    541540            return true;
    542 
    543         //mostly copied from CombineWayAction.java.
     541        }
     542
    544543        TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways);
    545         TagCollection completeWayTags = new TagCollection(wayTags);
    546         TagConflictResolutionUtil.combineTigerTags(completeWayTags);
    547         TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, ways);
    548         TagCollection tagsToEdit = new TagCollection(completeWayTags);
    549         TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);
    550 
    551         CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
    552         dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues());
    553         dialog.setTargetPrimitive(ways.get(0));
    554         Collection<Relation> parentRelations = CombineWayAction.getParentRelations(ways);
    555         parentRelations = filterOwnMultipolygonRelations(parentRelations, polygons);
    556         dialog.getRelationMemberConflictResolverModel().populate(
    557                 parentRelations,
    558                 ways
    559         );
    560         dialog.prepareDefaultDecisions();
    561 
    562         // resolve tag conflicts if necessary
    563         //
    564         if (!completeWayTags.isApplicableToPrimitive() || !parentRelations.isEmpty()) {
    565             dialog.setVisible(true);
    566             if (dialog.isCanceled())
    567                 return false;
    568         }
    569 
    570         for (Way way : ways) {
    571             dialog.setTargetPrimitive(way);
    572             cmds.addAll(dialog.buildResolutionCommands());
    573         }
    574 
    575         commitCommands(marktr("Fix tag conflicts"));
    576         return true;
     544        try {
     545            cmds.addAll(CombinePrimitiveResolverDialog.launchIfNecessary(wayTags, ways, ways));
     546            commitCommands(marktr("Fix tag conflicts"));
     547            return true;
     548        } catch (UserCancelException ex) {
     549            return false;
     550        }
    577551    }
    578552
     
    12091183        Set<Way> processedInnerWays = new LinkedHashSet<Way>();
    12101184
    1211         for (Relation r : CombineWayAction.getParentRelations(selectedWays)) {
     1185        for (Relation r : OsmPrimitive.getParentRelations(selectedWays)) {
    12121186            if (r.isDeleted() || !r.isMultipolygon()) {
    12131187                continue;
  • trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java

    r5059 r5132  
    99import java.util.ArrayList;
    1010import java.util.Collection;
     11import java.util.Collections;
    1112import java.util.HashSet;
    1213import java.util.LinkedList;
     
    2223import org.openstreetmap.josm.command.DeleteCommand;
    2324import org.openstreetmap.josm.command.SequenceCommand;
     25import org.openstreetmap.josm.corrector.UserCancelException;
    2426import org.openstreetmap.josm.data.coor.EastNorth;
    2527import org.openstreetmap.josm.data.coor.LatLon;
     
    3335import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
    3436import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog;
    35 import org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil;
    3637import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    3738import org.openstreetmap.josm.tools.CheckParameterUtil;
     
    256257        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
    257258        CheckParameterUtil.ensureParameterNotNull(targetNode, "targetNode");
    258         if (nodes == null)
     259        if (nodes == null) {
    259260            return null;
     261        }
    260262
    261263        Set<RelationToChildReference> relationToNodeReferences = RelationToChildReference.getRelationToChildReferences(nodes);
    262264
    263         // build the tag collection
    264         //
    265         TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes);
    266         TagConflictResolutionUtil.combineTigerTags(nodeTags);
    267         TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(nodeTags, nodes);
    268         TagCollection nodeTagsToEdit = new TagCollection(nodeTags);
    269         TagConflictResolutionUtil.completeTagCollectionForEditing(nodeTagsToEdit);
    270 
    271         // launch a conflict resolution dialog, if necessary
    272         //
    273         CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
    274         dialog.getTagConflictResolverModel().populate(nodeTagsToEdit, nodeTags.getKeysWithMultipleValues());
    275         dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences);
    276         dialog.setTargetPrimitive(targetNode);
    277         dialog.prepareDefaultDecisions();
    278         // conflict resolution is necessary if there are conflicts in the merged tags
    279         // or if at least one of the merged nodes is referred to by a relation
    280         //
    281         if (! nodeTags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) {
    282             dialog.setVisible(true);
    283             if (dialog.isCanceled())
     265        try {
     266            TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes);
     267            List<Command> resultion = CombinePrimitiveResolverDialog.launchIfNecessary(nodeTags, nodes, Collections.singleton(targetNode));
     268            LinkedList<Command> cmds = new LinkedList<Command>();
     269
     270            // the nodes we will have to delete
     271            //
     272            Collection<Node> nodesToDelete = new HashSet<Node>(nodes);
     273            nodesToDelete.remove(targetNode);
     274
     275            // fix the ways referring to at least one of the merged nodes
     276            //
     277            Collection<Way> waysToDelete = new HashSet<Way>();
     278            List<Command> wayFixCommands = fixParentWays(
     279                    nodesToDelete,
     280                    targetNode);
     281            if (wayFixCommands == null) {
    284282                return null;
    285         }
    286         LinkedList<Command> cmds = new LinkedList<Command>();
    287 
    288         // the nodes we will have to delete
    289         //
    290         Collection<Node> nodesToDelete = new HashSet<Node>(nodes);
    291         nodesToDelete.remove(targetNode);
    292 
    293         // fix the ways referring to at least one of the merged nodes
    294         //
    295         Collection<Way> waysToDelete= new HashSet<Way>();
    296         List<Command> wayFixCommands = fixParentWays(
    297                 nodesToDelete,
    298                 targetNode
    299         );
    300         if (wayFixCommands == null)
     283            }
     284            cmds.addAll(wayFixCommands);
     285
     286            // build the commands
     287            //
     288            if (targetNode != targetLocationNode) {
     289                Node newTargetNode = new Node(targetNode);
     290                newTargetNode.setCoor(targetLocationNode.getCoor());
     291                cmds.add(new ChangeCommand(targetNode, newTargetNode));
     292            }
     293            cmds.addAll(resultion);
     294            if (!nodesToDelete.isEmpty()) {
     295                cmds.add(new DeleteCommand(nodesToDelete));
     296            }
     297            if (!waysToDelete.isEmpty()) {
     298                cmds.add(new DeleteCommand(waysToDelete));
     299            }
     300            Command cmd = new SequenceCommand(tr("Merge {0} nodes", nodes.size()), cmds);
     301            return cmd;
     302        } catch (UserCancelException ex) {
    301303            return null;
    302         cmds.addAll(wayFixCommands);
    303 
    304         // build the commands
    305         //
    306         if (targetNode != targetLocationNode) {
    307             Node newTargetNode = new Node(targetNode);
    308             newTargetNode.setCoor(targetLocationNode.getCoor());
    309             cmds.add(new ChangeCommand(targetNode, newTargetNode));
    310         }
    311         cmds.addAll(dialog.buildResolutionCommands());
    312         if (!nodesToDelete.isEmpty()) {
    313             cmds.add(new DeleteCommand(nodesToDelete));
    314         }
    315         if (!waysToDelete.isEmpty()) {
    316             cmds.add(new DeleteCommand(waysToDelete));
    317         }
    318         Command cmd = new SequenceCommand(tr("Merge {0} nodes", nodes.size()), cmds);
    319         return cmd;
     304        }
    320305    }
    321306
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r4895 r5132  
    11351135    }
    11361136
     1137    /**
     1138     * Replies the set of referring relations
     1139     *
     1140     * @return the set of referring relations
     1141     */
     1142    public static Set<Relation> getParentRelations(Collection<? extends OsmPrimitive> primitives) {
     1143        HashSet<Relation> ret = new HashSet<Relation>();
     1144        for (OsmPrimitive w : primitives) {
     1145            ret.addAll(OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class));
     1146        }
     1147        return ret;
     1148    }
    11371149}
  • trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java

    r5083 r5132  
    3737import org.openstreetmap.josm.tools.I18n;
    3838import org.openstreetmap.josm.tools.TaggingPresetNameTemplateList;
     39import org.openstreetmap.josm.tools.Utils;
     40import org.openstreetmap.josm.tools.Utils.Function;
    3941
    4042/**
     
    686688
    687689    public String formatAsHtmlUnorderedList(Collection<? extends OsmPrimitive> primitives) {
    688         StringBuilder sb = new StringBuilder(1024);
    689         sb.append("<ul>");
    690         for (OsmPrimitive i : primitives) {
    691             sb.append("<li>").append(i.getDisplayName(this)).append("</li>");
    692         }
    693         sb.append("</ul>");
    694         return sb.toString();
     690        return Utils.joinAsHtmlUnorderedList(Utils.transform(primitives, new Function<OsmPrimitive, String>() {
     691
     692            @Override
     693            public String apply(OsmPrimitive x) {
     694                return x.getDisplayName(DefaultNameFormatter.this);
     695            }
     696        }));
    695697    }
    696698
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java

    r4310 r5132  
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
     6import static org.openstreetmap.josm.tools.I18n.trn;
    67
    78import java.awt.BorderLayout;
     
    1617import java.beans.PropertyChangeEvent;
    1718import java.beans.PropertyChangeListener;
     19import java.util.Collection;
    1820import java.util.HashSet;
    1921import java.util.LinkedList;
     
    3032
    3133import org.openstreetmap.josm.Main;
     34import org.openstreetmap.josm.actions.ExpertToggleAction;
    3235import org.openstreetmap.josm.command.ChangePropertyCommand;
    3336import org.openstreetmap.josm.command.Command;
     37import org.openstreetmap.josm.corrector.UserCancelException;
     38import org.openstreetmap.josm.data.osm.NameFormatter;
    3439import org.openstreetmap.josm.data.osm.Node;
    3540import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    3742import org.openstreetmap.josm.data.osm.TagCollection;
    3843import org.openstreetmap.josm.data.osm.Way;
     44import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
    3945import org.openstreetmap.josm.gui.DefaultNameFormatter;
    4046import org.openstreetmap.josm.gui.SideButton;
     
    4248import org.openstreetmap.josm.gui.help.HelpUtil;
    4349import org.openstreetmap.josm.tools.ImageProvider;
     50import org.openstreetmap.josm.tools.Utils;
     51import org.openstreetmap.josm.tools.Utils.Function;
    4452import org.openstreetmap.josm.tools.WindowGeometry;
    4553
     
    4755 * This dialog helps to resolve conflicts occurring when ways are combined or
    4856 * nodes are merged.
     57 *
     58 * Usage: {@link #launchIfNecessary} followed by {@link #buildResolutionCommands}.
     59 *
     60 * Prior to {@link #launchIfNecessary}, the following usage sequence was needed:
    4961 *
    5062 * There is a singleton instance of this dialog which can be retrieved using
     
    8294     *
    8395     * @return the unique instance of the dialog
     96     * @deprecated use {@link #launchIfNecessary} instead.
    8497     */
     98    @Deprecated
    8599    public static CombinePrimitiveResolverDialog getInstance() {
    86100        if (instance == null) {
     
    421435        }
    422436    }
     437
     438    public static List<Command> launchIfNecessary(
     439            final TagCollection tagsOfPrimitives,
     440            final Collection<? extends OsmPrimitive> primitives,
     441            final Collection<? extends OsmPrimitive> targetPrimitives) throws UserCancelException {
     442
     443        final TagCollection completeWayTags = new TagCollection(tagsOfPrimitives);
     444        TagConflictResolutionUtil.combineTigerTags(completeWayTags);
     445        TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, primitives);
     446        final TagCollection tagsToEdit = new TagCollection(completeWayTags);
     447        TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);
     448
     449        final CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
     450
     451        dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues());
     452
     453        final Set<Relation> parentRelations = OsmPrimitive.getParentRelations(primitives);
     454        dialog.getRelationMemberConflictResolverModel().populate(parentRelations, primitives);
     455        dialog.prepareDefaultDecisions();
     456
     457        // show information dialog to non-experts
     458        if (!completeWayTags.isApplicableToPrimitive() && !ExpertToggleAction.isExpert()) {
     459            String conflicts = Utils.joinAsHtmlUnorderedList(Utils.transform(completeWayTags.getKeysWithMultipleValues(), new Function<String, String>() {
     460
     461                @Override
     462                public String apply(String key) {
     463                    return tr("{0} ({1})", key, Utils.join(tr(", "), Utils.transform(completeWayTags.getValues(key), new Function<String, String>() {
     464
     465                        @Override
     466                        public String apply(String x) {
     467                            return x == null || x.isEmpty() ? tr("<i>missing</i>") : x;
     468                        }
     469                    })));
     470                }
     471            }));
     472            String msg = tr("You are about to combine {0} objects, "
     473                    + "but the following tags are used conflictingly:<br/>{1}"
     474                    + "If these objects are combined, the resulting object may have unwanted tags.<br/>"
     475                    + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>"
     476                    + "Do you want to continue?",
     477                    primitives.size(), conflicts);
     478            if (!ConditionalOptionPaneUtil.showConfirmationDialog(
     479                    "combine_tags",
     480                    Main.parent,
     481                    "<html>" + msg + "</html>",
     482                    tr("Combine confirmation"),
     483                    JOptionPane.YES_NO_OPTION,
     484                    JOptionPane.QUESTION_MESSAGE,
     485                    JOptionPane.YES_OPTION)) {
     486                throw new UserCancelException();
     487            }
     488        }
     489
     490        if (!parentRelations.isEmpty() && !ExpertToggleAction.isExpert()) {
     491            String msg = trn("You are about to combine {1} objects, "
     492                    + "which are part of {0} relation:<br/>{2}"
     493                    + "Combining these objects may break this relation. If you are unsure, please cancel this operation.<br/>"
     494                    + "If you want to continue, you are shown a dialog to decide how to adapt the relation.<br/><br/>"
     495                    + "Do you want to continue?",
     496                    "You are about to combine {1} objects, "
     497                    + "which are part of {0} relations:<br/>{2}"
     498                    + "Combining these objects may break these relations. If you are unsure, please cancel this operation.<br/>"
     499                    + "If you want to continue, you are shown a dialog to decide how to adapt the relations.<br/><br/>"
     500                    + "Do you want to continue?",
     501                    parentRelations.size(), parentRelations.size(), primitives.size(),
     502                    DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(parentRelations));
     503            if (!ConditionalOptionPaneUtil.showConfirmationDialog(
     504                    "combine_tags",
     505                    Main.parent,
     506                    "<html>" + msg + "</html>",
     507                    tr("Combine confirmation"),
     508                    JOptionPane.YES_NO_OPTION,
     509                    JOptionPane.QUESTION_MESSAGE,
     510                    JOptionPane.YES_OPTION)) {
     511                throw new UserCancelException();
     512            }
     513        }
     514
     515        // resolve tag conflicts if necessary
     516        if (!completeWayTags.isApplicableToPrimitive() || !parentRelations.isEmpty()) {
     517            dialog.setVisible(true);
     518            if (dialog.isCanceled()) {
     519                throw new UserCancelException();
     520            }
     521        }
     522        List<Command> cmds = new LinkedList<Command>();
     523        for (OsmPrimitive i : targetPrimitives) {
     524            dialog.setTargetPrimitive(i);
     525            cmds.addAll(dialog.buildResolutionCommands());
     526        }
     527        return cmds;
     528    }
    423529}
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolver.java

    r3083 r5132  
    2828    /** the model for the tag conflict resolver */
    2929    private TagConflictResolverModel model;
    30     /** selects wheter only tags with conflicts are displayed */
     30    /** selects whether only tags with conflicts are displayed */
    3131    private JCheckBox cbShowTagsWithConflictsOnly;
    3232    private JCheckBox cbShowTagsWithMultiValuesOnly;
     
    4141        gc.weightx = 1.0;
    4242        gc.anchor = GridBagConstraints.LINE_START;
     43        gc.gridwidth = 2;
    4344        pnl.add(new JLabel(tr("<html>Please select the values to keep for the following tags.</html>")), gc);
    4445
     46        gc.gridwidth = 1;
    4547        gc.gridy = 1;
    4648        gc.fill = GridBagConstraints.HORIZONTAL;
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r4816 r5132  
    2323import java.util.Iterator;
    2424import java.util.List;
     25import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2526
    2627/**
     
    166167    }
    167168
     169    public static String joinAsHtmlUnorderedList(Collection<?> values) {
     170        StringBuilder sb = new StringBuilder(1024);
     171        sb.append("<ul>");
     172        for (Object i : values) {
     173            sb.append("<li>").append(i).append("</li>");
     174        }
     175        sb.append("</ul>");
     176        return sb.toString();
     177    }
     178
    168179    /**
    169180     * convert Color to String
     
    423434     * @return the transformed unmodifiable collection
    424435     */
    425     public static <A, B> Collection<B> transform(final Collection<A> c, final Function<A, B> f) {
     436    public static <A, B> Collection<B> transform(final Collection<? extends A> c, final Function<A, B> f) {
    426437        return new Collection<B>() {
    427438
     
    460471                return new Iterator<B>() {
    461472
    462                     private Iterator<A> it = c.iterator();
     473                    private Iterator<? extends A> it = c.iterator();
    463474
    464475                    @Override
Note: See TracChangeset for help on using the changeset viewer.