From 0396bf30a5059488156112381e22d79eaf6b3c65 Mon Sep 17 00:00:00 2001
From: Dominik Hebeler <dominik@suma-ev.de>
Date: Tue, 6 Feb 2024 15:22:21 +0100
Subject: [PATCH] add more controls back to the map

---
 app/resources/js/GpsManager.js                |  8 +--
 app/resources/js/maps/Maplibre.js             | 17 ++++-
 app/resources/js/maps/MetaGerMap.js           |  3 +
 app/resources/js/maps/Openlayers.js           | 31 +++++++-
 .../js/openlayers/GeolocationControl.js       | 50 +++++++++++++
 .../js/openlayers/NavigationControl.js        | 30 ++++++++
 app/resources/less/map.less                   |  4 +-
 app/resources/less/openlayers/controls.less   | 71 +++++++++++++++++++
 app/resources/views/map.blade.php             | 43 +++++------
 app/webpack.mix.js                            |  5 +-
 10 files changed, 227 insertions(+), 35 deletions(-)
 create mode 100644 app/resources/js/openlayers/GeolocationControl.js
 create mode 100644 app/resources/js/openlayers/NavigationControl.js
 create mode 100644 app/resources/less/openlayers/controls.less

diff --git a/app/resources/js/GpsManager.js b/app/resources/js/GpsManager.js
index 22631f9..80aeffe 100644
--- a/app/resources/js/GpsManager.js
+++ b/app/resources/js/GpsManager.js
@@ -36,9 +36,9 @@ export class GpsManager {
       this.last_location = location;
       success_callback(location);
       return "localstorage";
-    } else if (map.androidConnector.is_connected) {
+    } else if (androidConnector.is_connected) {
       // Android App is connected. We can use the devices Geolocation
-      return map.androidConnector.watchPosition(
+      return androidConnector.watchPosition(
         (location) => {
           this.last_location = location;
           success_callback(location);
@@ -61,8 +61,8 @@ export class GpsManager {
   clearWatch(id) {
     if (id == "localstorage") {
       window.removeEventListener("storage", this.#watch_callback);
-    } else if (map.androidConnector.is_connected) {
-      map.androidConnector.clearWatch(id);
+    } else if (androidConnector.is_connected) {
+      androidConnector.clearWatch(id);
     } else {
       navigator.geolocation.clearWatch(id);
     }
diff --git a/app/resources/js/maps/Maplibre.js b/app/resources/js/maps/Maplibre.js
index de11ce0..8c5fff8 100644
--- a/app/resources/js/maps/Maplibre.js
+++ b/app/resources/js/maps/Maplibre.js
@@ -1,6 +1,6 @@
 import { config } from "../Config";
 import { MetaGerMap } from "./MetaGerMap";
-import { LngLat, Map, setRTLTextPlugin } from "maplibre-gl";
+import { LngLat, Map, NavigationControl, setRTLTextPlugin } from "maplibre-gl";
 
 export class Maplibre extends MetaGerMap {
   /** @type {Map} */
@@ -13,12 +13,19 @@ export class Maplibre extends MetaGerMap {
   };
 
   #layer_types = ["fill", "line", "point"];
-  #symbol_types = ["marker"];
+
+  #navigationControl = null;
 
   constructor() {
     super();
     setRTLTextPlugin(`${this._tileserver_host}/mapbox-gl-rtl-text.js`);
 
+    this.#navigationControl = new NavigationControl({
+      showCompass: true,
+      showZoom: true,
+      visualizePitch: true
+    });
+
     if (config.init.bbox != null) {
       this.#map_params.bounds = config.init.bbox;
     } else {
@@ -37,7 +44,7 @@ export class Maplibre extends MetaGerMap {
     // Marker Icon
     let image = await this._map.loadImage("/img/marker-icon.png");
     this._map.addImage("result-marker", image.data);
-
+    this.#addControls();
     return new Promise((resolve, reject) => {
       if (!this._map.loaded()) {
         this._map.once("load", (e) => {
@@ -49,6 +56,10 @@ export class Maplibre extends MetaGerMap {
     });
   }
 
+  #addControls() {
+    this._map.addControl(this.#navigationControl, "bottom-left");
+  }
+
   getBounds() {
     return this._map.getBounds();
   }
diff --git a/app/resources/js/maps/MetaGerMap.js b/app/resources/js/maps/MetaGerMap.js
index bc1c7c6..d5b7cb2 100644
--- a/app/resources/js/maps/MetaGerMap.js
+++ b/app/resources/js/maps/MetaGerMap.js
@@ -14,6 +14,9 @@ export class MetaGerMap {
 
   // The map object of the implementation
   _map = null;
+  _controls = {
+    zoomControl: null,
+  }
 
   constructor() {
     if (this.constructor == Map) {
diff --git a/app/resources/js/maps/Openlayers.js b/app/resources/js/maps/Openlayers.js
index 14891ac..f202262 100644
--- a/app/resources/js/maps/Openlayers.js
+++ b/app/resources/js/maps/Openlayers.js
@@ -18,6 +18,12 @@ import VectorSource from "ol/source/Vector";
 import Stroke from "ol/style/Stroke";
 import Icon from "ol/style/Icon";
 import Text from "ol/style/Text";
+import Zoom from "ol/control/Zoom";
+import Rotate from "ol/control/Rotate";
+import Attribution from "ol/control/Attribution";
+import { NavigationControl } from "../openlayers/NavigationControl";
+import ScaleLine from "ol/control/ScaleLine";
+import { GeolocationControl } from "../openlayers/GeolocationControl";
 
 export class Openlayers extends MetaGerMap {
   /** @type {Map} */
@@ -46,16 +52,19 @@ export class Openlayers extends MetaGerMap {
         useGeographic();
         this._map = new Map({
           target: "map",
+          controls: [],
           layers: [
             new TileLayer({
               source: new XYZ({
                 extent: [-180, -90, 180, 90],
                 url: response.tiles[0],
-                tilesize: [512, 512]
+                tilesize: [512, 512],
+                attributions: response.attribution,
               }),
+              preload: Infinity,
               minZoom: response.minzoom,
               maxZoom: response.maxzoom,
-              attribution: response.attribution,
+
             })
           ],
           view: new View({
@@ -66,6 +75,7 @@ export class Openlayers extends MetaGerMap {
           })
         });
 
+        this.#addControls();
         if (config.init.bbox != null) {
           this._map
             .getView()
@@ -87,6 +97,23 @@ export class Openlayers extends MetaGerMap {
       });
   }
 
+  #addControls() {
+    this._controls.zoomControl = new Zoom({ className: "mg-ol-control-zoom" });
+    this._map.addControl(this._controls.zoomControl);
+
+    this._controls.geolocationControl = new GeolocationControl();
+    this._map.addControl(this._controls.geolocationControl);
+
+    this._controls.attributionControl = new Attribution({ collapsible: true });
+    this._map.addControl(this._controls.attributionControl);
+
+    this._controls.navigationControl = new NavigationControl();
+    this._map.addControl(this._controls.navigationControl);
+
+    this._controls.scaleControl = new ScaleLine();
+    this._map.addControl(this._controls.scaleControl);
+  }
+
   /**
    *
    * @param {import("maplibre-gl").LngLatBounds} bounds
diff --git a/app/resources/js/openlayers/GeolocationControl.js b/app/resources/js/openlayers/GeolocationControl.js
new file mode 100644
index 0000000..3c75fb3
--- /dev/null
+++ b/app/resources/js/openlayers/GeolocationControl.js
@@ -0,0 +1,50 @@
+import Control from "ol/control/Control";
+import { gpsManager } from "../GpsManager";
+
+export class GeolocationControl extends Control {
+    state = "off";
+
+    watch_id = null;
+
+    constructor(opt_options) {
+        const options = opt_options || {};
+
+        let container = document.createElement("div");
+        container.classList.add("control-geolocation", "off", "ol-unselectable", "ol-control");
+
+        let button = document.createElement("button");
+        button.textContent = "⌖";
+        container.appendChild(button);
+
+        super({
+            element: container,
+            target: options.target
+        });
+
+        button.onclick = e => {
+            container.classList.remove(this.state);
+            switch (this.state) {
+                case "off":
+                    this.state = "locked";
+                    this.watchPosition();
+                    break;
+                case "locked":
+                    this.state = "on";
+                    break;
+                case "on":
+                    this.state = "off";
+                    break;
+
+            }
+            container.classList.add(this.state);
+        }
+    }
+
+    watchPosition() {
+        this.watch_id = gpsManager.watchPosition(this.watch_position_callback.bind(this));
+    }
+
+    watch_position_callback(location) {
+        console.log(location);
+    }
+}
\ No newline at end of file
diff --git a/app/resources/js/openlayers/NavigationControl.js b/app/resources/js/openlayers/NavigationControl.js
new file mode 100644
index 0000000..1f7eaec
--- /dev/null
+++ b/app/resources/js/openlayers/NavigationControl.js
@@ -0,0 +1,30 @@
+import Control from "ol/control/Control";
+
+export class NavigationControl extends Control {
+    constructor(opt_options) {
+        const options = opt_options || {};
+
+        let container = document.createElement("div");
+        container.classList.add("nav-opener", "ol-unselectable", "ol-control");
+
+        let button = document.createElement("button");
+        button.textContent = "≡";
+        container.appendChild(button);
+
+        button.onclick = e => {
+            let opener = document.querySelector("#nav-opener");
+            if (opener) {
+                if (opener.checked) {
+                    opener.checked = false;
+                } else {
+                    opener.checked = true;
+                }
+            }
+        }
+
+        super({
+            element: container,
+            target: options.target
+        });
+    }
+}
\ No newline at end of file
diff --git a/app/resources/less/map.less b/app/resources/less/map.less
index 601c959..89c27f8 100644
--- a/app/resources/less/map.less
+++ b/app/resources/less/map.less
@@ -1 +1,3 @@
-@import "../../node_modules/leaflet/dist/leaflet.css";
+@import "../../node_modules/maplibre-gl/dist/maplibre-gl.css";
+@import "../../node_modules/ol/ol.css";
+@import "./openlayers/controls.less";
\ No newline at end of file
diff --git a/app/resources/less/openlayers/controls.less b/app/resources/less/openlayers/controls.less
new file mode 100644
index 0000000..97f278e
--- /dev/null
+++ b/app/resources/less/openlayers/controls.less
@@ -0,0 +1,71 @@
+.mg-ol-control-zoom {
+    bottom: 3rem;
+    left: 1rem;
+
+    >button {
+        height: 3rem;
+        width: 3rem;
+        font-size: 2.5rem;
+    }
+}
+
+.control-geolocation {
+    bottom: 10rem;
+    left: 1rem;
+
+    >button {
+        height: 3rem;
+        width: 3rem;
+        font-size: 3rem;
+    }
+
+    &.locked {
+        >button {
+            color: rgb(255, 127, 0);
+        }
+    }
+
+    &.on {
+        >button {
+            color: rgba(255, 127, 0, 0.5);
+        }
+    }
+}
+
+.ol-attribution {
+    bottom: 6rem !important;
+    right: 1rem;
+
+    >button {
+        height: 2rem;
+        width: 2rem;
+        font-size: 2rem;
+    }
+
+    >ul {
+        height: 2rem;
+        line-height: 2rem !important;
+        background-color: white;
+        font-size: 1.25rem !important;
+    }
+}
+
+.ol-control {
+
+    &.nav-opener {
+        right: 1rem;
+        top: 2rem;
+
+        >button {
+            height: 4rem;
+            width: 4rem;
+            font-size: 3rem;
+        }
+    }
+}
+
+.ol-scale-line {
+    left: unset;
+    right: 1rem;
+    bottom: 3rem;
+}
\ No newline at end of file
diff --git a/app/resources/views/map.blade.php b/app/resources/views/map.blade.php
index f237b65..f1a7287 100644
--- a/app/resources/views/map.blade.php
+++ b/app/resources/views/map.blade.php
@@ -9,40 +9,41 @@
         Maps - MetaGer
     </title>
     @if (isset($css))
-        @foreach ($css as $el)
-            <link href="{{ $el }}" rel="stylesheet" type="text/css" />
-        @endforeach
+    @foreach ($css as $el)
+    <link href="{{ $el }}" rel="stylesheet" type="text/css" />
+    @endforeach
     @endif
     <meta name="tileserverhost" content="{{ config('maps.tileserver.host') }}">
 
     @if (isset($bbox))
-        <meta name="start-bbox" content="{{ $bbox }}">
+    <meta name="start-bbox" content="{{ $bbox }}">
     @endif
     @if (isset($module))
-        <meta name="module" content="{{ $module }}">
+    <meta name="module" content="{{ $module }}">
     @endif
     @if (isset($query))
-        <meta name="start-query" content="{{ $query }}">
+    <meta name="start-query" content="{{ $query }}">
     @endif
     @if (isset($viewbox))
-        <meta name="start-viewbox" content="{{ $viewbox }}">
+    <meta name="start-viewbox" content="{{ $viewbox }}">
     @endif
     @if (isset($reverse_lon) && isset($reverse_lat) && isset($reverse_zoom))
-        <meta name="start-reverselon" content="{{ $reverse_lon }}">
-        <meta name="start-reverselat" content="{{ $reverse_lat }}">
-        <meta name="start-reversezoom" content="{{ $reverse_zoom }}">
+    <meta name="start-reverselon" content="{{ $reverse_lon }}">
+    <meta name="start-reverselat" content="{{ $reverse_lat }}">
+    <meta name="start-reversezoom" content="{{ $reverse_zoom }}">
     @endif
     @if (isset($waypoints) && isset($vehicle))
-        <meta name="start-vehicle" content="{{ $vehicle }}">
-        <meta name="start-waypoints" content="{{ $waypoints }}">
+    <meta name="start-vehicle" content="{{ $vehicle }}">
+    <meta name="start-waypoints" content="{{ $waypoints }}">
     @endif
     @if (Request::filled('bounded') && in_array(Request::input('bounded'), ['enabled', 'disabled']))
-        <meta name="start-bounded" content="{{ Request::input('bounded') }}">
+    <meta name="start-bounded" content="{{ Request::input('bounded') }}">
     @endif
     @if (isset($navigation_active) && $navigation_active === true)
-        <meta name="navigation_active" content="true">
+    <meta name="navigation_active" content="true">
     @endif
 
+    <link rel="stylesheet" href="{{ mix('/css/map.css') }}" rel="stylesheet" type="text/css">
     <link rel="stylesheet" href="{{ mix('/css/result.css') }}" rel="stylesheet" type="text/css">
     <link rel="stylesheet" href="{{ mix('/css/nav-arrows.css') }}" rel="stylesheet" type="text/css">
     <link rel="stylesheet" href="{{ mix('/css/navbar.css') }}" rel="stylesheet" type="text/css">
@@ -52,13 +53,14 @@
     <link rel="stylesheet" href="{{ mix('/css/routefinder.css') }}" rel="stylesheet" type="text/css">
     <link rel="stylesheet" href="{{ mix('/css/navigation.css') }}" rel="stylesheet" type="text/css">
     @if (!\App::environment('production'))
-        <link rel="stylesheet" href="{{ mix('/css/fakegps.css') }}" rel="stylesheet" type="text/css">
+    <link rel="stylesheet" href="{{ mix('/css/fakegps.css') }}" rel="stylesheet" type="text/css">
     @endif
 </head>
 
 <body>
     <div id="page">
-        <div class="close-container"><button class="close" aria-label="Unterseite schließen" title="Unterseite schließen">❌</button></div>
+        <div class="close-container"><button class="close" aria-label="Unterseite schließen"
+                title="Unterseite schließen">❌</button></div>
         <iframe src="" frameborder="0"></iframe>
     </div>
     <main>
@@ -68,7 +70,7 @@
         @include('addons/route')
         @include('addons/offline')
         @if (!\App::environment('production'))
-            @include('addons/fakegps')
+        @include('addons/fakegps')
         @endif
 
         <div class="map" id="map">
@@ -88,7 +90,7 @@
                 <li><a href="https://metager.de/datenschutz" target="_blank">Datenschutz</a></li>
                 <li><a href="https://metager.de/impressum" target="_blank">Impressum</a></li>
                 @if (!\App::environment('production'))
-                    <li><a href="#" id="nav-fakegps">Fake GPS</a></li>
+                <li><a href="#" id="nav-fakegps">Fake GPS</a></li>
                 @endif
             </ul>
             <div class="updates">
@@ -97,8 +99,7 @@
                 <div>Zeitzone: <span class="data-date-timezone"></span></div>
                 <div>Karte: <span class="data-date-tiles"><img src="/img/ajax-loader.gif" alt="Loading data..."></span>
                 </div>
-                <div>Suche: <span class="data-date-search"><img src="/img/ajax-loader.gif"
-                            alt="Loading data..."></span>
+                <div>Suche: <span class="data-date-search"><img src="/img/ajax-loader.gif" alt="Loading data..."></span>
                 </div>
                 <div>Routen: <span class="data-date-routing"><img src="/img/ajax-loader.gif"
                             alt="Loading data..."></span></div>
@@ -111,4 +112,4 @@
     <script src="{{ mix('js/map.js') }}" type="text/javascript" defer></script>
 </body>
 
-</html>
+</html>
\ No newline at end of file
diff --git a/app/webpack.mix.js b/app/webpack.mix.js
index 03d0d9e..c041d9f 100644
--- a/app/webpack.mix.js
+++ b/app/webpack.mix.js
@@ -13,10 +13,6 @@ const mix = require("laravel-mix");
 
 var mapJsFiles = [
   "resources/js/app.js",
-  "resources/js/map/MetaGerMap.js",
-  "resources/js/map/Maplibre.js",
-  "resources/js/map/Openlayers.js",
-  "resources/js/map/MetaGerMapModule.js",
   "resources/js/AndroidConnector.js",
   "resources/js/NavbarControl.js",
   "resources/js/NominatimParser.js",
@@ -53,6 +49,7 @@ mix
 mix.js(mapJsFiles, "public/js/map.js");
 
 mix.scripts(["resources/js/turf.min.js"], "public/js/turf.min.js");
+mix.less("resources/less/map.less", "public/css/map.css");
 mix.less("resources/less/navbar.less", "public/css/navbar.css");
 mix.less("resources/less/result.less", "public/css/result.css");
 mix.less("resources/less/nav-arrows.less", "public/css/nav-arrows.css");
-- 
GitLab