/** * their way has a higher version and different nodes. My way is modified. * * <p>=> merge onto my way not possible, create a conflict */ @Test public void waySimple_DifferentNodesAndMyIsModified() { // -- the target dataset Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(n1); myWay.addNode(n2); myWay.setModified(true); myWay.put("key1", "value1"); my.addPrimitive(myWay); // -- the source dataset Node n3 = new Node(new LatLon(0, 0)); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n5 = new Node(new LatLon(1, 1)); n5.setOsmId(4, 1); their.addPrimitive(n5); Node n4 = new Node(new LatLon(2, 2)); n4.setOsmId(2, 1); n4.put("key1", "value1"); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.addNode(n3); theirWay.addNode(n5); // insert a node theirWay.addNode(n4); // this one is updated their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(1, visitor.getConflicts().size()); assertEquals(3, merged.getId()); assertEquals(1, merged.getVersion()); assertEquals(2, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(2, merged.getNode(1).getId()); assertEquals("value1", merged.get("key1")); }
protected void populate() { Way w1 = new Way(1); w1.addNode(new Node(10)); w1.addNode(new Node(11)); Way w2 = new Way(1); w2.addNode(new Node(10)); w2.addNode(new Node(11)); dialog.getConflictResolver().populate(new Conflict<OsmPrimitive>(w1, w2)); }
/** * my and their way have no ids, nodes they refer to have an id. but my and their way are * semantically equal. so technical attributes of their way can be merged on my way. No conflict. */ @Test public void waySimple_twoWaysWithNoId_NodesWithId() { // -- my data set Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // -- their data set Node n3 = new Node(new LatLon(0, 0)); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n4 = new Node(new LatLon(1, 1)); n4.setOsmId(2, 1); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.addNode(n3); theirWay.addNode(n4); User user = User.createOsmUser(1111, "their"); theirWay.setUser(user); theirWay.setTimestamp(new Date()); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getWays().toArray()[0]; assertEquals(0, visitor.getConflicts().size()); assertEquals("their", merged.getUser().getName()); assertEquals(1111, merged.getUser().getId()); assertEquals(theirWay.getRawTimestamp(), merged.getRawTimestamp()); assertSame(merged, myWay); assertSame(merged.getNode(0), n1); assertSame(merged.getNode(1), n2); assertFalse(merged.isModified()); }
/** * My dataset includes a deleted node. Their dataset includes a way with three nodes, the first * one being my node. * * <p>=> the merged way should include all three nodes. Deleted node should have deleted=false and * special conflict with isDeleted should exist */ @Test public void wayComplex_mergingADeletedNode() { // -- my dataset Node mn1 = new Node(new LatLon(0, 0)); mn1.setOsmId(1, 1); mn1.setDeleted(true); my.addPrimitive(mn1); Node tn1 = new Node(new LatLon(0, 0)); tn1.setOsmId(1, 1); their.addPrimitive(tn1); Node tn2 = new Node(new LatLon(1, 1)); tn2.setOsmId(2, 1); their.addPrimitive(tn2); Node tn3 = new Node(new LatLon(2, 2)); tn3.setOsmId(3, 1); their.addPrimitive(tn3); // -- their data set Way theirWay = new Way(); theirWay.setOsmId(4, 1); theirWay.addNode(tn1); theirWay.addNode(tn2); theirWay.addNode(tn3); theirWay.setUser(User.createOsmUser(1111, "their")); theirWay.setTimestamp(new Date()); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); assertEquals(1, visitor.getConflicts().size()); assertTrue(visitor.getConflicts().get(0).isMyDeleted()); Way myWay = (Way) my.getPrimitiveById(4, OsmPrimitiveType.WAY); assertEquals(3, myWay.getNodesCount()); Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertTrue(myWay.getNodes().contains(n)); assertFalse(myWay.isModified()); }
private SearchContext(String state) throws ParseError { m = SearchCompiler.compile(state); n = SearchCompiler.compile('-' + state); ds.addPrimitive(n1); ds.addPrimitive(n2); w1.addNode(n1); w1.addNode(n2); w2.addNode(n1); w2.addNode(n2); ds.addPrimitive(w1); ds.addPrimitive(w2); r1.addMember(new RelationMember("", w1)); r1.addMember(new RelationMember("", w2)); r2.addMember(new RelationMember("", w1)); r2.addMember(new RelationMember("", w2)); ds.addPrimitive(r1); ds.addPrimitive(r2); }
protected static void populateTestDataSetWithWays(DataSet ds) { for (int i = 0; i < 20; i++) { Way w = new Way(); for (int j = 0; j < 10; j++) { w.addNode(lookupNode(ds, i + j)); } w.put("name", "way-" + i); ds.addPrimitive(w); } }
private void appendNode(double x, double y) throws IOException { if (currentway == null) { throw new IOException("Shape is started incorectly"); } Node nd = new Node(projection.eastNorth2latlon(center.add(x * scale, -y * scale))); if (nd.getCoor().isOutSideWorld()) { throw new IOException("Shape goes outside the world"); } currentway.addNode(nd); nodes.add(nd); lastX = x; lastY = y; }
/** * merge to complete nodes onto an incomplete way with the same two nodes, but incomplete. => both * the nodes and the way should be complete in the target dataset after merging */ @Test public void twoCompleteNodesOntoAnIncompleteWay() { // -- source dataset // an complete node Node n1 = new Node(1, 1); n1.setCoor(new LatLon(1, 1)); their.addPrimitive(n1); // another complete node Node n2 = new Node(2, 1); n2.setCoor(new LatLon(2, 2)); their.addPrimitive(n2); // --- target dataset Node n4 = new Node(1); my.addPrimitive(n4); Node n5 = new Node(2); my.addPrimitive(n5); Way w6 = new Way(3, 1); w6.addNode(n4); w6.addNode(n5); my.addPrimitive(w6); // -- merge it DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- test it assertEquals(0, visitor.getConflicts().size()); Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(n); assertFalse(n.isIncomplete()); n = (Node) my.getPrimitiveById(2, OsmPrimitiveType.NODE); assertNotNull(n); assertFalse(n.isIncomplete()); Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertNotNull(w); assertFalse(w.hasIncompleteNodes()); assertTrue(w.isUsable()); assertEquals(2, w.getNodesCount()); assertEquals(1, w.getNode(0).getId()); assertEquals(2, w.getNode(1).getId()); }
/** * Search by nth. * * @throws ParseError if an error has been encountered while compiling */ @Test public void testNth() throws ParseError { final DataSet dataSet = new DataSet(); final Way way = new Way(); final Node node0 = new Node(new LatLon(1, 1)); final Node node1 = new Node(new LatLon(2, 2)); final Node node2 = new Node(new LatLon(3, 3)); dataSet.addPrimitive(way); dataSet.addPrimitive(node0); dataSet.addPrimitive(node1); dataSet.addPrimitive(node2); way.addNode(node0); way.addNode(node1); way.addNode(node2); assertFalse(SearchCompiler.compile("nth:2").match(node1)); assertTrue(SearchCompiler.compile("nth:1").match(node1)); assertFalse(SearchCompiler.compile("nth:0").match(node1)); assertTrue(SearchCompiler.compile("nth:0").match(node0)); assertTrue(SearchCompiler.compile("nth:2").match(node2)); assertTrue(SearchCompiler.compile("nth:-1").match(node2)); assertTrue(SearchCompiler.compile("nth:-2").match(node1)); assertTrue(SearchCompiler.compile("nth:-3").match(node0)); }
/** * their way is not visible anymore. * * <p>=> conflict */ @Test public void waySimple_TheirVersionNotVisibleMyIsModified() { Node mn1 = new Node(new LatLon(0, 0)); mn1.setOsmId(1, 1); my.addPrimitive(mn1); Node mn2 = new Node(new LatLon(1, 1)); mn2.setOsmId(2, 1); my.addPrimitive(mn2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(mn1); myWay.addNode(mn2); myWay.setModified(true); my.addPrimitive(myWay); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.setVisible(false); /* Invisible objects fetched from the server should be marked as "deleted". * Otherwise it's an error. */ theirWay.setDeleted(true); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(1, visitor.getConflicts().size()); assertTrue(visitor.getConflicts().hasConflictForMy(myWay)); assertTrue(visitor.getConflicts().hasConflictForTheir(theirWay)); assertEquals(myWay, merged); }
/** * Perform AlignInCircle action. * * <p>A fixed node is a node for which it is forbidden to change the angle relative to center of * the circle. All other nodes are uniformly distributed. * * <p>Case 1: One unclosed way. --> allow action, and align selected way nodes If nodes * contained by this way are selected, there are fix. If nodes outside from the way are selected * there are ignored. * * <p>Case 2: One or more ways are selected and can be joined into a polygon --> allow action, * and align selected ways nodes If 1 node outside of way is selected, it became center If 1 node * outside and 1 node inside are selected there define center and radius If no outside node and 2 * inside nodes are selected those 2 nodes define diameter In all other cases outside nodes are * ignored In all cases, selected nodes are fix, nodes with more than one referrers are fix (first * referrer is the selected way) * * <p>Case 3: Only nodes are selected --> Align these nodes, all are fix */ @Override public void actionPerformed(ActionEvent e) { if (!isEnabled()) return; Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected(); List<Node> nodes = new LinkedList<>(); // fixNodes: All nodes for which the angle relative to center should not be modified Set<Node> fixNodes = new HashSet<>(); List<Way> ways = new LinkedList<>(); EastNorth center = null; double radius = 0; for (OsmPrimitive osm : sel) { if (osm instanceof Node) { nodes.add((Node) osm); } else if (osm instanceof Way) { ways.add((Way) osm); } } if (ways.size() == 1 && !ways.get(0).isClosed()) { // Case 1 Way w = ways.get(0); fixNodes.add(w.firstNode()); fixNodes.add(w.lastNode()); fixNodes.addAll(nodes); fixNodes.addAll(collectNodesWithExternReferers(ways)); // Temporary closed way used to reorder nodes Way closedWay = new Way(w); closedWay.addNode(w.firstNode()); List<Way> usedWays = new ArrayList<>(1); usedWays.add(closedWay); nodes = collectNodesAnticlockwise(usedWays); } else if (!ways.isEmpty() && checkWaysArePolygon(ways)) { // Case 2 List<Node> inside = new ArrayList<>(); List<Node> outside = new ArrayList<>(); for (Node n : nodes) { boolean isInside = false; for (Way w : ways) { if (w.getNodes().contains(n)) { isInside = true; break; } } if (isInside) inside.add(n); else outside.add(n); } if (outside.size() == 1 && inside.isEmpty()) { center = outside.get(0).getEastNorth(); } else if (outside.size() == 1 && inside.size() == 1) { center = outside.get(0).getEastNorth(); radius = distance(center, inside.get(0).getEastNorth()); } else if (inside.size() == 2 && outside.isEmpty()) { // 2 nodes inside, define diameter EastNorth en0 = inside.get(0).getEastNorth(); EastNorth en1 = inside.get(1).getEastNorth(); center = new EastNorth((en0.east() + en1.east()) / 2, (en0.north() + en1.north()) / 2); radius = distance(en0, en1) / 2; } fixNodes.addAll(inside); fixNodes.addAll(collectNodesWithExternReferers(ways)); nodes = collectNodesAnticlockwise(ways); if (nodes.size() < 4) { new Notification(tr("Not enough nodes in selected ways.")) .setIcon(JOptionPane.INFORMATION_MESSAGE) .setDuration(Notification.TIME_SHORT) .show(); return; } } else if (ways.isEmpty() && nodes.size() > 3) { // Case 3 fixNodes.addAll(nodes); // No need to reorder nodes since all are fix } else { // Invalid action new Notification(tr("Please select at least four nodes.")) .setIcon(JOptionPane.INFORMATION_MESSAGE) .setDuration(Notification.TIME_SHORT) .show(); return; } if (center == null) { // Compute the center of nodes center = Geometry.getCenter(nodes); if (center == null) { new Notification(tr("Cannot determine center of selected nodes.")) .setIcon(JOptionPane.INFORMATION_MESSAGE) .setDuration(Notification.TIME_SHORT) .show(); return; } } // Now calculate the average distance to each node from the // center. This method is ok as long as distances are short // relative to the distance from the N or S poles. if (radius == 0) { for (Node n : nodes) { radius += distance(center, n.getEastNorth()); } radius = radius / nodes.size(); } if (!actionAllowed(nodes)) return; Collection<Command> cmds = new LinkedList<>(); // Move each node to that distance from the center. // Nodes that are not "fix" will be adjust making regular arcs. int nodeCount = nodes.size(); // Search first fixed node int startPosition = 0; for (startPosition = 0; startPosition < nodeCount; startPosition++) { if (fixNodes.contains(nodes.get(startPosition % nodeCount))) break; } int i = startPosition; // Start position for current arc int j; // End position for current arc while (i < startPosition + nodeCount) { for (j = i + 1; j < startPosition + nodeCount; j++) { if (fixNodes.contains(nodes.get(j % nodeCount))) break; } Node first = nodes.get(i % nodeCount); PolarCoor pcFirst = new PolarCoor(first.getEastNorth(), center, 0); pcFirst.radius = radius; cmds.add(pcFirst.createMoveCommand(first)); if (j > i + 1) { double delta; if (j == i + nodeCount) { delta = 2 * Math.PI / nodeCount; } else { PolarCoor pcLast = new PolarCoor(nodes.get(j % nodeCount).getEastNorth(), center, 0); delta = pcLast.angle - pcFirst.angle; if (delta < 0) // Assume each PolarCoor.angle is in range ]-pi; pi] delta += 2 * Math.PI; delta /= j - i; } for (int k = i + 1; k < j; k++) { PolarCoor p = new PolarCoor(radius, pcFirst.angle + (k - i) * delta, center, 0); cmds.add(p.createMoveCommand(nodes.get(k % nodeCount))); } } i = j; // Update start point for next iteration } Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Circle"), cmds)); Main.map.repaint(); }
/** * builds a large data set to be used later for testing MULTI FETCH on the server * * @return a large data set */ protected static DataSet buildTestDataSet() { DataSet ds = new DataSet(); ds.setVersion("0.6"); int numNodes = 1000; int numWays = 1000; int numRelations = 1000; ArrayList<Node> nodes = new ArrayList<>(); ArrayList<Way> ways = new ArrayList<>(); // create a set of nodes // for (int i = 0; i < numNodes; i++) { Node n = new Node(); n.setCoor(new LatLon(-36.6, 47.6)); n.put("name", "node-" + i); ds.addPrimitive(n); nodes.add(n); } // create a set of ways, each with a random number of nodes // for (int i = 0; i < numWays; i++) { Way w = new Way(); int numNodesInWay = 2 + (int) Math.round(Math.random() * 5); int start = (int) Math.round(Math.random() * numNodes); for (int j = 0; j < numNodesInWay; j++) { int idx = (start + j) % numNodes; Node n = nodes.get(idx); w.addNode(n); } w.put("name", "way-" + i); ds.addPrimitive(w); ways.add(w); } // create a set of relations each with a random number of nodes, and ways // for (int i = 0; i < numRelations; i++) { Relation r = new Relation(); r.put("name", "relation-" + i); int numNodesInRelation = (int) Math.round(Math.random() * 10); int start = (int) Math.round(Math.random() * numNodes); for (int j = 0; j < numNodesInRelation; j++) { int idx = (start + j) % 500; Node n = nodes.get(idx); r.addMember(new RelationMember("role-" + j, n)); } int numWaysInRelation = (int) Math.round(Math.random() * 10); start = (int) Math.round(Math.random() * numWays); for (int j = 0; j < numWaysInRelation; j++) { int idx = (start + j) % 500; Way w = ways.get(idx); r.addMember(new RelationMember("role-" + j, w)); } ds.addPrimitive(r); } return ds; }
/** * their way has a higher version and different tags. And it has more nodes. Two of the existing * nodes are modified. * * <p>=> merge it onto my way, no conflict */ @Test public void waySimple_AdditionalNodesAndChangedNodes() { // -- my data set Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // --- their data set Node n3 = new Node(new LatLon(0, 0)); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n5 = new Node(new LatLon(1, 1)); n5.setOsmId(4, 1); their.addPrimitive(n5); Node n4 = new Node(new LatLon(2, 2)); n4.setOsmId(2, 2); n4.put("key1", "value1"); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.addNode(n3); theirWay.addNode(n5); // insert a node theirWay.addNode(n4); // this one is updated their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(0, visitor.getConflicts().size()); assertEquals(3, merged.getId()); assertEquals(2, merged.getVersion()); assertEquals(3, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(4, merged.getNode(1).getId()); assertEquals(2, merged.getNode(2).getId()); assertEquals("value1", merged.getNode(2).get("key1")); assertSame(merged.getNode(0), n1); assertNotSame(merged.getNode(1), n5); // must be clone of the original node in their assertSame(merged.getNode(2), n2); assertFalse( merged .isModified()); // the target wasn't modified before merging, it mustn't be after // merging }
/** * their way has a higher version and different tags. the nodes are the same. My way is not * modified. Merge is possible. No conflict. * * <p>=> merge it onto my way. */ @Test public void waySimple_IdenicalNodesDifferentTags() { // -- the target dataset Node n1 = new Node(); n1.setCoor(new LatLon(0, 0)); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(); n2.setCoor(new LatLon(0, 0)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.put("key1", "value1"); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // -- the source data set Node n3 = new Node(new LatLon(0, 0)); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n4 = new Node(new LatLon(1, 1)); n4.setOsmId(2, 1); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.put("key1", "value1"); theirWay.put("key2", "value2"); theirWay.addNode(n3); theirWay.addNode(n4); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(0, visitor.getConflicts().size()); assertEquals("value1", merged.get("key1")); assertEquals("value2", merged.get("key2")); assertEquals(3, merged.getId()); assertEquals(2, merged.getVersion()); assertEquals(2, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(2, merged.getNode(1).getId()); assertSame(merged, myWay); assertSame(merged.getDataSet(), my); Node mergedNode = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertSame(mergedNode, n1); mergedNode = (Node) my.getPrimitiveById(2, OsmPrimitiveType.NODE); assertSame(mergedNode, n2); assertFalse(merged.isModified()); }
private void processElement(SVGElement el, AffineTransform transform) throws IOException { if (el instanceof Group) { AffineTransform oldTransform = transform; AffineTransform xform = ((Group) el).getXForm(); if (transform == null) { transform = xform; } else if (xform != null) { transform = new AffineTransform(transform); transform.concatenate(xform); } for (Object child : ((Group) el).getChildren(null)) { processElement((SVGElement) child, transform); } transform = oldTransform; } else if (el instanceof ShapeElement) { Shape shape = ((ShapeElement) el).getShape(); if (transform != null) { shape = transform.createTransformedShape(shape); } PathIterator it = shape.getPathIterator(null); while (!it.isDone()) { double[] coords = new double[6]; switch (it.currentSegment(coords)) { case PathIterator.SEG_MOVETO: currentway = new Way(); ways.add(currentway); appendNode(coords[0], coords[1]); break; case PathIterator.SEG_LINETO: appendNode(coords[0], coords[1]); break; case PathIterator.SEG_CLOSE: if (currentway.firstNode().getCoor().equalsEpsilon(nodes.getLast().getCoor())) { currentway.removeNode(nodes.removeLast()); } currentway.addNode(currentway.firstNode()); break; case PathIterator.SEG_QUADTO: double lastx = lastX; double lasty = lastY; for (int i = 1; i < Settings.getCurveSteps(); i++) { appendNode( interpolate_quad( lastx, lasty, coords[0], coords[1], coords[2], coords[3], i / Settings.getCurveSteps())); } appendNode(coords[2], coords[3]); break; case PathIterator.SEG_CUBICTO: lastx = lastX; lasty = lastY; for (int i = 1; i < Settings.getCurveSteps(); i++) { appendNode( interpolate_cubic( lastx, lasty, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], i / Settings.getCurveSteps())); } appendNode(coords[4], coords[5]); break; } it.next(); } } }