1package Geo::Coordinates::Converter::Format::Dms;
2use strict;
3use warnings;
4use parent 'Geo::Coordinates::Converter::Format';
5
6use POSIX;
7
8our $DIGITS = 3;
9
10sub name { 'dms' }
11
12sub detect {
13    my($self, $point) = @_;
14
15    return unless defined $point->lat && $point->lat =~ /^[\-\+NS]?[0-9]{1,2}\.[0-9][0-9]?\.[0-9][0-9]?(?:\.[0-9]+)$/i;
16    return unless defined $point->lng && $point->lng =~ /^[\-\+EW]?[0-9]{1,3}\.[0-9][0-9]?\.[0-9][0-9]?(?:\.[0-9]+)$/i;
17
18    return $self->name;
19}
20
21sub to {
22    my($self, $point) = @_;
23
24    for my $meth (qw/ lat lng /) {
25        my($ws, $deg, $min, $sec) = $point->$meth =~ /^(\-?)([0-9]+)\.([0-9]+)\.([0-9]+(?:\.[0-9]+)?)$/i;
26        my $ret = $deg + ($min / 60) + ($sec / 3600);
27        $ret = $ws =~ /\-/i ? -1 * $ret : $ret;
28        $point->$meth($ret);
29    }
30
31    $point;
32}
33
34sub from {
35    my($self, $point) = @_;
36
37    for my $meth (qw/ lat lng /) {
38        my($ws, $degree) = $point->$meth =~ /^(\-?)(.+)$/;
39
40        my $deg  = floor($degree);
41        my $min  = floor(($degree - $deg) * 60 % 60);
42        my $sec  = ($degree - $deg) * 3600 - $min * 60;
43        $point->$meth(sprintf "%s%s.%s.%s", $ws || '', $deg, $min, $sec);
44    }
45
46    $point;
47}
48
49sub round {
50    my($self, $val) = @_;
51    sprintf "%s%s.%02d.%06.${DIGITS}f", ($val =~ /^(\-?)([^\.]+)\.([^\.]+)\.(.+)$/);
52}
53
541;
55