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