From 4e8d4f929ff81d0692a455d2ceef2dfebf45dd5f Mon Sep 17 00:00:00 2001 From: Taylor Smock Date: Thu, 17 Oct 2019 16:42:22 -0600 Subject: [PATCH] Move dupe/conn commands out to make it easier to add new commands Signed-off-by: Taylor Smock --- .../conflation/AbstractConflationCommand.java | 134 ++++++++++ .../commands/conflation/ConnectedCommand.java | 99 ++++++++ .../commands/conflation/DuplicateCommand.java | 99 ++++++++ .../commands/CreateConnectionsCommand.java | 229 ++++-------------- .../backend/MapWithAIMoveActionTest.java | 27 ++- .../CreateConnectionsCommandTest.java | 35 +-- 6 files changed, 411 insertions(+), 212 deletions(-) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/AbstractConflationCommand.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/ConnectedCommand.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DuplicateCommand.java diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/AbstractConflationCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/AbstractConflationCommand.java new file mode 100644 index 0000000..5cabb30 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/AbstractConflationCommand.java @@ -0,0 +1,134 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import org.openstreetmap.josm.command.Command; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.data.osm.OsmPrimitive; +import org.openstreetmap.josm.data.osm.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.PrimitiveId; +import org.openstreetmap.josm.data.osm.SimplePrimitiveId; +import org.openstreetmap.josm.gui.MainApplication; +import org.openstreetmap.josm.gui.io.DownloadPrimitivesTask; +import org.openstreetmap.josm.gui.layer.OsmDataLayer; +import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; +import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; +import org.openstreetmap.josm.tools.Pair; + +public abstract class AbstractConflationCommand extends Command { + Collection possiblyAffectedPrimitives; + public AbstractConflationCommand(DataSet data) { + super(data); + } + + @Override + public void fillModifiedData(Collection modified, Collection deleted, + Collection added) { + // Do nothing -- the sequence commands should take care of it. + } + + /** + * @return The types of primitive that the command is interested in + */ + public abstract Collection> getInterestedTypes(); + + /** + * @return The key that the command is interested in + */ + public abstract String getKey(); + + /** + * @param primitives The primitives to run the command on + * @return The command that will be run + */ + public Command getCommand(List primitives) { + possiblyAffectedPrimitives = primitives.stream().distinct().collect(Collectors.toList()); + return getRealCommand(); + } + + /** + * @return The command to do whatever is required for the result + */ + public abstract Command getRealCommand(); + + /** + * Get the primitives from a dataset with specified ids + * + * @param dataSet The dataset holding the primitives (hopefully) + * @param ids The ids formated like + * n<NUMBER>,r<NUMBER>,w<NUMBER> + * @return The primitives that the ids point to, if in the dataset. + */ + public static OsmPrimitive[] getPrimitives(DataSet dataSet, String ids) { + final Map> missingPrimitives = new TreeMap<>(); + final String[] connections = ids.split(",", -1); + final OsmPrimitive[] primitiveConnections = new OsmPrimitive[connections.length]; + for (int i = 0; i < connections.length; i++) { + final String member = connections[i]; + final long id = Long.parseLong(member.substring(1)); + final char firstChar = member.charAt(0); + OsmPrimitiveType type = null; + if (firstChar == 'w') { + type = OsmPrimitiveType.WAY; + } else if (firstChar == 'n') { + type = OsmPrimitiveType.NODE; + } else if (firstChar == 'r') { + type = OsmPrimitiveType.RELATION; + } else { + throw new IllegalArgumentException( + tr("{0}: We don't know how to handle {1} types", MapWithAIPlugin.NAME, firstChar)); + } + primitiveConnections[i] = dataSet.getPrimitiveById(id, type); + if (primitiveConnections[i] == null) { + missingPrimitives.put(i, new Pair<>(id, type)); + } + } + obtainMissingPrimitives(dataSet, primitiveConnections, missingPrimitives); + return primitiveConnections; + } + + private static void obtainMissingPrimitives(DataSet dataSet, OsmPrimitive[] primitiveConnections, + Map> missingPrimitives) { + if (!missingPrimitives.isEmpty()) { + final Map ids = missingPrimitives.entrySet().stream().collect(Collectors + .toMap(entry -> new SimplePrimitiveId(entry.getValue().a, entry.getValue().b), Entry::getKey)); + final List toFetch = new ArrayList<>(ids.keySet()); + final Optional optionalLayer = MainApplication.getLayerManager() + .getLayersOfType(OsmDataLayer.class).parallelStream() + .filter(layer -> layer.getDataSet().equals(dataSet)).findFirst(); + + OsmDataLayer layer; + final String generatedLayerName = "EvKlVarShAiAllsM generated layer"; + if (optionalLayer.isPresent()) { + layer = optionalLayer.get(); + } else { + layer = new OsmDataLayer(dataSet, generatedLayerName, null); + } + + final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor( + tr("Downloading additional OsmPrimitives")); + final DownloadPrimitivesTask downloadPrimitivesTask = new DownloadPrimitivesTask(layer, toFetch, true, + monitor); + downloadPrimitivesTask.run(); + for (final Entry entry : ids.entrySet()) { + final int index = entry.getValue().intValue(); + final OsmPrimitive primitive = dataSet.getPrimitiveById(entry.getKey()); + primitiveConnections[index] = primitive; + } + + if (generatedLayerName.equals(layer.getName())) { + layer.destroy(); + } + } + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/ConnectedCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/ConnectedCommand.java new file mode 100644 index 0000000..28566c8 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/ConnectedCommand.java @@ -0,0 +1,99 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.openstreetmap.josm.command.ChangePropertyCommand; +import org.openstreetmap.josm.command.Command; +import org.openstreetmap.josm.command.SequenceCommand; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitive; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.mapwithai.commands.AddNodeToWayCommand; +import org.openstreetmap.josm.tools.Geometry; +import org.openstreetmap.josm.tools.Logging; + +public class ConnectedCommand extends AbstractConflationCommand { + public static final String CONN_KEY = "conn"; + + public ConnectedCommand(DataSet data) { + super(data); + } + + @Override + public String getDescriptionText() { + return tr("Connect nodes to ways"); + } + + /** + * Add a node to a way + * + * @param toAddNode The node to add + * @param way The way to add the node to + * @param first The first node in a waysegment (the node is between this and + * the second node) + * @param second The second node in a waysegment + * @return Command to add a node to a way, or null if it won't be done + */ + public static Command addNodesToWay(Node toAddNode, Way way, Node first, Node second) { + Command tCommand = null; + final Way tWay = new Way(); + tWay.addNode(first); + tWay.addNode(second); + final double distance = Geometry.getDistanceWayNode(tWay, toAddNode); + if (distance < 5) { + tCommand = new AddNodeToWayCommand(toAddNode, way, first, second); + } + return tCommand; + } + + private static List connectedCommand(DataSet dataSet, Node node) { + final List commands = new ArrayList<>(); + final OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(CONN_KEY)); + for (int i = 0; i < primitiveConnections.length / 3; i++) { + if (primitiveConnections[i] instanceof Way && primitiveConnections[i + 1] instanceof Node + && primitiveConnections[i + 2] instanceof Node) { + final Command addNodesToWayCommand = addNodesToWay(node, (Way) primitiveConnections[i], + (Node) primitiveConnections[i + 1], (Node) primitiveConnections[i + 2]); + if (addNodesToWayCommand != null) { + commands.add(addNodesToWayCommand); + } + } else { + Logging.error("{0}: {1}, {2}: {3}, {4}: {5}", i, primitiveConnections[i].getClass(), i + 1, + primitiveConnections[i + 1].getClass(), i + 2, primitiveConnections[i + 2].getClass()); + } + } + commands.add(new ChangePropertyCommand(node, CONN_KEY, null)); + return commands; + } + + @Override + public Collection> getInterestedTypes() { + return Arrays.asList(Node.class); + } + + @Override + public String getKey() { + return CONN_KEY; + } + + @Override + public Command getRealCommand() { + List commands = new ArrayList<>(); + possiblyAffectedPrimitives.stream().filter(Node.class::isInstance).map(Node.class::cast) + .forEach(node -> commands.addAll(connectedCommand(getAffectedDataSet(), node))); + Command returnCommand; + if (!commands.isEmpty()) { + returnCommand = new SequenceCommand(getDescriptionText(), commands); + } else { + returnCommand = null; + } + return returnCommand; + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DuplicateCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DuplicateCommand.java new file mode 100644 index 0000000..5d61104 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DuplicateCommand.java @@ -0,0 +1,99 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.openstreetmap.josm.actions.MergeNodesAction; +import org.openstreetmap.josm.command.ChangePropertyCommand; +import org.openstreetmap.josm.command.Command; +import org.openstreetmap.josm.command.SequenceCommand; +import org.openstreetmap.josm.data.osm.DataSet; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitive; +import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; +import org.openstreetmap.josm.tools.Logging; + +public class DuplicateCommand extends AbstractConflationCommand { + public static final String DUPE_KEY = "dupe"; + + public DuplicateCommand(DataSet data) { + super(data); + } + + private static List duplicateNode(DataSet dataSet, Node node) { + final OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(DUPE_KEY)); + if (primitiveConnections.length != 1) { + Logging.error("{0}: {3} connection connected to more than one node? ({3}={1})", MapWithAIPlugin.NAME, + node.get(DUPE_KEY), DUPE_KEY); + } + + final List commands = new ArrayList<>(); + if (primitiveConnections[0] instanceof Node) { + final Node replaceNode = (Node) primitiveConnections[0]; + final Command tCommand = replaceNode(node, replaceNode); + if (tCommand != null) { + commands.add(tCommand); + if (replaceNode.hasKey(DUPE_KEY)) { + final String key = replaceNode.get(DUPE_KEY); + commands.add(new ChangePropertyCommand(replaceNode, DUPE_KEY, key)); + } else { + replaceNode.put(DUPE_KEY, "empty_value"); // This is needed to actually have a command. + commands.add(new ChangePropertyCommand(replaceNode, DUPE_KEY, null)); + replaceNode.remove(DUPE_KEY); + } + } + } + return commands; + } + + /** + * Replace nodes that are in the same location + * + * @param original The original node (the one to replace) + * @param newNode The node that is replacing the original node + * @return A command that replaces the node, or null if they are not at the same + * location. + */ + public static Command replaceNode(Node original, Node newNode) { + Command tCommand = null; + if (original.getCoor().equalsEpsilon(newNode.getCoor())) { + tCommand = MergeNodesAction.mergeNodes(Arrays.asList(original), newNode, newNode); + } + return tCommand; + } + + @Override + public String getDescriptionText() { + return tr("Remove duplicated nodes"); + } + + @Override + public Collection> getInterestedTypes() { + return Arrays.asList(Node.class); + } + + @Override + public String getKey() { + return DUPE_KEY; + } + + @Override + public Command getRealCommand() { + List commands = new ArrayList<>(); + possiblyAffectedPrimitives.stream().filter(Node.class::isInstance).map(Node.class::cast) + .filter(node -> node.hasKey(DUPE_KEY)) + .forEach(node -> commands.addAll(duplicateNode(getAffectedDataSet(), node))); + Command returnCommand; + if (!commands.isEmpty()) { + returnCommand = new SequenceCommand(getDescriptionText(), commands); + } else { + returnCommand = null; + } + return returnCommand; + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommand.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommand.java index e8740c7..66f4a67 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommand.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommand.java @@ -3,43 +3,34 @@ package org.openstreetmap.josm.plugins.mapwithai.commands; import static org.openstreetmap.josm.tools.I18n.tr; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; -import java.util.Optional; -import java.util.TreeMap; +import java.util.TreeSet; import java.util.stream.Collectors; -import org.openstreetmap.josm.actions.MergeNodesAction; -import org.openstreetmap.josm.command.ChangePropertyCommand; import org.openstreetmap.josm.command.Command; import org.openstreetmap.josm.command.SequenceCommand; import org.openstreetmap.josm.data.osm.DataSet; -import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; -import org.openstreetmap.josm.data.osm.OsmPrimitiveType; -import org.openstreetmap.josm.data.osm.PrimitiveId; -import org.openstreetmap.josm.data.osm.SimplePrimitiveId; -import org.openstreetmap.josm.data.osm.Way; -import org.openstreetmap.josm.gui.MainApplication; -import org.openstreetmap.josm.gui.io.DownloadPrimitivesTask; -import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; -import org.openstreetmap.josm.tools.Geometry; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.AbstractConflationCommand; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.ConnectedCommand; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.DuplicateCommand; import org.openstreetmap.josm.tools.Logging; -import org.openstreetmap.josm.tools.Pair; import org.openstreetmap.josm.tools.Utils; public class CreateConnectionsCommand extends Command { private final Collection primitives; - public static final String DUPE_KEY = "dupe"; - public static final String CONN_KEY = "conn"; private Command command = null; + private static final List> conflationCommands = new ArrayList<>(); + static { + conflationCommands.add(ConnectedCommand.class); + conflationCommands.add(DuplicateCommand.class); + } public CreateConnectionsCommand(DataSet data, Collection primitives) { super(data); @@ -48,7 +39,9 @@ public class CreateConnectionsCommand extends Command { @Override public boolean executeCommand() { - command = createConnections(getAffectedDataSet(), primitives); + if (command == null) { + command = createConnections(getAffectedDataSet(), primitives); + } if (command != null) { command.executeCommand(); } @@ -72,17 +65,27 @@ public class CreateConnectionsCommand extends Command { * @return A {@link SequenceCommand} to create connections with */ public static SequenceCommand createConnections(DataSet dataSet, Collection collection) { - final Collection nodes = Utils.filteredCollection(collection, Node.class).stream() - .map(dataSet::getPrimitiveById).map(Node.class::cast).filter(Objects::nonNull) - .collect(Collectors.toList()); final List changedKeyList = new ArrayList<>(); SequenceCommand returnSequence = null; - for (final Node node : nodes) { - if (node.hasKey(CONN_KEY)) { - changedKeyList.addAll(connectedCommand(dataSet, node)); + final Collection realPrimitives = collection.stream().map(dataSet::getPrimitiveById) + .filter(Objects::nonNull).collect(Collectors.toList()); + for (Class abstractCommandClass : getConflationCommands()) { + AbstractConflationCommand abstractCommand; + try { + abstractCommand = abstractCommandClass.getConstructor(DataSet.class).newInstance(dataSet); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException e) { + Logging.debug(e); + continue; } - if (node.hasKey(DUPE_KEY)) { - changedKeyList.addAll(duplicateNode(dataSet, node)); + Collection tPrimitives = new TreeSet<>(); + abstractCommand.getInterestedTypes() + .forEach(clazz -> tPrimitives.addAll(Utils.filteredCollection(realPrimitives, clazz))); + + Command actualCommand = abstractCommand.getCommand(tPrimitives.stream() + .filter(prim -> prim.hasKey(abstractCommand.getKey())).collect(Collectors.toList())); + if (Objects.nonNull(actualCommand)) { + changedKeyList.add(actualCommand); } } if (!changedKeyList.isEmpty()) { @@ -91,160 +94,6 @@ public class CreateConnectionsCommand extends Command { return returnSequence; } - private static List connectedCommand(DataSet dataSet, Node node) { - final List commands = new ArrayList<>(); - final OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(CONN_KEY)); - for (int i = 0; i < primitiveConnections.length / 3; i++) { - if (primitiveConnections[i] instanceof Way && primitiveConnections[i + 1] instanceof Node - && primitiveConnections[i + 2] instanceof Node) { - final Command addNodesToWayCommand = addNodesToWay(node, (Way) primitiveConnections[i], - (Node) primitiveConnections[i + 1], (Node) primitiveConnections[i + 2]); - if (addNodesToWayCommand != null) { - commands.add(addNodesToWayCommand); - } - } else { - Logging.error("{0}: {1}, {2}: {3}, {4}: {5}", i, primitiveConnections[i].getClass(), i + 1, - primitiveConnections[i + 1].getClass(), i + 2, primitiveConnections[i + 2].getClass()); - } - } - commands.add(new ChangePropertyCommand(node, CONN_KEY, null)); - return commands; - } - - private static List duplicateNode(DataSet dataSet, Node node) { - final OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(DUPE_KEY)); - if (primitiveConnections.length != 1) { - Logging.error("{0}: {3} connection connected to more than one node? ({3}={1})", MapWithAIPlugin.NAME, - node.get(DUPE_KEY), DUPE_KEY); - } - - final List commands = new ArrayList<>(); - if (primitiveConnections[0] instanceof Node) { - final Node replaceNode = (Node) primitiveConnections[0]; - final Command tCommand = replaceNode(node, replaceNode); - if (tCommand != null) { - commands.add(tCommand); - if (replaceNode.hasKey(DUPE_KEY)) { - final String key = replaceNode.get(DUPE_KEY); - commands.add(new ChangePropertyCommand(replaceNode, DUPE_KEY, key)); - } else { - replaceNode.put(DUPE_KEY, "empty_value"); // This is needed to actually have a command. - commands.add(new ChangePropertyCommand(replaceNode, DUPE_KEY, null)); - replaceNode.remove(DUPE_KEY); - } - } - } - return commands; - } - - /** - * Get the primitives from a dataset with specified ids - * - * @param dataSet The dataset holding the primitives (hopefully) - * @param ids The ids formated like - * n<NUMBER>,r<NUMBER>,w<NUMBER> - * @return The primitives that the ids point to, if in the dataset. - */ - private static OsmPrimitive[] getPrimitives(DataSet dataSet, String ids) { - final Map> missingPrimitives = new TreeMap<>(); - final String[] connections = ids.split(",", -1); - final OsmPrimitive[] primitiveConnections = new OsmPrimitive[connections.length]; - for (int i = 0; i < connections.length; i++) { - final String member = connections[i]; - final long id = Long.parseLong(member.substring(1)); - final char firstChar = member.charAt(0); - OsmPrimitiveType type = null; - if (firstChar == 'w') { - type = OsmPrimitiveType.WAY; - } else if (firstChar == 'n') { - type = OsmPrimitiveType.NODE; - } else if (firstChar == 'r') { - type = OsmPrimitiveType.RELATION; - } else { - throw new IllegalArgumentException( - tr("{0}: We don't know how to handle {1} types", MapWithAIPlugin.NAME, firstChar)); - } - primitiveConnections[i] = dataSet.getPrimitiveById(id, type); - if (primitiveConnections[i] == null) { - missingPrimitives.put(i, new Pair<>(id, type)); - } - } - obtainMissingPrimitives(dataSet, primitiveConnections, missingPrimitives); - return primitiveConnections; - } - - private static void obtainMissingPrimitives(DataSet dataSet, OsmPrimitive[] primitiveConnections, - Map> missingPrimitives) { - if (!missingPrimitives.isEmpty()) { - final Map ids = missingPrimitives.entrySet().stream().collect(Collectors - .toMap(entry -> new SimplePrimitiveId(entry.getValue().a, entry.getValue().b), Entry::getKey)); - final List toFetch = new ArrayList<>(ids.keySet()); - final Optional optionalLayer = MainApplication.getLayerManager() - .getLayersOfType(OsmDataLayer.class).parallelStream() - .filter(layer -> layer.getDataSet().equals(dataSet)).findFirst(); - - OsmDataLayer layer; - final String generatedLayerName = "EvKlVarShAiAllsM generated layer"; - if (optionalLayer.isPresent()) { - layer = optionalLayer.get(); - } else { - layer = new OsmDataLayer(dataSet, generatedLayerName, null); - } - - final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor( - tr("Downloading additional OsmPrimitives")); - final DownloadPrimitivesTask downloadPrimitivesTask = new DownloadPrimitivesTask(layer, toFetch, true, monitor); - downloadPrimitivesTask.run(); - for (final Entry entry : ids.entrySet()) { - final int index = entry.getValue().intValue(); - final OsmPrimitive primitive = dataSet.getPrimitiveById(entry.getKey()); - primitiveConnections[index] = primitive; - } - - if (generatedLayerName.equals(layer.getName())) { - layer.destroy(); - } - } - } - - /** - * Add a node to a way - * - * @param toAddNode The node to add - * @param way The way to add the node to - * @param first The first node in a waysegment (the node is between this and - * the second node) - * @param second The second node in a waysegment - * @return Command to add a node to a way, or null if it won't be done - */ - public static Command addNodesToWay(Node toAddNode, Way way, Node first, Node second) { - Command tCommand = null; - final Way tWay = new Way(); - tWay.addNode(first); - tWay.addNode(second); - final double distance = Geometry.getDistanceWayNode(tWay, toAddNode); - if (distance < 5) { - tCommand = new AddNodeToWayCommand(toAddNode, way, first, second); - } - return tCommand; - } - - /** - * Replace nodes that are in the same location - * - * @param original The original node (the one to replace) - * @param newNode The node that is replacing the original node - * @return A command that replaces the node, or null if they are not at the same - * location. - */ - public static Command replaceNode(Node original, Node newNode) { - Command tCommand = null; - if (original.getCoor().equalsEpsilon(newNode.getCoor())) { - tCommand = MergeNodesAction.mergeNodes(Arrays.asList(original), newNode, newNode); - } - return tCommand; - } - @Override public String getDescriptionText() { return getRealDescriptionText(); @@ -259,4 +108,18 @@ public class CreateConnectionsCommand extends Command { Collection added) { command.fillModifiedData(modified, deleted, added); } + + /** + * @param command A command to run when copying data from the MapWithAI layer + */ + public static void addConflationCommand(Class command) { + conflationCommands.add(command); + } + + /** + * @return A set of commands to run when copying data from the MapWithAI layer + */ + public static List> getConflationCommands() { + return Collections.unmodifiableList(conflationCommands); + } } diff --git a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIMoveActionTest.java b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIMoveActionTest.java index 2450afc..eb7bca6 100644 --- a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIMoveActionTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIMoveActionTest.java @@ -13,7 +13,8 @@ import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.MainApplication; import org.openstreetmap.josm.gui.layer.OsmDataLayer; -import org.openstreetmap.josm.plugins.mapwithai.commands.CreateConnectionsCommand; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.ConnectedCommand; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.DuplicateCommand; import org.openstreetmap.josm.testutils.JOSMTestRules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -64,37 +65,39 @@ public class MapWithAIMoveActionTest { @Test public void testConflationDupeKeyRemoval() { mapWithAIData.unlock(); - way1.lastNode().put(CreateConnectionsCommand.DUPE_KEY, "n" + Long.toString(way2.lastNode().getUniqueId())); + way1.lastNode().put(DuplicateCommand.DUPE_KEY, "n" + Long.toString(way2.lastNode().getUniqueId())); mapWithAIData.lock(); mapWithAIData.addSelected(way1); final DataSet ds = osmLayer.getDataSet(); moveAction.actionPerformed(null); - Assert.assertFalse(((Way) ds.getPrimitiveById(way2)).lastNode().hasKey(CreateConnectionsCommand.DUPE_KEY)); - Assert.assertFalse(((Way) ds.getPrimitiveById(way1)).lastNode().hasKey(CreateConnectionsCommand.DUPE_KEY)); + Assert.assertTrue( + ((Way) ds.getPrimitiveById(way1)).lastNode().equals(((Way) ds.getPrimitiveById(way2)).lastNode())); + Assert.assertFalse(((Way) ds.getPrimitiveById(way2)).lastNode().hasKey(DuplicateCommand.DUPE_KEY)); + Assert.assertFalse(((Way) ds.getPrimitiveById(way1)).lastNode().hasKey(DuplicateCommand.DUPE_KEY)); UndoRedoHandler.getInstance().undo(); - Assert.assertFalse(way2.lastNode().hasKey(CreateConnectionsCommand.DUPE_KEY)); - Assert.assertTrue(way1.lastNode().hasKey(CreateConnectionsCommand.DUPE_KEY)); + Assert.assertFalse(way2.lastNode().hasKey(DuplicateCommand.DUPE_KEY)); + Assert.assertTrue(way1.lastNode().hasKey(DuplicateCommand.DUPE_KEY)); } @Test public void testConflationConnKeyRemoval() { mapWithAIData.unlock(); - way1.lastNode().put(CreateConnectionsCommand.CONN_KEY, "w" + Long.toString(way2.getUniqueId()) + ",n" + way1.lastNode().put(ConnectedCommand.CONN_KEY, "w" + Long.toString(way2.getUniqueId()) + ",n" + Long.toString(way2.lastNode().getUniqueId()) + ",n" + Long.toString(way2.firstNode().getUniqueId())); mapWithAIData.lock(); mapWithAIData.addSelected(way1); moveAction.actionPerformed(null); - Assert.assertFalse(way2.lastNode().hasKey(CreateConnectionsCommand.CONN_KEY)); - Assert.assertFalse(way2.firstNode().hasKey(CreateConnectionsCommand.CONN_KEY)); - Assert.assertFalse(way2.getNode(1).hasKey(CreateConnectionsCommand.CONN_KEY)); + Assert.assertFalse(way2.lastNode().hasKey(ConnectedCommand.CONN_KEY)); + Assert.assertFalse(way2.firstNode().hasKey(ConnectedCommand.CONN_KEY)); + Assert.assertFalse(way2.getNode(1).hasKey(ConnectedCommand.CONN_KEY)); Assert.assertTrue(way1.lastNode().isDeleted()); UndoRedoHandler.getInstance().undo(); - Assert.assertFalse(way2.lastNode().hasKey(CreateConnectionsCommand.CONN_KEY)); - Assert.assertTrue(way1.lastNode().hasKey(CreateConnectionsCommand.CONN_KEY)); + Assert.assertFalse(way2.lastNode().hasKey(ConnectedCommand.CONN_KEY)); + Assert.assertTrue(way1.lastNode().hasKey(ConnectedCommand.CONN_KEY)); Assert.assertFalse(way1.lastNode().isDeleted()); } } diff --git a/test/unit/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommandTest.java b/test/unit/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommandTest.java index f9cae08..32876d7 100644 --- a/test/unit/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommandTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/mapwithai/commands/CreateConnectionsCommandTest.java @@ -21,6 +21,8 @@ import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.MainApplication; import org.openstreetmap.josm.gui.layer.OsmDataLayer; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.ConnectedCommand; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.DuplicateCommand; import org.openstreetmap.josm.spi.preferences.Config; import org.openstreetmap.josm.testutils.JOSMTestRules; @@ -70,13 +72,13 @@ public class CreateConnectionsCommandTest { createConnections.undoCommand(); Assert.assertFalse(dataSet.isModified()); - node3.put(CreateConnectionsCommand.CONN_KEY, + node3.put(ConnectedCommand.CONN_KEY, "w" + way.getUniqueId() + ",n" + node1.getUniqueId() + ",n" + node2.getUniqueId()); createConnections = new CreateConnectionsCommand(dataSet, Collections.singleton(node3)); createConnections.executeCommand(); Assert.assertTrue(dataSet.isModified()); Assert.assertEquals(3, way.getNodesCount()); - Assert.assertFalse(node3.hasKey(CreateConnectionsCommand.CONN_KEY)); + Assert.assertFalse(node3.hasKey(ConnectedCommand.CONN_KEY)); createConnections.fillModifiedData(modified, deleted, added); Assert.assertEquals(3, modified.size()); // 3 since we remove the key from the node Assert.assertTrue(deleted.isEmpty()); @@ -84,22 +86,22 @@ public class CreateConnectionsCommandTest { createConnections.undoCommand(); Assert.assertFalse(dataSet.isModified()); Assert.assertEquals(2, way.getNodesCount()); - Assert.assertTrue(node3.hasKey(CreateConnectionsCommand.CONN_KEY)); + Assert.assertTrue(node3.hasKey(ConnectedCommand.CONN_KEY)); - dupe.put(CreateConnectionsCommand.DUPE_KEY, "n" + node1.getUniqueId()); + dupe.put(DuplicateCommand.DUPE_KEY, "n" + node1.getUniqueId()); createConnections = new CreateConnectionsCommand(dataSet, Collections.singleton(dupe)); createConnections.executeCommand(); Assert.assertTrue(dataSet.isModified()); Assert.assertEquals(2, way.getNodesCount()); - Assert.assertFalse(node1.hasKey(CreateConnectionsCommand.DUPE_KEY)); + Assert.assertFalse(node1.hasKey(DuplicateCommand.DUPE_KEY)); modified.clear(); createConnections.fillModifiedData(modified, deleted, added); Assert.assertEquals(2, modified.size()); Assert.assertTrue(deleted.isEmpty()); Assert.assertTrue(added.isEmpty()); createConnections.undoCommand(); - Assert.assertFalse(node1.hasKey(CreateConnectionsCommand.DUPE_KEY)); - Assert.assertTrue(dupe.hasKey(CreateConnectionsCommand.DUPE_KEY)); + Assert.assertFalse(node1.hasKey(DuplicateCommand.DUPE_KEY)); + Assert.assertTrue(dupe.hasKey(DuplicateCommand.DUPE_KEY)); Assert.assertFalse(dataSet.isModified()); Assert.assertEquals(2, way.getNodesCount()); } @@ -115,7 +117,7 @@ public class CreateConnectionsCommandTest { final Node node3 = new Node(new LatLon(0.5, 0)); final Way way = TestUtils.newWay("highway=residential", node1, node2); new DataSet(node1, node2, node3, way); - Command addNodeToWayCommand = CreateConnectionsCommand.addNodesToWay(node3, way, node1, node2); + Command addNodeToWayCommand = ConnectedCommand.addNodesToWay(node3, way, node1, node2); Assert.assertEquals(2, way.getNodesCount()); addNodeToWayCommand.executeCommand(); Assert.assertEquals(3, way.getNodesCount()); @@ -123,15 +125,15 @@ public class CreateConnectionsCommandTest { Assert.assertEquals(2, way.getNodesCount()); node2.setCoor(new LatLon(1, 0.1)); - addNodeToWayCommand = CreateConnectionsCommand.addNodesToWay(node3, way, node1, node2); + addNodeToWayCommand = ConnectedCommand.addNodesToWay(node3, way, node1, node2); Assert.assertNull(addNodeToWayCommand); node2.setCoor(new LatLon(1, 0.01)); - addNodeToWayCommand = CreateConnectionsCommand.addNodesToWay(node3, way, node1, node2); + addNodeToWayCommand = ConnectedCommand.addNodesToWay(node3, way, node1, node2); Assert.assertNull(addNodeToWayCommand); node2.setCoor(new LatLon(1, 0.00008)); - addNodeToWayCommand = CreateConnectionsCommand.addNodesToWay(node3, way, node1, node2); + addNodeToWayCommand = ConnectedCommand.addNodesToWay(node3, way, node1, node2); addNodeToWayCommand.executeCommand(); Assert.assertEquals(3, way.getNodesCount()); addNodeToWayCommand.undoCommand(); @@ -146,14 +148,14 @@ public class CreateConnectionsCommandTest { final Node node1 = new Node(new LatLon(0, 0)); final Node node2 = new Node(new LatLon(0, 0)); new DataSet(node1, node2); - final Command replaceNodeCommand = CreateConnectionsCommand.replaceNode(node1, node2); + final Command replaceNodeCommand = DuplicateCommand.replaceNode(node1, node2); replaceNodeCommand.executeCommand(); Assert.assertTrue(node1.isDeleted()); replaceNodeCommand.undoCommand(); Assert.assertFalse(node1.isDeleted()); node2.setCoor(new LatLon(0.1, 0.1)); - Assert.assertNull(CreateConnectionsCommand.replaceNode(node1, node2)); + Assert.assertNull(DuplicateCommand.replaceNode(node1, node2)); } /** @@ -163,9 +165,8 @@ public class CreateConnectionsCommandTest { public void testGetMissingPrimitives() { final Node node1 = new Node(new LatLon(39.0674124, -108.5592645)); final DataSet dataSet = new DataSet(node1); - node1.put(CreateConnectionsCommand.DUPE_KEY, "n6146500887"); - Command replaceNodeCommand = CreateConnectionsCommand.createConnections(dataSet, - Collections.singleton(node1)); + node1.put(DuplicateCommand.DUPE_KEY, "n6146500887"); + Command replaceNodeCommand = CreateConnectionsCommand.createConnections(dataSet, Collections.singleton(node1)); replaceNodeCommand.executeCommand(); Assert.assertEquals(1, dataSet.allNonDeletedPrimitives().size()); @@ -176,7 +177,7 @@ public class CreateConnectionsCommandTest { Assert.assertNotNull(dataSet.getPrimitiveById(6146500887L, OsmPrimitiveType.NODE)); node1.setCoor(new LatLon(39.067399, -108.5608433)); - node1.put(CreateConnectionsCommand.DUPE_KEY, "n6151680832"); + node1.put(DuplicateCommand.DUPE_KEY, "n6151680832"); final OsmDataLayer layer = new OsmDataLayer(dataSet, "temp layer", null); MainApplication.getLayerManager().addLayer(layer);