From 2f89a33f8ca189478527aff9b03dcba850c51ea7 Mon Sep 17 00:00:00 2001 From: mg4gh Date: Sat, 11 May 2024 18:46:02 +0200 Subject: [PATCH] refactor tile connect/disconnect --- .../mgmap/features/routing/RoutingEngine.java | 8 +- .../mg/mgmap/generic/graph/GGraphMulti.java | 106 +-------------- .../mg/mgmap/generic/graph/GGraphTile.java | 17 +-- .../mgmap/generic/graph/GGraphTileCache.java | 127 ------------------ .../generic/graph/GGraphTileFactory.java | 4 +- .../java/mg/mgmap/generic/graph/GNode.java | 9 ++ .../mg/mgmap/generic/graph/GTileCache.java | 25 ++-- .../mgmap/generic/graph/GTileConnector.java | 115 ++++++++++++++++ 8 files changed, 145 insertions(+), 266 deletions(-) delete mode 100644 mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileCache.java create mode 100644 mgmap/src/main/java/mg/mgmap/generic/graph/GTileConnector.java diff --git a/mgmap/src/main/java/mg/mgmap/activity/mgmap/features/routing/RoutingEngine.java b/mgmap/src/main/java/mg/mgmap/activity/mgmap/features/routing/RoutingEngine.java index c3f843be..538b51aa 100644 --- a/mgmap/src/main/java/mg/mgmap/activity/mgmap/features/routing/RoutingEngine.java +++ b/mgmap/src/main/java/mg/mgmap/activity/mgmap/features/routing/RoutingEngine.java @@ -300,10 +300,10 @@ MultiPointModelImpl calcRouting(RoutePointModel source, RoutePointModel target, sourceApproachModel = validateApproachModel(source.selectedApproach); targetApproachModel = validateApproachModel(target.selectedApproach); - multi.connect(gStart,sourceApproachModel.getNode1()); - multi.connect(gStart,sourceApproachModel.getNode2()); - multi.connect(gEnd,targetApproachModel.getNode1()); - multi.connect(gEnd,targetApproachModel.getNode2()); + gStart.bidirectionalConnect(sourceApproachModel.getNode1()); + gStart.bidirectionalConnect(sourceApproachModel.getNode2()); + gEnd.bidirectionalConnect(targetApproachModel.getNode1()); + gEnd.bidirectionalConnect(targetApproachModel.getNode2()); double distLimit = Math.min(routingContext.maxBeelineDistance, routingContext.maxRouteLengthFactor * routingProfile.heuristic(gStart, gEnd) + 500); diff --git a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphMulti.java b/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphMulti.java index 45800b83..a02a91d0 100644 --- a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphMulti.java +++ b/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphMulti.java @@ -14,10 +14,7 @@ */ package mg.mgmap.generic.graph; -import mg.mgmap.generic.model.WriteablePointModel; -import mg.mgmap.generic.model.WriteablePointModelImpl; import mg.mgmap.generic.util.basic.MGLog; -import mg.mgmap.generic.model.PointModelUtil; import mg.mgmap.generic.util.basic.MemoryUtil; import java.lang.invoke.MethodHandles; @@ -34,14 +31,12 @@ public class GGraphMulti extends GGraph { private static final MGLog mgLog = new MGLog(MethodHandles.lookup().lookupClass().getName()); private final GGraphTileFactory gGraphTileFactory; -// ArrayList overlayNeighbours = new ArrayList<>(); // used for neighbour tile connections and for approaches private int useCnt = 0; public GGraphMulti(GGraphTileFactory gGraphTileFactory, ArrayList gGraphTiles){ this.gGraphTileFactory = gGraphTileFactory; for (GGraphTile gGraphTile : gGraphTiles){ use(gGraphTile); - connectGGraphTile(gGraphTile); } } @@ -50,10 +45,8 @@ public int getTileCount(){ } /** - * Redefines getNodes() implementation og GGraph, which simply returns the ArrayList of its nodes. - * Here we get a new ArrayList Object with all nodes from each included GGraphTile plus additionally the - * nodes that were created due to connecting neighbour GGraphTile instances, plus the additional - * overlay nodes from approaches for start and end point of a routing access. + * Redefines getNodes() implementation of GGraph, which simply returns the ArrayList of its nodes. + * Here we get a new ArrayList Object with all nodes from each included GGraphTile * @return all node ot the multi graph */ @Override @@ -102,10 +95,7 @@ private boolean checkGGraphTileNeighbour(GNode node, byte border){ if (gGraphTileNeighbour == null){ mgLog.d(String.format(Locale.ENGLISH, "border=%d tileX=%d tileY=%d",border,tileXn,tileYn)); gGraphTileNeighbour = gGraphTileFactory.getGGraphTile(tileXn, tileYn, true); - connectGGraphTile(gGraphTileNeighbour); bRes = true; - } else if (!gGraphTileNeighbour.used){ - connectGGraphTile(gGraphTileNeighbour); } use(gGraphTileNeighbour); } @@ -113,98 +103,6 @@ private boolean checkGGraphTileNeighbour(GNode node, byte border){ return bRes; } - private void connectGGraphTile(GGraphTile newGGraphTile){ - connectTiles(gGraphTileFactory.getGGraphTile(newGGraphTile.getTileX()-1,newGGraphTile.getTileY(), false), newGGraphTile, true); - connectTiles(newGGraphTile, gGraphTileFactory.getGGraphTile(newGGraphTile.getTileX()+1,newGGraphTile.getTileY(),false), true); - connectTiles(gGraphTileFactory.getGGraphTile(newGGraphTile.getTileX(),newGGraphTile.getTileY()-1, false), newGGraphTile, false); - connectTiles(newGGraphTile, gGraphTileFactory.getGGraphTile(newGGraphTile.getTileX(),newGGraphTile.getTileY()+1, false), false); - } - - private void connectTiles(GGraphTile gGraphTile1, GGraphTile gGraphTile2,boolean horizontal){ // horizontal true: gGraphTile1 is left, gGraphTile2 is right - false: gGraphTile1 is above, gGraphTile2 is below - if ((gGraphTile1 == null) || (gGraphTile2 == null)) return; // cannot connect yet - if ((gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH] == gGraphTile2) && - (gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH] == gGraphTile1)){ // tiles already connected - return; - } - if ((gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH] != null) || - (gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH] != null)){ // inconsistency detected!!!! - mgLog.e("connectTiles failed"+gGraphTile1.getTileX()+","+gGraphTile1.getTileY()+" "+gGraphTile2.getTileX()+","+gGraphTile2.getTileY()+" "+horizontal); - mgLog.e("found="+gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH]+" expected="+gGraphTile2); - mgLog.e("found="+gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH]+" expected="+gGraphTile1); - throw new IllegalArgumentException("inconsistent neighbour tiles - check logfile for more details."); - } - double connectAt = (horizontal)?gGraphTile1.tbBox.maxLongitude:gGraphTile1.tbBox.minLatitude; - if (horizontal? (connectAt!=gGraphTile2.tbBox.minLongitude):(connectAt!=gGraphTile2.tbBox.maxLatitude)){ - throw new IllegalArgumentException("cannot connectHorizontal Tiles with BB " + gGraphTile1.tbBox +" and "+gGraphTile2.tbBox); - } - ArrayList borderNodes1 = new ArrayList<>(); - for (GNode node : gGraphTile1.getNodes()){ - if ((horizontal)?(node.getLon() == connectAt):(node.getLat() == connectAt)){ - borderNodes1.add(node); - } - } - ArrayList borderNodes2 = new ArrayList<>(); - for (GNode node : gGraphTile2.getNodes()){ - if ((horizontal)?(node.getLon() == connectAt):(node.getLat() == connectAt)){ - borderNodes2.add(node); - } - } - final ArrayList remainingNodes1 = new ArrayList<>(borderNodes1); - final ArrayList remainingNodes2 = new ArrayList<>(borderNodes2); - double threshold = (horizontal)?PointModelUtil.latitudeDistance(CONNECT_THRESHOLD_METER):PointModelUtil.longitudeDistance(CONNECT_THRESHOLD_METER, connectAt); - for (GNode node1 : borderNodes1){ - for (GNode node2 : borderNodes2){ - if ( (horizontal?Math.abs( node1.getLat() - node2.getLat() ):Math.abs( node1.getLon() - node2.getLon() )) <= threshold){ //distance less than 0.5m -> connect nodes - connect(node1, node2); - remainingNodes1.remove(node1); - remainingNodes2.remove(node2); - } - } - } - if ((remainingNodes1.size() > 0) && ((remainingNodes2.size() > 0))){ - ArrayList stillRemainingNodes1 = new ArrayList<>(remainingNodes1); - ArrayList stillRemainingNodes2 = new ArrayList<>(remainingNodes2); - mgLog.v(remainingNodes1::toString); - mgLog.v(remainingNodes2::toString); - for (GNode node1 : remainingNodes1){ - for (GNode node2 : remainingNodes2){ - if ((node1.countNeighbours() == 1) && (node2.countNeighbours() == 1) && (PointModelUtil.distance(node1,node2) CONNECT_THRESHOLD_METER) continue; - if (!PointModelUtil.findApproach(node2,node1Neighbour,node2Neighbour,approachPoint,0)) continue; // approach not found try next points - if (PointModelUtil.distance(approachPoint,node2) > CONNECT_THRESHOLD_METER) continue; - mgLog.d(()->"OK, connect: node1 " + node1 + " node1neighbour " + node1Neighbour + " node2 " + node2 + " node2neighbour " + node2Neighbour); - connect(node1, node2); - stillRemainingNodes1.remove(node1); - stillRemainingNodes2.remove(node2); - } - } - } - remainingNodes1.clear(); - remainingNodes1.addAll(stillRemainingNodes1); - remainingNodes2.clear(); - remainingNodes2.addAll(stillRemainingNodes2); - } - if ((remainingNodes1.size() > 0) || ((remainingNodes2.size() > 0))){ - mgLog.d(()->"remainings1 " + remainingNodes1); - mgLog.d(()->"remainings2 " + remainingNodes2); - } - gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH] = gGraphTile2; - gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH] = gGraphTile1; - } - - public void connect(GNode node1, GNode node2){ - GNeighbour n12 = new GNeighbour(node2,null); - GNeighbour n21 = new GNeighbour(node1,null); - n12.setReverse(n21); - n21.setReverse(n12); - node1.addNeighbour(n12); - node2.addNeighbour(n21); - } - private void use(GGraphTile gGraphTile){ if (!gGraphTile.used){ gGraphTile.resetNodeRefs(); diff --git a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTile.java b/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTile.java index baee25b1..a8d8cd9d 100644 --- a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTile.java +++ b/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTile.java @@ -159,24 +159,9 @@ void resetNodeRefs(){ } } - /** - * @param tileIdx Tile to be dropped from cache - so drop all references to this tileIdx - * @param border tileIdx border that point to this gGraphTile - */ - void dropNeighboursToTile(int tileIdx, byte border){ - byte ownBorder = GNode.oppositeBorder(border); - for (GNode node : getNodes()){ - if ((node.borderNode & ownBorder) != 0){ - node.removeNeighbourNode(tileIdx); - } - } - assert (neighbourTiles[ownBorder].tileIdx == tileIdx):"neighbourTiles[ownBorder].tileIdx"+neighbourTiles[ownBorder].tileIdx+" tileIdx="+tileIdx; - neighbourTiles[ownBorder] = null; - } - @NonNull @Override public String toString() { - return "GGraphTile-"+tbBox.toString(); + return "GGraphTile-("+getTileX()+","+getTileY()+")"; } } diff --git a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileCache.java b/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileCache.java deleted file mode 100644 index 745c406b..00000000 --- a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileCache.java +++ /dev/null @@ -1,127 +0,0 @@ -package mg.mgmap.generic.graph; - -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.TreeMap; - -import mg.mgmap.generic.util.basic.MGLog; - -public class GGraphTileCache { -// -// private static final MGLog mgLog = new MGLog(MethodHandles.lookup().lookupClass().getName()); -// -// private final ArrayList workQueue = new ArrayList<>(); -// private final LinkedHashMap cache = new LinkedHashMap<>(100, 0.6f, true) { -// @Override -// protected boolean removeEldestEntry(Entry eldest) { -// GGraphTile old = eldest.getValue(); -// boolean bDrop = ((size() > GGraphTileFactory.CACHE_LIMIT) && (!old.used)); -// if (bDrop) { -// cleanupNeighbourTile(old,GNode.BORDER_NODE_WEST); -// cleanupNeighbourTile(old,GNode.BORDER_NODE_NORTH); -// cleanupNeighbourTile(old,GNode.BORDER_NODE_EAST); -// cleanupNeighbourTile(old,GNode.BORDER_NODE_SOUTH); -// mgLog.d(() -> "remove from cache: tile x=" + old.tile.tileX + " y=" + old.tile.tileY + " Cache Size:" + cache.size()); -// } -// return bDrop; -// } -// -// private void cleanupNeighbourTile(GGraphTile gGraphTile, byte border){ -// GGraphTile neighbourTile = gGraphTile.neighbourTiles[border]; -// if (neighbourTile != null){ -// neighbourTile.dropNeighboursToTile(gGraphTile.tileIdx, border); -// } -// } -// }; -// -// private TreeMap cacheMap = new TreeMap<>(); -// private TreeMap accessMap = new TreeMap<>(); -// -// GGraphTileFactory gGraphTileFactory; -// public GGraphTileCache(GGraphTileFactory gGraphTileFactory){ -// this.gGraphTileFactory = gGraphTileFactory; -// new Thread(() -> { -// while (true){ -// try { -// int key; -// boolean load; -// synchronized (this){ -// while (workQueue.size() == 0){ -// wait(1000); -// } -// key = workQueue.remove(0); -// load = (cache.get(key) == null); -// } -// if (load){ -// int tileX = key>>16; -// int tileY = key & 0xFFFF; -// GGraphTile gGraphTile = gGraphTileFactory.loadGGraphTile(tileX,tileY); -// -// synchronized (this){ -// cache.put(key, gGraphTile); -// notifyAll(); -// } -// } -// } catch (Throwable t){ -// mgLog.e(t); -// } -// } -// }).start(); -// } -// -// synchronized GGraphTile get(byte originatorBorder, int tileX, int tileY){ -// int key = GGraphTileFactory.getKey(tileX,tileY); -// GGraphTile gGraphTile; -//// if (!cache.containsKey(key)) -//// workQueue.add(0, key); -//// if (originatorBorder != 0) triggerPrefetch(originatorBorder,tileX,tileY); -//// mgLog.d("workQueueSize="+workQueue.size()); -//// notifyAll(); -// boolean first = true; -// while ((gGraphTile = cache.get(key)) == null){ -// if (first){ -// workQueue.add(0,key); -// notifyAll(); -// first = false; -// } -// try { -// wait(1000); -// } catch (InterruptedException e) { mgLog.e(e); } -// } -//// if (originatorBorder != 0) triggerPrefetch(originatorBorder,tileX,tileY); -// mgLog.d("workQueueSize="+workQueue.size()); -// return gGraphTile; -// } -// -// synchronized void triggerPrefetch(byte originatorBorder, int tileX, int tileY) { -// int key = GGraphTileFactory.getKey(tileX+GNode.deltaX(originatorBorder),tileY+GNode.deltaY(originatorBorder)); -// if (!cache.containsKey(key)){ -// workQueue.add(key); -// notifyAll(); -// } -// -//// int xMin = (GNode.deltaX(originatorBorder)==0)?-1:GNode.deltaX(originatorBorder); -//// int xMax = (GNode.deltaX(originatorBorder)==0)? 1:GNode.deltaX(originatorBorder); -//// int yMin = (GNode.deltaY(originatorBorder)==0)?-1:GNode.deltaX(originatorBorder); -//// int yMax = (GNode.deltaY(originatorBorder)==0)? 1:GNode.deltaX(originatorBorder); -//// for (int i=tileX+xMin; i<=tileX+xMax; i++){ -//// for (int j=tileY+yMin; j<=tileY+yMax; j++){ -//// workQueue.add(GGraphTileFactory.getKey(i,j)); -//// } -//// } -// } -// -// -// synchronized ArrayList getAllTiles(){ -// return new ArrayList<>(cache.values()); -// } -// -// synchronized void clear(){ -// cache.clear(); -// } -// -// int size(){ -// return cache.size(); -// } -} diff --git a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileFactory.java b/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileFactory.java index df612b37..ef6352aa 100644 --- a/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileFactory.java +++ b/mgmap/src/main/java/mg/mgmap/generic/graph/GGraphTileFactory.java @@ -138,10 +138,10 @@ public GGraphTile getGGraphTile(int tileX, int tileY){ return getGGraphTile(tileX, tileY, true); } public GGraphTile getGGraphTile(int tileX, int tileY, boolean load){ - GGraphTile gGraphTile = gTileCache.get(getKey(tileX, tileY)); + GGraphTile gGraphTile = gTileCache.get(tileX, tileY); if (load && (gGraphTile == null)){ gGraphTile = loadGGraphTile(tileX, tileY); - gTileCache.put(GGraphTileFactory.getKey(tileX, tileY), gGraphTile); + gTileCache.put(tileX, tileY, gGraphTile); } return gGraphTile; } diff --git a/mgmap/src/main/java/mg/mgmap/generic/graph/GNode.java b/mgmap/src/main/java/mg/mgmap/generic/graph/GNode.java index 3fc91225..8ebe4b64 100644 --- a/mgmap/src/main/java/mg/mgmap/generic/graph/GNode.java +++ b/mgmap/src/main/java/mg/mgmap/generic/graph/GNode.java @@ -124,6 +124,15 @@ public void resetNodeRefs(){ nodeReverseRef = null; } + public void bidirectionalConnect(GNode neighbourNode){ + GNeighbour neighbour = new GNeighbour(neighbourNode,null); + GNeighbour reverseNeighbour = new GNeighbour(this,null); + neighbour.setReverse(reverseNeighbour); + reverseNeighbour.setReverse(neighbour); + this.addNeighbour(neighbour); + neighbourNode.addNeighbour(reverseNeighbour); + } + public void removeNeighbourNode(GNode neighbourNode){ GNeighbour nextNeighbour = this.neighbour; while (nextNeighbour.getNextNeighbour() != null) { diff --git a/mgmap/src/main/java/mg/mgmap/generic/graph/GTileCache.java b/mgmap/src/main/java/mg/mgmap/generic/graph/GTileCache.java index 88df3e3f..4a43f7a6 100644 --- a/mgmap/src/main/java/mg/mgmap/generic/graph/GTileCache.java +++ b/mgmap/src/main/java/mg/mgmap/generic/graph/GTileCache.java @@ -19,18 +19,24 @@ public GTileCache(int limit){ TreeMap tileMap = new TreeMap<>(); TreeMap accessMap = new TreeMap<>(); - synchronized void put(int tileIdx, GGraphTile tile){ + synchronized void put (int tileX, int tileY, GGraphTile tile){ + int tileIdx = GGraphTileFactory.getKey(tileX,tileY); GGraphTile cacheTile = tileMap.put(tileIdx, tile); if (cacheTile != null){ accessMap.remove(cacheTile.accessTime); } + GTileConnector.connect(get( tileX-1,tileY), tile, true); + GTileConnector.connect(tile, get( tileX+1,tileY), true); + GTileConnector.connect(get( tileX,tileY-1), tile, false); + GTileConnector.connect(tile, get( tileX,tileY+1), false); long now = System.nanoTime(); tile.accessTime = now; accessMap.put(now, tileIdx); service(); } - synchronized GGraphTile get(int tileIdx){ + synchronized GGraphTile get(int tileX, int tileY){ + int tileIdx = GGraphTileFactory.getKey(tileX,tileY); GGraphTile cacheTile = tileMap.get(tileIdx); if (cacheTile != null){ accessMap.remove(cacheTile.accessTime); @@ -66,10 +72,10 @@ void service(){ } else { accessMap.remove(firstAccessEntry.getKey()); tileMap.remove(firstAccessEntry.getValue()); - cleanupNeighbourTile(oldestTile,GNode.BORDER_NODE_WEST); - cleanupNeighbourTile(oldestTile,GNode.BORDER_NODE_NORTH); - cleanupNeighbourTile(oldestTile,GNode.BORDER_NODE_EAST); - cleanupNeighbourTile(oldestTile,GNode.BORDER_NODE_SOUTH); + GTileConnector.disconnect(oldestTile,GNode.BORDER_NODE_WEST); + GTileConnector.disconnect(oldestTile,GNode.BORDER_NODE_NORTH); + GTileConnector.disconnect(oldestTile,GNode.BORDER_NODE_EAST); + GTileConnector.disconnect(oldestTile,GNode.BORDER_NODE_SOUTH); } } else { mgLog.e("tileMap.size()="+tileMap.size()+" accessMap.size()="+accessMap.size()); @@ -81,11 +87,4 @@ void service(){ } } - private void cleanupNeighbourTile(GGraphTile gGraphTile, byte border){ - GGraphTile neighbourTile = gGraphTile.neighbourTiles[border]; - if (neighbourTile != null){ - neighbourTile.dropNeighboursToTile(gGraphTile.tileIdx, border); - } - } - } diff --git a/mgmap/src/main/java/mg/mgmap/generic/graph/GTileConnector.java b/mgmap/src/main/java/mg/mgmap/generic/graph/GTileConnector.java new file mode 100644 index 00000000..1a61c52c --- /dev/null +++ b/mgmap/src/main/java/mg/mgmap/generic/graph/GTileConnector.java @@ -0,0 +1,115 @@ +package mg.mgmap.generic.graph; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; + +import mg.mgmap.generic.model.PointModelUtil; +import mg.mgmap.generic.model.WriteablePointModel; +import mg.mgmap.generic.model.WriteablePointModelImpl; +import mg.mgmap.generic.util.basic.MGLog; + +public class GTileConnector { + + private static final MGLog mgLog = new MGLog(MethodHandles.lookup().lookupClass().getName()); + + public static final double CONNECT_THRESHOLD_METER = 0.5; // means 0.5m + + + /** symmetric connect gGraphTile1 and gGraphTile2 */ + static void connect(GGraphTile gGraphTile1, GGraphTile gGraphTile2, boolean horizontal){ // horizontal true: gGraphTile1 is left, gGraphTile2 is right - false: gGraphTile1 is above, gGraphTile2 is below + if ((gGraphTile1 == null) || (gGraphTile2 == null)) return; // cannot connect yet + if ((gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH] == gGraphTile2) && + (gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH] == gGraphTile1)){ // tiles already connected + return; + } + if ((gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH] != null) || + (gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH] != null)){ // inconsistency detected!!!! + mgLog.e("connectTiles failed"+gGraphTile1.getTileX()+","+gGraphTile1.getTileY()+" "+gGraphTile2.getTileX()+","+gGraphTile2.getTileY()+" "+horizontal); + mgLog.e("found="+gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH]+" expected="+gGraphTile2); + mgLog.e("found="+gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH]+" expected="+gGraphTile1); + throw new IllegalArgumentException("inconsistent neighbour tiles - check logfile for more details."); + } + double connectAt = (horizontal)?gGraphTile1.tbBox.maxLongitude:gGraphTile1.tbBox.minLatitude; + if (horizontal? (connectAt!=gGraphTile2.tbBox.minLongitude):(connectAt!=gGraphTile2.tbBox.maxLatitude)){ + throw new IllegalArgumentException("cannot connectHorizontal Tiles with BB " + gGraphTile1.tbBox +" and "+gGraphTile2.tbBox); + } + ArrayList borderNodes1 = new ArrayList<>(); + for (GNode node : gGraphTile1.getNodes()){ + if ((horizontal)?(node.getLon() == connectAt):(node.getLat() == connectAt)){ + borderNodes1.add(node); + } + } + ArrayList borderNodes2 = new ArrayList<>(); + for (GNode node : gGraphTile2.getNodes()){ + if ((horizontal)?(node.getLon() == connectAt):(node.getLat() == connectAt)){ + borderNodes2.add(node); + } + } + final ArrayList remainingNodes1 = new ArrayList<>(borderNodes1); + final ArrayList remainingNodes2 = new ArrayList<>(borderNodes2); + double threshold = (horizontal)? PointModelUtil.latitudeDistance(CONNECT_THRESHOLD_METER):PointModelUtil.longitudeDistance(CONNECT_THRESHOLD_METER, connectAt); + for (GNode node1 : borderNodes1){ + for (GNode node2 : borderNodes2){ + if ( (horizontal?Math.abs( node1.getLat() - node2.getLat() ):Math.abs( node1.getLon() - node2.getLon() )) <= threshold){ //distance less than 0.5m -> connect nodes + node1.bidirectionalConnect(node2); + remainingNodes1.remove(node1); + remainingNodes2.remove(node2); + } + } + } + if ((remainingNodes1.size() > 0) && ((remainingNodes2.size() > 0))){ + ArrayList stillRemainingNodes1 = new ArrayList<>(remainingNodes1); + ArrayList stillRemainingNodes2 = new ArrayList<>(remainingNodes2); + mgLog.v(remainingNodes1::toString); + mgLog.v(remainingNodes2::toString); + for (GNode node1 : remainingNodes1){ + for (GNode node2 : remainingNodes2){ + if ((node1.countNeighbours() == 1) && (node2.countNeighbours() == 1) && (PointModelUtil.distance(node1,node2) CONNECT_THRESHOLD_METER) continue; + if (!PointModelUtil.findApproach(node2,node1Neighbour,node2Neighbour,approachPoint,0)) continue; // approach not found try next points + if (PointModelUtil.distance(approachPoint,node2) > CONNECT_THRESHOLD_METER) continue; + mgLog.d(()->"OK, connect: node1 " + node1 + " node1neighbour " + node1Neighbour + " node2 " + node2 + " node2neighbour " + node2Neighbour); + node1.bidirectionalConnect(node2); + stillRemainingNodes1.remove(node1); + stillRemainingNodes2.remove(node2); + } + } + } + remainingNodes1.clear(); + remainingNodes1.addAll(stillRemainingNodes1); + remainingNodes2.clear(); + remainingNodes2.addAll(stillRemainingNodes2); + } + if ((remainingNodes1.size() > 0) || ((remainingNodes2.size() > 0))){ + mgLog.d(()->"remainings1 " + remainingNodes1); + mgLog.d(()->"remainings2 " + remainingNodes2); + } + gGraphTile1.neighbourTiles[horizontal?GNode.BORDER_NODE_EAST:GNode.BORDER_NODE_SOUTH] = gGraphTile2; + gGraphTile2.neighbourTiles[horizontal?GNode.BORDER_NODE_WEST:GNode.BORDER_NODE_NORTH] = gGraphTile1; + } + + /** + * Asymmetric operation, remove all references to gGraphTile and its nodes (so garbage collector can remove it) + * @param gGraphTile tile that should not longer be connected + * @param border border of tile where neighbourTile should not longer reference to the tile or nodes of the tile + */ + static void disconnect(GGraphTile gGraphTile, byte border){ + GGraphTile neighbourTile = gGraphTile.neighbourTiles[border]; + if (neighbourTile == null) return; // nothing to do + byte neighboursBorder = GNode.oppositeBorder(border); + for (GNode node : neighbourTile.getNodes()){ + if ((node.borderNode & neighboursBorder) != 0){ + node.removeNeighbourNode(gGraphTile.tileIdx); + } + } + assert (neighbourTile.neighbourTiles[neighboursBorder].tileIdx == gGraphTile.tileIdx) + :"neighbourTiles[neighboursBorder].tileIdx"+neighbourTile.neighbourTiles[neighboursBorder].tileIdx+" tileIdx="+gGraphTile.tileIdx; + neighbourTile.neighbourTiles[neighboursBorder] = null; + } + + +}