5 /*********************************************************
7 *********************************************************/
9 function get_config_value(str, default_val) {
10 return (typeof Nominatim_Config[str] !== 'undefined' ? Nominatim_Config[str] : default_val);
13 function parse_and_normalize_geojson_string(raw_string){
14 // normalize places the geometry into a featurecollection, similar to
15 // https://github.com/mapbox/geojson-normalize
16 var parsed_geojson = {
17 type: "FeatureCollection",
21 geometry: JSON.parse(raw_string),
26 return parsed_geojson;
29 function map_link_to_osm(){
30 return "https://openstreetmap.org/#map=" + map.getZoom() + "/" + map.getCenter().lat + "/" + map.getCenter().lng;
33 function map_viewbox_as_string() {
34 // since .toBBoxString() doesn't round numbers
36 map.getBounds().getSouthWest().lng.toFixed(5), // left
37 map.getBounds().getNorthEast().lat.toFixed(5), // top
38 map.getBounds().getNorthEast().lng.toFixed(5), // right
39 map.getBounds().getSouthWest().lat.toFixed(5) // bottom
44 /*********************************************************
46 *********************************************************/
48 function fetch_from_api(endpoint_name, params, callback) {
49 var api_url = get_config_value('Nominatim_API_Endpoint') + endpoint_name + '.php?' + $.param(params);
50 if (endpoint_name !== 'status') {
51 $('#api-request-link').attr('href', api_url);
53 $.get(api_url, function(data){
58 function update_data_date() {
59 fetch_from_api('status', {format: 'json'}, function(data){
60 $('#data-date').text(data.data_last_updated.formatted);
64 function render_template(el, template_name, page_context) {
65 var template_source = $('#' + template_name).text();
66 var template = Handlebars.compile(template_source);
67 var html = template(page_context);
72 /*********************************************************
73 * FORWARD/REVERSE SEARCH PAGE
74 *********************************************************/
77 function display_map_position(mouse_lat_lng){
79 html_mouse = "mouse position " + (mouse_lat_lng ? [mouse_lat_lng.lat.toFixed(5), mouse_lat_lng.lng.toFixed(5)].join(',') : '-');
80 html_click = "last click: " + (last_click_latlng ? [last_click_latlng.lat.toFixed(5),last_click_latlng.lng.toFixed(5)].join(',') : '-');
84 map.getCenter().lat.toFixed(5) + ',' + map.getCenter().lng.toFixed(5) +
85 " <a target='_blank' href='" + map_link_to_osm() + "'>view on osm.org</a>";
87 html_zoom = "map zoom: " + map.getZoom();
89 html_viewbox = "viewbox: " + map_viewbox_as_string();
91 $('#map-position-inner').html([html_center,html_zoom,html_viewbox,html_click,html_mouse].join('<br/>'));
93 var reverse_params = {
94 // lat: map.getCenter().lat.toFixed(5),
95 // lon: map.getCenter().lng.toFixed(5),
99 $('#switch-to-reverse').attr('href', 'reverse.html?' + $.param(reverse_params));
101 $('input#use_viewbox').trigger('change');
107 function init_map_on_search_page(is_reverse_search, nominatim_results, request_lat, request_lon, init_zoom) {
109 map = new L.map('map', {
110 // center: [nominatim_map_init.lat, nominatim_map_init.lon],
111 // zoom: nominatim_map_init.zoom,
112 attributionControl: (get_config_value('Map_Tile_Attribution') && get_config_value('Map_Tile_Attribution').length),
113 scrollWheelZoom: true, // !L.Browser.touch,
118 L.tileLayer(get_config_value('Map_Tile_URL'), {
119 noWrap: true, // otherwise we end up with click coordinates like latitude -728
121 attribution: (get_config_value('Map_Tile_Attribution') || null ) //'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
124 // console.log(Nominatim_Config);
126 map.setView([request_lat, request_lon], init_zoom);
128 var osm2 = new L.TileLayer(get_config_value('Map_Tile_URL'), {minZoom: 0, maxZoom: 13, attribution: (get_config_value('Map_Tile_Attribution') || null )});
129 var miniMap = new L.Control.MiniMap(osm2, {toggleDisplay: true}).addTo(map);
131 if (is_reverse_search) {
132 // We don't need a marker, but an L.circle instance changes radius once you zoom in/out
133 var cm = L.circleMarker([request_lat, request_lon], { radius: 5, weight: 2, fillColor: '#ff7800', color: 'red', opacity: 0.75, clickable: false});
137 var MapPositionControl = L.Control.extend({
141 onAdd: function (map) {
142 var container = L.DomUtil.create('div', 'my-custom-control');
144 $(container).text('show map bounds').addClass('leaflet-bar btn btn-sm btn-default').on('click', function(e){
147 $('#map-position').show();
150 $('#map-position-close a').on('click', function(e){
153 $('#map-position').hide();
161 map.addControl(new MapPositionControl());
167 function update_viewbox_field(){
169 $('input[name=viewbox]').val( $('input#use_viewbox').prop('checked') ? map_viewbox_as_string() : '');
172 map.on('move', function(e) {
173 display_map_position();
174 update_viewbox_field();
177 map.on('mousemove', function(e) {
178 display_map_position(e.latlng);
181 map.on('click', function(e) {
182 last_click_latlng = e.latlng;
183 display_map_position();
186 map.on('load', function(e){
187 display_map_position();
191 $('input#use_viewbox').on('change', function(){
192 update_viewbox_field();
198 function get_result_element(position){
199 return $('.result').eq(position);
201 function marker_for_result(result){
202 return L.marker([result.lat,result.lon], {riseOnHover:true,title:result.name });
204 function circle_for_result(result){
205 return L.circleMarker([result.lat,result.lon], { radius: 10, weight: 2, fillColor: '#ff7800', color: 'blue', opacity: 0.75, clickable: !is_reverse_search});
208 var layerGroup = new L.layerGroup().addTo(map);
209 function highlight_result(position, bool_focus){
210 var result = nominatim_results[position];
211 if (!result){ return }
212 var result_el = get_result_element(position);
214 $('.result').removeClass('highlight');
215 result_el.addClass('highlight');
217 layerGroup.clearLayers();
220 var circle = circle_for_result(result);
221 circle.on('click', function(){
222 highlight_result(position);
224 layerGroup.addLayer(circle);
226 if (result.aBoundingBox){
228 var bounds = [[result.aBoundingBox[0]*1,result.aBoundingBox[2]*1], [result.aBoundingBox[1]*1,result.aBoundingBox[3]*1]];
229 map.fitBounds(bounds);
231 if (result.asgeojson && result.asgeojson.match(/(Polygon)|(Line)/) ){
233 var geojson_layer = L.geoJson(
234 parse_and_normalize_geojson_string(result.asgeojson),
236 // http://leafletjs.com/reference-1.0.3.html#path-option
237 style: function(feature) {
238 return { interactive: false, color: 'blue' };
242 layerGroup.addLayer(geojson_layer);
245 // var layer = L.rectangle(bounds, {color: "#ff7800", weight: 1} );
246 // layerGroup.addLayer(layer);
250 var result_coord = L.latLng(result.lat, result.lon);
252 if ( is_reverse_search ){
253 // console.dir([result_coord, [request_lat, request_lon]]);
254 // make sure the search coordinates are in the map view as well
255 map.fitBounds([result_coord, [request_lat, request_lon]], {padding: [50,50], maxZoom: map.getZoom()});
257 // better, but causes a leaflet warning
258 // map.panInsideBounds([[result.lat,result.lon], [nominatim_map_init.lat,nominatim_map_init.lon]], {animate: false});
261 map.panTo(result_coord, result.zoom || get_config_value('Map_Default_Zoom'));
271 $('.result').on('click', function(e){
272 highlight_result($(this).data('position'), true);
275 if ( is_reverse_search ){
276 map.on('click', function(e){
277 $('form input[name=lat]').val( e.latlng.lat);
278 $('form input[name=lon]').val( e.latlng.lng);
282 $('#switch-coords').on('click', function(e){
285 var lat = $('form input[name=lat]').val();
286 var lon = $('form input[name=lon]').val();
287 $('form input[name=lat]').val(lon);
288 $('form input[name=lon]').val(lat);
293 highlight_result(0, false);
295 // common mistake is to copy&paste latitude and longitude into the 'lat' search box
296 $('form input[name=lat]').on('change', function(){
297 var coords = $(this).val().split(',');
298 if (coords.length == 2) {
299 $(this).val(L.Util.trim(coords[0]));
300 $(this).siblings('input[name=lon]').val(L.Util.trim(coords[1]));
314 jQuery(document).ready(function(){
316 if ( !$('#search-page,#reverse-page').length ){ return; }
318 var is_reverse_search = !!( $('#reverse-page').length );
319 var endpoint = is_reverse_search ? 'reverse' : 'search';
322 var search_params = new URLSearchParams(location.search);
325 // return view('search', [
326 // 'sQuery' => $sQuery,
329 // 'aSearchResults' => $aSearchResults,
330 // 'sMoreURL' => 'example.com',
331 // 'sDataDate' => $this->fetch_status_date(),
336 if (is_reverse_search) {
337 var api_request_params = {
338 lat: typeof(search_params.get('lat') !== 'undefined') ? search_params.get('lat') : get_config_value('Map_Default_Lat'),
339 lon: typeof(search_params.get('lon') !== 'undefined') ? search_params.get('lon') : get_config_value('Map_Default_Lon'),
340 zoom: (search_params.get('zoom') !== '' ? search_params.get('zoom') : get_config_value('Map_Default_Zoom')),
344 fetch_from_api('reverse', api_request_params, function(aPlace){
352 fLat: api_request_params.lat,
353 fLon: api_request_params.lon,
354 iZoom: (api_request_params.zoom !== '' ? api_request_params.zoom : undefined)
357 render_template($('main'), 'reversepage-template', context);
359 init_map_on_search_page(is_reverse_search, [aPlace], api_request_params.lat, api_request_params.lon, api_request_params.zoom);
364 var api_request_params = {
365 q: search_params.get('q'),
366 polygon_geojson: search_params.get('polygon_geojson') ? 1 : 0,
367 polygon: search_params.get('polygon'),
371 fetch_from_api('search', api_request_params, function(aResults){
374 aSearchResults: aResults,
375 sQuery: api_request_params.q,
377 env: Nominatim_Config,
381 render_template($('main'), 'searchpage-template', context);
383 init_map_on_search_page(is_reverse_search, aResults);
393 /*********************************************************
395 *********************************************************/
399 function init_map_on_detail_page(lat, lon, geojson) {
400 map = new L.map('map', {
401 // center: [nominatim_map_init.lat, nominatim_map_init.lon],
402 // zoom: nominatim_map_init.zoom,
403 attributionControl: (get_config_value('Map_Tile_Attribution') && get_config_value('Map_Tile_Attribution').length),
404 scrollWheelZoom: true, // !L.Browser.touch,
408 L.tileLayer(get_config_value('Map_Tile_URL'), {
410 attribution: (get_config_value('Map_Tile_Attribution') || null ) //'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
413 var layerGroup = new L.layerGroup().addTo(map);
415 var circle = L.circleMarker([lat,lon], { radius: 10, weight: 2, fillColor: '#ff7800', color: 'blue', opacity: 0.75});
416 map.addLayer(circle);
419 var geojson_layer = L.geoJson(
420 // http://leafletjs.com/reference-1.0.3.html#path-option
421 parse_and_normalize_geojson_string(geojson),
423 style: function(feature) {
424 return { interactive: false, color: 'blue' };
428 map.addLayer(geojson_layer);
429 map.fitBounds(geojson_layer.getBounds());
431 map.setView([lat,lon],10);
434 var osm2 = new L.TileLayer(get_config_value('Map_Tile_URL'), {minZoom: 0, maxZoom: 13, attribution: (get_config_value('Map_Tile_Attribution') || null )});
435 var miniMap = new L.Control.MiniMap(osm2, {toggleDisplay: true}).addTo(map);
438 jQuery(document).ready(function(){
440 if ( !$('#details-page').length ){ return; }
442 var search_params = new URLSearchParams(location.search);
443 // var place_id = search_params.get('place_id');
445 var api_request_params = {
446 place_id: search_params.get('place_id'),
451 fetch_from_api('details', api_request_params, function(aFeature){
453 var context = { aPlace: aFeature };
455 render_template($('main'), 'detailspage-template', context);
459 init_map_on_detail_page(aFeature.lat, aFeature.lon, aFeature.asgeojson);