Click here to Skip to main content
Click here to Skip to main content

GeoNames .NET WCF Client

By , 21 Apr 2009
 

Introduction

GeoNames is a geographical database available under a Creative Commons attribution license. This data is accessible free of charge through a number (30+) of Web services.

The Web services include direct and reverse geocoding, finding places through postal codes, finding places next to a given place, weather observations for a given location, and finding Wikipedia articles about neighbouring places, etc.

Background

I have recently been working with the GeoNames services, both from JavaScript and from .NET. Among other things, we have been working with postal code auto-completion on the Web using JavaScript, and server-side postal code validation using .NET.

All these cool GeoNames services could definitely be used in many different contexts and applications. I have some ideas myself of UI controls and widgets that I would like to develop using GeoNames as data provider.

Therefore, I developed this WCF client for accessing the GeoNames services from .NET in a uniform, easy object-oriented manner.

Using the Code

These are just a few C# examples of using the client.

All the services are available through a single class, the GeoNamesClient (inherits System.ServiceModel.ClientBase<T>). We first create an instance of this object:

  using ( GeoNamesClient client = new GeoNamesClient ( ) )
  {
    ...
  }

Once the GeoNamesClient is created, any service may be called through this object. The first example shows how to retrieve all the cities within a geographical bounding box:

  double north = 44.1, south = -9.9, east = -22.4, west = 55.2;
  var cities = client.GetCities ( north, south, east, west );
  foreach ( GeoCity city in cities )
  {
    // GeoCity has many more properties to choose from...
    Console.WriteLine ( "City, Name={0}, Population={1}", city.Name, city.Population );
  }

The next example shows how to find weather observation for a given location:

  double latitude = 42, longitude = -2;
  var response = client.FindNearbyWeather ( latitude, longitude );
  GeoWeatherObservation weather = response.WeatherObservation;

  // GeoWeatherObservation has many more properties to choose from...
  Console.WriteLine ( "Current temperature is {0} degrees
    (delivered from the {1} weather station)",
    weather.Temperature, weather.StationName );

The client also provides overloaded methods for specifying culture, service verbosity, and controlling the max number of records to return for each service. The example shows how to search any geographical toponym that matches "london," with Spanish culture settings (place names, etc.) and limit the number of returned results to 10:

  GeoToponymSearchCriteria criteria = new GeoToponymSearchCriteria ( );
  criteria.Query = "london";
  criteria.Culture = "es";
  criteria.MaxRows = 10;
  var response = client.SearchToponyms ( criteria );
  foreach ( GeoToponym toponym in response )
  {
    // GeoToponym has many more properties to choose from...
    Console.WriteLine ( "Found toponym match, name:{0}", toponym.Name );
  }

Updated: Added support for geocoding addresses. This new example shows geocoding an address to determine it's spatial coordinate, and then pass coordinate to FindNearbyPostalCodes in order to find nearby postal codes for this address:

  string address = "1600 Amphitheatre Parkway Mountain View, CA  94043";
  double latitude, longitude;
  if ( client.TryGeocode ( address, out latitude, out longitude ) )
  {
    var response = client.FindNearbyPostalCodes ( latitude, longitude, 10 );

    foreach ( GeoPostalCode code in response )
    {
      ...
    }
  }
  else
    Console.WriteLine ( "-- Geocoding failed --" );

2.0.0.0 Update: Added support for approximating the bounding box for a given latitude/longitude pair, and a given radius in kilometers. The formula is based on the WGS84 system. A new useful GetBoundingBox method has been added to the GeoNamesClient class. This method could be used in conjunction with IGeoNamesClient methods that take bounding boxes, such as the GetCities method(s).

Below is an example of using GetBoundingBox to get a bounding box from a latitude/longitude pair and a radius, and then pass the bounding box to the GetCities method to retrieve cities within this bounding box:

double lat = 23.854, lng = 5.78;
double radius = 100; // kilometers
double north, south, east, west;

client.GetBoundingBox ( lat, lng, radius,
  out north, out south, out east, out west );

var cities = client.GetCities ( north, south, east, west );
foreach ( GeoCity city in cities )
{
  ...
}

Conclusion

If you need to access GeoNames services from your .NET application, you might want to look into my client. It supports all the available services, through a nice, object-oriented API.

History

  • 2008-11-02 1.0.0.0
  • 2008-11-04 1.5.0.0 Added support for geocoding
  • 2009-04-17 2.0.0.0
    • added support for WGS84 bounding box calculation (read updated article)
    • fixed Distance property serialization error, by changing type to String. The data returned in the Distance property is actually a String and not a Double; it is already formatted based on the specified return culture. Sorry for any existing code I might break..
    • fixed mapping to GeoStreetSegment.FeatureClasses, now mapped to MAF/TIGER Feature Class Codes
    • fixed/resolved failing tests

License

This article, along with any associated source code and files, is licensed under The MIT License

About the Author

baretta2
Software Developer (Senior)
Norway Norway
Member
Software Engineer at FAST, a Microsoft Subsidiary, in Oslo, Norway. Background in C++/PASCAL. Specialized in .NET and XML.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionhelp on the webservice.membermacupryk5 May '13 - 10:09 
The maximum message size quota for incoming messages (131072) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
 
Exception Details: System.ServiceModel.QuotaExceededException: The maximum message size quota for incoming messages (131072) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
 
Source Error:
 

Line 86:                 var countryName = client.GetCountry(_ddlCountry.SelectedValue);
Line 87:                 int GeoNameID = countryName.Items[0].GeoNameID;
Line 88:                 var stateprovinces = client.GetSiblings(GeoNameID);
Line 89:
Line 90:                 Dictionary<string, string> stateprovinceList = new Dictionary<string, string>();

QuestionReturn Null result [modified]memberJoe Yap25 Nov '11 - 2:29 
I am wondering do we need to send username and password over. I seem only gething empty result.
 
GeoStreetAddressResponse oGSAR = client.FindNearestAddress(23.854, 5.78);
System.Diagnostics.Debug.WriteLine("Address = {0}", oGSAR.Address);

modified 25 Nov '11 - 8:37.

QuestionHow to implement this code?membershanNSK22 Oct '11 - 1:10 
Thanks a lot for providing this article.
 
How to implement this code?
 
Please provide the steps.
GeneralWould be awesome to see this as a NuGet package.memberdanludwig29 Mar '11 - 17:02 
Any objections to having it added to the NuGet repository?
GeneralNew username requirement with geonames.orgmemberJoelHales3 Feb '11 - 12:24 
Anyone still using this code? Has anyone been able to access the web service with the new username requirement?
GeneralPolicyException under Medium TrustmemberManzoni Fausto9 Dec '09 - 2:01 
Exception Details: System.Security.Policy.PolicyException: Required permissions cannot be acquired.
 
[FileLoadException: Could not load file or assembly 'Myren.GeoNames.Client, Version=1.5.0.0, Culture=neutral, PublicKeyToken=2cbdaafbe8de925b' or one of its dependencies. 
 

There is some configuration to write into web.config?
 
Thanks in advance.
GeneralRe: PolicyException under Medium Trustmemberbaretta29 Dec '09 - 7:48 
Normally this would work:
<trust level="full" orginurl=""/>
 
However, I assume you are hosting the app in some environment which will not let you override the trust level in your web.config;
then this might help: http://www.chilkatsoft.com/p/p_116.asp
 
Or you can always just download the GeoNamesClient sources, and paste them into your web app so you don't need that dll reference.
GeneralRe: PolicyException under Medium TrustmemberManzoni Fausto9 Dec '09 - 23:24 
Many thanks
GeneralGetting Cities by PostCode [modified]membermws-h13 Oct '09 - 4:25 
Hi,
 
it looks very fine this tool but I'm having some problems. I need to show all the Cities corresponding to one post code in Germany. I cant find a way to do this. Can you show me a sample?
 
I got an error 417 if I try to use the postalCodeSearch function.
 
The query itselfe works fine when I type it in in the browser.
 
Thanks
 
modified on Tuesday, October 13, 2009 10:52 AM

GeneralRe: Getting Cities by PostCodememberbaretta213 Oct '09 - 6:21 
I don't know why you are getting a 417, is this consistent? Which method are you using, SearchPostalCodes or LookupPostalCode?
There are simple examples of using each different method in the .NET client in the Program.cs file in the Debug project, and also the tests that is included shows how to use the different methods provided.
GeneralRe: Getting Cities by PostCodemembermws-h15 Oct '09 - 21:29 
I get the 417 using the SearchPostalCode method also if I start the unit test.
However the tip with the LookupPostalCode was very helpful. With this it is working well.
 
Tanks a lot
QuestionVB examples?memberdhalls20 Sep '09 - 9:02 
I would like to use this client from within my ASP.NET application, but I can't seem to figure out the syntax to get it running in a VB environment. Does anyone have a sample or two in VB that they could post?
 
Thanks.
GeneralBroken. :(memberMike-EEEE28 Aug '09 - 6:27 
Looks like the following call is broken:
http://ws.geonames.org/postalCodeCountryInfoJSON?formatted=true&&style=full[^]
GeneralRe: Broken. :(memberbaretta228 Aug '09 - 6:38 
It seem to be removed from the GeoNames servers, i get a 404, while the XML version is still up. However, there are no information on the GeoNames site about removing this service, so let's wait and see what happens.
GeneralGood Work, and suggestionmemberl0t3k17 Apr '09 - 8:42 
Great job on this. i was about to have to write this...
 
A suggestion ( i have code you can use, if needed)
 
For GetCities (really any call taking a bounding box) , create an overload taking a starting point and a radius and internally calculate the bounding box.
 
"Fried ice-cream is a reality !" - George Clinton

GeneralRe: Good Work, and suggestion [modified]memberDennis JD Myren17 Apr '09 - 12:14 
Yeah, it's actually done now, i have posted updates to this article, however it could take a couple of days before they will have it published i would guess. So check in again in a few days, and download the 2.0.
BTW, i used the WGS84 system for approximating the bounding box. However since i am too lazy to write another 3-4 overloads of maybe 4 different functions, i will expose this new method as an utility on the GeoNamesClient class, which the user can call in conjunction with other calls that needs the bounding box.
 
But if you really want these overloads, then you could write C# 3.0 GeoNamesClient extension methods, it's a perfect job for 'em.
 
modified on Friday, April 17, 2009 6:21 PM

GeneralError in using Web Servicememberinterface Mirror3 Apr '09 - 5:26 
Hi,
 
I have about 21000 cities in my application and I am trying to use your great component to retrieve time-zone for them. But the problem is that whenever I run my application it only gets something like 900 to 1000 cities and after that I get "The HTTP request was forbidden with client authentication scheme 'Anonymous'." exception.
 
Yet, this was till yesterday that I could get 1000 cities at least but now I am not able to get a single time-zone and once I start my app it gives me above error.
 
Please guide me what to do?
GeneralRe: Error in using Web ServicememberDennis JD Myren3 Apr '09 - 6:26 
The free GeoNames is services is unpredictable. Sometimes they are really slow, sometimes they are even down.
It's frustrating, i know. I have been there myself.
 
The GeoNames responses you are getting indicates that you have reach the limit
for how much data you can request/how many requests you can do, while using the free GeoNames servers.
For more information regarding this, you can reach GeoNames through their forum at http://forum.geonames.org/
 
If you are using GeoNames large-scale, they provide commercial, much faster servers,
or you can donate money for more hardware to support the free version.
 

Maybe you have an alternative solution, maybe you could load time zones more on-demand based, rather than all at once?
And once you have them, you could consider storing them locally, since they are constant.
 

BTW, if you are returning large sets of data, you should also consider if it will be needed
to use the overloaded GeoNamesClient constructor that will let you specify your own binding,
since it might be necessary to increase MaxReceivedMessageSize, default is 131072.
However, this is not the problem in this case.
GeneralRe: Error in using Web Servicememberinterface Mirror3 Apr '09 - 6:57 
Hi Dennis,
 
A bunch of thanks for your detailed reply.
 
Well, I need the time zones only once. I have a set of 21000 cities and i need time zone for those cities. I have longitude and Latitude and all i need is time zone. Once I get the time zones I am not dependent on any service. But the question is how to get!
 
Is there a way to find time zone of a location based on its coordinates? Or are they providing any dump file that includes time zones?
 
Please guide.
GeneralRe: Error in using Web ServicememberDennis JD Myren3 Apr '09 - 7:20 
The GetTimeZone method will give you a time zone for the given latitude/longitude, so at least the WCF client will not stop you Smile | :)
 
It is also very possible that this data can be downloaded, i have never looked at the database files myself, but they can be downloaded from here: http://download.geonames.org/export/dump/
I guess the data is split up in zips by countries.
You could download a zip, and see if you find the timezone data in there, it should be there somewhere. Or ask on the forum, they'll know.
GeneralDeserialization error [modified]memberManzoni Fausto26 Mar '09 - 3:10 
Hi,
I give you just an example:
Dim cities = client.FindNearbyPlaceName(lat, lon, 1, "it", GeoStyle.Full, 1)
 
return an Exception because the "Distance" format for IT culture is for example("0,467" with "," as decimal separator) so, when datacontract try to convert this string in double, it throw an ex.
GeneralRe: Deserialization errormemberDennis JD Myren26 Mar '09 - 6:57 
Thanks for pointing this out, i wasn't aware of this.
I would think that setting the culture on the current thread would fix this, but i tried and indeed it didn't work.
I tried this:
System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo ( "it-IT" );
System.Threading.Thread.CurrentThread.CurrentCulture = ci;
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
var response = client.FindNearbyPlaceName ( 47.3, 9, 10, "it", GeoStyle.Full, 10 );
 
It failed.
 
I will look into this as soon as i have time, but obviously i don't know of a direct solution to this problem, while using declarative invocations (WebInvoke), which is really limiting the possibilites sometimes. But i would hope there's a way to fix this, and if anyone know a solution please share.
GeneralRe: Deserialization errormemberDennis JD Myren17 Apr '09 - 12:17 
It is fixed in 2.0 which should be published here in a day or two. However, i had to change Distance to a String, since that is actually what is returned from the service (see it as a properly formatted value, based on given return culture). Hope i wont break any existing code.
GeneralThanks!!!!memberDZaK8322 Mar '09 - 7:02 
Thank you, I was looking for method TryGeocode Smile | :)
 
Good work and thanks for sharing.
Jacek Ciereszko Smile | :)
 
Work smart not hard

GeneralThanks!memberveepee7818 Mar '09 - 10:47 
Nicely done piece of code, helps a lot using geonames! I'm combining exif and nmea data to put my pictures on map.
 
VP

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 21 Apr 2009
Article Copyright 2008 by baretta2
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid