private WayInPolygon advanceNextWay(boolean rightmost) { Node headNode = !lastWayReverse ? lastWay.way.lastNode() : lastWay.way.firstNode(); Node prevNode = !lastWayReverse ? lastWay.way.getNode(lastWay.way.getNodesCount() - 2) : lastWay.way.getNode(1); // find best next way WayInPolygon bestWay = null; Node bestWayNextNode = null; boolean bestWayReverse = false; for (WayInPolygon way : availableWays) { if (way.way.firstNode().equals(headNode)) { // start adjacent to headNode Node nextNode = way.way.getNode(1); if (nextNode.equals(prevNode)) { // this is the path we came from - ignore it. } else if (bestWay == null || (Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) { // the new way is better bestWay = way; bestWayReverse = false; bestWayNextNode = nextNode; } } if (way.way.lastNode().equals(headNode)) { // end adjacent to headNode Node nextNode = way.way.getNode(way.way.getNodesCount() - 2); if (nextNode.equals(prevNode)) { // this is the path we came from - ignore it. } else if (bestWay == null || (Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) { // the new way is better bestWay = way; bestWayReverse = true; bestWayNextNode = nextNode; } } } lastWay = bestWay; lastWayReverse = bestWayReverse; return lastWay; }
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Segment other = (Segment) obj; if (end == null) { if (other.end != null) return false; } else if (!end.equals(other.end)) return false; if (start == null) { if (other.start != null) return false; } else if (!start.equals(other.start)) return false; if (way == null) { if (other.way != null) return false; } else if (!way.equals(other.way)) return false; return true; }
@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; } } } }
/** * 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 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; }