Changeset 8871 in josm for trunk/src/org/openstreetmap/josm


Ignore:
Timestamp:
2015-10-14T22:23:55+02:00 (9 years ago)
Author:
simon04
Message:

fix #10018 fix #11945 - Improve relation member conflict resolution when combining ways

Keep/delete decisions are made if every member has the same role and the
members are in consecutive order within the relation.

Moves CombinePrimitiveResolverDialog#prepareDefaultRelationDecisions
to RelationMemberConflictResolverModel#prepareDefaultRelationDecisions.

Location:
trunk/src/org/openstreetmap/josm/gui/conflict/tags
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java

    r8836 r8871  
    1818import java.beans.PropertyChangeListener;
    1919import java.util.Collection;
    20 import java.util.Collections;
    21 import java.util.HashMap;
    2220import java.util.LinkedList;
    2321import java.util.List;
    24 import java.util.Map;
    2522import java.util.Set;
    2623
     
    5148import org.openstreetmap.josm.tools.CheckParameterUtil;
    5249import org.openstreetmap.josm.tools.ImageProvider;
    53 import org.openstreetmap.josm.tools.MultiMap;
    54 import org.openstreetmap.josm.tools.Predicates;
    5550import org.openstreetmap.josm.tools.Utils;
    5651import org.openstreetmap.josm.tools.Utils.Function;
     
    298293    }
    299294
    300     protected void prepareDefaultTagDecisions() {
     295    /**
     296     * Prepares the default decisions for populated tag and relation membership conflicts.
     297     */
     298    public void prepareDefaultDecisions() {
    301299        getTagConflictResolverModel().prepareDefaultTagDecisions();
    302     }
    303 
    304     protected void prepareDefaultRelationDecisions() {
    305         final RelationMemberConflictResolverModel model = getRelationMemberConflictResolverModel();
    306         final Map<Relation, Integer> numberOfKeepResolutions = new HashMap<>();
    307         final MultiMap<OsmPrimitive, Relation> resolvedRelationsPerPrimitive = new MultiMap<>();
    308 
    309         for (int i = 0; i < model.getNumDecisions(); i++) {
    310             final RelationMemberConflictDecision decision = model.getDecision(i);
    311             final Relation r = decision.getRelation();
    312             final OsmPrimitive p = decision.getOriginalPrimitive();
    313             if (!numberOfKeepResolutions.containsKey(r)) {
    314                 decision.decide(RelationMemberConflictDecisionType.KEEP);
    315                 numberOfKeepResolutions.put(r, 1);
    316                 resolvedRelationsPerPrimitive.put(p, r);
    317                 continue;
    318             }
    319 
    320             final Integer keepResolutions = numberOfKeepResolutions.get(r);
    321             final Collection<Relation> resolvedRelations = Utils.firstNonNull(
    322                     resolvedRelationsPerPrimitive.get(p), Collections.<Relation>emptyList());
    323             if (keepResolutions <= Utils.filter(resolvedRelations, Predicates.equalTo(r)).size()) {
    324                 // old relation contains one primitive more often than the current resolution => keep the current member
    325                 decision.decide(RelationMemberConflictDecisionType.KEEP);
    326                 numberOfKeepResolutions.put(r, keepResolutions + 1);
    327                 resolvedRelationsPerPrimitive.put(p, r);
    328             } else {
    329                 decision.decide(RelationMemberConflictDecisionType.REMOVE);
    330                 resolvedRelationsPerPrimitive.put(p, r);
    331             }
    332         }
    333         model.refresh();
    334     }
    335 
    336     /**
    337      * Prepares the default decisions for populated tag and relation membership conflicts.
    338      */
    339     public void prepareDefaultDecisions() {
    340         prepareDefaultTagDecisions();
    341         prepareDefaultRelationDecisions();
     300        getRelationMemberConflictResolverModel().prepareDefaultRelationDecisions();
    342301    }
    343302
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictDecision.java

    r8510 r8871  
    115115        return true;
    116116    }
     117
     118    @Override
     119    public String toString() {
     120        return originalPrimitive.getPrimitiveId() + " at index " + pos + " with role " + role + " in " + relation.getUniqueId() + " => " + decision;
     121    }
    117122}
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolverModel.java

    r8510 r8871  
    66import java.util.ArrayList;
    77import java.util.Collection;
     8import java.util.Collections;
    89import java.util.HashSet;
     10import java.util.Iterator;
     11import java.util.LinkedHashMap;
    912import java.util.LinkedList;
    1013import java.util.List;
     14import java.util.Map;
    1115import java.util.Set;
     16import java.util.TreeSet;
    1217
    1318import javax.swing.table.DefaultTableModel;
     
    2025import org.openstreetmap.josm.data.osm.RelationToChildReference;
    2126import org.openstreetmap.josm.gui.util.GuiHelper;
     27import org.openstreetmap.josm.tools.Predicate;
     28import org.openstreetmap.josm.tools.Utils;
    2229
    2330/**
     
    3441    /** the collection of relations for which we manage conflicts */
    3542    protected transient Collection<Relation> relations;
     43    /** the collection of primitives for which we manage conflicts */
     44    protected transient Collection<? extends OsmPrimitive> primitives;
    3645    /** the number of conflicts */
    3746    private int numConflicts;
     
    151160    public void populate(Collection<Relation> relations, Collection<? extends OsmPrimitive> memberPrimitives) {
    152161        decisions.clear();
    153         relations = relations == null ? new LinkedList<Relation>() : relations;
     162        relations = relations == null ? Collections.<Relation>emptyList() : relations;
    154163        memberPrimitives = memberPrimitives == null ? new LinkedList<OsmPrimitive>() : memberPrimitives;
    155164        for (Relation r : relations) {
     
    159168        }
    160169        this.relations = relations;
     170        this.primitives = memberPrimitives;
    161171        refresh();
    162172    }
     
    172182        decisions.clear();
    173183        this.relations = new HashSet<>(references.size());
     184        final Collection<OsmPrimitive> primitives = new HashSet<>();
    174185        for (RelationToChildReference reference: references) {
    175186            decisions.add(new RelationMemberConflictDecision(reference.getParent(), reference.getPosition()));
    176187            relations.add(reference.getParent());
    177         }
     188            primitives.add(reference.getChild());
     189        }
     190        this.primitives = primitives;
    178191        refresh();
     192    }
     193
     194    /**
     195     * Prepare the default decisions for the current model.
     196     *
     197     * Keep/delete decisions are made if every member has the same role and the members are in consecutive order within the relation.
     198     * For multiple occurrences those conditions are tested stepwise for each occurrence.
     199     */
     200    public void prepareDefaultRelationDecisions() {
     201
     202        for (final Relation relation : relations) {
     203            final Map<OsmPrimitive, List<RelationMemberConflictDecision>> decisionsByPrimitive = new LinkedHashMap<>(primitives.size(), 1);
     204            for (final RelationMemberConflictDecision decision : decisions) {
     205                if (decision.getRelation() == relation) {
     206                    final OsmPrimitive primitive = decision.getOriginalPrimitive();
     207                    if (!decisionsByPrimitive.containsKey(primitive)) {
     208                        decisionsByPrimitive.put(primitive, new ArrayList<RelationMemberConflictDecision>());
     209                    }
     210                    decisionsByPrimitive.get(primitive).add(decision);
     211                }
     212            }
     213
     214            //noinspection StatementWithEmptyBody
     215            if (!decisionsByPrimitive.keySet().containsAll(primitives)) {
     216                // some primitives are not part of the relation, leave undecided
     217            } else {
     218                final Collection<Iterator<RelationMemberConflictDecision>> iterators = new ArrayList<>(primitives.size());
     219                for (final Collection<RelationMemberConflictDecision> i : decisionsByPrimitive.values()) {
     220                    iterators.add(i.iterator());
     221                }
     222                while (Utils.forAll(iterators, new Predicate<Iterator<RelationMemberConflictDecision>>() {
     223                    @Override
     224                    public boolean evaluate(Iterator<RelationMemberConflictDecision> it) {
     225                        return it.hasNext();
     226                    }
     227                })) {
     228                    final List<RelationMemberConflictDecision> decisions = new ArrayList<>();
     229                    final Collection<String> roles = new HashSet<>();
     230                    final Collection<Integer> indices = new TreeSet<>();
     231                    for (Iterator<RelationMemberConflictDecision> it : iterators) {
     232                        final RelationMemberConflictDecision decision = it.next();
     233                        decisions.add(decision);
     234                        roles.add(decision.getRole());
     235                        indices.add(decision.getPos());
     236                    }
     237                    if (roles.size() != 1) {
     238                        // roles to not patch, leave undecided
     239                        continue;
     240                    } else if (!isCollectionOfConsecutiveNumbers(indices)) {
     241                        // not consecutive members in relation, leave undecided
     242                        continue;
     243                    }
     244                    decisions.get(0).decide(RelationMemberConflictDecisionType.KEEP);
     245                    for (RelationMemberConflictDecision decision : decisions.subList(1, decisions.size())) {
     246                        decision.decide(RelationMemberConflictDecisionType.REMOVE);
     247                    }
     248                }
     249            }
     250        }
     251
     252        refresh();
     253    }
     254
     255    static boolean isCollectionOfConsecutiveNumbers(Collection<Integer> numbers) {
     256        if (numbers.isEmpty()) {
     257            return true;
     258        }
     259        final Iterator<Integer> it = numbers.iterator();
     260        Integer previousValue = it.next();
     261        while (it.hasNext()) {
     262            final Integer i = it.next();
     263            if (previousValue + 1 != i) {
     264                return false;
     265            }
     266            previousValue = i;
     267        }
     268        return true;
    179269    }
    180270
     
    195285     */
    196286    public int getNumDecisions() {
    197         return decisions == null ? 0 : decisions.size();
     287        return decisions == null /* accessed via super constructor */ ? 0 : decisions.size();
    198288    }
    199289
Note: See TracChangeset for help on using the changeset viewer.