Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added live Google Earth KML view

  • Loading branch information...
commit 2a65c3704d524edc55d0b43ea93fe4c93e08611b 1 parent 3a6ee96
@adrianshort authored
Showing with 169 additions and 2 deletions.
  1. +2 −0  Gemfile
  2. +145 −0 app.rb
  3. +10 −1 models.rb
  4. +12 −1 views/index.haml
View
2  Gemfile
@@ -6,7 +6,9 @@ gem 'dm-core'
gem 'dm-validations'
gem 'dm-timestamps'
gem 'dm-aggregates'
+gem 'dm-sqlite-adapter'
gem 'dm-postgres-adapter'
gem 'dm-migrations'
gem 'dm-serializer'
gem 'pg'
+gem 'builder'
View
145 app.rb
@@ -2,6 +2,11 @@
require 'sinatra'
require 'haml'
require 'models'
+require 'builder'
+
+def hostname
+ (request.env['HTTP_X_FORWARDED_SERVER'] =~ /[a-z]*/) ? request.env['HTTP_X_FORWARDED_SERVER'] : request.env['HTTP_HOST']
+end
get '/' do
Station.scrape
@@ -41,3 +46,143 @@
"Content-Type" => "application/json"
@stations.to_json
end
+
+get '/london-cycle-hire.kml' do
+ headers "Content-Disposition" => "inline",
+ "Content-Type" => "application/vnd.google-earth.kml+xml"
+
+ xml = Builder::XmlMarkup.new( :indent => 2 )
+ xml.instruct!
+ xml.kml :xmlns => "http://earth.google.com/kml/2.0" do
+ xml.Document do
+ xml.name "London Cycle Hire Bike Availability"
+ xml.NetworkLink do
+ xml.description "London Cycle Hire Bike Availability"
+ xml.Link do
+ xml.href "http://" + hostname + "/stations.kml"
+ xml.refreshMode "onInterval"
+ xml.refreshInterval "60"
+ end
+ end
+ end
+ end
+end
+
+get '/stations.kml' do
+ Station.scrape
+ @stations = Station.all( :order => [ :nb_bikes.desc ] )
+ headers "Content-Disposition" => "inline",
+ "Content-Type" => "application/vnd.google-earth.kml+xml"
+
+ # Define the footprint of the buildings
+ # Adjust these to taste. Bear in mind they're angles not distances so they'll
+ # appear differently depending where they're plotted on the surface of the Earth
+ LAT_OFFSET = 0.0004
+ LNG_OFFSET = 0.0010
+ HEIGHT_MULTIPLIER = 30
+
+ xml = Builder::XmlMarkup.new( :indent => 2 )
+
+ # Output the XML prologue
+ xml.instruct!
+
+ # Like all XML docs, our KML file has a single root element.
+ # In KML it's called <kml>
+ xml.kml :xmlns => "http://earth.google.com/kml/2.0" do
+ xml.Document do
+
+ xml.name "London Cycle Hire Bike Availability"
+
+ # Set a default position of the user's view to something more interesting
+ # than directly above the centre of London looking downwards
+ xml.Camera do
+ xml.longitude -0.2177463169040695
+ xml.latitude 51.6037421685679
+ xml.altitude 9650
+ xml.heading 154
+ xml.tilt 46
+ xml.roll "-9.541664044390549e-15"
+ xml.altitudeMode "relativeToGround"
+ end
+
+ # Create a common marker style with no icon href so markers don't appear
+ # This is done so we can label the tops of the buildings (Polygons)
+ # as Polygons can't have labels
+ xml.Style( :id => "marker" ) do
+ xml.IconStyle do
+ xml.Icon do
+ xml.href ""
+ end
+ end
+ xml.PolyStyle do
+ xml.color "ffff0000"
+ end
+ end
+
+
+ for station in @stations
+
+ # Remove the header row & Penton Workshop which isn't a real station & has lat 0, lng 0
+ if station.lat.to_f > 0.0
+
+ # Set the height of the buildings
+ @alt = station.nb_bikes.to_i * HEIGHT_MULTIPLIER
+
+ xml.Placemark do
+
+ # Give all placemarks a common style as specified earlier in the Style block
+ xml.styleUrl "#marker"
+
+ # This is the text label that appears on the buildings
+ xml.name "#{station.name}: #{station.nb_bikes} bikes"
+
+ # MultiGeometry groups the Point & Polygon together
+ xml.MultiGeometry do
+
+ # Create a Point so we get a text label on top of each building
+ xml.Point do
+
+ # KML coords are always longitude, latitude, altitude (in metres)
+ xml.coordinates "#{station.long}, #{station.lat}, #{@alt.to_s}"
+
+ # Relative to ground, not sea level
+ xml.altitudeMode "relativeToGround"
+
+ # Don't draw a line between this elevated marker & the ground
+ xml.extrude "0"
+ end
+
+ # The Polygons are the buildings, rectangles extruded down to the ground
+ xml.Polygon do
+ xml.outerBoundaryIs do
+ xml.LinearRing do
+
+ # Polygons have one set of coords (lng, lat, alt) for each vertex
+ # The last set is the same as the first, closing the loop
+ # We start in the top-left corner of the rectangle at the actual lat, lng
+ # and work clockwise making the rectangle LAT_OFFSET x LNG_OFFSET
+ # except these offsets are angles, not lengths
+ xml.coordinates [
+ "\n\t\t#{station.long}, #{station.lat}, #{@alt.to_s}\n",
+ "\t\t#{station.long}, #{station.lat.to_f + LAT_OFFSET}, #{@alt.to_s}\n",
+ "\t\t#{station.long.to_f + LNG_OFFSET}, #{station.lat.to_f + LAT_OFFSET}, #{@alt.to_s}\n",
+ "\t\t#{station.long.to_f + LNG_OFFSET}, #{station.lat}, #{@alt.to_s}\n",
+ "\t\t#{station.long}, #{station.lat}, #{@alt.to_s}\n"
+ ]
+ end
+ end
+ xml.altitudeMode "relativeToGround"
+
+ # Extrude the Polygon down to the ground
+ xml.extrude "1"
+ end
+
+ end
+ end
+ end
+ end
+
+ end
+ end
+
+end
View
11 models.rb
@@ -5,6 +5,7 @@
require 'dm-aggregates'
require 'dm-migrations'
require 'dm-serializer'
+require 'time'
require 'pp'
require 'open-uri'
@@ -26,11 +27,12 @@ class Station
def self.scrape
set = Setting.first
- unless set.stations_last_updated.nil? || set.stations_last_updated.to_time < Time.now - (1 * 60)
+ unless true || set.stations_last_updated.nil? || set.stations_last_updated < DateTime.now - (1 * 60)
puts "Data updated less than five minutes ago"
return
end
+
contents = open("https://web.barclayscyclehire.tfl.gov.uk/maps").read
# Fetch from local file
@@ -114,6 +116,13 @@ class Setting
property :stations_last_updated, DateTime
end
+# class App
+# include DataMapper::Resource
+#
+# property :id, Serial
+# property :name, String, :required => true, :length => 255
+# property :url, String, :required => true, :length => 255
+# end
DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/db.sqlite3")
DataMapper.auto_upgrade!
View
13 views/index.haml
@@ -14,6 +14,14 @@
%a{:href => "http://twitter.com/AdrianShort"}<
@AdrianShort on Twitter
for more fun with #opendata.
+
+%p Apps and websites using this data or code:
+
+%ul
+ - for app in @apps
+ %li
+ %a{ :href => "#{app.url}" }
+ = app.name
%p.download
%a{ :href => "stations.csv"}
@@ -21,7 +29,10 @@
&nbsp;
%a{ :href => "stations.json"}
Download as JSON
-
+ &nbsp;
+ %a{ :href => "london-cycle-hire.kml"}
+ View live in Google Earth
+
%table
%tr
%th ID
Please sign in to comment.
Something went wrong with that request. Please try again.