2011-05-02 00:36:19 +02:00
|
|
|
package net.osmand.data;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
2011-06-19 19:06:35 +02:00
|
|
|
import java.util.Collections;
|
2011-05-02 00:36:19 +02:00
|
|
|
import java.util.List;
|
|
|
|
|
2011-06-19 19:06:35 +02:00
|
|
|
import net.osmand.osm.LatLon;
|
2011-05-02 00:36:19 +02:00
|
|
|
import net.osmand.osm.MapUtils;
|
|
|
|
import net.osmand.osm.Node;
|
|
|
|
import net.osmand.osm.Way;
|
|
|
|
|
|
|
|
public class MapAlgorithms {
|
|
|
|
|
|
|
|
public static void simplifyDouglasPeucker(List<Node> n, int zoom, int epsilon, Way w){
|
|
|
|
ArrayList<Integer> l = new ArrayList<Integer>();
|
|
|
|
int first = 0;
|
|
|
|
while(first < n.size()){
|
|
|
|
if(n.get(first) != null){
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
first++;
|
|
|
|
}
|
|
|
|
int last = n.size() - 1;
|
|
|
|
while (last >= 0) {
|
|
|
|
if (n.get(last) != null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
last--;
|
|
|
|
}
|
|
|
|
if(last - first < 1){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
boolean cycle = n.get(first).getId() == n.get(last).getId();
|
|
|
|
simplifyDouglasPeucker(n, zoom, epsilon, l, first, cycle ? last - 1: last);
|
|
|
|
w.addNode(n.get(first));
|
|
|
|
for (int i = 0; i < l.size(); i++) {
|
|
|
|
w.addNode(n.get(l.get(i)));
|
|
|
|
}
|
|
|
|
if (cycle) {
|
|
|
|
w.addNode(n.get(first));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void simplifyDouglasPeucker(List<Node> n, int zoom, int epsilon, List<Integer> ints, int start, int end){
|
|
|
|
double dmax = -1;
|
|
|
|
int index = -1;
|
|
|
|
for (int i = start + 1; i <= end - 1; i++) {
|
|
|
|
if(n.get(i) == null){
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
double d = orthogonalDistance(zoom, n.get(start), n.get(end), n.get(i));// calculate distance from line
|
|
|
|
if (d > dmax) {
|
|
|
|
dmax = d;
|
|
|
|
index = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(dmax >= epsilon){
|
|
|
|
simplifyDouglasPeucker(n, zoom, epsilon, ints, start, index);
|
|
|
|
simplifyDouglasPeucker(n, zoom, epsilon, ints, index, end);
|
|
|
|
} else {
|
|
|
|
ints.add(end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static double orthogonalDistance(int zoom, Node nodeLineStart, Node nodeLineEnd, Node node) {
|
2011-06-19 19:06:35 +02:00
|
|
|
if(zoom > 31){
|
|
|
|
zoom = 31;
|
|
|
|
}
|
2011-05-02 00:36:19 +02:00
|
|
|
double x1 = MapUtils.getTileNumberX(zoom, nodeLineStart.getLongitude());
|
|
|
|
double y1 = MapUtils.getTileNumberY(zoom, nodeLineStart.getLatitude());
|
|
|
|
double x2 = MapUtils.getTileNumberX(zoom, nodeLineEnd.getLongitude());
|
|
|
|
double y2 = MapUtils.getTileNumberY(zoom, nodeLineEnd.getLatitude());
|
|
|
|
double x = MapUtils.getTileNumberX(zoom, node.getLongitude());
|
|
|
|
double y = MapUtils.getTileNumberY(zoom, node.getLatitude());
|
|
|
|
double A = x - x1;
|
|
|
|
double B = y - y1;
|
|
|
|
double C = x2 - x1;
|
|
|
|
double D = y2 - y1;
|
|
|
|
return Math.abs(A * D - C * B) / Math.sqrt(C * C + D * D);
|
|
|
|
}
|
2011-06-19 19:06:35 +02:00
|
|
|
|
|
|
|
public static boolean isClockwiseWay(Way w){
|
|
|
|
return isClockwiseWay(Collections.singletonList(w));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean isClockwiseWay(List<Way> ways){
|
|
|
|
if(ways.isEmpty()){
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
LatLon latLon = ways.get(0).getLatLon();
|
|
|
|
double lat = latLon.getLatitude();
|
|
|
|
double lon = 180;
|
|
|
|
double firstLon = -360;
|
|
|
|
boolean firstDirectionUp = false;
|
|
|
|
double previousLon = -360;
|
|
|
|
|
|
|
|
double clockwiseSum = 0;
|
|
|
|
|
|
|
|
Node prev = null;
|
|
|
|
boolean firstWay = true;
|
|
|
|
for(Way w : ways){
|
|
|
|
List<Node> ns = w.getNodes();
|
|
|
|
int startInd = 0;
|
|
|
|
if(firstWay && ns.size() > 0){
|
|
|
|
prev = ns.get(0);
|
|
|
|
startInd = 1;
|
|
|
|
firstWay = false;
|
|
|
|
}
|
|
|
|
for(int i = startInd; i < ns.size();i++) {
|
|
|
|
Node next = ns.get(i);
|
|
|
|
double rlon = ray_intersect_lon(prev, next, lat, lon);
|
|
|
|
if(rlon != - 360d){
|
|
|
|
boolean skipSameSide = (prev.getLatitude() <= lat) == (next.getLatitude() <= lat);
|
|
|
|
if(skipSameSide){
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
boolean directionUp = prev.getLatitude() <= lat;
|
|
|
|
if(firstLon == - 360){
|
|
|
|
firstDirectionUp = directionUp;
|
|
|
|
firstLon = rlon;
|
|
|
|
} else {
|
|
|
|
boolean clockwise = (!directionUp) == (previousLon < rlon);
|
|
|
|
if(clockwise){
|
|
|
|
clockwiseSum += Math.abs(previousLon - rlon);
|
|
|
|
} else {
|
|
|
|
clockwiseSum -= Math.abs(previousLon - rlon);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
previousLon = rlon;
|
|
|
|
}
|
|
|
|
prev = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(firstLon != -360){
|
|
|
|
boolean clockwise = (!firstDirectionUp) == (previousLon < firstLon);
|
|
|
|
if(clockwise){
|
|
|
|
clockwiseSum += Math.abs(previousLon - firstLon);
|
|
|
|
} else {
|
|
|
|
clockwiseSum -= Math.abs(previousLon - firstLon);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return clockwiseSum >= 0;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public static double ray_intersect_lon(Node node, Node node2, double latitude, double longitude) {
|
|
|
|
// a node below
|
|
|
|
Node a = node.getLatitude() < node2.getLatitude() ? node : node2;
|
|
|
|
// b node above
|
|
|
|
Node b = a == node2 ? node : node2;
|
|
|
|
if (latitude == a.getLatitude() || latitude == b.getLatitude()) {
|
|
|
|
latitude += 0.00000001d;
|
|
|
|
}
|
|
|
|
if (latitude < a.getLatitude() || latitude > b.getLatitude()) {
|
|
|
|
return -360d;
|
|
|
|
} else {
|
|
|
|
if (longitude < Math.min(a.getLongitude(), b.getLongitude())) {
|
|
|
|
return -360d;
|
|
|
|
} else {
|
|
|
|
if (a.getLongitude() == b.getLongitude() && longitude == a.getLongitude()) {
|
|
|
|
// the node on the boundary !!!
|
|
|
|
return longitude;
|
|
|
|
}
|
|
|
|
// that tested on all cases (left/right)
|
|
|
|
double lon = b.getLongitude()+
|
|
|
|
(b.getLatitude() - latitude) * (b.getLongitude() - a.getLongitude()) / (b.getLatitude() - a.getLatitude());
|
|
|
|
if (lon <= longitude) {
|
|
|
|
return lon;
|
|
|
|
} else {
|
|
|
|
return -360d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to intersect with ray from left to right
|
|
|
|
public static boolean ray_intersect(Node node, Node node2, double latitude, double longitude) {
|
|
|
|
// a node below
|
|
|
|
Node a = node.getLatitude() < node2.getLatitude() ? node : node2;
|
|
|
|
// b node above
|
|
|
|
Node b = a == node2 ? node : node2;
|
|
|
|
if(latitude == a.getLatitude() || latitude == b.getLatitude()){
|
|
|
|
latitude += 0.00000001d;
|
|
|
|
}
|
|
|
|
if(latitude < a.getLatitude() || latitude > b.getLatitude()){
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if(longitude > Math.max(a.getLongitude(), b.getLongitude())) {
|
|
|
|
return true;
|
|
|
|
} else if(longitude < Math.min(a.getLongitude(), b.getLongitude())){
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if(a.getLongitude() == b.getLongitude()) {
|
|
|
|
// the node on the boundary !!!
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// that tested on all cases (left/right)
|
|
|
|
double mR = (b.getLatitude() - a.getLatitude()) / (b.getLongitude() - a.getLongitude());
|
|
|
|
double mB = (latitude - a.getLatitude()) / (longitude - a.getLongitude());
|
|
|
|
if(mB <= mR){
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-02 00:36:19 +02:00
|
|
|
}
|