From e2877d35194aea799f2469eb6e91baa44f812b36 Mon Sep 17 00:00:00 2001 From: Sander Deryckere Date: Tue, 18 Sep 2012 11:06:12 +0200 Subject: [PATCH] Rings simplify cache, remove obsolete methods and get rid of some unexpected behaviours --- .../src/net/osmand/data/Multipolygon.java | 27 +- .../src/net/osmand/data/Ring.java | 355 +++++++++--------- 2 files changed, 204 insertions(+), 178 deletions(-) diff --git a/DataExtractionOSM/src/net/osmand/data/Multipolygon.java b/DataExtractionOSM/src/net/osmand/data/Multipolygon.java index eed1d4d21b..ac50e84853 100644 --- a/DataExtractionOSM/src/net/osmand/data/Multipolygon.java +++ b/DataExtractionOSM/src/net/osmand/data/Multipolygon.java @@ -105,23 +105,34 @@ public class Multipolygon { * @return true if this multipolygon is correct and contains the point */ public boolean containsPoint(double latitude, double longitude) { - boolean outerContain = false; - for (Ring outer : getOuterRings()) { + Ring containedInOuter = null; + // use a sortedset to get the smallest outer containing the point + SortedSet outers = new TreeSet (getOuterRings()); + for (Ring outer : outers) { if (outer.containsPoint(latitude, longitude)) { - outerContain = true; + containedInOuter = outer; break; } } - if (!outerContain) { + + if (containedInOuter == null) { return false; } - for (Ring inner : getInnerRings()) { + + //use a sortedSet to get the smallest inner Ring + SortedSet inners = new TreeSet (getInnerRings()); + Ring containedInInner = null; + for (Ring inner : inners) { 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 */ public List getOuterNodes() { diff --git a/DataExtractionOSM/src/net/osmand/data/Ring.java b/DataExtractionOSM/src/net/osmand/data/Ring.java index f9d110720f..03f720b4aa 100644 --- a/DataExtractionOSM/src/net/osmand/data/Ring.java +++ b/DataExtractionOSM/src/net/osmand/data/Ring.java @@ -1,6 +1,5 @@ package net.osmand.data; -import gnu.trove.list.array.TLongArrayList; import java.util.ArrayList; import java.util.List; @@ -26,7 +25,7 @@ public class Ring implements Comparable { /** * 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 */ private Way border; @@ -49,7 +48,7 @@ public class Ring implements Comparable { * @return the ways added to the Ring */ public List getWays() { - return ways; + return new ArrayList(ways); } @@ -66,182 +65,124 @@ public class Ring implements Comparable { * get a single closed way that represents the border * this method is CPU intensive * @return a list of Nodes that represents the border + * if the border can't be created, an empty list will be returned */ public List getBorder() { mergeWays(); List l = border.getNodes(); - if (!isClosed()) { + if (border.getNodes().size() != 0 && !isClosed()) { l.add(border.getNodes().get(0)); } - return l; } /** - * Merge all ways from the closedways into a single way - * If the original ways are initialized with nodes, the new one will be so too + * Merge all ways from the into a single border way + * 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() { if (border != null) return; - List closedWays = closeWays(); - border = new Way(randId()); - Long previousConnection = getMultiLineEndNodes(closedWays)[0]; + //make a copy of the ways + List ways = new ArrayList(getWays()); - for (Way w : closedWays) { - boolean firstNode = true; - TLongArrayList nodeIds = w.getNodeIds(); - List nodes = w.getNodes(); + // 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 borderWays = new ArrayList(); + + + 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; - 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)); - } - - } - firstNode = false; + 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; } - - previousConnection = w.getLastNodeId(); + } + + 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); - // 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)); - } + 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; } - firstNode = false; } - previousConnection = w.getFirstNodeId(); + 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()); + return; + } + + border = borderWays.get(0); + + return; + } - - - - - /** - * Check if there exists a cache, if so, return it - * If there isn't a cache, sort the ways to form connected strings

- * - * If a Ring contains a gap, one way (without initialized nodes and id=0) is added to the list - */ - private List closeWays(){ - List closedWays = new ArrayList(); - if (ways.size() == 0) { - closedWays = new ArrayList(); - return closedWays; - } - closedWays = new ArrayList(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.
- * * The first node is the end node of the first way in the multiLine.
- * * The second node is the end node of the last way in the multiLine. - */ - private long[] getMultiLineEndNodes(List 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 @@ -272,7 +213,6 @@ public class Ring implements Comparable { private int countIntersections(double latitude, double longitude) { int intersections = 0; - mergeWays(); List polyNodes = getBorder(); for (int i = 0; i < polyNodes.size() - 1; i++) { if (MapAlgorithms.ray_intersect_lon(polyNodes.get(i), @@ -289,23 +229,6 @@ public class Ring implements Comparable { return intersections; } - /** - * collect the points of all ways added by the user
- * automatically added ways because of closing the Ring won't be added
- * Only ways with initialized points can be handled. - * @return a List with nodes - */ - public List collectPoints() { - - ArrayList collected = new ArrayList(); - - for (Way w : ways) { - collected.addAll(w.getNodes()); - } - - return collected; - - } /** * Check if this is in Ring r @@ -317,7 +240,7 @@ public class Ring implements Comparable { * bi-directional check is needed because some concave rings can intersect * and would only fail on one of the checks */ - List points = this.collectPoints(); + List points = this.getBorder(); // r should contain all nodes of this for(Node n : points) { @@ -326,7 +249,7 @@ public class Ring implements Comparable { } } - points = r.collectPoints(); + points = r.getBorder(); // this should not contain a node from r for(Node n : points) { @@ -603,5 +526,97 @@ public class Ring implements Comparable { private static long randId() { 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; + + } + }