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())); } }
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); }
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; }
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; }
/** * 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); }
/** * 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; }
/** * 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(); }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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()); }
/** * 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(); }
/** * 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; }
/** * 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); }