mourningdove/cgi-bin/LJ/Location.pm

106 lines
2.9 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
# This code was forked from the LiveJournal project owned and operated
# by Live Journal, Inc. The code has been modified and expanded by
# Dreamwidth Studios, LLC. These files were originally licensed under
# the terms of the license supplied by Live Journal, Inc, which can
# currently be found at:
#
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
#
# In accordance with the original license, this code and all its
# modifications are provided under the GNU General Public License.
# A copy of that license can be found in the LICENSE file included as
# part of this distribution.
package LJ::Location;
use strict;
use warnings;
use Math::Trig qw(deg2rad);
sub new {
my ( $class, %opts ) = @_;
my $self = bless {}, $class;
my $coords = delete $opts{'coords'};
my $loc = delete $opts{'location'};
die if %opts;
$self->set_coords($coords) if $coords;
$self->set_location($loc) if $loc;
return $self;
}
sub set_location {
my ( $self, $loc ) = @_;
$self->{location} = $loc;
return $self;
}
sub set_coords {
my ( $self, $coords ) = @_;
my ( $lat, $long );
if ( $coords =~ /^(\d+\.\d+)\s*([NS])\s*,?\s*(\d+\.\d+)\s*([EW])$/i ) {
my ( $latpos, $latside, $longpos, $longside ) = ( $1, uc $2, $3, uc $4 );
$lat = $latpos;
$lat = -$latpos if $latside eq "S";
$long = $longpos;
$long = -$longpos if $longside eq "W";
}
elsif ( $coords =~ /^(-?\d+\.\d+)\s*\,?\s*(-?\d+\.\d+)$/ ) {
$lat = $1;
$long = $2;
}
else {
die "Invalid coords format";
}
die "Latitude out of range" if abs $lat > 90;
die "Longitude out of range" if abs $long > 180;
$self->{lat} = $lat;
$self->{long} = $long;
return $self;
}
sub coordinates {
my $self = shift;
return $self->{lat}, $self->{long};
}
sub as_posneg_comma {
my $self = shift;
return undef unless $self->{lat} || $self->{long};
return sprintf( "%0.04f,%0.04f", $self->{lat}, $self->{long} );
}
sub as_current {
my $self = shift;
return $self->{location} || $self->as_posneg_comma;
}
# Average of polar and equatorial radius of the earth
sub EARTH_RADIUS_KILOMETERS () { 6371.005 }
sub EARTH_RADIUS_MILES () { 3958.759 }
sub _haversine_distance {
my ( $lat1, $lon1, $lat2, $lon2 ) = map { deg2rad($_) } @_;
my $dlon = $lon2 - $lon1;
my $dlat = $lat2 - $lat1;
my $a = ( sin( $dlat / 2 ) )**2 + cos($lat1) * cos($lat2) * ( sin( $dlon / 2 ) )**2;
return 2 * atan2( sqrt($a), sqrt( 1 - $a ) );
}
sub kilometers_to {
my $loc = shift;
my $loc2 = shift;
return EARTH_RADIUS_KILOMETERS * _haversine_distance( $loc->coordinates, $loc2->coordinates );
}
sub miles_to {
my $loc = shift;
my $loc2 = shift;
return EARTH_RADIUS_MILES * _haversine_distance( $loc->coordinates, $loc2->coordinates );
}
1;