Whereis Processing Code:
import processing.xml.*;
//import processing.pdf.*;
//
// variables
//
XMLElement source1,source2,source3,source4,source5,source6;
int repaint_me = 2;
int my_width = 800;
int my_height = 600;
float world_center_longitude = -122.871975;
float world_center_latitude = 45.715567;
float zoom = 1;
boolean fast = true;
//
// print our prettyness
//
void printxml(XMLElement xml, int r, int g, int b) {
XMLElement[] kids = xml.getChildren();
float old_lat = 0;
float old_lon = 0;
for (int i=0; i < kids.length; i++) {
float lat = kids[i].getFloatAttribute("lat");
float lon = kids[i].getFloatAttribute("lon");
lon = ( lon - world_center_longitude ) * ((float)Math.pow(2,zoom)) + my_width / 2;
lat = ( lat - world_center_latitude ) * ((float)Math.pow(2,zoom)) + my_height / 2;
if( old_lat == 0) { old_lat = lat; old_lon = lon; continue; }
if( fast == false ) {
stroke(r+80,g+80,b+80, 20);
strokeWeight(70);
line(old_lon,old_lat,lon,lat);
stroke(r+60,g+60,b+60, 60);
strokeWeight(20);
line(old_lon,old_lat,lon,lat);
stroke(r+40,g+40,b+40, 80);
strokeWeight(10);
line(old_lon,old_lat,lon,lat);
stroke(r+20,g+20,b+20, 128);
strokeWeight(9);
line(old_lon,old_lat,lon,lat);
}
stroke(r,g,b, 210);
strokeWeight(3);
line(old_lon,old_lat,lon,lat);
old_lat = lat;
old_lon = lon;
}
}
//
// processing startup - processing calls this for us to start things off
//
void setup() {
size(my_width,my_height);
source1 = new XMLElement(this, "http://makerlab.com/whereis/kmikeym.xml");
source2 = new XMLElement(this, "http://makerlab.com/whereis/blake.xml");
source3 = new XMLElement(this, "http://makerlab.com/whereis/anselm.xml");
source4 = new XMLElement(this, "http://makerlab.com/whereis/meg.xml");
source5 = new XMLElement(this, "http://makerlab.com/whereis/paige.xml");
source6 = new XMLElement(this, "http://makerlab.com/whereis/jason.xml");
if( source1 != null ) println("started up");
if( source2 != null ) println("kmikeym " );
if( source3 != null ) println("blake " );
}
//
// processing calls this every refresh for us
//
int counter = 1;
void draw() {
if( repaint_me > 0 ) {
//String value = "/Users/paige/anselm_map_" + counter + ".pdf";
//counter = counter + 1;
//if( fast == false ) beginRecord (PDF,value);
background(205);
stroke(0,0,0);
line(20,20,100,100);
//anselm
if( source3 != null ) printxml(source3, 37, 4, 111 );
//meg
if( source4 != null ) printxml(source4, 120, 2,60 );
//paige
if( source5 != null ) printxml(source5, 101-50,161-50,237-50 );
//jason
if( source6 != null ) printxml(source6, 190,10,200 );
//kmikeym
if( source1 != null ) printxml(source1, 246, 157, 62 );
//blake
if( source2 != null ) printxml(source2, 70, 194, 139 );
// if( fast == false ) endRecord ();
// save ("map4.tiff");
repaint_me = 0;
fast = true;
}
}
//
// processing calls this when the mouse is pressed
//
void mousePressed() {
world_center_longitude += (((mouseX - my_width/2.0) * 360.0 / ((float)my_width))) / Math.pow(2,zoom-1) ;
world_center_latitude += (((mouseY - my_height/2.0) * 180.0 / ((float)my_height))) / Math.pow(2,zoom-1) ;
if( mouseButton == LEFT ) {
zoom = zoom + 1;
} else {
zoom = zoom - 1;
}
repaint_me = 2;
fast = true;
}
void keyPressed() {
println("key");
if (key == CODED) {
if (keyCode == UP) {
world_center_latitude -= ((60 * 180.0 / ((float)my_height))) / Math.pow(2,zoom-1) ;
// centery -= (bottom-top)/64;
}
else if (keyCode == DOWN) {
world_center_latitude += ((60 * 180.0 / ((float)my_height))) / Math.pow(2,zoom-1) ;
//centery += (bottom-top)/64;
}
else if (keyCode == LEFT) {
world_center_longitude -= ((60 * 360.0 / ((float)my_width))) / Math.pow(2,zoom-1) ;
//centerx -= (right-left)/64;
}
else if (keyCode == RIGHT) {
world_center_longitude += ((60 * 360.0 / ((float)my_width))) / Math.pow(2,zoom-1) ;
//centerx += (right-left)/64;
}
} else {
if (key == '-') {
zoom = zoom - 1;
}
if (key == '=') {
zoom = zoom + 1;
}
if (key == 'p') {
repaint_me = 2;
fast = false;
return;
}
}
repaint_me = 2;
fast = true;
}
War Tweets
Code:
http://twitter.com/IRAQDEATHS
>
>
> #!/usr/local/bin/ruby
>
> #
> # we want to fetch posts from the iraq death count and show them
> # and we want to only check every day or so ( an external cron job )
> # basically then we take a look at the most recent 50 only...
> # and we twitter up to 50 of them basically...
> # we reverse the twitter order so that the most recent ones are first
> #
>
> twittercap = 50 # twitter this many posts max
>
> #
> # 1) fetch the data
> #
> # http://www.iraqbodycount.org/database/download/ibc-individuals
>
> require 'net/http'
>
> url = "http://www.iraqbodycount.org/database/download/ibc-individuals"
>
> data = Net::HTTP.get_response(URI.parse(url)).body
>
>
>
> #
> # 2) chew on the data a bit - masticate it good
> #
> # our first goal is to read the rather dorky csv file iraq war deaths
> provides
> # we have to skip past the header crap... so lets make a copy of the data
> without that header
> # we will just loop through all the data and pull out the good stuff and
> store it into another array
> # clearly not the "perfectly clean" way to do it but good enough and we
> don't really care
> #
> # http://www.rubytips.org/2008/01/06/csv-processing-in-ruby/
> # http://fastercsv.rubyforge.org/classes/FasterCSV.html
>
> require 'rubygems'
> require "fastercsv"
> @results = FasterCSV.parse(data) # unused approach ->
> read("ibc-individuals")
> @deaths = []
> inside_stupid_header = true
> @results.each do |death|
> if death[0] == "IBC code"
> inside_stupid_header = false
> elsif inside_stupid_header == false
> @deaths << death
> end
> end
>
> #
> # 3) put the data in our storage area... keep a copy in our belly
> #
> # http://sqlite-ruby.rubyforge.org/sqlite3/faq.html
> #
> # our second goal is to store this in a structured way so we can process it
> # i will bother to keep it in a database although i could just hold it
> memory
> # we are going to want to avoid storing duplicates - because we will call
> this multiple times
> #
> # this gets slightly more involved
> #
> # 3a) let us make a table to store the data - if it already exists this code
> will crash but thats ok
> # we wrap the whole thing in a begin / rescue and just ignore the crash if
> it happens
> #
> # The format seems to be here in the ibc file : "IBC code","Name or
> Identifying Details","Age","Sex","Marital Status","Parental
> Status","Earliest Date","Latest Date","Location"
> #
> # 3b) um, store everything...
>
>
> =begin
> require 'rubygems'
> require 'sqlite'
> # this was a manual database approach - tedious!
> db = SQLite::Database.new( "endiraqwar.db" )
> begin
> result = db.execute("CREATE TABLE deaths(id INTEGER, code VARCHAR(80),
> name VARCHAR(255), age INTEGER, sex VARCHAR(32), marital VARCHAR(64),
> parental VARCHAR(64), earliest DATE, latest DATE, location VARCHAR(255))");
> rescue
> end
> @deaths.each do |death|
> result = db.execute("INSERT INTO ")
> end
> =end
>
> #
> # 3) ok scratch the above, lets use this:
> #
> # http://datamapper.org/why.html
> #
> # lets manage the data in a way that takes advantage of object oriented
> design
> # we will grab datamapper which lets us pretend our data is a ruby data
> object
> # (instead of treating it like radioactive waste and holding it at arms
> length)
> # that means we have to define what our data "is" for ruby...
> # also we will hop over to postgres as our back end datastore - away from
> sqlite ... just because
> #
>
> require 'rubygems'
> require 'dm-core'
>
> DataMapper.setup(:default, {
> :adapter => 'postgres',
> :database => "endiraqwar",
> :username => 'endiraqwar',
> :password => '',
> :host => 'localhost'
> })
>
> class Death
> include DataMapper::Resource
> property :id, Integer, :serial => true
> property :code, String
> property :name, Text
> property :age, Text
> property :sex, Text
> property :marital, Text
> property :parental, String
> property :earliest, DateTime
> property :latest, DateTime
> property :location, Text
> property :created_at, DateTime
> property :posted, DateTime, :default => nil
> end
>
> # we do not want to do this now
> # because it would erase our database
> # DataMapper.auto_migrate!
>
> # go ahead and store all the deaths
> @deaths.each do |death|
> if Death.first(:code => death[0] )
> # puts "We already found this death #{death[1]} #{death[0]} so not
> saving"
> next
> end
> # take a second to convert the date phrase into a machine date
> death[6] = DateTime.parse(death[6])
> death[7] = DateTime.parse(death[7])
> # go ahead and make a blobby thing to hold all of this
> record = Death.new(
> :code => death[0],
> :name => death[1],
> :age => death[2],
> :sex => death[3],
> :marital => death[4],
> :parental => death[5],
> :earliest => death[6],
> :latest => death[7],
> :location => death[8]
> )
> puts "recording the passing of #{record.name} at #{record.earliest} and
> #{record.code}"
> record.save
> end
>
> #
> # 4) ok cool, now we have the data - lets twitter!
> #
> # http://twitter.rubyforge.org/
> # http://github.com/jnunemaker/twitter/tree/master/examples/posting.rb
> #
>
> require 'twitter'
> twitter = Twitter::Base.new("endiraqwar","")
> @deaths = Death.all(:order => [:earliest.desc], :limit => twittercap)
> @copyofdeaths = []
> @deaths.each do |death|
> @copyofdeaths << death
> end
>
> @copyofdeaths.reverse.each do |death|
> # publish deaths that are new
> next if death.posted != nil
> result = twitter.post("#{death.name}, #{death.age}, #{death.sex},
> #{death.marital}, #{death.parental} killed on #{death.earliest.strftime("%d
> %b %Y")} at L:#{death.location}")
> # remember that we already published this death
> death.posted = DateTime.now
> death.save
> puts "posted the death of #{death.name} #{death.code}"
> end
