Example #1
0
 /**
  * Determines which way chunk should reuse the old id and its history. Selects the one with the
  * highest node count.
  *
  * @param wayChunks the way chunks
  * @return the way to keep
  */
 protected static Way determineWayToKeep(Iterable<Way> wayChunks) {
   Way wayToKeep = null;
   for (Way i : wayChunks) {
     if (wayToKeep == null || i.getNodesCount() > wayToKeep.getNodesCount()) {
       wayToKeep = i;
     }
   }
   return wayToKeep;
 }
Example #2
0
  @Override
  public void visit(Way w) {
    if (!w.isUsable()) return;

    Map<String, String> tags = w.getKeys();
    if (!tags.isEmpty()) {
      String highway = tags.get("highway");
      if (highway != null && NAMED_WAYS.contains(highway)) {
        if (!tags.containsKey("name") && !tags.containsKey("ref")) {
          boolean isRoundabout = false;
          boolean hasName = false;
          for (String key : w.keySet()) {
            hasName = key.startsWith("name:") || key.endsWith("_name") || key.endsWith("_ref");
            if (hasName) {
              break;
            }
            if (key.equals("junction")) {
              isRoundabout = w.get("junction").equals("roundabout");
              break;
            }
          }

          if (!hasName && !isRoundabout) {
            errors.add(new TestError(this, Severity.WARNING, tr("Unnamed ways"), UNNAMED_WAY, w));
          } else if (isRoundabout) {
            errors.add(
                new TestError(this, Severity.WARNING, tr("Unnamed junction"), UNNAMED_JUNCTION, w));
          }
        }
      }
    }

    if (!w.isTagged() && !multipolygonways.contains(w)) {
      if (w.hasKeys()) {
        errors.add(
            new TestError(
                this, Severity.WARNING, tr("Untagged ways (commented)"), COMMENTED_WAY, w));
      } else {
        errors.add(new TestError(this, Severity.WARNING, tr("Untagged ways"), UNTAGGED_WAY, w));
      }
    }

    if (w.getNodesCount() == 0) {
      errors.add(new TestError(this, Severity.ERROR, tr("Empty ways"), EMPTY_WAY, w));
    } else if (w.getNodesCount() == 1) {
      errors.add(new TestError(this, Severity.ERROR, tr("One node ways"), ONE_NODE_WAY, w));
    }
  }
 void addWayNodes(Way w) {
   add(tr("{0} Nodes: ", w.getNodesCount()));
   for (Node n : w.getNodes()) {
     s.append(INDENT).append(INDENT);
     addNameAndId(n);
     s.append(NL);
   }
 }
 @Test
 public void testMultiGet10Ways() throws OsmTransferException {
   MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader();
   ArrayList<Way> ways = new ArrayList<>(ds.getWays());
   for (int i = 0; i < 10; i++) {
     reader.append(ways.get(i));
   }
   DataSet out = reader.parseOsm(NullProgressMonitor.INSTANCE);
   assertEquals(10, out.getWays().size());
   for (Way w1 : out.getWays()) {
     Way w2 = (Way) ds.getPrimitiveById(w1);
     assertNotNull(w2);
     assertEquals(w2.getNodesCount(), w1.getNodesCount());
     assertEquals(w2.get("name"), w1.get("name"));
   }
   assertTrue(reader.getMissingPrimitives().isEmpty());
 }
  /**
   * 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"));
  }
Example #6
0
 @Override
 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
   if (!(other instanceof Way)) return false;
   if (!super.hasEqualSemanticAttributes(other)) return false;
   Way w = (Way) other;
   if (getNodesCount() != w.getNodesCount()) return false;
   for (int i = 0; i < getNodesCount(); i++) {
     if (!getNode(i).hasEqualSemanticAttributes(w.getNode(i))) return false;
   }
   return true;
 }
Example #7
0
  /**
   * Return a list of all objects in the selection, respecting the different modifier.
   *
   * @param alt Whether the alt key was pressed, which means select all objects that are touched,
   *     instead those which are completely covered.
   * @return The collection of selected objects.
   */
  public Collection<OsmPrimitive> getSelectedObjects(boolean alt) {

    Collection<OsmPrimitive> selection = new LinkedList<>();

    // whether user only clicked, not dragged.
    boolean clicked = false;
    Rectangle bounding = lasso.getBounds();
    if (bounding.height <= 2 && bounding.width <= 2) {
      clicked = true;
    }

    if (clicked) {
      Point center = new Point(lasso.xpoints[0], lasso.ypoints[0]);
      OsmPrimitive osm = nc.getNearestNodeOrWay(center, OsmPrimitive.isSelectablePredicate, false);
      if (osm != null) {
        selection.add(osm);
      }
    } else {
      // nodes
      for (Node n : nc.getCurrentDataSet().getNodes()) {
        if (n.isSelectable() && lasso.contains(nc.getPoint2D(n))) {
          selection.add(n);
        }
      }

      // ways
      for (Way w : nc.getCurrentDataSet().getWays()) {
        if (!w.isSelectable() || w.getNodesCount() == 0) {
          continue;
        }
        if (alt) {
          for (Node n : w.getNodes()) {
            if (!n.isIncomplete() && lasso.contains(nc.getPoint2D(n))) {
              selection.add(w);
              break;
            }
          }
        } else {
          boolean allIn = true;
          for (Node n : w.getNodes()) {
            if (!n.isIncomplete() && !lasso.contains(nc.getPoint(n))) {
              allIn = false;
              break;
            }
          }
          if (allIn) {
            selection.add(w);
          }
        }
      }
    }
    return selection;
  }
Example #8
0
      @Override
      public void visit(Way w) {
        /*
         * If e.parent is already set to the first matching referrer. We skip any following
         * referrer injected into the visitor.
         */
        if (e.parent != null) return;

        if (!left.matches(e.withPrimitive(w))) return;
        for (int i = 0; i < w.getNodesCount(); i++) {
          Node n = w.getNode(i);
          if (n.equals(e.osm)) {
            if (link.matches(e.withParentAndIndexAndLinkContext(w, i, w.getNodesCount()))) {
              e.parent = w;
              e.index = i;
              e.count = w.getNodesCount();
              return;
            }
          }
        }
      }
  /**
   * 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());
  }
  /**
   * Merge an incomplete way with two incomplete nodes into a dataset where the way already exists
   * as complete way.
   *
   * <p>Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456 after a "Update
   * selection " of this way
   */
  @Test
  public void incompleteWayOntoCompleteWay() {

    // an incomplete node
    Node n1 = new Node(1);
    their.addPrimitive(n1);

    // another incomplete node
    Node n2 = new Node(2);
    their.addPrimitive(n2);

    // an incomplete way with two incomplete nodes
    Way w3 = new Way(3);
    w3.setNodes(Arrays.asList(n1, n2));
    their.addPrimitive(w3);

    Node n4 = new Node(new LatLon(0, 0));
    n4.setOsmId(1, 1);
    my.addPrimitive(n4);

    Node n5 = new Node(new LatLon(1, 1));
    n5.setOsmId(2, 1);
    my.addPrimitive(n5);

    Way w6 = new Way(3, 1);
    w6.setNodes(Arrays.asList(n4, n5));
    my.addPrimitive(w6);

    DataSetMerger visitor = new DataSetMerger(my, their);
    visitor.merge();

    assertEquals(0, visitor.getConflicts().size());

    OsmPrimitive p = my.getPrimitiveById(1, OsmPrimitiveType.NODE);
    assertNotNull(p);
    assertFalse(p.isIncomplete());
    p = my.getPrimitiveById(2, OsmPrimitiveType.NODE);
    assertNotNull(p);
    assertFalse(p.isIncomplete());
    p = my.getPrimitiveById(3, OsmPrimitiveType.WAY);
    assertNotNull(p);
    assertFalse(p.isIncomplete());

    Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
    assertNotNull(w);
    assertFalse(p.isIncomplete());
    assertEquals(2, w.getNodesCount());
    assertFalse(w.getNode(0).isIncomplete());
    assertFalse(w.getNode(1).isIncomplete());
  }
Example #11
0
  /**
   * Determines whether a way is oriented clockwise.
   *
   * <p>Internals: Assuming a closed non-looping way, compute twice the area of the polygon using
   * the formula {@code 2 * area = sum (X[n] * Y[n+1] - X[n+1] * Y[n])}. If the area is negative the
   * way is ordered in a clockwise direction.
   *
   * <p>See http://paulbourke.net/geometry/polyarea/
   *
   * @param w the way to be checked.
   * @return true if and only if way is oriented clockwise.
   * @throws IllegalArgumentException if way is not closed (see {@link Way#isClosed}).
   */
  public static boolean isClockwise(Way w) {
    if (!w.isClosed()) {
      throw new IllegalArgumentException("Way must be closed to check orientation.");
    }

    double area2 = 0.;
    int nodesCount = w.getNodesCount();

    for (int node = 1; node <= /*sic! consider last-first as well*/ nodesCount; node++) {
      LatLon coorPrev = w.getNode(node - 1).getCoor();
      LatLon coorCurr = w.getNode(node % nodesCount).getCoor();
      area2 += coorPrev.lon() * coorCurr.lat();
      area2 -= coorCurr.lon() * coorPrev.lat();
    }
    return area2 < 0;
  }
  /**
   * 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());
  }
Example #13
0
  public void visit(Way w) {
    if (w.isIncomplete() || w.getNodesCount() < 2) return;
    c.setStrokeStyle(w.isSelected() ? "#ff0000" : "#000000");
    c.beginPath();

    Iterator<Node> it = w.getNodes().iterator();
    if (it.hasNext()) {
      Point lastP = nc.getPoint(it.next());
      c.moveTo(lastP.x, lastP.y);

      for (int orderNumber = 1; it.hasNext(); orderNumber++) {
        Point p = nc.getPoint(it.next());
        c.lineTo(p.x, p.y);
        lastP = p;
      }
      c.stroke();
    }
  }
Example #14
0
 @Override
 public void visit(Way w) {
   if (w.getNodesCount() > 0 // do not consider empty ways
       && !w.hasKey(
           "addr:interpolation") // ignore addr:interpolation ways as they are not physical
                                 // features and most of
       // the time very near the associated highway, which is perfectly normal, see #9332
       && !w.hasTag("highway", "platform")
       && !w.hasTag("railway", "platform") // similarly for public transport platforms
   ) {
     ways.addAll(getWaySegments(w));
     QuadBuckets<Node> set = endnodes;
     if (w.hasKey("highway") || w.hasKey("railway")) {
       set = endnodesHighway;
     }
     addNode(w.firstNode(), set);
     addNode(w.lastNode(), set);
   }
 }
  @Test
  public void testBackrefrenceForNode_Full() throws OsmTransferException {
    Node n = lookupNode(ds, 0);
    assertNotNull(n);
    Way w = lookupWay(ds, 0);
    assertNotNull(w);

    OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(n);
    reader.setReadFull(true);
    DataSet referers = reader.parseOsm(NullProgressMonitor.INSTANCE);
    assertEquals(10, referers.getNodes().size());
    assertEquals(1, referers.getWays().size());
    assertEquals(0, referers.getRelations().size());
    for (Way way : referers.getWays()) {
      assertEquals(w.getId(), way.getId());
      assertEquals(false, way.isIncomplete());
      assertEquals(10, w.getNodesCount());
    }
  }
Example #16
0
  List<MyWaySegment> getWaySegments(Way w) {
    List<MyWaySegment> ret = new ArrayList<>();
    if (!w.isUsable() || w.hasKey("barrier") || w.hasTag("natural", "cliff")) return ret;

    int size = w.getNodesCount();
    if (size < 2) return ret;
    for (int i = 1; i < size; ++i) {
      if (i < size - 1) {
        addNode(w.getNode(i), middlenodes);
      }
      Node a = w.getNode(i - 1);
      Node b = w.getNode(i);
      if (a.isDrawable() && b.isDrawable()) {
        MyWaySegment ws = new MyWaySegment(w, a, b);
        if (ws.isBoundary || ws.isAbandoned) {
          continue;
        }
        ret.add(ws);
      }
    }
    return ret;
  }
  /**
   * Merge an incomplete way with two incomplete nodes into an empty dataset.
   *
   * <p>Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456
   */
  @Test
  public void newIncompleteWay() {

    Node n1 = new Node(1);
    their.addPrimitive(n1);

    Node n2 = new Node(2);
    their.addPrimitive(n2);

    Way w3 = new Way(3);
    w3.setNodes(Arrays.asList(n1, n2));
    their.addPrimitive(w3);
    assertTrue(w3.isIncomplete());

    DataSetMerger visitor = new DataSetMerger(my, their);
    visitor.merge();

    assertEquals(0, visitor.getConflicts().size());

    OsmPrimitive p = my.getPrimitiveById(1, OsmPrimitiveType.NODE);
    assertNotNull(p);
    assertTrue(p.isIncomplete());
    p = my.getPrimitiveById(2, OsmPrimitiveType.NODE);
    assertNotNull(p);
    assertTrue(p.isIncomplete());
    p = my.getPrimitiveById(3, OsmPrimitiveType.WAY);
    assertNotNull(p);
    assertTrue(p.isIncomplete());

    Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
    assertNotNull(w);
    assertTrue(p.isIncomplete());
    assertEquals(2, w.getNodesCount());
    assertTrue(w.getNode(0).isIncomplete());
    assertTrue(w.getNode(1).isIncomplete());
  }
Example #18
0
  /**
   * This method analyzes the way and assigns each part what direction polygon "inside" is.
   *
   * @param parts the split parts of the way
   * @param isInner - if true, reverts the direction (for multipolygon islands)
   * @return list of parts, marked with the inside orientation.
   */
  private List<WayInPolygon> markWayInsideSide(List<Way> parts, boolean isInner) {

    List<WayInPolygon> result = new ArrayList<WayInPolygon>();

    // prepare prev and next maps
    Map<Way, Way> nextWayMap = new HashMap<Way, Way>();
    Map<Way, Way> prevWayMap = new HashMap<Way, Way>();

    for (int pos = 0; pos < parts.size(); pos++) {

      if (!parts.get(pos).lastNode().equals(parts.get((pos + 1) % parts.size()).firstNode()))
        throw new RuntimeException("Way not circular");

      nextWayMap.put(parts.get(pos), parts.get((pos + 1) % parts.size()));
      prevWayMap.put(parts.get(pos), parts.get((pos + parts.size() - 1) % parts.size()));
    }

    // find the node with minimum y - it's guaranteed to be outer. (What about the south pole?)
    Way topWay = null;
    Node topNode = null;
    int topIndex = 0;
    double minY = Double.POSITIVE_INFINITY;

    for (Way way : parts) {
      for (int pos = 0; pos < way.getNodesCount(); pos++) {
        Node node = way.getNode(pos);

        if (node.getEastNorth().getY() < minY) {
          minY = node.getEastNorth().getY();
          topWay = way;
          topNode = node;
          topIndex = pos;
        }
      }
    }

    // get the upper way and it's orientation.

    boolean wayClockwise; // orientation of the top way.

    if (topNode.equals(topWay.firstNode()) || topNode.equals(topWay.lastNode())) {
      Node headNode = null; // the node at junction
      Node prevNode = null; // last node from previous path
      wayClockwise = false;

      // node is in split point - find the outermost way from this point

      headNode = topNode;
      // make a fake node that is downwards from head node (smaller Y). It will be a division point
      // between paths.
      prevNode =
          new Node(
              new EastNorth(headNode.getEastNorth().getX(), headNode.getEastNorth().getY() - 1e5));

      topWay = null;
      wayClockwise = false;
      Node bestWayNextNode = null;

      for (Way way : parts) {
        if (way.firstNode().equals(headNode)) {
          Node nextNode = way.getNode(1);

          if (topWay == null
              || !Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
            // the new way is better
            topWay = way;
            wayClockwise = true;
            bestWayNextNode = nextNode;
          }
        }

        if (way.lastNode().equals(headNode)) {
          // end adjacent to headNode
          Node nextNode = way.getNode(way.getNodesCount() - 2);

          if (topWay == null
              || !Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
            // the new way is better
            topWay = way;
            wayClockwise = false;
            bestWayNextNode = nextNode;
          }
        }
      }
    } else {
      // node is inside way - pick the clockwise going end.
      Node prev = topWay.getNode(topIndex - 1);
      Node next = topWay.getNode(topIndex + 1);

      // there will be no parallel segments in the middle of way, so all fine.
      wayClockwise = Geometry.angleIsClockwise(prev, topNode, next);
    }

    Way curWay = topWay;
    boolean curWayInsideToTheRight = wayClockwise ^ isInner;

    // iterate till full circle is reached
    while (true) {

      // add cur way
      WayInPolygon resultWay = new WayInPolygon(curWay, curWayInsideToTheRight);
      result.add(resultWay);

      // process next way
      Way nextWay = nextWayMap.get(curWay);
      Node prevNode = curWay.getNode(curWay.getNodesCount() - 2);
      Node headNode = curWay.lastNode();
      Node nextNode = nextWay.getNode(1);

      if (nextWay == topWay) {
        // full loop traversed - all done.
        break;
      }

      // find intersecting segments
      // the intersections will look like this:
      //
      //                       ^
      //                       |
      //                       X wayBNode
      //                       |
      //                  wayB |
      //                       |
      //             curWay    |       nextWay
      // ----X----------------->X----------------------X---->
      //    prevNode           ^headNode              nextNode
      //                       |
      //                       |
      //                  wayA |
      //                       |
      //                       X wayANode
      //                       |

      int intersectionCount = 0;

      for (Way wayA : parts) {

        if (wayA == curWay) {
          continue;
        }

        if (wayA.lastNode().equals(headNode)) {

          Way wayB = nextWayMap.get(wayA);

          // test if wayA is opposite wayB relative to curWay and nextWay

          Node wayANode = wayA.getNode(wayA.getNodesCount() - 2);
          Node wayBNode = wayB.getNode(1);

          boolean wayAToTheRight =
              Geometry.isToTheRightSideOfLine(prevNode, headNode, nextNode, wayANode);
          boolean wayBToTheRight =
              Geometry.isToTheRightSideOfLine(prevNode, headNode, nextNode, wayBNode);

          if (wayAToTheRight != wayBToTheRight) {
            intersectionCount++;
          }
        }
      }

      // if odd number of crossings, invert orientation
      if (intersectionCount % 2 != 0) {
        curWayInsideToTheRight = !curWayInsideToTheRight;
      }

      curWay = nextWay;
    }

    return result;
  }
Example #19
0
  /**
   * Try to delete all given primitives.
   *
   * <p>If a node is used by a way, it's removed from that way. If a node or a way is used by a
   * relation, inform the user and do not delete.
   *
   * <p>If this would cause ways with less than 2 nodes to be created, delete these ways instead. If
   * they are part of a relation, inform the user and do not delete.
   *
   * @param layer the {@link OsmDataLayer} in whose context the primitives are deleted
   * @param selection the objects to delete.
   * @param alsoDeleteNodesInWay <code>true</code> if nodes should be deleted as well
   * @param silent set to true if the user should not be bugged with additional questions
   * @return command a command to perform the deletions, or null if there is nothing to delete.
   */
  public static Command delete(
      OsmDataLayer layer,
      Collection<? extends OsmPrimitive> selection,
      boolean alsoDeleteNodesInWay,
      boolean silent) {
    if (selection == null || selection.isEmpty()) return null;

    // Diamond operator does not work with Java 9 here
    Set<OsmPrimitive> primitivesToDelete = new HashSet<OsmPrimitive>(selection);

    Collection<Relation> relationsToDelete =
        Utils.filteredCollection(primitivesToDelete, Relation.class);
    if (!relationsToDelete.isEmpty() && !silent && !confirmRelationDeletion(relationsToDelete))
      return null;

    if (alsoDeleteNodesInWay) {
      // delete untagged nodes only referenced by primitives in primitivesToDelete, too
      Collection<Node> nodesToDelete = computeNodesToDelete(primitivesToDelete);
      primitivesToDelete.addAll(nodesToDelete);
    }

    if (!silent
        && !checkAndConfirmOutlyingDelete(
            primitivesToDelete, Utils.filteredCollection(primitivesToDelete, Way.class)))
      return null;

    Collection<Way> waysToBeChanged =
        new HashSet<>(
            OsmPrimitive.getFilteredSet(OsmPrimitive.getReferrer(primitivesToDelete), Way.class));

    Collection<Command> cmds = new LinkedList<>();
    for (Way w : waysToBeChanged) {
      Way wnew = new Way(w);
      wnew.removeNodes(OsmPrimitive.getFilteredSet(primitivesToDelete, Node.class));
      if (wnew.getNodesCount() < 2) {
        primitivesToDelete.add(w);
      } else {
        cmds.add(new ChangeNodesCommand(w, wnew.getNodes()));
      }
    }

    // get a confirmation that the objects to delete can be removed from their parent relations
    //
    if (!silent) {
      Set<RelationToChildReference> references =
          RelationToChildReference.getRelationToChildReferences(primitivesToDelete);
      Iterator<RelationToChildReference> it = references.iterator();
      while (it.hasNext()) {
        RelationToChildReference ref = it.next();
        if (ref.getParent().isDeleted()) {
          it.remove();
        }
      }
      if (!references.isEmpty()) {
        DeleteFromRelationConfirmationDialog dialog =
            DeleteFromRelationConfirmationDialog.getInstance();
        dialog.getModel().populate(references);
        dialog.setVisible(true);
        if (dialog.isCanceled()) return null;
      }
    }

    // remove the objects from their parent relations
    //
    for (Relation cur :
        OsmPrimitive.getFilteredSet(OsmPrimitive.getReferrer(primitivesToDelete), Relation.class)) {
      Relation rel = new Relation(cur);
      rel.removeMembersFor(primitivesToDelete);
      cmds.add(new ChangeCommand(cur, rel));
    }

    // build the delete command
    //
    if (!primitivesToDelete.isEmpty()) {
      cmds.add(new DeleteCommand(layer, primitivesToDelete));
    }

    return new SequenceCommand(tr("Delete"), cmds);
  }
Example #20
0
    @Override
    public boolean matches(Environment e) {

      if (!right.matches(e)) return false;

      if (ChildOrParentSelectorType.ELEMENT_OF.equals(type)) {

        if (e.osm instanceof Node || e.osm.getDataSet() == null) {
          // nodes cannot contain elements
          return false;
        }

        ContainsFinder containsFinder;
        try {
          // if right selector also matches relations and if matched primitive is a way which is
          // part of a multipolygon,
          // use the multipolygon for further analysis
          if (!(e.osm instanceof Way)
              || (right instanceof OptimizedGeneralSelector
                  && !((OptimizedGeneralSelector) right).matchesBase(OsmPrimitiveType.RELATION))) {
            throw new NoSuchElementException();
          }
          final Collection<Relation> multipolygons =
              Utils.filteredCollection(
                  Utils.filter(e.osm.getReferrers(), Predicates.hasTag("type", "multipolygon")),
                  Relation.class);
          final Relation multipolygon = multipolygons.iterator().next();
          if (multipolygon == null) throw new NoSuchElementException();
          containsFinder =
              new ContainsFinder(new Environment(multipolygon)) {
                @Override
                public boolean isPrimitiveUsable(OsmPrimitive p) {
                  return super.isPrimitiveUsable(p)
                      && !multipolygon.getMemberPrimitives().contains(p);
                }
              };
        } catch (NoSuchElementException ignore) {
          containsFinder = new ContainsFinder(e);
        }
        e.parent = e.osm;

        if (left instanceof OptimizedGeneralSelector) {
          if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
            containsFinder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
          }
          if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
            containsFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
          }
        } else {
          // use slow test
          containsFinder.visit(e.osm.getDataSet().allPrimitives());
        }

        return e.child != null;

      } else if (ChildOrParentSelectorType.CROSSING.equals(type) && e.osm instanceof Way) {
        e.parent = e.osm;
        final CrossingFinder crossingFinder = new CrossingFinder(e);
        if (right instanceof OptimizedGeneralSelector
            && ((OptimizedGeneralSelector) right).matchesBase(OsmPrimitiveType.WAY)) {
          crossingFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
        }
        return e.child != null;
      } else if (ChildOrParentSelectorType.SIBLING.equals(type)) {
        if (e.osm instanceof Node) {
          for (Way w : Utils.filteredCollection(e.osm.getReferrers(true), Way.class)) {
            final int i = w.getNodes().indexOf(e.osm);
            if (i - 1 >= 0) {
              final Node n = w.getNode(i - 1);
              final Environment e2 = e.withPrimitive(n).withParent(w).withChild(e.osm);
              if (left.matches(e2) && link.matches(e2.withLinkContext())) {
                e.child = n;
                e.index = i;
                e.count = w.getNodesCount();
                e.parent = w;
                return true;
              }
            }
          }
        }
      } else if (ChildOrParentSelectorType.CHILD.equals(type)
          && link.conds != null
          && !link.conds.isEmpty()
          && link.conds.get(0) instanceof Condition.OpenEndPseudoClassCondition) {
        if (e.osm instanceof Node) {
          e.osm.visitReferrers(new MultipolygonOpenEndFinder(e));
          return e.parent != null;
        }
      } else if (ChildOrParentSelectorType.CHILD.equals(type)) {
        MatchingReferrerFinder collector = new MatchingReferrerFinder(e);
        e.osm.visitReferrers(collector);
        if (e.parent != null) return true;
      } else if (ChildOrParentSelectorType.PARENT.equals(type)) {
        if (e.osm instanceof Way) {
          List<Node> wayNodes = ((Way) e.osm).getNodes();
          for (int i = 0; i < wayNodes.size(); i++) {
            Node n = wayNodes.get(i);
            if (left.matches(e.withPrimitive(n))) {
              if (link.matches(e.withChildAndIndexAndLinkContext(n, i, wayNodes.size()))) {
                e.child = n;
                e.index = i;
                e.count = wayNodes.size();
                return true;
              }
            }
          }
        } else if (e.osm instanceof Relation) {
          List<RelationMember> members = ((Relation) e.osm).getMembers();
          for (int i = 0; i < members.size(); i++) {
            OsmPrimitive member = members.get(i).getMember();
            if (left.matches(e.withPrimitive(member))) {
              if (link.matches(e.withChildAndIndexAndLinkContext(member, i, members.size()))) {
                e.child = member;
                e.index = i;
                e.count = members.size();
                return true;
              }
            }
          }
        }
      }
      return false;
    }
  /**
   * 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());
  }
  /**
   * 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
  }
Example #23
0
  /**
   * Returns the start and end cells of a way.
   *
   * @param w The way
   * @param cellWays The map with all cells
   * @return A list with all the cells the way starts or ends
   */
  public static List<List<Way>> getWaysInCell(Way w, Map<Point2D, List<Way>> cellWays) {
    if (w.getNodesCount() == 0) return Collections.emptyList();

    Node n1 = w.getNode(0);
    Node n2 = w.getNode(w.getNodesCount() - 1);

    List<List<Way>> cells = new ArrayList<>(2);
    Set<Point2D> cellNodes = new HashSet<>();
    Point2D cell;

    // First, round coordinates
    long x0 = Math.round(n1.getEastNorth().east() * OsmValidator.griddetail);
    long y0 = Math.round(n1.getEastNorth().north() * OsmValidator.griddetail);
    long x1 = Math.round(n2.getEastNorth().east() * OsmValidator.griddetail);
    long y1 = Math.round(n2.getEastNorth().north() * OsmValidator.griddetail);

    // Start of the way
    cell = new Point2D.Double(x0, y0);
    cellNodes.add(cell);
    List<Way> ways = cellWays.get(cell);
    if (ways == null) {
      ways = new ArrayList<>();
      cellWays.put(cell, ways);
    }
    cells.add(ways);

    // End of the way
    cell = new Point2D.Double(x1, y1);
    if (!cellNodes.contains(cell)) {
      cellNodes.add(cell);
      ways = cellWays.get(cell);
      if (ways == null) {
        ways = new ArrayList<>();
        cellWays.put(cell, ways);
      }
      cells.add(ways);
    }

    // Then floor coordinates, in case the way is in the border of the cell.
    x0 = (long) Math.floor(n1.getEastNorth().east() * OsmValidator.griddetail);
    y0 = (long) Math.floor(n1.getEastNorth().north() * OsmValidator.griddetail);
    x1 = (long) Math.floor(n2.getEastNorth().east() * OsmValidator.griddetail);
    y1 = (long) Math.floor(n2.getEastNorth().north() * OsmValidator.griddetail);

    // Start of the way
    cell = new Point2D.Double(x0, y0);
    if (!cellNodes.contains(cell)) {
      cellNodes.add(cell);
      ways = cellWays.get(cell);
      if (ways == null) {
        ways = new ArrayList<>();
        cellWays.put(cell, ways);
      }
      cells.add(ways);
    }

    // End of the way
    cell = new Point2D.Double(x1, y1);
    if (!cellNodes.contains(cell)) {
      cellNodes.add(cell);
      ways = cellWays.get(cell);
      if (ways == null) {
        ways = new ArrayList<>();
        cellWays.put(cell, ways);
      }
      cells.add(ways);
    }
    return cells;
  }