Tidy up map browse code and qualify various global names to avoid
[rails.git] / app / views / browse / start.rjs
1 page.replace_html :sidebar_title, 'Data'
2 page.replace_html :sidebar_content, :partial => 'start'
3 page << <<EOJ
4   var browseBoxControl;
5   var browseActive;
6   var browseMode = "auto";
7   var browseBounds;
8   var browseFeatureList;
9   var browseActiveFeature;
10   var browseDataLayer;
11   var browseSelectControl;
12   var browseObjectList;
13
14   OpenLayers.Feature.Vector.style['default'].strokeWidth = 3;
15   OpenLayers.Feature.Vector.style['default'].cursor = "pointer";
16     
17   function startBrowse() {
18     openSidebar({ onclose: stopBrowse });
19
20     var vectors = new OpenLayers.Layer.Vector();
21     
22     browseBoxControl = new OpenLayers.Control.DrawFeature(vectors, OpenLayers.Handler.RegularPolygon, { 
23       handlerOptions: {
24         sides: 4,
25         snapAngle: 90,
26         irregular: true,
27         persist: true,
28         callbacks: { done: endDrag }
29       }
30     });
31     map.addControl(browseBoxControl);
32
33     map.events.register("moveend", map, showData);
34     map.events.triggerEvent("moveend");
35
36     browseActive = true;
37   }
38
39   function showData() {
40     if (browseMode == "auto") {
41       if (map.getZoom() >= 15) {
42           useMap();
43       } else {
44           $("browse_status").innerHTML = "Zoom in or Select an area of the map to view.";
45       }    
46     }
47   }
48
49   function stopBrowse() {
50     if (browseActive) {
51       browseActive = false;
52
53       if (browseDataLayer) {
54         browseDataLayer.destroy();
55         browseDataLayer = null; 
56       } 
57
58       if (browseSelectControl) {   
59         browseSelectControl.destroy();  
60         browseSelectControl = null;
61       } 
62
63       if (browseBoxControl) {
64         browseBoxControl.destroy();
65         browseBoxControl = null;
66       }         
67
68       if (browseActiveFeature) {
69         browseActiveFeature.destroy(); 
70         browseActiveFeature = null; 
71       }
72
73       map.dataLayer.setVisibility(false);
74       map.events.unregister("moveend", map, showData);
75     }    
76   }
77     
78   function startDrag() {
79     $("browse_select_box").innerHTML='Drag a box on the map to select an area';
80
81     browseBoxControl.activate();
82
83     return false;
84   };
85
86   $("browse_select_box").onclick = startDrag;
87     
88   function useMap() {
89     var bounds = map.getExtent();
90     var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
91
92     if (!browseBounds || !browseBounds.containsBounds(projected)) {
93       var center = bounds.getCenterLonLat();
94       var tileWidth = bounds.getWidth() * 1.2;
95       var tileHeight = bounds.getHeight() * 1.2;
96       var tileBounds = new OpenLayers.Bounds(center.lon - (tileWidth / 2),
97                                              center.lat - (tileHeight / 2),
98                                              center.lon + (tileWidth / 2),
99                                              center.lat + (tileHeight / 2));
100
101       browseBounds = tileBounds;
102       getData(tileBounds);
103
104       browseMode = "auto";
105
106       $("browse_select_view").style.display = "none";
107     }
108
109     return false;
110   }
111
112   $("browse_select_view").onclick = useMap;
113     
114   function endDrag(bbox) {
115     var bounds = bbox.getBounds();
116     var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
117
118     browseBoxControl.deactivate();
119     browseBounds = projected;
120     getData(bounds);
121
122     browseMode = "manual";  
123
124     $("browse_select_box").innerHTML = "Manually select a different area";
125     $("browse_select_view").style.display = "inline";
126   }
127     
128   function displayFeatureWarning() {
129     $("browse_status").innerHTML = "";
130
131     var div = document.createElement("div");
132
133     var p = document.createElement("p");
134     p.appendChild(document.createTextNode("You have loaded an area which contains " + browseFeatureList.length + " features. In general, some browsers may not cope well with displaying this quantity of data. Generally, browsers work best at displaying less than 100 features at a time: doing anything else may make your browser slow/unresponsive. If you are sure you want to display this data, you may do so by clicking the button below."));
135     div.appendChild(p);
136
137     var input = document.createElement("input");
138     input.type = "submit";
139     input.value = "Load Data";
140     input.onclick = loadFeatureList;
141     div.appendChild(input); 
142
143     $("browse_object_list").innerHTML = "";
144     $("browse_object_list").appendChild(div);
145   }
146     
147   function loadFeatureList() {
148     browseDataLayer.addFeatures(browseFeatureList);
149     browseDataLayer.events.triggerEvent("loadend");
150
151     browseFeatureList = []; 
152
153     return false;
154   }    
155
156   function customDataLoader(request) { 
157     if (browseActive) {
158       var doc = request.responseXML;
159
160       if (!doc || !doc.documentElement) {
161         doc = request.responseText;
162       }
163
164       var options = {};
165
166       OpenLayers.Util.extend(options, this.formatOptions);
167
168       if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
169         options.externalProjection = this.projection;
170         options.internalProjection = this.map.getProjectionObject();
171       }    
172
173       var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
174
175       browseFeatureList = gml.read(doc);
176
177       if (!this.maxFeatures || browseFeatureList.length <= this.maxFeatures) {
178         loadFeatureList();
179       } else {
180         displayFeatureWarning();
181       }
182     }
183   }
184
185   function getData(bounds) {
186     var projected = bounds.clone().transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
187     var size = projected.getWidth() * projected.getHeight();
188
189     if (size > 0.25) {
190       $("browse_status").innerHTML = "Unable to load: Bounding box size of " + size + " is too large. (Must be smaller than 0.25)<br />";
191     } else {
192       loadGML("/api/0.5/map?bbox=" + projected.toBBOX());
193     }
194   }
195
196   function loadGML(url) {
197     $("browse_status").innerHTML = "Loading...";
198     $("browse_object_list").innerHTML = "";
199
200     if (!browseDataLayer) {
201       var style = new OpenLayers.Style();
202
203       style.addRules([new OpenLayers.Rule({
204         symbolizer: {
205           Polygon: { fillColor: '#ff0000', strokeColor: '#ff0000' },
206           Line: { fillColor: '#ffff00', strokeColor: '#000000', strokeOpacity: '0.4' },
207           Point: { fillColor: '#00ff00', strokeColor: '#00ff00' }
208         }
209       })]);
210
211       browseDataLayer = new OpenLayers.Layer.GML("Data", url, {
212         format: OpenLayers.Format.OSM,
213         formatOptions: { checkTags: true },
214         maxFeatures: 100,
215         requestSuccess: customDataLoader,
216         displayInLayerSwitcher: false,
217         styleMap: new OpenLayers.StyleMap({
218           default: style,
219           select: { strokeColor: '#0000ff', strokeWidth: 8 }
220         })
221       });
222       browseDataLayer.events.register("loadend", browseDataLayer, dataLoaded );
223       map.addLayer(browseDataLayer);
224             
225       browseSelectControl = new OpenLayers.Control.SelectFeature(browseDataLayer, { onSelect: onFeatureSelect });
226       browseSelectControl.handler.stopDown = false;
227       browseSelectControl.handler.stopUp = false;
228       map.addControl(browseSelectControl);
229       browseSelectControl.activate();
230     } else {
231       browseDataLayer.setUrl(url);
232     }
233
234     browseActiveFeature = null;
235   }
236
237   function dataLoaded() {
238     if (browseActive) {
239       $("browse_status").innerHTML = "Loaded."
240         
241       var list = document.createElement("ul");
242
243       for (var i = 0; i < this.features.length; i++) {
244         var feature = this.features[i]; 
245             
246         // Type, for linking
247         var type = "way";
248         if (feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
249           type = "node";
250         } 
251         var typeName = type.substr(0,1).toUpperCase() + type.substr(1,type.length); 
252         var li = document.createElement("li");
253         li.appendChild(document.createTextNode(typeName + " "));
254             
255         // Link, for viewing in the tab
256         var link = document.createElement("a");
257         link.href =  "/browse/" + type + "/" + feature.osm_id; 
258         var name = feature.attributes.name || feature.osm_id;
259         link.appendChild(document.createTextNode(name));
260         link.feature = feature;
261         link.onclick = OpenLayers.Function.bind(viewFeatureLink, link);   
262         li.appendChild(link);
263
264         list.appendChild(li);
265       }
266
267       browseObjectList = document.createElement("div")
268       browseObjectList.appendChild(list)
269
270       var link = document.createElement("a");
271       link.href = this.url;
272       link.appendChild(document.createTextNode("API"));
273       browseObjectList.appendChild(link);
274
275       $("browse_object_list").innerHTML = "";
276       $("browse_object_list").appendChild(browseObjectList); 
277     }
278   }
279     
280   function viewFeatureLink() {
281     var layer = this.feature.layer;
282
283     for (var i = 0; i < layer.selectedFeatures.length; i++) {
284       var f = layer.selectedFeatures[i]; 
285       layer.drawFeature(f, layer.styleMap.createSymbolizer(f, "default"));
286     }
287
288     onFeatureSelect(this.feature);
289
290     if (browseMode != "auto") {
291       map.setCenter(this.feature.geometry.getBounds().getCenterLonLat()); 
292     }
293
294     return false;
295   }
296     
297   function loadObjectList() {
298     $("browse_object_list").innerHTML="";
299     $("browse_object_list").appendChild(browseObjectList);
300
301     return false;
302   }
303       
304   function onFeatureSelect(feature) {
305     // Unselect previously selected feature
306     if (browseActiveFeature) {
307       browseActiveFeature.layer.drawFeature(
308         browseActiveFeature, 
309         browseActiveFeature.layer.styleMap.createSymbolizer(browseActiveFeature, "default")
310       );
311     }
312
313     // Redraw in selected style
314     feature.layer.drawFeature(
315       feature, feature.layer.styleMap.createSymbolizer(feature, "select")
316     );
317
318     // If the current object is the list, don't innerHTML="", since that could clear it.
319     if ($("browse_object_list").firstChild == browseObjectList) { 
320       $("browse_object_list").removeChild(browseObjectList);
321     } else { 
322       $("browse_object_list").innerHTML = "";
323     }   
324         
325     // Create a link back to the object list
326     var link = document.createElement("a");
327     link.href = "#";
328     link.onclick = loadObjectList;
329     link.appendChild(document.createTextNode("Back to Object List"));
330
331     var div = document.createElement("div");
332     div.appendChild(link)
333
334     $("browse_object_list").appendChild(div);    
335         
336     var type = "way";
337     if (feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
338         type = "node";
339     }    
340         
341     var link = document.createElement("a");   
342     link.href = "/browse/" + type + "/" + feature.osm_id;
343     link.appendChild(document.createTextNode("Database entry for " + feature.osm_id));
344         
345     var div = document.createElement("div");
346     div.style.marginTop = "20px"
347     div.appendChild(link);
348
349     $("browse_object_list").appendChild(div);
350
351     // Now the list of attributes
352     var ul = document.createElement("ul");
353     for (var key in feature.attributes) {
354       var li = document.createElement("li");
355       var b = document.createElement("b");
356       b.appendChild(document.createTextNode(key));
357       li.appendChild(b);
358       li.appendChild(document.createTextNode(": " + feature.attributes[key]));
359       ul.appendChild(li);
360     }
361         
362     $("browse_object_list").appendChild(ul);
363         
364     var link = document.createElement("a");   
365     link.href =  "/browse/"+type+"/"+feature.osm_id+"/history";
366     link.appendChild(document.createTextNode("History"));
367     link.onclick = OpenLayers.Function.bind(loadHistory, {
368       type: type, feature: feature, link: link
369     });
370         
371     $("browse_object_list").appendChild(link);
372
373     // Stash the currently drawn feature
374     browseActiveFeature = feature; 
375   }   
376
377   function loadHistory() {
378     this.link.href = "";
379     this.link.innerHTML = "Wait...";
380
381     new Ajax.Request("/api/0.5/" + this.type + "/" + this.feature.osm_id + "/history", {
382       onComplete: OpenLayers.Function.bind(displayHistory, this)
383     });
384
385     return false;
386   }
387
388   function displayHistory(request) {
389     if (browseActiveFeature.osm_id != this.feature.osm_id || $("browse_object_list").firstChild == browseObjectList)  { 
390         return false;
391     } 
392
393     this.link.parentNode.removeChild(this.link);
394
395     var doc = request.responseXML;
396     var div = document.createElement("div");
397     var h3 = document.createElement("h3"); 
398     h3.appendChild(document.createTextNode("History"));
399     div.appendChild(h3);
400
401     var nodes = doc.getElementsByTagName(this.type);
402     var history = document.createElement("ul");  
403     for (var i = nodes.length - 1; i >= 0; i--) {
404       var user = nodes[i].getAttribute("user") || "private user";
405       var timestamp = nodes[i].getAttribute("timestamp");
406       var item = document.createElement("li");
407       item.appendChild(document.createTextNode("Edited by " + user + " at " + timestamp));
408       history.appendChild(item);
409     }
410     div.appendChild(history);
411
412     var link = document.createElement("a");
413     link.appendChild(document.createTextNode("History entry for " + this.feature.osm_id));
414     link.href = "/browse/"+this.type+"/"+this.feature.osm_id+"/history";
415     div.appendChild(link);
416
417     $("browse_object_list").appendChild(div); 
418   }
419     
420   startBrowse();
421 EOJ