# Station.pm - WWW::Velib::Station
# Copyright (c) 2007 David Landgren
# All rights reserved

package WWW::Velib::Station;
use strict;

use LWP::Simple;
use Math::Trig qw(deg2rad great_circle_distance);
use XML::Twig;

use vars '$VERSION';
$VERSION = '0.01';

use constant DETAILS => 'http://www.velib.paris.fr/service/stationdetails/';

sub new {
    my $class = shift;
    my $self  = bless {number => shift}, $class;
    return $self;

sub make {
    my $class = shift;
    my $self  = {};
    @{$self}{qw(number name address fullAddress lat lng open)} = @_;
    @{$self}{qw(theta phi)} = _theta_phi(@{$self}{qw(lat lng)});
    return bless $self, $class;

sub _theta_phi { deg2rad($_[0]), deg2rad(90 - $_[1]) }

sub load_v1 {
    my $class = shift;
    my $self  = {};
    @{$self}{qw(number open lat lng theta phi name address fullAddress)} = @_;
    return bless $self, $class;

sub coords {
    my $self = shift;
    return @{$self}{qw(theta phi)};

sub distance_from {
    my $self  = shift;
    my $there = shift;
    my $scale = shift || 5;
    $self->{dist} = $scale * sprintf( '%0.0f',
            great_circle_distance( $there->coords, $self->coords, 6378249)
        / $scale);
    return $self->{dist};

sub refresh {
    my $self = shift;
    if (my $content = get(DETAILS . $self->number)) {
        $self->{_html} = $content;
        my $twig = XML::Twig->new(
            twig_handlers => {
                available => sub {$self->{available} = $_[1]->text},
                free      => sub {$self->{free     } = $_[1]->text},
                total     => sub {$self->{total    } = $_[1]->text},
                ticket    => sub {$self->{ticket   } = $_[1]->text},
    else {
        @{$self}{qw(available free total ticket)} = (-1) x 4;
    return $self;

sub number       {return $_[0]->{number     }}
sub open         {return $_[0]->{open       }}
sub name         {return $_[0]->{name       }}
sub latitude     {return $_[0]->{lat        }}
sub longitude    {return $_[0]->{lng        }}
sub address      {return $_[0]->{address    }}
sub full_address {return $_[0]->{fullAddress}}
sub available    {return $_[0]->{available  }}
sub free         {return $_[0]->{free       }}
sub total        {return $_[0]->{total      }}
sub disabled  {
    return $_[0]->total == -1
        ? -1
        : $_[0]->total - ($_[0]->free + $_[0]->available)

'The Lusty Decadent Delights of Imperial Pompeii';

=head1 NAME

WWW::Velib::Station - Details of Velib' station bicycle and parking availability

=head1 VERSION

This document describes version 0.01 of WWW::Velib::Station, released


  use WWW::Velib::Station;

  my $s = WWW::Velib::Station->new(2007);
  print $s->available, $/; # hopefully a positive number


=head1 METHODS

=over 4

=item new

Create a WWW::Velib::Station object. A single input parameter
is given, representing the station number.

=item make

Create a WWW::Velib::Station object, based on the geographic
information provided by WWW::Velib::Map. Not expected to be called
from client code.

=item load_v1

Create a WWW::Velib::Station object, based on locally-cached contents
of the geographic information provided by WWW::Velib::Map. Not
expected to be called from client code.

=item coords

Returns a two-element list containing the theta and phi coordinates
of the station.

=item distance_from

Returns the distance in metres of a station from the current station.
The result is rounded by default to the nearest 5 metres.

    my $depart; # two WWW::Velib::Station objects
    my $arrive;
    my $distance = $depart->distance_from($arrive);

To round off to another interval, specify the rounding as a second

    my $dist_km = $depart->distance_from($arrive, 1000);

=item number

Returns the number (indentifier) of the station.

=item open

Indicates whether the station is open for business or not.

=item name

Returns the name of the station.

=item latitude

Returns the station's latitude, in degrees.

=item longitude

Returns the station's longitude, in degrees.

=item address

Returns the short address of the station.

=item full_address

Returns the full address of the station.

=item refresh

If a station has been built by loading a map, the following details
will not be loaded (it takes time to fetch a couple of thousand web
pages). This method will fetch the current status of the station
(bikes available, slots available). On a long-running process, may
be called repeatedly (at a suitable interval) to update the status.

=item available

Returns the number of bicycles available at the specified station.

=item total

Returns the total number of bicycle posts installed at the station.

=item free

Returns the number of bicycle posts that are able to receive a

=item disabled

Returns the number of bicycle posts that are locked or have a
locked bicycle attached.


=head1 AUTHOR

David Landgren, copyright (C) 2007. All rights reserved.


If you (find a) use this module, I'd love to hear about it. If you
want to be informed of updates, send me a note. You know my first
name, you know my domain. Can you guess my e-mail address?

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.