]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index/browse.js
Show OSM objects for Nominatim search results as map overlays
[rails.git] / app / assets / javascripts / index / browse.js
1 //= require templates/browse/feature
2 //= require templates/browse/feature_list
3 //= require templates/browse/feature_history
4
5 $(document).ready(function () {
6   var browseBounds;
7   var layersById;
8   var selectedLayer;
9   var browseObjectList;
10   var areasHidden = false;
11   var locationFilter;
12
13   var dataLayer = new L.OSM.DataLayer(null, {
14     styles: {
15       way: {
16         weight: 3,
17         color: "#000000",
18         opacity: 0.4
19       },
20       area: {
21         weight: 3,
22         color: "#ff0000"
23       },
24       node: {
25         color: "#00ff00"
26       }
27     }
28   });
29
30   dataLayer.isWayArea = function () {
31     return !areasHidden && L.OSM.DataLayer.prototype.isWayArea.apply(this, arguments);
32   };
33
34   dataLayer.on("click", function (e) {
35     onSelect(e.layer);
36   });
37
38   if (OSM.STATUS != 'api_offline' && OSM.STATUS != 'database_offline') {
39     map.layersControl.addOverlay(dataLayer, I18n.t("browse.start_rjs.data_layer_name"));
40   }
41
42   map.on('layeradd', function (e) {
43     if (e.layer === dataLayer) {
44       $.ajax({ url: "/browse/start", success: function (sidebarHtml) {
45         startBrowse(sidebarHtml);
46       }});
47     }
48   });
49
50   map.on('layerremove', function (e) {
51     if (e.layer === dataLayer) {
52       closeSidebar();
53     }
54   });
55
56   function startBrowse(sidebarHtml) {
57     locationFilter = new L.LocationFilter({
58       enableButton: false,
59       adjustButton: false
60     }).addTo(map);
61
62     locationFilter.on("change", getData);
63
64     $("#sidebar_title").html(I18n.t('browse.start_rjs.data_frame_title'));
65     $("#sidebar_content").html(sidebarHtml);
66
67     openSidebar();
68
69     map.on("moveend", updateData);
70     updateData();
71
72     $("#browse_filter_toggle").click(toggleFilter);
73
74     $("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.hide_areas'));
75     $("#browse_hide_areas_box").click(toggleAreas);
76
77     $("#sidebar").one("closed", function () {
78       map.removeLayer(dataLayer);
79       map.removeLayer(locationFilter);
80       map.off("moveend", updateData);
81       locationFilter.off("change", getData);
82     });
83   }
84
85   function updateData() {
86     if (!locationFilter.isEnabled()) {
87       if (map.getZoom() >= 15) {
88         var bounds = map.getBounds();
89         if (!browseBounds || !browseBounds.contains(bounds)) {
90           browseBounds = bounds;
91           getData();
92         }
93       } else {
94         setStatus(I18n.t('browse.start_rjs.zoom_or_select'));
95       }
96     }
97   }
98
99   function toggleFilter() {
100     if (locationFilter.isEnabled()) {
101       $("#browse_filter_toggle").html(I18n.t('browse.start_rjs.manually_select'));
102       locationFilter.disable();
103     } else {
104       $("#browse_filter_toggle").html(I18n.t('browse.start_rjs.view_data'));
105       locationFilter.setBounds(map.getBounds().pad(-0.2));
106       locationFilter.enable();
107     }
108
109     getData();
110   }
111
112   function toggleAreas() {
113     if (areasHidden) {
114       $("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.hide_areas'));
115       areasHidden = false;
116     } else {
117       $("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.show_areas'));
118       areasHidden = true;
119     }
120
121     getData();
122   }
123
124   function displayFeatureWarning(count, limit, callback) {
125     clearStatus();
126
127     var div = document.createElement("div");
128
129     var p = document.createElement("p");
130     p.appendChild(document.createTextNode(I18n.t("browse.start_rjs.loaded_an_area_with_num_features", { num_features: count, max_features: limit })));
131     div.appendChild(p);
132
133     var input = document.createElement("input");
134     input.type = "submit";
135     input.value = I18n.t('browse.start_rjs.load_data');
136     input.onclick = callback;
137     div.appendChild(input);
138
139     $("#browse_content").html("");
140     $("#browse_content").append(div);
141   }
142
143   function getData() {
144     var bounds = locationFilter.isEnabled() ? locationFilter.getBounds() : map.getBounds();
145     var size = bounds.getSize();
146
147     if (size > OSM.MAX_REQUEST_AREA) {
148       setStatus(I18n.t("browse.start_rjs.unable_to_load_size", { max_bbox_size: OSM.MAX_REQUEST_AREA, bbox_size: size }));
149       return;
150     }
151
152     setStatus(I18n.t('browse.start_rjs.loading'));
153
154     var url = "/api/" + OSM.API_VERSION + "/map?bbox=" + bounds.toBBOX();
155
156     /*
157      * Modern browsers are quite happy showing far more than 100 features in
158      * the data browser, so increase the limit to 2000 by default, but keep
159      * it restricted to 500 for IE8 and 100 for older IEs.
160      */
161     var maxFeatures = 2000;
162
163     /*@cc_on
164       if (navigator.appVersion < 8) {
165         maxFeatures = 100;
166       } else if (navigator.appVersion < 9) {
167         maxFeatures = 500;
168       }
169     @*/
170
171     $.ajax({
172       url: url,
173       success: function (xml) {
174         clearStatus();
175
176         $("#browse_content").empty();
177         dataLayer.clearLayers();
178         selectedLayer = null;
179
180         var features = dataLayer.buildFeatures(xml);
181
182         function addFeatures() {
183           dataLayer.addData(features);
184
185           layersById = {};
186
187           dataLayer.eachLayer(function (layer) {
188             var feature = layer.feature;
189             layersById[feature.id] = layer;
190             $.extend(feature, {
191               typeName: featureTypeName(feature),
192               url: "/browse/" + feature.type + "/" + feature.id,
193               name: featureName(feature)
194             });
195           });
196
197           browseObjectList = $(JST["templates/browse/feature_list"]({
198             features: features,
199             url: url
200           }))[0];
201
202           loadObjectList();
203         }
204
205         if (features.length < maxFeatures) {
206           addFeatures();
207         } else {
208           displayFeatureWarning(features.length, maxFeatures, addFeatures);
209         }
210       }
211     });
212   }
213
214   function viewFeatureLink() {
215     var layer = layersById[$(this).data("feature-id")];
216
217     onSelect(layer);
218
219     if (locationFilter.isEnabled()) {
220       map.panTo(layer.getBounds().getCenter());
221     }
222
223     return false;
224   }
225
226   function loadObjectList() {
227     $("#browse_content").html(browseObjectList);
228     $("#browse_content").find("a[data-feature-id]").click(viewFeatureLink);
229
230     return false;
231   }
232
233   function onSelect(layer) {
234     // Unselect previously selected feature
235     if (selectedLayer) {
236       selectedLayer.setStyle(selectedLayer.originalStyle);
237     }
238
239     // Redraw in selected style
240     layer.originalStyle = layer.options;
241     layer.setStyle({color: '#0000ff', weight: 8});
242
243     // If the current object is the list, don't innerHTML="", since that could clear it.
244     if ($("#browse_content").firstChild == browseObjectList) {
245       $("#browse_content").removeChild(browseObjectList);
246     } else {
247       $("#browse_content").empty();
248     }
249
250     var feature = layer.feature;
251
252     $("#browse_content").html(JST["templates/browse/feature"]({
253       name: featureNameSelect(feature),
254       url: "/browse/" + feature.type + "/" + feature.id,
255       attributes: feature.tags
256     }));
257
258     $("#browse_content").find("a.browse_show_list").click(loadObjectList);
259     $("#browse_content").find("a.browse_show_history").click(loadHistory);
260
261     // Stash the currently drawn feature
262     selectedLayer = layer;
263   }
264
265   function loadHistory() {
266     $(this).attr("href", "").text(I18n.t('browse.start_rjs.wait'));
267
268     var feature = selectedLayer.feature;
269
270     $.ajax({
271       url: "/api/" + OSM.API_VERSION + "/" + feature.type + "/" + feature.id + "/history",
272       success: function (xml) {
273         if (selectedLayer.feature != feature || $("#browse_content").firstChild == browseObjectList) {
274           return;
275         }
276
277         $(this).remove();
278
279         var history = [];
280         var nodes = xml.getElementsByTagName(feature.type);
281         for (var i = nodes.length - 1; i >= 0; i--) {
282           history.push({
283             user: nodes[i].getAttribute("user") || I18n.t('browse.start_rjs.private_user'),
284             timestamp: nodes[i].getAttribute("timestamp")
285           });
286         }
287
288         $("#browse_content").append(JST["templates/browse/feature_history"]({
289           name: featureNameHistory(feature),
290           url: "/browse/" + feature.type + "/" + feature.id,
291           history: history
292         }));
293       }.bind(this)
294     });
295
296     return false;
297   }
298
299   function featureTypeName(feature) {
300     return I18n.t('browse.start_rjs.object_list.type.' + feature.type);
301   }
302
303   function featureName(feature) {
304     return feature.tags['name:' + $('html').attr('lang')] ||
305       feature.tags.name ||
306       feature.id;
307   }
308
309   function featureNameSelect(feature) {
310     return feature.tags['name:' + $('html').attr('lang')] ||
311       feature.tags.name ||
312       I18n.t("browse.start_rjs.object_list.selected.type." + feature.type, { id: feature.id });
313   }
314
315   function featureNameHistory(feature) {
316     return feature.tags['name:' + $('html').attr('lang')] ||
317       feature.tags.name ||
318       I18n.t("browse.start_rjs.object_list.history.type." + feature.type, { id: feature.id });
319   }
320
321   function setStatus(status) {
322     $("#browse_status").html(status);
323     $("#browse_status").show();
324   }
325
326   function clearStatus() {
327     $("#browse_status").html("");
328     $("#browse_status").hide();
329   }
330 });