Rings simplify cache, remove obsolete methods and get rid of some

unexpected behaviours
This commit is contained in:
Sander Deryckere 2012-09-18 11:06:12 +02:00
parent 611e16ecf1
commit e2877d3519
2 changed files with 204 additions and 178 deletions

View file

@ -105,23 +105,34 @@ public class Multipolygon {
* @return true if this multipolygon is correct and contains the point * @return true if this multipolygon is correct and contains the point
*/ */
public boolean containsPoint(double latitude, double longitude) { public boolean containsPoint(double latitude, double longitude) {
boolean outerContain = false; Ring containedInOuter = null;
for (Ring outer : getOuterRings()) { // use a sortedset to get the smallest outer containing the point
SortedSet<Ring> outers = new TreeSet<Ring> (getOuterRings());
for (Ring outer : outers) {
if (outer.containsPoint(latitude, longitude)) { if (outer.containsPoint(latitude, longitude)) {
outerContain = true; containedInOuter = outer;
break; break;
} }
} }
if (!outerContain) {
if (containedInOuter == null) {
return false; return false;
} }
for (Ring inner : getInnerRings()) {
//use a sortedSet to get the smallest inner Ring
SortedSet<Ring> inners = new TreeSet<Ring> (getInnerRings());
Ring containedInInner = null;
for (Ring inner : inners) {
if (inner.containsPoint(latitude, longitude)) { if (inner.containsPoint(latitude, longitude)) {
return false; containedInInner = inner;
break;
} }
} }
return true; if (containedInInner == null) return true;
// if it is both, in an inner and in an outer, check if the inner is indeed the smallest one
return !containedInInner.isIn(containedInOuter);
} }
/** /**
@ -331,7 +342,7 @@ public class Multipolygon {
} }
/** /**
* This method only works when the multipolygon has exaclt one outer Ring * This method only works when the multipolygon has exactly one outer Ring
* @return the list of nodes in the outer ring * @return the list of nodes in the outer ring
*/ */
public List<Node> getOuterNodes() { public List<Node> getOuterNodes() {

View file

@ -1,6 +1,5 @@
package net.osmand.data; package net.osmand.data;
import gnu.trove.list.array.TLongArrayList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -26,7 +25,7 @@ public class Ring implements Comparable<Ring> {
/** /**
* a concatenation of the ways to form the border * a concatenation of the ways to form the border
* this is not necessarily a closed way * this is NOT necessarily a CLOSED way
* The id is random, so this may never leave the Ring object * The id is random, so this may never leave the Ring object
*/ */
private Way border; private Way border;
@ -49,7 +48,7 @@ public class Ring implements Comparable<Ring> {
* @return the ways added to the Ring * @return the ways added to the Ring
*/ */
public List<Way> getWays() { public List<Way> getWays() {
return ways; return new ArrayList<Way>(ways);
} }
@ -66,182 +65,124 @@ public class Ring implements Comparable<Ring> {
* get a single closed way that represents the border * get a single closed way that represents the border
* this method is CPU intensive * this method is CPU intensive
* @return a list of Nodes that represents the border * @return a list of Nodes that represents the border
* if the border can't be created, an empty list will be returned
*/ */
public List<Node> getBorder() { public List<Node> getBorder() {
mergeWays(); mergeWays();
List<Node> l = border.getNodes(); List<Node> l = border.getNodes();
if (!isClosed()) { if (border.getNodes().size() != 0 && !isClosed()) {
l.add(border.getNodes().get(0)); l.add(border.getNodes().get(0));
} }
return l; return l;
} }
/** /**
* Merge all ways from the closedways into a single way * Merge all ways from the into a single border way
* If the original ways are initialized with nodes, the new one will be so too * If the original ways are initialized with nodes, the border will be so too
* If the original ways aren't initialized with nodes, the border won't be either
* If only some original ways are initialized with nodes, the border will only have the nodes of the initialized ways
*/ */
private void mergeWays() { private void mergeWays() {
if (border != null) return; if (border != null) return;
List<Way> closedWays = closeWays();
//make a copy of the ways
List<Way> ways = new ArrayList<Way>(getWays());
// do we have to include ways with uninitialized nodes?
// Only if all ways have uninitialized nodes
boolean unInitializedNodes = true;
for (Way w : ways) {
if (w.getNodes() != null && w.getNodes().size() != 0) {
unInitializedNodes = false;
break;
}
}
List<Way> borderWays = new ArrayList<Way>();
for (Way w : ways) {
// if the way has no nodes initialized, and we should initialize them, continue
if ((w.getNodes() == null || w.getNodes().size() == 0) &&
!unInitializedNodes) continue;
Way newWay = null;
Way addedTo = null;
// merge the Way w with the first borderway suitable;
for (Way borderWay : borderWays) {
if (w.getFirstNodeId() == borderWay.getFirstNodeId()) {
newWay = combineTwoWays(w, borderWay, true, true);
addedTo = borderWay;
break;
} else if (w.getFirstNodeId() == borderWay.getLastNodeId()) {
newWay = combineTwoWays(w, borderWay, true, false);
addedTo = borderWay;
break;
} else if (w.getLastNodeId() == borderWay.getLastNodeId()) {
newWay = combineTwoWays(w, borderWay, false, false);
addedTo = borderWay;
break;
} else if (w.getLastNodeId() == borderWay.getFirstNodeId()) {
newWay = combineTwoWays(w, borderWay, false, true);
addedTo = borderWay;
break;
}
}
if (newWay == null) {
// no suitable borderWay has been found, add this way as one of the boundaries
borderWays.add(w);
} else {
// ways are combined, remove the original borderway
borderWays.remove(addedTo);
addedTo = null;
// search if it can be combined with something else
for (Way borderWay : borderWays) {
if (newWay.getFirstNodeId() == borderWay.getFirstNodeId()) {
newWay = combineTwoWays(newWay, borderWay, true, true);
addedTo = borderWay;
break;
} else if (newWay.getFirstNodeId() == borderWay.getLastNodeId()) {
newWay = combineTwoWays(newWay, borderWay, true, false);
addedTo = borderWay;
break;
} else if (newWay.getLastNodeId() == borderWay.getLastNodeId()) {
newWay = combineTwoWays(newWay, borderWay, false, false);
addedTo = borderWay;
break;
} else if (newWay.getLastNodeId() == borderWay.getFirstNodeId()) {
newWay = combineTwoWays(newWay, borderWay, false, true);
addedTo = borderWay;
break;
}
}
if (addedTo != null) {
// newWay has enlarged a second time
borderWays.remove(addedTo);
}
// newWay is now a concatenation of 2 or 3 ways, needs to be added to the borderWays
borderWays.add(newWay);
}
}
if (borderWays.size() != 1) {
border = new Way(randId()); border = new Way(randId());
return;
Long previousConnection = getMultiLineEndNodes(closedWays)[0];
for (Way w : closedWays) {
boolean firstNode = true;
TLongArrayList nodeIds = w.getNodeIds();
List<Node> nodes = w.getNodes();
if (w.getFirstNodeId() == previousConnection) {
for (int i = 0; i< nodeIds.size(); i++) {
// don't need to add the first node, that one was added by the previous way
if (!firstNode) {
if(nodes == null || i>=nodes.size()) {
border.addNode(nodeIds.get(i));
} else {
border.addNode(nodes.get(i));
} }
} border = borderWays.get(0);
firstNode = false;
}
previousConnection = w.getLastNodeId(); return;
} else {
// add the nodes in reverse order
for (int i = nodeIds.size() - 1; i >= 0; i--) {
// don't need to add the first node, that one was added by the previous way
if (!firstNode) {
if(nodes == null || i>=nodes.size()) {
border.addNode(nodeIds.get(i));
} else {
border.addNode(nodes.get(i));
}
}
firstNode = false;
}
previousConnection = w.getFirstNodeId();
} }
}
}
/**
* Check if there exists a cache, if so, return it
* If there isn't a cache, sort the ways to form connected strings <p />
*
* If a Ring contains a gap, one way (without initialized nodes and id=0) is added to the list
*/
private List<Way> closeWays(){
List<Way> closedWays = new ArrayList<Way>();
if (ways.size() == 0) {
closedWays = new ArrayList<Way>();
return closedWays;
}
closedWays = new ArrayList<Way>(ways);
long[] endNodes = getMultiLineEndNodes(ways);
if (endNodes[0] != endNodes[1]) {
if(ways.get(0).getNodes() == null) {
Way w = new Way(randId());
w.addNode(endNodes[0]);
w.addNode(endNodes[1]);
closedWays.add(w);
} else {
Node n1 = null, n2 = null;
if (ways.get(0).getFirstNodeId() == endNodes[0]) {
n1 = ways.get(0).getNodes().get(0);
} else {
int index = ways.get(0).getNodes().size() - 1;
n1 = ways.get(0).getNodes().get(index);
}
int lastML = ways.size() - 1;
if (ways.get(lastML).getFirstNodeId() == endNodes[0]) {
n2 = ways.get(lastML).getNodes().get(0);
} else {
int index = ways.get(lastML).getNodes().size() - 1;
n2 = ways.get(lastML).getNodes().get(index);
}
Way w = new Way(randId());
w.addNode(n1);
w.addNode(n2);
closedWays.add(w);
}
}
return closedWays;
}
/**
* Get the end nodes of a multiLine
* The ways in the multiLine don't have to be initialized for this.
*
* @param multiLine the multiLine to get the end nodes of
* @return an array of size two with the end nodes on both sides. <br />
* * The first node is the end node of the first way in the multiLine. <br />
* * The second node is the end node of the last way in the multiLine.
*/
private long[] getMultiLineEndNodes(List<Way> multiLine) {
// special case, the multiLine contains only a single way, return the end nodes of the way
if (multiLine.size() == 1){
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getLastNodeId()};
}
if (multiLine.size() == 2) {
// ring of two elements, arbitrary choice of the end nodes
if(multiLine.get(0).getFirstNodeId() == multiLine.get(1).getFirstNodeId() &&
multiLine.get(0).getLastNodeId() == multiLine.get(1).getLastNodeId()) {
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getFirstNodeId()};
} else if(multiLine.get(0).getFirstNodeId() == multiLine.get(1).getLastNodeId() &&
multiLine.get(0).getLastNodeId() == multiLine.get(1).getFirstNodeId()) {
return new long[] {multiLine.get(0).getFirstNodeId(), multiLine.get(0).getFirstNodeId()};
}
}
// For all other multiLine lenghts, or for non-closed multiLines with two elements, proceed
long n1 = 0, n2 = 0;
if (multiLine.get(0).getFirstNodeId() == multiLine.get(1).getFirstNodeId() ||
multiLine.get(0).getFirstNodeId() == multiLine.get(1).getLastNodeId()) {
n1 = multiLine.get(0).getLastNodeId();
} else if (multiLine.get(0).getLastNodeId() == multiLine.get(1).getFirstNodeId() ||
multiLine.get(0).getLastNodeId() == multiLine.get(1).getLastNodeId()) {
n1 = multiLine.get(0).getFirstNodeId();
}
int lastIdx = multiLine.size()-1;
if (multiLine.get(lastIdx).getFirstNodeId() == multiLine.get(1).getFirstNodeId() ||
multiLine.get(lastIdx).getFirstNodeId() == multiLine.get(1).getLastNodeId()) {
n2 = multiLine.get(lastIdx).getLastNodeId();
} else if (multiLine.get(lastIdx).getLastNodeId() == multiLine.get(lastIdx - 1).getFirstNodeId() ||
multiLine.get(lastIdx).getLastNodeId() == multiLine.get(lastIdx - 1).getLastNodeId()) {
n2 = multiLine.get(lastIdx).getFirstNodeId();
}
return new long[] {n1, n2};
}
/** /**
* check if this Ring contains the node * check if this Ring contains the node
@ -272,7 +213,6 @@ public class Ring implements Comparable<Ring> {
private int countIntersections(double latitude, double longitude) { private int countIntersections(double latitude, double longitude) {
int intersections = 0; int intersections = 0;
mergeWays();
List<Node> polyNodes = getBorder(); List<Node> polyNodes = getBorder();
for (int i = 0; i < polyNodes.size() - 1; i++) { for (int i = 0; i < polyNodes.size() - 1; i++) {
if (MapAlgorithms.ray_intersect_lon(polyNodes.get(i), if (MapAlgorithms.ray_intersect_lon(polyNodes.get(i),
@ -289,23 +229,6 @@ public class Ring implements Comparable<Ring> {
return intersections; return intersections;
} }
/**
* collect the points of all ways added by the user <br />
* automatically added ways because of closing the Ring won't be added <br />
* Only ways with initialized points can be handled.
* @return a List with nodes
*/
public List<Node> collectPoints() {
ArrayList<Node> collected = new ArrayList<Node>();
for (Way w : ways) {
collected.addAll(w.getNodes());
}
return collected;
}
/** /**
* Check if this is in Ring r * Check if this is in Ring r
@ -317,7 +240,7 @@ public class Ring implements Comparable<Ring> {
* bi-directional check is needed because some concave rings can intersect * bi-directional check is needed because some concave rings can intersect
* and would only fail on one of the checks * and would only fail on one of the checks
*/ */
List<Node> points = this.collectPoints(); List<Node> points = this.getBorder();
// r should contain all nodes of this // r should contain all nodes of this
for(Node n : points) { for(Node n : points) {
@ -326,7 +249,7 @@ public class Ring implements Comparable<Ring> {
} }
} }
points = r.collectPoints(); points = r.getBorder();
// this should not contain a node from r // this should not contain a node from r
for(Node n : points) { for(Node n : points) {
@ -604,4 +527,96 @@ public class Ring implements Comparable<Ring> {
return Math.round(Math.random()*Long.MIN_VALUE); return Math.round(Math.random()*Long.MIN_VALUE);
} }
/**
* make a new Way with the nodes from two other ways
* @param w1 the first way
* @param w2 the second way
* @param firstNodeW1 set true if the first node of w1 is also in the other way
* @param firstNodeW2 set true if the first node of w2 is also in the other way
*/
private static Way combineTwoWays(Way w1, Way w2, boolean firstNodeW1, boolean firstNodeW2) {
Way newWay = new Way(randId());
if(w1.getNodes() != null || w1.getNodes().size() != 0) {
if (firstNodeW1 && firstNodeW2) {
// add the nodes of w1 in reversed order, without the first node
for (int i = w1.getNodes().size() - 1; i>0; i--) {
newWay.addNode(w1.getNodes().get(i));
}
//add the nodes from w2
for (Node n : w2.getNodes()) {
newWay.addNode(n);
}
} else if (firstNodeW1 && !firstNodeW2) {
// add all nodes from w2
for (Node n : w2.getNodes()) {
newWay.addNode(n);
}
// add the nodes from w1, except the first one
for (int i = 1; i < w1.getNodes().size(); i++) {
newWay.addNode(w1.getNodes().get(i));
}
} else if (!firstNodeW1 && firstNodeW2) {
// add all nodes from w1
for (Node n : w1.getNodes()) {
newWay.addNode(n);
}
// add the nodes from w2, except the first one
for (int i = 1; i < w2.getNodes().size(); i++) {
newWay.addNode(w2.getNodes().get(i));
}
} else if (!firstNodeW1 && !firstNodeW2) {
// add all nodes from w1
for (Node n : w1.getNodes()) {
newWay.addNode(n);
}
// add the nodes from w2 in reversed order, except the last one
for (int i = w2.getNodes().size() -2 ; i >= 0; i--) {
newWay.addNode(w2.getNodes().get(i));
}
}
} else {
if (firstNodeW1 && firstNodeW2) {
// add the nodes of w1 in reversed order, without the first node
for (int i = w1.getNodeIds().size() - 1; i>0; i--) {
newWay.addNode(w1.getNodeIds().get(i));
}
//add the nodes from w2
for (int i = 0; i < w2.getNodeIds().size(); i++) {
newWay.addNode(w2.getNodeIds().get(i));
}
} else if (firstNodeW1 && !firstNodeW2) {
// add all nodes from w2
for (int i = 0; i < w2.getNodeIds().size(); i++) {
newWay.addNode(w2.getNodeIds().get(i));
}
// add the nodes from w1, except the first one
for (int i = 1; i < w1.getNodeIds().size(); i++) {
newWay.addNode(w1.getNodeIds().get(i));
}
} else if (!firstNodeW1 && firstNodeW2) {
// add all nodes from w1
for (int i = 0; i < w1.getNodeIds().size(); i++) {
newWay.addNode(w1.getNodeIds().get(i));
}
// add the nodes from w2, except the first one
for (int i = 1; i < w2.getNodeIds().size(); i++) {
newWay.addNode(w2.getNodeIds().get(i));
}
} else if (!firstNodeW1 && !firstNodeW2) {
// add all nodes from w1
for (int i = 0; i < w1.getNodeIds().size(); i++) {
newWay.addNode(w1.getNodeIds().get(i));
}
// add the nodes from w2 in reversed order, except the last one
for (int i = w2.getNodeIds().size() -2 ; i >= 0; i--) {
newWay.addNode(w2.getNodeIds().get(i));
}
}
}
return newWay;
}
} }