fusion_tables 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +0 -1
- data/LICENSE +1 -1
- data/README.textile +58 -43
- data/VERSION +1 -1
- data/examples/compare_tweets.rb +142 -0
- data/examples/credentials.example.yml +4 -0
- data/fusion_tables.gemspec +8 -3
- data/lib/fusion_tables/data/table.rb +5 -5
- data/pkg/fusion_tables-0.1.0.gem +0 -0
- data/pkg/fusion_tables-0.1.1.gem +0 -0
- metadata +9 -4
data/.gitignore
CHANGED
data/LICENSE
CHANGED
data/README.textile
CHANGED
|
@@ -1,63 +1,81 @@
|
|
|
1
1
|
h1. fusion-tables
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This gem lets you easily interact with Google Fusion Tables from your Ruby application. Here are some "example maps and charts":http://www.tokumine.com/2010/08/10/fusion-tables-gem/.
|
|
4
|
+
|
|
5
|
+
h2. Gem Dependencies
|
|
6
|
+
|
|
7
|
+
* gdata >= 1.1.1
|
|
4
8
|
|
|
5
9
|
h2. Installation
|
|
6
10
|
|
|
7
|
-
gem install
|
|
11
|
+
bc. gem install fusion_tables
|
|
12
|
+
|
|
13
|
+
h2. To Use
|
|
14
|
+
|
|
15
|
+
bc. require 'fusion_tables'
|
|
16
|
+
|
|
17
|
+
or in Rails 2.3.x
|
|
18
|
+
|
|
19
|
+
bc. config.gem 'fusion_tables'
|
|
8
20
|
|
|
9
21
|
h2. API examples
|
|
10
22
|
|
|
11
|
-
The best place to see
|
|
23
|
+
The best place to see examples (including uploading KML) is in the tests, but this is a good starting point.
|
|
12
24
|
|
|
13
|
-
<pre><code
|
|
14
|
-
# Connect to service
|
|
25
|
+
<pre><code># Connect to service
|
|
15
26
|
@ft = GData::Client::FusionTables.new
|
|
16
27
|
@ft.clientlogin(username, password)
|
|
17
28
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{:name => "age", :datatype => 'number' },
|
|
22
|
-
{:name => "meeting time", :datatype => 'datetime' },
|
|
23
|
-
{:name => "where", :datatype => 'location' }]
|
|
24
|
-
|
|
25
|
-
# Create the table
|
|
26
|
-
@table = @ft.create_table "My upcoming meetings", cols
|
|
27
|
-
@table.id #=> 42342 (the table's google id)
|
|
28
|
-
|
|
29
|
-
# Insert data
|
|
30
|
-
data = [{"friend name" => "Eric Wimp",
|
|
31
|
-
"age" => 25,
|
|
32
|
-
"meeting time" => Time.utc(2010,"aug",10,20,15,1),
|
|
33
|
-
"where" => "29 Acacia Road, Nuttytown"]
|
|
29
|
+
# Browse existing tables
|
|
30
|
+
@ft.show_tables
|
|
31
|
+
# => [table_1, table_2]
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# Note - the insert method chunks the data array into 500 row chunks
|
|
33
|
+
# Getting table id suitable for using with google maps (see more below)
|
|
34
|
+
table_1.id #=> 42342 (the table's google id)
|
|
38
35
|
|
|
39
36
|
# Count data
|
|
40
|
-
|
|
37
|
+
table_1.count #=> 1
|
|
41
38
|
|
|
42
39
|
# Select data
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
table_1.select
|
|
41
|
+
#=> data
|
|
45
42
|
|
|
46
|
-
#
|
|
47
|
-
|
|
43
|
+
# Select data with conditions
|
|
44
|
+
table_1.select "name", "WHERE x=n"
|
|
45
|
+
#=> data
|
|
48
46
|
|
|
49
|
-
#
|
|
50
|
-
|
|
47
|
+
# Select ROWIDs
|
|
48
|
+
row_ids = table_1.select "ROWID"
|
|
51
49
|
|
|
52
50
|
# Drop tables
|
|
53
|
-
@ft.drop
|
|
54
|
-
@ft.drop [
|
|
55
|
-
@ft.drop /yacht/ # regex
|
|
51
|
+
@ft.drop table_1.id # table id
|
|
52
|
+
@ft.drop [table_1.id, table_2.id] # arrays of table ids
|
|
53
|
+
@ft.drop /yacht/ # regex on table name
|
|
54
|
+
|
|
55
|
+
# Creating a table
|
|
56
|
+
cols = [{:name => "friend name", :type => 'string' },
|
|
57
|
+
{:name => "age", :type => 'number' },
|
|
58
|
+
{:name => "meeting time", :type => 'datetime' },
|
|
59
|
+
{:name => "where", :type => 'location' }]
|
|
60
|
+
|
|
61
|
+
new_table = @ft.create_table "My upcoming meetings", cols
|
|
62
|
+
|
|
63
|
+
# Inserting rows (auto chunks every 500)
|
|
64
|
+
data = [{"friend name" => "Eric Wimp",
|
|
65
|
+
"age" => 25,
|
|
66
|
+
"meeting time" => Time.utc(2010,"aug",10,20,15,1),
|
|
67
|
+
"where" => "29 Acacia Road, Nuttytown"}]
|
|
68
|
+
new_table.insert data
|
|
69
|
+
|
|
70
|
+
# Delete row
|
|
71
|
+
new_table.delete row_id
|
|
56
72
|
</code></pre>
|
|
57
73
|
|
|
58
74
|
h2. Fusion Tables secret Geospatial Sauce
|
|
59
75
|
|
|
60
|
-
|
|
76
|
+
*"Geolocated Tweets example":http://tables.googlelabs.com/DataSource?snapid=73106*
|
|
77
|
+
|
|
78
|
+
Fusion Tables is a labs product from Google. You can "read more here":http://tables.googlelabs.com/, but the key thing is that it gives you *access to the google tile mill for fast generation of google map layers across large datasets*
|
|
61
79
|
|
|
62
80
|
Fusion Tables supports the following geometry types:
|
|
63
81
|
|
|
@@ -65,13 +83,11 @@ Fusion Tables supports the following geometry types:
|
|
|
65
83
|
* addresses (automatically geocodes them for you)
|
|
66
84
|
* KML (point, polyline, polygon, multipolygon)
|
|
67
85
|
|
|
68
|
-
h2.
|
|
86
|
+
h2. Integrate with google maps v3
|
|
69
87
|
|
|
70
88
|
Adding a fusion tables datalayer with many points/polygons to your v3 map is as simple as:
|
|
71
89
|
|
|
72
|
-
|
|
73
|
-
layer = new google.maps.FusionTablesLayer(139529);
|
|
74
|
-
</code></pre>
|
|
90
|
+
bc. layer = new google.maps.FusionTablesLayer(139529);
|
|
75
91
|
|
|
76
92
|
That's it
|
|
77
93
|
|
|
@@ -95,10 +111,9 @@ You can also export your data (filtered and geocoded) to KML. As an example, her
|
|
|
95
111
|
|
|
96
112
|
read "more here":http://code.google.com/apis/maps/documentation/javascript/overlays.html#FusionTables
|
|
97
113
|
|
|
98
|
-
h2. A
|
|
114
|
+
h2. A few words of warning...
|
|
99
115
|
|
|
100
|
-
#
|
|
101
|
-
# The API is still very young and *will change*
|
|
116
|
+
# The API is still very young and *will probably change*
|
|
102
117
|
# Currently you have to make a table public before you can display it on a map, unfortunately, this can only be done on the web interface. A suggested workaround is to put all your data in 1 big public table, and then query for the data you want to display based off a key/flag column
|
|
103
118
|
|
|
104
119
|
h2. Note on Patches/Pull Requests
|
|
@@ -115,7 +130,7 @@ h2. Copyright
|
|
|
115
130
|
|
|
116
131
|
Largely based on Tom Verbeure's work for MTBGuru: http://code.google.com/p/mtbguru-fusiontables/
|
|
117
132
|
|
|
118
|
-
Copyright (c) 2010 Simon Tokumine. See LICENSE for details.
|
|
133
|
+
Copyright (c) 2010 Tom Verbeure, Simon Tokumine. See LICENSE for details.
|
|
119
134
|
|
|
120
135
|
|
|
121
136
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.1
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby
|
|
2
|
+
#
|
|
3
|
+
# Twitter Fusion Tables Mashup
|
|
4
|
+
# S.Tokumine 2010
|
|
5
|
+
#
|
|
6
|
+
# looks for tweets in the live stream around certain
|
|
7
|
+
# cities and posts them to fusion tables with KML attached
|
|
8
|
+
#
|
|
9
|
+
# Gem dependencies:
|
|
10
|
+
#
|
|
11
|
+
# tweetstream
|
|
12
|
+
# GeoRuby
|
|
13
|
+
# fusion_tables
|
|
14
|
+
#
|
|
15
|
+
# Output from running this for an evening
|
|
16
|
+
# http://tables.googlelabs.com/DataSource?snapid=72509
|
|
17
|
+
#
|
|
18
|
+
require 'rubygems'
|
|
19
|
+
require 'tweetstream'
|
|
20
|
+
require 'geo_ruby'
|
|
21
|
+
include GeoRuby
|
|
22
|
+
include SimpleFeatures
|
|
23
|
+
require 'fusion_tables'
|
|
24
|
+
require 'time'
|
|
25
|
+
|
|
26
|
+
class Object
|
|
27
|
+
def try(method, *args, &block)
|
|
28
|
+
send(method, *args, &block)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Configure settings
|
|
33
|
+
config = YAML::load_file(File.join(File.dirname(__FILE__), 'credentials.yml'))
|
|
34
|
+
DEFAULT_SRID = 4328
|
|
35
|
+
|
|
36
|
+
# Twitter places
|
|
37
|
+
places = {
|
|
38
|
+
:san_francisco => [-122.75,36.8,-121.75,37.8],
|
|
39
|
+
:new_york => [-74,40,-73,41],
|
|
40
|
+
:tokyo => [139.3,35,140.3,36],
|
|
41
|
+
:london => [-0.54,51.2,0.46,52.2],
|
|
42
|
+
:madrid => [-4.2,40,-3.2,41],
|
|
43
|
+
:paris => [1.75,48.5,2.75, 49.5],
|
|
44
|
+
:beijing => [115.9,39,116.9,40],
|
|
45
|
+
:mumbai => [72.75,18.88,73.75,19.88],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Configure fusion tables
|
|
49
|
+
ft = GData::Client::FusionTables.new
|
|
50
|
+
ft.clientlogin(config["google_username"], config["google_password"])
|
|
51
|
+
table_name = "twitter_fusion"
|
|
52
|
+
cols = [
|
|
53
|
+
{:name => 'screen_name', :type => 'string'},
|
|
54
|
+
{:name => 'avatar', :type => 'string'},
|
|
55
|
+
{:name => 'text', :type => 'string'},
|
|
56
|
+
{:name => 'created', :type => 'datetime'},
|
|
57
|
+
{:name => 'url', :type => 'string'},
|
|
58
|
+
{:name => 'location', :type => 'location'},
|
|
59
|
+
{:name => 'iso', :type => 'location'},
|
|
60
|
+
{:name => 'country_name', :type => 'location'},
|
|
61
|
+
{:name => 'city', :type => 'string'}
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
# Create FT if it doesn't exist
|
|
65
|
+
tables = ft.show_tables
|
|
66
|
+
table = tables.select{|t| t.name == table_name}.first
|
|
67
|
+
table = ft.create_table(table_name, cols) if !table
|
|
68
|
+
|
|
69
|
+
# Configure Twitter stream client
|
|
70
|
+
data = []
|
|
71
|
+
tw = TweetStream::Client.new(config["twitter_username"],config["twitter_password"])
|
|
72
|
+
|
|
73
|
+
# configure friendly rate limit handling
|
|
74
|
+
tw.on_limit do |skip_count|
|
|
75
|
+
sleep 5
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# start searching twitter stream and posting to FT
|
|
79
|
+
tw.filter(:locations => places.values.join(",")) do |tweet|
|
|
80
|
+
begin
|
|
81
|
+
|
|
82
|
+
country = "unknown"
|
|
83
|
+
iso = "unknown"
|
|
84
|
+
begin
|
|
85
|
+
country = tweet.try(:[],:place).try(:[], :country)
|
|
86
|
+
iso = tweet.try(:[],:place).try(:[], :country_code)
|
|
87
|
+
rescue
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Divine the tweets geometry
|
|
91
|
+
#
|
|
92
|
+
# overly complex due to
|
|
93
|
+
# * some US tweets have their lat/longs flipped (but not all...)
|
|
94
|
+
# * some geo tweets are made using a "place" envelope rather than exact lat/kng
|
|
95
|
+
if tweet[:geo]
|
|
96
|
+
if iso == 'US' && tweet[:geo][:coordinates][1] > 0
|
|
97
|
+
p = Point.from_x_y(tweet[:geo][:coordinates][0],tweet[:geo][:coordinates][1])
|
|
98
|
+
else
|
|
99
|
+
p = Point.from_x_y(tweet[:geo][:coordinates][1],tweet[:geo][:coordinates][0])
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
p = Polygon.from_coordinates(tweet[:place][:bounding_box][:coordinates]).envelope.center
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# work out which city the tweet is from by testing with an extended bounding box
|
|
106
|
+
# BBox extention needed as twitter returns things outside our defined bboxes...
|
|
107
|
+
city = "unknown"
|
|
108
|
+
places.each do |key, value|
|
|
109
|
+
if !(p.x < value[0]-1 || p.x > value[2]+1 || p.y < value[1]-1 || p.y > value[3]+1)
|
|
110
|
+
city = key.to_s.gsub("_"," ")
|
|
111
|
+
break
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# pack data
|
|
116
|
+
data << {
|
|
117
|
+
"screen_name" => tweet[:user][:screen_name],
|
|
118
|
+
"avatar" => tweet[:user][:profile_image_url],
|
|
119
|
+
"text" => tweet[:text],
|
|
120
|
+
"created" => Time.parse(tweet[:created_at]),
|
|
121
|
+
"url" => "http://twitter.com/#{tweet[:user][:screen_name]}/status/#{tweet[:id]}",
|
|
122
|
+
"location" => p.as_kml,
|
|
123
|
+
"iso" => iso,
|
|
124
|
+
"country_name" => country,
|
|
125
|
+
"city" => city
|
|
126
|
+
}
|
|
127
|
+
rescue => e
|
|
128
|
+
puts "ERROR: #{e.inspect}, #{e.backtrace}"
|
|
129
|
+
#let sleeping dogs lie...
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# let us know how we're doing
|
|
133
|
+
puts "#{50-data.size}: #{city}, #{tweet.text}"
|
|
134
|
+
|
|
135
|
+
# Post to fusion tables
|
|
136
|
+
if data.size == 50
|
|
137
|
+
puts "sending data to fusion tables..."
|
|
138
|
+
ft_data = data
|
|
139
|
+
data = []
|
|
140
|
+
table.insert ft_data
|
|
141
|
+
end
|
|
142
|
+
end
|
data/fusion_tables.gemspec
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = %q{fusion_tables}
|
|
8
|
-
s.version = "0.1.
|
|
8
|
+
s.version = "0.1.1"
|
|
9
9
|
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
11
|
s.authors = ["Simon Tokumine", "Tom Verbeure"]
|
|
12
|
-
s.date = %q{2010-08-
|
|
12
|
+
s.date = %q{2010-08-12}
|
|
13
13
|
s.description = %q{A simple Google Fusion Tables API wrapper. Supports bulk inserts and most API functions}
|
|
14
14
|
s.email = %q{simon@tinypla.net}
|
|
15
15
|
s.extra_rdoc_files = [
|
|
@@ -25,12 +25,16 @@ Gem::Specification.new do |s|
|
|
|
25
25
|
"Rakefile",
|
|
26
26
|
"TODO",
|
|
27
27
|
"VERSION",
|
|
28
|
+
"examples/compare_tweets.rb",
|
|
29
|
+
"examples/credentials.example.yml",
|
|
28
30
|
"fusion_tables.gemspec",
|
|
29
31
|
"lib/fusion_tables.rb",
|
|
30
32
|
"lib/fusion_tables/client/fusion_tables.rb",
|
|
31
33
|
"lib/fusion_tables/data/data.rb",
|
|
32
34
|
"lib/fusion_tables/data/table.rb",
|
|
33
35
|
"lib/fusion_tables/ext/fusion_tables.rb",
|
|
36
|
+
"pkg/fusion_tables-0.1.0.gem",
|
|
37
|
+
"pkg/fusion_tables-0.1.1.gem",
|
|
34
38
|
"test/README",
|
|
35
39
|
"test/helper.rb",
|
|
36
40
|
"test/test_client.rb",
|
|
@@ -47,7 +51,8 @@ Gem::Specification.new do |s|
|
|
|
47
51
|
"test/helper.rb",
|
|
48
52
|
"test/test_client.rb",
|
|
49
53
|
"test/test_ext.rb",
|
|
50
|
-
"test/test_table.rb"
|
|
54
|
+
"test/test_table.rb",
|
|
55
|
+
"examples/compare_tweets.rb"
|
|
51
56
|
]
|
|
52
57
|
|
|
53
58
|
if s.respond_to? :specification_version then
|
|
@@ -114,11 +114,11 @@ module GData
|
|
|
114
114
|
data.inject([]) do |ar,h|
|
|
115
115
|
ret = {}
|
|
116
116
|
h.each do |key, value|
|
|
117
|
-
ret[key] = case get_datatype(key)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
ret["'#{key.to_s}'"] = case get_datatype(key)
|
|
118
|
+
when "number" then "#{value}"
|
|
119
|
+
when "datetime" then "'#{value.strftime("%m-%d-%Y")}'"
|
|
120
|
+
else "'#{value.gsub(/\\/, '\&\&').gsub(/'/, "''")}'"
|
|
121
|
+
end
|
|
122
122
|
end
|
|
123
123
|
ar << ret
|
|
124
124
|
ar
|
|
Binary file
|
|
Binary file
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fusion_tables
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 25
|
|
5
5
|
prerelease: false
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
8
|
- 1
|
|
9
|
-
-
|
|
10
|
-
version: 0.1.
|
|
9
|
+
- 1
|
|
10
|
+
version: 0.1.1
|
|
11
11
|
platform: ruby
|
|
12
12
|
authors:
|
|
13
13
|
- Simon Tokumine
|
|
@@ -16,7 +16,7 @@ autorequire:
|
|
|
16
16
|
bindir: bin
|
|
17
17
|
cert_chain: []
|
|
18
18
|
|
|
19
|
-
date: 2010-08-
|
|
19
|
+
date: 2010-08-12 00:00:00 +01:00
|
|
20
20
|
default_executable:
|
|
21
21
|
dependencies:
|
|
22
22
|
- !ruby/object:Gem::Dependency
|
|
@@ -67,12 +67,16 @@ files:
|
|
|
67
67
|
- Rakefile
|
|
68
68
|
- TODO
|
|
69
69
|
- VERSION
|
|
70
|
+
- examples/compare_tweets.rb
|
|
71
|
+
- examples/credentials.example.yml
|
|
70
72
|
- fusion_tables.gemspec
|
|
71
73
|
- lib/fusion_tables.rb
|
|
72
74
|
- lib/fusion_tables/client/fusion_tables.rb
|
|
73
75
|
- lib/fusion_tables/data/data.rb
|
|
74
76
|
- lib/fusion_tables/data/table.rb
|
|
75
77
|
- lib/fusion_tables/ext/fusion_tables.rb
|
|
78
|
+
- pkg/fusion_tables-0.1.0.gem
|
|
79
|
+
- pkg/fusion_tables-0.1.1.gem
|
|
76
80
|
- test/README
|
|
77
81
|
- test/helper.rb
|
|
78
82
|
- test/test_client.rb
|
|
@@ -118,3 +122,4 @@ test_files:
|
|
|
118
122
|
- test/test_client.rb
|
|
119
123
|
- test/test_ext.rb
|
|
120
124
|
- test/test_table.rb
|
|
125
|
+
- examples/compare_tweets.rb
|