1package Geo::GeoNames; 2use utf8; 3use v5.10; 4use strict; 5use warnings; 6 7use Carp; 8use Mojo::UserAgent; 9use Scalar::Util qw/blessed/; 10 11use vars qw($DEBUG $CACHE); 12 13our $VERSION = '1.13'; 14 15our %searches = ( 16 cities => 'cities?', 17 country_code => 'countrycode?type=xml&', 18 country_info => 'countryInfo?', 19 earthquakes => 'earthquakesJSON?', 20 find_nearby_placename => 'findNearbyPlaceName?', 21 find_nearby_postalcodes => 'findNearbyPostalCodes?', 22 find_nearby_streets => 'findNearbyStreets?', 23 find_nearby_weather => 'findNearByWeatherXML?', 24 find_nearby_wikipedia => 'findNearbyWikipedia?', 25 find_nearby_wikipedia_by_postalcode => 'findNearbyWikipedia?', 26 find_nearest_address => 'findNearestAddress?', 27 find_nearest_intersection => 'findNearestIntersection?', 28 postalcode_country_info => 'postalCodeCountryInfo?', 29 postalcode_search => 'postalCodeSearch?', 30 search => 'search?', 31 wikipedia_bounding_box => 'wikipediaBoundingBox?', 32 wikipedia_search => 'wikipediaSearch?', 33 get => 'get?', 34 hierarchy => 'hierarchy?', 35 children => 'children?', 36 ); 37 38# r = required 39# o = optional 40# rc = required - only one of the fields marked with rc is allowed. At least one must be present 41# om = optional, multiple entries allowed 42# d = deprecated - will be removed in later versions 43our %valid_parameters = ( 44 search => { 45 'q' => 'rc', 46 name => 'rc', 47 name_equals => 'rc', 48 maxRows => 'o', 49 startRow => 'o', 50 country => 'om', 51 continentCode => 'o', 52 adminCode1 => 'o', 53 adminCode2 => 'o', 54 adminCode3 => 'o', 55 fclass => 'omd', 56 featureClass => 'om', 57 featureCode => 'om', 58 lang => 'o', 59 type => 'o', 60 style => 'o', 61 isNameRequired => 'o', 62 tag => 'o', 63 username => 'r', 64 name_startsWith => 'o', 65 countryBias => 'o', 66 cities => 'om', 67 operator => 'o', 68 searchlang => 'o', 69 charset => 'o', 70 fuzzy => 'o', 71 north => 'o', 72 west => 'o', 73 east => 'o', 74 south => 'o', 75 orderby => 'o', 76 }, 77 postalcode_search => { 78 postalcode => 'rc', 79 placename => 'rc', 80 country => 'o', 81 maxRows => 'o', 82 style => 'o', 83 username => 'r', 84 }, 85 find_nearby_postalcodes => { 86 lat => 'r', 87 lng => 'r', 88 radius => 'o', 89 maxRows => 'o', 90 style => 'o', 91 country => 'o', 92 username => 'r', 93 }, 94 postalcode_country_info => { 95 username => 'r', 96 }, 97 find_nearby_placename => { 98 lat => 'r', 99 lng => 'r', 100 radius => 'o', 101 style => 'o', 102 maxRows => 'o', 103 lang => 'o', 104 cities => 'o', 105 username => 'r', 106 }, 107 find_nearest_address => { 108 lat => 'r', 109 lng => 'r', 110 username => 'r', 111 }, 112 find_nearest_intersection => { 113 lat => 'r', 114 lng => 'r', 115 username => 'r', 116 }, 117 find_nearby_streets => { 118 lat => 'r', 119 lng => 'r', 120 username => 'r', 121 }, 122 find_nearby_wikipedia => { 123 lang => 'o', 124 lat => 'r', 125 lng => 'r', 126 radius => 'o', 127 maxRows => 'o', 128 country => 'o', 129 username => 'r', 130 }, 131 find_nearby_wikipedia_by_postalcode => { 132 postalcode => 'r', 133 country => 'r', 134 radius => 'o', 135 maxRows => 'o', 136 username => 'r', 137 }, 138 wikipedia_search => { 139 'q' => 'r', 140 lang => 'o', 141 title => 'o', 142 maxRows => 'o', 143 username => 'r', 144 }, 145 wikipedia_bounding_box => { 146 south => 'r', 147 north => 'r', 148 east => 'r', 149 west => 'r', 150 lang => 'o', 151 maxRows => 'o', 152 username => 'r', 153 }, 154 country_info => { 155 country => 'o', 156 lang => 'o', 157 username => 'r', 158 }, 159 country_code => { 160 lat => 'r', 161 lng => 'r', 162 lang => 'o', 163 radius => 'o', 164 username => 'r', 165 }, 166 find_nearby_weather => { 167 lat => 'r', 168 lng => 'r', 169 username => 'r', 170 }, 171 cities => { 172 north => 'r', 173 south => 'r', 174 east => 'r', 175 west => 'r', 176 lang => 'o', 177 maxRows => 'o', 178 username => 'r', 179 }, 180 earthquakes => { 181 north => 'r', 182 south => 'r', 183 east => 'r', 184 west => 'r', 185 date => 'o', 186 minMagnutide => 'o', 187 maxRows => 'o', 188 username => 'r', 189 }, 190 get => { 191 geonameId => 'r', 192 lang => 'o', 193 style => 'o', 194 username => 'r', 195 }, 196 hierarchy => { 197 geonameId => 'r', 198 username => 'r', 199 style => 'o', 200 }, 201 children => { 202 geonameId => 'r', 203 username => 'r', 204 style => 'o', 205 }, 206 ); 207 208sub new { 209 my( $class, %hash ) = @_; 210 211 my $self = bless { _functions => \%searches }, $class; 212 213 croak <<"HERE" unless length $hash{username}; 214You must specify a GeoNames username to use Geo::GeoNames. 215See http://www.geonames.org/export/web-services.html 216HERE 217 218 $self->username( $hash{username} ); 219 $self->url( $hash{url} // $self->default_url ); 220 221 croak 'Illegal ua object, needs either a Mojo::UserAgent or an LWP::UserAgent derived object' 222 if exists $hash{ua} && !(ref $hash{ua} && blessed($hash{ua}) && ( $hash{ua}->isa('Mojo::UserAgent') || $hash{ua}->isa('LWP::UserAgent') ) ); 223 $self->ua($hash{ua} || $self->default_ua ); 224 225 (exists($hash{debug})) ? $DEBUG = $hash{debug} : 0; 226 (exists($hash{cache})) ? $CACHE = $hash{cache} : 0; 227 $self->{_functions} = \%searches; 228 229 return $self; 230 } 231 232sub username { 233 my( $self, $username ) = @_; 234 235 $self->{username} = $username if @_ == 2; 236 237 $self->{username}; 238 } 239 240sub ua { 241 my( $self, $ua ) = @_; 242 243 $self->{ua} = $ua if @_ == 2; 244 245 $self->{ua}; 246 } 247 248sub default_ua { 249 my $ua = Mojo::UserAgent->new; 250 $ua->on( error => sub { carp "Can't get request" } ); 251 $ua; 252 } 253sub default_url { 'http://api.geonames.org' } 254 255sub url { 256 my( $self, $url ) = @_; 257 258 $self->{url} = $url if @_ == 2; 259 260 $self->{url}; 261 } 262 263sub _build_request_url { 264 my( $self, $request, @args ) = @_; 265 my $hash = { @args, username => $self->username }; 266 my $request_url = $self->url . '/' . $searches{$request}; 267 268 # check to see that mandatory arguments are present 269 my $conditional_mandatory_flag = 0; 270 my $conditional_mandatory_required = 0; 271 foreach my $arg (keys %{$valid_parameters{$request}}) { 272 my $flags = $valid_parameters{$request}->{$arg}; 273 if($flags =~ /d/ && exists($hash->{$arg})) { 274 carp("Argument $arg is deprecated."); 275 } 276 $flags =~ s/d//g; 277 if($flags eq 'r' && !exists($hash->{$arg})) { 278 carp("Mandatory argument $arg is missing!"); 279 } 280 if($flags !~ /m/ && exists($hash->{$arg}) && ref($hash->{$arg})) { 281 carp("Argument $arg cannot have multiple values."); 282 } 283 if($flags eq 'rc') { 284 $conditional_mandatory_required = 1; 285 if(exists($hash->{$arg})) { 286 $conditional_mandatory_flag++; 287 } 288 } 289 } 290 291 if($conditional_mandatory_required == 1 && $conditional_mandatory_flag != 1) { 292 carp("Invalid number of mandatory arguments (there can be only one)"); 293 } 294 foreach my $key (sort keys(%$hash)) { 295 carp("Invalid argument $key") if(!defined($valid_parameters{$request}->{$key})); 296 my @vals = ref($hash->{$key}) ? @{$hash->{$key}} : $hash->{$key}; 297 no warnings 'uninitialized'; 298 $request_url .= join('', map { "$key=$_&" } sort @vals ); 299 } 300 301 chop($request_url); # loose the trailing & 302 return $request_url; 303 } 304 305sub _parse_xml_result { 306 require XML::Simple; 307 my( $self, $geonamesresponse, $single_result ) = @_; 308 my @result; 309 my $xmlsimple = XML::Simple->new; 310 my $xml = $xmlsimple->XMLin( $geonamesresponse, KeyAttr => [], ForceArray => 1 ); 311 312 if ($xml->{'status'}) { 313 carp "GeoNames error: " . $xml->{'status'}->[0]->{message}; 314 return []; 315 } 316 317 $xml = { geoname => [ $xml ], totalResultsCount => '1' } if $single_result; 318 319 my $i = 0; 320 foreach my $element (keys %{$xml}) { 321 next if (ref($xml->{$element}) ne "ARRAY"); 322 foreach my $list (@{$xml->{$element}}) { 323 next if (ref($list) ne "HASH"); 324 foreach my $attribute (%{$list}) { 325 next if !defined($list->{$attribute}->[0]); 326 $result[$i]->{$attribute} = (scalar @{$list->{$attribute}} == 1 ? $list->{$attribute}->[0] : $list->{$attribute}); 327 } 328 $i++; 329 } 330 } 331 return \@result; 332 } 333 334sub _parse_json_result { 335 require JSON; 336 my( $self, $geonamesresponse ) = @_; 337 my @result; 338 return JSON->new->utf8->decode($geonamesresponse); 339 } 340 341sub _parse_text_result { 342 my( $self, $geonamesresponse ) = @_; 343 my @result; 344 $result[0]->{Result} = $geonamesresponse; 345 return \@result; 346 } 347 348sub _request { 349 my( $self, $request_url ) = @_; 350 351 my $res = $self->{ua}->get( $request_url ); 352 return $res->can('res') ? $res->res : $res; 353 } 354 355sub _do_search { 356 my( $self, $searchtype, @args ) = @_; 357 358 my $request_url = $self->_build_request_url( $searchtype, @args ); 359 my $response = $self->_request( $request_url ); 360 361 # check mime-type to determine which parse method to use. 362 # we accept text/xml, text/plain (how do see if it is JSON or not?) 363 my $mime_type = $response->headers->content_type || ''; 364 365 my $body = ''; 366 if ($response->can('body')) { 367 $body = $response->body; 368 } 369 else { 370 $body = $response->content; 371 } 372 373 if($mime_type =~ m(\Atext/xml;?) ) { 374 return $self->_parse_xml_result( $body, $searchtype eq 'get' ); 375 } 376 if($mime_type =~ m(\Aapplication/json;?) ) { 377 # a JSON object always start with a left-brace { 378 # according to http://json.org/ 379 if( $body =~ m/\A\{/ ) { 380 if ($response->can('json')) { 381 return $response->json; 382 } 383 else { 384 return $self->_parse_json_result( $body ); 385 } 386 } 387 else { 388 return $self->_parse_text_result( $body ); 389 } 390 } 391 392 if($mime_type eq 'text/plain') { 393 carp 'Invalid mime type [text/plain]. ', $response->content(); 394 } else { 395 carp "Invalid mime type [$mime_type]. Maybe you aren't connected."; 396 } 397 398 return []; 399 } 400 401sub geocode { 402 my( $self, $q ) = @_; 403 $self->search( 'q' => $q ); 404 } 405 406sub AUTOLOAD { 407 my $self = shift; 408 my $type = ref($self) || croak "$self is not an object"; 409 my $name = our $AUTOLOAD; 410 $name =~ s/.*://; 411 412 unless (exists $self->{_functions}->{$name}) { 413 croak "No such method '$AUTOLOAD'"; 414 } 415 416 return($self->_do_search($name, @_)); 417 } 418 419sub DESTROY { 1 } 420 4211; 422 423__END__ 424 425=encoding utf8 426 427=head1 NAME 428 429Geo::GeoNames - Perform geographical queries using GeoNames Web Services 430 431=head1 SYNOPSIS 432 433 use Geo::GeoNames; 434 my $geo = Geo::GeoNames->new( username => $username ); 435 436 # make a query based on placename 437 my $result = $geo->search(q => 'Fredrikstad', maxRows => 2); 438 439 # print the first result 440 print " Name: " . $result->[0]->{name}; 441 print " Longitude: " . $result->[0]->{lng}; 442 print " Lattitude: " . $result->[0]->{lat}; 443 444 # Make a query based on postcode 445 my $result = $geo->postalcode_search( 446 postalcode => "1630", maxRows => 3, style => "FULL" 447 ); 448 449=head1 DESCRIPTION 450 451Before you start, get a free GeoNames account and enable it for 452access to the free web service: 453 454=over 4 455 456=item * Get an account 457 458Go to L<http://www.geonames.org/login> 459 460=item * Respond to the email 461 462=item * Login and enable your account for free access 463 464L<http://www.geonames.org/enablefreewebservice> 465 466=back 467 468Provides a perl interface to the webservices found at 469L<http://api.geonames.org>. That is, given a given placename or 470postalcode, the module will look it up and return more information 471(longitude, lattitude, etc) for the given placename or postalcode. 472Wikipedia lookups are also supported. If more than one match is found, 473a list of locations will be returned. 474 475=head1 METHODS 476 477=over 4 478 479=item new 480 481 $geo = Geo::GeoNames->new( username => '...' ) 482 $geo = Geo::GeoNames->new( username => '...', url => $url ) 483 484Constructor for Geo::GeoNames. It returns a reference to an 485Geo::GeoNames object. You may also pass the url of the webservices to 486use. The default value is L<http://api.geonames.org> and is the only url, 487to my knowledge, that provides the services needed by this module. The 488username parameter is required. 489 490=item ua( $ua ) 491 492With a single argument, set the UserAgent to be used by all API calls 493and return that UserAgent object. Supports L<Mojo::UserAgent> and 494 L<LWP::UserAgent> derivatives. 495 496With no arguments, return the current UserAgent used. 497 498=item username( $username ) 499 500With a single argument, set the GeoNames username and return that 501username. With no arguments, return the username. 502 503=item default_ua 504 505Returns the default UserAgent used a Mojo::UserAgent object that 506carps on errors. 507 508=item default_url 509 510Returns C<http://api.geonames.org>. 511 512=item url( $url ) 513 514With a single argument, set the GeoNames url and return that 515url. With no arguments, return the url. 516 517=item geocode( $placename ) 518 519This method is just an easy access to search. It is the same as 520saying: 521 522 $geo->search( q => $placename ); 523 524=item search( arg => $arg ) 525 526Searches for information about a placename. Valid names for B<arg> are 527as follows: 528 529 q => $placename 530 name => $placename 531 name_equals => $placename 532 maxRows => $maxrows 533 startRow => $startrow 534 country => $countrycode 535 continentCode => $continentcode 536 adminCode1 => $admin1 537 adminCode2 => $admin2 538 adminCode3 => $admin3 539 fclass => $fclass 540 featureClass => $fclass, 541 featureCode => $code 542 lang => $lang 543 type => $type 544 style => $style 545 isNameRequired => $isnamerequired 546 tag => $tag 547 name_startsWith => $name_startsWith 548 countryBias => $countryBias 549 cities => $cities 550 operator => $operator 551 searchlang => $searchlang 552 charset => $charset 553 fuzzy => $fuzzy 554 north => $north 555 west => $west 556 east => $east 557 south => $south 558 orderby => $orderby 559 560One, and only one, of B<q>, B<name>, B<name_equals>, or B<name_startsWith> must be 561supplied to this method. 562 563fclass is deprecated. 564 565For a thorough description of the arguments, see 566L<http://www.geonames.org/export/geonames-search.html> 567 568=item find_nearby_placename( arg => $arg ) 569 570Reverse lookup for closest placename to a given coordinate. Valid 571names for B<arg> are as follows: 572 573 lat => $lat 574 lng => $lng 575 radius => $radius 576 style => $style 577 maxRows => $maxrows 578 579Both B<lat> and B<lng> must be supplied to this method. 580 581For a thorough descriptions of the arguments, see 582L<http://www.geonames.org/export> 583 584=item find_nearest_address(arg => $arg) 585 586Reverse lookup for closest address to a given coordinate. Valid names 587for B<arg> are as follows: 588 589 lat => $lat 590 lng => $lng 591 592Both B<lat> and B<lng> must be supplied to this method. 593 594For a thorough descriptions of the arguments, see 595L<http://www.geonames.org/maps/reverse-geocoder.html> 596 597US only. 598 599=item find_nearest_intersection(arg => $arg) 600 601Reverse lookup for closest intersection to a given coordinate. Valid 602names for B<arg> are as follows: 603 604 lat => $lat 605 lng => $lng 606 607Both B<lat> and B<lng> must be supplied to this method. 608 609For a thorough descriptions of the arguments, see 610L<http://www.geonames.org/maps/reverse-geocoder.html> 611 612US only. 613 614=item find_nearby_streets(arg => $arg) 615 616Reverse lookup for closest streets to a given coordinate. Valid names 617for B<arg> are as follows: 618 619 lat => $lat 620 lng => $lng 621 622Both B<lat> and B<lng> must be supplied to this method. 623 624For a thorough descriptions of the arguments, see 625L<http://www.geonames.org/maps/reverse-geocoder.html> 626 627US only. 628 629=item postalcode_search(arg => $arg) 630 631Searches for information about a postalcode. Valid names for B<arg> 632are as follows: 633 634 postalcode => $postalcode 635 placename => $placename 636 country => $country 637 maxRows => $maxrows 638 style => $style 639 640One, and only one, of B<postalcode> or B<placename> must be supplied 641to this method. 642 643For a thorough description of the arguments, see 644L<http://www.geonames.org/export> 645 646=item find_nearby_postalcodes(arg => $arg) 647 648Reverse lookup for postalcodes. Valid names for B<arg> are as follows: 649 650 lat => $lat 651 lng => $lng 652 radius => $radius 653 maxRows => $maxrows 654 style => $style 655 country => $country 656 657Both B<lat> and B<lng> must be supplied to this method. 658 659For a thorough description of the arguments, see 660L<http://www.geonames.org/export> 661 662=item postalcode_country_info 663 664Returns a list of all postalcodes found on GeoNames. This method 665takes no arguments. 666 667=item country_info(arg => $arg) 668 669Returns country information. Valid names for B<arg> are as follows: 670 671 country => $country 672 lang => $lang 673 674For a thorough description of the arguments, see 675L<http://www.geonames.org/export> 676 677=item find_nearby_wikipedia(arg => $arg) 678 679Reverse lookup for Wikipedia articles. Valid names for B<arg> are as 680follows: 681 682 lat => $lat 683 lng => $lng 684 radius => $radius 685 maxRows => $maxrows 686 lang => $lang 687 country => $country 688 689Both B<lat> and B<lng> must be supplied to this method. 690 691For a thorough description of the arguments, see 692L<http://www.geonames.org/export> 693 694=item find_nearby_wikipediaby_postalcode(arg => $arg) 695 696Reverse lookup for Wikipedia articles. Valid names for B<arg> are as 697follows: 698 699 postalcode => $postalcode 700 country => $country 701 radius => $radius 702 maxRows => $maxrows 703 704Both B<postalcode> and B<country> must be supplied to this method. 705 706For a thorough description of the arguments, see 707L<http://www.geonames.org/export> 708 709=item wikipedia_search(arg => $arg) 710 711Searches for Wikipedia articles. Valid names for B<arg> are as 712follows: 713 714 q => $placename 715 maxRows => $maxrows 716 lang => $lang 717 title => $title 718 719B<q> must be supplied to this method. 720 721For a thorough description of the arguments, see 722L<http://www.geonames.org/export> 723 724=item wikipedia_bounding_box(arg => $arg) 725 726Searches for Wikipedia articles. Valid names for B<arg> are as 727follows: 728 729 south => $south 730 north => $north 731 east => $east 732 west => $west 733 lang => $lang 734 maxRows => $maxrows 735 736B<south>, B<north>, B<east>, and B<west> and must be supplied to this method. 737 738For a thorough description of the arguments, see 739L<http://www.geonames.org/export> 740 741=item cities(arg => $arg) 742 743Returns a list of cities and placenames within the bounding box. 744Valid names for B<arg> are as follows: 745 746 south => $south 747 north => $north 748 east => $east 749 west => $west 750 lang => $lang 751 maxRows => $maxrows 752 753B<south>, B<north>, B<east>, and B<west> and must be supplied to this method. 754 755For a thorough description of the arguments, see 756L<http://www.geonames.org/export> 757 758=item country_code(arg => $arg) 759 760Return the country code for a given point. Valid names for B<arg> are 761as follows: 762 763 lat => $lat 764 lng => $lng 765 radius => $radius 766 lang => $lang 767 768Both B<lat> and B<lng> must be supplied to this method. 769 770For a thorough description of the arguments, see 771L<http://www.geonames.org/export> 772 773=item earthquakes(arg => $arg) 774 775Returns a list of cities and placenames within the bounding box. 776Valid names for B<arg> are as follows: 777 778 south => $south 779 north => $north 780 east => $east 781 west => $west 782 date => $date 783 minMagnitude => $minmagnitude 784 maxRows => $maxrows 785 786B<south>, B<north>, B<east>, and B<west> and must be supplied to this method. 787 788For a thorough description of the arguments, see 789L<http://www.geonames.org/export> 790 791=item find_nearby_weather(arg => $arg) 792 793Return the country code for a given point. Valid names for B<arg> are 794as follows: 795 796 lat => $lat 797 lng => $lng 798 799Both B<lat> and B<lng> must be supplied to this method. 800 801For a thorough description of the arguments, see 802L<http://www.geonames.org/export> 803 804=item get(arg => $arg) 805 806Returns information about a given place based on a geonameId. 807 808 geonameId => $geonameId 809 lang => $lang 810 style => $style (Seems to be ignored, although documented) 811 812B<geonamesId> must be supplied to this method. B<lang> and B<style> are optional. 813 814For a thorough description of the arguments, see 815L<http://www.geonames.org/export> 816 817=item hiearchy(arg => $arg) 818 819Returns all GeoNames higher up in the hierarchy of a place based on a geonameId. 820 821 geonameId => $geonameId 822 style => $style (Not documented, but seems to be respected) 823 824B<geonamesId> must be supplied to this method. B<style> is optional. 825 826For a thorough description of the arguments, see 827L<http://www.geonames.org/export/place-hierarchy.html#hierarchy> 828 829=item children(arg => $arg) 830 831Returns the children (admin divisions and populated places) for a given geonameId. 832 833 geonameId => $geonameId 834 style => $style (Not documented, but seems to be respected) 835 836B<geonamesId> must be supplied to this method. B<style> is optional. 837 838For a thorough description of the arguments, see 839L<https://www.geonames.org/export/place-hierarchy.html> 840 841=back 842 843=head1 RETURNED DATASTRUCTURE 844 845The datastructure returned from methods in this module is an array of 846hashes. Each array element contains a hash which in turn contains the 847information about the placename/postalcode. 848 849For example, running the statement 850 851 my $result = $geo->search( 852 q => "Fredrikstad", maxRows => 3, style => "FULL" 853 ); 854 855yields the result: 856 857 $VAR1 = { 858 'population' => {}, 859 'lat' => '59.2166667', 860 'elevation' => {}, 861 'countryCode' => 'NO', 862 'adminName1' => "\x{d8}stfold", 863 'fclName' => 'city, village,...', 864 'adminCode2' => {}, 865 'lng' => '10.95', 866 'geonameId' => '3156529', 867 'timezone' => { 868 'dstOffset' => '2.0', 869 'content' => 'Europe/Oslo', 870 'gmtOffset' => '1.0' 871 }, 872 'fcode' => 'PPL', 873 'countryName' => 'Norway', 874 'name' => 'Fredrikstad', 875 'fcodeName' => 'populated place', 876 'alternateNames' => 'Frederikstad,Fredrikstad,Fredrikstad kommun', 877 'adminCode1' => '13', 878 'adminName2' => {}, 879 'fcl' => 'P' 880 }; 881 882The elements in the hashes depends on which B<style> is passed to the 883method, but will always contain B<name>, B<lng>, and B<lat> except for 884postalcode_country_info(), find_nearest_address(), 885find_nearest_intersection(), and find_nearby_streets(). 886 887=head1 BUGS 888 889Not a bug, but the GeoNames services expects placenames to be UTF-8 890encoded, and all data received from the webservices are also UTF-8 891encoded. So make sure that strings are encoded/decoded based on the 892correct encoding. 893 894Please report any bugs found or feature requests through GitHub issues 895L<https://github.com/briandfoy/geo-geonames/issues> 896 897=head1 SEE ALSO 898 899=over 4 900 901=item * L<http://www.geonames.org/export> 902 903=item * L<http://www.geonames.org/export/ws-overview.html> 904 905=back 906 907=head1 SOURCE AVAILABILITY 908 909The source code for this module is available from Github 910at L<https://github.com/briandfoy/geo-geonames> 911 912=head1 AUTHOR 913 914Per Henrik Johansen, C<< <per.henrik.johansen@gmail.com> >>. 915 916Currently maintained by brian d foy, C<< <brian.d.foy@gmail.com> >> 917and Nicolas Mendoza, C<< <mendoza@pvv.ntnu.no> >> 918 919=head1 COPYRIGHT AND LICENSE 920 921Copyright (C) 2007-2008 by Per Henrik Johansen 922 923This library is available under the Artistic License 2.0. 924 925=cut 926