XQuery/Google Geocoding

From Wikibooks, open books for an open world
Jump to: navigation, search

Motivation[edit]

You have one or more geographic names and you want to create a map of these locations.

Method[edit]

We will use a Google RESTful web service to return geographical data from a list of place names. Google provides an HTTP-based Geocoding service. This requires registration of a site for a API key and there are limitations on the usage of this API.

Querying Google's API[edit]

The following script takes a location and returns the xml from the service:

let $key := "ABQIAAAAVehr0_0wqgw_UOdLv0TYtxSGVrvsBPWDlNZ2fWdNTHNT32FpbBR1ygnaHxJdv-8mkOaL2BJb4V_yOQ"
(: get the geo name as parameter from the incomming URL :)
let $location := request:get-parameter("location",())
(: adjust the escape codes :)
let $location := escape-uri($location, false())
(: construct a new URL that appends the geo-name and key to the service URL.  Tell the service that we want XML output format. :)
let $url := concat("http://maps.google.com/maps/geo?q=",$location,"&output=xml&key=",$key)
(: send the URL out and put the result in the $response variable :)
let $response := doc($url)
return
   $response

Examples[edit]

Single City Examples:

  • Minneapolis - example using a single city with no country
  • Bristol,UK - example using a city and country

Multiple matches may be returned:

Response as KML[edit]

The XML response can be reformated as a simpler KML file. Note the addition of the relevant media-type for KML and the declaration of the KML namespace required to access the returned XML.

declare option exist:serialize "method=xml media-type=application/vnd.google-earth.kml+xml  indent=yes";
 
declare namespace kml = "http://earth.google.com/kml/2.0";
 
let $key := "ABQIAAAAVehr0_0wqgw_UOdLv0TYtxSGVrvsBPWDlNZ2fWdNTHNT32FpbBR1ygnaHxJdv-8mkOaL2BJb4V_yOQ"
let $location := request:get-parameter("location",())
let $location := escape-uri($location,false())
let $url := concat("http://maps.google.com/maps/geo?q=",$location,"&output=xml&key=",$key)
let $response := doc($url)
let $x := response:set-header('Content-disposition',concat('inline;filename="',$location,'.kml";'))
return
<kml xmlns="http://earth.google.com/kml/2.0">
   <Folder>
      <name>{$location}</name>
      {
      for $place in $response//kml:Placemark
      return 
         <Placemark>
           <name>{string($place/kml:address)}</name>
           {$place/kml:Point}
         </Placemark>
      }
   </Folder>
 </kml>

If you have GoogleEarth, this should load an overlay: Utopia KML

GoogleMap[edit]

A simple way to view the generated kml is to use GoogleMaps. This script simply constructs the relevant GoogleMap URL and then redirects to that URL:


let $location := request:get-parameter("location",())
let $location := escape-uri($location,false())
let $wikiurl := escape-uri(concat("http://www.cems.uwe.ac.uk/xmlwiki/geocodekml.xq?location=",$location),false())
let $url := concat("http://maps.google.co.uk/maps?q=",$wikiurl)
return 
   response:redirect-to(xs:anyURI($url))

Map of Utopia Locations

This mimic of the GoogleMap is useful to check that the scripts are working, but more usefully, the geocoding service could be used within an application.

Simple Location Service[edit]

In the UK, the Google service geocodes full postcodes. In the case where only the latitude and longitude of the place is required, the following script may be sufficient:

declare namespace kml = "http://earth.google.com/kml/2.0";
declare option exist:serialize "method=xml media-type=text/xml omit-xml-declaration=no";
let $key := "ABQIAAAAVehr0_0wqgw_UOdLv0TYtxSGVrvsBPWDlNZ2fWdNTHNT32FpbBR1ygnaHxJdv-8mkOaL2BJb4V_yOQ"
let $location := request:get-parameter("location",())
let $location := escape-uri($location,false())
let $url := concat("http://maps.google.com/maps/geo?q=",$location,"&amp;output=xml&amp;key=",$key)
let $response := doc($url)
let  $place := $response//kml:Placemark[1]
let $point := $place/kml:Point/kml:coordinates
let $coords := tokenize($point,",")
return 
 
<location>
     <lat>{$coords[2]}</lat>
     <long>{$coords[1]}</long>
</location>

This is now a service which can be used as a REST service.

The Postcode for UWE, Bristol

Here's a Yahoo Pipe to take some data on Scotch Whiskies:

<?xml version="1.0" encoding="UTF-8"?>
<WhiskyList>
 <Whisky>
  <Brand>Glen Ord</Brand>
  <Address>Glen Ord Distillery, Muir of Ord, Ross-shire</Address>
  <Postcode>IV67UJ</Postcode>
 </Whisky>
 <Whisky>
  <Brand>Dalwhinnie</Brand>
  <Address>Dalwhinnie Distillery, Dalwhinnie, Inverness-shire</Address>
  <Postcode>PH191AB</Postcode>
 </Whisky> 
 <Whisky>
  <Brand>Laphroaig</Brand>
  <Address>Laphroaig Distillery, Port Ellen, Isle of Islay</Address>
  <Postcode>PA427DU</Postcode>
 </Whisky>
</WhiskyList>

and generate a geo-coded RSS feed:

Whisky Map

RSS feed[edit]

Of course this feed could be generated in XQuery alone:

declare namespace geo = "http://www.w3.org/2003/01/geo/wgs84_pos#";
declare namespace kml = "http://earth.google.com/kml/2.0";
declare option exist:serialize "method=xml omit-xml-declaration=no indent=yes encoding=iso-8859-1 media-type=application/rss+xml";
declare variable  $key := "ABQIAAAAVehr0_0wqgw_UOdLv0TYtxSGVrvsBPWDlNZ2fWdNTHNT32FpbBR1ygnaHxJdv-8mkOaL2BJb4V_yOQ";
 
declare function local:geocode-location($location as xs:string) {
let $url := concat("http://maps.google.com/maps/geo?q=",$location,"&amp;output=xml&amp;key=",$key)
let $response := doc($url)
let  $place := $response//kml:Placemark[1]
let $point := $place/kml:Point/kml:coordinates
let $coords := tokenize($point,",")
return 
  (
      <geo:lat>{$coords[2]}</geo:lat>,
      <geo:long>{$coords[1]}</geo:long>
   )
};
 
<rss version='2.0' 
   xmlns:geo = "http://www.w3.org/2003/01/geo/wgs84_pos#">
  <channel>
    <title>Whiskies of Scotland</title>
    {
    for $whisky in //Whisky
    let $postcode := $whisky/Postcode
    let $location := local:geocode-location($postcode)
       return 
      <item>
         <title>{string($whisky/Brand)}</title>
         <description>{string($whisky/Address)}</description>
           {$location}
       </item>
    }
  </channel>
</rss>

RSS feed