diff --git a/mapbuilder/pom.xml b/mapbuilder/pom.xml index 523013e73f4d10126fdf1eeff9ece035df745060..c89e4ee59ac0836a19cb73e8a7230e1434f0142f 100644 --- a/mapbuilder/pom.xml +++ b/mapbuilder/pom.xml @@ -18,6 +18,13 @@ </properties> <dependencies> + + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.10.1</version> + </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> @@ -34,16 +41,12 @@ <artifactId>json</artifactId> <version>20230227</version> </dependency> - <dependency> - <groupId>com.google.code.gson</groupId> - <artifactId>gson</artifactId> - <version>2.10.1</version> - </dependency> - <dependency> + <dependency> <groupId>de.fuberlin.navigator</groupId> <artifactId>proto</artifactId> - <version>1.00.05</version> + <version>1.00.08</version> </dependency> + </dependencies> @@ -79,4 +82,31 @@ <url>https://git.imp.fu-berlin.de/api/v4/projects/8345/packages/maven</url> </repository> </repositories> + + + + <dependencyManagement> + + + <dependencies> + + + <dependency> + <groupId>com.google.protobuf</groupId> + <artifactId>protobuf-java</artifactId> + <version>3.22.2</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.10</version> + </dependency> + + + + </dependencies> + + </dependencyManagement> </project> diff --git a/mapbuilder/src/main/java/map/builder/App.java b/mapbuilder/src/main/java/map/builder/App.java index 01ec7d2b690fcede4d60cc55946a9e5a3e9c3d2a..cb773aa8823867387a29871121bb12e9b3ccbc84 100644 --- a/mapbuilder/src/main/java/map/builder/App.java +++ b/mapbuilder/src/main/java/map/builder/App.java @@ -1,52 +1,79 @@ package map.builder; +import static map.builder.utilities.Config.ROAD_NETWORK_OUTPUT_PATH; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + import de.fuberlin.navigator.protos.map_builder.RoadNetwork; +import map.builder.osm.OSMConnectedComponentParser; import map.builder.osm.OSMFetcher; import map.builder.osm.OSMParser; import map.builder.osm.json.model.OSMJsonDto; import map.builder.utilities.BoundingBox; import map.builder.utilities.Config; +import map.builder.utilities.ConnectedComponentGraph; import map.builder.utilities.FileHandler; -import java.io.IOException; -import java.util.List; - -import static map.builder.utilities.Config.ROAD_NETWORK_OUTPUT_PATH; - public class App { public static void main(String[] args) throws IOException { System.out.println("Map builder started!"); Config.load(); RoadNetwork.Builder roadNetworkBuilder = RoadNetwork.newBuilder(); - OSMParser parser = new OSMParser(roadNetworkBuilder); + OSMParser parser = new OSMParser(); // A small BBox inside Cottbus /* - float minLat = 51.765120241998865f; - float minLon = 14.32669617537409f; - float maxLat = 51.77116774623326f; - float maxLon = 14.330334220133722f; + * float minLat = 51.765120241998865f; + * float minLon = 14.32669617537409f; + * float maxLat = 51.77116774623326f; + * float maxLon = 14.330334220133722f; */ - // BBox around Central + South Brandenburg (about 1.2GB in size) - float minLat = 52.788831211664586f; - float minLon = 11.333053381241731f; - float maxLat = 50.92993593954551f; - float maxLon = 14.714574575766516f; + // smaller BBox inside Cottbus, better for the debug tool + float minLat = 51.754092326645475f; + float minLon = 14.300615062713623f; + float maxLat = 51.766591637718435f; + float maxLon = 14.314413070678711f; + + // BBox around Cottbus + /* + * float minLat = 51.714692361306376f; + * float minLon = 14.26197052001953f; + * float maxLat = 51.79290380494767f; + * float maxLon = 14.415779113769531f; + */ BoundingBox bbox = new BoundingBox(minLat, minLon, maxLat, maxLon); List<OSMJsonDto> restrictions = OSMFetcher.fetchTurnRestrictions(bbox); List<OSMJsonDto> roads = OSMFetcher.fetchNodesAndWays(bbox); - System.out.println("Starting to parse."); parser.parseTurnRestrictions(restrictions); parser.parseRoads(roads); System.out.println("Parsed road network."); - RoadNetwork roadNetwork = roadNetworkBuilder.build(); + // set it to 0 so that it can be collected by garbage collector + restrictions = null; + roads = null; + bbox = null; + + // create the nodes graph in order to run LCC on it + ConnectedComponentGraph graph = new ConnectedComponentGraph(); + OSMConnectedComponentParser.addNodes(parser.nodes, graph); + OSMConnectedComponentParser.addEdges(parser.segments, graph); + + ArrayList<Long> component = graph.getSCCs(); + + // cleanup + graph = null; + OSMConnectedComponentParser.cleanUp(parser.nodes, parser.segments, component); + + RoadNetwork roadNetwork = roadNetworkBuilder.putAllNodes(parser.nodes).putAllSegments(parser.segments) + .addAllTurnRestrictions(parser.restrictions).build(); System.out.println("Turn restrictions count: " + roadNetwork.getTurnRestrictionsCount()); System.out.println("Nodes count: " + roadNetwork.getNodesCount()); System.out.println("Segments count: " + roadNetwork.getSegmentsCount()); diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMConnectedComponentParser.java b/mapbuilder/src/main/java/map/builder/osm/OSMConnectedComponentParser.java new file mode 100644 index 0000000000000000000000000000000000000000..d66ab80b4f87499c1a2847d93bbd13a0a0f42a85 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/OSMConnectedComponentParser.java @@ -0,0 +1,46 @@ +package map.builder.osm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; + +import de.fuberlin.navigator.protos.map_builder.Node; +import de.fuberlin.navigator.protos.map_builder.Segment; +import map.builder.utilities.ConnectedComponentGraph; + +public class OSMConnectedComponentParser { + + public static void addNodes(Map<Long, Node> nodesMap, ConnectedComponentGraph graph) { + for (Long nodeId : nodesMap.keySet()) { + graph.addNode(nodeId); + } + } + + public static void addEdges(Map<Long, Segment> segmentsMap, ConnectedComponentGraph graph) { + for (Long edgeId : segmentsMap.keySet()) { + Segment segment = segmentsMap.get(edgeId); + Long startNode = segment.getStartNode(); + Long endNode = segment.getEndNode(); + graph.addEdge(startNode, endNode); + if (!segment.getOneWay()) { + graph.addEdge(endNode, startNode); + } + } + } + + public static void cleanUp(Map<Long, Node> nodesMap, Map<Long, Segment> segmentsMap, ArrayList<Long> component) { + nodesMap.keySet().retainAll(Collections.unmodifiableCollection(component)); + + ArrayList<Long> segmentIDsToRemove = new ArrayList<Long>(); + for (Long key : segmentsMap.keySet()) { + Segment segment = segmentsMap.get(key); + Long startNodeID = segment.getStartNode(); + Long endNodeID = segment.getEndNode(); + if ((!component.contains(startNodeID)) && (!component.contains(endNodeID))) { + segmentIDsToRemove.add(key); + } + } + segmentsMap.keySet().removeAll(segmentIDsToRemove); + } + +} \ No newline at end of file diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java b/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java index a867735a15176fdc919889f34986b5ea98e938f6..d7760b3b100cb128a1911638aeec4c95a1d18b63 100644 --- a/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java +++ b/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java @@ -1,6 +1,8 @@ package map.builder.osm; -import java.io.BufferedReader; +import static map.builder.osm.json.serialization.OSMJsonDtoDeserializer.Mode.NODES_AND_WAYS; +import static map.builder.osm.json.serialization.OSMJsonDtoDeserializer.Mode.TURN_RESTRICTIONS; + import java.io.DataOutputStream; import java.io.File; import java.io.FileWriter; @@ -16,15 +18,11 @@ import java.util.stream.Collectors; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.stream.JsonReader; + import map.builder.osm.json.model.OSMJsonDto; import map.builder.osm.json.serialization.OSMJsonDtoDeserializer; -import org.json.JSONObject; - import map.builder.utilities.BoundingBox; -import static map.builder.osm.json.serialization.OSMJsonDtoDeserializer.Mode.NODES_AND_WAYS; -import static map.builder.osm.json.serialization.OSMJsonDtoDeserializer.Mode.TURN_RESTRICTIONS; - public class OSMFetcher { private final static String OverpassURL = "https://overpass.kumi.systems/api/interpreter"; private final static String nodeQuery = "[out:json];way[highway](%s);(._;>;);out;"; @@ -35,7 +33,7 @@ public class OSMFetcher { public static List<OSMJsonDto> fetchTurnRestrictions(BoundingBox boundingBox) throws IOException { System.out.println("Start to fetch turn restrictions."); - List<OSMJsonDto> dtos = OSMFetcher.runQueryForBBox(OSMFetcher.relationQuery, boundingBox, TURN_RESTRICTIONS); + List<OSMJsonDto> dtos = OSMFetcher.runQueryForBBox(OSMFetcher.relationQuery, boundingBox, TURN_RESTRICTIONS); System.out.println("Turn restrictions fetched."); return dtos; @@ -49,7 +47,8 @@ public class OSMFetcher { return dtos; } - private static List<OSMJsonDto> runQueryForBBox(String query, BoundingBox bbox, OSMJsonDtoDeserializer.Mode mode) throws IOException { + private static List<OSMJsonDto> runQueryForBBox(String query, BoundingBox bbox, OSMJsonDtoDeserializer.Mode mode) + throws IOException { InputStream response = requestData(query, bbox); JsonReader jsonReader = new JsonReader(new InputStreamReader(response)); @@ -104,11 +103,10 @@ public class OSMFetcher { FileWriter file = new FileWriter(path); file.write( "[" + - data.stream() - .map(OSMJsonDto::toJson) - .collect(Collectors.joining(", ")) - + "]" - ); + data.stream() + .map(OSMJsonDto::toJson) + .collect(Collectors.joining(", ")) + + "]"); file.close(); } } diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMParser.java b/mapbuilder/src/main/java/map/builder/osm/OSMParser.java index 3e050ad3deb1f44cb3bb0e6844632165013d0cef..b8bc958a985d8bc8e1277d8c0b9c84efaf76a7e6 100644 --- a/mapbuilder/src/main/java/map/builder/osm/OSMParser.java +++ b/mapbuilder/src/main/java/map/builder/osm/OSMParser.java @@ -4,24 +4,28 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import map.builder.osm.json.model.*; - import de.fuberlin.navigator.protos.map_builder.Coordinates; import de.fuberlin.navigator.protos.map_builder.Node; import de.fuberlin.navigator.protos.map_builder.Restriction; -import de.fuberlin.navigator.protos.map_builder.RoadNetwork; import de.fuberlin.navigator.protos.map_builder.Segment; +import map.builder.osm.json.model.OSMJsonDto; +import map.builder.osm.json.model.OSMJsonMemberDto; +import map.builder.osm.json.model.OSMJsonNodeDto; +import map.builder.osm.json.model.OSMJsonTurnRestrictionDto; +import map.builder.osm.json.model.OSMJsonWayDto; import map.builder.utilities.ComputationalUtils; - public class OSMParser { private final Coordinates.Builder coordinatesBuilder; private final HashMap<Long, Node> nodesDump; - RoadNetwork.Builder roadNetworkBuilder; - public OSMParser(RoadNetwork.Builder roadNetworkBuilder) { - this.roadNetworkBuilder = roadNetworkBuilder; + // maps to put the entries + public HashMap<Long, Node> nodes = new HashMap<Long, Node>(); + public HashMap<Long, Segment> segments = new HashMap<Long, Segment>(); + public ArrayList<Restriction> restrictions = new ArrayList<Restriction>(); + + public OSMParser() { this.coordinatesBuilder = Coordinates.newBuilder(); this.nodesDump = new HashMap<>(); } @@ -50,6 +54,7 @@ public class OSMParser { } } } + } private void splitSegment(OSMJsonWayDto dto) { @@ -73,8 +78,7 @@ public class OSMParser { try { line = this.findNodePositions(geometry); - } - catch (NullPointerException e) { + } catch (NullPointerException e) { System.out.println("Dropping segment with ID " + osmId); System.out.println(e.getMessage()); return; @@ -86,7 +90,12 @@ public class OSMParser { boolean oneWay = SegmentUtils.isSegmentOneway(dto); int maxSpeed = SegmentUtils.getMaxSpeed(dto); - int internalId = this.roadNetworkBuilder.getSegmentsCount(); + System.out.printf("Max speed: %d \n", maxSpeed); + System.out.printf("Way id: %d, Start node id : %d, End node id: %d \n", osmId, + this.nodes.get(startNodeId).getId(), + this.nodes.get(endNodeId).getId()); + + long internalId = this.segments.size(); Segment segment = Segment.newBuilder() .setOsmId(osmId) @@ -100,19 +109,19 @@ public class OSMParser { .setLength(this.computeSegmentLength(line)) .build(); - this.roadNetworkBuilder.putSegments(internalId, segment); + this.segments.put(internalId, segment); } private void moveNodeToProto(Node node) { long osmId = node.getOsmId(); - this.roadNetworkBuilder.putNodes(osmId, node); + this.nodes.put(osmId, node); } private void createNode(OSMJsonNodeDto dto) { Coordinates position = this.createLocationFromElement(dto.getLon(), dto.getLat()); Node node = Node.newBuilder() - .setId(this.roadNetworkBuilder.getNodesCount()) + .setId(this.nodes.size() + this.nodesDump.size()) .setOsmId(dto.getOsmId()) .setPosition(position) .build(); @@ -134,8 +143,10 @@ public class OSMParser { } /** - * Returns an array list of array lists of geometries. If the outer list has only one entry, it means the segment + * Returns an array list of array lists of geometries. If the outer list has + * only one entry, it means the segment * doesn't need to be split, otherwise it needs to. + * * @param nodeIds * @return * @throws NullPointerException @@ -168,12 +179,11 @@ public class OSMParser { private Node findParsedNodeById(long osmId) throws NullPointerException { if (this.nodesDump.containsKey(osmId)) { Node node = this.nodesDump.get(osmId); - return node; } - if (this.roadNetworkBuilder.getNodesMap().containsKey(osmId)) { - return this.roadNetworkBuilder.getNodesMap().get(osmId); + if (this.nodes.containsKey(osmId)) { + return this.nodes.get(osmId); } throw new NullPointerException("Node with id " + osmId + " not found!"); @@ -210,7 +220,7 @@ public class OSMParser { } } Restriction restriction = Restriction.newBuilder().setFromId(from).setToId(to).build(); - this.roadNetworkBuilder.addTurnRestrictions(restriction); + this.restrictions.add(restriction); } } diff --git a/mapbuilder/src/main/java/map/builder/utilities/ComputationalUtils.java b/mapbuilder/src/main/java/map/builder/utilities/ComputationalUtils.java index 706865e78abdb592c2f15cfef075ca60857f069a..371b6e3358ee70d1b79fb7377b46d1bcb9911aed 100644 --- a/mapbuilder/src/main/java/map/builder/utilities/ComputationalUtils.java +++ b/mapbuilder/src/main/java/map/builder/utilities/ComputationalUtils.java @@ -3,8 +3,6 @@ package map.builder.utilities; import de.fuberlin.navigator.protos.map_builder.Coordinates; public class ComputationalUtils { - private ComputationalUtils() { - } public static double haversine(Coordinates position_0, de.fuberlin.navigator.protos.map_builder.Coordinates position_1) { diff --git a/mapbuilder/src/main/java/map/builder/utilities/ConnectedComponentGraph.java b/mapbuilder/src/main/java/map/builder/utilities/ConnectedComponentGraph.java new file mode 100644 index 0000000000000000000000000000000000000000..9504289a703259db68c28ad8783f63c2ca125eac --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/utilities/ConnectedComponentGraph.java @@ -0,0 +1,165 @@ +package map.builder.utilities; + +import java.util.ArrayList; +import java.util.HashMap; +// Code from : https://www.geeksforgeeks.org/strongly-connected-components/ +// Java implementation of Kosaraju's algorithm to print all SCCs +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Stack; + +//TODO: change data structure here to be more appropriate +// This class represents a directed ConnectedComponentGraph using adjacency list +// representation +public class ConnectedComponentGraph { + private HashMap<Long, LinkedList<Long>> adj; // Adjacency List + + // Constructor + public ConnectedComponentGraph() { + adj = new HashMap<Long, LinkedList<Long>>(); + } + + public void addNode(Long v) { + adj.put(v, new LinkedList<Long>()); + } + + // Function to add an edge into the ConnectedComponentGraph + public void addEdge(Long v, Long w) { + adj.get(v).add(w); + } + + // A recursive function to print DFS starting from v + public ArrayList<Long> DFSUtil(Long v, HashMap<Long, Boolean> visited) { + // Mark the current node as visited and print it + visited.put(v, true); + + ArrayList<Long> component = new ArrayList<Long>(); + Long n; + + // Recur for all the vertices adjacent to this vertex + Iterator<Long> i = adj.get(v).iterator(); + while (i.hasNext()) { + n = i.next(); + if (!visited.get(n)) + component = DFSUtil(n, visited, component); + } + component.add(v); + return component; + } + + public ArrayList<Long> DFSUtil(Long v, HashMap<Long, Boolean> visited, ArrayList<Long> list) { + // Mark the current node as visited and print it + visited.put(v, true); + Long n; + + // Recur for all the vertices adjacent to this vertex + Iterator<Long> i = adj.get(v).iterator(); + while (i.hasNext()) { + n = i.next(); + if (!visited.get(n)) + DFSUtil(n, visited, list); + } + list.add(v); + return list; + } + + // Function that returns reverse (or transpose) of this ConnectedComponentGraph + public ConnectedComponentGraph getTranspose() { + ConnectedComponentGraph g = new ConnectedComponentGraph(); + for (Long v : adj.keySet()) { + g.addNode(v); + } + for (Long v : adj.keySet()) { + // Recur for all the vertices adjacent to this vertex + Iterator<Long> i = adj.get(v).listIterator(); + while (i.hasNext()) { + Long next = i.next(); + g.adj.get(next).add(v); + } + } + return g; + } + + public void fillOrder(Long v, HashMap<Long, Boolean> visited, Stack<Long> stack) { + // Mark the current node as visited and print it + visited.put(v, true); + + // Recur for all the vertices adjacent to this vertex + Iterator<Long> i = adj.get(v).iterator(); + while (i.hasNext()) { + Long n = i.next(); + if (!visited.get(n)) + fillOrder(n, visited, stack); + } + + // All vertices reachable from v are processed by now, + // push v to Stack + stack.push(v); + } + + // The main function that finds and prints all strongly + // connected components + public ArrayList<Long> getSCCs() { + Stack<Long> stack = new Stack<Long>(); + + // Mark all the vertices as not visited (For first DFS) + HashMap<Long, Boolean> visited = new HashMap<Long, Boolean>(); + for (Long i : this.adj.keySet()) { + visited.put(i, false); + } + + // Fill vertices in stack according to their finishing + // times + for (Long i : this.adj.keySet()) + if (visited.get(i) == false) + fillOrder(i, visited, stack); + + // Create a reversed ConnectedComponentGraph + ConnectedComponentGraph gr = getTranspose(); + + // Mark all the vertices as not visited (For second DFS) + for (Long i : this.adj.keySet()) + visited.put(i, false); + + // Now process all vertices in order defined by Stack + ArrayList<Long> largestComponent = new ArrayList<Long>(); + int maxLength = 0; + while (stack.empty() == false) { + // Pop a vertex from stack + Long v = stack.pop(); + + // Print Strongly connected component of the popped vertex + if (visited.get(v) == false) { + ArrayList<Long> component = gr.DFSUtil(v, visited); + if (maxLength < component.size()) { + maxLength = component.size(); + largestComponent = component; + } + } + } + return largestComponent; + } + + // Driver method + public static void main(String args[]) { + // Create a ConnectedComponentGraph given in the above diagram + ConnectedComponentGraph g = new ConnectedComponentGraph(); + // add nodes + g.addNode((long) 0); + g.addNode((long) 1); + g.addNode((long) 2); + g.addNode((long) 3); + g.addNode((long) 4); + + g.addEdge((long) 1, (long) 0); + g.addEdge((long) 0, (long) 2); + g.addEdge((long) 2, (long) 1); + g.addEdge((long) 0, (long) 3); + g.addEdge((long) 3, (long) 4); + + System.out.println("Following are strongly connected components " + + "in given ConnectedComponentGraph "); + g.getSCCs(); + } +} +// This code is contributed by Aakash Hasija \ No newline at end of file