/** * Applies the current state in the model to a turn restriction * * @param turnRestriction the turn restriction. Must not be null. */ public void apply(Relation turnRestriction) { CheckParameterUtil.ensureParameterNotNull(turnRestriction, "turnRestriction"); TagCollection tags = tagEditorModel.getTagCollection(); turnRestriction.removeAll(); tags.applyTo(turnRestriction); memberModel.applyTo(turnRestriction); }
/** * Populates the conflict resolver with one tag collection * * @param tagsForAllPrimitives the tag collection * @param sourceStatistics histogram of tag source, number of primitives of each type in the * source * @param targetStatistics histogram of paste targets, number of primitives of each type in the * paste target */ public void populate( TagCollection tagsForAllPrimitives, Map<OsmPrimitiveType, Integer> sourceStatistics, Map<OsmPrimitiveType, Integer> targetStatistics) { mode = Mode.RESOLVING_ONE_TAGCOLLECTION_ONLY; tagsForAllPrimitives = tagsForAllPrimitives == null ? new TagCollection() : tagsForAllPrimitives; sourceStatistics = sourceStatistics == null ? new HashMap<>() : sourceStatistics; targetStatistics = targetStatistics == null ? new HashMap<>() : targetStatistics; // init the resolver // allPrimitivesResolver .getModel() .populate(tagsForAllPrimitives, tagsForAllPrimitives.getKeysWithMultipleValues()); allPrimitivesResolver.getModel().prepareDefaultTagDecisions(); // prepare the dialog with one tag resolver pnlTagResolver.removeAll(); pnlTagResolver.add(allPrimitivesResolver, BorderLayout.CENTER); statisticsModel.reset(); StatisticsInfo info = new StatisticsInfo(); info.numTags = tagsForAllPrimitives.getKeys().size(); info.sourceInfo.putAll(sourceStatistics); info.targetInfo.putAll(targetStatistics); statisticsModel.append(info); validate(); }
/** * Replies the list of {@link Command commands} needed to apply resolution choices. * * @return The list of {@link Command commands} needed to apply resolution choices. */ public List<Command> buildResolutionCommands() { List<Command> cmds = new LinkedList<>(); TagCollection allResolutions = getTagConflictResolverModel().getAllResolutions(); if (!allResolutions.isEmpty()) { cmds.addAll(buildTagChangeCommand(targetPrimitive, allResolutions)); } for (String p : OsmPrimitive.getDiscardableKeys()) { if (targetPrimitive.get(p) != null) { cmds.add(new ChangePropertyCommand(targetPrimitive, p, null)); } } if (getRelationMemberConflictResolverModel().getNumDecisions() > 0) { cmds.addAll( getRelationMemberConflictResolverModel().buildResolutionCommands(targetPrimitive)); } Command cmd = pnlRelationMemberConflictResolver.buildTagApplyCommands( getRelationMemberConflictResolverModel().getModifiedRelations(targetPrimitive)); if (cmd != null) { cmds.add(cmd); } return cmds; }
/** * Combines tags from TIGER data * * @param tc the tag collection */ public static void combineTigerTags(TagCollection tc) { for (String key : tc.getKeys()) { if (TigerUtils.isTigerTag(key)) { tc.setUniqueForKey(key, TigerUtils.combineTags(key, tc.getValues(key))); } } }
/** * Normalizes the tags in the tag collection <code>tc</code> before resolving tag conflicts. * * <p>Removes irrelevant tags like "created_by". * * <p>For tags which are not present on at least one of the merged nodes, the empty value "" is * added to the list of values for this tag, but only if there are at least two primitives with * tags, and at least one tagged primitive do not have this tag. * * @param tc the tag collection * @param merged the collection of merged primitives */ public static void normalizeTagCollectionBeforeEditing( TagCollection tc, Collection<? extends OsmPrimitive> merged) { // remove irrelevant tags // for (String key : OsmPrimitive.getDiscardableKeys()) { tc.removeByKey(key); } Collection<OsmPrimitive> taggedPrimitives = new ArrayList<>(); for (OsmPrimitive p : merged) { if (p.isTagged()) { taggedPrimitives.add(p); } } if (taggedPrimitives.size() <= 1) return; for (String key : tc.getKeys()) { // make sure the empty value is in the tag set if a tag is not present // on all merged nodes // for (OsmPrimitive p : taggedPrimitives) { if (p.get(key) == null) { tc.add(new Tag(key, "")); // add a tag with key and empty value } } } }
/** * Initializes the conflict resolver for a specific type of primitives * * @param type the type of primitives * @param tc the tags belonging to this type of primitives * @param targetStatistics histogram of paste targets, number of primitives of each type in the * paste target */ protected void initResolver( OsmPrimitiveType type, TagCollection tc, Map<OsmPrimitiveType, Integer> targetStatistics) { resolvers.get(type).getModel().populate(tc, tc.getKeysWithMultipleValues()); resolvers.get(type).getModel().prepareDefaultTagDecisions(); if (!tc.isEmpty() && targetStatistics.get(type) != null && targetStatistics.get(type) > 0) { tpResolvers.add(PANE_TITLES.get(type), resolvers.get(type)); } }
/** * Completes tags in the tag collection <code>tc</code> with the empty value for each tag. If the * empty value is present the tag conflict resolution dialog will offer an option for removing the * tag and not only options for selecting one of the current values of the tag. * * @param tc the tag collection */ public static void completeTagCollectionForEditing(TagCollection tc) { for (String key : tc.getKeys()) { // make sure the empty value is in the tag set such that we can delete the tag // in the conflict dialog if necessary // tc.add(new Tag(key, "")); } }
/** * Replies true if {@code tp1} and {@code tp2} have the same tags and the same members * * @param tp1 a turn restriction. Must not be null. * @param tp2 a turn restriction . Must not be null. * @return true if {@code tp1} and {@code tp2} have the same tags and the same members * @throws IllegalArgumentException thrown if {@code tp1} is null * @throws IllegalArgumentException thrown if {@code tp2} is null */ public static boolean hasSameMembersAndTags(Relation tp1, Relation tp2) throws IllegalArgumentException { CheckParameterUtil.ensureParameterNotNull(tp1, "tp1"); CheckParameterUtil.ensureParameterNotNull(tp2, "tp2"); if (!TagCollection.from(tp1).asSet().equals(TagCollection.from(tp2).asSet())) return false; if (tp1.getMembersCount() != tp2.getMembersCount()) return false; for (int i = 0; i < tp1.getMembersCount(); i++) { if (!tp1.getMember(i).equals(tp2.getMember(i))) return false; } return true; }
/** * Initializes the model from a relation representing a turn restriction * * @param turnRestriction the turn restriction */ protected void initFromTurnRestriction(Relation turnRestriction) { // populate the member model memberModel.populate(turnRestriction); // make sure we have a restriction tag TagCollection tags = TagCollection.from(turnRestriction); tags.setUniqueForKey("type", "restriction"); tagEditorModel.initFromTags(tags); setChanged(); notifyObservers(); }
protected List<Command> buildTagChangeCommand(OsmPrimitive primitive, TagCollection tc) { List<Command> cmds = new LinkedList<>(); for (String key : tc.getKeys()) { if (tc.hasUniqueEmptyValue(key)) { if (primitive.get(key) != null) { cmds.add(new ChangePropertyCommand(primitive, key, null)); } } else { String value = tc.getJoinedValues(key); if (!value.equals(primitive.get(key))) { cmds.add(new ChangePropertyCommand(primitive, key, value)); } } } return cmds; }
/** * Inform a non-expert user about what tag conflict resolution means. * * @param primitives The primitives to be combined * @param normalizedTags The normalized tag collection of the primitives to be combined * @throws UserCancelException If the user cancels the dialog. */ protected static void informAboutTagConflicts( final Collection<? extends OsmPrimitive> primitives, final TagCollection normalizedTags) throws UserCancelException { String conflicts = Utils.joinAsHtmlUnorderedList( Utils.transform( normalizedTags.getKeysWithMultipleValues(), new Function<String, String>() { @Override public String apply(String key) { return tr( "{0} ({1})", key, Utils.join( tr(", "), Utils.transform( normalizedTags.getValues(key), new Function<String, String>() { @Override public String apply(String x) { return x == null || x.isEmpty() ? tr("<i>missing</i>") : x; } }))); } })); String msg = /* for correct i18n of plural forms - see #9110 */ trn( "You are about to combine {0} objects, " + "but the following tags are used conflictingly:<br/>{1}" + "If these objects are combined, the resulting object may have unwanted tags.<br/>" + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>" + "Do you want to continue?", "You are about to combine {0} objects, " + "but the following tags are used conflictingly:<br/>{1}" + "If these objects are combined, the resulting object may have unwanted tags.<br/>" + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>" + "Do you want to continue?", primitives.size(), primitives.size(), conflicts); if (!ConditionalOptionPaneUtil.showConfirmationDialog( "combine_tags", Main.parent, "<html>" + msg + "</html>", tr("Combine confirmation"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_OPTION)) { throw new UserCancelException(); } }
/** * Checks if tags of two given ways differ, and presents the user a dialog to solve conflicts * * @param polygons ways to check * @return {@code true} if all conflicts are resolved, {@code false} if conflicts remain. */ private boolean resolveTagConflicts(List<Multipolygon> polygons) { List<Way> ways = new ArrayList<Way>(); for (Multipolygon pol : polygons) { ways.add(pol.outerWay); ways.addAll(pol.innerWays); } if (ways.size() < 2) { return true; } TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways); try { cmds.addAll(CombinePrimitiveResolverDialog.launchIfNecessary(wayTags, ways, ways)); commitCommands(marktr("Fix tag conflicts")); return true; } catch (UserCancelException ex) { return false; } }
/** * Populate the tag conflict resolver with tags for each type of primitives * * @param tagsForNodes the tags belonging to nodes in the paste source * @param tagsForWays the tags belonging to way in the paste source * @param tagsForRelations the tags belonging to relations in the paste source * @param sourceStatistics histogram of tag source, number of primitives of each type in the * source * @param targetStatistics histogram of paste targets, number of primitives of each type in the * paste target */ public void populate( TagCollection tagsForNodes, TagCollection tagsForWays, TagCollection tagsForRelations, Map<OsmPrimitiveType, Integer> sourceStatistics, Map<OsmPrimitiveType, Integer> targetStatistics) { tagsForNodes = (tagsForNodes == null) ? new TagCollection() : tagsForNodes; tagsForWays = (tagsForWays == null) ? new TagCollection() : tagsForWays; tagsForRelations = (tagsForRelations == null) ? new TagCollection() : tagsForRelations; if (tagsForNodes.isEmpty() && tagsForWays.isEmpty() && tagsForRelations.isEmpty()) { populate(null, null, null); return; } tpResolvers.removeAll(); initResolver(OsmPrimitiveType.NODE, tagsForNodes, targetStatistics); initResolver(OsmPrimitiveType.WAY, tagsForWays, targetStatistics); initResolver(OsmPrimitiveType.RELATION, tagsForRelations, targetStatistics); pnlTagResolver.removeAll(); pnlTagResolver.add(tpResolvers, BorderLayout.CENTER); mode = Mode.RESOLVING_TYPED_TAGCOLLECTIONS; validate(); statisticsModel.reset(); if (!tagsForNodes.isEmpty()) { StatisticsInfo info = new StatisticsInfo(); info.numTags = tagsForNodes.getKeys().size(); int numTargets = targetStatistics.get(OsmPrimitiveType.NODE) == null ? 0 : targetStatistics.get(OsmPrimitiveType.NODE); if (numTargets > 0) { info.sourceInfo.put(OsmPrimitiveType.NODE, sourceStatistics.get(OsmPrimitiveType.NODE)); info.targetInfo.put(OsmPrimitiveType.NODE, numTargets); statisticsModel.append(info); } } if (!tagsForWays.isEmpty()) { StatisticsInfo info = new StatisticsInfo(); info.numTags = tagsForWays.getKeys().size(); int numTargets = targetStatistics.get(OsmPrimitiveType.WAY) == null ? 0 : targetStatistics.get(OsmPrimitiveType.WAY); if (numTargets > 0) { info.sourceInfo.put(OsmPrimitiveType.WAY, sourceStatistics.get(OsmPrimitiveType.WAY)); info.targetInfo.put(OsmPrimitiveType.WAY, numTargets); statisticsModel.append(info); } } if (!tagsForRelations.isEmpty()) { StatisticsInfo info = new StatisticsInfo(); info.numTags = tagsForRelations.getKeys().size(); int numTargets = targetStatistics.get(OsmPrimitiveType.RELATION) == null ? 0 : targetStatistics.get(OsmPrimitiveType.RELATION); if (numTargets > 0) { info.sourceInfo.put( OsmPrimitiveType.RELATION, sourceStatistics.get(OsmPrimitiveType.RELATION)); info.targetInfo.put(OsmPrimitiveType.RELATION, numTargets); statisticsModel.append(info); } } for (int i = 0; i < getNumResolverTabs(); i++) { if (!getResolver(i).getModel().isResolvedCompletely()) { tpResolvers.setSelectedIndex(i); break; } } }
/** * Replies the list of {@link Command commands} needed to resolve specified conflicts, by * displaying if necessary a {@link CombinePrimitiveResolverDialog} to the user. This dialog will * allow the user to choose conflict resolution actions. * * <p>Non-expert users are informed first of the meaning of these operations, allowing them to * cancel. * * @param tagsOfPrimitives The tag collection of the primitives to be combined. Should generally * be equal to {@code TagCollection.unionOfAllPrimitives(primitives)} * @param primitives The primitives to be combined * @param targetPrimitives The primitives the collection of primitives are merged or combined to. * @return The list of {@link Command commands} needed to apply resolution actions. * @throws UserCancelException If the user cancelled a dialog. */ public static List<Command> launchIfNecessary( final TagCollection tagsOfPrimitives, final Collection<? extends OsmPrimitive> primitives, final Collection<? extends OsmPrimitive> targetPrimitives) throws UserCancelException { CheckParameterUtil.ensureParameterNotNull(tagsOfPrimitives, "tagsOfPrimitives"); CheckParameterUtil.ensureParameterNotNull(primitives, "primitives"); CheckParameterUtil.ensureParameterNotNull(targetPrimitives, "targetPrimitives"); final TagCollection completeWayTags = new TagCollection(tagsOfPrimitives); TagConflictResolutionUtil.combineTigerTags(completeWayTags); TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, primitives); final TagCollection tagsToEdit = new TagCollection(completeWayTags); TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit); final Set<Relation> parentRelations = OsmPrimitive.getParentRelations(primitives); // Show information dialogs about conflicts to non-experts if (!ExpertToggleAction.isExpert()) { // Tag conflicts if (!completeWayTags.isApplicableToPrimitive()) { informAboutTagConflicts(primitives, completeWayTags); } // Relation membership conflicts if (!parentRelations.isEmpty()) { informAboutRelationMembershipConflicts(primitives, parentRelations); } } List<Command> cmds = new LinkedList<>(); if (!GraphicsEnvironment.isHeadless()) { // Build conflict resolution dialog final CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance(); dialog .getTagConflictResolverModel() .populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues()); dialog.getRelationMemberConflictResolverModel().populate(parentRelations, primitives); dialog.prepareDefaultDecisions(); // Ensure a proper title is displayed instead of a previous target (fix #7925) if (targetPrimitives.size() == 1) { dialog.setTargetPrimitive(targetPrimitives.iterator().next()); } else { dialog.setTargetPrimitive(null); } // Resolve tag conflicts if necessary if (!dialog.isResolvedCompletely()) { dialog.setVisible(true); if (!dialog.isApplied()) { throw new UserCancelException(); } } for (OsmPrimitive i : targetPrimitives) { dialog.setTargetPrimitive(i); cmds.addAll(dialog.buildResolutionCommands()); } } return cmds; }
/** * Replies the current tag value for the tag <tt>restriction</tt>. The empty tag, if there isn't a * tag <tt>restriction</tt>. * * @return the tag value */ public String getRestrictionTagValue() { TagCollection tags = tagEditorModel.getTagCollection(); if (!tags.hasTagsFor("restriction")) return ""; return tags.getJoinedValues("restriction"); }