From b758be1b76011abebb897a3e3ab9f808e4da1631 Mon Sep 17 00:00:00 2001
From: awenjb <126257927+awenjb@users.noreply.github.com>
Date: Fri, 7 Mar 2025 15:04:33 +0100
Subject: [PATCH] add output and simple visualisation

- add simple solution exporter (to json)
- add jupyter visualisation (map only)
---
 CMakeLists.txt                   |   1 +
 python/solution_reader.py        | 129 +++++++++
 python/visualisation.ipynb       | 447 +++++++++++++++++++++++++++++++
 src/input/json_parser.cpp        |  68 ++---
 src/input/json_parser.h          |   2 +-
 src/input/pdptw_data.cpp         |  10 +-
 src/input/pdptw_data.h           |   5 +-
 src/mains/main.cpp               |   3 +
 src/output/solution_exporter.cpp |  67 +++++
 src/output/solution_exporter.h   |  30 +++
 10 files changed, 726 insertions(+), 36 deletions(-)
 create mode 100644 python/solution_reader.py
 create mode 100644 python/visualisation.ipynb
 create mode 100644 src/output/solution_exporter.cpp
 create mode 100644 src/output/solution_exporter.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f6cae39..9f1351b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,7 @@ add_executable(pdptw src/mains/main.cpp
                 src/lns/modification/route/insert_route.cpp
                 src/lns/modification/route/remove_route.cpp
                 src/output/solution_checker.cpp
+                src/output/solution_exporter.cpp
                 src/lns/operators/sorting_strategy.cpp
                 src/lns/operators/destruction/random_destroy.cpp
                 src/lns/operators/generators/enumerate.cpp 
diff --git a/python/solution_reader.py b/python/solution_reader.py
new file mode 100644
index 0000000..790695e
--- /dev/null
+++ b/python/solution_reader.py
@@ -0,0 +1,129 @@
+import json
+import datetime
+import enum
+
+import folium
+import numpy as np
+import pandas as pd
+import osmnx as ox
+import networkx as nx
+
+def read_json(file):
+    try:
+        with open(file, 'r') as file:
+            data = json.load(file)
+            return data
+    except FileNotFoundError:
+        print(f"Error : The file {file} does not exist.")
+    except json.JSONDecodeError:
+        print(f"Error : The file {file} is not a valid JSON file.")
+    except Exception as e:
+        print(f"Error : {e}")
+
+
+G_drive = ox.graph_from_point((47.213811, -1.553168), dist=10000,
+                              network_type='bike')
+
+class PDPTWSolution:
+    def __init__(self, filename: str):
+        with open(filename, mode="r", encoding="utf-8") as f:
+            self._json_file = json.load(f)
+        self._parse_json()
+
+    def _parse_json(self):
+        if self._json_file is None:
+            raise "Trying to parse json, but was not loaded"
+        self._routes: list[dict] = self._json_file["routes"]
+
+    def get_routes(self):
+        return self._routes
+    
+    def get_json(self):
+        return self._json_file
+
+
+class PDPTWData:
+    def __init__(self, filename: str):
+        with open(filename, mode="r", encoding="utf-8") as f:
+            self._json_file = json.load(f)
+        self._parse_json()
+
+    def _parse_json(self):
+        if self._json_file is None:
+            raise "Trying to parse json, but was not loaded"
+        self._locations: list[dict] = self._json_file["locations"]
+        self._depot: list[dict] = self._json_file["depot"]
+        self._capacity = self._json_file["capacity"]
+
+    def get_depot(self):
+        return self._depot
+    
+    def get_locations(self):
+        return self._locations
+
+    def get_capacity(self):
+        return self._capacity
+
+    def get_json(self):
+        return self._json_file
+
+
+def get_route_as_dataframe(data: PDPTWData, solution: PDPTWSolution, route_index:int) -> pd.DataFrame:
+    route_df=pd.DataFrame()
+    route_ids = solution.get_routes()[route_index]["locationIDs"]
+    for id in route_ids:
+        line = {"route": route_index, 
+                "location": id,
+                "type": data.get_locations()[id - 1]["locType"],
+                "latitude": data.get_locations()[id - 1]["latitude"],
+                "longitude": data.get_locations()[id - 1]["longitude"]}
+        route_df = pd.concat([route_df, pd.DataFrame.from_dict([line])], ignore_index=True)
+    return route_df
+
+def display_points_of_interest(map: folium.Map, data: PDPTWData):
+    folium.Marker(location=[data.get_depot()["latitude"], data.get_depot()["longitude"]],
+                    icon=folium.Icon(color="red", popup="DEPOT")).add_to(map)
+    for location in data.get_locations():
+        if location["locType"] == "PICKUP":
+            color = "darkblue"
+            text = "pickup"
+        elif location["locType"] == "DELIVERY":
+            color = "lightblue"
+            text = "delivery"
+        else:
+            continue
+        folium.Marker(location=[location["latitude"], location["longitude"]],
+                      icon=folium.Icon(color=color, popup=text)).add_to(map)
+        
+def display_route(map: folium.Map, data: PDPTWData, solution: PDPTWSolution, route_index: int, color: str="red"):
+    df = get_route_as_dataframe(data, solution, route_index)
+    # add depot at the begining and the end
+    types = ["DEPOT"] + list(df["type"]) + ["DEPOT"]
+    depot_coord = (data.get_depot()["latitude"], data.get_depot()["longitude"])
+    locations = [depot_coord] + list(zip(df["latitude"], df["longitude"])) + [depot_coord]
+    real_path_locations = []
+    network_locations = ox.nearest_nodes(G_drive,
+                                         [data.get_depot()["longitude"]] + list(df["longitude"]) + [data.get_depot()["longitude"]],
+                                         [data.get_depot()["latitude"]] + list(df["latitude"]) + [data.get_depot()["latitude"]])
+
+    for i in range(1, len(locations)):
+        edge = [network_locations[i - 1], network_locations[i]]
+        real_nodes = ox.shortest_path(G_drive, edge[0], edge[1], weight="length")
+        if real_nodes is not None:
+            long_lat_edges = []
+            for node in real_nodes:
+                g_drive_node = G_drive.nodes[node]
+                if "x" in g_drive_node and "y" in g_drive_node:
+                    long_lat_edges.append((g_drive_node["y"], g_drive_node["x"]))
+            real_path_locations.extend(long_lat_edges)
+        else:
+            real_path_locations.append(locations[i])
+
+    folium.PolyLine(real_path_locations, color=color, weight=5, opacity=0.5, smooth_factor=0).add_to(map)
+    for index, row in df.iterrows():
+        if row["type"] == "REQUEST":
+            text = f'{index} - {row["latitude"], row["longitude"]} : times ({row["arrival"]},{row["service"]},{row["departure"]}), time window [{row["Hmin"]},{row["Hmax"]}]'
+            folium.Circle(location=(row["latitude"], row["longitude"]), fill_color="orange", radius=4,
+                          tooltip=text).add_to(map)
+
+
diff --git a/python/visualisation.ipynb b/python/visualisation.ipynb
new file mode 100644
index 0000000..1c250ee
--- /dev/null
+++ b/python/visualisation.ipynb
@@ -0,0 +1,447 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Requirement already satisfied: folium in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (0.19.4)\n",
+      "Requirement already satisfied: pandas in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (2.2.3)\n",
+      "Requirement already satisfied: OSMnx in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (2.0.1)\n",
+      "Requirement already satisfied: networkx in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (3.4.2)\n",
+      "Requirement already satisfied: scikit-learn in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (1.6.1)\n",
+      "Requirement already satisfied: branca>=0.6.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from folium) (0.8.1)\n",
+      "Requirement already satisfied: jinja2>=2.9 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from folium) (3.1.5)\n",
+      "Requirement already satisfied: numpy in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from folium) (2.2.3)\n",
+      "Requirement already satisfied: requests in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from folium) (2.32.3)\n",
+      "Requirement already satisfied: xyzservices in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from folium) (2025.1.0)\n",
+      "Requirement already satisfied: python-dateutil>=2.8.2 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from pandas) (2.9.0.post0)\n",
+      "Requirement already satisfied: pytz>=2020.1 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from pandas) (2025.1)\n",
+      "Requirement already satisfied: tzdata>=2022.7 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from pandas) (2025.1)\n",
+      "Requirement already satisfied: geopandas>=1.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from OSMnx) (1.0.1)\n",
+      "Requirement already satisfied: shapely>=2.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from OSMnx) (2.0.7)\n",
+      "Requirement already satisfied: scipy>=1.6.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from scikit-learn) (1.15.2)\n",
+      "Requirement already satisfied: joblib>=1.2.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from scikit-learn) (1.4.2)\n",
+      "Requirement already satisfied: threadpoolctl>=3.1.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from scikit-learn) (3.5.0)\n",
+      "Requirement already satisfied: pyogrio>=0.7.2 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from geopandas>=1.0->OSMnx) (0.10.0)\n",
+      "Requirement already satisfied: packaging in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from geopandas>=1.0->OSMnx) (24.2)\n",
+      "Requirement already satisfied: pyproj>=3.3.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from geopandas>=1.0->OSMnx) (3.7.1)\n",
+      "Requirement already satisfied: MarkupSafe>=2.0 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from jinja2>=2.9->folium) (3.0.2)\n",
+      "Requirement already satisfied: six>=1.5 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)\n",
+      "Requirement already satisfied: charset_normalizer<4,>=2 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from requests->folium) (3.4.1)\n",
+      "Requirement already satisfied: idna<4,>=2.5 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from requests->folium) (3.10)\n",
+      "Requirement already satisfied: urllib3<3,>=1.21.1 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from requests->folium) (2.3.0)\n",
+      "Requirement already satisfied: certifi>=2017.4.17 in /home/a24jacqb/.julia/conda/3/x86_64/lib/python3.12/site-packages (from requests->folium) (2025.1.31)\n",
+      "Note: you may need to restart the kernel to use updated packages.\n"
+     ]
+    }
+   ],
+   "source": [
+    "%pip install folium pandas OSMnx networkx scikit-learn"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import solution_reader\n",
+    "import datetime as dt\n",
+    "import folium"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Files\n",
+    "solutionFile = \"./../output/Nantes_1_sol.json\"\n",
+    "dataFile = \"./../data_in/Nantes_1.json\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "solution = solution_reader.PDPTWSolution(solutionFile)\n",
+    "data = solution_reader.PDPTWData(dataFile)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Map"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<iframe srcdoc=\"&lt;!DOCTYPE html&gt;\n",
+       "&lt;html&gt;\n",
+       "&lt;head&gt;\n",
+       "    \n",
+       "    &lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;\n",
+       "    \n",
+       "        &lt;script&gt;\n",
+       "            L_NO_TOUCH = false;\n",
+       "            L_DISABLE_3D = false;\n",
+       "        &lt;/script&gt;\n",
+       "    \n",
+       "    &lt;style&gt;html, body {width: 100%;height: 100%;margin: 0;padding: 0;}&lt;/style&gt;\n",
+       "    &lt;style&gt;#map {position:absolute;top:0;bottom:0;right:0;left:0;}&lt;/style&gt;\n",
+       "    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js&quot;&gt;&lt;/script&gt;\n",
+       "    &lt;script src=&quot;https://code.jquery.com/jquery-3.7.1.min.js&quot;&gt;&lt;/script&gt;\n",
+       "    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&quot;&gt;&lt;/script&gt;\n",
+       "    &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.js&quot;&gt;&lt;/script&gt;\n",
+       "    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css&quot;/&gt;\n",
+       "    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&quot;/&gt;\n",
+       "    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css&quot;/&gt;\n",
+       "    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.2.0/css/all.min.css&quot;/&gt;\n",
+       "    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css&quot;/&gt;\n",
+       "    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/leaflet.awesome.rotate.min.css&quot;/&gt;\n",
+       "    \n",
+       "            &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width,\n",
+       "                initial-scale=1.0, maximum-scale=1.0, user-scalable=no&quot; /&gt;\n",
+       "            &lt;style&gt;\n",
+       "                #map_c353624f2ef6701bef2dfdb918ec18f1 {\n",
+       "                    position: relative;\n",
+       "                    width: 100.0%;\n",
+       "                    height: 100.0%;\n",
+       "                    left: 0.0%;\n",
+       "                    top: 0.0%;\n",
+       "                }\n",
+       "                .leaflet-container { font-size: 1rem; }\n",
+       "            &lt;/style&gt;\n",
+       "        \n",
+       "&lt;/head&gt;\n",
+       "&lt;body&gt;\n",
+       "    \n",
+       "    \n",
+       "            &lt;div class=&quot;folium-map&quot; id=&quot;map_c353624f2ef6701bef2dfdb918ec18f1&quot; &gt;&lt;/div&gt;\n",
+       "        \n",
+       "&lt;/body&gt;\n",
+       "&lt;script&gt;\n",
+       "    \n",
+       "    \n",
+       "            var map_c353624f2ef6701bef2dfdb918ec18f1 = L.map(\n",
+       "                &quot;map_c353624f2ef6701bef2dfdb918ec18f1&quot;,\n",
+       "                {\n",
+       "                    center: [47.213811, -1.553168],\n",
+       "                    crs: L.CRS.EPSG3857,\n",
+       "                    ...{\n",
+       "  &quot;zoom&quot;: 15,\n",
+       "  &quot;zoomControl&quot;: true,\n",
+       "  &quot;preferCanvas&quot;: false,\n",
+       "}\n",
+       "\n",
+       "                }\n",
+       "            );\n",
+       "\n",
+       "            \n",
+       "\n",
+       "        \n",
+       "    \n",
+       "            var tile_layer_8895f6ed2b442e297dc430f0081dcb8f = L.tileLayer(\n",
+       "                &quot;https://tile.openstreetmap.org/{z}/{x}/{y}.png&quot;,\n",
+       "                {\n",
+       "  &quot;minZoom&quot;: 0,\n",
+       "  &quot;maxZoom&quot;: 19,\n",
+       "  &quot;maxNativeZoom&quot;: 19,\n",
+       "  &quot;noWrap&quot;: false,\n",
+       "  &quot;attribution&quot;: &quot;\\u0026copy; \\u003ca href=\\&quot;https://www.openstreetmap.org/copyright\\&quot;\\u003eOpenStreetMap\\u003c/a\\u003e contributors&quot;,\n",
+       "  &quot;subdomains&quot;: &quot;abc&quot;,\n",
+       "  &quot;detectRetina&quot;: false,\n",
+       "  &quot;tms&quot;: false,\n",
+       "  &quot;opacity&quot;: 1,\n",
+       "}\n",
+       "\n",
+       "            );\n",
+       "        \n",
+       "    \n",
+       "            tile_layer_8895f6ed2b442e297dc430f0081dcb8f.addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var marker_20cbbc5fa19a011514524b35d837b38b = L.marker(\n",
+       "                [47.218371, -1.553621],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_4caaf078bbfddb1dbb7303167daf370d = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;red&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;DEPOT&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_20cbbc5fa19a011514524b35d837b38b.setIcon(icon_4caaf078bbfddb1dbb7303167daf370d);\n",
+       "        \n",
+       "    \n",
+       "            var marker_e1ec6cd694e9c61c45f08fde56983ef3 = L.marker(\n",
+       "                [47.213568, -1.555056],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_f2cfa910989fb46a0591df6585bf4253 = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;darkblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;pickup&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_e1ec6cd694e9c61c45f08fde56983ef3.setIcon(icon_f2cfa910989fb46a0591df6585bf4253);\n",
+       "        \n",
+       "    \n",
+       "            var marker_f80d5165e7d392cb682a9c8d15512f07 = L.marker(\n",
+       "                [47.220253, -1.550774],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_1d3d3f82ab67ef25193e1c1db5bc81c0 = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;darkblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;pickup&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_f80d5165e7d392cb682a9c8d15512f07.setIcon(icon_1d3d3f82ab67ef25193e1c1db5bc81c0);\n",
+       "        \n",
+       "    \n",
+       "            var marker_8853de7a8d8a2cc515cdf1cb3193f948 = L.marker(\n",
+       "                [47.209292, -1.560482],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_8ee68172cbeb64dde52575bb3c0070e8 = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;darkblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;pickup&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_8853de7a8d8a2cc515cdf1cb3193f948.setIcon(icon_8ee68172cbeb64dde52575bb3c0070e8);\n",
+       "        \n",
+       "    \n",
+       "            var marker_e63ff96c3c8263e9bab166a1c1e224e3 = L.marker(\n",
+       "                [47.216312, -1.548712],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_86c688a45ab441ffe9688a93aa0447d5 = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;darkblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;pickup&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_e63ff96c3c8263e9bab166a1c1e224e3.setIcon(icon_86c688a45ab441ffe9688a93aa0447d5);\n",
+       "        \n",
+       "    \n",
+       "            var marker_615cd366e4f967cdeceb9b1cb84e9002 = L.marker(\n",
+       "                [47.223158, -1.557308],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_7547818df1dfe54f69c51fc646080dad = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;darkblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;pickup&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_615cd366e4f967cdeceb9b1cb84e9002.setIcon(icon_7547818df1dfe54f69c51fc646080dad);\n",
+       "        \n",
+       "    \n",
+       "            var marker_4b1c02fe9b43add497d10ce2a87606c3 = L.marker(\n",
+       "                [47.219011, -1.563922],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_bdfc7560b738c96e5e5639a0a1851278 = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;lightblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;delivery&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_4b1c02fe9b43add497d10ce2a87606c3.setIcon(icon_bdfc7560b738c96e5e5639a0a1851278);\n",
+       "        \n",
+       "    \n",
+       "            var marker_f3d5f6fa59ad8c59dffb574c5a80e698 = L.marker(\n",
+       "                [47.214682, -1.554387],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_4ecafabf83aceb11746c6346d174202b = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;lightblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;delivery&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_f3d5f6fa59ad8c59dffb574c5a80e698.setIcon(icon_4ecafabf83aceb11746c6346d174202b);\n",
+       "        \n",
+       "    \n",
+       "            var marker_0f8d08e8869e5939f234b80dfb3cc480 = L.marker(\n",
+       "                [47.217748, -1.552834],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_0c04360857e24a27af8f3606098ec5c1 = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;lightblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;delivery&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_0f8d08e8869e5939f234b80dfb3cc480.setIcon(icon_0c04360857e24a27af8f3606098ec5c1);\n",
+       "        \n",
+       "    \n",
+       "            var marker_0f14f775c0140a6f3d2ab6a7c0bf13de = L.marker(\n",
+       "                [47.210421, -1.561234],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_c3e9638f3929bda329b5a4f5507e22aa = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;lightblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;delivery&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_0f14f775c0140a6f3d2ab6a7c0bf13de.setIcon(icon_c3e9638f3929bda329b5a4f5507e22aa);\n",
+       "        \n",
+       "    \n",
+       "            var marker_6decad1d5222cc076ea596bc00ce4797 = L.marker(\n",
+       "                [47.213824, -1.558907],\n",
+       "                {\n",
+       "}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "    \n",
+       "            var icon_51f0648f6226ca2b848827e94b6857df = L.AwesomeMarkers.icon(\n",
+       "                {\n",
+       "  &quot;markerColor&quot;: &quot;lightblue&quot;,\n",
+       "  &quot;iconColor&quot;: &quot;white&quot;,\n",
+       "  &quot;icon&quot;: &quot;info-sign&quot;,\n",
+       "  &quot;prefix&quot;: &quot;glyphicon&quot;,\n",
+       "  &quot;extraClasses&quot;: &quot;fa-rotate-0&quot;,\n",
+       "  &quot;popup&quot;: &quot;delivery&quot;,\n",
+       "}\n",
+       "            );\n",
+       "            marker_6decad1d5222cc076ea596bc00ce4797.setIcon(icon_51f0648f6226ca2b848827e94b6857df);\n",
+       "        \n",
+       "    \n",
+       "            var poly_line_527a754ef0df57c187022733d15c2bb3 = L.polyline(\n",
+       "                [[47.2183453, -1.5534755], [47.2180456, -1.5534249], [47.2179026, -1.5534213], [47.2173096, -1.5533652], [47.2163273, -1.552867], [47.2160168, -1.5534298], [47.2158832, -1.5537904], [47.2156671, -1.5543686], [47.2155362, -1.5547237], [47.2154727, -1.5548879], [47.2154154, -1.5550336], [47.2153044, -1.555331], [47.2150828, -1.5559266], [47.2150388, -1.5560491], [47.2150223, -1.5560952], [47.2150106, -1.5561331], [47.2149286, -1.5563563], [47.2148674, -1.5565232], [47.2144755, -1.5576091], [47.2143928, -1.5578413], [47.2143668, -1.5579057], [47.2141806, -1.5584372], [47.2141855, -1.5586343], [47.2139823, -1.5587194], [47.2138656, -1.558768], [47.213595, -1.5588636], [47.2133024, -1.5589669], [47.2132707, -1.5589781], [47.2126378, -1.5591137], [47.2121042, -1.5593879], [47.2117136, -1.559891], [47.2116749, -1.5597488], [47.2113887, -1.5605287], [47.2112181, -1.5609503], [47.2110607, -1.5613661], [47.2101237, -1.5616227], [47.2100646, -1.5616045], [47.2100053, -1.5616541], [47.2099268, -1.5616842], [47.2098593, -1.5615066], [47.209841, -1.5614154], [47.2098043, -1.5612944], [47.2097481, -1.5610591], [47.2097481, -1.5610591], [47.2098572, -1.5612284], [47.2098934, -1.5611817], [47.210006, -1.5612451], [47.2104866, -1.5600564], [47.2105383, -1.5599477], [47.2106919, -1.5595986], [47.210698, -1.5595018], [47.210765, -1.559475], [47.2112648, -1.5589293], [47.2113092, -1.5587873], [47.2114439, -1.5587814], [47.2114847, -1.5588541], [47.2115747, -1.5588769], [47.2119118, -1.5588963], [47.2124823, -1.5576001], [47.2127522, -1.5569902], [47.2130621, -1.5562719], [47.2133885, -1.5554467], [47.2137115, -1.5552366], [47.2137115, -1.5552366], [47.2133885, -1.5554467], [47.2139676, -1.5558336], [47.214047, -1.5555956], [47.214322, -1.5558496], [47.2149286, -1.5563563], [47.2152704, -1.5565161], [47.2156942, -1.5566909], [47.2160719, -1.5568626], [47.2161023, -1.5566372], [47.2160668, -1.556594], [47.216062, -1.55652], [47.2160888, -1.5564689], [47.2161627, -1.5564754], [47.2162459, -1.5565005], [47.2162987, -1.5563239], [47.2163182, -1.5562613], [47.2166103, -1.5553853], [47.2166552, -1.5552407], [47.2167391, -1.5549874], [47.2168734, -1.5545202], [47.2170421, -1.5545425], [47.2171995, -1.5538062], [47.2173096, -1.5533652], [47.2179026, -1.5534213], [47.2179732, -1.5529077], [47.2179732, -1.5529077], [47.2179026, -1.5534213], [47.2178056, -1.5542105], [47.2177651, -1.5545826], [47.2177545, -1.554897], [47.2176684, -1.5548815], [47.2173841, -1.5547654], [47.2167391, -1.5549874], [47.2166552, -1.5552407], [47.2166103, -1.5553853], [47.2163182, -1.5562613], [47.2162987, -1.5563239], [47.2164163, -1.5564274], [47.2163872, -1.5565937], [47.2163782, -1.5566448], [47.2163685, -1.5566999], [47.2163262, -1.5569411], [47.2162653, -1.5569158], [47.2163908, -1.5570911], [47.2168916, -1.5577689], [47.2178606, -1.5591488], [47.2177585, -1.5592958], [47.2174626, -1.5595293], [47.2173537, -1.5596481], [47.2173628, -1.5597536], [47.217422, -1.5601737], [47.2176085, -1.5613937], [47.2176974, -1.5620016], [47.217875, -1.5623626], [47.2180253, -1.5626025], [47.2184235, -1.5632157], [47.2184899, -1.5633144], [47.2185675, -1.5632755], [47.2186651, -1.563122], [47.2187473, -1.5632832], [47.2186498, -1.56339], [47.2186631, -1.5636429], [47.219081, -1.5643201], [47.219081, -1.5643201], [47.2195174, -1.5638054], [47.2192922, -1.5634829], [47.2202091, -1.5623961], [47.2202406, -1.5620657], [47.2201827, -1.561978], [47.2202337, -1.5618217], [47.2202964, -1.5618185], [47.2208756, -1.5619707], [47.2209515, -1.5616614], [47.2208425, -1.5614354], [47.2205302, -1.5607196], [47.2204977, -1.5606348], [47.2204344, -1.560452], [47.2200804, -1.5592354], [47.2200109, -1.5589945], [47.2199823, -1.558947], [47.2199731, -1.5589098], [47.2200193, -1.5586361], [47.2200296, -1.5586236], [47.2201608, -1.558607], [47.2203358, -1.558474], [47.2207131, -1.557133], [47.2209603, -1.5563172], [47.2209331, -1.5562339], [47.2209355, -1.5562025], [47.220957, -1.5561485], [47.221036, -1.5557407], [47.2210876, -1.5556392], [47.2211358, -1.5556193], [47.2211711, -1.5555632], [47.2211884, -1.5555797], [47.2211918, -1.5556247], [47.2215321, -1.5559809], [47.2218819, -1.5562763], [47.2225196, -1.556822], [47.2227481, -1.5570086], [47.2227481, -1.5570086], [47.2225196, -1.556822], [47.2218819, -1.5562763], [47.2215321, -1.5559809], [47.2211643, -1.5556505], [47.2211358, -1.5556193], [47.2210876, -1.5556392], [47.2205384, -1.5551774], [47.220372, -1.5554198], [47.2192285, -1.5569368], [47.218466, -1.5575159], [47.2177308, -1.5576904], [47.2175953, -1.5575156], [47.2173674, -1.5579742], [47.2155435, -1.558366], [47.2150354, -1.558371], [47.2148049, -1.5582289], [47.2146123, -1.5587577], [47.214493, -1.5588765], [47.2143149, -1.5588703], [47.2141855, -1.5586343], [47.2139823, -1.5587194], [47.2138656, -1.558768], [47.2138656, -1.558768], [47.2139823, -1.5587194], [47.2141855, -1.5586343], [47.2143149, -1.5588703], [47.214493, -1.5588765], [47.2146123, -1.5587577], [47.2148049, -1.5582289], [47.214887, -1.5579725], [47.2148938, -1.5575822], [47.2155597, -1.557499], [47.2159756, -1.5575279], [47.2160405, -1.5570888], [47.2160719, -1.5568626], [47.2161023, -1.5566372], [47.2160668, -1.556594], [47.216062, -1.55652], [47.2160888, -1.5564689], [47.2161627, -1.5564754], [47.2162459, -1.5565005], [47.2162987, -1.5563239], [47.2163182, -1.5562613], [47.2166103, -1.5553853], [47.2166552, -1.5552407], [47.2167391, -1.5549874], [47.2168734, -1.5545202], [47.2170421, -1.5545425], [47.2171995, -1.5538062], [47.2173096, -1.5533652], [47.2179026, -1.5534213], [47.2179732, -1.5529077], [47.218073, -1.5520648], [47.2182157, -1.5515226], [47.2182438, -1.5515083], [47.218417, -1.5510646], [47.2185079, -1.5509195], [47.2190188, -1.5503685], [47.2191832, -1.5500264], [47.2195042, -1.5493542], [47.2198147, -1.5496147], [47.2200423, -1.5497925], [47.2204434, -1.5501244], [47.2204434, -1.5501244], [47.220934, -1.5505257], [47.2212358, -1.5507737], [47.2214556, -1.5509554], [47.221637, -1.5511152], [47.2216744, -1.551218], [47.2216325, -1.5513221], [47.2214653, -1.5517728], [47.2213479, -1.5518279], [47.221019, -1.5515593], [47.2209031, -1.5514647], [47.2195467, -1.550357], [47.219292, -1.5501333], [47.2190188, -1.5503685], [47.2185079, -1.5509195], [47.218417, -1.5510646], [47.2183259, -1.5510647], [47.2181201, -1.550995], [47.2180341, -1.5509182], [47.2178395, -1.5507793], [47.2177663, -1.5507359], [47.2168008, -1.5502658], [47.2163767, -1.5505283], [47.2161313, -1.550635], [47.2158726, -1.5506172], [47.2152464, -1.5503023], [47.2164709, -1.5469394], [47.216637, -1.5470663], [47.2169383, -1.5473231], [47.2168447, -1.5475416], [47.2167752, -1.5477259], [47.2167752, -1.5477259], [47.2168447, -1.5475416], [47.2169383, -1.5473231], [47.2171505, -1.5467129], [47.2167226, -1.5464322], [47.2165366, -1.5468755], [47.216637, -1.5470663], [47.2164709, -1.5469394], [47.2152464, -1.5503023], [47.2151655, -1.5505279], [47.2149818, -1.5512832], [47.214797, -1.5521377], [47.2145791, -1.5531581], [47.2147045, -1.5531551], [47.214902, -1.5541819], [47.2147737, -1.5544478], [47.2147737, -1.5544478], [47.2146992, -1.5547583], [47.214458, -1.5554], [47.2147699, -1.5556873], [47.2147364, -1.5557976], [47.2150388, -1.5560491], [47.2150223, -1.5560952], [47.2150106, -1.5561331], [47.2149286, -1.5563563], [47.2148674, -1.5565232], [47.2144755, -1.5576091], [47.2143928, -1.5578413], [47.2143668, -1.5579057], [47.2141806, -1.5584372], [47.2141855, -1.5586343], [47.2139823, -1.5587194], [47.2138656, -1.558768], [47.213595, -1.5588636], [47.2133024, -1.5589669], [47.2132707, -1.5589781], [47.2126378, -1.5591137], [47.2121042, -1.5593879], [47.2117136, -1.559891], [47.2116749, -1.5597488], [47.2113887, -1.5605287], [47.2111879, -1.5603633], [47.2109976, -1.5604866], [47.210882, -1.5603657], [47.2108289, -1.5605005], [47.2105292, -1.5612745], [47.2105292, -1.5612745], [47.2108289, -1.5605005], [47.210882, -1.5603657], [47.2107742, -1.560242], [47.2106689, -1.5601512], [47.2106179, -1.5601071], [47.2105539, -1.5601544], [47.2104866, -1.5600564], [47.2105383, -1.5599477], [47.2106919, -1.5595986], [47.210698, -1.5595018], [47.210765, -1.559475], [47.2112648, -1.5589293], [47.2113092, -1.5587873], [47.2114439, -1.5587814], [47.2114847, -1.5588541], [47.2115747, -1.5588769], [47.2119118, -1.5588963], [47.2120003, -1.5591216], [47.2121042, -1.5593879], [47.2126378, -1.5591137], [47.2132707, -1.5589781], [47.2133024, -1.5589669], [47.213595, -1.5588636], [47.2138656, -1.558768], [47.2139823, -1.5587194], [47.2141855, -1.5586343], [47.2143149, -1.5588703], [47.214493, -1.5588765], [47.2146123, -1.5587577], [47.2148049, -1.5582289], [47.214887, -1.5579725], [47.2148938, -1.5575822], [47.2155597, -1.557499], [47.2159756, -1.5575279], [47.2160405, -1.5570888], [47.2160719, -1.5568626], [47.2161023, -1.5566372], [47.2160668, -1.556594], [47.216062, -1.55652], [47.2160888, -1.5564689], [47.2161627, -1.5564754], [47.2162459, -1.5565005], [47.2162987, -1.5563239], [47.2163182, -1.5562613], [47.2166103, -1.5553853], [47.2166552, -1.5552407], [47.2167391, -1.5549874], [47.2173841, -1.5547654], [47.2176684, -1.5548815], [47.2177545, -1.554897], [47.2177651, -1.5545826], [47.2178056, -1.5542105], [47.2179086, -1.5542302], [47.2179566, -1.5542666], [47.2181433, -1.5539675], [47.2183453, -1.5534755]],\n",
+       "                {&quot;bubblingMouseEvents&quot;: true, &quot;color&quot;: &quot;red&quot;, &quot;dashArray&quot;: null, &quot;dashOffset&quot;: null, &quot;fill&quot;: false, &quot;fillColor&quot;: &quot;red&quot;, &quot;fillOpacity&quot;: 0.2, &quot;fillRule&quot;: &quot;evenodd&quot;, &quot;lineCap&quot;: &quot;round&quot;, &quot;lineJoin&quot;: &quot;round&quot;, &quot;noClip&quot;: false, &quot;opacity&quot;: 0.5, &quot;smoothFactor&quot;: 0, &quot;stroke&quot;: true, &quot;weight&quot;: 5}\n",
+       "            ).addTo(map_c353624f2ef6701bef2dfdb918ec18f1);\n",
+       "        \n",
+       "&lt;/script&gt;\n",
+       "&lt;/html&gt;\" width=\"1000\" height=\"1000\"style=\"border:none !important;\" \"allowfullscreen\" \"webkitallowfullscreen\" \"mozallowfullscreen\"></iframe>"
+      ],
+      "text/plain": [
+       "<branca.element.Figure at 0x7fadee233080>"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "f = folium.Figure(width=1000, height=1000)\n",
+    "map = folium.Map(location=(47.213811, -1.553168), zoom_start=15)\n",
+    "solution_reader.display_points_of_interest(map, data)\n",
+    "solution_reader.display_route(map, data, solution, 0)\n",
+    "f.add_child(map)\n",
+    "f"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "base",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.12.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/src/input/json_parser.cpp b/src/input/json_parser.cpp
index 31d221c..b5b0094 100644
--- a/src/input/json_parser.cpp
+++ b/src/input/json_parser.cpp
@@ -4,19 +4,18 @@
 #include <filesystem>
 #include <fstream>
 #include <nlohmann/json.hpp>
+#include <utility>
 
 // spdlog repris du code lns-framework mais pas utilisé ici -> TODO
 
 namespace fs = std::filesystem;
 using json = nlohmann::json;
 
-
 bool checkFilePresence(std::string &filepath)
 {
     return fs::is_regular_file(filepath);
 }
 
-
 PDPTWData parsing::parseJson(std::string filepath)
 {
     if (!checkFilePresence(filepath))
@@ -24,6 +23,7 @@ PDPTWData parsing::parseJson(std::string filepath)
         spdlog::error("Data file \"{}\" does not exist", filepath);
         exit(1);
     }
+
     std::ifstream jsonFile(filepath);
 
     if (!jsonFile.is_open())
@@ -35,11 +35,22 @@ PDPTWData parsing::parseJson(std::string filepath)
 
     try
     {
+        // extract filename (data name)
+        size_t pos = filepath.find_last_of("/\\");
+        std::string filename = (pos != std::string::npos) ? filepath.substr(pos + 1) : filepath;
+
+        size_t dotPos = filename.find_last_of('.');
+        if (dotPos != std::string::npos)
+        {
+            filename = filename.substr(0, dotPos);
+        }
+
+        // generate PDPTWData
         json j;
         jsonFile >> j;
-        return json_to_data(j);
-    } 
-    catch (std::exception const &e)// catching all exceptions to properly log the error and quit gracefully
+        return json_to_data(filename, j);
+        
+    } catch (std::exception const &e)// catching all exceptions to properly log the error and quit gracefully
     {
         spdlog::error("Error while parsing the input json:");
         spdlog::error("{}", e.what());
@@ -48,46 +59,41 @@ PDPTWData parsing::parseJson(std::string filepath)
     }
 }
 
-PDPTWData json_to_data(const json& j)
+PDPTWData json_to_data(std::string dataName, json const &j)
 {
-   
     int size = j.at("size").get<int>();
     int capacity = j.at("capacity").get<int>();
-    
+
     auto depot_json = j.at("depot");
 
     TimeWindow tw(depot_json.at("timeWindow").at(0), depot_json.at("timeWindow").at(1));
-    Location depot(
-        0,
-        depot_json.at("longitude"), 
-        depot_json.at("latitude"),
-        0,
-        tw,
-        depot_json.at("serviceDuration"),
-        0,
-        LocType::DEPOT
-    );
-    
+    Location depot(0,
+                   depot_json.at("longitude"),
+                   depot_json.at("latitude"),
+                   0,
+                   tw,
+                   depot_json.at("serviceDuration"),
+                   0,
+                   LocType::DEPOT);
+
     std::vector<Location> locations;
 
-    for (const auto& loc : j.at("locations")) 
+    for (auto const &loc: j.at("locations"))
     {
         LocType loc_type = (loc.at("locType") == "PICKUP") ? LocType::PICKUP : LocType::DELIVERY;
         TimeWindow loc_tw(loc.at("timeWindow").at(0), loc.at("timeWindow").at(1));
 
-        locations.emplace_back(
-            loc.at("id"),
-            loc.at("longitude"), 
-            loc.at("latitude"),
-            loc.at("demand"),
-            loc_tw,
-            loc.at("serviceDuration"),
-            loc.at("pairedLocation"),
-            loc_type
-        );
+        locations.emplace_back(loc.at("id"),
+                               loc.at("longitude"),
+                               loc.at("latitude"),
+                               loc.at("demand"),
+                               loc_tw,
+                               loc.at("serviceDuration"),
+                               loc.at("pairedLocation"),
+                               loc_type);
     }
 
     Matrix distance_matrix = j.at("distance_matrix").get<Matrix>();
 
-    return {size, capacity, depot, locations, distance_matrix};
+    return {std::move(dataName), size, capacity, depot, locations, distance_matrix};
 }
diff --git a/src/input/json_parser.h b/src/input/json_parser.h
index 56acdbd..72de0a8 100644
--- a/src/input/json_parser.h
+++ b/src/input/json_parser.h
@@ -10,4 +10,4 @@ namespace parsing
     PDPTWData parseJson(std::string filepath);
 }
 
-PDPTWData json_to_data(const json& j);
\ No newline at end of file
+PDPTWData json_to_data(std::string dataName, const json& j);
\ No newline at end of file
diff --git a/src/input/pdptw_data.cpp b/src/input/pdptw_data.cpp
index 5682449..a21d8e6 100644
--- a/src/input/pdptw_data.cpp
+++ b/src/input/pdptw_data.cpp
@@ -30,6 +30,11 @@ Location const &PDPTWData::getDepot() const
     return depot;
 }
 
+std::string PDPTWData::getDataName() const
+{
+    return dataName;
+}
+
 Location const &PDPTWData::getLocation(int id) const
 {
     if (id==0)
@@ -45,8 +50,8 @@ Matrix const &PDPTWData::getMatrix() const
     return distanceMatrix;
 }
 
-PDPTWData::PDPTWData(int size, int capacity, Location depot, std::vector<Location> locations, Matrix distanceMatrix)
-    : size(size), capacity(capacity), depot(depot), locations(std::move(locations)), distanceMatrix(std::move(distanceMatrix)) 
+PDPTWData::PDPTWData(std::string dataName, int size, int capacity, Location depot, std::vector<Location> locations, Matrix distanceMatrix)
+    : dataName(dataName), size(size), capacity(capacity), depot(depot), locations(std::move(locations)), distanceMatrix(std::move(distanceMatrix)) 
 {
     // Associate pair of locations
     pairs.clear();
@@ -75,6 +80,7 @@ const Pair &PDPTWData::getPair(int id) const
 
 void PDPTWData::print() const 
 {
+    std::cout << "Instance name : " << dataName << "\n";
     std::cout << "Instance size: " << size << "\n";
     std::cout << "Capacity: " << capacity << "\n";
     std::cout << "Depot:\n";
diff --git a/src/input/pdptw_data.h b/src/input/pdptw_data.h
index 3cfb829..a0a7ae5 100644
--- a/src/input/pdptw_data.h
+++ b/src/input/pdptw_data.h
@@ -23,6 +23,7 @@ public:
 
 class PDPTWData
 {
+    std::string dataName;
     int size;
     int capacity;
     Location depot;
@@ -42,7 +43,7 @@ public:
      * Constructs an empty PDPTWData.
      * @see parsing::parseJson
      */
-    PDPTWData(int size, int capacity, Location depot, std::vector<Location> locations, Matrix distanceMatrix);
+    PDPTWData(std::string dataName, int size, int capacity, Location depot, std::vector<Location> locations, Matrix distanceMatrix);
     /**
      * Checks some data coherence
      */
@@ -67,6 +68,6 @@ public:
 
     int getSize() const;
     int getCapacity() const;
-
+    std::string getDataName() const;
     void print() const;
 };
diff --git a/src/mains/main.cpp b/src/mains/main.cpp
index 4f306b8..fb240d1 100644
--- a/src/mains/main.cpp
+++ b/src/mains/main.cpp
@@ -25,6 +25,7 @@
 #include "lns/operators/generators/modification_generator.h"
 #include "lns/operators/sorting_strategy.h"
 
+#include "output/solution_exporter.h"
 
 using json = nlohmann::json;
 
@@ -82,5 +83,7 @@ int main(int argc, char const *argv[])
     operatorInstance.reconstructSolution(solution, blinkRate);
 
     solution.print();
+
+    output::exportToJson(solution);
     return 0;
 }
diff --git a/src/output/solution_exporter.cpp b/src/output/solution_exporter.cpp
new file mode 100644
index 0000000..a537cdf
--- /dev/null
+++ b/src/output/solution_exporter.cpp
@@ -0,0 +1,67 @@
+#include "solution_exporter.h"
+
+std::string getCurrentDate()
+{
+    std::time_t t = std::time(nullptr);
+    std::tm tm = *std::localtime(&t);
+    char buffer[11];// "DD-MM-YYYY" + null terminator
+    std::strftime(buffer, sizeof(buffer), "%d-%m-%Y", &tm);
+    return std::string(buffer);
+}
+
+nlohmann::ordered_json output::getMinimalJson(Solution const &solution)
+{
+    nlohmann::ordered_json jsonSolution;
+
+    nlohmann::ordered_json jsonRoutes = nlohmann::ordered_json::array();
+    int routeID = 0;
+    for (auto const &route: solution.getRoutes())
+    {
+        jsonRoutes.push_back(routeToJson(routeID, route));
+        ++routeID;
+    }
+
+    jsonSolution["InstanceName"] = solution.getData().getDataName();
+    jsonSolution["Authors"] = "...";
+    jsonSolution["Date"] = getCurrentDate();
+    jsonSolution["Reference"] = "...";
+    jsonSolution["routes"] = jsonRoutes;
+
+    return jsonSolution;
+}
+
+nlohmann::ordered_json output::getCompleteJson(Solution const &solution)
+{
+    nlohmann::ordered_json jsonSolution;
+    // TO DO
+    return jsonSolution;
+}
+
+void output::exportToJson(Solution const &solution)
+{
+    std::string directory = "./../../output";
+    std::string filename = directory + "/" + solution.getData().getDataName() + "_sol.json";
+
+    if (!std::filesystem::exists(directory))
+    {
+        std::filesystem::create_directory(directory);
+    }
+
+    std::ofstream file(filename);
+    if (!file)
+    {
+        spdlog::error("Error when opening the file {}", filename);
+        exit(1);
+        return;
+    }
+
+    nlohmann::ordered_json jsonData = output::getMinimalJson(solution);
+    file << jsonData.dump();
+    file.close();
+    std::cout << "Solution exported" << std::endl;
+}
+
+nlohmann::ordered_json output::routeToJson(int routeID, Route const &route)
+{
+    return nlohmann::ordered_json{{"routeID", routeID}, {"locationIDs", route.getRoute()}};
+}
diff --git a/src/output/solution_exporter.h b/src/output/solution_exporter.h
new file mode 100644
index 0000000..62a6f09
--- /dev/null
+++ b/src/output/solution_exporter.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "lns/solution/solution.h"
+
+#include <nlohmann/json.hpp>
+#include <ctime> 
+#include <fstream>
+
+
+namespace output
+{
+    /**
+     * Get a json representation of a solution with the same info as in the benchmarks results (Li&Lim, Sartori&Buriol)
+     */
+    nlohmann::ordered_json getMinimalJson(Solution const &solution);
+
+    /**
+     * Get a complete json representation of a solution (heavier)
+     */
+    nlohmann::ordered_json getCompleteJson(Solution const &solution);
+
+
+    nlohmann::ordered_json routeToJson(int routeID, const Route& route);
+
+
+    void exportToJson(Solution const &solution);
+
+}// namespace output
+
+std::string getCurrentDate();
\ No newline at end of file
-- 
GitLab