How to get Coordinates when Address is Known?

19,821

Solution 1

What you're asking about is simply "geocoding". If you want to use Nominatim specifically, they call this "Search". This is, to a certain extent, address validation; but part of the "validation" is including coordinates (bounding box, lat/long, etc.; depending on what is searched for and what the type of result). There's lots of details about the results, too much to simply post here; but this detail can be found here: http://wiki.openstreetmap.org/wiki/Nominatim#Search (including examles).

You'll have to parse the results (XML, JSON, or HTML) to get the fields you're interested it.

Update 1:

As to what to do with the actual values: it depends. If you want to view the coordinates in a form, you can simply put the lat and long strings into individual controls. If you want to put it in a single control, could use string.Format("{0}, {1}", latString, longString). If you want to use the coords with various methods/types for a Windows Store app, you might need to use the Microsoft.Maps.MapControl.Location class. For example:

  Double latNumber;
  Double longNumber;
  if(false == Double.TryParse(latString, out latNumber)) throw new InvalidOperationException();
  if(false == Double.TryParse(longString, out longNumber)) throw new InvalidOperationException();
  var location = new Location(latNumber, longNumber);

The above assumes you've extracted the lat and long from the response and put them in latString, longString respectively.

Some interfaces may require lat/long as separate double values, in which case just use latNumber and longNumber above.

Over and above that, it really depends specifically on the interfaces you want to use. But, the above should give you enough to use most interfaces.

Update 2:

If the question isn't "how to get coordinates" but "how to parse json objects" then I'd recommend using JSon.Net to get at the lat/long strings in the json result. For example:

    var httpClient = new HttpClient();
    var httpResult = await httpClient.GetAsync(
        "http://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=json&polygon=1&addressdetails=1");

    var result = await httpResult.Content.ReadAsStringAsync();
    var r = (JArray) JsonConvert.DeserializeObject(result);
    var latString = ((JValue) r[0]["lat"]).Value as string;
    var longString = ((JValue)r[0]["lon"]).Value as string;

...see above w.r.t. what to do with latString and longString

Solution 2

If your looking for google maps geocoding, you can find it here:
https://developers.google.com/maps/documentation/geocoding/

and an example use is:
http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false

convert to Json, and take the Geometry object.

in regards to how to, basically, return two values from a function, in C# you have 4 options:

  1. convert both objects into a single object of the same type (in case of strings, you just separate them by a delimiter, like you did) and then parse both out afterwards.
  2. return a list - in this case a Task (or array).
  3. create a new class/object that contains both (in this case you can call it Geometry for instance).
  4. return one or both of the objects by requiring them to be passed as a reference to the function. In an async function, this is bound to be very tricky, but depending on who calls the function and who processes the result, it might be possible.

Solution 3

Microsoft Mappoint also contaisn an API you could make use of. It is able to return geocoordinates.

Share:
19,821
B. Clay Shannon-B. Crow Raven
Author by

B. Clay Shannon-B. Crow Raven

My novel about climate change and social justice featuring talking animals traveling through time and space to prevent disasters is now available on amazon, in three formats: Taterskin & The Eco Defenders Kindle eBook; Taterskin & The Eco Defenders Paperback; Taterskin & The Eco Defenders Hardcover Taterskin & The Eco Defenders, told in “first canine” by the titular character, a Labrador Retriever, is the story of a few humans and several talking animals who travel through time and space to make the past—and thus the future—a better place. The improvements effected by the Eco Defenders benefit not just the earth itself, but also mistreated humans and animals. In Book 1 (“Wonders Never Cease”), The Eco Defenders travel 150 million years into the past, to meet a Pterodactyl and make plans to “nip Nazism in the bud.” After that, it's on to 1787 Australia to protect the indigenous people and the environment there. The Eco Defenders next go to India, where they assemble animals from all over that country to put an end to Thuggee and fights to the death between Cobras and Mongooses. Their final stop is 1885 Africa, where the Eco Defenders band together with the local animals to prevent King Leopold of Belgium from taking control of the Congo, following which they put an end to the poaching of animals throughout the continent. Book 2 (“Tell it to Future Generations”) takes up with the Eco Defenders following up on their earlier adventures by 1) Preventing the American Civil War in 1861, after which a slave they free joins them; 2) Saving the Indians from being massacred at Wounded Knee in 1890, following which Chapawee, a Sioux Indian, joins the Eco Defenders; 3) Putting an end to the practice of vivisection (experimentation on live animals) in 1903; 4) Coming to the aid of exploited workers in 1911 Manhattan, saving hundreds from the Triangle Shirtwaist Fire; and 5) Traveling to the Amazon Basin in 1978 to protect and preserve the Amazon rainforest. @@@@@@@@@@@@@@@@@@@@@@@ I have lived in eight states; besides my native California (where I was born and where I now again reside), in chronological order I have infested: New York (Brooklyn), Montana (Helena), Alaska (Anchorage), Oklahoma (Bethany), Wisconsin (New Berlin and Oconomowoc), Idaho (Coeur d'Alene), and Missouri (Piedmont). I am a writer of both fiction (for which I use the nom de guerre "Blackbird Crow Raven", as a nod to my Native American heritage - I am "½ Cowboy, ½ Indian") and nonfiction, including a two-volume social and cultural history of the U.S. which covers important events from 1620-2006 and can be downloaded gratis here.

Updated on June 04, 2022

Comments

  • B. Clay Shannon-B. Crow Raven
    B. Clay Shannon-B. Crow Raven almost 2 years

    I derived/adapted the following code from Adam Freeman's book "Metro Revealed: Building Windows 8 apps with XAML and C#" to get the Address when the Coordinates are known:

    public static async Task<string> GetAddressForCoordinates(double latitude, double longitude)
    {
        HttpClient httpClient = new HttpClient {BaseAddress = new Uri("http://nominatim.openstreetmap.org")};
        HttpResponseMessage httpResult = await httpClient.GetAsync(
            String.Format("reverse?format=json&lat={0}&lon={1}", latitude, longitude));
    JsonObject jsonObject = JsonObject.Parse(await httpResult.Content.ReadAsStringAsync());
    
    return jsonObject.GetNamedObject("address").GetNamedString("road");
    

    }

    How can I get the opposite (the Coordinates if the Address is known)?

    UPDATE

    I'm adding a bounty to this; what I've got already (shown above) is the reverse geocoding (getting the address for the coordinates); what I need is geocoding (getting the coordinates for the address).

    Based on my reverse geocoding code above, I'm guessing it might be something like this:

    public static async Task<string> GetCoordinatesForAddress(string address)
    {
        HttpClient httpClient = new HttpClient {BaseAddress = new Uri("http://nominatim.openstreetmap.org")};
        HttpResponseMessage httpResult = await httpClient.GetAsync(
            String.Format("format=json&address={0}", address));
    
        JsonObject jsonObject = JsonObject.Parse(await httpResult.Content.ReadAsStringAsync());
    
        return jsonObject.GetNamedObject("address").GetNamedString("lat"); // <-- what about "lon"?
    }
    

    ...but I don't know how to combine the two coordinate (longitude and latitude) values (assuming this is correct, or close to being correct). Can anybody verify this, clean it up, or provide a better example (either using nominatim or otherwise)?

    UPDATE 2

    To answer Peter Ritchie's question/comment below:

    In the original (reverse geocoding code), I've got:

    return jsonObject.GetNamedObject("address").GetNamedString("road");
    

    It simply returns the road; so something like "157 Riverside Avenue" I assume.

    But for geocoding (needing two values, a longitude and a latitude), I've got this pseudocode:

    return jsonObject.GetNamedObject("address").GetNamedString("lat"); // <-- what about "lon"?
    

    So I don't know if I need to change the return value from Task<string> to Task<List and call (verbose pseudocode) [Note: I'm having a hard time escaping the angle brackets for Task with a List of string]:

    var latitude jsonObject.GetNamedObject("address").GetNamedString("lat");
    var longitude jsonObject.GetNamedObject("address").GetNamedString("lat");
    List<string> listCoordinates = new List<string>();
    listCoordinates.Add(latitude);
    listCoordinates.Add(longitude);
    return listCoordinates;
    

    ...or like so:

    string latitude jsonObject.GetNamedObject("address").GetNamedString("lat");
    string longtude jsonObject.GetNamedObject("address").GetNamedString("long");
    return string.Format("{0};{1}", latitude, longitude);
    

    ...or ???

    UPDATE 3

    In response to the proffered Json code for geocoding:

    Based on the original reverse geocode code, shouldn't the call be more like this:

    HttpClient httpClient = new HttpClient { BaseAddress = new Uri("http://nominatim.openstreetmap.org/") };
    var httpResult = await httpClient.GetAsync(
        String.Format("search?format=json&addressdetails={0}", address);
    

    ...but at any rate: The JArray type is not recognized, though JsonArray is. The JValue type is not recognized, though JsonValue is. The JsonConverter type is not recognized; perhaps part of Json.Net?

    The closest I can come to getting the proferred code to compile is:

    var result = await httpResult.Content.ReadAsStringAsync();
    var r = (JsonArray)JsonConverter.DeserializeObject(result);//<-- JsonConvert[er] not recognized; part of Json.NET?
    var latString = ((JsonValue)r[0]["lat"]).ValueType as string;
    var longString = ((JsonValue)r[0]["lon"]).ValueType as string;
    

    ...but even with this (close but no Bob Seger), JsonConvert as well as JsonConverter are not recognized.

    UPDATE 4

    After slogging more concertedly through the documentation at http://wiki.openstreetmap.org/wiki/Nominatim#Search, I think my original (reverse geocode) method might be better as:

    public static async Task`<string`> GetAddressForCoordinates(double latitude, double longitude)
    {
        HttpClient httpClient = new HttpClient {BaseAddress = new Uri("http://nominatim.openstreetmap.org/")};
        HttpResponseMessage httpResult = await httpClient.GetAsync(
            String.Format("reverse?format=json&lat={0}&lon={1}", latitude, longitude));
    
        JsonObject jsonObject = JsonObject.Parse(await httpResult.Content.ReadAsStringAsync());
    
        string house = jsonObject.GetNamedObject("addressparts").GetNamedString("house");
        string road = jsonObject.GetNamedObject("addressparts").GetNamedString("road");
        string city = jsonObject.GetNamedObject("addressparts").GetNamedString("city");
        string state = jsonObject.GetNamedObject("addressparts").GetNamedString("state");
        string postcode = jsonObject.GetNamedObject("addressparts").GetNamedString("postcode");
        string country = jsonObject.GetNamedObject("addressparts").GetNamedString("country");
        return string.Format("{0} {1}, {2}, {3} {4} ({5})", house, road, city, state, postcode, country);
    }
    

    This would return, for the corresponding coordinate args passed in, something like: "157 Riverside Avenue, Champaign, IL 55555 (USA)"

    What I find odd about the documentation is there is no "state" element among the address parts; if that is really true, and not just a documentation oversight, my code above would fail on the call to GetNamedString("state").

    I'm still not sure what the right syntax, etc., should be for the opposite (geocode) method, getting coordinates back after passing in an address.

    UPDATE 5

    Okay, I downloaded Json.NET and got it compiling. I haven't tested yet, but I've marked Peter Ritchie's as THE (50-point) answer.

    This is the code I'm using:

    public static async Task<string> GetCoordinatesForAddress(string address)
    {
        HttpClient httpClient = new HttpClient { BaseAddress = new Uri("http://nominatim.openstreetmap.org/") };
        HttpResponseMessage httpResult = await httpClient.GetAsync(
            String.Format("search?q={0}&format=json&addressdetails=1", Pluggify(address))); // In my Pluggify() method, I replace spaces with + and then lowercase it all
    
        var result = await httpResult.Content.ReadAsStringAsync();
        var r = (JArray)JsonConvert.DeserializeObject(result);
        var latString = ((JValue)r[0]["lat"]).Value as string;
        var longString = ((JValue)r[0]["lon"]).Value as string;
        return string.Format("{0};{1}", latString, longString);
    }
    

    Also: A funny thing happened on the way back to this forum: while installing Json.NET via NuGet, I also saw ".NET's fastest JSON Serializer by ServiceStack" which claims to be 3X faster than Json.NET. FIWW, it has been updated more recently than Json.NET. Thoughts/reaction?

    UPDATE 6

    I have this code to implement this (app id and code have been changed to protect the semi-innocent (me)):

    // If address has not been explicitly entered, try to suss it out:
                        address = textBoxAddress1.Text.Trim();
                        lat = textBoxLatitude1.Text.Trim();
                        lng = textBoxLongitude1.Text.Trim();
                        if (string.IsNullOrWhiteSpace(address))
                        {
                            address = await SOs_Classes.SOs_Utils.GetAddressForCoordinates(lat, lng);
                        }
    
    . . .
    
            public async static Task<string> GetAddressForCoordinates(string latitude, string longitude)
            {
                string currentgeoLoc = string.Format("{0},{1}", latitude, longitude);
                string queryString = string.Empty;
                string nokiaAppID = "j;dsfj;fasdkdf";
                object nokiaAppCode = "-14-14-1-7-47-178-78-4";
                var hereNetUrl = string.Format(
                    "http://demo.places.nlp.nokia.com/places/v1/discover/search?at={0}&q={1}&app_id={2}    
    &app_code={3}&accept=application/json",
                        currentgeoLoc, queryString, nokiaAppID, nokiaAppCode);    
                // get data from HERE.net REST API
                var httpClient = new HttpClient();
                var hereNetResponse = await httpClient.GetStringAsync(hereNetUrl);    
                // deseralize JSON from Here.net 
                using (var tr = new StringReader(hereNetResponse))
                using (var jr = new JsonTextReader(tr))
                {
                    var rootObjectResponse = new JsonSerializer    
    ().Deserialize<JsonDOTNetHelperClasses.RootObject>(jr);    
                    var firstplace = rootObjectResponse.results.items.First();
                    return HtmlUtilities.ConvertToText(firstplace.vicinity);
                    // NOTE: There is also a title (such as "Donut Shop", "Fire stations", etc.?) and type (such as "residence" or "business", etc.?)
                }
            }
    

    ...but on this line in GetAddressForCoordinates():

            var firstplace = rootObjectResponse.results.items.First();
    

    ...I get this err msg: "*System.InvalidOperationException was unhandled by user code HResult=-2146233079 Message=Sequence contains no elements Source=System.Core StackTrace: at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) at SpaceOverlays.SOs_Classes.SOs_Utils.d__12.MoveNext() in c:...*"

    The value of hereNetResponse is:

    {"results":{"items":[]},"search":{"context":{"location":{"position":[38.804967,-90.113183],"address":
    {"postalCode":"62048","city":"Hartford","stateCode":"IL","county":"Madison","countryCode":"USA","country":"
    USA","text":"Hartford IL 62048
    USA"}},"type":"urn:nlp-types:place","href":"http://demo.places.nlp.nokia.com/places/v1/places/loc-
    dmVyc2lvbj0xO3RpdGxlPUhhcnRmb3JkO2xhdD0zOC44MDQ5Njc7bG9uPS05MC4xMTMxODM7Y2l0eT1IY
    XJ0Zm9yZDtwb3N0YWxDb2RlPTYyMDQ4O2NvdW50cnk9VVNBO3N0YXRlQ29kZT1JTDtjb3VudHk9TWFka
    XNvbjtjYXRlZ29yeUlkPWNpdHktdG93bi12aWxsYWdl;context=Zmxvdy1pZD02YmUzZDM4Yi0wNGVhLTUyM
    jgtOWZmNy1kNWNkZGM0ODI5OThfMTM1NzQyMDI1NTg1M18wXzE2MA?
    app_id=F6zpNc3TjnkiCLwl_Xmh&app_code=QoAM_5BaVDZvkE2jRvc0mw"}}}
    

    ...so it would appear that there is valid info inside there, such as should return "Hartford, IL"

    And at any rate, a blank return value shouldn't throw an exception, I would think...