kopia lustrzana https://github.com/JOSM/MapWithAI
230 wiersze
9.8 KiB
Java
230 wiersze
9.8 KiB
Java
// License: GPL. For details, see LICENSE file.
|
|
package org.openstreetmap.josm.plugins.rapid.commands;
|
|
|
|
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 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.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.rapid.RapiDPlugin;
|
|
import org.openstreetmap.josm.tools.Geometry;
|
|
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<OsmPrimitive> primitives;
|
|
public static final String DUPE_KEY = "dupe";
|
|
public static final String CONN_KEY = "conn";
|
|
private Command command = null;
|
|
|
|
public CreateConnectionsCommand(DataSet data, Collection<OsmPrimitive> primitives) {
|
|
super(data);
|
|
this.primitives = primitives;
|
|
}
|
|
|
|
@Override
|
|
public boolean executeCommand() {
|
|
command = createConnections(getAffectedDataSet(), primitives);
|
|
if (command != null) {
|
|
command.executeCommand();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void undoCommand() {
|
|
if (command != null) {
|
|
command.undoCommand();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create connections based off of current RapiD syntax
|
|
*
|
|
* @param dataSet The {@link DataSet} that should have the primitives we are
|
|
* connecting to
|
|
* @param collection The primitives with connection information (currently only
|
|
* checks Nodes)
|
|
* @return
|
|
*/
|
|
public SequenceCommand createConnections(DataSet dataSet, Collection<OsmPrimitive> collection) {
|
|
final Collection<Node> nodes = Utils.filteredCollection(collection, Node.class);
|
|
final List<Command> changedKeyList = new ArrayList<>();
|
|
for (final Node node : nodes) {
|
|
if (node.hasKey(CONN_KEY)) {
|
|
changedKeyList.addAll(connectedCommand(dataSet, node));
|
|
}
|
|
if (node.hasKey(DUPE_KEY)) {
|
|
final Command replaceCommand = duplicateNode(dataSet, node);
|
|
if (replaceCommand != null) {
|
|
changedKeyList.add(replaceCommand);
|
|
}
|
|
}
|
|
}
|
|
if (!changedKeyList.isEmpty()) {
|
|
return new SequenceCommand(getDescriptionText(), changedKeyList);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static List<Command> connectedCommand(DataSet dataSet, Node node) {
|
|
final List<Command> 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());
|
|
}
|
|
}
|
|
Logging.debug("RapiD: Removing conn from {0} in {1}", node, dataSet.getName());
|
|
commands.add(new ChangePropertyCommand(node, CONN_KEY, null));
|
|
return commands;
|
|
}
|
|
|
|
private static Command duplicateNode(DataSet dataSet, Node node) {
|
|
final OsmPrimitive[] primitiveConnections = getPrimitives(dataSet, node.get(DUPE_KEY));
|
|
if (primitiveConnections.length != 1) {
|
|
Logging.error("RapiD: dupe connection connected to more than one node? (dupe={0})", node.get(DUPE_KEY));
|
|
}
|
|
return replaceNode(node, (Node) primitiveConnections[0]);
|
|
}
|
|
|
|
/**
|
|
* 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<Integer, Pair<Long, OsmPrimitiveType>> 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", RapiDPlugin.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<Integer, Pair<Long, OsmPrimitiveType>> missingPrimitives) {
|
|
final Map<PrimitiveId, Integer> ids = missingPrimitives.entrySet().stream().collect(Collectors
|
|
.toMap(entry -> new SimplePrimitiveId(entry.getValue().a, entry.getValue().b), Entry::getKey));
|
|
final List<PrimitiveId> toFetch = new ArrayList<>(ids.keySet());
|
|
final Optional<OsmDataLayer> optionalLayer = MainApplication.getLayerManager()
|
|
.getLayersOfType(OsmDataLayer.class).parallelStream()
|
|
.filter(layer -> layer.getDataSet().equals(dataSet)).findFirst();
|
|
OsmDataLayer layer;
|
|
if (optionalLayer.isPresent()) {
|
|
layer = optionalLayer.get();
|
|
} else {
|
|
layer = new OsmDataLayer(dataSet, "generated layer", null);
|
|
}
|
|
final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(
|
|
tr("Downloading additional OsmPrimitives"));
|
|
final DownloadPrimitivesTask downloadPrimitivesTask = new DownloadPrimitivesTask(layer, toFetch, true, monitor);
|
|
downloadPrimitivesTask.run();
|
|
for (final Entry<PrimitiveId, Integer> entry : ids.entrySet()) {
|
|
final int index = entry.getValue().intValue();
|
|
final OsmPrimitive primitive = dataSet.getPrimitiveById(entry.getKey());
|
|
primitiveConnections[index] = primitive;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 waysegemnt
|
|
*/
|
|
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 tr("Create connections from {0} data", RapiDPlugin.NAME);
|
|
}
|
|
|
|
@Override
|
|
public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
|
|
Collection<OsmPrimitive> added) {
|
|
command.fillModifiedData(modified, deleted, added);
|
|
}
|
|
}
|