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