OsmAnd/OsmAnd-java/src/net/osmand/util/MapAlgorithms.java
2015-07-07 21:28:17 +03:00

333 lines
No EOL
9.4 KiB
Java

package net.osmand.util;
import java.util.Collection;
import java.util.List;
import gnu.trove.list.TLongList;
import net.osmand.data.LatLon;
import net.osmand.osm.edit.Node;
import net.osmand.osm.edit.OsmMapUtils;
public class MapAlgorithms {
public static boolean isClockwiseWay(TLongList c) {
if (c.size() == 0) {
return true;
}
// calculate middle Y
long mask = 0xffffffffL;
long middleY = 0;
for (int i = 0; i < c.size(); i++) {
middleY = middleY + (long)(c.get(i) & mask);
}
middleY = middleY /(long) c.size();
double clockwiseSum = 0;
boolean firstDirectionUp = false;
int previousX = Integer.MIN_VALUE;
int firstX = Integer.MIN_VALUE;
int prevX = (int) (c.get(0) >> 32);
int prevY = (int) (c.get(0) & mask);
for (int i = 1; i < c.size(); i++) {
int x = (int) (c.get(i) >> 32);
int y = (int) (c.get(i) & mask);
int rX = ray_intersect_x(prevX, prevY, x, y, (int) middleY);
if (rX != Integer.MIN_VALUE) {
boolean skipSameSide = (y <= middleY) == (prevY <= middleY);
if (skipSameSide) {
continue;
}
boolean directionUp = prevY >= middleY;
if (firstX == Integer.MIN_VALUE) {
firstDirectionUp = directionUp;
firstX = rX;
} else {
boolean clockwise = (!directionUp) == (previousX < rX);
if (clockwise) {
clockwiseSum += Math.abs(previousX - rX);
} else {
clockwiseSum -= Math.abs(previousX - rX);
}
}
previousX = rX;
}
prevX = x;
prevY = y;
}
if (firstX != Integer.MIN_VALUE) {
boolean clockwise = (!firstDirectionUp) == (previousX < firstX);
if (clockwise) {
clockwiseSum += Math.abs(previousX - firstX);
} else {
clockwiseSum -= Math.abs(previousX - firstX);
}
}
return clockwiseSum >= 0;
}
public static int ray_intersect_x(int prevX, int prevY, int x, int y, int middleY) {
// prev node above line
// x,y node below line
if (prevY > y) {
int tx = x;
int ty = y;
x = prevX;
y = prevY;
prevX = tx;
prevY = ty;
}
if (y == middleY || prevY == middleY) {
middleY -= 1;
}
if (prevY > middleY || y < middleY) {
return Integer.MIN_VALUE;
} else {
if (y == prevY) {
// the node on the boundary !!!
return x;
}
// that tested on all cases (left/right)
double rx = x + ((double) middleY - y) * ((double) x - prevX) / (((double) y - prevY));
return (int) rx;
}
}
private static long combine2Points(int x, int y) {
return (((long) x ) <<32) | ((long)y );
}
/**
* outx,outy are the coordinates out of the box
* inx,iny are the coordinates from the box (NOT IMPORTANT in/out, just one should be in second out)
* @return -1 if there is no instersection or x<<32 | y
*/
public static long calculateIntersection(int inx, int iny, int outx, int outy, int leftX, int rightX, int bottomY, int topY) {
int by = -1;
int bx = -1;
// firstly try to search if the line goes in
if (outy < topY && iny >= topY) {
int tx = (int) (outx + ((double) (inx - outx) * (topY - outy)) / (iny - outy));
if (leftX <= tx && tx <= rightX) {
bx = tx;
by = topY;
return combine2Points(bx, by);
}
}
if (outy > bottomY && iny <= bottomY) {
int tx = (int) (outx + ((double) (inx - outx) * (outy - bottomY)) / (outy - iny));
if (leftX <= tx && tx <= rightX) {
bx = tx;
by = bottomY;
return combine2Points(bx, by);
}
}
if (outx < leftX && inx >= leftX) {
int ty = (int) (outy + ((double) (iny - outy) * (leftX - outx)) / (inx - outx));
if (ty >= topY && ty <= bottomY) {
by = ty;
bx = leftX;
return combine2Points(bx, by);
}
}
if (outx > rightX && inx <= rightX) {
int ty = (int) (outy + ((double) (iny - outy) * (outx - rightX)) / (outx - inx));
if (ty >= topY && ty <= bottomY) {
by = ty;
bx = rightX;
return combine2Points(bx, by);
}
}
// try to search if point goes out
if (outy > topY && iny <= topY) {
int tx = (int) (outx + ((double) (inx - outx) * (topY - outy)) / (iny - outy));
if (leftX <= tx && tx <= rightX) {
bx = tx;
by = topY;
return combine2Points(bx, by);
}
}
if (outy < bottomY && iny >= bottomY) {
int tx = (int) (outx + ((double) (inx - outx) * (outy - bottomY)) / (outy - iny));
if (leftX <= tx && tx <= rightX) {
bx = tx;
by = bottomY;
return combine2Points(bx, by);
}
}
if (outx > leftX && inx <= leftX) {
int ty = (int) (outy + ((double) (iny - outy) * (leftX - outx)) / (inx - outx));
if (ty >= topY && ty <= bottomY) {
by = ty;
bx = leftX;
return combine2Points(bx, by);
}
}
if (outx < rightX && inx >= rightX) {
int ty = (int) (outy + ((double) (iny - outy) * (outx - rightX)) / (outx - inx));
if (ty >= topY && ty <= bottomY) {
by = ty;
bx = rightX;
return combine2Points(bx, by);
}
}
if (outx == rightX || outx == leftX || outy == topY || outy == bottomY) {
bx = outx;
by = outy;
//return (((long) bx) << 32) | ((long) by);
}
return -1L;
}
/**
* return true if the line segment [a,b] intersects [c,d]
* @param a point 1
* @param b point 2
* @param c point 3
* @param d point 4
* @return true if the line segment [a,b] intersects [c,d]
*/
public static boolean linesIntersect(LatLon a, LatLon b, LatLon c, LatLon d){
return linesIntersect(
a.getLatitude(), a.getLongitude(),
b.getLatitude(), b.getLongitude(),
c.getLatitude(), c.getLongitude(),
d.getLatitude(), d.getLongitude());
}
/**
* Return true if two line segments intersect inside the segment
*
* source: http://www.java-gaming.org/index.php?topic=22590.0
* @param x1 line 1 point 1 latitude
* @param y1 line 1 point 1 longitude
* @param x2 line 1 point 2 latitude
* @param y2 line 1 point 2 longitude
* @param x3 line 2 point 1 latitude
* @param y3 line 2 point 1 longitude
* @param x4 line 2 point 2 latitude
* @param y4 line 2 point 2 longitude
* @return
*/
public static boolean linesIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){
// Return false if either of the lines have zero length
if (x1 == x2 && y1 == y2 ||
x3 == x4 && y3 == y4){
return false;
}
// Fastest method, based on Franklin Antonio's "Faster Line Segment Intersection" topic "in Graphics Gems III" book (http://www.graphicsgems.org/)
double ax = x2-x1;
double ay = y2-y1;
double bx = x3-x4;
double by = y3-y4;
double cx = x1-x3;
double cy = y1-y3;
double alphaNumerator = by*cx - bx*cy;
double commonDenominator = ay*bx - ax*by;
if (commonDenominator > 0){
if (alphaNumerator < 0 || alphaNumerator > commonDenominator){
return false;
}
}else if (commonDenominator < 0){
if (alphaNumerator > 0 || alphaNumerator < commonDenominator){
return false;
}
}
double betaNumerator = ax*cy - ay*cx;
if (commonDenominator > 0){
if (betaNumerator < 0 || betaNumerator > commonDenominator){
return false;
}
}else if (commonDenominator < 0){
if (betaNumerator > 0 || betaNumerator < commonDenominator){
return false;
}
}
if (commonDenominator == 0){
// This code wasn't in Franklin Antonio's method. It was added by Keith Woodward.
// The lines are parallel.
// Check if they're collinear.
double y3LessY1 = y3-y1;
double collinearityTestForP3 = x1*(y2-y3) + x2*(y3LessY1) + x3*(y1-y2); // see http://mathworld.wolfram.com/Collinear.html
// If p3 is collinear with p1 and p2 then p4 will also be collinear, since p1-p2 is parallel with p3-p4
if (collinearityTestForP3 == 0){
// The lines are collinear. Now check if they overlap.
if (x1 >= x3 && x1 <= x4 || x1 <= x3 && x1 >= x4 ||
x2 >= x3 && x2 <= x4 || x2 <= x3 && x2 >= x4 ||
x3 >= x1 && x3 <= x2 || x3 <= x1 && x3 >= x2){
if (y1 >= y3 && y1 <= y4 || y1 <= y3 && y1 >= y4 ||
y2 >= y3 && y2 <= y4 || y2 <= y3 && y2 >= y4 ||
y3 >= y1 && y3 <= y2 || y3 <= y1 && y3 >= y2){
return true;
}
}
}
return false;
}
return true;
}
public static boolean containsPoint(Collection<Node> polyNodes, double latitude, double longitude){
return countIntersections(polyNodes, latitude, longitude) % 2 == 1;
}
/**
* count the intersections when going from lat, lon to outside the ring
* @param polyNodes2
*/
private static int countIntersections(Collection<Node> polyNodes, double latitude, double longitude) {
int intersections = 0;
if (polyNodes.size() == 0) return 0;
Node prev = null;
Node first = null;
Node last = null;
for(Node n : polyNodes) {
if(prev == null) {
prev = n;
first = prev;
continue;
}
if(n == null) {
continue;
}
last = n;
if (OsmMapUtils.ray_intersect_lon(prev,
n, latitude, longitude) != -360.0d) {
intersections++;
}
prev = n;
}
// special handling, also count first and last, might not be closed, but
// we want this!
if (OsmMapUtils.ray_intersect_lon(first,
last, latitude, longitude) != -360.0d) {
intersections++;
}
return intersections;
}
}