WikiProject Brazil/Brasil 250 Cidades/Scripts

From OpenStreetMap Wiki
Jump to: navigation, search

Os scripts foram gentilmente cedidos por Matt Amos.

Arquivo exemplo usa_extract.yml, com a lista das cidades.

--- 
Los Angeles, California:
- 34.05
- -118.25
Tempe, Arizona: 
- 33.41463
- -111.90938
Kansas City, Kansas: 
- 39.09947
- -94.58133
Costa Mesa, California: 
- 33.66978
- -117.90432
Peoria, Illinois: 
- 40.69365
- -89.58899

Arquivo routes.rb, que gera as distâncias entre as cidades. Um arquivo .yml, como o de acima, deverá ser passado como parâmetro na linha de comando.

require 'rubygems'
require 'cloudmade'
require 'yaml'
include CloudMade

API_KEY='' # PUT YOUR API KEY HERE
CITIES = YAML::load_file(ARGV[0])
CM = Client.from_parameters(API_KEY)

def route_or_nil(from, to)
  backoff = 60
  loop do
    begin
      # To calculate the distance using the 'fastest' route:
      # return CM.routing.route(Point.new(CITIES[from]), Point.new(CITIES[to]))
      # To calculate the distance using the 'shortest' route:
      return CM.routing.route(Point.new(CITIES[from]), Point.new(CITIES[to]), nil, "car", "en", "shortest")
    rescue Timeout::Error
      STDERR.puts "[#{Time.now}] Timeout, retrying..."
      sleep(backoff)
      backoff = [60 * 30, backoff * 2].min
    rescue HTTPError => e
      STDERR.puts "[#{Time.now}] HTTP error: #{e}, retrying..."
      sleep(backoff)
      backoff = [60 * 30, backoff * 2].min      
    rescue
      STDERR.puts "[#{Time.now}] Other error: #{e}, retrying..."
      sleep(backoff)
      backoff = [60 * 30, backoff * 2].min      
    end
  end
rescue RouteNotFound
  nil
end

num_cities = CITIES.keys.length
CITIES.keys.sort.each do |i|
  CITIES.keys.sort.each do |j|
    r = route_or_nil(i, j)
    if r.nil?
      puts "#{i};#{j};NO ROUTE"
    else
      puts "#{i};#{j};#{r.summary.total_distance}"
    end
  end
end

Arquivo to_html.rb, que gera, a partir do resultado do routes.rb, uma página html com as distâncias e links.

require 'rubygems'
require 'yaml'

CITIES = YAML::load_file(ARGV[0])

# average radius of the earth
RADIUS = 6371010.0

# ratio of route to great circle distance to be considered "bad". this will
# vary regionally (i.e: much higher in the mountains), so should be found by
# experiment.
FACTOR = 1.5

# calculate the great circle distance
# from wikipedia: 
# arctan(sqrt((cos(phi_f) * sin(Delta_lambda))^2 + 
#             (cos(phi_s) * sin(phi_f) - sin(phi_s) * cos(phi_f) * cos(Delta_lambda)^2)) / 
#        (sin(phi_s) * sin(phi_f) + cos(phi_s) * cos(phi_f) * cos(Delta_lambda)))
def great_circle(a, b)
  lambda = Math::PI * (a[1] - b[1]) / 180.0
  phi_s = Math::PI * a[0] / 180.0
  phi_f = Math::PI * b[0] / 180.0

  sin_phi_s = Math::sin(phi_s)
  sin_phi_f = Math::sin(phi_f)
  sin_lambda = Math::sin(lambda)
  cos_phi_s = Math::cos(phi_s)
  cos_phi_f = Math::cos(phi_f)
  cos_lambda = Math::cos(lambda)

  sigma = Math::atan2(Math::sqrt((cos_phi_f * sin_lambda)**2 + (cos_phi_s * sin_phi_f - sin_phi_s * cos_phi_f * cos_lambda)**2),
                      sin_phi_s * sin_phi_f + cos_phi_s * cos_phi_f * cos_lambda)

  return RADIUS * sigma
end

# define this as the success test - whether something worked or
# not. return a :symbol, which will be used as the CSS class for
# the table cell.
def css_class(from, to, dist)
  if dist.nil?
    return :fail
  else
    if dist > FACTOR * great_circle(CITIES[from], CITIES[to])
      return :bad
    elsif dist > 0.0
      return :success
    else
      return :zero
    end
  end
end

city_dist = {}
File.readlines(ARGV[1]).each do |line|
  from, to, dist = line.split(/;/)
  if dist == "S/ ROTA"
    dist = nil
  else
    dist = dist.to_f
  end
  if city_dist[from].nil?
    city_dist[from] = { to => dist }
  else
    city_dist[from][to] = dist
  end
end

city_names = CITIES.keys.sort
puts <<END
<html><head><title>Distancias</title>
<style type="text/css">
a.success { color: green; }
a.fail { color: red; }
a.zero { color: #888; }
a.bad { color: #fa0; }
</style>
</head><body>
END

puts "<table><tr><th></th>"
city_names.each do |to| 
  to_pos = CITIES[to]
  # url to 'fastest' routing
  # url = "http://maps.cloudmade.com/?lat=#{to_pos[0]}&lng=#{to_pos[1]}&zoom=6"
  # url to 'shortest' routing
  url = "http://maps.cloudmade.com/?lat=#{to_pos[0]}&lng=#{to_pos[1]}&zoom=6&travel=car/shortest"
  puts "<th><a href=\"#{url}\">#{to}</a></th>" 
end
puts "</tr>"
counters = {}
city_names.each do |from|
  from_pos = CITIES[from]
  # url to 'fastest' routing
  # url = "http://maps.cloudmade.com/?lat=#{from_pos[0]}&lng=#{from_pos[1]}&zoom=6"
  # url to 'shortest' routing
  url = "http://maps.cloudmade.com/?lat=#{from_pos[0]}&lng=#{from_pos[1]}&zoom=6&travel=car/shortest"
  puts "<tr><td><a href=\"#{url}\">#{from}</a></td>" 
  city_names.each do |to|
    to_pos = CITIES[to]
    dist = city_dist[from][to]
    cc = css_class(from, to, dist)
    counters[cc] = (counters[cc] || 0) + 1
    # url to 'fastest' routing
    # url = "http://maps.cloudmade.com/?directions=#{[from_pos,to_pos].flatten.join(',')}&lat=#{0.5*(from_pos[0]+to_pos[0])}&lng=#{0.5*(from_pos[1]+to_pos[1])}&zoom=6"
    # url to 'shortest' routing
    url = "http://maps.cloudmade.com/?directions=#{[from_pos,to_pos].flatten.join(',')}&lat=#{0.5*(from_pos[0]+to_pos[0])}&lng=#{0.5*(from_pos[1]+to_pos[1])}&zoom=6&travel=car/shortest"
    if from == to
      puts "<td>X</td>"
    elsif dist.nil? or dist == 0
      puts "<td><a href='#{url}' class='#{cc}'>Falhou</a></td>"
    else
      title_pretty_dist = dist.round.to_s.gsub(/(\d)(\d{3})$/, '\1.\2')
      pretty_dist = title_pretty_dist.to_i.to_s
      puts "<td><a href='#{url}' title='(#{from}) #{title_pretty_dist} km (#{to})' class='#{cc}'>#{pretty_dist} km</a></td>"
    end
  end
  puts "</tr>"
end
puts "</table>"
total = city_names.length ** 2
counters.each do |cc, n|
  puts "<!--p>Class #{cc}: #{n} of #{total} (#{(100.0*n)/total}%)</p-->"
end
puts "</body></html>"

Arquivo gerargrid.bat, para rodar os scripts no windows:

@echo off
echo Buscando distancias...
ruby routes.rb cidades.yml >cidades-distancias.csv 
echo Gerando html...
ruby to_html.rb cidades.yml cidades-distancias.csv > cidades-distancias.html
echo Script finalizado.