]> git.openstreetmap.org Git - rails.git/commitdiff
Merge data browser branch to trunk.
authorTom Hughes <tom@compton.nu>
Fri, 27 Jun 2008 23:09:05 +0000 (23:09 +0000)
committerTom Hughes <tom@compton.nu>
Fri, 27 Jun 2008 23:09:05 +0000 (23:09 +0000)
31 files changed:
app/controllers/application.rb
app/controllers/browse_controller.rb [new file with mode: 0644]
app/helpers/browse_helper.rb [new file with mode: 0644]
app/models/old_node.rb
app/models/old_relation.rb
app/models/old_way.rb
app/models/relation.rb
app/models/way.rb
app/views/browse/_common_details.rhtml [new file with mode: 0644]
app/views/browse/_containing_relation.rhtml [new file with mode: 0644]
app/views/browse/_map.rhtml [new file with mode: 0644]
app/views/browse/_navigation.rhtml [new file with mode: 0644]
app/views/browse/_node_details.rhtml [new file with mode: 0644]
app/views/browse/_relation_details.rhtml [new file with mode: 0644]
app/views/browse/_relation_member.rhtml [new file with mode: 0644]
app/views/browse/_start.rhtml [new file with mode: 0644]
app/views/browse/_tag.rhtml [new file with mode: 0644]
app/views/browse/_way_details.rhtml [new file with mode: 0644]
app/views/browse/index.rhtml [new file with mode: 0644]
app/views/browse/node.rhtml [new file with mode: 0644]
app/views/browse/node_history.rhtml [new file with mode: 0644]
app/views/browse/relation.rhtml [new file with mode: 0644]
app/views/browse/relation_history.rhtml [new file with mode: 0644]
app/views/browse/start.rjs [new file with mode: 0644]
app/views/browse/way.rhtml [new file with mode: 0644]
app/views/browse/way_history.rhtml [new file with mode: 0644]
app/views/site/index.rhtml
config/routes.rb
public/javascripts/map.js
public/openlayers/OpenStreetMap.js
public/stylesheets/site.css

index 7570fd82a072d2af2d2e70dd0e33cd2d83df391d..918e4b617822610bcd33c23ae5c48660af7e8b08 100644 (file)
@@ -43,8 +43,8 @@ class ApplicationController < ActionController::Base
     end 
   end 
 
-  def check_database_availability
-    if OSM_STATUS == :database_offline
+  def check_database_availability(need_api = false)
+    if OSM_STATUS == :database_offline or (need_api and OSM_STATUS == :api_offline)
       redirect_to :controller => 'site', :action => 'offline'
     end
   end
diff --git a/app/controllers/browse_controller.rb b/app/controllers/browse_controller.rb
new file mode 100644 (file)
index 0000000..f3a0451
--- /dev/null
@@ -0,0 +1,109 @@
+class BrowseController < ApplicationController
+  layout 'site'
+
+  before_filter :authorize_web  
+  before_filter { |c| c.check_database_availability(true) }
+
+  def start 
+  end
+  
+  def index
+    @nodes = Node.find(:all, :order => "timestamp DESC", :limit=> 20)  
+  end
+  
+  def relation 
+    begin
+      @relation = Relation.find(params[:id])
+     
+      @name = @relation.tags['name'].to_s 
+      if @name.length == 0:
+       @name = "#" + @relation.id.to_s
+      end
+       
+      @title = 'Relation | ' + (@name)
+      @next = Relation.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @relation.id }] ) 
+      @prev = Relation.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @relation.id }] ) 
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    end
+  end
+  
+  def relation_history
+    begin
+      @relation = Relation.find(params[:id])
+     
+      @name = @relation.tags['name'].to_s 
+      if @name.length == 0:
+       @name = "#" + @relation.id.to_s
+      end
+       
+      @title = 'Relation History | ' + (@name)
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    end
+  end
+  
+  def way 
+    begin
+      @way = Way.find(params[:id])
+     
+      @name = @way.tags['name'].to_s 
+      if @name.length == 0:
+       @name = "#" + @way.id.to_s
+      end
+       
+      @title = 'Way | ' + (@name)
+      @next = Way.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @way.id }] ) 
+      @prev = Way.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @way.id }] ) 
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    end
+  end
+  
+  def way_history 
+    begin
+      @way = Way.find(params[:id])
+     
+      @name = @way.tags['name'].to_s 
+      if @name.length == 0:
+       @name = "#" + @way.id.to_s
+      end
+       
+      @title = 'Way History | ' + (@name)
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    end
+  end
+
+  def node 
+    begin
+      @node = Node.find(params[:id])
+     
+      @name = @node.tags_as_hash['name'].to_s 
+      if @name.length == 0:
+       @name = "#" + @node.id.to_s
+      end
+       
+      @title = 'Node | ' + (@name)
+      @next = Node.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @node.id }] ) 
+      @prev = Node.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @node.id }] ) 
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    end
+  end
+  
+  def node_history 
+    begin
+      @node = Node.find(params[:id])
+     
+      @name = @node.tags_as_hash['name'].to_s 
+      if @name.length == 0:
+       @name = "#" + @node.id.to_s
+      end
+       
+      @title = 'Node History | ' + (@name)
+    rescue ActiveRecord::RecordNotFound
+      render :nothing => true, :status => :not_found
+    end
+  end
+end
diff --git a/app/helpers/browse_helper.rb b/app/helpers/browse_helper.rb
new file mode 100644 (file)
index 0000000..c86ad5b
--- /dev/null
@@ -0,0 +1,2 @@
+module BrowseHelper
+end
index 891b89731ec2062071b06f5605e7442ec971b3f2..76eab8427b2c570cce79846887706eb6c10923b6 100644 (file)
@@ -50,4 +50,22 @@ class OldNode < ActiveRecord::Base
     el1['timestamp'] = self.timestamp.xmlschema
     return el1
   end
+  
+  def tags_as_hash
+    hash = {}
+    Tags.split(self.tags) do |k,v|
+      hash[k] = v
+    end
+    hash
+  end
+
+  # Pretend we're not in any ways
+  def ways
+    return []
+  end
+
+  # Pretend we're not in any relations
+  def containing_relation_members
+    return []
+  end
 end
index 6da7814c28a40d442e856239e09149b2ca8727c5..bac03c4d2eff6811a9dc4b5d2c32e3bb988e76b3 100644 (file)
@@ -107,5 +107,20 @@ class OldRelation < ActiveRecord::Base
       el1 << e
     end
     return el1
-  end 
+  end
+
+  # Temporary method to match interface to nodes
+  def tags_as_hash
+    return self.tags
+  end
+
+  # Temporary method to match interface to relations
+  def relation_members
+    return self.old_members
+  end
+
+  # Pretend we're not in any relations
+  def containing_relation_members
+    return []
+  end
 end
index a2b165e42716f5cfa7f095aa76654616274f95b5..1abb23bbbac13292b4f5f6949781b5cd4b55ff8f 100644 (file)
@@ -108,5 +108,20 @@ class OldWay < ActiveRecord::Base
       el1 << e
     end
     return el1
-  end 
+  end
+
+  # Temporary method to match interface to nodes
+  def tags_as_hash
+    return self.tags
+  end
+
+  # Temporary method to match interface to ways
+  def way_nodes
+    return self.old_nodes
+  end
+
+  # Pretend we're not in any relations
+  def containing_relation_members
+    return []
+  end
 end
index bdba25f8f50c148a4a85d669ba96ddfe72dda408..9ee118f6e0c22fb26f4bff0012e30e3cb2c92173 100644 (file)
@@ -234,4 +234,8 @@ class Relation < ActiveRecord::Base
     return false
   end
 
+  # Temporary method to match interface to nodes
+  def tags_as_hash
+    return self.tags
+  end
 end
index a77f35fe9f7e7a4ed0ebbcb9ba229727abfa9f0f..64b11cf672aabebe946e5473a7da601a500d7201 100644 (file)
@@ -257,4 +257,9 @@ class Way < ActiveRecord::Base
     self.delete_with_relations_and_history(user)
 
   end
+
+  # Temporary method to match interface to nodes
+  def tags_as_hash
+    return self.tags
+  end
 end
diff --git a/app/views/browse/_common_details.rhtml b/app/views/browse/_common_details.rhtml
new file mode 100644 (file)
index 0000000..ee5f22c
--- /dev/null
@@ -0,0 +1,22 @@
+<tr>
+  <th>Edited at:</th>
+  <td><%= h(common_details.timestamp) %></td>
+</tr>
+
+<% if common_details.user.data_public %>
+  <tr>
+    <th>Edited by:</th>
+    <td><%= link_to h(common_details.user.display_name), :controller => "user", :action => "view", :display_name => common_details.user.display_name %></td>
+  </tr>
+<% end %>
+
+<% unless common_details.tags_as_hash.empty? %>
+  <tr valign="top">
+    <th>Tags:</th>
+    <td>
+      <table padding="0">
+        <%= render :partial => "tag", :collection => common_details.tags_as_hash %>
+      </table>
+    </td>
+  </tr>      
+<% end %>
diff --git a/app/views/browse/_containing_relation.rhtml b/app/views/browse/_containing_relation.rhtml
new file mode 100644 (file)
index 0000000..b33dea5
--- /dev/null
@@ -0,0 +1,8 @@
+<tr>
+  <td>
+    <%= link_to "Relation " + containing_relation.id.to_s, :action => "relation", :id => containing_relation.id.to_s %>
+    <% unless containing_relation.member_role.blank? %>
+      (as <%= h(containing_relation.member_role) %>)
+    <% end %>
+  </td>
+</tr>
diff --git a/app/views/browse/_map.rhtml b/app/views/browse/_map.rhtml
new file mode 100644 (file)
index 0000000..ad2d2d3
--- /dev/null
@@ -0,0 +1,64 @@
+<%= javascript_include_tag '/openlayers/OpenLayers.js' %>
+<%= javascript_include_tag '/openlayers/OpenStreetMap.js' %>
+<%= javascript_include_tag 'map.js' %>
+<td align="right">
+  <% if map.visible %>
+    <div id="small_map" style="width:250px; height: 300px; border: solid 1px black">
+    </div>
+    <span id="loading">Loading...</span>
+    <a id="larger_map" href=""></a>
+  <% else %>
+    Deleted
+  <% end %>
+</td>
+<script type="text/javascript">
+  function init() {
+    var obj_type = "<%= map.class.name.downcase %>";
+    var obj_id = <%= map.id %>;
+    var url = "/api/<%= "#{API_VERSION}" %>/<%= map.class.name.downcase %>/<%= map.id %>";
+
+    if (obj_type != "node") {
+      url += "/full";
+    }
+
+    var map = createMap("small_map", {
+      controls: [ new OpenLayers.Control.Navigation() ]
+    });
+
+    var osm_layer = new OpenLayers.Layer.GML("OSM", url, {
+      format: OpenLayers.Format.OSM,
+      projection: new OpenLayers.Projection("EPSG:4326")
+    });
+
+    osm_layer.events.register("loadend", osm_layer, function() {
+      $("loading").innerHTML = "";
+
+      if (this.features.length) { 
+        var extent =  this.features[0].geometry.getBounds();
+
+        for (var i = 1; i < this.features.length; i++) {
+          extent.extend(this.features[i].geometry.getBounds());
+        }
+
+        if (extent) {
+          this.map.zoomToExtent(extent);
+        } else {
+          this.map.zoomToMaxExtent();
+       }
+
+       var center = getMapCenter();
+        $("larger_map").href = '/?lat='+center.lat+'&lon='+center.lon+'&zoom='+this.map.getZoom();
+        $("larger_map").innerHTML = "View Larger Map";
+      } else {
+        $("small_map").style.display = "none";
+      }
+    });
+
+    map.addLayer(osm_layer);
+
+    osm_layer.loadGML();
+    osm_layer.loaded = true;
+  }
+
+  window.onload = init;
+</script>
diff --git a/app/views/browse/_navigation.rhtml b/app/views/browse/_navigation.rhtml
new file mode 100644 (file)
index 0000000..57e724d
--- /dev/null
@@ -0,0 +1,13 @@
+<div style="float:right; text-align:center; width: 250px;">
+  <% if @prev %>
+    &lt;
+    <%= link_to @prev.id.to_s, :id => @prev.id %>
+  <% end %>
+  <% if @prev and @next %>
+    | 
+  <% end %>
+  <% if @next %>
+    <%= link_to @next.id.to_s, :id => @next.id %>
+    &gt;
+  <% end %>
+</div>
diff --git a/app/views/browse/_node_details.rhtml b/app/views/browse/_node_details.rhtml
new file mode 100644 (file)
index 0000000..e3d11e0
--- /dev/null
@@ -0,0 +1,19 @@
+<table>
+
+  <%= render :partial => "common_details", :object => node_details %>
+
+  <% unless node_details.ways.empty? and node_details.containing_relation_members.empty? %>
+    <tr valign="top">
+      <th>Part of:</th>
+      <td>
+        <table padding="0">
+          <% node_details.ways.each do |way| %>
+            <tr><td><%= link_to "Way " + way.id.to_s, :action => "way", :id => way.id.to_s %></td></tr>
+          <% end %>
+          <%= render :partial => "containing_relation", :collection => node_details.containing_relation_members %>
+        </table>
+      </td>
+    </tr>      
+  <% end %>
+
+</table>
diff --git a/app/views/browse/_relation_details.rhtml b/app/views/browse/_relation_details.rhtml
new file mode 100644 (file)
index 0000000..b8874d9
--- /dev/null
@@ -0,0 +1,27 @@
+<table>
+
+  <%= render :partial => "common_details", :object => relation_details %>
+
+  <% unless relation_details.relation_members.empty? %>
+    <tr valign="top">
+      <th>Members:</th>
+      <td>
+        <table padding="0">
+          <%= render :partial => "relation_member", :collection => relation_details.relation_members %>
+        </table>
+      </td>
+    </tr>   
+  <% end %>
+
+  <% unless relation_details.containing_relation_members.empty? %>
+    <tr>
+      <th>Part of:</th>
+      <td>
+        <table padding="0">
+          <%= render :partial => "containing_relation", :collection => relation_details.containing_relation_members %>
+        </table>
+      </td>
+    </tr>      
+  <% end %>
+
+</table>
diff --git a/app/views/browse/_relation_member.rhtml b/app/views/browse/_relation_member.rhtml
new file mode 100644 (file)
index 0000000..516d9e3
--- /dev/null
@@ -0,0 +1,10 @@
+<tr>
+  <td>
+    <%= h(relation_member.member_type.capitalize) %>
+    <%= link_to relation_member.member_id.to_s, :action => relation_member.member_type, :id => relation_member.member_id %>
+    <% unless relation_member.member_role.blank? %>
+      as
+      <%= h(relation_member.member_role) %>
+    <% end %>
+  </td>
+</tr> 
diff --git a/app/views/browse/_start.rhtml b/app/views/browse/_start.rhtml
new file mode 100644 (file)
index 0000000..2e120c1
--- /dev/null
@@ -0,0 +1,13 @@
+<div>
+  <div style="text-align: center">
+    <p style="margin-top: 10px; margin-bottom: 20px">
+      <a id="browse_select_view" href="#">View data for current map view</a>
+      <br />
+      <a id="browse_select_box" href="#">Manually select a different area</a>  
+    </p>
+  </div>
+  <div id="browse_status" style="text-align: center; display: none">
+  </div>
+  <div id="browse_content">
+  </div>
+</div>
diff --git a/app/views/browse/_tag.rhtml b/app/views/browse/_tag.rhtml
new file mode 100644 (file)
index 0000000..8a57387
--- /dev/null
@@ -0,0 +1,3 @@
+<tr>
+  <td><%= h(tag[0]) %> = <%= h(tag[1]) %></td>
+</tr> 
diff --git a/app/views/browse/_way_details.rhtml b/app/views/browse/_way_details.rhtml
new file mode 100644 (file)
index 0000000..99b8e1d
--- /dev/null
@@ -0,0 +1,27 @@
+<table>
+
+  <%= render :partial => "common_details", :object => way_details %>
+
+  <tr valign="top">
+    <th>Nodes:</th>
+    <td>
+      <table padding="0">
+        <% way_details.way_nodes.each do |wn| %>
+          <tr><td><%= link_to "Node " + wn.node_id.to_s, :action => "node", :id => wn.node_id.to_s %></td></tr>
+        <% end %>
+      </table>
+    </td>
+  </tr>
+
+  <% unless way_details.containing_relation_members.empty? %>
+    <tr>
+      <th>Part of:</th>
+      <td>
+        <table padding="0">
+          <%= render :partial => "containing_relation", :collection => way_details.containing_relation_members %>
+        </table>
+      </td>
+    </tr>      
+  <% end %>
+
+</table>
diff --git a/app/views/browse/index.rhtml b/app/views/browse/index.rhtml
new file mode 100644 (file)
index 0000000..2cd5cc9
--- /dev/null
@@ -0,0 +1,12 @@
+<h2><%= @nodes.length %> Recently Changed Nodes</h2> 
+<ul>
+<% @nodes.each do |node| 
+   name = node.tags_as_hash['name'].to_s 
+   if name.length == 0:
+     name = "(No name)"
+   end
+   name = name + " - " + node.id.to_s 
+%>
+   <li><%= link_to h(name), :action => "node", :id => node.id %></li>
+<% end %>
+</ul>
diff --git a/app/views/browse/node.rhtml b/app/views/browse/node.rhtml
new file mode 100644 (file)
index 0000000..e2db18f
--- /dev/null
@@ -0,0 +1,20 @@
+<table width="100%">
+  <tr>
+    <td>
+      <h2>Node: <%= h(@name) %></h2>
+    </td>
+    <td>
+      <%= render :partial => "navigation" %>
+    </td>
+  </tr>
+  <tr valign="top">
+    <td>
+      <%= render :partial => "node_details", :object => @node %>
+      <hr />
+      <%= link_to "Download XML", :controller => "node", :action => "read" %>
+      or
+      <%= link_to "view history", :action => "node_history" %>
+    </td>
+    <%= render :partial => "map", :object => @node %>
+  </tr>
+</table>
diff --git a/app/views/browse/node_history.rhtml b/app/views/browse/node_history.rhtml
new file mode 100644 (file)
index 0000000..5fd3278
--- /dev/null
@@ -0,0 +1,16 @@
+<h2>Node History: <%= h(@name) %></h2>
+
+<table width="100%">
+  <tr valign="top">
+    <td>
+      <% @node.old_nodes.reverse.each do |node| %>
+        <%= render :partial => "node_details", :object => node %>
+        <hr />
+      <% end %>
+      <%= link_to "Download XML", :controller => "old_node", :action => "history" %>
+      or
+      <%= link_to "view details", :action => "node" %>
+    </td>
+    <%= render :partial => "map", :object => @node %>
+  </tr>
+</table>
diff --git a/app/views/browse/relation.rhtml b/app/views/browse/relation.rhtml
new file mode 100644 (file)
index 0000000..f981938
--- /dev/null
@@ -0,0 +1,20 @@
+<table width="100%">
+  <tr>
+    <td>
+      <h2>Relation: <%= h(@name) %></h2>
+    </td>
+    <td>
+      <%= render :partial => "navigation" %>
+    </td>
+  </tr>
+  <tr valign="top">
+    <td>
+      <%= render :partial => "relation_details", :object => @relation %>
+      <hr />
+      <%= link_to "Download XML", :controller => "relation", :action => "read" %>
+      or
+      <%= link_to "view history", :action => "relation_history" %>
+    </td>
+    <%= render :partial => "map", :object => @relation %>
+  </tr>
+</table>
diff --git a/app/views/browse/relation_history.rhtml b/app/views/browse/relation_history.rhtml
new file mode 100644 (file)
index 0000000..60e0ffd
--- /dev/null
@@ -0,0 +1,16 @@
+<h2>Relation History: <%= h(@name) %></h2>
+
+<table width="100%">
+  <tr valign="top">
+    <td>
+      <% @relation.old_relations.reverse.each do |relation| %>
+        <%= render :partial => "relation_details", :object => relation %>
+        <hr />
+      <% end %>
+      <%= link_to "Download XML", :controller => "old_relation", :action => "history" %>
+      or
+      <%= link_to "view details", :action => "relation" %>
+    </td>
+    <%= render :partial => "map", :object => @relation %>
+  </tr>
+</table>
diff --git a/app/views/browse/start.rjs b/app/views/browse/start.rjs
new file mode 100644 (file)
index 0000000..04f5e12
--- /dev/null
@@ -0,0 +1,481 @@
+page.replace_html :sidebar_title, 'Data'
+page.replace_html :sidebar_content, :partial => 'start'
+page << <<EOJ
+  var browseBoxControl;
+  var browseActive;
+  var browseMode = "auto";
+  var browseBounds;
+  var browseFeatureList;
+  var browseActiveFeature;
+  var browseDataLayer;
+  var browseSelectControl;
+  var browseObjectList;
+
+  OpenLayers.Feature.Vector.style['default'].strokeWidth = 3;
+  OpenLayers.Feature.Vector.style['default'].cursor = "pointer";
+    
+  function startBrowse() {
+    openSidebar({ onclose: stopBrowse });
+
+    var vectors = new OpenLayers.Layer.Vector();
+    
+    browseBoxControl = new OpenLayers.Control.DrawFeature(vectors, OpenLayers.Handler.RegularPolygon, { 
+      handlerOptions: {
+        sides: 4,
+        snapAngle: 90,
+        irregular: true,
+        persist: true,
+        callbacks: { done: endDrag }
+      }
+    });
+    map.addControl(browseBoxControl);
+
+    map.events.register("moveend", map, showData);
+    map.events.triggerEvent("moveend");
+
+    browseActive = true;
+  }
+
+  function showData() {
+    if (browseMode == "auto") {
+      if (map.getZoom() >= 15) {
+          useMap();
+      } else {
+          setStatus("Zoom in or select an area of the map to view");
+      }    
+    }
+  }
+
+  function stopBrowse() {
+    if (browseActive) {
+      browseActive = false;
+
+      if (browseDataLayer) {
+        browseDataLayer.destroy();
+        browseDataLayer = null; 
+      } 
+
+      if (browseSelectControl) {   
+        browseSelectControl.destroy();  
+        browseSelectControl = null;
+      } 
+
+      if (browseBoxControl) {
+       browseBoxControl.destroy();
+       browseBoxControl = null;
+      }                
+
+      if (browseActiveFeature) {
+        browseActiveFeature.destroy(); 
+        browseActiveFeature = null; 
+      }
+
+      map.dataLayer.setVisibility(false);
+      map.events.unregister("moveend", map, showData);
+    }    
+  }
+    
+  function startDrag() {
+    $("browse_select_box").innerHTML='Drag a box on the map to select an area';
+
+    browseBoxControl.activate();
+
+    return false;
+  };
+
+  $("browse_select_box").onclick = startDrag;
+    
+  function useMap() {
+    var bounds = map.getExtent();
+    var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
+
+    if (!browseBounds || !browseBounds.containsBounds(projected)) {
+      var center = bounds.getCenterLonLat();
+      var tileWidth = bounds.getWidth() * 1.2;
+      var tileHeight = bounds.getHeight() * 1.2;
+      var tileBounds = new OpenLayers.Bounds(center.lon - (tileWidth / 2),
+                                             center.lat - (tileHeight / 2),
+                                             center.lon + (tileWidth / 2),
+                                             center.lat + (tileHeight / 2));
+
+      browseBounds = tileBounds;
+      getData(tileBounds);
+
+      browseMode = "auto";
+
+      $("browse_select_view").style.display = "none";
+    }
+
+    return false;
+  }
+
+  $("browse_select_view").onclick = useMap;
+    
+  function endDrag(bbox) {
+    var bounds = bbox.getBounds();
+    var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
+
+    browseBoxControl.deactivate();
+    browseBounds = projected;
+    getData(bounds);
+
+    browseMode = "manual";  
+
+    $("browse_select_box").innerHTML = "Manually select a different area";
+    $("browse_select_view").style.display = "inline";
+  }
+    
+  function displayFeatureWarning() {
+    clearStatus();
+
+    var div = document.createElement("div");
+
+    var p = document.createElement("p");
+    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."));
+    div.appendChild(p);
+
+    var input = document.createElement("input");
+    input.type = "submit";
+    input.value = "Load Data";
+    input.onclick = loadFeatureList;
+    div.appendChild(input); 
+
+    $("browse_content").innerHTML = "";
+    $("browse_content").appendChild(div);
+  }
+    
+  function loadFeatureList() {
+    browseDataLayer.addFeatures(browseFeatureList);
+    browseDataLayer.events.triggerEvent("loadend");
+
+    browseFeatureList = []; 
+
+    return false;
+  }    
+
+  function customDataLoader(request) { 
+    if (browseActive) {
+      var doc = request.responseXML;
+
+      if (!doc || !doc.documentElement) {
+        doc = request.responseText;
+      }
+
+      var options = {};
+
+      OpenLayers.Util.extend(options, this.formatOptions);
+
+      if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+        options.externalProjection = this.projection;
+        options.internalProjection = this.map.getProjectionObject();
+      }    
+
+      var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
+
+      browseFeatureList = gml.read(doc);
+
+      if (!this.maxFeatures || browseFeatureList.length <= this.maxFeatures) {
+        loadFeatureList();
+      } else {
+        displayFeatureWarning();
+      }
+    }
+  }
+
+  function getData(bounds) {
+    var projected = bounds.clone().transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
+    var size = projected.getWidth() * projected.getHeight();
+
+    if (size > 0.25) {
+      setStatus("Unable to load: Bounding box size of " + size + " is too large (must be smaller than 0.25)");
+    } else {
+      loadGML("/api/0.5/map?bbox=" + projected.toBBOX());
+    }
+  }
+
+  function loadGML(url) {
+    setStatus("Loading...");
+    $("browse_content").innerHTML = "";
+
+    if (!browseDataLayer) {
+      var style = new OpenLayers.Style();
+
+      style.addRules([new OpenLayers.Rule({
+        symbolizer: {
+          Polygon: { fillColor: '#ff0000', strokeColor: '#ff0000' },
+          Line: { fillColor: '#ffff00', strokeColor: '#000000', strokeOpacity: '0.4' },
+          Point: { fillColor: '#00ff00', strokeColor: '#00ff00' }
+        }
+      })]);
+
+      browseDataLayer = new OpenLayers.Layer.GML("Data", url, {
+        format: OpenLayers.Format.OSM,
+        formatOptions: { checkTags: true },
+        maxFeatures: 100,
+        requestSuccess: customDataLoader,
+        displayInLayerSwitcher: false,
+        styleMap: new OpenLayers.StyleMap({
+          default: style,
+          select: { strokeColor: '#0000ff', strokeWidth: 8 }
+        })
+      });
+      browseDataLayer.events.register("loadend", browseDataLayer, dataLoaded );
+      map.addLayer(browseDataLayer);
+            
+      browseSelectControl = new OpenLayers.Control.SelectFeature(browseDataLayer, { onSelect: onFeatureSelect });
+      browseSelectControl.handler.stopDown = false;
+      browseSelectControl.handler.stopUp = false;
+      map.addControl(browseSelectControl);
+      browseSelectControl.activate();
+    } else {
+      browseDataLayer.setUrl(url);
+    }
+
+    browseActiveFeature = null;
+  }
+
+  function dataLoaded() {
+    if (browseActive) {
+      clearStatus();
+        
+      browseObjectList = document.createElement("div")
+
+      var heading = document.createElement("p");
+      heading.className = "browse_heading";
+      heading.appendChild(document.createTextNode("Object list"));
+      browseObjectList.appendChild(heading);
+
+      var list = document.createElement("ul");
+
+      for (var i = 0; i < this.features.length; i++) {
+        var feature = this.features[i]; 
+            
+        // Type, for linking
+        var type = featureType(feature);
+        var typeName = ucFirst(type);
+        var li = document.createElement("li");
+        li.appendChild(document.createTextNode(typeName + " "));
+            
+        // Link, for viewing in the tab
+        var link = document.createElement("a");
+        link.href =  "/browse/" + type + "/" + feature.osm_id; 
+        var name = feature.attributes.name || feature.osm_id;
+        link.appendChild(document.createTextNode(name));
+        link.feature = feature;
+        link.onclick = OpenLayers.Function.bind(viewFeatureLink, link);   
+        li.appendChild(link);
+
+        list.appendChild(li);
+      }
+
+      browseObjectList.appendChild(list)
+
+      var link = document.createElement("a");
+      link.href = this.url;
+      link.appendChild(document.createTextNode("API"));
+      browseObjectList.appendChild(link);
+
+      $("browse_content").innerHTML = "";
+      $("browse_content").appendChild(browseObjectList); 
+    }
+  }
+    
+  function viewFeatureLink() {
+    var layer = this.feature.layer;
+
+    for (var i = 0; i < layer.selectedFeatures.length; i++) {
+      var f = layer.selectedFeatures[i]; 
+      layer.drawFeature(f, layer.styleMap.createSymbolizer(f, "default"));
+    }
+
+    onFeatureSelect(this.feature);
+
+    if (browseMode != "auto") {
+      map.setCenter(this.feature.geometry.getBounds().getCenterLonLat()); 
+    }
+
+    return false;
+  }
+    
+  function loadObjectList() {
+    $("browse_content").innerHTML="";
+    $("browse_content").appendChild(browseObjectList);
+
+    return false;
+  }
+      
+  function onFeatureSelect(feature) {
+    // Unselect previously selected feature
+    if (browseActiveFeature) {
+      browseActiveFeature.layer.drawFeature(
+        browseActiveFeature, 
+        browseActiveFeature.layer.styleMap.createSymbolizer(browseActiveFeature, "default")
+      );
+    }
+
+    // Redraw in selected style
+    feature.layer.drawFeature(
+      feature, feature.layer.styleMap.createSymbolizer(feature, "select")
+    );
+
+    // If the current object is the list, don't innerHTML="", since that could clear it.
+    if ($("browse_content").firstChild == browseObjectList) { 
+      $("browse_content").removeChild(browseObjectList);
+    } else { 
+      $("browse_content").innerHTML = "";
+    }   
+        
+    // Create a link back to the object list
+    var div = document.createElement("div");
+    div.style.textAlign = "center";
+    div.style.marginBottom = "20px";
+    $("browse_content").appendChild(div);
+    var link = document.createElement("a");
+    link.href = "#";
+    link.onclick = loadObjectList;
+    link.appendChild(document.createTextNode("Display object list"));
+    div.appendChild(link);
+
+    var table = document.createElement("table");
+    table.width = "100%";
+    table.className = "browse_heading";
+    $("browse_content").appendChild(table);
+
+    var tr = document.createElement("tr");
+    table.appendChild(tr);
+
+    var heading = document.createElement("td");
+    heading.appendChild(document.createTextNode(featureName(feature)));
+    tr.appendChild(heading);
+
+    var td = document.createElement("td");
+    td.align = "right";
+    tr.appendChild(td);
+
+    var type = featureType(feature);
+    var link = document.createElement("a");   
+    link.href = "/browse/" + type + "/" + feature.osm_id;
+    link.appendChild(document.createTextNode("Details"));
+    td.appendChild(link);
+
+    var div = document.createElement("div");
+    div.className = "browse_details";
+
+    $("browse_content").appendChild(div);
+
+    // Now the list of attributes
+    var ul = document.createElement("ul");
+    for (var key in feature.attributes) {
+      var li = document.createElement("li");
+      var b = document.createElement("b");
+      b.appendChild(document.createTextNode(key));
+      li.appendChild(b);
+      li.appendChild(document.createTextNode(": " + feature.attributes[key]));
+      ul.appendChild(li);
+    }
+        
+    div.appendChild(ul);
+        
+    var link = document.createElement("a");   
+    link.href =  "/browse/" + type + "/" + feature.osm_id + "/history";
+    link.appendChild(document.createTextNode("Show history"));
+    link.onclick = OpenLayers.Function.bind(loadHistory, {
+      type: type, feature: feature, link: link
+    });
+        
+    div.appendChild(link);
+
+    // Stash the currently drawn feature
+    browseActiveFeature = feature; 
+  }   
+
+  function loadHistory() {
+    this.link.href = "";
+    this.link.innerHTML = "Wait...";
+
+    new Ajax.Request("/api/0.5/" + this.type + "/" + this.feature.osm_id + "/history", {
+      onComplete: OpenLayers.Function.bind(displayHistory, this)
+    });
+
+    return false;
+  }
+
+  function displayHistory(request) {
+    if (browseActiveFeature.osm_id != this.feature.osm_id || $("browse_content").firstChild == browseObjectList)  { 
+        return false;
+    } 
+
+    this.link.parentNode.removeChild(this.link);
+
+    var doc = request.responseXML;
+
+    var table = document.createElement("table");
+    table.width = "100%";
+    table.className = "browse_heading";
+    $("browse_content").appendChild(table);
+
+    var tr = document.createElement("tr");
+    table.appendChild(tr);
+
+    var heading = document.createElement("td");
+    heading.appendChild(document.createTextNode("History for " + featureName(this.feature)));
+    tr.appendChild(heading);
+
+    var td = document.createElement("td");
+    td.align = "right";
+    tr.appendChild(td);
+
+    var link = document.createElement("a");   
+    link.href = "/browse/" + this.type + "/" + this.feature.osm_id + "/history";
+    link.appendChild(document.createTextNode("Details"));
+    td.appendChild(link);
+
+    var div = document.createElement("div");
+    div.className = "browse_details";
+
+    var nodes = doc.getElementsByTagName(this.type);
+    var history = document.createElement("ul");  
+    for (var i = nodes.length - 1; i >= 0; i--) {
+      var user = nodes[i].getAttribute("user") || "private user";
+      var timestamp = nodes[i].getAttribute("timestamp");
+      var item = document.createElement("li");
+      item.appendChild(document.createTextNode("Edited by " + user + " at " + timestamp));
+      history.appendChild(item);
+    }
+    div.appendChild(history);
+
+    $("browse_content").appendChild(div); 
+  }
+
+  function featureType(feature) {
+    if (feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+      return "node";
+    } else {
+      return "way";
+    }
+  }
+  
+  function featureName(feature) {
+    if (feature.attributes.name) {
+      return feature.attributes.name;
+    } else {
+      return ucFirst(featureType(feature)) + " " + feature.osm_id;
+    }
+  }
+
+  function setStatus(status) {
+    $("browse_status").innerHTML = status;
+    $("browse_status").style.display = "block";
+  }
+  
+  function clearStatus() {
+    $("browse_status").innerHTML = "";
+    $("browse_status").style.display = "none";
+  }
+
+  function ucFirst(str) {
+    return str.substr(0,1).toUpperCase() + str.substr(1,str.length);
+  }
+
+  startBrowse();
+EOJ
diff --git a/app/views/browse/way.rhtml b/app/views/browse/way.rhtml
new file mode 100644 (file)
index 0000000..2e86e65
--- /dev/null
@@ -0,0 +1,20 @@
+<table width="100%">
+  <tr>
+    <td>
+      <h2>Way: <%= h(@name) %></h2>
+    </td>
+    <td>
+      <%= render :partial => "navigation" %>
+    </td>
+  </tr>
+  <tr valign="top">
+    <td>
+      <%= render :partial => "way_details", :object => @way %>
+      <hr />
+      <%= link_to "Download XML", :controller => "way", :action => "read" %>
+      or
+      <%= link_to "view history", :action => "way_history" %>
+    </td>
+    <%= render :partial => "map", :object => @way %>
+  </tr>
+</table>
diff --git a/app/views/browse/way_history.rhtml b/app/views/browse/way_history.rhtml
new file mode 100644 (file)
index 0000000..f44405f
--- /dev/null
@@ -0,0 +1,16 @@
+<h2>Way History: <%= h(@name) %></h2>
+
+<table width="100%">
+  <tr valign="top">
+    <td>
+      <% @way.old_ways.reverse.each do |way| %>
+        <%= render :partial => "way_details", :object => way %>
+        <hr />
+      <% end %>
+      <%= link_to "Download XML", :controller => "old_way", :action => "history" %>
+      or
+      <%= link_to "view details", :action => "way" %>
+    </td>
+    <%= render :partial => "map", :object => @way %>
+  </tr>
+</table>
index a79ea10c39f867705bac09a1365f2dec46c7d259..2cca4d5292317b8a3728e94ed33e3ec6e057312a 100644 (file)
@@ -94,6 +94,12 @@ by the OpenStreetMap project and its contributors.
   function mapInit(){
     map = createMap("map");
 
+    <% unless OSM_STATUS == :api_offline or OSM_STATUS == :database_offline %>
+    map.dataLayer = new OpenLayers.Layer("Data", { "visibility": false });
+    map.dataLayer.events.register("visibilitychanged", map.dataLayer, toggleData);
+    map.addLayer(map.dataLayer);
+    <% end %>
+
     <% if bbox %>
     var bbox = new OpenLayers.Bounds(<%= minlon %>, <%= minlat %>, <%= maxlon %>, <%= maxlat %>);
 
@@ -126,6 +132,14 @@ by the OpenStreetMap project and its contributors.
     handleResize();
   }
 
+  function toggleData() {
+    if (map.dataLayer.visibility) {
+      <%= remote_function :url => { :controller => 'browse', :action => 'start' } %>
+    } else {
+      closeSidebar();
+    }
+  }
+
   function getPosition() {
     return getMapCenter();
   }
index 290ad6e08d8623229de33769a5723a680c535945..854e7f00364370c8fd4e41c743324761f27103ef 100644 (file)
@@ -57,6 +57,16 @@ ActionController::Routing::Routes.draw do |map|
   map.connect "api/#{API_VERSION}/amf", :controller =>'amf', :action =>'talk'
   map.connect "api/#{API_VERSION}/swf/trackpoints", :controller =>'swf', :action =>'trackpoints'
   
+  # Data browsing
+  map.connect '/browse', :controller => 'browse', :action => 'index'
+  map.connect '/browse/start', :controller => 'browse', :action => 'start'
+  map.connect '/browse/way/:id', :controller => 'browse', :action => 'way', :id => /\d+/
+  map.connect '/browse/way/:id/history', :controller => 'browse', :action => 'way_history', :id => /\d+/
+  map.connect '/browse/node/:id', :controller => 'browse', :action => 'node', :id => /\d+/
+  map.connect '/browse/node/:id/history', :controller => 'browse', :action => 'node_history', :id => /\d+/
+  map.connect '/browse/relation/:id', :controller => 'browse', :action => 'relation', :id => /\d+/
+  map.connect '/browse/relation/:id/history', :controller => 'browse', :action => 'relation_history', :id => /\d+/
+  
   # web site
 
   map.connect '/', :controller => 'site', :action => 'index'
index 9f9be0987d4fa3d0071f0d5f8c55a465db16b452..291fd18fa29ff645520a1038b0dfa92e338d9bc6 100644 (file)
@@ -7,9 +7,11 @@ OpenLayers._getScriptLocation = function () {
    return "/openlayers/";
 }
 
-function createMap(divName) {
+function createMap(divName, options) {
+   options = options || {};
+
    map = new OpenLayers.Map(divName, {
-      controls: [
+      controls: options.controls || [
          new OpenLayers.Control.ArgParser(),
          new OpenLayers.Control.Attribution(),
          new OpenLayers.Control.LayerSwitcher(),
@@ -18,7 +20,7 @@ function createMap(divName) {
          new OpenLayers.Control.ScaleLine()
       ],
       units: "m",
-      maxResolution: 156543,
+      maxResolution: 156543.0339,
       numZoomLevels: 20
    });
 
index c783dba20f2b940a48d2edbfbfbefd827a45a642..69e8840b2a33e5152da97bbc1a0d824cc225f416 100644 (file)
@@ -48,8 +48,8 @@ OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.TMS, {
     initialize: function(name, url, options) {
         options = OpenLayers.Util.extend({
             attribution: "Data by <a href='http://openstreetmap.org/'>OpenStreetMap</a>",
-            maxExtent: new OpenLayers.Bounds(-20037508,-20037508,20037508,20037508),
-            maxResolution: 156543,
+            maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
+            maxResolution: 156543.0339,
             units: "m",
             projection: "EPSG:900913",
             transitionEffect: "resize"
index df04b7c99d7721a2d9856ff54ec75047df66ea4d..c0cef39cb5558bfd2324afa24f3d0f471c2b0765 100644 (file)
@@ -454,6 +454,18 @@ hides rule from IE5-Mac \*/
   background: #bbb;
 }
 
+.browse_heading {
+  margin: 0px;
+  padding: 3px 6px 3px 6px;
+  border: 1px solid #ccc;
+  background: #ddd;
+}
+
+.browse_details {
+  margin: 0px;
+  padding: 0px 6px 0px 6px;
+}
+
 .search_results_heading {
   margin: 0px;
   padding: 3px 6px 3px 6px;