void addCoordinates(Node n) {
   if (n.getCoor() != null) {
     add(
         tr("Coordinates: "),
         Double.toString(n.getCoor().lat()),
         ", ",
         Double.toString(n.getCoor().lon()));
     add(
         tr("Coordinates (projected): "),
         Double.toString(n.getEastNorth().east()),
         ", ",
         Double.toString(n.getEastNorth().north()));
   }
 }
Example #2
0
  private static Area getArea(List<Node> polygon) {
    Path2D path = new Path2D.Double();

    boolean begin = true;
    for (Node n : polygon) {
      if (begin) {
        path.moveTo(n.getEastNorth().getX(), n.getEastNorth().getY());
        begin = false;
      } else {
        path.lineTo(n.getEastNorth().getX(), n.getEastNorth().getY());
      }
    }
    path.closePath();

    return new Area(path);
  }
Example #3
0
 MyWaySegment(Way w, Node n1, Node n2) {
   this.w = w;
   String railway = w.get("railway");
   String highway = w.get("highway");
   this.isAbandoned = "abandoned".equals(railway) || w.isKeyTrue("disused");
   this.highway = (highway != null || railway != null) && !isAbandoned;
   this.isBoundary = !this.highway && "administrative".equals(w.get("boundary"));
   line =
       new Line2D.Double(
           n1.getEastNorth().east(),
           n1.getEastNorth().north(),
           n2.getEastNorth().east(),
           n2.getEastNorth().north());
   len = line.getP1().distance(line.getP2());
   this.n1 = n1;
   this.n2 = n2;
 }
Example #4
0
 public boolean nearby(Node n, double dist) {
   if (w == null) {
     Main.debug("way null");
     return false;
   }
   if (w.containsNode(n)) return false;
   if (n.isKeyTrue("noexit")) return false;
   EastNorth coord = n.getEastNorth();
   if (coord == null) return false;
   Point2D p = new Point2D.Double(coord.east(), coord.north());
   if (line.getP1().distance(p) > len + dist) return false;
   if (line.getP2().distance(p) > len + dist) return false;
   return line.ptSegDist(p) < dist;
 }
Example #5
0
  /**
   * Returns area of a closed way in square meters. (approximate(?), but should be OK for small
   * areas)
   *
   * <p>Relies on the current projection: Works correctly, when one unit in projected coordinates
   * corresponds to one meter. This is true for most projections, but not for WGS84 and Mercator
   * (EPSG:3857).
   *
   * @param way Way to measure, should be closed (first node is the same as last node)
   * @return area of the closed way.
   */
  public static double closedWayArea(Way way) {

    // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
    double area = 0;
    Node lastN = null;
    for (Node n : way.getNodes()) {
      if (lastN != null) {
        n.getEastNorth().getX();

        area += (calcX(n) * calcY(lastN)) - (calcY(n) * calcX(lastN));
      }
      lastN = n;
    }
    return Math.abs(area / 2);
  }
Example #6
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 #7
0
  /**
   * 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. --&gt; 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 --&gt; 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 --&gt; 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();
  }
Example #8
0
 /**
  * Create a MoveCommand to move a node to this PolarCoor.
  *
  * @param n Node to move
  * @return new MoveCommand
  */
 public MoveCommand createMoveCommand(Node n) {
   EastNorth en = toEastNorth();
   return new MoveCommand(
       n, en.east() - n.getEastNorth().east(), en.north() - n.getEastNorth().north());
 }
  /**
   * Outline: 1. Find direction of all segments - direction = 0..3 (right,up,left,down) - right is
   * not really right, you may have to turn your screen 2. Find average heading of all segments -
   * heading = angle of a vector in polar coordinates - sum up horizontal segments (those with
   * direction 0 or 2) - sum up vertical segments - turn the vertical sum by 90 degrees and add it
   * to the horizontal sum - get the average heading from this total sum 3. Rotate all nodes by the
   * average heading so that right is really right and all segments are approximately NS or EW. 4.
   * If nodes are connected by a horizontal segment: Replace their y-Coordinate by the mean value of
   * their y-Coordinates. - The same for vertical segments. 5. Rotate back.
   */
  private static Collection<Command> orthogonalize(
      List<WayData> wayDataList, List<Node> headingNodes) throws InvalidUserInputException {
    // find average heading
    double headingAll;
    try {
      if (headingNodes.isEmpty()) {
        // find directions of the segments and make them consistent between different ways
        wayDataList.get(0).calcDirections(Direction.RIGHT);
        double refHeading = wayDataList.get(0).heading;
        EastNorth totSum = new EastNorth(0., 0.);
        for (WayData w : wayDataList) {
          w.calcDirections(Direction.RIGHT);
          int directionOffset = angleToDirectionChange(w.heading - refHeading, TOLERANCE2);
          w.calcDirections(Direction.RIGHT.changeBy(directionOffset));
          if (angleToDirectionChange(refHeading - w.heading, TOLERANCE2) != 0)
            throw new RuntimeException();
          totSum = EN.sum(totSum, w.segSum);
        }
        headingAll = EN.polar(new EastNorth(0., 0.), totSum);
      } else {
        headingAll =
            EN.polar(headingNodes.get(0).getEastNorth(), headingNodes.get(1).getEastNorth());
        for (WayData w : wayDataList) {
          w.calcDirections(Direction.RIGHT);
          int directionOffset = angleToDirectionChange(w.heading - headingAll, TOLERANCE2);
          w.calcDirections(Direction.RIGHT.changeBy(directionOffset));
        }
      }
    } catch (RejectedAngleException ex) {
      throw new InvalidUserInputException(
          tr(
              "<html>Please make sure all selected ways head in a similar direction<br>"
                  + "or orthogonalize them one by one.</html>"),
          ex);
    }

    // put the nodes of all ways in a set
    final Set<Node> allNodes = new HashSet<>();
    for (WayData w : wayDataList) {
      for (Node n : w.way.getNodes()) {
        allNodes.add(n);
      }
    }

    // the new x and y value for each node
    final Map<Node, Double> nX = new HashMap<>();
    final Map<Node, Double> nY = new HashMap<>();

    // calculate the centroid of all nodes
    // it is used as rotation center
    EastNorth pivot = new EastNorth(0., 0.);
    for (Node n : allNodes) {
      pivot = EN.sum(pivot, n.getEastNorth());
    }
    pivot = new EastNorth(pivot.east() / allNodes.size(), pivot.north() / allNodes.size());

    // rotate
    for (Node n : allNodes) {
      EastNorth tmp = EN.rotateCC(pivot, n.getEastNorth(), -headingAll);
      nX.put(n, tmp.east());
      nY.put(n, tmp.north());
    }

    // orthogonalize
    final Direction[] HORIZONTAL = {Direction.RIGHT, Direction.LEFT};
    final Direction[] VERTICAL = {Direction.UP, Direction.DOWN};
    final Direction[][] ORIENTATIONS = {HORIZONTAL, VERTICAL};
    for (Direction[] orientation : ORIENTATIONS) {
      final Set<Node> s = new HashSet<>(allNodes);
      int s_size = s.size();
      for (int dummy = 0; dummy < s_size; ++dummy) {
        if (s.isEmpty()) {
          break;
        }
        final Node dummy_n = s.iterator().next(); // pick arbitrary element of s

        final Set<Node> cs =
            new HashSet<>(); // will contain each node that can be reached from dummy_n
        cs.add(dummy_n); // walking only on horizontal / vertical segments

        boolean somethingHappened = true;
        while (somethingHappened) {
          somethingHappened = false;
          for (WayData w : wayDataList) {
            for (int i = 0; i < w.nSeg; ++i) {
              Node n1 = w.way.getNodes().get(i);
              Node n2 = w.way.getNodes().get(i + 1);
              if (Arrays.asList(orientation).contains(w.segDirections[i])) {
                if (cs.contains(n1) && !cs.contains(n2)) {
                  cs.add(n2);
                  somethingHappened = true;
                }
                if (cs.contains(n2) && !cs.contains(n1)) {
                  cs.add(n1);
                  somethingHappened = true;
                }
              }
            }
          }
        }

        final Map<Node, Double> nC = (orientation == HORIZONTAL) ? nY : nX;

        double average = 0;
        for (Node n : cs) {
          s.remove(n);
          average += nC.get(n).doubleValue();
        }
        average = average / cs.size();

        // if one of the nodes is a heading node, forget about the average and use its value
        for (Node fn : headingNodes) {
          if (cs.contains(fn)) {
            average = nC.get(fn);
          }
        }

        // At this point, the two heading nodes (if any) are horizontally aligned, i.e. they
        // have the same y coordinate. So in general we shouldn't find them in a vertical string
        // of segments. This can still happen in some pathological cases (see #7889). To avoid
        // both heading nodes collapsing to one point, we simply skip this segment string and
        // don't touch the node coordinates.
        if (orientation == VERTICAL && headingNodes.size() == 2 && cs.containsAll(headingNodes)) {
          continue;
        }

        for (Node n : cs) {
          nC.put(n, average);
        }
      }
      if (!s.isEmpty()) throw new RuntimeException();
    }

    // rotate back and log the change
    final Collection<Command> commands = new LinkedList<>();
    for (Node n : allNodes) {
      EastNorth tmp = new EastNorth(nX.get(n), nY.get(n));
      tmp = EN.rotateCC(pivot, tmp, headingAll);
      final double dx = tmp.east() - n.getEastNorth().east();
      final double dy = tmp.north() - n.getEastNorth().north();
      if (headingNodes.contains(n)) { // The heading nodes should not have changed
        final double EPSILON = 1E-6;
        if (Math.abs(dx) > Math.abs(EPSILON * tmp.east())
            || Math.abs(dy) > Math.abs(EPSILON * tmp.east())) throw new AssertionError();
      } else {
        OrthogonalizeAction.rememberMovements.put(n, new EastNorth(dx, dy));
        commands.add(new MoveCommand(n, dx, dy));
      }
    }
    return commands;
  }
Example #10
0
  /**
   * Will find all intersection and add nodes there for list of given ways. Handles
   * self-intersections too. And makes commands to add the intersection points to ways.
   *
   * <p>Prerequisite: no two nodes have the same coordinates.
   *
   * @param ways a list of ways to test
   * @param test if false, do not build list of Commands, just return nodes
   * @param cmds list of commands, typically empty when handed to this method. Will be filled with
   *     commands that add intersection nodes to the ways.
   * @return list of new nodes
   */
  public static Set<Node> addIntersections(List<Way> ways, boolean test, List<Command> cmds) {

    // stupid java, cannot instantiate array of generic classes..
    @SuppressWarnings("unchecked")
    ArrayList<Node>[] newNodes = new ArrayList[ways.size()];
    BBox[] wayBounds = new BBox[ways.size()];
    boolean[] changedWays = new boolean[ways.size()];

    Set<Node> intersectionNodes = new LinkedHashSet<Node>();

    // copy node arrays for local usage.
    for (int pos = 0; pos < ways.size(); pos++) {
      newNodes[pos] = new ArrayList<Node>(ways.get(pos).getNodes());
      wayBounds[pos] = getNodesBounds(newNodes[pos]);
      changedWays[pos] = false;
    }

    // iterate over all way pairs and introduce the intersections
    Comparator<Node> coordsComparator = new NodePositionComparator();

    WayLoop:
    for (int seg1Way = 0; seg1Way < ways.size(); seg1Way++) {
      for (int seg2Way = seg1Way; seg2Way < ways.size(); seg2Way++) {

        // do not waste time on bounds that do not intersect
        if (!wayBounds[seg1Way].intersects(wayBounds[seg2Way])) {
          continue;
        }

        ArrayList<Node> way1Nodes = newNodes[seg1Way];
        ArrayList<Node> way2Nodes = newNodes[seg2Way];

        // iterate over primary segmemt
        for (int seg1Pos = 0; seg1Pos + 1 < way1Nodes.size(); seg1Pos++) {

          // iterate over secondary segment
          int seg2Start = seg1Way != seg2Way ? 0 : seg1Pos + 2; // skip the adjacent segment

          for (int seg2Pos = seg2Start; seg2Pos + 1 < way2Nodes.size(); seg2Pos++) {

            // need to get them again every time, because other segments may be changed
            Node seg1Node1 = way1Nodes.get(seg1Pos);
            Node seg1Node2 = way1Nodes.get(seg1Pos + 1);
            Node seg2Node1 = way2Nodes.get(seg2Pos);
            Node seg2Node2 = way2Nodes.get(seg2Pos + 1);

            int commonCount = 0;
            // test if we have common nodes to add.
            if (seg1Node1 == seg2Node1 || seg1Node1 == seg2Node2) {
              commonCount++;

              if (seg1Way == seg2Way && seg1Pos == 0 && seg2Pos == way2Nodes.size() - 2) {
                // do not add - this is first and last segment of the same way.
              } else {
                intersectionNodes.add(seg1Node1);
              }
            }

            if (seg1Node2 == seg2Node1 || seg1Node2 == seg2Node2) {
              commonCount++;

              intersectionNodes.add(seg1Node2);
            }

            // no common nodes - find intersection
            if (commonCount == 0) {
              EastNorth intersection =
                  getSegmentSegmentIntersection(
                      seg1Node1.getEastNorth(), seg1Node2.getEastNorth(),
                      seg2Node1.getEastNorth(), seg2Node2.getEastNorth());

              if (intersection != null) {
                if (test) {
                  intersectionNodes.add(seg2Node1);
                  return intersectionNodes;
                }

                Node newNode = new Node(Main.getProjection().eastNorth2latlon(intersection));
                Node intNode = newNode;
                boolean insertInSeg1 = false;
                boolean insertInSeg2 = false;

                // find if the intersection point is at end point of one of the segments, if so use
                // that point

                // segment 1
                if (coordsComparator.compare(newNode, seg1Node1) == 0) {
                  intNode = seg1Node1;
                } else if (coordsComparator.compare(newNode, seg1Node2) == 0) {
                  intNode = seg1Node2;
                } else {
                  insertInSeg1 = true;
                }

                // segment 2
                if (coordsComparator.compare(newNode, seg2Node1) == 0) {
                  intNode = seg2Node1;
                } else if (coordsComparator.compare(newNode, seg2Node2) == 0) {
                  intNode = seg2Node2;
                } else {
                  insertInSeg2 = true;
                }

                if (insertInSeg1) {
                  way1Nodes.add(seg1Pos + 1, intNode);
                  changedWays[seg1Way] = true;

                  // fix seg2 position, as indexes have changed, seg2Pos is always bigger than
                  // seg1Pos on the same segment.
                  if (seg2Way == seg1Way) {
                    seg2Pos++;
                  }
                }

                if (insertInSeg2) {
                  way2Nodes.add(seg2Pos + 1, intNode);
                  changedWays[seg2Way] = true;

                  // Do not need to compare again to already split segment
                  seg2Pos++;
                }

                intersectionNodes.add(intNode);

                if (intNode == newNode) {
                  cmds.add(new AddCommand(intNode));
                }
              }
            } else if (test && intersectionNodes.size() > 0) return intersectionNodes;
          }
        }
      }
    }

    for (int pos = 0; pos < ways.size(); pos++) {
      if (changedWays[pos] == false) {
        continue;
      }

      Way way = ways.get(pos);
      Way newWay = new Way(way);
      newWay.setNodes(newNodes[pos]);

      cmds.add(new ChangeCommand(way, newWay));
    }

    return intersectionNodes;
  }
Example #11
0
  /**
   * Tests if point is inside a polygon. The polygon can be self-intersecting. In such case the
   * contains function works in xor-like manner.
   *
   * @param polygonNodes list of nodes from polygon path.
   * @param point the point to test
   * @return true if the point is inside polygon.
   */
  public static boolean nodeInsidePolygon(Node point, List<Node> polygonNodes) {
    if (polygonNodes.size() < 2) return false;

    boolean inside = false;
    Node p1, p2;

    // iterate each side of the polygon, start with the last segment
    Node oldPoint = polygonNodes.get(polygonNodes.size() - 1);

    for (Node newPoint : polygonNodes) {
      // skip duplicate points
      if (newPoint.equals(oldPoint)) {
        continue;
      }

      // order points so p1.lat <= p2.lat;
      if (newPoint.getEastNorth().getY() > oldPoint.getEastNorth().getY()) {
        p1 = oldPoint;
        p2 = newPoint;
      } else {
        p1 = newPoint;
        p2 = oldPoint;
      }

      // test if the line is crossed and if so invert the inside flag.
      if ((newPoint.getEastNorth().getY() < point.getEastNorth().getY())
              == (point.getEastNorth().getY() <= oldPoint.getEastNorth().getY())
          && (point.getEastNorth().getX() - p1.getEastNorth().getX())
                  * (p2.getEastNorth().getY() - p1.getEastNorth().getY())
              < (p2.getEastNorth().getX() - p1.getEastNorth().getX())
                  * (point.getEastNorth().getY() - p1.getEastNorth().getY())) {
        inside = !inside;
      }

      oldPoint = newPoint;
    }

    return inside;
  }
Example #12
0
 /**
  * This method tests if secondNode is clockwise to first node.
  *
  * @param commonNode starting point for both vectors
  * @param firstNode first vector end node
  * @param secondNode second vector end node
  * @return true if first vector is clockwise before second vector.
  */
 public static boolean angleIsClockwise(Node commonNode, Node firstNode, Node secondNode) {
   return angleIsClockwise(
       commonNode.getEastNorth(), firstNode.getEastNorth(), secondNode.getEastNorth());
 }
Example #13
0
 /**
  * Constructs a new {@code OldNodeState} for the given node.
  *
  * @param node The node whose state has to be remembered
  */
 public OldNodeState(Node node) {
   latlon = node.getCoor();
   eastNorth = node.getEastNorth();
   modified = node.isModified();
 }
Example #14
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;
  }
Example #15
0
 /**
  * Returns the coordinates of all cells in a grid that a line between 2 nodes intersects with.
  *
  * @param n1 The first node.
  * @param n2 The second node.
  * @param gridDetail The detail of the grid. Bigger values give smaller cells, but a bigger number
  *     of them.
  * @return A list with the coordinates of all cells
  * @throws IllegalArgumentException if n1 or n2 is {@code null} or without coordinates
  */
 public static List<Point2D> getSegmentCells(Node n1, Node n2, double gridDetail)
     throws IllegalArgumentException {
   CheckParameterUtil.ensureParameterNotNull(n1, "n1");
   CheckParameterUtil.ensureParameterNotNull(n1, "n2");
   return getSegmentCells(n1.getEastNorth(), n2.getEastNorth(), gridDetail);
 }