10

In GeoListPlot the locations are supposed to be GeoPosition or Entity objects representing entities with geographic coordinates.

But say I have a few exact addresses that I'd like to plot (with callout labels):

addresses = <|
   "Land's End Bed and Breakfast" -> 
    "22 Commercial St, Provincetown, MA 02657", 
   "Favorite Restaurant" -> "9 Park St, Boston, MA 02108"|>;

If wolfram alpha can't find them, then how can I get this to work:

enter image description here

Is there an "address to lat/long" conversion tool on the web accessible though MMA?

M.R.
  • 31,425
  • 8
  • 90
  • 281

2 Answers2

11

"address to lat/long" is usually called Geocoding, in case that helps with your research. Some options for a Geocoding API include OpenStreetMaps' Nominatim, Google Maps' API, and many others. Beware that virtually every option will either require payment or have a very stringent usage quota.

FindGeoLocation or Interpreter

FindGeoLocation["9 Park St, Boston, MA 02108"]

GeoPosition[{42.3565, -71.062}]

Another official Wolfram Language way to do this is by using Interpreter, like so:

Interpreter["StreetAddress"]["9 Park St, Boston, MA 02108"]

GeoPosition[{42.3565, -71.062}]

Both of those functions work for the addresses you have, so you can plot them like so:

GeoListPlot[
 KeyValueMap[
  GeoMarker[Interpreter["StreetAddress"][#2], #1] &, 
  addresses]]

or

GeoListPlot[
     KeyValueMap[
      GeoMarker[FindGeoLocation[#2], #1] &, 
      addresses]]

enter image description here

You can experiment with different marker styling and so on - check the documentation for GeoMarker. Many of the typical Plot labeling functions like Callout aren't supported in GeoGraphics functions, however.

Nominatim

You might consider using Nominatim, in which case you could do it the following way:

First, query the service:

result = Import[
  HTTPRequest[
   "https://nominatim.openstreetmap.org/search", <|
    "Query" -> URLQueryEncode[<|
       "format" -> "xml",
       "q" -> "9 Park St, Boston, MA 02108",
       "limit" -> 1
       |>]|>], "XML"]

Extract the attributes of the first place:

attrs = Association[
  Cases[result, XMLElement["place", attr_, _] -> attr, \[Infinity]]]

Finally, the actual position can be determined:

position = GeoPosition@ToExpression@Values[attrs[[{"lat", "lon"}]]]

Simply do this for each address you have and plot it like above.

We can bundle it into a single function:

geocode[address_] := Module[{result =
    Import[
     HTTPRequest[
      "https://nominatim.openstreetmap.org/search", <|
       "Query" -> 
        URLQueryEncode[<|"format" -> "xml", "q" -> address, 
          "limit" -> 1|>]|>], "XML"],
   attrs
   },
  attrs = 
   Association[
    Cases[result, XMLElement["place", attr_, _] -> attr, \[Infinity]]];
  GeoPosition@ToExpression@Values[attrs[[{"lat", "lon"}]]]
  ]

And now we can do this:

geocode["9 Park St, Boston, MA 02108"]

GeoPosition[{42.3576, -71.0629}]

That's extremely close to the Nominatim result. So close that I bet Wolfram have their own Nominatim deployment for this functionality.

Anyway, since we have this function, we can plot things the same way as if we used Interpreter:

GeoListPlot[KeyValueMap[GeoMarker[geocode[#2], #1] &, addresses]]

enter image description here

As I said above: You can experiment with different marker styling and so on - check the documentation for GeoMarker. Many of the typical Plot labeling functions like Callout aren't supported in GeoGraphics functions, however.

Carl Lange
  • 13,065
  • 1
  • 36
  • 70
  • I love learning new words... "Geocoding"! @Carl_Lange (ugh, not sure why when I type '@CarlLange' and press save - it disappears) – M.R. Jan 22 '19 at 17:59
  • @M.R. It's been an important topic here in Ireland, because we didn't have postcodes until very recently, making geocoding an extremely lucrative business for the post office :) – Carl Lange Jan 22 '19 at 21:59
9

Pulling up an old question here, but wanted to add a new answer that is a bit more straight forward. The US Census Bureau has an API that can help with this. If all your addresses are in the US, then is an easy, free way to get started with minimal code. You can read the details here: https://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.pdf

Using that API, the following function will get you what you need:

geoCoder[string_]:=GeoPosition[Reverse[First[Values[Import["https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?address="<>URLEncode[string]<>"&benchmark=2020&format=json","RawJSON"][["result","addressMatches",All,"coordinates"]]]]]]

Using that function, you can now run it on your addresses.

GeoListPlot[geoCoder[#] & /@ Values[addresses]]

That yields the following:

enter image description here

kickert
  • 1,820
  • 8
  • 22