1package Geo::Coordinates::Converter::Format::IArea;
2use strict;
3use warnings;
4use base 'Geo::Coordinates::Converter::Format';
5our $VERSION = '0.01';
6use File::ShareDir 'dist_file';
7use CDB_File;
8use Geo::Coordinates::Converter::iArea;
9
10sub name { 'iarea' }
11
12sub detect {
13    my($self, $point) = @_;
14    return unless Geo::Coordinates::Converter::iArea->get_center( $point->areacode );
15    return $self->name;
16}
17
18# other(e.g. wgs84) to iarea
19sub from {
20    my ($self, $point) = @_;
21
22    my @mesh = _calc_meshcode($point);
23    if (my $areacode = $self->_meshcode2areacode(@mesh)) {
24        $point->areacode($areacode);
25    }
26    $point;
27}
28
29sub _meshcode2areacode {
30    my ($self, @mesh) = @_;
31
32    my $file = dist_file('Geo-Coordinates-Converter-iArea', 'meshcode2areacode.cdb');
33    my $cdb = CDB_File->TIEHASH($file);
34    for my $meshcode (@mesh) {
35        if ($cdb->EXISTS($meshcode)) {
36            return $cdb->FETCH($meshcode);
37        }
38    }
39    return;
40}
41
42sub _calc_meshcode {
43    my $point = shift;
44
45    # normalize
46    $point = do {
47        my $geo = Geo::Coordinates::Converter->new(point => $point);
48        $geo->convert('degree', 'tokyo');
49    };
50
51    my ($lat,$lng) = map { int ($_ * 60 * 60 * 1000) } ($point->lat, $point->lng);
52
53    my $mesh;
54    my @mesh;
55    my $ab = int($lat / 2400000);
56    my $cd = int($lng / 3600000) - 100;
57    my $x1 = ($cd +100) * 3600000;
58    my $y1 = $ab * 2400000;
59    my $e = int(($lat - $y1) / 300000);
60    my $f = int(($lng - $x1) / 450000);
61    $mesh = $ab.$cd.$e.$f;
62    push @mesh, $mesh;
63
64    my $x2 = $x1 + $f * 450000;
65    my $y2 = $y1 + $e * 300000;
66    my $l3 = int(($lng - $x2) / 225000);
67    my $m3 = int(($lat - $y2) / 150000);
68    my $g = $l3 + $m3 * 2;
69    $mesh .= $g;
70    push @mesh, $mesh;
71
72    my $x3 = $x2 + $l3 * 225000;
73    my $y3 = $y2 + $m3 * 150000;
74    my $l4 = int(($lng - $x3) / 112500);
75    my $m4 = int(($lat - $y3) / 75000);
76    my $h = $l4 + $m4 * 2;
77    $mesh .= $h;
78    push @mesh, $mesh;
79
80    my $x4 = $x3 + $l4 * 112500;
81    my $y4 = $y3 + $m4 * 75000;
82    my $l5 = int(($lng - $x4) / 56250);
83    my $m5 = int(($lat - $y4) / 37500);
84    my $i = $l5 + $m5 * 2;
85    $mesh .= $i;
86    push @mesh, $mesh;
87
88    my $x5 = $x4 + $l5 * 56250;
89    my $y5 = $y4 + $m5 * 37500;
90    my $l6 = int(($lng - $x5) / 28125);
91    my $m6 = int(($lat - $y5) / 18750);
92    my $j = $l6 + $m6 * 2;
93    $mesh .= $j;
94    push @mesh, $mesh;
95
96    my $x6 = $x5 + $l6 * 28125;
97    my $y6 = $y5 + $m6 * 18750;
98    my $l7 = int(($lng - $x6) / 14062.5);
99    my $m7 = int(($lat - $y6) / 9375);
100    my $k = $l7 + $m7 * 2;
101    $mesh .= $k;
102    push @mesh, $mesh;
103
104    @mesh;
105}
106
107# iarea to other(e.g. wgs84)
108sub to {
109    my($self, $point) = @_;
110
111    my $area_geo = _get_center($point) || { lat => '0.000000', lng => '0.000000' };
112
113    $point->lat($area_geo->{lat});
114    $point->lng($area_geo->{lng});
115    $point->datum('tokyo');
116
117    $point;
118}
119
120sub _get_center {
121    my $point = shift;
122    my $center = Geo::Coordinates::Converter::iArea->get_center( $point->areacode );
123    +{ lat => $center->lat, lng => $center->lng };
124}
125
126sub Geo::Coordinates::Converter::Point::areacode {
127    return $_[0]->{'areacode'} if @_ == 1;
128    return $_[0]->{'areacode'} = $_[1] if @_ == 2;
129    shift->{'areacode'} = \@_;
130}
131
132sub Geo::Coordinates::Converter::areacode {
133    my $self = shift;
134    my $point = shift || $self->current;
135    $point->areacode;
136}
137
1381;
139__END__
140
141=for stopwords aaaatttt dotottto gmail DoCoMo MOVA csv FOMA kazuhiro osawa areacode
142
143=head1 NAME
144
145Geo::Coordinates::Converter::Format::IArea - get center point from iArea
146
147=head1 SYNOPSIS
148
149=over 4
150
151=item GET CENTER POINT FROM AREA CODE
152
153  use Geo::Coordinates::Converter;
154  use Geo::Coordinates::Converter::iArea;
155
156  my $geo = Geo::Coordinates::Converter->new( format => 'iarea', areacode => '00205' );
157  my $point = $geo->convert('degree' => 'wgs84');
158  print "lat: ", $point->lat, " lng: ", $point->lng, "\n";
159  # => lat: 42.859220 lng: 141.492367
160
161=item GET AREACODE FROM LOCATION POINT
162
163  use Geo::Coordinates::Converter;
164  use Geo::Coordinates::Converter::iArea;
165
166  my $geo = Geo::Coordinates::Converter->new( format => 'degree', datum => 'tokyo', lat => '42.859220', lng => '141.492367' );
167  my $point = $geo->convert('iarea');
168  print $point->areacode, "\n";
169  # => 00205
170
171=item INTEGRATE WITH HTTP::MobileAgent::Plugin::Locator
172
173  use Geo::Coordinates::Converter;
174  use Geo::Coordinates::Converter::iArea;
175  use CGI;
176  use HTTP::MobileAgent;
177  use HTTP::MobileAgent::Plugin::Locator;
178
179  my $q = CGI->new();
180  my $agent = HTTP::MobileAgent->new();
181  my $orig_point = $agent->get_location($q);
182  my $geo = Geo::Coordinates::Converter->new( point => $orig_point->clone );
183  my $point = $geo->convert('iarea');
184  print $point->areacode, "\n";
185  # => 00205
186
187=back
188
189=head1 DESCRIPTION
190
191Geo::Coordinates::Converter::Format::IArea is utilities for DoCoMo iArea.
192
193easy to get the center point of area.
194
195=head1 ADDITIONAL METHODS FOR Geo::Coordinates::Converter core.
196
197=over 4
198
199=item Geo::Coordinates::Converter->areacode()
200
201areacode accessor
202
203=item Geo::Coordinates::Converter::Point->areacode()
204
205areacode accessor
206
207=back
208
209=head1 INTERNAL METHODS
210
211=over 4
212
213=item name
214
215=item detect
216
217=item from
218
219=item to
220
221DO NOT USE DIRECTLY
222
223=back
224
225=head1 AUTHOR
226
227Kazuhiro Osawa, Tokuhiro Matsuno
228
229=head1 SEE ALSO
230
231C<Geo::Coordinates::Converter::iArea>, L<Location::Area::DoCoMo::iArea>
232
233=head1 LICENSE
234
235This library is free software; you can redistribute it and/or modify
236it under the same terms as Perl itself.
237
238=cut
239