Skip to content

Commit

Permalink
Midway through adding tweet time restrictions and tweet/map options; …
Browse files Browse the repository at this point in the history
…also fixed radius bug
  • Loading branch information
wdenton committed Apr 9, 2013
1 parent b77e9e6 commit 256f586
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 129 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
@@ -1,5 +1,5 @@
GEM GEM
remote: http://rubygems.org/ remote: https://rubygems.org/
specs: specs:
nokogiri (1.5.6) nokogiri (1.5.6)
rack (1.5.0) rack (1.5.0)
Expand Down
11 changes: 11 additions & 0 deletions config.ru
@@ -1,3 +1,14 @@
require 'rubygems'
require 'sinatra'
require 'rack/logger'

configure :development do
set :logging, Logger::DEBUG
end

set :environment, ENV['RACK_ENV'].to_sym
disable :run, :reload

require './laertes.rb' require './laertes.rb'
run Sinatra::Application run Sinatra::Application


Expand Down
316 changes: 188 additions & 128 deletions laertes.rb
Expand Up @@ -83,67 +83,123 @@


layer = settings.config.find {|l| l["layer"] == params[:layerName] } layer = settings.config.find {|l| l["layer"] == params[:layerName] }


show_tweets = true
show_map_points = true

if layer if layer


radius = params[:radius].to_f || 1500 # Default to 1500m radius if none provided radius = 1500 # Default to 1500m radius if none provided

if params[:radius] # But override it if another radius is passed in
radius = params[:radius].to_f
end

hotspots = [] hotspots = []


icon_url = layer["icon_url"] || "https://maps.gstatic.com/mapfiles/ms2/micons/blue-dot.png" icon_url = layer["icon_url"] || "https://maps.gstatic.com/mapfiles/ms2/micons/blue-dot.png"


# Along with the mandatory search radius option, two others can be set up.
# #
# First source: grab points of interest from Google Maps. # First, a checkbox to set which or both of tweets and map points should be shown.
# Use these values:
# CHECKBOXLIST = 1 : Show tweets
# CHECKBOXLIST = 2 : Show map points
# #
if params[:CHECKBOXLIST]
checkboxes = params[:CHECKBOXLIST].split(",")
logger.debug "Checkboxes = #{checkboxes}"
if ! checkboxes.include? "1"
show_tweets = false
logger.debug "Not showing tweets"
end
if ! checkboxes.include? "2"
show_map_points = false
logger.debug "Not showing map points"
end
end

if ! show_tweets and ! show_map_points
logger.debug "This will not be informative"
end

# CHECKBOXLIST=1&version=7.1&radius=2000&lat=43.6840131&layerName=laertesdev&action=refresh&acc
# CHECKBOXLIST=1%2C2

# Second, radio buttons to limit tweets to certain time ranges:
# RADIOLIST = 1: Last hour
# RADIOLIST = 4: Last 4 hours
# RADIOLIST = 8: Last 24 hours
# RADIOLIST = 16: Today
# RADIOLIST = 32: All
tweet_time_limit = 100000 # Hours, some very high number by default
case params[:RADIOLIST]
when 1
tweet_time_limit = 1
when 4
tweet_time_limit = 4
when 8
tweet_time_limit = 24
when 16
# Need to calculate what "today" means
tweet_time_limit = 1
end


counter = 1; counter = 1;
layer["google_maps"].each do |map_url|
begin #
kml = Nokogiri::XML(open(map_url + "&output=kml")) # First source: grab points of interest from Google Maps.
kml.xpath("//xmlns:Placemark").each do |p| #
if p.css("Point").size > 0 # Nicer way to ignore everything that doesn't have a Point element?

if show_map_points
# Some of the points will be out of range, but lets assume there won't be too many, layer["google_maps"].each do |map_url|
# and we'll deal with it below begin

kml = Nokogiri::XML(open(map_url + "&output=kml"))
# Ignore all points that are too far away kml.xpath("//xmlns:Placemark").each do |p|
longitude, latitude, altitude = p.css("coordinates").text.split(",") if p.css("Point").size > 0 # Nicer way to ignore everything that doesn't have a Point element?
next if distance_between(params[:lat], params[:lon], latitude, longitude) > radius

# Some of the points will be out of range, but lets assume there won't be too many,
# But if it's within range, build the hotspot information for Layar # and we'll deal with it below
hotspot = {
"id" => counter, # Could keep a counter but this is good enough # Ignore all points that are too far away
"text" => { longitude, latitude, altitude = p.css("coordinates").text.split(",")
"title" => p.css("name").text, next if distance_between(params[:lat], params[:lon], latitude, longitude) > radius
"description" => Nokogiri::HTML(p.css("description").text).css("div").text,
# For the description, which is in HTML, we need to pick out the text of the # But if it's within range, build the hotspot information for Layar
# element from the XML and then parse it as HTML. I think. Seems kooky. hotspot = {
"footnote" => "" "id" => counter, # Could keep a counter but this is good enough
}, "text" => {
"anchor" => { "title" => p.css("name").text,
"geolocation" => { "description" => Nokogiri::HTML(p.css("description").text).css("div").text,
"lat" => latitude.to_f, # For the description, which is in HTML, we need to pick out the text of the
"lon" => longitude.to_f # element from the XML and then parse it as HTML. I think. Seems kooky.
} "footnote" => ""
}, },
"imageURL" => icon_url, "anchor" => {
"icon" => { "geolocation" => {
"url" => icon_url, "lat" => latitude.to_f,
"type" => 0 "lon" => longitude.to_f
}, }
} },
hotspots << hotspot "imageURL" => icon_url,
counter += 1 "icon" => {
"url" => icon_url,
"type" => 0
},
}
hotspots << hotspot
counter += 1
end
end end
rescue Exception => error
# TODO Catch errors better
logger.error "Error: #{error}"
end end
rescue Exception => error
# TODO Catch errors better
logger.error "Error: #{error}"
end end
end end


# #
# Second source: look for any tweets that were made nearby and also use the right hash tags. # Second source: look for any tweets that were made nearby and also use the right hash tags.
# #

# Details about the Twitter Search API (simpler than the full API): # Details about the Twitter Search API (simpler than the full API):
# Twitter Search API: https://dev.twitter.com/docs/using-search # Twitter Search API: https://dev.twitter.com/docs/using-search
# #
Expand All @@ -152,100 +208,104 @@
# Geolocating of tweets in the response: # Geolocating of tweets in the response:
# https://dev.twitter.com/docs/platform-objects/tweets#obj-coordinates # https://dev.twitter.com/docs/platform-objects/tweets#obj-coordinates


radius_km = radius / 1000 # Twitter wants the radius in km if show_tweets

radius_km = radius / 1000 # Twitter wants the radius in km
# Test search: geolocated only (grabs a lot of results, good for making sure things work)
# twitter_search_url = "https://search.twitter.com/search.json?geocode=#{params[:lat]},#{params[:lon]},#{radius_km}km&rpp=100" # Test search: geolocated only (grabs a lot of results, good for making sure things work)

# twitter_search_url = "https://search.twitter.com/search.json?geocode=#{params[:lat]},#{params[:lon]},#{radius_km}km&rpp=100"
# The real search: hashtag plus geolocated
twitter_search_url = # The real search: hashtag plus geolocated
"https://search.twitter.com/search.json?q=" + twitter_search_url =
CGI.escape(layer["search"]) + "https://search.twitter.com/search.json?q=" +
"&geocode=#{params[:lat]},#{params[:lon]},#{radius_km}km" + CGI.escape(layer["search"]) +
"&rpp=100&include_entities=1" "&geocode=#{params[:lat]},#{params[:lon]},#{radius_km}km" +
logger.info "Twitter search URL: #{twitter_search_url}" "&rpp=100&include_entities=1"

logger.info "Twitter search URL: #{twitter_search_url}"
open(twitter_search_url) do |f|
unless f.status[0] == "200" open(twitter_search_url) do |f|
logger.error f.status unless f.status[0] == "200"
# Set up error for Layar logger.error f.status
else # Set up error for Layar
@twitter = JSON.parse(f.read) else
@twitter = JSON.parse(f.read)
end
end end
end


# TODO: Wrap this in a loop so we page back through results until we get # TODO: Wrap this in a loop so we page back through results until we get
# n results? Or will this be a problem when there's a large cluster of # n results? Or will this be a problem when there's a large cluster of
# geolocated tweets happening with the same hash tag? # geolocated tweets happening with the same hash tag?


logger.info "Found #{@twitter['results'].size} results" logger.info "Found #{@twitter['results'].size} results"


@twitter["results"].each do |r| @twitter["results"].each do |r|
# puts r["from_user"] # puts r["from_user"]
if r["geo"] if r["geo"]
# There is a known latitude and longitude. (Otherwise it's null.) # There is a known latitude and longitude. (Otherwise it's null.)
# By the way, there is only one kind of point, so this will always be true: # By the way, there is only one kind of point, so this will always be true:
# r["geo"]["type"] == "Point" # r["geo"]["type"] == "Point"


# Same as before: ignore if the point is not within the radius # Same as before: ignore if the point is not within the radius
latitude, longitude = r["geo"]["coordinates"] latitude, longitude = r["geo"]["coordinates"]
# No: rely on Twitter to do it, because we're specifying it in the query. # No: rely on Twitter to do it, because we're specifying it in the query.
# next if distance_between(params[:lat], params[:lon], latitude, longitude) > radius # next if distance_between(params[:lat], params[:lon], latitude, longitude) > radius


hotspot = { logger.debug "Tweet created at: #{r["created_at"]}"
"id" => r["id"],
"text" => { hotspot = {
"title" => "@#{r["from_user"]} (#{r["from_user_name"]})", "id" => r["id"],
"description" => r["text"], "text" => {
"footnote" => since(r["created_at"]) "title" => "@#{r["from_user"]} (#{r["from_user_name"]})",
}, "description" => r["text"],
# TODO Show local time, or how long ago. "footnote" => since(r["created_at"])
"anchor" => { },
"geolocation" => { # TODO Show local time, or how long ago.
"lat" => latitude, "anchor" => {
"lon" => longitude "geolocation" => {
"lat" => latitude,
"lon" => longitude
}
} }
} }
}

# imageURL is the image in the BIW, the banner at the bottom
# imageURL is the image in the BIW, the banner at the bottom hotspot["imageURL"] = r["profile_image_url"].gsub("normal", "bigger") # https://dev.twitter.com/docs/user-profile-images-and-banners
hotspot["imageURL"] = r["profile_image_url"].gsub("normal", "bigger") # https://dev.twitter.com/docs/user-profile-images-and-banners

# Set up an action so the person can go to Twitter and see the
# Set up an action so the person can go to Twitter and see the # actual tweet. Unfortunately Layar opens web pages
# actual tweet. Unfortunately Layar opens web pages # internally and doesn't pass them over to a preferred
# internally and doesn't pass them over to a preferred # application.
# application. # See http://layar.com/documentation/browser/api/getpois-response/actions/
# See http://layar.com/documentation/browser/api/getpois-response/actions/ hotspot["actions"] = [{
hotspot["actions"] = [{ "uri" => "https://twitter.com/" + r["from_user"] + "/status/" + r["id_str"],
"uri" => "https://twitter.com/" + r["from_user"] + "/status/" + r["id_str"], "label" => "Read on Twitter",
"label" => "Read on Twitter", "contentType" => "text/html",
"contentType" => "text/html", "activityType" => 27, # Show eye icon
"activityType" => 27, # Show eye icon "method" => "GET"
"method" => "GET" }]
}]

# icon is the image in the CIW, floating in space
# icon is the image in the CIW, floating in space # By saying "include_entities=1" in the search URL we retrieve more information ...
# By saying "include_entities=1" in the search URL we retrieve more information ... # if someone attached a photo to a tweet, show it instead of their profile image
# if someone attached a photo to a tweet, show it instead of their profile image # Documentation: https://dev.twitter.com/docs/tweet-entities
# Documentation: https://dev.twitter.com/docs/tweet-entities if r["entities"] && r["entities"]["media"]
if r["entities"] && r["entities"]["media"] # There is media attached. Look for an attached photo.
# There is media attached. Look for an attached photo. r["entities"]["media"].each do |m|
r["entities"]["media"].each do |m| # Will there ever be more than one?
# Will there ever be more than one? if m["type"] && m["type"] == "photo"
if m["type"] && m["type"] == "photo" icon_url = m["media_url"] + ":thumb"
icon_url = m["media_url"] + ":thumb" logger.info "#{r['id']} has photo attached, icon_url = #{icon_url}"
logger.info "#{r['id']} has photo attached, icon_url = #{icon_url}" end
end end
else
icon_url = r["profile_image_url"]
end end
else hotspot["icon"] = {
icon_url = r["profile_image_url"]
end
hotspot["icon"] = {
"url" => icon_url, "url" => icon_url,
"type" => 0 "type" => 0
} }


hotspots << hotspot hotspots << hotspot
end
end end
end end


Expand Down

0 comments on commit 256f586

Please sign in to comment.