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