Weather-Com-0.5.3/0000755000000000000000000000000010721352300013561 5ustar rootroot00000000000000Weather-Com-0.5.3/t/0000755000000000000000000000000010721352300014024 5ustar rootroot00000000000000Weather-Com-0.5.3/t/Wind.t0000644000175000017500000000607710232233124015775 0ustar thomasthomas00000000000000##################################################################### # # Test suite for 'Weather::Com::Wind' # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/Wind.t' # ##################################################################### # # initialization # no warnings; use Test::More tests => 25; BEGIN { use_ok('Weather::Com::Wind'); } ##################################################################### # # Testing object instantiation (do we use the right class)? # my $wind = Weather::Com::Wind->new(); isa_ok( $wind, "Weather::Com::Wind", 'Is a Weatcher::Com::Wind object' ); isa_ok( $wind, "Weather::Com::Object", 'Is a Weatcher::Com::Object object' ); # # Test negative init when instantiated without arguments # is( $wind->speed(), -1, 'Test negative initialization of speed.' ); is( $wind->maximum_gust(), -1, 'Test negative initialization of speed.' ); is( $wind->direction_degrees(), -1, 'Test negative initialization of wind direction.' ); is( $wind->direction_short(), 'N/A', 'Test negative initialization of wind direction.' ); is( $wind->direction_long(), "Not Available", 'Test negative initialization of wind direction.' ); # # Test negative init when instantiated for German # $wind = Weather::Com::Wind->new( lang => 'de' ); is( $wind->direction_short(), 'nicht verfügbar', 'Test negative initialization of wind direction.' ); is( $wind->direction_long(), "nicht verfügbar", 'Test negative initialization of wind direction.' ); # # Test positive update # $wind->update( 'gust' => '50', 'd' => '104', 's' => '29', 't' => 'ESE' ); is( $wind->speed(), 29, 'Test speed update.' ); is( $wind->maximum_gust(), 50, 'Test max gust update.' ); is( $wind->direction_degrees(), 104, 'Test wind direction update.' ); is( $wind->direction_short(), 'OSO', 'Test wind direction update.' ); is( $wind->direction_long(), "Ost Südost", 'Test wind direction update.' ); # # Test positive update # $wind->update( 'gust' => '50', 'd' => '104', 's' => undef, 't' => 'ESE' ); is( $wind->speed(), -1, 'Test negative update of speed.' ); is( $wind->maximum_gust(), -1, 'Test negative update of speed.' ); is( $wind->direction_degrees(), -1, 'Test negative update of wind direction.' ); is( $wind->direction_short(), 'nicht verfügbar', 'Test negative update of wind direction.' ); is( $wind->direction_long(), "nicht verfügbar", 'Test negative update of wind direction.' ); # # Test calm update # $wind->update( 'gust' => '50', 'd' => '104', 's' => 'calm', 't' => 'ESE' ); is( $wind->speed(), 0, 'Test calm update of speed.' ); is( $wind->maximum_gust(), 0, 'Test calm update of speed.' ); is( $wind->direction_degrees(), -1, 'Test calm update of wind direction.' ); is( $wind->direction_short(), 'nicht verfügbar', 'Test calm update of wind direction.' ); is( $wind->direction_long(), "nicht verfügbar", 'Test calm update of wind direction.' ); Weather-Com-0.5.3/t/Base.t0000644000175000017500000000402210644507215015746 0ustar thomasthomas00000000000000##################################################################### # # Test suite for 'Weather::Com::Base' # # Testing network connection (without proxy). # Functional tests with 'Test::MockObject'. These could only be run # if Test::MockObject is installed. # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/Base.t' # ##################################################################### # # initialization # no warnings; use Test::More tests => 6; require 't/TestData.pm'; BEGIN { use_ok('Weather::Com::Base'); } ##################################################################### # # Testing object instantiation (do we use the right class)? # my %weatherargs = ( 'debug' => 0, 'language' => 'en', ); my $wc = Weather::Com::Base->new(%weatherargs); isa_ok( $wc, "Weather::Com::Base", 'Is a Weatcher::Com::Base object' ); # # Test static methods of Weather::Com::Base # is( &Weather::Com::Base::celsius2fahrenheit(20), 68, 'Celsius to Fahrenheit conversion' ); is( &Weather::Com::Base::fahrenheit2celsius(68), 20, 'Fahrenheit to Celsius conversion' ); # remove all old cache files unlink <*.dat>; # # Test functionality if Test::MockObject is installed. # SKIP: { eval { require Test::MockObject }; skip "Test::MockObject not installed", 2 if $@; my $mock = Test::MockObject->new(); $mock->fake_module( 'LWP::UserAgent' => ( 'request' => sub { return HTTP::Response->new() }, ) ); $mock->fake_module( 'HTTP::Response' => ( 'is_success' => sub { return 1 }, 'content' => sub { return $NY_HTML }, ) ); is_deeply( $wc->search('Heidelberg'), $NY_Hash, 'Locations search with faked UserAgent' ); $mock->fake_module( 'HTTP::Response' => ( 'is_success' => sub { return 1 }, 'content' => sub { return $NYCP_HTML }, ) ); is_deeply( $wc->get_weather('USNY0998'), $NYCP_Hash, 'Look for weather data for "New York/Central Park, NY"' ); } Weather-Com-0.5.3/t/Simple.t0000644000175000017500000000234110232170442016316 0ustar thomasthomas00000000000000##################################################################### # # Test suite for 'Weather::Com::Simple' # # Functional tests with 'Test::MockObject'. These could only be run # if Test::MockObject is installed. # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/Simple.t' # ##################################################################### # # initialization # no warnings; use Data::Dumper; use Test::More tests => 2; require 't/TestData.pm'; BEGIN { use_ok('Weather::Com::Simple'); } ##################################################################### # # Test functionality if Test::MockObject is installed. # SKIP: { eval { require Test::MockObject; }; skip( "Test::MockObject not installed", 1 ) if ($@); my %weatherargs = ( 'place' => 'New York/Central Park, NY', 'debug' => 0, 'language' => 'en', ); my $wc = Weather::Com::Simple->new(%weatherargs); # define mock object my $mock = Test::MockObject->new(); $mock->fake_module( 'Weather::Com::Cached' => ( '_cache_time' => sub { return 1110000000 } ) ); is_deeply( $wc->get_weather, $simpleWeather, 'Checking simple weather format.' ); } Weather-Com-0.5.3/t/OOInterface.t0000644000175000017500000001452510644505502017240 0ustar thomasthomas00000000000000##################################################################### # # Test suite for the OO Interface # # Functional tests with 'Test::MockObject'. These could only be run # if Test::MockObject is installed. # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/OOInterface.t' # ##################################################################### # # initialization # no warnings; use Test::More tests => 62; require 't/TestData.pm'; BEGIN { use_ok('Weather::Com::Finder'); } ##################################################################### # # Testing object instantiation (do we use the right class)? # my %weatherargs = ( 'debug' => 0, 'language' => 'en', ); my $wc = Weather::Com::Finder->new(%weatherargs); isa_ok( $wc, "Weather::Com::Finder", 'Test class.' ); # # Test functionality if Test::MockObject is installed. # SKIP: { eval { require Test::MockObject; }; skip "Test::MockObject not installed", 60 if $@; # define mock object, set cache time to be able to # access the cache my $mock = Test::MockObject->new(); $mock->fake_module( 'Weather::Com::Cached' => ( '_cache_time' => sub { return 1110000000 } ) ); # test finder method my $locations = $wc->find('New York'); is( @{$locations}, 4, 'Did we get 4 locations?' ); # test if we have the right 4 location objects my @sorted_locations = sort { $a->name() cmp $b->name() } @{$locations}; isa_ok( $sorted_locations[0], "Weather::Com::Location", 'Test location class.' ); isa_ok( $sorted_locations[0], "Weather::Com::Cached", 'Test location class.' ); isa_ok( $sorted_locations[0], "Weather::Com::Base", 'Test location class.' ); isa_ok( $sorted_locations[1], "Weather::Com::Location", 'Test location class.' ); isa_ok( $sorted_locations[1], "Weather::Com::Cached", 'Test location class.' ); isa_ok( $sorted_locations[1], "Weather::Com::Base", 'Test location class.' ); isa_ok( $sorted_locations[2], "Weather::Com::Location", 'Test location class.' ); isa_ok( $sorted_locations[2], "Weather::Com::Cached", 'Test location class.' ); isa_ok( $sorted_locations[2], "Weather::Com::Base", 'Test location class.' ); isa_ok( $sorted_locations[3], "Weather::Com::Location", 'Test location class.' ); isa_ok( $sorted_locations[3], "Weather::Com::Cached", 'Test location class.' ); isa_ok( $sorted_locations[3], "Weather::Com::Base", 'Test location class.' ); is( $sorted_locations[0]->name(), 'New York, NY', 'Test for location names.' ); is( $sorted_locations[1]->name(), 'New York/Central Park, NY', 'Test for location names.' ); is( $sorted_locations[2]->name(), 'New York/JFK Intl Arpt, NY', 'Test for location names.' ); is( $sorted_locations[3]->name(), 'New York/La Guardia Arpt, NY', 'Test for location names.' ); # test data of New York, Central Park my $ny = $sorted_locations[1]; # 1. test units isa_ok( $ny->units(), 'Weather::Com::Units', 'Test units class:' ); is( $ny->units->distance(), 'km', 'Test distance unit.' ); is( $ny->units->precipitation(), 'mm', 'Test precipitation unit.' ); is( $ny->units->pressure(), 'mb', 'Test pressure unit.' ); is( $ny->units->speed(), 'km/h', 'Test speed unit.' ); is( $ny->units->temperature(), 'C', 'Test temperature unit.' ); # 2. test timezone is( $ny->timezone, '-4', 'Test timezone' ); # 3. test geographic data is( $ny->latitude, '40.79', 'Test latitude.' ); is( $ny->longitude, '-73.96', 'Test longitude.' ); # 4. test date and time objects isa_ok( $ny->localtime, 'Weather::Com::DateTime', 'localtime:' ); isa_ok( $ny->sunrise, 'Weather::Com::DateTime', 'sunrise:' ); isa_ok( $ny->sunset, 'Weather::Com::DateTime', 'sunset:' ); is( $ny->sunrise->formatted('hhmm'), '0608', 'Sunrise value.' ); is( $ny->sunset->formatted('hhmm'), '1942', 'Sunset value.' ); # 5. test current conditions my $cc = $ny->current_conditions(); isa_ok( $cc, 'Weather::Com::CurrentConditions', 'current conditions:' ); is( $cc->id, $ny->id, 'Test current conditions id.' ); is( $cc->name, $ny->name, 'Test current conditions name.' ); is( $cc->icon, '34', 'Test current conditions icon.' ); is( $cc->description, 'fair', 'Test current conditions description.' ); is( $cc->temperature, '14', 'Test current conditions temperature.' ); is( $cc->windchill, '10', 'Test current conditions windchill.' ); is( $cc->humidity, '62', 'Test current conditions humidity.' ); is( $cc->dewpoint, '7', 'Test current conditions dewpoint.' ); is( $cc->visibility, '16.1', 'Test current conditions visibility.' ); # 5.a test current conditions moon data my $cc_moon = $cc->moon; isa_ok( $cc_moon, 'Weather::Com::Moon', 'current conditions moon:' ); is( $cc_moon->icon, '12', 'current conditions moon icon' ); is( $cc_moon->description, 'waxing gibbous', 'current conditions moon description' ); # 5.b test current conditions barometric pressure data my $cc_bar = $cc->pressure; isa_ok( $cc_bar, 'Weather::Com::AirPressure', 'current conditions barom. pressure:' ); is( $cc_bar->pressure, '1,014.6', 'current conditions pressure (mb)' ); is( $cc_bar->tendency, 'steady', 'current conditions pressure tendency' ); # 5.c test current conditions uv index my $cc_uv = $cc->uv_index; isa_ok( $cc_uv, 'Weather::Com::UVIndex', 'current conditions uv index:' ); is( $cc_uv->index, '2', 'current conditions uv index' ); is( $cc_uv->description, 'low', 'current conditions uv description' ); # 5.d test current conditions moon data my $cc_wind = $cc->wind; isa_ok( $cc_wind, 'Weather::Com::Wind', 'current conditions wind:' ); is( $cc_wind->speed, '19', 'current conditions wind speed' ); is( $cc_wind->direction_degrees, '160', 'current conditions wind direction' ); # 6. test forecasts isa_ok( $ny->forecast, 'Weather::Com::Forecast', 'forecast:' ); my $d3 = $ny->forecast->day(2); isa_ok( $d3, 'Weather::Com::DayForecast', 'day forecast:' ); is( $d3->date->formatted('ddmm'), '2304', "Forecast date." ); is( $d3->high, '18', 'Forecast high temp.' ); is( $d3->low, '11', 'Forecast low temp.' ); my $night = $d3->night; isa_ok( $night, 'Weather::Com::DayPart', 'Test night and day.' ); is( $night->conditions, 'light rain', 'Test nightly conditions.' ); } Weather-Com-0.5.3/t/Cached.t0000644000175000017500000000436310644507215016253 0ustar thomasthomas00000000000000##################################################################### # # Test suite for 'Weather::Com::Cached' # # Testing network connection (without proxy). # Functional tests with 'Test::MockObject'. These could only be run # if Test::MockObject is installed. # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/Cached.t' # ##################################################################### # # initialization # no warnings; use Test::More tests => 6; require 't/TestData.pm'; BEGIN { use_ok('Weather::Com::Cached'); } ##################################################################### # # Testing object instantiation (do we use the right class)? # my %weatherargs = ( 'debug' => 0, 'language' => 'en', ); my $wc = Weather::Com::Cached->new(%weatherargs); isa_ok( $wc, "Weather::Com::Cached", 'Is a Weatcher::Com::Cached object' ); isa_ok( $wc, "Weather::Com::Base", 'Is a Weatcher::Com::Base object' ); # # Test functionality if Test::MockObject is installed. # SKIP: { eval { require Test::MockObject; }; skip "Test::MockObject not installed", 3 if $@; # remove all old cache files unlink <*.dat>; # define mock object my $mock = Test::MockObject->new(); $mock->fake_module( 'LWP::UserAgent' => ( 'request' => sub { return HTTP::Response->new() }, ) ); $mock->fake_module( 'HTTP::Response' => ( 'is_success' => sub { return 1 }, 'content' => sub { return $NY_HTML }, ) ); $mock->fake_module( 'Weather::Com::Cached' => ( '_cache_time' => sub { return 1110000000 } ) ); # test search method is_deeply( $wc->search('New York'), $NY_Hash, 'Look for locations named "New York"' ); # search for 'New York/Central Park, NY' to check locations cache my $NYCP = { 'USNY0998' => 'New York/Central Park, NY', }; is_deeply( $wc->search('New York/Central Park, NY'), $NYCP, 'Check locations cache' ); $mock->fake_module( 'HTTP::Response' => ( 'is_success' => sub { return 1 }, 'content' => sub { return $NYCP_HTML }, ) ); is_deeply( $wc->get_weather('USNY0998'), $NYCP_HashCached, 'Look for weather data for "New York/Central Park, NY"' ); } Weather-Com-0.5.3/t/DateTime.t0000644000175000017500000000237310354615517016602 0ustar thomasthomas00000000000000##################################################################### # # Test suite for 'Weather::Com::Simple' # # Functional tests with 'Test::MockObject'. These could only be run # if Test::MockObject is installed. # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/Simple.t' # ##################################################################### # # initialization # use Test::More tests => 8; BEGIN { use_ok('Weather::Com::DateTime'); }; my $testtime = 1109430000; ##################################################################### # # Testing object instantiation (do we use the right class)? # my $wc = Weather::Com::DateTime->new(-6); $wc->set_lsup('02/25/05 11:21 PM Local Time'); isa_ok($wc, "Weather::Com::DateTime", 'Is a Weatcher::Com::DateTime object'); is($wc->time(), "23:21", '24 hour time'); is($wc->time_ampm(), "11:21 PM", 'AM/PM mode'); is($wc->day(), "25", 'Day Number'); is($wc->mon(), "02", 'Number of month'); is($wc->year(), "2005", 'Year'); is(gmtime($wc->epoc()), 'Sat Feb 26 05:21:00 2005', 'GMTime'); Weather-Com-0.5.3/t/L10N.t0000644000175000017500000000226310232677730015556 0ustar thomasthomas00000000000000##################################################################### # # Test suite for 'Weather::Com::L10N' # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/L10N.t' # ##################################################################### # # initialization # no warnings; use Test::More tests => 7; BEGIN { use_ok('Weather::Com::L10N'); } ##################################################################### # # Testing object instantiation (do we use the right class)? # my $wind = Weather::Com::L10N->get_handle('de'); isa_ok( $wind, "Weather::Com::L10N", 'Is a Weatcher::Com::L10N object' ); isa_ok( $wind, "Locale::Maketext", 'Is a Locale::Maketext object' ); # test for existing translations is($wind->maketext('unknown'), 'unbekannt', 'Translation of known words'); is($wind->maketext('NNE'), 'NNO', 'Translation of known words'); # test for non-existent translations is($wind->maketext('hello'), 'hello', 'Translation of unknown words'); # test for non-existent languages $wind = Weather::Com::L10N->get_handle('kr'); is($wind->maketext('unknown'), 'unknown', 'Test for default language'); Weather-Com-0.5.3/t/TestData.pm0000644000175000017500000010261410232170442016753 0ustar thomasthomas00000000000000# # Test data for Weather::Com::Base and ::Cached location search # $NY_HTML = < New York, NY New York/Central Park, NY New York/JFK Intl Arpt, NY New York/La Guardia Arpt, NY END $NY_Hash = { 'USNY0996' => 'New York, NY', 'USNY0998' => 'New York/Central Park, NY', 'USNY0999' => 'New York/JFK Intl Arpt, NY', 'USNY1000' => 'New York/La Guardia Arpt, NY', }; # # HTML returned by a seach for NY, Central Park with 10 days forecast # $NYCP_HTML = < en_US
MEDIUM
C km km/h mb mm New York/Central Park, NY 5:35 PM 40.79 -73.96 6:08 AM 7:42 PM -4 http://www.weather.com/outlook/health/allergies/USNY0998?par=xoap Pollen Reports http://www.weather.com/outlook/travel/flights/citywx/USNY0998?par=xoap Airport Delays http://www.weather.com/outlook/events/special/result/USNY0998?when=thisweek&par=xoap Special Events http://www.weather.com/services/desktop.html?par=xoap Download Desktop Weather 4/21/05 4:51 PM EDT Central Park, NY 14 10 Fair 34 1,014.6 steady 19 29 160 SSE 62 16.1 2 Low 7 12 Waxing Gibbous 4/21/05 5:03 PM EDT N/A 8 6:08 AM 7:42 PM 44 N/A N/A N/A N/A N/A N/A 10 N/A 33 Mostly Clear 13 N/A 153 SSE M Clear 10 55 16 11 6:06 AM 7:43 PM 30 Partly Cloudy 18 N/A 139 SE P Cloudy 10 44 12 Rain 29 N/A 104 ESE Rain 80 69 18 11 6:05 AM 7:44 PM 12 Rain 27 N/A 148 SSE Rain 80 70 11 Light Rain 11 N/A 62 ENE Light Rain 60 61 13 6 6:03 AM 7:45 PM 11 Few Showers 16 N/A 22 NNE Few Showers 30 47 29 Partly Cloudy 8 N/A 349 N P Cloudy 10 53 11 7 6:02 AM 7:46 PM 11 Showers 11 N/A 59 ENE Showers 40 48 29 Partly Cloudy 8 N/A 162 SSE P Cloudy 10 53 13 9 6:01 AM 7:47 PM 30 Partly Cloudy 10 N/A 197 SSW P Cloudy 20 50 33 Mostly Clear 5 N/A 264 W M Clear 10 54 17 11 5:59 AM 7:48 PM 11 Few Showers 10 N/A 106 ESE Few Showers 30 52 12 Rain 10 N/A 170 S Rain 60 60 14 9 5:58 AM 7:49 PM 11 Few Showers 11 N/A 254 WSW Few Showers 30 52 29 Partly Cloudy 10 N/A 240 WSW P Cloudy 20 55 16 10 5:57 AM 7:50 PM 30 Partly Cloudy 13 N/A 237 WSW P Cloudy 20 51 11 Light Rain 8 N/A 284 WNW Light Rain 60 60 17 11 5:56 AM 7:51 PM 11 Light Rain 11 N/A 358 N Light Rain 60 53 29 Partly Cloudy 10 N/A 217 SW P Cloudy 20 58
NYCP # # Hash corresponding to HTML returned by a seach for # NY, Central Park with 10 days forecast # $NYCP_Hash = { 'dayf' => { 'lsup' => '4/21/05 5:03 PM EDT', 'day' => [ { 'hi' => 'N/A', 'suns' => '7:42 PM', 'dt' => 'Apr 21', 'part' => [ { 'hmid' => 'N/A', 'wind' => { 'gust' => 'N/A', 'd' => 'N/A', 's' => 'N/A', 't' => 'N/A' }, 'icon' => '44', 'p' => 'd', 'ppcp' => '10', 'bt' => 'N/A', 't' => 'N/A' }, { 'hmid' => '55', 'wind' => { 'gust' => 'N/A', 'd' => '153', 's' => '13', 't' => 'SSE' }, 'icon' => '33', 'p' => 'n', 'ppcp' => '10', 'bt' => 'M Clear', 't' => 'Mostly Clear' } ], 'd' => '0', 'sunr' => '6:08 AM', 'low' => '8', 't' => 'Thursday' }, { 'hi' => '16', 'suns' => '7:43 PM', 'dt' => 'Apr 22', 'part' => [ { 'hmid' => '44', 'wind' => { 'gust' => 'N/A', 'd' => '139', 's' => '18', 't' => 'SE' }, 'icon' => '30', 'p' => 'd', 'ppcp' => '10', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' }, { 'hmid' => '69', 'wind' => { 'gust' => 'N/A', 'd' => '104', 's' => '29', 't' => 'ESE' }, 'icon' => '12', 'p' => 'n', 'ppcp' => '80', 'bt' => 'Rain', 't' => 'Rain' } ], 'd' => '1', 'sunr' => '6:06 AM', 'low' => '11', 't' => 'Friday' }, { 'hi' => '18', 'suns' => '7:44 PM', 'dt' => 'Apr 23', 'part' => [ { 'hmid' => '70', 'wind' => { 'gust' => 'N/A', 'd' => '148', 's' => '27', 't' => 'SSE' }, 'icon' => '12', 'p' => 'd', 'ppcp' => '80', 'bt' => 'Rain', 't' => 'Rain' }, { 'hmid' => '61', 'wind' => { 'gust' => 'N/A', 'd' => '62', 's' => '11', 't' => 'ENE' }, 'icon' => '11', 'p' => 'n', 'ppcp' => '60', 'bt' => 'Light Rain', 't' => 'Light Rain' } ], 'd' => '2', 'sunr' => '6:05 AM', 'low' => '11', 't' => 'Saturday' }, { 'hi' => '13', 'suns' => '7:45 PM', 'dt' => 'Apr 24', 'part' => [ { 'hmid' => '47', 'wind' => { 'gust' => 'N/A', 'd' => '22', 's' => '16', 't' => 'NNE' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '30', 'bt' => 'Few Showers', 't' => 'Few Showers' }, { 'hmid' => '53', 'wind' => { 'gust' => 'N/A', 'd' => '349', 's' => '8', 't' => 'N' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '10', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '3', 'sunr' => '6:03 AM', 'low' => '6', 't' => 'Sunday' }, { 'hi' => '11', 'suns' => '7:46 PM', 'dt' => 'Apr 25', 'part' => [ { 'hmid' => '48', 'wind' => { 'gust' => 'N/A', 'd' => '59', 's' => '11', 't' => 'ENE' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '40', 'bt' => 'Showers', 't' => 'Showers' }, { 'hmid' => '53', 'wind' => { 'gust' => 'N/A', 'd' => '162', 's' => '8', 't' => 'SSE' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '10', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '4', 'sunr' => '6:02 AM', 'low' => '7', 't' => 'Monday' }, { 'hi' => '13', 'suns' => '7:47 PM', 'dt' => 'Apr 26', 'part' => [ { 'hmid' => '50', 'wind' => { 'gust' => 'N/A', 'd' => '197', 's' => '10', 't' => 'SSW' }, 'icon' => '30', 'p' => 'd', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' }, { 'hmid' => '54', 'wind' => { 'gust' => 'N/A', 'd' => '264', 's' => '5', 't' => 'W' }, 'icon' => '33', 'p' => 'n', 'ppcp' => '10', 'bt' => 'M Clear', 't' => 'Mostly Clear' } ], 'd' => '5', 'sunr' => '6:01 AM', 'low' => '9', 't' => 'Tuesday' }, { 'hi' => '17', 'suns' => '7:48 PM', 'dt' => 'Apr 27', 'part' => [ { 'hmid' => '52', 'wind' => { 'gust' => 'N/A', 'd' => '106', 's' => '10', 't' => 'ESE' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '30', 'bt' => 'Few Showers', 't' => 'Few Showers' }, { 'hmid' => '60', 'wind' => { 'gust' => 'N/A', 'd' => '170', 's' => '10', 't' => 'S' }, 'icon' => '12', 'p' => 'n', 'ppcp' => '60', 'bt' => 'Rain', 't' => 'Rain' } ], 'd' => '6', 'sunr' => '5:59 AM', 'low' => '11', 't' => 'Wednesday' }, { 'hi' => '14', 'suns' => '7:49 PM', 'dt' => 'Apr 28', 'part' => [ { 'hmid' => '52', 'wind' => { 'gust' => 'N/A', 'd' => '254', 's' => '11', 't' => 'WSW' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '30', 'bt' => 'Few Showers', 't' => 'Few Showers' }, { 'hmid' => '55', 'wind' => { 'gust' => 'N/A', 'd' => '240', 's' => '10', 't' => 'WSW' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '7', 'sunr' => '5:58 AM', 'low' => '9', 't' => 'Thursday' }, { 'hi' => '16', 'suns' => '7:50 PM', 'dt' => 'Apr 29', 'part' => [ { 'hmid' => '51', 'wind' => { 'gust' => 'N/A', 'd' => '237', 's' => '13', 't' => 'WSW' }, 'icon' => '30', 'p' => 'd', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' }, { 'hmid' => '60', 'wind' => { 'gust' => 'N/A', 'd' => '284', 's' => '8', 't' => 'WNW' }, 'icon' => '11', 'p' => 'n', 'ppcp' => '60', 'bt' => 'Light Rain', 't' => 'Light Rain' } ], 'd' => '8', 'sunr' => '5:57 AM', 'low' => '10', 't' => 'Friday' }, { 'hi' => '17', 'suns' => '7:51 PM', 'dt' => 'Apr 30', 'part' => [ { 'hmid' => '53', 'wind' => { 'gust' => 'N/A', 'd' => '358', 's' => '11', 't' => 'N' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '60', 'bt' => 'Light Rain', 't' => 'Light Rain' }, { 'hmid' => '58', 'wind' => { 'gust' => 'N/A', 'd' => '217', 's' => '10', 't' => 'SW' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '9', 'sunr' => '5:56 AM', 'low' => '11', 't' => 'Saturday' } ] }, 'head' => { 'ur' => 'mm', 'ud' => 'km', 'us' => 'km/h', 'form' => 'MEDIUM', 'up' => 'mb', 'locale' => 'en_US', 'ut' => 'C', }, 'cc' => { 'icon' => '34', 'flik' => '10', 'obst' => 'Central Park, NY', 'lsup' => '4/21/05 4:51 PM EDT', 'tmp' => '14', 'hmid' => '62', 'wind' => { 'gust' => '29', 'd' => '160', 's' => '19', 't' => 'SSE' }, 'bar' => { 'r' => '1,014.6', 'd' => 'steady' }, 'moon' => { 'icon' => '12', 't' => 'Waxing Gibbous' }, 'dewp' => '7', 'uv' => { 't' => 'Low', 'i' => '2' }, 'vis' => '16.1', 't' => 'Fair' }, 'lnks' => { 'link' => [ { 'l' => 'http://www.weather.com/outlook/health/allergies/USNY0998?par=xoap', 'pos' => '1', 't' => 'Pollen Reports' }, { 'l' => 'http://www.weather.com/outlook/travel/flights/citywx/USNY0998?par=xoap', 'pos' => '2', 't' => 'Airport Delays' }, { 'l' => 'http://www.weather.com/outlook/events/special/result/USNY0998?when=thisweek&par=xoap', 'pos' => '3', 't' => 'Special Events' }, { 'l' => 'http://www.weather.com/services/desktop.html?par=xoap', 'pos' => '4', 't' => 'Download Desktop Weather' } ], 'type' => 'prmo' }, 'loc' => { 'suns' => '7:42 PM', 'zone' => '-4', 'lat' => '40.79', 'tm' => '5:35 PM', 'sunr' => '6:08 AM', 'dnam' => 'New York/Central Park, NY', 'id' => 'USNY0998', 'lon' => '-73.96' }, 'ver' => '2.0' }; # # Hash corresponding to HTML returned by a seach for # NY, Central Park with 10 days forecast # $NYCP_HashCached = { 'dayf' => { 'lsup' => '4/21/05 5:03 PM EDT', 'cached' => '1110000000', 'day' => [ { 'hi' => 'N/A', 'suns' => '7:42 PM', 'dt' => 'Apr 21', 'part' => [ { 'hmid' => 'N/A', 'wind' => { 'gust' => 'N/A', 'd' => 'N/A', 's' => 'N/A', 't' => 'N/A' }, 'icon' => '44', 'p' => 'd', 'ppcp' => '10', 'bt' => 'N/A', 't' => 'N/A' }, { 'hmid' => '55', 'wind' => { 'gust' => 'N/A', 'd' => '153', 's' => '13', 't' => 'SSE' }, 'icon' => '33', 'p' => 'n', 'ppcp' => '10', 'bt' => 'M Clear', 't' => 'Mostly Clear' } ], 'd' => '0', 'sunr' => '6:08 AM', 'low' => '8', 't' => 'Thursday' }, { 'hi' => '16', 'suns' => '7:43 PM', 'dt' => 'Apr 22', 'part' => [ { 'hmid' => '44', 'wind' => { 'gust' => 'N/A', 'd' => '139', 's' => '18', 't' => 'SE' }, 'icon' => '30', 'p' => 'd', 'ppcp' => '10', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' }, { 'hmid' => '69', 'wind' => { 'gust' => 'N/A', 'd' => '104', 's' => '29', 't' => 'ESE' }, 'icon' => '12', 'p' => 'n', 'ppcp' => '80', 'bt' => 'Rain', 't' => 'Rain' } ], 'd' => '1', 'sunr' => '6:06 AM', 'low' => '11', 't' => 'Friday' }, { 'hi' => '18', 'suns' => '7:44 PM', 'dt' => 'Apr 23', 'part' => [ { 'hmid' => '70', 'wind' => { 'gust' => 'N/A', 'd' => '148', 's' => '27', 't' => 'SSE' }, 'icon' => '12', 'p' => 'd', 'ppcp' => '80', 'bt' => 'Rain', 't' => 'Rain' }, { 'hmid' => '61', 'wind' => { 'gust' => 'N/A', 'd' => '62', 's' => '11', 't' => 'ENE' }, 'icon' => '11', 'p' => 'n', 'ppcp' => '60', 'bt' => 'Light Rain', 't' => 'Light Rain' } ], 'd' => '2', 'sunr' => '6:05 AM', 'low' => '11', 't' => 'Saturday' }, { 'hi' => '13', 'suns' => '7:45 PM', 'dt' => 'Apr 24', 'part' => [ { 'hmid' => '47', 'wind' => { 'gust' => 'N/A', 'd' => '22', 's' => '16', 't' => 'NNE' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '30', 'bt' => 'Few Showers', 't' => 'Few Showers' }, { 'hmid' => '53', 'wind' => { 'gust' => 'N/A', 'd' => '349', 's' => '8', 't' => 'N' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '10', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '3', 'sunr' => '6:03 AM', 'low' => '6', 't' => 'Sunday' }, { 'hi' => '11', 'suns' => '7:46 PM', 'dt' => 'Apr 25', 'part' => [ { 'hmid' => '48', 'wind' => { 'gust' => 'N/A', 'd' => '59', 's' => '11', 't' => 'ENE' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '40', 'bt' => 'Showers', 't' => 'Showers' }, { 'hmid' => '53', 'wind' => { 'gust' => 'N/A', 'd' => '162', 's' => '8', 't' => 'SSE' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '10', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '4', 'sunr' => '6:02 AM', 'low' => '7', 't' => 'Monday' }, { 'hi' => '13', 'suns' => '7:47 PM', 'dt' => 'Apr 26', 'part' => [ { 'hmid' => '50', 'wind' => { 'gust' => 'N/A', 'd' => '197', 's' => '10', 't' => 'SSW' }, 'icon' => '30', 'p' => 'd', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' }, { 'hmid' => '54', 'wind' => { 'gust' => 'N/A', 'd' => '264', 's' => '5', 't' => 'W' }, 'icon' => '33', 'p' => 'n', 'ppcp' => '10', 'bt' => 'M Clear', 't' => 'Mostly Clear' } ], 'd' => '5', 'sunr' => '6:01 AM', 'low' => '9', 't' => 'Tuesday' }, { 'hi' => '17', 'suns' => '7:48 PM', 'dt' => 'Apr 27', 'part' => [ { 'hmid' => '52', 'wind' => { 'gust' => 'N/A', 'd' => '106', 's' => '10', 't' => 'ESE' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '30', 'bt' => 'Few Showers', 't' => 'Few Showers' }, { 'hmid' => '60', 'wind' => { 'gust' => 'N/A', 'd' => '170', 's' => '10', 't' => 'S' }, 'icon' => '12', 'p' => 'n', 'ppcp' => '60', 'bt' => 'Rain', 't' => 'Rain' } ], 'd' => '6', 'sunr' => '5:59 AM', 'low' => '11', 't' => 'Wednesday' }, { 'hi' => '14', 'suns' => '7:49 PM', 'dt' => 'Apr 28', 'part' => [ { 'hmid' => '52', 'wind' => { 'gust' => 'N/A', 'd' => '254', 's' => '11', 't' => 'WSW' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '30', 'bt' => 'Few Showers', 't' => 'Few Showers' }, { 'hmid' => '55', 'wind' => { 'gust' => 'N/A', 'd' => '240', 's' => '10', 't' => 'WSW' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '7', 'sunr' => '5:58 AM', 'low' => '9', 't' => 'Thursday' }, { 'hi' => '16', 'suns' => '7:50 PM', 'dt' => 'Apr 29', 'part' => [ { 'hmid' => '51', 'wind' => { 'gust' => 'N/A', 'd' => '237', 's' => '13', 't' => 'WSW' }, 'icon' => '30', 'p' => 'd', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' }, { 'hmid' => '60', 'wind' => { 'gust' => 'N/A', 'd' => '284', 's' => '8', 't' => 'WNW' }, 'icon' => '11', 'p' => 'n', 'ppcp' => '60', 'bt' => 'Light Rain', 't' => 'Light Rain' } ], 'd' => '8', 'sunr' => '5:57 AM', 'low' => '10', 't' => 'Friday' }, { 'hi' => '17', 'suns' => '7:51 PM', 'dt' => 'Apr 30', 'part' => [ { 'hmid' => '53', 'wind' => { 'gust' => 'N/A', 'd' => '358', 's' => '11', 't' => 'N' }, 'icon' => '11', 'p' => 'd', 'ppcp' => '60', 'bt' => 'Light Rain', 't' => 'Light Rain' }, { 'hmid' => '58', 'wind' => { 'gust' => 'N/A', 'd' => '217', 's' => '10', 't' => 'SW' }, 'icon' => '29', 'p' => 'n', 'ppcp' => '20', 'bt' => 'P Cloudy', 't' => 'Partly Cloudy' } ], 'd' => '9', 'sunr' => '5:56 AM', 'low' => '11', 't' => 'Saturday' } ] }, 'head' => { 'ur' => 'mm', 'ud' => 'km', 'us' => 'km/h', 'form' => 'MEDIUM', 'up' => 'mb', 'locale' => 'en_US', 'ut' => 'C', 'cached' => '1110000000', }, 'cc' => { 'cached' => '1110000000', 'icon' => '34', 'flik' => '10', 'obst' => 'Central Park, NY', 'lsup' => '4/21/05 4:51 PM EDT', 'tmp' => '14', 'hmid' => '62', 'wind' => { 'gust' => '29', 'd' => '160', 's' => '19', 't' => 'SSE' }, 'bar' => { 'r' => '1,014.6', 'd' => 'steady' }, 'moon' => { 'icon' => '12', 't' => 'Waxing Gibbous' }, 'dewp' => '7', 'uv' => { 't' => 'Low', 'i' => '2' }, 'vis' => '16.1', 't' => 'Fair' }, 'lnks' => { 'cached' => '1110000000', 'link' => [ { 'l' => 'http://www.weather.com/outlook/health/allergies/USNY0998?par=xoap', 'pos' => '1', 't' => 'Pollen Reports' }, { 'l' => 'http://www.weather.com/outlook/travel/flights/citywx/USNY0998?par=xoap', 'pos' => '2', 't' => 'Airport Delays' }, { 'l' => 'http://www.weather.com/outlook/events/special/result/USNY0998?when=thisweek&par=xoap', 'pos' => '3', 't' => 'Special Events' }, { 'l' => 'http://www.weather.com/services/desktop.html?par=xoap', 'pos' => '4', 't' => 'Download Desktop Weather' } ], 'type' => 'prmo' }, 'loc' => { 'cached' => '1110000000', 'suns' => '7:42 PM', 'zone' => '-4', 'lat' => '40.79', 'tm' => '5:35 PM', 'sunr' => '6:08 AM', 'dnam' => 'New York/Central Park, NY', 'id' => 'USNY0998', 'lon' => '-73.96' }, 'ver' => '2.0' }; # # Testdata for Weather::Com::Simple::get_weather # $simpleWeather =[{ 'place' => 'New York/Central Park, NY', 'updated' => '4:51 PM EDT on April 21, 2005', 'celsius' => '14', 'temperature_celsius' => '14', 'windchill_celsius' => '10', 'fahrenheit' => '57', 'temperature_fahrenheit' => '57', 'windchill_fahrenheit' => '50', 'wind' => '11 mph 19 km/h from the South Southeast', 'windspeed_kmh' => '19', 'windspeed_mph' => '11', 'humidity' => '62', 'conditions' => 'Fair', 'pressure' => '29.96 in / 1014.6 hPa' }]; Weather-Com-0.5.3/t/AirPressure.t0000644000175000017500000000320610232717764017350 0ustar thomasthomas00000000000000##################################################################### # # Test suite for 'Weather::Com::AirPressure' # # Before `make install' is performed this script should be runnable # with `make test'. After `make install' it should work as # `perl t/AirPressure.t' # ##################################################################### # # initialization # no warnings; use Test::More tests => 11; BEGIN { use_ok('Weather::Com::AirPressure'); } ##################################################################### # # Testing object instantiation (do we use the right class)? # my $bar = Weather::Com::AirPressure->new(); isa_ok( $bar, "Weather::Com::AirPressure", 'Right class?' ); isa_ok( $bar, "Weather::Com::Object", 'Right inheritance?' ); # # Test negative init when instantiated without arguments # is( $bar->pressure, -1, 'Test negative initialization of pressure.' ); is( $bar->tendency(), 'unknown', 'Test negative initialization of tendency.' ); # # Test negative init when instantiated for German # $bar = Weather::Com::AirPressure->new( lang => 'de' ); is( $bar->pressure, -1, 'Test negative initialization of pressure.' ); is( $bar->tendency(), 'unbekannt', 'Test negative initialization of tendency.' ); # # Test negative update # $bar->update( 'r' => undef, 'd' => undef, ); is( $bar->pressure, -1, 'Test negative update of pressure.' ); is( $bar->tendency(), 'unbekannt', 'Test negative update of tendency.' ); # # Test positive update # $bar->update( 'r' => '1,100', 'd' => 'steady' ); is( $bar->pressure, '1,100', 'Test update of pressure.' ); is( $bar->tendency(), 'stabil', 'Test update of tendency.' ); Weather-Com-0.5.3/lib/0000755000000000000000000000000010721352300014327 5ustar rootroot00000000000000Weather-Com-0.5.3/lib/Weather/0000755000000000000000000000000010721352300015726 5ustar rootroot00000000000000Weather-Com-0.5.3/lib/Weather/Com/0000755000000000000000000000000010721352300016444 5ustar rootroot00000000000000Weather-Com-0.5.3/lib/Weather/Com/Simple.pm0000644000175000017500000002044510644511444021124 0ustar thomasthomas00000000000000package Weather::Com::Simple; use Carp; use Weather::Com::Base qw(celsius2fahrenheit convert_winddirection); use Weather::Com::Cached; our $VERSION = sprintf "%d.%03d", q$Revision: 1.3 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; my %parameters; # some general attributes $self->{PROXY} = "none"; $self->{DEBUG} = 0; $self->{CACHE} = "."; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } $self = bless( $self, $class ); # check mandatory parameters unless ( $parameters{place} ) { $self->_debug("ERROR: Location not specified"); return undef; } # put wanted parameters into $self $self->{PLACE} = $parameters{place}; $self->{PROXY} = $parameters{proxy} if ( $parameters{proxy} ); $self->{PROXY_USER} = $parameters{proxy_user} if ( $parameters{proxy_user} ); $self->{PROXY_PASS} = $parameters{proxy_pass} if ( $parameters{proxy_pass} ); $self->{DEBUG} = $parameters{debug} if ( $parameters{debug} ); $self->{CACHE} = $parameters{cache} if ( $parameters{cache} ); # Weather::Com::Cached object my %weatherargs = ( 'current' => 1, 'forecast' => 0, 'proxy' => $self->{PROXY}, 'proxy_user' => $self->{PROXY_USER}, 'proxy_pass' => $self->{PROXY_PASS}, 'debug' => $self->{DEBUG}, 'cache' => $self->{CACHE}, ); $weatherargs{timeout} = $parameters{timeout} if ( $parameters{timeout} ); $weatherargs{partner_id} = $parameters{partner_id} if ( $parameters{partner_id} ); $weatherargs{license} = $parameters{license} if ( $parameters{license} ); # initialize weather object $self->{WEATHER} = Weather::Com::Cached->new(%weatherargs); return $self; } # end new() #------------------------------------------------------------------------ # accessor methods #------------------------------------------------------------------------ sub get_weather { my $self = shift; my $allweather; my $place_result = $self->{WEATHER}->search( $self->{PLACE} ); # check if search succeeded unless ($place_result) { $self->_debug($@); return undef; } foreach ( keys %{$place_result} ) { my $weatherdata = $self->{WEATHER}->get_weather($_); unless ($weatherdata) { $self->_debug($@); return 0; } my $place_weather = { # header data 'place' => $weatherdata->{loc}->{dnam}, 'updated' => _parse_timestring( $weatherdata->{cc}->{lsup} ), # temperature celsius/fahrenheit 'celsius' => $weatherdata->{cc}->{tmp}, 'temperature_celsius' => $weatherdata->{cc}->{tmp}, 'windchill_celsius' => $weatherdata->{cc}->{flik}, 'fahrenheit' => celsius2fahrenheit( $weatherdata->{cc}->{tmp} ), 'temperature_fahrenheit' => celsius2fahrenheit( $weatherdata->{cc}->{tmp} ), 'windchill_fahrenheit' => celsius2fahrenheit( $weatherdata->{cc}->{flik} ), # wind 'wind' => _parse_wind( $weatherdata->{cc}->{wind} ), 'windspeed_kmh' => _parse_windspeed_kmh( $weatherdata->{cc}->{wind} ), 'windspeed_mph' => _parse_windspeed_mph( $weatherdata->{cc}->{wind} ), # other 'humidity' => $weatherdata->{cc}->{hmid}, 'conditions' => $weatherdata->{cc}->{t}, 'pressure' => _parse_pressure( $weatherdata->{cc}->{bar} ), }; push( @{$allweather}, $place_weather ); } return $allweather; } sub getweather { return get_weather(@_); } #------------------------------------------------------------------------ # internal parsing utilities #------------------------------------------------------------------------ sub _parse_wind { my $winddata = shift; my $wind; if ( lc( $winddata->{s} ) =~ /calm/ ) { $wind = "calm"; } else { my $kmh = _parse_windspeed_kmh($winddata); my $mph = _parse_windspeed_mph($winddata); my $direction = convert_winddirection( $winddata->{t} ); $wind = "$mph mph $kmh km/h from the $direction"; } return $wind; } sub _parse_windspeed_kmh { my $winddata = shift; my $speed; if ( lc( $winddata->{s} ) =~ /calm/ ) { $speed = "calm"; } else { $speed = $winddata->{s}; } return $speed; } sub _parse_windspeed_mph { my $winddata = shift; my $speed; if ( lc( $winddata->{s} ) =~ /calm/ ) { $speed = "calm"; } else { $speed = sprintf( "%d", $winddata->{s} * 0.6213722 ); } return $speed; } sub _parse_pressure { my $pressuredata = shift; my $pressure; my $hPa = $pressuredata->{r}; $hPa =~ s/,//g; my $in = $hPa * 0.02953; $pressure = sprintf( "%.2f in / %.1f hPa", $in, $hPa ); } sub _parse_timestring { my $timestring = shift; my @months = ( "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "Dezember" ); my ( $date, $time, $ampm, $zone ) = split( / /, $timestring ); my ( $month, $mday, $year ) = split( "/", $date ); return "$time $ampm $zone on $months[$month] $mday, " . ( $year + 2000 ); } #------------------------------------------------------------------------ # other internals #------------------------------------------------------------------------ sub _debug { my $self = shift; my $notice = shift; if ( $self->{DEBUG} ) { carp ref($self) . " DEBUG NOTE: $notice\n"; return 1; } return 0; } 1; __END__ =pod =head1 NAME Weather::Com::Simple - Simple Wrapper around the L API =head1 SYNOPSIS use Data::Dumper; use Weather::Com::Simple; # define parameters for weather search my %params = ( 'partner_id' => 'somepartnerid', 'license' => '12345678', 'place' => 'Heidelberg', ); # instantiate a new weather.com object my $simple_weather = Weather::Com::Simple->new(%params); my $weather = $simple_weather->get_weather(); print Dumper($weather); =head1 DESCRIPTION I is a very high level wrapper around I. You provide a place to search for (e.g. a city or "city, country") and you'll get back a simple hash containing some usefull weather information about all locations whose name matches to the search string. =head1 CONSTRUCTOR =head2 new(hash or hashref) The constructor takes the same hash or hashref as L does. Please refer to that documentation for further details. Except from the L parameters this constructor takes a parameter I which defines the location to search for. It is not possible to provide the location to search to the I method! =head1 METHODS =head2 get_weather() This method invokes the L API to fetch some weather information and returns an arrayref containing one or many hashrefs with some high level weather information. If no location matching the search string is found, it returns I. When you construct a L object like shown in the synopsis above, the arrayref returned has the following structure: $VAR1 = [ { 'place' => 'Heidelberg, Germany', 'celsius' => '0', 'fahrenheit' => '32', 'temperature_celsius' => '0', 'temperature_fahrenheit' => '32' 'windchill_celsius' => '-6', 'windchill_fahrenheit' => '21', 'windspeed_kmh' => '26', 'windspeed_mph' => '16', 'wind' => '16 mph 26 km/h from the North Northeast', 'updated' => '11:50 AM Local on January 26, 2005', 'conditions' => 'Partly Cloudy', 'pressure' => '30.21 in / 1023.0 hPa', 'humidity' => '60', }, { 'place' => 'Heidelberg, KY', ... }, { 'place' => 'Heidelberg, MS', ... } ]; =head1 SEE ALSO See also documentation of L and L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html) =cut Weather-Com-0.5.3/lib/Weather/Com/Units.pm0000644000175000017500000000566510644511444021004 0ustar thomasthomas00000000000000package Weather::Com::Units; use 5.006; use strict; use warnings; use Class::Struct; our $VERSION = sprintf "%d.%03d", q$Revision: 1.5 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Weather::Com::Units consists almost only of pure data and no # significant logic has to be build in. Therefore, we simply use a # Class::Struct subclass. #------------------------------------------------------------------------ struct( distance => '$', precipitation => '$', pressure => '$', speed => '$', temperature => '$', ); #------------------------------------------------------------------------ # update wind data #------------------------------------------------------------------------ sub update { my $self = shift; my %units; if ( ref( $_[0] ) eq "HASH" ) { %units = %{ $_[0] }; } else { %units = @_; } # update data $self->distance( $units{ud} ); $self->precipitation( $units{ur} ); $self->pressure( $units{up} ); $self->speed( $units{us} ); $self->temperature( $units{ut} ); return 1; } 1; __END__ =pod =head1 NAME Weather::Com::Units - class representing units of measure =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, ); my $weather_finder = Weather::Com::Finder->new(%weatherargs); my @locations = $weather_finder->find('Heidelberg'); print "Speed is messured in ", $locations[0]->units()->speed(); print " for this location.\n"; =head1 DESCRIPTION Via I one can access the units of measure that correspond to the numeric values used in its parent location object. This class will B be updated automatically with each call to one of its methods. You need to call the C method of the parent object again to update your object. =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call the C method of one location object. =head1 METHODS =head2 distance() Returns the unit of distance used. =head2 precipitation() Returns the unit of precipitation used. =head2 pressure() Returns the unit of barometric pressure used. =head2 speed() Returns the unit of speed used. =head2 temperature() Returns the unit of temperature used. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com/L10N/0000755000000000000000000000000010721352300017116 5ustar rootroot00000000000000Weather-Com-0.5.3/lib/Weather/Com/L10N/en_us.pm0000644000175000017500000000031310354617153021450 0ustar thomasthomas00000000000000package Weather::Com::L10N::en_us; use base 'Weather::Com::L10N'; # have a cvs driven version... our $VERSION = sprintf "%d.%03d", q$Revision: 1.2 $ =~ /(\d+)/g; %Lexicon = ( '_AUTO' => 1, ); 1; Weather-Com-0.5.3/lib/Weather/Com/L10N/de.pm0000644000175000017500000001514110354625033020730 0ustar thomasthomas00000000000000package Weather::Com::L10N::de; use base 'Weather::Com::L10N'; # have a cvs driven version... our $VERSION = sprintf "%d.%03d", q$Revision: 1.6 $ =~ /(\d+)/g; %Lexicon = ( # some general things... 'n/a' => 'nicht verfügbar', 'N/A' => 'nicht verfügbar', 'Not Available' => 'nicht verfügbar', 'unknown' => 'unbekannt', 'NONE' => 'keine', 'day' => 'Tag', 'night' => 'Nacht', # first all about moon phases 'new' => 'Neumond', 'first quarter' => 'Viertelmond', 'full' => 'Vollmond', 'last quarter' => 'Dreiviertelmond', 'waning crescent' => 'abnehmend', 'waning gibbous' => 'abnehmend', 'waxing crescent' => 'zunehmend', 'waxing gibbous' => 'zunehmend', # about UV Index... 'extreme' => 'extrem', 'very high' => 'sehr hoch', 'high' => 'hoch', 'moderate' => 'moderat', 'low' => 'niedrig', # tendencies used for barometric pressure 'rising' => 'steigend', 'falling' => 'fallend', 'steady' => 'stabil', # all about weather conditions 'blowing dust' => 'Sandsturm', 'blowing dust and windy' => 'Sandsturm und windig', 'blowing snow' => 'Schneesturm', 'blowing snow and windy' => 'Schneestreiben und windig', 'clear' => 'klar', 'clear and windy' => 'klar und windig', 'cloudy' => 'bewölkt', 'cloudy and windy' => 'bewölkt und windig', 'drifting snow' => 'Schneetreiben', 'drizzle' => 'Nieselregen', 'fair' => 'heiter', 'fair and windy' => 'heiter und windig', 'fog' => 'Nebel', 'fog and windy' => 'Nebel und windig', 'haze' => 'trüb', 'haze and windy' => 'trüb und windig', 'heavy drizzle' => 'schwerer Sprühregen', 'heavy rain' => 'schwerer Regen', 'heavy rain and windy' => 'schwerer Regen und windig', 'heavy rain shower' => 'schwere Regenschauer', 'heavy rain shower and windy' => 'schwere Regenschauer und windig', 'heavy snow' => 'schwerer Schneefall', 'heavy snow and windy' => 'schwerer Schneefall und windig', 'heavy snow shower' => 'schwere Schneeschauer', 'heavy t-storm' => 'schweres Gewitter', 'heavy t-storm and windy' => 'schweres Gewitter und windig', 'light drizzle' => 'leichter Nieselregen', 'light drizzle and windy' => 'leichter Nieselregen und windig', 'light freezing drizzle' => 'leichter Eisregen', 'light freezing drizzle and fog' => 'leichter Eisregen und Nebel', 'light freezing rain' => 'leichter Eisregen', 'light rain' => 'leichter Regen', 'light rain shower' => 'leichte Regenschauer', 'light rain shower and windy' => 'leichte Regenschauer und windig', 'light rain and fog' => 'leichter Regen und Nebel', 'light rain and freezing rain' => 'leichter Regen und Eisregen', 'light rain with thunder' => 'leichter Regen und Donner', 'light rain and windy' => 'leichter Regen und windig', 'light snow' => 'leichter Schneefall', 'light snow grains' => 'leichte Schneegraupel', 'light snow shower' => 'leichte Schneeschauer', 'light snow shower and windy' => 'leichte Schneeschauer und windig', 'light snow and sleet' => 'leichter Schneefall und Schneeregen', 'light snow and windy' => 'leichter Schneefall und windig', 'mist' => 'Sprühregen', 'mostly cloudy' => 'überwiegend bewölkt', 'mostly cloudy and windy' => 'überwiegend bewölkt und windig', 'partial fog' => 'örtlich Nebel', 'partly cloudy' => 'teilweise bewölkt', 'partly cloudy and windy' => 'teilweise bewölkt und windig', 'patches of fog' => 'Nebelfelder', 'rain' => 'Regen', 'rain and sleet' => 'Regen und Schneeregen', 'rain and snow' => 'Schneeregen', 'rain shower' => 'Regenschauern', 'rain shower and windy' => 'Regenschauern und windig', 'rain and fog' => 'Regen und Nebel', 'rain and freezing rain' => 'Regen und Eisregen', 'rain and windy' => 'Regen und windig', 'sand' => 'Sand', 'sand and windy' => 'Sand und windig', 'shallow fog' => 'Bodennebel', 'showers in the vicinity' => 'örtliche Schauern', 'sleet' => 'Graupel', 'smoke' => 'Dunst', 'snow' => 'Schnee', 'snow and fog' => 'Schneefall und nebelig', 'snow and freezing rain' => 'Schnee und Eisregen', 'snow grains' => 'Schneegraupel', 'snow showers' => 'Schneeschauern', 'snow and windy and fog' => 'Schneefall, windig und nebelig', 'squalls' => 'Sturmböen', 'squalls and windy' => 'Wind und Sturmböen', 'sunny' => 'sonnig', 'sunny and windy' => 'sonnig und windig', 't-storm' => 'Gewitter', 't-storm and windy' => 'Gewitter und windig', 'thunder' => 'Gewitter', 'thunder and wintry mix' => 'Gewitter und wechselnde Winde', 'thunder in the vicinity' => 'örtliche Gewitter', 'unknown precip' => 'unbekannt', 'widespread dust' => 'verbreitet staubig', 'widespread dust and windy' => 'verbreitet staubig und windig', 'wintry mix' => 'wechselnde Winde', # wind directions long 'East' => 'Osten', 'East Northeast' => 'Ost Nordost', 'East Southeast' => 'Ost Südost', 'North' => 'Norden', 'Northeast' => 'Nordosten', 'North Northeast' => 'Nord Nordost', 'North Northwest' => 'Nord Nordwest', 'Northwest' => 'Nordwesten', 'South' => 'Süden', 'Souteast' => 'Südosten', 'South Southeast' => 'Süd Südost', 'South Southwest' => 'Süd Südwest', 'Southwest' => 'Südwesten', 'variable' => 'wechselnd', 'West' => 'Westen', 'West Northwest' => 'West Nordwest', 'West Southwest' => 'West Südwest', # wind directions short 'E' => 'O', 'ENE' => 'ONO', 'ESE' => 'OSO', 'N' => 'N', 'NE' => 'NO', 'NNE' => 'NNO', 'NNW' => 'NNW', 'NW' => 'NW', 'S' => 'S', 'SE' => 'SO', 'SSE' => 'SSO', 'SSW' => 'SSW', 'SW' => 'SW', 'VAR' => 'wechselnd', 'W' => 'W', 'WNW' => 'WNW', 'WSW' => 'WSW', ); 1; Weather-Com-0.5.3/lib/Weather/Com/L10N/fr.pm0000644000175000017500000001477310435574774021000 0ustar thomasthomas00000000000000package Weather::Com::L10N::fr; use base 'Weather::Com::L10N'; # have a cvs driven version... our $VERSION = sprintf "%d.%03d", q$Revision: 1.2 $ =~ /(\d+)/g; %Lexicon = ( # some general things... 'n/a' => 'n/d', 'N/A' => 'N/D', 'Not Available' => 'Pas Disponible', 'unknown' => 'inconnu', 'NONE' => 'AUCUN', 'day' => 'jour', 'night' => 'nuit', # first all about moon phases 'new' => 'nouvelle Lune', 'first quarter' => 'premier quartier', 'full' => 'pleine', 'last quarter' => 'dernier quartier', 'waning crescent' => 'premier croissant', 'waning gibbous' => 'gibbeuse', 'waxing crescent' => 'dernier croissant', 'waxing gibbous' => 'gibbeuse', # about UV Index... 'extreme' => 'extrême', 'very high' => 'très élévé', 'high' => 'élevé', 'moderate' => 'modéré', 'low' => 'négligeable', # tendencies used for barometric pressure 'rising' => 'augmente', 'falling' => 'diminue', 'steady' => 'stable', # all about weather conditions 'blowing dust' => 'tempête de sable', 'blowing snow' => 'tempête de neige', 'blowing snow and windy' => 'blizzard', 'clear' => 'clair ', 'cloudy' => 'nuageux', 'cloudy and windy' => 'nuageux et venteux', 'drizzle' => 'bruine', 'drifting snow' => 'accumulation de neige', 'fair' => 'clair', 'fair and windy' => 'clair et venteux', 'fog' => 'brouillard', 'haze' => 'smog', 'heavy drizzle' => 'bruine intense', 'heavy rain' => 'pluie intense', 'heavy rain and windy' => 'pluie intense et venteux', 'heavy snow' => 'neige intense', 'heavy snow and windy' => 'neige intense et venteux', 'heavy t-storm' => 'orage électrique intense', 'light drizzle' => 'faible bruine', 'light drizzle and windy' => 'bruine légère et venteux', 'light freezing drizzle' => 'bruine légère verglassante', 'light freezing rain' => 'faible pluie verglassante', 'light rain' => 'faible pluie', 'light rain shower' => 'faible averse de pluie', 'light rain and fog' => 'faible pluie et brouillard', 'light rain and freezing rain' => 'pluie faible et pluie erglassante', 'light rain with thunder' => 'faible pluie avec tonnerre', 'light rain and windy' => 'faible pluie et venteux', 'light snow' => 'faible neige', 'light snow shower' => 'faible averse de neige', 'light snow and sleet' => 'leichter Schneefall und Schneeregen', 'light snow and windy' => 'leichter Schneefall und windig', 'mist' => 'brume', 'mostly cloudy' => 'nuageux avec éclaircies', 'mostly cloudy and windy' => 'venteux et nuageux avec éclaircies', 'partial fog' => 'partiellement brumeux', 'partly cloudy' => 'partiellement nuageux', 'partly cloudy and windy' => 'partiellement nuageux et venteux', 'patches of fog' => 'partielles de brouillard', 'rain' => 'pluvieux', 'rain and sleet' => 'pluie et grésil', 'rain and snow' => 'pluie et neige', 'rain shower' => 'averse de pluie', 'rain and fog' => 'pluie et brouillard', 'rain and windy' => 'pluvieux et venteux', 'sand' => 'sable', 'shallow fog' => 'brouillard mince', 'showers in the vicinity' => 'averses à proximité', 'sleet' => 'grésil', 'smoke' => 'fumée', 'snow' => 'neige', 'snow and fog' => 'neige et brouillard', 'snow and freezing rain' => 'neige et pluie verglassante', 'snow grains' => 'neige intermittante', 'snow showers' => 'averse de neige', 'snow and windy and fog' => 'neige, brouillard et venteux', 'squalls and windy' => 'vent et grésil', 'sunny' => 'ensoleillé', 'sunny and windy' => 'ensoleillé et venteux', 't-storm' => 'Orage Électrique', 'thunder' => 'tonnerre', 'thunder in the vicinity' => 'tonnerre aux alentours', 'unknown precip' => 'précipitation inconnue', 'widespread dust' => 'vents de poussière', 'wintry mix' => 'conditions hivernales variables', # wind directions long 'East' => 'Est', 'East Northeast' => 'Nord-Est', 'East Southeast' => 'Ost Südost', 'North' => 'Nord', 'Northeast' => 'Nord-Est', 'North Northeast' => 'Nord-Nord-Est', 'North Northwest' => 'Nord-Nord-Ouest', 'Northwest' => 'Nord-Ouest', 'South' => 'Sud', 'Souteast' => 'Sud-Est', 'South Southeast' => 'Sud-Sud-Est', 'South Southwest' => 'Sud-Sud-Ouest', 'Southwest' => 'Sud-Ouest', 'variable' => 'variable', 'West' => 'Ouest', 'West Northwest' => 'Ouest-Nord-Ouest', 'West Southwest' => 'Ouest-Sud-Ouest', # wind directions short 'E' => 'O', 'ENE' => 'ONO', 'ESE' => 'OSO', 'N' => 'N', 'NE' => 'NO', 'NNE' => 'NNO', 'NNW' => 'NNW', 'NW' => 'NW', 'S' => 'S', 'SE' => 'SO', 'SSE' => 'SSO', 'SSW' => 'SSO', 'SW' => 'SO', 'VAR' => 'VAR', 'W' => 'O', 'WNW' => 'ONO', 'WSW' => 'OSO', ); 1; __END__ =pod =head1 NAME French language pack =head1 DESCRIPTION This is a Canadian French language pack to convert textual weather information from the original (English) text output to French. Thanks a lot to Jean-Philippe and Raphael to translate the weather descriptions into French! =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE Weather translations by: =over 4 =item * Jean-Philippe Goulet, Ejp.goulet@UMontreal.caE =item * Raphael Schmidt, Eraphael@intello.comE =back =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2005 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html)! =cut Weather-Com-0.5.3/lib/Weather/Com/Forecast.pm0000644000175000017500000001226010644511444021435 0ustar thomasthomas00000000000000package Weather::Com::Forecast; use 5.006; use strict; use Carp; use Data::Dumper; use Weather::Com::DayForecast; use base "Weather::Com::Cached"; our $VERSION = sprintf "%d.%03d", q$Revision: 1.6 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } unless ( $parameters{location_id} ) { die "You need to provide a location id!\n"; } # set some parameters to sensible values for a # current conditions object $parameters{current} = 0; $parameters{forecast} = 10; $parameters{links} = 0; # creating the SUPER instance my $self = $class->SUPER::new( \%parameters ); $self->{ID} = $parameters{location_id}; # getting first weather info $self->{WEATHER} = $self->get_weather( $self->{ID} ); $self->{DAYS} = undef; $self->_build_forecasts(); $self->{LSUP} = time(); return $self; } # end new() #------------------------------------------------------------------------ # refresh weather data #------------------------------------------------------------------------ sub refresh { my $self = shift; my $now = time(); # only refresh if last update has been more than 15 min ago if ( ( $now - $self->{LSUP} ) > 900 ) { $self->{WEATHER} = $self->get_weather( $self->{ID} ); $self->_build_forecasts(); $self->{LSUP} = $now; } return 1; } #------------------------------------------------------------------------ # access location data #------------------------------------------------------------------------ sub day { my $self = shift; my $day = shift; # 0 - 9 return 0 unless ( ( $day >= 0 ) && ( $day <= 9 ) ); $self->refresh(); return $self->{DAYS}->[$day]; } sub all { my $self = shift; $self->refresh(); if ( wantarray() ) { return @{ $self->{DAYS} }; } else { return $self->{DAYS}; } } #------------------------------------------------------------------------ # build up forecast hashes #------------------------------------------------------------------------ sub _build_forecasts { my $self = shift; # initialize $self->{DAYS} array of new DayForecast objects unless ( $self->{DAYS} ) { for ( my $i = 0 ; $i < 10 ; $i++ ) { $self->_debug("Initializing forecast for day $i"); $self->{DAYS}->[$i] = Weather::Com::DayForecast->new($self->{ARGS}); } } # then update the DayForecast objects # but put the timeszone into it... foreach my $day ( @{ $self->{WEATHER}->{dayf}->{day} } ) { my %args = (%{$day}, (zone => $self->{ARGS}->{zone})); $self->{DAYS}->[ $day->{d} ]->update(\%args); } return 1; } 1; __END__ =pod =head1 NAME Weather::Com::Forecast - class representing all available weather forecasts for one location =head1 SYNOPSIS [...] my @locations = $weather_finder->find('Heidelberg'); my $forecast = $locations[0]->forecast(); my $tomorrow = $forecast->day(1); print "Forecast for tomorrow:\n"; print " - tomorrow it's the ", $tomorrow->date()->date(), "\n"; print " - sunrise will be at ", $tomorrow->sunrise()->time(), "\n"; print " - maximum temperature will be ", $tomorrow->high(), "\n"; =head1 DESCRIPTION Using I objects is the way to access weather forecast information for one specific location (city) and 0 (today) to 9 days in the future. Each time you call the I objects' C method, you'll get an updated I object. This object is used to access the 10 I objects containing the actual data. =head1 CONSTRUCTOR =head2 new(hash or hashref) You usually would not construct an object of this class yourself. This is implicitely done when you call the C method of a I object. =head1 METHODS =head2 all() Returns an arrayref of all I objects if called in scalar context, an array if called in list context. =head2 day(day number) Returns the I object that corresponds to the day number you provided. The day number can be any number between 0 and 9. Day 0 is usually I. Due to a bug (I think it is one) in the I XOAP API, you may get the full forecast data of I if you call for day 0 just after midnight. I think this may have do something with the timezone. I have not fully investigated this issue, yet. Please contact me, if you have! =head1 SEE ALSO See also documentation of L, L, L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html) =cut Weather-Com-0.5.3/lib/Weather/Com/Location.pm0000644000175000017500000002441210644511444021441 0ustar thomasthomas00000000000000package Weather::Com::Location; use 5.006; use strict; use warnings; use Carp; use Time::Local; use Weather::Com::Cached; use Weather::Com::Units; use Weather::Com::CurrentConditions; use Weather::Com::Forecast; use base "Weather::Com::Cached"; our $VERSION = sprintf "%d.%03d", q$Revision: 1.10 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters = (); # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } unless ( $parameters{location_id} ) { die "You need to provide a location id!\n"; } # set some parameters to sensible values for a pure location # object $parameters{current} = 0; $parameters{forecast} = 0; $parameters{links} = 0; # creating the SUPER instance my $self = $class->SUPER::new( \%parameters ); $self->{ID} = $parameters{location_id}; $self->{NAME} = $parameters{location_name}; $self->{DEBUG} = $parameters{debug}; # the weather data will be initialized when the first call on # data is performed $self->{HEAD} = undef; $self->{WEATHER} = undef; $self->{CONDITIONS} = undef; $self->{FORECAST} = undef; $self->{LOCALTIME} = undef; $self->{SUNRISE} = undef; $self->{SUNSET} = undef; # last update will be used to trigger automatic refresh of # location data $self->{LSUP} = time(); bless( $self, $class ); # init object, add timezone to ARGS $self->{ARGS}->{lang} = $parameters{language} || 'en'; return $self; } # end new() #------------------------------------------------------------------------ # refresh weather data #------------------------------------------------------------------------ # this calls refresh if weather data is not initialized yet sub refresh { my $self = shift; if ( !$self->{WEATHER} || $self->_update ) { $self->{WEATHER} = $self->get_weather( $self->{ID} ); $self->_debug("Weather data refreshed!"); } return 1; } #------------------------------------------------------------------------ # access location data #------------------------------------------------------------------------ sub id { my $self = shift; return $self->{ID}; } sub name { my $self = shift; return $self->{NAME}; } sub units { my $self = shift; $self->refresh(); unless ( $self->{HEAD} ) { $self->{HEAD} = Weather::Com::Units->new(); } $self->{HEAD}->update( $self->{WEATHER}->{head} ); return $self->{HEAD}; } sub timezone { my $self = shift; unless ( $self->{WEATHER} ) { $self->{WEATHER} = $self->get_weather( $self->{ID} ); } $self->{ARGS}->{zone} = $self->{WEATHER}->{loc}->{zone}; return $self->{ARGS}->{zone}; } sub latitude { my $self = shift; $self->refresh(); return $self->{WEATHER}->{loc}->{lat}; } sub longitude { my $self = shift; $self->refresh(); return $self->{WEATHER}->{loc}->{lon}; } # localtime will be calculated because it does not make # any sense to used a cached time as current local time of # some location sub localtime { my $self = shift; $self->refresh(); unless ( $self->{LOCALTIME} ) { $self->{LOCALTIME} = Weather::Com::DateTime->new( $self->timezone() ); } return $self->{LOCALTIME}; } sub localtime_ampm { carp("Use of deprecated method 'localtime_ampm()'!"); carp("Please use 'localtime()->time_ampm()' instead."); my $self = shift; $self->refresh(); return $self->localtime()->time_ampm(); } sub sunrise { my $self = shift; $self->refresh(); unless ( $self->{SUNRISE} ) { $self->{SUNRISE} = Weather::Com::DateTime->new( $self->timezone() ); } $self->{SUNRISE}->set_time( $self->{WEATHER}->{loc}->{sunr} ); return $self->{SUNRISE}; } sub sunrise_ampm { carp("Use of deprecated method 'sunrise_ampm()'!"); carp("Please use 'sunrise()->time_ampm()' instead."); my $self = shift; $self->refresh(); return $self->sunrise()->time_ampm(); } sub sunset { my $self = shift; $self->refresh(); unless ( $self->{SUNSET} ) { $self->{SUNSET} = Weather::Com::DateTime->new( $self->timezone() ); } $self->{SUNSET}->set_time( $self->{WEATHER}->{loc}->{suns} ); return $self->{SUNSET}; } sub sunset_ampm { carp("Use of deprecated method 'sunset_ampm()'!"); carp("Please use 'sunset()->time_ampm()' instead."); my $self = shift; $self->refresh(); return $self->sunset()->time_ampm(); } sub current_conditions { my $self = shift; $self->refresh(); unless ( $self->{CONDITIONS} ) { $self->{CONDITIONS} = Weather::Com::CurrentConditions->new( $self->{ARGS} ); } return $self->{CONDITIONS}; } sub forecast { my $self = shift; $self->refresh(); unless ( $self->{FORECAST} ) { $self->{FORECAST} = Weather::Com::Forecast->new( $self->{ARGS} ); } return $self->{FORECAST}; } #------------------------------------------------------------------------ # internal methods go here #------------------------------------------------------------------------ sub _update { my $self = shift; # idea for check if now is one or more days after last update: # 1. transform last update to 00:00:00 of last updated date in # local time of location # 2. get 00:00:00 of today in local time of location # If both in epoc are equal, no update is needed, else we'll get # the new location information. my @lsup = gmtime( $self->timezone() * 3600 + $self->{LSUP} ); $lsup[0] = 0; $lsup[1] = 0; $lsup[2] = 0; my $local_epoc_lsup = timegm(@lsup); $self->{LSUP} = time(); my @now = gmtime( time() + ( $self->timezone() * 3600 ) ); $now[0] = 0; $now[1] = 0; $now[2] = 0; my $local_epoc_now = timegm(@now); if ( $local_epoc_now > $local_epoc_lsup ) { $self->_debug("should refresh location cache...\n"); return 1; } return 0; } 1; __END__ =pod =head1 NAME Weather::Com::Location - class representing one location and its weather =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, ); my $finder = Weather::Com::Finder->new(%weatherargs); # if you want an array of locations: my @locations = $finder->find('Heidelberg'); # or if you prefer an arrayref: my $locations = $finder->find('Heidelberg'); foreach my $location (@locations) { print "Found weather for city: ", $location->name(), "\n"; print "The city is located at: ", $location->latitude(), "deg N, ", $location->longitude(), "deg E\n"; print "Local time is ", $location->localtime()->time(), "\n"; print "Sunrise will be/has been at ", $location->sunrise()->time(), "\n"; } =head1 DESCRIPTION Using I objects is the way to access weather (and some location) information for one specific location (city). You get I objects by using a finder object (see L). I is a subclass of I. An instance of this class will update itself corresponding to the caching rules any time one of its methods is called. =head1 CONSTRUCTOR =head2 new(hash or hashref) The constructor will usually not be used directly because you get ready to use location objects by using a finder. If you ever want to instantiate location objects on your own, you have to provide the same configuration hash or hashref to the constructor you usually would provide to the C method of I. In addition it is necessary to add a hash element C to this config hash. The C has to be a valid I location id. =head1 METHODS =head2 id() Returns the location id used to instantiate this location. =head2 name() Returns the name of the location as provided by I. =head2 current_conditions() Returns a I object containing the current conditions of the location. The I object is instantiated with the first call of the C method. Please refer to L for further details. =head2 forecast() Returns a I object. Please refer to L for further details. =head2 latitude() Returns the latitude of the location. =head2 longitude() Returns the longitude of the location. =head2 localtime() Returns a Weather::Com::DateTime object containing the local time of the location. This value is evaluated each time you call this method. We do not use the value returned from I here because it does not make any sence to use a cached value to show the current time. =head2 localtime_ampm() B Returns the local time of the location. The time is returned in the format C. To get a 24 hour format use C instead. Sample: 10:30 PM =head2 sunrise() Returns a Weather::Com::DateTime object containing the time of sunrise. =head2 sunrise_ampm() B Returns the time of sunrise in 12 hour format (see C for details). =head2 sunset() Returns a Weather::Com::DateTime object containing the time of sunset. =head2 sunset_ampm() B Returns the time of sunset in 12 hour format (see C for details). =head2 timezone() Returns the timezone offset to GMT (without respecting daylight savings time). =head2 units() Returns a I object. Please refer to L for further details. =head1 SEE ALSO See also documentation of L, L, L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html) =cut Weather-Com-0.5.3/lib/Weather/Com/DateTime.pm0000644000175000017500000002257110644511444021371 0ustar thomasthomas00000000000000package Weather::Com::DateTime; use 5.006; use strict; no warnings; #use warnings; use Carp; use Data::Dumper; use Time::Format; use Time::Local; our $VERSION = sprintf "%d.%03d", q$Revision: 1.7 $ =~ /(\d+)/g; our %months = ( 'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11 ); #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; $self->{EPOC} = timelocal(gmtime(time())); if (@_) { $self->{ZONE} = shift; } else { $self->{ZONE} = 0; } bless( $self, $class ); return $self; } # end new() #------------------------------------------------------------------------ # very special setter methods #------------------------------------------------------------------------ # this method assumes that time is irrelevant => no conversion needed sub set_date { my $self = shift; my $datestring = shift; my $month = substr( $datestring, 0, 3 ); my $day = substr( $datestring, 3 ); my @now = gmtime(); my $localmidnight = undef; eval { $localmidnight = timelocal( 0, 0, 0, $day, $months{$month}, $now[5] ); }; if ($@) { croak($@); } my $gmtime = $localmidnight - ( $self->{ZONE} * 3600 ); $self->{EPOC} = $gmtime; return $self->{EPOC}; } # this method assumes that the date is irrelevant! sub set_time { my $self = shift; my $timestring = shift; my $colon = index( $timestring, ":" ); my $hour = substr( $timestring, 0, $colon ); my $minute = substr( $timestring, $colon + 1, 2 ); my $ampm = substr( $timestring, $colon + 4 ); $hour += 12 if ( lc($ampm) eq "pm" ); my @now = gmtime(); my $localtime = undef; eval { $localtime = timelocal( 0, $minute, $hour, $now[3], $now[4], $now[5] ); }; if ($@) { croak($@); } if ($@) { croak($@); } my $gmtime = $localtime - ( $self->{ZONE} * 3600 ); $self->{EPOC} = $gmtime; return $self->{EPOC}; } sub set_lsup { my $self = shift; # this method returns epoc for gmt corresponding to # the provided last update value (lsup) my $lsup = shift; my ( $date, $time, $ampm, $zone ) = split( / /, $lsup ); my ( $mon, $mday, $year ) = split( "/", $date ); my ( $hour, $min ) = split( /:/, $time ); $year += 100; $hour += 12 if ( $ampm eq "PM" ); my $localtime; eval { $localtime = timelocal( 0, $min, $hour, $mday, $mon - 1, $year ); }; if ($@) { croak($@); } my $gmtime = $localtime - ( $self->{ZONE} * 3600 ); $self->{EPOC} = $gmtime; return $self->{EPOC}; } #------------------------------------------------------------------------ # Access date and time #------------------------------------------------------------------------ # epoc is alway GMT sub epoc { my $self = shift; if (@_) { $self->{EPOC} = timelocal(gmtime(shift)); } # we have to fake up $epoc # because of Time::Format behaviour, $epoc has to be in localtime # and to be able to provide a GMT conform epoc, we have to transform # this here return timegm(localtime($self->{EPOC})); } sub formatted { my $self = shift; my $format = shift; # Time::Format always returns localtime of the server the # script runs on. We have to eliminate this. my $localepoc = $self->{EPOC} + ( $self->{ZONE} * 3600 ); return $time{ $format, $localepoc }; } #------------------------------------------------------------------------ # Access date #------------------------------------------------------------------------ sub weekday { my $self = shift; return $self->formatted('Weekday'); } sub date { my $self = shift; return $self->formatted('d. Month yyyy'); } sub year { my $self = shift; return $self->formatted('yyyy'); } sub month { my $self = shift; return $self->formatted('Month'); } sub mon { my $self = shift; return $self->formatted('mm{on}'); } sub day { my $self = shift; return $self->formatted('dd'); } #------------------------------------------------------------------------ # Access time #------------------------------------------------------------------------ sub time { my $self = shift; return $self->formatted('hh:mm'); } sub time_ampm { my $self = shift; return $self->formatted('H:mm AM'); } #------------------------------------------------------------------------ # 24hour HACK # This method overwrites Time::Local::timelocal # That is to easily workaround a problem between weather.com and # the original 'timelocal' function: 'timelocal' only accepts hours # from 0 to 23, but weather.com works from 0 to 24... #------------------------------------------------------------------------ sub timelocal { my ( $sec, $min, $hour, $mday, $mon, $year ) = @_; my $twentyforhack = 0; if ( $hour == 24 ) { $hour = 23; $twentyforhack = 1; } my $epoc = &Time::Local::timelocal( 0, $min, $hour, $mday, $mon, $year ); if ($twentyforhack) { $epoc += 3600; } return $epoc; } 1; __END__ =pod =head1 NAME Weather::Com::DateTime - date and time class =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::DateTime; my $gmt_offset = 1; # e.g. for Germany in winter my $datetime = Weather::Com::DateTime->new($gmt_offset); $datetime->set_lsup('02/25/05 11:21 PM Local Time'); print "This is the date '02/25/05 11:21 PM' in Germany:\n"; print "Epoc: ", $datetime->epoc(), "\n"; print "GMT (UTC): ". gmtime($datetime->epoc()). "\n"; print "My local time: ". localtime($datetime->epoc()). "\n"; print "And finally German time: ", $datetime->time(), " o'clock at ", $datetime->date(), "\n\n"; =head1 DESCRIPTION I objects are used to encapsulate a date or time provided by the OO interface (e.g. localtime, sunrise, sunset, etc.). This is done because there are many ways to use a date or time and to present it in your programs using I. This class provides some predefined formats for date and time but also enables you to easily define your own ones. These objects always represent the local time of a I object. That is, if you have a location object for New York City and your server running the weather script is located in Los Angeles, for example, this line print "Sunrise at: ", $location->sunrise()->time(), "\n"; will print the time of sunrise (in 24h format) in EST and not corresponding to the timezone of Los Angeles! If you'd like to now what this is in GMT you could call print "Sunrise at: ". gmtime($location->sunrise()->epoc()). "\n"; or if you want to know when the sun rises at the location in your servers local time than just call print "Sunrise at: ". localtime($location->sunrise()->epoc()). "\n"; There are two ways to get your own date or time format: =over 4 =item 1. You use the C method and provide a format string to it. =item 2. If you'd like to define your own C or C method, simply change the corresponding methods. What you can change in which way without destroying the whole class, is described in section B. =back =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call one of the OO interfaces date or time methods. The constructor can take a GMT offset in positive or negative hours. If one calls the constructor without any GMT offset, we assume you want a GMT object. =head1 METHODS =head2 epoc(epoc seconds) With this method you can set the date and time using epocs (GMT) directly. It returns the currently set epoc seconds (GMT). =head2 formatted(format) This method returns a date or time formatted in the way you ask for and corresponding to the local time of the parent object. The C you provide to this method has to be a valid I format. For details please refer to L. =head2 set_date(date) With this method one can set the date of the object using an input format like C which is the 13th of february of the current year. Using this method, the time is set to I<00:00>. The year is the current one. =head2 set_time(time) With this method one can set the time of the object using an input format like C<8:30 AM>. The date is set to the current date of the host the script is running on. =head2 set_lsup(lsup) With this method one can set the date of the object using the I's special last update format that is like C<2/12/05 4:50 PM Local Time>. =head2 date() Returns the date in the format C<1. February 2005>. =head2 time() Returns the time in the format C<22:15>. =head2 time_ampm() Returns the time in the format C<10:15 PM>. =head2 weekday() Returns the day of week with like C. =head2 day() Returns the day in month. =head2 month() Returns the name of the month. =head2 mon() Returns the number of the month =head2 year() Returns the year (4 digits). =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com/AirPressure.pm0000644000175000017500000000674510644511444022146 0ustar thomasthomas00000000000000package Weather::Com::AirPressure; use 5.006; use strict; use warnings; use Weather::Com::L10N; use base 'Weather::Com::Object'; our $VERSION = sprintf "%d.%03d", q$Revision: 1.10 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } my $self = $class->SUPER::new( \%parameters ); # getting first weather information $self->{PRESSURE} = -1; $self->{TENDENCY} = 'unknown'; return $self; } # end new() #------------------------------------------------------------------------ # update barometric data #------------------------------------------------------------------------ sub update { my $self = shift; my %bar; if ( ref( $_[0] ) eq "HASH" ) { %bar = %{ $_[0] }; } else { %bar = @_; } unless ( $bar{r} ) { $self->{PRESSURE} = -1; } else { $self->{PRESSURE} = lc( $bar{r} ); } unless ( $bar{d} ) { $self->{TENDENCY} = "unknown"; } else { $self->{TENDENCY} = lc( $bar{d} ); } return 1; } #------------------------------------------------------------------------ # access moon data #------------------------------------------------------------------------ sub pressure { my $self = shift; return $self->{PRESSURE}; } sub tendency { my $self = shift; my $language = shift; return $self->get_language_handle($language)->maketext(lc($self->{TENDENCY}) ); } 1; __END__ =pod =head1 NAME Weather::Com::AirPressure - class containing barometric pressure data =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, ); my $weather_finder = Weather::Com::Finder->new(%weatherargs); my @locations = $weather_finder->find('Heidelberg'); my $currconditions = $locations[0]->current_conditions(); print "Barometric pressure is ", $currconditions->pressure()->pressure(), "\n"; print "and it's ", $currconditions->pressure()->tendency(), "\n"; =head1 DESCRIPTION Via I one can access the barometric pressure and its tendency. This class will B be updated automatically with each call to one of its methods. You need to call the C method of the parent object again to update your object. =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call the C method of one current conditions or forecast object. =head1 METHODS =head2 pressure() Returns the barometric pressure. =head2 tendency([$language]) Returns the tendency of the barometric pressure. This attribute is I. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com/Cached.pm0000644000175000017500000003313410644511444021041 0ustar thomasthomas00000000000000package Weather::Com::Cached; use 5.006; use strict; use warnings; use Storable qw(lock_store lock_retrieve); use Data::Dumper; use Weather::Com::Base; use base "Weather::Com::Base"; our $VERSION = sprintf "%d.%03d", q$Revision: 1.7 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; # getting the parameters from @_ my %parameters = (); if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } # creating the SUPER instance my $self = $class->SUPER::new( \%parameters ); # where to put the cache files? if ( $parameters{cache} ) { $self->{PATH} = $parameters{cache}; } else { $self->{PATH} = "."; } # check if cache is writable unless ( -w $self->{PATH} ) { die ref($self) . ": path to cache not writable: " . $self->{PATH} . "!\n"; } # parameter cache $self->{PARAMS} = undef; bless( $self, $class ); return $self; } #------------------------------------------------------------------------ # searching for location codes # Weather::Com::Cached will not search on the web for location codes # that are cached in "locations.dat" #------------------------------------------------------------------------ sub search { my $self = shift; my $place = shift; my $locations = undef; # set error and die if no place provided unless ($place) { die ref($self), ": ERROR Please provide a location to search for!\n"; } # 1st, look for locations in cache my $locations_cached = undef; my $loccachefile = $self->{PATH} . "/locations.dat"; if ( -f $loccachefile ) { $locations_cached = lock_retrieve($loccachefile); if ( $locations_cached->{ lc($place) } ) { $self->_debug("Found locations in location cache."); $locations = $locations_cached->{ lc($place) }; } else { $self->_debug("No direct match found in cache."); } } # 2nd, if nothing has been found, search the Web and store data unless ( keys %{$locations} ) { $locations = $self->SUPER::search($place); if ($locations) { $self->_debug("Found locations on the web."); $self->_debug("Writing locations to cache."); # first save the direct search result $locations_cached->{ lc($place) } = $locations; # then store for each result the name => key hash foreach my $location ( keys %{$locations} ) { my $name = $locations->{$location}; $locations_cached->{ lc($name) } = { $location => $name }; } # then store in cache file unless ( lock_store( $locations_cached, $loccachefile ) ) { die ref($self), ": ERROR I/O problem while storing locations cachefile!"; } } elsif ($locations_cached) { # if neither the cache nor the weather.com server did return # an exact match, try a regexp search over the cache. $self->_debug("No direct match in cache or on the web."); $self->_debug("Trying regexp search."); $locations = {}; foreach my $location ( keys %{$locations_cached} ) { if ( $location =~ /$place/i ) { $self->_debug( "MATCH: '$place' matches location '$location'"); %{$locations} = ( %{$locations}, %{ $locations_cached->{ lc($location) } } ); } } } } return $locations; } #------------------------------------------------------------------------ # getting data from weather.com #------------------------------------------------------------------------ sub get_weather { my $self = shift; my $locid = shift; $self->_debug("Trying to get data for $locid"); unless ($locid) { die ref($self), ": Please provide a location id!\n"; } # try to load an existing cache file my $cachefile = $self->{PATH} . "/" . $self->{UNITS} . "_$locid.dat"; my $weathercache = {}; if ( -f $cachefile ) { $weathercache = lock_retrieve($cachefile); } else { $self->_debug("No cache file found."); } # find out which data is wanted by the modules user and # which parts of that are in the cache or must be # loaded from the web if ($weathercache) { $self->_debug("Cache file found."); # save parameters to be able to reset them at the end of # this method... $self->_store_params(); # load uncached or old requested data if ( $self->{PARAMS}->{CC} ) { if ( $weathercache->{cc} && !$self->_older_than( 30, $weathercache->{cc}->{cached} ) ) { $self->{CC} = 0; $self->_debug("Turning off 'cc' update. Cache is good enough."); } } if ( $self->{PARAMS}->{FORECAST} ) { if ( $weathercache->{dayf} ) { my $no_forecastdays; # number of days to be forecasted if ( ref( $weathercache->{dayf}->{day} ) eq "HASH" ) { $no_forecastdays = 1; } else { $no_forecastdays = $#{ $weathercache->{dayf}->{day} } + 1; } if ( ( $self->{PARAMS}->{FORECAST} == $no_forecastdays ) && !$self->_older_than( 120, $weathercache->{dayf}->{cached} ) ) { $self->{FORECAST} = 0; $self->_debug( "Turning off 'forecast' update. Cache is good enough."); } } } } $self->_debug("Params for cache conditions: ".Dumper($self->{PARAMS})); # only update weathercache if a current conditions update or a # forecast update is necessary or the location data is older than # 15 minutes. if ( $self->{CC} or $self->{FORECAST} or !$weathercache or $self->_older_than( 15, $weathercache->{loc}->{cached} ) ) { $self->_debug("All conditions met. Fetching weather from web."); my $weather = $self->SUPER::get_weather($locid); foreach ( keys %{$weather} ) { $weathercache->{$_} = $weather->{$_}; if ( ref( $weather->{$_} ) ) { $weathercache->{$_}->{cached} = $self->_cache_time(); } } # save data to cache file unless ( lock_store( $weathercache, $self->{PATH} . "/" . $self->{UNITS} . "_$locid.dat" ) ) { die ref($self), ": ERROR I/O problem while storing cachefile!"; } } $self->_reset_params(); $self->_debug( Dumper($weathercache) ); return $weathercache; } #------------------------------------------------------------------------ # store and reset search parameters #------------------------------------------------------------------------ sub _store_params { my $self = shift; my %params = ( 'PROXY' => $self->{PROXY}, 'TIMEOUT' => $self->{TIMEOUT}, 'DEBUG' => $self->{DEBUG}, 'PARTNER_ID' => $self->{PARTNER_ID}, 'LICENSE_KEY' => $self->{LICENSE_KEY}, 'UNITS' => $self->{UNITS}, 'CC' => $self->{CC}, 'FORECAST' => $self->{FORECAST}, 'LINKS' => $self->{LINKS}, ); $self->{PARAMS} = \%params; return 1; } sub _reset_params { my $self = shift; $self->{PROXY} = $self->{PARAMS}->{PROXY}; $self->{TIMEOUT} = $self->{PARAMS}->{TIMEOUT}; $self->{DEBUG} = $self->{PARAMS}->{DEBUG}; $self->{PARTNER_ID} = $self->{PARAMS}->{PARTNER_ID}; $self->{LICENSE_KEY} = $self->{PARAMS}->{LICENSE_KEY}; $self->{UNITS} = $self->{PARAMS}->{UNITS}; $self->{CC} = $self->{PARAMS}->{CC}; $self->{FORECAST} = $self->{PARAMS}->{FORECAST}; $self->{LINKS} = $self->{PARAMS}->{LINKS}; return 1; } sub _older_than { my $self = shift; my $caching_timeframe = shift; my $cached = shift || 0; my $now = $self->_cache_time(); if ( $cached < ( $now - $caching_timeframe * 60 ) ) { return 1; } else { return 0; } } # this method encapsulates the time() method to be able to test # with Test::MockObject and setting a fixed time sub _cache_time { my $self = shift; return time(); } 1; __END__ =pod =head1 NAME Weather::Com::Cached - Perl extension for getting weather information from I =head1 SYNOPSIS use Data::Dumper; use Weather::Com::Cached; # define parameters for weather search my %params = ( 'cache' => '/tmp/weathercache', 'current' => 1, 'forecast' => 3, 'links' => 1, 'units' => 's', 'proxy' => 'http://proxy.sonstwo.de', 'timeout' => 250, 'debug' => 1, 'partner_id' => 'somepartnerid', 'license' => '12345678', ); # instantiate a new weather.com object my $cached_weather = Weather::Com::Cached->new(%params); # search for locations called 'Heidelberg' my $locations = $cached_weather->search('Heidelberg') or die "No location found!\n"; # and then get the weather for each location found foreach (keys %{$locations}) { my $weather = $cached_weather->get_weather($_); print Dumper($weather); } =head1 DESCRIPTION I is a Perl module that provides low level OO interface to gather all weather information that is provided by I. Please refer to L for the high level interfaces. This module implements the caching business rules that apply to all applications programmed against the I API of I. Except from the I parameter to be used while instantiating a new object instance, this module has the same API than I. It's only a simple caching wrapper around it. The caching mechanism for location searches is very simple. We assume that location codes on I will never change. Therefore, a search string that has been successfully used once to search for locations will never cause another search on the web. Each location search results will be stored in the file C. If you want to refresh your locations cache, simply delete this file. Although it's really simple, the module uses I methods I and I to implement shared locking for reading cache files and exclusive locking for writing to chache files. By this way the same cache files should be able to be used by several application instances using I. You'll need to register at I to to get a free partner id and a license key to be used within all applications that you want to write against I's I interface. L =head1 CHANGES The location caching mechanism has been extended with version 0.4. Up to V0.4 searches were stored this way: $locations_cache = { 'New York' => { 'USNY1000' => 'New York/La Guardia Arpt, NY', 'USNY0998' => 'New York/Central Park, NY', 'USNY0999' => 'New York/JFK Intl Arpt, NY', 'USNY0996' => 'New York, NY' }, } This has changed the way it does not only store a search_string => locations hash. The cache now also stores a hash for B location name found: $locations_cache => { 'new york' => { 'USNY1000' => 'New York/La Guardia Arpt, NY', 'USNY0998' => 'New York/Central Park, NY', 'USNY0999' => 'New York/JFK Intl Arpt, NY', 'USNY0996' => 'New York, NY' }, 'new york/central park, ny' => { 'USNY0998' => 'New York/Central Park, NY' }, 'new york/la guardia arpt, ny' => { 'USNY1000' => 'New York/La Guardia Arpt, NY' }, 'new york, ny' => { 'USNY0996' => 'New York, NY' }, 'new york/jfk intl arpt, ny' => { 'USNY0999' => 'New York/JFK Intl Arpt, NY' }, } The new mechanism has the following advantages: =over 4 =item 1. The new chaching mechanism is B =item 2. This caching mechanism is a workaround one problem with I's XOAP API. Their server does not understand any search string with a '/' in it - no matter wether the '/' is URL encoded or not! This way, if you have searched for I once, you'll then also get a result for direct calls to I. =item 3. The new mechanism also allows searches for I substrings. A search for I will return the I location and if you simply search I, you'll get anything containing I. No matter if it's in the cache or not. Only if you specify B the name of a location in the cache, only this location is shown. =back =head1 CONSTRUCTOR =head2 new(hash or hashref) This constructor takes the same hash or hashref as I does. Please refer to that documentation for further details. Except from the I's parameters this constructor takes a parameter I which defines the path to a directory into which all cache files will be put. The cache directory defaults to '.'. =head1 METHODS =head2 search(search string) The C method has the same interface as the one of I. The difference is made by the caching. The search is performed in the following order: =over 4 =item 1. If there's a direct match in the locations cache, return the locations from the cache. =item 2. If not, if there's a direct match on the web, return the locations found on the web and write the search result to the cache. =item 3. If not, try a regexp search over all cached search strings and location names. This will return each location that matches the search string. =back The rest is all the same as for I. =head1 SEE ALSO See also documentation of L and L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I! L =cut Weather-Com-0.5.3/lib/Weather/Com/Object.pm0000644000175000017500000000321410354617066021101 0ustar thomasthomas00000000000000package Weather::Com::Object; use 5.006; use strict; use warnings; use Carp; #-------------------------------------------------------------------- # Define some globals #-------------------------------------------------------------------- our $VERSION = sprintf "%d.%03d", q$Revision: 1.3 $ =~ /(\d+)/g; my %LH = (); # our language handles #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } $self = bless( $self, $class ); # creating the SUPER instance $self->{ARGS} = \%parameters; if ( $parameters{lang} ) { $self->{LANGUAGE} = $parameters{lang}; } else { $self->{LANGUAGE} = 'en_US'; } return $self; } # end new() #------------------------------------------------------------------------ # update barometric data #------------------------------------------------------------------------ sub update { my $self = shift; return 1; } #------------------------------------------------------------------------ # Get a language handle #------------------------------------------------------------------------ sub get_language_handle { my $self = shift; my $lang = shift || $self->{LANGUAGE}; # check if we already have an open handle unless (defined($LH{$lang})) { $LH{$lang} = Weather::Com::L10N->get_handle($lang) or croak ("Language?"); } # return language handle return $LH{$lang}; } 1; Weather-Com-0.5.3/lib/Weather/Com/UVIndex.pm0000644000175000017500000000732210644511444021214 0ustar thomasthomas00000000000000package Weather::Com::UVIndex; use 5.006; use strict; use warnings; use Weather::Com::L10N; use base 'Weather::Com::Object'; our $VERSION = sprintf "%d.%03d", q$Revision: 1.9 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } my $self = $class->SUPER::new( \%parameters ); # getting first weather information $self->{INDEX} = -1; $self->{DESCRIPTION} = 'unknown'; return $self; } # end new() #------------------------------------------------------------------------ # update wind data #------------------------------------------------------------------------ sub update { my $self = shift; my %uv; if ( ref( $_[0] ) eq "HASH" ) { %uv = %{ $_[0] }; } else { %uv = @_; } unless ( $uv{i} ) { $self->{INDEX} = -1; $self->{DESCRIPTION} = "unknown"; } else { $self->{INDEX} = $uv{i}; $self->{DESCRIPTION} = lc( $uv{t} ); } return 1; } #------------------------------------------------------------------------ # accessor methods #------------------------------------------------------------------------ sub index { my $self = shift; return $self->{INDEX}; } sub description { my $self = shift; my $language = shift; return $self->get_language_handle($language) ->maketext( $self->{DESCRIPTION} ); } 1; __END__ =pod =head1 NAME Weather::Com::UVIndex - class containing the uv index data =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'language' => 'de', ); my $weather_finder = Weather::Com::Finder->new(%weatherargs); my @locations = $weather_finder->find('Heidelberg'); my $currconditions = $locations[0]->current_conditions(); print "The current uv index is ", $currconditions->uv_index()->index(), "\n"; print "This is relatively ", $currconditions->uv_index()->description(), "\n"; =head1 DESCRIPTION Via I one can access the uv index and its description (whether it's high or low). An uv index is usually an object belonging to current conditions or to a forecast (not implemented yet). This class will B be updated automatically with each call to one of its methods. You need to call the C method of the parent object again to update your object. =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call the uv_index() method of one current conditions or forecast object. =head1 METHODS =head2 index() Returns the uv index (number). =head2 description([$language]) Returns the description whether this index is high or low. This description is translated if you specified the I option as argument while instantiating your I. This attribute is I. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com/L10N.pm0000644000175000017500000000032710354617153020344 0ustar thomasthomas00000000000000package Weather::Com::L10N; use 5.006; use Carp; use base 'Locale::Maketext'; # have a cvs driven version... our $VERSION = sprintf "%d.%03d", q$Revision: 1.3 $ =~ /(\d+)/g; %Lexicon = ( '_AUTO' => 1, ); 1;Weather-Com-0.5.3/lib/Weather/Com/Wind.pm0000644000175000017500000001155710644511444020600 0ustar thomasthomas00000000000000package Weather::Com::Wind; use 5.006; use strict; use warnings; use Weather::Com::L10N; use Weather::Com::Base qw/convert_winddirection/; use base 'Weather::Com::Object'; our $VERSION = sprintf "%d.%03d", q$Revision: 1.10 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } my $self = $class->SUPER::new( \%parameters ); # getting first weather information $self->{SPEED} = -1; $self->{GUST} = -1; $self->{DIR_DEGREES} = -1; $self->{DIR_TXT} = 'N/A'; return $self; } # end new() #------------------------------------------------------------------------ # update wind data #------------------------------------------------------------------------ sub update { my $self = shift; my %wind; if ( ref( $_[0] ) eq "HASH" ) { %wind = %{ $_[0] }; } else { %wind = @_; } # handle non existent wind data unless ( $wind{s} ) { $self->{SPEED} = -1; $self->{GUST} = -1; $self->{DIR_DEGREES} = -1; $self->{DIR_TXT} = 'N/A'; } elsif ( lc( $wind{s} ) eq "calm" ) { # special rules apply if speed is non-numeric $self->{SPEED} = 0; $self->{GUST} = 0; $self->{DIR_DEGREES} = -1; $self->{DIR_TXT} = 'N/A'; } else { # else update object data $self->{SPEED} = $wind{s}; $self->{GUST} = $wind{gust}; $self->{DIR_DEGREES} = $wind{d}; $self->{DIR_TXT} = $wind{t}; } } #------------------------------------------------------------------------ # accessor methods #------------------------------------------------------------------------ sub speed { my $self = shift; return $self->{SPEED}; } sub maximum_gust { my $self = shift; return $self->{GUST}; } sub direction_degrees { my $self = shift; return $self->{DIR_DEGREES}; } sub direction_short { my $self = shift; my $language = shift; return $self->get_language_handle($language)->maketext( $self->{DIR_TXT} ); } sub direction_long { my $self = shift; my $language = shift; my $dir = convert_winddirection( $self->{DIR_TXT} ); return $self->get_language_handle($language)->maketext($dir); } 1; __END__ =pod =head1 NAME Weather::Com::Wind - class containing wind data =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'language' => 'de', ); my $weather_finder = Weather::Com::Finder->new(%weatherargs); my @locations = $weather_finder->find('Heidelberg'); my $currconditions = $locations[0]->current_conditions(); print "Wind comes from ", $currconditions->wind()->direction_long(), "\n"; print "and its speed is", $currconditions->wind()->speed(), "\n"; =head1 DESCRIPTION Via I one can access speed and direction (in degrees, short and long textual description) of the wind. Wind is usually an object belonging to current conditions or to a forecast (not implemented yet). This class will B be updated automatically with each call to one of its methods. You need to call the C method of the parent object again to update your object. =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call the C method of one current conditions or forecast object. =head1 METHODS =head2 speed() Returns the wind speed. =head2 direction_degrees() Returns the direction of the wind in degrees. =head2 direction_short([$language]) Returns the direction of the wind as wind mnemonic (N, NW, E, etc.). These directions are being translated if you specified a language in the parameters you provided to your I. This attribute is I. =head2 direction_long([$language]) Returns the direction of the wind as long textual description (North, East, Southwest, etc.). These directions are being translated if you specified a language in the parameters you provided to your I. This attribute is I. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com/CurrentConditions.pm0000644000175000017500000002102210644511444023337 0ustar thomasthomas00000000000000package Weather::Com::CurrentConditions; use 5.006; use strict; use warnings; use Weather::Com::AirPressure; use Weather::Com::DateTime; use Weather::Com::Moon; use Weather::Com::UVIndex; use Weather::Com::Wind; use Weather::Com::L10N; use base "Weather::Com::Cached"; our $VERSION = sprintf "%d.%03d", q$Revision: 1.14 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } unless ( $parameters{location_id} ) { die "You need to provide a location id!\n"; } # set some parameters to sensible values for a # current conditions object $parameters{current} = 1; $parameters{forecast} = 0; $parameters{links} = 0; # creating the SUPER instance my $self = $class->SUPER::new( \%parameters ); $self->{ID} = $parameters{location_id}; # getting first weather information $self->{BAR} = undef; $self->{UV} = undef; $self->{MOON} = undef; $self->{WEATHER} = $self->get_weather( $self->{ID} ); $self->{WIND} = undef; $self->{LAST_XML_UPDATE} = undef; # remember instantiation time (and later last update time) $self->{LSUP} = time(); return $self; } # end new() #------------------------------------------------------------------------ # refresh weather data #------------------------------------------------------------------------ sub refresh { my $self = shift; my $now = time(); # only refresh if last update has been more than 15 min ago if ( ( $now - $self->{LSUP} ) > 900 ) { $self->{WEATHER} = $self->get_weather( $self->{ID} ); $self->{LSUP} = $now; } return 1; } #------------------------------------------------------------------------ # access location data #------------------------------------------------------------------------ sub id { my $self = shift; return $self->{ID}; } sub name { my $self = shift; $self->refresh(); return $self->{WEATHER}->{loc}->{dnam}; } sub icon { my $self = shift; return $self->{WEATHER}->{cc}->{icon}; } sub description { my $self = shift; my $language = shift; $self->refresh(); return $self->get_language_handle($language) ->maketext( lc( $self->{WEATHER}->{cc}->{t} ) ); } sub temperature { my $self = shift; $self->refresh(); return $self->{WEATHER}->{cc}->{tmp}; } sub windchill { my $self = shift; $self->refresh(); return $self->{WEATHER}->{cc}->{flik}; } sub humidity { my $self = shift; $self->refresh(); return $self->{WEATHER}->{cc}->{hmid}; } sub pressure { my $self = shift; $self->refresh(); unless ( $self->{BAR} ) { $self->{BAR} = Weather::Com::AirPressure->new( $self->{ARGS} ); } $self->{BAR}->update( $self->{WEATHER}->{cc}->{bar} ); return $self->{BAR}; } sub dewpoint { my $self = shift; $self->refresh(); return $self->{WEATHER}->{cc}->{dewp}; } sub moon { my $self = shift; $self->refresh(); unless ( $self->{MOON} ) { $self->{MOON} = Weather::Com::Moon->new( $self->{ARGS} ); } $self->{MOON}->update( $self->{WEATHER}->{cc}->{moon} ); return $self->{MOON}; } sub uv_index { my $self = shift; $self->refresh(); unless ( $self->{UV} ) { $self->{UV} = Weather::Com::UVIndex->new( $self->{ARGS} ); } $self->{UV}->update( $self->{WEATHER}->{cc}->{uv} ); return $self->{UV}; } sub visibility { my $self = shift; $self->refresh(); return $self->{WEATHER}->{cc}->{vis}; } sub wind { my $self = shift; $self->refresh(); unless ( $self->{WIND} ) { $self->{WIND} = Weather::Com::Wind->new( $self->{ARGS} ); } $self->{WIND}->update( $self->{WEATHER}->{cc}->{wind} ); return $self->{WIND}; } sub observatory { my $self = shift; $self->refresh(); return $self->{WEATHER}->{cc}->{obst}; } sub last_updated { my $self = shift; $self->refresh(); unless ( $self->{LAST_XML_UPDATE} ) { $self->{LAST_XML_UPDATE} = Weather::Com::DateTime->new( $self->{ARGS}->{zone} ); } $self->{LAST_XML_UPDATE}->set_lsup( $self->{WEATHER}->{cc}->{lsup} ); return $self->{LAST_XML_UPDATE}; } 1; __END__ =pod =head1 NAME Weather::Com::CurrentConditions - class containing current weather conditions =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'language' => 'de', ); my $finder = Weather::Com::Finder->new(%weatherargs); # if you want an array of locations: my @locations = $finder->find('Heidelberg'); my $conditions = $locations[0]->current_conditions(); print "Found weather for city: ", $location->name(), "\n"; print "Current conditions are ", $conditions->description(), "\n"; print "Current temperature is ", $conditions->temperature(), "°C\n"; print "... as found out by observatory ", $conditions->observatory(), "\n"; =head1 DESCRIPTION Using I objects provide current weather conditions of its parent object (a location) to you. You get I objects by calling the method C of your location object. I is a subclass of I. An instance of this class will update itself corresponding to the caching rules any time one of its methods is called. =head1 CONSTRUCTOR =head2 new(hash or hashref) The constructor will usually not be used directly because you get a ready to use current conditions objects from your location object. If you ever want to instantiate current conditions objects on your own, you have to provide the same configuration hash or hashref to the constructor you usually would provide to the C method of I. In addition it is necessary to add a hash element C to this config hash. The C has to be a valid I location id. =head1 METHODS =head2 id() Returns the location id used to instantiate this object. =head2 name() Returns the name of the location this current conditions belong to. =head2 description([$language]) Returns a textual representation of the current weather conditions. This description is translated if you specified the I option as argument for your I. This attribute is I. =head2 dewpoint() Returns the dewpoint. =head2 humidity() Returns the humidity (in %). =head2 icon() Returns the number of the icon that can be used to display the current weather conditions. These icons are available with the I sdk. You can download this sdk from I after you've registered to get your license. =head2 last_updated() Returns a I object containing the date and time of the last update as provided by I. =head2 observatory() Returns the name of the observatory that provided the current conditions to I. =head2 pressure() Returns a I object. Please refer to L for further details. =head2 temperature() Returns the temperature (depending on how you instantiated your I you'll get centigrade (default) or degrees fahrenheit). =head2 uv_index() Returns a I object. Please refer to L for further details. =head2 visibility() Returns the visibility (depending on how you instantiated your I you'll get km (default) or miles). =head2 wind() Returns a I object. Please refer to L for further details. =head2 windchill() Returns the windchill temperature (depending on how you instantiated your I you'll get centigrade (default) or degrees fahrenheit). =head1 SEE ALSO See also documentation of L, L, L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html) =cut Weather-Com-0.5.3/lib/Weather/Com/Finder.pm0000644000175000017500000001262710644511444021105 0ustar thomasthomas00000000000000package Weather::Com::Finder; use 5.006; use strict; use warnings; use Carp; use Data::Dumper; use Weather::Com::Cached; use Weather::Com::Location; our $VERSION = sprintf "%d.%03d", q$Revision: 1.8 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; my %parameters = (); # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } # god, bless the object $self = bless( $self, $class ); # Weather::Com::Cached object for searching my %weatherargs = (); # define proxy args $weatherargs{proxy} = $parameters{proxy} if ( $parameters{proxy} ); $weatherargs{proxy_user} = $parameters{proxy_user} if ( $parameters{proxy_user} ); $weatherargs{proxy_pass} = $parameters{proxy_pass} if ( $parameters{proxy_pass} ); # other weather arguments... $weatherargs{units} = $parameters{units} if ( $parameters{units} ); $weatherargs{debug} = $parameters{debug} if ( $parameters{debug} ); $weatherargs{cache} = $parameters{cache} if ( $parameters{cache} ); $weatherargs{timeout} = $parameters{timeout} if ( $parameters{timeout} ); $weatherargs{partner_id} = $parameters{partner_id} if ( $parameters{partner_id} ); $weatherargs{license} = $parameters{license} if ( $parameters{license} ); $weatherargs{language} = $parameters{language} if ( $parameters{language} ); # initialize weather object $self->{ARGS} = \%weatherargs; $self->{WEATHER} = Weather::Com::Cached->new(%weatherargs); return $self; } # end new() #------------------------------------------------------------------------ # find weather #------------------------------------------------------------------------ sub find { my $self = shift; my $locString = shift; # search locations my $loc_weather = $self->{WEATHER}->search($locString); unless ($loc_weather) { $self->_debug("No location found using '$locString' for search!"); return 0; } # create a Weather::Com::Location for each location found my @locations = (); foreach ( keys %{$loc_weather} ) { my %weatherargs = %{ $self->{ARGS} }; $weatherargs{location_id} = $_; $weatherargs{location_name} = $loc_weather->{$_}; my $location = Weather::Com::Location->new(%weatherargs); push( @locations, $location ); } $self->_debug("Location Objects: " . Dumper(\@locations)); # return an array if called in list context or a scalar # if called in void or scalar context unless(@locations) { return undef; } elsif ( wantarray() ) { return @locations; } else { return \@locations; } } #------------------------------------------------------------------------ # other internals #------------------------------------------------------------------------ sub _debug { my $self = shift; my $notice = shift; if ( $self->{ARGS}->{debug} ) { carp ref($self) . " DEBUG NOTE: $notice\n"; return 1; } return 0; } 1; __END__ =pod =head1 NAME Weather::Com::Finder - finder class to search for I location's =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, ); my $finder = Weather::Com::Finder->new(%weatherargs); # if you want an array of locations: my @locations = $finder->find('Heidelberg'); # or if you prefer an arrayref: my $locations = $finder->find('Heidelberg'); foreach my $location (@locations) { print "Found weather for city: ", $location->name(), "\n"; print "Current Conditions are ", $location->current_conditions()->description(), "\n"; } =head1 DESCRIPTION The usual way to use the I module would be to instantiate a I that allows you to search for a location by providing a search string or postal code or any other search string that I understands. The finder returns an arrayref or an array of locations (depending on how you call the C method). Each location is an object of I. =head1 CONSTRUCTOR =head2 new(hash or hashref) The constructor takes a configuration hash or hashref as described in the I POD. Please refer to that documentation for further details. =head1 METHODS =head2 find(search string) Once you've instantiated a finder object, you can perform C calls to search for locations in the I database. The C method returns an array of I objects if you call it in list context, else an arrayref. Returns I if no matching location could be found. =head1 SEE ALSO See also documentation of L and L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html) =cut Weather-Com-0.5.3/lib/Weather/Com/DayPart.pm0000644000175000017500000001052710644511444021237 0ustar thomasthomas00000000000000package Weather::Com::DayPart; use 5.006; use strict; use Carp; use Weather::Com::L10N; use Weather::Com::Wind; use base 'Weather::Com::Object'; our $VERSION = sprintf "%d.%03d", q$Revision: 1.8 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } my $self = $class->SUPER::new( \%parameters ); # getting first weather information $self->{TYPE} = undef; $self->{CONDITIONS} = 'N/A'; $self->{HUMIDITY} = 'N/A'; $self->{ICON} = undef; $self->{PRECIPITATION} = undef; $self->{WIND} = undef; return $self; } # end new() #------------------------------------------------------------------------ # update data #------------------------------------------------------------------------ sub update { my $self = shift; my %daypart; if ( ref( $_[0] ) eq "HASH" ) { %daypart = %{ $_[0] }; } else { %daypart = @_; } if ( $daypart{p} eq "d" ) { $self->{TYPE} = "day"; } else { $self->{TYPE} = "night"; } $self->{CONDITIONS} = $daypart{t}; $self->{HUMIDITY} = $daypart{hmid}; $self->{ICON} = $daypart{icon}; $self->{PRECIPITATION} = $daypart{ppcp}; unless ( $self->{WIND} ) { $self->{WIND} = Weather::Com::Wind->new( $self->{ARGS} ); } $self->wind()->update( $daypart{wind} ); return 1; } #------------------------------------------------------------------------ # access data #------------------------------------------------------------------------ sub type { my $self = shift; my $language = shift; return $self->get_language_handle($language)->maketext( $self->{TYPE} ); } sub conditions { my $self = shift; my $language = shift; return $self->get_language_handle($language) ->maketext( lc( $self->{CONDITIONS} ) ); } sub humidity { my $self = shift; return $self->{HUMIDITY}; } sub icon { my $self = shift; return $self->{ICON}; } sub precipitation { my $self = shift; return $self->{PRECIPITATION}; } sub wind { my $self = shift; return $self->{WIND}; } 1; __END__ =pod =head1 NAME Weather::Com::DayPart - class representing daytime or night part of a forecast =head1 SYNOPSIS [...] my @locations = $weather_finder->find('Heidelberg'); my $forecast = $locations[0]->forecast(); my $tomorrow_night = $forecast->day(1)->night(); print "Forecast for tomorrow night:\n"; print " - conditions will be ", $tomorrow_night->conditions(), "\n"; print " - humidity will be ", $tomorrow_night->humidity(), "\%\n"; print " - wind speed will be ", $tomorrow_night->wind()->speed(), "km/h\n"; =head1 DESCRIPTION Via I objects one can access the daytime or night part of a I. This class will B be updated automatically with each call to one of its methods. You need to call a method of your I object to get updated objects. =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call the C or C method of a I object. =head1 METHODS =head2 type([$language]) Will return I or I. This attribute is I. =head2 conditions([$language]) Will return a textual description of the forecasted conditions. This attribute is I. =head2 humidity() Will return the humidity. =head2 icon() Will return the icon number of the icon describing the forecasted weather. =head2 precipitation() Will return the percentage chance of precipitation. =head2 wind() Will return a I object. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com/DayForecast.pm0000644000175000017500000001326510644511444022101 0ustar thomasthomas00000000000000package Weather::Com::DayForecast; use 5.006; use strict; use warnings; use Weather::Com::L10N; use Weather::Com::DayPart; use Weather::Com::DateTime; use base 'Weather::Com::Object'; our $VERSION = sprintf "%d.%03d", q$Revision: 1.8 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } my $self = $class->SUPER::new( \%parameters ); # getting first weather information $self->{DATE} = undef; $self->{HIGH} = 'N/A'; $self->{LOW} = 'N/A'; $self->{SUNRISE} = undef; $self->{SUNSET} = undef; $self->{DAY} = undef; $self->{NIGHT} = undef; return $self; } # end new() #------------------------------------------------------------------------ # update data #------------------------------------------------------------------------ sub update { my $self = shift; my %day; if ( ref( $_[0] ) eq "HASH" ) { %day = %{ $_[0] }; } else { %day = @_; } # update date and time data unless ( $self->date() ) { $self->{DATE} = Weather::Com::DateTime->new( $day{zone} ); $self->{SUNRISE} = Weather::Com::DateTime->new( $day{zone} ); $self->{SUNSET} = Weather::Com::DateTime->new( $day{zone} ); } $self->date()->set_date( $day{dt} ); $self->sunrise()->set_time( $day{sunr} ); $self->sunset()->set_time( $day{suns} ); # update weather data # if $day{hi} eq "N/A" then there is no daytime forecast unless ( $day{hi} eq 'N/A' ) { $self->{HIGH} = $day{hi}; } else { $self->{HIGH} = 'N/A'; } $self->{LOW} = $day{low}; foreach my $daypart ( @{ $day{part} } ) { if ( $daypart->{p} eq 'd' ) { # if $day{hi} eq "N/A" then there is no daytime forecast if ( $day{hi} ne 'N/A' ) { unless ( $self->{DAY} ) { $self->{DAY} = Weather::Com::DayPart->new( $self->{ARGS} ); } $self->day()->update($daypart); } } else { unless ( $self->{NIGHT} ) { $self->{NIGHT} = Weather::Com::DayPart->new( $self->{ARGS} ); } $self->night()->update($daypart); } } } #------------------------------------------------------------------------ # access data #------------------------------------------------------------------------ sub date { my $self = shift; return $self->{DATE}; } sub high { my $self = shift; return $self->{HIGH}; } sub low { my $self = shift; return $self->{LOW}; } sub sunrise { my $self = shift; return $self->{SUNRISE}; } sub sunset { my $self = shift; return $self->{SUNSET}; } sub day { my $self = shift; return $self->{DAY}; } sub night { my $self = shift; return $self->{NIGHT}; } 1; __END__ =pod =head1 NAME Weather::Com::DayForecast - class representing a forecast for one day =head1 SYNOPSIS [...] my @locations = $weather_finder->find('Heidelberg'); my $forecast = $locations[0]->forecast(); my $tomorrow = $forecast->day(1); print "Forecast for tomorrow:\n"; print " - tomorrow it's the ", $tomorrow->date()->date(), "\n"; print " - sunrise will be at ", $tomorrow->sunrise()->time(), "\n"; print " - maximum temperature will be ", $tomorrow->high(), "\n"; =head1 DESCRIPTION Via I objects one can access the weather forecast for one specific day. This class will B be updated automatically with each call to one of its methods. You need to call a method of your I object to get updated objects. =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call the C or the C method of a I object. =head1 METHODS =head2 date() Returns a I object containing the date the forecast is for. =head2 high() Returns the maximum temperature that will be reached at daytime. For day 0 (today), this will be 'N/A' when it's after noon... B in I's date and time mathematics: If you are asking for a location's forecast day 0 and it's short after midnight, day 0 will be "yesterday" and you'll get both, yesterday's daytime forecast and night forecast! I have not investigated this issue further, yet. If anyone has, please inform me! =head2 low() Returns the minimum temperature that will be reached at night. =head2 sunrise() Returns a I object containing the time of sunrise. =head2 sunset() Returns a I object containing the time of sunset. =head2 day() Returns a I object with all data belonging to the daytime. For day 0 (today), this will be C when it's after noon... B in I's date and time mathematics: If you are asking for a location's forecast day 0 and it's short after midnight, day 0 will be "yesterday" and you'll get both, yesterday's daytime forecast and night forecast! I have not investigated this issue further, yet. If anyone has, please inform me! =head2 night() Returns a I object with all data belonging to the night. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com/Base.pm0000644000175000017500000006103110644511444020541 0ustar thomasthomas00000000000000package Weather::Com::Base; use 5.006; use strict; use warnings; use Carp; use URI::Escape; use LWP::UserAgent; use HTTP::Request; use XML::Simple; use Data::Dumper; use Time::Local; use base qw(Weather::Com::Object Exporter); #-------------------------------------------------------------------- # Define some globals #-------------------------------------------------------------------- our @EXPORT_OK = qw( celsius2fahrenheit fahrenheit2celsius convert_winddirection ); our $VERSION = sprintf "%d.%03d", q$Revision: 1.9 $ =~ /(\d+)/g; my $CITY_SEARCH_URI = "http://xoap.weather.com/search/search?where="; my $WEATHER_SEARCH_URI = "http://xoap.weather.com/weather/local/"; my %winddir = ( "none" => "none", 'N/A' => "Not Available", "VAR" => "Variable", "N" => "North", "NNW" => "North Northwest", "NW" => "Northwest", "WNW" => "West Northwest", "W" => "West", "WSW" => "West Southwest", "SW" => "Southwest", "SSW" => "South Southwest", "S" => "South", "SSE" => "South Southeast", "SE" => "Southeast", "ESE" => "East Southeast", "E" => "East", "ENE" => "East Northeast", "NE" => "Northeast", "NNE" => "North Northeast", "Not Available" => 'N/A', "North" => "N", "North Northwest" => "NNW", "Northwest" => "NW", "West Northwest" => "WNW", "West" => "W", "West Southwest" => "WSW", "Southwest" => "SW", "South Southwest" => "SSW", "South" => "S", "South Southeast" => "SSE", "Southeast" => "SE", "East Southeast" => "ESE", "East" => "E", "East Northeast" => "ENE", "Northeast" => "NE", "North Northeast" => "NNE", "Variable" => "VAR", ); #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } # get SUPER instance my $self = $class->SUPER::new( \%parameters ); # some general attributes $self->{PROXY} = "none"; $self->{TIMEOUT} = 180; $self->{DEBUG} = 0; # license information $self->{PARTNER_ID} = undef; $self->{LICENSE_KEY} = undef; # API specific attributes $self->{UNITS} = 'm'; # could be 'm' for metric or 's' for us standard $self->{CC} = 0; # show current conditions true/false $self->{FORECAST} = 0; # multi day forecast 0 = no, 1..10 days $self->{LINKS} = 0; # save params for further use $self->{ARGS} = \%parameters; # do some initialization with validity checking $self = $self->_init(); # and some debugging output $self->_debug( "Returning object: " . Dumper($self) ); return $self; } sub _init { my $self = shift; my $params = $self->{ARGS}; # set proxy if param is set properly if ( $params->{proxy} && ( lc( $params->{proxy} ) ne "none" ) ) { unless ( $params->{proxy} =~ /^(http|HTTP)\:\/\// ) { die ref($self) . ": 'proxy' parameter has to start with 'http://'!\n"; } $self->{PROXY} = $params->{proxy}; } # set proxy authentication data if param is set properly if ( $params->{proxy_user} && $params->{proxy_pass} && ( lc( $params->{proxy_user} ) ne "none" ) ) { $self->{PROXY_USER} = $params->{proxy_user}; $self->{PROXY_PASS} = $params->{proxy_pass}; } # set timeout if it is a possitive integer or 0 if ( $params->{timeout} ) { unless ( ( $params->{timeout} =~ /^\d+$/ ) && ( $params->{timeout} > -1 ) ) { die ref($self) . ": 'timeout' parameter has to be a positive integer or 0!\n"; } $self->{TIMEOUT} = $params->{timeout}; } # set units of measure if set to 'm' or 's' if ( $params->{units} ) { unless ( lc( $params->{units} ) =~ /^m|s$/ ) { die ref($self) . ": 'units' parameter has to be set to 'm' or 's'!\n"; } $self->{UNITS} = $params->{units} if ( $params->{units} ); } # forecast has to be between 0 and 10 if ( $params->{forecast} ) { unless ( $params->{forecast} =~ /^[1]?\d$/ ) { die ref($self) . ": 'forecast' parameter has to between 0 and 10!\n"; } $self->{FORECAST} = $params->{forecast}; } # set params that don't need to be checked $self->{PARTNER_ID} = $params->{partner_id} if ( $params->{partner_id} ); $self->{LICENSE_KEY} = $params->{license} if ( $params->{license} ); $self->{DEBUG} = $params->{debug} if ( $params->{debug} ); $self->{CC} = $params->{current} if ( $params->{current} ); $self->{LINKS} = $params->{links} if ( $params->{links} ); if ( $params->{lang} ) { $self->{LANGUAGE} = $params->{lang}; } else { $self->{LANGUAGE} = 'en_US'; } return $self; } #------------------------------------------------------------------------ # getting data from weather.com #------------------------------------------------------------------------ sub search { my $self = shift; my $place = shift; # set error and die if no place provided unless ($place) { die ref($self), ": ERROR Please provide a location to search for!\n"; } # build up HTTP GET request $place = uri_escape($place); my $searchlocation = $CITY_SEARCH_URI . $place; $searchlocation .= '&prod=xoap'; if ( $self->{PARTNER_ID} && $self->{LICENSE_KEY} ) { $searchlocation .= '&par=' . $self->{PARTNER_ID}; $searchlocation .= '&key=' . $self->{LICENSE_KEY}; } $self->_debug($searchlocation); # get information my $locationXML; my $i = 0; while ( !$locationXML || ( $locationXML !~ /^\<\?xml/ ) && ( $i < 3 ) ) { eval { $locationXML = $self->_getWebPage($searchlocation); }; if ($@) { die ref($self), ": ERROR No response from weather server while searching place: $@\n"; } # check if at least one location has been returned # if not return 0 if ( $locationXML =~ /^\s*$/g ) { $self->_debug( "No location found using $place (url escaped) as search string." ); return 0; } $i++; } return 0 unless ( $locationXML =~ /^\<\?xml/ ); # parse answer my $simpleHash = XMLin($locationXML); # XML::Simple behaves differently when one location is return than # when more locations are returned ... my $locations = undef; if ( $simpleHash->{loc}->{content} ) { $locations->{ $simpleHash->{loc}->{id} } = $simpleHash->{loc}->{content}; } else { foreach ( keys %{ $simpleHash->{loc} } ) { $locations->{$_} = $simpleHash->{loc}->{$_}->{content}; } } $self->_debug( Dumper($locations) ); return $locations; } # end search() sub get_weather { my $self = shift; my $locid = shift; unless ($locid) { die ref($self), ": ERROR Please provide a location id!\n"; } # prepare HTTP Request my $searchlocation = $WEATHER_SEARCH_URI . $locid; $searchlocation .= '?unit=' . $self->{UNITS}; $searchlocation .= '&prod=xoap'; if ( $self->{PARTNER_ID} && $self->{LICENSE_KEY} ) { $searchlocation .= '&par=' . $self->{PARTNER_ID}; $searchlocation .= '&key=' . $self->{LICENSE_KEY}; } if ( $self->{CC} ) { $searchlocation .= '&cc=*'; } if ( $self->{FORECAST} ) { $searchlocation .= '&dayf=' . $self->{FORECAST}; } if ( $self->{LINKS} ) { $searchlocation .= '&link=xoap'; } # get weather data $self->_debug($searchlocation); my $weatherXML; eval { $weatherXML = $self->_getWebPage($searchlocation); }; if ($@) { die ref($self), ": ERROR No response from weather server while loading data: $@\n"; } # parse weather data my %options = ( ForceArray => ["day"], ); my $simpleHash = XMLin($weatherXML, %options); # do some error handling if weather.com returns an error message if ( $simpleHash->{err} ) { die ref($self), ": ERROR ", $simpleHash->{err}->{content}, "\n"; } $self->_debug(Dumper($simpleHash)); return $simpleHash; } #-------------------------------------------------------------------- # Utility function to get one web pages content or die on error #-------------------------------------------------------------------- sub _getWebPage { my $self = shift; my $path = shift; # instantiate a new user agent, with proxy if necessary my $ua = LWP::UserAgent->new(); my $request = HTTP::Request->new( "GET" => $path ); $ua->proxy( 'http', $self->{PROXY} ) if ( lc( $self->{PROXY} ) ne "none" ); $request->proxy_authorization_basic( $self->{PROXY_USER}, $self->{PROXY_PASS} ) if ( lc( $self->{PROXY_USER} ) ne "none" ); $ua->timeout( $self->{TIMEOUT} ); # print some debugging info on the user agent object $self->_debug("This is the user agent we wanna use:"); $self->_debug( Dumper($ua) ); $self->_debug("Together with this request:"); $self->_debug( Dumper($request) ); # get the html page my $response = $ua->request($request); # and do some error handling my $html = undef; if ( $response->is_success() ) { $html = $response->content(); } else { die "ERROR While getting resource: $path :\n", $response->status_line(), "\n"; } # and print the complete HTML response for debugging purposes $self->_debug($html); return $html; } #------------------------------------------------------------------------ # other internals #------------------------------------------------------------------------ sub _debug { my $self = shift; my $notice = shift; if ( $self->{DEBUG} ) { carp ref($self) . " DEBUG NOTE: $notice\n"; return 1; } return 0; } ######################################################################### # # STATIC methods go here # #------------------------------------------------------------------------ # methods for temperature conversion #------------------------------------------------------------------------ sub celsius2fahrenheit { my $celsius = shift; my $fahrenheit = sprintf( "%d", ( $celsius * 1.8 ) + 32 ); return $fahrenheit; } sub fahrenheit2celsius { my $fahrenheit = shift; my $celsius = sprintf( "%d", ( $fahrenheit - 32 ) / 1.8 ); return $celsius; } #------------------------------------------------------------------------ # internal time conversion methods #------------------------------------------------------------------------ sub _lsup2epoc { # this method returns epoc for gmt corresponding to # the provided last update value (lsup) my $lsup = shift; my $gmt_offset = shift; my ( $date, $time, $ampm, $zone ) = split( / /, $lsup ); my ( $mon, $mday, $year ) = split( "/", $date ); my ( $hour, $min ) = split( /:/, $time ); $year += 100; $hour += 12 if ( $ampm eq "PM" ); my $gmtime = timegm( 0, $min, $hour, $mday, $mon - 1, $year ); $gmtime -= $gmt_offset * 3600; return $gmtime; } sub _epoc2lsup { # this method takes epoc (gmt) and builds up the weather.com # internally used last update format (lsup) my $epoc = shift; my $gmt_offset = shift; my ( $sec, $min, $hour, $mday, $mon, $year ) = gmtime( $epoc + $gmt_offset * 3600 ); $year -= 100; $year = '0' . $year if ( $year < 10 ); $mon++; my $ampm = "AM"; if ( $hour > 12 ) { $hour -= 12; $ampm = "PM"; } my $time = join( ":", $hour, $min ); my $date = join( "/", $mon, $mday, $year ); my $lsup = join( " ", $date, $time, $ampm, "Local Time" ); return $lsup; } sub _simple2twentyfour { my $stime = shift; my $colon = index( $stime, ":" ); my $hour = substr( $stime, 0, $colon ); my $minutes = substr( $stime, $colon + 1, 2 ); my $ampm = substr( $stime, 6 ); if ( lc($ampm) =~ /PM/ ) { return $hour + 12 . ":" . $minutes; } else { return "$hour:$minutes"; } } #------------------------------------------------------------------------ # wind direction conversion methods #------------------------------------------------------------------------ sub convert_winddirection { my $indir = shift; return $winddir{$indir}; } 1; __END__ =pod =head1 NAME Weather::Com::Base - Perl extension for getting weather information from weather.com =head1 SYNOPSIS use Data::Dumper; use Weather::Com::Base; # define parameters for weather search my %params = ( 'current' => 1, 'forecast' => 3, 'links' => 1, 'units' => 's', 'proxy' => 'http://proxy.sonstwo.de', 'timeout' => 250, 'debug' => 1, 'partner_id' => 'somepartnerid', 'license' => '12345678', ); # instantiate a new weather.com object my $weather_com = Weather::Com::Base->new(%params); # search for locations called 'Heidelberg' my $locations = $weather_com->search('Heidelberg') or die "No location found!\n"; # and then get the weather for each location found foreach (keys %{$locations}) { my $weather = $weather_com->get_weather($_); print Dumper($weather); } =head1 DESCRIPTION I is a Perl module that provides low level OO interface to gather all weather information that is provided by I. This module should not be used directly because of the business rules applying if one want's to use the I's I interface. These business rules enforce you to implement several caching rules. Therefore, if you want to use a low level interface, please use I instead. It has the same interface as this module but it implements all caching rules of I. =head2 EXPORT None by default. But there are a few static methods for conversion purposes you may wanna use: =over 4 =item * celsius2fahrenheit(celsius) Takes the temperature in celsius and returns the temperature in fahrenheit (as an integer value). =item * fahrenheit2celsius(fahrenheit) Takes the temperature in fahrenheit and returns the temperature in celius (as an integer value). =item * convert_winddirection(direction) Takes a wind mnemonic ("N", "WNW", etc.) or a long name of a wind direction ("North Northeast") and returns the other format. The long names are only understood if used as follows: "North" "North Northwest" "Northwest" "West Northwest" "West" "West Southwest" "Southwest" "South Southwest" "South" "South Southeast" "Southeast" "East Southeast" "East" "East Northeast" "Northeast" "North Northeast" "Variable" =back =head1 CONSTRUCTOR =head2 new(hash or hashref) The constructor takes a hash or a hashref containing a bunch of parameters used to configure your weather search. None of these paramters is mandatory. As there are reasonable defaults for any of them, you need only to provide those parameters where you whish to go with non-default values. The parameters in detail: =over 4 =item current => 0 | 1 This parameter defines whether to fetch the current conditions of a location or not. Defaults to 0 (false). =item forecast => 0 | 1 .. 10 This parameter defines whether to fetch a weather forecast or not. If set to 0 (false) no forecast is read, if set to a value between 1 and 10 a forecast for the number of days is requested. If set to any other value, this is interpreted as 0! Defaults to 0 (false). =item links => 0 | 1 This parameter specifies whether to load some http links from I also or not. =item units => s | m This parameter defines whether to fetch information in metric (m) or US (s) format. Defaults to 'm'. =item proxy => 'none' | 'http://some.proxy.de:8080' Usually no proxy is used by the L module used to communicate with I. If you want to use an HTTP proxy you can specify one here. =item proxy_user => undef | 'myuser' If specified, this parameter is provided to the proxy for authentication purposes. Defaults to I. =item proxy_pass => undef | 'mypassword' If specified, this parameter is provided to the proxy for authentication purposes. Defaults to I. =item timeout => some integer (in seconds) The timeout for I to get an HTTP request done usually is set to 180s. If you need a longer timeout or for some reasons a shorter one you can set this here. Defaults to 180 seconds. =item debug => 0 | 1 Set debugging on/off. Defaults to 0 (off). =item partner_id => 'somepartnerid' To be allowed to fetch weather information from I you need to register (free of charge) to get a so called I and a I. =item license => 'somelicensekey' See I. =back =head1 METHODS =head2 search(place to search) Searches for all known locations matching the provided search string. At I you have to request weather data for a specific location. Therefor you first have to find the location id for the location you are looking for. I provides two possibilities to search: =over 4 =item 1. by name You may search for a location by name, e.g. "Heidelberg", "Heidelberg, Germany", "New York, NY", etc. =item 2. by postal code You may search for a location by the postal code. =back If the search causes an error, this methods dies with a (hopefully) helpful error message. If the search returns no matches, 0 is returned. Else, the method returns a hashref containing all locations found. The hashref looks as follows if you search for I: $HASHREF = { 'GMXX0053' => 'Heidelberg, Germany', 'USKY0990' => 'Heidelberg, KY', 'USMS0154' => 'Heidelberg, MS' }; The keys of the hash are the location ids as used within I. This keys have to be used for fetching the weather information of one location. =head2 get_weather(location id) This method fetches all weather information as defined by the object attributes you may have modified while instantiating the weather object. If an error occurs while fetching weather information, this method dies setting $@ to a meaningfull error message. The following hashref shows the maximum of data that can be fetched from I. The parts of the hashref are explained afterwards. $HASHREF = { 'head' => { 'ur' => 'mm', 'ud' => 'km', 'us' => 'km/h', 'form' => 'MEDIUM', 'up' => 'mb', 'locale' => 'en_US', 'ut' => 'C' }, 'loc' => { 'suns' => '8:40 PM', 'zone' => '2', 'lat' => '49.41', 'tm' => '3:48 PM', 'sunr' => '6:18 AM', 'dnam' => 'Heidelberg, Germany', 'id' => 'GMXX0053', 'lon' => '8.68' }, 'cc' => { 'icon' => '28', 'flik' => '21', 'obst' => 'Mannhein, Germany', 'lsup' => '8/16/04 3:20 PM Local Time', 'tmp' => '21', 'hmid' => '78', 'wind' => { 'gust' => 'N/A', 'd' => '170', 's' => '11', 't' => 'S' }, 'bar' => { 'r' => '1,010.8', 'd' => 'steady' }, 'dewp' => '17', 'uv' => { 't' => 'Moderate', 'i' => '3' }, 'vis' => '10.0', 't' => 'Mostly Cloudy' }, 'dayf' => { 'lsup' => '8/16/04 12:17 PM Local Time', 'day' => [ { 'hi' => '27', 'suns' => '8:40 PM', 'dt' => 'Aug 16', 'part' => [ { 'hmid' => '57', 'wind' => { 'gust' => 'N/A', 'd' => '204', 's' => '16', 't' => 'SSW' }, 'icon' => '28', 'p' => 'd', 'ppcp' => '20', 't' => 'Mostly Cloudy' }, { 'hmid' => '87', 'wind' => { 'gust' => 'N/A', 'd' => '215', 's' => '13', 't' => 'SW' }, 'icon' => '11', 'p' => 'n', 'ppcp' => '60', 't' => 'Showers' } ], 'd' => '0', 'sunr' => '6:18 AM', 'low' => '16', 't' => 'Monday' }, ... next day ..., ... and so on ..., ] }, 'lnks' => { 'link' => [ { 'l' => 'http://www.weather.com/outlook/health/allergies/USMS0154?par=xoap', 'pos' => '1', 't' => 'Pollen Reports' }, { 'l' => 'http://www.weather.com/outlook/travel/flights/citywx/USMS0154?par=xoap', 'pos' => '2', 't' => 'Airport Delays' }, { 'l' => 'http://www.weather.com/outlook/events/special/result/USMS0154?when=thisweek&par=xoap', 'pos' => '3', 't' => 'Special Events' }, { 'l' => 'http://www.weather.com/services/desktop.html?par=xoap', 'pos' => '4', 't' => 'Download Desktop Weather' } ], 'type' => 'prmo' }, 'ver' => '2.0' }; =head3 Header Information Block The hashref I contains unit of measure definitions for the whole dataset. Usually they are either all metric or all us. =over 4 =item locale Should always be I. Up to now I have not found any possiblity to get other locales. =item ut (unit of temperature) Could be I for Celsius or I for Fahrenheit. =item us (unit of speed) Could be I or I. =item ud (unit of distance) Could be I or I. =item ur (unit of precipitation) Could be I (millibar) or I. =item up (unit of pressure) Could be I or I for in Hg. =item format (textformat) Could be I, I or I. =back =head3 Location Information Block The hashref I contains some information about the location that does not change very much each hour or day. =over 4 =item id (location id) This is the location id as used to get the weather for the location. =item dnam (descriptive name) This is a name describing the location, e.g. I. =item tm (time) This is the current local time of the location (if not using cached data). Time is always presented like I<8:45 AM> or I<11:33 PM>. =item zone (timezone) This is the timezone. It is presented as time offset from GMT. =item suns (time of sunset) =item sunr (time of sunrise) =item lat (latitude) The latitude is presented as 2 digit decimal. =item lon (longitude) The longitude is presented as 2 digit decimal. =back =head3 Current Conditions Block The hashref I contains information about the current conditions. =over 4 =item icon The SDK from I contains a set of weather icons. These icons have filenames like I<28.png>. This element is this icon number. =item flik (windchill) This is the temperature considering the windchill factor. =item obst (observatory) The observatory that reported the weather data. =item lsup (last updated) Date and time when the weather data has been reported. Format is I<8/16/04 6:10 AM EDT>. In some cases (e.g. for Heidelberg in Germany) there may be no official timezone identifier but the keyword I or I. =item tmp (temperature) =item hmid (humidity) =item wind =over 8 =item gust Maximum gust speed. =item d Wind direction in degrees. =item t Text description of direction. =item s Wind speed =back =item bar (air pressure) =over 8 =item r (pressure) Decimal current pressure. =item d (description) Text description of raise or fall of pressure. =back =item dewp Integer dew point. =item uv (uv index data) =over 8 =item i Integer index value. =item t Text description of value. =back =item vis (decimal visibility) =item t Text description of condition. =back =head3 Forecasts Up to 10 days of forecasts can be found in the hashref I. =over 4 =item lsup (last updated) self explanatory =item day I contains either a hash containing the forecast for one day or it contains an an array of hashes, one for each day. =over 8 =item dt (date) The date of the forecasted day. Only name of month and day, e.g. I. =item d (number of the day) =item t (name of the day) e.g. I =item hi (highest temperature) =item low (lowest temperature) =item suns (time of sunset) =item sunr (time of sunrise) =item part (block of day part data) There are always to blocks of day part data. One for the the night and one for the day. =over 12 =item hmid (humidity) =item wind (see current conditions block) =item icon (see current conditions block) =item p (part of day) Maybe I for I or I for I. =item ppcp (percent chance of precipitation) =item t (description of conditions) =back =back =back =head3 Links The hashref I contains some links to other weather information that may be interesting for the chosen location. This will not be explained in further detail here. Just play around with the sample... =head1 SEE ALSO See also L for the cached version of the low level API. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (http://www.weather.com/services/xmloap.html)! =cut Weather-Com-0.5.3/lib/Weather/Com/Moon.pm0000644000175000017500000000663710644511444020612 0ustar thomasthomas00000000000000package Weather::Com::Moon; use 5.006; use strict; use warnings; use Weather::Com::L10N; use base 'Weather::Com::Object'; our $VERSION = sprintf "%d.%03d", q$Revision: 1.7 $ =~ /(\d+)/g; #------------------------------------------------------------------------ # Constructor #------------------------------------------------------------------------ sub new { my $proto = shift; my $class = ref($proto) || $proto; my %parameters; # parameters provided by new method if ( ref( $_[0] ) eq "HASH" ) { %parameters = %{ $_[0] }; } else { %parameters = @_; } my $self = $class->SUPER::new( \%parameters ); # getting first weather information $self->{ICON} = undef; $self->{DESCRIPTION} = ''; return $self; } # end new() #------------------------------------------------------------------------ # update moon data #------------------------------------------------------------------------ sub update { my $self = shift; my %moon; if ( ref( $_[0] ) eq "HASH" ) { %moon = %{ $_[0] }; } else { %moon = @_; } $self->{ICON} = $moon{icon}; $self->{DESCRIPTION} = lc( $moon{t} ); return 1; } #------------------------------------------------------------------------ # access moon data #------------------------------------------------------------------------ sub icon { my $self = shift; return $self->{ICON}; } sub description { my $self = shift; my $language = shift; return $self->get_language_handle($language) ->maketext( lc( $self->{DESCRIPTION} ) ); } 1; __END__ =pod =head1 NAME Weather::Com::Moon - class containing moon phase information =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'language' => 'de', ); my $weather_finder = Weather::Com::Finder->new(%weatherargs); my @locations = $weather_finder->find('Heidelberg'); my $currconditions = $locations[0]->current_conditions(); print "The moon phase is currently ", $currconditions->moon()->description(), "\n"; =head1 DESCRIPTION Via I one can access the current moon phase. This class will B be updated automatically with each call to one of its methods. You need to call the C method of the parent object again to update your object. =head1 CONSTRUCTOR You usually would not construct an object of this class yourself. This is implicitely done when you call the C method of one current conditions or forecast object. =head1 METHODS =head2 description([$language]) Returns a textual description of the current moon phase. This description is translated if you specified the I option for you I. This attribute is I. =head2 icon() Returns the number of the icon describing the current moon phase. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L)! =cut Weather-Com-0.5.3/lib/Weather/Com.pm0000644000175000017500000003100610721350740017662 0ustar thomasthomas00000000000000package Weather::Com; use 5.006; use strict; use warnings; use Carp; use Weather::Com::Cached; use Weather::Com::Location; our $VERSION = sprintf "%d.%03d", q$Revision: 1.8 $ =~ /(\d+)/g; #-------------------------------------------------------------------- # This is just a dummy package containing POD # # Weather::Com.pod has been removed and this package has been added # to make Weather-Com work with CPAN shell. # # Therefore, the whole tutorial went into this file. # Weather::Com.pod is no longer maintained. #-------------------------------------------------------------------- 1; =pod =head1 NAME Weather::Com - fetching weather information from I =head1 SYNOPSIS #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'language' => 'de', ); my $weather_finder = Weather::Com::Finder->new(%weatherargs); my @locations = $weather_finder->find('Heidelberg'); foreach my $location (@locations) { print "Found weather for city: ", $location->name(), "\n"; print "Current Conditions are ", $location->current_conditions()->description(), "\n"; } =head1 DESCRIPTION I provides three interfaces to access weather information from I. Except from the main high level interface, there is also a simple, very easy to use one called I. And if you want, you can also use the low level interface that is the basis for the two high level interfaces, directly (I or even I). Please refer to the POD of these modules directly for detailed information. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I L =head1 LOCALIZATION Weather-Com uses I for the l10n. Foreach language there has to be package I (e.g. I). Localization is new with version 0.4 of Weather-Com. Therefore, there are not too many languages supported, yet. If one wants to create such a language definition package for a language that is not part of this module, please do the following: =over 4 =item 1. check my homepage to verify that the language package has not been created by someone else, yet (L). =item 2. contact me to verify if anybody else already is already translating into this language =item 3. just do it and send me your new language package. It then will be first put onto my website for download and then it will be part of the next release. =back =head2 Dynamic Language Support With version 0.5 of Weather-Com I have introduced a new feature called I. The language for all textual attributes that usually are translated in your default language you chose while creating your C instance can now dynamically be changed on a I. Have a look at this example: #!/usr/bin/perl -w use Weather::Com::Finder; # you have to fill in your ids from weather.com here my $PartnerId = 'somepartnerid'; my $LicenseKey = 'mylicense'; my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'language' => 'en', ); my $weather_finder = Weather::Com::Finder->new(%weatherargs); my @locations = $weather_finder->find('Heidelberg'); foreach my $location (@locations) { print "Found weather for city: ", $location->name(), "\n"; print "Current Conditions are ", $location->current_conditions()->description(), "\n"; # HERE WE USE DYNAMIC LANGUAGE SUPPORT print "That is in German: ", $location->current_conditions()->description('de'), "\n"; } As you can see in this example, you can provide a language tag to a method that returns textual information. If you want to find out if the I is already implemented for a specific attribute of one Weather-Com class, have a look at the corresponding packages POD. =head1 TUTORIAL The usual way to use the I module would be to instantiate a I that allows you to search for a location by a search string or postal code or whatever I may understand. The finder returns an arrayref or an array of locations (depending on how you call the C method). Each location is an object of I. The locations consist of location specific data, a I object, a I object and a I object. =head2 Configuration parameters You will need a configuration hash to instantiate a I object. Except of the I and the I all parameters are optional and have sensible defaults. use Weather::Com::Finder; my %config = ( partner_id => 'somepartnerid', # mandatory license => 'somelicensekey' # mandatory language => 'de', units => 's', cache => '/tmp/weather', timeout => 300, debug => 1, proxy => 'http://some.proxy.de:8080', proxy_user => 'myaccount', proxy_pass => 'myproxy_pass' ); The valid parameters are: =over 4 =item partner_id => 'somepartnerid' To be allowed to fetch weather information from I you need to register (free of charge) to get a so called I and a I. =item license => 'somelicensekey' See I. =item language => 'somelanguagecode' I returns some textual data to describe the weather conditions, uv index, moon phase, wind direction, etc. If one specifies a valid language as configuration parameter, this textual descriptions are translated into that language. If one specifies a language for that there's no translation, the objects will return the english texts. =item cache => '/any/path' Maybe you want to define a special path to put the cache files into. The cache directory defaults to ".". =item units => s | m This parameter defines whether to fetch information in metric (m) or US (s) format. Defaults to 'm'. =item timeout => some integer (in seconds) The timeout for I to get an HTTP request done usually is set to 180s. If you need a longer timeout or for some reasons a shorter one you can set this here. Defaults to 180 seconds. =item debug => 0 | 1 Set debugging on/off. Defaults to 0 (off). =item proxy => 'none' | 'http://some.proxy.de:8080' Usually no proxy is used by the I module used to communicate with I. If you want to use an HTTP proxy you can specify one here. =item proxy_user => undef | 'myuser' If specified, this parameter is provided to the proxy for authentication purposes. Defaults to I. =item proxy_pass => undef | 'mypassword' If specified, this parameter is provided to the proxy for authentication purposes. Defaults to I. =back =head2 Weather::Com::Finder Usually one would start by searching for a location. This is done by instantiating a I object providing all necessary information about the I license, the proxy if one is needed, etc. my $finder = Weather::Com::Finder->new(%config); Then you call the finders C method to search for locations whose name or whose postal code matches against the searchstring. The finder then returns an array (or arrayref) of I objects. # if you want an array of locations: my @locations = $finder->find('Heidelberg'); # or if you prefer an arrayref: my $locations = $finder->find('Heidelberg'); For further information please refer to L. =head2 Weather::Com::Location The I object contains information about the location itself (longitude, latitude, current local time, etc.), a I object that contains all information about the units of messures currently used with this location, and a I object containing the current weather conditions of the location. foreach my $location (@locations) { print "Found location with name: ", $location->name(), "\n"; print "The longitude of this location is: ", $location->longitude(), "\n"; } All information in the I object is updated with each single call of one of its methods corresponding to the caching rules implemented in I. For detailed information about the I class please refer to L. =head2 Weather::Com::Units The units class provides all units of measure corresponding to the data of the location object. You'll get an instance of this class by calling the C method of your location object. For detailed information about the I class please refer to L. =head2 Weather::Com::CurrentConditions Each location has a I object accessible via its C method. my $conditions = $location->current_conditions(); print "Current temperature is ", $conditions->temperature(), "°C\n"; print "but it feels like ", $conditions->windchill(), "°C!\n"; Anytime you call a method of your I object, its data is refreshed automatically if needed according to the caching rules. For detailed information about this class please refer to L. =head2 Weather::Com::Forecast Each location has a I object to access weather forecasts. I provides up to 9 days of forecast - or 10 days if one wants to count day 0 which is I in most cases. my $forecast = $location->forecast(); print "Max. temperature tomorrow will be ", $forecast->day(1)->high(), "°C\n"; Any time you call a method of your I object, forecast data is updated if necessary. For detailed information about this class please refer to L. =head2 Other classes There are a some other classes that are used to represent groups of weather data like wind (speed, direction, etc.), UV index, air pressure, etc. Objects of these classes belong to objects of class I or I that will be introduced with the next release. These objects data will only refresh when you call the corresponding method of the parent object. For detailed information about these classes please refer to their own POD. Classes available with this version are: =head3 Weather::Com::AirPressure Provides access to the barometric pressure data. For detailed information about this class please refer to L. =head3 Weather::Com::UVIndex Provides access to the uv index of the parent object. For detailed information about this class please refer to L. =head3 Weather::Com::Wind Provides access to wind speed, maximum gust, direction in degrees, etc. =head1 EXTENSIONS If you plan to extend these module, e.g. with some other caching mechanisms, please contact me. Perhaps we can add your stuff to this module. =head1 SEE ALSO =head2 Detailed documentation of the main interface L, L, L, L, L, L, L =head2 Detailed documentation of the I API L =head2 Detailed documentation of the low level interface L and L =head1 BUGS/SUPPORT/PRE-RELEASES If you want to report a bug, please use the CPAN bug reporting tool. If you have any question, suggestion, feature request, etc. please use the CPAN forum. If you are looking for fixes, pre-releases, etc. have a look at my website L. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2007 by Thomas Schnuecker This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by I and made accessible by this OO interface can be used for free under special terms. Please have a look at the application programming guide of I (L) =cut Weather-Com-0.5.3/Changes0000644000175000017500000001035510721351601015735 0ustar thomasthomas00000000000000Revision history for Perl extension Weather::Com. $Revision: 1.23 $ 0.5.3 Thu Nov 22 20:00 2007 - FIXED BUG #30869: Cannot install via CPAN shell 0.5.2 Mon Jul 09 21:39 2007 - FIXED BUG #26126: Timeout did not work due to typo - FIXED BUG #25154: (test within OOInterface.t failed) - FIXED BUG #25151: Installing Weather-Com install 0.1 - FIXED BUG: test for network connection failed due to more New Yorks... 0.5.1 Fri May 26 14:41 2006 - added French language pack - fixed bug: air pressure tendency was not translated 0.5 Thu Dec 29 00:11 2005 - now we have dynamic language support 0.4.1 Fri Apr 29 11:30 2005 - re-release of 0.4 because 0.4 version uploaded to CPAN was broken - rewrote parts of README in terms of localization 0.4 Sun Apr 24 16:00 2005 - moved to 'Locale::Maketext' for L10N - reworked all tests, now using Test::MockObject to be able to run all tests even without a network connection - developed a bunch of new tests - fixed some minor bugs 0.4_pre2 Tue Apr 14 23:50 2005 - reworked the locations cache mechnism to make it case insensitive - reworked the locations cache to use not only the search string as key but also the name of each location found (see CHANGES section of Weather::Com::Cached POD for details) - reworked locations search (see Weather::Com::Cached POD for details) 0.4_pre1 Sat Apr 09 15:30 2005 - introducing I18n package to translate weather conditions, uv indices, moon phases and directions (North, South, etc.) into: * English * German - fixed bug: AirPressure->tendency() did never return a valid value 0.3.1 Tue Mar 01 00:30 2005 - Fixed Bug [11720]: The new Weather::Com::DateTime objects seemed to work properly only in Germany (home of author)?!?! Some localtime/gmtime/Time::Format problems. This is fixed now. Weather::Com::DateTime has been totally rewritten. 0.3 Do Feb 26 16:16:00 2005 - Introducing forecasts. There are the following new classes: - Weather::Com::Forecast - Weather::Com::DayForecast - Weather::Com::DayPart - Now, moon phase information is also available with current conditions - Weather::Com::Moon - Any date or time in main OO interface is now a Weather::Com::DateTime object. - Weather::Com now requires Time::Format - Fixed Bug [11448]: Weather::Com::Wind->maximum_gust() did not return any valid data - Introducing locations cache (build into Weather::Com::Cached) => we'll only search for location codes on the web, if we didn't have done so before and therefore already have them in our cachefile "locations.dat" - Optimized caching for high level OO interface => significant speed improvements 0.2 Do Feb 03 23:08:00 2005 - New default high level OO interface: - Weather::Com::Finder - Weather::Com::Location - Weather::Com::CurrentConditions - coming soon: Weather::Com::Forecast (up to 10 days) - Added a short tutorial and starting point as Weather::Com.pod - Changed package structure due to namespace issues. Weather::Com will become Weather::Com::Base, the other's will be called: - Weather::Com::Cached - Weather::Com::Simple So we'll get some structure in it. This will be usefull if one wants to write some other caching package, we than can tell it Weather::Com::Cached::MySQL for example, etc. - added automated version information (using the CVS $Revision: 1.23 $ tag) to all packages - added consistency checking for paramters passed to Weather::Com - Weather::Com now only accepts paths to put the cache in if it is writable to the user that runs the programm - changed caching so we now hold one cache file for metric and one for US system data for each location (only the ones used, if you're only asking for metric data, you'll never get a us cache files and vice versa) - Added Proxy Authentication capabilities so you can use these module for applications behind an authenticated http proxy, also. - enhanced Weather::Com::Simple with some methods to more granular data 0.1 Sa Aug 21 22:00:00 2004 - original version Weather-Com-0.5.3/MANIFEST0000644000175000017500000000152010721351602015566 0ustar thomasthomas00000000000000Makefile.PL BUGS Changes INSTALL MANIFEST README TODO lib/Weather/Com.pm lib/Weather/Com/AirPressure.pm lib/Weather/Com/Base.pm lib/Weather/Com/Cached.pm lib/Weather/Com/CurrentConditions.pm lib/Weather/Com/DateTime.pm lib/Weather/Com/DayForecast.pm lib/Weather/Com/DayPart.pm lib/Weather/Com/Finder.pm lib/Weather/Com/Forecast.pm lib/Weather/Com/L10N.pm lib/Weather/Com/L10N/de.pm lib/Weather/Com/L10N/en_us.pm lib/Weather/Com/L10N/fr.pm lib/Weather/Com/Location.pm lib/Weather/Com/Moon.pm lib/Weather/Com/Object.pm lib/Weather/Com/Simple.pm lib/Weather/Com/Units.pm lib/Weather/Com/UVIndex.pm lib/Weather/Com/Wind.pm samples/weather.pl samples/weather_ext.pl t/AirPressure.t t/Base.t t/Cached.t t/DateTime.t t/L10N.t t/OOInterface.t t/Simple.t t/TestData.pm t/Wind.t META.yml Module meta-data (added by MakeMaker) Weather-Com-0.5.3/TODO0000644000175000017500000000020010234376340015124 0ustar thomasthomas00000000000000Following things have to be done next... * even more testing * switch time representations from epoc seconds to ISO8601 formatWeather-Com-0.5.3/META.yml0000644000175000017500000000110410721352277015715 0ustar thomasthomas00000000000000# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: Weather-Com version: 0.5.3 version_from: installdirs: site requires: Data::Dumper: Locale::Maketext: LWP::UserAgent: Storable: Time::Format: 1.0 Time::Local: URI::Escape: XML::Simple: distribution_type: module generated_by: ExtUtils::MakeMaker version 6.17 Weather-Com-0.5.3/BUGS0000644000175000017500000000305410227573244015135 0ustar thomasthomas00000000000000Yes, there are bugs! And some belong to the weather.com servers and cannot be fixed with this API (or should not...). BUG 1: ====== Tue Apr 14 23:50 2005 Abstract: Cannot use strings containing a '/' for searches (like 'New York/Central Park'). Problem Description: -------------------- The weather.com server does not work with any search string containing a '/' - wether it is URL encoded or not! Problem. Problem Solution: ----------------- There's no real solution as long as nobody fixes the server software, but there's a workaround. The new locations cache as extended with V0.4 does store each location found by any search. Therefore, if one searches for 'New York', for example, the cache will store the location IDs for the search string itself but also for all locations found: $locations_cache => { 'new york' => { 'USNY1000' => 'New York/La Guardia Arpt, NY', 'USNY0998' => 'New York/Central Park, NY', 'USNY0999' => 'New York/JFK Intl Arpt, NY', 'USNY0996' => 'New York, NY' }, 'new york/central park, ny' => { 'USNY0998' => 'New York/Central Park, NY' }, 'new york/la guardia arpt, ny' => { 'USNY1000' => 'New York/La Guardia Arpt, NY' }, 'new york, ny' => { 'USNY0996' => 'New York, NY' }, 'new york/jfk intl arpt, ny' => { 'USNY0999' => 'New York/JFK Intl Arpt, NY' }, } By this way, if one now searches for 'New York/Central Park, NY' he'll get the proper response. The search mechanism has been reworked in a way that also a search for 'york/central' would return a valid match. Weather-Com-0.5.3/samples/0000755000000000000000000000000010721352300015225 5ustar rootroot00000000000000Weather-Com-0.5.3/samples/weather.pl0000644000175000017500000001033010211671023020070 0ustar thomasthomas00000000000000#!/usr/bin/perl -w # $Revision: 1.12 $ use strict; use Weather::Com::Simple; $| = 1; # have a cvs driven version... our $VERSION = sprintf "%d.%03d", q$Revision: 1.12 $ =~ /(\d+)/g; # you have to fill in your ids from weather.com here my $PartnerId = ''; my $LicenseKey = ''; # if you need a proxy... maybe with authentication my $Proxy = ''; my $Proxy_User = ''; my $Proxy_Pass = ''; # debugging on/off my $debugmode = 0; if ( $ARGV[0] && ( $ARGV[0] eq "-d" ) ) { warn "Debug mode turned on.\n"; $debugmode = 1; } my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'debug' => $debugmode, 'proxy' => $Proxy, 'proxy_user' => $Proxy_User, 'proxy_pass' => $Proxy_Pass, ); # print greeting print "\nWelcome to Uncle Tom's weather station...\n"; print "This is V$VERSION\n"; print "\nPlease enter a location name to look for, e.g\n"; print "'Heidelberg' or 'Seattle, WA', or 'Munich, Germany'\n\n"; # define prompt my $prompt = '$> '; print $prompt; while ( chomp( my $input = ) ) { # don't want any empty input unless ( $input =~ /\S+/ ) { print $prompt; next; } # and if the user wants to exit... last if ( $input =~ /^end|quit|exit$/ ); # add place to config hash and get weather $weatherargs{'place'} = $input; my $ws = Weather::Com::Simple->new(%weatherargs); my $weather = $ws->get_weather(); # if $ws->get_weather() returned 'undef', we haven't found anything... unless ($weather) { print "No weather found for location '$input'\n"; print $prompt; next; } # if we found anything we'll print it out... # Weather::Simple allways returns an arrayref. maybe we found more # than 1 location's weather print "\nFound weather data for " . ( $#{$weather} + 1 ) . " locations:\n\n"; foreach my $location_weather ( @{$weather} ) { # print a nice heading underlined by '====' my $heading = "These are the current conditions for " . $location_weather->{place} . "\n"; print $heading; print "=" x length($heading), "\n"; # print weather data we're interested in print " * current conditions are ", $location_weather->{conditions}, "\n"; print " * the current temperature is ", $location_weather->{temperature_celsius}, " degrees celsius\n"; print " * the current windchill is ", $location_weather->{windchill_celsius}, " degrees celsius\n"; print " * wind is ", $location_weather->{wind}, "\n"; print " * humidity is ", $location_weather->{humidity}, "%\n"; print " * air pressure is ", $location_weather->{pressure}, "\n"; print " => this data has been updated on ", $location_weather->{updated}, "\n\n"; } # last but not least print the next prompt print $prompt; } __END__ =pod =head1 NAME weather.pl - Sample script to show the usage of the I module =head1 SYNOPSIS #> ./weather.pl [-d] Welcome to Uncle Tom's weather station... Please enter a location name to look for, e.g 'Heidelberg' or 'Seattle, WA', or 'Munich, Germany' Type 'end' to exit. $> =head1 DESCRIPTION **IMPORTANT** You first have to register at I to get a partner id and a license key for free. Please visit their web site L. Then edit this script and fill in the data into the corresponding variables at the top of the script. The sample script I asks you for a location name - either a city or a 'city, region' or 'city, country' combination. It then uses the I module to get the current weather conditions for this location(s). If no location matching your input is found, a "no locations found" message is printed out. Else, the number of locations found is printed followed by nicely formatted weather data for each location. The command line parameter '-d' enables debugging mode (which is enabling debugging within all used packages (Weather::Com::Simple, Weather::Com::Cached, Weather::Com::Base). =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2005 by Thomas Schnuecker This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Weather-Com-0.5.3/samples/weather_ext.pl0000644000175000017500000002263510435600413020766 0ustar thomasthomas00000000000000#!/usr/bin/perl # $Revision: 1.7 $ use strict; use warnings; use Weather::Com::Finder; # autoflush $| = 1; # have a cvs driven version... our $VERSION = sprintf "%d.%03d", q$Revision: 1.7 $ =~ /(\d+)/g; # you have to fill in your ids from weather.com here my $PartnerId = ''; my $LicenseKey = ''; # you can preset units of messures here # (m for metric (default), s for us) my $units = 'm'; my $language = 'de'; # if you need a proxy... maybe with authentication my $Proxy = ''; my $Proxy_User = ''; my $Proxy_Pass = ''; # debugging on/off my $debugmode = 0; if ( $ARGV[0] && ( $ARGV[0] eq "-d" ) ) { warn "Debug mode turned on.\n"; $debugmode = 1; } my %weatherargs = ( 'partner_id' => $PartnerId, 'license' => $LicenseKey, 'units' => $units, 'debug' => $debugmode, 'proxy' => $Proxy, 'proxy_user' => $Proxy_User, 'proxy_pass' => $Proxy_Pass, 'language' => $language, ); # This is a global var for dynamic language test # it can be set from within the program like # 'set language de_DE' my $dyn_lang = undef; # dynamic language, to be changed # my weather finder instance my $weather_finder = Weather::Com::Finder->new(%weatherargs); # print greeting print "\nWelcome to Uncle Tom's weather station, extended edition...\n"; print "This is V$VERSION\n"; print "\nPlease enter a location name to look for, e.g\n"; print "'Heidelberg' or 'Seattle, WA', or 'Munich, Germany'\n\n"; print "Additional commands are:\n"; print " 'end' to quit this program\n"; print " 'set language ' to dynamically change the language\n"; print " 'language' to get the current languages tag\n"; print " 'reset language' to change back to your default language\n\n"; # define prompt my $prompt = '$> '; print $prompt; while ( chomp( my $input = ) ) { # don't want any empty input unless ( $input =~ /\S+/ ) { print $prompt; next; } # handle dynamic language if ( $input =~ /^set language (.+)$/ ) { $dyn_lang = $1; print $prompt; next; } if ( $input =~ /^reset language$/ ) { $dyn_lang = undef; print $prompt; next; } if ( $input =~ /^language$/ ) { print $dyn_lang || $language, "\n"; print $prompt; next; } # and if the user wants to exit... last if ( $input =~ /^end|quit|exit|:q$/ ); # search for matching locations my $locations = $weather_finder->find($input); # if $weather_finder->find() returned 0, we haven't found anything... unless ($locations) { print "No weather found for location '$input'\n"; print $prompt; next; } # if we found anything we'll print it out... print "\nFound weather data for " . @{$locations} . " locations:\n\n"; # define all units of measure (we can use the units of the first location # because they should all be equal...) my $uodist = $locations->[0]->units()->distance(); my $uoprecip = $locations->[0]->units()->precipitation(); my $uopress = $locations->[0]->units()->pressure(); my $uotemp = $locations->[0]->units()->temperature(); my $uospeed = $locations->[0]->units()->speed(); foreach my $location ( @{$locations} ) { # print a nice heading underlined by '====' my $heading = "This is the data for " . $location->name() . "\n"; print $heading; print "=" x length($heading), "\n"; # print location data print " * this city is located at: ", $location->latitude(), "deg N, ", $location->longitude(), "deg E\n"; print " * local time is ", $location->localtime()->time(), "\n"; print " * sunrise will be/has been at ", $location->sunrise()->time(), "\n"; print " * sunset (am/pm) will be/has been at ", $location->sunset()->time_ampm(), "\n"; print " * timezone is GMT + ", $location->timezone(), "hour(s)\n"; # current conditions print "\nCurrent Conditions (last update ", $location->current_conditions()->last_updated()->time(), " on ", $location->current_conditions()->last_updated() ->formatted('dd.mm.yyyy'), "):\n"; print " * current conditions are ", $location->current_conditions()->description($dyn_lang), ".\n"; print " * visibilty is about ", $location->current_conditions()->visibility(), " $uodist.\n"; print " * and the temperature is ", $location->current_conditions()->temperature(), "deg $uotemp.\n"; print " * the current windchill is ", $location->current_conditions()->windchill(), "deg $uotemp.\n"; print " * the humidity is ", $location->current_conditions()->humidity(), "\%.\n"; print " * the dewpoint is ", $location->current_conditions()->dewpoint(), "deg $uotemp.\n"; # all about wind print " * wind speed is ", $location->current_conditions()->wind()->speed(), " $uospeed.\n"; print " * wind comes from ", $location->current_conditions()->wind()->direction_long($dyn_lang), ".\n"; print " ... in short ", $location->current_conditions()->wind()->direction_short($dyn_lang), ".\n"; print " ... in degrees ", $location->current_conditions()->wind()->direction_degrees(), ".\n"; print " ... max. gust ", $location->current_conditions()->wind()->maximum_gust(), " $uospeed.\n"; # all about uv index print " * uv index is ", $location->current_conditions()->uv_index()->index(), ".\n"; print " ... that is ", $location->current_conditions()->uv_index()->description($dyn_lang), ".\n"; # all about barometric pressure print " * air pressure is ", $location->current_conditions()->pressure()->pressure(), " $uopress.\n"; print " ... tendency ", $location->current_conditions()->pressure()->tendency(), ".\n"; # moon... print " * moon phase is ", $location->current_conditions()->moon()->description($dyn_lang), "\n"; # forecasts my $forecast = $location->forecast(); my $today = $forecast->day(0); print "Today:\n"; print "... day of week: ", $today->date()->weekday(), ", ", $today->date()->date(), "\n"; if ( $today->high() ) { print "... max temp: ", $today->high(), "\n"; } print "... min temp: ", $today->low(), "\n"; print "... sunrise: ", $today->sunrise()->time(), "\n"; print "... sunset: ", $today->sunset()->time(), "\n"; print "Daytime data:\n"; if ( $today->day() ) { print "... conditions: ", $today->day()->conditions($dyn_lang), "\n"; print "... humidity: ", $today->day()->humidity(), "\n"; print "... precipitation: ", $today->day()->precipitation(), "\n"; print "... wind speed: ", $today->day()->wind()->speed(), "\n"; print "... max gust: ", $today->day()->wind()->maximum_gust(), "\n"; print "... wind dir: ", $today->day()->wind()->direction_long($dyn_lang), "\n"; print "... wind dir: ", $today->day()->wind()->direction_degrees(), "\n"; } print "Nightly data:\n"; print "... conditions: ", $today->night()->conditions($dyn_lang), "\n"; print "... humidity: ", $today->night()->humidity(), "\n"; print "... precipitation: ", $today->night()->precipitation(), "\n"; print "... wind speed: ", $today->night()->wind()->speed(), "\n"; print "... max gust: ", $today->night()->wind()->maximum_gust(), "\n"; print "... wind dir: ", $today->night()->wind()->direction_long($dyn_lang), "\n"; print "... wind dir: ", $today->night()->wind()->direction_degrees(), "\n"; foreach my $day ( $forecast->all() ) { print "Have forecast for ", $day->date()->weekday(), ", ", $day->date()->date(), "\n"; print "Max Temp is ", $day->high(), "\n"; print "Min Temp is ", $day->low(), "\n"; print "Percent chance of precipitation at night: ", $day->night()->precipitation(), "\%\n"; print "\n"; } print "\n"; } # last but not least print the next prompt print $prompt; } __END__ =pod =head1 NAME weather_ext.pl - Sample script to show the usage of the OO API of I =head1 SYNOPSIS #> ./weather_ext.pl [-d] Welcome to Uncle Tom's weather station, extended edition... This is V1.006 Please enter a location name to look for, e.g 'Heidelberg' or 'Seattle, WA', or 'Munich, Germany' Additional commands are: 'end' to quit this program 'set language ' to dynamically change the language 'language' to get the current languages tag 'reset language' to change back to your default language $> =head1 DESCRIPTION **IMPORTANT** You first have to register at I to get a partner id and a license key for free. Please visit their web site L. Then edit this script and fill in the data into the corresponding variables at the top of the script. The sample script I asks you for a location name - either a city or a 'city, region' or 'city, country' combination. It then uses the I OO API to get location information, current weather conditions and 9 days of weahter forecast for this location(s). If no location matching your input is found, a "no locations found" message is printed out. Else, the number of locations found is printed followed by nicely formatted weather data for each location. The command line parameter '-d' enables debugging mode (which is enabling debugging within all used packages. =head1 AUTHOR Thomas Schnuecker, Ethomas@schnuecker.deE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-2005 by Thomas Schnuecker This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Weather-Com-0.5.3/INSTALL0000644000175000017500000000171210644511444015477 0ustar thomasthomas00000000000000Weather-Com Installation Instructions ===================================== Installation of Weather-Com is pretty much Perl standard. To install this module type the following: perl Makefile.PL make make test # best with Test::MockObject installed make install DEPENDENCIES This module requires these other modules and libraries: - Locale::Maketext - LWP::UserAgent - Storable - Time::Local - Time::Format - URI::Escape - XML::Simple COPYRIGHT AND LICENCE Copyright (C) 2004-2007 by Thomas Schnuecker (thomas@schnuecker.de) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The data provided by weather.com and made accessible by this OO interface can be used for free under special terms. Please register at http://www.weather.com/services/xmloap.html, download the SDK and have a look at the application programming guide for further information about legal issues. Weather-Com-0.5.3/Makefile.PL0000644000175000017500000000137410721351602016416 0ustar thomasthomas00000000000000use 5.006; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'Weather::Com', VERSION => '0.5.3', # sets $VERSION PREREQ_PM => { Data::Dumper => "", Locale::Maketext => "", LWP::UserAgent => "", Storable => "", Time::Format => "1.0", Time::Local => "", URI::Escape => "", XML::Simple => "", }, ( $] >= 5.005 ? ## Add these new keywords supported since 5.005 ( ABSTRACT_FROM => 'lib/Weather/Com.pm', # retrieve abstract from module AUTHOR => 'Thomas Schnuecker ' ) : () ), ); Weather-Com-0.5.3/README0000644000175000017500000000664610354621667015350 0ustar thomasthomas00000000000000Weather-Com version 0.5 ======================= Introduction: -------------- Weather-Com is meant to be a module suite enabling the perl programmer to access weather information as provided by weather.com's XOAP interface. This interface is a kind of HTTP-GET/XMLResponse API (not really a WebService). They also provide an SDK containing implementation guides, weather icons in several sizes, 'The Weather Channel' logos, etc. By this XOAP API they provide lots of information - not only the current conditions like other weather services do, but also up to 10 days of forecast containing most information one could need, e.g. the time of sunrise and sunset, the longitude and latitude of the location, data precompiled either in the US system or in the metric system, etc. Up from version 0.4 this module contains localization support. That is, textual weather data provided by weather.com (wind directions, current conditions descriptions, uv index descriptions, moon phases, etc.) can be translated into your favourite language - if someone did the translation. For each language there has to be a language package (e.g. Weather::Com::L10N::de for German). If there are new languages available that are not already included in this release, you'll find them on my support webpage (http://www.schnuecker.org/weather-com). If you don't find your prefered language, feel free to build your own language pack and send it to me. I'll then put it on my support page and it will be part of the next release. Up from version 0.5 there is 'dynamic language support'. That is, you can provide a language tag (e.g. 'de_DE') to a method returning textual information and the returned text than is not in the default language you defined while creating your Weather::Com::Finder instance but in the language you requested with your method call. To use the weather.com's service you have to register first (http://www.weather.com/services/xmloap.html), but's for free. You'll be send an email containing your 'partner id', a license key and a link where to download the SDK. Components of this module: -------------------------- This module consists of several parts: 1. Weather::Com::Base - the low level API 2. Weather::Com::Cached - implements the caching rules from weather.com 3. Weather::Com::Simple - high level wrapper around the API providing the same interace as Weather::Underground 4. Weather::Com::Finder - The entrance point to the main high level interface. 5. weather.pl - a sample console program using Weather::Com::Simple 6. weather_ext.pl - a sample console program using the new high level interface Usually you should always use one of the high level interfaces as parsing the hash structure returned by Weather::Com::Base and Weather::Com::Cached is not self-explanatory. If you plan to use the low level interface directly, please use Weather::Com::Cached instead of Weather::Com::Base to be sure to have the right caching rules implemented. You should only use Weather::Com::Base directly, if you plan to implement other caching mechanisms, e.g. a database driven one instead of the file- and 'Storable-' based one that is used in Weather::Com::Cached. If you want to implement another caching package for Weather::Com please contact the author of this module and tell him... There's no need to to the same work twice. Same applies for other packages to be build around the above ones.