Add data to data layer (left on RapiD layer for now)

Signed-off-by: Taylor Smock <smocktaylor@gmail.com>
pull/1/head
Taylor Smock 2019-09-19 20:54:05 -06:00
rodzic 78b7ad5048
commit 55f34dd95c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 9FDE4FFEF1C4CCB7
6 zmienionych plików z 359 dodań i 20 usunięć

Wyświetl plik

@ -1,10 +1,13 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.rapid;
import javax.swing.AbstractAction;
import org.openstreetmap.josm.gui.MainApplication;
import org.openstreetmap.josm.plugins.Plugin;
import org.openstreetmap.josm.plugins.PluginInformation;
import org.openstreetmap.josm.plugins.rapid.backend.RapiDAction;
import org.openstreetmap.josm.plugins.rapid.backend.RapiDMoveAction;
public final class RapiDPlugin extends Plugin {
/** The name of the plugin */
@ -14,7 +17,11 @@ public final class RapiDPlugin extends Plugin {
super(info);
RapiDAction action = new RapiDAction();
AbstractAction add = new RapiDMoveAction();
MainApplication.getMenu().dataMenu.add(action);
MainApplication.getMenu().dataMenu.add(add);
MainApplication.getMenu().fileMenu.add(action);
MainApplication.getMenu().fileMenu.add(add);
}
}

Wyświetl plik

@ -0,0 +1,179 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.rapid.backend;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.openstreetmap.josm.command.AddPrimitivesCommand;
import org.openstreetmap.josm.command.Command;
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.PrimitiveData;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor;
import org.openstreetmap.josm.plugins.rapid.RapiDPlugin;
import org.openstreetmap.josm.tools.Logging;
import org.openstreetmap.josm.tools.Utils;
public class RapiDAddCommand extends Command {
DataSet editable;
DataSet rapid;
Collection<OsmPrimitive> primitives;
AddPrimitivesCommand addPrimitivesCommand;
Collection<OsmPrimitive> modifiedPrimitives;
/**
* Add primitives from RapiD to the OSM data layer
*
* @param rapid The rapid dataset
* @param editable The OSM dataset
* @param selection The primitives to add from RapiD
*/
public RapiDAddCommand(DataSet rapid, DataSet editable, Collection<OsmPrimitive> selection) {
super(rapid);
this.rapid = rapid;
this.editable = editable;
this.primitives = selection;
modifiedPrimitives = null;
}
@Override
public boolean executeCommand() {
if (rapid.equals(editable)) {
Logging.error("{0}: DataSet rapid ({1}) should not be the same as DataSet editable ({2})", RapiDPlugin.NAME,
rapid, editable);
throw new IllegalArgumentException();
}
primitives = new HashSet<>(primitives);
addPrimitivesToCollection(/* collection= */ primitives, /* primitives= */ primitives);
synchronized (this) {
rapid.unlock();
Collection<OsmPrimitive> newPrimitives = new TreeSet<>(moveCollection(rapid, editable, primitives));
createConnections(editable, newPrimitives);
rapid.lock();
}
return true;
}
/**
* Create connections based off of current RapiD syntax
*
* @param collection The primitives with connection information (currently only
* checks Nodes)
*/
public static void createConnections(DataSet dataSet, Collection<OsmPrimitive> collection) {
Collection<Node> nodes = Utils.filteredCollection(collection, Node.class);
for (Node node : nodes) {
if (node.hasKey("conn")) {
// Currently w<way id>,n<node1>,n<node2>
String[] connections = node.get("conn").split(",");
OsmPrimitive[] primitiveConnections = new OsmPrimitive[connections.length];
for (int i = 0; i < connections.length; i++) {
String member = connections[i];
long id = Long.parseLong(member.substring(1));
char firstChar = member.charAt(0);
if (firstChar == 'w') {
primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.WAY);
} else if (firstChar == 'n') {
primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.NODE);
} else if (firstChar == 'r') {
primitiveConnections[i] = dataSet.getPrimitiveById(id, OsmPrimitiveType.RELATION);
}
}
for (int i = 0; i < primitiveConnections.length / 3; i++) {
if (primitiveConnections[i] instanceof Way && primitiveConnections[i + 1] instanceof Node
&& primitiveConnections[i + 2] instanceof Node) {
addNodesToWay(node, (Way) primitiveConnections[i], (Node) primitiveConnections[i + 1],
(Node) primitiveConnections[i + 2]);
} 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.error("RapiD: Removing conn from {0} in {1}", node, dataSet.getName());
node.remove("conn");
}
}
}
/**
* 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
* @param recursion The recursion (how many times this has called itself). Use 0
* when calling.
*/
public static void addNodesToWay(Node toAddNode, Way way, Node first, Node second) {
int index = Math.min(way.getNodes().indexOf(first), way.getNodes().indexOf(second));
way.addNode(index, toAddNode);
}
/**
* Move primitives from one dataset to another
*
* @param to The receiving dataset
* @param from The sending dataset
* @param selection The primitives to move
* @return true if the primitives have moved datasets
*/
public Collection<? extends OsmPrimitive> moveCollection(DataSet from, DataSet to,
Collection<OsmPrimitive> selection) {
if (from == null || to.isLocked() || from.isLocked()) {
Logging.error("RapiD: Cannot move primitives from {0} to {1}", from, to);
return Collections.emptySet();
}
Collection<OsmPrimitive> originalSelection = from.getSelected();
from.setSelected(selection);
MergeSourceBuildingVisitor mergeBuilder = new MergeSourceBuildingVisitor(from);
List<PrimitiveData> primitiveDataList = mergeBuilder.build().allPrimitives().stream().map(OsmPrimitive::save)
.collect(Collectors.toList());
from.setSelected(originalSelection);
addPrimitivesCommand = new AddPrimitivesCommand(primitiveDataList, primitiveDataList, to);
addPrimitivesCommand.executeCommand();
return addPrimitivesCommand.getParticipatingPrimitives();
}
/**
* Add primitives and their children to a collection
*
* @param collection A collection to add the primitives to
* @param primitives The primitives to add to the collection
*/
public static void addPrimitivesToCollection(Collection<OsmPrimitive> collection,
Collection<OsmPrimitive> primitives) {
for (OsmPrimitive primitive : primitives) {
if (primitive instanceof Way) {
collection.addAll(((Way) primitive).getNodes());
} else if (primitive instanceof Relation) {
addPrimitivesToCollection(collection, ((Relation) primitive).getMemberPrimitives());
}
collection.add(primitive);
}
}
@Override
public String getDescriptionText() {
return tr("Add object from RapiD");
}
@Override
public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
Collection<OsmPrimitive> added) {
// TODO Auto-generated method stub
}
}

Wyświetl plik

@ -3,9 +3,9 @@ package org.openstreetmap.josm.plugins.rapid.backend;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.openstreetmap.josm.data.osm.BBox;
import org.openstreetmap.josm.data.osm.DataSet;
@ -20,6 +20,14 @@ import org.openstreetmap.josm.tools.Logging;
*/
public final class RapiDDataUtils {
private static final String RAPID_API_TOKEN = "ASZUVdYpCkd3M6ZrzjXdQzHulqRMnxdlkeBJWEKOeTUoY_Gwm9fuEd2YObLrClgDB_xfavizBsh0oDfTWTF7Zb4C";
private static Set<String> API_LIST = new HashSet<>();
static {
addRapidApi(new StringBuilder().append("https://www.facebook.com/maps/ml_roads?").append("conflate_with_osm=")
.append(true).append("&").append("theme=")
.append("ml_road_vector").append("&").append("collaborator=").append("fbid").append("&")
.append("token=").append(RAPID_API_TOKEN).append("&").append("hash=").append("ASYM8LPNy8k1XoJiI7A")
.append("&").append("bbox={bbox}").toString());
}
private RapiDDataUtils() {
// Hide the constructor
@ -30,27 +38,29 @@ public final class RapiDDataUtils {
Logging.setLogLevel(Logging.LEVEL_DEBUG);
InputStream inputStream = null;
DataSet dataSet = new DataSet();
try {
final String query = new StringBuilder().append("conflate_with_osm=").append(true).append("theme=")
.append("ml_road_vector").append("&").append("collaborator=").append("fbid").append("&")
.append("token=").append(RAPID_API_TOKEN).append("&").append("hash=").append("ASYM8LPNy8k1XoJiI7A")
.append("&").append("bbox=").append(bbox.toString()).toString();
final URI uri = new URI("https", null, "www.facebook.com", 80, "/maps/ml_roads", query, null);
final URL url = uri.toURL();
inputStream = url.openStream();
Logging.info("{0}: Getting {1}", RapiDPlugin.NAME, uri.toASCIIString());
dataSet = OsmReader.parseDataSet(inputStream, null);
} catch (URISyntaxException | UnsupportedOperationException | IllegalDataException | IOException e) {
Logging.debug(e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
Logging.debug(e);
for (String urlString : API_LIST) {
try {
final URL url = new URL(urlString.replace("{bbox}", bbox.toStringCSV(",")));
Logging.error("{0}: Getting {1}", RapiDPlugin.NAME, url.toString());
inputStream = url.openStream();
dataSet.mergeFrom(OsmReader.parseDataSet(inputStream, null));
} catch (UnsupportedOperationException | IllegalDataException | IOException e) {
Logging.debug(e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
Logging.debug(e);
}
}
}
}
return dataSet;
}
public static void addRapidApi(String url) {
API_LIST.add(url);
}
}

Wyświetl plik

@ -0,0 +1,46 @@
package org.openstreetmap.josm.plugins.rapid.backend;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.List;
import javax.swing.AbstractAction;
import org.openstreetmap.josm.data.UndoRedoHandler;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.gui.MainApplication;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.plugins.rapid.RapiDPlugin;
public class RapiDMoveAction extends AbstractAction {
/** UID for abstract action */
private static final long serialVersionUID = 319374598;
public RapiDMoveAction() {
super(tr("Add from ".concat(RapiDPlugin.NAME)));
}
@Override
public void actionPerformed(ActionEvent event) {
for (RapiDLayer layer : MainApplication.getLayerManager().getLayersOfType(RapiDLayer.class)) {
List<OsmDataLayer> osmLayers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class);
DataSet editData = null;
DataSet rapid = layer.getDataSet();
Collection<OsmPrimitive> selected = rapid.getSelected();
for (OsmDataLayer osmLayer : osmLayers) {
if (!osmLayer.isLocked() && osmLayer.isVisible() && osmLayer.isUploadable()
&& osmLayer.getClass().equals(OsmDataLayer.class)) {
editData = osmLayer.getDataSet();
break;
}
}
if (editData != null) {
RapiDAddCommand command = new RapiDAddCommand(rapid, editData, selected);
UndoRedoHandler.getInstance().add(command);
}
}
}
}

Wyświetl plik

@ -0,0 +1,70 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.rapid;
import java.util.Collection;
import java.util.Collections;
import java.util.TreeSet;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.openstreetmap.josm.TestUtils;
import org.openstreetmap.josm.data.coor.LatLon;
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.rapid.backend.RapiDAddCommand;
import org.openstreetmap.josm.testutils.JOSMTestRules;
public class RapiDAddComandTest {
@Rule
public JOSMTestRules test = new JOSMTestRules();
@Test
public void testMoveCollection() {
DataSet ds1 = new DataSet();
DataSet ds2 = new DataSet();
Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)),
new Node(new LatLon(0, 0.1)));
for (Node node : way1.getNodes()) {
ds1.addPrimitive(node);
}
ds1.addPrimitive(way1);
ds1.lock();
RapiDAddCommand command = new RapiDAddCommand(ds1, ds2, Collections.singleton(way1));
command.executeCommand();
Assert.assertTrue(ds2.containsWay(way1));
Assert.assertTrue(ds2.containsNode(way1.firstNode()));
Assert.assertTrue(ds2.containsNode(way1.lastNode()));
// Assert.assertFalse(ds1.containsWay(way1)); // Only if we delete ways from
// rapid dataset
}
@Test
public void testAddPrimitivesToCollection() {
Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(0, 0.1)));
Collection<OsmPrimitive> collection = new TreeSet<>();
Assert.assertEquals(0, collection.size());
RapiDAddCommand.addPrimitivesToCollection(collection, Collections.singletonList(way1));
Assert.assertEquals(3, collection.size());
}
@Test
public void testCreateConnections() {
DataSet ds1 = new DataSet();
Way way1 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)), new Node(new LatLon(0, 0.1)));
Way way2 = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0.05)),
new Node(new LatLon(0.05, 0.2)));
way2.firstNode().put("conn",
"w".concat(Long.toString(way1.getUniqueId())).concat(",n")
.concat(Long.toString(way1.firstNode().getUniqueId())).concat(",n")
.concat(Long.toString(way1.lastNode().getUniqueId())));
way1.getNodes().forEach(node -> ds1.addPrimitive(node));
way2.getNodes().forEach(node -> ds1.addPrimitive(node));
ds1.addPrimitive(way2);
ds1.addPrimitive(way1);
RapiDAddCommand.createConnections(ds1, Collections.singletonList(way2.firstNode()));
Assert.assertEquals(3, way1.getNodesCount());
}
}

Wyświetl plik

@ -0,0 +1,27 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.rapid;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.BBox;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.plugins.rapid.backend.RapiDDataUtils;
import org.openstreetmap.josm.testutils.JOSMTestRules;
public class RapiDDataUtilsTest {
@Rule
public JOSMTestRules test = new JOSMTestRules();
/**
* This gets data from RapiD. This test may fail if someone adds the data to OSM.
*/
@Test
public void testGetData() {
BBox testBBox = new BBox();
testBBox.add(new LatLon(39.066058, -108.5683808));
testBBox.add(new LatLon(39.0667526, -108.5681757));
DataSet ds = new DataSet(RapiDDataUtils.getData(testBBox));
Assert.assertEquals(1, ds.getWays().size());
}
}