/**
  * 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();
    }
  }
Exemple #12
0
  /**
   * 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");
 }