/**
* This file is part of OSM2ShareNav
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Copyright (C) 2010 Harald Mueller
*/
package net.sharenav.osmToShareNav.area;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.sharenav.osmToShareNav.Configuration;
import net.sharenav.osmToShareNav.MyMath;
import net.sharenav.osmToShareNav.OsmParser;
import net.sharenav.osmToShareNav.model.Bounds;
import net.sharenav.osmToShareNav.model.Node;
import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
public class Area {
private ArrayList<Outline> outlineList = new ArrayList<Outline>();
private ArrayList<Outline> holeList = new ArrayList<Outline>();
Outline outline = new Outline();
public Triangle triangle;
ArrayList<Triangle> triangleList = null;
static DebugViewer viewer = null;
public Vertex edgeInside;
public boolean debug = false;
public double maxdist = 0d;
//double limitdist = 25000d;
// mapmid format errors
//double limitdist = 50000d;
double limitdist = 32000d;
//double limitdist = 1250000d;
//double limitdist = 10000d;
private static OsmParser parser;
public static void setParser(OsmParser setParser) {
parser = setParser;
}
public void addOutline(Outline p) {
if (p.isValid()) {
outlineList.add(p);
// p.calcNextPrev();
}
}
public void addHole(Outline p) {
if (p.isValid()) {
holeList.add(p);
}
}
public void clean() {
outlineList = new ArrayList<Outline>();
holeList = new ArrayList<Outline>();
// tri = new ArrayList<Triangle>();
outline = new Outline();
}
public List<Triangle> triangulate() {
if (debug) {
if (viewer == null) {
viewer = new DebugViewer(this);
} else {
viewer.setArea(this);
}
}
// if there are more ways than one are used to build the outline, try to construct one outline for that
ArrayList<Outline> outlineTempList = new ArrayList<Outline>();
while (outlineList.size() > 0) {
outline = outlineList.get(0);
if (!outline.isClosed()) {
outline.connectPartWays(outlineList);
}
if (outline.isClosed()) {
outlineTempList.add(outline);
}
outlineList.remove(0);
}
outlineList = outlineTempList;
// the same for the holes
outlineTempList = new ArrayList<Outline>();
//System.err.println("Starting to connect part ways");
while (holeList.size() > 0) {
outline = holeList.get(0);
if (!outline.isClosed()) {
outline.connectPartWays(holeList);
}
if (outline.isClosed()) {
outlineTempList.add(outline);
}
holeList.remove(0);
}
//System.err.println("Finished connecting part ways");
holeList = outlineTempList;
int dir = 0;
ArrayList<Triangle> ret = new ArrayList<Triangle>(1);
triangleList = ret;
repaint();
int loop = 0;
while (outlineList.size() > 0) {
outline = outlineList.get(0);
if (! outline.isValid()) {
outlineList.remove(0);
continue;
}
outline.calcNextPrev();
outlineList.remove(0);
//System.err.println("Starting to do the cutOneEar thing");
while (outline.vertexCount() > 2) {
loop++;
if (loop % 5000 == 0) {
if (Configuration.getConfiguration().verbose >= 0) {
System.err.println("Triangulating outline "
+ outline.getWayId() + " looped "
+ loop + " times");
}
}
if (loop > 4000000) {
System.err.println("Break because of infinite loop for outline " + outline.getWayId());
System.err.println(" see http://www.openstreetmap.org/browse/way/" + outline.getWayId());
break;
}
Triangle t = cutOneEar(outline, holeList, dir);
splitTriangleIfNeeded(t, ret, 0);
dir = (dir + 1) % 4;
}
//System.err.println("Finished doing the cutOneEar thing");
}
//System.out.println(ret);
//System.out.println("loops :" + loop);
//System.err.println("Starting to optimize");
optimize();
ret.trimToSize();
//System.err.println("Finished optimizing");
return ret;
}
private void splitTriangleIfNeeded(Triangle t, ArrayList<Triangle> ret, int recurselevel) {
// check the size; if a line is too long, split the tringle
Node n0 = t.getVert()[0].getNode();
Node n1 = t.getVert()[1].getNode();
Node n2 = t.getVert()[2].getNode();
double dist0 = MyMath.dist(n0, n1);
double dist1 = MyMath.dist(n1, n2);
double dist2 = MyMath.dist(n2, n0);
if (dist0 > limitdist ||
dist1 > limitdist ||
dist2 > limitdist) {
if (recurselevel > 80) {
System.out.println("WARNING: Recurselevel > 80, giving up splitting triangle " + t);
ret.add(t);
return;
}
Triangle t1 = new Triangle(t.getVert()[0], t.getVert()[1], t.getVert()[2]);
Triangle t2 = new Triangle(t.getVert()[0], t.getVert()[1], t.getVert()[2]);
int longest = 0;
double longestDist = 0d;
Node newNode = null;
if (dist0 > longestDist) {
longestDist = dist0;
longest = 0;
}
if (dist1 > longestDist) {
longestDist = dist1;
longest = 1;
}
if (dist2 > longestDist) {
longestDist = dist2;
longest = 2;
}
//System.out.println("Splitting triangle " + t + ", dist= " + longestDist);
//System.out.println("Longest edge: " + longest);
switch(longest) {
case 0:
newNode = n0.midNode(n1, FakeIdGenerator.makeFakeId());
t1.getVert()[1] = new Vertex(newNode,null);
t2.getVert()[0] = new Vertex(newNode,null);
break;
case 1:
newNode = n1.midNode(n2, FakeIdGenerator.makeFakeId());
t1.getVert()[2] = new Vertex(newNode,null);
t2.getVert()[1] = new Vertex(newNode,null);
break;
case 2:
newNode = n2.midNode(n0, FakeIdGenerator.makeFakeId());
t1.getVert()[0] = new Vertex(newNode,null);
t2.getVert()[2] = new Vertex(newNode,null);
break;
}
splitTriangleIfNeeded(t1, ret, recurselevel + 1);
//System.out.println("Split or add triangle t2: " + t2);
splitTriangleIfNeeded(t2, ret, recurselevel + 1);
} else {
//System.out.println("Adding side " + side + " of triangle");
ret.add(t);
}
}
private void optimize() {
for (Triangle t:triangleList) {
t.opt = false;
}
// while (true) {
Iterator<Triangle> it = triangleList.iterator();
while (it.hasNext()) {
Triangle t1 = it.next();
if (t1.getVert()[0].getNode() == t1.getVert()[1].getNode()
|| t1.getVert()[0].getNode() == t1.getVert()[2].getNode()
|| t1.getVert()[1].getNode() == t1.getVert()[2].getNode()) {
it.remove();
// System.out.println("remove degenerated Triangle");
}
// if (! t1.opt) {
// for (Triangle t2:triangleList) {
// if (t1.equalVert(t2) == 2) {
// optimize(t1,t2);
// }
// }
// }
}
// }
}
/**
*
*/
private void repaint() {
if (debug) {
if (viewer == null) {
viewer = DebugViewer.getInstanz(this);
} else {
viewer.setArea(this);
}
if (viewer != null) {
viewer.repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
// System.out.println("Area.repaint()");
}
}
}
private Triangle cutOneEar(Outline outline, ArrayList<Outline> holeList, int dir) {
//List<Vertex> orderedOutline = outline.getOrdered(dir);
Vertex orderedOutlineMin = outline.getMin(dir);
while (true) {
Vertex n = orderedOutlineMin;
triangle = new Triangle(n, n.getNext(), n.getPrev());
edgeInside = findEdgeInside(outline, triangle,dir);
repaint();
if (edgeInside == null) {
// this is an ear with nothing in it so cut the ear
outline.remove(n);
return triangle;
} else {
boolean handled = false;
// at least one edge is inside this ear
if (edgeInside.partOf(outline)) {
handled = true;
// node of the outline is in the ear so we have to cut the outline into two parts
// one will handled now and the other goes to the stack
outline.clean();
Vertex nt = n;
// create a fresh copy of the old outline starting from outer edge of the expected ear
while (nt != edgeInside) {
outline.append(nt);
nt = nt.getNext();
}
// go ahead the edge that was found inside the test triangle
outline.append(edgeInside);
Outline newOutline = new Outline();
newOutline.setWayId(outline.getWayId());
while (nt != n) {
newOutline.append(nt);
nt = nt.getNext();
}
newOutline.append(n);
if (newOutline.isValid()) {
addOutline(newOutline);
}
// reinititalisize outline;
outline.calcNextPrev();
//orderedOutline = outline.getOrdered(dir);
orderedOutlineMin = outline.getMin(dir);
} else {
for (Outline p : holeList) {
if (edgeInside.partOf(p)) {
// now we have an edge of a hole inside the rectangle
// lets join the hole with the outline and have a next try
// Outline hole = edgeInside.getOutline();
Outline hole = p;
if (hole != edgeInside.getOutline()) {
System.out.println("Warning: something wrong with internal data!");
}
hole.calcNextPrev();
repaint();
// Outline newOutline = new Outline();
Vertex nt = n;
boolean clockWise = outline.isClockWiseFast();
outline.clean();
do {
outline.append(nt);
if (clockWise) {
nt = nt.getNext();
} else {
nt = nt.getPrev();
}
} while (nt != n);
repaint();
outline.append(n.clone());
repaint();
nt = edgeInside;
// the following makes triangulation
// of Finnish sea fail after 75 000 triangles with:
/* Triangulating outline 4611686018427401182 looped 75000 times
Something went wrong when trying to triangulate relation
http://www.openstreetmap.org/browse/relation/4611686018427388922 I'll attempt to ignore this relation
java.util.NoSuchElementException
at java.util.ArrayList$Itr.next(ArrayList.java:757)
at java.util.Collections.min(Collections.java:624)
at net.sharenav.osmToShareNav.area.Outline.getLonMin(Outline.java:174)
at net.sharenav.osmToShareNav.area.Outline.isClockWiseFast(Outline.java:290)
at net.sharenav.osmToShareNav.area.Area.cutOneEar(Area.java:326)
at net.sharenav.osmToShareNav.area.Area.triangulate(Area.java:131)
at net.sharenav.osmToShareNav.Relations.processRelations(Relations.java:316)
at net.sharenav.osmToShareNav.Relations.<init>(Relations.java:48)
at net.sharenav.osmToShareNav.BundleShareNav.run(BundleShareNav.java:516)
at java.lang.Thread.run(Thread.java:679) */
//clockWise = hole.isClockWiseFast();
clockWise = hole.isClockWise();
do {
outline.append(nt);
if (clockWise) {
nt = nt.getPrev();
} else {
nt = nt.getNext();
}
// repaint();
} while (nt != edgeInside);
outline.append(edgeInside.clone());
holeList.remove(hole);
outline.calcNextPrev();
//orderedOutline = outline.getOrdered(dir);
orderedOutlineMin = outline.getMin(dir);
handled = true;
break; // we found the hole so break this for loop
}
}
if (!handled) {
System.err.println("Something strange happened, there is an edge inside, but the member outline "
+ edgeInside.getOutline().getWayId() + " wasn't found");
System.err.println(" see http://www.openstreetmap.org/?node=" + edgeInside.getId());
// debug = true;
// repaint();
return triangle;
}
}
}
}
}
// private Vertex findEdgeInside(Outline outline, Triangle triangle) {
// Vertex leftmost = null;
// Vertex n = outline.findVertexInside(triangle);
// if (leftmost == null) {
// leftmost = n;
// } else {
// if (n.getX() < leftmost.getX()) {
// leftmost = n;
// }
// }
// for (Outline p : holeList) {
// n = p.findVertexInside(triangle);
// if (leftmost == null) {
// leftmost = n;
// } else {
// if (n != null && n.getX() < leftmost.getX()) {
// leftmost = n;
// }
// }
// }
// return leftmost;
// }
private Vertex findEdgeInside(Outline outline, Triangle triangle, int dir) {
ArrayList<Vertex> ret = outline.findVertexInside(triangle, null);
for (Outline p : holeList) {
ret = p.findVertexInside(triangle, ret);
}
if (ret == null) {
return null;
}
//System.err.println("Starting to sort in findEdgeInside()");
// switch (dir) {
// case 0:
// Collections.sort(ret, new DirectionComperator0());
// break;
// case 1:
// Collections.sort(ret, new DirectionComperator1());
// break;
// case 2:
// Collections.sort(ret, new DirectionComperator2());
// break;
// default:
// Collections.sort(ret, new DirectionComperatorX());
// break;
// }
//System.err.println("Starting to sort in findEdgeInside()");
switch (dir) {
case 0:
return Collections.min(ret, new DirectionComperator0());
case 1:
return Collections.min(ret, new DirectionComperator1());
case 2:
return Collections.min(ret, new DirectionComperator2());
default:
return Collections.min(ret, new DirectionComperatorX());
}
}
public Bounds extendBounds(Bounds b) {
if (b == null) {
b = new Bounds();
}
for (Outline o:outlineList) {
o.extendBounds(b);
}
for (Outline o:holeList) {
o.extendBounds(b);
}
if (triangleList != null) {
for (Triangle t: triangleList) {
t.extendBound(b);
}
}
return b;
}
public ArrayList<Outline> getOutlineList() {
return outlineList;
}
public ArrayList<Outline> getHoleList() {
return holeList;
}
}