From 666326928a57aabe47f9b9ccccf3d052dd9a958e Mon Sep 17 00:00:00 2001 From: lazarog98 <lazarog98@mi.fu-berlin.de> Date: Thu, 30 Mar 2023 06:05:01 +0000 Subject: [PATCH] Makes Json processing waaay more efficient (and also more faster). Use of Gson... --- mapbuilder/pom.xml | 5 + mapbuilder/src/main/java/map/builder/App.java | 26 ++-- .../main/java/map/builder/osm/OSMFetcher.java | 82 +++++++----- .../java/map/builder/osm/OSMJSONUtils.java | 71 ----------- .../java/map/builder/osm/OSMJsonUtils.java | 60 +++++++++ .../src/main/java/map/builder/osm/OSMKey.java | 5 - .../main/java/map/builder/osm/OSMParser.java | 119 ++++++++---------- .../java/map/builder/osm/SegmentUtils.java | 86 ++++++------- .../builder/osm/json/model/OSMJsonDto.java | 16 +++ .../osm/json/model/OSMJsonMemberDto.java | 39 ++++++ .../osm/json/model/OSMJsonNodeDto.java | 41 ++++++ .../json/model/OSMJsonTurnRestrictionDto.java | 30 +++++ .../builder/osm/json/model/OSMJsonWayDto.java | 75 +++++++++++ .../serialization/OSMJsonDtoDeserializer.java | 33 +++++ .../OSMJsonNodeDtoDeserializer.java | 27 ++++ ...OSMJsonTurnRestrictionDtoDeserializer.java | 43 +++++++ .../OSMJsonWayDtoDeserializer.java | 50 ++++++++ 17 files changed, 581 insertions(+), 227 deletions(-) delete mode 100644 mapbuilder/src/main/java/map/builder/osm/OSMJSONUtils.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/OSMJsonUtils.java delete mode 100644 mapbuilder/src/main/java/map/builder/osm/OSMKey.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonDto.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonMemberDto.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonNodeDto.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonTurnRestrictionDto.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonWayDto.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonDtoDeserializer.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonNodeDtoDeserializer.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonTurnRestrictionDtoDeserializer.java create mode 100644 mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonWayDtoDeserializer.java diff --git a/mapbuilder/pom.xml b/mapbuilder/pom.xml index 6217efb..523013e 100644 --- a/mapbuilder/pom.xml +++ b/mapbuilder/pom.xml @@ -34,6 +34,11 @@ <artifactId>json</artifactId> <version>20230227</version> </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.10.1</version> + </dependency> <dependency> <groupId>de.fuberlin.navigator</groupId> <artifactId>proto</artifactId> diff --git a/mapbuilder/src/main/java/map/builder/App.java b/mapbuilder/src/main/java/map/builder/App.java index 3aaccbd..01ec7d2 100644 --- a/mapbuilder/src/main/java/map/builder/App.java +++ b/mapbuilder/src/main/java/map/builder/App.java @@ -1,16 +1,16 @@ package map.builder; -import java.io.IOException; - -import map.builder.utilities.Config; -import org.json.JSONArray; - import de.fuberlin.navigator.protos.map_builder.RoadNetwork; 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.FileHandler; +import java.io.IOException; +import java.util.List; + import static map.builder.utilities.Config.ROAD_NETWORK_OUTPUT_PATH; public class App { @@ -29,17 +29,17 @@ public class App { float maxLon = 14.330334220133722f; */ - // BBox around central and south Brandenburg - float minLat = 51.968768f; - float minLon = 12.144459f; - float maxLat = 52.674160f; - float maxLon = 15.016778f; + // 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; BoundingBox bbox = new BoundingBox(minLat, minLon, maxLat, maxLon); - JSONArray restrictions = OSMFetcher.fetchTurnRestrictions(bbox); - JSONArray roads = OSMFetcher.fetchNodesAndWays(bbox); - OSMFetcher.dumpJsonData(roads, "test_data.json"); + List<OSMJsonDto> restrictions = OSMFetcher.fetchTurnRestrictions(bbox); + List<OSMJsonDto> roads = OSMFetcher.fetchNodesAndWays(bbox); + System.out.println("Starting to parse."); parser.parseTurnRestrictions(restrictions); diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java b/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java index bc77269..a867735 100644 --- a/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java +++ b/mapbuilder/src/main/java/map/builder/osm/OSMFetcher.java @@ -9,12 +9,22 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; - -import org.json.JSONArray; +import java.util.ArrayList; +import java.util.List; +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;"; @@ -23,28 +33,51 @@ public class OSMFetcher { private OSMFetcher() { } - public static JSONArray fetchTurnRestrictions(BoundingBox boundingBox) throws IOException { + public static List<OSMJsonDto> fetchTurnRestrictions(BoundingBox boundingBox) throws IOException { System.out.println("Start to fetch turn restrictions."); - JSONArray jsonArray = OSMFetcher.runQueryForBBox(OSMFetcher.relationQuery, boundingBox); + List<OSMJsonDto> dtos = OSMFetcher.runQueryForBBox(OSMFetcher.relationQuery, boundingBox, TURN_RESTRICTIONS); System.out.println("Turn restrictions fetched."); - return jsonArray; + return dtos; } - public static JSONArray fetchNodesAndWays(BoundingBox boundingBox) throws IOException { + public static List<OSMJsonDto> fetchNodesAndWays(BoundingBox boundingBox) throws IOException { System.out.println("Start to fetch nodes and ways."); - JSONArray jsonArray = OSMFetcher.runQueryForBBox(OSMFetcher.nodeQuery, boundingBox); + List<OSMJsonDto> dtos = OSMFetcher.runQueryForBBox(OSMFetcher.nodeQuery, boundingBox, NODES_AND_WAYS); System.out.println("Nodes and ways fetched."); - return jsonArray; + return dtos; } - private static JSONArray runQueryForBBox(String query, BoundingBox bbox) throws IOException { + private static List<OSMJsonDto> runQueryForBBox(String query, BoundingBox bbox, OSMJsonDtoDeserializer.Mode mode) throws IOException { InputStream response = requestData(query, bbox); - - JSONObject parsedResponse = getJSONObjectFromStream(response); - return parsedResponse.getJSONArray("elements"); + JsonReader jsonReader = new JsonReader(new InputStreamReader(response)); + List<OSMJsonDto> dtos = new ArrayList<>(); + + Gson gson = new GsonBuilder() + .registerTypeAdapter(OSMJsonDto.class, new OSMJsonDtoDeserializer(mode)) + .create(); + + jsonReader.beginObject(); + String key; + while ((key = jsonReader.nextName()) != null) { + if (key.equals("elements")) { + jsonReader.beginArray(); + while (jsonReader.hasNext()) { + OSMJsonDto dto = gson.fromJson(jsonReader, OSMJsonDto.class); + + if (dto != null) { + dtos.add(dto); + } + } + + break; + } + jsonReader.skipValue(); + } + + return dtos; } private static InputStream requestData(String query, BoundingBox bbox) throws IOException { @@ -65,26 +98,17 @@ public class OSMFetcher { return connection.getInputStream(); } - private static JSONObject getJSONObjectFromStream(InputStream stream) throws IOException { - BufferedReader rd = new BufferedReader(new InputStreamReader(stream)); - StringBuilder response = new StringBuilder(); // or StringBuffer if Java version 5+ - String line; - - while ((line = rd.readLine()) != null) { - - response.append(line); - response.append('\n'); - } - rd.close(); - - return new JSONObject(response.toString()); - } - - public static void dumpJsonData(JSONArray data, String path) throws IOException { + public static void dumpJsonData(List<OSMJsonDto> data, String path) throws IOException { File yourFile = new File(path); yourFile.createNewFile(); FileWriter file = new FileWriter(path); - file.write(data.toString()); + file.write( + "[" + + data.stream() + .map(OSMJsonDto::toJson) + .collect(Collectors.joining(", ")) + + "]" + ); file.close(); } } diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMJSONUtils.java b/mapbuilder/src/main/java/map/builder/osm/OSMJSONUtils.java deleted file mode 100644 index d97dc67..0000000 --- a/mapbuilder/src/main/java/map/builder/osm/OSMJSONUtils.java +++ /dev/null @@ -1,71 +0,0 @@ -package map.builder.osm; - -import org.json.JSONObject; - -import java.util.List; -import java.util.Objects; - -public class OSMJSONUtils { - private OSMJSONUtils() {} - - public static boolean isNode(JSONObject element) { - return Objects.equals(OSMJSONUtils.getType(element), "node"); - } - public static boolean isWay(JSONObject element) { - return Objects.equals(OSMJSONUtils.getType(element), "way"); - } - public static boolean isRelation(JSONObject element) { - return Objects.equals(OSMJSONUtils.getType(element), "relation"); - } - - public static boolean isTRFrom(JSONObject turnRestriction) { - return Objects.equals(OSMJSONUtils.getTRType(turnRestriction), "from"); - } - - public static boolean isTRTo(JSONObject turnRestriction) { - return Objects.equals(OSMJSONUtils.getTRType(turnRestriction), "to"); - } - - public static boolean isTRVia(JSONObject turnRestriction) { - return Objects.equals(OSMJSONUtils.getTRType(turnRestriction), "via"); - } - - public static String getType(JSONObject element) { - return element.getString(OSMKey.type.name()); - } - - public static String getTRType(JSONObject turnRestriction) { - return turnRestriction.getString(OSMKey.role.name()); - } - - public static boolean hasTag(JSONObject element, String tag) { - if (!element.has("tags")) { - return false; - } - - JSONObject tags = element.getJSONObject("tags"); - - return tags.has(tag); - } - - public static String getTag(JSONObject element, String tag) { - if (!OSMJSONUtils.hasTag(element, tag)) { - return null; - } - - JSONObject tags = element.getJSONObject("tags"); - return tags.getString(tag); - } - - public static boolean isTagValueOneOf(JSONObject element, String tag, List<String> values) { - String tagValue = OSMJSONUtils.getTag(element, tag); - - return values.contains(tagValue); - } - - public static boolean isTagValueEqualTo(JSONObject element, String tag, String value) { - String tagValue = OSMJSONUtils.getTag(element, tag); - - return Objects.equals(tagValue, value); - } -} diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMJsonUtils.java b/mapbuilder/src/main/java/map/builder/osm/OSMJsonUtils.java new file mode 100644 index 0000000..56a94a9 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/OSMJsonUtils.java @@ -0,0 +1,60 @@ +package map.builder.osm; + +import map.builder.osm.json.model.*; +import org.json.JSONObject; + +import java.util.List; +import java.util.Objects; + +public class OSMJsonUtils { + private OSMJsonUtils() {} + + public static boolean isNode(OSMJsonDto dto) { + return Objects.equals(OSMJsonUtils.getType(dto), "node") && dto instanceof OSMJsonNodeDto; + } + public static boolean isWay(OSMJsonDto dto) { + return Objects.equals(OSMJsonUtils.getType(dto), "way") && dto instanceof OSMJsonWayDto; + } + public static boolean isRelation(OSMJsonDto dto) { + return Objects.equals(OSMJsonUtils.getType(dto), "relation") && dto instanceof OSMJsonTurnRestrictionDto; + } + + public static boolean isTRFrom(OSMJsonMemberDto dto) { + return Objects.equals(OSMJsonUtils.getTRType(dto), "from"); + } + + public static boolean isTRTo(OSMJsonMemberDto dto) { + return Objects.equals(OSMJsonUtils.getTRType(dto), "to"); + } + + public static boolean isTRVia(OSMJsonMemberDto dto) { + return Objects.equals(OSMJsonUtils.getTRType(dto), "via"); + } + + public static String getType(OSMJsonDto dto) { + return dto.getType(); + } + + public static String getTRType(OSMJsonMemberDto dto) { + return dto.getRole(); + } + + public static boolean hasTag(OSMJsonWayDto dto, String tag) { + return getTag(dto, tag) != null; + } + + public static String getTag(OSMJsonWayDto dto, String tag) { + return switch (tag) { + case "highway" -> dto.getHighway(); + case "junction" -> dto.getJunction(); + + default -> null; + }; + } + + public static boolean isTagValueEqualTo(OSMJsonWayDto dto, String tag, String value) { + String tagValue = getTag(dto, tag); + + return Objects.equals(tagValue, value); + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMKey.java b/mapbuilder/src/main/java/map/builder/osm/OSMKey.java deleted file mode 100644 index 352c9c3..0000000 --- a/mapbuilder/src/main/java/map/builder/osm/OSMKey.java +++ /dev/null @@ -1,5 +0,0 @@ -package map.builder.osm; - -public enum OSMKey { - type, tags, nodes, id, uid, lon, lat, element, role, members, ref -} diff --git a/mapbuilder/src/main/java/map/builder/osm/OSMParser.java b/mapbuilder/src/main/java/map/builder/osm/OSMParser.java index 09a1235..3e050ad 100644 --- a/mapbuilder/src/main/java/map/builder/osm/OSMParser.java +++ b/mapbuilder/src/main/java/map/builder/osm/OSMParser.java @@ -2,11 +2,9 @@ package map.builder.osm; import java.util.ArrayList; import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.List; -import org.json.JSONArray; -import org.json.JSONObject; +import map.builder.osm.json.model.*; import de.fuberlin.navigator.protos.map_builder.Coordinates; import de.fuberlin.navigator.protos.map_builder.Node; @@ -15,6 +13,7 @@ import de.fuberlin.navigator.protos.map_builder.RoadNetwork; import de.fuberlin.navigator.protos.map_builder.Segment; import map.builder.utilities.ComputationalUtils; + public class OSMParser { private final Coordinates.Builder coordinatesBuilder; private final HashMap<Long, Node> nodesDump; @@ -27,33 +26,35 @@ public class OSMParser { this.nodesDump = new HashMap<>(); } - public void parseRoads(JSONArray elements) { - for (int i = 0; i < elements.length(); i++) { - JSONObject element = elements.getJSONObject(i); - - if (OSMJSONUtils.isNode(element)) { - this.createNode(element); + public void parseRoads(List<OSMJsonDto> elements) { + for (OSMJsonDto dto : elements) { + if (OSMJsonUtils.isNode(dto)) { + this.createNode((OSMJsonNodeDto) dto); } } - for (int i = 0; i < elements.length(); i++) { - JSONObject element = elements.getJSONObject(i); - if (OSMJSONUtils.isWay(element) && SegmentUtils.isSegmentDrivable(element)) { - SegmentUtils.markEndNodes(element); + for (OSMJsonDto dto : elements) { + if (OSMJsonUtils.isWay(dto)) { + OSMJsonWayDto wayDto = (OSMJsonWayDto) dto; + if (SegmentUtils.isSegmentDrivable(wayDto)) { + SegmentUtils.markEndNodes(wayDto); + } } } - for (int i = 0; i < elements.length(); i++) { - JSONObject element = elements.getJSONObject(i); - if (OSMJSONUtils.isWay(element) && SegmentUtils.isSegmentDrivable(element)) { - this.splitSegment(element); + for (OSMJsonDto dto : elements) { + if (OSMJsonUtils.isWay(dto)) { + OSMJsonWayDto wayDto = (OSMJsonWayDto) dto; + if (SegmentUtils.isSegmentDrivable(wayDto)) { + this.splitSegment(wayDto); + } } } } - private void splitSegment(JSONObject element) { - JSONArray nodes = element.getJSONArray(OSMKey.nodes.name()); - long osmId = element.getLong(OSMKey.id.name()); + private void splitSegment(OSMJsonWayDto dto) { + long[] nodes = dto.getNodes(); + long osmId = dto.getOsmId(); ArrayList<ArrayList<Long>> geometry = SegmentUtils.splitGeometry(nodes); @@ -61,13 +62,13 @@ public class OSMParser { System.out.println("Split segment with ID " + osmId + " in " + geometry.size()); } - for (int i = 0; i<geometry.size(); i++) { - this.createSegment(element, geometry.get(i)); + for (int i = 0; i < geometry.size(); i++) { + this.createSegment(dto, geometry.get(i)); } } - private void createSegment(JSONObject element, ArrayList<Long> geometry) { - long osmId = element.getLong(OSMKey.id.name()); + private void createSegment(OSMJsonWayDto dto, ArrayList<Long> geometry) { + long osmId = dto.getOsmId(); ArrayList<Coordinates> line = null; try { @@ -82,13 +83,8 @@ public class OSMParser { long startNodeId = geometry.get(0); long endNodeId = geometry.get(geometry.size() - 1); - boolean oneWay = this.isOneWay(element); - int maxSpeed = SegmentUtils.getMaxSpeed(element); - - /* - System.out.printf("Max speed: %d \n", maxSpeed); - System.out.printf("Way id: %d, Start node id : %d, End node id: %d \n", osmId, startNodeId, endNodeId); - */ + boolean oneWay = SegmentUtils.isSegmentOneway(dto); + int maxSpeed = SegmentUtils.getMaxSpeed(dto); int internalId = this.roadNetworkBuilder.getSegmentsCount(); @@ -98,7 +94,7 @@ public class OSMParser { .setOneWay(oneWay) .setMaxSpeed(maxSpeed) .setId(internalId) - .setCategory(SegmentUtils.parseRoadType(element)) + .setCategory(SegmentUtils.parseRoadType(dto)) .setStartNode(startNodeId) .setEndNode(endNodeId) .setLength(this.computeSegmentLength(line)) @@ -107,28 +103,21 @@ public class OSMParser { this.roadNetworkBuilder.putSegments(internalId, segment); } - private boolean isOneWay(JSONObject element) { - return OSMJSONUtils.isTagValueEqualTo(element, "oneway", "yes") || - OSMJSONUtils.isTagValueEqualTo(element, "highway", "motorway") || - OSMJSONUtils.isTagValueEqualTo(element, "junction", "roundabout"); - } - private void moveNodeToProto(Node node) { long osmId = node.getOsmId(); this.roadNetworkBuilder.putNodes(osmId, node); } - private void createNode(JSONObject element) { - Coordinates position = this.createLocationFromElement(element); - long osmId = element.getLong(OSMKey.id.name()); + private void createNode(OSMJsonNodeDto dto) { + Coordinates position = this.createLocationFromElement(dto.getLon(), dto.getLat()); Node node = Node.newBuilder() .setId(this.roadNetworkBuilder.getNodesCount()) - .setOsmId(osmId) + .setOsmId(dto.getOsmId()) .setPosition(position) .build(); - this.nodesDump.put(osmId, node); + this.nodesDump.put(dto.getOsmId(), node); } private double computeSegmentLength(ArrayList<Coordinates> line) { @@ -163,6 +152,7 @@ public class OSMParser { } if (i == 0 || i == nodeIds.size() - 1) { + // if it is the first or last node, we need to save it to the proto moveNodeToProto(currentNode); } @@ -189,18 +179,17 @@ public class OSMParser { throw new NullPointerException("Node with id " + osmId + " not found!"); } - private Coordinates createLocationFromElement(JSONObject element) { + private Coordinates createLocationFromElement(float lon, float lat) { return coordinatesBuilder - .setLon(element.getFloat(OSMKey.lon.name())) - .setLat(element.getFloat(OSMKey.lat.name())) + .setLon(lon) + .setLat(lat) .build(); } - public void parseTurnRestrictions(JSONArray elements) { - for (int i = 0; i < elements.length(); i++) { - JSONObject element = elements.getJSONObject(i); - if (OSMJSONUtils.isRelation(element)) { - JSONArray members = element.getJSONArray(OSMKey.members.name()); + public void parseTurnRestrictions(List<OSMJsonDto> elements) { + for (OSMJsonDto dto : elements) { + if (OSMJsonUtils.isRelation(dto)) { + OSMJsonMemberDto[] members = ((OSMJsonTurnRestrictionDto) dto).getMembers(); if (!hasWayVia(members)) { parseFromTo(members); } @@ -208,17 +197,16 @@ public class OSMParser { } } - private void parseFromTo(JSONArray elements) { - if (!hasWayVia(elements)) { + private void parseFromTo(OSMJsonMemberDto[] members) { + if (!hasWayVia(members)) { int from = 0; int to = 0; - for (int i = 0; i < elements.length(); i++) { - JSONObject element = elements.getJSONObject(i); - if (OSMJSONUtils.isTRFrom(element)) { - from = element.getInt(OSMKey.ref.name()); + for (OSMJsonMemberDto member : members) { + if (OSMJsonUtils.isTRFrom(member)) { + from = (int) member.getOsmId(); } - if (OSMJSONUtils.isTRTo(element)) { - to = element.getInt(OSMKey.ref.name()); + if (OSMJsonUtils.isTRTo(member)) { + to = (int) member.getOsmId(); } } Restriction restriction = Restriction.newBuilder().setFromId(from).setToId(to).build(); @@ -226,14 +214,13 @@ public class OSMParser { } } - private boolean hasWayVia(JSONArray members) { - if (members.length() < 3) { + private boolean hasWayVia(OSMJsonMemberDto[] members) { + if (members.length < 3) { return false; } - for (int i = 0; i < members.length(); i++) { - JSONObject element = members.getJSONObject(i); - if (OSMJSONUtils.isTRVia(element)) { - if (OSMJSONUtils.isWay(element)) { + for (OSMJsonMemberDto member : members) { + if (OSMJsonUtils.isTRVia(member)) { + if (OSMJsonUtils.isWay(member)) { return true; } return false; diff --git a/mapbuilder/src/main/java/map/builder/osm/SegmentUtils.java b/mapbuilder/src/main/java/map/builder/osm/SegmentUtils.java index f328df7..98ad594 100644 --- a/mapbuilder/src/main/java/map/builder/osm/SegmentUtils.java +++ b/mapbuilder/src/main/java/map/builder/osm/SegmentUtils.java @@ -1,17 +1,13 @@ package map.builder.osm; import de.fuberlin.navigator.protos.map_builder.RoadCategory; -import org.json.JSONObject; -import org.json.JSONArray; - -import java.util.HashMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import map.builder.osm.json.model.OSMJsonWayDto; + +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static de.fuberlin.navigator.protos.map_builder.RoadCategory.*; public class SegmentUtils { // this is a hash-map to allow us to check if a node is a start/end node in linear time @@ -37,21 +33,33 @@ public class SegmentUtils { private static final Map<RoadCategory, Integer> speedMap = Map.ofEntries( Map.entry(RoadCategory.ROAD_CATEGORY_HIGHWAY, 120), Map.entry(RoadCategory.ROAD_CATEGORY_MAIN, 90), - Map.entry(RoadCategory.ROAD_CATEGORY_LOCAL, 50), - Map.entry(RoadCategory.ROAD_CATEGORY_RESIDENTIAL, 30), - Map.entry(RoadCategory.ROAD_CATEGORY_INVALID, 0)); - private static final Pattern patternMaxSpeed = Pattern - .compile("^([0-9][\\.0-9]+?)(?:[ ]?(?:km/h|kmh|kph|mph|knots))?$"); - - public static boolean isSegmentDrivable(JSONObject element) { - return OSMJSONUtils.isTagValueOneOf(element, "highway", drivableRoads); + Map.entry(ROAD_CATEGORY_LOCAL, 50), + Map.entry(ROAD_CATEGORY_RESIDENTIAL, 30), + Map.entry(ROAD_CATEGORY_INVALID, 0) + ); + private static final Pattern patternMaxSpeed = Pattern.compile("^([0-9][\\.0-9]+?)(?:[ ]?(?:km/h|kmh|kph|mph|knots))?$"); + + public static boolean isSegmentDrivable(OSMJsonWayDto dto) { + if (dto.getHighway() == null) { + return false; + } + + return drivableRoads.contains(dto.getHighway()); } - public static RoadCategory parseRoadType(JSONObject element) { - String roadTypeTag = OSMJSONUtils.getTag(element, OSMTag.highway.name()); + public static boolean isSegmentOneway(OSMJsonWayDto dto) { + return dto.isOneway() + || OSMJsonUtils.isTagValueEqualTo(dto, "highway", "motorway") + || OSMJsonUtils.isTagValueEqualTo(dto, "junction", "roundabout"); + } - return switch (roadTypeTag) { - case "motorway", "highway" -> RoadCategory.ROAD_CATEGORY_HIGHWAY; + public static RoadCategory parseRoadType(OSMJsonWayDto dto) { + if (dto.getHighway() == null) { + return ROAD_CATEGORY_INVALID; + } + + return switch (dto.getHighway()) { + case "motorway", "highway" -> ROAD_CATEGORY_HIGHWAY; case "trunk", "primary", "secondary", "motorway_link", "trunk_link", "primary_link", "secondary_link" -> RoadCategory.ROAD_CATEGORY_MAIN; case "unclassified", "tertiary", "tertiary_link", "track", "road" -> @@ -61,13 +69,11 @@ public class SegmentUtils { }; } - public static int getMaxSpeed(JSONObject element) { - String maxSpeedTag = OSMTag.maxspeed.name(); + public static int getMaxSpeed(OSMJsonWayDto dto) { // if there's a max speed tag, use it - if (OSMJSONUtils.hasTag(element, maxSpeedTag)) { - String maxspeed = OSMJSONUtils.getTag(element, maxSpeedTag); - Matcher matcherMaxSpeed = SegmentUtils.patternMaxSpeed.matcher(maxspeed); + if (dto.getMaxspeed() != null) { + Matcher matcherMaxSpeed = SegmentUtils.patternMaxSpeed.matcher(dto.getMaxspeed()); if (matcherMaxSpeed.find()) { return Integer.parseInt(matcherMaxSpeed.group(1)); @@ -75,15 +81,15 @@ public class SegmentUtils { } // if no tag is available, parse the max speed based on road category - RoadCategory roadCategory = SegmentUtils.parseRoadType(element); + RoadCategory roadCategory = SegmentUtils.parseRoadType(dto); return SegmentUtils.speedMap.get(roadCategory); } - public static void markEndNodes(JSONObject element) { - JSONArray nodes = element.getJSONArray(OSMKey.nodes.name()); + public static void markEndNodes(OSMJsonWayDto element) { + long[] nodes = element.getNodes(); - long startNodeId = nodes.getLong(0); - long endNodeId = nodes.getLong(nodes.length() - 1); + long startNodeId = nodes[0]; + long endNodeId = nodes[nodes.length - 1]; System.out.println("marking " + startNodeId + " and " + endNodeId); @@ -91,23 +97,17 @@ public class SegmentUtils { SegmentUtils.endNodes.put(endNodeId, true); } - public static boolean isNodeAnEndNode(JSONObject element) { - long osmId = element.getLong(OSMKey.id.name()); - - return isNodeAnEndNode(osmId); - } - public static boolean isNodeAnEndNode(long osmId) { return SegmentUtils.endNodes.containsKey(osmId); } - public static ArrayList<ArrayList<Long>> splitGeometry(JSONArray nodes) { + public static ArrayList<ArrayList<Long>> splitGeometry(long[] nodes) { ArrayList<ArrayList<Long>> geometry = new ArrayList<>(); int from = -1; - for (int i = 0; i<nodes.length(); i++) { - long nodeId = nodes.getLong(i); + for (int i = 0; i < nodes.length; i++) { + long nodeId = nodes[i]; if (isNodeAnEndNode(nodeId)) { if (from == -1) { @@ -130,14 +130,14 @@ public class SegmentUtils { return geometry; } - private static ArrayList<Long> extractIdsFromTo(JSONArray nodes, int from, int to) { + private static ArrayList<Long> extractIdsFromTo(long[] nodes, int from, int to) { ArrayList<Long> extractedIds = new ArrayList<>(); - for (int i = from; i<=to; i++) { - long nodeId = nodes.getLong(i); + for (int i = from; i <= to; i++) { + long nodeId = nodes[i]; extractedIds.add(nodeId); } return extractedIds; } -} +} \ No newline at end of file diff --git a/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonDto.java b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonDto.java new file mode 100644 index 0000000..676fe51 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonDto.java @@ -0,0 +1,16 @@ +package map.builder.osm.json.model; + +public abstract class OSMJsonDto { + + protected String type; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public abstract String toJson(); +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonMemberDto.java b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonMemberDto.java new file mode 100644 index 0000000..0ce8930 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonMemberDto.java @@ -0,0 +1,39 @@ +package map.builder.osm.json.model; + +public class OSMJsonMemberDto extends OSMJsonDto { + + private String type; + private String role; + private long osmId; + + @Override + public String getType() { + return type; + } + + @Override + public void setType(String type) { + this.type = type; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public long getOsmId() { + return osmId; + } + + public void setOsmId(long osmId) { + this.osmId = osmId; + } + + @Override + public String toJson() { + return "{ type: " + type + ", role: " + role + ", osmId:" + osmId + " }"; + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonNodeDto.java b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonNodeDto.java new file mode 100644 index 0000000..6a9b99c --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonNodeDto.java @@ -0,0 +1,41 @@ +package map.builder.osm.json.model; + +public class OSMJsonNodeDto extends OSMJsonDto { + + private long osmId; + private float lon; + private float lat; + + public OSMJsonNodeDto() { + this.type = "node"; + } + + public long getOsmId() { + return osmId; + } + + public void setOsmId(long osmId) { + this.osmId = osmId; + } + + public float getLon() { + return lon; + } + + public void setLon(float lon) { + this.lon = lon; + } + + public float getLat() { + return lat; + } + + public void setLat(float lat) { + this.lat = lat; + } + + @Override + public String toJson() { + return "{ type: node, osmId: " + osmId + ", lon: " + lon + ", lat: " + lat + " }"; + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonTurnRestrictionDto.java b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonTurnRestrictionDto.java new file mode 100644 index 0000000..6289df4 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonTurnRestrictionDto.java @@ -0,0 +1,30 @@ +package map.builder.osm.json.model; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class OSMJsonTurnRestrictionDto extends OSMJsonDto { + + private OSMJsonMemberDto[] members; + + public OSMJsonTurnRestrictionDto() { + this.type = "relation"; + } + + public OSMJsonMemberDto[] getMembers() { + return members; + } + + public void setMembers(OSMJsonMemberDto[] members) { + this.members = members; + } + + @Override + public String toJson() { + String members = Arrays.asList(getMembers()) + .stream() + .map(OSMJsonMemberDto::toJson) + .collect(Collectors.joining(", ")); + return "{ type: relation, members: " + members + " }"; + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonWayDto.java b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonWayDto.java new file mode 100644 index 0000000..9a75040 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/model/OSMJsonWayDto.java @@ -0,0 +1,75 @@ +package map.builder.osm.json.model; + +public class OSMJsonWayDto extends OSMJsonDto { + + private long[] nodes; + private long osmId; + + private String highway; + private boolean oneway; + private String junction; + private String maxspeed; + + public OSMJsonWayDto() { + this.type = "way"; + } + + public long[] getNodes() { + return nodes; + } + + public void setNodes(long[] nodes) { + this.nodes = nodes; + } + + public long getOsmId() { + return osmId; + } + + public void setOsmId(long osmId) { + this.osmId = osmId; + } + + public String getHighway() { + return highway; + } + + public void setHighway(String highway) { + this.highway = highway; + } + + public boolean isOneway() { + return oneway; + } + + public void setOneway(boolean oneway) { + this.oneway = oneway; + } + + public String getJunction() { + return junction; + } + + public void setJunction(String junction) { + this.junction = junction; + } + + public String getMaxspeed() { + return maxspeed; + } + + public void setMaxspeed(String maxspeed) { + this.maxspeed = maxspeed; + } + + @Override + public String toJson() { + String nodes = ""; + for (long node : this.nodes) { + nodes = nodes + node + ","; + } + + return "{ type: way, nodes: [" + nodes + "], osmId: " + osmId + ", highway: " + highway + ", oneway: " + oneway + ", junction: " + + junction + ", maxspeed: " + maxspeed + "}"; + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonDtoDeserializer.java b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonDtoDeserializer.java new file mode 100644 index 0000000..d5dd198 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonDtoDeserializer.java @@ -0,0 +1,33 @@ +package map.builder.osm.json.serialization; + +import com.google.gson.*; +import map.builder.osm.json.model.OSMJsonDto; + +import java.lang.reflect.Type; + +public class OSMJsonDtoDeserializer implements JsonDeserializer<OSMJsonDto> { + + public enum Mode { + TURN_RESTRICTIONS, NODES_AND_WAYS; + } + + private Mode mode; + + public OSMJsonDtoDeserializer(Mode mode) { + this.mode = mode; + } + + @Override + public OSMJsonDto deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + JsonObject object = jsonElement.getAsJsonObject(); + OSMJsonDto dto = switch (object.get("type").getAsString()) { + case "relation" -> new OSMJsonTurnRestrictionDtoDeserializer().deserialize(object, mode); + case "node" -> new OSMJsonNodeDtoDeserializer().deserialize(object, mode); + case "way" -> new OSMJsonWayDtoDeserializer().deserialize(object, mode); + + default -> null; + }; + + return dto; + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonNodeDtoDeserializer.java b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonNodeDtoDeserializer.java new file mode 100644 index 0000000..3dcb25d --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonNodeDtoDeserializer.java @@ -0,0 +1,27 @@ +package map.builder.osm.json.serialization; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import map.builder.osm.json.model.OSMJsonNodeDto; + +import java.util.Iterator; +import java.util.Map; + +import static map.builder.osm.json.serialization.OSMJsonDtoDeserializer.Mode.NODES_AND_WAYS; + +public class OSMJsonNodeDtoDeserializer { + + public OSMJsonNodeDto deserialize(JsonObject obj, OSMJsonDtoDeserializer.Mode mode) throws JsonParseException { + if (!mode.equals(NODES_AND_WAYS)) { + return null; + } + + OSMJsonNodeDto dto = new OSMJsonNodeDto(); + dto.setOsmId(obj.get("id").getAsLong()); + dto.setLat(obj.get("lat").getAsFloat()); + dto.setLon(obj.get("lon").getAsFloat()); + + return dto; + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonTurnRestrictionDtoDeserializer.java b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonTurnRestrictionDtoDeserializer.java new file mode 100644 index 0000000..17e6e48 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonTurnRestrictionDtoDeserializer.java @@ -0,0 +1,43 @@ +package map.builder.osm.json.serialization; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import map.builder.osm.json.model.OSMJsonMemberDto; +import map.builder.osm.json.model.OSMJsonTurnRestrictionDto; + +import java.util.Iterator; +import java.util.Map; + +import static map.builder.osm.json.serialization.OSMJsonDtoDeserializer.Mode.TURN_RESTRICTIONS; + +public class OSMJsonTurnRestrictionDtoDeserializer { + + public OSMJsonTurnRestrictionDto deserialize(JsonObject obj, OSMJsonDtoDeserializer.Mode mode) throws JsonParseException { + if (!mode.equals(TURN_RESTRICTIONS)) { + return null; + } + + OSMJsonTurnRestrictionDto dto = new OSMJsonTurnRestrictionDto(); + setMembers(obj.getAsJsonArray("members"), dto); + + return dto; + } + + public void setMembers(JsonArray elements, OSMJsonTurnRestrictionDto dto) { + OSMJsonMemberDto[] members = new OSMJsonMemberDto[elements.size()]; + for (int i = 0; i < elements.size(); i++) { + JsonObject obj = elements.get(i).getAsJsonObject(); + + OSMJsonMemberDto member = new OSMJsonMemberDto(); + member.setType(obj.get("type").getAsString()); + member.setRole(obj.get("role").getAsString()); + member.setOsmId(obj.get("ref").getAsLong()); + + members[i] = member; + } + + dto.setMembers(members); + } +} diff --git a/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonWayDtoDeserializer.java b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonWayDtoDeserializer.java new file mode 100644 index 0000000..f384d16 --- /dev/null +++ b/mapbuilder/src/main/java/map/builder/osm/json/serialization/OSMJsonWayDtoDeserializer.java @@ -0,0 +1,50 @@ +package map.builder.osm.json.serialization; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import map.builder.osm.json.model.OSMJsonWayDto; + +import java.util.Iterator; +import java.util.Map; + +import static map.builder.osm.json.serialization.OSMJsonDtoDeserializer.Mode.NODES_AND_WAYS; + +public class OSMJsonWayDtoDeserializer { + + public OSMJsonWayDto deserialize(JsonObject obj, OSMJsonDtoDeserializer.Mode mode) throws JsonParseException { + if (!mode.equals(NODES_AND_WAYS)) { + return null; + } + + OSMJsonWayDto dto = new OSMJsonWayDto(); + dto.setOsmId(obj.get("id").getAsLong()); + setNodes(obj.getAsJsonArray("nodes"), dto); + resolveTags(obj.getAsJsonObject("tags"), dto); + + return dto; + } + + private void setNodes(JsonArray arr, OSMJsonWayDto dto) { + long[] nodes = new long[arr.size()]; + for (int i = 0; i < arr.size(); i++) { + nodes[i] = arr.get(i).getAsLong(); + } + + dto.setNodes(nodes); + } + + private void resolveTags(JsonObject obj, OSMJsonWayDto dto) { + Iterator<Map.Entry<String, JsonElement>> iterator = obj.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry<String, JsonElement> entry = iterator.next(); + switch (entry.getKey()) { + case "highway" -> dto.setHighway(entry.getValue().getAsString()); + case "oneway" -> dto.setOneway(entry.getValue().getAsString().equals("yes")); + case "junction" -> dto.setJunction(entry.getValue().getAsString()); + case "maxspeed" -> dto.setMaxspeed(entry.getValue().getAsString()); + } + } + } +} -- GitLab