diff --git a/app/components/sidebar/container.vue b/app/components/sidebar/lines.vue
similarity index 100%
rename from app/components/sidebar/container.vue
rename to app/components/sidebar/lines.vue
diff --git a/app/components/sidebar/trip.vue b/app/components/sidebar/trip.vue
new file mode 100644
index 0000000..b831021
--- /dev/null
+++ b/app/components/sidebar/trip.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/pages/index.vue b/app/pages/index.vue
index bd6e6f1..ebf83a0 100644
--- a/app/pages/index.vue
+++ b/app/pages/index.vue
@@ -1,6 +1,6 @@
-
+
diff --git a/app/pages/trip.vue b/app/pages/trip.vue
new file mode 100644
index 0000000..52e883f
--- /dev/null
+++ b/app/pages/trip.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/app/types.d.ts b/app/types.d.ts
index 67d8943..c5a337a 100644
--- a/app/types.d.ts
+++ b/app/types.d.ts
@@ -52,3 +52,29 @@ interface StopTimes {
occupancy: string;
occupancyId: number;
}
+
+interface PlannerResource extends Record {
+ routerId: string;
+ fromPlace: string;
+ toPlace: string;
+ arriveBy: string;
+ time: string;
+ date: string;
+ routerId: string;
+ optimize: OptimizeType;
+ walkSpeed: string;
+ walkReluctance: string;
+ locale: string;
+ mode: string;
+ showIntermediateStops?: string;
+ minTransferTime?: string;
+ transferPenalty?: string;
+ numItineraries?: string;
+ walkBoardCost?: string;
+ bannedAgencies?: string;
+}
+
+type OptimizeType =
+ "FLAT" | "GREENWAYS" | "QUICK" | "SAFE" | "TRANSFERS" | "TRIANGLE";
+
+type PlannerModes = "WALK" | "TRANSIT";
diff --git a/app/utils/trip.ts b/app/utils/trip.ts
new file mode 100644
index 0000000..6dc2275
--- /dev/null
+++ b/app/utils/trip.ts
@@ -0,0 +1,81 @@
+import type { LngLat } from "maplibre-gl";
+
+class RoutePlanner {
+ fromPlace: LngLat;
+ toPlace: LngLat;
+ date: Date;
+
+ arriveBy = false;
+ routerId = "default";
+ optimize: OptimizeType = "QUICK";
+ walkSpeed = 1.1112;
+ walkReluctance = 10;
+ locale: string = "fr";
+ mode: Set = new Set(["WALK"]);
+ otpUrl = new URL("https://data.mobilites-m.fr/api/routers/default/plan");
+
+ showIntermediateStops?: boolean;
+ minTransferTime?: number;
+ transferPenalty?: number;
+ numItineraries?: number;
+ walkBoardCost?: number;
+ bannedAgencies?: string;
+
+ constructor(from: LngLat, to: LngLat) {
+ this.fromPlace = from;
+ this.toPlace = to;
+ this.date = new Date();
+ }
+
+ toURLSearchParams() {
+ const params: PlannerResource = {
+ fromPlace: `${this.fromPlace.lng},${this.fromPlace.lat}`,
+ toPlace: `${this.toPlace.lng},${this.toPlace.lat}`,
+ arriveBy: this.arriveBy.toString(),
+ date: this.date.toISOString().substring(0, 10),
+ time: this.date.toISOString().substring(11, 16),
+ routerId: this.routerId,
+ optimize: this.optimize,
+ walkSpeed: this.walkSpeed.toString(),
+ walkReluctance: this.walkReluctance.toString(),
+ locale: this.locale,
+ mode: Array.from(this.mode.values()).join(","),
+ };
+ if (this.showIntermediateStops) {
+ params["showIntermediateStops"] = this.showIntermediateStops.toString();
+ }
+ if (this.minTransferTime) {
+ params["minTransferTime"] = this.minTransferTime.toString();
+ }
+ if (this.transferPenalty) {
+ params["transferPenalty"] = this.transferPenalty.toString();
+ }
+ if (this.numItineraries) {
+ params["numItineraries"] = this.numItineraries.toString();
+ }
+ if (this.walkBoardCost) {
+ params["walkBoardCost"] = this.walkBoardCost.toString();
+ }
+ if (this.bannedAgencies) {
+ params["bannedAgencies"] = this.bannedAgencies.toString();
+ }
+ return new URLSearchParams(params);
+ }
+
+ async request() {
+ this.otpUrl.search = this.toURLSearchParams().toString();
+ return await $fetch(this.otpUrl.toString());
+ }
+}
+
+class TransitRoutePlanner extends RoutePlanner {
+ override mode = new Set(["WALK", "TRANSIT"]);
+ override showIntermediateStops = true;
+ override minTransferTime = 60;
+ override transferPenalty = 60;
+ override numItineraries = 2;
+ override walkBoardCost = 300;
+ override bannedAgencies = "MCO:MCO";
+}
+
+export { RoutePlanner, TransitRoutePlanner };