// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.opendata.core.datasets; import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.applyAutomaticTagConflictResolution; import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing; import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.CombineWayAction.NodeGraph; import org.openstreetmap.josm.command.Command; import org.openstreetmap.josm.corrector.ReverseWayTagCorrector; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.TagCollection; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog; import org.openstreetmap.josm.tools.UserCancelException; // FIXME: Try to refactor CombineWayAction instead of using this class public final class WayCombiner { private WayCombiner() { // Hide default constructor for utilities classes } protected static Way getTargetWay(Collection<Way> combinedWays) { // init with an arbitrary way Way targetWay = combinedWays.iterator().next(); // look for the first way already existing on // the server for (Way w : combinedWays) { targetWay = w; if (!w.isNew()) { break; } } return targetWay; } public static void combineWays(Collection<Way> ways) throws UserCancelException { // prepare and clean the list of ways to combine // if (ways == null || ways.isEmpty()) return; ways.remove(null); // just in case - remove all null ways from the collection // remove duplicates, preserving order ways = new LinkedHashSet<>(ways); // try to build a new way which includes all the combined // ways // NodeGraph graph = NodeGraph.createUndirectedGraphFromNodeWays(ways); List<Node> path = graph.buildSpanningPath(); if (path == null) { return; } // check whether any ways have been reversed in the process // and build the collection of tags used by the ways to combine // TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways); List<Way> reversedWays = new LinkedList<>(); List<Way> unreversedWays = new LinkedList<>(); for (Way w: ways) { if ((path.indexOf(w.getNode(0)) + 1) == path.lastIndexOf(w.getNode(1))) { unreversedWays.add(w); } else { reversedWays.add(w); } } // reverse path if all ways have been reversed if (unreversedWays.isEmpty()) { Collections.reverse(path); unreversedWays = reversedWays; reversedWays = null; } if ((reversedWays != null) && !reversedWays.isEmpty()) { // filter out ways that have no direction-dependent tags unreversedWays = ReverseWayTagCorrector.irreversibleWays(unreversedWays); reversedWays = ReverseWayTagCorrector.irreversibleWays(reversedWays); // reverse path if there are more reversed than unreversed ways with direction-dependent tags if (reversedWays.size() > unreversedWays.size()) { Collections.reverse(path); List<Way> tempWays = unreversedWays; unreversedWays = reversedWays; reversedWays = tempWays; } // if there are still reversed ways with direction-dependent tags, reverse their tags if (!reversedWays.isEmpty()) { List<Way> unreversedTagWays = new ArrayList<>(ways); unreversedTagWays.removeAll(reversedWays); ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector(); List<Way> reversedTagWays = new ArrayList<>(); Collection<Command> changePropertyCommands = null; for (Way w : reversedWays) { Way wnew = new Way(w); reversedTagWays.add(wnew); changePropertyCommands = reverseWayTagCorrector.execute(w, wnew); } if ((changePropertyCommands != null) && !changePropertyCommands.isEmpty()) { for (Command c : changePropertyCommands) { c.executeCommand(); } } wayTags = TagCollection.unionOfAllPrimitives(reversedTagWays); wayTags.add(TagCollection.unionOfAllPrimitives(unreversedTagWays)); } } // create the new way and apply the new node list // Way targetWay = getTargetWay(ways); Way modifiedTargetWay = new Way(targetWay); modifiedTargetWay.setNodes(path); TagCollection completeWayTags = new TagCollection(wayTags); applyAutomaticTagConflictResolution(completeWayTags); normalizeTagCollectionBeforeEditing(completeWayTags, ways); TagCollection tagsToEdit = new TagCollection(completeWayTags); completeTagCollectionForEditing(tagsToEdit); CombinePrimitiveResolverDialog dialog = new CombinePrimitiveResolverDialog(Main.parent); dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues()); dialog.setTargetPrimitive(targetWay); Set<Relation> parentRelations = OsmPrimitive.getParentRelations(ways); dialog.getRelationMemberConflictResolverModel().populate( parentRelations, ways ); dialog.prepareDefaultDecisions(); // resolve tag conflicts if necessary // if (!completeWayTags.isApplicableToPrimitive() || !parentRelations.isEmpty()) { dialog.setVisible(true); //if (dialog.isCanceled()) // FIXME throw new UserCancelException(); } LinkedList<Way> deletedWays = new LinkedList<>(ways); deletedWays.remove(targetWay); //new ChangeCommand(targetWay, modifiedTargetWay).executeCommand(); targetWay.cloneFrom(modifiedTargetWay); /*for (Command c : dialog.buildResolutionCommands()) { c.executeCommand();//FIXME }*/ //new DeleteCommand(deletedWays).executeCommand(); for (Way way: deletedWays) { way.setNodes(null); way.setDeleted(true); way.getDataSet().removePrimitive(way); } } }