Setting an address column and geolocating it

1.0
Fernando Blat 14 years ago
parent 26e019fb30
commit d0acc4c348

@ -324,11 +324,15 @@ class Api::Json::TablesController < ApplicationController
# * Request Method: +PUT+
# * URI: +/api/json/table/:id/set_geometry_columns
# * Format: +JSON+
# * Parameters:
# * Parameters for setting lat and lon columns:
# {
# "lat_column" => "<lat_column_name>",
# "lon_column" => "<lon_column_name>"
# }
# * Parameters for setting an address column:
# {
# "address_column" => "<address_column_name>"
# }
# * Response if _success_:
# * status code: 200
# * Response if _error_:
@ -336,8 +340,16 @@ class Api::Json::TablesController < ApplicationController
# * body:
# { "errors" => ["error message"] }
def set_geometry_columns
@table.set_lan_lon_columns!(params[:lat_column].to_sym, params[:lon_column].to_sym)
if params[:lat_column] && params[:lon_column]
@table.set_lan_lon_columns!(params[:lat_column].try(:to_sym), params[:lon_column].try(:to_sym))
render :json => ''.to_json, :status => 200, :callback => params[:callback]
elsif params[:address_column]
@table.set_address_column!(params[:address_column].try(:to_sym))
render :json => ''.to_json, :status => 200, :callback => params[:callback]
else
render :json => { :errors => ["Invalid parameters"] }.to_json,
:status => 400, :callback => params[:callback] and return
end
rescue => e
render :json => { :errors => [translate_error(e.message.split("\n").first)] }.to_json,
:status => 400, :callback => params[:callback] and return

@ -177,43 +177,51 @@ class Table < Sequel::Model(:user_tables)
def insert_row!(attributes)
owner.in_database do |user_database|
attributes = attributes.dup.select{ |k,v| user_database[name.to_sym].columns.include?(k.to_sym) }
user_database[name.to_sym].insert(attributes) unless attributes.empty?
unless attributes.empty?
user_database[name.to_sym].insert(attributes)
unless address_column.blank?
geocode_address_column!(attributes[address_column])
end
end
end
end
def update_row!(row_id, attributes)
owner.in_database do |user_database|
attributes = attributes.dup.select{ |k,v| user_database[name.to_sym].columns.include?(k.to_sym) }
user_database[name.to_sym].filter(:cartodb_id => row_id).update(attributes) unless attributes.empty?
unless attributes.empty?
user_database[name.to_sym].filter(:cartodb_id => row_id).update(attributes)
if !address_column.blank? && attributes.keys.include?(address_column)
geocode_address_column!(attributes[address_column])
end
end
end
return true
end
def schema(options = {})
options[:cartodb_types] ||= false
temporal_schema = owner.in_database do |user_database|
user_database.schema(name.to_sym).map{ |c| [c.first, c[1][:db_type]] }
end
schema = temporal_schema.delete([:cartodb_id, "integer"])
schema = [schema] + temporal_schema
created_at = schema.delete([:created_at, "timestamp without time zone"])
updated_at = schema.delete([:updated_at, "timestamp without time zone"])
schema.delete([:the_geom, "geometry"])
if lat_column && lon_column
user_database.schema(name.to_sym).map do |column|
[
column.first,
(options[:cartodb_types] == true ? column[1][:db_type].convert_to_cartodb_type : column[1][:db_type])
] unless CARTODB_COLUMNS.include?(column.first.to_s)
end.compact
end
schema = [[:cartodb_id, (options[:cartodb_types] == true ? "integer".convert_to_cartodb_type : "integer")]] +
temporal_schema +
[[:created_at, (options[:cartodb_types] == true ? "timestamp".convert_to_cartodb_type : "timestamp")]] +
[[:updated_at, (options[:cartodb_types] == true ? "timestamp".convert_to_cartodb_type : "timestamp")]]
unless geometry_columns.blank?
schema.each do |col|
col << "latitude" if col[0].to_sym == lat_column
col << "longitude" if col[0].to_sym == lon_column
col << "address" if col[0].to_sym == address_column
end
end
schema = schema.push([:created_at, "timestamp"]).push([:updated_at, "timestamp"])
if options[:cartodb_types] == true
schema.map do |col|
col[1] = col[1].convert_to_cartodb_type
col
end
else
return schema
end
end
def add_column!(options)
type = options[:type].convert_to_db_type
@ -325,7 +333,7 @@ TRIGGER
end
def lat_column
unless geometry_columns.blank?
if !geometry_columns.blank? && geometry_columns.include?('|')
geometry_columns.split('|')[0].to_sym
else
nil
@ -333,12 +341,26 @@ TRIGGER
end
def lon_column
unless geometry_columns.blank?
if !geometry_columns.blank? && geometry_columns.include?('|')
geometry_columns.split('|')[1].to_sym
else
nil
end
end
def set_address_column!(address_column)
self.geometry_columns = address_column.try(:to_s)
save_changes
end
def address_column
unless geometry_columns.blank? || geometry_columns.include?('|')
geometry_columns.try(:to_sym)
else
nil
end
end
private
def update_updated_at
@ -527,4 +549,17 @@ TRIGGER
end
end
def geocode_address_column!(address)
return if address_column.blank?
url = URI.parse("http://maps.google.com/maps/api/geocode/json?address=#{CGI.escape(address)}&sensor=false")
req = Net::HTTP::Get.new(url.request_uri)
res = Net::HTTP.start(url.host, url.port){ |http| http.request(req) }
json = JSON.parse(res.body)
if json['status'] == 'OK' && !json['results'][0]['geometry']['location']['lng'].blank? && !json['results'][0]['geometry']['location']['lat'].blank?
owner.in_database do |user_database|
user_database.run("UPDATE #{self.name} SET the_geom = PointFromText('POINT(' || #{json['results'][0]['geometry']['location']['lng']} || ' ' || #{json['results'][0]['geometry']['location']['lat']} || ')',4236)")
end
end
end
end

@ -596,7 +596,18 @@ feature "Tables JSON API" do
FileUtils.rm("#{Rails.root}/public/test_jsonp.html")
end
scenario "Set the geometry from a table" do
scenario "Get the available types for columns" do
user = create_user
authenticate_api user
get_json "/api/json/column_types"
response.status.should == 200
json_response = JSON(response.body)
json_response.should == %W{ String Number Date }
end
scenario "Set the geometry from a table to latitude and longitude" do
user = create_user
table = new_table
table.user_id = user.id
@ -610,17 +621,28 @@ feature "Tables JSON API" do
table.reload
table.lat_column.should == :latitude
table.lon_column.should == :longitude
put_json "/api/json/tables/#{table.id}/set_geometry_columns", {:lat_column => nil, :lon_column => nil}
response.status.should == 200
table.reload
table.lat_column.should be_nil
table.lon_column.should be_nil
end
scenario "Get the available types for columns" do
scenario "Set the geometry from a table to an address column" do
user = create_user
table = new_table
table.user_id = user.id
table.save
authenticate_api user
get_json "/api/json/column_types"
put_json "/api/json/tables/#{table.id}/set_geometry_columns", {:address_column => :address}
response.status.should == 200
json_response = JSON(response.body)
json_response.should == %W{ String Number Date }
table.reload
table.address_column.should == :address
end
end

@ -475,7 +475,7 @@ describe Table do
row[:idparada] == 34
end
it "should import data from an external url returning JSON data and set the geometric fields" do
it "should import data from an external url returning JSON data and set the geometric fields which are updated on each row updated" do
user = create_user
json = JSON.parse(File.read("#{Rails.root}/spec/support/points_json.json"))
JSON.stubs(:parse).returns(json)
@ -493,12 +493,6 @@ describe Table do
row[:name].should == "Hawai"
table.set_lan_lon_columns!(:lat, :lon)
table.reload
table.lat_column.should == :lat
table.lon_column.should == :lon
table.schema.should == [[:cartodb_id, "integer"], [:id, "integer"], [:name, "character varying"], [:lat, "double precision", "latitude"],
[:lon, "double precision", "longitude"], [:created_at, "timestamp"], [:updated_at, "timestamp"]
]
# Vizzuality HQ
current_lat = "40.422546"
@ -515,6 +509,17 @@ describe Table do
query_result[:rows][1][:name].should == "Hawai"
end
it "should add an extra column in the schema for latitude and longitude columns" do
table = create_table
table.lat_column.should == :latitude
table.lon_column.should == :longitude
table.schema.should == [
[:cartodb_id, "integer"], [:name, "text"], [:latitude, "double precision", "latitude"],
[:longitude, "double precision", "longitude"], [:description, "text"],
[:created_at, "timestamp"], [:updated_at, "timestamp"]
]
end
it "should set latitude and longitude if the default schema is loaded" do
table = create_table
table.lat_column.should == :latitude
@ -534,4 +539,87 @@ describe Table do
table.lat_column.should be_nil
end
it "should be able to set an address column to a given column" do
res_mock = mock()
res_mock.stubs(:body).returns("")
Net::HTTP.stubs(:start).returns(res_mock)
raw_json = {"status"=>"OK", "results"=>[{"types"=>["street_address"], "formatted_address"=>"Calle de Manuel Fernández y González, 8, 28014 Madrid, Spain", "address_components"=>[{"long_name"=>"8", "short_name"=>"8", "types"=>["street_number"]}, {"long_name"=>"Calle de Manuel Fernández y González", "short_name"=>"Calle de Manuel Fernández y González", "types"=>["route"]}, {"long_name"=>"Madrid", "short_name"=>"Madrid", "types"=>["locality", "political"]}, {"long_name"=>"Community of Madrid", "short_name"=>"M", "types"=>["administrative_area_level_2", "political"]}, {"long_name"=>"Madrid", "short_name"=>"Madrid", "types"=>["administrative_area_level_1", "political"]}, {"long_name"=>"Spain", "short_name"=>"ES", "types"=>["country", "political"]}, {"long_name"=>"28014", "short_name"=>"28014", "types"=>["postal_code"]}], "geometry"=>{"location"=>{"lat"=>40.4151476, "lng"=>-3.6994168}, "location_type"=>"RANGE_INTERPOLATED", "viewport"=>{"southwest"=>{"lat"=>40.4120053, "lng"=>-3.7025647}, "northeast"=>{"lat"=>40.4183006, "lng"=>-3.6962695}}, "bounds"=>{"southwest"=>{"lat"=>40.4151476, "lng"=>-3.6994174}, "northeast"=>{"lat"=>40.4151583, "lng"=>-3.6994168}}}}]}
JSON.stubs(:parse).returns(raw_json)
user = create_user
table = new_table
table.user_id = user.id
table.force_schema = "name varchar, address varchar"
table.save
table.set_address_column!(:address)
table.reload
table.lat_column.should be_nil
table.lon_column.should be_nil
table.address_column.should == :address
table.insert_row!({:name => 'El Lacón', :address => 'Calle de Manuel Fernández y González 8, Madrid'})
query_result = user.run_query("select ST_X(the_geom) as lon, ST_Y(the_geom) as lat from #{table.name} limit 1")
query_result[:rows][0][:lon].should == -3.6994168
query_result[:rows][0][:lat].should == 40.4151476
raw_json = {"status"=>"OK", "results"=>[{"types"=>["street_address"], "formatted_address"=>"Calle de la Palma, 72, 28015 Madrid, Spain", "address_components"=>[{"long_name"=>"72", "short_name"=>"72", "types"=>["street_number"]}, {"long_name"=>"Calle de la Palma", "short_name"=>"Calle de la Palma", "types"=>["route"]}, {"long_name"=>"Madrid", "short_name"=>"Madrid", "types"=>["locality", "political"]}, {"long_name"=>"Community of Madrid", "short_name"=>"M", "types"=>["administrative_area_level_2", "political"]}, {"long_name"=>"Madrid", "short_name"=>"Madrid", "types"=>["administrative_area_level_1", "political"]}, {"long_name"=>"Spain", "short_name"=>"ES", "types"=>["country", "political"]}, {"long_name"=>"28015", "short_name"=>"28015", "types"=>["postal_code"]}], "geometry"=>{"location"=>{"lat"=>40.4268336, "lng"=>-3.7089444}, "location_type"=>"RANGE_INTERPOLATED", "viewport"=>{"southwest"=>{"lat"=>40.4236786, "lng"=>-3.7120931}, "northeast"=>{"lat"=>40.4299739, "lng"=>-3.7057979}}, "bounds"=>{"southwest"=>{"lat"=>40.4268189, "lng"=>-3.7089466}, "northeast"=>{"lat"=>40.4268336, "lng"=>-3.7089444}}}}]}
JSON.stubs(:parse).returns(raw_json)
table.update_row!(query_result[:rows][0][:cartodb_id], {:name => 'El Estocolmo', :address => 'Calle de La Palma 72, Madrid'})
query_result = user.run_query("select ST_X(the_geom) as lon, ST_Y(the_geom) as lat from #{table.name} limit 1")
query_result[:rows][0][:lon].should == -3.7089444
query_result[:rows][0][:lat].should == 40.4268336
end
it "should add an extra column in the schema for address columns" do
user = create_user
table = new_table
table.user_id = user.id
table.force_schema = "name varchar, address varchar"
table.save
table.set_address_column!(:address)
table.schema.should == [
[:cartodb_id, "integer"], [:name, "character varying"], [:address, "character varying", "address"], [:created_at, "timestamp"], [:updated_at, "timestamp"]
]
end
it "should set the_geom in blank when an address can't be geoencoded" do
raw_json = {"status"=>"ZERO_RESULTS", "results"=>[]}
JSON.stubs(:parse).returns(raw_json)
user = create_user
table = new_table
table.user_id = user.id
table.force_schema = "name varchar, address varchar"
table.save
table.set_address_column!(:address)
table.reload
table.lat_column.should be_nil
table.lon_column.should be_nil
table.address_column.should == :address
table.insert_row!({:name => 'El Lacón', :address => ''})
query_result = user.run_query("select ST_X(the_geom) as lon, ST_Y(the_geom) as lat from #{table.name} limit 1")
query_result[:rows][0][:lon].should be_nil
query_result[:rows][0][:lat].should be_nil
raw_json = {"status"=>"OK", "results"=>[{"types"=>["street_address"], "formatted_address"=>"Calle de la Palma, 72, 28015 Madrid, Spain", "address_components"=>[{"long_name"=>"72", "short_name"=>"72", "types"=>["street_number"]}, {"long_name"=>"Calle de la Palma", "short_name"=>"Calle de la Palma", "types"=>["route"]}, {"long_name"=>"Madrid", "short_name"=>"Madrid", "types"=>["locality", "political"]}, {"long_name"=>"Community of Madrid", "short_name"=>"M", "types"=>["administrative_area_level_2", "political"]}, {"long_name"=>"Madrid", "short_name"=>"Madrid", "types"=>["administrative_area_level_1", "political"]}, {"long_name"=>"Spain", "short_name"=>"ES", "types"=>["country", "political"]}, {"long_name"=>"28015", "short_name"=>"28015", "types"=>["postal_code"]}], "geometry"=>{"location"=>{"lat"=>40.4268336, "lng"=>-3.7089444}, "location_type"=>"RANGE_INTERPOLATED", "viewport"=>{"southwest"=>{"lat"=>40.4236786, "lng"=>-3.7120931}, "northeast"=>{"lat"=>40.4299739, "lng"=>-3.7057979}}, "bounds"=>{"southwest"=>{"lat"=>40.4268189, "lng"=>-3.7089466}, "northeast"=>{"lat"=>40.4268336, "lng"=>-3.7089444}}}}]}
JSON.stubs(:parse).returns(raw_json)
table.update_row!(query_result[:rows][0][:cartodb_id], {:name => 'El Estocolmo', :address => 'Calle de La Palma 72, Madrid'})
query_result = user.run_query("select ST_X(the_geom) as lon, ST_Y(the_geom) as lat from #{table.name} limit 1")
query_result[:rows][0][:lon].should == -3.7089444
query_result[:rows][0][:lat].should == 40.4268336
end
end

Loading…
Cancel
Save