diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 14:40:05 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 14:40:05 -0300 |
| commit | 0bc578d6d334c7f1ccaf329cd61bb8bce76b7f59 (patch) | |
| tree | 86f73108f0432fd8330edcaab8b8ae7018322dee /lib/Cepis/Geo.pm | |
| download | perl-0.1.0.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'lib/Cepis/Geo.pm')
| -rw-r--r-- | lib/Cepis/Geo.pm | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/lib/Cepis/Geo.pm b/lib/Cepis/Geo.pm new file mode 100644 index 0000000..bee2515 --- /dev/null +++ b/lib/Cepis/Geo.pm @@ -0,0 +1,148 @@ +package Cepis::Geo; + +use strict; +use warnings; + +use Scalar::Util (); + +our $VERSION = '0.1.0'; + +sub from_json { + my ( $class, $data ) = @_; + + return undef unless ref $data eq 'HASH'; + + for my $key (qw(lat lon raio_m precisao)) { + return undef unless defined $data->{$key}; + } + for my $key (qw(lat lon raio_m)) { + return undef unless Scalar::Util::looks_like_number( $data->{$key} ); + } + return undef if ref $data->{precisao}; + + return bless { + lat => 0 + $data->{lat}, + lon => 0 + $data->{lon}, + raio_m => int( 0 + $data->{raio_m} ), + precisao => "$data->{precisao}", + }, $class; +} + +sub lat { return $_[0]->{lat} } +sub lon { return $_[0]->{lon} } +sub raio_m { return $_[0]->{raio_m} } +sub precisao { return $_[0]->{precisao} } + +sub is_precise { return $_[0]->{precisao} eq 'logradouro' ? 1 : 0 } + +sub to_hash { + my ($self) = @_; + return { + lat => $self->{lat}, + lon => $self->{lon}, + raio_m => $self->{raio_m}, + precisao => $self->{precisao}, + }; +} + +1; + +__END__ + +=encoding UTF-8 + +=head1 NAME + +Cepis::Geo - the coordinate of a CEP + +=head1 SYNOPSIS + + my $address = Cepis::lookup('01310100'); + + if ( my $geo = $address->geo ) { + printf "%s %s\n", $geo->lat, $geo->lon; # -23.567202 -46.649659 + print $geo->raio_m, "\n"; # 1084 + print $geo->precisao, "\n"; # logradouro + } + +=head1 DESCRIPTION + +The CEP's coordinate, when the database has one — about 57% of them. When there +is none, C<< $address->geo >> is C<undef>; the API omits the whole block rather +than sending null. + +The point is the B<median> of the coordinates of that CEP's addresses, not the +mean: one badly geocoded address 40 km away shifts a mean and does not move a +median. + +B<Always check C<precisao> before using C<lat>/C<lon> for distance.> There are +two kinds of point and the difference between them is tens of kilometres: + +=over 4 + +=item C<"logradouro"> + +The point is a stretch of street. C<raio_m> lands around 100–150 m. Good for +maps and for distance. + +=item C<"localidade"> + +The CEP I<is> the whole municipality (a city-wide general CEP), and the point is +its centroid. C<raio_m> runs from 7 km to 40 km. Computing shipping from this is +wrong by tens of kilometres. + +=back + +C<raio_m> is the radius containing 90% of that CEP's addresses. It exists +precisely so that error cannot pass unnoticed. + +Field names are the API's own, in Portuguese. See L<Cepis::Address> for why. + +=head1 CONSTRUCTOR + +=head2 from_json + + my $geo = Cepis::Geo->from_json( $hashref ); + +Builds from the C<geo> block the API returned. Returns C<undef> when the block +is missing, is not a hash, or is malformed — a bad C<geo> is not worth losing +the whole address over, since the rest of the response is still useful. + +=head1 ACCESSORS + +=head2 lat + +Latitude, as a number. + +=head2 lon + +Longitude, as a number. + +=head2 raio_m + +Radius in metres containing 90% of that CEP's addresses, as an integer. + +=head2 precisao + +Either C<"logradouro"> or C<"localidade">. Read the warning above before using +the coordinate for anything metric. + +=head2 is_precise + +True when the coordinate points at a street rather than a municipality. +Shorthand for C<< $geo->precisao eq 'logradouro' >>, which is the check to make +before using the point to measure distance. + +=head2 to_hash + +The four fields as a plain hashref. + +=head1 SEE ALSO + +L<Cepis>, L<Cepis::Address> + +=head1 LICENCE + +ISC. Copyright (c) 2026 Murilo Ijanc E<lt>[email protected]<gt>. + +=cut |