]> git.openstreetmap.org Git - nominatim-ui.git/blob - src/components/Map.svelte
54268647af9ff2ae297922e2fdebdaff3768dbe0
[nominatim-ui.git] / src / components / Map.svelte
1
2 <script>
3   import * as L from 'leaflet';
4   import 'leaflet-minimap';
5   import 'leaflet/dist/leaflet.css';
6   import 'leaflet-minimap/dist/Control.MiniMap.min.css';
7
8   import { get } from 'svelte/store';
9   import { map_store } from '../lib/stores.js';
10   import MapPosition from '../components/MapPosition.svelte';
11
12   export let display_minimap = false;
13   export let current_result = null;
14   export let position_marker = null;
15
16   let dataLayers = [];
17
18   function createMap(container) {
19     const attribution = Nominatim_Config.Map_Tile_Attribution;
20
21     let map = new L.map(container, {
22       attributionControl: false,
23       scrollWheelZoom: true, // !L.Browser.touch,
24       touchZoom: false,
25       center: [
26         Nominatim_Config.Map_Default_Lat,
27         Nominatim_Config.Map_Default_Lon
28       ],
29       zoom: Nominatim_Config.Map_Default_Zoom
30     });
31
32     if (attribution && attribution.length) {
33       L.control.attribution({ prefix: '<a href="https://leafletjs.com/">Leaflet</a>' }).addTo(map);
34     }
35
36     L.tileLayer(Nominatim_Config.Map_Tile_URL, {
37       attribution: attribution
38     }).addTo(map);
39
40     if (display_minimap) {
41       let osm2 = new L.TileLayer(Nominatim_Config.Map_Tile_URL, {
42         minZoom: 0,
43         maxZoom: 13,
44         attribution: attribution
45       });
46       new L.Control.MiniMap(osm2, { toggleDisplay: true }).addTo(map);
47     }
48
49     const MapPositionControl = L.Control.extend({
50       options: { position: 'topright' },
51       onAdd: () => { return document.getElementById('show-map-position'); }
52     });
53     map.addControl(new MapPositionControl());
54
55     return map;
56   }
57
58   function mapAction(container) {
59     let map = createMap(container);
60     map_store.set(map);
61     setMapData(current_result);
62
63     return {
64       destroy: () => {
65         map_store.set(null);
66         map.remove();
67       }
68     };
69   }
70
71   function parse_and_normalize_geojson_string(part) {
72     // normalize places the geometry into a featurecollection, similar to
73     // https://github.com/mapbox/geojson-normalize
74     var parsed_geojson = {
75       type: 'FeatureCollection',
76       features: [
77         {
78           type: 'Feature',
79           geometry: part,
80           properties: {}
81         }
82       ]
83     };
84     return parsed_geojson;
85   }
86
87   function resetMapData() {
88     let map = get(map_store);
89     if (!map) { return; }
90
91     dataLayers.forEach(function (layer) {
92       map.removeLayer(layer);
93     });
94   }
95
96   function setMapData(aFeature) {
97     let map = get(map_store);
98     if (!map) { return; }
99
100     resetMapData();
101
102     if (position_marker) {
103       // We don't need a marker, but L.circle would change radius when you zoom in/out
104       let cm = L.circleMarker(
105         position_marker,
106         {
107           radius: 5,
108           weight: 2,
109           fillColor: '#ff7800',
110           color: 'red',
111           opacity: 0.75,
112           zIndexOffset: 100,
113           clickable: false
114         }
115       );
116       cm.bindTooltip(`Search (${position_marker[0]},${position_marker[1]})`).openTooltip();
117       cm.addTo(map);
118       dataLayers.push(cm);
119     }
120
121     var search_params = new URLSearchParams(window.location.search);
122     var viewbox = search_params.get('viewbox');
123     if (viewbox) {
124       let coords = viewbox.split(','); // <x1>,<y1>,<x2>,<y2>
125       let bounds = L.latLngBounds([coords[1], coords[0]], [coords[3], coords[2]]);
126       L.rectangle(bounds, {
127         color: '#69d53e',
128         weight: 3,
129         dashArray: '5 5',
130         opacity: 0.8,
131         fill: false
132       }).addTo(map);
133     }
134
135     if (!aFeature) return;
136
137     let lat = aFeature.centroid ? aFeature.centroid.coordinates[1] : aFeature.lat;
138     let lon = aFeature.centroid ? aFeature.centroid.coordinates[0] : aFeature.lon;
139     let geojson = aFeature.geometry || aFeature.geojson;
140
141     if (lat && lon) {
142       let circle = L.circleMarker([lat, lon], {
143         radius: 10, weight: 2, fillColor: '#ff7800', color: 'blue', opacity: 0.75
144       });
145       if (position_marker) { // reverse result
146         circle.bindTooltip('Result').openTooltip();
147       }
148       map.addLayer(circle);
149       dataLayers.push(circle);
150     }
151
152
153     if (geojson) {
154       var geojson_layer = L.geoJson(
155         // https://leafletjs.com/reference-1.7.1.html#path-option
156         parse_and_normalize_geojson_string(geojson),
157         {
158           style: function () {
159             return { interactive: false, color: 'blue' };
160           }
161         }
162       );
163       map.addLayer(geojson_layer);
164       dataLayers.push(geojson_layer);
165       map.fitBounds(geojson_layer.getBounds());
166     } else if (lat && lon && position_marker) {
167       map.fitBounds([[lat, lon], position_marker], { padding: [50, 50] });
168     } else if (lat && lon) {
169       map.setView([lat, lon], 10);
170     }
171   }
172
173   $: setMapData(current_result);
174
175   function show_map_position_click(e) {
176     e.target.style.display = 'none';
177     document.getElementById('map-position').style.display = 'block';
178   }
179 </script>
180
181 <MapPosition />
182 <div id="map" use:mapAction />
183 <div id="show-map-position" class="leaflet-bar btn btn-sm btn-outline-secondary"
184       on:click|stopPropagation={show_map_position_click}
185 >show map bounds</div>
186
187 <style>
188   #map {
189     height: 100%;
190     background:#eee;
191   }
192
193   .btn-outline-secondary {
194     background-color: white;
195   }
196
197   .btn-outline-secondary:hover {
198     color: #111;
199   }
200
201   @media (max-width: 768px) {
202     #map {
203       height: 300px;
204     }
205   }
206
207 </style>