« November 2004 | Main | January 2005 »

December 30, 2004

Help Tsunami Victims

The death toll is now over 125,000. I've been looking around for good information. Probably the best link I have right now is one from Scoble. I'll update this entry with more info and links as I find good ones. Post if you have links to information or donation sites. You can also donate to the American Red Cross through Amazon.

*edit*

Ran across this story on BoingBoing.

December 29, 2004

A Sad Day

Jerry Orbach, probably best known as Detective Lennie Briscoe on TV's Law & Order, passed away late Tuesday at New York's Memorial Sloan-Kettering Cancer Center where he was being treated for prostate cancer. He was 69. Fans of L&O will remember that earlier this year he was replaced by Dennis Farina, who has done an admirable job, but I think we're all still trying to figure out where he got all his money.

Orbach was on L&O for 12 seasons as the wise-cracking detective, but he was also well-known for his work on the big screen and on Broadway. L&O really made him a household name, however. Of the star-studded cast that has come through that show, he was my favorite, by far.

Jerry, you'll be missed.

December 20, 2004

Weather Podcasting

A few days ago, Ben Hammersley threw out an idea for weather podcasting. I had made a previous contribution to some of his work and, as Ben apparently knows, flattery will get you everywhere with an engineer. So I took up his latest challenge. I was able to get the bulk of the script done in just a few hours one evening. There is still some work that could be done to improve it, but it's stable enough that I wanted to go ahead and share it now.

How To Use
The URL accepts 3 parameters, a locid, a dayf and a unit. The locid is a weather.com location identifier, such as a zip code or city code (e.g., 92126 for San Diego, CA, or ITXX0067 for Rome, Italy). The dayf parameter indicates how many days of weather forecast you wish to receive (one RSS entry for each day, up to a maximum of 10, defaults to 2). The unit parameter is optional and can be either m (metric) or s (standard), and defaults to standard.

Anyway, if you want to give it a spin, the first thing you'll need to do is go to weather.com and get an id for the location in which you are interested. This id can be a zip code (if you're in the states) or some other kind of code which you can extract from the URL. Look up the weather for a location and you can get the location id from the URL. For example, if you look up weather in Rome, Italy, the URL will look like this:

http://www.weather.com/outlook/travel/local/ITXX0067?from=search_city

In this example, ITXX0067 is the location id. Using that, here is a sample URL you could use for my weather podcast:

http://www.jorgev.com/cgi-bin/weather.cgi?locid=ITXX0067&dayf=5&unit=m

This will create a podcast for a 5-day weather forecast for Rome, Italy in metric units. Or...

http://www.jorgev.com/cgi-bin/weather.cgi?locid=92126

This will create a podcast for a 2-day weather forecast for San Diego, CA, reported in standard units.

Note that the podcast currently creates (dayf + 1) RSS items...one with current conditions and the remainder for the number of forecast days requested.

Keep in mind this is still a work in progress. Maybe I should just call it a perpetual beta, like GMail or ICQ. Give it a try, feel free to leave comments or suggestions.

A brief explanation of how the script works, it's actually quite simple: I first call into weather.com's XML Data Feed. Then I use XPath to extract the data in which I am interested. I format this information into a text file, which I then pass onto the text2wave utility which performs the text-to-speech conversion. Finally, since wav files are so huge, I convert it to MP3 using the LAME encoder. Piece of cake, eh?

Here's the source code to my script (some items removed, such as key info):

#!/usr/bin/perl -w
# 2004-12-13 Created, Jorge Velázquez
# 2004-12-22 added dayf parameter
# 2004-12-28 replaced some text with phrases more suitable for text-to-speech conversion
# 2005-03-29 added contact info to the feed, fixed date to conform to RFC822

use strict;
use CGI qw(:standard);
use XML::RSS;
use XML::XPath;
use LWP::Simple;
use File::Temp;
use File::Basename;
use POSIX qw(strftime);

# partner and key information
my $par = 'partnerid';
my $key = 'key';

# get parameters
my $cgi = CGI::new();
my $locid = $cgi->param('locid');
my $unit = $cgi->param('unit');
if (not $unit) {
$unit = 's';
}
my $dayf = $cgi->param('dayf');
if (not $dayf) {
$dayf = 2;
}
if ($dayf > 10) {
$dayf = 10;
}

# wind direction hash
my %direction = (
'N' => 'north',
'NNE' => 'north northeast',
'NE' => 'northeast',
'ENE' => 'east northeast',
'E' => 'east',
'ESE' => 'east southeast',
'SE' => 'southeast',
'SSE' => 'south southeast',
'S' => 'south',
'SSW' => 'south southwest',
'SW' => 'southwest',
'WSW' => 'west southwest',
'W' => 'west',
'WNW' => 'west northwest',
'NW' => 'northwest',
'NNW' => 'north northwest'
);

# query weather info, current conditions with 2 day forecast
my $xml = get("http://xoap.weather.com/weather/local/$locid?cc=*&dayf=$dayf&prod=xoap&par=$par&key=$key&unit=$unit");

#load it into XPath object
my $xp = XML::XPath->new($xml);

# extract unit information from feed
my $ut = $xp->findvalue('/weather/head/ut');
my $ud = $xp->findvalue('/weather/head/ud');
my $us = $xp->findvalue('/weather/head/us');

# format current date/time according to RFC822
my $pubDate = strftime("%a, %d %b %Y %T PST", localtime());

# create rss 2.0 feed
my $rss = new XML::RSS(version => '2.0');

# channel information
$rss->channel(title => 'Weather Podcast',
link => 'http://www.weather.com/',
description => 'Local weather podcasting feed',
pubDate => $pubDate,
language => 'en-us',
webMaster => 'jorgev@jorgev.com (Jorge Velazquez)',
ttl => '720');

# get current weather conditions
my $dnam = $xp->findvalue('/weather/loc/dnam');
my $ccobst = $xp->findvalue('/weather/cc/obst');
my $cclsup = $xp->findvalue('/weather/cc/lsup');
my $cctemp = $xp->findvalue('/weather/cc/tmp');
my $ccwinds = $xp->findvalue('/weather/cc/wind/s');
my $ccwindt = $xp->findvalue('/weather/cc/wind/t');
my $cct = $xp->findvalue('/weather/cc/t');
my $ccbarr = $xp->findvalue('/weather/cc/bar/r');
my $ccbard = $xp->findvalue('/weather/cc/bar/d');
my $cchmid = $xp->findvalue('/weather/cc/hmid');

# convert the units to words
my $utword = $ut eq 'F' ? 'fahrenheit' : 'celsius';
my $udword = $ud eq 'km' ? 'kilometers' : 'miles';
my $usword = $us eq 'mph' ? 'miles per hour' : 'kilometers per hour';

# build the text file for converting to speech
my $text = "Current conditions for $dnam.";
my $desc = $text;
if ($cct ne 'N/A') {
$text .= " $cct.";
}
if ($cctemp ne 'N/A') {
$text .= " The temperature is $cctemp $utword.";
}
if ($ccwinds ne 'N/A') {
$text .= " The wind is from the $direction{$ccwindt} at $ccwinds $usword.";
}
if ($cchmid ne 'N/A') {
$text .= " The humidity is $cchmid percent.";
}

# write the file and get the stats
my $fileinfo = writemp3($text);

# add rss item for current weather
$rss->add_item(title => "Current weather conditions for $ccobst",
link => "http://www.weather.com/weather/local/$locid",
description => $desc,
pubDate => $pubDate,
enclosure => {
url => $fileinfo->{'url'},
length => $fileinfo->{'length'},
type => 'audio/mpeg'
});

# get forecast timestamp
my $lsup = $xp->findvalue('/weather/dayf/lsup');

# iterate through forecast days
my $nodeset = $xp->find('/weather/dayf/day');
foreach my $node ($nodeset->get_nodelist) {
# get the items of interest to our script
my $t = $node->findvalue('@t');
my $dt = $node->findvalue('@dt');
my $hi = $node->findvalue('hi');
my $low = $node->findvalue('low');
my $d = $node->findvalue('part[@p="d"]/t');
my $n = $node->findvalue('part[@p="n"]/t');
my $dwinds = $node->findvalue('part[@p="d"]/wind/s');
my $dwindt = $node->findvalue('part[@p="d"]/wind/t');
my $nwinds = $node->findvalue('part[@p="n"]/wind/s');
my $nwindt = $node->findvalue('part[@p="n"]/wind/t');
my $dhmid = $node->findvalue('part[@p="d"]/hmid');
my $nhmid = $node->findvalue('part[@p="n"]/hmid');

# build the text file for converting to speech
$text = "Weather forecast for $t, $dt in $dnam.";
$desc = $text;
if ($hi ne 'N/A') {
$text .= " The high is $hi $utword.";
}
if ($low ne 'N/A') {
$text .= " The low is $low $utword.";
}
$text .= " Daytime";
if ($d ne 'N/A') {
$d =~ s/T-Showers/thundershowers/i;
$text .= ", $d";
} else {
$text .= " conditions unavailable";
}
if ($dwinds ne 'N/A') {
$text .= ", wind from the $direction{$dwindt} at $dwinds $usword";
}
if ($dhmid ne 'N/A') {
$text .= ", humidity $dhmid percent";
}
$text .= ".";
$text .= " Nighttime";
if ($n ne 'N/A') {
$n =~ s/T-Showers/thundershowers/i;
$text .= ", $n";
} else {
$text .= " conditions unavailable";
}
if ($nwinds ne 'N/A') {
$text .= ", wind from the $direction{$nwindt} at $nwinds $usword";
}
if ($nhmid ne 'N/A') {
$text .= ", humidity $nhmid percent";
}
$text .= ".";

# write the file and get the stats
$fileinfo = writemp3($text);

# add this forecast
$rss->add_item(title => "Weather forecast for $t, $dt at $dnam",
link => "http://www.weather.com/weather/local/$locid",
description => $desc,
pubDate => $pubDate,
enclosure => {
url => $fileinfo->{'url'},
length => $fileinfo->{'length'},
type => 'audio/mpeg'
});
}

print header('application/rss+xml');
print $rss->as_string;

sub writemp3 {
# write the text file
my $fhtxt = new File::Temp(suffix => '.txt');
print $fhtxt @_;
close $fhtxt;

# write the wav file
my $fhwav = new File::Temp(suffix => '.wav');
system "text2wave", "-o", $fhwav->filename, $fhtxt->filename;

# convert it to mp3
my $fhmp3 = new File::Temp(unlink => 0, dir => '../mp3', suffix => '.mp3');
system "lame", "-h", $fhwav->filename, $fhmp3->filename;

# make it world-readable
chmod 0644, $fhmp3->filename;

# caller needs this information
my $basename = basename $fhmp3->filename;
my %fi = ('url' => "http://www.jorgev.com/mp3/$basename", 'length' => (stat $fhmp3->filename)[7]);

return \%fi;
}

December 16, 2004

Anyone Into Podcasting?

I've been playing around with a podcasting idea. I don't own an iPod and probably never will, but I think the idea on which I am currently working is cool. If you own an iPod and would like to help me test my work, I'd be extremely grateful. I think you'll like it. To be fair, the idea isn't mine, I picked it up from another source, which I'll reveal once I get my code working to my satisfaction.

On a side note, it's amazing what you can do with Perl in 3-4 hours. That's about how long it took me to get a prototype of this idea working. And once you see what it does, I think you'll be impressed.

December 12, 2004

Holiday Card

My long-time friend Gus and his wife Linda have three wonderful kids. This year, Linda asked me to help her with an idea she had for holiday cards. The card folds into four panels with a holiday message in the first panel and a picture of each of the kids in the remaining panels. I spent a couple hours taking pictures of the kids and a couple hours in Photoshop putting it all together. I was pretty happy with the final result so I thought I'd share it here, with her permission. Click on the image to see a larger version.


alva.jpg

December 9, 2004

Full-Text Feeds

From looking at my web stats, it seems that most of my hits are through my RSS feeds. I converted them to full-text, which is a good idea in general since many people read them offline, but also will eliminate the need to click through to see the full entry. Of course, I tend to post a lot of pictures, so I'll try to be considerate and keep the image size small, or create an extended entry for image-heavy articles.

Also, I'd recommend you use my RSS 1.0 feed over the others, since my blog software automatically puts the body text into a nicely formatted CDATA section and preserves all the links and other HTML.

December 6, 2004

World of Warcraft

I'm playing World of Warcraft these days and having an absolute blast. It has everything I loved about EQ, and without everything I hated about it. It is also a lot more friendly to the casual player. The game actually rewards you for logging off periodically: you get an exp boost which increases the longer you are logged off. The game is really designed with the idea of "let's make this game fun", whereas EQ2 is "let's make as much money as we can by finding out what the tolerance threshold of players is and staying just within that". Go here for a good read, starting on the third paragraph. Not only that, you can accomplish many quests on your own. And they all scale. Which means, if you do a quest appropriate for your level you will see a *noticeable* increase in your experience when you complete it. And by "noticeable", I mean 10-20% of your level, regardless if you're level 1 or level 30. You'll actually spend most of your time questing, and the quests are all intelligently thought out and designed...and I have yet to find a broken one, where in EQ2 it only took me 2 quests to find a broken one. Also, you can only complete quests once, so you won't find people farming quest items or competing for mobs that are required to complete the quest. Quest experience reward also varies, so if you do a "green" quest you'll get minimal exp, but if you do the same quest when it is "red", you'll get much more exp for it, again encouraging people to do quests appropriate for their level.

Right now I've got a group of friends playing with me on the Windrunner server in the Pacific realm. I've got a level 19 dwarf priest. This weekend we entered our first instanced dungeon, on our way to complete a quest. We're making pretty good progess on it, although two failed attempts to get to the boss. Here is a screenshot from our dungeon crawl on Saturday. Check out the monster with a circular saw as its right hand, lol. Too cool.

wow.jpg

Here's another screenshot.

wow2.jpg