aboutsummaryrefslogtreecommitdiff
path: root/modern/src/map
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-06-19 09:36:12 -0700
committerAnton Tananaev <anton@traccar.org>2022-06-19 09:36:12 -0700
commite3827fbad0cb51e0e1e92b722d1c0c132c3d07c9 (patch)
tree585321727f053558cfac8bbcd0e2041cb7fbf297 /modern/src/map
parente67aafb900f474da2381ea418c3e8eb09bc4683b (diff)
downloadtrackermap-web-e3827fbad0cb51e0e1e92b722d1c0c132c3d07c9.tar.gz
trackermap-web-e3827fbad0cb51e0e1e92b722d1c0c132c3d07c9.tar.bz2
trackermap-web-e3827fbad0cb51e0e1e92b722d1c0c132c3d07c9.zip
Implement geocoding search
Diffstat (limited to 'modern/src/map')
-rw-r--r--modern/src/map/geocoder/MapGeocoder.js56
-rw-r--r--modern/src/map/geocoder/geocoder.css223
2 files changed, 279 insertions, 0 deletions
diff --git a/modern/src/map/geocoder/MapGeocoder.js b/modern/src/map/geocoder/MapGeocoder.js
new file mode 100644
index 00000000..de1791e6
--- /dev/null
+++ b/modern/src/map/geocoder/MapGeocoder.js
@@ -0,0 +1,56 @@
+import './geocoder.css';
+import maplibregl from 'maplibre-gl';
+import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder';
+import { useEffect } from 'react';
+import { useDispatch } from 'react-redux';
+import { map } from '../core/MapView';
+import { errorsActions } from '../../store';
+
+const MapGeocoder = () => {
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ const geocoder = {
+ forwardGeocode: async (config) => {
+ const features = [];
+ try {
+ const request = `https://nominatim.openstreetmap.org/search?q=${config.query}&format=geojson&polygon_geojson=1&addressdetails=1`;
+ const response = await fetch(request);
+ const geojson = await response.json();
+ geojson.features.forEach((feature) => {
+ const center = [
+ feature.bbox[0] + (feature.bbox[2] - feature.bbox[0]) / 2,
+ feature.bbox[1] + (feature.bbox[3] - feature.bbox[1]) / 2,
+ ];
+ features.push({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: center,
+ },
+ place_name: feature.properties.display_name,
+ properties: feature.properties,
+ text: feature.properties.display_name,
+ place_type: ['place'],
+ center,
+ });
+ });
+ } catch (e) {
+ dispatch(errorsActions.push(e.message));
+ }
+ return { features };
+ },
+ };
+
+ const control = new MaplibreGeocoder(geocoder, {
+ maplibregl,
+ collapsed: true,
+ });
+ map.addControl(control);
+ return () => map.removeControl(control);
+ }, [dispatch]);
+
+ return null;
+};
+
+export default MapGeocoder;
diff --git a/modern/src/map/geocoder/geocoder.css b/modern/src/map/geocoder/geocoder.css
new file mode 100644
index 00000000..86ebf6e5
--- /dev/null
+++ b/modern/src/map/geocoder/geocoder.css
@@ -0,0 +1,223 @@
+/* Basics */
+.maplibregl-ctrl-geocoder,
+.maplibregl-ctrl-geocoder *,
+.maplibregl-ctrl-geocoder *:after,
+.maplibregl-ctrl-geocoder *:before {
+ box-sizing: border-box;
+}
+
+.maplibregl-ctrl-geocoder {
+ font-size: 15px;
+ line-height: 20px;
+ font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, sans-serif;
+ position: relative;
+ background-color: #fff;
+ width: 100%;
+ min-width: 240px;
+ max-width: 360px;
+ z-index: 1;
+ border-radius: 4px;
+ transition: width 0.25s, min-width 0.25s;
+}
+
+.maplibregl-ctrl-geocoder--input {
+ font: inherit;
+ width: 100%;
+ border: 0;
+ background-color: transparent;
+ margin: 0;
+ height: 29px;
+ color: #404040; /* fallback */
+ color: rgba(0, 0, 0, 0.75);
+ padding: 6px 30px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.maplibregl-ctrl-geocoder--input:focus {
+ color: #404040; /* fallback */
+ color: rgba(0, 0, 0, 0.75);
+ outline: 0;
+ box-shadow: none;
+ outline: thin dotted;
+}
+
+.maplibregl-ctrl-geocoder .maplibregl-ctrl-geocoder--pin-right > * {
+ z-index: 2;
+ position: absolute;
+ right: 5px;
+ top: 5px;
+ display: none;
+}
+
+.maplibregl-ctrl-geocoder,
+.maplibregl-ctrl-geocoder .suggestions {
+ box-shadow: 0 0 0 2px rgb(0 0 0 / 10%);
+}
+
+/* Collapsed */
+.maplibregl-ctrl-geocoder.maplibregl-ctrl-geocoder--collapsed {
+ width: 29px;
+ min-width: 29px;
+ transition: width 0.25s, min-width 0.25s;
+}
+
+/* Suggestions */
+.maplibregl-ctrl-geocoder .suggestions {
+ background-color: #fff;
+ border-radius: 4px;
+ left: 0;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ width: 100%;
+ top: 110%; /* fallback */
+ top: calc(100% + 6px);
+ z-index: 1000;
+ overflow: hidden;
+ font-size: 13px;
+}
+
+.maplibregl-ctrl-bottom-left .suggestions,
+.maplibregl-ctrl-bottom-right .suggestions {
+ top: auto;
+ bottom: 100%;
+}
+
+.maplibregl-ctrl-geocoder .suggestions > li > a {
+ cursor: default;
+ display: block;
+ padding: 6px 12px;
+ color: #404040;
+}
+
+.maplibregl-ctrl-geocoder .suggestions > .active > a,
+.maplibregl-ctrl-geocoder .suggestions > li > a:hover {
+ color: #404040;
+ background-color: #f3f3f3;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.maplibregl-ctrl-geocoder--suggestion {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.maplibre-ctrl-geocoder--suggestion-icon {
+ min-width: 30px;
+ min-height: 24px;
+ max-width: 30px;
+ max-height: 24px;
+ padding-right: 12px;
+}
+
+.maplibregl-ctrl-geocoder--suggestion-info {
+ display: flex;
+ flex-direction: column;
+}
+
+.maplibregl-ctrl-geocoder--suggestion-match {
+ font-weight: bold;
+}
+
+.maplibregl-ctrl-geocoder--suggestion-title,
+.maplibregl-ctrl-geocoder--suggestion-address {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.maplibregl-ctrl-geocoder--result {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.maplibre-ctrl-geocoder--result-icon {
+ min-width: 30px;
+ min-height: 24px;
+ max-width: 30px;
+ max-height: 24px;
+ padding-right: 12px;
+}
+
+.maplibregl-ctrl-geocoder--result-title {
+ font-weight: bold;
+}
+
+.maplibregl-ctrl-geocoder--result-title,
+.maplibregl-ctrl-geocoder--result-address {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+/* Icons */
+.maplibregl-ctrl-geocoder--icon {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.maplibregl-ctrl-geocoder--icon-search {
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 23px;
+ height: 23px;
+}
+
+.maplibregl-ctrl-geocoder--button {
+ padding: 0;
+ margin: 0;
+ border: none;
+ cursor: pointer;
+ background: #fff;
+ line-height: 1;
+}
+
+.maplibregl-ctrl-geocoder--icon-close {
+ width: 20px;
+ height: 20px;
+}
+
+.maplibregl-ctrl-geocoder--icon-loading {
+ width: 20px;
+ height: 20px;
+ -moz-animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
+ -webkit-animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
+ animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
+}
+
+/* Animation */
+@-webkit-keyframes rotate {
+ from {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes rotate {
+ from {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+.maplibre-gl-geocoder--error {
+ color: #909090;
+ padding: 6px 12px;
+ font-size: 16px;
+ text-align: center;
+}