<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>Derick Rethans - tag: openstreetmap</title>
    <link>http://derickrethans.nl/feed-openstreetmap.xml</link>
    <description>This feed shows the latest 15 items with the tag openstreetmap</description>
    <language>en-us</language>
    <copyright>All rights reserved - Derick Rethans</copyright>
    <managingEditor>derick@derickrethans.nl (Derick Rethans)</managingEditor>
    <pubDate>Thu, 05 Jan 2012 08:03:07 +0000</pubDate>
    <lastBuildDate>Thu, 05 Jan 2012 08:03:07 +0000</lastBuildDate>
    <generator>eZ Components Feed dev (http://ezcomponents.org/docs/tutorials/Feed)</generator>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <ttl>60</ttl>
    <item>
      <title>OpenStreetMap: A Year of Edits</title>
      <link>http://derickrethans.nl/year-of-edits.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="openstreetmap_a_year_of_edits"/&gt;OpenStreetMap: A Year of Edits&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Sunday, January 1st 2012, 00:00 GMT&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;In the past weeks I've been working on some visualisations related to additions and changes being made to OpenStreetMap. To start of the new year, I hereby present: &lt;strong&gt;OpenStreetMap: A Year of Edits (2011)&lt;/strong&gt;:&lt;/p&gt;
      &lt;div class="video"&gt;
        &lt;iframe src="http://player.vimeo.com/video/34404102?title=0&amp;byline=0&amp;portrait=0" width=" 599" height=" 337" frameborder="0"/&gt;
        &lt;p&gt;&lt;a href="http://vimeo.com/34404102"/&gt;OpenStreetMap: A Year of Edits (2011)&lt;/p&gt;
      &lt;/div&gt;
      &lt;p&gt;You can see the video in HD on &lt;a href="http://vimeo.com/derickr/osm-2011"&gt;Vimeo&lt;/a&gt;. &lt;strong&gt;Happy New Year!&lt;/strong&gt;&lt;/p&gt;
      &lt;p&gt;
        &lt;em&gt;How Did He Do That?&lt;/em&gt;
      &lt;/p&gt;
      &lt;p&gt;This animation is all made with Open Source software. I haven't had the time to clean up the code to make it releasable yet, but I am intending to do that in the near feature and add it to my &lt;a href="https://github.com/derickr/osm-tools"&gt;OpenStreetMap tools&lt;/a&gt; git repository on github.&lt;/p&gt;
      &lt;p&gt;The first thing I did, was download all the hourly replication files from &lt;a href="http://planet.openstreetmap.org/hour-replicate"&gt;http://planet.openstreetmap.org/hour-replicate&lt;/a&gt; for the year.&lt;/p&gt;
      &lt;p&gt;With a script, I scanned over all those XML files and for every three hours I created an image showing the edits of those last three hours (in white), as well as all the other edits in the background (in purple). With 4 frames per day, this gives me 1460 frames.&lt;/p&gt;
      &lt;p&gt;Each of the frames is an equirectangular projection which I then map onto a sphere with a faded background of "earthmap10k" of &lt;a href="http://planetpixelemporium.com/earth.html"&gt;http://planetpixelemporium.com/earth.html&lt;/a&gt; (I downloaded it &lt;em&gt;years&lt;/em&gt; ago when it was still free to download). For the 3D rendering of the Earth with the edits layed on top of it I used &lt;a href="http://www.povray.org/"&gt;POV-Ray&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;I generated two different loops so that I can make two full rotations at different angles. This gives me in total 2882 frames which is about two minutes. The POV-Ray generated images I post-processed by overlaying the progress bar in the lower left and added the fading and merging effects. Then I used &lt;a href="http://www.mplayerhq.hu/"&gt;mencoder&lt;/a&gt; to create a MPEG video out of it.  Finally I added the &lt;a href="http://www.archive.org/details/TBECD005-MP3-192k-VBR"&gt;sound track&lt;/a&gt; again by using mencoder and uploaded it to &lt;a href="http://vimeo.com/derickr/osm-2011"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
      &lt;div class="flattr"&gt;
        &lt;a class="FlattrButton" rev="flattr;button:compact;" style="display: none" href="http://derickrethans.nl"/&gt;
        &lt;noscript&gt;
          &lt;a href="http://flattr.com/thing/429095/Derick-Rethans-website"&gt;
            &lt;img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this"/&gt;
          &lt;/a&gt;
        &lt;/noscript&gt;
      &lt;/div&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201201010000</guid>
      <pubDate>Sun, 01 Jan 2012 00:00:00 +0000</pubDate>
    </item>
    <item>
      <title>South Kensington Mapping Party Animation</title>
      <link>http://derickrethans.nl/mapping-party-animation.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="south_kensington_mapping_party_animation"/&gt;South Kensington Mapping Party Animation&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Tuesday, July 19th 2011, 09:17 BST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;Every two weeks or-so, the &lt;a href="http://wiki.openstreetmap.org/wiki/London/Summer_2011_mapping_parties"&gt;London OpenStreetMap group&lt;/a&gt; comes together for a &lt;a href="http://wiki.openstreetmap.org/wiki/Mapping_party"&gt;mapping party&lt;/a&gt;. The main idea is to go to an area of London to improve the map there. Sometimes that's adding streets, but as most of those are now completed, time is spend on buildings, address information and points-of-interest.&lt;/p&gt;
      &lt;p&gt;As an experiment I recorded a snapshot of the map for the area (&lt;a href="http://mapcraft.nanodesu.ru/pie/73"&gt;http://mapcraft.nanodesu.ru/pie/73&lt;/a&gt;) every hour to follow the progress of people adding new and improved data to the map. I then rendered an image of each map snapshot and put those together to create a video:&lt;/p&gt;
      &lt;div class="video"&gt;
        &lt;iframe src="http://player.vimeo.com/video/26470999?title=0&amp;byline=0&amp;portrait=0" width=" 599" height=" 337" frameborder="0"/&gt;
        &lt;p&gt;&lt;a href="http://vimeo.com/26470999"/&gt;South Kensington Mapping Party animation — Map data © OpenStreetMap contributors. CC-BY-SA&lt;/p&gt;
      &lt;/div&gt;
      &lt;p&gt;You can see the video in HD on &lt;a href="http://vimeo.com/derickr/osm-south-ken"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;How Did He Do That?&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;Well, first of all, I already have a script to keep a local version of the London XML map data (as &lt;code&gt;london.osm&lt;/code&gt;) up to date. Each hour, this script:&lt;/p&gt;
      &lt;ul&gt;
        &lt;li&gt;
          &lt;p&gt;Read the old &lt;code&gt;london.osm&lt;/code&gt; file and merge in changes through the OpenStreetMap &lt;a href="http://wiki.openstreetmap.org/wiki/Minutely_Mapnik"&gt;replication&lt;/a&gt; service and write it as &lt;code&gt;london-new.osm&lt;/code&gt;.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;Compare the old file and the new file and write out a changes file to &lt;code&gt;changes/diff-&lt;timestamp&gt;.osc&lt;/code&gt;. This is not needed for this to work, but I am interested in this information as well.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;Move the new file to the old file.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;Import it into my local PostGreSQL/PostGIS instance.&lt;/p&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;In script form, that looks like:&lt;/p&gt;
      &lt;pre&gt;#!/bin/bash

NOW=`date +%Y%m%d-%H%M`

export JAVACMD_OPTIONS="$JAVACMD_OPTIONS -Djava.net.preferIPv4Stack=true"
../osmosis-0.39/bin/osmosis --rri --simc --rx london.osm --ac --bb left=-0.563 right=0.28 top=51.68 bottom=51.26 --wx london-new.osm
../osmosis-0.39/bin/osmosis --rx london-new.osm --rx london.osm --derive-change --write-xml-change file="changes/diff-$NOW.osc"
mv london-new.osm london.osm
../try4/bin/osm2pgsql/osm2pgsql -S ../try4/bin/osm2pgsql/default.style --slim -d gis -C 2400 london.osm #-e 15 -o expiry.log

&lt;/pre&gt;
      &lt;p&gt;And from that I then can render maps of London quite easily:&lt;/p&gt;
      &lt;pre&gt;cd /backup/osm/try4/bin/mapnik
./generate_tiles_london.py | grep -v " exists"

&lt;/pre&gt;
      &lt;p&gt;In order to make a snapshot of South Kensington, or at least, the area that we were going to map in, I added another task. I placed it just after the moving of the old file to the new file. This task cuts out the South Kensington area and puts it in a file &lt;code&gt;south-ken/south-kensington-&lt;timestamp&gt;.osm&lt;/code&gt;:&lt;/p&gt;
      &lt;pre&gt;../osmosis-0.39/bin/osmosis --rx london.osm --bb left=-0.18256 right=-0.16853 top=51.49426 bottom=51.48733 --wx south-ken/south-kensington-$NOW.osm

&lt;/pre&gt;
      &lt;p&gt;This generated about 100 files in the &lt;code&gt;south-ken&lt;/code&gt; directory, where each XML file contained a snapshot of the map on each specific hour.&lt;/p&gt;
      &lt;p&gt;I then ran the following script to create a image of each of those map files:&lt;/p&gt;
      &lt;pre&gt;#!/bin/bash

export MAPNIK_MAP_FILE=/backup/osm/try4/bin/mapnik/osm.xml
mkdir -p images
LAT1=-0.18256
LON1=51.48833
LAT2=-0.16853
LON2=51.49326

for i in south*osm; do
  /backup/osm/try4/bin/osm2pgsql/osm2pgsql -S /backup/osm/try4/bin/osm2pgsql/default.style --slim -d gis -C 2400 $i
  /backup/osm/try4/bin/mapnik/generate_image.py ${LAT1} ${LON1} ${LAT2} ${LON2}

  mv image.png images/${i}-0.png
  LAT1=`echo "${LAT1} + 0.000005" | bc -q`
  LON1=`echo "${LON1} + 0.00001" | bc -q`
  LAT2=`echo "${LAT2} - 0.00001" | bc -q`
  LON2=`echo "${LON2} - 0" | bc -q`

  /backup/osm/try4/bin/mapnik/generate_image.py ${LAT1} ${LON1} ${LAT2} ${LON2}
  mv image.png images/${i}-1.png
  LAT1=`echo "${LAT1} + 0.000005" | bc -q`
  LON1=`echo "${LON1} + 0.00001" | bc -q`
  LAT2=`echo "${LAT2} - 0.00001" | bc -q`
  LON2=`echo "${LON2} - 0" | bc -q`
done

&lt;/pre&gt;
      &lt;p&gt;I had to modify the &lt;code&gt;generate_image.py&lt;/code&gt; script from the &lt;a href="http://mapnik.org/"&gt;Mapnik&lt;/a&gt; distribution slightly to allow the bounding box to be passed in through the command line. I also set the image width and height to 1280 by 720:&lt;/p&gt;
      &lt;pre&gt;...
ll = [];
for arg in sys.argv:
    ll.append(arg)
ll.pop(0);
for index, arg in enumerate(ll):
    ll[index] = float(arg)

z = 15
imgx = 1280
imgy = 720

m = mapnik.Map(imgx,imgy)
...

&lt;/pre&gt;
      &lt;p&gt;Once all the PNG files were created, I converted them to the JPEG format and ran &lt;code&gt;mencoder&lt;/code&gt; to generate the AVI film:&lt;/p&gt;
      &lt;pre&gt;for i in *png; do convert -quality 100 $i $i.jpg; done
mencoder "mf://*.jpg" -mf fps=10 -o test.avi -ovc lavc -lavcopts vcodec=msmpeg4v2:vbitrate=7200

&lt;/pre&gt;
      &lt;p&gt;And this video I then uploaded to &lt;a href="http://vimeo.com/derickr/osm-south-ken"&gt;Vimeo&lt;/a&gt;. Voilà! One OpenStreetMap mapping party progress animation!&lt;/p&gt;
      &lt;div class="flattr"&gt;
        &lt;a class="FlattrButton" rev="flattr;button:compact;" style="display: none" href="http://derickrethans.nl"/&gt;
        &lt;noscript&gt;
          &lt;a href="http://flattr.com/thing/429095/Derick-Rethans-website"&gt;
            &lt;img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this"/&gt;
          &lt;/a&gt;
        &lt;/noscript&gt;
      &lt;/div&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201107190917</guid>
      <pubDate>Tue, 19 Jul 2011 08:17:00 +0000</pubDate>
    </item>
    <item>
      <title>OpenStreetMap Quality Assurance with a Garmin GPS</title>
      <link>http://derickrethans.nl/osm-garmin-tools.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="openstreetmap_quality_assurance_with_a_garmin_gps"/&gt;OpenStreetMap Quality Assurance with a Garmin GPS&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Tuesday, July 5th 2011, 10:43 BST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;I've recently bought myself a new &lt;a href="http://wiki.openstreetmap.org/wiki/Garmin/eTrex_Vista_HCx"&gt;Garmin eTrex Vista HCx&lt;/a&gt; GPS to replace my older Garmin eTrex Legend unit. Because I don't want to shell out money for outdated maps, I have spend some time building my own map images for the Garmin with &lt;a href="http://wiki.openstreetmap.org/wiki/Mkgmap"&gt;mkgmap&lt;/a&gt; and &lt;a href="http://wiki.openstreetmap.org/"&gt;OpenStreetMap&lt;/a&gt; data. I will write more on that later.&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/garmin-osm.jpg" class="left" alt="garmin-osm.jpg"/&gt;
      &lt;p&gt;Because most of the data in OpenStreetMap has been surveyed by volunteers, it is always possible that things are missing or done incorrectly. There are quite a few different tools on-line for quality assurance of the OpenStreetMap data, but I will be looking at two of them only.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;OSM Analysis&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;First of all there is &lt;a href="http://www.itoworld.com/"&gt;ITO World&lt;/a&gt;'s &lt;a href="http://www.itoworld.com/static/osm_analysis.html"&gt;OSM Analysis&lt;/a&gt;. This tool compares the OpenStreetMap data in the UK to the Ordnance Survey's &lt;a href="http://www.ordnancesurvey.co.uk/oswebsite/products/os-locator/"&gt;Locator&lt;/a&gt; data. The latter is supposed to be the authoritative source of road names in the UK. For each of the 417 areas in the UK, the tool produces two lists; for example the one for Brent is here: &lt;a href="http://www.itoworld.com/product/data/osm_analysis/area?name=Brent"&gt;http://www.itoworld.com/product/data/osm_analysis/area?name=Brent&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;The first column lists all the corrections that Ordnance Survey ought to make in an updated version because one of OpenStreetMap's surveyors found it incorrect. The second column lists all the streets that are missing from OpenStreetMap.&lt;/p&gt;
      &lt;p&gt;Because it would be really useful to have this sort of data available on a hand-held GPS to be able to notice any of the highlighted missing roads on the go, I set out to convert this data to a map file suitable for the Garmin devices. I've published the scripts on &lt;a href="https://github.com/derickr/osm-tools/tree/master/osm-analyis-to-garmin"&gt;github&lt;/a&gt; so that you can try it for yourself, but I've also put a ready made &lt;code&gt;osmanal.img&lt;/code&gt; file on-line for download at &lt;a href="http://derick.dev.openstreetmap.org/"&gt;http://derick.dev.openstreetmap.org/&lt;/a&gt;. In the near future I will use cron to run this conversion about once a week automatically.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Musical Chairs&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;Another tool that compares OpenStreetMap data with Ordnance Survey's Locator data is &lt;a href="http://ris.dev.openstreetmap.org/oslmusicalchairs/map"&gt;OS Locator Musical Chairs&lt;/a&gt;. Documentation on this project is available on the OpenStreetMap &lt;a href="http://wiki.openstreetmap.org/wiki/OS_Locator_Musical_Chairs/FAQ."&gt;wiki&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;The author of the Musical Chairs tool has provided a &lt;a href="http://wiki.openstreetmap.org/wiki/OS_Locator_Musical_Chairs/FAQ#Can_I_have_a_dump_of_the_data.3F"&gt;data dump&lt;/a&gt; highlighting all the disagreements it finds (including missing roads) while comparing OpenStreetMap data against Ordnance Survey's Locator data.  I've written a tool that converts this data to another Garmin map image files with the scripts that I have published at &lt;a href="https://github.com/derickr/osm-tools/tree/master/musical-chairs-to-garmin"&gt;github&lt;/a&gt;. With those scripts you can make your own image files, but like with the OSM Analysis I have made a ready-made file &lt;code&gt;muschair.img&lt;/code&gt; available at &lt;a href="http://derick.dev.openstreetmap.org/"&gt;http://derick.dev.openstreetmap.org/&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Using the map image files&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;Before you can use the two Garmin map image files, you either need to convert it to a stand-alone &lt;code&gt;gmapsupp.img&lt;/code&gt; file by running:&lt;/p&gt;
      &lt;pre&gt;java -jar /backup/osm/try4/bin/mkgmap-r1946/mkgmap.jar --gmapsupp osmanal.img

&lt;/pre&gt;
      &lt;p&gt;or:&lt;/p&gt;
      &lt;pre&gt;java -jar /backup/osm/try4/bin/mkgmap-r1946/mkgmap.jar --gmapsupp muschairs.img

&lt;/pre&gt;
      &lt;p&gt;These commands produce a &lt;code&gt;gmapsupp.img&lt;/code&gt; file that you can upload to your Garmin device. If you do this, you will &lt;strong&gt;not&lt;/strong&gt; have any road data.&lt;/p&gt;
      &lt;p&gt;You can also merge this file with all your other maps to keep the road data, by running something like:&lt;/p&gt;
      &lt;pre&gt;java -Xmx2048M -jar /backup/osm/try4/bin/mkgmap-r1946/mkgmap.jar \
    --gmapsupp /tmp/music.img /tmp/osmanal.img \
    UK/gmapsupp.img \
    contours/gmapsupp.img

&lt;/pre&gt;
      &lt;p&gt;Which merges the Musical Chairs image file (&lt;code&gt;music.img&lt;/code&gt;), the OSM Analysis image file (&lt;code&gt;osmanal.img&lt;/code&gt;), the UK maps (&lt;code&gt;UK/gmapsupp.img&lt;/code&gt;) as well as my UK contours line file (&lt;code&gt;contours/gmapsupp.img&lt;/code&gt;). This again produces a &lt;code&gt;gmapsupp.img&lt;/code&gt; file that you can &lt;a href="http://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin#Installing_the_map_onto_your_GPS"&gt;upload&lt;/a&gt; to your Garmin GPS.&lt;/p&gt;
      &lt;p&gt;For further &lt;code&gt;mkgmap&lt;/code&gt; usage details I would like you to point to it's page on the OpenStreetMap wiki: &lt;a href="http://wiki.openstreetmap.org/wiki/Mkgmap"&gt;http://wiki.openstreetmap.org/wiki/Mkgmap&lt;/a&gt; or more generally to &lt;a href="http://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin"&gt;http://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin&lt;/a&gt;. I am also intending to publish my UK and contour map creation scripts after I've streamlined this process.&lt;/p&gt;
      &lt;p&gt;And now the maps have been on my GPS for a few days, I've identified and &lt;a href="http://www.openstreetmap.org/browse/changeset/8628284"&gt;fixed&lt;/a&gt; a "&lt;a href="http://ris.dev.openstreetmap.org/oslmusicalchairs/map?osl_id=890373"&gt;disagreement&lt;/a&gt;" from Musical Chairs and a few &lt;a href="http://www.openstreetmap.org/browse/changeset/8580300"&gt;others&lt;/a&gt; from the ITO World OSM analysis tool.&lt;/p&gt;
      &lt;div class="flattr"&gt;
        &lt;a class="FlattrButton" rev="flattr;button:compact;" style="display: none" href="http://derickrethans.nl"/&gt;
        &lt;noscript&gt;
          &lt;a href="http://flattr.com/thing/429095/Derick-Rethans-website"&gt;
            &lt;img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this"/&gt;
          &lt;/a&gt;
        &lt;/noscript&gt;
      &lt;/div&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201107051043</guid>
      <pubDate>Tue, 05 Jul 2011 09:43:00 +0000</pubDate>
    </item>
    <item>
      <title>Spatial Indexes: Solr</title>
      <link>http://derickrethans.nl/spatial-indexes-solr.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="spatial_indexes_solr"/&gt;Spatial Indexes: Solr&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Tuesday, June 14th 2011, 09:04 BST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;In two previous articles I introduced &lt;a href="http://drck.me/spat-dist-8kf"&gt;the spherical Earth model&lt;/a&gt;, using &lt;a href="http://drck.me/spat-osm-sqlite-8la"&gt;SQLite&lt;/a&gt; as a geographical data storage and using &lt;a href="http://drck.me/spat-mysql-8ls"&gt;MySQL&lt;/a&gt; as a geographical data storage.  In this article we're going to have a look at importing the data into something else than a relational database: the search platform &lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt;. (Yes, I know I've skipped PostgreSQL, but I'll come back to that).&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Solr&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;&lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt; is "the popular, blazing fast open source enterprise search platform from the Apache Lucene project." Since version 3.1, Solr has support for &lt;a href="http://wiki.apache.org/solr/SpatialSearch"&gt;spatial search&lt;/a&gt;, including geospatial search. It can store coordinates in two different field types: &lt;code&gt;solr.PointType&lt;/code&gt; for n-dimensional points, and &lt;code&gt;solr.LatLonType&lt;/code&gt; for a two-dimensional point for geospatial search. The main difference is that with &lt;code&gt;solr.PointType&lt;/code&gt;, calculations are done according to the flat Earth model, and &lt;code&gt;solr.LatLonType&lt;/code&gt; does calculations for a spherical Earth model—which is just what we need.&lt;/p&gt;
      &lt;p&gt;In this example we will use the default Solr configuration, unless noted otherwise. First we download Solr 3.1 and then untar it with &lt;code&gt;tar -xvzf
apache-solr-3.1.0.tgz&lt;/code&gt;.  Then we edit &lt;code&gt;example/solr/conf/solrconfig.xml&lt;/code&gt; to comment out the section on &lt;code&gt;Query Elevation Component&lt;/code&gt; so that it looks like:&lt;/p&gt;
      &lt;pre&gt;&lt;!--
&lt;searchComponent name="elevator" class="solr.QueryElevationComponent" &gt;
  pick a fieldType to analyze queries
        &lt;str name="queryFieldType"&gt;string&lt;/str&gt;
        &lt;str name="config-file"&gt;elevate.xml&lt;/str&gt;
  &lt;/searchComponent&gt;
--&gt;

&lt;/pre&gt;
      &lt;p&gt;See &lt;a href="http://stackoverflow.com/questions/3631823/solr-queryelevationcomponent-requires-strfield-uniquekeyfield-error"&gt;here&lt;/a&gt; for the reason.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Setting Up Solr&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;Before we can start using Solr, we need to define a schema. This schema is quite analogous to a database schema, except that Solr only has one table.  By default Solr comes with a schema defining lots of fields in &lt;code&gt;example/solr/conf/schema.xml&lt;/code&gt;.  I've changed the whole &lt;code&gt;&lt;fields&gt;&lt;/code&gt; section to look like:&lt;/p&gt;
      &lt;pre&gt;&lt;fields&gt;
        &lt;field name="id" type="string" indexed="true" stored="true" /&gt;
        &lt;field name="type" type="int" indexed="true" stored="true" /&gt;
        &lt;field name="name" type="text" indexed="true" stored="true" /&gt;
        &lt;field name="amenity" type="string" indexed="true" stored="true" /&gt;
        &lt;field name="address" type="text" indexed="true" stored="true" /&gt;
        &lt;field name="postcode" type="text" indexed="true" stored="true" /&gt;
        &lt;field name="phone" type="text" indexed="true" stored="true" /&gt;
        &lt;field name="cuisine" type="string" indexed="true" stored="true" /&gt;
        &lt;field name="location" type="location" indexed="true" stored="true" /&gt;
        &lt;field name="location_0_coordinate" type="double" indexed="true" stored="true" /&gt;
        &lt;field name="location_1_coordinate" type="double" indexed="true" stored="true" /&gt;
&lt;/fields&gt;

&lt;/pre&gt;
      &lt;p&gt;I've defined specific fields for the &lt;code&gt;type&lt;/code&gt; of source (1=node, 2=way/area), the &lt;code&gt;name&lt;/code&gt; of the amenity (name of the pub, hospital, etc), what sort of &lt;code&gt;amenity&lt;/code&gt; it is (cafe, bank, etc), the &lt;code&gt;address&lt;/code&gt; and &lt;code&gt;postcode&lt;/code&gt;, the &lt;code&gt;phone&lt;/code&gt; number, the type of &lt;code&gt;cuisine&lt;/code&gt; as well as the &lt;code&gt;location&lt;/code&gt;. For the location I've also defined two subtypes: &lt;code&gt;location_0_coordinate&lt;/code&gt; and &lt;code&gt;location_1_coordinate&lt;/code&gt;. Solr requires this for multi-dimensional types such as &lt;code&gt;solr.LatLonType&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;The types for each field are also defined in &lt;code&gt;schema.xml&lt;/code&gt;. &lt;code&gt;text&lt;/code&gt; is for a string that is analysed and tokenized (broken down in smaller parts) so that you can search in only parts of the text, &lt;code&gt;string&lt;/code&gt; is for a string of characters that is not analysed or tokenized, and &lt;code&gt;location&lt;/code&gt; contains our latitude/longitude point. Each of those types are associated with a specific Java class with an entry such as:&lt;/p&gt;
      &lt;pre&gt;&lt;fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/&gt;

&lt;/pre&gt;
      &lt;p&gt;The &lt;code&gt;subFieldSuffix&lt;/code&gt; in this line configures the suffix in the fields &lt;code&gt;location_0_coordinate&lt;/code&gt; and &lt;code&gt;location_1_coordinate&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;Now we have changed the default schema, we can start Solr with: &lt;code&gt;cd example &amp;&amp;
java -jar start.jar&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;On the PHP side, we need to do a bit of set-up as well. I've been using the &lt;a href="http://pecl.php.net/package/solr"&gt;Solr PECL&lt;/a&gt; extension to talk to Solr which can be installed with &lt;code&gt;pecl
install solr&lt;/code&gt;.  Please do not forget to add the extension to php.ini with &lt;code&gt;extension=solr.so&lt;/code&gt;. Check whether it is installed correctly with: &lt;code&gt;php --ri
solr&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Importing&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;For our import, we need to change the script we used previously quite substantially.  Instead of just using London, I've also downloaded the whole of the UK from &lt;a href="http://geofabrik.de"&gt;Geofabrik&lt;/a&gt; and extracted all amenities from the dataset with &lt;a href="http://wiki.openstreetmap.org/wiki/Osmosis"&gt;Osmosis&lt;/a&gt;:&lt;/p&gt;
      &lt;pre&gt;wget http://download.geofabrik.de/osm/europe/great_britain.osm.bz2

./osmosis-0.39/bin/osmosis -v 5 \
--read-xml file=great_britain.osm.bz2 \
--tf reject-relations \
--tf accept-nodes amenity=* \
--tf reject-ways \
outPipe.0=POI \
\
--read-xml file=great_britain.osm.bz2 \
--tf reject-relations \
--tf accept-ways amenity=* \
--used-node outPipe.0=area \
\
--merge inPipe.0=POI inPipe.1=area \
--write-xml file=great_britain_amenity.osm

&lt;/pre&gt;
      &lt;p&gt;This is going to take quite some time, and will result in a 500MB XML file.&lt;/p&gt;
      &lt;p&gt;The main logic of the importing script remains the same, but instead of adding each item to a database, we now build a Solr import document and add it to the search index.  I've also split the address and postcode into two separate fields, and added the &lt;code&gt;amenity&lt;/code&gt; field to store all the different amenities. For now, I'm filtering out post boxes, parking areas and grave yards. The script, &lt;code&gt;parsepoi-solr.php&lt;/code&gt; is available &lt;a href="http://derickrethans.nl/files/parsepoi-solr.php.txt"&gt;here&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;After downloading, and renaming the downloaded file to &lt;code&gt;parsepoi-solr.php&lt;/code&gt; we can run the script with:&lt;/p&gt;
      &lt;pre&gt;php -dmemory_limit=1G parsepoi-solr.php great_britain_amenity.osm

&lt;/pre&gt;
      &lt;p&gt;When done, this should have imported more than 140.000 items into Solr. You can verify this, by going to &lt;a href="http://localhost:8983/solr/select/?q=*:*&amp;rows=0"&gt;http://localhost:8983/solr/select/?q=*:*&amp;rows=0&lt;/a&gt;&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Querying&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;As you can see, you can query Solr through its HTTP interface quite easily. In fact, all queries to Solr are done over HTTP. The Solr extension however abstracts this away from you for at least the import. I couldn't find any functionality in the extension to do spatial queries, so we'll do it manually.&lt;/p&gt;
      &lt;p&gt;If we look at the URL above, we can divide it into different parts:&lt;/p&gt;
      &lt;ul&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;http://localhost:8983/solr/&lt;/code&gt;: The base URL for this Solr instance.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;select/?&lt;/code&gt;: The action for running search queries.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;q=*:*&lt;/code&gt;: &lt;code&gt;q&lt;/code&gt; is the search query parameter, and its value, &lt;code&gt;*:*&lt;/code&gt; means all fields (the first &lt;code&gt;*&lt;/code&gt;) and all possible values (the second &lt;code&gt;*&lt;/code&gt;). In this case that means return everything.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;rows=0&lt;/code&gt;: Do not return any rows, so that we just get a count.&lt;/p&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;We get back the result as an XML file. If we want JSON instead, we can simply append &lt;code&gt;&amp;wt=json&lt;/code&gt;. In case you want a CSV file, append &lt;code&gt;&amp;wt=csv&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;In a first example, all we want to do is to return all cafes within 100 meter. We construct the search query as follows:&lt;/p&gt;
      &lt;ul&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;http://localhost:8983/solr/select/?&lt;/code&gt;: The base URL with search action.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;q=amenity:cafe&lt;/code&gt;: We search in the field &lt;code&gt;amenity&lt;/code&gt; and look for the value &lt;code&gt;cafe&lt;/code&gt;.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;fq={!geofilt}&lt;/code&gt;: We set a query filter to &lt;code&gt;geofilt_&lt;/code&gt;. This filter restricts the result set according to the location (&lt;code&gt;pt&lt;/code&gt;) and the maximum distance from this location (&lt;code&gt;d&lt;/code&gt;) in km.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;sfield=location&lt;/code&gt;: The field to use for location information.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;pt=51.5375,-0.1934&lt;/code&gt;: The point that we center our search around.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;d=0.1&lt;/code&gt;: The maximum distance in kilometers.&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;&lt;code&gt;wt=csv&lt;/code&gt;: The format to return, in our case, CSV.&lt;/p&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;Together this makes the full GET request: &lt;a href="http://localhost:8983/solr/select/?q=amenity:cafe&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.1&amp;wt=csv"&gt;http://localhost:8983/solr/select/?q=amenity:cafe&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.1&amp;wt=csv&lt;/a&gt;&lt;/p&gt;
      &lt;p&gt;And the result is:&lt;/p&gt;
      &lt;pre&gt;id,phone,cuisine,location,address,location_1_coordinate,name,amenity,type,location_0_coordinate,postcode
w62088838,,,51.53750834,-0.19329616,75 Kilburn High Road,-0.19329616,Costa,cafe,2,51.53750834,
w78337118,,,51.53828298,-0.19410346,101 Kilburn High Road,-0.19410346,Caffè Nero,cafe,2,51.53828298,NW6 6JE
w105467205,,,51.537555925,-0.192445525,274 Belsize Road,-0.192445525,Belsize Cafe,cafe,2,51.537555925,NW6 4BT
w105467209,+44 207 3724002,,51.537624071429,-0.19228221428571,270 Belsize Road,-0.19228221428571,Lord Jim,cafe,2,51.537624071429,NW6 4BT
w107710475,+44 20 76245736,,51.53695276,-0.19263662,2 Kilburn Bridge,-0.19263662,Famished Cafe,cafe,2,51.53695276,NW6 6HT
w107710482,,,51.53717868,-0.19288912,Kilburn Bridge,-0.19288912,Mike's,cafe,2,51.53717868,NW6 6HT
w107710490,+44 20 76246942,,51.53732946,-0.1930577,12 Kilburn Bridge,-0.1930577,La Dolce Vita,cafe,2,51.53732946,NW6 6HT

&lt;/pre&gt;
      &lt;p&gt;Which fields are returned can be configured. In this case, we are only interested in the location and name of each amenity, so we restrict the number of returned fields with: &lt;code&gt;fl=id,location,name&lt;/code&gt;. The result now becomes:&lt;/p&gt;
      &lt;pre&gt;id,location,name
w62088838,51.53750834,-0.19329616,Costa
w78337118,51.53828298,-0.19410346,Caffè Nero
w105467205,51.537555925,-0.192445525,Belsize Cafe
w105467209,51.537624071429,-0.19228221428571,Lord Jim
w107710475,51.53695276,-0.19263662,Famished Cafe
w107710482,51.53717868,-0.19288912,Mike's
w107710490,51.53732946,-0.1930577,La Dolce Vita

&lt;/pre&gt;
      &lt;p&gt;Sadly, the results do not come back ordered by distance from our starting point. In order to do that, we need to add one more query parameter: &lt;code&gt;sort=geodist() asc&lt;/code&gt;.  The full query is now: &lt;a href="http://localhost:8983/solr/select/?q=amenity:cafe&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.10&amp;wt=csv&amp;fl=id,location,name&amp;sort=geodist()+asc"&gt;http://localhost:8983/solr/select/?q=amenity:cafe&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.10&amp;wt=csv&amp;fl=id,location,name&amp;sort=geodist()+asc&lt;/a&gt;&lt;/p&gt;
      &lt;p&gt;And the result:&lt;/p&gt;
      &lt;pre&gt;id,location,name
w62088838,51.53750834,-0.19329616,Costa
w107710490,51.53732946,-0.1930577,La Dolce Vita
w107710482,51.53717868,-0.19288912,Mike's
w105467205,51.537555925,-0.192445525,Belsize Cafe
w105467209,51.537624071429,-0.19228221428571,Lord Jim
w107710475,51.53695276,-0.19263662,Famished Cafe
w78337118,51.53828298,-0.19410346,Caffè Nero

&lt;/pre&gt;
      &lt;p&gt;It is however not possible to return the distance from our starting point directly with Solr. With the exception being that if you use only the &lt;code&gt;geodist()&lt;/code&gt; function as query argument. In this case, make sure to include the special field &lt;code&gt;score&lt;/code&gt; in the &lt;code&gt;fl=&lt;/code&gt; argument as well. An URL showing this is: &lt;a href="http://localhost:8983/solr/select/?q={!func}geodist()&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.10&amp;wt=csv&amp;fl=id,location,name,score&amp;sort=geodist()+asc"&gt;http://localhost:8983/solr/select/?q={!func}geodist()&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.10&amp;wt=csv&amp;fl=id,location,name,score&amp;sort=geodist()+asc&lt;/a&gt; Of course, in this case you can not filter for specific amenities.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Conclusion&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;In currently released versions of Solr (3.1 at the time of writing), it is not possible to return the distance to the starting point for the search as part of the result set. The Solr developers have already &lt;a href="https://issues.apache.org/jira/browse/SOLR-1298"&gt;implemented&lt;/a&gt; the capabilities to add the results of function queries to result documents for version 4.0. You can verify this by downloading a &lt;a href="https://builds.apache.org//job/Solr-trunk/lastSuccessfulBuild/artifact/artifacts/"&gt;nightly build&lt;/a&gt; and running the query: &lt;a href="http://localhost:8983/solr/select/?q=amenity:cafe&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.10&amp;fl=id,location,name,score,geodist()&amp;sort=geodist()+asc"&gt;http://localhost:8983/solr/select/?q=amenity:cafe&amp;fq={!geofilt}&amp;sfield=location&amp;pt=51.5375,-0.1934&amp;d=0.10&amp;fl=id,location,name,score,geodist()&amp;sort=geodist()+asc&lt;/a&gt; In this query I've added &lt;code&gt;geodist()&lt;/code&gt; to the &lt;code&gt;fl=&lt;/code&gt; parameter. The result now includes an extra field called &lt;code&gt;geodist()&lt;/code&gt;:&lt;/p&gt;
      &lt;pre&gt;&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;response&gt;
  &lt;!-- missing header --&gt;
  &lt;result name="response" numFound="7" start="0" maxScore="4.352648"&gt;
        &lt;doc&gt;
          &lt;str name="id"&gt;w62088838&lt;/str&gt;
          &lt;str name="name"&gt;Costa&lt;/str&gt;
          &lt;str name="location"&gt;51.53750834,-0.19329616&lt;/str&gt;
          &lt;float name="score"&gt;4.352648&lt;/float&gt;
          &lt;double name="geodist()"&gt;0.0072415726934058865&lt;/double&gt;
        &lt;/doc&gt;
        &lt;doc&gt;
          &lt;str name="id"&gt;w107710490&lt;/str&gt;
          &lt;str name="name"&gt;La Dolce Vita&lt;/str&gt;
          &lt;str name="location"&gt;51.53732946,-0.1930577&lt;/str&gt;
          &lt;float name="score"&gt;4.352648&lt;/float&gt;
          &lt;double name="geodist()"&gt;0.030333097323281075&lt;/double&gt;
        &lt;/doc&gt;

&lt;/pre&gt;
      &lt;p&gt;I could not manage to alias it to a different field, or get it to work with the CSV format (&lt;code&gt;wt=csv&lt;/code&gt;).&lt;/p&gt;
      &lt;p&gt;If the return format is &lt;code&gt;xml&lt;/code&gt; (the default) or &lt;code&gt;json&lt;/code&gt;, then the result includes, besides the number of found items, the total search time for this query.  Look for this information in the &lt;code&gt;QTime&lt;/code&gt; field. In all of the examples here, the &lt;code&gt;QTime&lt;/code&gt; has been less than 5, meaning 5 milliseconds. Solr is extremely fast, even with huge amounts of data. I will try to import the amenities of the whole "planet" at some point, and report back with some benchmarking information.&lt;/p&gt;
      &lt;p&gt;In the next installment of this series on storing geospatial data, I will be looking at using &lt;a href="http://www.mongodb.org"&gt;MongoDB&lt;/a&gt; as data store for geographical information.&lt;/p&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201106140904</guid>
      <pubDate>Tue, 14 Jun 2011 08:04:00 +0000</pubDate>
    </item>
    <item>
      <title>What is OpenStreetMap?</title>
      <link>http://derickrethans.nl/what-is-openstreetmap.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="what_is_openstreetmap"/&gt;What is OpenStreetMap?&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Thursday, May 12th 2011, 09:04 BST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;With people more and more complaining that Google Maps &lt;a href="http://www.flickr.com/photos/harrywood/5702396769/"&gt;gets it wrong&lt;/a&gt;, I often reply that the complainers have a look at &lt;a href="http://openstreetmap.org"&gt;OpenStreetMap&lt;/a&gt; instead.  But there are a few misunderstandings on what OpenStreetMap actually is.&lt;/p&gt;
      &lt;img src="http://c.tile.openstreetmap.org/18/130974/87188.png" class="right" alt="87188.png"/&gt;
      &lt;p&gt;OpenStreetMap has its main site at &lt;a href="http://openstreetmap.org"&gt;http://openstreetmap.org&lt;/a&gt;. This shows a rendering of the map that we like to call a "&lt;a href="http://wiki.openstreetmap.org/wiki/Slippy_Map"&gt;slippy map&lt;/a&gt;". This is an online map, that can interactively be zoomed and paned. The slippy map on the site is nothing more than a display of map tiles, static images rendered from OpenStreetMap data as an example of what you can do with it. The real power of OpenStreetMap is not this default rendering, but the possibility to actually access the data behind this map rendering.  And this is where it differs from &lt;a href="http://maps.google.co.uk/"&gt;Google maps&lt;/a&gt;, &lt;a href="http://www.bing.com/maps/"&gt;Bing Maps&lt;/a&gt; and &lt;a href="http://uk.maps.yahoo.com/"&gt;Yahoo! Maps&lt;/a&gt; and various others. OpenStreetMap is the only mapping service that allows you to do something more than just look at pretty map tiles. OpenStreetMap is a database project, with as its main purpose to have an exhaustive database of every street, city, road, building etc on the planet, and not a map display project.&lt;/p&gt;
      &lt;p&gt;Now what does that actually mean? For one thing, it means that the sample map tiles (that are displayed in the slippy map) are not meant to be used heavily from applications.  It's all right to show a map tile of your neighbourhood on your web site, but it's not okay to write a mobile application that allows you to scrape map tiles of large areas. Have a look at the general &lt;a href="http://wiki.openstreetmap.org/wiki/Tile_usage_policy"&gt;tile usage policy&lt;/a&gt; for some background.  The only reason why there is a sample rendering is to aid mappers with improving the data that is the core of OpenStreetMap.&lt;/p&gt;
      &lt;p&gt;Obviously, the OpenStreetMap project wants its data to be used. But if you're not allowed to use the map tiles en-masse, then how can you use the data?&lt;/p&gt;
      &lt;p&gt;First of all, you can query the OpenStreetMap database in various ways. In most cases, you're going to get an XML file with descriptions of nodes (points of interest, facilities such as toilets, benches, addresses), ways (roads, water ways, transport routes) and areas (buildings, lakes). There is some more info on the (very simple schema) &lt;a href="http://wiki.openstreetmap.org/wiki/Xml_schema"&gt;here&lt;/a&gt;. This data you can parse and import (as I've shown in a previous &lt;a href="http://drck.me/spat-osm-sqlite-8la"&gt;article&lt;/a&gt;) and do all kinds of cool tricks with, such as finding out the closest pub. This functionality is not available on the main OpenStreetMap site, because "OpenStreetMap does not aim to create and host every webservice possible, but to provide the data so that others can"  make awesome map-related applications.&lt;/p&gt;
      &lt;p&gt;Secondly there is a growing number of web sites that uses the OpenStreetMap data to render specialized maps.  There is &lt;a href="http://opencyclemap.org/?zoom=13&amp;lat=51.50806&amp;lon=-0.14025&amp;layers=B0"&gt;OpenCycleMap&lt;/a&gt; that renders information related to using your bicycle and &lt;a href="http://www.openbusmap.org/?zoom=13&amp;lat=51.50526&amp;lon=-0.13869&amp;layers=BT"&gt;Öpvnkarte&lt;/a&gt; for public transport. There is even a tool that uses the &lt;a href="http://milliams.dev.openstreetmap.org/postcodefinder/NW6%25206TB/"&gt;postcode data&lt;/a&gt; in OpenStreetMap.  It's quite possible to render your own maps as well, but that I will get back to in a later article.&lt;/p&gt;
      &lt;img src="http://open.mapquestapi.com/staticmap/v3/getmap?size=320,260&amp;zoom=12&amp;center=51.51,-0.15" class="left" alt="getmap?size=320,260&amp;zoom=12&amp;center=51.51,-0.15"/&gt;
      &lt;p&gt;&lt;a href="http://open.mapquest.co.uk/link/2-Yq2eUVEO"&gt;MapQuest&lt;/a&gt; is a good example of a company that uses OpenStreetMap data. They have made their own rendering with its own styles for a visually different map. Their site implements &lt;a href="http://open.mapquest.co.uk/link/6-eU7PhaC7"&gt;searching&lt;/a&gt; for pubs/bars and restaurants for example and MapQuest are also &lt;a href="http://wiki.openstreetmap.org/wiki/MapQuest#Access_.2F_Downloads"&gt;happy with you using their map tiles&lt;/a&gt; for your applications. Recently, they have also announced &lt;a href="http://devblog.mapquest.com/2011/05/11/get-creative-with-the-open-static-maps-api/"&gt;a simple API&lt;/a&gt; to generate a static map based on OpenStreetMap with their rendering style.&lt;/p&gt;
      &lt;p&gt;The map above is generated by just requesting &lt;a href="http://open.mapquestapi.com/staticmap/v3/getmap?size=320%2C260&amp;zoom=12&amp;center=51.51%2C-0.15"&gt;http://open.mapquestapi.com/staticmap/v3/getmap?size=320,260&amp;zoom=12&amp;center=51.51,-0.15&lt;/a&gt;&lt;/p&gt;
      &lt;p&gt;Another major difference with established mapping providers is the ability to edit the map yourself. You can add your own business, houses, addresses, postcodes, your favourite restaurants with either &lt;a href="http://wiki.openstreetmap.org/wiki/Potlatch_2"&gt;on-line&lt;/a&gt; or &lt;a href="http://wiki.openstreetmap.org/wiki/Josm"&gt;off-line&lt;/a&gt; editors. My list of recent edits is &lt;a href="http://tinyurl.com/34l8wqu"&gt;here&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;There is also a whole plethora of special renderings of the map data to assist with debugging issues with the map.  There is the "&lt;a href="http://osm.org/go/cIrZqd@A?layers=N"&gt;no name&lt;/a&gt;" rendering which shows roads without name, &lt;a href="http://www.itoworld.com/static/index.html"&gt;ITO World's&lt;/a&gt; &lt;a href="http://www.itoworld.com/static/osm_analysis.html"&gt;osm analysis&lt;/a&gt; that shows deviations of road names from another source in the UK, &lt;a href="http://keepright.ipax.at/report_map.php?zoom=15&amp;lat=51.49309&amp;lon=-0.13344"&gt;keepright&lt;/a&gt; and &lt;a href="http://tools.geofabrik.de/osmi/?zoom=15&amp;lat=51.49309&amp;lon=-0.13344&amp;view=tagging"&gt;OSM Inspector&lt;/a&gt; that have some automated checks and a tool to &lt;a href="http://oscompare.raggedred.net/?layers=0B00FFT&amp;zoom=17&amp;lat=51.53719&amp;lon=-0.19543"&gt;overlay postcodes&lt;/a&gt; for checking.&lt;/p&gt;
      &lt;p&gt;Obviously, there are a few things missing that would make OpenStreetMap an even better resource for mapping related applications and web-applications. Feel free to suggest things that you find missing or lacking in the comments of this article. I'd be happy to hear why you would think that OpenStreetMap is not a good fit for your applications and usage.&lt;/p&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201105120904</guid>
      <pubDate>Thu, 12 May 2011 08:04:00 +0000</pubDate>
    </item>
    <item>
      <title>Spatial Indexes: MySQL</title>
      <link>http://derickrethans.nl/spatial-indexes-mysql.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="spatial_indexes_mysql"/&gt;Spatial Indexes: MySQL&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Tuesday, April 12th 2011, 09:04 BST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;In two previous articles I introduced &lt;a href="http://drck.me/spat-dist-8kf"&gt;The spherical Earth model&lt;/a&gt; and &lt;a href="http://drck.me/spat-osm-sqlite-8la"&gt;importing data&lt;/a&gt; into SQLite for querying geographical data. In this article we're going to have a look at importing the data into &lt;a href="http://dev.mysql.com/"&gt;MySQL&lt;/a&gt; and finding out how to best store and query spatial data in the databases.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;MySQL&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;MySQL has some support for &lt;a href="http://dev.mysql.com/doc/refman/5.1/en/spatial-extensions.html"&gt;Spatial Extensions&lt;/a&gt;, but it's not particularly useful. For example, there is no way to query anything within the radius around a specific point. Their community pages list a &lt;a href="http://forge.mysql.com/tools/tool.php?id=41"&gt;method&lt;/a&gt; of implementing it, but it only calculates for a flat Earth model.&lt;/p&gt;
      &lt;p&gt;Instead, we'll have to implement our own algorithms again. But first of all, let us import the data into MySQL. First we create the MySQL database:&lt;/p&gt;
      &lt;pre&gt;derick@whisky:~$ mysqladmin -u root -p create poi
mysql&gt; CREATE TABLE poi(id int, type int, lat float, lon float, name char(255), address char(255), cuisine char(64), phone char(18));

&lt;/pre&gt;
      &lt;p&gt;And then we take the &lt;a href="http://derickrethans.nl/files/parsepoi.php.txt"&gt;script&lt;/a&gt; from the previous article on &lt;a href="http://drck.me/spat-osm-sqlite-8la"&gt;importing data&lt;/a&gt;, and change the third line from:&lt;/p&gt;
      &lt;pre&gt;$d = ezcDbFactory::create( 'sqlite://' . dirname( __FILE__ ) . '/pois.sqlite' );

&lt;/pre&gt;
      &lt;p&gt;to:&lt;/p&gt;
      &lt;pre&gt;$d = ezcDbFactory::create( 'mysql://root:root@localhost/poi' );

&lt;/pre&gt;
      &lt;p&gt;Of course, substitute the username and password (&lt;code&gt;root&lt;/code&gt;, &lt;code&gt;root&lt;/code&gt;) and the database name (&lt;code&gt;poi&lt;/code&gt;) to one that suits yourself. We then run again:&lt;/p&gt;
      &lt;pre&gt;php parseoi.php.txt great_britain_pubs.osm

&lt;/pre&gt;
      &lt;p&gt;And check that our import is complete:&lt;/p&gt;
      &lt;pre&gt;mysql&gt; SELECT count(*) from poi\G
*************************** 1. row ***************************
count(*): 28147

&lt;/pre&gt;
      &lt;p&gt;Now that we have all the POIs imported, we can query the database. Because MySQL doesn't have working spatial extensions, nor the register-function capabilities from SQLite such as we saw in the previous article, we have to come up with a new solution. The most obvious one is writing a stored procedure for the task, another (non-explored option) would be to write a &lt;a href="http://dev.mysql.com/doc/refman/5.1/en/adding-udf.html"&gt;user defined function&lt;/a&gt;. Writing the stored procedure is simple enough; we just have to convert the &lt;code&gt;distance()&lt;/code&gt; function to SQL. On the MySQL command line you can enter:&lt;/p&gt;
      &lt;pre&gt;delimiter //

CREATE FUNCTION distance (latA double, lonA double, latB double, LonB double)
    RETURNS double DETERMINISTIC
BEGIN
    SET @RlatA = radians(latA);
    SET @RlonA = radians(lonA);
    SET @RlatB = radians(latB);
    SET @RlonB = radians(LonB);
    SET @deltaLat = @RlatA - @RlatB;
    SET @deltaLon = @RlonA - @RlonB;
    SET @d = SIN(@deltaLat/2) * SIN(@deltaLat/2) +
        COS(@RlatA) * COS(@RlatB) * SIN(@deltaLon/2)*SIN(@deltaLon/2);
    RETURN 2 * ASIN(SQRT(@d)) * 6371.01;
END//

&lt;/pre&gt;
      &lt;p&gt;Fetching all the pubs within a 250 meter radius around 51.5375°N, 0.1933°W is than as easy as running:&lt;/p&gt;
      &lt;pre&gt;mysql&gt; SELECT name, address, phone
       FROM poi
       WHERE DISTANCE(lat, lon, 51.5375, -0.1933) &lt; 0.25;

&lt;/pre&gt;
      &lt;p&gt;With the result being:&lt;/p&gt;
      &lt;pre&gt;| name            | address                        | phone           |
+-----------------+--------------------------------+-----------------+
| Mrs Betsy Smith | Kilburn High Road 77 , NW6 6HY | +44 20 76245793 |
| The Cock Tavern | Kilburn High Road 125          | NULL            |
| The Old Bell    | NULL                           | NULL            |
| The Westbury    | Kilburn High Road 34 , NW6 5UA | +44 20 76257500 |
+-----------------+--------------------------------+-----------------+
4 rows in set (0.72 sec)

&lt;/pre&gt;
      &lt;p&gt;If we want to also include the distance from the centre point in the result, we need to modify the query to:&lt;/p&gt;
      &lt;pre&gt;mysql&gt; SELECT name, DISTANCE(lat, lon, 51.5375, -0.1933) AS dist
       FROM poi
       HAVING dist &lt; 0.25
       ORDER BY dist;

&lt;/pre&gt;
      &lt;p&gt;with as result:&lt;/p&gt;
      &lt;pre&gt;| name            | dist                |
+-----------------+---------------------+
| Mrs Betsy Smith | 0.00825473345748987 |
| The Cock Tavern |     0.2420193460511 |
| The Old Bell    |   0.103123484090313 |
| The Westbury    |   0.150294300836645 |
+-----------------+---------------------+
4 rows in set (0.72 sec)

&lt;/pre&gt;
      &lt;p&gt;In both cases, the query takes 0.72 seconds. This is not overly fast, and the main reason for this is that the &lt;code&gt;distance()&lt;/code&gt; function has to be called for every row in the table. An index can not be created on this either. However, what we can do is to filter out the results roughly first, by calculating a bounding box of latitude/longitude pairs around the centre point. Calculating the latitude boundaries can be done by:&lt;/p&gt;
      &lt;pre&gt;d = (distInKm / 6371.01 * 2π) * 360
lat1 = centreLat - d
lat2 = centreLat + d

&lt;/pre&gt;
      &lt;p&gt;In our example that becomes:&lt;/p&gt;
      &lt;pre&gt;d = (0.250 / 6371.01 * 2π) * 360 = 0.0022483
lat1 = 51.5375 - 0.0022483 = 51.5352.. -&gt; 51.5351
lat2 = 51.5375 + 0.0022483 = 51.5397.. -&gt; 51.5398

&lt;/pre&gt;
      &lt;p&gt;Or in PHP:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
function findLatBoundary($dist, $lat, &amp;$lat1, &amp;$lat2)
{
    $d = ($dist / 6371.01 * 2 * M_PI) * 360;
    $lat1 = $lat - $d;
    $lat2 = $lat + $d;
}
?&gt;

&lt;/pre&gt;
      &lt;p&gt;We round slightly up and down to combat inaccuracies in the calculations—it is a rough estimate after all.  After adding the index on the &lt;code&gt;lat&lt;/code&gt; column, and the index on the &lt;code&gt;lon&lt;/code&gt; column, we reissue the query:&lt;/p&gt;
      &lt;pre&gt;mysql&gt; CREATE index poi_lat ON poi(lat);
mysql&gt; CREATE index poi_lon ON poi(lon);

mysql&gt; SELECT name, DISTANCE(lat, lon, 51.5375, -0.1933) AS dist
       FROM poi
       WHERE lat BETWEEN 51.5351 AND 51.5398
       HAVING dist &lt; 0.25;

&lt;/pre&gt;
      &lt;p&gt;Which returns the same result as before, but faster:&lt;/p&gt;
      &lt;pre&gt;| name            | dist                |
+-----------------+---------------------+
| The Westbury    |   0.150294300836645 |
| The Old Bell    |   0.103123484090313 |
| Mrs Betsy Smith | 0.00825473345748987 |
| The Cock Tavern |     0.2420193460511 |
+-----------------+---------------------+
4 rows in set (0.01 sec)

&lt;/pre&gt;
      &lt;p&gt;We can pre-filter the result set even more, by also limiting on the longitude boundary. This involves a few more calculations than for the latitude boundaries. The distance in degrees longitude that belongs to a distance in km depends on the latitude of the location. So first we need to calculate the latitude boundaries as we have done above, and with that information calculate the longitude boundaries.&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/sphere-distance.png" alt="sphere-distance.png"/&gt;
      &lt;p&gt;In the first step (red), we calculate the northern and southern boundaries of the circle. We then calculate the western and eastern boundaries for the northern boundary (green), and southern boundary (blue).&lt;/p&gt;
      &lt;p&gt;In PHP this algorithm becomes:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
function findLonBoundary($dist, $lat, $lon, $lat1, $lat2, &amp;$lon1, &amp;$lon2)
{
    $d = $lat - $lat1;

    $d1 = $d / cos(deg2rad($lat1));
    $d2 = $d / cos(deg2rad($lat2));

    $lon1 = min($lon - $d1, $lon - $d2);
    $lon2 = max($lon + $d1, $lon + $d2);
}
?&gt;

&lt;/pre&gt;
      &lt;p&gt;If we use both functions we calculate as boundaries:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
$dist = 0.25;
$lat = 51.5375;
$lon = -0.1933;

findLatBoundary($dist, $lat, $lat1, $lat2);
findLonBoundary($dist, $lat, $lon, $lat1, $lat2, $lon1, $lon2);

echo "SELECT name, DISTANCE(lat, lon, $lat, $lon) AS dist
      FROM poi
      WHERE lat BETWEEN $lat1 AND $lat2
        AND lon BETWEEN $lon1 AND $lon2
      HAVING dist &lt; $dist
      ORDER BY dist;\n";
?&gt;

&lt;/pre&gt;
      &lt;p&gt;Which returns the query to execute:&lt;/p&gt;
      &lt;pre&gt;SELECT name, DISTANCE(lat, lon, 51.5375, -0.1933) AS dist
FROM poi
WHERE lat BETWEEN 51.535251699514 AND 51.539748300486
  AND lon BETWEEN -0.19691479627963 AND -0.18968520372037
  HAVING dist &lt; 0.25
  ORDER BY dist;

&lt;/pre&gt;
      &lt;p&gt;If we run this query, we get as result:&lt;/p&gt;
      &lt;pre&gt;| name            | dist                |
+-----------------+---------------------+
| Mrs Betsy Smith | 0.00825473345748987 |
| The Old Bell    |   0.103123484090313 |
| The Westbury    |   0.150294300836645 |
| The Cock Tavern |     0.2420193460511 |
+-----------------+---------------------+
4 rows in set (0.00 sec)

&lt;/pre&gt;
      &lt;p&gt;As you can see, we are getting the result a lot faster now—0.00 sec vs 0.72 sec.  Please do note, that if we would just have used the boundaries without using the &lt;code&gt;HAVING dist &lt; 0.25&lt;/code&gt; clause, we would have gotten one extra result that is slightly too far away (0.281 km):&lt;/p&gt;
      &lt;pre&gt;| name            | dist                |
+-----------------+---------------------+
| Mrs Betsy Smith | 0.00825473345748987 |
| The Old Bell    |   0.103123484090313 |
| The Westbury    |   0.150294300836645 |
| The Cock Tavern |     0.2420193460511 |
| Bar Hemia       |   0.281138534935208 |
+-----------------+---------------------+

&lt;/pre&gt;
      &lt;p&gt;
        &lt;strong&gt;Conclusion&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;In this article we saw how we can use a MySQL stored procedure to find our pubs and bars within a certain distance from a central location.  In the next part, we will be looking on how to solve the same problem with &lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;.&lt;/p&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201104120904</guid>
      <pubDate>Tue, 12 Apr 2011 08:04:00 +0000</pubDate>
    </item>
    <item>
      <title>Spatial Indexes: Fetching Data/SQLite</title>
      <link>http://derickrethans.nl/spatial-indexes-data-sqlite.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="spatial_indexes_fetching_data_sqlite"/&gt;Spatial Indexes: Fetching Data/SQLite&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Thursday, March 31st 2011, 09:17 BST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;In a previous &lt;a href="http://drck.me/spat-dist-8kf"&gt;article&lt;/a&gt; I introduced 'The Flat Earth Model' and the 'The spherical Earth model'. In this article we're going to have a look at fetching a data set and importing them into a SQLite database to query from PHP. What better data set is there to import than all of the UK's pubs?&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Getting the Data&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;In order to get a suitable data set we are going to use the data from the &lt;a href="http://openstreetmap.org"&gt;OpenStreetMap&lt;/a&gt; project. This project is mainly concerned with making an open map of the entire globe, but it also contains a vast database of points-of-interest (POI). OpenStreetMap contributes seems to be a big fan of pubs, and hence they are mapped really well. POIs can either be stored as a node (a single point with a geographical location) such as &lt;a href="http://www.openstreetmap.org/browse/node/603112458"&gt;The Long Acre&lt;/a&gt; or as a closed way (an ordered collection of nodes where the first and last node are the same), such as &lt;a href="http://www.openstreetmap.org/browse/way/72773168"&gt;Brondes Age&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;I will demonstrate two methods to fetch the pubs in an XML format containing nodes and ways.  The first method is with &lt;a href="http://wiki.openstreetmap.org/wiki/Xapi"&gt;XAPI&lt;/a&gt;. XAPI is an interface to the OpenStreetMap database to allow users to query and filter items. In order to fetch data through it, you specify a bounding box and a predicate. A bounding box specifies the most Eastern, Southern, Western and Northern coordinates. The UK has roughly as bounding box &lt;em&gt;9.05°W, 48.77°N, 2.19°E, 58.88°N&lt;/em&gt;, or short: &lt;em&gt;-9.05, 48.77, 2.19, 58.88&lt;/em&gt;. Fetching the data can simply be done by querying the server with wget:&lt;/p&gt;
      &lt;pre&gt;wget -O pubs.xml 'http://xapi.openstreetmap.org/api/0.6/*[amenity=pub][bbox=-9.05,48.77,2.19,58.88]'

&lt;/pre&gt;
      &lt;p&gt;This is going to take a long time; and will most likely just not work or time-out. The current XAPI server, written in an obscure programming language called &lt;a href="http://en.wikipedia.org/wiki/MUMPS"&gt;MUMPS&lt;/a&gt;, is extremely unreliable and is really slow. A new version of XAPI build in Java is on the way, but right now it's limited to 10 square degrees.&lt;/p&gt;
      &lt;p&gt;Luckily, there is an alternative in the form of parsing (an extract of) the planet file. The planet file is an enormous database dump of OpenStreetMap's data. The people at &lt;a href="http://geofabrik.de"&gt;Geofabrik&lt;/a&gt; have extracts for specific parts of the world at &lt;a href="http://download.geofabrik.de/osm/"&gt;http://download.geofabrik.de/osm/&lt;/a&gt;. From them I downloaded the &lt;code&gt;europe/great_britain.osm.pbf&lt;/code&gt; file (274 MB) for my example. With a tool called &lt;a href="http://wiki.openstreetmap.org/wiki/Osmosis"&gt;Osmosis&lt;/a&gt; we can then filter out all pubs into a similar formatted XML file. I am only giving the command to do our task at hand, but a detailed usage guide for Osmosis is &lt;a href="http://wiki.openstreetmap.org/wiki/Osmosis/Detailed_Usage"&gt;available&lt;/a&gt; too. The command runs in about four minutes and looks like:&lt;/p&gt;
      &lt;pre&gt;./osmosis-0.38/bin/osmosis -v 5 \
\
--read-pbf file=great_britain.osm.pbf \
--tf reject-relations \
--tf accept-nodes amenity=pub,bar \
--tf reject-ways \
outPipe.0=POI \
\
--read-pbf file=great_britain.osm.pbf \
--tf reject-relations \
--tf accept-ways amenity=pub,bar \
--used-node \
outPipe.0=area \
\
--merge inPipe.0=POI inPipe.1=area \
--write-xml file=great_britain_pubs.osm

&lt;/pre&gt;
      &lt;p&gt;
        &lt;strong&gt;Importing the Data&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;The resulting XML file has two important elements: nodes and ways. The XML for The Long Acre looks like:&lt;/p&gt;
      &lt;pre&gt;&lt;node id="603112458"
  version="1" timestamp="2010-01-02T16:12:57Z"
  uid="1185" user="dankarran" changeset="3519803"
  lat="51.511831" lon="-0.126789"&gt;

  &lt;tag k="amenity" v="pub"/&gt;
  &lt;tag k="name" v="The Long Acre"/&gt;
&lt;/node&gt;

&lt;/pre&gt;
      &lt;p&gt;Important here are the latitude (51.51°N) and longitude (0.12°W) and of course, the name of the pub in the &lt;em&gt;tag&lt;/em&gt; element.&lt;/p&gt;
      &lt;p&gt;And the XML for Brondes Age (a way) is a bit more complex, and looks like:&lt;/p&gt;
      &lt;pre&gt;&lt;way id="72773168"
  version="2" timestamp="2011-01-31T10:07:45Z"
  uid="346" user="Tom Chance" changeset="7143502"&gt;

  &lt;nd ref="863936779"/&gt;
  &lt;nd ref="863936774"/&gt;
  &lt;nd ref="863936777"/&gt;
  &lt;nd ref="863936796"/&gt;
  &lt;nd ref="863936791"/&gt;
  &lt;nd ref="863936768"/&gt;
  &lt;nd ref="863936779"/&gt;
  &lt;tag k="addr:housenumber" v="328"/&gt;
  &lt;tag k="addr:postcode" v="NW6 2QN"/&gt;
  &lt;tag k="addr:street" v="Kilburn High Road"/&gt;
  &lt;tag k="amenity" v="pub"/&gt;
  &lt;tag k="building" v="yes"/&gt;
  &lt;tag k="contact:email" v="brondesage@aol.com"/&gt;
  &lt;tag k="contact:phone" v="+44 20 76249010"/&gt;
  &lt;tag k="contact:website" v="http://www.brondesage.com/"/&gt;
  &lt;tag k="name" v="Brondes Age"/&gt;
  &lt;tag k="toilets" v="yes"/&gt;
  &lt;tag k="toilets:access" v="customers"/&gt;
&lt;/way&gt;

&lt;/pre&gt;
      &lt;p&gt;There is no latitude and longitude, but instead there are references to nodes (the &lt;em&gt;nd&lt;/em&gt; elements). There is also a large collection of descriptive tags, such as &lt;em&gt;addr:housenumber&lt;/em&gt; and &lt;em&gt;contact:phone&lt;/em&gt;. In order to calculate the latitude and longitude of Brondes Age we can either take the &lt;em&gt;correct&lt;/em&gt; approach by calculating the &lt;a href="http://paulbourke.net/geometry/polyarea/"&gt;centroid&lt;/a&gt; or we can simply take the average latitude and longitude of all the nodes. To make things simple, I will take the simple approach.&lt;/p&gt;
      &lt;p&gt;In order to find the latitude and longitude of all the nodes, we simply scan through the file again and find all the nodes that correspond to the &lt;em&gt;ref&lt;/em&gt; attribute of each &lt;em&gt;nd&lt;/em&gt; element. We should disregard the last one of each &lt;em&gt;way&lt;/em&gt; though, as it is the same as the first one.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;SQLite&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;To start, we will use a very simple RDBMS: &lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt;. We will still have to define a database schema. We can simply do that with:&lt;/p&gt;
      &lt;pre&gt;derick@whisky:~$ sqlite pois.sqlite
sqlite&gt; CREATE TABLE poi(id int, type int, lat float, lon float, name char, address char, cuisine char, phone char);

&lt;/pre&gt;
      &lt;p&gt;The importing of data then can be done by this "simple" &lt;a href="http://derickrethans.nl/files/parsepoi.php.txt"&gt;script&lt;/a&gt; (after adjusting the path to eZ Components/ &lt;a href="http://incubator.apache.org/zetacomponents/"&gt;Zeta Components&lt;/a&gt;) with:&lt;/p&gt;
      &lt;pre&gt;php parsepoi.php.txt great_britain_pubs.osm

&lt;/pre&gt;
      &lt;p&gt;After running the import script, there should be about 28000 POIs in the database.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Querying the Data&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;Once we have imported the POIs into our SQLite database, we are ready to query them. SQLite does not have a very extensive set of &lt;a href="http://www.sqlite.org/lang_corefunc.html"&gt;functions&lt;/a&gt; so we can not do the calculation in the query directly. Just to iterate from the &lt;a href="http://drck.me/spat-dist-8kf"&gt;previous&lt;/a&gt; article in this series, the formula for calculating the distance in a spherical Earth model is:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
function distance($latA, $lonA, $latB, $lonB)
{
  // convert from degrees to radians
  $latA = deg2rad($latA); $lonA = deg2rad($lonA);
  $latB = deg2rad($latB); $lonB = deg2rad($lonB);

  // calculate absolute difference for latitude and longitude
  $dLat = ($latA - $latB);
  $dLon = ($lonA - $lonB);

  // do trigonometry magic
  $d =
    sin($dLat/2) * sin($dLat/2) +
    cos($latA) * cos($latB) * sin($dLon/2) *sin($dLon/2);
  $d = 2 * asin(sqrt($d));
  return $d * 6371;
}
?&gt;

&lt;/pre&gt;
      &lt;p&gt;One solution would be to query the database, and then use the &lt;code&gt;distance()&lt;/code&gt; function to filter out unwanted elements, like:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
include 'distance.php';
require '/home/derick/dev/zetacomponents/trunk/Base/src/ezc_bootstrap.php';
$d = ezcDbFactory::create( 'sqlite://' . dirname( __FILE__ ) . '/pois.sqlite' );

// Centre point
$lat = 51.5375;
$lon = -0.1933;

// Distance (in km)
$wantedD = 0.25;

$q = $d-&gt;createSelectQuery();
$q-&gt;select( '*' )-&gt;from( 'poi' );
$s = $q-&gt;prepare();
$s-&gt;execute();

foreach ( $s as $res )
{
  $e = distance( $lat, $lon, $res['lat'], $res['lon'] );
  if ( $e &lt; $wantedD )
  {
    echo sprintf( '%.3f,%.3f %-40s %.2f km away',
      $res['lat'], $res['lon'], $res['name'],
      $e ), "\n";
  }
}
?&gt;

&lt;/pre&gt;
      &lt;p&gt;This will show all pubs in a 250 meter radius around 51.53°N, 0.19°W:&lt;/p&gt;
      &lt;pre&gt;derick@whisky:/home/httpd/html/test/maps$ php fetch-sqlite-simple.php
51.538,-0.193 Mrs Betsy Smith                          0.01 km away
51.539,-0.195 The Cock Tavern                          0.24 km away
51.537,-0.192 The Old Bell                             0.10 km away
51.537,-0.192 The Westbury                             0.15 km away

&lt;/pre&gt;
      &lt;p&gt;Of course, this is not very efficient as &lt;strong&gt;all&lt;/strong&gt; items are selected from the database, and then filtered out depending on their calculated distance. Luckily, SQLite supports user defined functions written in PHP. This would mean that we can increase performance a bit by letting PHP's internals call the distance function for every row:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
include 'distance.php';
require '/home/derick/dev/zetacomponents/trunk/Base/src/ezc_bootstrap.php';
$d = ezcDbFactory::create( 'sqlite://' . dirname( __FILE__ ) . '/pois.sqlite' );

// Register SQLite function "dist" to our PHP function "distance".
$d-&gt;sqliteCreateFunction( 'dist', 'distance' );

// Centre point
$lat = 51.5375;
$lon = -0.1933;

// Distance (in km)
$wantedD = 0.25;

$q = $d-&gt;createSelectQuery();

// Use the user defined dist() function as additional column
$q-&gt;select( "*, dist($lat, $lon, lat, lon) as e" )
  -&gt;from( 'poi' )
  -&gt;where( "e &lt; $wantedD" );

$s = $q-&gt;prepare();
$s-&gt;execute();

foreach ( $s as $res )
{
  echo sprintf( '%.3f,%.3f %-40s %.2f km away',
    $res['lat'], $res['lon'], $res['name'],
    $res['e'] ), "\n";
}
?&gt;

&lt;/pre&gt;
      &lt;p&gt;The result is naturally the same as before.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Conclusion&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;In this installment we have seen how to retrieve information from &lt;a href="http://openstreetmap.org"&gt;OpenStreetMap&lt;/a&gt;'s database and import them into SQLite for querying. In the next installment we will have a look at how to import and query with MySQL and PostgreSQL.&lt;/p&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201103310917</guid>
      <pubDate>Thu, 31 Mar 2011 08:17:00 +0000</pubDate>
    </item>
    <item>
      <title>Spatial Indexes: Calculating Distance</title>
      <link>http://derickrethans.nl/spatial-indexes-calculating-distance.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="spatial_indexes_calculating_distance"/&gt;Spatial Indexes: Calculating Distance&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; Montréal, Canada&lt;/div&gt;
        &lt;div class="date"&gt;Wednesday, March 9th 2011, 07:17 EST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;During my "Geolocation and Mapping with PHP" talk that I've given a few times I briefly touch on the subject of indexes on data-sets of spatial data. This isn't as simple as just solving &lt;a href="http://en.wikipedia.org/wiki/Pythagorean_theorem"&gt;Pythagoras theorem&lt;/a&gt; and this article is meant to clarify this.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;The flat Earth model&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;Pythagoras theorem can be used to calculate the distance between two points quite easily; you take the square root of the square of the absolute vertical distance plus the square of the absolute horizontal distance; in short:&lt;/p&gt;
      &lt;pre&gt;d = √(|x1 - x2|² + |y1 - y2|²)

&lt;/pre&gt;
      &lt;p&gt;or in PHP:&lt;/p&gt;
      &lt;pre&gt;$d = sqrt(pow(abs($x1 - $x2), 2) + pow(abs($y1 - $y2), 2));

&lt;/pre&gt;
      &lt;p&gt;If you take for example London's coordinates (51.50°N, 0.13°W) and Amsterdam's coordinates (52.37°N, 4.90°E) we can calculate the distance with:&lt;/p&gt;
      &lt;pre&gt;d = √(|-0.13 - 4.90|² + |51.50 - 52.37|²)
d = √(5.03² + 0.87²)
d = √(26.0578)
d ≅ 5.10

&lt;/pre&gt;
      &lt;p&gt;And on a map:&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/distance-amsterdam-london.png" alt="distance-amsterdam-london.png"/&gt;
      &lt;p&gt;But what does a difference of "5.10°" actually mean? How far is this in useful units, such as meters?&lt;/p&gt;
      &lt;p&gt;If we show the whole map of which the above is an extract, we come to:&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/map-flat.png" alt="map-flat.png"/&gt;
      &lt;p&gt;The distance around the equator, and through the poles is roughly the same, 40.000km (please be aware that the blue line only shows half of it, the other half is going through the anti-meridian at 180°W/E). 5.03° in East-West difference is then about &lt;code&gt;40 000 ✕ (5.03/360) = 559 km&lt;/code&gt; and the North-South difference about &lt;code&gt;20 000 ✕ (0.87/180) = 97 km&lt;/code&gt;. Using those numbers within the Pythagoras theorem we end up with a distance of &lt;code&gt;√(559² + 97²) = 567 km&lt;/code&gt;. Although the calculation is correct, the answer is still wrong. The real distance is closer to 360 km.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;The spherical Earth model&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;If we look at the Earth in its original (mostly) spherical &lt;a href="http://derickrethans.nl#_footnote_0_1" class="footnote"&gt;1&lt;/a&gt; shape, then it's clear that 10° longitude (East/West) at 60°N is going to be less of a distance than 10°E/W at the equator. It's actually fairly easy to calculate how much 1° longitude is at 60°N by using &lt;code&gt;cos(60) * 1/360 * 6371 * 2π&lt;/code&gt; &lt;a href="http://derickrethans.nl#_footnote_0_2" class="footnote"&gt;2&lt;/a&gt;. Or in PHP:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
$oneDeg =
        cos(deg2rad(60)) * // adjustment for latitude and radians/degrees
        1/360 * // 1 out of 360°
        6371 * 2 * M_PI, // circumference of the Earth at the equator
        "\n";
?&gt;

&lt;/pre&gt;
      &lt;p&gt;This returns &lt;code&gt;55.597 km per °&lt;/code&gt; for 60°N. The same distance in degrees on the Equator gives &lt;code&gt;6371 * 2 * M_PI / 360 = 111.195&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;The following diagram shows ones more that latitudinal degrees always correspond with the same distance in kilometer, whereas longitudinal degrees differ.&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/sphere2.png" alt="sphere2.png"/&gt;
      &lt;p&gt;In the diagram the line A is a line from 0°N, 90°W to 10°N, 90°W. It has the same length as line B, from 30°N, 90°W to 40°N, 90°W: a 36th of the circumference of the Earth through the poles. Line C, from 0°N, 30°W to 0°N, 20°W has the same length. Line D however, from 50°N, 30°W to 50°N, 20°W is shorter by a factor of &lt;code&gt;cos(50°) ≅ 0.64&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;If we look again at the distance between London and Amsterdam, ignore the differences in latitude and instead pick the average, we see:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
$d = abs(-0.13 - 4.90);
// $d = 5.03 degrees

$e = 5.03/360 * cos(deg2rad(51.935)) * 6371 * 2 * M_PI;
$e = 5.03 * 0.617 * 6371 * 2 * M_PI;
// $e ≅ 345 km
?&gt;

&lt;/pre&gt;
      &lt;p&gt;Which is a bit shorter than the expected 360km, but that's because we conveniently forgot about the difference in latitude.&lt;/p&gt;
      &lt;p&gt;Sadly, we can't use Pythagoras's theorem to calculate the real distance with the latitude difference taken account as well. This is because the theorem is meant for &lt;a href="http://en.wikipedia.org/wiki/Euclidean_geometry"&gt;Euclidean&lt;/a&gt; geometry, and a sphere does not follow the rules of this geometry. Instead we need to use a formula that is called the &lt;a href="http://en.wikipedia.org/wiki/Great-circle_distance"&gt;great-circle distance&lt;/a&gt; formula. The main concept behind it is that a circle is drawn across the whole sphere that connects both the start (point P) as well as the end (point V). Then with that circle the distance can be calculated. The following diagram shows this:&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/great-circle.jpg" alt="great-circle.jpg"/&gt;
      &lt;p&gt;I will spare you how the function is derived, but the distance calculation ends up as being:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
function distance($latA, $lonA, $latB, $lonB)
{
        // convert from degrees to radians
        $latA = deg2rad($latA); $lonA = deg2rad($lonA);
        $latB = deg2rad($latB); $lonB = deg2rad($lonB);

        // calculate absolute difference for latitude and longitude
        $dLat = ($latA - $latB);
        $dLon = ($lonA - $lonB);

        // do trigonometry magic
        $d =
                sin($dLat/2) * sin($dLat/2) +
                cos($latA) * cos($latB) * sin($dLon/2) *sin($dLon/2);
        $d = 2 * asin(sqrt($d));
        return $d * 6371;
}
?&gt;

&lt;/pre&gt;
      &lt;p&gt;If we punch in our original numbers form London (51.50°N, 0.13°W) and Amsterdam (52.37°N, 4.90°E), we calculate the following:&lt;/p&gt;
      &lt;pre&gt;&lt;?php
$d = distance(51.50, -0.13, 52.37, 4.90);
echo $d, " km\n";
?&gt;

&lt;/pre&gt;
      &lt;p&gt;Which gives us the expected result of &lt;code&gt;358.07 km&lt;/code&gt;.&lt;/p&gt;
      &lt;p&gt;
        &lt;strong&gt;Conclusion&lt;/strong&gt;
      &lt;/p&gt;
      &lt;p&gt;I hope that the above clarified the difference between 2D spatial indexing with the &lt;em&gt;flat Earth model&lt;/em&gt; and spatial indexing of geo-located data (&lt;em&gt;the spherical Earth model&lt;/em&gt;). In future articles I will go into specific implementations of spatial indexing by traditional databases such as SQLite, MySQL and PostGreSQL; NoSQL databases such as MongoDB and CouchDB; and Solr.&lt;/p&gt;
      
      
    &lt;/div&gt;
    &lt;ul class="footnotes"&gt;
      &lt;li&gt;
        &lt;a name="_footnote_0_1"&gt;1&lt;/a&gt;
        &lt;p&gt;The Earth is not really a sphere, but an approximation of it. However, doing the same calculations for an ellipsoid can (as far as I know) only be done by approximation. The difference would hardly matter for finding the closest pub.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a name="_footnote_0_2"&gt;2&lt;/a&gt;
        &lt;p&gt;In this article, I've used an average radius of the Earth of 6371 km.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201103090717</guid>
      <pubDate>Wed, 09 Mar 2011 12:17:00 +0000</pubDate>
    </item>
    <item>
      <title>Using OpenStreetMap tiles with Flickr</title>
      <link>http://derickrethans.nl/using-openstreetmap-with-flickr.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="using_openstreetmap_tiles_with_flickr"/&gt;Using OpenStreetMap tiles with Flickr&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Tuesday, March 1st 2011, 09:20 GMT&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; August 4th, 2011: I've changed the script to use any of the three &lt;a href="http://openstreetmap.org"&gt;OpenStreetMap&lt;/a&gt; tile servers, instead of hard coding it just to one.&lt;/p&gt;
      &lt;p&gt;I like taking pictures, and I usually take a GPS so that I can place them on a map on my &lt;a href="http://www.flickr.com/photos/derickrethans/map"&gt;Flickr&lt;/a&gt; page. On my last excursion however, the battery of my GPS had died, so I did not have location information available to store in my pictures' &lt;a href="http://en.wikipedia.org/wiki/Exif"&gt;EXIF&lt;/a&gt; headers. Flickr can use the EXIF headers to then show the images on the map.&lt;/p&gt;
      &lt;p&gt;Because I did not have the location information to automatically place my pictures on the map, I wanted to do that by hand. Flickr, being owned by Yahoo!, uses &lt;a href="http://maps.yahoo.co.uk/"&gt;Yahoo! Maps&lt;/a&gt;. Yahoo! Maps however, is not terribly great in the country side. Actually, it is mostly empty, especially &lt;a href="http://sautter.com/map/?zoom=16&amp;lat=53.05598&amp;lon=-1.78709&amp;layers=00000B0TFFFFFTFF"&gt;compared&lt;/a&gt; to OpenStreetMap's version. This made placing photos onto the map by hand quite impossible. So I set out to have only &lt;a href="http://openstreetmap.org"&gt;OpenStreetMap&lt;/a&gt; tiles as back ground in Flickr like they already do for &lt;a href="http://www.flickr.com/map?&amp;fLat=39.9123&amp;fLon=116.4179&amp;zl=7"&gt;Bejing&lt;/a&gt; and some other &lt;a href="http://en.wikipedia.org/wiki/OpenStreetMap#Flickr"&gt;places&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;Inspired by the &lt;a href="http://www.ex-parrot.com/pete/upside-down-ternet.html"&gt;Upside-Down-Ternet&lt;/a&gt; I installed &lt;a href="http://www.squid-cache.org/"&gt;Squid&lt;/a&gt;, and set the &lt;code&gt;url_rewrite_program&lt;/code&gt; to a PHP script (with execute bit on):&lt;/p&gt;
      &lt;pre&gt;url_rewrite_program /home/derick/bin/redirect.php

&lt;/pre&gt;
      &lt;p&gt;Squid will fire up a few of those scripts (depending on the &lt;code&gt;url_rewrite_children&lt;/code&gt; setting) and then supplies them with URLs to rewrite. Each line of input is an URL to rewrite, and the script should echo a rewritten URL.&lt;/p&gt;
      &lt;p&gt;The script itself is rather simple. It just needs to account for two different formats because the Yahoo!Maps URLs are different from the Flickr ones (as indicated by the &lt;code&gt;r&lt;/code&gt; argument in the original URL). I'm including the script here (and as &lt;a href="http://derickrethans.nl/files/redirectYahooMapsToOsm.php.txt"&gt;download&lt;/a&gt;):&lt;/p&gt;
      &lt;pre&gt;#!/usr/local/php/5.3dev/bin/php
&lt;?php
do {
    // Open the log file
    $a = fopen( '/tmp/test.log', 'a' );

    // Read the URL line
    $input = fgets( STDIN );
    if ( $input == '' )
    {
        continue;
    }

    // Split the data so that we can access the host and
    // query string elements
    $parts = explode( ' ', $input );
    $urlParts = parse_url( $parts[0] );
    $queryParts = array();
    if ( isset( $urlParts['query'] ) )
    {
        parse_str( $urlParts['query'], $queryParts );
    }

    // The block to test for Yahoo! Maps:
    if ( preg_match( '@maps\d?.yimg.com@', $urlParts['host'] ) )
    {
        if ( !isset( $queryParts['r'] ) || $queryParts['r'] == 0 )
        {
            // This is the format that Flickr uses

            // Do the math to calculate the OSM tile
            // coordinates from the Yahoo!Maps one
            $z = 18 - $queryParts['z'];
            $x = $queryParts['x'];
            $y = pow(2, $z-1) - $queryParts['y'] - 1;

            // Assemble new URL and write log line
            $newUrl = "http://b.tile.openstreetmap.org/$z/$x/$y.png";
            fwrite( $a, "REDIR: $parts[0] =&gt; $newUrl\n" );
        }
        else
        {
            // This is the format that Yahoo!Maps uses

            // Do the math to calculate the OSM tile
            // coordinates from the Yahoo!Maps one
            $z = $queryParts['z'] - 1;
            $x = $queryParts['x'];
            $y = pow( 2, $z - 1 ) - $queryParts['y'] - 1;

            // Assemble new URL and write log line
            $range = range('a', 'c');
            $server = $range[rand(0, sizeof($range)-1)];
            $newUrl = "http://{$server}.tile.openstreetmap.org/$z/$x/$y.png";
            fwrite( $a, "REDIR: $parts[0] =&gt; $newUrl\n" );
        }
    } else {
        $newUrl = $parts[0];
        fwrite($a, "NORMAL: $newUrl\n");
    }

    // Output the rewritten (or original) URL
    echo $newUrl, "\n";
} while ( true );

&lt;/pre&gt;
      &lt;p&gt;After I configured my browser to use the Squid proxy running on localhost, Flickr is now shown with &lt;a href="http://openstreetmap.org"&gt;OpenStreetMap&lt;/a&gt; tiles as background:&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/flickrosm.png" alt="flickrosm.png"/&gt;
      &lt;p&gt;And with the OpenStreetMap tiles in the background, I could place my photos on the correct location on the &lt;a href="http://www.flickr.com/photos/36163802@N00/sets/72157625953902689/map?&amp;fLat=53.0599&amp;fLon=-1.7893&amp;zl=4&amp;order_by=recent"&gt;map&lt;/a&gt;.&lt;/p&gt;
      
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201103010920</guid>
      <pubDate>Tue, 01 Mar 2011 09:20:00 +0000</pubDate>
    </item>
    <item>
      <title>PHP and Ordnance Survey Mapping</title>
      <link>http://derickrethans.nl/php-mapping.html</link>
      <description>&lt;div class="article"&gt;
  &lt;div class="body"&gt;
    &lt;div class="articleListItem"&gt;
      &lt;h1&gt;&lt;a name="php_and_ordnance_survey_mapping"/&gt;PHP and Ordnance Survey Mapping&lt;/h1&gt;
      &lt;dl class="head"/&gt;
      &lt;div class="articleMetaData"&gt;
        &lt;div class="location"&gt; London, UK&lt;/div&gt;
        &lt;div class="date"&gt;Tuesday, April 27th 2010, 14:27 BST&lt;/div&gt;
      &lt;/div&gt;
      &lt;p&gt;About a month ago, &lt;a href="http://www.ordnancesurvey.co.uk/"&gt;Ordnance Survey&lt;/a&gt; &lt;a href="http://www.ordnancesurvey.co.uk/oswebsite/media/news/2010/April/OpenData.html"&gt;opened&lt;/a&gt; up some of their data for public consumption under a brand called &lt;a href="http://www.ordnancesurvey.co.uk/oswebsite/opendata/"&gt;OpenData&lt;/a&gt;. The data is &lt;a href="http://www.ordnancesurvey.co.uk/oswebsite/opendata/licence/docs/licence.pdf"&gt;licenced&lt;/a&gt; under a "Creative Commons Attribute"-like license. One of the data sets they provide is &lt;a href="http://www.ordnancesurvey.co.uk/oswebsite/products/code-point-open/"&gt;Code-Point Open&lt;/a&gt; and provides a dataset "that contains postcode units, each of which have a precise geographical location". I've always been a bit of a map-geek, and have always geotagged my &lt;a href="http://www.flickr.com/photos/derickrethans/map"&gt;pictures&lt;/a&gt; and some of my &lt;a href="http://twitter.com/derickr"&gt;tweets&lt;/a&gt;. So I was wondering what cool thing I could do with this newly released data.  I decided to map all the postcodes onto the UK map where more postcodes for a specific place would create a "lighter" colour. Each postcode has on average about 15 addresses, so in more densely populated areas you have more "postcodes-per-area". Doing this wasn't very difficult and it resulted in the following map:&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/postcode-uk-scaled.jpg" alt="postcode-uk-scaled.jpg"/&gt;
      &lt;p&gt;You can very clearly see the more densely populated areas such as London.&lt;/p&gt;
      &lt;p&gt;This generated postcode-density map I wanted to overlay on a real map, such as OpenStreetMap provides. When I tried to align the image that I generated with the OSM map, I ended up with this:&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/uk-osm-postcode-overlay.jpg" alt="uk-osm-postcode-overlay.jpg"/&gt;
      &lt;p&gt;Obviously, the maps don't align. In order to fix this, I ended up learning a lot more about map projections.&lt;/p&gt;
      &lt;p&gt;The &lt;a href="http://www.ordnancesurvey.co.uk/oswebsite/products/code-point-open/"&gt;Code-Point Open&lt;/a&gt; data uses the UK's &lt;a href="http://en.wikipedia.org/wiki/British_national_grid_reference_system#Grid_letters"&gt;National Grid&lt;/a&gt; to store location data in. The National Grid consists of 100*100 km squares that are then further subdivided into smaller squares creating grid references such as &lt;em&gt;TQ3012780512&lt;/em&gt;, or the numerical version &lt;em&gt;530128 180512&lt;/em&gt;. &lt;em&gt;TQ&lt;/em&gt; translates into the "hundred thousands" of the &lt;em&gt;Eastings&lt;/em&gt; and &lt;em&gt;Northings&lt;/em&gt; according to the grid that you can see &lt;a href="http://en.wikipedia.org/wiki/File:National_Grid_for_Great_Britain_with_central_meridian.gif"&gt;here&lt;/a&gt;. In this case, it specifies a point 530 128 meters East and 180 512 meters North of the origin.  (If you work it out, you'll end up in London).&lt;/p&gt;
      &lt;p&gt;&lt;a href="http://www.openstreetmap.org/"&gt;OpenStreetMap&lt;/a&gt; uses a &lt;a href="http://en.wikipedia.org/wiki/Mercator_projection"&gt;Mercator&lt;/a&gt; projection to visualize maps. The Mercator projection is a cylindrical map projection, and it distorts the size and shape of large objects, as the scale increases from the Equator to the poles. Therefore it only works from about 85°N to 85°S (why it is 85° only be came clear after doing all the maths for it). Google Maps uses the same projection.&lt;/p&gt;
      &lt;p&gt;There were a few challenges plotting the &lt;a href="http://www.ordnancesurvey.co.uk/oswebsite/products/code-point-open/"&gt;Code-Point Open&lt;/a&gt; data on an OpenStreetMap map:&lt;/p&gt;
      &lt;ul&gt;
        &lt;li&gt;
          &lt;p&gt;The National Grid coordinates need to be converted to Latitude/Longitude pairs&lt;/p&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;p&gt;Latitude/Longitude pairs need to be mapped to pixels to align with the Mercator projection of OpenStreetMap maps.&lt;/p&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
      &lt;p&gt;For &lt;strong&gt;converting National Grid&lt;/strong&gt; references to Latitude/Longitude pairs I found some &lt;a href="http://www.movable-type.co.uk/scripts/latlong-gridref.html"&gt;JavaScript code&lt;/a&gt;. I converted this code to PHP and to verify whether my code was working properly, I used &lt;a href="http://www.fieldenmaps.info/cconv/cconv_gb.html"&gt;this site&lt;/a&gt;.&lt;/p&gt;
      &lt;p&gt;&lt;strong&gt;Converting Latitude/Longitude&lt;/strong&gt; pairs to the pixel coordinates of OSM required a bit of maths. OSM provides maps in different zoom levels, from &lt;a href="http://www.openstreetmap.org/?lat=0&amp;lon=0&amp;zoom=2&amp;layers=B000FTFT"&gt;2&lt;/a&gt; to &lt;a href="http://www.openstreetmap.org/?lat=51.500834&amp;lon=-0.142455&amp;zoom=17&amp;layers=B000FTFT"&gt;17&lt;/a&gt;. Each zoom level having twice the amount of pixels horizontally and vertically.  Zoom level 2 has 4 times 4 tiles, where each tile is 256*256 pixels. At zoom level two, the whole world fits into 1024*1024 pixels. The number of pixels for each axis for a specific zoom-level can be calculated by:&lt;/p&gt;
      &lt;pre&gt;pow(2^zoom) * 256

&lt;/pre&gt;
      &lt;p&gt;For a zoom level of 7 that makes 16384 pixels in each direction. To convert a longitude (in the range -180° to 180°) we can simply apply:&lt;/p&gt;
      &lt;pre&gt;x = ((lon + 180) / 360) * (pow(2^zoom) * 256)

&lt;/pre&gt;
      &lt;p&gt;For a Longitude of 0.003117° E at zoom-level 13 that turns out to be pixel 1048594.&lt;/p&gt;
      &lt;p&gt;Longitude conversions are more difficult due to the Mercator projection itself. To convert we apply:&lt;/p&gt;
      &lt;pre&gt;y = ((atanh(sin(deg2rad(-lat))) / π) + 1) * (pow(2^(zoom-1)))

&lt;/pre&gt;
      &lt;p&gt;For a Latitude of 51.502817° N at zoom-level 13 that turns out to be pixel 697399.&lt;/p&gt;
      &lt;p&gt;Creating an image for the whole world at zoom level 13 is impractical as it is 2097152*2097152 pixels and downloading all the 67 million tiles for this zoom level is probably not liked by the OSM people either. So instead we take a cut out for a specific area only. For the UK (61.37°N -9.49° W, 49.76°N +3.63°E) at zoom level 7 we end up with a 1536x2048 map with the North-Western pixel being 15360,9216. On this map, we can draw the latitudes and longitudes as well as the National Grid lines (full image is on &lt;a href="http://farm4.static.flickr.com/3001/4557130189_af995cab6b_o.png"&gt;flickr&lt;/a&gt;):&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/uk-osm-lines-crop.jpg" alt="uk-osm-lines-crop.jpg"/&gt;
      &lt;p&gt;The only thing left to do now, is to map the postcode density information to the map. I picked zoom level 6 for this, and the result is (after cropping it to 640 pixels width):&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/uk-osm-postcode.jpg" alt="uk-osm-postcode.jpg"/&gt;
      &lt;p&gt;As you can see, this is perfect fit to the outlines of the country. But unfortunately, when we look very closely at the plotted map data, for example for the &lt;em&gt;NW10 3&lt;/em&gt; postcodes, we notice that the mapping is slightly off. The blue dots are what we plotted, and the red dots are what the locations &lt;strong&gt;should&lt;/strong&gt; have been:&lt;/p&gt;
      &lt;img src="http://derickrethans.nl/images/content/uk-osm-nw103-1.png" alt="uk-osm-nw103-1.png"/&gt;
      &lt;p&gt;The reason for this is that when we converted the National Grid locations to Latitude/Longitude pairs to plot on the OSM maps, I forgot to take into account the different &lt;a href="http://en.wikipedia.org/wiki/Datum_%2528geodesy%2529"&gt;Datums&lt;/a&gt; that are used in the projections. The Earth is not a perfect sphere, and an approximation of the ellipsoid of the whole Earth is not necessarily the best fitting for a specific area such as the UK. Therefore, the National Grid uses the &lt;a href="http://en.wikipedia.org/wiki/OSGB36#Datum_shift_between_OSGB_36_and_WGS_84"&gt;OSGB36&lt;/a&gt; Datum which fits more closely to the UK, where as OpenStreetMap uses the WGS84 Datum that is also used by GPS. The &lt;a href="http://osi.ie/"&gt;Ordnance Survey Ireland&lt;/a&gt; has a more thorough &lt;a href="http://www.osi.ie/GetAttachment.aspx?id=b2bd07a6-858e-4eb1-9d09-25917f0c713a"&gt;explanation&lt;/a&gt; on their site. As you can see above, using the wrong Datum can mean locations can be off. In our example about 100 meters. Converting between different Datums is possible, albeit processor intensive.&lt;/p&gt;
      &lt;p&gt;After I figured out all the maths for this, the only problem that remains that implementing those algorithms in PHP is show—calculating all the positions from the 1.6 million postcode locations takes up to 10 minutes. This is why I am not presenting any code yet. I am planning to implement all the necessary calculations in a &lt;a href="http://derickrethans.nl/available-for-php-extension-writing.html"&gt;PHP extension&lt;/a&gt; to speed up the calculations&lt;/p&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</description>
      <guid>201004271427</guid>
      <pubDate>Tue, 27 Apr 2010 13:27:00 +0000</pubDate>
    </item>
  </channel>
</rss>

