Chapter 4. Community

Hacks 24-29

Flickr is part of a new category of web applications called social software. It’s considered social because Flickr lets you connect with other members in a number of ways. Sharing is built into the fabric of Flickr. Instead of posting your photos to a personal, isolated gallery, you can share them with friends by passing around your URL. By inviting people [Hack #24] to participate at Flickr, you can build a contact list, and by posting comments and adding other members to your contact list you can meet other photographers and have conversations about photos across Flickr.

This chapter will show you how to add contacts at Flickr and follow their activity both on and off Flickr. You’ll find out how to track your friends’ favorite photos [Hack #25] , visualize where your Flickr friends are located [Hack #27] , and even compile a list of your Flickr contacts’ web sites [Hack #29] .

By extending the social features of Flickr, you’ll discover some new things about your Flickr contacts and see their photos in a new light.

Add and Track Contacts

Half the fun of using Flickr is staying in touch with friends and family by watching their photographs.

Your Flickr contact list represents the other Flickr members that you want to keep track of. If you add your friends, family, and other photographers you admire to your list, keeping up with their new photos is a snap.

Adding Contacts

As you explore photos on Flickr, you might spot a particular photographer whom you’d like to keep up with as she adds new photos. To add any Flickr user to your contact list, you first need to get to that user’s photostream.

From any photo’s detail page, look for a byline in the upper-right corner. The byline will say, “Uploaded on [date] by [member].” Clicking the member’s name brings you to her photostream. At the top of the photostream you’ll see a link to add that member as a contact. Click the link, mark her as a friend or family member if you want, and then click OK. That member will now be listed among your contacts.

Tip

Keep in mind that adding someone as a Flickr contact is a one-way operation. If you add someone to your contact list, you won’t automatically appear on her contact list; she’ll receive a notification that you’ve added her, but she’ll have to manually add you as a contact if she wants to.

If you want to add someone to your contact list who isn’t yet a member, you can send an email invitation. Click Invite from the top of any Flickr page to bring up the Invitation page shown in Figure 4-1.

Inviting contacts via email
Figure 4-1. Inviting contacts via email

Enter up to three email addresses (and their Friend or Family status, if applicable), customize the message, and click Send. The people you listed will receive emails from Flickr asking them to join. Once they join, they’ll be added to your contact list with the relationships you specified. Specifying people as Friend or Family means they’ll be able to see photos that are just for them [Hack #2] .

Watching Contacts’ Photos

Once you have a list of contacts, you can keep up with their daily postings on your contacts page (Figure 4-2).

All of your contacts’ recent photos appear on one page
Figure 4-2. All of your contacts’ recent photos appear on one page

Click Your Contacts’ at the top of your Flickr home page to see recent photos. You can choose to limit the page to contacts you’ve designated as friends or family members, or to limit the page to one photo per contact. Viewing your contacts’ recent photos is a great way to see what your friends and relations have been up to lately.

If you’d rather not have to visit Flickr to keep up with your contacts, you can use the RSS and Atom links at the bottom of the contacts page to keep up with their photos in a newsreader [Hack #17] . Figure 4-3 shows the contacts feed in the Safari web browser, which doubles as an RSS newsreader.

Viewing contacts’ photos via RSS
Figure 4-3. Viewing contacts’ photos via RSS

Clicking on any of the photos in the feed will take you to that photo’s detail page.

Watching Contacts’ Activity

Friends, family, and other Flickr members can interact with your photos by leaving comments. Click the “Recent activity” link next to the “Your photos” link on your Flickr home page to see the latest comments on your photos. As with many other spaces around Flickr, you can also subscribe to your recent activity via RSS or Atom [Hack #17] , so you can keep up with comments in your preferred newsreader.

Recent activity feeds are typically used to keep tabs on one’s own photos, but you can use those feeds to see recent activity with your contacts’ photos, too. This will let you know which of your contacts’ photos are getting the most comments, and what people are saying about them.

To start reading others’ recent activity feeds, you need to know a bit about Flickr member IDs. In addition to the member ID you see on a user’s photostream (e.g., pb), every Flickr member has an internal NSID—an alphanumeric string that represents that user behind the scenes (e.g., 33853652177@N01). Once you have an NSID in hand, you can build your own recent activity feed URL.

A quick way to find your own NSID is to browse to your Flickr home page, choose View → Page Source on your browser’s menu, and search for the variable global_nsid toward the top of the page. Whatever this variable is set to is your Flickr NSID.

To find someone else’s NSID, browse to his photostream and click on the Profile link on the right side of the page. From the Profile page, you’ll find a link that says, “Send [member] a message.” Right-click the link, choose Copy Link Location or Copy Shortcut from the menu, and paste the link into a text editor such as Notepad. You should see a URL like this:

http://www.flickr.com/messages_write.gne?to=33853652177@N01

Everything after the equals sign is the member’s NSID. Jot down the member’s name and the NSID, and go to your favorite RSS newsreader. Here’s the format for recent activity feed URLs:

http://www.flickr.com/recent_comments_feed.gne?id=insert NSID&format=rss_200

Insert the NSID, and add the URL to your newsreader. Repeat the process for as many of your contacts as you want to watch. If possible, group the feeds together so you can browse them all at once. Figure 4-4 shows a group of contact activity feeds in the RSS reader Bloglines (http://www.bloglines.com).

Watching contacts’ activity at Bloglines
Figure 4-4. Watching contacts’ activity at Bloglines

The feeds will still be titled “Comments on your photos,” but don’t let the title fool you—you’ll bee seeing your contacts’ photos and recent comments people have left about them. It’s a good way to see which of your contacts’ photos are generating the most conversation.

See Also

Track Your Friends’ Favorites

With a weblog tool called Blosxom and some extra Perl, you can set up a page to show your Flickr contacts’ favorite photos.

Flickr makes it easy to see new photos posted by your contacts—simply log in and click the Your Contacts’ link at the top of the page (or browse directly to http://www.flickr.com/photos/friends/) to keep up with your friends’ photos. Flickr also makes it easy to bookmark your favorite photos by clicking the “Add to Favorites” button above any photo. It’s not easy to see which photos your friends and family have marked as favorites, though—you have to view each of your contacts’ favorites lists individually by going to each one’s photostream page and clicking the Favorites link at the top right.

If you find this a bit cumbersome, instead of viewing all of your friends’ favorites pages individually and looking for new photos, you can let some scripting assemble them for you. You’ll need to do some work to put the pieces into place, but in the end you’ll have a web page that automatically shows any photos your contacts mark as favorites. Watching your friends’ favorites is a fun way to see photos that are interesting to your circle of contacts, and you can use the page to be sure you’re not missing great photos.

This hack uses the Flickr API to gather a list of contacts and find those contacts’ favorite photos. The weblog system Blosxom (http://www.blosxom.com) then displays the latest photos in reverse-chronological order, like posts on a weblog.

What You Need

The first step to implementing this hack is setting up Blosxom to run on your server. Blosxom is an extremely lightweight weblog tool; in fact, it’s only one Perl script (so, of course, you’ll need web space that can run Perl CGI scripts).

To set up Blosxom, grab a copy of the script blosxom.cgi from http://www.blosxom.com and modify the first few lines of the script to match your environment. You’ll find the settings under the “Configurable variables” heading. Be sure to enter the full path to the directory where the script is installed.

You also might want to reduce the default number of entries shown on the home page from 40 to 25; because each entry will be a photograph, a lower number will help the page load faster. With those settings configured, copy blosxom.cgi to a CGI-capable directory on your server. If you run into trouble, click the Install link on the left side of the Blosxom home page for detailed installation instructions for various operating systems.

One key to Blosxom’s simplicity is that the system stores posts as text files in the filesystem. There’s no need to have access to a database or understand how to access a database.

To add posts to a Blosxom weblog, you simply create a text file in the proper location. Blosxom recognizes the new file and places the text contents of the file at the top of the weblog. Thus, to create an automated weblog, you need a script that can automatically write text files.

The Code

This Perl script finds all of the contacts for a specific Flickr user, finds those contacts’ favorite photos, and writes the HTML to display the photos as text files.

You’ll need a few nonstandard Perl modules before you can run the script. The first is Flickr::API (http://search.cpan.org/~iamcal/Flickr-API/lib/Flickr/API.pm), a module that makes working with the Flickr API a snap. The second is XML::Parser::Lite::Tree::XPath (http://search.cpan.org/~iamcal/XML-Parser-Lite-Tree-XPath-0.02/XPath.pm), a module that helps navigate the Flickr API’s response XML.

Create a text file called getFavorites.pl and add the following code. Be sure to include your own Flickr NSID and API key (available at http://www.flickr.com/services/api/) and the absolute path to your Blosxom directory, including a trailing slash.

#!/usr/bin/perl
# getFavorites.pl
#
# Find out which photos your Flickr contacts are 
# marking as favorites. This script contacts the 
# Flickr API, gets a list of your contacts, and 
# finds their favorite photos. You can get a Flickr
# API key and look up your Flickr NSID at:
#
# http://www.flickr.com/services/api/
#
# This script works in conjuction with the weblog
# system Blosxom to display the photos. You can 
# download Blosxom and find out how to install it at:
#
# http://www.blosxom.com/
# 
# Usage: getFavorites.pl

use strict;
use Flickr::API;
use Flickr::API::Response;
use XML::Parser::Lite::Tree::XPath;

# --- Configurable variables -----
my $nsid = 'insert your Flickr NSID';
my $api_key = 'insert your API key';
my $dir = 'insert path/to/blosxom/directory/';

# --- Script begins -----
# Get list of your contacts
my $api = new Flickr::API({'key' => $api_key});

my $response = $api->execute_method('flickr.contacts.getPublicList', {
                'user_id' => $nsid,
});

# Make sure there's a response
if (!$response->{success}) {
    die "Contact list not found! $response->{error_message}";
}

# Parse the response and loop through contacts
my $xpath = new XML::Parser::Lite::Tree::XPath($response->{tree});
my @contacts = $xpath->select_nodes('/contacts/contact');
foreach (@contacts) {
    my $username = $_->{attributes}->{username};
    my $usernsid = $_->{attributes}->{nsid};
    my $userdir = $username;
    $userdir =~ s/^\s+//g; # trim leading spaces
    $userdir =~ s/\s+$//g; # trim trailing spaces
    $userdir =~ s/://g; # transform other spaces
    $userdir =~ s/'//g; # transform apostrophes
    $userdir =~ s/\///g; # transform slashes
    $userdir =~ s/\?//g; # transform question marks
    $userdir =~ s/\*//g; # transform asterisks
    $userdir =~ s/\.//g; # transform periods
    $userdir =~ s/\s/_/g; # underscore spaces, etc.

    my $full_dir = $dir . "/" . $userdir;
    unless (-d $full_dir) {
         mkdir($full_dir);
    }

    # Find this contact's favorites
    my $user_res = $api->execute_method('flickr.favorites.getPublicList', {
                'user_id' => $usernsid,
                'extras' => 'owner_name',
    });
    
    # Make sure there's a response
    if (!$user_res->{success}) {
        warn "Favorite photos not found! $user_res->{error_message}";
    }
    
    # Parse the response and loop through photos
    my $userxpath = new XML::Parser::Lite::Tree::XPath($user_res->{tree});
    my @photos = $userxpath->select_nodes('/photos/photo');
    my $cntPhoto = 0;
    foreach (@photos) {
        my $id = $_->{attributes}->{id};
        my $title = $_->{attributes}->{title};
        my $server = $_->{attributes}->{server};
        my $secret = $_->{attributes}->{secret};
        my $owner = $_->{attributes}->{owner};
        my $ownername = $_->{attributes}->{ownername};
        my $img_src = "http://static.flickr.com/$server/$id\_$secret\_m.jpg";
        my $href = "http://www.flickr.com/photos/$owner/$id/";
        # Make sure there's a title
        if (!$title) {
            $title = $id;
        }
        
        # Write post file with image HTML
        my $file = "$full_dir$id.txt";

        unless (-e $file) {
             open FILE, ">$file" or warn "Can't open $file\n";
             print FILE <<"END_POST";
$title 

<a href="$href"><img src=\"$img_src\" /></a><br />
<div class="clsByline">photo by <a href="http://www.flickr.com/photos/$owner/">$ownername</a></div>
<div class="clsFavline">favorite of <a href="http://www.flickr.com/photos/$usernsid/favorites/">$username</a></div>
END_POST
             close FILE;
         }
         $cntPhoto++;
         last if ($cntPhoto == 5);
    }
}

This script creates a directory for every Flickr member in your contact list. Be aware that if you have 300 contacts, the script will create 300 directories on your server. In each directory, the script will write one text file for every favorite photo from that contact. The text file includes the title of the photo and a bit of HTML to display the photo. Each bit of HTML includes a link to the original photographer’s photostream, and to the complete list of favorites by the person who marked the photo as a favorite. Blosxom handles the work of organizing the mass of folders and text files into something coherent.

Running the Hack

To run the script, simply call it from a command line like this:

perl getFavorites.pl

The first time you run the script, it will generate text files for every favorite photo it can find. Flickr doesn’t provide dates for when a photo was marked a favorite, so there’s no way to backdate photos as marked as favorites on a certain date. This means that when Blosxom initially displays the list, it won’t be very meaningful. You’ll simply see potentially hundreds of photos under the same date heading. But as you run the script in the future, any new favorite that’s displayed will be noted with the time it was found as a favorite.

To take advantage of the script, you’ll want to run it on a regular schedule—every hour or two. You can do this with cron on Unix-based systems, or with the Windows Scheduler on Windows systems.

Visit the Blosxom script in a web browser to see the photos. With the script up and running regularly, you should see a frequently updated listing of any photos your contacts mark as favorites, as shown in Figure 4-5.

Friends’ favorites weblog
Figure 4-5. Friends’ favorites weblog

You’ll probably need to tweak the default templates in Blosxom to change the look and feel of your friends’ favorites page. Blosxom has a robust template system that lets you control every aspect of the display. In fact, Blosxom includes a built-in RSS template, so you’ll find an RSS feed of your friends’ favorites available at:

http://www.example.com/blosxom.cgi?flav=rss

You’ll also find a number of prebuilt templates you can choose from at the Blosxom web site.

As you watch your friend’s favorites, you’ll see a number of photos that you wouldn’t have otherwise, and they’ll have more meaning than a random sampling of photos across Flickr.

Hacking the Hack

Blosxom is a very hackable system, and there are dozens of plug-ins available that can add even more features to your automated weblog. For example, a plug-in called categorylist can print a list of all the folders under the Blosxom directory. With this hack, that means you could have a list of all your contacts running down the side of the page, and clicking one contact’s name would give you just that person’s favorites on one page, as shown in Figure 4-6.

Flickr friends’ favorites sidebar
Figure 4-6. Flickr friends’ favorites sidebar

Visit the Blosxom plug-in registry at http://www.blosxom.com/plugins/ for a look at some of the other available plug-ins.

Find Your Friends’ Favorite Tags

With some Perl and the Flickr API, you can see which tags are most popular among your contacts.

Tagging individual photos [Hack #10] on Flickr can help you organize your own photos. It also means Flickr can show interesting group patterns, like the Tags page shown in Figure 4-7.

Popular tags page
Figure 4-7. Popular tags page

With this page, at a glance you can see which tags are being used most often by all Flickr members. With a bit of scripting, you can narrow down this list and find out which tags are being used most by just your Flickr contacts. This list of contacts’ tags is a good way to visualize what your friends are interested in, and it’s a bit more personal than the overall tags page.

The Code

To run this hack, you’ll need a couple of nonstandard Perl modules. Make sure you have Flickr::API (http://search.cpan.org/~iamcal/Flickr-API/lib/Flickr/API.pm) for working with Flickr, and XML::Parser::Lite::Tree::Xpath (http://search.cpan.org/~iamcal/XML-Parser-Lite-Tree-XPath-0.02/XPath.pm) for sorting through the API responses.

With those two modules in place, copy the following code to a file called friends_tags_list.pl. Add your NSID and Flickr API key (both of which you can find at the Flickr API site, http://www.flickr.com/services/api/) to the code.

#!/usr/bin/perl
# friends_tags_list.pl
#
# Find out which tags your Flickr contacts are 
# using most often. This script contacts the 
# Flickr API, gets a list of your contacts, and 
# finds their most popular tags. The script then
# adds up the totals and lists the tags by the
# most popular. You can get a Flickr
# API key and look up your Flickr NSID at:
#
# http://www.flickr.com/services/api/
# 
# Usage: perl friends_tags_list.pl

use strict;
use Flickr::API;
use Flickr::API::Response;
use XML::Parser::Lite::Tree::XPath;

# Set your NSID
my $nsid = 'insert your NSID';
my $api_key = 'insert your Flickr API key';
my @tags;
my @sortTags;

# Get your list of contacts
my $api = new Flickr::API({'key' => $api_key});

my $response = $api->execute_method('flickr.contacts.getPublicList', {
                'user_id' => $nsid,
});

# Make sure there's a response
if (!$response->{success}) {
    die "Contact list not found! $response->{error_message}";
}

# Parse the response and loop through contacts
my $xpath = new XML::Parser::Lite::Tree::XPath($response->{tree});
my @contacts = $xpath->select_nodes('/contacts/contact');
foreach (@contacts) {
    my $usernsid = $_->{attributes}->{nsid};

    # Find this contact's tags
    my $user_res = $api->execute_method('flickr.tags.getListUserPopular', {
                'user_id' => $usernsid,
    });
    
    # Make sure there's a response
    if (!$user_res->{success}) {
        warn "Tags not found! $user_res->{error_message}";
    }
    
    # Parse the response and loop through photos
    my $userxpath = new XML::Parser::Lite::Tree::XPath($user_res->{tree});
    my @utags = $userxpath->select_nodes('/who/tags/tag');
    foreach (@utags) {
        my $match_index = 0;
        my $usertagcount = $_->{attributes}->{count};
        my $usertag = $_->{children}[0]->{content};
        
        # Make sure there's only one entry for each tag    
        for my $i ( 0 .. $#tags) {
            if ($tags[$i]->{tag} eq $usertag) {
                $match_index = $i;
            }
        }

        # If a tag already exists, add to the total
        # otherwise, add the new tag to the hash
        if ($match_index > 0) {
$tags[$match_index]->{tagcount} = $tags[$match_index]->{tagcount} + $usertagcount;
        } else {
            my $thisTag = {
                tag => $usertag,
                tagcount => $usertagcount,
            };
            push @tags, $thisTag;
        }
    }
}

# --- Begin Output -----

# Sort the hashes numerically to put popular tags first
@sortTags = sort { $b->{'tagcount'} <=> $a->{'tagcount'} } @tags;

# Print the tags in a simple text list
foreach (@sortTags) {
    my $tagcount = $_->{tagcount};
    my $tag = $_->{tag};
    print "$tag ($tagcount)\n";
}

The script fetches a list of contacts for the given NSID and uses the flickr.tags.getListUserPopular API method to find the 10 most popular tags for each contact. The script also adds the totals of the tags to see which tags are the most popular across all of your contacts.

Running the Hack

To run the script, call it from a command prompt, piping the output to a text file, like so:

perl friends_tags_list.pl > friends_tags.txt

Take a look at friends_tags.txt and you should see something like the following list of tags:

cameraphone (7676)
sanfrancisco (3345)
archive (2999)
7610 (2628)
nyc (1765)
sxsw (1608)
2001 (1512)
sxswi (1259)
travel (1248)
sf (1212)
moblog (1198)
europe (1109)
sxswinteractive (1018)
livemusic (986)
austintx (945)
oregon (733)
sharptm150 (636)
dog (606)
2005 (570)
s700i (559)
lifeblog (523)
nokia6630 (522)
seattle (479)
...

You’ll see your contacts’ most popular tags, along with the number of times each tag has been used by your contacts. You might learn some things about your friends that you didn’t know already, and you might find some tags that you can start using as well.

Hacking the Hack

If you want to display your contacts’ tags in a tag map with relative font sizes similar to the Tags page at Flickr, you can tweak this script a bit to output HTML instead of text. Replace everything after the # --- Begin Output --- comment with the following code:

# --- Begin Output -----

# Sort the hashes alphabetically
@sortTags = sort { $a->{'tag'} cmp $b->{'tag'} } @tags;

# Print the top of the page
print <<"END_HEADER";
<html>
<head>
    <style type="text/css">
        body { font-family:arial; width:760px;}
        h3 { font: normal 18px Arial, Helvetica, sans-serif; color: #FF0084; margin-bottom: 5px; }
    </style>
</head>
<body>
<h3>My Contacts' Tags</h3>
END_HEADER

# Print the tags as an HTML Tag Map with relative sizes
foreach (@sortTags) {
    my $fontsize;
    my $tagcount = $_->{tagcount};
    $tagcount /= 10;
    my $tag = $_->{tag};
    my $minfontsize = 9;
    my $fontsize = 9 + $tagcount;
    if ($fontsize >= 60) {
        $fontsize = 60;    
    }
    $fontsize = sprintf("%.0f", $fontsize); 
    print "<a href=\"http://www.flickr.com/photos/tags/$tag\"";
    print "style=\"font-size:$fontsize";
    print "px;font-weight:normal;text-decoration:none;";
    print "line-height:110%;\">$tag</a>&nbsp; \n";
}

# Print the bottom of the page
print <<"END_FOOTER";
</body>
</html>
END_FOOTER

This new code sorts the tags alphabetically instead of by the number of times each tag has been used by your contacts. The script then generates an HTML page, giving each tag a font size based on the number of times it has been used. Run the code in the same way, but send the output to an HTML file, like this:

perl friends_tag_list.pl > friends_tags.html

Open friends_tags.html in a browser, and you’ll see a list of tags like the one in Figure 4-8.

Viewing friends’ tags as a tag map
Figure 4-8. Viewing friends’ tags as a tag map

Each tag links to the public tag page for that particular tag, so you can see how Flickr members across the site have used the tag. With some more Flickr API work, you could probably find a way to link to your contacts’ photos for a given tag, but that’s another hack! In the meantime, you can get a quick glance at your friends’ tagging. And once you’ve mastered your friend’s tags, you might want to try your hand at other ways to make tag clouds [Hack #14] .

Map Your Contacts

By combining the Flickr API and the Google Maps API, you can see how your Flickr contacts are dispersed throughout the world.

As you connect with other Flickr members, you’ll notice that they can be anywhere in the world. You might admire a photographer who lives in a far-off country, and find another photographer who lives just down the street. Even though Flickr makes geography irrelevant, it’s still an important part of our lives. Learning where your Flickr contacts are based can tell you more about them and help you visualize just how geographically dispersed your virtual contacts really are.

As part of its member profiles, Flickr allows any member to include his location. The location can range from something very specific, like a city and state, to something general, like a country. Figure 4-9 shows my own Flickr profile, with the location “Corvallis, OR” listed directly under my web site.

Flickr profile page with a location set
Figure 4-9. Flickr profile page with a location set

To find out where a particular location is, you can go to Google Maps (http://maps.google.com) and type in the full address. Figure 4-10 shows a Google Map with a point for the O’Reilly headquarters in Sebastopol, CA.

An address plotted on a Google Map
Figure 4-10. An address plotted on a Google Map

Clicking on the point brings up an information window that includes more Google Maps options and the full address.

Google also provides a way for developers to create their own maps with their own points and information windows, called the Google Maps API (http://www.google.com/apis/maps/). Using this API, you can take a data source such as your list of Flickr contacts and build a map based on the location information they’ve provided.

Geocoding a Location

The Google Maps API needs information about points on a map in the form of coordinates, or listings of latitude and longitude that represent specific geographic points. Most of us don’t know our current longitude and latitude, and Flickr doesn’t expect us to. That’s why the simple city/state/country location information that people enter into Flickr needs to be geocoded before you can build your map—that is, a listing like Corvallis, OR, needs to be translated into its latitude and longitude pair: 44.564722, -123.260833.

Unfortunately, at the time of this writing, Google doesn’t explicitly offer a geocoding service. However, you can use Google Maps to get the longitude and latitude of a place name by tweaking a Google Maps URL. A Google Maps URL like the following will return a bit of JavaScript that’s intended to be used in some Google Maps applications:

http://maps.google.com/maps?q=insert location&output=js

The URL for our example location would be:

http://maps.google.com/maps?q=Corvallis,%20OR&output=js

Note that the space in the location has been encoded as %20 because spaces aren’t allowed in URLs. Inside this page, you’ll find some code and a bit of structured XML that looks like this:

...
    <locations>
        <location infoStyle="/maps?file=li&amp;hl=en" id="A">
        <point lat="44.564722" lng="-123.260833"/>
        <icon class="noicon"/>
        <info>
            <address>
                <line>Corvallis, OR</line>
            </address>
        <description></description>
        </info>
        </location>
    </locations>
...

As you can see, you can enter a location and get back coordinates. With some simple scripting, you can automate this process and geocode a large number of place names. With this hurdle out of the way, you can simply assemble your list of places and plot them on a map. However, you’ll need to make sure you have what you need before you begin.

What You Need

The most difficult part of assembling this hack is making sure you have the prerequisites in place. You might have to spend some time gathering the pieces you need, but once your environment is set, building the map takes only a few minutes.

As with any of the hacks in this book that use the Flickr API, you’ll need an API key. You’ll also need your own Flickr user ID (NSID) so you can request a list of your contacts from the Flickr API. You can find both of these at the Flickr API page (http://www.flickr.com/services/api/).

To build your own Google Map, you’ll need a Google Maps API key associated with a public domain. For example, if you’re going to view your map at a specific web address such as http://www.example.com, you’ll need to register that domain with Google when you request your key. And if you’re going to view your map at a subdirectory such as http://www.example.com/path/to/map/, you’ll need to specify the path as well when you register for a key. The requirement for a specific domain is a limitation of Google Maps, but it’s a free service and you’ll need to play by their rules. You can register your domain and get an associated key at http://www.google.com/apis/maps/.

Tip

If you’re not planning to make your Flickr contacts map public, you can associate your Google Maps API key with the URL http://localhost/. That way, you can access the map on a web server running on your local machine.

The Perl script for this hack that writes the JavaScript requires several Perl modules that aren’t usually preinstalled on most systems. Flickr::API (http://search.cpan.org/~iamcal/Flickr-API-0.07/lib/Flickr/API.pm) handles working with the Flickr API, and XML::Parser::Lite::Tree::XPath (http://search.cpan.org/~iamcal/XML-Parser-Lite-Tree-XPath-0.02/XPath.pm) helps parse the Flickr responses. You’ll also need URI::Escape (http://search.cpan.org/~gaas/URI-1.35/URI/Escape.pm) for encoding locations for use in a URL and LWP::Simple (http://search.cpan.org/~gaas/libwww-perl-5.803/lib/LWP/Simple.pm) for passing those locations to Google Maps for geocoding.

The Code

The following Perl script does the heavy lifting for the hack by finding the locations associated with your Flickr contacts, looking up the coordinates for those locations at Google, and writing the necessary JavaScript to display the points on a map. The script also assembles the HTML necessary to display your contacts’ buddy icons in the map itself.

Copy the following code to a file called gmap-contacts.pl, and be sure to include your own Flickr NSID and API key in the code:

#!/usr/bin/perl
# gmap-contacts.pl
# This script creates a JavaScript file that plots points 
# on a Google Map for a given Flickr member's contacts.
#
# You can get a Flickr API key, find your NSID, and read the 
# full documentation for the Flickr API at:
#
# http://www.flickr.com/services/api/
#
# You can get a Google Maps API key and read the full 
# documentation for the Google Maps API at:
#
# http://www.google.com/apis/maps/

use strict;
use Flickr::API;
use Flickr::API::Response;
use LWP::Simple;
use XML::Parser::Lite::Tree::XPath;
use URI::Escape;

# Set your NSID
my $nsid = 'insert your Flickr NSID';
my $api_key = 'insert your Flickr API key';

# Start the API
my $api = new Flickr::API({'key' => $api_key});

my @users;
my @locations;

# Start the JavaScript file, and set the initial center point
# for the Google Map
print <<JSHEADER;
function addMapPoints( ) {
    map.addControl(new GSmallMapControl( ));
    map.centerAndZoom(new GPoint(-96.66, 40.817), 13);
JSHEADER

# Get a list of contacts
my $response = $api->execute_method('flickr.contacts.getPublicList', {
                'user_id' => $nsid,
});

# Make sure there's a response
if (!$response->{success}) {
    die "Contact list not found! $response->{error_message}";
}

# Parse the response and loop through contacts
my $xpath = new XML::Parser::Lite::Tree::XPath($response->{tree});
my @contacts = $xpath->select_nodes('/contacts/contact');
foreach (@contacts) {
    my ($location,$lat,$lon,$lonlat,$near);
    my $usernsid = $_->{attributes}->{nsid};

    # Find this contact's location
    my $user_res = $api->execute_method('flickr.people.getInfo', {
                'user_id' => $usernsid,
    });

    # Make sure there's a response
    if (!$user_res->{success}) {
        warn "\nLocation for user $usernsid not found! $user_res->{error_message}";
    }

    # Grab location w/ regex
    if ($user_res->{_content} =~ m!<location>(.*?)</location>!gis) {
        $location = $1;
    } else {
        warn "\nNo location found for user $usernsid";
    }

    # Find Lat/Lon for location at Google
    if ($location ne "") {
        my $esc_location = uri_escape($location);
        my $url = "http://maps.google.com/maps?q=$esc_location&output=js";
        my $response = get($url);
        # Note if the location has a related longitude/latitude
        if ($response =~ m!<point lat="(.*?)" lng="(.*?)"/>!gis) {
           $lat = $1;
           $lon = $2;
           $lonlat = "$lon,$lat";
        # Otherwise, warn the user that the coordinates can't be found
        } else {
            warn "\nNo coordinates found for location $location";
        }
        # Normalize the name, if possible
        while ($response =~ m!<line>(.*?)</line>!gis) {
           $near = $1;
           $near =~ s! \d{5}!!gs; #Remove Zips
        }
    }

    # Save location to the locations array
    my $exists = grep $locations[$_]{lonlat} eq $lonlat, 0 .. $#locations;
    if (($exists eq 0) && ($lonlat ne "")) {
        my $thisLoc = {
            lonlat => $lonlat,
            lon => $lon,
            lat => $lat,
            near => $near,
        };
        push(@locations, $thisLoc);
    }

    # Save user to the user array
    my ($iconserver,$username,$realname,$photosurl);
    if ($user_res->{_content} =~ m!iconserver="(\d{1,2})"!s) {
        $iconserver = $1;
    }
    if ($user_res->{_content} =~ m!<username>(.*?)</username>!s) {
        $username = $1;
    }
    if ($user_res->{_content} =~ m!<realname>(.*?)</realname>!s) {
        $realname = $1;
    }
    if ($user_res->{_content} =~ m!<photosurl>(.*?)</photosurl>!s) {
        $photosurl = $1;
    }
    my $thisUser = {
        nsid => $usernsid,
        iconserver => $iconserver,
        username => $username,
        realname => $realname,
        photosurl => $photosurl,
        lonlat => $lonlat,
    };
    push(@users, $thisUser);
}

# Loop through the locations, adding users for that location
# to info window HTML
for my $i ( 0 .. $#locations) {
    my $lonlat = $locations[$i]{lonlat};
    my $near = $locations[$i]{near};

    # Start HTML for info window
    my $html = "<b>$near</b><div style=\"width:200px;\">";

    # Find users for this location
    for my $j ( 0 .. $#users) {
        my $user_lonlat = $users[$j]{lonlat};
        if ($lonlat eq $user_lonlat) {
            my $nsid = $users[$j]{nsid};
            my $username = $users[$j]{username};
            my $realname = $users[$j]{realname};
            $realname = s!'!\\'!g; #Escape apostrophes for JavaScript
            my $photosurl = $users[$j]{photosurl};
            my $iconserver = $users[$j]{iconserver};
            my $iconsrc;
            # If no buddy icon is found, use the standard default
            if ($iconserver eq 0) {
                $iconsrc = "http://www.flickr.com/images/buddyicon.jpg";
            } else {
                $iconsrc = "http://photos$iconserver";
                $iconsrc .= ".flickr.com/buddyicons/$nsid";
                $iconsrc .= ".jpg";
            }
            $html .= "<div style=\"float:left;padding:3px;\">";
            $html .= "<a href=\"$photosurl\" title=\"$realname\">";
            $html .= "<img src=\"$iconsrc\" width=\"48\" height=\"48\"";
            $html .= " border=\"0\" />";
            $html .= "</a>";
            $html .= "</div>";
        }
    }
    $html .= "</div>";

    # Print out the line of JavaScript for this point
    if ($lonlat ne "") {
        print "\tcreateMarker(new GPoint($lonlat),'$html');\n";
    }
}

# Print the last character to close the JavaScript function
print "}\n";

As you can see, the code is fairly complex, but it assembles everything you need for your map quickly. Because many Flickr users can be from the same location, the script stores both locations and Flickr member information in the arrays @locations and @users. At the end of the script, the last loop runs through the locations, building the HTML necessary to display buddy icons from members in each location.

Running the Hack

Once the script is ready to go, run it from the command line and specify a descriptive name for the JavaScript file, such as addMapPoints.js, like this:

gmap-contacts.pl > addMapPoints.js

The script will build the JavaScript file and note any locations it couldn’t translate into coordinates. Next, you’ll need to put together the standard HTML file that will hold the map. Create a text file called contacts-map.html and add the following HTML, making sure to include your Google Maps API key and the JavaScript file you just created in the header:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Flickr Contacts, Mapped</title>
    <script src="addMapPoints.js" type="text/javascript"></script>
    <script src="http://maps.google.com/maps?file=api&v=1&key=insert key" 
       type="text/javascript"></script>
    <style type="text/css">
    body {
        font-family:arial,helvetica,sans-serif;
    }
    </style>
</head>

<body onload="addMapPoints( );">
<h1>Flickr Contacts, Mapped</h1>
<div id="map" style="width:800px;height:600px;border:solid #C3CEAE 1px;);"></div>
    <script type="text/javascript">
    //<![CDATA[
    // Create a "tiny" green marker icon
    var icon = new GIcon( );
    icon.image = "http://labs.google.com/ridefinder/images/mm_20_green.png";
    icon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
    icon.iconSize = new GSize(12, 20);
    icon.shadowSize = new GSize(22, 20);
    icon.iconAnchor = new GPoint(6, 20);
    icon.infoWindowAnchor = new GPoint(5, 1);

    var map = new GMap(document.getElementById("map"));
    
    // Creates a tiny green marker at the given point
    function createMarker(point,html) {
        var marker = new GMarker(point, icon);
        map.addOverlay(marker);
        GEvent.addListener(marker, "click", function( ) {
            marker.openInfoWindowHtml(html);
        });
    }

    //]]>
    </script>
</body>
</html>

Note in the <body> tag that the page will run the addMapPoints() function once the page has loaded. This function—generated by gmaps-contacts.pl and stored in addMapPoints.js—initializes the Google Map and adds all of the points and contact information.

Also note that, in this example, the initial center of the map is set to -96.66, 40.817—roughly the middle of the United States. If most of your Flickr contacts are in the UK, you might want to change this value so that you don’t have to drag the Google Map to a new location to see your contacts’ locations. You can change this value by editing the third line of addMapPoints.js or by changing the coordinates in the gmaps-contacts.pl script that writes that file.

Once both files are in place on your server, bring up contacts-map.html in a web browser, and you should see a map like the one shown in Figure 4-11.

Flickr contacts on a Google Map
Figure 4-11. Flickr contacts on a Google Map

Click any of the green points on the map, and you’ll see an info window containing the Flickr buddy icons that represent your contacts from that location. You can hover over a buddy icon to see the member’s real name, or click the icon to view that member’s photostream at Flickr.

And, of course, you can zoom or click and drag the map, just as you can with any Google Map. You can even drag the map to other countries and see if you have contacts there. Most importantly, you’ll see how your Flickr contacts are dispersed, which might help you realize that Flickr really is a way to share photos with people throughout the world.

Find Flickrites in Your Mac Address Book

Find anyone in your Address Book that has a Flickr photostream.

If you use the Mac Address Book to organize your contacts, you probably have dozens of entries for friends, family members, coworkers, and acquaintances. With a large list, the odds are good that at least a few of them have Flickr memberships. You might be connected to your closest contacts on Flickr already, but finding out who else in your list of personal contacts is on Flickr can give you more people to connect with.

You could copy and paste each of your contacts’ email addresses into the People Search page at Flickr (http://www.flickr.com/find.gne), but some quick scripting can save you time. Mac OS X is a highly scriptable environment, and this hack shows how you can use AppleScript and the underlying Unix tool called curl to find people in your Address Book that also have Flickr photostreams.

The Code

Open the AppleScript Script Editor in /Applications/AppleScript and enter the following code. Be sure to enter your own Flickr API key, and save the script with a memorable name, such as Find Flickerites.

(*
Find Flickr Members in Address Book

The script loops through people in your Address Book, checking Flickr
to see if they're members. If a photostream is found, you
have the option to view it in your default browser. You can get a
Flickr API key at http://www.flickr.com/services/api/.

by Paul Bausch
*)

-- set your Flickr API key

set apiKey to "insert your API key"

-- set some variables for the curl command

set userAgent to "Mozilla/5.0 (Macintosh; U; PPC Mac OSX; en-us)"
set curlCommand to "curl -i -b -A -L \"" & userAgent & "\" "

-- open Address Book and loop through people

tell application "Address Book"
    repeat with thisPerson in the people
        set thisName to name of thisPerson
        repeat with thisAddress in email of thisPerson
           set thisEmail to value of thisAddress
            
           -- build the URL that will search for the Wish List
            
           set baseURL to "http://www.flickr.com/services/rest/?method="
           set thisURL to baseURL & "flickr.people.findByEmail\\&api_key=" & apiKey
           set thisURL to thisURL & "\\&find_email=" & thisEmail
           -- use curl to fetch the search page
            
           set thisResponse to do shell script curlCommand & thisURL
            
            -- if the Wish List URL is found in headers, prompt user for action
           if thisResponse contains "nsid=" then
                set theAction to display dialog thisName & " has a Flickr photostream." buttons {"View", "Ignore"}
               if button returned of theAction is "View" then
                    
               -- build Flickr URL based on returned NSID, and bring it up
                    
               set beginID to (offset of "nsid=" in thisResponse) + 6
               set thisNSID to get text beginID thru (beginID + 15) of thisResponse
                    
               set beginID to 1
               set endID to (offset of "\"" in thisNSID) - 1
               set thisNSID to get text beginID thru endID of thisNSID
                    
               tell application "Safari"
                   activate
                   open location "http://www.flickr.com/photos/" & thisNSID
               end tell
               end if
            end if
        end repeat
    end repeat
end tell

The top of the script sets some variables for curl that will be used later.

Tip

If you’d like to learn more about curl and what the settings in the script mean, open a Terminal window (found in Applications/Utilities/) and type man curl. You’ll get the complete documentation that explains what all of the command switches do, and a list of all of the options available.

With the variables set, the script loops through all of the entries in the Address Book, looking for email addresses. Each individual email address found is used to build a Flickr API request URL with the flickr.people.findByEmail method. Once the URL is set, the do shell script command uses curl to fetch the response, and the contains command looks for a Flickr NSID associated with the email address.

If a Flickr NSID is found, the script uses display dialog to bring up a window with View and Ignore options. If you click View, the script will open the Safari browser, which will take you to that Flickr member’s photostream. From there, you can view that person’s photos, take a look at her profile details, or add her as a contact.

Running the Hack

You can run the script by clicking the Run button in the Script Editor. As you find people in your Address Book who are also on Flickr, you’ll have the option to view their photostreams, as shown in Figure 4-12.

AppleScript prompt to view a contact’s photostream
Figure 4-12. AppleScript prompt to view a contact’s photostream

To keep this script one click away from your Address Book, move the script to the /Library/Scripts/Address Book Scripts folder. This way, you can access the script from your Address Book’s Scripts menu. Run the script at any time by selecting Script Menu → Find Flickerites from the Address Book application’s menu bar.

Finding fellow Flickr users via your Address Book may help you learn more about some people you know casually and build your own list of contacts at Flickr.

Surf Your Contacts

Find out what your contacts are up to outside of Flickr.

Most of your Flickr contacts really do have lives outside of Flickr, and you can often find a link to a friend’s other life on his Flickr profile page. When you create a Flickr profile, you have the option to enter your web site URL and web site name, which will show up in your profile just above your location. Most Flickr members use this space to point to their home pages or weblogs.

If you don’t already regularly visit your Flickr contacts’ external sites, you might learn something more about your acquaintances and the photographers you admire by taking a look. If you have just a handful of contacts, you could visit each of your contacts’ profile pages one by one and make a list of URLs you’d like to visit; but if you have more than a dozen contacts, a bit of PHP scripting can help you put together a master surfing list in a few minutes.

This hack compiles a list of your contacts, assembles their profile page URLs, and visits each one, looking for a web site. You’ll need a free Flickr API key and your Flickr NSID, both of which can be found at the Flickr API documentation page (http://www.flickr.com/services/api/). This hack also relies on the simple XML tools built into PHP 5, which make working with Flickr API responses a snap.

The Code

Copy the following code to a file called flickr_surf.php, and be sure to put in your own Flickr API key and Flickr NSID:

<?php
// flickr_surf.php
// Finds web sites of Flickr contacts for given
// Flickr NSIDs. Use it to gather your Flickr 
// contacts' web sites onto one page for easy 
// surfing. 
//
// You can get an API key and read the full documentation
// for the Flickr API at http://www.flickr.com/services/api/

// Set your Flickr API key and your user NSID
$api_key = "insert your Flickr API key";
$my_nsid = "insert your Flickr NSID";

// This is a greedy script requiring lots of time
set_time_limit(0);

// Construct a Flickr query to grab your contacts
$req_url = "http://www.flickr.com/services/rest/";
$req_url .= "?method=flickr.contacts.getPublicList";
$req_url .= "&api_key=$api_key";
$req_url .= "&user_id=$my_nsid";

// Make the request
$flickr_contacts = file_get_contents($req_url);

// Parse the XML
$fc_xml = simplexml_load_string($flickr_contacts);
?>
<html>
<head>
    <style type="text/css">
    BODY {
        font-family:verdana,arial;
        font-size:10pt;
        line-height:130%;
    }
    </style>
</head>
<body>
<h2>Surf Flickr Friends</h2>
<ul>
<?php
// Loop through the contacts
foreach ($fc_xml->xpath('//contact') as $contact) {
    $nsid = $contact['nsid'];
    $username = $contact['username'];

    // Construct a Flickr query to grab contact info
    $req_url = "http://www.flickr.com/services/rest/";
    $req_url .= "?method=flickr.people.getInfo";
    $req_url .= "&api_key=$api_key";
    $req_url .= "&user_id=$nsid";

    // Make the request
    $contact_info = file_get_contents($req_url);

    // Parse the XML
    $ci_xml = simplexml_load_string($contact_info);

    // Get contact info
    $iconserver = $ci_xml->person[0]['iconserver'];
    $realname = $ci_xml->person[0]->realname;
    $profile_url = $ci_xml->person[0]->profileurl;

    // See if a web site exists on the member's profile page
    // with screen-scraping
    $profile_html = file_get_contents($profile_url);
    $regex = '!<p style="font-size: 14px;">.*?<a ';
    $regex .= 'href="(.*?)"><strong>(.*?)</strong></a>!is';
    if (preg_match($regex,$profile_html, $matches)) {
        $contact_siteurl = $matches[1];
        $contact_sitename = $matches[2];

        // If a web site is found, print out the web site URL
        // and contact info
        print "<li style=\"margin-bottom:10px;\"><a href=\"$contact_siteurl\">";
        print "$contact_sitename</a><br />";
        print "<span style=\"font-size:8pt;color:#666;\">";
        print "by <a href=\"$profile_url\">$username</a> /";
        print " $realname</span></li>\n";
    }
    $matches = "";

    // Sleep for 3 seconds to give the Flickr
    // servers a break
    sleep(3);
}
?>
</ul>
</body>
</html>

You’ll notice that the script contacts the Flickr API to find your list of contacts, and then contacts the API again for each contact to find the user’s real name and profile URL. Flickr doesn’t make site information available via the API, so the script relies on screen-scraping the profile pages to find that data. Screen-scraping involves downloading the HTML of the page and picking through it with regular expressions to cherry-pick the required information. It’s a notoriously brittle process, and if Flickr changes the HTML of the profile page, the script won’t be able to find the information you want. Be aware that you may need to tweak the regular expression set as $regex to find the web site name and URL.

Note also that the script is fairly request-intensive. For every person in your contact list, this script will make two requests of the Flickr servers. To give the Flickr servers a bit of a break, the script then rests for three seconds by calling sleep(3). This will give the Flickr servers a small rest between each contact, but it also means you’ll need to be patient while the script is compiling your list.

Running the Hack

To run the code, upload the script to your web server and browse to the page. The URL should look something like this:

http://www.example.com/flickr_surf.php

Because of the numerous HTTP requests and the built-in delay, expect your script to take several minutes to execute. And, of course, the more contacts you have, the more time the script will need to compile the list. When it’s finished, you should see a list of web sites from your Flickr contacts like the one in Figure 4-13.

A list of the author’s Flickr contacts’ web sites
Figure 4-13. A list of the author’s Flickr contacts’ web sites

If you want to visit the page over and over again, you’ll want to save the HTML the script generated rather than rebuilding the list each time you visit the page. To save the HTML, choose File → Save As... (or File → Save Page As...) from your browser’s menu. Your contacts’ web sites shouldn’t change too often, and when you add or remove contacts you can visit flickr_surf.php again to recompile the list and bring it up to date.

Get Flickr Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.