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