[routino] 04/13: Imported Upstream version 2.7

Uwe Steinmann steinm-guest at moszumanska.debian.org
Thu Apr 17 11:40:02 UTC 2014


This is an automated email from the git hooks/post-receive script.

steinm-guest pushed a commit to branch master
in repository routino.

commit 2e74f67a10c059eb4ec8a0b6fb06bc0d6f9ee3e6
Author: Uwe Steinmann <steinm at debian.org>
Date:   Thu Apr 17 07:35:05 2014 +0200

    Imported Upstream version 2.7
---
 ChangeLog                                          | 1965 ++++++++++++++++++++
 Makefile                                           |   36 +-
 Makefile.conf                                      |   81 +
 doc/ALGORITHM.txt                                  |   53 +-
 doc/CONFIGURATION.txt                              |   76 +-
 doc/DATALIFE.txt                                   |   77 +-
 doc/INSTALL.txt                                    |  211 ++-
 doc/LIMITS.txt                                     |  167 ++
 doc/Makefile                                       |   59 +-
 doc/NEWS.txt                                       |  157 +-
 doc/README.txt                                     |   45 +-
 doc/TAGGING.txt                                    |  161 +-
 doc/USAGE.txt                                      |  162 +-
 doc/html/algorithm.html                            |  113 +-
 doc/html/configuration.html                        |  122 +-
 doc/html/data.html                                 |   42 +-
 doc/html/index.html                                |   51 +-
 doc/html/installation.html                         |  289 ++-
 doc/html/limits.html                               |  244 +++
 doc/html/output.html                               |   42 +-
 doc/html/readme.html                               |  171 +-
 doc/html/style.css                                 |   34 +-
 doc/html/tagging.html                              |  252 +--
 doc/html/usage.html                                |  185 +-
 Makefile => extras/Makefile                        |   38 +-
 extras/README.txt                                  |   27 +
 extras/errorlog/README.txt                         |   29 +
 {web/bin => extras/errorlog}/summarise-log.pl      |  144 +-
 extras/find-fixme/Makefile                         |  175 ++
 extras/find-fixme/README.txt                       |   96 +
 extras/find-fixme/fixme-dumper.c                   |  346 ++++
 extras/find-fixme/fixme-finder.c                   |  365 ++++
 extras/find-fixme/fixme.xml                        |   74 +
 extras/find-fixme/osmparser.c                      |  314 ++++
 extras/find-fixme/web/www/fixme.cgi                |  147 ++
 .../find-fixme/web/www/fixme.css                   |   30 +-
 extras/find-fixme/web/www/fixme.html               |  162 ++
 extras/find-fixme/web/www/fixme.leaflet.js         |  563 ++++++
 extras/find-fixme/web/www/fixme.openlayers.js      |  623 +++++++
 .../find-fixme/web/www}/index.html                 |   38 +-
 .../find-fixme/web/www/paths.pl                    |   28 +-
 extras/plot-time/README.txt                        |   18 +
 extras/plot-time/plot-planetsplitter-time.pl       |  108 ++
 extras/tagmodifier/Makefile                        |   84 +
 extras/tagmodifier/README.txt                      |   42 +
 {src => extras/tagmodifier}/tagmodifier.c          |  254 +--
 src/Makefile                                       |  133 +-
 src/cache.h                                        |  180 ++
 src/errorlog.c                                     |  178 ++
 src/errorlog.h                                     |  195 ++
 src/errorlogx.c                                    |  980 ++++++++++
 src/errorlogx.h                                    |   70 +
 src/fakes.c                                        |   16 +-
 src/filedumper.c                                   |  391 +++-
 src/filedumperx.c                                  |  173 +-
 src/files.c                                        |  429 ++++-
 src/files.h                                        |  130 +-
 src/functions.h                                    |    6 +-
 src/logerror.c                                     |  219 +++
 src/logging.c                                      |  102 +-
 src/logging.h                                      |   35 +-
 src/nodes.c                                        |  151 +-
 src/nodes.h                                        |   35 +-
 src/nodesx.c                                       |  196 +-
 src/nodesx.h                                       |   36 +-
 src/optimiser.c                                    |  733 ++++++--
 src/osmo5mparse.c                                  |  856 +++++++++
 src/osmparser.c                                    | 1123 ++++-------
 src/osmparser.h                                    |   43 +-
 src/osmpbfparse.c                                  | 1234 ++++++++++++
 src/osmxmlparse.c                                  |  703 +++++++
 src/output.c                                       |  188 +-
 src/planetsplitter.c                               |  359 ++--
 src/profiles.c                                     |  443 ++---
 src/prunex.c                                       |  281 +--
 src/queue.c                                        |  113 +-
 src/relations.c                                    |   57 +-
 src/relations.h                                    |   33 +-
 src/relationsx.c                                   |  558 +++---
 src/relationsx.h                                   |   36 +-
 src/results.c                                      |  207 +--
 src/results.h                                      |   29 +-
 src/router.c                                       |  444 +++--
 src/segments.c                                     |   59 +-
 src/segments.h                                     |   35 +-
 src/segmentsx.c                                    |  527 ++----
 src/segmentsx.h                                    |   62 +-
 src/sorting.c                                      |  120 +-
 src/superx.c                                       |  160 +-
 src/tagging.c                                      |  662 ++++---
 src/tagging.h                                      |   52 +-
 src/test/Makefile                                  |   44 +-
 src/test/a-b-c.sh                                  |    7 +-
 src/test/a-b.sh                                    |    7 +-
 src/test/dead-ends.osm                             |    4 +-
 src/test/expected/dead-ends-WP01.txt               |   10 +-
 src/test/expected/dead-ends-WP02.txt               |   14 +-
 src/test/expected/dead-ends-WP03.txt               |   16 +-
 src/test/expected/dead-ends-WP04.txt               |   22 +-
 src/test/expected/dead-ends-WP05.txt               |   12 +-
 src/test/expected/dead-ends-WP06.txt               |   16 +-
 src/test/expected/dead-ends-WP07.txt               |   18 +-
 src/test/expected/dead-ends-WP08.txt               |   12 +-
 src/test/expected/dead-ends-WP09.txt               |   16 +-
 src/test/expected/dead-ends-WP10.txt               |   18 +-
 src/test/expected/dead-ends-WP11.txt               |   24 +-
 src/test/expected/node-restrictions-WP08.txt       |   26 +-
 src/test/is-fast-math.c                            |   20 +
 src/test/node-restrictions.osm                     |    2 +-
 src/test/oneway-loop.osm                           |    2 +-
 src/test/only-split.sh                             |    9 +-
 src/test/prune-short.osm                           |  629 +++++++
 src/test/prune-short.sh                            |    1 +
 src/test/prune-straight.osm                        |  167 ++
 src/test/prune-straight.sh                         |    1 +
 src/test/start-1-finish.sh                         |    7 +-
 src/test/waypoints.pl                              |   32 +-
 src/translations.c                                 |  742 ++++----
 src/types.c                                        |   28 +-
 src/types.h                                        |   65 +-
 src/uncompress.c                                   |  450 +++++
 src/{osmparser.h => uncompress.h}                  |   22 +-
 src/visualiser.c                                   |  211 ++-
 src/visualiser.h                                   |    7 +-
 src/ways.c                                         |   40 +-
 src/ways.h                                         |   50 +-
 src/waysx.c                                        |  411 ++--
 src/waysx.h                                        |   43 +-
 src/xml/Makefile                                   |   24 +-
 src/xml/xsd-to-xmlparser.c                         |  269 ++-
 src/xmlparse.c                                     | 1706 +++++++++++++++++
 src/xmlparse.h                                     |   21 +-
 src/xmlparse.l                                     |  931 ----------
 web/Makefile                                       |  149 ++
 web/data/create.sh                                 |   24 +-
 web/translations/router.html                       |  396 ++++
 web/translations/translate.pl                      |  327 ++++
 web/translations/translation.de.txt                |  204 ++
 web/translations/translation.en.txt                |  390 ++++
 web/translations/translation.fr.txt                |  240 +++
 web/translations/translation.nl.txt                |  206 ++
 web/translations/visualiser.html                   |  424 +++++
 web/www/leaflet/install.sh                         |   11 +
 web/www/openlayers/install.sh                      |    2 +-
 web/www/openlayers/routino.cfg                     |    2 +
 web/www/routino/icons/waypoint-left.png            |  Bin 0 -> 164 bytes
 web/www/routino/icons/waypoint-right.png           |  Bin 0 -> 153 bytes
 web/www/routino/index.html                         |   32 +-
 web/www/routino/maplayout-ie6-bugfixes.css         |   83 -
 web/www/routino/maplayout-ie7-bugfixes.css         |   64 -
 web/www/routino/maplayout.css                      |   12 +-
 web/www/routino/maploader.js                       |   63 +
 web/www/routino/mapprops.js                        |   83 +-
 web/www/routino/page-elements.css                  |   16 +-
 web/www/routino/page-elements.js                   |   17 +-
 web/www/routino/results.cgi                        |   18 +-
 web/www/routino/router.cgi                         |   26 +-
 web/www/routino/router.css                         |   12 +-
 web/www/routino/router.html.de                     |  904 +++++----
 web/www/routino/router.html.en                     |  926 +++++----
 web/www/routino/router.html.fr                     |  493 +++++
 web/www/routino/router.html.nl                     |  927 +++++----
 web/www/routino/{router.js => router.leaflet.js}   |  745 +++++---
 .../routino/{router.js => router.openlayers.js}    |  583 ++++--
 web/www/routino/router.pl                          |   50 +-
 web/www/routino/search.cgi                         |   36 +-
 web/www/routino/search.pl                          |   35 +-
 web/www/routino/statistics.cgi                     |   12 +-
 web/www/routino/update-profiles.pl                 |   14 +-
 web/www/routino/visualiser.cgi                     |  140 +-
 web/www/routino/visualiser.css                     |   18 +-
 web/www/routino/visualiser.html.de                 |  458 +++++
 web/www/routino/visualiser.html.en                 |  788 ++++----
 web/www/routino/visualiser.html.fr                 |  458 +++++
 web/www/routino/visualiser.html.nl                 |  458 +++++
 web/www/routino/visualiser.js                      |  920 ---------
 web/www/routino/visualiser.leaflet.js              | 1226 ++++++++++++
 web/www/routino/visualiser.openlayers.js           | 1309 +++++++++++++
 xml/Makefile                                       |   51 +-
 xml/routino-profiles.xml                           |  134 +-
 xml/routino-tagging.xml                            | 1559 +++++++++-------
 xml/routino-tagging.xsd                            |   31 +-
 xml/routino-translations.xml                       |  157 +-
 xml/routino-translations.xsd                       |    6 +-
 xml/scripts/drive.pl                               |   24 +
 xml/scripts/ride.pl                                |   26 +-
 xml/scripts/walk.pl                                |   26 +-
 187 files changed, 31981 insertions(+), 10762 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index bc9d4f3..d38e42c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,1968 @@
+2014-03-22  Andrew M. Bishop <amb>
+
+	Version 2.7 released.
+
+2014-03-22 [r1529-1530]  Andrew M. Bishop <amb>
+
+	* FILES, doc/NEWS.txt, doc/README.txt, doc/html/readme.html:
+	  Updated for new release.
+
+	* doc/INSTALL.txt: Fix missing text when translated from HTML to
+	  plain text.
+
+2014-03-22 [r1528]  Andrew M. Bishop <amb>
+
+	* doc/Makefile, extras/find-fixme/Makefile,
+	  extras/tagmodifier/Makefile, src/Makefile, src/test/Makefile,
+	  src/xml/Makefile, web/Makefile, xml/Makefile: Another iteration
+	  through the Makefiles with some small changes.
+
+2014-03-18 [r1527]  Andrew M. Bishop <amb>
+
+	* src/profiles.c, web/www/routino/router.leaflet.js,
+	  web/www/routino/router.openlayers.js: Run jshint again and fix
+	  some recently added changes.
+
+2014-03-17 [r1525-1526]  Andrew M. Bishop <amb>
+
+	* src/osmo5mparse.c, src/osmparser.h, src/osmpbfparse.c: Make two
+	  global functions local to the files that contain them.
+
+	* src/errorlog.h, src/tagging.c: Fix some invalid function
+	  comments.
+
+2014-03-15 [r1524]  Andrew M. Bishop <amb>
+
+	* web/translations/translate.pl,
+	  web/translations/translation.de.txt: Store the translations as
+	  UTF-8 in the text files and convert to HTML character entities
+	  when writing the HTML.
+
+2014-03-11 [r1523]  Andrew M. Bishop <amb>
+
+	* Makefile, Makefile.conf, doc/Makefile,
+	  extras/find-fixme/Makefile, extras/tagmodifier/Makefile,
+	  src/Makefile, web/Makefile (added), xml/Makefile: Update the
+	  Makefiles by creating one in the web directory and moving parts
+	  from the src, doc and xml Makefiles into it.
+
+2014-03-11 [r1522]  Andrew M. Bishop <amb>
+
+	* web/www/routino: Ignore the translated HTML files.
+
+2014-03-08 [r1521]  Andrew M. Bishop <amb>
+
+	* web/translations (added), web/translations/router.html (added),
+	  web/translations/translate.pl (added),
+	  web/translations/translation.de.txt (added),
+	  web/translations/translation.en.txt (added),
+	  web/translations/translation.fr.txt (added),
+	  web/translations/translation.nl.txt (added),
+	  web/translations/visualiser.html (added),
+	  web/www/routino/router.html.de (removed),
+	  web/www/routino/router.html.en (removed),
+	  web/www/routino/router.html.fr (removed),
+	  web/www/routino/router.html.nl (removed),
+	  web/www/routino/visualiser.html.en (removed): Create the router
+	  and visualiser HTML files from templates and lists of translated
+	  phrases.
+
+2014-03-08 [r1520]  Andrew M. Bishop <amb>
+
+	* extras/errorlog/summarise-log.pl,
+	  extras/find-fixme/web/www/fixme.cgi,
+	  extras/plot-time/plot-planetsplitter-time.pl,
+	  src/test/waypoints.pl, web/www/routino/results.cgi,
+	  web/www/routino/router.cgi, web/www/routino/router.pl,
+	  web/www/routino/search.cgi, web/www/routino/search.pl,
+	  web/www/routino/statistics.cgi,
+	  web/www/routino/update-profiles.pl,
+	  web/www/routino/visualiser.cgi, xml/scripts/drive.pl,
+	  xml/scripts/ride.pl, xml/scripts/walk.pl: Update all Perl scripts
+	  to "use strict".
+
+2014-03-08 [r1518-1519]  Andrew M. Bishop <amb>
+
+	* src/xmlparse.h: Print the XML line number using the correct
+	  formatting for a uint64_t type.
+
+	* src/files.c: Don't ignore the return value of the write()
+	  function call.
+
+2014-03-01 [r1516]  Andrew M. Bishop <amb>
+
+	* doc/INSTALL.txt, doc/html/installation.html,
+	  extras/find-fixme/web/www/fixme.openlayers.js,
+	  web/www/openlayers/install.sh,
+	  web/www/routino/router.openlayers.js,
+	  web/www/routino/visualiser.openlayers.js: Updated to version
+	  2.13.1 of OpenLayers (also 2.12 still supported).
+
+2014-02-23 [r1515]  Andrew M. Bishop <amb>
+
+	* web/www/routino/icons/waypoint-left.png (added),
+	  web/www/routino/icons/waypoint-right.png (added),
+	  web/www/routino/router.html.de, web/www/routino/router.html.en,
+	  web/www/routino/router.html.fr, web/www/routino/router.html.nl,
+	  web/www/routino/router.leaflet.js,
+	  web/www/routino/router.openlayers.js: Add buttons to allow
+	  increasing/decreasing the transport properties and preferences
+	  (to help on tablet devices where typing a number in a box is
+	  hard).
+
+2014-02-22 [r1514]  Andrew M. Bishop <amb>
+
+	* extras/tagmodifier/README.txt, extras/tagmodifier/tagmodifier.c,
+	  src/uncompress.h, doc/html/installation.html,
+	  doc/html/usage.html, Makefile.conf, src/planetsplitter.c,
+	  src/uncompress.c, doc/USAGE.txt, extras/find-fixme/README.txt,
+	  doc/INSTALL.txt, extras/find-fixme/fixme-finder.c: Add the option
+	  for automatic uncompression of .xz compressed files (not enabled
+	  by default in Makefile.conf).
+
+2014-02-22 [r1513]  Andrew M. Bishop <amb>
+
+	* web/data/create.sh: Downloads from GeoFabrik only and URLs
+	  changed.
+
+2014-02-22 [r1512]  Andrew M. Bishop <amb>
+
+	* doc/INSTALL.txt, doc/html/installation.html: Clarify that gzip
+	  support is required for some PBF files. Clarify that
+	  multi-threading, gzip and bzip2 are enabled by default at
+	  compilation time.
+
+2014-02-02 [r1511]  Andrew M. Bishop <amb>
+
+	* xml/routino-tagging.xml: Add some synonyms for
+	  "cycleway=opposite_lane".
+
+2014-02-02 [r1510]  Andrew M. Bishop <amb>
+
+	* xml/routino-tagging.xml: Handle "access=bus" like "access=psv".
+
+2014-02-01 [r1509]  Andrew M. Bishop <amb>
+
+	* web/www/routino/maploader.js: Ensure that all Javascript files
+	  are loaded before calling the callbacks (better implementation).
+
+2014-02-01 [r1508]  Andrew M. Bishop <amb>
+
+	* web/www/routino/maploader.js: Ensure that all Javascript files
+	  are loaded before calling the callbacks.
+
+2014-02-01 [r1507]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.openlayers.js,
+	  web/www/routino/router.leaflet.js: Allow mouse clicks in the
+	  router result description to popup the details and not just
+	  mouseovers. Hopefully this should work better for touch devices.
+
+2014-01-31 [r1506]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.leaflet.js,
+	  extras/find-fixme/web/www/fixme.openlayers.js: Handle the new
+	  format XML (using ''' instead of ''').
+
+2014-01-31 [r1505]  Andrew M. Bishop <amb>
+
+	* src/xmlparse.c, src/output.c: Output HTML4 strict DTD compliant
+	  HTML (fix bug with using ''' instead of ''').
+
+2014-01-30 [r1504]  Andrew M. Bishop <amb>
+
+	* doc/html/usage.html, doc/USAGE.txt, src/router.c: Add an option
+	  to calculate a circular route.
+
+2014-01-30 [r1503]  Andrew M. Bishop <amb>
+
+	* doc/USAGE.txt, src/router.c, doc/html/usage.html: Add an option
+	  to calculate a route in the reverse order.
+
+2014-01-30 [r1502]  Andrew M. Bishop <amb>
+
+	* src/types.h: Make the type conversion inline functions static so
+	  that compiling with -O0 still works.
+
+2014-01-29 [r1501]  Andrew M. Bishop <amb>
+
+	* src/results.h, src/types.h, src/output.c, src/router.c,
+	  src/results.c: Refactor the code so that the Results data type
+	  has the start and finish waypoints defined within it and the
+	  array passed to the PrintRoute() function doesn't have holes in
+	  it.
+
+2014-01-28 [r1500]  Andrew M. Bishop <amb>
+
+	* src/router.c: Remove ancient option that allowed latitude and
+	  longitude to be specified on the command line without the
+	  --lat<n> or --lon<n> options.
+
+2014-01-28 [r1499]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.leaflet.js,
+	  extras/find-fixme/web/www/fixme.leaflet.js,
+	  web/www/routino/router.openlayers.js,
+	  web/www/routino/visualiser.leaflet.js,
+	  extras/find-fixme/web/www/fixme.openlayers.js,
+	  web/www/routino/visualiser.openlayers.js: Add close buttons to
+	  the popup boxes.
+
+2014-01-27 [r1498]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.html.en, src/profiles.c: Allow
+	  displaying the cyclebothways property in the visualiser.
+
+2014-01-27 [r1497]  Andrew M. Bishop <amb>
+
+	* doc/html/tagging.html, src/types.c, xml/routino-tagging.xml,
+	  src/segments.c, xml/routino-profiles.xml, src/types.h,
+	  doc/TAGGING.txt, src/optimiser.c, src/osmparser.c: Detect the
+	  "cycleway=opposite_lane" tag and allow bicycles to travel in both
+	  directions along those highways. Based on parts of
+	  https://github.com/cgravier/routino-2.6-bikeopposite-elevation/commit/47d68b37f1ea0d2f967ea6aa1555991747491200
+	  https://github.com/cgravier/routino-2.6-bikeopposite-elevation/commit/764832f2671e6f6d8176be65cea3992bd1a488d3
+	  https://github.com/cgravier/routino-2.6-bikeopposite-elevation/commit/37af908880c045309fba1125c4d683f6925f7d25
+	  by Christophe Collard.
+
+2014-01-26 [r1496]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.leaflet.js,
+	  web/www/routino/router.openlayers.js,
+	  web/www/routino/visualiser.leaflet.js,
+	  extras/find-fixme/web/www/fixme.openlayers.js,
+	  web/www/routino/visualiser.openlayers.js,
+	  web/www/routino/router.leaflet.js: Fix bugs in displayStatus()
+	  function after jshint tidy-up.
+
+2014-01-26 [r1495]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.leaflet.js,
+	  extras/find-fixme/web/www/fixme.leaflet.js,
+	  web/www/routino/visualiser.leaflet.js: Fix bug with Leaflet map
+	  startup.
+
+2014-01-26 [r1494]  Andrew M. Bishop <amb>
+
+	* web/www/routino/mapprops.js: Reformat the whitespace.
+
+2014-01-26 [r1493]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.leaflet.js,
+	  extras/find-fixme/web/www/fixme.leaflet.js, src/profiles.c,
+	  web/www/routino/router.openlayers.js,
+	  web/www/routino/visualiser.leaflet.js,
+	  web/www/routino/maploader.js,
+	  extras/find-fixme/web/www/fixme.openlayers.js,
+	  web/www/routino/page-elements.js,
+	  web/www/routino/visualiser.openlayers.js: Run jshint again and
+	  update some more in the JavaScript.
+
+2014-01-25 [r1492]  Andrew M. Bishop <amb>
+
+	* doc/INSTALL.txt, doc/html/readme.html, web/www/leaflet/install.sh
+	  (added), doc/html/installation.html, web/www/leaflet (added),
+	  doc/README.txt: Add a script to download Leaflet Javascript and
+	  update documentation to match.
+
+2014-01-25 [r1491]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.html, extras/find-fixme/Makefile,
+	  web/www/routino/router.html.de,
+	  web/www/routino/router.openlayers.js (added),
+	  web/www/routino/mapprops.js,
+	  web/www/routino/visualiser.leaflet.js (added),
+	  web/www/routino/maploader.js (added),
+	  extras/find-fixme/web/www/fixme.js (removed),
+	  web/www/routino/visualiser.openlayers.js (added),
+	  web/www/routino/router.html.en, web/www/routino/router.html.fr,
+	  web/www/routino/visualiser.html.en,
+	  extras/find-fixme/web/www/fixme.leaflet.js (added),
+	  web/www/routino/router.html.nl, web/www/routino/router.js
+	  (removed), extras/find-fixme/web/www,
+	  extras/find-fixme/web/www/fixme.openlayers.js (added),
+	  web/www/routino/visualiser.js (removed),
+	  web/www/routino/maplayout.css, web/www/routino/router.leaflet.js
+	  (added): Add the option to use Leaflet Javascript library instead
+	  of OpenLayers. Dynamically load the appropriate Javascript
+	  library based on mapprops.js.
+
+2014-01-16 [r1489-1490]  Andrew M. Bishop <amb>
+
+	* src/superx.c: When merging segments and super-segments ensure
+	  that all super-segments are added to the merged list.
+
+	* src/visualiser.c: When searching for nodes for the visualiser
+	  don't exceed the database lat/long limits.
+
+2014-01-13 [r1488]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.html.en, web/www/routino/router.html.fr
+	  (added), web/www/routino/router.html.de,
+	  web/www/routino/router.html.nl, xml/routino-translations.xml:
+	  French language web page and routing translations based on
+	  https://github.com/ocivelo/routino-2.6-bikeopposite-elevation/commit/d426c7ff42e217ca5ace1244afc085c1433843c8
+	  by Christophe Collard.
+
+2014-01-11 [r1487]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.js, web/www/routino/search.pl,
+	  extras/find-fixme/web/www/fixme.js,
+	  web/www/routino/visualiser.js, web/www/routino/search.cgi: Change
+	  the search CGI to use latmin/max and lonmin/max instead of left,
+	  right, top, bottom. Change the Javascript to send the parameters
+	  to 5 decimal places only.
+
+2014-01-11 [r1486]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.js,
+	  web/www/routino/visualiser.js, web/www/routino/router.html.en,
+	  extras/find-fixme/web/www/fixme.html,
+	  web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl, web/www/routino/router.js: Update
+	  the web page URLs as things change rather than using
+	  on(focus|click) action handlers in the HTML that don't work very
+	  well.
+
+2014-01-05 [r1485]  Andrew M. Bishop <amb>
+
+	* web/www/routino/maplayout-ie6-bugfixes.css (removed),
+	  web/www/routino/maplayout-ie7-bugfixes.css (removed),
+	  web/www/routino/router.html.en,
+	  extras/find-fixme/web/www/fixme.html,
+	  web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl: Remove special support for IE6
+	  and IE7 browsers.
+
+2014-01-05 [r1484]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.js, extras/find-fixme/web/www/fixme.js,
+	  web/www/routino/visualiser.js: Replace the unescape() function
+	  with standard decodeURIComponent() function.
+
+2014-01-03 [r1483]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.js, extras/find-fixme/web/www/fixme.js,
+	  web/www/routino/visualiser.js: Run 'jshint' to check for
+	  Javascript errors (fix the important ones).
+
+2014-01-03 [r1480-1482]  Andrew M. Bishop <amb>
+
+	* web/www/routino/page-elements.js: Add a new function to hide and
+	  show sections (not currently used).
+
+	* web/www/routino/page-elements.css: Make the tabs and hide/show
+	  targets a few pixels wider to make them easier to select.
+
+	* web/www/routino/router.html.en, web/www/routino/router.html.de,
+	  web/www/routino/router.html.nl: Squeeze out a bit more space for
+	  the waypoint coords/name.
+
+2014-01-02 [r1479]  Andrew M. Bishop <amb>
+
+	* src/relationsx.h: Fix error in function prototype (argument names
+	  switched).
+
+2013-12-31 [r1477-1478]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.js,
+	  web/www/routino/visualiser.js, web/www/routino/router.js: Update
+	  Javascript to make map tile function more self-contained.
+
+	* extras/find-fixme/web/www/fixme.html,
+	  web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl, web/www/openlayers/routino.cfg,
+	  web/www/routino/router.html.en: Update HTML to make it work
+	  better on mobile devices (no zooming allowed).
+
+2013-12-31 [r1476]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.html.en, web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl: Update HTML ready for HTML5 but
+	  keeping HTML4.01 loose DTD (form action attribute must not be
+	  empty).
+
+2013-12-31 [r1475]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.css, extras/find-fixme/web/www/fixme.html,
+	  web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl, web/www/routino/router.js,
+	  extras/find-fixme/web/www/fixme.css,
+	  web/www/routino/visualiser.css, web/www/routino/router.html.en:
+	  Update HTML ready for HTML5 but keeping HTML4.01 loose DTD (align
+	  attribute and image tags with names are deprecated).
+
+2013-12-31 [r1474]  Andrew M. Bishop <amb>
+
+	* doc/html/readme.html, doc/html/output.html,
+	  doc/html/tagging.html, doc/html/installation.html,
+	  doc/html/limits.html, doc/html/usage.html,
+	  doc/html/algorithm.html, doc/html/configuration.html,
+	  doc/html/index.html, doc/html/data.html: Update HTML ready for
+	  HTML5 but keeping HTML4.01 strict DTD (anchors with names are
+	  deprecated).
+
+2013-12-30 [r1473]  Andrew M. Bishop <amb>
+
+	* doc/html/readme.html, doc/html/data.html: Update some URLs.
+
+2013-12-30 [r1472]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.html.en,
+	  extras/find-fixme/web/www/fixme.html, web/www/routino/index.html,
+	  web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl: Fix some changes that were missed
+	  from the last set of HTML updates.
+
+2013-12-30 [r1471]  Andrew M. Bishop <amb>
+
+	* doc/html/installation.html: Fix links within the page.
+
+2013-12-30 [r1469-1470]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.html, web/www/routino/index.html,
+	  web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl, web/www/routino/router.html.en:
+	  Convert map pages to use lower case HTML tags and fix some HTML
+	  4.01 strict DTD errors (but not converted to strict DTD due to
+	  use of target attribute on <a> tags).
+
+	* doc/html/style.css, doc/html/usage.html, doc/html/algorithm.html,
+	  doc/html/configuration.html, doc/html/index.html,
+	  doc/html/data.html, extras/find-fixme/web/www/index.html,
+	  doc/html/readme.html, doc/html/output.html,
+	  doc/html/tagging.html, doc/html/installation.html,
+	  doc/html/limits.html: Convert documentation to HTML 4.01 strict
+	  DTD (from loose DTD).
+
+2013-11-17 [r1468]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.cgi: Fix bug that did not allow more than
+	  9 waypoints to be routed.
+
+2013-11-13 [r1467]  Andrew M. Bishop <amb>
+
+	* src/osmparser.c: Refactor the length, weight and speed parsing
+	  functions a little bit and add some new formats.
+
+2013-09-07 [r1466]  Andrew M. Bishop <amb>
+
+	* xml/routino-tagging.xml: Add the same "access=foot" aliasing to
+	  nodes as previously existed for ways.
+
+2013-08-02 [r1465]  Andrew M. Bishop <amb>
+
+	* doc/html/usage.html, doc/USAGE.txt, src/output.c, src/router.c:
+	  Add a '--output-stdout' option.
+
+2013-07-31 [r1464]  Andrew M. Bishop <amb>
+
+	* xml/routino-translations.xml: Updated Dutch translations.
+
+2013-07-18 [r1463]  Andrew M. Bishop <amb>
+
+	* src/types.h: Replace the macros for type conversion with inline
+	  functions where there is a risk of overflow for normal data.
+
+2013-07-14 [r1462]  Andrew M. Bishop <amb>
+
+	* src/optimiser.c: Removal of some code intended to speed things up
+	  but that actually slows things down.
+
+2013-07-12 [r1461]  Andrew M. Bishop <amb>
+
+	* doc/html/algorithm.html, doc/ALGORITHM.txt: Update the algorithm
+	  documentation with a description of the algorithm used for
+	  finding the shortest path.
+
+2013-07-10 [r1460]  Andrew M. Bishop <amb>
+
+	* Makefile.conf: Add an option to enable debugging symbols.
+
+2013-07-08 [r1459]  Andrew M. Bishop <amb>
+
+	* doc/USAGE.txt: Fix typo in documentation string.
+
+2013-07-08 [r1458]  Andrew M. Bishop <amb>
+
+	* doc/html/usage.html, src/filedumper.c: Fix typo in documentation
+	  string.
+
+2013-07-06  Andrew M. Bishop <amb>
+
+	Version 2.6 released.
+
+2013-07-06 [r1455-1456]  Andrew M. Bishop <amb>
+
+	* FILES: Add some new files for version 2.6.
+
+	* doc/Makefile, extras/Makefile, src/test/Makefile, src/Makefile,
+	  extras/find-fixme/Makefile, Makefile,
+	  extras/tagmodifier/Makefile, xml/Makefile: Update some Makefiles
+	  for running 'make test' from a clean set of source code.
+
+2013-07-06 [r1454]  Andrew M. Bishop <amb>
+
+	* doc/NEWS.txt, doc/README.txt, FILES, doc/html/readme.html: Update
+	  for version 2.6 release.
+
+2013-07-04 [r1453]  Andrew M. Bishop <amb>
+
+	* doc/html/installation.html, doc/INSTALL.txt: Added a 'quick
+	  start' set of installation instructions.
+
+2013-07-04 [r1452]  Andrew M. Bishop <amb>
+
+	* web/data/create.sh: Fix error with URL.
+
+2013-07-03 [r1451]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c: Revert r1268 which was applied between v2.5
+	  and v2.5.1 and could be the cause of the slight slowdown in
+	  version 2.5.1.
+
+2013-07-02 [r1450]  Andrew M. Bishop <amb>
+
+	* src/relationsx.c, src/errorlog.c, src/logerror.c,
+	  src/optimiser.c, src/waysx.c, src/xmlparse.c: Fix some comments.
+
+2013-07-02 [r1449]  Andrew M. Bishop <amb>
+
+	* src/logging.c: Fix bug in time printed by --logtime option.
+
+2013-07-02 [r1448]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c, src/prunex.c, src/segmentsx.h: Revert r1432
+	  because even though it seemed like it should have been faster in
+	  slim mode it was actually much slower on the routino.org virtual
+	  server.
+
+2013-07-01 [r1447]  Andrew M. Bishop <amb>
+
+	* src/output.c: Small optimisation for calling GetLatLong().
+
+2013-07-01 [r1446]  Andrew M. Bishop <amb>
+
+	* web/data/create.sh: Fix the URLs in the example download script.
+
+2013-07-01 [r1445]  Andrew M. Bishop <amb>
+
+	* src/xmlparse.c, src/xmlparse.h: Forcing the xmlparse tags to be
+	  lower case adds a further speed-up (and was implicitly assumed
+	  already in other code).
+
+2013-07-01 [r1444]  Andrew M. Bishop <amb>
+
+	* src/results.h, src/superx.c, src/results.c, src/queue.c: Re-use
+	  the Queue and Results data structure for all routes - saves a
+	  huge number of malloc/free calls (found by valgrind/callgrind).
+
+2013-06-29 [r1443]  Andrew M. Bishop <amb>
+
+	* src/xmlparse.c: Optimise out most calls to strcasecmp the
+	  most-called C library functions (found by valgrind/callgrind).
+
+2013-06-29 [r1442]  Andrew M. Bishop <amb>
+
+	* src/prunex.c: Close file and free memory (found by valgrind).
+
+2013-06-29 [r1441]  Andrew M. Bishop <amb>
+
+	* src/sorting.c: Free allocated memory (found by valgrind).
+
+2013-06-29 [r1440]  Andrew M. Bishop <amb>
+
+	* src/logerror.c: Avoid writing uninitialised to disk (found by
+	  valgrind).
+
+2013-06-29 [r1439]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.js,
+	  extras/find-fixme/web/www/fixme.html: Update highlight size and
+	  help text as was done with visualiser.
+
+2013-06-29 [r1438]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.html.en, web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl: Add some help to the visualiser
+	  web page and slightly reformatted the help on the router web
+	  pages.
+
+2013-06-29 [r1435-1437]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.js: Increase the size of the nodes in
+	  the super-nodes/segments display to make them clickable.
+
+	* web/www/routino/visualiser.cgi: Increase the maximum size that
+	  will be returned by CGI.
+
+	* web/www/routino/visualiser.js: Fix bug with visualiser failing to
+	  run failure callback in case of CGI error.
+
+2013-06-29 [r1433-1434]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.js: Make the node highlights smaller
+	  (to match the segment highlight width).
+
+	* src/filedumper.c: Print the fake XML for the visualiser with
+	  special routino:* top-level tag names.
+
+2013-06-28 [r1431-1432]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c, src/prunex.c, src/segmentsx.h: Keep the next2
+	  pointer in memory while pruning rather than in the segmentx
+	  object.
+
+	* src/prunex.h, src/planetsplitter.c, src/segmentsx.c,
+	  src/prunex.c, src/segmentsx.h, doc/DATALIFE.txt: Revert the last
+	  three changes because r1430 didn't work and r1428+r1429 didn't
+	  give any speed advantage and was possibly marginally slower.
+
+2013-06-28 [r1430]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c: The IndexSegments() function can now process the
+	  file in forward order rather than reverse.
+
+2013-06-27 [r1429]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c: Fixed error with last checkin.
+
+2013-06-27 [r1428]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c, src/prunex.c, src/segmentsx.h, doc/DATALIFE.txt,
+	  src/prunex.h, src/planetsplitter.c: Put the next1 pointer in the
+	  segmentx object rather than in-memory.
+
+2013-06-27 [r1427]  Andrew M. Bishop <amb>
+
+	* doc/DATALIFE.txt, src/relationsx.c, src/planetsplitter.c,
+	  src/waysx.c, src/relationsx.h, src/segmentsx.c, src/nodesx.c,
+	  src/segmentsx.h: Revert the last change because, paradoxically,
+	  it was faster to create the database (as expected) but slower to
+	  route.
+
+2013-06-26 [r1426]  Andrew M. Bishop <amb>
+
+	* doc/DATALIFE.txt, src/relationsx.c, src/planetsplitter.c,
+	  src/waysx.c, src/relationsx.h, src/segmentsx.c, src/nodesx.c,
+	  src/segmentsx.h: Sort the nodes geographically at the beginning
+	  rather than at the end.
+
+2013-06-26 [r1425]  Andrew M. Bishop <amb>
+
+	* doc/html/installation.html, doc/INSTALL.txt: Add some more
+	  details about configuring Apache.
+
+2013-06-25 [r1424]  Andrew M. Bishop <amb>
+
+	* src/relationsx.c, src/planetsplitter.c, src/nodesx.c,
+	  doc/DATALIFE.txt: Remove one usage of the nodesx->gdata array.
+	  Remove one call of the IndexSegments() function. Update the
+	  DATALIFE.txt file with both and the previous changes to mapped
+	  files and add a new column for segmentx->next2.
+
+2013-06-25 [r1423]  Andrew M. Bishop <amb>
+
+	* src/superx.c: Change the ChooseSuperNodes() and
+	  MergeSuperSegments() functions to read through the file instead
+	  of mapping the data to help slim mode (since the nodes in the
+	  first case and segments in the second case are read
+	  sequentially).
+
+2013-06-24 [r1422]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c: Change the IndexSegments() function so that in
+	  slim mode larger chunks are read from the file (since it reads
+	  sequentially but in reverse through the file).
+
+2013-06-24 [r1420-1421]  Andrew M. Bishop <amb>
+
+	* src/files.c: Increase the buffer to 4kB from 1kB.
+
+	* src/osmo5mparse.c, src/waysx.c, src/nodesx.c, src/osmpbfparse.c,
+	  src/prunex.c, src/superx.c, src/relationsx.c: Move some
+	  printf_first() messages to the front of the function and
+	  printf_last() function to the end of the function.
+
+2013-06-23 [r1419]  Andrew M. Bishop <amb>
+
+	* src/ways.h: Fix error in slim mode WayName() function.
+
+2013-06-22 [r1412-1418]  Andrew M. Bishop <amb>
+
+	* src/files.c, src/files.h: Make the changes required to handle the
+	  new function names from the last few checkins.
+
+	* src/prunex.c: Use SlimMapFile(), SlimUnmapFile(), SlimFetch() and
+	  SlimReplace() [see earlier log message] and also refactor the
+	  code so that the additional ways are placed in a separate file
+	  using the Slim*() functions rather than being appended to the
+	  open (or mapped) way file.
+
+	* src/ways.h: Use SlimFetch() instead of SeekReadFileUnbuffered()
+	  [see previous log message] and also refactor the code for reading
+	  the way name from file.
+
+	* src/errorlog.h, src/relations.c, src/ways.c, src/segments.c,
+	  src/cache.h, src/nodes.c, src/errorlog.c: Use SlimMapFile() and
+	  SlimUnmapFile() [see previous log message] and also use
+	  SlimFetch() instead of SeekReadFileUnbuffered() and SlimReplace()
+	  instead of SeekWriteFileUnbuffered() to hide the internals of the
+	  slim mode.
+
+	* src/superx.c, src/relationsx.c, src/waysx.c, src/segmentsx.c,
+	  src/errorlogx.c: Use SlimMapFile() and SlimUnmapFile() as the
+	  function names for the slim mode of operation to open and close
+	  the files (to hide the fact that they are files being opened for
+	  reading or writing unbuffered).
+
+	* src/errorlogx.c: Use buffered file accesses for appending the
+	  error strings to the binary file.
+
+	* src/planetsplitter.c, src/tagging.c, src/translations.c,
+	  extras/find-fixme/fixme-finder.c,
+	  extras/tagmodifier/tagmodifier.c, src/profiles.c: Create simple
+	  OpenFile() and CloseFile() functions for those files which are
+	  used by the parsers (they just call open() and close()
+	  internally).
+
+2013-06-22 [r1411]  Andrew M. Bishop <amb>
+
+	* src/logerror.c: Fix for closing binary error file that only shows
+	  up after the recent file function changes.
+
+2013-06-21 [r1410]  Andrew M. Bishop <amb>
+
+	* src/files.c, extras/tagmodifier/tagmodifier.c, src/files.h,
+	  src/cache.h, src/nodes.c, src/planetsplitter.c, src/waysx.c,
+	  src/segmentsx.c, src/tagging.c, src/prunex.c, src/translations.c,
+	  extras/find-fixme/fixme-finder.c, src/errorlogx.c,
+	  src/profiles.c, src/ways.c, src/filedumperx.c, src/segments.c,
+	  src/superx.c, src/ways.h, src/relationsx.c, src/errorlog.c,
+	  src/errorlog.h, src/relations.c: Rename the functions for
+	  unbuffered file access to make this clear.
+
+2013-06-21 [r1409]  Andrew M. Bishop <amb>
+
+	* src/waysx.c, src/segmentsx.c, src/nodesx.c, src/errorlogx.c,
+	  src/sorting.c, src/relationsx.c: Use the new buffered functions
+	  in the filesort functions.
+
+2013-06-21 [r1407-1408]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c, src/nodesx.c, src/prunex.c, src/errorlogx.c,
+	  src/filedumperx.c, src/relationsx.c, src/logerror.c, src/waysx.c:
+	  Use the new functions for buffering while reading when looping
+	  through files other than the ones already done that use the
+	  FILESORT_VARINT method of storing data.
+
+	* src/files.c, src/files.h: Add a new function for seeking in one
+	  of the buffered files.
+
+2013-06-20 [r1406]  Andrew M. Bishop <amb>
+
+	* src/errorlogx.c, src/files.h, src/relationsx.c, src/waysx.c,
+	  src/nodesx.c, src/files.c: Rename the function that skips forward
+	  in a buffered file to avoid confusion.
+
+2013-06-20 [r1404-1405]  Andrew M. Bishop <amb>
+
+	* src/relationsx.c, src/waysx.c, src/nodesx.c, src/errorlogx.c,
+	  src/filedumperx.c: Use the new functions for buffering while
+	  reading when looping through files that use the FILESORT_VARINT
+	  method of storing data.
+
+	* src/files.c, src/files.h: Add new functions for buffering when
+	  reading and also allow seeking in one of these buffered files.
+
+2013-06-20 [r1403]  Andrew M. Bishop <amb>
+
+	* src/files.c: Fix the non-buffered close function assertion and
+	  re-factor the code for buffered reading (although not used yet).
+
+2013-06-19 [r1401-1402]  Andrew M. Bishop <amb>
+
+	* src/nodesx.c, src/relationsx.c, src/waysx.c, src/segmentsx.c: Use
+	  the buffered write functions when creating the nodes, segments,
+	  ways and relation raw files.
+
+	* src/files.c, src/files.h: Add functions to read and write file
+	  descriptors with buffering.
+
+2013-06-19 [r1400]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/fixme-finder.c, src/logging.c,
+	  src/planetsplitter.c: Bug fix for the change to the --logtime
+	  option functions.
+
+2013-06-19 [r1399]  Andrew M. Bishop <amb>
+
+	* src/files.h: Split the function prototypes into two groups for
+	  the ones in files.c and the inline ones in files.h.
+
+2013-06-17 [r1398]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/fixme-finder.c, src/logging.c,
+	  src/planetsplitter.c, src/logging.h: Change the --logtime
+	  internals so that programs don't need to store their start time
+	  themselves.
+
+2013-06-15 [r1397]  Andrew M. Bishop <amb>
+
+	* xml/routino-tagging.xml: Add some more barriers which are too
+	  generic to warn about and fix spelling of motorcycle_barrier.
+
+2013-06-13 [r1396]  Andrew M. Bishop <amb>
+
+	* doc/html/usage.html, doc/USAGE.txt: Document that --errorlog and
+	  --keep together will create the binary database of errors.
+
+2013-06-13 [r1395]  Andrew M. Bishop <amb>
+
+	* src/errorlogx.c, src/logerror.c, src/logging.h: Separate out the
+	  type and id parts of the errorlogobject.
+
+2013-06-12 [r1394]  Andrew M. Bishop <amb>
+
+	* web/www/routino/mapprops.js, doc/html/installation.html,
+	  extras/find-fixme/web/www/fixme.js,
+	  web/www/routino/visualiser.js, doc/INSTALL.txt: Add links to the
+	  OpenStreetMap browse URLs from the error logs.
+
+2013-06-11 [r1393]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/web/www/fixme.js,
+	  extras/find-fixme/web/www/fixme.cgi,
+	  extras/find-fixme/web/www/fixme.html: Put the data tab back and
+	  display the statistics in it.
+
+2013-06-10 [r1391-1392]  Andrew M. Bishop <amb>
+
+	* src/errorlogx.c: Fix typo in program logging.
+
+	* doc/html/usage.html, doc/USAGE.txt: Fix typo in documentation.
+
+2013-06-06 [r1387-1390]  Andrew M. Bishop <amb>
+
+	* src/Makefile: Tidy up the compilation and linking flags again.
+
+	* Makefile.conf: Tidy up the compilation and linking flags again.
+
+	* src/test/Makefile, src/test/is-fast-math.c (added), src/test,
+	  src/test/start-1-finish.sh, src/test/a-b-c.sh, src/test/a-b.sh:
+	  When compiled with -ffast-math don't expect the results to match
+	  exactly, display the differences.
+
+	* src/test/only-split.sh: Remove a debugging message.
+
+2013-06-06 [r1386]  Andrew M. Bishop <amb>
+
+	* src/test/expected/dead-ends-WP08.txt,
+	  src/test/expected/dead-ends-WP09.txt, src/test/dead-ends.osm,
+	  src/test/expected/node-restrictions-WP08.txt,
+	  src/test/expected/dead-ends-WP01.txt,
+	  src/test/expected/dead-ends-WP10.txt,
+	  src/test/expected/dead-ends-WP02.txt,
+	  src/test/expected/dead-ends-WP11.txt,
+	  src/test/expected/dead-ends-WP03.txt,
+	  src/test/expected/dead-ends-WP04.txt,
+	  src/test/expected/dead-ends-WP05.txt,
+	  src/test/expected/dead-ends-WP06.txt,
+	  src/test/expected/dead-ends-WP07.txt: Change test cases so that
+	  they don't show differences due to pruning or the last checkin
+	  for divide by zero which changed behaviour in trivial case.
+
+2013-06-06 [r1385]  Andrew M. Bishop <amb>
+
+	* src/prunex.c, src/nodes.c, src/fakes.c: Fix some code that could,
+	  potentially, have given a divide by zero and which therefore
+	  behaves differently with FPU and -ffast-math compilation options.
+
+2013-06-06 [r1384]  Andrew M. Bishop <amb>
+
+	* src/translations.c: Fix bug if using the built-in translations
+	  for GPX route files (present since v2.0). Make a few other
+	  translations match the ones in the XML file.
+
+2013-06-05 [r1383]  Andrew M. Bishop <amb>
+
+	* doc/DATALIFE.txt, src/nodesx.c: Update the node id just before
+	  sorting geographically rather than relying on the pruning process
+	  to do it.
+
+2013-06-05 [r1381-1382]  Andrew M. Bishop <amb>
+
+	* web/www/routino/index.html, doc/Makefile: Copy the style.css into
+	  the web page documentation directory, fix the link to it from
+	  index.html.
+
+	* extras/find-fixme/Makefile, extras/find-fixme/web/www,
+	  extras/find-fixme/web/www/paths.pl,
+	  extras/find-fixme/web/www/index.html (added),
+	  extras/find-fixme/web/www/fixme.html: Add an index.html page to
+	  redirect to fixme.html, copy in a style.css file, slightly change
+	  the web page titles.
+
+2013-06-04 [r1380]  Andrew M. Bishop <amb>
+
+	* src/waysx.c, src/waysx.h, src/prunex.c: Remember what types of
+	  transports we have so that we don't spend time pruning for
+	  transport types we don't have.
+
+2013-06-04 [r1379]  Andrew M. Bishop <amb>
+
+	* src/tagging.c: Unconditionally execute the tagging rules within
+	  '<if> ... </if>' even if there are no input tags to match against
+	  the match-all rule.
+
+2013-06-03 [r1377-1378]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/fixme-dumper.c, extras/find-fixme/README.txt:
+	  Reinstate the --statistics option.
+
+	* extras/find-fixme/web/www/fixme.js: Nicely format the item tag
+	  information.
+
+2013-06-03 [r1374-1376]  Andrew M. Bishop <amb>
+
+	* extras/find-fixme/fixme.xml, extras/find-fixme/README.txt: Make
+	  it easier to select arbitrary tags and store them.
+
+	* extras/find-fixme/osmparser.c: Store a whole XML-like item in the
+	  log file.
+
+	* src/tagging.c, src/tagging.h: Add a new function to convert a
+	  TagList into an HTML-like string.
+
+2013-06-03 [r1373]  Andrew M. Bishop <amb>
+
+	* Makefile.conf: Use the -ffast-math compilation option (trivial
+	  changes to estimated journey times and distances).
+
+2013-06-03 [r1371-1372]  Andrew M. Bishop <amb>
+
+	* web/bin: Don't ignore tagmodifier (not put here any more).
+
+	* extras/find-fixme/Makefile, extras/tagmodifier/Makefile: Compile
+	  all of src directory before starting here.
+
+2013-06-03 [r1370]  Andrew M. Bishop <amb>
+
+	* src/osmparser.c, src/relationsx.h, extras/find-fixme/osmparser.c,
+	  src/errorlogx.c, src/relationsx.c: Write relation nodes into the
+	  file as well as ways and relations.
+
+2013-06-01 [r1367-1369]  Andrew M. Bishop <amb>
+
+	* extras/errorlog/README.txt: Make the README files more consistent
+	  with each other in style.
+
+	* extras/README.txt, extras/plot-time/README.txt,
+	  extras/tagmodifier/README.txt: Make the README files more
+	  consistent with each other in style.
+
+	* extras/find-fixme/fixme-dumper.c (added),
+	  extras/find-fixme/web/www/fixme.js (added),
+	  extras/find-fixme/web/bin (added), extras/find-fixme/web/data
+	  (added), extras/find-fixme/web/www/paths.pl (added),
+	  extras/find-fixme/osmparser.c (added),
+	  extras/find-fixme/fixme-finder.c (added),
+	  extras/find-fixme/web/www/fixme.css (added), extras/find-fixme
+	  (added), extras/find-fixme/web/www (added),
+	  extras/find-fixme/fixme.xml (added),
+	  extras/find-fixme/web/www/fixme.cgi (added),
+	  extras/find-fixme/README.txt (added),
+	  extras/find-fixme/web/www/fixme.html (added),
+	  extras/find-fixme/web (added), extras/find-fixme/Makefile
+	  (added): A tool to search an OSM file for "fixme" tags to create
+	  a database and display them on an interactive map.
+
+2013-06-01 [r1366]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c: Fix minor error with usage message.
+
+2013-06-01 [r1363-1365]  Andrew M. Bishop <amb>
+
+	* extras/tagmodifier/README.txt, extras/tagmodifier/Makefile:
+	  Compile all main Routino files before trying to build
+	  tagmodifier. Small changes to the README.txt file.
+
+	* src/Makefile: Use the WEBBINDIR variable rather than a hard-coded
+	  path.
+
+	* doc/Makefile: Make the Makefile clearer with respect to the
+	  copying of HTML files to the web directory.
+
+2013-06-01 [r1361-1362]  Andrew M. Bishop <amb>
+
+	* src/errorlogx.h, src/planetsplitter.c, src/errorlogx.c: Make the
+	  functions for processing error logs more like the ones for nodes,
+	  ways and relations.
+
+	* src/waysx.c, src/nodesx.c, src/relationsx.c: Store the number of
+	  kept nodes, ways and relations when first sorted.
+
+2013-06-01 [r1360]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.cgi: Return an error if neither type
+	  of recognised request was made.
+
+2013-05-31 [r1359]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c, src/router.c: Use exit(EXIT_FAILURE)
+	  instead of return(1).
+
+2013-05-31 [r1357-1358]  Andrew M. Bishop <amb>
+
+	* extras/README.txt, extras/plot-time/README.txt (added),
+	  extras/plot-time/plot-planetsplitter-time.pl (added),
+	  extras/plot-time (added): Add a Perl script that allows plotting
+	  a graph of the time taken to run planetsplitter.
+
+	* extras/errorlog/README.txt (added): Add a README file for the
+	  summarise-log.pl script.
+
+2013-05-31 [r1356]  Andrew M. Bishop <amb>
+
+	* src/osmo5mparse.c, src/osmxmlparse.c, src/osmparser.c,
+	  src/osmparser.h, src/osmpbfparse.c: Move some functions about so
+	  that osmparser.c can be replaced for other types of parsing.
+
+2013-05-31 [r1355]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c, src/planetsplitter.c, src/waysx.c: Small changes
+	  to comments and log messages.
+
+2013-05-30 [r1354]  Andrew M. Bishop <amb>
+
+	* src/segmentsx.c: Don't de-duplicate when sorting segments (now
+	  done in ProcessSegments() function).
+
+2013-05-30 [r1353]  Andrew M. Bishop <amb>
+
+	* src/relationsx.c, src/planetsplitter.c, src/relationsx.h,
+	  doc/DATALIFE.txt: Merge the ProcessTurnRelations1() and
+	  ProcessTurnRelations2() functions into a single one now that
+	  segment processing is already complete.
+
+2013-05-30 [r1352]  Andrew M. Bishop <amb>
+
+	* doc/DATALIFE.txt, src/planetsplitter.c: Move the first
+	  IndexSegments() function call earlier in the sequence.
+
+2013-05-30 [r1351]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h,
+	  doc/DATALIFE.txt: Merge the RemoveBadSegments() and
+	  MeasureSegments() functions. Saves one read/write iteration
+	  through the segments file.
+
+2013-05-30 [r1350]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c, src/segmentsx.c, src/nodesx.c,
+	  src/segmentsx.h, doc/DATALIFE.txt, src/nodesx.h,
+	  src/relationsx.c: Delete the non-highway nodes by searching for
+	  them in the ways rather than marking them when processing the
+	  segments.
+
+2013-05-29 [r1349]  Andrew M. Bishop <amb>
+
+	* doc/DATALIFE.txt, extras/errorlog/summarise-log.pl,
+	  src/relationsx.c, src/planetsplitter.c, src/waysx.c,
+	  src/segmentsx.c, src/nodesx.c, src/waysx.h, src/segmentsx.h:
+	  Simplify the segments by using the node and way index instead of
+	  node and way id which avoids lots of IndexNodeX() lookups. Move
+	  some other code around to cope with it.
+
+2013-05-27 [r1348]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c, src/waysx.c, src/waysx.h, doc/DATALIFE.txt:
+	  Simplify the sorting of the way names (only sort the names, not
+	  all the ways twice), merge the generating of segments and
+	  separation of way names.
+
+2013-05-27 [r1347]  Andrew M. Bishop <amb>
+
+	* src/osmparser.c, src/waysx.c, src/segmentsx.c: Redistributed
+	  error log messages from osmparser way handling.
+
+2013-05-26 [r1346]  Andrew M. Bishop <amb>
+
+	* extras/errorlog/summarise-log.pl (added), extras/errorlog
+	  (added), extras/README.txt, web/bin/summarise-log.pl (removed):
+	  Move the summarise-log.pl script into the extras directory.
+
+2013-05-26 [r1345]  Andrew M. Bishop <amb>
+
+	* extras/tagmodifier/README.txt (added),
+	  extras/tagmodifier/tagmodifier.c (added),
+	  extras/tagmodifier/Makefile (added), doc/html/installation.html,
+	  extras/tagmodifier (added), doc/html/usage.html,
+	  extras/README.txt, src/tagmodifier.c (removed), src/Makefile,
+	  src, doc/USAGE.txt, doc/INSTALL.txt: Move the tagmodifier program
+	  into the extras directory.
+
+2013-05-26 [r1344]  Andrew M. Bishop <amb>
+
+	* extras (added), extras/README.txt (added), extras/Makefile
+	  (added): Create a new directory for extra (non-essential)
+	  programs
+
+2013-05-26 [r1343]  Andrew M. Bishop <amb>
+
+	* doc/Makefile, src/test/Makefile, src/Makefile, src/xml/Makefile,
+	  doc/INSTALL.txt, Makefile, doc/html/installation.html,
+	  xml/Makefile, Makefile.conf (added): Re-organise the Makefiles so
+	  that all configuration information is one of them (Makefile.conf
+	  at the top level) rather than being spread between them.
+
+2013-05-25 [r1342]  Andrew M. Bishop <amb>
+
+	* src/errorlogx.c, src/relationsx.c, src/relationsx.h: Rename the
+	  route relation structure parameters (r* -> rr*) to match the turn
+	  relation parameter names (tr*).
+
+2013-05-25 [r1341]  Andrew M. Bishop <amb>
+
+	* src/errorlogx.c, src/relationsx.c, src/relationsx.h: Finished
+	  allocating coordinates to error logs, nodes in preference to ways
+	  in preference to relations.
+
+2013-05-23 [r1340]  Andrew M. Bishop <amb>
+
+	* doc/USAGE.txt, src/filedumperx.c, doc/html/usage.html: There are
+	  no segments stored after parsing so cannot be dumper by
+	  filedumperx.
+
+2013-05-23 [r1339]  Andrew M. Bishop <amb>
+
+	* src/superx.c, src/types.h, src/planetsplitter.c, src/osmparser.c,
+	  src/waysx.c, src/segmentsx.c, src/osmparser.h, src/waysx.h,
+	  src/segmentsx.h, doc/DATALIFE.txt, src/types.c: Don't create
+	  segments when parsing input file but create the segments later
+	  using the nodes stored in the ways file. This makes applying
+	  changes simpler (segments file is not kept with the --keep
+	  option) and handling changed ways is simpler than changed
+	  segments.
+
+2013-05-22 [r1338]  Andrew M. Bishop <amb>
+
+	* src/osmparser.c, src/waysx.c, src/waysx.h, src/errorlogx.c,
+	  src/filedumperx.c: Store the list of nodes in the raw ways file
+	  for use by the errorlog functions.
+
+2013-05-21 [r1337]  Andrew M. Bishop <amb>
+
+	* src/sorting.c: Don't waste memory in filesort_vary() when the
+	  pre-sort function drops the data.
+
+2013-05-20 [r1336]  Andrew M. Bishop <amb>
+
+	* src/osmparser.c: Store more logerror information even for items
+	  that are discarded to allow them to be located geographically.
+
+2013-05-20 [r1335]  Andrew M. Bishop <amb>
+
+	* src/xml/Makefile: Update CLFLAGS and LDFLAGS.
+
+2013-05-18 [r1334]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.html.en, doc/INSTALL.txt,
+	  web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl, web/www/routino/router.js,
+	  web/www/routino/mapprops.js, doc/html/installation.html,
+	  web/www/routino/visualiser.js: Make the OSM editor URL be
+	  configurable in the mapprops.js file.
+
+2013-05-18 [r1333]  Andrew M. Bishop <amb>
+
+	* web/www/openlayers/routino.cfg: Need to include
+	  OpenLayers/Control/SelectFeature.js when building OpenLayers.js.
+
+2013-05-18 [r1332]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.css, web/www/routino/visualiser.js:
+	  Put the popup colours in the Javascript, not CSS. Set the popup
+	  font (fixed) and size (smaller).
+
+2013-05-18 [r1330-1331]  Andrew M. Bishop <amb>
+
+	* src/visualiser.c: Remove unused header file.
+
+	* src/filedumper.c, doc/USAGE.txt, web/www/routino/visualiser.cgi,
+	  doc/html/usage.html: Create specific HTML formatted output from
+	  filedumper for the visualiser web page.
+
+2013-05-18 [r1329]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.cgi, src/visualiser.c,
+	  web/www/routino/visualiser.js: Add the ability to select any item
+	  displayed in the visualiser and display information about it in a
+	  popup.
+
+2013-05-18 [r1327-1328]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.js, web/www/routino/visualiser.js: Don't
+	  display the marker layers in the layer switcher control.
+
+	* web/www/routino/visualiser.js: Store the feature attributes using
+	  the feature.attributes object.
+
+2013-05-18 [r1326]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.js, web/www/routino/visualiser.css:
+	  Change to using a popup like in the router and highlight the
+	  selected item when the popup is displayed.
+
+2013-05-17 [r1324-1325]  Andrew M. Bishop <amb>
+
+	* web/www/routino/visualiser.html.en: Selecting the radio buttons
+	  updates the display.
+
+	* web/www/routino/router.html.de, web/www/routino/router.html.nl,
+	  web/www/routino/router.html.en: Added some missing ';' to the
+	  Javascript actions in the HTML.
+
+2013-05-17 [r1323]  Andrew M. Bishop <amb>
+
+	* doc/html/usage.html, doc/USAGE.txt: Add the documentation for the
+	  change to the filedumper program.
+
+2013-05-17 [r1322]  Andrew M. Bishop <amb>
+
+	* src/visualiser.c: Limit the error log outputs to the geographical
+	  ones and make the strings HTML safe.
+
+2013-05-17 [r1321]  Andrew M. Bishop <amb>
+
+	* src/errorlog.h, web/www/routino/visualiser.html.en,
+	  src/errorlogx.c, src/visualiser.c, web/www/routino/visualiser.js,
+	  src/planetsplitter.c, src/errorlog.c, src/visualiser.h,
+	  src/Makefile, src/filedumper.c, web/www/routino/visualiser.cgi:
+	  Allow dumping error logs from filedumper.
+
+2013-05-14 [r1320]  Andrew M. Bishop <amb>
+
+	* src/errorlogx.c, src/errorlogx.h, src/errorlog.c (added),
+	  src/errorlog.h (added): Copy errorlogx.h to errorlog.h and create
+	  errorlog.c so that they mirror the nodes.h and nodes.c filenames.
+	  Add functions to read in a set of error logs from a file.
+
+2013-05-13 [r1319]  Andrew M. Bishop <amb>
+
+	* src/logerror.c, src/Makefile, src/logerror.h (removed),
+	  src/errorlogx.c (added), src/logerrorx.c (removed),
+	  src/errorlogx.h (added), src/planetsplitter.c: Rename logerrorx.c
+	  to errorlogx.c and logerrorx.h to errorlogx.h so that they mirror
+	  the nodesx.c and nodesx.h filenames.
+
+2013-05-13 [r1318]  Andrew M. Bishop <amb>
+
+	* src/tagmodifier.c, src/logging.h, src/osmparser.c,
+	  src/segmentsx.c, src/logerror.h, src/tagging.c, src/relationsx.c:
+	  Move the logerror function prototypes back into logging.h and
+	  remove the logerror.h header file from most source files again.
+
+2013-05-12 [r1317]  Andrew M. Bishop <amb>
+
+	* src/logerror.c, src/waysx.c, src/Makefile, src/relationsx.h,
+	  src/segmentsx.c, src/logerror.h, src/nodesx.c, src/waysx.h,
+	  src/segmentsx.h, src/logerrorx.c (added), src/nodesx.h,
+	  src/relationsx.c, src/planetsplitter.c: Add functions to process
+	  the binary error log file and convert it into a geographically
+	  searchable database.
+
+2013-05-11 [r1314]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c: Rename the Nodes, Segments, Ways and
+	  Relations variables to avoid confusion with types of the same
+	  name.
+
+2013-05-11 [r1313]  Andrew M. Bishop <amb>
+
+	* src/logerror.h (added), src/tagging.c, src/logging.c,
+	  src/relationsx.c, src/planetsplitter.c, src/tagmodifier.c,
+	  src/logerror.c (added), src/logging.h, src/osmparser.c,
+	  src/Makefile, src/segmentsx.c: Create a binary log file that
+	  contains the node, way and relation id and a link to the error
+	  message for easy parsing.
+
+2013-05-11 [r1312]  Andrew M. Bishop <amb>
+
+	* src/ways.c, src/relations.h, src/segments.c, src/nodes.c,
+	  src/ways.h, src/segments.h, src/nodes.h, src/relations.c,
+	  src/router.c: Add functions to destroy the
+	  node/segment/way/relation lists and don't call them from the end
+	  of the router by default.
+
+2013-05-10 [r1308-1311]  Andrew M. Bishop <amb>
+
+	* src/Makefile: Enable -Wextra compilation option (but not
+	  -Wunused-parameter option) by default.
+
+	* src/sorting.c: Change data type from signed to unsigned (pedantic
+	  compiler warning).
+
+	* src/results.c: Change data value from signed to unsigned
+	  (pedantic compiler warning).
+
+	* src/waysx.h, src/segmentsx.h, src/nodesx.h: Make no-op macros
+	  look like real statements.
+
+2013-05-10 [r1305-1307]  Andrew M. Bishop <amb>
+
+	* src/results.c: Change data value from signed to unsigned
+	  (pedantic compiler warning).
+
+	* src/prunex.c: Change data value from unsigned to signed (pedantic
+	  compiler warning).
+
+	* src/filedumper.c: Remove always true condition (pedantic compiler
+	  warning).
+
+2013-05-10 [r1304]  Andrew M. Bishop <amb>
+
+	* src/queue.c: Small change to algorithm to match sorting.c.
+
+2013-05-09 [r1302-1303]  Andrew M. Bishop <amb>
+
+	* src/relations.c: Change data value from unsigned to signed
+	  (pedantic compiler warning).
+
+	* src/planetsplitter.c, src/waysx.c: Change datatype from signed to
+	  unsigned (pedantic compiler warning).
+
+2013-05-09 [r1299-1301]  Andrew M. Bishop <amb>
+
+	* src/osmpbfparse.c, src/osmo5mparse.c, src/xmlparse.c: Change
+	  datatype from signed to unsigned (pedantic compiler warning). Use
+	  inttypes.h printf formatting for unsigned long long data type.
+
+	* src/files.h: Cast data to signed before comparison (pedantic
+	  compiler warning).
+
+	* src/filedumperx.c: Change datatype from signed to unsigned
+	  (pedantic compiler warning).
+
+2013-05-07 [r1298]  Andrew M. Bishop <amb>
+
+	* src/nodes.c: Change the GetLatLong() function to have one binary
+	  search instead of two.
+
+2013-05-07 [r1297]  Andrew M. Bishop <amb>
+
+	* src/cache.h, src/nodes.c, src/relationsx.c, src/waysx.c,
+	  src/segmentsx.c, src/nodesx.c, src/waysx.h, src/prunex.c,
+	  src/segmentsx.h, src/nodesx.h, src/superx.c: Add cache functions
+	  for NodesX, SegmentsX and WaysX to speed up the planetsplitter in
+	  slim mode.
+
+2013-05-07 [r1296]  Andrew M. Bishop <amb>
+
+	* src/cache.c (removed), src/relations.h, src/cache.h, src/ways.h,
+	  src/segments.h, src/Makefile, src/nodes.h: Move the cache
+	  functions out of cache.c and into each data type's header file as
+	  inline functions.
+
+2013-05-03 [r1294-1295]  Andrew M. Bishop <amb>
+
+	* src/cache.c: Revert to the round-robin cache eviction algorithm.
+
+	* src/cache.c: Implement an LRU eviction algorithm for the cached
+	  objects - it's slower, but code kept for possible future re-use.
+
+2013-05-03 [r1293]  Andrew M. Bishop <amb>
+
+	* src/cache.c, src/relations.h, src/cache.h, src/ways.h,
+	  src/segments.h, src/nodes.h: Tidy up the code for the last
+	  check-in and use macros to allow replication of the functions for
+	  each type.
+
+2013-05-03 [r1292]  Andrew M. Bishop <amb>
+
+	* src/segments.h, src/Makefile, src/nodes.h, src/relations.c,
+	  src/router.c, src/cache.c (added), src/ways.c, src/relations.h,
+	  src/segments.c, src/cache.h (added), src/nodes.c, src/ways.h: Add
+	  node, segment, way and turn relation cache for slim mode. Approx
+	  40% speed-up for router.
+
+2013-05-01 [r1291]  Andrew M. Bishop <amb>
+
+	* src/output.c, src/router.c, src/segments.c, src/visualiser.c,
+	  src/nodes.c, src/fakes.c, src/optimiser.c, src/filedumper.c,
+	  src/nodes.h: The GetLatLong function takes a pointer to the node
+	  as an argument - must be an optimisation for slim mode if not
+	  normal mode.
+
+2013-05-01 [r1290]  Andrew M. Bishop <amb>
+
+	* src/results.c, src/queue.c, src/results.h: Move the queue score
+	  back into the results structure since it is quicker but uses
+	  slightly more memory.
+
+2013-05-01 [r1289]  Andrew M. Bishop <amb>
+
+	* src/queue.c, src/results.h, src/superx.c, src/optimiser.c,
+	  src/results.c: Try to speed up the priority queue by allocating
+	  less memory and storing the score in the queue rather than in the
+	  result.
+
+2013-04-28 [r1288]  Andrew M. Bishop <amb>
+
+	* src/results.c: Set pointers to NULL when resizing the hash table.
+
+2013-04-27 [r1287]  Andrew M. Bishop <amb>
+
+	* src/superx.c: Update for change to NewResultsList() function.
+
+2013-04-27 [r1286]  Andrew M. Bishop <amb>
+
+	* src/optimiser.c, src/results.c, src/results.h: Increase the
+	  starting number of bins to allow more results to be stored before
+	  resizing.
+
+2013-04-27 [r1285]  Andrew M. Bishop <amb>
+
+	* src/results.c, src/results.h: Use a linked list to store the
+	  results in each bin rather than pre-allocated pointers.
+
+2013-04-27 [r1283-1284]  Andrew M. Bishop <amb>
+
+	* src/results.c: Improve the hash function to avoid node/segment
+	  correlations in some geographic areas.
+
+	* src/router.c: De-allocate the final routes at the end.
+
+2013-04-27 [r1282]  Andrew M. Bishop <amb>
+
+	* src/results.c, src/results.h: Increase the allowed number of
+	  collisions as the number of bins increases.
+
+2013-04-27 [r1281]  Andrew M. Bishop <amb>
+
+	* src/results.h, src/optimiser.c, src/router.c, src/results.c:
+	  Remove the FindResult1 function which allows hashing to be
+	  performed on a combination of node and segment rather than just
+	  node.
+
+2013-04-26 [r1280]  Andrew M. Bishop <amb>
+
+	* src/results.c, src/results.h: Force a hard limit on the number of
+	  hash collisions.
+
+2013-04-20  Andrew M. Bishop <amb>
+
+	Version 2.5.1 released
+
+2013-04-20 [r1279]  Andrew M. Bishop <amb>
+
+	* doc/html/readme.html, doc/NEWS.txt, FILES: Updated for version
+	  2.5.1 release.
+
+2013-04-18 [r1278]  Andrew M. Bishop <amb>
+
+	* src/router.c, src/optimiser.c, src/functions.h: Fix a bug where
+	  the shortest route crossing super-nodes requires two U-turns and
+	  is therefore impossible to compute even though an obvious shorter
+	  route without crossing super-nodes exists (but cannot be taken
+	  until the super-node route is fully tested). Requires quite a
+	  major change in router handling of this special case.
+
+2013-04-18 [r1277]  Andrew M. Bishop <amb>
+
+	* src/xmlparse.c: Fix bug with handling UTF-8 characters that are
+	  four bytes long (it didn't since v2.5).
+
+2013-04-17 [r1276]  Andrew M. Bishop <amb>
+
+	* src/optimiser.c: Fix bug that corrupts the combined route score
+	  when combining the route (only important if comparing
+	  non-super-node route with super-node route).
+
+2013-04-15 [r1275]  Andrew M. Bishop <amb>
+
+	* web/www/openlayers/install.sh: Default to downloading openlayers
+	  2.12.
+
+2013-04-13 [r1274]  Andrew M. Bishop <amb>
+
+	* doc/INSTALL.txt, doc/html/installation.html: Add a note about the
+	  *-slim executables to the installation section.
+
+2013-04-13 [r1273]  Andrew M. Bishop <amb>
+
+	* src/tagmodifier.c: Use single quotes rather than double quotes
+	  when writing out the XML for similarity with filedumper.
+
+2013-04-13 [r1272]  Andrew M. Bishop <amb>
+
+	* src/xmlparse.c: Fix XML character quoting for characters within
+	  the 7-bit printable ASCII range (bug reported by Dirk Eversmann).
+
+2013-04-13 [1271]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.js, web/www/routino/visualiser.js: Fix the
+	  Javascript so that it works with OpenLayers version 2.11 again
+	  (bug reported by Dirk Eversmann).
+
+2013-04-13 [r1269-1270]  Andrew M. Bishop <amb>
+
+	* doc/html/readme.html, doc/README.txt: Remove some differences
+	  between the HTML and text versions of the documents.
+
+	* doc/INSTALL.txt, doc/html/installation.html: Fix some errors in
+	  the installation documents and improve the description of
+	  pre-requisites and compilation (prompted by bug report from Dirk
+	  Eversmann).
+
+2013-03-24 [r1268]  Andrew M. Bishop <amb>
+
+	* src/planetsplitter.c: Prune the straight highways after removing
+	  the isolated sections and short segments rather than before.
+
+2013-03-19 [r1267]  Andrew M. Bishop <amb>
+
+	* doc/html/output.html, doc/html/tagging.html,
+	  doc/html/installation.html, doc/html/limits.html, doc/README.txt,
+	  doc/html/usage.html, doc/html/algorithm.html,
+	  doc/html/configuration.html, doc/html/index.html,
+	  doc/html/data.html, doc/html/readme.html,
+	  web/www/routino/index.html: Remove e-mail addresses and replace
+	  with links to website.
+
+2013-03-03 [r1266]  Andrew M. Bishop <amb>
+
+	* src/prunex.c: Improve the pruning of straight highways (more
+	  likely to remove larger sections).
+
+2013-03-03 [r1265]  Andrew M. Bishop <amb>
+
+	* src/test, src/prunex.c, src/test/prune-straight.sh (added),
+	  src/test/prune-straight.osm (added): Add a test case for pruning
+	  straight segments. Found an error case related to loops and fixed
+	  it.
+
+2013-03-02 [r1264]  Andrew M. Bishop <amb>
+
+	* src/test/prune-short.sh (added), src/test,
+	  src/test/prune-short.osm (added), src/prunex.c,
+	  src/test/only-split.sh: Add a test case for pruning short
+	  segments. Found some disallowed cases that had not been detected
+	  before and fixed them.
+
+2013-03-02 [r1263]  Andrew M. Bishop <amb>
+
+	* doc/html/tagging.html, xml/routino-tagging.xml, doc/TAGGING.txt,
+	  src/osmparser.c, src/filedumper.c: Recognise mini-roundabouts
+	  tagged as junction=roundabout (as well as
+	  highway=mini_roundabout). Pass them through the parser as
+	  roundabout=yes. Output them from the filedumper as
+	  junction=roundabout. Update the documentation for
+	  mini-roundabouts.
+
+2013-03-02 [r1262]  Andrew M. Bishop <amb>
+
+	* src/test/oneway-loop.osm, src/test/node-restrictions.osm: Remove
+	  the 'update' element from the osm tag.
+
+2013-02-28 [r1261]  Andrew M. Bishop <amb>
+
+	* src/relationsx.c: Rationalise some of the turn relation error
+	  messages.
+
+2013-02-26 [r1260]  Andrew M. Bishop <amb>
+
+	* web/www/routino/router.js, web/www/routino/visualiser.js: Clarify
+	  comment.
+
+2013-02-18 [r1259]  Andrew M. Bishop <amb>
+
+	* xml/routino-tagging.xml: Add some more tags to not be reported as
+	  errors (barrier, highway), add some more aliases for highway
+	  types, bridges and tunnels are enabled for anything except 'no'.
+
+2013-02-16 [r1257-1258]  Andrew M. Bishop <amb>
+
+	* web/www/routino/index.html: Move the meta tag for charset
+	  declaration to the top of the head, before the copyright notice,
+	  within the first 1024 bytes.
+
+	* doc/html/readme.html, web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl, doc/html/output.html,
+	  doc/html/tagging.html, doc/html/installation.html,
+	  doc/html/limits.html, doc/html/usage.html,
+	  doc/html/algorithm.html, doc/html/configuration.html,
+	  doc/html/index.html, web/www/routino/router.html.en,
+	  doc/html/data.html: Move the meta tag for charset declaration to
+	  the top of the head, before the copyright notice, within the
+	  first 1024 bytes.
+
+2013-02-09 [r1256]  Andrew M. Bishop <amb>
+
+	* xml/routino-tagging.xml: Stop two contradictory errors messages
+	  about 'access = foot' etc.
+
+2013-02-09  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Version 2.5 released
+
+2013-02-09 [r1255]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/usage.html, doc/USAGE.txt: Fix HTML validation error.
+
+2013-02-09 [r1254]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/readme.html, doc/NEWS.txt, doc/README.txt, FILES: Update
+	  documentation for version 2.5.
+
+2013-02-09 [r1253]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/tagging.html, doc/TAGGING.txt: Update documentation for
+	  the new tagging transformations.
+
+2013-02-09 [r1252]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml: Add some more highway tagging
+	  transformations (motorroad, sidewalk, footway, cycleway), remove
+	  some rare bicycle specific ones and add some access tag values.
+
+2013-02-09 [r1251]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmo5mparse.c: Rename the local functions that perform the
+	  integer conversions (from pbf_* to o5m_*).
+
+2013-02-08 [r1250]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/documentation, doc/html/limits.html (added),
+	  doc/html/index.html, doc/LIMITS.txt (added): Add documentation
+	  about the numerical limits (OSM identifiers and database size).
+
+2013-02-03 [r1249]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/prunex.c: Some trivial changes, same functionality.
+
+2013-01-24 [r1248]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/visualiser.js, src/visualiser.h,
+	  src/filedumper.c, doc/USAGE.txt, web/www/routino/visualiser.cgi,
+	  web/www/routino/visualiser.html.en, src/visualiser.c,
+	  doc/html/usage.html: Add the ability for the visualiser to
+	  display highways that have a particular property.
+
+2013-01-24 [r1247]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/tagging.html, xml/routino-tagging.xml, doc/TAGGING.txt,
+	  src/osmparser.c: Change the "lanes=..." tag processing because it
+	  counts lanes in both directions. A normal road may be tagged as
+	  having two lanes (one in each direction) but the multilane
+	  property is intended to allow prioritisation of roads where
+	  traffic can use multiple lanes in each direction.
+
+2013-01-21 [r1246]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/tagging.c, src/osmparser.c: Remove unnecessary word from
+	  logerror messages.
+
+2013-01-21 [r1245]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-translations.xml: Updated German translations from
+	  Alex Treiber.
+
+2013-01-20 [r1244]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml: Change the comments to clarify what the
+	  way access tag rules are for. Change slightly the logged message.
+	  Add new tagging rules to transform (for example) access=foot to
+	  foot=yes, access=no.
+
+2013-01-20 [r1243]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml, src/types.c, xml/routino-profiles.xml,
+	  doc/README.txt, doc/html/usage.html, src/types.h,
+	  doc/html/configuration.html, src/osmparser.c,
+	  xml/scripts/ride.pl, web/www/routino/router.html.en,
+	  doc/USAGE.txt, web/www/routino/visualiser.html.en,
+	  doc/CONFIGURATION.txt, web/www/routino/router.html.nl,
+	  doc/html/tagging.html, xml/scripts/walk.pl, src/relationsx.c,
+	  doc/TAGGING.txt, doc/html/readme.html,
+	  web/www/routino/router.html.de: Replace 'motorbike' with
+	  'motorcycle' everywhere.
+
+2013-01-20 [r1242]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/CONFIGURATION.txt, xml/routino-tagging.xsd,
+	  xml/routino-tagging.xml, src/tagging.h,
+	  doc/html/configuration.html, src/tagging.c: Change the <logerror>
+	  element of the configuration file to lookup the tag value if not
+	  specified and add a custom error message. Rework the access tag
+	  checking to use the new logcheck.
+
+2013-01-19 [r1241]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml: Ignore some more highway types ('no' and
+	  'disused').
+
+2013-01-19 [r1240]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/visualiser.html.en: Fix link to documentation
+	  directory.
+
+2013-01-19 [r1239]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/router.html.de,
+	  web/www/routino/visualiser.html.en,
+	  web/www/routino/router.html.nl, web/www/routino/router.js,
+	  web/www/routino/mapprops.js, web/www/routino/visualiser.js,
+	  web/www/routino/router.html.en: Added MapQuest as an optional map
+	  layer, added layer specific attributions to mapprops.js.
+
+2013-01-15 [r1238]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/bin/summarise-log.pl: Fix stupid typo.
+
+2013-01-12 [r1237]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/bin/summarise-log.pl: Sort the listed nodes, ways or
+	  relations by ID.
+
+2013-01-09 [r1236]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c: Reset the to/via/from indexes before parsing
+	  each relation.
+
+2012-12-29 [r1235]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xmlparse.h, src/osmpbfparse.c, src/osmo5mparse.c,
+	  src/osmxmlparse.c, src/xmlparse.c, src/filedumper.c: Replace the
+	  remaining 'long long' and 'unsigned long long' types with
+	  uint64_t.
+
+2012-12-29 [r1234]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmo5mparse.c, src/osmxmlparse.c, src/tagmodifier.c,
+	  src/osmparser.c, src/osmparser.h, src/tagging.c,
+	  src/osmpbfparse.c, src/tagging.h: Re-factor parsing code to
+	  remove duplicated parts from three parsers (osmo5mparse.c,
+	  osmpbfparse.c and osmxmlparse.c) into a common place
+	  (osmparser.c), also removes lots of global variables. Change the
+	  node, way and relation count to uint64_t instead of index_t to
+	  avoid wrap-around (although it would have been a cosmetic problem
+	  only), also removes dependency on types.h. Make the node, way and
+	  relation counters be 'int64_t' instead of 'long long' in the XML
+	  parsers for consistency with the non-XML parsers.
+
+2012-12-28 [r1233]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c: Log errors for areas that are oneway.
+
+2012-12-28 [r1232]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c: Log errors for areas that are not closed.
+
+2012-12-27 [r1231]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c, src/osmparser.c, web/bin/summarise-log.pl: Don't
+	  append segments if they are duplicates within a way or have
+	  duplicated nodes. Log errors for middle nodes that repeat within
+	  a way (can be non-trivial unintentional loops).
+
+2012-12-26 [r1230]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmpbfparse.c: Some small changes for similarity with the
+	  O5M/O5C parser.
+
+2012-12-26 [r1229]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/ALGORITHM.txt, doc/html/algorithm.html: Remove the
+	  "practicalities" section because it is out of date and not very
+	  relevant.
+
+2012-12-26 [r1228]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c, src/segmentsx.c: Make the log error messages
+	  more useful when there are missing nodes or ways.
+
+2012-12-24 [r1227]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.h, doc/html/usage.html, src/osmo5mparse.c (added),
+	  src/planetsplitter.c, src/osmparser.c, src/Makefile,
+	  doc/USAGE.txt: Added parsing of O5M/O5C format (a binary format
+	  but otherwise very close to OSM/OSC).
+
+2012-12-24 [r1226]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/planetsplitter.c, src/osmparser.c, src/osmparser.h,
+	  src/osmpbfparse.c: The PBF format does not support change files
+	  (the 'visible' part of the info message is only for historical
+	  data and not for changes).
+
+2012-12-24 [r1225]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmpbfparse.c: Fix memory allocation error.
+
+2012-12-22 [r1224]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/usage.html, src/planetsplitter.c, src/tagmodifier.c,
+	  doc/USAGE.txt: Data can no longer be read from stdin for
+	  planetsplitter or tagmodifier.
+
+2012-12-21 [r1223]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/data/create.sh: Correct the URLs to use to download data.
+
+2012-12-21 [r1221]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/usage.html, src/osmxmlparse.c, src/planetsplitter.c,
+	  src/osmparser.c, src/Makefile, doc/USAGE.txt, src/osmparser.h,
+	  src/osmpbfparse.c (added): Add a parser for OSM PBF format.
+	  Separate the XML parser from the data processing in osmparser.c.
+	  Update planetsplitter and documentation to use new format.
+
+2012-12-19 [r1219-1220]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xmlparse.c: Use 'unsigned char' instead of 'char' for buffer.
+	  Renumber the LEX states to remove hole.
+
+	* src/uncompress.c: Trivial change to not set a state variable
+	  where not needed.
+
+2012-12-19 [r1218]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmxmlparse.c (added): Copying osmparser.c to create
+	  osmxmlparse.c for the XML callback functions and shared
+	  variables.
+
+2012-12-17 [r1217]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Refactor to remove duplicated code in each
+	  branch of if statement (in each optimiser loop).
+
+2012-12-17 [r1216]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/NEWS.txt, doc/README.txt, FILES, doc/html/readme.html:
+	  Reintegrate the changes from 2.4.1 branch back into trunk.
+
+2012-12-15 [r1209]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml: Fix some errors that appeared in the
+	  tagging file after adding nesting.
+
+2012-12-15 [r1206-1207]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml: Change the tagging rules to use the
+	  nested <if> rules.
+
+	* doc/html/configuration.html, src/tagging.c,
+	  doc/CONFIGURATION.txt, xml/routino-tagging.xsd: Change the
+	  tagging rules to have an <ifnot ...> rule which can also be
+	  nested.
+
+2012-12-15 [r1205]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/translations.c, xml/routino-translations.xsd: Change one
+	  entry in the translations XSD file that used a different case
+	  from the other defined types (not consistent with other XSD files
+	  but self-consistent).
+
+2012-12-15 [r1204]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xml/xsd-to-xmlparser.c, src/osmparser.c, src/tagging.c,
+	  src/translations.c, src/profiles.c: Change the xsd-to-xmlparser
+	  functions to output the source code in the same order as the XSD
+	  file and do not attempt to sort them into reverse order of
+	  reference.
+
+2012-12-15 [r1203]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/configuration.html, src/tagging.c,
+	  doc/CONFIGURATION.txt, xml/routino-tagging.xml: Change the
+	  tagging rules to use the nested <if> rules.
+
+2012-12-15 [r1200-1202]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/planetsplitter.c: Change the order of the command line option
+	  parsing to match the program usage output.
+
+	* doc/USAGE.txt: Add the --logtime and --errorlog options to
+	  tagmodifier.
+
+	* doc/html/usage.html, src/tagmodifier.c: Add the --logtime and
+	  --errorlog options to tagmodifier.
+
+2012-12-15 [r1199]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xsd, src/tagging.h, src/tagmodifier.c,
+	  src/osmparser.c, src/tagging.c: Allow the tagging rule syntax to
+	  contain nested <if ...> statements.
+
+2012-12-14 [r1197]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/USAGE.txt, doc/html/usage.html, src/planetsplitter.c,
+	  src/tagmodifier.c: Update the usage messages and documentation
+	  for bzip2/gzip uncompression.
+
+2012-12-14 [r1196]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/Makefile, src/uncompress.c, src/uncompress.h,
+	  src/planetsplitter.c, src/tagmodifier.c: Add the ability to read
+	  gzip compressed files when specified by name.
+
+2012-12-13 [r1194-1195]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xmlparse.c: Handle the output of the uncompressor where
+	  reading may return only a partial buffer. Makes it more robust
+	  generally against short reads.
+
+	* src/Makefile, src/uncompress.c (added), src/uncompress.h (added),
+	  src/planetsplitter.c, src/tagmodifier.c: Add the ability to read
+	  bzip2 compressed files when specified by name.
+
+2012-12-12 [r1192]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xml/xsd-to-xmlparser.c, src/planetsplitter.c: Use
+	  STDIN_FILENO instead of 0 for the stdin file descriptor.
+
+2012-12-11 [r1190]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xmlparse.c: Reorder if/then/else statements so that most
+	  common ones come first (using profiling when parsing GB OSM
+	  file).
+
+2012-12-11 [r1189]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xmlparse.c: Most xml attribute values are ASCII so optimise
+	  for that case.
+
+2012-12-10 [r1188]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/Makefile: Re-enable the optimisation option.
+
+2012-12-10 [r1185-1187]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src: Change svn ignored files (don't ignore xmlparse.c now).
+
+	* src/xml/xsd-to-xmlparser.c, src/planetsplitter.c,
+	  src/tagmodifier.c, src/osmparser.c, src/xml/Makefile,
+	  src/osmparser.h, src/tagging.c, src/xmlparse.h,
+	  src/translations.c, src/profiles.c: New XML parser doesn't use
+	  stdio buffered file access but lower level read functions.
+
+	* src/xmlparse.l (removed), src/xmlparse.c (added), src/Makefile:
+	  Remove flex based XML parser and replace with a parser created by
+	  implementing the same lex rules by hand. Operates faster because
+	  tag attributes do not need memory allocated or copying from file
+	  buffer and there are no yylex() function calls/returns.
+
 2012-12-17  Andrew M. Bishop <amb at gedanken.demon.co.uk>
 
 	Version 2.4.1 released
diff --git a/Makefile b/Makefile
index e157c1e..1c1cbd3 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2009-2011 Andrew M. Bishop
+# This file Copyright 2009-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -18,56 +18,50 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Installation locations
+# All configuration is in the top-level Makefile.conf
 
-prefix=/usr/local
-bindir=$(prefix)/bin
-docdir=$(prefix)/doc/routino
-datadir=$(prefix)/share/routino
+include Makefile.conf
 
 # Sub-directories and sub-makefiles
 
-TOPFILES=$(wildcard */Makefile)
-TOPDIRS=$(foreach f,$(TOPFILES),$(dir $f))
+SUBFILES=$(wildcard */Makefile)
+SUBDIRS=$(foreach f,$(SUBFILES),$(dir $f))
 
 ########
 
-all$(top):
-	for dir in $(TOPDIRS); do \
+all:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-test$(top):
-	for dir in $(TOPDIRS); do \
+test:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-install$(top): all$(top)
-	for dir in $(TOPDIRS); do \
+install:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
-	@echo "Note: web directory is not installed automatically"
 
 ########
 
-clean$(top):
-	for dir in $(TOPDIRS); do \
+clean:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-distclean$(top): clean$(top)
-	for dir in $(TOPDIRS); do \
+distclean:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-.PHONY:: all$(top) test$(top) install$(top) clean$(top) distclean$(top)
-
 .PHONY:: all test install clean distclean
diff --git a/Makefile.conf b/Makefile.conf
new file mode 100644
index 0000000..a7976ce
--- /dev/null
+++ b/Makefile.conf
@@ -0,0 +1,81 @@
+# Configuration Makefile
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2013-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Installation locations (edit if required)
+
+prefix=/usr/local
+bindir=$(prefix)/bin
+docdir=$(prefix)/doc/routino
+datadir=$(prefix)/share/routino
+
+
+# Compilation programs
+
+CC=gcc
+LD=gcc
+
+
+# Language dialect selection
+CFLAGS=-std=c99
+
+# Warning options
+CFLAGS+=-Wall -Wmissing-prototypes -Wextra -Wno-unused-parameter
+
+# Optimisation options
+CFLAGS+=-O3
+CFLAGS+=-ffast-math
+
+# Optimisation option (only works if compilation and execution use exactly the same CPU architecture).
+#CFLAGS+=-march=native
+
+# Debugging symbols
+#CFLAGS+=-g
+
+
+# Maths library
+LDFLAGS=-lm
+
+
+# Required for multi-threaded support (comment these two lines out if not required)
+CFLAGS+=-pthread -DUSE_PTHREADS
+LDFLAGS+=-pthread -lpthread
+
+
+# Required for bzip2 support (comment these two lines out if not required)
+CFLAGS+=-DUSE_BZIP2
+LDFLAGS+=-lbz2
+
+
+# Required for gzip support (comment these two lines out if not required)
+CFLAGS+=-DUSE_GZIP
+LDFLAGS+=-lz
+
+
+# Required for xz support (uncomment these two lines if required)
+#CFLAGS+=-DUSE_XZ
+#LDFLAGS+=-llzma
+
+
+# Required to use stdio with files > 2GiB on 32-bit system.
+CFLAGS+=-D_FILE_OFFSET_BITS=64
+
+
+# Required to compile on Linux without a warning about pread() and pwrite() functions.
+CFLAGS+=-D_POSIX_C_SOURCE=200809L
diff --git a/doc/ALGORITHM.txt b/doc/ALGORITHM.txt
index 929db12..569281b 100644
--- a/doc/ALGORITHM.txt
+++ b/doc/ALGORITHM.txt
@@ -118,9 +118,8 @@ Final Algorithm
    handling in the algorithm but it gives mode flexibility for the start,
    finish and intermediate points in a route.
 
-
 Algorithm Evolution
--------------------
+- - - - - - - - - -
 
    In Routino versions 1.0 to 1.4 the algorithm used to select a
    super-node was the same as above except that node properties were not
@@ -132,6 +131,29 @@ Algorithm Evolution
    the original algorithm since the super-segments more accurately reflect
    the underlying topology.
 
+Algorithm Implementation
+- - - - - - - - - - - -
+
+   The algorithm that is used for finding the route between the
+   super-nodes using super-segments is the A* algorithm (or a slight
+   variation of it). This was not a deliberate design decision, but
+   evolved into it during development. This algorithm relies on
+   calculating the lowest score (shortest distance or quickest time) to
+   each node from the starting node. The remaining score for the path to
+   the destination node is estimated (based on a straight line using the
+   fastest type of highway) and added to the current score and the result
+   recorded. At each step the unvisited node that has the lowest current
+   score is examined and all nodes connected to it have their scores
+   calculated. When the destination node has been reached all remaining
+   unvisited nodes with scores higher than the destination node's score
+   can be discarded and the few remaining nodes examined.
+
+   The algorithm used to find the route between super-nodes using normal
+   segments is Dijkstra's algorithm (although it is implemented as the
+   same algorithm as above but with no estimated cost). Since these routes
+   tend to be short and the CPU time for calculating the heuristic cost
+   function is relatively large this tends to give a quicker solution.
+
 
 Routing Preferences
 -------------------
@@ -230,6 +252,7 @@ Data Pruning
        segments) but again care is taken not to remove node access
        permissions or changes in way properties.
 
+
 Turn Restrictions
 -----------------
 
@@ -262,7 +285,9 @@ Turn Restrictions
    exist more than once along a route between two points. With turn
    restrictions the route is defined by a node and the segment used to get
    there; no route between two points will ever need to follow the same
-   segment in the same direction more than once.
+   segment in the same direction more than once. This means that the
+   optimisation algorithm calculates scores for directed segments (indexed
+   by segment and end node) rather than for nodes.
 
    A side-effect of this is that a route that works around a turn
    restriction must be calculable using the super-segments that are stored
@@ -341,26 +366,6 @@ Data Implementation
    this information.
 
 
-Practicalities
---------------
-
-   At the time of writing (April 2010) the OpenStreetMap data for Great
-   Britain (taken from GeoFabrik) contains:
-     * 14,675,098 nodes
-          + 8,767,521 are highway nodes
-          + 1,120,297 are super-nodes
-     * 1,876,822 ways
-          + 1,412,898 are highways
-               o 9,316,328 highway segments
-               o 1,641,009 are super-segments
-     * 60,572 relations
-
-   The database files when generated are 41.5 MB for nodes, 121.6 MB for
-   segments and 12.6 MB for ways and are stored uncompressed. By having at
-   least 200 MB or RAM available the routing can be performed with no disk
-   accesses (once the data has been read once).
-
-
 --------
 
-Copyright 2008-2012 Andrew M. Bishop.
+Copyright 2008-2013 Andrew M. Bishop.
diff --git a/doc/CONFIGURATION.txt b/doc/CONFIGURATION.txt
index 3a1a131..14aaf46 100644
--- a/doc/CONFIGURATION.txt
+++ b/doc/CONFIGURATION.txt
@@ -43,7 +43,7 @@ Tag Transformation Rules
     <if k="highway" v="motorway">
       <output k="highway"/>
 
-      <output k="motorbike"  v="yes"/>
+      <output k="motorcycle" v="yes"/>
       <output k="motorcar"   v="yes"/>
       <output k="goods"      v="yes"/>
       <output k="hgv"        v="yes"/>
@@ -52,26 +52,67 @@ Tag Transformation Rules
       <output k="paved"      v="yes"/>
       <output k="multilane"  v="yes"/>
       <output k="oneway"     v="yes"/>
+
+      <unset k="highway"/>
     </if>
 ...
   <way>
 
 </routino-tagging>
 
-   The rules all have the same format; an if element for matching the
-   input and some set or output elements to either change the input tags
-   or create an output tag. The k and v attributes have the same meaning
-   as the attributes with the same names in the OSM XML file - the tag key
-   and tag value.
-
-   An if rule that has both k and v specified is only applied if a tag
-   exists in the input that matches both. An if rule that has only the k
-   attribute is applied if a tag with that key exists and an if rule that
-   has only the v attribute is applied to all tags with that value.
-
-   For the set and output elements the tag that is created in the input or
-   output tag set uses the k and v attributes specified. If one or both
-   are not specified then the original ones are used.
+   The rules all have the same format; an if or ifnot element at the top
+   level for matching the input and some other elements inside to be used
+   if there was a match.
+
+   Within the if and ifnot rules any of the rules can be used. These are
+   if, ifnot, set, unset, output or logerror elements.
+
+   The rules for matching the if or ifnot elements are the following:
+     * An if rule that has both k and v specified is only matched if a tag
+       exists in the input that matches both.
+     * An if rule that has only the k attribute is matched if a tag with
+       that key exists.
+     * An if rule that has only the v attribute is matched for each tag
+       with that value (i.e. the contents may be used more than once).
+     * An if rule that has neither attribute specified always matches.
+     * An ifnot rule that has both k and v specified is only matched if no
+       tag exists in the input that matches both.
+     * An ifnot rule that has only the k attribute is matched only if no
+       tag with that key exists.
+     * An ifnot rule that has only the v attribute is only matched if no
+       tag with that value exists.
+     * An ifnot rule that has neither attribute specified never matches.
+
+   For set, unset, output or logerror elements inside of an if rule an
+   unspecified value for the k or v attribute is replaced by the values
+   from the tag that matched the outer if rule. This makes it simple to
+   delete tags that match a particular rule without having to specify the
+   parameters more than once. For elements inside of an ifnot element an
+   unspecified value for the k or v attribute is replaced by the values
+   from the outer ifnot rule. This means that either the outer ifnot
+   element or the inner element must specify both k and v attributes
+   between them. For nested if or ifnot elements the outer k and v
+   attributes are not inherited by the inner elements.
+
+   The set and unset elements either create or delete a tag from the input
+   data that was read from the file. If the set element is used and the
+   tag already exists then it is modified. The output element adds a tag
+   to the set that will be used by Routino to determine the data
+   properties.
+
+   The logerror element will cause an error message to be added to the
+   error log file that reports that the key and attribute combination are
+   not recognised. If the k attribute is specified but not the v attribute
+   then the tag value that matches the specified key is looked up and
+   used. An additional message attribute can be specified to be printed at
+   the end of the logged error.
+
+   The default logged error message is:
+
+   Node XXX has an unrecognised tag 'key' = 'value' (in tagging rules); ignoring it.
+
+   The specified message attribute will replace the final part of the
+   logged error.
 
 
 Routing Profiles
@@ -114,8 +155,7 @@ Routing Profiles
       <property type="multilane"    percent="25" />
       <property type="bridge"       percent="50" />
       <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="55" />
-      <property type="bicycleroute" percent="55" />
+...
     </properties>
     <restrictions>
       <oneway obey="0" />
@@ -176,4 +216,4 @@ Output Translations
 
 --------
 
-Copyright 2010 Andrew M. Bishop.
+Copyright 2010-2013 Andrew M. Bishop.
diff --git a/doc/DATALIFE.txt b/doc/DATALIFE.txt
index 9c88db5..15c3597 100644
--- a/doc/DATALIFE.txt
+++ b/doc/DATALIFE.txt
@@ -28,9 +28,9 @@ Key (structure parameter usage):
                               :      : | . | . | . | . | ...................................
                               :      : v . v . v . v . v : segmentsx->firstnode
                               :      :   .   .   .   .   : | . segmentsx->next1
-                              :      :   .   .   .   .   : | . | . segmentsx->usednode
-                              :      :   .   .   .   .   : | . | . | . segmentsx->usedway
-                              :      :   .   .   .   .   : | . | . | . | . segmentx->node1,2
+                              :      :   .   .   .   .   : | . | . segmentsx->usedway
+                              :      :   .   .   .   .   : | . | . | . segmentx->node1,2
+                              :      :   .   .   .   .   : | . | . | . | . segmentx->next2
                               :      :   .   .   .   .   : | . | . | . | . | . segmentx->way
                               :      :   .   .   .   .   : | . | . | . | . | . | ..................
                               :      :   .   .   .   .   : v . v . v . v . v . v : waysx->idata
@@ -41,48 +41,45 @@ Function name (in order)      :      :   .   .   .   .   :   .   .   .   .   .
 |                             :      :   .   .   .   .   :   .   .   .   .   .   :   .   .   : | ...........
 v                             :      :   .   .   .   .   :   .   .   .   .   .   :   .   .   : v :
                               :......:...................:.......................:...........:...:
-SortNodeList                  :      : C .   .   .   . U :   .   .   .   . | . | :   .   . | : | :
-ApplySegmentChanges           :      : | .   .   .   .   :   .   .   .   . U . U :   .   . | : | : - Changes
-SortSegmentList               :      : | .   .   .   .   :   .   .   .   . U . | :   .   . | : | :
-SortWayList                   :      : | .   .   .   .   :   .   .   .   . | . | :   .   . | : | :
-SortRelationList              :      : | .   .   .   .   :   .   .   .   . | . | :   .   . | : U :
-ExtractWayNames               :      : | .   .   .   .   :   .   .   .   . | . | : C .   . U : | :
-RemoveBadSegments             :      : U .   .   .   .   :   .   . C .   . U . U : U .   .   : | :
-RemoveNonHighwayNodes         :      : M .   .   .   . R :   .   . D .   . | . | : | .   .   : | :
-ProcessRouteRelations         :   W  : | .   .   .   . | :   .   .   .   . | . | : U .   .   : U :
-ProcessTurnRelations1         :      : U .   .   .   . | :   .   .   .   . | . | : U .   .   : U :
-MeasureSegments               : n    : D .   .   .   . | :   .   .   . C . M . M : D .   .   : | :
-IndexSegments                 :  S   :   .   .   .   . | : C .   .   . | . M . | :   .   .   : | :
-ProcessTurnRelations2         : Nsw  :   .   .   .   . | : U .   .   . | . U . | :   .   .   : U :
-CompactWayList                :      :   .   .   .   . | :   .   .   . D . | . | :   . C . M :   :
-IndexSegments                 :  S   :   .   .   .   . | : R .   .   .   . M . | :   . D .   :   :
+SortNodeList                  :      : C .   .   .   . U :   .   .   .   .   .   :   .   . | : | :
+SortWayList                   :      : | .   .   .   . | :   .   .   .   .   .   : C .   . | : | :
+SortRelationList              :      : | .   .   .   . | :   .   .   .   .   .   : | .   . | : U :
+RemoveNonHighwayNodes         :      : M .   .   .   . U :   .   .   .   .   .   : | .   . | : | :
+SplitWays                     :      : U .   .   .   . | :   .   .   . C .   . C : | .   . U : | :
+SortWayNames                  :   W  : | .   .   .   . | :   .   .   . | .   . | : | .   . | : | :
+SortSegmentList               :      : | .   .   .   . | :   .   .   . U .   . | : | .   . | : | :
+ProcessSegments               : n w  : | .   .   .   . U :   .   . C . U .   . U : | .   . U : | :
+IndexSegments                 :  S   : | .   .   .   .   : C .   . | . U . C . | : | .   . | : | :
+ProcessRouteRelations         :   W  : | .   .   .   .   : | .   . | . | . | . | : U .   . | : U :
+ProcessTurnRelations          : Nsw  : D .   .   .   .   : U .   . | . U . U . | : D .   . | : U :
+CompactWayList                :      :   .   .   .   .   :   .   . D . | .   . | :   . C . M :   :
+IndexSegments                 :  S   :   .   .   .   .   : R .   .   . U . R . M :   . D . | :   :
                               :......:...................:.......................:...........:...:
-StartPruning                  :      :   .   .   .   .   : | . C .   .   . U . | :   .   .   :   : \
-PruneStraightHighwayNodes     : nSw  :   .   .   .   .   : U . U .   .   . U . | :   .   .   :   :  | O
-PruneIsolatedRegions          : nSw  :   .   .   .   .   : U . U .   .   . U . | :   .   .   :   :  | p
-PruneShortSegments            : NSw  :   .   .   .   .   : U . U .   .   . U . | :   .   .   :   :  | t
-FinishPruning                 :      :   .   .   .   .   : | . D .   .   . | . | :   .   .   :   :  | i
-RemovePrunedNodes             :      :   .   . C .   . R : U .   .   .   . | . | :   .   .   :   :  | o
-RemovePrunedSegments          :      :   .   . | .   . | :   .   .   . C . U . | :   .   .   :   :  | n
-CompactWayList                :      :   .   . | .   . | :   .   .   . D . | . | :   . C . M :   :  | a
-RemovePrunedTurnRelations     :      :   .   . U .   . | :   .   .   .   . | . | :   . | .   :   :  | l
-IndexSegments                 :  S   :   .   . D .   . | : R .   .   .   . M . | :   . D .   :   : /
+StartPruning                  :      :   .   .   .   .   : | . C .   . U . U . | :   .   . | :   : \
+PruneStraightHighwayNodes     : nSw  :   .   .   .   .   : U . U .   . U . U . | :   .   . | :   :  | O
+PruneIsolatedRegions          : nSw  :   .   .   .   .   : U . U .   . U . U . | :   .   . | :   :  | p
+PruneShortSegments            : NSw  :   .   .   .   .   : U . U .   . U . U . | :   .   . | :   :  | t
+FinishPruning                 :      :   .   .   .   .   : | . D .   . | .   . | :   .   . | :   :  | i
+RemovePrunedNodes             :      :   .   . C .   .   : U .   .   . | .   . | :   .   . | :   :  | o
+RemovePrunedSegments          :      :   .   . | .   .   :   .   . C . U .   . | :   .   . | :   :  | n
+CompactWayList                :      :   .   . | .   .   :   .   . D . | .   . | :   . C . M :   :  | a
+RemovePrunedTurnRelations     :      :   .   . U .   .   :   .   .   . | .   . | :   . | .   :   :  | l
+IndexSegments                 :  S   :   .   . D .   .   : R .   .   . M . R . | :   . D .   :   : /
                               :......:...................:.......................:...........:...:
-ChooseSuperNodes              : nsw  :   .   .   . C . | : U .   .   .   . | . | :   .   .   :   : <-+ L
-CreateSuperSegments           : nsw  :   .   .   . U . | : U .   .   .   . R*. | :   .   .   :   :   | o
-DeduplicateSuperSegments      :   w  :   .   .   . | . | :   .   .   .   . U*. | :   .   .   :   :   | o
-IndexSegments                 :  S   :   .   .   . | . | : C*.   .   .   . U*. | :   .   .   :   :   | p
+ChooseSuperNodes              :  sw  :   .   .   . M .   : U .   .   . | . U*. | :   .   .   :   : <-+ L
+CreateSuperSegments           : nsw  :   .   .   . U .   : U .   .   . R*. U*. | :   .   .   :   :   | o
+DeduplicateSuperSegments      :   w  :   .   .   . | .   :   .   .   . U*.   . | :   .   .   :   :   | o
+IndexSegments                 :  S   :   .   .   . | .   : C*.   .   . U*. C*. | :   .   .   :   :   | p
                               :......:...................:.......................:...........:...: --+
-MergeSuperSegments            :  s   :   .   .   . | . | :   .   .   .   . U . | :   .   .   :   :
-IndexSegments                 :  S   :   .   .   . | . | : R .   .   .   . U . | :   .   .   :   :
+MergeSuperSegments            :      :   .   .   . | .   :   .   .   . U .   . | :   .   .   :   :
                               :......:...................:.......................:...........:...:
-SortNodeListGeographically    :      :   . C .   . | . U :   .   .   .   . | . | :   .   .   :   :
-SortSegmentListGeographically :      :   . U .   . D . | :   .   .   .   . U . | :   .   .   :   :
-IndexSegments                 :  S   :   . | .   .   . | : R .   .   .   . U . | :   .   .   :   :
-SortTurnRelationListGeogra... : n    :   . U .   .   . | : U .   .   .   . U . | :   .   .   :   :
+SortNodeListGeographically    :      :   . C .   . D . R :   .   .   . | .   . | :   .   .   :   :
+SortSegmentListGeographically :      :   . U .   .   .   :   .   .   . U .   . | :   .   .   :   :
+IndexSegments                 :  S   :   . | .   .   .   : R .   .   . U . R . | :   .   .   :   :
+SortTurnRelationListGeogra... :  s   :   . D .   .   .   : U .   .   . U . U . | :   .   .   :   :
                               :......:...................:.......................:...........:...:
-SaveNodeList                  :      :   . D .   .   . U : D .   .   .   . | . | :   .   .   :   :
-SaveSegmentList               :      :   .   .   .   .   :   .   .   .   . U . U :   .   .   :   :
+SaveNodeList                  :      :   .   .   .   .   : D .   .   . | . | . | :   .   .   :   :
+SaveSegmentList               :      :   .   .   .   .   :   .   .   . U . U . U :   .   .   :   :
 SaveWayList                   :      :   .   .   .   .   :   .   .   .   .   .   :   .   .   :   :
 SaveRelationList              :      :   .   .   .   .   :   .   .   .   .   .   :   .   .   :   :
                               :......:...................:.......................:...........:...:
diff --git a/doc/INSTALL.txt b/doc/INSTALL.txt
index 2a010a6..18e6bd9 100644
--- a/doc/INSTALL.txt
+++ b/doc/INSTALL.txt
@@ -2,23 +2,135 @@
                            ======================
 
 
+Quick Start Guide
+-----------------
+
+   The instructions below are a complete list of the minimum required to
+   get Routino installed and running under Apache on Debian Linux (other
+   Linux versions will be similar).
+
+   ***********************************************************************
+   *** These instructions should not be considered as complete or a    ***
+   *** secure installation for the reasons given in more detail below. ***
+   ***********************************************************************
+
+   The starting point for the installation is in the Routino source code
+   directory after it has been uncompressed. Most of the steps will need
+   to be run as the root user.
+
+   The first thing to do is to install the packages which are required for
+   compilation of Routino as described in the Pre-Requisites section
+   below.
+
+        apt-get install gcc make libc6-dev libz-dev libbz2-dev
+
+   After this the programs can be compiled:
+
+        make
+
+   The files for the web interface can now be copied to the web server
+   directory. On Debian this is '/var/www' and the files changed to be
+   owned by the user Apache runs as.
+
+        cp -a web /var/www/routino
+        chown -R www-data:www-data /var/www/routino
+
+   To be able to use Routino some data will need to be processed and a
+   script is provided for this. This will download the data for the UK
+   which may take a while and then process it.
+
+        cd /var/www/routino/data
+        sh -x create.sh
+
+   The routino web pages also requires either the OpenLayers or Leaflet
+   Javascript library to be downloaded and installed and scripts are
+   provided for this.
+
+        cd /var/www/routino/www/openlayers
+        sh -x install.sh
+
+        cd /var/www/routino/www/leaflet
+        sh -x install.sh
+
+   To make full use of the location search feature on the Routino web page
+   you will need to install one extra perl package.
+
+        apt-get install libjson-pp-perl
+
+   Finally to make the web pages work you will need to add the extra lines
+   to the Apache configuration file as described in the Configuration of
+   Web Server section below.  You don't have to use 'vi' and can use any
+   text editor.
+
+        vi /etc/apache2/sites-enabled/000-default
+        apache2ctl restart
+
+   Now everything should be set up and the web page should work if
+   accessed at 'http://localhost/routino/www/routino/router.html'.
+
+   When everything is working you should read the rest of this document
+   carefully and make the following changes:
+     * Download your choice of OSM data - edit the file data/create.sh and
+       run it again.
+     * Edit the www/routino/mapprops.js file to match the downloaded data
+       and personal map preferences.
+     * Move the files in the web server directory so that only the
+       contents of the www directory are accessible by the web server.
+     * Edit the file www/routino/paths.pl to reference the new file
+       locations.
+
+
+Pre-Requisites
+--------------
+
+   The programs are written in standard C language with minimal external
+   requirements so only a small set of development tools are required
+   (gcc, make). The compilation tools to use and the command line options
+   are defined in the file 'Makefile.conf'.
+
+   There is some support for multi-threading that uses pthreads and
+   additional development libraries for this may be required (on Linux
+   this might be part of the standard C language development files). The
+   multi-threading is enabled by default but can be disabled at compile
+   time by commenting out two lines in the file 'Makefile.conf'.
+
+   To use the built-in gzip file decompression function and to process all
+   PBF format files the zlib (or libz) development library is required (on
+   Linux this might be in a package called libz-dev). The gzip function is
+   enabled by default but can be disabled by commenting out two lines in
+   the file 'Makefile.conf'.
+
+   To use the built-in bzip2 file decompression functions the bzip2 (or
+   libbz2) development library is required (on Linux this might be in a
+   package called libbz2-dev). The bzip2 function is enabled by default
+   but can be disabled by commenting out two lines in the file
+   'Makefile.conf'.
+
+   To use the built-in xz file decompression functions the liblzma
+   development library is required (on Linux this might be in a package
+   called liblzma-dev). The xz function is not enabled by default but can
+   be enabled by uncommenting two lines in the file 'Makefile.conf'.
+
+   To use the web page interface an http server is required. Instructions
+   below are for Apache but any server that supports CGIs should work.
+
+   The web pages use the Perl scripting language and a number of the
+   default Perl modules. To use the search function on the router web page
+   the additional Perl module "JSON::PP" must be installed.
+
+
 Compilation
 -----------
 
+   To compile the programs just type 'make' at the top level of the source
+   tree.
+
    This program has been written to run on Linux, no cross-platform
    compatibility has been specifically included but on the other hand
-   nothing platform specific has been knowingly included either.
+   other platforms have not knowingly been excluded either.
 
    Any information on improving the compilation process on anything other
-   than 32-bit x86 Linux is welcome.
-
-   No external libraries are required and the programs are written in
-   standard C language.
-
-   To compile the programs just type 'make'.
-
-   To use the search function on the web page the perl module "JSON::PP"
-   must be installed.
+   than x86 Linux is welcome.
 
 
 Installation
@@ -30,15 +142,15 @@ Installation
    example web pages but is also a useful location to copy the files from
    for normal use.
 
-   The executable files are called 'planetsplitter', 'router' and
-   'filedumper' (also 'tagmodifier' for debugging tag modifications). They
-   can be copied to any location and need no special installation
-   environment.
+   The required executable files are 'planetsplitter', 'router' and
+   'filedumper' and the '*-slim' versions of the same files. They can be
+   copied to any location and need no special installation environment.
+   The 'filedumperx' executable is for debugging and not required.
 
-   The default configuration files are called 'profiles.xml',
-   'tagging.xml' and 'translations.xml'. The names of the configuration
-   files can be specified on the command line but by default are also
-   looked for in the directory that contains the routing database.
+   The configuration files are called 'profiles.xml', 'tagging.xml' and
+   'translations.xml'. The names of the configuration files can be
+   specified on the command line but by default are also looked for in the
+   directory that contains the routing database with these names.
 
 
 Example Web Page
@@ -51,7 +163,7 @@ Example Web Page
    that is accessible by a web server. After copying the files some of
    them need to be edited; search through the files for lines that contain
    the words "EDIT THIS" and make appropriate edits. The files that need
-   editing are 'paths.pl' (to set the directory paths) and 'mapprefs.js' (to
+   editing are 'paths.pl' (to set the directory paths) and 'mapprops.js' (to
    set the map properties).
 
 
@@ -82,13 +194,16 @@ Configuration of web files
     + /www/                    <- The files that must be available to the web
         |                         server are below this level.
         |
-        + /openlayers/         <- A directory to hold the OpenLayers scripts.
+        + /openlayers/         <- A directory to hold the OpenLayers library
+        |                         (optional; leaflet can be used instead).
+        |
+        + /leaflet/            <- A directory to hold the Leaflet library.
+        |                         (optional; openlayers can be used instead).
         |
         + /routino/            <- The main HTML, Javascript, CSS and CGI files.
             |
             + /documentation/  <- The HTML version of the Routino documentation.
 
-
    The directory 'bin' will be filled by running the compilation process.
    For a secure installation the 'bin' directory should be outside of the
    web server, the file 'www/routino/paths.pl' contains the path to the
@@ -115,15 +230,25 @@ Configuration of web files
    The directory 'www' and its sub-directories are the only ones that need
    to be within the web server accessible directory.
 
-   The directory 'www/openlayers' must be filled with the openlayers
-   Javascript library that can be downloaded from
-   http://www.openlayers.org/.  (This version of Routino has been tested
-   with OpenLayers library version 2.11). The files must be installed so
-   that the file 'www/openlayers/OpenLayers.js' and the directories
-   'www/openlayers/img/', 'www/openlayers/theme/' all exist. There is a
-   script in the directory that will automatically download the files,
-   create an optimised "OpenLayers.js" and copy the files to the required
-   locations.
+   A Javascript map drawing library is required and either OpenLayers or
+   Leaflet can be used. The library is loaded dynamically when the HTML is
+   opened based on the value that is selected in 'mapprops.js'.
+
+   The directory 'www/openlayers' is for the OpenLayers Javascript library
+   that can be downloaded from 'http://www.openlayers.org/'.  (This version
+   of Routino has been tested with OpenLayers library versions 2.12 and
+   2.13.1). The file 'www/openlayers/OpenLayers.js' and the directories
+   'www/openlayers/img/' and 'www/openlayers/theme/' must all exist. There
+   is a script in the 'www/openlayers' directory that will automatically
+   download the files, create an optimised 'OpenLayers.js' and copy the
+   files to the required locations.
+
+   The directory 'www/leaflet' is for the Leaflet Javascript library that
+   can be downloaded from 'http://leafletjs.com/'. (This version of Routino
+   has been tested with Leaflet library versions 0.7.1 and 0.7.2). The files
+   'www/leaflet/leaflet.js' and 'www/leaflet/leaflet.css' and the directory
+   'www/leaflet/images/' must all exist. There is a script in the
+   'www/leaflet' directory that will automatically download the files.
 
    The directory 'www/routino' contains the main HTML, Javascript and CSS
    files as well as the CGI scripts that perform the server-side routing
@@ -137,11 +262,13 @@ Configuration of web files
           executables.
 
    mapprops.js
-          The parameters in this file control the boundary of the visible
-          map (defaults to UK), the minimum and maximum zoom levels
-          (defaults to between 4 and 15 inclusive), the source of map tiles
-          (defaults to the main OpenStreetMap tile server) and the number
-          of waypoints allowed (defaults to 9).
+          The parameters in this file control the Javascript map library
+          (defaults to OpenLayers), the boundary of the visible map
+          (defaults to UK), the minimum and maximum zoom levels (defaults
+          to between 4 and 15 inclusive), the source of map tiles
+          (defaults to the main OpenStreetMap tile server), the data
+          editing and browsing URLs (default to the OpenStreetMap website)
+          and the number of waypoints allowed (defaults to 9).
 
    The directory www/routino/documentation contains the HTML version of
    the Routino documentation.
@@ -157,7 +284,19 @@ Configuration of web server
    commented out. This must be enabled in the main Apache server
    configuration file.
 
+   If you have copied the routino 'web' directory into '/var/www' and named
+   it 'routino' then the entry that you need in your Apache configuration
+   file is this one:
+  
+     <Directory /var/www/routino>
+         AllowOverride All
+         Options +ExecCGI
+     </Directory>
+
+   This can be placed anywhere between the <VirtualHost *:80> and
+   </VirtualHost> tags which should be at the start and end of the file.
+
 
 --------
 
-Copyright 2008-2012 Andrew M. Bishop.
+Copyright 2008-2014 Andrew M. Bishop.
diff --git a/doc/LIMITS.txt b/doc/LIMITS.txt
new file mode 100644
index 0000000..a434ea4
--- /dev/null
+++ b/doc/LIMITS.txt
@@ -0,0 +1,167 @@
+                         Routino : Numerical Limits
+                         ==========================
+
+
+32/64-bit Data IDs
+------------------
+
+   The OpenStreetMap data uses a numerical identifier for each node, way
+   and relation. These identifiers started at 1 and increase for every new
+   item of each type that is added. When an object is deleted the
+   identifier is not re-used so the highest identifier will always be
+   higher than the number of objects.
+
+   The identifier needs to be handled carefully to ensure that it does not
+   overflow the data type allocated for it. Depending on the data type
+   used to store the identifier there are are a number of numerical limits
+   as described below:
+
+    1. If a signed 32-bit integer is used to store the identifier then the
+       maximum value that can be handled is 2147483647 (2^31-1) before
+       overflow.
+    2. If an unsigned 32-bit integer is used to store the identifier then
+       the maximum value that can be handled is 4294967295 (2^32-1) before
+       overflow.
+    3. If a signed 64-bit integer is used to store the identifier then the
+       maximum value that can be handled is 9223372036854775807 (2^63-1)
+       before overflow.
+
+   For the purposes of this document the possibility of overflow of a
+   64-bit integer is ignored.
+
+   The part of Routino that handles the node, way and relation identifiers
+   is the planetsplitter program.
+
+
+ID Above 31-bits
+- - - - - - - -
+
+   The first identifier exceeding 31-bits (for a node) is predicted to be
+   created in the OpenStreetMap database in February 2013.
+
+   All versions of Routino use unsigned 32-bit integers to store the
+   identifier. Therefore all versions of Routino will continue working
+   correctly when node number 2147483648 (2^31) or higher is present.
+
+
+ID Above 32-bits
+- - - - - - - -
+
+   The ability of Routino to handle identifiers larger than 32-bits does
+   not depend on having a 64-bit operating system.
+
+   Before version 2.0.1 of Routino there was no check that the identifier
+   read from the input data would fit within an unsigned 32-bit integer.
+   Earlier versions of Routino will therefore fail to report an error and
+   will process data incorrectly when node number 4294967296 (2^32) or
+   higher is present.
+
+   From version 2.0.2 the code is written to allow the node, way and
+   relation identifier data type to be changed to 64-bits. This means that
+   a consistent data type is used for handling identifiers and the format
+   used for printing them is consistent with the variable type.
+
+   From version 2.0.2 onwards it is possible to make a simple change to
+   the code to process data with node identifiers above 4294967296 (2^32)
+   without error. The binary format of the database will be unchanged by
+   the use of 64-bit identifiers (since the identifiers are not stored in
+   the database).
+
+   To recompile with 64-bit node identifiers the file src/typesx.h should
+   be edited and the two lines below changed from:
+
+typedef uint32_t node_t;
+
+#define Pnode_t PRIu32
+
+   to:
+
+typedef uint64_t node_t;
+
+#define Pnode_t PRIu64
+
+   A similar change can also be made for way or relation identifiers
+   although since there are currently fewer of these the limit is not as
+   close to being reached.
+
+   Between version 2.0.2 and version 2.4 a bug means that route relations
+   will ignore the way or relation identifier if it is equal to 4294967295
+   (2^32-1).
+
+   From version 2.4 onwards when a numerical limit is reached the
+   planetsplitter program will exit with an error message that describes
+   which limit was reached and which data type needs to be changed.
+
+
+Database Format
+---------------
+
+   The other limitation in Routino is the number of objects stored in the
+   database that is generated by the planetsplitter data processing. This
+   number may be significantly different from the highest identifier in
+   the input data set for two reasons. Firstly any nodes, ways or
+   relations that have been deleted will not be present in the data.
+   Secondly when a partial planet database (continent, country or smaller)
+   is processed there will be only a fraction of the total number of
+   nodes, ways and relations.
+
+   The limiting factor is the largest of the following.
+
+    1. The number of nodes in the input data files.
+    2. The number of segments in the input data files.
+    3. The number of highways in the input data files.
+    4. The number of relations in the input data files.
+
+   Normally the number of nodes will be the limiting factor.
+
+
+32-bit Indexes
+- - - - - - -
+
+   Before version 1.2 the database could hold up to 4294967295 (2^32-1)
+   items of each type (node, segment, way) since an unsigned 32-bit
+   integer is used.
+
+   Versions 1.3 to 1.4.1 have a limit of 2147483647 (2^31-1) items since
+   half of the 32-bit integer range is reserved for fake nodes and
+   segments that are inserted if a waypoint is not close to a node.
+
+   From version 1.5 the limit is 4294901760 (2^32-2^16) for the number of
+   items of each type that can be stored. The small remaining part of the
+   32-bit unsigned integer range is reserved for fake nodes and segments.
+
+
+64-bit Indexes
+- - - - - - -
+
+   When using a 32-bit operating system it is not possible to create a
+   database that exceeds about 2GB in total. This will be fewer than 2^32
+   objects in the database in total. The use of 64-bit indexes will
+   require a 64-bit operating system.
+
+   From version 2.0.2 onwards it is possible to make a simple change to
+   the code to index the database objects with 64-bit integers insted of
+   32-bit integers.
+
+   To recompile with 64-bit index integers the file src/types.h should be
+   edited and the two lines below changed from:
+
+typedef uint32_t index_t;
+
+#define Pindex_t PRIu32
+
+   to:
+
+typedef uint64_t index_t;
+
+#define Pindex_t PRIu64
+
+   This change will affect nodes, segments, ways and relations together.
+   The database that is generated will no longer be compatible with
+   Routino that has been compiled with 32-bit indexes. The size of the
+   database will also grow by about 50% when this change is made.
+
+
+--------
+
+Copyright 2013 Andrew M. Bishop.
diff --git a/doc/Makefile b/doc/Makefile
index 3d1dc88..f094b9d 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -2,7 +2,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2010-2011 Andrew M. Bishop
+# This file Copyright 2010-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -18,26 +18,19 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Web file paths
+# All configuration is in the top-level Makefile.conf
 
-WEBDIR=../web/www/routino/documentation
+include ../Makefile.conf
 
 # Files to install
 
-HTML_FILES=html/*
+HTML_FILES=$(notdir $(wildcard html/*.html)) $(notdir $(wildcard html/*.css))
 TXT_FILES=*.txt
 TOP_FILES=../agpl-3.0.txt
 
 ########
 
 all:
-	-@[ -d $(WEBDIR) ] && \
-	  for file in $(HTML_FILES); do \
-	     if [ ! -f $(WEBDIR)/`basename $$file` ] || [ $$file -nt $(WEBDIR)/`basename $$file` ]; then \
-	        echo cp $$file $(WEBDIR) ;\
-	        cp -f $$file $(WEBDIR) ;\
-	     fi ;\
-	  done
 
 ########
 
@@ -45,38 +38,38 @@ test:
 
 ########
 
-install: all
-	-[ -d $(DESTDIR)$(docdir) ] || mkdir -p $(DESTDIR)$(docdir)
-	@[ -d $(DESTDIR)$(docdir) ] && \
-	  for file in $(TOP_FILES); do \
-	     echo cp $$file $(DESTDIR)$(docdir) ;\
-	     cp -f $$file $(DESTDIR)$(docdir) ;\
-	  done
-	@[ -d $(DESTDIR)$(docdir) ] && \
-	  for file in $(TXT_FILES); do \
-	     echo cp $$file $(DESTDIR)$(docdir) ;\
-	     cp -f $$file $(DESTDIR)$(docdir) ;\
-	  done
-	-[ -d $(DESTDIR)$(docdir)/html ] || mkdir -p $(DESTDIR)$(docdir)/html
-	@[ -d $(DESTDIR)$(docdir)/html ] && \
-	  for file in $(HTML_FILES); do \
-	     echo cp $$file $(DESTDIR)$(docdir)/html ;\
-	     cp -f $$file $(DESTDIR)$(docdir)/html ;\
-	  done
+install: install-txt install-html
+
+install-txt:
+	@[ -d $(DESTDIR)$(docdir) ] || mkdir -p $(DESTDIR)$(docdir)
+	@for file in $(TOP_FILES); do \
+	    echo cp $$file $(DESTDIR)$(docdir) ;\
+	    cp -f $$file $(DESTDIR)$(docdir) ;\
+	 done
+	@for file in $(TXT_FILES); do \
+	    echo cp $$file $(DESTDIR)$(docdir) ;\
+	    cp -f $$file $(DESTDIR)$(docdir) ;\
+	 done
+
+install-html:
+	@[ -d $(DESTDIR)$(docdir)/html ] || mkdir -p $(DESTDIR)$(docdir)/html
+	@for file in $(HTML_FILES); do \
+	    echo cp html/$$file $(DESTDIR)$(docdir)/html ;\
+	    cp -f html/$$file $(DESTDIR)$(docdir)/html ;\
+	 done
 
 ########
 
 clean:
 	rm -f *~
 	rm -f html/*~
-	rm -f $(WEBDIR)/*~
 
 ########
 
 distclean: clean
-	rm -f $(WEBDIR)/*
 
 ########
 
-top=-top
-include ../Makefile
+.PHONY:: all test install clean distclean
+
+.PHONY:: install-txt install-html
diff --git a/doc/NEWS.txt b/doc/NEWS.txt
index b2a92cd..fce77bc 100644
--- a/doc/NEWS.txt
+++ b/doc/NEWS.txt
@@ -1,3 +1,157 @@
+Version 2.7 of Routino released : Sat Mar 22 2014
+-------------------------------------------------
+
+Bug fixes:
+  Fix web-page CGI bug that did not allow more than 9 waypoints to be routed.
+  Fix typo in documentation strings in filedumper program.
+  Fix error in function prototype that stopped 64-bit node type being used.
+  Don't lose super-segments when merging them with normal segments.
+  Don't exceed the database lat/long limits when searching for visualiser data.
+
+planetsplitter:
+  Don't overflow (and wrap-around) conversions of lengths, weights etc.
+  Add some new formats of length, weight and speed parsing.
+  Add .xz uncompression as a compile-time option (default is disabled).
+
+router:
+  Remove ancient undocumented option to specify lat/lon without --lat/--lon.
+  Add a '--output-stdout' option to output the route in a selected format.
+  Add a '--reverse' option to calculate a route in the reverse order.
+  Add a '--loop' option to calculate a route that returns to the first waypoint.
+  Output valid HTML4 (use strict DTD and use numeric entity for apostrophe).
+
+OSM tagging:
+  Allow bicycles both ways on certain oneway roads if tagging allows.
+  Handle "access=bus" like "access=psv".
+
+Configuration Files:
+  Updated Dutch translations.
+  Updates to the XML parser tagging rules.
+  Added French translations for the routing output.
+
+Documentation:
+  Update the algorithm documentation for finding the shortest path.
+  Update documentation HTML to strict 4.01 DTD.
+
+Web pages:
+  Some changes to HTML, CSS formatting and Javascript to improve usability.
+  Added a French translation of the router web page.
+  Add the option to choose between OpenLayers and Leaflet for map rendering.
+  Check compatible with OpenLayers v2.13.1 and make this the default.
+  Create the router and visualiser pages from templates and translated phrases.
+
+
+Note: This version has removed specific support for IE6 and IE7 browsers.
+
+Note: This version is compatible with databases from version 2.6.
+
+
+Version 2.6 of Routino released : Sat Jul 6 2013
+------------------------------------------------
+
+Bug fixes:
+  Force '<if>...</if>' in tagging rules to match even with no input tags.
+  Built-in translations for GPX-route file gave nonsense durations.
+  Handle some cases that potentially caused divide by zero (not crashes).
+
+Compilation:
+  All configuration is now contained in the top level file Makefile.conf.
+  Default to using -ffast-math option for faster maths and glibc workaround.
+
+Code improvements:
+  Improve router internal data structures to increase performance.
+  Add another layer of caching to significantly speed up slim mode operation.
+  Add a layer of file buffering to significantly speed up reading/writing.
+  Enable more compile-time warnings and fix them.
+
+planetsplitter:
+  Create a binary log file to allow searching for errors geographically.
+  Simplify processing for changes (segment files not kept).
+  Don't prune isolated regions for transport types we don't have.
+
+Web pages (visualiser):
+  Allow displaying the error logs on the map.
+  Allow selecting any item displayed and showing more information about it.
+
+Extras:
+  Create a separate directory to put extra (non-essential) programs and scripts.
+  * tagmodifier - a tagging rule testing program.
+  * errorlog - a script to summarise the planetsplitter error log.
+  * plot-time - a script to plot a graph of the planetsplitter execution time.
+  * find-fixme - search an OSM file for "fixme" tags and display them on a map.
+
+
+Note: This version is not compatible with databases from previous versions.
+
+
+Version 2.5.1 of Routino released : Sat Apr 20 2013
+---------------------------------------------------
+
+Bug fixes:
+  Stop contradictory log error messages about 'access=foot' etc.
+  Move the HTML charset definition to within the first 1024 bytes of file.
+  Don't prune short segments in some cases that would change routing.
+  Fix bug with pruning straight highways around loops.
+  Fix some bugs with installation documents and scripts.
+  Fix Javascript to work with OpenLayers v2.11 again.
+  Fix XML character quoting for special characters in 7-bit ASCII range.
+  Fix bug with parsing XML containing UTF-8 characters four bytes long.
+  Fix two bugs for simple routes with the option of not passing a super-node.
+
+planetsplitter:
+  Improve the pruning of straight highways (detect larger straight sections).
+
+Configuration Files:
+  Accept some more tag values for OSM file parsing.
+  Handle alternate forms of mini roundabouts (junction=roundabout).
+
+
+Note: This version is compatible with databases from version 2.4 / 2.4.1 / 2.5.
+
+
+Version 2.5 of Routino released : Sun Feb 9 2013
+------------------------------------------------
+
+General:
+  Replace 'motorbike' with 'motorcycle' everywhere.
+
+planetsplitter/tagmodifier:
+  Major changes to file reading:
+    Faster XML parser.
+    Reads PBF files natively (not for changes, not tagmodifier).
+    Reads o5m/o5c files natively (not tagmodifier).
+    Reads bzip2 or gzip compressed files natively (if compiled for them).
+    Data can no longer be read from standard input.
+
+planetsplitter:
+  Report errors with self-intersecting ways, clarify some other error messages.
+
+Configuration Files:
+  Tagging configuration can now use an <ifnot> rule.
+  The tagging configuration <if> and <ifnot> rules can be nested.
+  Change the way that the multilane property is derived from the lanes tag.
+  Accept some more tag values for OSM file parsing.
+  German translation now supports roundabouts.
+
+Documentation:
+  Describe numerical limits (OSM identifiers and maximum database size).
+
+Web pages:
+  Allow different data and tile attributions for each map source.
+  Include MapQuest as an optional tile source.
+
+Web pages (visualiser):
+  Allow plotting segments of highways that have a particular property.
+
+
+Note: Starting with this version the planetsplitter and tagmodifier programs
+      will no longer read data from standard input.
+
+Note: Existing mapprops.js files need to be updated for this version.
+
+Note: This version is compatible with databases from version 2.4 / 2.4.1.
+
+
 Version 2.4.1 of Routino released : Mon Dec 17 2012
 ---------------------------------------------------
 
@@ -7,6 +161,7 @@ Bug fixes:
   Fix bug with printing log messages when output is not stdout (tagmodifier).
   Stop various crashes if trying to process file with no data (planetsplitter).
 
+
 Note: This version is compatible with databases from version 2.4.
 
 
@@ -16,7 +171,7 @@ Version 2.4 of Routino released : Sat Dec 8 2012
 Bug fixes:
   Fix pruning short segments in slim mode (gave different results to non-slim).
   Fix error with segment lengths for some segments from ways that are areas.
-  Fix latent bug with route relations when compiled for 64-bit IDs.
+  Fix latent bug with route relations when compiled for 64-bit way/relation IDs.
 
 router/planetsplitter:
   Replace all debugging "assert" statements with fatal error messages.
diff --git a/doc/README.txt b/doc/README.txt
index dc14a04..4a8b99b 100644
--- a/doc/README.txt
+++ b/doc/README.txt
@@ -13,7 +13,7 @@
    shortest or quickest) is determined. The route is calculated for
    OpenStreetMap highways (roads, paths etc) using one of the common forms
    of transport defined in OpenStreetMap (foot, bicycle, horse, motorcar,
-   motorbike etc).
+   motorcycle etc).
 
    When processing the OpenStreetMap data the types of highways are
    recorded and these set default limits on the types of traffic allowed.
@@ -70,22 +70,25 @@ Demonstration
 -------------
 
    A live demonstration of the router for the UK is available on the
-   internet:
+   internet in both OpenLayers and Leaflet versions:
+   http://www.routino.org/uk-leaflet/
+   http://www.routino.org/uk-openlayers/
 
-   http://www.routino.org/uk/router.html
+   http://www.routino.org/uk/
 
-   The source code download available also includes a set of files that can
-   be used to create your own interactive map.
+   The source code download available below also includes a set of files
+   that can be used to create your own interactive map.
 
-   The interactive map is made possible by use of the OpenLayers Javascript
-   library from http://www.openlayers.org/.
+   The interactive map is made possible by use of the OpenLayers or
+   Leaflet Javascript library from http://www.openlayers.org/ or
+   http://leafletjs.com/.
 
 
 Documentation
 -------------
 
-   The algorithm used is described in the file ALGORITHM.txt and some
-   notes about the limitations of the data is in DATA.txt.
+   The algorithm used is described in the file ALGORITHM.txt with some notes
+   about the input data in DATA.txt and numerical limitations in LIMITS.txt.
 
    The configuration files and in particular the default set of rules for
    processing the OpenStreetMap data tags are described in detail in
@@ -120,6 +123,10 @@ Status
    Version 2.3.2 of Routino was released on 6th October 2012.
    Version 2.4 of Routino was released on 8th December 2012.
    Version 2.4.1 of Routino was released on 17th December 2012.
+   Version 2.5 of Routino was released on 9th February 2013.
+   Version 2.5.1 of Routino was released on 20th April 2013.
+   Version 2.6 of Routino was released on 6th July 2013.
+   Version 2.7 of Routino was released on 22nd March 2014.
 
    The full version history is available in the NEWS.txt file.
 
@@ -141,9 +148,7 @@ License
 Copyright
 ---------
 
-   Routino is copyright Andrew M. Bishop 2008-2012.
-
-   Contact amb at gedanken.demon.co.uk for any questions or queries.
+   Routino is copyright Andrew M. Bishop 2008-2014.
 
 
 Homepage
@@ -161,7 +166,21 @@ Download
 
    http://www.routino.org/download/
 
+Subversion
+- - - - -
+
+   The source code can also be downloaded from the Subversion repository
+   with a command like the following:
+
+   svn co http://gedanken.org.uk/svn/cxref/trunk cxref
+
+   The source code can also be browsed in the Subversion viewer which also
+   has a list of the latest changes:
+
+   http://www.routino.org/viewvc/trunk/
+   http://www.routino.org/viewvc/trunk/?view=log
+
 
 --------
 
-Copyright 2008-2012 Andrew M. Bishop.
+Copyright 2008-2014 Andrew M. Bishop.
diff --git a/doc/TAGGING.txt b/doc/TAGGING.txt
index 615234e..2161848 100644
--- a/doc/TAGGING.txt
+++ b/doc/TAGGING.txt
@@ -35,7 +35,7 @@ Transport Specific Tags
 - - - - - - - - - - - -
 
    One tag is recognised for each of the different modes of transport:
-   foot, horse, bicycle, wheelchair, moped, motorbike, motorcar, goods,
+   foot, horse, bicycle, wheelchair, moped, motorcycle, motorcar, goods,
    hgv and psv. These indicate whether the specific type of transport is
    allowed to pass through the node or not.
 
@@ -43,11 +43,11 @@ Transport Specific Tags
    a node and specific tags must be used to remove the permissions for the
    transport.
 
-The highway Tag
-- - - - - - - -
+The roundabout Tag
+- - - - - - - - -
 
-   The highway tag for mini_roundabout is recognised and used to improve
-   the description of the route.
+   The roundabout tag for mini-roundabouts is recognised and used to
+   improve the description of the route.
 
 
 Way Tags And Attributes
@@ -90,7 +90,7 @@ Transport Specific Tags
 - - - - - - - - - - - -
 
    One tag is recognised for each of the different modes of transport:
-   foot, horse, bicycle, wheelchair, moped, motorbike, motorcar, goods,
+   foot, horse, bicycle, wheelchair, moped, motorcycle, motorcar, goods,
    hgv and psv. These indicate whether the specific type of transport is
    allowed on the highway or not.
 
@@ -110,13 +110,12 @@ The ref Tag
    The ref tag is used to provide the label for the highway when printing
    the results.
 
-The multilane Tag
-- - - - - - - - -
+The lanes Tag
+- - - - - - -
 
-   The multilane tag is used to identify whether a highway has multiple
-   lanes for traffic and this sets one of the highway properties. There is
-   not normally a multilane tag but one needs to be added by the tag
-   processing transformations. Values of yes or no are expected.
+   The lanes tag is used to identify whether a highway has multiple lanes
+   for traffic and this is used to derive the multilane highway
+   properties.
 
 The paved Tag
 - - - - - - -
@@ -126,6 +125,12 @@ The paved Tag
    in the original data but normally the surface tag needs to be
    transformed into the paved tag.
 
+The multilane Tag
+- - - - - - - - -
+
+   The multilane tag is used to indicate that a highway has multiple lanes
+   for traffic.
+
 The bridge Tag
 - - - - - - -
 
@@ -156,6 +161,12 @@ The bicycleroute Tag
    highways by looking for route relations that are designated for bicycle
    access.
 
+The cyclebothways Tag
+- - - - - - - - - - -
+
+   The cyclebothways tag is used to identify whether a highway allows
+   cycling in the opposite direction to a signposted oneway restriction.
+
 The oneway Tag
 - - - - - - -
 
@@ -271,32 +282,32 @@ Barrier Defaults
    each type of node. This uses the barrier tag in the OSM file and
    converts it into a default set of disallowed transport types.
 
-   Barrier       foot horse wheelchair bicycle moped motorbike motorcar goods hgv psv
-   -------       ---- ----- ---------- ------- ----- --------- -------- ----- --- ---
-   kissing_gate, footgate, stile, v_stile, turnstile, squeeze, squeeze_stile,
+   Barrier       foot horse wheelchair bicycle moped motorcycle motorcar goods hgv psv
+   -------       ---- ----- ---------- ------- ----- ---------- -------- ----- --- ---
+   kissing_gate, footgate, stile, v_stile, turnstile, squeeze,  squeeze_stile,
    cycle_barrier, bicycle_barrier
-                 yes  no    no         no      no    no        no       no    no  no
+                 yes  no    no         no      no    no         no       no    no  no
    horse_stile, horse_jump, step_over
-                 yes  yes   no         no      no    no        no       no    no  no
+                 yes  yes   no         no      no    no         no       no    no  no
    horse_barrier, cattle_grid
-                 yes  no    yes        yes     yes   yes       yes      yes   yes yes
+                 yes  no    yes        yes     yes   yes        yes      yes   yes yes
    motorcyle_barrier
-                 yes  yes   yes        yes     no    no        no       no    no  no
+                 yes  yes   yes        yes     no    no         no       no    no  no
    bollard, car_barrier, car_trap
-                 yes  yes   yes        yes     yes   yes       no       no    no  no
+                 yes  yes   yes        yes     yes   yes        no       no    no  no
 
 Generic Access Permissions
 - - - - - - - - - - - - -
 
    The access tag is used to specify the default access restrictions
-   through the node. If the tag value is no or private or limited then all
-   transport types are denied access (later tag transformation rules may
-   add specific transport types back again).
+   through the node. If the tag value is no or private or a selection of
+   other values then all transport types are denied access (later tag
+   transformation rules may add specific transport types back again).
 
 Other Access Permissions
 - - - - - - - - - - - -
 
-   A tag named vehicle means any of the bicycle, moped, motorbike,
+   A tag named vehicle means any of the bicycle, moped, motorcycle,
    motorcar, goods, hgv and psv transport types. A tag named motor_vehicle
    is transformed to mean any vehicle except a bicycle.
 
@@ -307,7 +318,7 @@ Specific Access Permissions
    transport type tags.
 
    One tag is recognised for each of the different modes of transport:
-   foot, horse, bicycle, wheelchair, moped, motorbike, motorcar, goods,
+   foot, horse, bicycle, wheelchair, moped, motorcycle, motorcar, goods,
    hgv and psv. These indicate whether the specific type of transport is
    allowed through the node or not; the values listed for the access tag
    are also accepted here.
@@ -315,7 +326,9 @@ Specific Access Permissions
 Mini-roundabouts
 - - - - - - - -
 
-   The highway tag for mini_roundabout is passed through without change.
+   If the highway tag has the value mini_roundabout or the junction tag
+   has the value roundabout then a junction tag with value roundaboutis
+   passed through.
 
 
 Way Tag Transformations
@@ -330,8 +343,14 @@ Highway Defaults
    defining the default allowed transport types and adding a number of
    properties.
 
-   The first part of the transformation is to convert the highway tag into
-   one that is recognised by Routino.
+   The first part of the highway tag checking is to ignore the highway tag
+   if it has a value that indicates a non-highway. These are the proposed
+   and construction values for future highways, the no, abandoned and
+   disused values for previous highways and a small number of other
+   values.
+
+   The second part of the highway transformation is to convert the highway
+   tag into one that is recognised by Routino.
 
    Original tag                            Transformed tag
    ------------                            ---------------
@@ -354,58 +373,62 @@ Highway Defaults
    transport allowed on the highway. The default assumptions are as shown
    in the table below.
 
-   Highway      foot horse  wheelchair bicycle moped motorbike motorcar goods hgv psv
-   -------      ---- -----  ---------- ------- ----- --------- -------- ----- --- ---
-   motorway     no   no     no         no      no    yes       yes      yes   yes yes
-   trunk        no   no     no         yes     yes   yes       yes      yes   yes yes
-   primary      yes  yes    yes        yes     yes   yes       yes      yes   yes yes
-   secondary    yes  yes    yes        yes     yes   yes       yes      yes   yes yes
-   tertiary     yes  yes    yes        yes     yes   yes       yes      yes   yes yes
-   unclassified yes  yes    yes        yes     yes   yes       yes      yes   yes yes
-   residential  yes  yes    yes        yes     yes   yes       yes      yes   yes yes
-   service      yes  yes    yes        yes     yes   yes       yes      yes   yes yes
-   track        yes  yes    yes        yes     no    no        no       no    no  no
-   cycleway     yes  no     yes        yes     no    no        no       no    no  no
-   path         yes  yes(1) yes        yes(1)  no    no        no       no    no  no
-   steps        yes  no     no         no      no    no        no       no    no  no
-
-   Note 1: A path allows bicycle or horse access by default only if
+   Highway      foot  horse  wheelchair bicycle moped motorcycle motorcar goods hgv psv
+   -------      ----  -----  ---------- ------- ----- ---------  -------- ----- --- ---
+   motorway     no    no     no         no      no    yes        yes      yes   yes yes
+   trunk        no(1) no(1)  no(1)      yes     yes   yes        yes      yes   yes yes
+   primary      yes   yes    yes        yes     yes   yes        yes      yes   yes yes
+   secondary    yes   yes    yes        yes     yes   yes        yes      yes   yes yes
+   tertiary     yes   yes    yes        yes     yes   yes        yes      yes   yes yes
+   unclassified yes   yes    yes        yes     yes   yes        yes      yes   yes yes
+   residential  yes   yes    yes        yes     yes   yes        yes      yes   yes yes
+   service      yes   yes    yes        yes     yes   yes        yes      yes   yes yes
+   track        yes   yes    yes        yes     no    no         no       no    no  no
+   cycleway     yes   no     yes        yes     no    no         no       no    no  no
+   path         yes   yes(2) yes        yes(2)  no    no         no       no    no  no
+   steps        yes   no     no         no      no    no         no       no    no  no
+
+   Note 1: A trunk road may legally allow foot, horse or wheelchair access
+   but in the absence of other tags is considered to be too dangerous.
+   Note 2: A path allows bicycle or horse access by default only if
    actually labelled as a highway of type "bridleway".
 
-   Finally for the highway tag a number of properties are added depending
-   on the highway type.
+   Finally for the highway tag a number of default properties are added
+   depending on the highway type.
 
    Highway      Properties
    -------      ----------
    motorway     paved, oneway, multilane
-   trunk        paved
-   primary      paved
+   trunk        paved, multilane (1)
+   primary      paved, multilane (1)
    secondary    paved
    tertiary     paved
    unclassified paved
    residential  paved
    service      paved
-   track        paved (1)
+   track        paved (2)
    cycleway     paved
-   path         paved (2)
+   path         paved (3)
    steps
 
-   Note 1: A track is paved only if it is tagged as tracktype=grade1.
-   Note 2: A path is paved only if it was originally tagged as
+   Note 1: A highway of this type has the multilane property by default if
+   it is oneway.
+   Note 2: A track is paved only if it is tagged as tracktype=grade1.
+   Note 3: A path is paved only if it was originally tagged as
    highway=walkway or highway=pedestrian.
 
 Generic Access Permissions
 - - - - - - - - - - - - -
 
    The access tag is used to specify the default access restrictions on
-   the highway. If the tag value is no or private then all transport types
-   are denied access (later tag transformation rules may add specific
-   transport types back again).
+   the highway. If the tag value is no or private or a selection of other
+   values then all transport types are denied access (later tag
+   transformation rules may add specific transport types back again).
 
 Other Access Permissions
 - - - - - - - - - - - -
 
-   A tag named vehicle means any of the bicycle, moped, motorbike,
+   A tag named vehicle means any of the bicycle, moped, motorcycle,
    motorcar, goods, hgv and psv transport types. A tag named motor_vehicle
    is transformed to mean any vehicle except a bicycle.
 
@@ -419,7 +442,7 @@ Other Access Permissions
    restricted_byway          foot=yes, wheelchair=yes, horse=yes, bicycle=yes
    public_byway, byway,
    byway_open_to_all_traffic foot=yes, wheelchair=yes, horse=yes, bicycle=yes,
-                             moped=yes, motorbike=yes, motorcar=yes
+                             moped=yes, motorcycle=yes, motorcar=yes
    permissive_bridleway,
    public_bridleway,
    bridleway                 foot=yes, wheelchair=yes, horse=yes, bicycle=yes
@@ -428,6 +451,20 @@ Other Access Permissions
    public_footpath,
    footpath                  foot=yes, wheelchair=yes
 
+   In addition to these there are some other tags and values that will
+   modify the transport permissions on the highway.
+
+   A highway that is tagged as motorroad with a value of yes will deny
+   access to foot, horse, wheelchair, bicycle and moped transport.
+
+   A highway that is tagged with footway or sidewalk and one of a set of
+   popular values will allow foot and wheelchair access even if the road
+   type would not normally do so.
+
+   A highway that is tagged as cycleway with one of several values will
+   allow bicycle access. If the value of the cycleway tag is opposite_lane
+   then the cyclebothways tag is set.
+
 Specific Access Permissions
 - - - - - - - - - - - - - -
 
@@ -435,7 +472,7 @@ Specific Access Permissions
    transport type tags.
 
    One tag is recognised for each of the different modes of transport:
-   foot, horse, bicycle, wheelchair, moped, motorbike, motorcar, goods,
+   foot, horse, bicycle, wheelchair, moped, motorcycle, motorcar, goods,
    hgv and psv. These indicate whether the specific type of transport is
    allowed on the highway or not.
 
@@ -449,10 +486,8 @@ Highway Properties
    Support for the obsolete paved tag is also provided and the highway is
    paved if this is set to a true value.
 
-   The lanes tag is used to identify whether a highway has multiple lanes
-   for traffic or not (the number of lanes is not important in this case,
-   only whether it is more than one) this sets one of the highway
-   properties.
+   The lanes tag is passed through to be used to set the multilane highway
+   property.
 
    The bridge and tunnel tags are copied directly from the input to the
    output.
@@ -510,4 +545,4 @@ Turn Restrictions
 
 --------
 
-Copyright 2008-2012 Andrew M. Bishop.
+Copyright 2008-2013 Andrew M. Bishop.
diff --git a/doc/USAGE.txt b/doc/USAGE.txt
index 49d866c..376e809 100644
--- a/doc/USAGE.txt
+++ b/doc/USAGE.txt
@@ -11,6 +11,7 @@
    parsed data for test purposes and the fifth is a test program for the
    tag transformations.
 
+
 planetsplitter
 --------------
 
@@ -31,7 +32,12 @@ planetsplitter
                          [--prune-isolated=<len>]
                          [--prune-short=<len>]
                          [--prune-straight=<len>]
-                         [<filename.osm> ...]
+                         [<filename.osm> ... | <filename.osc> ...
+                          | <filename.pbf> ...
+                          | <filename.o5m> ... | <filename.o5c> ...
+                          | <filename.(osm|osc|o5m|o5c).bz2> ...
+                          | <filename.(osm|osc|o5m|o5c).gz> ...
+                          | <filename.(osm|osc|o5m|o5c).xz> ...]
 
    --help
           Prints out the help information.
@@ -79,7 +85,9 @@ planetsplitter
           Log OSM parsing and processing errors to 'error.log' or the
           specified file name (the '--dir' and '--prefix' options are
           applied). If the --append option is used then the existing log
-          file will be appended, otherwise a new one will be created.
+          file will be appended, otherwise a new one will be created. If
+          the --keep option is also used a geographically searchable
+          database of error logs is created for use in the visualiser.
 
    --parse-only
           Parse the input files and store the data in intermediate files
@@ -119,7 +127,7 @@ planetsplitter
    --prune-isolated=<length>
           Remove the access permissions for a transport type from small
           disconnected groups of segments and remove the segments if they
-          nd up with no access permission (defaults to removing groups
+          end up with no access permission (defaults to removing groups
           under 500m).
 
    --prune-short=<length>
@@ -130,22 +138,21 @@ planetsplitter
           Remove nodes in almost straight highways (defaults to removing
           nodes up to 3m offset from a straight line).
 
-   <filename.osm> ...
-          Specifies the filename(s) to read data from, by default data is
-          read from the standard input.
-
-   Note: In version 1.4 of Routino the --transport, --not-highway and
-   --not-property options have been removed. The same functionality can be
-   achieved by editing the tagging rules file to not output unwanted data.
-
-   Note: In version 1.5 of Routino the --slim option has been removed but
-   at compilation time a separate program called planetsplitter-slim is
-   created that operates in slim mode. In slim mode the temporary files
-   and database files are read as needed rather than being mapped into
-   memory. This allows a database size greater than 2 GB on 32-bit
-   machines or usage with little or no virtual memory (e.g. some virtual
-   machines). The penalty for this is that the program takes about twice
-   as long to run.
+   <filename.osm>, <filename.osc>, <filename.pbf>, <filename.o5m>,
+          <filename.o5c>
+          Specifies the filename(s) to read data from. Filenames ending
+          '.pbf' will be read as PBF, filenames ending in '.o5m' or '.o5c'
+          will be read as O5M/O5C, otherwise as XML. Filenames ending
+          '.bz2' will be bzip2 uncompressed (if bzip2 support compiled
+          in). Filenames ending '.gz' will be gzip uncompressed (if gzip
+          support compiled in). Filenames ending '.xz' will be xz
+          uncompressed (if xz support compiled in).
+
+   Note: In version 2.5 of Routino the ability to read data from the
+   standard input has been removed. This is because there is now the
+   ability to read compressed files (bzip2, gzip, xz) and PBF files
+   directly. Also using standard input the file type cannot be
+   auto-detected from the filename.
 
    Example usage 1:
 
@@ -170,7 +177,7 @@ planetsplitter
 
    Example usage 3:
 
-   planetsplitter --dir=data --prefix=gb --keep    great_britain_part.osm
+   planetsplitter --dir=data --prefix=gb --keep    great_britain.osm
 
    planetsplitter --dir=data --prefix=gb --changes great_britain.osc
 
@@ -185,6 +192,7 @@ planetsplitter
    the initial OSM file(s) and the --changes option used with --parse-only
    or --process-only for every OSC file.
 
+
 router
 ------
 
@@ -200,13 +208,14 @@ router
                  [--output-html]
                  [--output-gpx-track] [--output-gpx-route]
                  [--output-text] [--output-text-all]
-                 [--output-none]
+                 [--output-none] [--output-stdout]
                  [--profile=<name>]
                  [--transport=<transport>]
                  [--shortest | --quickest]
                  --lon1=<longitude> --lat1=<latitude>
                  --lon2=<longitude> --lon2=<latitude>
                  [ ... --lon99=<longitude> --lon99=<latitude>]
+                 [--reverse] [--loop]
                  [--heading=<bearing>]
                  [--highway-<highway>=<preference> ...]
                  [--speed-<highway>=<speed> ...]
@@ -292,6 +301,10 @@ router
    --output-none
           Do not generate any output or read in any translations files.
 
+   --output-stdout
+          Write to stdout instead of a file (requires exactly one output
+          format option, implies '--quiet').
+
    --profile=<name>
           Specifies the name of the profile to use.
 
@@ -302,8 +315,8 @@ router
           + horse = Horse
           + wheelchair = Wheelchair
           + bicycle = Bicycle
-          + moped = Moped (Small motorbike, limited speed)
-          + motorbike = Motorbike
+          + moped = Moped (Small motorcycle, limited speed)
+          + motorcycle = Motorcycle
           + motorcar = Motorcar
           + goods = Goods (Small lorry, van)
           + hgv = HGV (Heavy Goods Vehicle - large lorry)
@@ -328,6 +341,13 @@ router
           sequence. The algorithm will use the closest node or point
           within a segment that allows the specified traffic type.
 
+   --reverse
+          Find a route between the waypoints in reverse order.
+
+   --loop
+          Find a route that returns to the first waypoint after the last
+          one.
+
    --heading=<bearing>
           Specifies the initial direction of travel at the start of the
           route (from the lowest numbered waypoint) as a compass bearing
@@ -403,11 +423,6 @@ router
           that the length limit on the highway is not exceeded. Default
           value depends on the profile selected by the --transport option.
 
-   Note: In version 1.5 of Routino a slim option has been added and at
-   compilation time a separate program called router-slim is created that
-   operates in slim mode. In slim mode the database files are read as
-   needed rather than being mapped into memory.
-
    The meaning of the <preference> parameter in the command line options
    is slightly different for the highway preferences and the property
    preferences. For the highway preference consider the choice between two
@@ -424,13 +439,13 @@ router
    preference for the highway type and all of the preferences for the
    highway properties.
 
-   Example usage (motorbike journey, scenic route, not very fast):
+   Example usage (motorcycle journey, scenic route, not very fast):
 
-   router --dir=data --prefix=gb --transport=motorbike --highway-motorway=0 \
+   router --dir=data --prefix=gb --transport=motorcycle --highway-motorway=0 \
           --highway-trunk=0 --speed-primary=80 --speed-secondary=80 --quickest
 
    This will use the files 'data/gb-nodes.mem', 'data/gb-segments.mem' and
-   'data/gb-ways.mem' to find the quickest route by motorbike not using
+   'data/gb-ways.mem' to find the quickest route by motorcycle not using
    motorways or trunk roads and not exceeding 80 km/hr.
 
 
@@ -450,10 +465,15 @@ filedumper
                      [--dump [--node=<node> ...]
                              [--segment=<segment> ...]
                              [--way=<way> ...]
-                             [--turn-relation=<relation> ...]]
+                             [--turn-relation=<relation> ...]
+                             [--errorlog=<number> ...]]
                      [--dump-osm [--no-super]
                                  [--latmin=<latmin> --latmax=<latmax>
                                   --lonmin=<lonmin> --lonmax=<lonmax>]]
+                     [--dump-visualiser [--data=node<node>]
+                                        [--data=segment<segment>]
+                                        [--data=turn-relation<rel>]
+                                        [--data=errorlog<number>]]
 
    --help
           Prints out the help information.
@@ -496,6 +516,9 @@ filedumper
                o height = height limits.
                o width = width limits.
                o length = length limits.
+               o property-* = segments having the specified property (e.g.
+                 property-paved to display segments of paved highway).
+               o errorlogs = errors logged during parsing.
 
    --dump
           Selects a data dumping mode which allows looking at individual
@@ -521,6 +544,10 @@ filedumper
                 number (internal number, not the relation id number in the
                 original source file).
 
+        --errorlog=<number>
+                Prints the information about the selected error log that
+                was stored when the data was parsed.
+
    --osm-dump
           Dumps the contents of the database as an OSM format XML file,
           the whole database will be dumped unless the latitude and
@@ -535,10 +562,27 @@ filedumper
         --lonmin=<lonmin> --lonmax=<lonmax>
                 The range of longitudes to dump the data for.
 
-   Note: In version 1.5 of Routino a slim option has been added and at
-   compilation time a separate program called filedumper-slim is created
-   that operates in slim mode. In slim mode the database files are read as
-   needed rather than being mapped into memory.
+   --dump-visualiser
+          Dumps the contents of the database as HTML formatted items for
+          display in the visualiser web page.
+
+        --data=node<node>
+                Prints the information about the selected node number
+                (internal node number, not from the original source file).
+
+        --data=segment<segment>
+                Prints the information about the selected segment number
+                as if it was a way (internal segment number, unrelated to
+                original source file).
+
+        --data=turn-relation<relation>
+                Prints the information about the selected turn relation
+                number (internal turn relation number, not from the
+                original source file).
+
+        --data=errorlog<number>
+                Prints the information about the selected error log that
+                was stored when the data was parsed.
 
 
 filedumperx
@@ -550,13 +594,12 @@ filedumperx
    for test purposes only and gives no useful information about the
    routing database.
 
-   Usage: filedumper [--help]
-                     [--dir=<dirname>] [--prefix=<name>]
-                     [--dump [--nodes]
-                             [--segments]
-                             [--ways]
-                             [--route-relations]
-                             [--turn-relations]]
+   Usage: filedumperx [--help]
+                      [--dir=<dirname>] [--prefix=<name>]
+                      [--dump [--nodes]
+                              [--ways]
+                              [--route-relations]
+                              [--turn-relations]]
 
    --help
           Prints out the help information.
@@ -576,9 +619,6 @@ filedumperx
         --nodes
                 Dumps the node data.
 
-        --segments
-                Dumps the segment data.
-
         --ways
                 Dumps the way data.
 
@@ -589,34 +629,6 @@ filedumperx
                 Dumps the turn relation data.
 
 
-tagmodifier
------------
-
-   This program is used to run the tag transformation process on an OSM
-   XML file for test purposes.
-
-   Usage: tagmodifier [--help]
-                      [--loggable]
-                      [--tagging=<filename>]
-                      [<filename.osm>]
-
-   --help
-          Prints out the help information.
-
-   --loggable
-          Print progress messages that are suitable for logging to a file;
-          normally an incrementing counter is printed which is more
-          suitable for real-time display than logging.
-
-   --tagging=<filename>
-          The name of the XML file containing the tagging rules (defaults
-          to 'tagging.xml' in the current directory).
-
-   <filename.osm> ...
-          Specifies the filename to read data from, by default data is
-          read from the standard input.
-
-
 --------
 
-Copyright 2008-2012 Andrew M. Bishop.
+Copyright 2008-2014 Andrew M. Bishop.
diff --git a/doc/html/algorithm.html b/doc/html/algorithm.html
index fbc5964..7361905 100644
--- a/doc/html/algorithm.html
+++ b/doc/html/algorithm.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Algorithm</title>
 
 <!--
  Routino documentation - algorithm
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Algorithm</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Algorithm</h1>
 
@@ -45,12 +47,12 @@
 
 <div class="content">
 
-<h2><a name="H_1_1"></a>Algorithms</h2>
+<h2 id="H_1_1">Algorithms</h2>
 
 This page describes the development of the algorithm that is used in Routino for
 finding routes.
 
-<h3><a name="H_1_1_1"></a>Simplest Algorithm</h3>
+<h3 id="H_1_1_1">Simplest Algorithm</h3>
 
 The algorithm to find a route is fundamentally simple: Start at the beginning,
 follow all possible routes and keep going until you reach the end.
@@ -59,7 +61,7 @@ While this method does work, it isn't fast.  To be able to find a route quickly
 needs a different algorithm, one that can find the correct answer without
 wasting time on routes that lead nowhere.
 
-<h3><a name="H_1_1_2"></a>Improved Algorithm</h3>
+<h3 id="H_1_1_2">Improved Algorithm</h3>
 
 The simplest way to do this is to follow all possible segments from the starting
 node to the next nearest node (an intermediate node in the complete journey).
@@ -95,7 +97,7 @@ complexity is proportional to the number of nodes or segments in all routes
 examined between the start and end nodes.  Maintaining the list of intermediate
 nodes in order is the most complex part.
 
-<h3><a name="H_1_1_3"></a>Final Algorithm</h3>
+<h3 id="H_1_1_3">Final Algorithm</h3>
 
 The final algorithm that is implemented in the router is basically the one above
 but with an important difference.  Instead of finding a long route among a data
@@ -118,23 +120,23 @@ choosing the shortest route is that since all segments considered have identical
 properties they will be treated identically when properties are taken into
 account.  This decision making process can be repeated until the only the most
 important and interesting nodes remain.
-<p align="center">
+<p class="center">
 <img alt="Original data" src="example0.png">
 <br>
 Original Highways
-<p align="center">
+<p class="center">
 <img alt="Iteration 1" src="example1.png">
 <br>
 First Iteration
-<p align="center">
+<p class="center">
 <img alt="Iteration 2" src="example2.png">
 <br>
 Second Iteration
-<p align="center">
+<p class="center">
 <img alt="Iteration 3" src="example3.png">
 <br>
 Third Iteration
-<p align="center">
+<p class="center">
 <img alt="Iteration 4" src="example4.png">
 <br>
 Fourth Iteration
@@ -172,7 +174,7 @@ node is added to an existing segment.  This requires special handling in the
 algorithm but it gives mode flexibility for the start, finish and intermediate
 points in a route.
 
-<h4><a name="H_1_1_3_1"></a>Algorithm Evolution</h4>
+<h4 id="H_1_1_3_1">Algorithm Evolution</h4>
 
 In Routino versions 1.0 to 1.4 the algorithm used to select a super-node was the
 same as above except that node properties were not included.  Routino versions
@@ -183,7 +185,28 @@ restriction that is different from connecting segments in versions 1.5 and
 requires the original algorithm since the super-segments more accurately reflect
 the underlying topology.
 
-<h3><a name="H_1_1_4"></a>Routing Preferences</h3>
+<h4 id="H_1_1_3_2">Algorithm Implementation</h4>
+
+The algorithm that is used for finding the route between the super-nodes using
+super-segments is the A* algorithm (or a slight variation of it).  This was not
+a deliberate design decision, but evolved into it during development.  This
+algorithm relies on calculating the lowest score (shortest distance or quickest
+time) to each node from the starting node.  The remaining score for the path to
+the destination node is estimated (based on a straight line using the fastest
+type of highway) and added to the current score and the result recorded.  At
+each step the unvisited node that has the lowest current score is examined and
+all nodes connected to it have their scores calculated.  When the destination
+node has been reached all remaining unvisited nodes with scores higher than the
+destination node's score can be discarded and the few remaining nodes examined.
+<p>
+The algorithm used to find the route between super-nodes using normal segments
+is Dijkstra's algorithm (although it is implemented as the same algorithm as
+above but with no estimated cost).  Since these routes tend to be short and the
+CPU time for calculating the heuristic cost function is relatively large this
+tends to give a quicker solution.
+
+
+<h3 id="H_1_1_4">Routing Preferences</h3>
 
 One of the important features of Routino is the ability to select a route that
 is optimum for a set of criteria such as preferences for each type of highway,
@@ -225,7 +248,7 @@ minimising the length.
   property preferences close to 50% do not cause large variations in routes.
 </dl>
 
-<h3><a name="H_1_1_5"></a>Data Pruning</h3>
+<h3 id="H_1_1_5">Data Pruning</h3>
 
 From version 2.2 there are options to "prune" nodes and segments from the input
 data which means to remove nodes and/or segments without significantly changing
@@ -269,7 +292,7 @@ The pruning options that are available are:
   taken not to remove node access permissions or changes in way properties.
 </ul>
 
-<h3><a name="H_1_1_6"></a>Turn Restrictions</h3>
+<h3 id="H_1_1_6">Turn Restrictions</h3>
 
 The addition of turn restrictions in version 2.0 adds a set of further
 complications because it introduces a set of constraints that are far more
@@ -298,7 +321,9 @@ restrictions a route could be defined purely by the set of nodes that were
 passed; no node would exist more than once along a route between two points.
 With turn restrictions the route is defined by a node and the segment used to
 get there; no route between two points will ever need to follow the same segment
-in the same direction more than once.
+in the same direction more than once.  This means that the optimisation
+algorithm calculates scores for directed segments (indexed by segment and end
+node) rather than for nodes.
 <p>
 A side-effect of this is that a route that works around a turn restriction must
 be calculable using the super-segments that are stored in the database.  This
@@ -328,7 +353,7 @@ section is itself optimum based on the start conditions.
 Overall the presence of turn restrictions in the database makes the routing
 slower even for regions of the map that have no turn restrictions.
 
-<h3><a name="H_1_1_7"></a>Data Implementation</h3>
+<h3 id="H_1_1_7">Data Implementation</h3>
 
 The hardest part of implementing this router is the data organisation.  The
 arrangement of the data to minimise the number of operations required to follow
@@ -367,53 +392,23 @@ turn restriction relations the initial and final segments are stored along with
 the restricted node itself.  Each node that has a turn restriction is marked in
 the main node storage with a flag to indicate this information.
 
-
-<h3><a name="H_1_1_8"></a>Practicalities</h3>
-
-At the time of writing (April 2010) the OpenStreetMap data for Great Britain
-(taken from
-<a class="ext" href="http://download.geofabrik.de/osm/europe/" title="GeoFabrik Mirror of OpenStreetMap Data">GeoFabrik</a>
-) contains:
-<ul>
-  <li>14,675,098 nodes
-  <ul>
-    <li>8,767,521 are highway nodes
-    <li>1,120,297 are super-nodes
-  </ul>
-  <li>1,876,822 ways
-  <ul>
-    <li>1,412,898 are highways
-    <ul>
-      <li>9,316,328 highway segments
-      <li>1,641,009 are super-segments
-    </ul>
-  </ul>
-  <li>60,572 relations
-</ul>
-
-The database files when generated are 41.5 MB for nodes, 121.6 MB for segments
-and 12.6 MB for ways and are stored uncompressed.  By having at least 200 MB or
-RAM available the routing can be performed with no disk accesses (once the data
-has been read once).
-
-
 </div>
 
 <!-- Content End -->
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/configuration.html b/doc/html/configuration.html
index 93a60cf..9fe7901 100644
--- a/doc/html/configuration.html
+++ b/doc/html/configuration.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Configuration</title>
 
 <!--
  Routino documentation - configuration
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2011 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Configuration</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Configuration</h1>
 
@@ -45,7 +47,7 @@
 
 <div class="content">
 
-<h2><a name="H_1_1"></a>XML Configuration Files</h2>
+<h2 id="H_1_1">XML Configuration Files</h2>
 
 New in version 1.4 of Routino is the use of configuration files to allow more
 information to be provided to the programs at run-time.  The configuration files
@@ -60,7 +62,7 @@ In keeping with the nature of the input and output files the configuration files
 are also XML files.  Each of the files uses a custom defined XML schema and an
 XSD file is provided for each of them.
 
-<h3><a name="H_1_1_1" title="Tagging rules"></a>Tag Transformation Rules</h3>
+<h3 id="H_1_1_1" title="Tagging rules">Tag Transformation Rules</h3>
 
 The default name of the tagging transformation rules XML configuration file
 is <em>tagging.xml</em> in the same directory as the generated database files.
@@ -90,7 +92,7 @@ for motorway_link and motorway highway types.
     <if k="highway" v="motorway">
       <output k="highway"/>
 
-      <output k="motorbike"  v="yes"/>
+      <output k="motorcycle" v="yes"/>
       <output k="motorcar"   v="yes"/>
       <output k="goods"      v="yes"/>
       <output k="hgv"        v="yes"/>
@@ -99,6 +101,8 @@ for motorway_link and motorway highway types.
       <output k="paved"      v="yes"/>
       <output k="multilane"  v="yes"/>
       <output k="oneway"     v="yes"/>
+
+      <unset k="highway"/>
     </if>
 ...
   <way>
@@ -106,28 +110,82 @@ for motorway_link and motorway highway types.
 </routino-tagging>
 </pre>
 
-The rules all have the same format; an <em>if</em> element for matching the
-input and some <em>set</em> or <em>output</em> elements to either change the
-input tags or create an output tag.  The <em>k</em> and <em>v</em> attributes
-have the same meaning as the attributes with the same names in the OSM XML file
-- the tag key and tag value.
+The rules all have the same format; an <em>if</em> or <em>ifnot</em> element at
+the top level for matching the input and some other elements inside to be used
+if there was a match.
 
 <p>
 
-An <em>if</em> rule that has both <em>k</em> and <em>v</em> specified is only
-applied if a tag exists in the input that matches both.  An <em>if</em> rule
-that has only the <em>k</em> attribute is applied if a tag with that key exists
-and an <em>if</em> rule that has only the <em>v</em> attribute is applied to all
-tags with that value.
+Within the <em>if</em> and <em>ifnot</em> rules any of the rules can be used.
+These are <em>if</em>, <em>ifnot</em>, <em>set</em>, <em>unset</em>,
+<em>output</em> or <em>logerror</em> elements.
 
 <p>
 
-For the <em>set</em> and <em>output</em> elements the tag that is created in the
-input or output tag set uses the <em>k</em> and <em>v</em> attributes specified.
-If one or both are not specified then the original ones are used.
+The rules for matching the <em>if</em> or <em>ifnot</em> elements are the
+following:
+
+<ul>
+  <li>An <em>if</em> rule that has both <em>k</em> and <em>v</em> specified is
+    only matched if a tag exists in the input that matches both.
+  <li>An <em>if</em> rule that has only the <em>k</em> attribute is matched if a
+    tag with that key exists.
+  <li>An <em>if</em> rule that has only the <em>v</em> attribute is matched for
+    each tag with that value (i.e. the contents may be used more than once).
+  <li>An <em>if</em> rule that has neither attribute specified always matches.
+  <li>An <em>ifnot</em> rule that has both <em>k</em> and <em>v</em> specified
+    is only matched if no tag exists in the input that matches both.
+  <li>An <em>ifnot</em> rule that has only the <em>k</em> attribute is matched
+    only if no tag with that key exists.
+  <li>An <em>ifnot</em> rule that has only the <em>v</em> attribute is only
+    matched if no tag with that value exists.
+  <li>An <em>ifnot</em> rule that has neither attribute specified never matches.
+</ul>
+
+<p>
+
+For <em>set</em>, <em>unset</em>, <em>output</em> or <em>logerror</em> elements
+inside of an <em>if</em> rule an unspecified value for the <em>k</em>
+or <em>v</em> attribute is replaced by the values from the tag that matched the
+outer <em>if</em> rule.  This makes it simple to delete tags that match a
+particular rule without having to specify the parameters more than once.  For
+elements inside of an <em>ifnot</em> element an unspecified value for
+the <em>k</em> or <em>v</em> attribute is replaced by the values from the
+outer <em>ifnot</em> rule.  This means that either the outer <em>ifnot</em>
+element or the inner element must specify both <em>k</em> and <em>v</em>
+attributes between them.  For nested <em>if</em> or <em>ifnot</em> elements the
+outer <em>k</em> and <em>v</em> attributes are not inherited by the inner
+elements.
+
+<p>
+
+The <em>set</em> and <em>unset</em> elements either create or delete a tag from
+the input data that was read from the file.  If the <em>set</em> element is used
+and the tag already exists then it is modified.  The <em>output</em> element
+adds a tag to the set that will be used by Routino to determine the data
+properties.
+
+<p>
+
+The <em>logerror</em> element will cause an error message to be added to the
+error log file that reports that the key and attribute combination are not
+recognised.  If the <em>k</em> attribute is specified but not the <em>v</em>
+attribute then the tag value that matches the specified key is looked up and
+used.  An additional <em>message</em> attribute can be specified to be printed
+at the end of the logged error.
+
+<p>
+The default logged error message is:
+<pre class="boxed">
+Node XXX has an unrecognised tag 'key' = 'value' (in tagging rules); ignoring it.
+</pre>
+
+<p>
+The specified <em>message</em> attribute will replace the final part of the
+logged error.
 
 
-<h3><a name="H_1_1_2" title="Profiles"></a>Routing Profiles</h3>
+<h3 id="H_1_1_2" title="Profiles">Routing Profiles</h3>
 
 The default name of the routing profiles XML configuration file
 is <em>profiles.xml</em> in the same directory as the database files.  Other
@@ -189,7 +247,7 @@ foot is shown below:
 </pre>
 
 
-<h3><a name="H_1_1_3" title="Translations"></a>Output Translations</h3>
+<h3 id="H_1_1_3" title="Translations">Output Translations</h3>
 
 The default name of the output translations XML configuration file
 is <em>translations.xml</em> in the same directory as the database files.  Other
@@ -241,17 +299,17 @@ Part of the provided translations.xml file showing some of the English language
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/data.html b/doc/html/data.html
index 4ddad96..fedf8a9 100644
--- a/doc/html/data.html
+++ b/doc/html/data.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Data</title>
 
 <!--
  Routino documentation - data
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2011 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Data</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Data</h1>
 
@@ -45,11 +47,11 @@
 
 <div class="content">
 
-<h2><a name="H_1_1"></a>Data</h2>
+<h2 id="H_1_1">Data</h2>
 
 A router relies on data to be able to find a route.
 
-<h3><a name="H_1_1_1"></a>OpenStreetMap Data</h3>
+<h3 id="H_1_1_1">OpenStreetMap Data</h3>
 
 The data that is collected by the OpenStreetMap project consists of
 <em>nodes</em>, <em>ways</em> and <em>relations</em>.
@@ -72,7 +74,7 @@ The
 <a class="ext" title="OpenStreetMap Wiki" href="http://wiki.openstreetmap.org/wiki/Main_Page">OpenStreetMap Wiki</a>
 explains the data much better than I can.
 
-<h3><a name="H_1_1_2"></a>Router Data</h3>
+<h3 id="H_1_1_2">Router Data</h3>
 
 The information that is needed by a routing algorithm is only a subset of the
 information that is collected by the OpenStreetMap project.  For routing what is
@@ -108,7 +110,7 @@ connections between the highways and the properties of those highways.
 The information that is extracted from the OpenStreetMap data is stored in an
 optimised way that allows the routing to be performed quickly.
 
-<h3><a name="H_1_1_3" title="Data Tags"></a>Interpreting Data Tags</h3>
+<h3 id="H_1_1_3" title="Data Tags">Interpreting Data Tags</h3>
 
 The <em>tags</em> are the information that is attached to the nodes and ways in
 OpenStreetMap.  The router needs to interpret these tags and use them when
@@ -117,7 +119,7 @@ deciding what type of traffic can use a highway (for example).
 
 There are no well defined rules in OpenStreetMap about tagging, but there is
 guidance on the
-<a class="ext" title="Map Features" href="http://wiki.openstreetmap.org/index.php/Map_Features">OpenStreetMap Wiki "Map_Features"</a>
+<a class="ext" title="Map Features" href="http://wiki.openstreetmap.org/wiki/Map_Features">OpenStreetMap Wiki "Map_Features"</a>
 page.  This describes a set of recommended tags but these are not universally used
 so it is up to each application how to interpret them.
 <p>
@@ -128,7 +130,7 @@ data tags can be modified when the data is imported to allow customisation of
 the information used for routing.
 
 
-<h3><a name="H_1_1_4" title="Problems With Data"></a>Problems With OpenStreetMap Data</h3>
+<h3 id="H_1_1_4" title="Problems With Data">Problems With OpenStreetMap Data</h3>
 
 The route that can be found is only as good as the data that is available.  This
 is not intended as a criticism of the OpenStreetMap data; it is generally good.
@@ -149,17 +151,17 @@ uses the same Routino routing database.
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/index.html b/doc/html/index.html
index b66ff11..31bd22a 100644
--- a/doc/html/index.html
+++ b/doc/html/index.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Documentation</title>
 
 <!--
  Routino documentation - index
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2011 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Documentation</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Documentation</h1>
 
@@ -46,7 +48,7 @@
 <div class="content">
 
 
-<h2><a name="H_1_1"></a>Data</h2>
+<h2 id="H_1_1">Data</h2>
 
 A good router relies on good data and the
 <a class="ext" title="OpenStreetMap" href="http://www.openstreetmap.org/">OpenStreetMap</a>
@@ -55,7 +57,7 @@ considered about
 <a href="data.html" title="Data considerations">the data used</a>.
 
 
-<h2><a name="H_1_2"></a>Tagging</h2>
+<h2 id="H_1_2">Tagging</h2>
 
 In addition to the raw data the way that are tags are used is also important.
 With Routino the
@@ -64,7 +66,7 @@ are contained in a configuration file and can easily be customised to change the
 interpretation of each tag.
 
 
-<h2><a name="H_1_3"></a>Program Usage</h2>
+<h2 id="H_1_3">Program Usage</h2>
 
 There are four programs that make up this software, two create the routing
 database and use the information in it and the other two perform additional functions.
@@ -72,7 +74,7 @@ database and use the information in it and the other two perform additional func
 for using the four programs are provided.
 
 
-<h2><a name="H_1_4"></a>Configuration Files</h2>
+<h2 id="H_1_4">Configuration Files</h2>
 
 When the programs are run they read in one or more
 <a href="configuration.html" title="Configuration Files">configuration files</a>.
@@ -80,21 +82,28 @@ These files contain information about the routing preferences (types of highways
 preferred speeds etc), tagging rules and translation information for the outputs.
 
 
-<h2><a name="H_1_5"></a>Output Files</h2>
+<h2 id="H_1_5">Output Files</h2>
 
 The final result of running the router is one or more
 <a href="output.html" title="Output Files">output files</a>
 that contain the calculated route.
 
 
-<h2><a name="H_1_6"></a>Algorithm</h2>
+<h2 id="H_1_6">Numerical Limits</h2>
+
+When processing data there are 
+<a href="limits.html" title="Numerical Limits">numerical limits</a> for the
+range of data identifiers that can be handled and the size of the database.
+
+
+<h2 id="H_1_7">Algorithm</h2>
 
 The <a title="Algorithm" href="algorithm.html">algorithm</a> that is used by
 Routino takes the OpenStreetMap data and creates a local database of the
 important information for rapid routing.
 
 
-<h2><a name="H_1_7"></a>Installation</h2>
+<h2 id="H_1_8">Installation</h2>
 
 The Routino source code comes with a set of files that can be used to create
 a working server very easily.  The full information about
@@ -108,17 +117,17 @@ describes how to compile the programs and install them.
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/installation.html b/doc/html/installation.html
index 0951950..a49a336 100644
--- a/doc/html/installation.html
+++ b/doc/html/installation.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Installation</title>
 
 <!--
  Routino documentation - installation
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Installation</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Installation</h1>
 
@@ -46,28 +48,174 @@
 <div class="content">
 
 
-<h2><a name="H_1_1"></a>Compilation</h2>
+<h2 id="H_1_1">Quick Start Guide</h2>
 
-This program has been written to run on Linux, no cross-platform compatibility
-has been specifically included but on the other hand nothing platform specific
-has been knowingly included either.
+The instructions below are a complete list of the minimum required to get
+Routino installed and running under Apache on Debian Linux (other Linux versions
+will be similar).
+
+<p>
+
+<em>These instructions should not be considered as complete or a secure
+installation for the reasons given in more detail in
+the <a href="#H_1_5_1">Configuration of Web Files</a> section below.</em>
+
+<p>
+
+The starting point for the installation is in the Routino source code directory
+after it has been uncompressed.  Most of the steps will need to be run as the
+root user.
+
+<p>
+The first thing to do is to install the packages which are required for
+compilation of Routino as described in the <a href="#H_1_2">Pre-Requisites</a>
+section below.
+
+<pre class="boxed">
+apt-get install gcc make libc6-dev libz-dev libbz2-dev
+</pre>
+
+After this the programs can be compiled:
+
+<pre class="boxed">
+make
+</pre>
+
+The files for the web interface can now be copied to the web server directory.
+On Debian this is <tt>/var/www</tt> and the files changed to be owned by the
+user Apache runs as.
+
+<pre class="boxed">
+cp -a web /var/www/routino
+chown -R www-data:www-data /var/www/routino
+</pre>
+
+To be able to use Routino some data will need to be processed and a script is
+provided for this.  This will download the data for the UK which may take a
+while and then process it.
+
+<pre class="boxed">
+cd /var/www/routino/data
+sh -x create.sh
+</pre>
+
+The routino web pages also requires either the OpenLayers or Leaflet Javascript
+library to be downloaded and installed and scripts are provided for this.
+
+<pre class="boxed">
+cd /var/www/routino/www/openlayers
+sh -x install.sh
+</pre>
+
+<pre class="boxed">
+cd /var/www/routino/www/leaflet
+sh -x install.sh
+</pre>
+
+To make full use of the location search feature on the Routino web page you will
+need to install one extra perl package.
+
+<pre class="boxed">
+apt-get install libjson-pp-perl
+</pre>
+
+Finally to make the web pages work you will need to add the extra lines to the
+Apache configuration file as described in the <a href="#H_1_5_2">Configuration of
+Web Server</a> section below.  You don't have to use <tt>vi</tt> and can use any
+text editor.
+
+<pre class="boxed">
+vi /etc/apache2/sites-enabled/000-default
+apache2ctl restart
+</pre>
+
+Now everything should be set up and the web page should work if accessed at
+http://localhost/routino/www/routino/router.html.
+
+<p>
+
+When everything is working you should read the rest of this document carefully
+and make the following changes:
+
+<ul>
+  <li>Download your choice of OSM data - edit the file <tt>data/create.sh</tt>
+    and run it again.
+  <li>Edit the <tt>www/routino/mapprops.js</tt> file to match the downloaded
+    data and personal map preferences.
+  <li>Move the files in the web server directory so that only the contents of
+    the <tt>www</tt> directory are accessible by the web server.
+  <li>Edit the file <tt>www/routino/paths.pl</tt> to reference the new file
+    locations.
+</ul>
+
+
+<h2 id="H_1_2">Pre-Requisites</h2>
+
+The programs are written in standard C language with minimal external
+requirements so only a small set of development tools are required (gcc, make).
+The compilation tools to use and the command line options are defined in the
+file <tt>Makefile.conf</tt>.
+
+<p>
+
+There is some support for multi-threading that uses pthreads and additional
+development libraries for this may be required (on Linux this might be part of
+the standard C language development files).  The multi-threading is enabled by
+default but can be disabled at compile time by commenting out two lines in the
+file <tt>Makefile.conf</tt>.
 
 <p>
 
-Any information on improving the compilation process on anything other than
-32-bit x86 Linux is welcome.
+To use the built-in gzip file decompression function and to process all PBF
+format files the zlib (or libz) development library is required (on Linux this
+might be in a package called libz-dev).  The gzip function is enabled by default
+but can be disabled by commenting out two lines in the
+file <tt>Makefile.conf</tt>.
 
 <p>
 
-No external libraries are required and the programs are written in standard C
-language.
+To use the built-in bzip2 file decompression functions the bzip2 (or libbz2)
+development library is required (on Linux this might be in a package called
+libbz2-dev).  The bzip2 function is enabled by default but can be disabled by
+commenting out two lines in the file <tt>Makefile.conf</tt>.
 
 <p>
 
-To compile the programs just type 'make'.
+To use the built-in xz file decompression functions the liblzma development
+library is required (on Linux this might be in a package called liblzma-dev).
+The xz function is not enabled by default but can be enabled by uncommenting
+two lines in the file <tt>Makefile.conf</tt>.
 
+<p>
 
-<h2><a name="H_1_2"></a>Installation</h2>
+To use the web page interface an http server is required.  Instructions below
+are for Apache but any server that supports CGIs should work.
+
+<p>
+
+The web pages use the Perl scripting language and a number of the default Perl
+modules.  To use the search function on the router web page the additional Perl
+module "JSON::PP" must be installed.
+
+
+<h2 id="H_1_3">Compilation</h2>
+
+To compile the programs just type <tt>make</tt> at the top level of the source
+tree.
+
+<p>
+
+This program has been written to run on Linux, no cross-platform compatibility
+has been specifically included but on the other hand other platforms have not
+knowingly been excluded either.
+
+<p>
+
+Any information on improving the compilation process on anything other than x86
+Linux is welcome.
+
+
+<h2 id="H_1_4">Installation</h2>
 
 After compilation the executable files are copied into the directory
 <tt>web/bin</tt> and the default XML configuration files are copied into the
@@ -77,19 +225,20 @@ normal use.
 
 <p>
 
-The executable files are called <tt>planetsplitter</tt>, <tt>router</tt> and
-<tt>filedumper</tt> (also <tt>tagmodifier</tt> for debugging tag modifications).
-They can be copied to any location and need no special installation environment.
+The required executable files are <tt>planetsplitter</tt>, <tt>router</tt> and
+<tt>filedumper</tt> and the <tt>*-slim</tt> versions of the same files.  They
+can be copied to any location and need no special installation environment.
+The <tt>filedumperx</tt> executable is for debugging and not required.
 
 <p>
 
-The default configuration files are called <tt>profiles.xml</tt>,
-<tt>tagging.xml</tt> and <tt>translations.xml</tt>.  The names of the
-configuration files can be specified on the command line but by default are also
-looked for in the directory that contains the routing database.
+The configuration files are called <tt>profiles.xml</tt>, <tt>tagging.xml</tt>
+and <tt>translations.xml</tt>.  The names of the configuration files can be
+specified on the command line but by default are also looked for in the
+directory that contains the routing database with these names.
 
 
-<h2><a name="H_1_3"></a>Example Web Page</h2>
+<h2 id="H_1_5">Example Web Page</h2>
 
 The directory <tt>web</tt> contains a set of files that can be used to create a
 working set of web pages with interfaces to the routing algorithm.
@@ -99,12 +248,10 @@ working set of web pages with interfaces to the routing algorithm.
 The files in the <tt>web</tt> directory will require copying to a location that
 is accessible by a web server.  After copying the files some of them need to be
 edited; search through the files for lines that contain the words "EDIT THIS"
-and make appropriate edits.  The files that need editing are <tt>paths.pl</tt>
-(to set the directory paths) and <tt>mapprefs.js</tt> (to set the map
-properties).
+and make appropriate edits (more details below).
 
 
-<h3><a name="H_1_3_1"></a>Configuration of web files</h3>
+<h3 id="H_1_5_1">Configuration of Web Files</h3>
 
 The assumption in this description is that the whole of the directory called
 <tt>web</tt> is copied into a directory that is accessible by an Apache web
@@ -115,7 +262,6 @@ server.
 <em>This is not a secure configuration but an easy one to configure.</em>
 <br>
 <em>Only the directory <tt>www</tt> should be accessible by the web server.</em>
-<br>
 <em>Do not use this configuration unmodified in a public web server.</em>
 
 <p>
@@ -135,7 +281,11 @@ The directory structure is as follows:
     + /www/                    <- The files that must be available to the web
         |                         server are below this level.
         |
-        + /openlayers/         <- A directory to hold the OpenLayers scripts.
+        + /openlayers/         <- A directory to hold the OpenLayers library
+        |                         (optional; leaflet can be used instead).
+        |
+        + /leaflet/            <- A directory to hold the Leaflet library.
+        |                         (optional; openlayers can be used instead).
         |
         + /routino/            <- The main HTML, Javascript, CSS and CGI files.
             |
@@ -175,14 +325,29 @@ to be within the web server accessible directory.
 
 <p>
 
-The directory <tt>www/openlayers</tt> must be filled with the openlayers
-Javascript library that can be downloaded from http://www.openlayers.org/.
-(This version of Routino has been tested with OpenLayers library version 2.11).
-The files must be installed so that the file <tt>www/openlayers/OpenLayers.js</tt>
-and the directories <tt>www/openlayers/img/</tt>, <tt>www/openlayers/theme/</tt>
-all exist.  There is a script in the directory that will automatically download
-the files, create an optimised "OpenLayers.js" and copy the files to the required
-locations.
+A Javascript map drawing library is required and either OpenLayers or Leaflet
+can be used.  The library is loaded dynamically when the HTML is opened based on
+the value that is selected in <tt>mapprops.js</tt>.
+
+<p>
+
+The directory <tt>www/openlayers</tt> is for the OpenLayers Javascript library
+that can be downloaded from http://www.openlayers.org/.  (This version of
+Routino has been tested with OpenLayers library versions 2.12 and 2.13.1).  The
+file <tt>www/openlayers/OpenLayers.js</tt> and the directories
+<tt>www/openlayers/img/</tt> and <tt>www/openlayers/theme/</tt> must all exist.
+There is a script in the <tt>www/openlayers</tt> directory that will
+automatically download the files, create an optimised <tt>OpenLayers.js</tt> and
+copy the files to the required locations.
+
+<p>
+
+The directory <tt>www/leaflet</tt> is for the Leaflet Javascript library that
+can be downloaded from http://leafletjs.com/.  (This version of Routino has been
+tested with Leaflet library versions 0.7.1 and 0.7.2).  The files
+<tt>www/leaflet/leaflet.js</tt> and <tt>www/leaflet/leaflet.css</tt> and the
+directory <tt>www/leaflet/images/</tt> must all exist.  There is a script in the
+<tt>www/leaflet</tt> directory that will automatically download the files.
 
 <p>
 
@@ -191,15 +356,17 @@ files as well as the CGI scripts that perform the server-side routing functions.
 The description below lists all of the files that contain editable information.
 
 <dl>
-  <dt>paths.pl
+  <dt><tt>paths.pl</tt>
   <dd>This contains the names of the directories that contain the executable
       files, router database and temporary results; the prefix for the routing
       database; and the names of the executables.
-  <dt>mapprefs.js
-  <dd>The parameters in this file control the boundary of the visible map
-      (defaults to UK), the minimum and maximum zoom levels (defaults to between
-      4 and 15 inclusive) and the source of map tiles (defaults to the main
-      OpenStreetMap tile server).
+  <dt><tt>mapprops.js</tt>
+  <dd>The parameters in this file control the Javascript map library (defaults
+      to OpenLayers), the boundary of the visible map (defaults to UK), the
+      minimum and maximum zoom levels (defaults to between 4 and 15 inclusive),
+      the source of map tiles (defaults to the main OpenStreetMap tile server),
+      the data editing and browsing URLs (default to the OpenStreetMap website)
+      and the number of waypoints allowed (defaults to 9).
 </dl>
 
 <p>
@@ -208,7 +375,7 @@ The directory <tt>www/routino/documentation</tt> contains the HTML version of
 the Routino documentation.
 
 
-<h3><a name="H_1_3_2"></a>Configuration of web server</h3>
+<h3 id="H_1_5_2">Configuration of Web Server</h3>
 
 The file <tt>www/routino/.htaccess</tt> contains all of the Apache configuration
 options that are required to get the example web pages running.  The only
@@ -216,6 +383,22 @@ problem is that because of the way that the <tt>AllowOverride</tt> option works
 one of the configuration options has been commented out.  This must be enabled
 in the main Apache server configuration file.
 
+<p>
+
+If you have copied the routino <tt>web</tt> directory into <tt>/var/www</tt> and
+named it <tt>routino</tt> then the entry that you need in your Apache
+configuration file is this one:
+
+<pre>
+     <Directory /var/www/routino>
+         AllowOverride All
+         Options +ExecCGI
+     </Directory>
+</pre>
+
+This can be placed anywhere between the <tt><VirtualHost *:80></tt>
+and <tt></VirtualHost></tt> tags which should be at the start and end of
+the file.
 
 </div>
 
@@ -223,17 +406,17 @@ in the main Apache server configuration file.
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/limits.html b/doc/html/limits.html
new file mode 100644
index 0000000..f7074bb
--- /dev/null
+++ b/doc/html/limits.html
@@ -0,0 +1,244 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Numerical Limits</title>
+
+<!--
+ Routino documentation - ID limits
+
+ Part of the Routino routing software.
+
+ This file Copyright 2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see http://www.gnu.org/licenses/.
+-->
+
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
+
+<body>
+
+<!-- Header Start -->
+
+<div class="header">
+
+<h1>Routino : Numerical Limits</h1>
+
+<hr>
+</div>
+
+<!-- Header End -->
+
+<!-- Content Start -->
+
+<div class="content">
+
+<h2 id="H_1_1">32/64-bit Data IDs</h2>
+
+The
+<a class="ext" title="OpenStreetMap" href="http://www.openstreetmap.org/">OpenStreetMap</a>
+data uses a numerical identifier for each node, way and relation.  These
+identifiers started at 1 and increase for every new item of each type that is
+added.  When an object is deleted the identifier is not re-used so the highest
+identifier will always be higher than the number of objects.
+
+<p>
+The identifier needs to be handled carefully to ensure that it does not overflow
+the data type allocated for it.  Depending on the data type used to store the
+identifier there are are a number of numerical limits as described below:
+
+<ol>
+  <li>If a signed 32-bit integer is used to store the identifier then the
+    maximum value that can be handled is 2147483647 (2<sup>31</sup>-1) before
+    overflow.
+  <li>If an unsigned 32-bit integer is used to store the identifier then the
+    maximum value that can be handled is 4294967295 (2<sup>32</sup>-1) before
+    overflow.
+  <li>If a signed 64-bit integer is used to store the identifier then the
+    maximum value that can be handled is 9223372036854775807 (2<sup>63</sup>-1)
+    before overflow.
+</ol>
+
+For the purposes of this document the possibility of overflow of a 64-bit
+integer is ignored.
+
+<p>
+The part of Routino that handles the node, way and relation identifiers is the
+<tt>planetsplitter</tt> program.
+
+
+<h3 id="H_1_1_1">ID Above 31-bits</h3>
+
+The first identifier exceeding 31-bits (for a node) is predicted to be created
+in the OpenStreetMap database in February 2013.
+
+<p>
+All versions of Routino use unsigned 32-bit integers to store the identifier.
+Therefore all versions of Routino will continue working correctly when node
+number 2147483648 (2<sup>31</sup>) or higher is present.
+
+
+<h3 id="H_1_1_2">ID Above 32-bits</h3>
+
+The ability of Routino to handle identifiers larger than 32-bits does not depend
+on having a 64-bit operating system.
+
+<p>
+Before version 2.0.1 of Routino there was no check that the identifier read from
+the input data would fit within an unsigned 32-bit integer.  Earlier versions of
+Routino will therefore fail to report an error and will process data incorrectly
+when node number 4294967296 (2<sup>32</sup>) or higher is present.
+
+<p>
+From version 2.0.2 the code is written to allow the node, way and relation
+identifier data type to be changed to 64-bits.  This means that a consistent
+data type is used for handling identifiers and the format used for printing them
+is consistent with the variable type.
+
+<p>
+From version 2.0.2 onwards it is possible to make a simple change to the code to
+process data with node identifiers above 4294967296 (2<sup>32</sup>) without
+error.  The binary format of the database will be unchanged by the use of 64-bit
+identifiers (since the identifiers are not stored in the database).
+
+<p>
+To recompile with 64-bit node identifiers the file <tt>src/typesx.h</tt> should
+be edited and the two lines below changed from:
+
+<pre class="boxed">
+typedef uint32_t node_t;
+
+#define Pnode_t PRIu32
+</pre>
+
+to:
+
+<pre class="boxed">
+typedef uint64_t node_t;
+
+#define Pnode_t PRIu64
+</pre>
+
+<p>
+A similar change can also be made for way or relation identifiers although since
+there are currently fewer of these the limit is not as close to being reached.
+
+<p>
+Between version 2.0.2 and version 2.4 a bug means that route relations will
+ignore the way or relation identifier if it is equal to 4294967295
+(2<sup>32</sup>-1).
+
+<p>
+From version 2.4 onwards when a numerical limit is reached the
+<tt>planetsplitter</tt> program will exit with an error message that describes
+which limit was reached and which data type needs to be changed.
+
+
+<h2 id="H_1_2">Database Format</h2>
+
+The other limitation in Routino is the number of objects stored in the database
+that is generated by the <tt>planetsplitter</tt> data processing.  This number
+may be significantly different from the highest identifier in the input data set
+for two reasons.  Firstly any nodes, ways or relations that have been deleted
+will not be present in the data.  Secondly when a partial planet database
+(continent, country or smaller) is processed there will be only a fraction of
+the total number of nodes, ways and relations.
+
+<p>
+The limiting factor is the largest of the following.
+<ol>
+  <li>The number of nodes in the input data files.
+  <li>The number of segments in the input data files.
+  <li>The number of highways in the input data files.
+  <li>The number of relations in the input data files.
+</ol>
+
+Normally the number of nodes will be the limiting factor.
+
+
+<h3 id="H_1_2_1">32-bit Indexes</h3>
+
+Before version 1.2 the database could hold up to 4294967295 (2<sup>32</sup>-1)
+items of each type (node, segment, way) since an unsigned 32-bit integer is
+used.
+
+<p>
+Versions 1.3 to 1.4.1 have a limit of 2147483647 (2<sup>31</sup>-1) items since
+half of the 32-bit integer range is reserved for fake nodes and segments that
+are inserted if a waypoint is not close to a node.
+
+<p>
+From version 1.5 the limit is 4294901760 (2<sup>32</sup>-2<sup>16</sup>) for the
+number of items of each type that can be stored.  The small remaining part of
+the 32-bit unsigned integer range is reserved for fake nodes and segments.
+
+
+<h3 id="H_1_2_2">64-bit Indexes</h3>
+
+When using a 32-bit operating system it is not possible to create a database
+that exceeds about 2GB in total.  This will be fewer than 2<sup>32</sup> objects
+in the database in total.  The use of 64-bit indexes will require a 64-bit
+operating system.
+
+<p>
+From version 2.0.2 onwards it is possible to make a simple change to the code to
+index the database objects with 64-bit integers insted of 32-bit integers.
+
+<p>
+To recompile with 64-bit index integers the file <tt>src/types.h</tt> should be
+edited and the two lines below changed from:
+
+<pre class="boxed">
+typedef uint32_t index_t;
+
+#define Pindex_t PRIu32
+</pre>
+
+to:
+
+<pre class="boxed">
+typedef uint64_t index_t;
+
+#define Pindex_t PRIu64
+</pre>
+
+This change will affect nodes, segments, ways and relations together.  The
+database that is generated will no longer be compatible with Routino that has
+been compiled with 32-bit indexes.  The size of the database will also grow by
+about 50% when this change is made.
+
+
+</div>
+
+<!-- Content End -->
+
+<!-- Footer Start -->
+
+<div class="footer">
+<hr>
+
+<address>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
+</address>
+
+</div>
+
+<!-- Footer End -->
+
+</body>
+
+</html>
diff --git a/doc/html/output.html b/doc/html/output.html
index bf7f915..4049f55 100644
--- a/doc/html/output.html
+++ b/doc/html/output.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Output</title>
 
 <!--
  Routino documentation - output
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Output</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Output</h1>
 
@@ -45,7 +47,7 @@
 
 <div class="content">
 
-<h2><a name="H_1_1"></a>Router Output</h2>
+<h2 id="H_1_1">Router Output</h2>
 
 There are three different formats of output from the router, HTML,
 <a class="ext" title="GPX format" href="http://www.topografix.com/gpx.asp">GPX (GPS eXchange) XML format</a>
@@ -83,7 +85,7 @@ translated.
 <!-- For reference the examples were produced from the following URL:
      http://www.routino.org/uk/router.html?transport=motorcar;lon1=-0.12790;lat1=51.52468;lon2=-0.10365;lat2=51.47824 -->
 
-<h3><a name="H_1_1_1" title="HTML file"></a>HTML Route Instructions</h3>
+<h3 id="H_1_1_1" title="HTML file">HTML Route Instructions</h3>
 
 The HTML route instructions file contains one line for the description of each
 of the interesting junctions in the route and one line for each of the highways
@@ -133,7 +135,7 @@ style definitions):
 </pre>
 
 
-<h3><a name="H_1_1_2" title="GPX track file"></a>GPX Track File</h3>
+<h3 id="H_1_1_2" title="GPX track file">GPX Track File</h3>
 
 The GPX track file contains a track with all of the individual nodes that the
 route passes through.
@@ -167,7 +169,7 @@ An example GPX track file output is below:
 </pre>
 
 
-<h3><a name="H_1_1_3" title="GPX route file"></a>GPX Route File</h3>
+<h3 id="H_1_1_3" title="GPX route file">GPX Route File</h3>
 
 The GPX route file contains a route (ordered set of waypoints) with all of the
 interesting junctions that the route passes through and a description of the
@@ -219,7 +221,7 @@ An example GPX route file output is below:
 </pre>
 
 
-<h3><a name="H_1_1_4" title="Text file"></a>Text File</h3>
+<h3 id="H_1_1_4" title="Text file">Text File</h3>
 
 The text file format contains one entry for all of the interesting junctions in
 the route and is intended to be easy to interpret, for example for creating
@@ -306,7 +308,7 @@ The individual items are separated by tabs but some of the items contain spaces
 as well.
 
 
-<h3><a name="H_1_1_5" title="All nodes text file"></a>All Nodes Text File</h3>
+<h3 id="H_1_1_5" title="All nodes text file">All Nodes Text File</h3>
 
 The all nodes text file format contains one entry for each of the nodes on the
 route.
@@ -392,17 +394,17 @@ For each of the lines the individual fields contain the following:
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/readme.html b/doc/html/readme.html
index 09f7a63..f84c153 100644
--- a/doc/html/readme.html
+++ b/doc/html/readme.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Software</title>
 
 <!--
  Routino documentation - readme
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Software</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Software</h1>
 
@@ -45,7 +47,7 @@
 
 <div class="content">
 
-<h2><a name="H_1_1" title="Introduction"></a>Routino Introduction</h2>
+<h2 id="H_1_1" title="Introduction">Routino Introduction</h2>
 
 Routino is an application for finding a route between two points using the
 dataset of topographical information collected by
@@ -59,7 +61,7 @@ information useful for routing.  With this database and two points specified by
 latitude and longitude an optimum route (either shortest or quickest) is
 determined.  The route is calculated for OpenStreetMap highways (roads, paths
 etc) using one of the common forms of transport defined in OpenStreetMap (foot,
-bicycle, horse, motorcar, motorbike etc).
+bicycle, horse, motorcar, motorcycle etc).
 
 <p>
 
@@ -108,7 +110,7 @@ very fast and most of the speed increases come from the carefully chosen and
 optimised data format.
 
 
-<h2><a name="H_1_2"></a>Disclaimer</h2>
+<h2 id="H_1_2">Disclaimer</h2>
 
 The route that is calculated by this software is only as good as the input data.
 
@@ -118,11 +120,14 @@ Routino comes with ABSOLUTELY NO WARRANTY for the software itself or the route
 that is calculated by it.
 
 
-<h2><a name="H_1_3"></a>Demonstration</h2>
+<h2 id="H_1_3">Demonstration</h2>
 
-A live demonstration of the router for the UK is available on the internet:
+A live demonstration of the router for the UK is available on the internet in
+both OpenLayers and Leaflet versions:
+<br>
+<a href="http://www.routino.org/uk-leaflet/" title="Leaflet Router">http://www.routino.org/uk-leaflet/</a>
 <br>
-<a title="Routino demo" href="http://www.routino.org/uk/">http://www.routino.org/uk/</a>
+<a href="http://www.routino.org/uk-openlayers/" title="OpenLayers Router">http://www.routino.org/uk-openlayers/</a>
 
 <p>
 
@@ -131,11 +136,14 @@ be used to create your own interactive map.
 
 <p>
 
-The interactive map is made possible by use of the OpenLayers Javascript
-library from <a class="ext" href="http://www.openlayers.org" title="OpenLayers">http://www.openlayers.org/</a>.
+The interactive map is made possible by use of the OpenLayers or Leaflet
+Javascript library from
+<a class="ext" href="http://www.openlayers.org" title="OpenLayers">http://www.openlayers.org/</a>
+or
+<a class="ext" href="http://leafletjs.com" title="Leaflet">http://leafletjs.com/</a>.
 
 
-<h2><a name="H_1_4"></a>Documentation</h2>
+<h2 id="H_1_4">Documentation</h2>
 
 A full set of
 <a href="index.html" title="Documentation">documentation</a>
@@ -143,7 +151,7 @@ is available that describes how to install and use the programs as well as
 what should go in the configuration files and how it works.
 
 
-<h2><a name="H_1_5"></a>Status</h2>
+<h2 id="H_1_5">Status</h2>
 
 Version 1.0 of Routino was released on 8th April 2009.
 <br>
@@ -186,69 +194,76 @@ Version 2.3.2 of Routino was released on 6th October 2012.
 Version 2.4 of Routino was released on 8th December 2012.
 <br>
 Version 2.4.1 of Routino was released on 17th December 2012.
+<br>
+Version 2.5 of Routino was released on 9th February 2013.
+<br>
+Version 2.5.1 of Routino was released on 20th April 2013.
+<br>
+Version 2.6 of Routino was released on 6th July 2013.
+<br>
+Version 2.7 of Routino was released on 22nd March 2014.
 
 <p>
 
-The full version history is available in the NEWS file.
-
-
-<h3><a name="H_1_5_1" title="Changes"></a>Changes in Version 2.4/2.4.1</h3>
+The full version history is available in the NEWS.txt file.
 
-In version 2.4.1 - bug fixes.
 
-<dl>
-  <dt>Bug fixes:
-  <dd>Fix error with finding routes with low preference values (router).
-  <dd>Fix error when searching for default profiles.xml (router).
-  <dd>Fix bug with printing log messages when output is not stdout (tagmodifier).
-  <dd>Stop various crashes if trying to process file with no data (planetsplitter).
-</dl>
+<h3 id="H_1_5_1" title="Changes">Changes in Versions 2.7</h3>
 
-In version 2.4 - mostly new features.
+Version 2.7 - mostly web page usability improvements.
 
 <dl>
   <dt>Bug fixes:
-  <dd>Fix pruning short segments in slim mode (gave different results to non-slim).
-  <br>Fix error with segment lengths for some segments from ways that are areas.
-  <br>Fix latent bug with route relations when compiled for 64-bit IDs.
-
-  <dt>router/planetsplitter:
-  <dd>Replace all debugging "assert" statements with fatal error messages.
+    <dd>Fix web-page CGI bug that did not allow more than 9 waypoints to be routed.
+    <br>Fix typo in documentation strings in filedumper program.
+    <br>Fix error in function prototype that stopped 64-bit node type being used.
+    <br>Don't lose super-segments when merging them with normal segments.
+    <br>Don't exceed the database lat/long limits when searching for visualiser data.
 
   <dt>planetsplitter:
-  <dd>Delete ways that are not used from the output files (names remain though).
-  <br>Delete turn relations that are not used from the output files.
-  <br>Speed up the processing, mainly by reducing the number of I/O operations.
-  <br>Change the pruning of isolated regions to look at each transport type.
-  <br>Slim and normal mode now give identical results (sorting preserves order).
-  <br>Log some more error cases, clarify some existing ones.
-  <br>Added a --append option which must be used to append files to existing data.
-  <br>Added a --keep option which can be used to keep parsed, sorted data.
-  <br>Added a --changes option to allow appending OSM change files (.osc files).
+    <dd>Don't overflow (and wrap-around) conversions of lengths, weights etc.
+    <br>Add some new formats of length, weight and speed parsing.
+    <br>Add .xz uncompression as a compile-time option (default is disabled).
 
-  <dt>Configuration Files:
-  <dd>Accept some more tag values for OSM file parsing.
+  <dt>router:
+    <dd>Remove ancient undocumented option to specify lat/lon without --lat/--lon.
+    <br>Add a '--output-stdout' option to output the route in a selected format.
+    <br>Add a '--reverse' option to calculate a route in the reverse order.
+    <br>Add a '--loop' option to calculate a route that returns to the first waypoint.
+    <br>Output valid HTML4 (use strict DTD and use numeric entity for apostrophe).
 
-  <dt>summarise-log.pl:
-  <dd>Can now generate an HTML version with links to OSM information for each item.
+  <dt>OSM tagging:
+    <dd>Allow bicycles both ways on certain oneway roads if tagging allows.
+    <br>Handle "access=bus" like "access=psv".
 
-  <dt>Deleted obsoleted files:
-  <dd>The CGI scripts customrouter.cgi and customvisualiser.cgi have been removed.
-  <br>The noscript.cgi and noscript.html web pages have been removed.
+  <dt>Configuration Files:
+    <dd>Updated Dutch translations.
+    <br>Updates to the XML parser tagging rules.
+    <br>Added French translations for the routing output.
+
+  <dt>Documentation:
+    <dd>Update the algorithm documentation for finding the shortest path.
+    <br>Update documentation HTML to strict 4.01 DTD.
+
+  <dt>Web pages:
+    <dd>Some changes to HTML, CSS formatting and Javascript to improve usability.
+    <br>Added a French translation of the router web page.
+    <br>Add the option to choose between OpenLayers and Leaflet for map rendering.
+    <br>Check compatible with OpenLayers v2.13.1 and make this the default.
+    <br>Create the router and visualiser pages from templates and translated phrases.
 </dl>
 
 <p>
-<b>Note:</b> Files deprecated in version 2.3 have been removed in version 2.4.
-
+<b>Note:</b> This version has removed specific support for IE6 and IE7 browsers.
 <p>
-<b>Note:</b> This version is not compatible with databases from previous versions.
+<b>Note:</b> Note: This version is compatible with databases from version 2.6.
 
 
-<h3><a name="H_1_5_2"></a>License</h3>
+<h3 id="H_1_5_2">License</h3>
 
 This program is free software: you can redistribute it and/or modify it under
 the terms of the
-<a class="ext" title="Affero GPLv3" href="http://www.fsf.org/licensing/licenses/agpl-3.0.html">GNU Affero General Public License</a>
+<a class="ext" title="Affero GPLv3" href="http://www.gnu.org/licenses/agpl-3.0.html">GNU Affero General Public License</a>
 as published by the Free Software Foundation; either version 3 of the License,
 or (at your option) any later version.
 
@@ -260,16 +275,38 @@ additional requirements to anybody who provides a networked service using this
 software.
 
 
-<h3><a name="H_1_5_3"></a>Copyright</h3>
+<h3 id="H_1_5_3">Copyright</h3>
+
+Routino is copyright Andrew M. Bishop 2008-2014.
+
 
-Routino is copyright Andrew M. Bishop 2008-2012.
+<h2 id="H_1_6">Homepage</h2>
 
+The <a title="Homepage" href="http://www.routino.org/">Routino homepage</a>
+has the latest news about the program.
 
-<h2><a name="H_1_6"></a>Download</h2>
+
+<h2 id="H_1_7">Download</h2>
 
 The <a title="Download directory" href="http://www.routino.org/download/">download directory</a>
 contains the latest version of the source code.
 
+<h3 id="H_1_7_2">Subversion</h3>
+
+The source code can also be downloaded from the
+<a title="SVN Repository" href="http://routino.org/svn/trunk/">Subversion repository</a>
+with a command like the following:
+
+<pre class="boxed">
+svn co http://gedanken.org.uk/svn/cxref/trunk cxref
+</pre>
+
+<p>
+The source code can also be browsed in the
+<a title="SVN Viewer" href="http://www.routino.org/viewvc/trunk/">Subversion viewer</a>
+which also has a list of the
+<a title="SVN Changes" href="http://www.routino.org/viewvc/trunk/?view=log">latest changes</a>.
+
 
 </div>
 
@@ -277,17 +314,17 @@ contains the latest version of the source code.
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/style.css b/doc/html/style.css
index 535e062..89a3ce7 100644
--- a/doc/html/style.css
+++ b/doc/html/style.css
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2010 Andrew M. Bishop
+// This file Copyright 2008-2013 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -81,6 +81,10 @@ DIV.header
 
  margin: 0;
 
+ /* text alignment */
+
+ text-align: center;
+
  /* floats */
 
  clear: left;
@@ -133,6 +137,10 @@ DIV.footer
 
  margin: 0;
 
+ /* text alignment */
+
+ text-align: center;
+
  /* floats */
 
  clear: left;
@@ -217,6 +225,13 @@ DIV.content H4
  margin-bottom: 0.125em;
 }
 
+DIV.content P.center
+{
+ /* text alignment */
+
+ text-align: center;
+}
+
 DIV.content OL, DIV.content UL, DIV.content DIR, DIV.content MENU, DIV.content DL
 {
  /* margins, borders, padding and sizes */
@@ -306,6 +321,13 @@ DIV.content TABLE
  border-collapse: collapse;
 }
 
+DIV.content TABLE.center
+{
+ /* text alignment */
+
+ text-align: center;
+}
+
 DIV.content TABLE.noborder
 {
  /* margins, borders, padding and sizes */
@@ -383,6 +405,16 @@ DIV.content TD.right, DIV.content TH.right, DIV.content TR.right
  text-align: right;
 }
 
+DIV.content IMG.center
+{
+ display: block;
+
+ /* margins, borders, padding and sizes */
+
+ margin-left:  auto;
+ margin-right: auto;
+}
+
 DIV.content IMG
 {
  /* margins, borders, padding and sizes */
diff --git a/doc/html/tagging.html b/doc/html/tagging.html
index e7cce07..a482430 100644
--- a/doc/html/tagging.html
+++ b/doc/html/tagging.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Tagging Rules</title>
 
 <!--
  Routino documentation - tagging
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Tagging Rules</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Tagging Rules</h1>
 
@@ -45,7 +47,7 @@
 
 <div class="content">
 
-<h2><a name="H_1_1"></a>Tags And Attributes</h2>
+<h2 id="H_1_1">Tags And Attributes</h2>
 
 The different tags and attributes in the 
 <a class="ext" title="OpenStreetMap" href="http://www.openstreetmap.org/">OSM</a>
@@ -63,7 +65,7 @@ the default tagging configuration file.
 <!-- ==================== Tags Recognised After Processing ==================== -->
 
 
-<h2><a name="H_1_2" title="After Processing"></a>Tags Recognised After Processing</h2>
+<h2 id="H_1_2" title="After Processing">Tags Recognised After Processing</h2>
 
 This section describes the tags that are recognised by Routino after the tag
 transformations have been applied.  This is therefore a much reduced set of tags
@@ -78,18 +80,18 @@ are recognised as being affirmative and any other value is negative.
 
 <!-- ========================= Routino Node Tags ========================= -->
 
-<h3><a name="H_1_2_1" title="Nodes"></a>Node Tags And Attributes</h3>
+<h3 id="H_1_2_1" title="Nodes">Node Tags And Attributes</h3>
 
 The node attributes <em>id</em>, <em>latitude</em> and <em>longitude</em> are
 used.  The id attribute is required to associate the node with the ways and the
 position attributes are required to locate the node.
 
 
-<h4><a name="H_1_2_1_1" title="transport tags"></a>Transport Specific Tags</h4>
+<h4 id="H_1_2_1_1" title="transport tags">Transport Specific Tags</h4>
 
 One tag is recognised for each of the different modes of transport: <em>foot</em>,
 <em>horse</em>, <em>bicycle</em>, <em>wheelchair</em>, <em>moped</em>,
-<em>motorbike</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
+<em>motorcycle</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
 and <em>psv</em>.  These indicate whether the specific type of transport is
 allowed to pass through the node or not.
 
@@ -99,15 +101,15 @@ By default for nodes all types of transport are allowed to pass through a node
 and specific tags must be used to remove the permissions for the transport.
 
 
-<h4><a name="H_1_2_1_2" title="highway (node)"></a>The highway Tag</h4>
+<h4 id="H_1_2_1_2" title="roundabout (node)">The roundabout Tag</h4>
 
-The <em>highway</em> tag for <em>mini_roundabout</em> is recognised and used to
+The <em>roundabout</em> tag for mini-roundabouts is recognised and used to
 improve the description of the route.
 
 
 <!-- ========================= Routino Way Tags ========================= -->
 
-<h3><a name="H_1_2_2" title="Ways"></a>Way Tags And Attributes</h3>
+<h3 id="H_1_2_2" title="Ways">Way Tags And Attributes</h3>
 
 The tags from the ways in the data are the ones that provide most of the
 information for routing.  The <em>id</em> attribute is used only so that the
@@ -115,7 +117,7 @@ many segments associated with a way can share a set of tags taken from the way.
 The <em>nd</em> information is used to identify the nodes that make up the way.
 
 
-<h4><a name="H_1_2_2_1" title="highway (way)"></a>The highway Tag</h4>
+<h4 id="H_1_2_2_1" title="highway (way)">The highway Tag</h4>
 
 The most important tag that is used from a way is the <em>highway</em> tag.
 This defines the type of highway that the way represents.  Any way that does not
@@ -152,11 +154,11 @@ that the router uses are:
 </i>
 
 
-<h4><a name="H_1_2_2_2" title="transport tags"></a>Transport Specific Tags</h4>
+<h4 id="H_1_2_2_2" title="transport tags">Transport Specific Tags</h4>
 
 One tag is recognised for each of the different modes of transport: <em>foot</em>,
 <em>horse</em>, <em>bicycle</em>, <em>wheelchair</em>, <em>moped</em>,
-<em>motorbike</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
+<em>motorcycle</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
 and <em>psv</em>.  These indicate whether the specific type of transport is
 allowed on the highway or not.
 
@@ -166,27 +168,26 @@ By default for ways no types of transport are allowed to pass along a highway
 and specific tags must be used to add the permissions for the transport.
 
 
-<h4><a name="H_1_2_2_3" title="name"></a>The name Tag</h4>
+<h4 id="H_1_2_2_3" title="name">The name Tag</h4>
 
 The <em>name</em> tag is used to provide the label for the highway when printing
 the results.
 
 
-<h4><a name="H_1_2_2_4" title="ref"></a>The ref Tag</h4>
+<h4 id="H_1_2_2_4" title="ref">The ref Tag</h4>
 
 The <em>ref</em> tag is used to provide the label for the highway when printing
 the results.
 
 
-<h4><a name="H_1_2_2_5" title="multilane"></a>The multilane Tag</h4>
+<h4 id="H_1_2_2_5" title="lanes">The lanes Tag</h4>
 
-The <em>multilane</em> tag is used to identify whether a highway has multiple
-lanes for traffic and this sets one of the highway properties.  There is not
-normally a <em>multilane</em> tag but one needs to be added by the tag
-processing transformations.  Values of <em>yes</em> or <em>no</em> are expected.
+The <em>lanes</em> tag is used to identify whether a highway has multiple lanes
+for traffic and this is used to derive the <em>multilane</em> highway
+properties.
 
 
-<h4><a name="H_1_2_2_6" title="paved"></a>The paved Tag</h4>
+<h4 id="H_1_2_2_6" title="paved">The paved Tag</h4>
 
 The <em>paved</em> tag is used to identify whether a highway is paved or not,
 this is one of the available highway properties.  A <em>paved</em> tag may exist
@@ -194,19 +195,25 @@ in the original data but normally the <em>surface</em> tag needs to be
 transformed into the <em>paved</em> tag.
 
 
-<h4><a name="H_1_2_2_7" title="bridge"></a>The bridge Tag</h4>
+<h4 id="H_1_2_2_7" title="multilane">The multilane Tag</h4>
+
+The <em>multilane</em> tag is used to indicate that a highway has multiple lanes
+for traffic.
+
+
+<h4 id="H_1_2_2_8" title="bridge">The bridge Tag</h4>
 
 The <em>bridge</em> tag is used to identify whether a highway is a bridge and
 therefore set one of the available properties.
 
 
-<h4><a name="H_1_2_2_8" title="tunnel"></a>The tunnel Tag</h4>
+<h4 id="H_1_2_2_9" title="tunnel">The tunnel Tag</h4>
 
 The <em>tunnel</em> tag is used to identify whether a highway is a tunnel and
 therefore set one of the available properties.
 
 
-<h4><a name="H_1_2_2_9" title="footroute"></a>The footroute Tag</h4>
+<h4 id="H_1_2_2_10" title="footroute">The footroute Tag</h4>
 
 The <em>footroute</em> tag is used to identify whether a highway is part of a
 walking route and therefore set one of the available properties.  This is not a
@@ -214,7 +221,7 @@ standard OSM tag and is normally added to the individual highways by looking for
 route relations that are designated for foot access.
 
 
-<h4><a name="H_1_2_2_10" title="bicycleroute"></a>The bicycleroute Tag</h4>
+<h4 id="H_1_2_2_11" title="bicycleroute">The bicycleroute Tag</h4>
 
 The <em>bicycleroute</em> tag is used to identify whether a highway is part of a
 bicycle route and therefore set one of the available properties.  This is not a
@@ -222,19 +229,25 @@ standard OSM tag and is normally added to the individual highways by looking for
 route relations that are designated for bicycle access.
 
 
-<h4><a name="H_1_2_2_11" title="oneway"></a>The oneway Tag</h4>
+<h4 id="H_1_2_2_12" title="cyclebothways">The cyclebothways Tag</h4>
+
+The <em>cyclebothways</em> tag is used to identify whether a highway allows
+cycling in the opposite direction to a signposted oneway restriction.
+
+
+<h4 id="H_1_2_2_13" title="oneway">The oneway Tag</h4>
 
 The <em>oneway</em> tag is used to specify that traffic is only allowed to
 travel in one direction.
 
 
-<h4><a name="H_1_2_2_12" title="roundabout"></a>The roundabout Tag</h4>
+<h4 id="H_1_2_2_14" title="roundabout (way)">The roundabout Tag</h4>
 
 The <em>roundabout</em> tag is used to specify that a highway is part of a
 roundabout to improve the description of the calculated route.
 
 
-<h4><a name="H_1_2_2_13" title="maxspeed"></a>The maxspeed Tag</h4>
+<h4 id="H_1_2_2_15" title="maxspeed">The maxspeed Tag</h4>
 
 The <em>maxspeed</em> tag is used to specify the maximum speed limit on the
 highway; this is always measured in km/hr in OpenStreetMap data.  If the tag
@@ -242,7 +255,7 @@ value contains "mph" then it is assumed to be a value in those units and
 converted to km/hr.
 
 
-<h4><a name="H_1_2_2_14" title="maxweight"></a>The maxweight Tag</h4>
+<h4 id="H_1_2_2_16" title="maxweight">The maxweight Tag</h4>
 
 The <em>maxweight</em> tag is used to specify the maximum weight of any traffic
 on the highway.  In other words this must be set to the heaviest weight allowed
@@ -250,7 +263,7 @@ on the highway (for example a bridge) in tonnes.  If the tag value contains "kg"
 then it is assumed that the value is in these units and converted to tonnes.
 
 
-<h4><a name="H_1_2_2_15" title="maxheight"></a>The maxheight Tag</h4>
+<h4 id="H_1_2_2_17" title="maxheight">The maxheight Tag</h4>
 
 The <em>maxheight</em> tag is used to specify the maximum height of any traffic
 on the highway.  In other words this must be set to the lowest height of
@@ -259,7 +272,7 @@ a measurement in feet or feet and inches then attempts are made to convert this
 to metres.
 
 
-<h4><a name="H_1_2_2_16" title="maxwidth"></a>The maxwidth Tag</h4>
+<h4 id="H_1_2_2_18" title="maxwidth">The maxwidth Tag</h4>
 
 The <em>maxwidth</em> tag is used to specify the maximum width of any traffic on
 the highway.  This must be set to the minimum width of the constraints at the
@@ -267,7 +280,7 @@ wayside in metres.  If the tag value contains a measurement in feet or feet and
 inches then attempts are made to convert this to metres.
 
 
-<h4><a name="H_1_2_2_17" title="maxlength"></a>The maxlength Tag</h4>
+<h4 id="H_1_2_2_19" title="maxlength">The maxlength Tag</h4>
 
 The <em>maxlength</em> tag is used to specify the maximum length of any traffic
 on the highway (usually from a traffic sign) in metres.  If the tag value
@@ -275,7 +288,7 @@ contains a measurement in feet or feet and inches then attempts are made to
 convert this to metres.
 
 
-<h4><a name="H_1_2_2_18" title="area"></a>The area Tag</h4>
+<h4 id="H_1_2_2_20" title="area">The area Tag</h4>
 
 The <em>area</em> tag is used to specify that a way defines an area.  This is
 used only so that in the case of duplicated segments those belonging to an area
@@ -284,7 +297,7 @@ can be discarded in preference to those that are not.
 
 <!-- ========================= Routino Relation Tags ========================= -->
 
-<h3><a name="H_1_2_3" title="Relations"></a>Relation Tags And Attributes</h3>
+<h3 id="H_1_2_3" title="Relations">Relation Tags And Attributes</h3>
 
 The tags from the relations are used to associate more properties with the
 highways that are part of that relation.  The <em>id</em> attribute is used so
@@ -293,19 +306,19 @@ The <em>member</em> information is used to identify the nodes and ways that make
 up the relation.
 
 
-<h4><a name="H_1_2_3_1" title="footroute"></a>The footroute Tag</h4>
+<h4 id="H_1_2_3_1" title="footroute">The footroute Tag</h4>
 
 The <em>footroute</em> tag is used to identify whether a relation defines a
 walking route and therefore should be applied to the individual member highways.
 
 
-<h4><a name="H_1_2_3_2" title="bicycleroute"></a>The bicycleroute Tag</h4>
+<h4 id="H_1_2_3_2" title="bicycleroute">The bicycleroute Tag</h4>
 
 The <em>bicycleroute</em> tag is used to identify whether a relation defines a
 bicycle route and therefore should be applied to the individual member highways.
 
 
-<h4><a name="H_1_2_3_3" title="type, restriction, except"></a>The type, restriction & except Tags</h4>
+<h4 id="H_1_2_3_3" title="type, restriction, except">The type, restriction & except Tags</h4>
 
 For turn relations the information about the allowed or disallowed turns are
 stored in the <em>type</em>, <em>restriction</em> and <em>except</em> tags.  For
@@ -317,7 +330,7 @@ restriction.
 
 <!-- ==================== Tag Transformations ==================== -->
 
-<h2><a name="H_1_3" title="Tag Transformations"></a>Tag Transformations</h2>
+<h2 id="H_1_3">Tag Transformations</h2>
 
 This section describes the set of tag transformations that are contained in the
 default configuration file.  The configuration file tagging rules are applied in
@@ -326,9 +339,9 @@ sequence and this section of the document is arranged in the same order.
 
 <!-- ==================== Node Tag Transformations ==================== -->
 
-<h3><a name="H_1_3_1" title="Nodes"></a>Node Tag Transformations</h3>
+<h3 id="H_1_3_1" title="Nodes">Node Tag Transformations</h3>
 
-<h4><a name="H_1_3_1_1" title="Barrier Defaults"></a>Barrier Defaults</h4>
+<h4 id="H_1_3_1_1">Barrier Defaults</h4>
 
 The first part of the tag transformations is to decide on defaults for each type
 of node.  This uses the <em>barrier</em> tag in the OSM file and converts it into
@@ -345,7 +358,7 @@ a default set of disallowed transport types.
     <th class="center">wheelchair
     <th class="center">bicycle
     <th class="center">moped
-    <th class="center">motorbike
+    <th class="center">motorcycle
     <th class="center">motorcar
     <th class="center">goods
     <th class="center">hgv
@@ -412,23 +425,23 @@ a default set of disallowed transport types.
     <td class="center">no
 </table>
 
-<h4><a name="H_1_3_1_2" title="Generic access"></a>Generic Access Permissions</h4>
+<h4 id="H_1_3_1_2" title="Generic access">Generic Access Permissions</h4>
 
 The <em>access</em> tag is used to specify the default access restrictions
-through the node.  If the tag value is <em>no</em> or <em>private</em>
-or <em>limited</em> then all transport types are denied access (later tag
+through the node.  If the tag value is <em>no</em> or <em>private</em> or a
+selection of other values then all transport types are denied access (later tag
 transformation rules may add specific transport types back again).
 
 
-<h4><a name="H_1_3_1_3" title="Other access"></a>Other Access Permissions</h4>
+<h4 id="H_1_3_1_3" title="Other access">Other Access Permissions</h4>
 
 A tag named <em>vehicle</em> means any of the <em>bicycle</em>, <em>moped</em>,
-<em>motorbike</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
+<em>motorcycle</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
 and <em>psv</em> transport types.  A tag named <em>motor_vehicle</em> is
 transformed to mean any vehicle except a <em>bicycle</em>.
 
 
-<h4><a name="H_1_3_1_4" title="Specific access"></a>Specific Access Permissions</h4>
+<h4 id="H_1_3_1_4" title="Specific access">Specific Access Permissions</h4>
 
 The final part of the access permissions is to use the specific transport type
 tags.
@@ -437,24 +450,25 @@ tags.
 
 One tag is recognised for each of the different modes of transport: <em>foot</em>,
 <em>horse</em>, <em>bicycle</em>, <em>wheelchair</em>, <em>moped</em>,
-<em>motorbike</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
+<em>motorcycle</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
 and <em>psv</em>.  These indicate whether the specific type of transport is
 allowed through the node or not; the values listed for the <em>access</em> tag
 are also accepted here.
 
 
-<h4><a name="H_1_3_1_5" title="Mini-roundabouts"></a>Mini-roundabouts</h4>
+<h4 id="H_1_3_1_5">Mini-roundabouts</h4>
 
-The <em>highway</em> tag for <em>mini_roundabout</em> is passed through without
-change.
+If the <em>highway</em> tag has the value <em>mini_roundabout</em> or
+the <em>junction</em> tag has the value <em>roundabout</em> then
+a <em>junction</em> tag with value <em>roundabout</em>is passed through.
 
 
 <!-- ==================== Way Tag Transformations ==================== -->
 
-<h3><a name="H_1_3_2" title="Ways"></a>Way Tag Transformations</h3>
+<h3 id="H_1_3_2" title="Ways">Way Tag Transformations</h3>
 
 
-<h4><a name="H_1_3_2_1" title="Highway Defaults"></a>Highway Defaults</h4>
+<h4 id="H_1_3_2_1">Highway Defaults</h4>
 
 The first part of the tag transformations is to decide on defaults for each type
 of highway.  This uses the <em>highway</em> tag in the OSM file and maps it into
@@ -462,9 +476,15 @@ one of the <em>highway</em> tags that are recognised by Routino, defining the
 default allowed transport types and adding a number of properties.
 
 <p>
+The first part of the highway tag checking is to ignore the <em>highway</em> tag
+if it has a value that indicates a non-highway.  These are the <em>proposed</em>
+and <em>construction</em> values for future highways, the <em>no</em>,
+<em>abandoned</em> and <em>disused</em> values for previous highways and a small
+number of other values.
 
-The first part of the transformation is to convert the highway tag into one that
-is recognised by Routino.
+<p>
+The second part of the highway transformation is to convert the <em>highway</em>
+tag into one that is recognised by Routino.
 
 <p>
 
@@ -514,7 +534,6 @@ is recognised by Routino.
 </i>
 
 <p>
-
 The type of highway also determines the defaults for the types of transport
 allowed on the highway.  The default assumptions are as shown in the table
 below.
@@ -530,7 +549,7 @@ below.
     <th class="center">wheelchair
     <th class="center">bicycle
     <th class="center">moped
-    <th class="center">motorbike
+    <th class="center">motorcycle
     <th class="center">motorcar
     <th class="center">goods
     <th class="center">hgv
@@ -549,9 +568,9 @@ below.
     <td class="center">yes
   <tr>
     <td class="left">trunk
-    <td class="center">no
-    <td class="center">no
-    <td class="center">no
+    <td class="center">no (1)
+    <td class="center">no (1)
+    <td class="center">no (1)
     <td class="center">yes
     <td class="center">yes
     <td class="center">yes
@@ -658,9 +677,9 @@ below.
   <tr>
     <td class="left">path
     <td class="center">yes
-    <td class="center">yes (1)
+    <td class="center">yes (2)
     <td class="center">yes
-    <td class="center">yes (1)
+    <td class="center">yes (2)
     <td class="center">no
     <td class="center">no
     <td class="center">no
@@ -684,19 +703,21 @@ below.
 <p>
 
 <i>
-  Note 1: A path allows bicycle or horse access by default only if actually
+  Note 1: A trunk road may legally allow foot, horse or wheelchair access but in
+  the absence of other tags is considered to be too dangerous.
+  <br>
+  Note 2: A path allows bicycle or horse access by default only if actually
   labelled as a highway of type "bridleway".
 </i>
 
 <p>
-
-Finally for the highway tag a number of properties are added depending on the
-highway type.
+Finally for the highway tag a number of default properties are added depending
+on the highway type.
 
 <p>
 
 <table>
-  <caption>Properties on different highway types</caption>
+  <caption>Default properties on different highway types</caption>
   <tr>
     <th class="center">Highway
     <th class="center">Properties
@@ -705,10 +726,10 @@ highway type.
     <td class="center">paved, oneway, multilane
   <tr>
     <td class="center">trunk
-    <td class="center">paved
+    <td class="center">paved, multilane (1)
   <tr>
     <td class="center">primary
-    <td class="center">paved
+    <td class="center">paved, multilane (1)
   <tr>
     <td class="center">secondary
     <td class="center">paved
@@ -726,13 +747,13 @@ highway type.
     <td class="center">paved
   <tr>
     <td class="center">track
-    <td class="center">paved (1)
+    <td class="center">paved (2)
   <tr>
     <td class="center">cycleway
     <td class="center">paved
   <tr>
     <td class="center">path
-    <td class="center">paved (2)
+    <td class="center">paved (3)
   <tr>
     <td class="center">steps
     <td class="center">
@@ -741,30 +762,32 @@ highway type.
 <p>
 
 <i>
-  Note 1: A track is paved only if it is tagged as tracktype=grade1.
+  Note 1: A highway of this type has the multilane property by default if it is
+  oneway.
   <br>
-  Note 2: A path is paved only if it was originally tagged as highway=walkway or
+  Note 2: A track is paved only if it is tagged as tracktype=grade1.
+  <br>
+  Note 3: A path is paved only if it was originally tagged as highway=walkway or
   highway=pedestrian.
 </i>
 
 
-<h4><a name="H_1_3_2_2" title="Generic access"></a>Generic Access Permissions</h4>
+<h4 id="H_1_3_2_2" title="Generic access">Generic Access Permissions</h4>
 
 The <em>access</em> tag is used to specify the default access restrictions on
-the highway.  If the tag value is <em>no</em> or <em>private</em> then all
-transport types are denied access (later tag transformation rules may add
-specific transport types back again).
+the highway.  If the tag value is <em>no</em> or <em>private</em> or a selection
+of other values then all transport types are denied access (later tag
+transformation rules may add specific transport types back again).
 
 
-<h4><a name="H_1_3_2_3" title="Other access"></a>Other Access Permissions</h4>
+<h4 id="H_1_3_2_3" title="Other access">Other Access Permissions</h4>
 
 A tag named <em>vehicle</em> means any of the <em>bicycle</em>, <em>moped</em>,
-<em>motorbike</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
+<em>motorcycle</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
 and <em>psv</em> transport types.  A tag named <em>motor_vehicle</em> is
 transformed to mean any vehicle except a <em>bicycle</em>.
 
 <p>
-
 The <em>designation</em> tag is used as an alternative method of identifying the
 legal right of way on a path (in the UK at least).  The tag transformations
 convert these tags into a set of allowed transport types as shown below.
@@ -781,7 +804,7 @@ convert these tags into a set of allowed transport types as shown below.
     <td class="left">foot=yes, wheelchair=yes, horse=yes, bicycle=yes
   <tr>
     <td class="left">public_byway, byway, byway_open_to_all_traffic
-    <td class="left">foot=yes, wheelchair=yes, horse=yes, bicycle=yes, moped=yes, motorbike=yes, motorcar=yes
+    <td class="left">foot=yes, wheelchair=yes, horse=yes, bicycle=yes, moped=yes, motorcycle=yes, motorcar=yes
   <tr>
     <td class="left">permissive_bridleway, public_bridleway, bridleway
     <td class="left">foot=yes, wheelchair=yes, horse=yes, bicycle=yes
@@ -793,8 +816,26 @@ convert these tags into a set of allowed transport types as shown below.
     <td class="left">foot=yes, wheelchair=yes
 </table>
 
+<p>
+In addition to these there are some other tags and values that will modify the
+transport permissions on the highway.
+
+<p>
+A highway that is tagged as <em>motorroad</em> with a value of <em>yes</em> will
+deny access to foot, horse, wheelchair, bicycle and moped transport.
+
+<p>
+A highway that is tagged with <em>footway</em> or <em>sidewalk</em> and one of a
+set of popular values will allow foot and wheelchair access even if the road
+type would not normally do so.
+
+<p>
+A highway that is tagged as <em>cycleway</em> with one of several values will
+allow bicycle access.  If the value of the <em>cycleway</em> tag
+is <em>opposite_lane</em> then the <em>cyclebothways</em> tag is set.
+
 
-<h4><a name="H_1_3_2_4" title="Specific access"></a>Specific Access Permissions</h4>
+<h4 id="H_1_3_2_4" title="Specific access">Specific Access Permissions</h4>
 
 The final part of the access permissions is to use the specific transport type
 tags.
@@ -803,12 +844,12 @@ tags.
 
 One tag is recognised for each of the different modes of transport: <em>foot</em>,
 <em>horse</em>, <em>bicycle</em>, <em>wheelchair</em>, <em>moped</em>,
-<em>motorbike</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
+<em>motorcycle</em>, <em>motorcar</em>, <em>goods</em>, <em>hgv</em>
 and <em>psv</em>.  These indicate whether the specific type of transport is
 allowed on the highway or not.
 
 
-<h4><a name="H_1_3_2_5" title="Properties"></a>Highway Properties</h4>
+<h4 id="H_1_3_2_5" title="Properties">Highway Properties</h4>
 
 If there is a surface tag then the highway is assumed to be unpaved unless the
 tag value matches one of the following: <em>paved</em>, <em>asphalt</em>,
@@ -821,9 +862,8 @@ paved if this is set to a true value.
 
 <p>
 
-The <em>lanes</em> tag is used to identify whether a highway has multiple lanes
-for traffic or not (the number of lanes is not important in this case, only
-whether it is more than one) this sets one of the highway properties.
+The <em>lanes</em> tag is passed through to be used to set
+the <em>multilane</em> highway property.
 
 <p>
 
@@ -831,38 +871,38 @@ The <em>bridge</em> and <em>tunnel</em> tags are copied directly from the input
 to the output.
 
 
-<h4><a name="H_1_3_2_6" title="Restrictions"></a>Highway Restrictions</h4>
+<h4 id="H_1_3_2_6" title="Restrictions">Highway Restrictions</h4>
 
 The <em>oneway</em>, <em>maxspeed</em>, <em>maxweight</em>, <em>maxheight</em>,
 <em>maxwidth</em> and <em>maxlength</em> are copied directly from the input to
 the output without modification.
 
 
-<h4><a name="H_1_3_2_7" title="Roundabouts"></a>Roundabouts</h4>
+<h4 id="H_1_3_2_7">Roundabouts</h4>
 
 If a highway is tagged as <em>junction=roundabout</em> then
 a <em>roundabout=yes</em> tag created on the output.
 
 
-<h4><a name="H_1_3_2_8" title="Names and Refs"></a>Highway Names and References</h4>
+<h4 id="H_1_3_2_8" title="Names and Refs">Highway Names and References</h4>
 
 The <em>name</em> and <em>ref</em> tags are copied directly from the input to
 the output.
 
 
-<h4><a name="H_1_3_2_9" title="Areas"></a>Highway Areas</h4>
+<h4 id="H_1_3_2_9" title="Areas">Highway Areas</h4>
 
 The <em>area</em> tag is copied directly from the input to the output.
 
 
 <!-- ==================== Relation Tag Transformations ==================== -->
 
-<h3><a name="H_1_3_3" title="Relations"></a>Relation Tag Transformations</h3>
+<h3 id="H_1_3_3" title="Relations">Relation Tag Transformations</h3>
 
 The <em>type</em> tag is passed through without change.
 
 
-<h4><a name="H_1_3_3_1" title="Routes"></a>Routes</h4>
+<h4 id="H_1_3_3_1">Routes</h4>
 
 The <em>route</em> tag can be used to determine whether a relation is part of a
 walking of bicycle route so that the footroute and bicycleroute properties can
@@ -896,7 +936,7 @@ table below.
 </table>
 
 
-<h4><a name="H_1_3_3_2" title="Turn Restrictions"></a>Turn Restrictions</h4>
+<h4 id="H_1_3_3_2">Turn Restrictions</h4>
 
 No tag transformations are defined for turn restriction relations but
 the <em>restriction</em> and <em>except</em> tags are passed through without
@@ -909,17 +949,17 @@ change.
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/doc/html/usage.html b/doc/html/usage.html
index 1b65bc0..ea148b9 100644
--- a/doc/html/usage.html
+++ b/doc/html/usage.html
@@ -1,12 +1,17 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Routino : Usage</title>
 
 <!--
  Routino documentation - usage
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,17 +27,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Usage</TITLE>
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Usage</h1>
 
@@ -45,7 +47,7 @@
 
 <div class="content">
 
-<h2><a name="H_1_1"></a>Program Usage</h2>
+<h2 id="H_1_1">Program Usage</h2>
 
 There are five programs that make up this software.  The first one takes the
 planet.osm datafile from OpenStreetMap (or other source of data using the same
@@ -55,7 +57,7 @@ allows visualisation of the data and statistics to be extracted.  The fourth
 program allows dumping the raw parsed data for test purposes and the fifth is a
 test program for the tag transformations.
 
-<h3><a name="H_1_1_1"></a>planetsplitter</h3>
+<h3 id="H_1_1_1">planetsplitter</h3>
 
 This program reads in the OSM format XML file and splits it up to create the
 database that is used for routing.
@@ -75,7 +77,12 @@ Usage: planetsplitter [--help]
                       [--prune-isolated=<len>]
                       [--prune-short=<len>]
                       [--prune-straight=<len>]
-                      [<filename.osm> ...]
+                      [<filename.osm> ... | <filename.osc> ...
+                       | <filename.pbf> ...
+                       | <filename.o5m> ... | <filename.o5c> ...
+                       | <filename.(osm|osc|o5m|o5c).bz2> ...
+                       | <filename.(osm|osc|o5m|o5c).gz> ...
+                       | <filename.(osm|osc|o5m|o5c).xz> ...]
 </pre>
 
 <dl>
@@ -114,7 +121,8 @@ Usage: planetsplitter [--help]
   <dd>Log OSM parsing and processing errors to 'error.log' or the specified file
     name (the '--dir' and '--prefix' options are applied).  If the --append
     option is used then the existing log file will be appended, otherwise a new
-    one will be created.
+    one will be created.  If the --keep option is also used a geographically
+    searchable database of error logs is created for use in the visualiser.
   <dt>--parse-only
   <dd>Parse the input files and store the data in intermediate files but don't
     process the data into a routing database.  This option must be used with the
@@ -142,7 +150,7 @@ Usage: planetsplitter [--help]
     the command line after this option.
   <dt>--prune-isolated=<length>
   <dd>Remove the access permissions for a transport type from small disconnected
-    groups of segments and remove the segments if they nd up with no access
+    groups of segments and remove the segments if they end up with no access
     permission (defaults to removing groups under 500m).
   <dt>--prune-short=<length>
   <dd>Remove short segments (defaults to removing segments up to a maximum
@@ -150,24 +158,20 @@ Usage: planetsplitter [--help]
   <dt>--prune-straight=<length>
   <dd>Remove nodes in almost straight highways (defaults to removing nodes up to
     3m offset from a straight line).
-  <dt><filename.osm> ...
-  <dd>Specifies the filename(s) to read data from, by default data is read from
-    the standard input.
+  <dt><filename.osm>, <filename.osc>, <filename.pbf>, <filename.o5m>, <filename.o5c>
+  <dd>Specifies the filename(s) to read data from.  Filenames ending '.pbf' will
+    be read as PBF, filenames ending in '.o5m' or '.o5c' will be read as
+    O5M/O5C, otherwise as XML.  Filenames ending '.bz2' will be bzip2
+    uncompressed (if bzip2 support compiled in).  Filenames ending '.gz' will be
+    gzip uncompressed (if gzip support compiled in).  Filenames ending '.xz'
+    will be xz uncompressed (if xz support compiled in).
 </dl>
 
 <p>
-<i>Note: In version 1.4 of Routino the --transport, --not-highway and
---not-property options have been removed.  The same functionality can be
-achieved by editing the tagging rules file to not output unwanted data.</i>
-
-<p>
-<i>Note: In version 1.5 of Routino the --slim option has been removed but at
-compilation time a separate program called <em>planetsplitter-slim</em> is
-created that operates in slim mode.  In slim mode the temporary files and
-database files are read as needed rather than being mapped into memory.  This
-allows a database size greater than 2 GB on 32-bit machines or usage with little
-or no virtual memory (e.g. some virtual machines).  The penalty for this is that
-the program takes about twice as long to run.</i>
+<i>Note: In version 2.5 of Routino the ability to read data from the standard
+input has been removed.  This is because there is now the ability to read
+compressed files (bzip2, gzip, xz) and PBF files directly.  Also using standard
+input the file type cannot be auto-detected from the filename.</i>
 
 <p>
 Example usage 1:
@@ -198,7 +202,7 @@ read in must not use the --append option but the later ones must.
 Example usage 3:
 
 <pre class="boxed">
-planetsplitter --dir=data --prefix=gb --keep    great_britain_part.osm
+planetsplitter --dir=data --prefix=gb --keep    great_britain.osm
 
 planetsplitter --dir=data --prefix=gb --changes great_britain.osc
 </pre>
@@ -215,7 +219,7 @@ file(s) and the --changes option used with --parse-only or --process-only for
 every OSC file.
 
 
-<h3><a name="H_1_1_2"></a>router</h3>
+<h3 id="H_1_1_2">router</h3>
 
 This program performs the calculation of the optimum routes using the database
 generated by the planetsplitter program.
@@ -230,13 +234,14 @@ Usage: router [--help | --help-profile | --help-profile-xml |
               [--output-html]
               [--output-gpx-track] [--output-gpx-route]
               [--output-text] [--output-text-all]
-              [--output-none]
+              [--output-none] [--output-stdout]
               [--profile=<name>]
               [--transport=<transport>]
               [--shortest | --quickest]
               --lon1=<longitude> --lat1=<latitude>
               --lon2=<longitude> --lon2=<latitude>
               [ ... --lon99=<longitude> --lon99=<latitude>]
+              [--reverse] [--loop]
               [--heading=<bearing>]
               [--highway-<highway>=<preference> ...]
               [--speed-<highway>=<speed> ...]
@@ -305,6 +310,9 @@ Usage: router [--help | --help-profile | --help-profile-xml |
   not specified.
   <dt>--output-none
   <dd>Do not generate any output or read in any translations files.
+  <dt>--output-stdout
+  <dd>Write to stdout instead of a file (requires exactly one output format
+    option, implies '--quiet').
   <dt>--profile=<name>
   <dd>Specifies the name of the profile to use.
   <dt>--transport=<transport>
@@ -314,8 +322,8 @@ Usage: router [--help | --help-profile | --help-profile-xml |
       <li>horse      = Horse
       <li>wheelchair = Wheelchair
       <li>bicycle    = Bicycle
-      <li>moped      = Moped     (Small motorbike, limited speed)
-      <li>motorbike  = Motorbike
+      <li>moped      = Moped     (Small motorcycle, limited speed)
+      <li>motorcycle = Motorcycle
       <li>motorcar   = Motorcar
       <li>goods      = Goods     (Small lorry, van)
       <li>hgv        = HGV       (Heavy Goods Vehicle - large lorry)
@@ -336,6 +344,10 @@ Usage: router [--help | --help-profile | --help-profile-xml |
   pass through each of the specified ones in sequence.  The algorithm will use
   the closest node or point within a segment that allows the specified traffic
   type.
+  <dt>--reverse
+  <dd>Find a route between the waypoints in reverse order.
+  <dt>--loop
+  <dd>Find a route that returns to the first waypoint after the last one.
   <dt>--heading=<bearing>
   <dd>Specifies the initial direction of travel at the start of the route (from
   the lowest numbered waypoint) as a compass bearing from 0 to 360 degrees.
@@ -401,12 +413,6 @@ Usage: router [--help | --help-profile | --help-profile-xml |
 </dl>
 
 <p>
-<i>Note: In version 1.5 of Routino a slim option has been added and at
-compilation time a separate program called <em>router-slim</em> is created that
-operates in slim mode.  In slim mode the database files are read as needed
-rather than being mapped into memory.</i>
-
-<p>
 The meaning of the <preference> parameter in the command line options is
 slightly different for the highway preferences and the property preferences.
 For the highway preference consider the choice between two possible highways
@@ -422,19 +428,19 @@ for each highway segment is the product of the preference for the highway type
 and all of the preferences for the highway properties.
 
 <p>
-Example usage (motorbike journey, scenic route, not very fast):
+Example usage (motorcycle journey, scenic route, not very fast):
 
 <pre class="boxed">
-router --dir=data --prefix=gb --transport=motorbike --highway-motorway=0 \
+router --dir=data --prefix=gb --transport=motorcycle --highway-motorway=0 \
        --highway-trunk=0 --speed-primary=80 --speed-secondary=80 --quickest
 </pre>
 
 This will use the files 'data/gb-nodes.mem', 'data/gb-segments.mem' and
-'data/gb-ways.mem' to find the quickest route by motorbike not using motorways
+'data/gb-ways.mem' to find the quickest route by motorcycle not using motorways
 or trunk roads and not exceeding 80 km/hr.
 
 
-<h3><a name="H_1_1_3"></a>filedumper</h3>
+<h3 id="H_1_1_3">filedumper</h3>
 
 This program is used to extract statistics from the database, extract particular
 information for visualisation purposes or for dumping the database contents.
@@ -449,10 +455,15 @@ Usage: filedumper [--help]
                   [--dump [--node=<node> ...]
                           [--segment=<segment> ...]
                           [--way=<way> ...]
-                          [--turn-relation=<relation> ...]]
+                          [--turn-relation=<relation> ...]
+                          [--errorlog=<number> ...]]
                   [--dump-osm [--no-super]
                               [--latmin=<latmin> --latmax=<latmax>
                                --lonmin=<lonmin> --lonmax=<lonmax>]]
+                  [--dump-visualiser [--data=node<node>]
+                                     [--data=segment<segment>]
+                                     [--data=turn-relation<rel>]
+                                     [--data=errorlog<number>]]
 </pre>
 
 <dl>
@@ -489,6 +500,9 @@ Usage: filedumper [--help]
           <li>height      = height limits.
           <li>width       = width limits.
           <li>length      = length limits.
+          <li>property-*  = segments having the specified property
+          (e.g. property-paved to display segments of paved highway).
+          <li>errorlogs   = errors logged during parsing.
         </ul>
     </dl>
   <dt>--dump
@@ -509,6 +523,9 @@ Usage: filedumper [--help]
       <dd>Prints the information about the selected turn relation number
         (internal number, not the relation id number in the original source
         file).
+      <dt>--errorlog=<number>
+      <dd>Prints the information about the selected error log that was stored
+        when the data was parsed.
     </dl>
   <dt>--osm-dump
   <dd>Dumps the contents of the database as an OSM format XML file, the whole
@@ -522,16 +539,27 @@ Usage: filedumper [--help]
       <dt>--lonmin=<lonmin> --lonmax=<lonmax>
       <dd>The range of longitudes to dump the data for.
     </dl>
+  <dt>--dump-visualiser
+  <dd>Dumps the contents of the database as HTML formatted items for display in
+    the visualiser web page.
+    <dl>
+      <dt>--data=node<node>
+      <dd>Prints the information about the selected node number (internal
+        node number, not from the original source file).
+      <dt>--data=segment<segment>
+      <dd>Prints the information about the selected segment number as if it was
+        a way (internal segment number, unrelated to original source file).
+      <dt>--data=turn-relation<relation>
+      <dd>Prints the information about the selected turn relation number
+        (internal turn relation number, not from the original source file).
+      <dt>--data=errorlog<number>
+      <dd>Prints the information about the selected error log that was stored
+        when the data was parsed.
+    </dl>
 </dl>
 
-<p>
-<i>Note: In version 1.5 of Routino a slim option has been added and at
-compilation time a separate program called <em>filedumper-slim</em> is created
-that operates in slim mode.  In slim mode the database files are read as needed
-rather than being mapped into memory.</i>
-
 
-<h3><a name="H_1_1_4"></a>filedumperx</h3>
+<h3 id="H_1_1_4">filedumperx</h3>
 
 This program is a modified version of filedumper that will dump out the contents
 of the intermediate data that is saved by planetsplitter after processing using
@@ -539,13 +567,12 @@ the --keep or --changes option.  This is intended for test purposes only and
 gives no useful information about the routing database.
 
 <pre class="boxed">
-Usage: filedumper [--help]
-                  [--dir=<dirname>] [--prefix=<name>]
-                  [--dump [--nodes]
-                          [--segments]
-                          [--ways]
-                          [--route-relations]
-                          [--turn-relations]]
+Usage: filedumperx [--help]
+                   [--dir=<dirname>] [--prefix=<name>]
+                   [--dump [--nodes]
+                           [--ways]
+                           [--route-relations]
+                           [--turn-relations]]
 </pre>
 
 <dl>
@@ -562,8 +589,6 @@ Usage: filedumper [--help]
     <dl>
       <dt>--nodes
       <dd>Dumps the node data.
-      <dt>--segments
-      <dd>Dumps the segment data.
       <dt>--ways
       <dd>Dumps the way data.
       <dt>--route-relations
@@ -573,51 +598,23 @@ Usage: filedumper [--help]
     </dl>
 </dl>
 
-
-<h3><a name="H_1_1_5"></a>tagmodifier</h3>
-
-This program is used to run the tag transformation process on an OSM XML file
-for test purposes.
-
-<pre class="boxed">
-Usage: tagmodifier [--help]
-                   [--loggable]
-                   [--tagging=<filename>]
-                   [<filename.osm>]
-</pre>
-
-<dl>
-  <dt>--help
-  <dd>Prints out the help information.
-  <dt>--loggable
-  <dd>Print progress messages that are suitable for logging to a file; normally
-    an incrementing counter is printed which is more suitable for real-time
-    display than logging.
-  <dt>--tagging=<filename>
-  <dd>The name of the XML file containing the tagging rules (defaults to
-    'tagging.xml' in the current directory).
-  <dt><filename.osm> ...
-  <dd>Specifies the filename to read data from, by default data is read from
-    the standard input.
-</dl>
-
 </div>
 
 <!-- Content End -->
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/Makefile b/extras/Makefile
similarity index 62%
copy from Makefile
copy to extras/Makefile
index e157c1e..f36a56d 100644
--- a/Makefile
+++ b/extras/Makefile
@@ -1,8 +1,8 @@
-# Top level Makefile
+# Extra files Makefile
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2009-2011 Andrew M. Bishop
+# This file Copyright 2013 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -18,56 +18,50 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Installation locations
+# All configuration is in the top-level Makefile.conf
 
-prefix=/usr/local
-bindir=$(prefix)/bin
-docdir=$(prefix)/doc/routino
-datadir=$(prefix)/share/routino
+include ../Makefile.conf
 
 # Sub-directories and sub-makefiles
 
-TOPFILES=$(wildcard */Makefile)
-TOPDIRS=$(foreach f,$(TOPFILES),$(dir $f))
+SUBFILES=$(wildcard */Makefile)
+SUBDIRS=$(foreach f,$(SUBFILES),$(dir $f))
 
 ########
 
-all$(top):
-	for dir in $(TOPDIRS); do \
+all:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-test$(top):
-	for dir in $(TOPDIRS); do \
+test:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-install$(top): all$(top)
-	for dir in $(TOPDIRS); do \
+install:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
-	@echo "Note: web directory is not installed automatically"
 
 ########
 
-clean$(top):
-	for dir in $(TOPDIRS); do \
+clean:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-distclean$(top): clean$(top)
-	for dir in $(TOPDIRS); do \
+distclean:
+	for dir in $(SUBDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
 
 ########
 
-.PHONY:: all$(top) test$(top) install$(top) clean$(top) distclean$(top)
-
 .PHONY:: all test install clean distclean
diff --git a/extras/README.txt b/extras/README.txt
new file mode 100644
index 0000000..d5faff4
--- /dev/null
+++ b/extras/README.txt
@@ -0,0 +1,27 @@
+                                 ROUTINO EXTRAS
+                                 ==============
+
+This directory contains some programs and scripts that although distributed with
+Routino are not necessary components of a working OSM router.  They are
+generally either programs that use some components of Routino (i.e. they are
+compiled and linked with some of the Routino source code) or they are scripts to
+be used to process the outputs of Routino.
+
+Each program or script has its own directory which contains all of the necessary
+source code, documentation and/or web pages for that program or script.  None of
+them will be installed when Routino is installed.
+
+--------------------------------------------------------------------------------
+
+tagmodifier - A program to read an OSM XML file and process it using a Routino
+              tagging rules file to create a modified output XML file.
+
+errorlog    - Scripts for processing the error log file (created by running
+              planetsplitter with the --errorlog option).
+
+plot-time   - Plots the output of 'planetsplitter --loggable --logtime' to show
+              how long each part of the processing takes.
+
+find-fixme -  A modified version of the Routino planetsplitter and filedumper
+              programs to scan an OSM file for "fixme" tags and create a
+              database so that web pages provided can display them.
diff --git a/extras/errorlog/README.txt b/extras/errorlog/README.txt
new file mode 100644
index 0000000..2e2d065
--- /dev/null
+++ b/extras/errorlog/README.txt
@@ -0,0 +1,29 @@
+                              Error Log Summariser
+                              ====================
+
+This Perl script can be used to process the log file generated by runing
+'planetsplitter --errorlog' and generate a summary of the most common types of
+errors.
+
+summarise-log.pl
+----------------
+
+Example usage:
+
+summarise-log.pl < error.log
+
+          Generate a summary of the number of each type of error that appear in
+          the error log file as plain text.
+
+
+summarise-log.pl -v < error.log
+
+          Generate a verbose version of the plain text summary of errors with
+          each error item (node, way or relation) listed.
+
+
+summarise-log.pl -html < error.log
+
+          Generate an HTML file with a summary of the number of errors of each
+          type with links to each of the items (node, way or relation) on the
+          OSM website.
diff --git a/web/bin/summarise-log.pl b/extras/errorlog/summarise-log.pl
similarity index 59%
rename from web/bin/summarise-log.pl
rename to extras/errorlog/summarise-log.pl
index 9fe0c1b..6b89f99 100755
--- a/web/bin/summarise-log.pl
+++ b/extras/errorlog/summarise-log.pl
@@ -1,9 +1,33 @@
 #!/usr/bin/perl
-
-$verbose=0;
+#
+# Routino log summary tool.
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2011-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+# Command line
+
+my $verbose=0;
 $verbose=1 if($#ARGV==0 && $ARGV[0] eq "-v");
 
-$html=0;
+my $html=0;
 $html=1 if($#ARGV==0 && $ARGV[0] eq "-html");
 
 die "Usage: $0 [-v | -html] < <error-log-file>\n" if($#ARGV>0 || ($#ARGV==0 && !$verbose && !$html));
@@ -11,15 +35,16 @@ die "Usage: $0 [-v | -html] < <error-log-file>\n" if($#ARGV>0 || ($#ARGV==0 && !
 
 # Read in each line from the error log and store them
 
-%errors=();
-%errorids=();
-%errortypes=();
+my %errors=();
+my %errorids=();
+my %errortypes=();
 
 while(<STDIN>)
   {
    s%\r*\n%%;
 
-   undef $errorid;
+   my $errorid="";
+   my $errortype="";
 
    if(m%nodes ([0-9]+) and ([0-9]+) in way ([0-9]+)%i) # Special case pair of nodes and a way
      {
@@ -28,6 +53,20 @@ while(<STDIN>)
       s%nodes [0-9]+ and [0-9]+ in way [0-9]+%nodes <node-id1> and <node-id2> in way <way-id>%;
      }
 
+   elsif(m%node ([0-9]+) in way ([0-9]+)%i) # Special case node and a way
+     {
+      $errorid="($1 $2)";
+      $errortype="NW";
+      s%Node [0-9]+ in way [0-9]+%Node <node-id> in way <way-id>%;
+     }
+
+   elsif(m%way ([0-9]+) contains node ([0-9]+)%i) # Special case way and node
+     {
+      $errorid="($1 $2)";
+      $errortype="WN";
+      s%Way [0-9]+ contains node [0-9]+%Way <way-id> contains node <node-id>%;
+     }
+
    elsif(m%nodes ([0-9]+) and ([0-9]+)%i) # Special case pair of nodes
      {
       $errorid="($1 $2)";
@@ -35,9 +74,9 @@ while(<STDIN>)
       s%nodes [0-9]+ and [0-9]+%nodes <node-id1> and <node-id2>%;
      }
 
-   elsif(m%Segment connects node ([0-9]+)%) # Special case segment
+   elsif(m%Segment (contains|connects) node ([0-9]+)%) # Special case node
      {
-      $errorid=$1;
+      $errorid=$2;
       $errortype="N";
       s%node [0-9]+%node <node-id>%;
      }
@@ -92,11 +131,11 @@ while(<STDIN>)
      {
       if(defined $errorids{$_})
         {
-         $errorids{$_}.=",$errorid";
+         push(@{$errorids{$_}},$errorid);
         }
       else
         {
-         $errorids{$_}="$errorid";
+         $errorids{$_}=[$errorid];
         }
      }
 
@@ -111,18 +150,18 @@ while(<STDIN>)
 
 if( ! $html )
   {
-
-   foreach $error (sort { if ( $errors{$b} == $errors{$a} ) { return $errorids{$a} cmp $errorids{$b} }
-                          else                              { return $errors{$b}   <=> $errors{$a}   } } (keys %errors))
+   foreach my $error (sort { if ( $errors{$b} == $errors{$a} ) { return $errors{$a} cmp $errors{$b} }
+                             else                              { return $errors{$b} <=> $errors{$a} } } (keys %errors))
      {
       printf "%9d : $error\n",$errors{$error};
 
-      if($verbose && defined $errorids{$error})
+      if($verbose)
         {
-         print "            $errorids{$error}\n";
+         my @ids=sort({ return $a <=> $b } @{$errorids{$error}});
+
+         print "            ".join(",", at ids)."\n";
         }
      }
-
   }
 
 # Print out the results as HTML
@@ -155,35 +194,39 @@ else
          "that are responsible for the error messages.\n".
          "\n";
 
-   %errortypeorder=(
-                    "N"   , 1,
-                    "N2W" , 2,
-                    "N2"  , 3,
-                    "W"   , 4,
-                    "R"   , 5,
-                    "RN"  , 6,
-                    "RW"  , 7,
-                    "E"   , 8
-                   );
-
-   %errortypelabel=(
-                    "N"   , "Nodes",
-                    "N2W" , "Node Pairs in a Way",
-                    "N2"  , "Node Pairs",
-                    "W"   , "Ways",
-                    "R"   , "Relations",
-                    "RN"  , "Relations/Nodes",
-                    "RW"  , "Relations/Ways",
-                    "E"   , "ERROR"
-                   );
-
-   $lasterrortype="";
-
-   foreach $error (sort { if    ( $errortypes{$b} ne $errortypes{$a} ) { return $errortypeorder{$errortypes{$a}} <=> $errortypeorder{$errortypes{$b}} }
-                          elsif ( $errors{$b}     == $errors{$a} )     { return $errorids{$a} cmp $errorids{$b} }
-                          else                                         { return $errors{$b}   <=> $errors{$a}   } } (keys %errors))
+   my %errortypeorder=(
+                       "N"   , 1,
+                       "NW"  , 2,
+                       "WN"  , 3,
+                       "N2W" , 4,
+                       "N2"  , 5,
+                       "W"   , 6,
+                       "R"   , 7,
+                       "RN"  , 8,
+                       "RW"  , 9,
+                       "E"   , 10
+                      );
+
+   my %errortypelabel=(
+                       "N"   , "Nodes",
+                       "NW"  , "Node in a Way",
+                       "WN"  , "Way contains Node",
+                       "N2W" , "Node Pairs in a Way",
+                       "N2"  , "Node Pairs",
+                       "W"   , "Ways",
+                       "R"   , "Relations",
+                       "RN"  , "Relations/Nodes",
+                       "RW"  , "Relations/Ways",
+                       "E"   , "ERROR"
+                      );
+
+   my $lasterrortype="";
+
+   foreach my $error (sort { if    ( $errortypes{$b} ne $errortypes{$a} ) { return $errortypeorder{$errortypes{$a}} <=> $errortypeorder{$errortypes{$b}} }
+                             elsif ( $errors{$b}     == $errors{$a} )     { return $errors{$a} cmp $errors{$b} }
+                             else                                         { return $errors{$b} <=> $errors{$a} } } (keys %errors))
      {
-      $errorhtml=$error;
+      my $errorhtml=$error;
 
       $errorhtml =~ s/&/&/g;
       $errorhtml =~ s/</</g;
@@ -203,11 +246,11 @@ else
         }
       else
         {
-         @ids=split(",",$errorids{$error});
+         my @ids=sort({ return $a <=> $b } @{$errorids{$error}});
 
-         $first=1;
+         my $first=1;
 
-         foreach $id (@ids)
+         foreach my $id (@ids)
            {
             if($first)
               {
@@ -224,9 +267,11 @@ else
             print "<a href=\"http://www.openstreetmap.org/browse/way/$id\">$id</a>" if($errortypes{$error} eq "W");
             print "<a href=\"http://www.openstreetmap.org/browse/relation/$id\">$id</a>" if($errortypes{$error} eq "R");
 
-            if($errortypes{$error} eq "N2" || $errortypes{$error} eq "RN" || $errortypes{$error} eq "RW")
+            if($errortypes{$error} eq "NW" || $errortypes{$error} eq "WN" || $errortypes{$error} eq "N2" || $errortypes{$error} eq "RN" || $errortypes{$error} eq "RW")
               {
                $id =~ m%\(([0-9]+) ([0-9]+)\)%;
+               print "(<a href=\"http://www.openstreetmap.org/browse/node/$1\">$1</a> <a href=\"http://www.openstreetmap.org/browse/way/$2\">$2</a>)" if($errortypes{$error} eq "NW");
+               print "(<a href=\"http://www.openstreetmap.org/browse/way/$1\">$1</a> <a href=\"http://www.openstreetmap.org/browse/node/$2\">$2</a>)" if($errortypes{$error} eq "WN");
                print "(<a href=\"http://www.openstreetmap.org/browse/node/$1\">$1</a> <a href=\"http://www.openstreetmap.org/browse/node/$2\">$2</a>)" if($errortypes{$error} eq "N2");
                print "(<a href=\"http://www.openstreetmap.org/browse/relation/$1\">$1</a> <a href=\"http://www.openstreetmap.org/browse/node/$2\">$2</a>)" if($errortypes{$error} eq "RN");
                print "(<a href=\"http://www.openstreetmap.org/browse/relation/$1\">$1</a> <a href=\"http://www.openstreetmap.org/browse/way/$2\">$2</a>)" if($errortypes{$error} eq "RW");
@@ -247,5 +292,4 @@ else
          "</BODY>\n".
          "\n".
          "</HTML>\n";
-
 }
diff --git a/extras/find-fixme/Makefile b/extras/find-fixme/Makefile
new file mode 100644
index 0000000..c3334ab
--- /dev/null
+++ b/extras/find-fixme/Makefile
@@ -0,0 +1,175 @@
+# find-fixme Makefile
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2013-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# All configuration is in the top-level Makefile.conf
+
+include ../../Makefile.conf
+
+# Web file paths
+
+WEBBINDIR=web/bin
+WEBDATADIR=web/data
+WEBWWWDIR=web/www
+
+# Compilation targets
+
+C=$(wildcard *.c)
+D=$(wildcard .deps/*.d)
+
+ROUTINO_SRC=../../src
+ROUTINO_WEBWWWDIR=../../web/www/routino
+ROUTINO_DOCDIR=../../doc/html
+
+EXE=fixme-finder fixme-finder-slim fixme-dumper fixme-dumper-slim
+DATA=fixme.xml
+WWW_COPY=page-elements.css page-elements.js maplayout.css mapprops.js maploader.js
+DOC_COPY=style.css
+
+########
+
+all: all-bin all-data all-www
+
+all-bin: $(EXE)
+	@[ -d $(WEBBINDIR) ] || mkdir -p $(WEBBINDIR)
+	@for file in $(EXE); do \
+	    if [ ! -f $(WEBBINDIR)/$$file ] || [ $$file -nt $(WEBBINDIR)/$$file ]; then \
+	       echo cp $$file $(WEBBINDIR) ;\
+	       cp -f $$file $(WEBBINDIR) ;\
+	    fi ;\
+	 done
+
+all-data:
+	@[ -d $(WEBDATADIR) ] || mkdir -p $(WEBDATADIR)
+	@for file in $(DATA); do \
+	    if [ ! -f $(WEBDATADIR)/$$file ] || [ $$file -nt $(WEBDATADIR)/$$file ]; then \
+	       echo cp $$file $(WEBDATADIR) ;\
+	       cp -f $$file $(WEBDATADIR) ;\
+	    fi ;\
+	 done
+
+all-www:
+	@for file in $(WWW_COPY); do \
+	    if [ ! -f $(WEBWWWDIR)/$$file ] || [ $(ROUTINO_WEBWWWDIR)/$$file -nt $(WEBWWWDIR)/$$file ]; then \
+	       echo cp $(ROUTINO_WEBWWWDIR)/$$file $(WEBWWWDIR) ;\
+	       cp -f $(ROUTINO_WEBWWWDIR)/$$file $(WEBWWWDIR) ;\
+	    fi ;\
+	 done
+	@for file in $(DOC_COPY); do \
+	    if [ ! -f $(WEBWWWDIR)/$$file ] || [ $(ROUTINO_DOCDIR)/$$file -nt $(WEBWWWDIR)/$$file ]; then \
+	       echo cp $(ROUTINO_DOCDIR)/$$file $(WEBWWWDIR) ;\
+	       cp -f $(ROUTINO_DOCDIR)/$$file $(WEBWWWDIR) ;\
+	    fi ;\
+	 done
+
+########
+
+FIXME_FINDER_OBJ=fixme-finder.o osmparser.o \
+	       	 $(ROUTINO_SRC)/nodesx.o $(ROUTINO_SRC)/segmentsx.o $(ROUTINO_SRC)/waysx.o $(ROUTINO_SRC)/relationsx.o \
+	       	 $(ROUTINO_SRC)/ways.o $(ROUTINO_SRC)/types.o \
+	       	 $(ROUTINO_SRC)/files.o $(ROUTINO_SRC)/logging.o $(ROUTINO_SRC)/logerror.o $(ROUTINO_SRC)/errorlogx.o \
+	       	 $(ROUTINO_SRC)/sorting.o \
+	       	 $(ROUTINO_SRC)/xmlparse.o $(ROUTINO_SRC)/tagging.o \
+	       	 $(ROUTINO_SRC)/uncompress.o $(ROUTINO_SRC)/osmxmlparse.o $(ROUTINO_SRC)/osmpbfparse.o $(ROUTINO_SRC)/osmo5mparse.o
+
+fixme-finder : $(FIXME_FINDER_OBJ)
+	$(LD) $(FIXME_FINDER_OBJ) -o $@ $(LDFLAGS)
+
+########
+
+FIXME_FINDER_SLIM_OBJ=fixme-finder-slim.o osmparser.o \
+	       	      $(ROUTINO_SRC)/nodesx-slim.o $(ROUTINO_SRC)/segmentsx-slim.o $(ROUTINO_SRC)/waysx-slim.o $(ROUTINO_SRC)/relationsx-slim.o \
+	              $(ROUTINO_SRC)/ways.o $(ROUTINO_SRC)/types.o \
+	       	      $(ROUTINO_SRC)/files.o $(ROUTINO_SRC)/logging.o $(ROUTINO_SRC)/logerror-slim.o $(ROUTINO_SRC)/errorlogx-slim.o \
+	              $(ROUTINO_SRC)/sorting.o \
+	       	      $(ROUTINO_SRC)/xmlparse.o $(ROUTINO_SRC)/tagging.o \
+	       	      $(ROUTINO_SRC)/uncompress.o $(ROUTINO_SRC)/osmxmlparse.o $(ROUTINO_SRC)/osmpbfparse.o $(ROUTINO_SRC)/osmo5mparse.o
+
+fixme-finder-slim : $(FIXME_FINDER_SLIM_OBJ)
+	$(LD) $(FIXME_FINDER_SLIM_OBJ) -o $@ $(LDFLAGS)
+
+########
+
+FIXME_DUMPER_OBJ=fixme-dumper.o \
+	         $(ROUTINO_SRC)/errorlog.o \
+	         $(ROUTINO_SRC)/files.o $(ROUTINO_SRC)/logging.o $(ROUTINO_SRC)/xmlparse.o
+
+fixme-dumper : $(FIXME_DUMPER_OBJ)
+	$(LD) $(FIXME_DUMPER_OBJ) -o $@ $(LDFLAGS)
+
+########
+
+FIXME_DUMPER_SLIM_OBJ=fixme-dumper-slim.o \
+	              $(ROUTINO_SRC)/errorlog-slim.o \
+	              $(ROUTINO_SRC)/files.o $(ROUTINO_SRC)/logging.o $(ROUTINO_SRC)/xmlparse.o
+
+fixme-dumper-slim : $(FIXME_DUMPER_SLIM_OBJ)
+	$(LD) $(FIXME_DUMPER_SLIM_OBJ) -o $@ $(LDFLAGS)
+
+########
+
+$(ROUTINO_SRC)/%.o :
+	cd $(ROUTINO_SRC) && $(MAKE) $(notdir $@)
+
+$(ROUTINO_SRC)/%-slim.o :
+	cd $(ROUTINO_SRC) && $(MAKE) $(notdir $@)
+
+%.o : %.c
+	@[ -d .deps ] || mkdir .deps
+	$(CC) -c $(CFLAGS) -DSLIM=0 -I$(ROUTINO_SRC) $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $@)))
+
+%-slim.o : %.c
+	@[ -d .deps ] || mkdir .deps
+	$(CC) -c $(CFLAGS) -DSLIM=1 -I$(ROUTINO_SRC) $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $@)))
+
+########
+
+test:
+
+########
+
+install:
+
+########
+
+clean:
+	rm -f *~
+	rm -f *.o
+	rm -f core
+
+########
+
+distclean: clean
+	-cd $(WEBBINDIR) && rm -f $(EXE)
+	-cd $(WEBDATADIR) && rm -f $(DATA)
+	-cd $(WEBWWWDIR) && rm -f $(WWW_COPY)
+	-cd $(WEBWWWDIR) && rm -f $(DOC_COPY)
+	-rm -f $(EXE)
+	-rm -f $(D)
+	-rm -fr .deps
+
+########
+
+include $(D)
+
+########
+
+.PHONY:: all test install clean distclean
+
+.PHONY:: all-bin all-data all-www
diff --git a/extras/find-fixme/README.txt b/extras/find-fixme/README.txt
new file mode 100644
index 0000000..7271182
--- /dev/null
+++ b/extras/find-fixme/README.txt
@@ -0,0 +1,96 @@
+                            Find and Display FIXME tags
+                            ===========================
+
+The "fixme" tag is often used in OSM data to mark an item whose details are not
+completely known - as a reminder or request for somebody to check it.  Since
+Routino can now generate a map of tagging problems that it finds it is easy to
+extend this to finding all "fixme" tags.  The files in this directory provide a
+complete set of executables and web pages for extracting and displaying all
+items with "fixme" tags on a map.
+
+Editing fixme.xml and changing the rules for selecting tags allows for creating
+custom databases to display items containing any desired tag(s).
+
+
+fixme-finder
+------------
+
+This program is a modified version of the Routino planetsplitter program and can
+be used on an OSM file to extract the fixme tags and generate a database of
+them.
+
+
+Usage: fixme-finder [--help]
+                    [--dir=<dirname>]
+                    [--sort-ram-size=<size>] [--sort-threads=<number>]
+                    [--tmpdir=<dirname>]
+                    [--tagging=<filename>]
+                    [--loggable] [--logtime]
+                    [<filename.osm> ...
+                     | <filename.pbf> ...
+                     | <filename.o5m> ...
+                     | <filename.(osm|o5m).bz2> ...
+                     | <filename.(osm|o5m).gz> ...
+                     | <filename.(osm|o5m).xz> ...]
+
+--help                    Prints this information.
+
+--dir=<dirname>           The directory containing the fixme database.
+
+--sort-ram-size=<size>    The amount of RAM (in MB) to use for data sorting
+                          (defaults to 256MB otherwise.)
+--sort-threads=<number>   The number of threads to use for data sorting.
+
+--tmpdir=<dirname>        The directory name for temporary files.
+                          (defaults to the '--dir' option directory.)
+
+--tagging=<filename>      The name of the XML file containing the tagging rules
+                          (defaults to 'fixme.xml' with '--dir' option)
+
+--loggable                Print progress messages suitable for logging to file.
+--logtime                 Print the elapsed time for each processing step.
+
+<filename.osm>, <filename.pbf>, <filename.o5m>
+                          The name(s) of the file(s) to read and parse.
+                          Filenames ending '.pbf' read as PBF, filenames ending
+                          '.o5m' read as O5M, others as XML.
+                          Filenames ending '.bz2' will be bzip2 uncompressed (if
+                          bzip2 support compiled in). Filenames ending '.gz'
+                          will be gzip uncompressed (if gzip  support compiled
+                          in). Filenames ending '.xz' will be xz uncompressed
+                          (if xz support compiled in).
+
+
+fixme-dumper
+------------
+
+This program is a modified version of the Routino filedumper program and is used
+by the web page CGI to display the information on a map.
+
+
+Usage: fixme-dumper [--help]
+                    [--dir=<dirname>]
+                    [--statistics]
+                    [--visualiser --latmin=<latmin> --latmax=<latmax>
+                                  --lonmin=<lonmin> --lonmax=<lonmax>
+                                  --data=<data-type>]
+                    [--dump--visualiser [--data=fixme<number>]]
+
+--help                    Prints this information.
+
+--dir=<dirname>           The directory containing the fixme database.
+
+--statistics              Print statistics about the fixme database.
+
+--visualiser              Extract selected data from the fixme database:
+  --latmin=<latmin>       * the minimum latitude (degrees N).
+  --latmax=<latmax>       * the maximum latitude (degrees N).
+  --lonmin=<lonmin>       * the minimum longitude (degrees E).
+  --lonmax=<lonmax>       * the maximum longitude (degrees E).
+  --data=<data-type>      * the type of data to select.
+
+  <data-type> can be selected from:
+      fixmes              = fixme tags extracted from the data.
+
+--dump-visualiser         Dump selected contents of the database in HTML.
+  --data=fixme<number>    * the fixme with the selected index.
diff --git a/extras/find-fixme/fixme-dumper.c b/extras/find-fixme/fixme-dumper.c
new file mode 100644
index 0000000..2be64d1
--- /dev/null
+++ b/extras/find-fixme/fixme-dumper.c
@@ -0,0 +1,346 @@
+/***************************************
+ Fixme file dumper.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+
+#include "types.h"
+#include "errorlog.h"
+
+#include "files.h"
+#include "xmlparse.h"
+
+
+/* Local functions */
+
+static void OutputErrorLog(ErrorLogs *errorlogs,double latmin,double latmax,double lonmin,double lonmax);
+
+static void print_errorlog_visualiser(ErrorLogs *errorlogs,index_t item);
+
+static char *RFC822Date(time_t t);
+
+static void print_usage(int detail,const char *argerr,const char *err);
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The main program for the fixme dumper.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int main(int argc,char** argv)
+{
+ ErrorLogs*OSMErrorLogs;
+ int       arg;
+ char     *dirname=NULL,*prefix=NULL;
+ char     *errorlogs_filename;
+ int       option_statistics=0;
+ int       option_visualiser=0,coordcount=0;
+ double    latmin=0,latmax=0,lonmin=0,lonmax=0;
+ char     *option_data=NULL;
+ int       option_dump_visualiser=0;
+
+ /* Parse the command line arguments */
+
+ for(arg=1;arg<argc;arg++)
+   {
+    if(!strcmp(argv[arg],"--help"))
+       print_usage(1,NULL,NULL);
+    else if(!strncmp(argv[arg],"--dir=",6))
+       dirname=&argv[arg][6];
+    else if(!strcmp(argv[arg],"--statistics"))
+       option_statistics=1;
+    else if(!strcmp(argv[arg],"--visualiser"))
+       option_visualiser=1;
+    else if(!strcmp(argv[arg],"--dump-visualiser"))
+       option_dump_visualiser=1;
+    else if(!strncmp(argv[arg],"--latmin",8) && argv[arg][8]=='=')
+      {latmin=degrees_to_radians(atof(&argv[arg][9]));coordcount++;}
+    else if(!strncmp(argv[arg],"--latmax",8) && argv[arg][8]=='=')
+      {latmax=degrees_to_radians(atof(&argv[arg][9]));coordcount++;}
+    else if(!strncmp(argv[arg],"--lonmin",8) && argv[arg][8]=='=')
+      {lonmin=degrees_to_radians(atof(&argv[arg][9]));coordcount++;}
+    else if(!strncmp(argv[arg],"--lonmax",8) && argv[arg][8]=='=')
+      {lonmax=degrees_to_radians(atof(&argv[arg][9]));coordcount++;}
+    else if(!strncmp(argv[arg],"--data",6) && argv[arg][6]=='=')
+       option_data=&argv[arg][7];
+    else if(!strncmp(argv[arg],"--fixme=",8))
+       ;
+    else
+       print_usage(0,argv[arg],NULL);
+   }
+
+ if((option_statistics + option_visualiser + option_dump_visualiser)!=1)
+    print_usage(0,NULL,"Must choose --visualiser, --statistics or --dump-visualiser.");
+
+ /* Load in the data - Note: No error checking because Load*List() will call exit() in case of an error. */
+
+ OSMErrorLogs=LoadErrorLogs(errorlogs_filename=FileName(dirname,prefix,"fixme.mem"));
+
+ /* Write out the visualiser data */
+
+ if(option_visualiser)
+   {
+    if(coordcount!=4)
+       print_usage(0,NULL,"The --visualiser option must have --latmin, --latmax, --lonmin, --lonmax.\n");
+
+    if(!option_data)
+       print_usage(0,NULL,"The --visualiser option must have --data.\n");
+
+    if(!strcmp(option_data,"fixmes"))
+       OutputErrorLog(OSMErrorLogs,latmin,latmax,lonmin,lonmax);
+    else
+       print_usage(0,option_data,NULL);
+   }
+
+ /* Print out statistics */
+
+ if(option_statistics)
+   {
+    struct stat buf;
+
+    /* Examine the files */
+
+    printf("Files\n");
+    printf("-----\n");
+    printf("\n");
+
+    stat(errorlogs_filename,&buf);
+
+    printf("'%s%sfixme.mem' - %9"PRIu64" Bytes\n",prefix?prefix:"",prefix?"-":"",(uint64_t)buf.st_size);
+    printf("%s\n",RFC822Date(buf.st_mtime));
+    printf("\n");
+
+    printf("\n");
+    printf("Error Logs\n");
+    printf("----------\n");
+    printf("\n");
+
+    printf("Number(total)           =%9"Pindex_t"\n",OSMErrorLogs->file.number);
+    printf("Number(geographical)    =%9"Pindex_t"\n",OSMErrorLogs->file.number_geo);
+    printf("Number(non-geographical)=%9"Pindex_t"\n",OSMErrorLogs->file.number_nongeo);
+
+    printf("\n");
+    stat(errorlogs_filename,&buf);
+#if !SLIM
+    printf("Total strings=%9lu Bytes\n",(unsigned long)buf.st_size-(unsigned long)(OSMErrorLogs->strings-(char*)OSMErrorLogs->data));
+#else
+    printf("Total strings=%9lu Bytes\n",(unsigned long)buf.st_size-(unsigned long)OSMErrorLogs->stringsoffset);
+#endif
+   }
+
+ /* Print out internal data (in HTML format for the visualiser) */
+
+ if(option_dump_visualiser)
+   {
+    index_t item;
+
+    if(!option_data)
+       print_usage(0,NULL,"The --dump-visualiser option must have --data.\n");
+
+    for(arg=1;arg<argc;arg++)
+       if(!strncmp(argv[arg],"--data=fixme",12))
+         {
+          item=atoi(&argv[arg][12]);
+
+          if(item<OSMErrorLogs->file.number)
+             print_errorlog_visualiser(OSMErrorLogs,item);
+          else
+             printf("Invalid fixme number; minimum=0, maximum=%"Pindex_t".\n",OSMErrorLogs->file.number-1);
+         }
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Output the data for error logs within the region.
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  double latmin The minimum latitude.
+
+  double latmax The maximum latitude.
+
+  double lonmin The minimum longitude.
+
+  double lonmax The maximum longitude.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void OutputErrorLog(ErrorLogs *errorlogs,double latmin,double latmax,double lonmin,double lonmax)
+{
+ ll_bin_t latminbin=latlong_to_bin(radians_to_latlong(latmin))-errorlogs->file.latzero;
+ ll_bin_t latmaxbin=latlong_to_bin(radians_to_latlong(latmax))-errorlogs->file.latzero;
+ ll_bin_t lonminbin=latlong_to_bin(radians_to_latlong(lonmin))-errorlogs->file.lonzero;
+ ll_bin_t lonmaxbin=latlong_to_bin(radians_to_latlong(lonmax))-errorlogs->file.lonzero;
+ ll_bin_t latb,lonb;
+ index_t i,index1,index2;
+
+ /* Loop through all of the error logs. */
+
+ for(latb=latminbin;latb<=latmaxbin;latb++)
+    for(lonb=lonminbin;lonb<=lonmaxbin;lonb++)
+      {
+       ll_bin2_t llbin=lonb*errorlogs->file.latbins+latb;
+
+       if(llbin<0 || llbin>(errorlogs->file.latbins*errorlogs->file.lonbins))
+          continue;
+
+       index1=LookupErrorLogOffset(errorlogs,llbin);
+       index2=LookupErrorLogOffset(errorlogs,llbin+1);
+
+       if(index2>errorlogs->file.number_geo)
+          index2=errorlogs->file.number_geo;
+
+       for(i=index1;i<index2;i++)
+         {
+          ErrorLog *errorlogp=LookupErrorLog(errorlogs,i,1);
+
+          double lat=latlong_to_radians(bin_to_latlong(errorlogs->file.latzero+latb)+off_to_latlong(errorlogp->latoffset));
+          double lon=latlong_to_radians(bin_to_latlong(errorlogs->file.lonzero+lonb)+off_to_latlong(errorlogp->lonoffset));
+
+          if(lat>latmin && lat<latmax && lon>lonmin && lon<lonmax)
+             printf("fixme%"Pindex_t" %.6f %.6f\n",i,radians_to_degrees(lat),radians_to_degrees(lon));
+         }
+      }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Print out an error log entry from the database (in visualiser format).
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  index_t item The error log index to print.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_errorlog_visualiser(ErrorLogs *errorlogs,index_t item)
+{
+ char *string=LookupErrorLogString(errorlogs,item);
+
+ printf("%s\n",ParseXML_Encode_Safe_XML(string));
+}
+
+
+/*+ Conversion from time_t to date string (day of week). +*/
+static const char* const weekdays[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+
+/*+ Conversion from time_t to date string (month of year). +*/
+static const char* const months[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Convert the time into an RFC 822 compliant date.
+
+  char *RFC822Date Returns a pointer to a fixed string containing the date.
+
+  time_t t The time.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static char *RFC822Date(time_t t)
+{
+ static char value[32];
+ char weekday[4];
+ char month[4];
+ struct tm *tim;
+
+ tim=gmtime(&t);
+
+ strcpy(weekday,weekdays[tim->tm_wday]);
+ strcpy(month,months[tim->tm_mon]);
+
+ /* Sun, 06 Nov 1994 08:49:37 GMT    ; RFC 822, updated by RFC 1123 */
+
+ sprintf(value,"%3s, %02d %3s %4d %02d:%02d:%02d %s",
+         weekday,
+         tim->tm_mday,
+         month,
+         tim->tm_year+1900,
+         tim->tm_hour,
+         tim->tm_min,
+         tim->tm_sec,
+         "GMT"
+         );
+
+ return(value);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Print out the usage information.
+
+  int detail The level of detail to use - 0 = low, 1 = high.
+
+  const char *argerr The argument that gave the error (if there is one).
+
+  const char *err Other error message (if there is one).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_usage(int detail,const char *argerr,const char *err)
+{
+ fprintf(stderr,
+         "Usage: fixme-dumper [--help]\n"
+         "                    [--dir=<dirname>]\n"
+         "                    [--statistics]\n"
+         "                    [--visualiser --latmin=<latmin> --latmax=<latmax>\n"
+         "                                  --lonmin=<lonmin> --lonmax=<lonmax>\n"
+         "                                  --data=<data-type>]\n"
+         "                    [--dump--visualiser [--data=fixme<number>]]\n");
+
+ if(argerr)
+    fprintf(stderr,
+            "\n"
+            "Error with command line parameter: %s\n",argerr);
+
+ if(err)
+    fprintf(stderr,
+            "\n"
+            "Error: %s\n",err);
+
+ if(detail)
+    fprintf(stderr,
+            "\n"
+            "--help                    Prints this information.\n"
+            "\n"
+            "--dir=<dirname>           The directory containing the fixme database.\n"
+            "\n"
+            "--statistics              Print statistics about the fixme database.\n"
+            "\n"
+            "--visualiser              Extract selected data from the fixme database:\n"
+            "  --latmin=<latmin>       * the minimum latitude (degrees N).\n"
+            "  --latmax=<latmax>       * the maximum latitude (degrees N).\n"
+            "  --lonmin=<lonmin>       * the minimum longitude (degrees E).\n"
+            "  --lonmax=<lonmax>       * the maximum longitude (degrees E).\n"
+            "  --data=<data-type>      * the type of data to select.\n"
+            "\n"
+            "  <data-type> can be selected from:\n"
+            "      fixmes              = fixme tags extracted from the data.\n"
+            "\n"
+            "--dump-visualiser         Dump selected contents of the database in HTML.\n"
+            "  --data=fixme<number>    * the fixme with the selected index.\n");
+
+ exit(!detail);
+}
diff --git a/extras/find-fixme/fixme-finder.c b/extras/find-fixme/fixme-finder.c
new file mode 100644
index 0000000..f1427f5
--- /dev/null
+++ b/extras/find-fixme/fixme-finder.c
@@ -0,0 +1,365 @@
+/***************************************
+ OSM planet file fixme finder.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "types.h"
+#include "ways.h"
+
+#include "typesx.h"
+#include "nodesx.h"
+#include "waysx.h"
+#include "relationsx.h"
+
+#include "files.h"
+#include "logging.h"
+#include "errorlogx.h"
+#include "functions.h"
+#include "osmparser.h"
+#include "tagging.h"
+#include "uncompress.h"
+
+
+/* Global variables */
+
+/*+ The name of the temporary directory. +*/
+char *option_tmpdirname=NULL;
+
+/*+ The amount of RAM to use for filesorting. +*/
+size_t option_filesort_ramsize=0;
+
+/*+ The number of threads to use for filesorting. +*/
+int option_filesort_threads=1;
+
+
+/* Local functions */
+
+static void print_usage(int detail,const char *argerr,const char *err);
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The main program for the find-fixme.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int main(int argc,char** argv)
+{
+ NodesX     *OSMNodes;
+ WaysX      *OSMWays;
+ RelationsX *OSMRelations;
+ ErrorLogsX *OSMErrorLogs;
+ char       *dirname=NULL,*prefix=NULL,*tagging="fixme.xml",*errorlog="fixme.log";
+ int         option_keep=1;
+ int         option_filenames=0;
+ int         arg;
+
+ printf_program_start();
+
+ /* Parse the command line arguments */
+
+ for(arg=1;arg<argc;arg++)
+   {
+    if(!strcmp(argv[arg],"--help"))
+       print_usage(1,NULL,NULL);
+    else if(!strncmp(argv[arg],"--dir=",6))
+       dirname=&argv[arg][6];
+    else if(!strncmp(argv[arg],"--sort-ram-size=",16))
+       option_filesort_ramsize=atoi(&argv[arg][16]);
+#if defined(USE_PTHREADS) && USE_PTHREADS
+    else if(!strncmp(argv[arg],"--sort-threads=",15))
+       option_filesort_threads=atoi(&argv[arg][15]);
+#endif
+    else if(!strncmp(argv[arg],"--tmpdir=",9))
+       option_tmpdirname=&argv[arg][9];
+    else if(!strncmp(argv[arg],"--tagging=",10))
+       tagging=&argv[arg][10];
+    else if(!strcmp(argv[arg],"--loggable"))
+       option_loggable=1;
+    else if(!strcmp(argv[arg],"--logtime"))
+       option_logtime=1;
+    else if(argv[arg][0]=='-' && argv[arg][1]=='-')
+       print_usage(0,argv[arg],NULL);
+    else
+       option_filenames++;
+   }
+
+ /* Check the specified command line options */
+
+ if(!option_filesort_ramsize)
+   {
+#if SLIM
+    option_filesort_ramsize=64*1024*1024;
+#else
+    option_filesort_ramsize=256*1024*1024;
+#endif
+   }
+ else
+    option_filesort_ramsize*=1024*1024;
+
+ if(!option_tmpdirname)
+   {
+    if(!dirname)
+       option_tmpdirname=".";
+    else
+       option_tmpdirname=dirname;
+   }
+
+ if(tagging)
+   {
+    if(!ExistsFile(tagging))
+      {
+       fprintf(stderr,"Error: The '--tagging' option specifies a file that does not exist.\n");
+       exit(EXIT_FAILURE);
+      }
+   }
+ else
+   {
+    if(ExistsFile(FileName(dirname,prefix,"tagging.xml")))
+       tagging=FileName(dirname,prefix,"tagging.xml");
+    else
+      {
+       fprintf(stderr,"Error: The '--tagging' option was not used and the default 'tagging.xml' does not exist.\n");
+       exit(EXIT_FAILURE);
+      }
+   }
+
+ if(ParseXMLTaggingRules(tagging))
+   {
+    fprintf(stderr,"Error: Cannot read the tagging rules in the file '%s'.\n",tagging);
+    exit(EXIT_FAILURE);
+   }
+
+ /* Create new node, segment, way and relation variables */
+
+ OSMNodes=NewNodeList(0,0);
+
+ OSMWays=NewWayList(0,0);
+
+ OSMRelations=NewRelationList(0,0);
+
+ /* Create the error log file */
+
+ if(errorlog)
+    open_errorlog(FileName(dirname,prefix,errorlog),0,option_keep);
+
+ /* Parse the file */
+
+ for(arg=1;arg<argc;arg++)
+   {
+    int fd;
+    char *filename,*p;
+
+    if(argv[arg][0]=='-' && argv[arg][1]=='-')
+       continue;
+
+    filename=strcpy(malloc(strlen(argv[arg])+1),argv[arg]);
+
+    fd=OpenFile(filename);
+
+    if((p=strstr(filename,".bz2")) && !strcmp(p,".bz2"))
+      {
+       fd=Uncompress_Bzip2(fd);
+       *p=0;
+      }
+
+    if((p=strstr(filename,".gz")) && !strcmp(p,".gz"))
+      {
+       fd=Uncompress_Gzip(fd);
+       *p=0;
+      }
+
+    if((p=strstr(filename,".xz")) && !strcmp(p,".xz"))
+      {
+       fd=Uncompress_Xz(fd);
+       *p=0;
+      }
+
+    printf("\nParse OSM Data [%s]\n==============\n\n",filename);
+    fflush(stdout);
+
+    if((p=strstr(filename,".pbf")) && !strcmp(p,".pbf"))
+      {
+       if(ParsePBFFile(fd,OSMNodes,OSMWays,OSMRelations))
+          exit(EXIT_FAILURE);
+      }
+    else if((p=strstr(filename,".o5m")) && !strcmp(p,".o5m"))
+      {
+       if(ParseO5MFile(fd,OSMNodes,OSMWays,OSMRelations))
+          exit(EXIT_FAILURE);
+      }
+    else
+      {
+       if(ParseOSMFile(fd,OSMNodes,OSMWays,OSMRelations))
+          exit(EXIT_FAILURE);
+      }
+
+    CloseFile(fd);
+
+    free(filename);
+   }
+
+ DeleteXMLTaggingRules();
+
+ FinishNodeList(OSMNodes);
+ FinishWayList(OSMWays);
+ FinishRelationList(OSMRelations);
+
+ /* Sort the data */
+
+ printf("\nSort OSM Data\n=============\n\n");
+ fflush(stdout);
+
+ /* Sort the nodes, ways and relations */
+
+ SortNodeList(OSMNodes);
+
+ SortWayList(OSMWays);
+
+ SortRelationList(OSMRelations);
+
+ /* Process the data */
+
+ RenameFile(OSMNodes->filename_tmp,OSMNodes->filename);
+ RenameFile(OSMWays->filename_tmp,OSMWays->filename);
+ RenameFile(OSMRelations->rrfilename_tmp,OSMRelations->rrfilename);
+ RenameFile(OSMRelations->trfilename_tmp,OSMRelations->trfilename);
+
+ close_errorlog();
+
+ printf("\nCreate Error Log\n================\n\n");
+ fflush(stdout);
+
+ OSMErrorLogs=NewErrorLogList();
+
+ ProcessErrorLogs(OSMErrorLogs,OSMNodes,OSMWays,OSMRelations);
+
+ SortErrorLogsGeographically(OSMErrorLogs);
+
+ SaveErrorLogs(OSMErrorLogs,FileName(dirname,prefix,"fixme.mem"));
+
+ FreeErrorLogList(OSMErrorLogs);
+
+ /* Free the memory (delete the temporary files) */
+
+ FreeNodeList(OSMNodes,0);
+ FreeWayList(OSMWays,0);
+ FreeRelationList(OSMRelations,0);
+
+ printf_program_end();
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Print out the usage information.
+
+  int detail The level of detail to use - 0 = low, 1 = high.
+
+  const char *argerr The argument that gave the error (if there is one).
+
+  const char *err Other error message (if there is one).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_usage(int detail,const char *argerr,const char *err)
+{
+ fprintf(stderr,
+         "Usage: fixme-finder [--help]\n"
+         "                    [--dir=<dirname>]\n"
+#if defined(USE_PTHREADS) && USE_PTHREADS
+         "                    [--sort-ram-size=<size>] [--sort-threads=<number>]\n"
+#else
+         "                    [--sort-ram-size=<size>]\n"
+#endif
+         "                    [--tmpdir=<dirname>]\n"
+         "                    [--tagging=<filename>]\n"
+         "                    [--loggable] [--logtime]\n"
+         "                    [<filename.osm> ...\n"
+         "                     | <filename.pbf> ...\n"
+         "                     | <filename.o5m> ..."
+#if defined(USE_BZIP2) && USE_BZIP2
+         "\n                     | <filename.(osm|o5m).bz2> ..."
+#endif
+#if defined(USE_GZIP) && USE_GZIP
+         "\n                     | <filename.(osm|o5m).gz> ..."
+#endif
+#if defined(USE_XZ) && USE_XZ
+         "\n                     | <filename.(osm|o5m).xz> ..."
+#endif
+         "]\n");
+
+ if(argerr)
+    fprintf(stderr,
+            "\n"
+            "Error with command line parameter: %s\n",argerr);
+
+ if(err)
+    fprintf(stderr,
+            "\n"
+            "Error: %s\n",err);
+
+ if(detail)
+    fprintf(stderr,
+            "\n"
+            "--help                    Prints this information.\n"
+            "\n"
+            "--dir=<dirname>           The directory containing the fixme database.\n"
+            "\n"
+            "--sort-ram-size=<size>    The amount of RAM (in MB) to use for data sorting\n"
+#if SLIM
+            "                          (defaults to 64MB otherwise.)\n"
+#else
+            "                          (defaults to 256MB otherwise.)\n"
+#endif
+#if defined(USE_PTHREADS) && USE_PTHREADS
+            "--sort-threads=<number>   The number of threads to use for data sorting.\n"
+#endif
+            "\n"
+            "--tmpdir=<dirname>        The directory name for temporary files.\n"
+            "                          (defaults to the '--dir' option directory.)\n"
+            "\n"
+            "--tagging=<filename>      The name of the XML file containing the tagging rules\n"
+            "                          (defaults to 'fixme.xml' with '--dir' option)\n"
+            "\n"
+            "--loggable                Print progress messages suitable for logging to file.\n"
+            "--logtime                 Print the elapsed time for each processing step.\n"
+            "\n"
+            "<filename.osm>, <filename.pbf>, <filename.o5m>\n"
+            "                          The name(s) of the file(s) to read and parse.\n"
+            "                          Filenames ending '.pbf' read as PBF, filenames ending\n"
+            "                          '.o5m' read as O5M, others as XML.\n"
+#if defined(USE_BZIP2) && USE_BZIP2
+            "                          Filenames ending '.bz2' will be bzip2 uncompressed.\n"
+#endif
+#if defined(USE_GZIP) && USE_GZIP
+            "                          Filenames ending '.gz' will be gzip uncompressed.\n"
+#endif
+#if defined(USE_XZ) && USE_XZ
+            "                          Filenames ending '.xz' will be xz uncompressed.\n"
+#endif
+            );
+
+ exit(!detail);
+}
diff --git a/extras/find-fixme/fixme.xml b/extras/find-fixme/fixme.xml
new file mode 100644
index 0000000..ba45253
--- /dev/null
+++ b/extras/find-fixme/fixme.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!-- ============================================================
+     An XML format file containing Routino tagging rules - copy the input file
+     directly to the output with no modifications.
+
+     Part of the Routino routing software.
+     ============================================================
+     This file Copyright 2010-2013 Andrew M. Bishop
+
+     This program is free software: you can redistribute it and/or modify
+     it under the terms of the GNU Affero General Public License as published by
+     the Free Software Foundation, either version 3 of the License, or
+     (at your option) any later version.
+     ============================================================ -->
+
+<routino-tagging xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                 xsi:noNamespaceSchemaLocation="http://www.routino.org/xml/routino-tagging.xsd">
+
+  <!-- - - - - - - - - - - Node rules - - - - - - - - - - -->
+
+  <node>
+
+    <!-- Add a marker for those nodes that match the wanted tag(s) -->
+
+    <if k="fixme">
+      <output k="fixme-finder:keep" v="yes" />
+    </if>
+
+    <!-- Copy all tags from input to output -->
+
+    <if>
+      <output />
+    </if>
+
+  </node>
+
+  <!-- - - - - - - - - - - Way rules - - - - - - - - - - -->
+
+  <way>
+
+    <!-- Add a marker for those ways that match the wanted tag(s) -->
+
+    <if k="fixme">
+      <output k="fixme-finder:keep" v="yes" />
+    </if>
+
+    <!-- Copy all tags from input to output -->
+
+    <if>
+      <output />
+    </if>
+
+  </way>
+
+  <!-- - - - - - - - - - - Relation rules - - - - - - - - - - -->
+
+  <relation>
+
+    <!-- Add a marker for those relations that match the wanted tag(s) -->
+
+    <if k="fixme">
+      <output k="fixme-finder:keep" v="yes" />
+    </if>
+
+    <!-- Copy all tags from input to output -->
+
+    <if>
+      <output />
+    </if>
+
+  </relation>
+
+</routino-tagging>
diff --git a/extras/find-fixme/osmparser.c b/extras/find-fixme/osmparser.c
new file mode 100644
index 0000000..60af70b
--- /dev/null
+++ b/extras/find-fixme/osmparser.c
@@ -0,0 +1,314 @@
+/***************************************
+ OSM file parser (either JOSM or planet)
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "types.h"
+#include "typesx.h"
+
+#include "nodesx.h"
+#include "waysx.h"
+#include "relationsx.h"
+
+#include "osmparser.h"
+#include "tagging.h"
+#include "logging.h"
+
+
+/* Macros */
+
+/*+ Checks if a value in the XML is one of the allowed values for true. +*/
+#define ISTRUE(xx)  (!strcmp(xx,"true") || !strcmp(xx,"yes") || !strcmp(xx,"1"))
+
+/*+ Checks if a value in the XML is one of the allowed values for false. +*/
+#define ISFALSE(xx) (!strcmp(xx,"false") || !strcmp(xx,"no") || !strcmp(xx,"0"))
+
+/* Local variables */
+
+static NodesX     *nodes;
+static WaysX      *ways;
+static RelationsX *relations;
+
+static node_t *way_nodes=NULL;
+static int     way_nnodes=0;
+
+static node_t     *relation_nodes=NULL;
+static int         relation_nnodes=0;
+static way_t      *relation_ways=NULL;
+static int         relation_nways=0;
+static relation_t *relation_relations=NULL;
+static int         relation_nrelations=0;
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Initialise the OSM parser by initialising the local variables.
+
+  NodesX *OSMNodes The data structure of nodes to fill in.
+
+  WaysX *OSMWays The data structure of ways to fill in.
+
+  RelationsX *OSMRelations The data structure of relations to fill in.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void InitialiseParser(NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations)
+{
+ /* Copy the function parameters and initialise the variables */
+
+ nodes=OSMNodes;
+ ways=OSMWays;
+ relations=OSMRelations;
+
+ way_nodes=(node_t*)malloc(256*sizeof(node_t));
+
+ relation_nodes    =(node_t    *)malloc(256*sizeof(node_t));
+ relation_ways     =(way_t     *)malloc(256*sizeof(way_t));
+ relation_relations=(relation_t*)malloc(256*sizeof(relation_t));
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Clean up the memory after parsing.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void CleanupParser(void)
+{
+ /* Free the variables */
+
+ free(way_nodes);
+
+ free(relation_nodes);
+ free(relation_ways);
+ free(relation_relations);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Add node references to a way.
+
+  int64_t node_id The node ID to add or zero to clear the list.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void AddWayRefs(int64_t node_id)
+{
+ if(node_id==0)
+    way_nnodes=0;
+ else
+   {
+    node_t id;
+
+    if(way_nnodes && (way_nnodes%256)==0)
+       way_nodes=(node_t*)realloc((void*)way_nodes,(way_nnodes+256)*sizeof(node_t));
+
+    id=(node_t)node_id;
+    logassert((int64_t)id==node_id,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
+
+    way_nodes[way_nnodes++]=id;
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Add node, way or relation references to a relation.
+
+  int64_t node_id The node ID to add or zero if it is not a node.
+
+  int64_t way_id The way ID to add or zero if it is not a way.
+
+  int64_t relation_id The relation ID to add or zero if it is not a relation.
+
+  const char *role The role played by this referenced item or NULL.
+
+  If all of node_id, way_id and relation_id are zero then the list is cleared.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void AddRelationRefs(int64_t node_id,int64_t way_id,int64_t relation_id,const char *role)
+{
+ if(node_id==0 && way_id==0 && relation_id==0)
+   {
+    relation_nnodes=0;
+    relation_nways=0;
+    relation_nrelations=0;
+   }
+ else if(node_id!=0)
+   {
+    node_t id;
+
+    id=(node_t)node_id;
+    logassert((int64_t)id==node_id,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
+
+    if(relation_nnodes && (relation_nnodes%256)==0)
+       relation_nodes=(node_t*)realloc((void*)relation_nodes,(relation_nnodes+256)*sizeof(node_t));
+
+    relation_nodes[relation_nnodes++]=id;
+   }
+ else if(way_id!=0)
+   {
+    way_t id;
+
+    id=(way_t)way_id;
+    logassert((int64_t)id==way_id,"Way ID too large (change way_t to 64-bits?)"); /* check way id can be stored in way_t data type. */
+
+    if(relation_nways && (relation_nways%256)==0)
+       relation_ways=(way_t*)realloc((void*)relation_ways,(relation_nways+256)*sizeof(way_t));
+
+    relation_ways[relation_nways++]=id;
+   }
+ else /* if(relation_id!=0) */
+   {
+    relation_t id;
+
+    id=(relation_t)relation_id;
+    logassert((int64_t)id==relation_id,"Relation ID too large (change relation_t to 64-bits?)"); /* check relation id can be stored in relation_t data type. */
+
+    if(relation_nrelations && (relation_nrelations%256)==0)
+       relation_relations=(relation_t*)realloc((void*)relation_relations,(relation_nrelations+256)*sizeof(relation_t));
+
+    relation_relations[relation_nrelations++]=relation_id;
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process the tags associated with a node.
+
+  TagList *tags The list of node tags.
+
+  int64_t node_id The id of the node.
+
+  double latitude The latitude of the node.
+
+  double longitude The longitude of the node.
+
+  int mode The mode of operation to take (create, modify, delete).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ProcessNodeTags(TagList *tags,int64_t node_id,double latitude,double longitude,int mode)
+{
+ node_t id;
+ int i;
+
+ /* Convert id */
+
+ id=(node_t)node_id;
+ logassert((int64_t)id==node_id,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
+
+ /* Parse the tags */
+
+ for(i=0;i<tags->ntags;i++)
+   {
+    char *k=tags->k[i];
+
+    if(!strcmp(k,"fixme-finder:keep"))
+      {
+       DeleteTag(tags,"fixme-finder:keep");
+       logerror("<node id='%"Pnode_t"'>%s</node>\n",logerror_node(id),StringifyTag(tags));
+      }
+   }
+
+ /* Store the node */
+
+ AppendNodeList(nodes,id,degrees_to_radians(latitude),degrees_to_radians(longitude),0,0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process the tags associated with a way.
+
+  TagList *tags The list of way tags.
+
+  int64_t way_id The id of the way.
+
+  int mode The mode of operation to take (create, modify, delete).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ProcessWayTags(TagList *tags,int64_t way_id,int mode)
+{
+ Way way={0};
+ way_t id;
+ int i;
+
+ /* Convert id */
+
+ id=(way_t)way_id;
+ logassert((int64_t)id==way_id,"Way ID too large (change way_t to 64-bits?)"); /* check way id can be stored in way_t data type. */
+
+ /* Parse the tags */
+
+ for(i=0;i<tags->ntags;i++)
+   {
+    char *k=tags->k[i];
+
+    if(!strcmp(k,"fixme-finder:keep"))
+      {
+       DeleteTag(tags,"fixme-finder:keep");
+       logerror("<way id='%"Pway_t"'>%s</way>\n",logerror_way(id),StringifyTag(tags));
+      }
+   }
+
+ /* Store the way */
+
+ AppendWayList(ways,id,&way,way_nodes,way_nnodes,"");
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process the tags associated with a relation.
+
+  TagList *tags The list of relation tags.
+
+  int64_t relation_id The id of the relation.
+
+  int mode The mode of operation to take (create, modify, delete).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ProcessRelationTags(TagList *tags,int64_t relation_id,int mode)
+{
+ relation_t id;
+ int i;
+
+ /* Convert id */
+
+ id=(relation_t)relation_id;
+ logassert((int64_t)id==relation_id,"Relation ID too large (change relation_t to 64-bits?)"); /* check relation id can be stored in relation_t data type. */
+
+ /* Parse the tags */
+
+ for(i=0;i<tags->ntags;i++)
+   {
+    char *k=tags->k[i];
+
+    if(!strcmp(k,"fixme-finder:keep"))
+      {
+       DeleteTag(tags,"fixme-finder:keep");
+       logerror("<relation id='%"Prelation_t"'>%s</relation>\n",logerror_relation(id),StringifyTag(tags));
+      }
+   }
+
+ /* Store the relation */
+
+ AppendRouteRelationList(relations,id,0,
+                         relation_nodes,relation_nnodes,
+                         relation_ways,relation_nways,
+                         relation_relations,relation_nrelations);
+}
diff --git a/extras/find-fixme/web/www/fixme.cgi b/extras/find-fixme/web/www/fixme.cgi
new file mode 100755
index 0000000..19422cd
--- /dev/null
+++ b/extras/find-fixme/web/www/fixme.cgi
@@ -0,0 +1,147 @@
+#!/usr/bin/perl
+#
+# Routino data visualiser CGI
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2008-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+# Use the directory paths script
+require "paths.pl";
+
+# Use the perl CGI module
+use CGI ':cgi';
+
+
+# Create the query and get the parameters
+
+my $query=new CGI;
+
+my @rawparams=$query->param;
+
+# Legal CGI parameters with regexp validity check
+
+my %legalparams=(
+                 "latmin"     => "[-0-9.]+",
+                 "latmax"     => "[-0-9.]+",
+                 "lonmin"     => "[-0-9.]+",
+                 "lonmax"     => "[-0-9.]+",
+                 "data"       => "fixmes",
+                 "dump"       => "fixme[0-9]+",
+                 "statistics" => "yes"
+                );
+
+# Validate the CGI parameters, ignore invalid ones
+
+my %cgiparams=();
+
+foreach my $key (@rawparams)
+  {
+   foreach my $test (keys (%legalparams))
+     {
+      if($key =~ m%^$test$%)
+        {
+         my $value=$query->param($key);
+
+         if($value =~ m%^$legalparams{$test}$%)
+           {
+            $cgiparams{$key}=$value;
+            last;
+           }
+        }
+     }
+  }
+
+# Data, dump or statistics?
+
+my $params="";
+
+my $data      =$cgiparams{"data"};
+my $dump      =$cgiparams{"dump"};
+my $statistics=$cgiparams{"statistics"};
+
+if(!defined $data && !defined $dump && !defined $statistics)
+  {
+   print header(-status => '500 Invalid CGI parameters');
+   exit;
+  }
+
+if(defined $statistics)
+  {
+   # Print the output
+
+   print header('text/plain');
+
+   # Set the parameters
+
+   $params.=" --statistics";
+  }
+elsif(defined $data)
+  {
+   # Parameters to limit range selected
+
+   my $limits=0.5;
+
+   # Check the parameters
+
+   my $latmin=$cgiparams{"latmin"};
+   my $latmax=$cgiparams{"latmax"};
+   my $lonmin=$cgiparams{"lonmin"};
+   my $lonmax=$cgiparams{"lonmax"};
+
+   if($latmin eq "" || $latmax eq "" || $lonmin eq "" || $lonmax eq "" || $data eq "")
+     {
+      print header(-status => '500 Invalid CGI parameters');
+      exit;
+     }
+
+   if(($latmax-$latmin)>$limits || ($lonmax-$lonmin)>$limits)
+     {
+      print header(-status => '500 Selected area too large');
+      exit;
+     }
+
+   # Print the output
+
+   print header('text/plain');
+
+   print "$latmin $lonmin $latmax $lonmax\n";
+
+   # Set the parameters
+
+   $params.=" --visualiser --data=$data";
+   $params.=" --latmin=$latmin --latmax=$latmax --lonmin=$lonmin --lonmax=$lonmax";
+  }
+else
+  {
+   # Print the output
+
+   print header('text/plain');
+
+   # Set the parameters
+
+   $params.=" --dump-visualiser --data=$dump";
+  }
+
+# Run the filedumper
+
+$params.=" --dir=$main::data_dir" if($main::data_dir);
+$params.=" --prefix=$main::data_prefix" if($main::data_prefix);
+
+system "$main::bin_dir/$main::fixme_dumper_exe $params 2>&1";
diff --git a/web/www/routino/visualiser.css b/extras/find-fixme/web/www/fixme.css
similarity index 76%
copy from web/www/routino/visualiser.css
copy to extras/find-fixme/web/www/fixme.css
index 23a7017..e1938d2 100644
--- a/web/www/routino/visualiser.css
+++ b/extras/find-fixme/web/www/fixme.css
@@ -1,9 +1,9 @@
 /*
-// Routino visualiser web page style sheet.
+// Routino (extras) fixme web page style sheet.
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2012 Andrew M. Bishop
+// This file Copyright 2008-2013 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -34,7 +34,7 @@ DIV.hideshow_box
 /* Left panel - specific tab options */
 /*-----------------------------------*/
 
-DIV#tab_visualiser_div INPUT
+DIV#tab_fixme_div INPUT
 {
  padding: 0;
  border:  1px solid;
@@ -43,28 +43,44 @@ DIV#tab_visualiser_div INPUT
  text-align: center;
 }
 
-DIV#tab_visualiser_div INPUT:hover
+DIV#tab_fixme_div INPUT:hover
 {
  background: #F0F0C0;
 }
 
-DIV#tab_visualiser_div TABLE
+DIV#tab_fixme_div DIV.center
+{
+ text-align: center;
+}
+
+DIV#tab_fixme_div TABLE
 {
  padding: 0;
  border:  0 hidden;
  margin:  0;
 }
 
-DIV#tab_visualiser_div TABLE TD
+DIV#tab_fixme_div TABLE TD
 {
  padding: 0;
  border:  0;
  margin:  0;
 }
 
-DIV#tab_visualiser_div INPUT
+DIV#tab_fixme_div INPUT
 {
  padding: 0;
  border:  1px solid;
  margin:  0;
 }
+
+
+/*-------*/
+/* Popup */
+/*-------*/
+
+DIV.popup
+{
+ font-family: monospace;
+ font-size:   10px;
+}
diff --git a/extras/find-fixme/web/www/fixme.html b/extras/find-fixme/web/www/fixme.html
new file mode 100644
index 0000000..f221918
--- /dev/null
+++ b/extras/find-fixme/web/www/fixme.html
@@ -0,0 +1,162 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routino fixme">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
+
+<title>Routino Extras : Viewer for OpenStreetMap "fixme" Tags</title>
+
+<!--
+ Routino (extras) fixme web page.
+
+ Part of the Routino routing software.
+
+ This file Copyright 2008-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see http://www.gnu.org/licenses/.
+-->
+
+<!-- Page elements -->
+<script src="page-elements.js" type="text/javascript"></script>
+<link href="page-elements.css" type="text/css" rel="stylesheet">
+
+<!-- Router and visualiser shared features -->
+<link href="maplayout.css" type="text/css" rel="stylesheet">
+
+<!-- Fixme specific features -->
+<link href="fixme.css" type="text/css" rel="stylesheet">
+
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('map_init();');">
+
+<!-- Left hand side of window - data panel -->
+
+<div class="left_panel">
+
+  <div class="tab_box">
+    <span id="tab_fixme" onclick="tab_select('fixme');" class="tab_selected"   title="View 'fixme' tags">Fixme</span>
+    <span id="tab_data"  onclick="tab_select('data');"  class="tab_unselected" title="View database information">Data</span>
+  </div>
+
+  <div class="tab_content" id="tab_fixme_div">
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">OSM "fixme" Tags</span> This web page allows
+      viewing the "fixme" tags in OSM data.  It is generated using a modified
+      version of the Routino router data processor.
+      <div class="center">
+        <a target="other" href="http://www.routino.org/">Routino Website</a>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">Instructions</span>
+      Zoom in and then use the button below to download the data.  The
+      server will only return data if the selected area is small enough.
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">Status</span>
+      <div id="result_status">
+        <div id="result_status_no_data">
+          <b><i>No data displayed</i></b>
+        </div>
+        <div id="result_status_data"      style="display: none;">
+        </div>
+        <div id="result_status_failed"    style="display: none;">
+          <b>Failed to get fixme data!</b>
+        </div>
+        <div id="result_status_fixme"     style="display: none;">
+          <b>Processed # "fixme" tags</b>
+        </div>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">Get Data</span>
+      <input type="button" id="fixme" onclick="displayData('fixmes');" value="Display "fixme" tags">
+      <input type="button" id="clear" onclick="displayData('');" value="Clear data">
+      <br>
+      The points displayed on the map are the location of items in the OSM data
+      that are tagged with "fixme" = "...".  Clicking on one of the points will
+      display the Node, Way or Relation identifier and the contents of the tag.
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">Links</span>
+      <a id="permalink_url" href="fixme.html">Permanent link to this view</a>
+      <br>
+      <a id="edit_url" target="edit" style="display: none;">Edit OSM source data</a>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+      <span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+      <span class="hideshow_title">Help</span>
+      <div id="hideshow_help_options_div">
+        <div class="scrollable">
+          <b>Quick Start</b>
+          <br>
+          Zoom to an area and select one of the buttons to display the fixme data.
+          <p>
+          <b>Data Failure</b>
+          <br>
+          If the area selected is too large (depends on the data type) then the
+          status will say "Failed to get fixme data" - zoom in and try again.
+          <br>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <div class="tab_content" id="tab_data_div" style="display: none;">
+    <div class="hideshow_box">
+      <span class="hideshow_title">Statistics</span>
+      <div id="statistics_data"></div>
+      <a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
+    </div>
+  </div>
+
+</div>
+
+<!-- Right hand side of window - map -->
+
+<div class="right_panel">
+  <div class="map" id="map">
+    <noscript>
+      <p>
+        Javascript is <em>required</em> to use this web page because of the
+        interactive map.
+    </noscript>
+  </div>
+  <div class="attribution">
+    Data Generator: <a href="http://www.routino.org/" target="routino">Routino</a>
+    |
+    Geo Data: <span id="attribution_data"></span>
+    |
+    Tiles: <span id="attribution_tile"></span>
+  </div>
+</div>
+
+</body>
+
+</html>
diff --git a/extras/find-fixme/web/www/fixme.leaflet.js b/extras/find-fixme/web/www/fixme.leaflet.js
new file mode 100644
index 0000000..d8e3515
--- /dev/null
+++ b/extras/find-fixme/web/www/fixme.leaflet.js
@@ -0,0 +1,563 @@
+//
+// Routino (extras) fixme web page Javascript
+//
+// Part of the Routino routing software.
+//
+// This file Copyright 2008-2014 Andrew M. Bishop
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Initialisation /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// Process the URL query string and extract the arguments
+
+var legal={"^lon"  : "^[-0-9.]+$",
+           "^lat"  : "^[-0-9.]+$",
+           "^zoom" : "^[0-9]+$"};
+
+var args={};
+
+if(location.search.length>1)
+  {
+   var query,queries;
+
+   query=location.search.replace(/^\?/,"");
+   query=query.replace(/;/g,"&");
+   queries=query.split("&");
+
+   for(var i=0;i<queries.length;i++)
+     {
+      queries[i].match(/^([^=]+)(=(.*))?$/);
+
+      var k=RegExp.$1;
+      var v=decodeURIComponent(RegExp.$3);
+
+      for(var l in legal)
+        {
+         if(k.match(RegExp(l)) && v.match(RegExp(legal[l])))
+            args[k]=v;
+        }
+     }
+  }
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// Map handling /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var map;
+var layerMap=[], layerHighlights, layerVectors, layerBoxes;
+
+var box;
+
+//
+// Initialise the 'map' object
+//
+
+function map_init()             // called from fixme.html
+{
+ // Create the map (Map URLs and limits are in mapprops.js)
+
+ map = L.map("map",
+             {
+              attributionControl: false,
+              zoomControl: false,
+
+              minZoom: mapprops.zoomout,
+              maxZoom: mapprops.zoomin,
+
+              maxBounds: L.latLngBounds(L.latLng(mapprops.southedge,mapprops.westedge),L.latLng(mapprops.northedge,mapprops.eastedge))
+              });
+
+ // Add map tile layers
+
+ var baselayers={};
+
+ for(var l=0; l<mapprops.mapdata.length; l++)
+   {
+    var urls=mapprops.mapdata[l].tiles.url.replace(/\${/g,"{");
+
+    if(mapprops.mapdata[l].tiles.subdomains===undefined)
+       layerMap[l] = L.tileLayer(urls);
+    else
+       layerMap[l] = L.tileLayer(urls, {subdomains: mapprops.mapdata[l].tiles.subdomains});
+
+    baselayers[mapprops.mapdata[l].label]=layerMap[l];
+
+    if(l===0)
+       map.addLayer(layerMap[l]);
+   }
+
+ // Add the controls
+
+ map.addControl(L.control.zoom());
+ map.addControl(L.control.scale());
+ map.addControl(L.control.layers(baselayers));
+
+ // Update the attribution if the layer changes
+
+ function change_attribution_event(event)
+ {
+  for(var l=0; l<mapprops.mapdata.length; l++)
+     if(layerMap[l] == event.layer)
+        change_attribution(l);
+ }
+
+ map.on("baselayerchange",change_attribution_event);
+
+ function change_attribution(l)
+ {
+  var data_url =mapprops.mapdata[l].attribution.data_url;
+  var data_text=mapprops.mapdata[l].attribution.data_text;
+  var tile_url =mapprops.mapdata[l].attribution.tile_url;
+  var tile_text=mapprops.mapdata[l].attribution.tile_text;
+
+  document.getElementById("attribution_data").innerHTML="<a href=\"" + data_url + "\" target=\"data_attribution\">" + data_text + "</a>";
+  document.getElementById("attribution_tile").innerHTML="<a href=\"" + tile_url + "\" target=\"tile_attribution\">" + tile_text + "</a>";
+ }
+
+ change_attribution(0);
+
+ // Add two vectors layers (one for highlights that display behind the vectors)
+
+ layerVectors = L.layerGroup();
+ map.addLayer(layerVectors);
+
+ layerHighlights = L.layerGroup();
+ map.addLayer(layerHighlights);
+
+ // Handle popup
+
+ createPopup();
+
+ // Add a boxes layer
+
+ layerBoxes = L.rectangle(map.options.maxBounds,{stroke: false, color: "#f00", weight: 1, opacity: 1.0,
+                                                 fill: false});
+
+ map.addLayer(layerBoxes);
+
+ box=false;
+
+ // Move the map
+
+ map.on("moveend", updateURLs);
+
+ var lon =args["lon"];
+ var lat =args["lat"];
+ var zoom=args["zoom"];
+
+ if(lon !== undefined && lat !== undefined && zoom !== undefined)
+   {
+    if(lon<mapprops.westedge) lon=mapprops.westedge;
+    if(lon>mapprops.eastedge) lon=mapprops.eastedge;
+
+    if(lat<mapprops.southedge) lat=mapprops.southedge;
+    if(lat>mapprops.northedge) lat=mapprops.northedge;
+
+    if(zoom<mapprops.zoomout) zoom=mapprops.zoomout;
+    if(zoom>mapprops.zoomin)  zoom=mapprops.zoomin;
+
+    map.setView(L.latLng(lat,lon),zoom);
+   }
+ else
+    map.fitBounds(map.options.maxBounds);
+
+ // Unhide editing URL if variable set
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var edit_url=document.getElementById("edit_url");
+
+    edit_url.style.display="";
+    edit_url.href=mapprops.editurl;
+   }
+
+ updateURLs();
+}
+
+
+//
+// Format a number in printf("%.5f") format.
+//
+
+function format5f(number)
+{
+ var newnumber=Math.floor(number*100000+0.5);
+ var delta=0;
+
+ if(newnumber>=0 && newnumber<100000) delta= 100000;
+ if(newnumber<0 && newnumber>-100000) delta=-100000;
+
+ var string=String(newnumber+delta);
+
+ var intpart =string.substring(0,string.length-5);
+ var fracpart=string.substring(string.length-5,string.length);
+
+ if(delta>0) intpart="0";
+ if(delta<0) intpart="-0";
+
+ return(intpart + "." + fracpart);
+}
+
+
+//
+// Build a set of URL arguments for the map location
+//
+
+function buildMapArguments()
+{
+ var lonlat = map.getCenter();
+
+ var zoom = map.getZoom();
+
+ return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lng) + ";zoom=" + zoom;
+}
+
+
+//
+// Update the URLs
+//
+
+function updateURLs()
+{
+ var mapargs=buildMapArguments();
+
+ var links=document.getElementsByTagName("a");
+
+ for(var i=0; i<links.length; i++)
+   {
+    var element=links[i];
+
+    if(element.id == "permalink_url")
+       element.href=location.pathname + "?" + mapargs;
+
+    if(element.id == "edit_url")
+       element.href=mapprops.editurl + "?" + mapargs;
+   }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////// Popup and selection handling /////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var popup=null;
+
+//
+// Create a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function createPopup()
+{
+ popup=document.createElement("div");
+
+ popup.className = "popup";
+
+ popup.innerHTML = "<span></span>";
+
+ popup.style.display = "none";
+
+ popup.style.position = "fixed";
+ popup.style.top = "-4000px";
+ popup.style.left = "-4000px";
+ popup.style.zIndex = "100";
+
+ popup.style.padding = "5px";
+
+ popup.style.opacity=0.85;
+ popup.style.backgroundColor="#C0C0C0";
+ popup.style.border="4px solid #404040";
+
+ document.body.appendChild(popup);
+}
+
+
+//
+// Draw a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function drawPopup(html)
+{
+ if(html===null)
+   {
+    popup.style.display="none";
+    return;
+   }
+
+ if(popup.style.display=="none")
+   {
+    var map_div=document.getElementById("map");
+
+    popup.style.left  =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px";
+    popup.style.top   =                                map_div.offsetTop +30 + "px";
+    popup.style.width =map_div.clientWidth-120 + "px";
+
+    popup.style.display="";
+   }
+
+ var close="<span style='float: right; cursor: pointer;' onclick='drawPopup(null)'>X</span>";
+
+ popup.innerHTML=close+html;
+}
+
+
+//
+// Select a feature
+//
+
+function selectCircleMarkerFeature(feature,dump,event)
+{
+ if(dump)
+    ajaxGET("fixme.cgi?dump=" + dump, runDumpSuccess);
+
+ layerHighlights.clearLayers();
+
+ var highlight = L.circleMarker(feature.getLatLng(),{radius: 2*feature.getRadius(), fill: true, fillColor: "#F0F000", fillOpacity: 1.0,
+                                                     stroke: false});
+
+ layerHighlights.addLayer(highlight);
+
+ highlight.bringToBack();
+}
+
+
+//
+// Un-select a feature
+//
+
+function unselectFeature(feature)
+{
+ layerHighlights.clearLayers();
+
+ drawPopup(null);
+}
+
+
+//
+// Display the dump data
+//
+
+function runDumpSuccess(response)
+{
+ var string=response.responseText;
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var types=["node", "way", "relation"];
+
+    for(var t in types)
+      {
+       var type=types[t];
+
+       var regexp=RegExp(type + " id='[0-9]+'");
+
+       var match=string.match(regexp);
+
+       if(match !== null)
+         {
+          match=String(match);
+
+          var id=match.slice(10+type.length,match.length-6);
+
+          string=string.replace(regexp,type + " id='<a href='" + mapprops.browseurl + "/" + type + "/" + id + "' target='" + type + id + "'>" + id + "</a>'");
+         }
+      }
+   }
+
+ drawPopup(string.split("><").join("><br><").split("<br><tag").join("<br>  <tag"));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Server handling ////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+//
+// Define an AJAX request object
+//
+
+function ajaxGET(url,success,failure,state)
+{
+ var ajaxRequest=new XMLHttpRequest();
+
+ function ajaxGOT(options) {
+  if(this.readyState==4)
+     if(this.status==200)
+       { if(typeof(options.success)=="function") options.success(this,options.state); }
+     else
+       { if(typeof(options.failure)=="function") options.failure(this,options.state); }
+ }
+
+ ajaxRequest.onreadystatechange = function(){ ajaxGOT.call(ajaxRequest,{success: success, failure: failure, state: state}); };
+ ajaxRequest.open("GET", url, true);
+ ajaxRequest.send(null);
+}
+
+
+//
+// Display the status
+//
+
+function displayStatus(type,subtype,content)
+{
+ var child=document.getElementById("result_status").firstChild;
+
+ do
+   {
+    if(child.id !== undefined)
+       child.style.display="none";
+
+    child=child.nextSibling;
+   }
+ while(child !== null);
+
+ var chosen_status=document.getElementById("result_status_" + type);
+
+ chosen_status.style.display="";
+
+ if(subtype !== undefined)
+   {
+    var format_status=document.getElementById("result_status_" + subtype).innerHTML;
+
+    chosen_status.innerHTML=format_status.replace("#",String(content));
+   }
+}
+
+
+//
+// Display data statistics
+//
+
+function displayStatistics()
+{
+ // Use AJAX to get the statistics
+
+ ajaxGET("fixme.cgi?statistics=yes", runStatisticsSuccess);
+}
+
+
+//
+// Success in running data statistics generation.
+//
+
+function runStatisticsSuccess(response)
+{
+ document.getElementById("statistics_data").innerHTML="<pre>" + response.responseText + "</pre>";
+ document.getElementById("statistics_link").style.display="none";
+}
+
+
+//
+// Get the requested data
+//
+
+function displayData(datatype)  // called from fixme.html
+{
+ // Delete the old data
+
+ unselectFeature();
+
+ layerVectors.clearLayers();
+ layerHighlights.clearLayers();
+
+ layerBoxes.setStyle({stroke:false});
+ box=false;
+
+ // Print the status
+
+ displayStatus("no_data");
+
+ // Return if just here to clear the data
+
+ if(datatype === "")
+    return;
+
+ // Get the new data
+
+ var mapbounds=map.getBounds();
+
+ var url="fixme.cgi";
+
+ url=url + "?lonmin=" + format5f(mapbounds.getWest());
+ url=url + ";latmin=" + format5f(mapbounds.getSouth());
+ url=url + ";lonmax=" + format5f(mapbounds.getEast());
+ url=url + ";latmax=" + format5f(mapbounds.getNorth());
+ url=url + ";data=" + datatype;
+
+ // Use AJAX to get the data
+
+ ajaxGET(url, runFixmeSuccess, runFailure);
+}
+
+
+//
+// Success in getting the error log data
+//
+
+function runFixmeSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat = L.latLng(lat,lon);
+
+       var feature = L.circleMarker(lonlat,{radius: 3, fill: true, fillColor: "#FF0000", fillOpacity: 1.0,
+                                            stroke: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectCircleMarkerFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","fixme",lines.length-2);
+}
+
+
+//
+// Failure in getting data.
+//
+
+function runFailure(response)
+{
+ displayStatus("failed");
+}
diff --git a/extras/find-fixme/web/www/fixme.openlayers.js b/extras/find-fixme/web/www/fixme.openlayers.js
new file mode 100644
index 0000000..c525ab9
--- /dev/null
+++ b/extras/find-fixme/web/www/fixme.openlayers.js
@@ -0,0 +1,623 @@
+//
+// Routino (extras) fixme web page Javascript
+//
+// Part of the Routino routing software.
+//
+// This file Copyright 2008-2014 Andrew M. Bishop
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Initialisation /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// Process the URL query string and extract the arguments
+
+var legal={"^lon"  : "^[-0-9.]+$",
+           "^lat"  : "^[-0-9.]+$",
+           "^zoom" : "^[0-9]+$"};
+
+var args={};
+
+if(location.search.length>1)
+  {
+   var query,queries;
+
+   query=location.search.replace(/^\?/,"");
+   query=query.replace(/;/g,"&");
+   queries=query.split("&");
+
+   for(var i=0;i<queries.length;i++)
+     {
+      queries[i].match(/^([^=]+)(=(.*))?$/);
+
+      var k=RegExp.$1;
+      var v=decodeURIComponent(RegExp.$3);
+
+      for(var l in legal)
+        {
+         if(k.match(RegExp(l)) && v.match(RegExp(legal[l])))
+            args[k]=v;
+        }
+     }
+  }
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// Map handling /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var map;
+var layerMap=[], layerHighlights, layerVectors, layerBoxes;
+var epsg4326, epsg900913;
+
+var box;
+var select;
+
+//
+// Initialise the 'map' object
+//
+
+function map_init()             // called from fixme.html
+{
+ // Create the map (Map URLs and limits are in mapprops.js)
+
+ epsg4326=new OpenLayers.Projection("EPSG:4326");
+ epsg900913=new OpenLayers.Projection("EPSG:900913");
+
+ map = new OpenLayers.Map ("map",
+                           {
+                            controls:[
+                                      new OpenLayers.Control.Navigation(),
+                                      new OpenLayers.Control.PanZoomBar(),
+                                      new OpenLayers.Control.ScaleLine(),
+                                      new OpenLayers.Control.LayerSwitcher()
+                                      ],
+
+                            projection: epsg900913,
+                            displayProjection: epsg4326,
+
+                            minZoomLevel: mapprops.zoomout,
+                            numZoomLevels: mapprops.zoomin-mapprops.zoomout+1,
+                            maxResolution: 156543.03390625 / Math.pow(2,mapprops.zoomout),
+
+                            restrictedExtent: new OpenLayers.Bounds(mapprops.westedge,mapprops.southedge,mapprops.eastedge,mapprops.northedge).transform(epsg4326,epsg900913)
+                           });
+
+ // Get a URL for the tile (mostly copied from OpenLayers/Layer/XYZ.js).
+
+ function limitedUrl(bounds)
+ {
+  var res = this.map.getResolution();
+
+  var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
+  var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
+  var z = this.map.getZoom() + this.map.minZoomLevel;
+
+  var limit = Math.pow(2, z);
+  x = ((x % limit) + limit) % limit;
+
+  var xyz = {"x": x, "y": y, "z": z};
+  var url = this.url;
+
+  if (OpenLayers.Util.isArray(url))
+    {
+     var s = "" + xyz.x + xyz.y + xyz.z;
+     url = this.selectUrl(s, url);
+    }
+
+  return OpenLayers.String.format(url, xyz);
+ }
+
+ // Add map tile layers
+
+ for(var l=0; l<mapprops.mapdata.length; l++)
+   {
+    var urls;
+
+    if(OpenLayers.Util.isArray(mapprops.mapdata[l].tiles.subdomains))
+      {
+       urls=[];
+
+       for(var s=0; s<mapprops.mapdata[l].tiles.subdomains.length; s++)
+          urls.push(mapprops.mapdata[l].tiles.url.replace(/\${s}/,mapprops.mapdata[l].tiles.subdomains[s]));
+      }
+    else
+       urls=mapprops.mapdata[l].tiles.url;
+
+    layerMap[l] = new OpenLayers.Layer.TMS(mapprops.mapdata[l].label,
+                                           urls,
+                                           {
+                                            getURL: limitedUrl,
+                                            displayOutsideMaxExtent: true,
+                                            buffer: 1
+                                           });
+    map.addLayer(layerMap[l]);
+   }
+
+ // Update the attribution if the layer changes
+
+ function change_attribution_event(event)
+ {
+  for(var l=0; l<mapprops.mapdata.length; l++)
+     if(layerMap[l] == event.layer)
+        change_attribution(l);
+ }
+
+ map.events.register("changelayer",layerMap,change_attribution_event);
+
+ function change_attribution(l)
+ {
+  var data_url =mapprops.mapdata[l].attribution.data_url;
+  var data_text=mapprops.mapdata[l].attribution.data_text;
+  var tile_url =mapprops.mapdata[l].attribution.tile_url;
+  var tile_text=mapprops.mapdata[l].attribution.tile_text;
+
+  document.getElementById("attribution_data").innerHTML="<a href=\"" + data_url + "\" target=\"data_attribution\">" + data_text + "</a>";
+  document.getElementById("attribution_tile").innerHTML="<a href=\"" + tile_url + "\" target=\"tile_attribution\">" + tile_text + "</a>";
+ }
+
+ change_attribution(0);
+
+ // Add two vectors layers (one for highlights that display behind the vectors)
+
+ layerHighlights = new OpenLayers.Layer.Vector("Highlights",{displayInLayerSwitcher: false});
+ map.addLayer(layerHighlights);
+
+ layerVectors = new OpenLayers.Layer.Vector("Markers",{displayInLayerSwitcher: false});
+ map.addLayer(layerVectors);
+
+ // Handle feature selection and popup
+
+ select = new OpenLayers.Control.SelectFeature(layerVectors,
+                                               {onSelect: selectFeature, onUnselect: unselectFeature});
+
+ map.addControl(select);
+ select.activate();
+
+ createPopup();
+
+ // Add a boxes layer
+
+ layerBoxes = new OpenLayers.Layer.Boxes("Boundary",{displayInLayerSwitcher: false});
+ map.addLayer(layerBoxes);
+
+ box=null;
+
+ // Move the map
+
+ map.events.register("moveend", map, updateURLs);
+
+ var lon =args["lon"];
+ var lat =args["lat"];
+ var zoom=args["zoom"];
+
+ if(lon !== undefined && lat !== undefined && zoom !== undefined)
+   {
+    if(lon<mapprops.westedge) lon=mapprops.westedge;
+    if(lon>mapprops.eastedge) lon=mapprops.eastedge;
+
+    if(lat<mapprops.southedge) lat=mapprops.southedge;
+    if(lat>mapprops.northedge) lat=mapprops.northedge;
+
+    if(zoom<mapprops.zoomout) zoom=mapprops.zoomout;
+    if(zoom>mapprops.zoomin)  zoom=mapprops.zoomin;
+
+    var lonlat = new OpenLayers.LonLat(lon,lat);
+    lonlat.transform(epsg4326,epsg900913);
+
+    map.moveTo(lonlat,zoom-map.minZoomLevel);
+   }
+ else
+   {
+    map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true));
+    map.maxResolution = map.getResolution();
+   }
+
+ // Unhide editing URL if variable set
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var edit_url=document.getElementById("edit_url");
+
+    edit_url.style.display="";
+    edit_url.href=mapprops.editurl;
+   }
+
+ updateURLs();
+}
+
+
+//
+// Format a number in printf("%.5f") format.
+//
+
+function format5f(number)
+{
+ var newnumber=Math.floor(number*100000+0.5);
+ var delta=0;
+
+ if(newnumber>=0 && newnumber<100000) delta= 100000;
+ if(newnumber<0 && newnumber>-100000) delta=-100000;
+
+ var string=String(newnumber+delta);
+
+ var intpart =string.substring(0,string.length-5);
+ var fracpart=string.substring(string.length-5,string.length);
+
+ if(delta>0) intpart="0";
+ if(delta<0) intpart="-0";
+
+ return(intpart + "." + fracpart);
+}
+
+
+//
+// Build a set of URL arguments for the map location
+//
+
+function buildMapArguments()
+{
+ var lonlat = map.getCenter().clone();
+ lonlat.transform(epsg900913,epsg4326);
+
+ var zoom = map.getZoom() + map.minZoomLevel;
+
+ return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lon) + ";zoom=" + zoom;
+}
+
+
+//
+// Update the URLs
+//
+
+function updateURLs()
+{
+ var mapargs=buildMapArguments();
+
+ var links=document.getElementsByTagName("a");
+
+ for(var i=0; i<links.length; i++)
+   {
+    var element=links[i];
+
+    if(element.id == "permalink_url")
+       element.href=location.pathname + "?" + mapargs;
+
+    if(element.id == "edit_url")
+       element.href=mapprops.editurl + "?" + mapargs;
+   }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////// Popup and selection handling /////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var popup=null;
+
+//
+// Create a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function createPopup()
+{
+ popup=document.createElement("div");
+
+ popup.className = "popup";
+
+ popup.innerHTML = "<span></span>";
+
+ popup.style.display = "none";
+
+ popup.style.position = "fixed";
+ popup.style.top = "-4000px";
+ popup.style.left = "-4000px";
+ popup.style.zIndex = "100";
+
+ popup.style.padding = "5px";
+
+ popup.style.opacity=0.85;
+ popup.style.backgroundColor="#C0C0C0";
+ popup.style.border="4px solid #404040";
+
+ document.body.appendChild(popup);
+}
+
+
+//
+// Draw a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function drawPopup(html)
+{
+ if(html===null)
+   {
+    popup.style.display="none";
+    return;
+   }
+
+ if(popup.style.display=="none")
+   {
+    var map_div=document.getElementById("map");
+
+    popup.style.left  =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px";
+    popup.style.top   =                                map_div.offsetTop +30 + "px";
+    popup.style.width =map_div.clientWidth-120 + "px";
+
+    popup.style.display="";
+   }
+
+ var close="<span style='float: right; cursor: pointer;' onclick='drawPopup(null)'>X</span>";
+
+ popup.innerHTML=close+html;
+}
+
+
+//
+// Select a feature
+//
+
+function selectFeature(feature)
+{
+ if(feature.attributes.dump)
+    ajaxGET("fixme.cgi?dump=" + feature.attributes.dump, runDumpSuccess);
+
+ layerHighlights.destroyFeatures();
+
+ var highlight_style = new OpenLayers.Style({},{strokeColor: "#F0F000",strokeWidth: 8,
+                                                fillColor: "#F0F000",pointRadius: 4});
+
+ var highlight = new OpenLayers.Feature.Vector(feature.geometry.clone(),{},highlight_style);
+
+ layerHighlights.addFeatures([highlight]);
+}
+
+
+//
+// Un-select a feature
+//
+
+function unselectFeature(feature)
+{
+ layerHighlights.destroyFeatures();
+
+ drawPopup(null);
+}
+
+
+//
+// Display the dump data
+//
+
+function runDumpSuccess(response)
+{
+ var string=response.responseText;
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var types=["node", "way", "relation"];
+
+    for(var t in types)
+      {
+       var type=types[t];
+
+       var regexp=RegExp(type + " id='[0-9]+'");
+
+       var match=string.match(regexp);
+
+       if(match !== null)
+         {
+          match=String(match);
+
+          var id=match.slice(10+type.length,match.length-6);
+
+          string=string.replace(regexp,type + " id='<a href='" + mapprops.browseurl + "/" + type + "/" + id + "' target='" + type + id + "'>" + id + "</a>'");
+         }
+      }
+   }
+
+ drawPopup(string.split("><").join("><br><").split("<br><tag").join("<br>  <tag"));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Server handling ////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+//
+// Define an AJAX request object
+//
+
+function ajaxGET(url,success,failure,state)
+{
+ var ajaxRequest=new XMLHttpRequest();
+
+ function ajaxGOT(options) {
+  if(this.readyState==4)
+     if(this.status==200)
+       { if(typeof(options.success)=="function") options.success(this,options.state); }
+     else
+       { if(typeof(options.failure)=="function") options.failure(this,options.state); }
+ }
+
+ ajaxRequest.onreadystatechange = function(){ ajaxGOT.call(ajaxRequest,{success: success, failure: failure, state: state}); };
+ ajaxRequest.open("GET", url, true);
+ ajaxRequest.send(null);
+}
+
+
+//
+// Display the status
+//
+
+function displayStatus(type,subtype,content)
+{
+ var child=document.getElementById("result_status").firstChild;
+
+ do
+   {
+    if(child.id !== undefined)
+       child.style.display="none";
+
+    child=child.nextSibling;
+   }
+ while(child !== null);
+
+ var chosen_status=document.getElementById("result_status_" + type);
+
+ chosen_status.style.display="";
+
+ if(subtype !== undefined)
+   {
+    var format_status=document.getElementById("result_status_" + subtype).innerHTML;
+
+    chosen_status.innerHTML=format_status.replace("#",String(content));
+   }
+}
+
+
+//
+// Display data statistics
+//
+
+function displayStatistics()
+{
+ // Use AJAX to get the statistics
+
+ ajaxGET("fixme.cgi?statistics=yes", runStatisticsSuccess);
+}
+
+
+//
+// Success in running data statistics generation.
+//
+
+function runStatisticsSuccess(response)
+{
+ document.getElementById("statistics_data").innerHTML="<pre>" + response.responseText + "</pre>";
+ document.getElementById("statistics_link").style.display="none";
+}
+
+
+//
+// Get the requested data
+//
+
+function displayData(datatype)  // called from fixme.html
+{
+ // Delete the old data
+
+ unselectFeature();
+
+ select.deactivate();
+
+ layerVectors.destroyFeatures();
+ layerHighlights.destroyFeatures();
+
+ if(box !== null)
+    layerBoxes.removeMarker(box);
+ box=null;
+
+ // Print the status
+
+ displayStatus("no_data");
+
+ // Return if just here to clear the data
+
+ if(datatype === "")
+    return;
+
+ // Get the new data
+
+ var mapbounds=map.getExtent().clone();
+ mapbounds.transform(epsg900913,epsg4326);
+
+ var url="fixme.cgi";
+
+ url=url + "?lonmin=" + format5f(mapbounds.left);
+ url=url + ";latmin=" + format5f(mapbounds.bottom);
+ url=url + ";lonmax=" + format5f(mapbounds.right);
+ url=url + ";latmax=" + format5f(mapbounds.top);
+ url=url + ";data=" + datatype;
+
+ // Use AJAX to get the data
+
+ ajaxGET(url, runFixmeSuccess, runFailure);
+}
+
+
+//
+// Success in getting the error log data
+//
+
+function runFixmeSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var style = new OpenLayers.Style({},{stroke: false,
+                                      pointRadius: 3,fillColor: "#FF0000",
+                                      cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat = new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
+
+       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
+
+       features.push(new OpenLayers.Feature.Vector(point,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","fixme",lines.length-2);
+}
+
+
+//
+// Failure in getting data.
+//
+
+function runFailure(response)
+{
+ displayStatus("failed");
+}
diff --git a/web/www/routino/index.html b/extras/find-fixme/web/www/index.html
similarity index 55%
copy from web/www/routino/index.html
copy to extras/find-fixme/web/www/index.html
index 831ef02..cc67c4f 100644
--- a/web/www/routino/index.html
+++ b/extras/find-fixme/web/www/index.html
@@ -1,12 +1,18 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta http-equiv="refresh" content="1; URL=fixme.html">
+
+<title>Routino Extras : Viewer for OpenStreetMap "fixme" Tags</title>
 
 <!--
- Routino redirect web page.
+ Routino extras fixme redirect web page.
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,20 +28,16 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Route Planner for OpenStreetMap Data</TITLE>
-<META http-equiv="refresh" content="1; URL=router.html">
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="../documentation/style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
-<h1>Routino : Route Planner for OpenStreetMap Data</h1>
+<h1>Routino Extras : Viewer for OpenStreetMap "fixme" Tags</h1>
 
 <hr>
 </div>
@@ -46,7 +48,7 @@
 
 <div class="content">
 
-<h2><a href="router.html" title="Page Moved">Page Moved</a></h2>
+<h2><a href="fixme.html" title="Page Moved">Page Moved</a></h2>
 
 </div>
 
@@ -54,17 +56,17 @@
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/web/www/routino/statistics.cgi b/extras/find-fixme/web/www/paths.pl
old mode 100755
new mode 100644
similarity index 61%
copy from web/www/routino/statistics.cgi
copy to extras/find-fixme/web/www/paths.pl
index fefa9fc..0791efd
--- a/web/www/routino/statistics.cgi
+++ b/extras/find-fixme/web/www/paths.pl
@@ -1,10 +1,9 @@
-#!/usr/bin/perl
 #
-# Routino data statistics
+# Routino CGI paths Perl script
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2012 Andrew M. Bishop
+# This file Copyright 2008-2013 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,21 +19,16 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Use the directory paths script
-require "paths.pl";
+# Directory path parameters
 
-# Use the perl CGI module
-use CGI ':cgi';
+# EDIT THIS to set the root directory for the non-web data files.
+$root_dir="..";
 
+# EDIT THIS to change the location of the individual directories.
+$bin_dir="$root_dir/bin";
+$data_dir="$root_dir/data";
 
-# Print the output
+# EDIT THIS to change the name of the executable (enables easy selection of slim mode).
+$fixme_dumper_exe="fixme-dumper";
 
-print header('text/plain');
-
-# Run the filedumper
-
-$params.=" --dir=$data_dir" if($data_dir);
-$params.=" --prefix=$data_prefix" if($data_prefix);
-$params.=" --statistics";
-
-system "$bin_dir/$filedumper_exe $params 2>&1";
+1;
diff --git a/extras/plot-time/README.txt b/extras/plot-time/README.txt
new file mode 100644
index 0000000..2e002f2
--- /dev/null
+++ b/extras/plot-time/README.txt
@@ -0,0 +1,18 @@
+                      Planetsplitter Execution Time Analysis
+                      ======================================
+
+A Perl script that uses Gnuplot to plot a graph of the time taken by the
+planetsplitter program to run.
+
+
+plot-planetsplitter-time.pl
+---------------------------
+
+Example usage:
+
+planetsplitter --loggable --logtime ... > planetsplitter.log
+
+plot-planetsplitter-time.pl < planetsplitter.log
+
+This will generate a file called planetsplitter.png that contains the graph of
+the execution time.
diff --git a/extras/plot-time/plot-planetsplitter-time.pl b/extras/plot-time/plot-planetsplitter-time.pl
new file mode 100755
index 0000000..e5fd92d
--- /dev/null
+++ b/extras/plot-time/plot-planetsplitter-time.pl
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+#
+# Routino execution log plotter.
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2013-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+# Read the planetsplitter log file
+
+open(SECTION   ,">gnuplot.section.tmp");
+open(SUBSECTION,">gnuplot.subsection.tmp");
+
+my $count=1;
+my $startcount=0;
+my $totaltime=0;
+
+while(<STDIN>)
+  {
+   s%\r*\n%%;
+
+   next if(! $_);
+
+   next if(m%^=%);
+
+   if( m%^\[ *([0-9]+):([0-9.]+)\] ([^:]+)% && ! m%Complete$% )
+     {
+      my $time=(60.0*$1)+$2;
+      my $description=$3;
+
+      print SUBSECTION "$count $time \"$description\"\n";
+
+      $totaltime+=$time;
+     }
+   else
+     {
+      if($startcount>0)
+        {
+         my $boxcentre=($count+$startcount+0.5)/2;
+         my $boxwidth=$count-$startcount-1;
+
+         print SECTION "$boxcentre $totaltime $boxwidth\n";
+        }
+
+      $startcount=$count-0.5;
+      $totaltime=0;
+     }
+
+   $count++;
+  }
+
+close(SECTION);
+close(SUBSECTION);
+
+# Plot using gnuplot
+
+open(GNUPLOT,"|gnuplot");
+
+print GNUPLOT <<EOF
+
+set title "Planetsplitter Execution Time"
+
+set noxtics
+
+set ylabel "Sub-section Time (seconds)"
+set logscale y
+set yrange [0.001:]
+
+set style fill solid 1.0
+set boxwidth 0.8
+
+set nokey
+
+set style line 1 lt rgb "#FFC0C0" lw 1
+set style line 2 lt rgb "#FF0000" lw 1
+
+set term png size 1000,750
+set output "planetsplitter.png"
+
+plot "gnuplot.section.tmp" using 1:2:3 with boxes linestyle 1, \\
+     "gnuplot.section.tmp" using 1:(\$2*1.1):(sprintf("%.1f",\$2)) with labels font "Sans,9" center textcolor rgbcolor "#000000", \\
+     "gnuplot.subsection.tmp" using 1:2 with boxes linestyle 2, \\
+     "gnuplot.subsection.tmp" using (\$1+0.1):(0.0013):3 with labels font "Sans,8" left rotate textcolor rgbcolor "#000000"
+
+exit
+EOF
+;
+
+close(GNUPLOT);
+
+unlink "gnuplot.section.tmp";
+unlink "gnuplot.subsection.tmp";
diff --git a/extras/tagmodifier/Makefile b/extras/tagmodifier/Makefile
new file mode 100644
index 0000000..72a6e2a
--- /dev/null
+++ b/extras/tagmodifier/Makefile
@@ -0,0 +1,84 @@
+# tagmodifier Makefile
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2013-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# All configuration is in the top-level Makefile.conf
+
+include ../../Makefile.conf
+
+# Compilation targets
+
+C=$(wildcard *.c)
+D=$(wildcard .deps/*.d)
+
+ROUTINO_SRC=../../src
+
+EXE=tagmodifier
+
+########
+
+all: $(EXE)
+
+########
+
+TAGMODIFIER_OBJ=tagmodifier.o \
+	        $(ROUTINO_SRC)/files.o $(ROUTINO_SRC)/logging.o $(ROUTINO_SRC)/logerror.o \
+	        $(ROUTINO_SRC)/uncompress.o $(ROUTINO_SRC)/xmlparse.o $(ROUTINO_SRC)/tagging.o
+
+tagmodifier : $(TAGMODIFIER_OBJ)
+	$(LD) $(TAGMODIFIER_OBJ) -o $@ $(LDFLAGS)
+
+########
+
+$(ROUTINO_SRC)/%.o :
+	cd $(ROUTINO_SRC) && $(MAKE) $(notdir $@)
+
+%.o : %.c
+	@[ -d .deps ] || mkdir .deps
+	$(CC) -c $(CFLAGS) -DSLIM=0 -I$(ROUTINO_SRC) $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $@)))
+
+########
+
+test:
+
+########
+
+install:
+
+########
+
+clean:
+	rm -f *~
+	rm -f *.o
+	rm -f core
+
+########
+
+distclean: clean
+	-rm -f $(EXE)
+	-rm -f $(D)
+	-rm -fr .deps
+
+########
+
+include $(D)
+
+########
+
+.PHONY:: all test install clean distclean
diff --git a/extras/tagmodifier/README.txt b/extras/tagmodifier/README.txt
new file mode 100644
index 0000000..f70b736
--- /dev/null
+++ b/extras/tagmodifier/README.txt
@@ -0,0 +1,42 @@
+                        Tagging Rule Tester / Tag Modifier
+                        ==================================
+
+This program is used to run the tag transformation process on an OSM XML file
+for test purposes.  This allows it to be used to test new tagging rules or to
+make automatic rule-based modifications to tags within an XML file.
+
+
+tagmodifier
+-----------
+
+Usage: tagmodifier [--help]
+                   [--tagging=<filename>]
+                   [--loggable] [--logtime]
+                   [--errorlog[<name>]]
+                   [<filename.osm> | <filename.osm.bz2> |
+                    <filename.osm.gz> | <filename.osm.xz>]
+
+--help
+       Prints out the help information.
+
+--tagging=<filename>
+       The name of the XML file containing the tagging rules (defaults
+       to 'tagging.xml' in the current directory).
+
+--loggable
+       Print progress messages that are suitable for logging to a file;
+       normally an incrementing counter is printed which is more
+       suitable for real-time display than logging.
+
+--logtime
+       Print the elapsed time for the processing.
+
+--errorlog[=<name>]
+       Log parsing errors to 'error.log' or the specified file name.
+
+<filename.osm>
+       Specifies the filename(s) to read data from. Filenames ending
+       '.bz2' will be bzip2 uncompressed (if bzip2 support compiled
+       in). Filenames ending '.gz' will be gzip uncompressed (if gzip
+       support compiled in). Filenames ending '.xz' will be xz
+       uncompressed (if xz support compiled in).
diff --git a/src/tagmodifier.c b/extras/tagmodifier/tagmodifier.c
similarity index 73%
rename from src/tagmodifier.c
rename to extras/tagmodifier/tagmodifier.c
index aa72544..de0b91c 100644
--- a/src/tagmodifier.c
+++ b/extras/tagmodifier/tagmodifier.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2012 Andrew M. Bishop
+ This file Copyright 2010-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,28 +22,29 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <string.h>
 #include <ctype.h>
-#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
 
-#include "files.h"
-#include "logging.h"
 #include "xmlparse.h"
 #include "tagging.h"
 
+#include "files.h"
+#include "uncompress.h"
+
 
 /* Local variables */
 
-static unsigned long nnodes=0;
-static unsigned long nways=0;
-static unsigned long nrelations=0;
+static uint64_t nnodes=0,nways=0,nrelations=0;
 
 TagList *current_tags=NULL;
 
 
 /* Local functions */
 
-static void print_usage(int detail);
+static void print_usage(int detail,const char *argerr,const char *err);
 
 
 /* The XML tag processing function prototypes */
@@ -163,11 +164,11 @@ static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&osmType_tag,NULL};
 static int boundsType_function(const char *_tag_,int _type_,const char *minlat,const char *minlon,const char *maxlat,const char *maxlon,const char *origin)
 {
  printf("  <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(minlat) printf(" minlat=\"%s\"",ParseXML_Encode_Safe_XML(minlat));
- if(minlon) printf(" minlon=\"%s\"",ParseXML_Encode_Safe_XML(minlon));
- if(maxlat) printf(" maxlat=\"%s\"",ParseXML_Encode_Safe_XML(maxlat));
- if(maxlon) printf(" maxlon=\"%s\"",ParseXML_Encode_Safe_XML(maxlon));
- if(origin) printf(" origin=\"%s\"",ParseXML_Encode_Safe_XML(origin));
+ if(minlat) printf(" minlat='%s'",ParseXML_Encode_Safe_XML(minlat));
+ if(minlon) printf(" minlon='%s'",ParseXML_Encode_Safe_XML(minlon));
+ if(maxlat) printf(" maxlat='%s'",ParseXML_Encode_Safe_XML(maxlat));
+ if(maxlon) printf(" maxlon='%s'",ParseXML_Encode_Safe_XML(maxlon));
+ if(origin) printf(" origin='%s'",ParseXML_Encode_Safe_XML(origin));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -190,8 +191,8 @@ static int boundsType_function(const char *_tag_,int _type_,const char *minlat,c
 static int boundType_function(const char *_tag_,int _type_,const char *box,const char *origin)
 {
  printf("  <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(box) printf(" box=\"%s\"",ParseXML_Encode_Safe_XML(box));
- if(origin) printf(" origin=\"%s\"",ParseXML_Encode_Safe_XML(origin));
+ if(box) printf(" box='%s'",ParseXML_Encode_Safe_XML(box));
+ if(origin) printf(" origin='%s'",ParseXML_Encode_Safe_XML(origin));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -255,36 +256,32 @@ static int tagType_function(const char *_tag_,int _type_,const char *k,const cha
 
 static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action)
 {
- static node_t node_id;
+ static int64_t llid;
 
  if(_type_&XMLPARSE_TAG_START)
    {
-    long long llid;
-
     nnodes++;
 
     if(!(nnodes%10000))
-       fprintf_middle(stderr,"Reading: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",ParseXML_LineNumber(),nnodes,nways,nrelations);
+       fprintf_middle(stderr,"Reading: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
 
     current_tags=NewTagList();
 
     /* Handle the node information */
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,id);   llid=atoll(id); /* need long long conversion */
-    node_id=(node_t)llid;
-    logassert((long long)node_id==llid,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need int64_t conversion */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&NodeRules,current_tags,node_id);
+    TagList *result=ApplyNodeTaggingRules(current_tags,llid);
     int i;
 
     for(i=0;i<result->ntags;i++)
       {
        printf("    <tag");
-       printf(" k=\"%s\"",ParseXML_Encode_Safe_XML(result->k[i]));
-       printf(" v=\"%s\"",ParseXML_Encode_Safe_XML(result->v[i]));
+       printf(" k='%s'",ParseXML_Encode_Safe_XML(result->k[i]));
+       printf(" v='%s'",ParseXML_Encode_Safe_XML(result->v[i]));
        printf(" />\n");
       }
 
@@ -293,15 +290,15 @@ static int nodeType_function(const char *_tag_,int _type_,const char *id,const c
    }
 
  printf("  <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(id) printf(" id=\"%s\"",ParseXML_Encode_Safe_XML(id));
- if(lat) printf(" lat=\"%s\"",ParseXML_Encode_Safe_XML(lat));
- if(lon) printf(" lon=\"%s\"",ParseXML_Encode_Safe_XML(lon));
- if(timestamp) printf(" timestamp=\"%s\"",ParseXML_Encode_Safe_XML(timestamp));
- if(uid) printf(" uid=\"%s\"",ParseXML_Encode_Safe_XML(uid));
- if(user) printf(" user=\"%s\"",ParseXML_Encode_Safe_XML(user));
- if(visible) printf(" visible=\"%s\"",ParseXML_Encode_Safe_XML(visible));
- if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version));
- if(action) printf(" action=\"%s\"",ParseXML_Encode_Safe_XML(action));
+ if(id) printf(" id='%s'",ParseXML_Encode_Safe_XML(id));
+ if(lat) printf(" lat='%s'",ParseXML_Encode_Safe_XML(lat));
+ if(lon) printf(" lon='%s'",ParseXML_Encode_Safe_XML(lon));
+ if(timestamp) printf(" timestamp='%s'",ParseXML_Encode_Safe_XML(timestamp));
+ if(uid) printf(" uid='%s'",ParseXML_Encode_Safe_XML(uid));
+ if(user) printf(" user='%s'",ParseXML_Encode_Safe_XML(user));
+ if(visible) printf(" visible='%s'",ParseXML_Encode_Safe_XML(visible));
+ if(version) printf(" version='%s'",ParseXML_Encode_Safe_XML(version));
+ if(action) printf(" action='%s'",ParseXML_Encode_Safe_XML(action));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -322,7 +319,7 @@ static int nodeType_function(const char *_tag_,int _type_,const char *id,const c
 static int ndType_function(const char *_tag_,int _type_,const char *ref)
 {
  printf("    <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(ref) printf(" ref=\"%s\"",ParseXML_Encode_Safe_XML(ref));
+ if(ref) printf(" ref='%s'",ParseXML_Encode_Safe_XML(ref));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -347,9 +344,9 @@ static int ndType_function(const char *_tag_,int _type_,const char *ref)
 static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role)
 {
  printf("    <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(type) printf(" type=\"%s\"",ParseXML_Encode_Safe_XML(type));
- if(ref) printf(" ref=\"%s\"",ParseXML_Encode_Safe_XML(ref));
- if(role) printf(" role=\"%s\"",ParseXML_Encode_Safe_XML(role));
+ if(type) printf(" type='%s'",ParseXML_Encode_Safe_XML(type));
+ if(ref) printf(" ref='%s'",ParseXML_Encode_Safe_XML(ref));
+ if(role) printf(" role='%s'",ParseXML_Encode_Safe_XML(role));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -381,37 +378,32 @@ static int memberType_function(const char *_tag_,int _type_,const char *type,con
 
 static int wayType_function(const char *_tag_,int _type_,const char *id,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action)
 {
- static way_t way_id;
+ static int64_t llid;
 
  if(_type_&XMLPARSE_TAG_START)
    {
-    long long llid;
-
     nways++;
 
     if(!(nways%1000))
-       fprintf_middle(stderr,"Reading: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",ParseXML_LineNumber(),nnodes,nways,nrelations);
+       fprintf_middle(stderr,"Reading: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
 
     current_tags=NewTagList();
 
     /* Handle the way information */
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need long long conversion */
-
-    way_id=(way_t)llid;
-    logassert((long long)way_id==llid,"Way ID too large (change way_t to 64-bits?)"); /* check way id can be stored in way_t data type. */
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need int64_t conversion */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&WayRules,current_tags,way_id);
+    TagList *result=ApplyWayTaggingRules(current_tags,llid);
     int i;
 
     for(i=0;i<result->ntags;i++)
       {
        printf("    <tag");
-       printf(" k=\"%s\"",ParseXML_Encode_Safe_XML(result->k[i]));
-       printf(" v=\"%s\"",ParseXML_Encode_Safe_XML(result->v[i]));
+       printf(" k='%s'",ParseXML_Encode_Safe_XML(result->k[i]));
+       printf(" v='%s'",ParseXML_Encode_Safe_XML(result->v[i]));
        printf(" />\n");
       }
 
@@ -420,13 +412,13 @@ static int wayType_function(const char *_tag_,int _type_,const char *id,const ch
    }
 
  printf("  <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(id) printf(" id=\"%s\"",ParseXML_Encode_Safe_XML(id));
- if(timestamp) printf(" timestamp=\"%s\"",ParseXML_Encode_Safe_XML(timestamp));
- if(uid) printf(" uid=\"%s\"",ParseXML_Encode_Safe_XML(uid));
- if(user) printf(" user=\"%s\"",ParseXML_Encode_Safe_XML(user));
- if(visible) printf(" visible=\"%s\"",ParseXML_Encode_Safe_XML(visible));
- if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version));
- if(action) printf(" action=\"%s\"",ParseXML_Encode_Safe_XML(action));
+ if(id) printf(" id='%s'",ParseXML_Encode_Safe_XML(id));
+ if(timestamp) printf(" timestamp='%s'",ParseXML_Encode_Safe_XML(timestamp));
+ if(uid) printf(" uid='%s'",ParseXML_Encode_Safe_XML(uid));
+ if(user) printf(" user='%s'",ParseXML_Encode_Safe_XML(user));
+ if(visible) printf(" visible='%s'",ParseXML_Encode_Safe_XML(visible));
+ if(version) printf(" version='%s'",ParseXML_Encode_Safe_XML(version));
+ if(action) printf(" action='%s'",ParseXML_Encode_Safe_XML(action));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -458,37 +450,32 @@ static int wayType_function(const char *_tag_,int _type_,const char *id,const ch
 
 static int relationType_function(const char *_tag_,int _type_,const char *id,const char *timestamp,const char *uid,const char *user,const char *visible,const char *version,const char *action)
 {
- static relation_t relation_id;
+ static int64_t llid;
 
  if(_type_&XMLPARSE_TAG_START)
    {
-    long long llid;
-
     nrelations++;
 
     if(!(nrelations%1000))
-       fprintf_middle(stderr,"Reading: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",ParseXML_LineNumber(),nnodes,nways,nrelations);
+       fprintf_middle(stderr,"Reading: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
 
     current_tags=NewTagList();
 
     /* Handle the relation information */
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need long long conversion */
-
-    relation_id=(relation_t)llid;
-    logassert((long long)relation_id==llid,"Relation ID too large (change relation_t to 64-bits?)"); /* check relation id can be stored in relation_t data type. */
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need int64_t conversion */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&RelationRules,current_tags,relation_id);
+    TagList *result=ApplyRelationTaggingRules(current_tags,llid);
     int i;
 
     for(i=0;i<result->ntags;i++)
       {
        printf("    <tag");
-       printf(" k=\"%s\"",ParseXML_Encode_Safe_XML(result->k[i]));
-       printf(" v=\"%s\"",ParseXML_Encode_Safe_XML(result->v[i]));
+       printf(" k='%s'",ParseXML_Encode_Safe_XML(result->k[i]));
+       printf(" v='%s'",ParseXML_Encode_Safe_XML(result->v[i]));
        printf(" />\n");
       }
 
@@ -497,13 +484,13 @@ static int relationType_function(const char *_tag_,int _type_,const char *id,con
    }
 
  printf("  <%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(id) printf(" id=\"%s\"",ParseXML_Encode_Safe_XML(id));
- if(timestamp) printf(" timestamp=\"%s\"",ParseXML_Encode_Safe_XML(timestamp));
- if(uid) printf(" uid=\"%s\"",ParseXML_Encode_Safe_XML(uid));
- if(user) printf(" user=\"%s\"",ParseXML_Encode_Safe_XML(user));
- if(visible) printf(" visible=\"%s\"",ParseXML_Encode_Safe_XML(visible));
- if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version));
- if(action) printf(" action=\"%s\"",ParseXML_Encode_Safe_XML(action));
+ if(id) printf(" id='%s'",ParseXML_Encode_Safe_XML(id));
+ if(timestamp) printf(" timestamp='%s'",ParseXML_Encode_Safe_XML(timestamp));
+ if(uid) printf(" uid='%s'",ParseXML_Encode_Safe_XML(uid));
+ if(user) printf(" user='%s'",ParseXML_Encode_Safe_XML(user));
+ if(visible) printf(" visible='%s'",ParseXML_Encode_Safe_XML(visible));
+ if(version) printf(" version='%s'",ParseXML_Encode_Safe_XML(version));
+ if(action) printf(" action='%s'",ParseXML_Encode_Safe_XML(action));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -526,8 +513,8 @@ static int relationType_function(const char *_tag_,int _type_,const char *id,con
 static int osmType_function(const char *_tag_,int _type_,const char *version,const char *generator)
 {
  printf("<%s%s",(_type_==XMLPARSE_TAG_END)?"/":"",_tag_);
- if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version));
- if(generator) printf(" generator=\"%s\"",ParseXML_Encode_Safe_XML(generator));
+ if(version) printf(" version='%s'",ParseXML_Encode_Safe_XML(version));
+ if(generator) printf(" generator='%s'",ParseXML_Encode_Safe_XML(generator));
  printf("%s>\n",(_type_==(XMLPARSE_TAG_START|XMLPARSE_TAG_END))?" /":"");
  return(0);
 }
@@ -550,8 +537,8 @@ static int osmType_function(const char *_tag_,int _type_,const char *version,con
 static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
 {
  printf("<?%s",_tag_);
- if(version) printf(" version=\"%s\"",ParseXML_Encode_Safe_XML(version));
- if(encoding) printf(" encoding=\"%s\"",ParseXML_Encode_Safe_XML(encoding));
+ if(version) printf(" version='%s'",ParseXML_Encode_Safe_XML(version));
+ if(encoding) printf(" encoding='%s'",ParseXML_Encode_Safe_XML(encoding));
  printf(" ?>\n");
  return(0);
 }
@@ -563,8 +550,9 @@ static int xmlDeclaration_function(const char *_tag_,int _type_,const char *vers
 
 int main(int argc,char **argv)
 {
- char *tagging=NULL,*filename=NULL;
- FILE *file;
+ char *tagging=NULL,*filename=NULL,*errorlog=NULL;
+ char *p;
+ int fd;
  int arg,retval;
 
  /* Parse the command line arguments */
@@ -572,21 +560,30 @@ int main(int argc,char **argv)
  for(arg=1;arg<argc;arg++)
    {
     if(!strcmp(argv[arg],"--help"))
-       print_usage(1);
-    else if(!strcmp(argv[arg],"--loggable"))
-       option_loggable=1;
+       print_usage(1,NULL,NULL);
     else if(!strncmp(argv[arg],"--tagging=",10))
        tagging=&argv[arg][10];
+    else if(!strcmp(argv[arg],"--loggable"))
+       option_loggable=1;
+    else if(!strcmp(argv[arg],"--logtime"))
+       option_logtime=1;
+    else if(!strcmp(argv[arg],"--errorlog"))
+       errorlog="error.log";
+    else if(!strncmp(argv[arg],"--errorlog=",11))
+       errorlog=&argv[arg][11];
     else if(argv[arg][0]=='-' && argv[arg][1]=='-')
-       print_usage(0);
+       print_usage(0,argv[arg],NULL);
     else if(filename)
-       print_usage(0);
+       print_usage(0,argv[arg],"Only one file name can be specified on the command line.");
     else
        filename=argv[arg];
    }
 
  /* Check the specified command line options */
 
+ if(!filename)
+    print_usage(0,NULL,"A single file name must be specified on the command line.");
+
  if(tagging)
    {
     if(!ExistsFile(tagging))
@@ -614,31 +611,38 @@ int main(int argc,char **argv)
 
  /* Open the input file */
 
- if(filename)
-   {
-    file=fopen(filename,"rb");
+ fd=OpenFile(filename);
 
-    if(!file)
-      {
-       fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",argv[arg],strerror(errno));
-       exit(EXIT_FAILURE);
-      }
-   }
- else
-    file=stdin;
+ if((p=strstr(filename,".bz2")) && !strcmp(p,".bz2"))
+    fd=Uncompress_Bzip2(fd);
+
+ if((p=strstr(filename,".gz")) && !strcmp(p,".gz"))
+    fd=Uncompress_Gzip(fd);
+
+ if((p=strstr(filename,".xz")) && !strcmp(p,".xz"))
+    fd=Uncompress_Xz(fd);
+
+ /* Create the error log file */
+
+ if(errorlog)
+    open_errorlog(errorlog,0,0);
 
  /* Parse the file */
 
  fprintf_first(stderr,"Reading: Lines=0 Nodes=0 Ways=0 Relations=0");
 
- retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
+ retval=ParseXML(fd,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
+
+ fprintf_last(stderr,"Read: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
 
- fprintf_last(stderr,"Read: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",ParseXML_LineNumber(),nnodes,nways,nrelations);
+ /* Close the error log file */
+
+ if(errorlog)
+    close_errorlog();
 
  /* Tidy up */
 
- if(filename)
-    fclose(file);
+ CloseFile(fd);
 
  return(retval);
 }
@@ -648,28 +652,64 @@ int main(int argc,char **argv)
   Print out the usage information.
 
   int detail The level of detail to use - 0 = low, 1 = high.
+
+  const char *argerr The argument that gave the error (if there is one).
+
+  const char *err Other error message (if there is one).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void print_usage(int detail)
+static void print_usage(int detail,const char *argerr,const char *err)
 {
  fprintf(stderr,
          "Usage: tagmodifier [--help]\n"
-         "                   [--loggable]\n"
          "                   [--tagging=<filename>]\n"
-         "                   [<filename.osm>]\n");
+         "                   [--loggable] [--logtime]\n"
+         "                   [--errorlog[=<name>]]\n"
+         "                   <filename.osm>"
+#if defined(USE_BZIP2) && USE_BZIP2
+         " | <filename.osm.bz2>"
+#endif
+#if defined(USE_GZIP) && USE_GZIP
+         " | <filename.osm.gz>"
+#endif
+#if defined(USE_XZ) && USE_XZ
+         " | <filename.osm.xz>"
+#endif
+         "\n");
+
+ if(argerr)
+    fprintf(stderr,
+            "\n"
+            "Error with command line parameter: %s\n",argerr);
+
+ if(err)
+    fprintf(stderr,
+            "\n"
+            "Error: %s\n",err);
 
  if(detail)
     fprintf(stderr,
             "\n"
             "--help                    Prints this information.\n"
             "\n"
-            "--loggable                Print progress messages suitable for logging to file.\n"
-            "\n"
             "--tagging=<filename>      The name of the XML file containing the tagging rules\n"
             "                          (defaults to 'tagging.xml' in current directory).\n"
             "\n"
-            "<filename.osm>            The name of the file to process (by default data is\n"
-            "                          read from standard input).\n");
+            "--loggable                Print progress messages suitable for logging to file.\n"
+            "--logtime                 Print the elapsed time for the processing.\n"
+            "--errorlog[=<name>]       Log parsing errors to 'error.log' or the given name.\n"
+            "\n"
+            "<filename.osm>            The name of the file to process.\n"
+#if defined(USE_BZIP2) && USE_BZIP2
+            "                          Filenames ending '.bz2' will be bzip2 uncompressed.\n"
+#endif
+#if defined(USE_GZIP) && USE_GZIP
+            "                          Filenames ending '.gz' will be gzip uncompressed.\n"
+#endif
+#if defined(USE_XZ) && USE_XZ
+            "                          Filenames ending '.xz' will be xz uncompressed.\n"
+#endif
+            );
 
  exit(!detail);
 }
diff --git a/src/Makefile b/src/Makefile
index 5fcf661..2857c85 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,7 +2,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2012 Andrew M. Bishop
+# This file Copyright 2008-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -18,24 +18,12 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Web file paths
+# All configuration is in the top-level Makefile.conf
 
-WEBDIR=../web/bin
+include ../Makefile.conf
 
-# Compilation programs
+# Debugging options
 
-CC=gcc
-LD=gcc
-
-LEX=flex
-
-# Compilation program options
-
-CFLAGS=-Wall -Wmissing-prototypes -std=c99
-#CFLAGS+=-Wextra -pedantic
-LDFLAGS=-lm
-
-CFLAGS+=-O3
 #CFLAGS+=-O0 -g
 #CFLAGS+=-pg
 #CFLAGS+=-fprofile-arcs -ftest-coverage
@@ -43,46 +31,36 @@ CFLAGS+=-O3
 #LDFLAGS+=-pg -static
 #LDFLAGS+=-fprofile-arcs -ftest-coverage
 
-LEXFLAGS=
-
-# Required for multi-threaded support
-CFLAGS+=-pthread -DUSE_PTHREADS
-LDFLAGS+=-pthread -lpthread
+# Sub-directories and sub-makefiles
 
-# Required to use stdio with files > 2GiB on 32-bit system.
-CFLAGS+=-D_FILE_OFFSET_BITS=64
-
-# Required to compile on Linux without a warning about pread() and pwrite() functions.
-CFLAGS+=-D_POSIX_C_SOURCE=200809L
+SUBFILES=$(wildcard */Makefile)
+SUBDIRS=$(foreach f,$(SUBFILES),$(dir $f))
 
 # Compilation targets
 
 C=$(wildcard *.c)
 D=$(wildcard .deps/*.d)
 
-EXE=planetsplitter planetsplitter-slim router router-slim filedumperx filedumper filedumper-slim tagmodifier
+EXE=planetsplitter planetsplitter-slim router router-slim filedumperx filedumper filedumper-slim
 
 ########
 
-all: $(EXE)
-	-@[ -d $(WEBDIR) ] && \
-	  for file in $(EXE); do \
-	     if [ ! -f $(WEBDIR)/$$file ] || [ $$file -nt $(WEBDIR)/$$file ]; then \
-	        echo cp $$file $(WEBDIR) ;\
-	        cp -f $$file $(WEBDIR) ;\
-	     fi ;\
-	  done
-	@cd xml  && $(MAKE) CC="$(CC)" LD="$(LD)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
-	@cd test && $(MAKE) CC="$(CC)" LD="$(LD)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
+all: all-local
+	for dir in $(SUBDIRS); do \
+	   ( cd $$dir && $(MAKE) $@ ); \
+	done
+
+all-local: $(EXE)
 
 ########
 
 PLANETSPLITTER_OBJ=planetsplitter.o \
 	           nodesx.o segmentsx.o waysx.o relationsx.o superx.o prunex.o \
 	           ways.o types.o \
-	           files.o logging.o \
+	           files.o logging.o logerror.o errorlogx.o \
 	           results.o queue.o sorting.o \
-	           xmlparse.o tagging.o osmparser.o
+	           xmlparse.o tagging.o \
+	           uncompress.o osmxmlparse.o osmpbfparse.o osmo5mparse.o osmparser.o
 
 planetsplitter : $(PLANETSPLITTER_OBJ)
 	$(LD) $(PLANETSPLITTER_OBJ) -o $@ $(LDFLAGS)
@@ -92,9 +70,10 @@ planetsplitter : $(PLANETSPLITTER_OBJ)
 PLANETSPLITTER_SLIM_OBJ=planetsplitter-slim.o \
 	                nodesx-slim.o segmentsx-slim.o waysx-slim.o relationsx-slim.o superx-slim.o prunex-slim.o \
 	                ways.o types.o \
-	                files.o logging.o \
+	                files.o logging.o logerror-slim.o errorlogx-slim.o \
 	                results.o queue.o sorting.o \
-	                xmlparse.o tagging.o osmparser.o
+	                xmlparse.o tagging.o \
+	                uncompress.o osmxmlparse.o osmpbfparse.o osmo5mparse.o osmparser.o
 
 planetsplitter-slim : $(PLANETSPLITTER_SLIM_OBJ)
 	$(LD) $(PLANETSPLITTER_SLIM_OBJ) -o $@ $(LDFLAGS)
@@ -132,7 +111,7 @@ filedumperx : $(FILEDUMPERX_OBJ)
 ########
 
 FILEDUMPER_OBJ=filedumper.o \
-	       nodes.o segments.o ways.o relations.o types.o fakes.o \
+	       nodes.o segments.o ways.o relations.o types.o fakes.o errorlog.o \
                visualiser.o \
 	       files.o logging.o xmlparse.o
 
@@ -142,7 +121,7 @@ filedumper : $(FILEDUMPER_OBJ)
 ########
 
 FILEDUMPER_SLIM_OBJ=filedumper-slim.o \
-	       nodes-slim.o segments-slim.o ways-slim.o relations-slim.o types.o fakes-slim.o \
+	       nodes-slim.o segments-slim.o ways-slim.o relations-slim.o types.o fakes-slim.o errorlog-slim.o \
                visualiser-slim.o \
 	       files.o logging.o xmlparse.o
 
@@ -151,22 +130,6 @@ filedumper-slim : $(FILEDUMPER_SLIM_OBJ)
 
 ########
 
-TAGMODIFIER_OBJ=tagmodifier.o \
-	        files.o logging.o \
-                xmlparse.o tagging.o
-
-tagmodifier : $(TAGMODIFIER_OBJ)
-	$(LD) $(TAGMODIFIER_OBJ) -o $@ $(LDFLAGS)
-
-########
-
-xmlparse.c : xmlparse.l
-	$(LEX) $(LEXFLAGS)  $<
-	- at mv lex.yy.c xmlparse.c
-	@echo Created xmlparse.c
-
-########
-
 %.o : %.c
 	@[ -d .deps ] || mkdir .deps
 	$(CC) -c $(CFLAGS) -DSLIM=0 -DDATADIR=\"$(datadir)\" $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $@)))
@@ -177,40 +140,51 @@ xmlparse.c : xmlparse.l
 
 ########
 
-test:
-	cd xml  && $(MAKE) test
-	cd test && $(MAKE) test
+test: test-local
+	for dir in $(SUBDIRS); do \
+	   ( cd $$dir && $(MAKE) $@ ); \
+	done
+
+test-local:
 
 ########
 
-install: all
-	-[ -d $(DESTDIR)$(bindir) ] || mkdir -p $(DESTDIR)$(bindir)
-	@[ -d $(DESTDIR)$(bindir) ] && \
-	  for file in $(EXE); do \
-	     echo cp $$file $(DESTDIR)$(bindir) ;\
-	     cp -f $$file $(DESTDIR)$(bindir) ;\
-	  done
+install: install-local
+	for dir in $(SUBDIRS); do \
+	   ( cd $$dir && $(MAKE) $@ ); \
+	done
+
+install-local: all-local
+	@[ -d $(DESTDIR)$(bindir) ] || mkdir -p $(DESTDIR)$(bindir)
+	@for file in $(EXE); do \
+	    echo cp $$file $(DESTDIR)$(bindir) ;\
+	    cp -f $$file $(DESTDIR)$(bindir) ;\
+	 done
 
 ########
 
-clean:
+clean: clean-local
+	for dir in $(SUBDIRS); do \
+	   ( cd $$dir && $(MAKE) $@ ); \
+	done
+
+clean-local:
 	rm -f *~
 	rm -f *.o
 	rm -f core
 	rm -f *.gcda *.gcno *.gcov gmon.out
-	rm -f xmlparse.c
-	cd xml  && $(MAKE) clean
-	cd test && $(MAKE) clean
 
 ########
 
-distclean: clean
-	-[ -d ../web/bin ] && cd ../web/bin/ && rm -f $(EXE)
+distclean: distclean-local
+	for dir in $(SUBDIRS); do \
+	   ( cd $$dir && $(MAKE) $@ ); \
+	done
+
+distclean-local: clean-local
 	-rm -f $(EXE)
 	-rm -f $(D)
 	-rm -fr .deps
-	cd xml  && $(MAKE) distclean
-	cd test && $(MAKE) distclean
 
 ########
 
@@ -218,5 +192,6 @@ include $(D)
 
 ########
 
-top=-top
-include ../Makefile
+.PHONY:: all test install clean distclean
+
+.PHONY:: all-local test-local install-local clean-local distclean-local
diff --git a/src/cache.h b/src/cache.h
new file mode 100644
index 0000000..88d6a77
--- /dev/null
+++ b/src/cache.h
@@ -0,0 +1,180 @@
+/***************************************
+ Functions to maintain an in-RAM cache of on-disk data for slim mode.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#if SLIM
+
+#ifndef CACHE_H
+#define CACHE_H    /*+ To stop multiple inclusions. +*/
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "types.h"
+
+
+/* Macros for constants */
+
+#define CACHEWIDTH 2048         /*+ The width of the cache. +*/
+#define CACHEDEPTH   16         /*+ The depth of the cache. +*/
+
+
+/* Macro for structure forward declaration */
+
+#define CACHE_STRUCTURE_FWD(type) typedef struct _##type##Cache type##Cache;
+
+
+/* Macro for structure declaration */
+
+/*+ A macro to create a cache structure. +*/
+#define CACHE_STRUCTURE(type) \
+                              \
+struct _##type##Cache                                                     \
+{                                                                         \
+ int     first  [CACHEWIDTH];             /*+ The first entry to fill +*/ \
+                                                                          \
+ type    data   [CACHEWIDTH][CACHEDEPTH]; /*+ The array of type##s. +*/   \
+ index_t indices[CACHEWIDTH][CACHEDEPTH]; /*+ The array of indexes. +*/   \
+};
+
+
+/* Macros for function prototypes */
+
+#define CACHE_NEWCACHE_PROTO(type) static inline type##Cache *New##type##Cache(void);
+
+#define CACHE_DELETECACHE_PROTO(type) static inline void Delete##type##Cache(type##Cache *cache);
+
+#define CACHE_FETCHCACHE_PROTO(type) static inline type *FetchCached##type(type##Cache *cache,index_t index,int fd,off_t offset);
+
+#define CACHE_REPLACECACHE_PROTO(type) static inline void ReplaceCached##type(type##Cache *cache,type *value,index_t index,int fd,off_t offset);
+
+#define CACHE_INVALIDATECACHE_PROTO(type) static inline void Invalidate##type##Cache(type##Cache *cache);
+
+
+/* Macros for function definitions */
+
+/*+ A macro to create a function that creates a new cache data structure. +*/
+#define CACHE_NEWCACHE(type) \
+                             \
+static inline type##Cache *New##type##Cache(void)       \
+{                                                       \
+ type##Cache *cache;                                    \
+                                                        \
+ cache=(type##Cache*)malloc(sizeof(type##Cache));       \
+                                                        \
+ Invalidate##type##Cache(cache);                        \
+                                                        \
+ return(cache);                                         \
+}
+
+
+/*+ A macro to create a function that deletes a cache data structure. +*/
+#define CACHE_DELETECACHE(type) \
+                                \
+static inline void Delete##type##Cache(type##Cache *cache)      \
+{                                                               \
+ free(cache);                                                   \
+}
+
+
+/*+ A macro to create a function that fetches an item from a cache data structure or reads from file. +*/
+#define CACHE_FETCHCACHE(type) \
+                               \
+static inline type *FetchCached##type(type##Cache *cache,index_t index,int fd,off_t offset) \
+{                                                                                           \
+ int row=index%CACHEWIDTH;                                                                  \
+ int col;                                                                                   \
+                                                                                            \
+ for(col=0;col<CACHEDEPTH;col++)                                                            \
+    if(cache->indices[row][col]==index)                                                     \
+       return(&cache->data[row][col]);                                                      \
+                                                                                            \
+ col=cache->first[row];                                                                     \
+                                                                                            \
+ cache->first[row]=(cache->first[row]+1)%CACHEDEPTH;                                        \
+                                                                                            \
+ SlimFetch(fd,&cache->data[row][col],sizeof(type),offset+(off_t)index*sizeof(type));        \
+                                                                                            \
+ cache->indices[row][col]=index;                                                            \
+                                                                                            \
+ return(&cache->data[row][col]);                                                            \
+}
+
+
+/*+ A macro to create a function that replaces an item in a cache data structure and writes to file. +*/
+#define CACHE_REPLACECACHE(type) \
+                                 \
+static inline void ReplaceCached##type(type##Cache *cache,type *value,index_t index,int fd,off_t offset) \
+{                                                                                                        \
+ int row=index%CACHEWIDTH;                                                                               \
+ int col;                                                                                                \
+                                                                                                         \
+ for(col=0;col<CACHEDEPTH;col++)                                                                         \
+    if(cache->indices[row][col]==index)                                                                  \
+       break;                                                                                            \
+                                                                                                         \
+ if(col==CACHEDEPTH)                                                                                     \
+   {                                                                                                     \
+    col=cache->first[row];                                                                               \
+                                                                                                         \
+    cache->first[row]=(cache->first[row]+1)%CACHEDEPTH;                                                  \
+   }                                                                                                     \
+                                                                                                         \
+ cache->indices[row][col]=index;                                                                         \
+                                                                                                         \
+ cache->data[row][col]=*value;                                                                           \
+                                                                                                         \
+ SlimReplace(fd,&cache->data[row][col],sizeof(type),offset+(off_t)index*sizeof(type));                   \
+}
+
+
+/*+ A macro to create a function that invalidates the contents of a cache data structure. +*/
+#define CACHE_INVALIDATECACHE(type) \
+                                    \
+static inline void Invalidate##type##Cache(type##Cache *cache) \
+{                                                              \
+ int row,col;                                                  \
+                                                               \
+ for(row=0;row<CACHEWIDTH;row++)                               \
+   {                                                           \
+    cache->first[row]=0;                                       \
+                                                               \
+    for(col=0;col<CACHEDEPTH;col++)                            \
+       cache->indices[row][col]=NO_NODE;                       \
+   }                                                           \
+}
+
+
+/*+ Cache data structure forward declarations (for planetsplitter). +*/
+CACHE_STRUCTURE_FWD(NodeX)
+CACHE_STRUCTURE_FWD(SegmentX)
+CACHE_STRUCTURE_FWD(WayX)
+
+/*+ Cache data structure forward declarations (for router). +*/
+CACHE_STRUCTURE_FWD(Node)
+CACHE_STRUCTURE_FWD(Segment)
+CACHE_STRUCTURE_FWD(Way)
+CACHE_STRUCTURE_FWD(TurnRelation)
+
+
+#endif /* CACHE_H */
+
+#endif  /* SLIM */
diff --git a/src/errorlog.c b/src/errorlog.c
new file mode 100644
index 0000000..8c574dc
--- /dev/null
+++ b/src/errorlog.c
@@ -0,0 +1,178 @@
+/***************************************
+ Error log data type functions.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "types.h"
+#include "errorlog.h"
+
+#include "files.h"
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Load in an error log list from a file.
+
+  ErrorLogs *LoadErrorLogs Returns the error log list.
+
+  const char *filename The name of the file to load.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+ErrorLogs *LoadErrorLogs(const char *filename)
+{
+ ErrorLogs *errorlogs;
+
+ errorlogs=(ErrorLogs*)malloc(sizeof(ErrorLogs));
+
+#if !SLIM
+
+ errorlogs->data=MapFile(filename);
+
+ /* Copy the ErrorLogsFile header structure from the loaded data */
+
+ errorlogs->file=*((ErrorLogsFile*)errorlogs->data);
+
+ /* Set the pointers in the ErrorLogs structure. */
+
+ errorlogs->offsets         =(index_t* )(errorlogs->data+sizeof(ErrorLogsFile));
+ errorlogs->errorlogs_geo   =(ErrorLog*)(errorlogs->data+sizeof(ErrorLogsFile)+(errorlogs->file.latbins*errorlogs->file.lonbins+1)*sizeof(index_t));
+ errorlogs->errorlogs_nongeo=(ErrorLog*)(errorlogs->data+sizeof(ErrorLogsFile)+(errorlogs->file.latbins*errorlogs->file.lonbins+1)*sizeof(index_t)+errorlogs->file.number_geo*sizeof(ErrorLog));
+ errorlogs->strings         =(char*    )(errorlogs->data+sizeof(ErrorLogsFile)+(errorlogs->file.latbins*errorlogs->file.lonbins+1)*sizeof(index_t)+errorlogs->file.number*sizeof(ErrorLog));
+
+#else
+
+ errorlogs->fd=SlimMapFile(filename);
+
+ /* Copy the ErrorLogsFile header structure from the loaded data */
+
+ SlimFetch(errorlogs->fd,&errorlogs->file,sizeof(ErrorLogsFile),0);
+
+ errorlogs->offsetsoffset         =sizeof(ErrorLogsFile);
+ errorlogs->errorlogsoffset_geo   =sizeof(ErrorLogsFile)+(errorlogs->file.latbins*errorlogs->file.lonbins+1)*sizeof(index_t);
+ errorlogs->errorlogsoffset_nongeo=sizeof(ErrorLogsFile)+(errorlogs->file.latbins*errorlogs->file.lonbins+1)*sizeof(index_t)+errorlogs->file.number_geo*sizeof(ErrorLog);
+ errorlogs->stringsoffset         =sizeof(ErrorLogsFile)+(errorlogs->file.latbins*errorlogs->file.lonbins+1)*sizeof(index_t)+errorlogs->file.number*sizeof(ErrorLog);
+
+#endif
+
+ return(errorlogs);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Destroy the node list.
+
+  ErrorLogs *errorlogs The node list to destroy.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DestroyErrorLogs(ErrorLogs *errorlogs)
+{
+#if !SLIM
+
+ errorlogs->data=UnmapFile(errorlogs->data);
+
+#else
+
+ errorlogs->fd=SlimUnmapFile(errorlogs->fd);
+
+#endif
+
+ free(errorlogs);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Get the latitude and longitude associated with an error log.
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  index_t index The errorlog index.
+
+  ErrorLog *errorlogp A pointer to the error log.
+
+  double *latitude Returns the latitude.
+
+  double *longitude Returns the logitude.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void GetErrorLogLatLong(ErrorLogs *errorlogs,index_t index,ErrorLog *errorlogp,double *latitude,double *longitude)
+{
+ ll_bin_t latbin,lonbin;
+ ll_bin2_t bin=-1;
+ ll_bin2_t start,end,mid;
+ index_t offset;
+
+ /* Binary search - search key nearest match below is required.
+  *
+  *  # <- start  |  Check mid and move start or end if it doesn't match
+  *  #           |
+  *  #           |  A lower bound match is wanted we can set end=mid-1 or
+  *  # <- mid    |  start=mid because we know that mid doesn't match.
+  *  #           |
+  *  #           |  Eventually either end=start or end=start+1 and one of
+  *  # <- end    |  start or end is the wanted one.
+  */
+
+ /* Search for offset */
+
+ start=0;
+ end=errorlogs->file.lonbins*errorlogs->file.latbins;
+
+ do
+   {
+    mid=(start+end)/2;                  /* Choose mid point */
+
+    offset=LookupErrorLogOffset(errorlogs,mid);
+
+    if(offset<index)                    /* Mid point is too low for an exact match but could be lower bound */
+       start=mid;
+    else if(offset>index)               /* Mid point is too high */
+       end=mid?(mid-1):mid;
+    else                                /* Mid point is correct */
+      {bin=mid;break;}
+   }
+ while((end-start)>1);
+
+ if(bin==-1)
+   {
+    offset=LookupErrorLogOffset(errorlogs,end);
+
+    if(offset>index)
+       bin=start;
+    else
+       bin=end;
+   }
+
+ while(bin<=(errorlogs->file.lonbins*errorlogs->file.latbins) && 
+       LookupErrorLogOffset(errorlogs,bin)==LookupErrorLogOffset(errorlogs,bin+1))
+    bin++;
+
+ latbin=bin%errorlogs->file.latbins;
+ lonbin=bin/errorlogs->file.latbins;
+
+ /* Return the values */
+
+ if(errorlogp==NULL)
+    errorlogp=LookupErrorLog(errorlogs,index,2);
+
+ *latitude =latlong_to_radians(bin_to_latlong(errorlogs->file.latzero+latbin)+off_to_latlong(errorlogp->latoffset));
+ *longitude=latlong_to_radians(bin_to_latlong(errorlogs->file.lonzero+lonbin)+off_to_latlong(errorlogp->lonoffset));
+}
diff --git a/src/errorlog.h b/src/errorlog.h
new file mode 100644
index 0000000..3b7f790
--- /dev/null
+++ b/src/errorlog.h
@@ -0,0 +1,195 @@
+/***************************************
+ Header file for error log file data types and associated function prototypes.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2013-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#ifndef ERRORLOG_H
+#define ERRORLOG_H    /*+ To stop multiple inclusions. +*/
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "types.h"
+#include "typesx.h"
+
+#include "files.h"
+
+
+/*+ A structure containing information for an error message in the file. +*/
+typedef struct _ErrorLog
+{
+ ll_off_t  latoffset;        /*+ The error message latitude offset within its bin. +*/
+ ll_off_t  lonoffset;        /*+ The error message longitude offset within its bin. +*/
+
+ uint32_t  offset;           /*+ The offset of the error message from the beginning of the text section. +*/
+ uint32_t  length;           /*+ The length of the error message in the text section. +*/
+}
+ ErrorLog;
+
+
+/*+ A structure containing the header from the error log file. +*/
+typedef struct _ErrorLogsFile
+{
+ index_t  number;               /*+ The total number of error messages. +*/
+ index_t  number_geo;           /*+ The number of error messages with a geographical location. +*/
+ index_t  number_nongeo;        /*+ The number of error messages without a geographical location. +*/
+
+ ll_bin_t latbins;              /*+ The number of bins containing latitude. +*/
+ ll_bin_t lonbins;              /*+ The number of bins containing longitude. +*/
+
+ ll_bin_t latzero;              /*+ The bin number of the furthest south bin. +*/
+ ll_bin_t lonzero;              /*+ The bin number of the furthest west bin. +*/
+}
+ ErrorLogsFile;
+
+
+/*+ A structure containing a set of error log messages read from the file. +*/
+typedef struct _ErrorLogs
+{
+ ErrorLogsFile file;            /*+ The header data from the file. +*/
+
+#if !SLIM
+
+ void     *data;                /*+ The memory mapped data in the file. +*/
+
+ index_t  *offsets;             /*+ A pointer to the array of offsets in the file. +*/
+
+ ErrorLog *errorlogs_geo;       /*+ A pointer to the array of geographical error logs in the file. +*/
+ ErrorLog *errorlogs_nongeo;    /*+ A pointer to the array of non-geographical error logs in the file. +*/
+
+ char     *strings;             /*+ A pointer to the array of error strings in the file. +*/
+
+#else
+
+ int       fd;                  /*+ The file descriptor for the file. +*/
+
+ off_t     offsetsoffset;       /*+ An allocated array with a copy of the file offsets. +*/
+
+ off_t     errorlogsoffset_geo;    /*+ The offset of the geographical error logs within the file. +*/
+ off_t     errorlogsoffset_nongeo; /*+ The offset of the non-geographical error logs within the file. +*/
+
+ off_t     stringsoffset;       /*+ The offset of the error strings within the file. +*/
+
+ ErrorLog  cached[2];           /*+ Some cached error logs read from the file in slim mode. +*/
+
+ char      cachestring[1024];   /*+ A cached copy of the error string read from the file in slim mode. +*/
+
+#endif
+}
+ ErrorLogs;
+
+
+/* Error log functions in errorlog.c */
+
+ErrorLogs *LoadErrorLogs(const char *filename);
+
+void DestroyErrorLogs(ErrorLogs *errorlogs);
+
+void GetErrorLogLatLong(ErrorLogs *errorlogs,index_t index,ErrorLog *errorlogp,double *latitude,double *longitude);
+
+
+/* Macros and inline functions */
+
+#if !SLIM
+
+/*+ Return an ErrorLog pointer given a set of errorlogs and an index. +*/
+#define LookupErrorLog(xxx,yyy,ppp)     (&(xxx)->errorlogs_geo[yyy])
+
+/*+ Return the offset of a geographical region given a set of errorlogs. +*/
+#define LookupErrorLogOffset(xxx,yyy)   ((xxx)->offsets[yyy])
+
+/*+ Return the string for an error log. +*/
+#define LookupErrorLogString(xxx,yyy)   (&(xxx)->strings[(xxx)->errorlogs_geo[yyy].offset])
+
+#else
+
+/* Prototypes */
+
+static inline ErrorLog *LookupErrorLog(ErrorLogs *errorlogs,index_t index,int position);
+
+static inline index_t LookupErrorLogOffset(ErrorLogs *errorlogs,index_t index);
+
+static inline char *LookupErrorLogString(ErrorLogs *errorlogs,index_t index);
+
+/* Inline functions */
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the ErrorLog information for a particular error log.
+
+  ErrorLog *LookupErrorLog Returns a pointer to the cached error log information.
+
+  ErrorLogs *errorlogs The set of errorlogs to use.
+
+  index_t index The index of the error log.
+
+  int position The position in the cache to store the value.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline ErrorLog *LookupErrorLog(ErrorLogs *errorlogs,index_t index,int position)
+{
+ SlimFetch(errorlogs->fd,&errorlogs->cached[position-1],sizeof(ErrorLog),errorlogs->errorlogsoffset_geo+(off_t)index*sizeof(ErrorLog));
+
+ return(&errorlogs->cached[position-1]);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the offset of error logs in a geographical region.
+
+  index_t LookupErrorLogOffset Returns the index offset.
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  index_t index The index of the offset.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline index_t LookupErrorLogOffset(ErrorLogs *errorlogs,index_t index)
+{
+ index_t offset;
+
+ SlimFetch(errorlogs->fd,&offset,sizeof(index_t),errorlogs->offsetsoffset+(off_t)index*sizeof(index_t));
+
+ return(offset);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the string associated with a particular error log.
+
+  char *LookupErrorLogString Returns the error string.
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  index_t index The index of the string.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline char *LookupErrorLogString(ErrorLogs *errorlogs,index_t index)
+{
+ ErrorLog *errorlog=LookupErrorLog(errorlogs,index,2);
+
+ SlimFetch(errorlogs->fd,errorlogs->cachestring,errorlog->length,errorlogs->stringsoffset+errorlog->offset);
+
+ return(errorlogs->cachestring);
+}
+
+#endif
+
+
+#endif /* ERRORLOG_H */
diff --git a/src/errorlogx.c b/src/errorlogx.c
new file mode 100644
index 0000000..08e5028
--- /dev/null
+++ b/src/errorlogx.c
@@ -0,0 +1,980 @@
+/***************************************
+ Error log processing functions.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include "typesx.h"
+#include "nodesx.h"
+#include "waysx.h"
+#include "relationsx.h"
+
+#include "errorlogx.h"
+#include "errorlog.h"
+
+#include "files.h"
+#include "sorting.h"
+
+
+/* Global variables */
+
+/*+ The name of the error log file. +*/
+extern char *errorlogfilename;
+
+/*+ The name of the binary error log file. +*/
+extern char *errorbinfilename;
+
+/* Local variables */
+
+/*+ Temporary file-local variables for use by the sort functions. +*/
+static latlong_t lat_min,lat_max,lon_min,lon_max;
+
+/* Local functions */
+
+static void reindex_nodes(NodesX *nodesx);
+static void reindex_ways(WaysX *waysx);
+static void reindex_relations(RelationsX *relationsx);
+
+static int lookup_lat_long_node(NodesX *nodesx,node_t node,latlong_t *latitude,latlong_t *longitude);
+static int lookup_lat_long_way(WaysX *waysx,NodesX *nodesx,way_t way,latlong_t *latitude,latlong_t *longitude,index_t error);
+static int lookup_lat_long_relation(RelationsX *relationsx,WaysX *waysx,NodesX *nodesx,relation_t relation,latlong_t *latitude,latlong_t *longitude,index_t error);
+
+static int sort_by_lat_long(ErrorLogX *a,ErrorLogX *b);
+static int measure_lat_long(ErrorLogX *errorlogx,index_t index);
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Allocate a new error log list (create a new file).
+
+  ErrorLogsX *NewErrorLogList Returns a pointer to the error log list.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+ErrorLogsX *NewErrorLogList(void)
+{
+ ErrorLogsX *errorlogsx;
+
+ errorlogsx=(ErrorLogsX*)calloc(1,sizeof(ErrorLogsX));
+
+ logassert(errorlogsx,"Failed to allocate memory (try using slim mode?)"); /* Check calloc() worked */
+
+ return(errorlogsx);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Free an error log list.
+
+  ErrorLogsX *errorlogsx The set of error logs to be freed.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void FreeErrorLogList(ErrorLogsX *errorlogsx)
+{
+ free(errorlogsx);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process the binary error log.
+
+  ErrorLogsX *errorlogsx The set of error logs to update.
+
+  NodesX *nodesx The set of nodes.
+
+  WaysX *waysx The set of ways.
+
+  RelationsX *relationsx The set of relations.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ProcessErrorLogs(ErrorLogsX *errorlogsx,NodesX *nodesx,WaysX *waysx,RelationsX *relationsx)
+{
+ int oldfd,newfd;
+ uint32_t offset=0;
+ int nerrorlogobjects=0;
+ int finished;
+ ErrorLogObject errorlogobjects[8];
+
+ /* Re-index the nodes, ways and relations */
+
+ printf_first("Re-indexing the Data: Nodes=0 Ways=0 Route-Relations=0 Turn-Relations=0");
+
+ reindex_nodes(nodesx);
+
+ printf_middle("Re-indexing the Data: Nodes=%"Pindex_t" Ways=0 Route-Relations=0 Turn-Relations=0",nodesx->number);
+
+ reindex_ways(waysx);
+
+ printf_middle("Re-indexing the Data: Nodes=%"Pindex_t" Ways=%"Pindex_t" Route-Relations=0 Turn-Relations=0",nodesx->number,waysx->number);
+
+ reindex_relations(relationsx);
+
+ printf_last("Re-indexed the Data: Nodes=%"Pindex_t" Ways=%"Pindex_t" Route-Relations=%"Pindex_t" Turn-Relations=%"Pindex_t,nodesx->number,waysx->number,relationsx->rrnumber,relationsx->trnumber);
+
+
+ /* Print the start message */
+
+ printf_first("Calculating Coordinates: Errors=0");
+
+ /* Map into memory / open the files */
+
+#if !SLIM
+ nodesx->data=MapFile(nodesx->filename);
+#else
+ nodesx->fd=SlimMapFile(nodesx->filename);
+
+ InvalidateNodeXCache(nodesx->cache);
+#endif
+
+ waysx->fd=ReOpenFileBuffered(waysx->filename);
+ relationsx->rrfd=ReOpenFileBuffered(relationsx->rrfilename);
+ relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename);
+
+ /* Open the binary log file read-only and a new file writeable */
+
+ oldfd=ReOpenFileBuffered(errorbinfilename);
+
+ DeleteFile(errorbinfilename);
+
+ newfd=OpenFileBufferedNew(errorbinfilename);
+
+ /* Loop through the file and merge the raw data into coordinates */
+
+ errorlogsx->number=0;
+
+ do
+   {
+    ErrorLogObject errorlogobject;
+
+    finished=ReadFileBuffered(oldfd,&errorlogobject,sizeof(ErrorLogObject));
+
+    if(finished)
+       errorlogobject.offset=SizeFile(errorlogfilename);
+
+    if(offset!=errorlogobject.offset)
+      {
+       ErrorLogX errorlogx;
+       latlong_t errorlat=NO_LATLONG,errorlon=NO_LATLONG;
+
+       /* Calculate suitable coordinates */
+
+       if(nerrorlogobjects==1)
+         {
+          if(errorlogobjects[0].type=='N')
+            {
+             node_t node=(node_t)errorlogobjects[0].id;
+
+             lookup_lat_long_node(nodesx,node,&errorlat,&errorlon);
+            }
+          else if(errorlogobjects[0].type=='W')
+            {
+             way_t way=(way_t)errorlogobjects[0].id;
+
+             lookup_lat_long_way(waysx,nodesx,way,&errorlat,&errorlon,errorlogsx->number);
+            }
+          else if(errorlogobjects[0].type=='R')
+            {
+             relation_t relation=(relation_t)errorlogobjects[0].type;
+
+             lookup_lat_long_relation(relationsx,waysx,nodesx,relation,&errorlat,&errorlon,errorlogsx->number);
+            }
+         }
+       else
+         {
+          latlong_t latitude[8],longitude[8];
+          int i;
+          int ncoords=0,nnodes=0,nways=0,nrelations=0;
+
+          for(i=0;i<nerrorlogobjects;i++)
+            {
+             if(errorlogobjects[i].type=='N')
+               {
+                node_t node=(node_t)errorlogobjects[i].id;
+
+                if(lookup_lat_long_node(nodesx,node,&latitude[ncoords],&longitude[ncoords]))
+                   ncoords++;
+
+                nnodes++;
+               }
+             else if(errorlogobjects[i].type=='W')
+                nways++;
+             else if(errorlogobjects[i].type=='R')
+                nrelations++;
+            }
+
+          if(nways==0 && nrelations==0) /* only nodes */
+             ;
+          else if(ncoords)      /* some good nodes, possibly ways and/or relations */
+             ;
+          else if(nways)        /* no good nodes, possibly some good ways */
+            {
+             for(i=0;i<nerrorlogobjects;i++)
+                if(errorlogobjects[i].type=='W')
+                  {
+                   way_t way=(way_t)errorlogobjects[i].id;
+
+                   if(lookup_lat_long_way(waysx,nodesx,way,&latitude[ncoords],&longitude[ncoords],errorlogsx->number))
+                      ncoords++;
+                  }
+            }
+
+          if(nrelations==0) /* only nodes and/or ways */
+             ;
+          else if(ncoords)  /* some good nodes and/or ways, possibly relations */
+             ;
+          else /* if(nrelations) */
+            {
+             for(i=0;i<nerrorlogobjects;i++)
+                if(errorlogobjects[i].type=='R')
+                  {
+                   relation_t relation=(relation_t)errorlogobjects[i].id;
+
+                   if(lookup_lat_long_relation(relationsx,waysx,nodesx,relation,&latitude[ncoords],&longitude[ncoords],errorlogsx->number))
+                      ncoords++;
+                  }
+            }
+
+          if(ncoords)
+            {
+             errorlat=0;
+             errorlon=0;
+
+             for(i=0;i<ncoords;i++)
+               {
+                errorlat+=latitude[i];
+                errorlon+=longitude[i];
+               }
+
+             errorlat/=ncoords;
+             errorlon/=ncoords;
+            }
+          else
+            {
+             errorlat=NO_LATLONG;
+             errorlon=NO_LATLONG;
+            }
+         }
+
+       /* Write to file */
+
+       errorlogx.offset=offset;
+       errorlogx.length=errorlogobject.offset-offset;
+
+       errorlogx.latitude =errorlat;
+       errorlogx.longitude=errorlon;
+
+       WriteFileBuffered(newfd,&errorlogx,sizeof(ErrorLogX));
+
+       errorlogsx->number++;
+
+       offset=errorlogobject.offset;
+       nerrorlogobjects=0;
+
+       if(!(errorlogsx->number%10000))
+          printf_middle("Calculating Coordinates: Errors=%"Pindex_t,errorlogsx->number);
+      }
+
+    /* Store for later */
+
+    logassert(nerrorlogobjects<8,"Too many error log objects for one error message."); /* Only a limited amount of information stored. */
+
+    errorlogobjects[nerrorlogobjects]=errorlogobject;
+
+    nerrorlogobjects++;
+   }
+ while(!finished);
+
+ /* Unmap from memory / close the files */
+
+#if !SLIM
+ nodesx->data=UnmapFile(nodesx->data);
+#else
+ nodesx->fd=SlimUnmapFile(nodesx->fd);
+#endif
+
+ waysx->fd=CloseFileBuffered(waysx->fd);
+ relationsx->rrfd=CloseFileBuffered(relationsx->rrfd);
+ relationsx->trfd=CloseFileBuffered(relationsx->trfd);
+
+ CloseFileBuffered(oldfd);
+ CloseFileBuffered(newfd);
+
+ /* Print the final message */
+
+ printf_last("Calculated Coordinates: Errors=%"Pindex_t,errorlogsx->number);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Re-index the nodes that were kept.
+
+  NodesX *nodesx The set of nodes to process (contains the filename and number of nodes).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void reindex_nodes(NodesX *nodesx)
+{
+ int fd;
+ index_t index=0;
+ NodeX nodex;
+
+ nodesx->number=nodesx->knumber;
+
+ nodesx->idata=(node_t*)malloc(nodesx->number*sizeof(node_t));
+
+ /* Get the node id for each node in the file. */
+
+ fd=ReOpenFileBuffered(nodesx->filename);
+
+ while(!ReadFileBuffered(fd,&nodex,sizeof(NodeX)))
+   {
+    nodesx->idata[index]=nodex.id;
+
+    index++;
+   }
+
+ CloseFileBuffered(fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Re-index the ways that were kept.
+
+  WaysX *waysx The set of ways to process (contains the filename and number of ways).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void reindex_ways(WaysX *waysx)
+{
+ FILESORT_VARINT waysize;
+ int fd;
+ off_t position=0;
+ index_t index=0;
+
+ waysx->number=waysx->knumber;
+
+ waysx->idata=(way_t*)malloc(waysx->number*sizeof(way_t));
+ waysx->odata=(off_t*)malloc(waysx->number*sizeof(off_t));
+
+ /* Get the way id and the offset for each way in the file */
+
+ fd=ReOpenFileBuffered(waysx->filename);
+
+ while(!ReadFileBuffered(fd,&waysize,FILESORT_VARSIZE))
+   {
+    WayX wayx;
+
+    ReadFileBuffered(fd,&wayx,sizeof(WayX));
+
+    waysx->idata[index]=wayx.id;
+    waysx->odata[index]=position+FILESORT_VARSIZE+sizeof(WayX);
+
+    index++;
+
+    SkipFileBuffered(fd,waysize-sizeof(WayX));
+
+    position+=waysize+FILESORT_VARSIZE;
+   }
+
+ CloseFileBuffered(fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Re-index the relations that were kept.
+
+  RelationsX *relationsx The set of relations to process (contains the filenames and numbers of relations).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void reindex_relations(RelationsX *relationsx)
+{
+ FILESORT_VARINT relationsize;
+ int fd;
+ off_t position=0;
+ index_t index;
+ TurnRelX turnrelx;
+
+ /* Route relations */
+
+ relationsx->rrnumber=relationsx->rrknumber;
+
+ relationsx->rridata=(relation_t*)malloc(relationsx->rrnumber*sizeof(relation_t));
+ relationsx->rrodata=(off_t*)malloc(relationsx->rrnumber*sizeof(off_t));
+
+ /* Get the relation id and the offset for each relation in the file */
+
+ fd=ReOpenFileBuffered(relationsx->rrfilename);
+
+ index=0;
+
+ while(!ReadFileBuffered(fd,&relationsize,FILESORT_VARSIZE))
+   {
+    RouteRelX routerelx;
+
+    ReadFileBuffered(fd,&routerelx,sizeof(RouteRelX));
+
+    relationsx->rridata[index]=routerelx.id;
+    relationsx->rrodata[index]=position+FILESORT_VARSIZE+sizeof(RouteRelX);
+
+    index++;
+
+    SkipFileBuffered(fd,relationsize-sizeof(RouteRelX));
+
+    position+=relationsize+FILESORT_VARSIZE;
+   }
+
+ CloseFileBuffered(fd);
+
+
+ /* Turn relations */
+
+ relationsx->trnumber=relationsx->trknumber;
+
+ relationsx->tridata=(relation_t*)malloc(relationsx->trnumber*sizeof(relation_t));
+
+ /* Get the relation id and the offset for each relation in the file */
+
+ fd=ReOpenFileBuffered(relationsx->trfilename);
+
+ index=0;
+
+ while(!ReadFileBuffered(fd,&turnrelx,sizeof(TurnRelX)))
+   {
+    relationsx->tridata[index]=turnrelx.id;
+
+    index++;
+   }
+
+ CloseFileBuffered(fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Lookup a node's latitude and longitude.
+
+  int lookup_lat_long_node Returns 1 if a node was found.
+
+  NodesX *nodesx The set of nodes to use.
+
+  node_t node The node number.
+
+  latlong_t *latitude Returns the latitude.
+
+  latlong_t *longitude Returns the longitude.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int lookup_lat_long_node(NodesX *nodesx,node_t node,latlong_t *latitude,latlong_t *longitude)
+{
+ index_t index=IndexNodeX(nodesx,node);
+
+ if(index==NO_NODE)
+    return 0;
+ else
+   {
+    NodeX *nodex=LookupNodeX(nodesx,index,1);
+
+    *latitude =nodex->latitude;
+    *longitude=nodex->longitude;
+
+    return 1;
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Lookup a way's latitude and longitude.
+
+  int lookup_lat_long_way Returns 1 if a way was found.
+
+  WaysX *waysx The set of ways to use.
+
+  NodesX *nodesx The set of nodes to use.
+
+  way_t way The way number.
+
+  latlong_t *latitude Returns the latitude.
+
+  latlong_t *longitude Returns the longitude.
+
+  index_t error The index of the error in the complete set of errors.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int lookup_lat_long_way(WaysX *waysx,NodesX *nodesx,way_t way,latlong_t *latitude,latlong_t *longitude,index_t error)
+{
+ index_t index=IndexWayX(waysx,way);
+
+ if(index==NO_WAY)
+    return 0;
+ else
+   {
+    int count=1;
+    off_t offset=waysx->odata[index];
+    node_t node1,node2,prevnode,node;
+    latlong_t latitude1,longitude1,latitude2,longitude2;
+
+    SeekFileBuffered(waysx->fd,offset);
+
+    /* Choose a random pair of adjacent nodes */
+
+    if(ReadFileBuffered(waysx->fd,&node1,sizeof(node_t)) || node1==NO_NODE_ID)
+       return 0;
+
+    if(ReadFileBuffered(waysx->fd,&node2,sizeof(node_t)) || node2==NO_NODE_ID)
+       return lookup_lat_long_node(nodesx,node1,latitude,longitude);
+
+    prevnode=node2;
+
+    while(!ReadFileBuffered(waysx->fd,&node,sizeof(node_t)) && node!=NO_NODE_ID)
+      {
+       count++;
+
+       if((error%count)==0)     /* A 1/count chance */
+         {
+          node1=prevnode;
+          node2=node;
+         }
+
+       prevnode=node;
+      }
+
+    if(!lookup_lat_long_node(nodesx,node1,&latitude1,&longitude1))
+       return lookup_lat_long_node(nodesx,node2,latitude,longitude);
+
+    if(!lookup_lat_long_node(nodesx,node2,&latitude2,&longitude2))
+       return lookup_lat_long_node(nodesx,node1,latitude,longitude);
+
+    *latitude =(latitude1 +latitude2 )/2;
+    *longitude=(longitude1+longitude2)/2;
+
+    return 1;
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Lookup a relation's latitude and longitude.
+
+  int lookup_lat_long_relation Returns 1 if a relation was found.
+
+  RelationsX *relationsx The set of relations to use.
+
+  WaysX *waysx The set of ways to use.
+
+  NodesX *nodesx The set of nodes to use.
+
+  relation_t relation The relation number.
+
+  latlong_t *latitude Returns the latitude.
+
+  latlong_t *longitude Returns the longitude.
+
+  index_t error The index of the error in the complete set of errors.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int lookup_lat_long_relation(RelationsX *relationsx,WaysX *waysx,NodesX *nodesx,relation_t relation,latlong_t *latitude,latlong_t *longitude,index_t error)
+{
+ index_t index=IndexRouteRelX(relationsx,relation);
+
+ if(index==NO_RELATION)
+   {
+    index=IndexTurnRelX(relationsx,relation);
+
+    if(index==NO_RELATION)
+       return 0;
+    else
+      {
+       TurnRelX turnrelx;
+
+       SeekFileBuffered(relationsx->trfd,index*sizeof(TurnRelX));
+       ReadFileBuffered(relationsx->trfd,&turnrelx,sizeof(TurnRelX));
+
+       if(lookup_lat_long_node(nodesx,turnrelx.via,latitude,longitude))
+          return 1;
+
+       if(lookup_lat_long_way(waysx,nodesx,turnrelx.from,latitude,longitude,error))
+          return 1;
+
+       if(lookup_lat_long_way(waysx,nodesx,turnrelx.to,latitude,longitude,error))
+          return 1;
+
+       return 0;
+      }
+   }
+ else
+   {
+    int count;
+    off_t offset=relationsx->rrodata[index];
+    node_t node=NO_NODE_ID,tempnode;
+    way_t way=NO_WAY_ID,tempway;
+    relation_t relation=NO_RELATION_ID,temprelation;
+
+    SeekFileBuffered(relationsx->rrfd,offset);
+
+    /* Choose a random node */
+
+    count=0;
+
+    while(!ReadFileBuffered(relationsx->rrfd,&tempnode,sizeof(node_t)) && tempnode!=NO_NODE_ID)
+      {
+       count++;
+
+       if((error%count)==0)     /* A 1/count chance */
+          node=tempnode;
+      }
+
+    if(lookup_lat_long_node(nodesx,node,latitude,longitude))
+       return 1;
+
+    /* Choose a random way */
+
+    count=0;
+
+    while(!ReadFileBuffered(relationsx->rrfd,&tempway,sizeof(way_t)) && tempway!=NO_WAY_ID)
+      {
+       count++;
+
+       if((error%count)==0)     /* A 1/count chance */
+          way=tempway;
+      }
+
+    if(lookup_lat_long_way(waysx,nodesx,way,latitude,longitude,error))
+       return 1;
+
+    /* Choose a random relation */
+
+    count=0;
+
+    while(!ReadFileBuffered(relationsx->rrfd,&temprelation,sizeof(relation_t)) && temprelation!=NO_RELATION_ID)
+      {
+       count++;
+
+       if((error%count)==0)     /* A 1/count chance */
+          relation=temprelation;
+      }
+
+    return lookup_lat_long_relation(relationsx,waysx,nodesx,relation,latitude,longitude,error);
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the error logs geographically.
+
+  ErrorLogsX *errorlogsx The set of error logs to sort.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SortErrorLogsGeographically(ErrorLogsX *errorlogsx)
+{
+ int oldfd,newfd;
+ ll_bin_t lat_min_bin,lat_max_bin,lon_min_bin,lon_max_bin;
+
+ /* Print the start message */
+
+ printf_first("Sorting Errors Geographically");
+
+ /* Work out the range of data */
+
+ lat_min=radians_to_latlong( 2);
+ lat_max=radians_to_latlong(-2);
+ lon_min=radians_to_latlong( 4);
+ lon_max=radians_to_latlong(-4);
+
+ /* Re-open the file read-only and a new file writeable */
+
+ oldfd=ReOpenFileBuffered(errorbinfilename);
+
+ DeleteFile(errorbinfilename);
+
+ newfd=OpenFileBufferedNew(errorbinfilename);
+
+ /* Sort errors geographically */
+
+ filesort_fixed(oldfd,newfd,sizeof(ErrorLogX),NULL,
+                                              (int (*)(const void*,const void*))sort_by_lat_long,
+                                              (int (*)(void*,index_t))measure_lat_long);
+
+ /* Close the files */
+
+ CloseFileBuffered(oldfd);
+ CloseFileBuffered(newfd);
+
+ /* Work out the number of bins */
+
+ lat_min_bin=latlong_to_bin(lat_min);
+ lon_min_bin=latlong_to_bin(lon_min);
+ lat_max_bin=latlong_to_bin(lat_max);
+ lon_max_bin=latlong_to_bin(lon_max);
+
+ errorlogsx->latzero=lat_min_bin;
+ errorlogsx->lonzero=lon_min_bin;
+
+ errorlogsx->latbins=(lat_max_bin-lat_min_bin)+1;
+ errorlogsx->lonbins=(lon_max_bin-lon_min_bin)+1;
+
+ /* Print the final message */
+
+ printf_last("Sorted Errors Geographically: Errors=%"Pindex_t,errorlogsx->number);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the errors into latitude and longitude order (first by longitude bin
+  number, then by latitude bin number and then by exact longitude and then by
+  exact latitude).
+
+  int sort_by_lat_long Returns the comparison of the latitude and longitude fields.
+
+  ErrorLogX *a The first error location.
+
+  ErrorLogX *b The second error location.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int sort_by_lat_long(ErrorLogX *a,ErrorLogX *b)
+{
+ ll_bin_t a_lon=latlong_to_bin(a->longitude);
+ ll_bin_t b_lon=latlong_to_bin(b->longitude);
+
+ if(a_lon<b_lon)
+    return(-1);
+ else if(a_lon>b_lon)
+    return(1);
+ else
+   {
+    ll_bin_t a_lat=latlong_to_bin(a->latitude);
+    ll_bin_t b_lat=latlong_to_bin(b->latitude);
+
+    if(a_lat<b_lat)
+       return(-1);
+    else if(a_lat>b_lat)
+       return(1);
+    else
+      {
+       if(a->longitude<b->longitude)
+          return(-1);
+       else if(a->longitude>b->longitude)
+          return(1);
+       else
+         {
+          if(a->latitude<b->latitude)
+             return(-1);
+          else if(a->latitude>b->latitude)
+             return(1);
+         }
+
+       return(FILESORT_PRESERVE_ORDER(a,b));
+      }
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Measure the extent of the data.
+
+  int measure_lat_long Return 1 if the value is to be kept, otherwise 0.
+
+  ErrorLogX *errorlogx The error location.
+
+  index_t index The number of sorted error locations that have already been written to the output file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int measure_lat_long(ErrorLogX *errorlogx,index_t index)
+{
+ if(errorlogx->latitude!=NO_LATLONG)
+   {
+    if(errorlogx->latitude<lat_min)
+       lat_min=errorlogx->latitude;
+    if(errorlogx->latitude>lat_max)
+       lat_max=errorlogx->latitude;
+    if(errorlogx->longitude<lon_min)
+       lon_min=errorlogx->longitude;
+    if(errorlogx->longitude>lon_max)
+       lon_max=errorlogx->longitude;
+   }
+
+ return(1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Save the binary error log.
+
+  ErrorLogsX *errorlogsx The set of error logs to write.
+
+  char *filename The name of the final file to write.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SaveErrorLogs(ErrorLogsX *errorlogsx,char *filename)
+{
+ ErrorLogsFile errorlogsfile;
+ ErrorLogX errorlogx;
+ int oldfd,newfd;
+ ll_bin2_t latlonbin=0,maxlatlonbins;
+ index_t *offsets;
+ index_t number=0,number_geo=0,number_nongeo=0;
+ off_t size;
+
+ /* Print the start message */
+
+ printf_first("Writing Errors: Geographical=0 Non-geographical=0");
+
+ /* Allocate the memory for the geographical offsets array */
+
+ offsets=(index_t*)malloc((errorlogsx->latbins*errorlogsx->lonbins+1)*sizeof(index_t));
+
+ logassert(offsets,"Failed to allocate memory (try using slim mode?)"); /* Check malloc() worked */
+
+ latlonbin=0;
+
+ /* Re-open the file */
+
+ oldfd=ReOpenFileBuffered(errorbinfilename);
+
+ newfd=OpenFileBufferedNew(filename);
+
+ /* Write out the geographical errors */
+
+ SeekFileBuffered(newfd,sizeof(ErrorLogsFile)+(errorlogsx->latbins*errorlogsx->lonbins+1)*sizeof(index_t));
+
+ while(!ReadFileBuffered(oldfd,&errorlogx,sizeof(ErrorLogX)))
+   {
+    ErrorLog errorlog={0};
+    ll_bin_t latbin,lonbin;
+    ll_bin2_t llbin;
+
+    if(errorlogx.latitude==NO_LATLONG)
+       continue;
+
+    /* Create the ErrorLog */
+
+    errorlog.latoffset=latlong_to_off(errorlogx.latitude);
+    errorlog.lonoffset=latlong_to_off(errorlogx.longitude);
+
+    errorlog.offset=errorlogx.offset;
+    errorlog.length=errorlogx.length;
+
+    /* Work out the offsets */
+
+    latbin=latlong_to_bin(errorlogx.latitude )-errorlogsx->latzero;
+    lonbin=latlong_to_bin(errorlogx.longitude)-errorlogsx->lonzero;
+    llbin=lonbin*errorlogsx->latbins+latbin;
+
+    for(;latlonbin<=llbin;latlonbin++)
+       offsets[latlonbin]=number_geo;
+
+    /* Write the data */
+
+    WriteFileBuffered(newfd,&errorlog,sizeof(ErrorLog));
+
+    number_geo++;
+    number++;
+
+    if(!(number%10000))
+       printf_middle("Writing Errors: Geographical=%"Pindex_t" Non-geographical=%"Pindex_t,number_geo,number_nongeo);
+   }
+
+ /* Write out the non-geographical errors */
+
+ SeekFileBuffered(oldfd,0);
+
+ while(!ReadFileBuffered(oldfd,&errorlogx,sizeof(ErrorLogX)))
+   {
+    ErrorLog errorlog={0};
+
+    if(errorlogx.latitude!=NO_LATLONG)
+       continue;
+
+    /* Create the ErrorLog */
+
+    errorlog.latoffset=0;
+    errorlog.lonoffset=0;
+
+    errorlog.offset=errorlogx.offset;
+    errorlog.length=errorlogx.length;
+
+    /* Write the data */
+
+    WriteFileBuffered(newfd,&errorlog,sizeof(ErrorLog));
+
+    number_nongeo++;
+    number++;
+
+    if(!(number%10000))
+       printf_middle("Writing Errors: Geographical=%"Pindex_t" Non-geographical=%"Pindex_t,number_geo,number_nongeo);
+   }
+
+ /* Close the input file */
+
+ CloseFileBuffered(oldfd);
+
+ DeleteFile(errorbinfilename);
+
+ /* Append the text from the log file */
+
+ size=SizeFile(errorlogfilename);
+
+ oldfd=ReOpenFileBuffered(errorlogfilename);
+
+ while(size)
+   {
+    int i;
+    char buffer[4096];
+    off_t chunksize=(size>sizeof(buffer)?sizeof(buffer):size);
+
+    ReadFileBuffered(oldfd,buffer,chunksize);
+
+    for(i=0;i<chunksize;i++)
+       if(buffer[i]=='\n')
+          buffer[i]=0;
+
+    WriteFileBuffered(newfd,buffer,chunksize);
+
+    size-=chunksize;
+   }
+
+ CloseFileBuffered(oldfd);
+
+ /* Finish off the offset indexing and write them out */
+
+ maxlatlonbins=errorlogsx->latbins*errorlogsx->lonbins;
+
+ for(;latlonbin<=maxlatlonbins;latlonbin++)
+    offsets[latlonbin]=number_geo;
+
+ SeekFileBuffered(newfd,sizeof(ErrorLogsFile));
+ WriteFileBuffered(newfd,offsets,(errorlogsx->latbins*errorlogsx->lonbins+1)*sizeof(index_t));
+
+ free(offsets);
+
+ /* Write out the header structure */
+
+ errorlogsfile.number       =number;
+ errorlogsfile.number_geo   =number_geo;
+ errorlogsfile.number_nongeo=number_nongeo;
+
+ errorlogsfile.latbins=errorlogsx->latbins;
+ errorlogsfile.lonbins=errorlogsx->lonbins;
+
+ errorlogsfile.latzero=errorlogsx->latzero;
+ errorlogsfile.lonzero=errorlogsx->lonzero;
+
+ SeekFileBuffered(newfd,0);
+ WriteFileBuffered(newfd,&errorlogsfile,sizeof(ErrorLogsFile));
+
+ CloseFileBuffered(newfd);
+
+ /* Print the final message */
+
+ printf_last("Wrote Errors: Geographical=%"Pindex_t" Non-geographical=%"Pindex_t,number_geo,number_nongeo);
+}
diff --git a/src/errorlogx.h b/src/errorlogx.h
new file mode 100644
index 0000000..21bbf6e
--- /dev/null
+++ b/src/errorlogx.h
@@ -0,0 +1,70 @@
+/***************************************
+ Header file for error log file data types and processing function prototypes.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#ifndef ERRORLOGX_H
+#define ERRORLOGX_H    /*+ To stop multiple inclusions. +*/
+
+#include <stdint.h>
+
+#include "types.h"
+#include "typesx.h"
+
+
+/*+ A structure containing information for an error message during processing. +*/
+typedef struct _ErrorLogX
+{
+ latlong_t latitude;         /*+ The error message latitude. +*/
+ latlong_t longitude;        /*+ The error message longitude. +*/
+
+ uint32_t  offset;           /*+ The offset of the error message from the beginning of the text file. +*/
+ uint32_t  length;           /*+ The length of the error message in the text file. +*/
+}
+ ErrorLogX;
+
+
+/*+ A structure containing a set of error logs (memory format). +*/
+typedef struct _ErrorLogsX
+{
+ index_t   number;              /*+ The number of error logs. +*/
+
+ index_t   latbins;             /*+ The number of bins containing latitude. +*/
+ index_t   lonbins;             /*+ The number of bins containing longitude. +*/
+
+ ll_bin_t  latzero;             /*+ The bin number of the furthest south bin. +*/
+ ll_bin_t  lonzero;             /*+ The bin number of the furthest west bin. +*/
+}
+ ErrorLogsX;
+
+
+/* Error log processing functions in errorlogx.c */
+
+ErrorLogsX *NewErrorLogList(void);
+void FreeErrorLogList(ErrorLogsX *errorlogsx);
+
+void ProcessErrorLogs(ErrorLogsX *errorlogsx,NodesX *nodesx,WaysX *waysx,RelationsX *relationsx);
+
+void SortErrorLogsGeographically(ErrorLogsX *errorlogsx);
+
+void SaveErrorLogs(ErrorLogsX *errorlogsx,char *filename);
+
+
+#endif /* ERRORLOGX_H */
diff --git a/src/fakes.c b/src/fakes.c
index 7076834..9fb0cef 100644
--- a/src/fakes.c
+++ b/src/fakes.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -89,19 +89,19 @@ index_t CreateFakes(Nodes *nodes,Segments *segments,int point,Segment *segmentp,
 
  /* Check if we are actually close enough to an existing node */
 
- if(dist1<km_to_distance(MINSEGMENT) && dist2>km_to_distance(MINSEGMENT))
+ if(dist1<=km_to_distance(MINSEGMENT) && dist2>km_to_distance(MINSEGMENT))
    {
     prevpoint=point;
     return(node1);
    }
 
- if(dist2<km_to_distance(MINSEGMENT) && dist1>km_to_distance(MINSEGMENT))
+ if(dist2<=km_to_distance(MINSEGMENT) && dist1>km_to_distance(MINSEGMENT))
    {
     prevpoint=point;
     return(node2);
    }
 
- if(dist1<km_to_distance(MINSEGMENT) && dist2<km_to_distance(MINSEGMENT))
+ if(dist1<=km_to_distance(MINSEGMENT) && dist2<=km_to_distance(MINSEGMENT))
    {
     prevpoint=point;
 
@@ -115,16 +115,16 @@ index_t CreateFakes(Nodes *nodes,Segments *segments,int point,Segment *segmentp,
 
  fakenode=NODE_FAKE+point;
 
- GetLatLong(nodes,node1,&lat1,&lon1);
- GetLatLong(nodes,node2,&lat2,&lon2);
+ GetLatLong(nodes,node1,NULL,&lat1,&lon1);
+ GetLatLong(nodes,node2,NULL,&lat2,&lon2);
 
  if(lat1>3 && lat2<-3)
     lat2+=2*M_PI;
  else if(lat1<-3 && lat2>3)
     lat1+=2*M_PI;
 
- fake_lat[point]=lat1+(lat2-lat1)*(double)dist1/(double)(dist1+dist2);
- fake_lon[point]=lon1+(lon2-lon1)*(double)dist1/(double)(dist1+dist2);
+ fake_lat[point]=lat1+(lat2-lat1)*(double)dist1/(double)(dist1+dist2); /* (dist1+dist2) must be > 0 */
+ fake_lon[point]=lon1+(lon2-lon1)*(double)dist1/(double)(dist1+dist2); /* (dist1+dist2) must be > 0 */
 
  if(fake_lat[point]>M_PI) fake_lat[point]-=2*M_PI;
 
diff --git a/src/filedumper.c b/src/filedumper.c
index c289aea..6e614a0 100644
--- a/src/filedumper.c
+++ b/src/filedumper.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -33,6 +33,7 @@
 #include "segments.h"
 #include "ways.h"
 #include "relations.h"
+#include "errorlog.h"
 
 #include "files.h"
 #include "visualiser.h"
@@ -45,6 +46,7 @@ static void print_node(Nodes *nodes,index_t item);
 static void print_segment(Segments *segments,index_t item);
 static void print_way(Ways *ways,index_t item);
 static void print_turn_relation(Relations *relations,index_t item,Segments *segments,Nodes *nodes);
+static void print_errorlog(ErrorLogs *errorlogs,index_t item);
 
 static void print_head_osm(int coordcount,double latmin,double latmax,double lonmin,double lonmax);
 static void print_region_osm(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,
@@ -54,6 +56,11 @@ static void print_segment_osm(Segments *segments,index_t item,Ways *ways);
 static void print_turn_relation_osm(Relations *relations,index_t item,Segments *segments,Nodes *nodes);
 static void print_tail_osm(void);
 
+static void print_node_visualiser(Nodes *nodes,index_t item);
+static void print_segment_visualiser(Segments *segments,index_t item,Ways *ways);
+static void print_turn_relation_visualiser(Relations *relations,index_t item,Segments *segments,Nodes *nodes);
+static void print_errorlog_visualiser(ErrorLogs *errorlogs,index_t item);
+
 static char *RFC822Date(time_t t);
 
 static void print_usage(int detail,const char *argerr,const char *err);
@@ -69,15 +76,17 @@ int main(int argc,char** argv)
  Segments *OSMSegments;
  Ways     *OSMWays;
  Relations*OSMRelations;
+ ErrorLogs*OSMErrorLogs=NULL;
  int       arg;
  char     *dirname=NULL,*prefix=NULL;
- char     *nodes_filename,*segments_filename,*ways_filename,*relations_filename;
+ char     *nodes_filename,*segments_filename,*ways_filename,*relations_filename,*errorlogs_filename;
  int       option_statistics=0;
  int       option_visualiser=0,coordcount=0;
  double    latmin=0,latmax=0,lonmin=0,lonmax=0;
  char     *option_data=NULL;
  int       option_dump=0;
  int       option_dump_osm=0,option_no_super=0;
+ int       option_dump_visualiser=0;
 
  /* Parse the command line arguments */
 
@@ -97,6 +106,8 @@ int main(int argc,char** argv)
        option_dump=1;
     else if(!strcmp(argv[arg],"--dump-osm"))
        option_dump_osm=1;
+    else if(!strcmp(argv[arg],"--dump-visualiser"))
+       option_dump_visualiser=1;
     else if(!strncmp(argv[arg],"--latmin",8) && argv[arg][8]=='=')
       {latmin=degrees_to_radians(atof(&argv[arg][9]));coordcount++;}
     else if(!strncmp(argv[arg],"--latmax",8) && argv[arg][8]=='=')
@@ -117,12 +128,14 @@ int main(int argc,char** argv)
        ;
     else if(!strncmp(argv[arg],"--turn-relation=",16))
        ;
+    else if(!strncmp(argv[arg],"--errorlog=",11))
+       ;
     else
        print_usage(0,argv[arg],NULL);
    }
 
- if((option_statistics + option_visualiser + option_dump + option_dump_osm)!=1)
-    print_usage(0,NULL,"Must choose --visualiser, --statistics, --dump or --dump-osm.");
+ if((option_statistics + option_visualiser + option_dump + option_dump_osm + option_dump_visualiser)!=1)
+    print_usage(0,NULL,"Must choose --visualiser, --statistics, --dump, --dump-osm or --dump-visualiser.");
 
  /* Load in the data - Note: No error checking because Load*List() will call exit() in case of an error. */
 
@@ -134,12 +147,18 @@ int main(int argc,char** argv)
 
  OSMRelations=LoadRelationList(relations_filename=FileName(dirname,prefix,"relations.mem"));
 
+ if(ExistsFile(errorlogs_filename=FileName(dirname,prefix,"errorlogs.mem")))
+    OSMErrorLogs=LoadErrorLogs(errorlogs_filename);
+ else
+    errorlogs_filename=NULL;
+
  /* Write out the visualiser data */
 
  if(option_visualiser)
    {
     Highway highway;
     Transport transport;
+    Property property;
 
     if(coordcount!=4)
        print_usage(0,NULL,"The --visualiser option must have --latmin, --latmax, --lonmin, --lonmax.\n");
@@ -171,6 +190,10 @@ int main(int argc,char** argv)
        OutputWidthLimits(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"length"))
        OutputLengthLimits(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
+    else if(!strncmp(option_data,"property",8) && option_data[8]=='-' && (property=PropertyType(option_data+9))!=Property_None)
+       OutputProperty(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax,property);
+    else if(!strcmp(option_data,"errorlogs"))
+       OutputErrorLog(OSMErrorLogs,latmin,latmax,lonmin,lonmax);
     else
        print_usage(0,option_data,NULL);
    }
@@ -189,28 +212,37 @@ int main(int argc,char** argv)
 
     stat(nodes_filename,&buf);
 
-    printf("'%s%snodes.mem'     - %9lld Bytes\n",prefix?prefix:"",prefix?"-":"",(long long)buf.st_size);
+    printf("'%s%snodes.mem'     - %9"PRIu64" Bytes\n",prefix?prefix:"",prefix?"-":"",(uint64_t)buf.st_size);
     printf("%s\n",RFC822Date(buf.st_mtime));
     printf("\n");
 
     stat(segments_filename,&buf);
 
-    printf("'%s%ssegments.mem'  - %9lld Bytes\n",prefix?prefix:"",prefix?"-":"",(long long)buf.st_size);
+    printf("'%s%ssegments.mem'  - %9"PRIu64" Bytes\n",prefix?prefix:"",prefix?"-":"",(uint64_t)buf.st_size);
     printf("%s\n",RFC822Date(buf.st_mtime));
     printf("\n");
 
     stat(ways_filename,&buf);
 
-    printf("'%s%sways.mem'      - %9lld Bytes\n",prefix?prefix:"",prefix?"-":"",(long long)buf.st_size);
+    printf("'%s%sways.mem'      - %9"PRIu64" Bytes\n",prefix?prefix:"",prefix?"-":"",(uint64_t)buf.st_size);
     printf("%s\n",RFC822Date(buf.st_mtime));
     printf("\n");
 
     stat(relations_filename,&buf);
 
-    printf("'%s%srelations.mem' - %9lld Bytes\n",prefix?prefix:"",prefix?"-":"",(long long)buf.st_size);
+    printf("'%s%srelations.mem' - %9"PRIu64" Bytes\n",prefix?prefix:"",prefix?"-":"",(uint64_t)buf.st_size);
     printf("%s\n",RFC822Date(buf.st_mtime));
     printf("\n");
 
+    if(errorlogs_filename)
+      {
+       stat(errorlogs_filename,&buf);
+
+       printf("'%s%serrorlogs.mem' - %9"PRIu64" Bytes\n",prefix?prefix:"",prefix?"-":"",(uint64_t)buf.st_size);
+       printf("%s\n",RFC822Date(buf.st_mtime));
+       printf("\n");
+      }
+
     /* Examine the nodes */
 
     printf("Nodes\n");
@@ -269,6 +301,26 @@ int main(int argc,char** argv)
 
     printf("sizeof(TurnRelation)=%9lu Bytes\n",(unsigned long)sizeof(TurnRelation));
     printf("Number              =%9"Pindex_t"\n",OSMRelations->file.trnumber);
+
+    if(errorlogs_filename)
+      {
+       printf("\n");
+       printf("Error Logs\n");
+       printf("----------\n");
+       printf("\n");
+
+       printf("Number(total)           =%9"Pindex_t"\n",OSMErrorLogs->file.number);
+       printf("Number(geographical)    =%9"Pindex_t"\n",OSMErrorLogs->file.number_geo);
+       printf("Number(non-geographical)=%9"Pindex_t"\n",OSMErrorLogs->file.number_nongeo);
+
+       printf("\n");
+       stat(errorlogs_filename,&buf);
+#if !SLIM
+       printf("Total strings=%9lu Bytes\n",(unsigned long)buf.st_size-(unsigned long)(OSMErrorLogs->strings-(char*)OSMErrorLogs->data));
+#else
+       printf("Total strings=%9lu Bytes\n",(unsigned long)buf.st_size-(unsigned long)OSMErrorLogs->stringsoffset);
+#endif
+      }
    }
 
  /* Print out internal data (in plain text format) */
@@ -287,7 +339,7 @@ int main(int argc,char** argv)
          {
           item=atoi(&argv[arg][7]);
 
-          if(item>=0 && item<OSMNodes->file.number)
+          if(item<OSMNodes->file.number)
              print_node(OSMNodes,item);
           else
              printf("Invalid node number; minimum=0, maximum=%"Pindex_t".\n",OSMNodes->file.number-1);
@@ -301,7 +353,7 @@ int main(int argc,char** argv)
          {
           item=atoi(&argv[arg][10]);
 
-          if(item>=0 && item<OSMSegments->file.number)
+          if(item<OSMSegments->file.number)
              print_segment(OSMSegments,item);
           else
              printf("Invalid segment number; minimum=0, maximum=%"Pindex_t".\n",OSMSegments->file.number-1);
@@ -315,7 +367,7 @@ int main(int argc,char** argv)
          {
           item=atoi(&argv[arg][6]);
 
-          if(item>=0 && item<OSMWays->file.number)
+          if(item<OSMWays->file.number)
              print_way(OSMWays,item);
           else
              printf("Invalid way number; minimum=0, maximum=%"Pindex_t".\n",OSMWays->file.number-1);
@@ -329,11 +381,25 @@ int main(int argc,char** argv)
          {
           item=atoi(&argv[arg][16]);
 
-          if(item>=0 && item<OSMRelations->file.trnumber)
+          if(item<OSMRelations->file.trnumber)
              print_turn_relation(OSMRelations,item,OSMSegments,OSMNodes);
           else
              printf("Invalid turn relation number; minimum=0, maximum=%"Pindex_t".\n",OSMRelations->file.trnumber-1);
          }
+       else if(!strcmp(argv[arg],"--errorlog=all"))
+         {
+          for(item=0;item<OSMErrorLogs->file.number;item++)
+             print_errorlog(OSMErrorLogs,item);
+         }
+       else if(!strncmp(argv[arg],"--errorlog=",11))
+         {
+          item=atoi(&argv[arg][11]);
+
+          if(item<OSMErrorLogs->file.number)
+             print_errorlog(OSMErrorLogs,item);
+          else
+             printf("Invalid error log number; minimum=0, maximum=%"Pindex_t".\n",OSMErrorLogs->file.number-1);
+         }
    }
 
  /* Print out internal data (in OSM XML format) */
@@ -365,6 +431,54 @@ int main(int argc,char** argv)
     print_tail_osm();
    }
 
+ /* Print out internal data (in HTML format for the visualiser) */
+
+ if(option_dump_visualiser)
+   {
+    index_t item;
+
+    if(!option_data)
+       print_usage(0,NULL,"The --dump-visualiser option must have --data.\n");
+
+    for(arg=1;arg<argc;arg++)
+       if(!strncmp(argv[arg],"--data=node",11))
+         {
+          item=atoi(&argv[arg][11]);
+
+          if(item<OSMNodes->file.number)
+             print_node_visualiser(OSMNodes,item);
+          else
+             printf("Invalid node number; minimum=0, maximum=%"Pindex_t".\n",OSMNodes->file.number-1);
+         }
+       else if(!strncmp(argv[arg],"--data=segment",14))
+         {
+          item=atoi(&argv[arg][14]);
+
+          if(item<OSMSegments->file.number)
+             print_segment_visualiser(OSMSegments,item,OSMWays);
+          else
+             printf("Invalid segment number; minimum=0, maximum=%"Pindex_t".\n",OSMSegments->file.number-1);
+         }
+       else if(!strncmp(argv[arg],"--data=turn-relation",20))
+         {
+          item=atoi(&argv[arg][20]);
+
+          if(item<OSMRelations->file.trnumber)
+             print_turn_relation_visualiser(OSMRelations,item,OSMSegments,OSMNodes);
+          else
+             printf("Invalid turn relation number; minimum=0, maximum=%"Pindex_t".\n",OSMRelations->file.trnumber-1);
+         }
+       else if(!strncmp(argv[arg],"--data=errorlog",15))
+         {
+          item=atoi(&argv[arg][15]);
+
+          if(item<OSMErrorLogs->file.number)
+             print_errorlog_visualiser(OSMErrorLogs,item);
+          else
+             printf("Invalid error log number; minimum=0, maximum=%"Pindex_t".\n",OSMErrorLogs->file.number-1);
+         }
+   }
+
  return(0);
 }
 
@@ -382,7 +496,7 @@ static void print_node(Nodes *nodes,index_t item)
  Node *nodep=LookupNode(nodes,item,1);
  double latitude,longitude;
 
- GetLatLong(nodes,item,&latitude,&longitude);
+ GetLatLong(nodes,item,nodep,&latitude,&longitude);
 
  printf("Node %"Pindex_t"\n",item);
  printf("  firstseg=%"Pindex_t"\n",nodep->firstseg);
@@ -390,6 +504,8 @@ static void print_node(Nodes *nodes,index_t item)
  printf("  allow=%02x (%s)\n",nodep->allow,AllowedNameList(nodep->allow));
  if(IsSuperNode(nodep))
     printf("  Super-Node\n");
+ if(nodep->flags & NODE_MINIRNDBT)
+    printf("  Mini-roundabout\n");
 }
 
 
@@ -506,6 +622,35 @@ static void print_turn_relation(Relations *relations,index_t item,Segments *segm
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Print out the contents of an error log from the routing database (as plain text).
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  index_t item The error log index to print.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_errorlog(ErrorLogs *errorlogs,index_t item)
+{
+ ErrorLog *errorlogp=LookupErrorLog(errorlogs,item,1);
+
+ printf("Error Log %"Pindex_t"\n",item);
+
+ if(item<errorlogs->file.number_geo)
+   {
+    double latitude,longitude;
+
+    GetErrorLogLatLong(errorlogs,item,errorlogp,&latitude,&longitude);
+
+    printf("  latoffset=%d lonoffset=%d (latitude=%.6f longitude=%.6f)\n",errorlogp->latoffset,errorlogp->lonoffset,radians_to_degrees(latitude),radians_to_degrees(longitude));
+   }
+ else
+    printf("  No geographical information\n");
+
+ printf("  '%s'\n",LookupErrorLogString(errorlogs,item));
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Print out a header in OSM XML format.
 
   int coordcount If true then include a bounding box.
@@ -599,7 +744,7 @@ static void print_region_osm(Nodes *nodes,Segments *segments,Ways *ways,Relation
                 double olat,olon;
                 index_t oitem=OtherNode(segmentp,item);
 
-                GetLatLong(nodes,oitem,&olat,&olon);
+                GetLatLong(nodes,oitem,NULL,&olat,&olon);
 
                 if(olat>latmin && olat<latmax && olon>lonmin && olon<lonmax)
                    if(item>oitem)
@@ -640,7 +785,7 @@ static void print_node_osm(Nodes *nodes,index_t item)
  double latitude,longitude;
  int i;
 
- GetLatLong(nodes,item,&latitude,&longitude);
+ GetLatLong(nodes,item,nodep,&latitude,&longitude);
 
  if(nodep->allow==Transports_ALL && nodep->flags==0)
     printf("  <node id='%lu' lat='%.7f' lon='%.7f' version='1' />\n",(unsigned long)item+1,radians_to_degrees(latitude),radians_to_degrees(longitude));
@@ -655,7 +800,7 @@ static void print_node_osm(Nodes *nodes,index_t item)
        printf("    <tag k='routino:uturn' v='yes' />\n");
 
     if(nodep->flags & NODE_MINIRNDBT)
-       printf("    <tag k='highway' v='mini_roundabout' />\n");
+       printf("    <tag k='junction' v='roundabout' />\n");
 
     if(nodep->flags & NODE_TURNRSTRCT)
        printf("    <tag k='routino:turnrestriction' v='yes' />\n");
@@ -798,6 +943,184 @@ static void print_tail_osm(void)
 }
 
 
+/*++++++++++++++++++++++++++++++++++++++
+  Print out the contents of a node from the routing database (in visualiser format).
+
+  Nodes *nodes The set of nodes to use.
+
+  index_t item The node index to print.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_node_visualiser(Nodes *nodes,index_t item)
+{
+ Node *nodep=LookupNode(nodes,item,1);
+ double latitude,longitude;
+ int i;
+
+ GetLatLong(nodes,item,nodep,&latitude,&longitude);
+
+ if(nodep->allow==Transports_ALL && nodep->flags==0)
+    printf("<routino:node id='%lu' lat='%.7f' lon='%.7f' />\n",(unsigned long)item+1,radians_to_degrees(latitude),radians_to_degrees(longitude));
+ else
+   {
+    printf("<routino:node id='%lu' lat='%.7f' lon='%.7f'>\n",(unsigned long)item+1,radians_to_degrees(latitude),radians_to_degrees(longitude));
+
+    if(nodep->flags & NODE_SUPER)
+       printf("   <tag k='routino:super' v='yes' />\n");
+
+    if(nodep->flags & NODE_UTURN)
+       printf("   <tag k='routino:uturn' v='yes' />\n");
+
+    if(nodep->flags & NODE_MINIRNDBT)
+       printf("   <tag k='junction' v='roundabout' />\n");
+
+    if(nodep->flags & NODE_TURNRSTRCT)
+       printf("   <tag k='routino:turnrestriction' v='yes' />\n");
+
+    for(i=1;i<Transport_Count;i++)
+       if(!(nodep->allow & TRANSPORTS(i)))
+          printf("   <tag k='%s' v='no' />\n",TransportName(i));
+
+    printf("</routino:node>\n");
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Print out the contents of a segment from the routing database (as a way in visualiser format).
+
+  Segments *segments The set of segments to use.
+
+  index_t item The segment index to print.
+
+  Ways *ways The set of ways to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_segment_visualiser(Segments *segments,index_t item,Ways *ways)
+{
+ Segment *segmentp=LookupSegment(segments,item,1);
+ Way *wayp=LookupWay(ways,segmentp->way,1);
+ char *name=WayName(ways,wayp);
+ int i;
+
+ printf("<routino:way id='%lu'>\n",(unsigned long)item+1);
+
+ if(IsOnewayTo(segmentp,segmentp->node1))
+   {
+    printf("   <nd ref='%lu' />\n",(unsigned long)segmentp->node2+1);
+    printf("   <nd ref='%lu' />\n",(unsigned long)segmentp->node1+1);
+   }
+ else
+   {
+    printf("   <nd ref='%lu' />\n",(unsigned long)segmentp->node1+1);
+    printf("   <nd ref='%lu' />\n",(unsigned long)segmentp->node2+1);
+   }
+
+ if(IsSuperSegment(segmentp))
+    printf("   <tag k='routino:super' v='yes' />\n");
+ if(IsNormalSegment(segmentp))
+    printf("   <tag k='routino:normal' v='yes' />\n");
+
+ printf("   <tag k='routino:distance' v='%.3f km' />\n",distance_to_km(DISTANCE(segmentp->distance)));
+
+ if(wayp->type & Highway_OneWay)
+    printf("   <tag k='oneway' v='yes' />\n");
+
+ if(wayp->type & Highway_Roundabout)
+    printf("   <tag k='roundabout' v='yes' />\n");
+
+ printf("   <tag k='highway' v='%s' />\n",HighwayName(HIGHWAY(wayp->type)));
+
+ if(IsNormalSegment(segmentp) && *name)
+    printf("   <tag k='name' v='%s' />\n",ParseXML_Encode_Safe_XML(name));
+
+ for(i=1;i<Transport_Count;i++)
+    if(wayp->allow & TRANSPORTS(i))
+       printf("   <tag k='%s' v='yes' />\n",TransportName(i));
+
+ for(i=1;i<Property_Count;i++)
+    if(wayp->props & PROPERTIES(i))
+       printf("   <tag k='%s' v='yes' />\n",PropertyName(i));
+
+ if(wayp->speed)
+    printf("   <tag k='maxspeed' v='%d kph' />\n",speed_to_kph(wayp->speed));
+
+ if(wayp->weight)
+    printf("   <tag k='maxweight' v='%.1f t' />\n",weight_to_tonnes(wayp->weight));
+ if(wayp->height)
+    printf("   <tag k='maxheight' v='%.1f m' />\n",height_to_metres(wayp->height));
+ if(wayp->width)
+    printf("   <tag k='maxwidth' v='%.1f m' />\n",width_to_metres(wayp->width));
+ if(wayp->length)
+    printf("   <tag k='maxlength' v='%.1f m' />\n",length_to_metres(wayp->length));
+
+ printf("</routino:way>\n");
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Print out the contents of a turn relation from the routing database (in visualiser format).
+
+  Relations *relations The set of relations to use.
+
+  index_t item The relation index to print.
+
+  Segments *segments The set of segments to use.
+
+  Nodes *nodes The set of nodes to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_turn_relation_visualiser(Relations *relations,index_t item,Segments *segments,Nodes *nodes)
+{
+ TurnRelation *relationp=LookupTurnRelation(relations,item,1);
+
+ Segment *segmentp_from=LookupSegment(segments,relationp->from,1);
+ Segment *segmentp_to  =LookupSegment(segments,relationp->to  ,2);
+
+ double angle=TurnAngle(nodes,segmentp_from,segmentp_to,relationp->via);
+
+ char *restriction;
+
+ if(angle>150 || angle<-150)
+    restriction="no_u_turn";
+ else if(angle>30)
+    restriction="no_right_turn";
+ else if(angle<-30)
+    restriction="no_left_turn";
+ else
+    restriction="no_straight_on";
+
+ printf("<routino:relation id='%lu'>\n",(unsigned long)item+1);
+ printf("   <tag k='type' v='restriction' />\n");
+ printf("   <tag k='restriction' v='%s'/>\n",restriction);
+
+ if(relationp->except)
+    printf("   <tag k='except' v='%s' />\n",AllowedNameList(relationp->except));
+
+ printf("   <member type='way' ref='%lu' role='from' />\n",(unsigned long)relationp->from+1);
+ printf("   <member type='node' ref='%lu' role='via' />\n",(unsigned long)relationp->via+1);
+ printf("   <member type='way' ref='%lu' role='to' />\n",(unsigned long)relationp->to+1);
+
+ printf("</routino:relation>\n");
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Print out an error log entry from the database (in visualiser format).
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  index_t item The error log index to print.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_errorlog_visualiser(ErrorLogs *errorlogs,index_t item)
+{
+ char *string=LookupErrorLogString(errorlogs,item);
+
+ printf("%s\n",ParseXML_Encode_Safe_XML(string));
+}
+
+
 /*+ Conversion from time_t to date string (day of week). +*/
 static const char* const weekdays[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
 
@@ -864,10 +1187,15 @@ static void print_usage(int detail,const char *argerr,const char *err)
          "                  [--dump [--node=<node> ...]\n"
          "                          [--segment=<segment> ...]\n"
          "                          [--way=<way> ...]\n"
-         "                          [--turn-relation=<rel> ...]]\n"
+         "                          [--turn-relation=<rel> ...]\n"
+         "                          [--errorlog=<number> ...]]\n"
          "                  [--dump-osm [--no-super]\n"
          "                              [--latmin=<latmin> --latmax=<latmax>\n"
-         "                               --lonmin=<lonmin> --lonmax=<lonmax>]]\n");
+         "                               --lonmin=<lonmin> --lonmax=<lonmax>]]\n"
+         "                  [--dump-visualiser [--data=node<node>]\n"
+         "                                     [--data=segment<segment>]\n"
+         "                                     [--data=turn-relation<rel>]\n"
+         "                                     [--data=errorlog<number>]]\n");
 
  if(argerr)
     fprintf(stderr,
@@ -909,20 +1237,29 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "      height      = height limits.\n"
             "      width       = width limits.\n"
             "      length      = length limits.\n"
+            "      property-*  = segments with the specified property.\n"
+            "      errorlogs   = errors logged during parsing.\n"
             "\n"
             "--dump                    Dump selected contents of the database.\n"
-            "  --node=<node>           * the node with the selected index.\n"
-            "  --segment=<segment>     * the segment with the selected index.\n"
-            "  --way=<way>             * the way with the selected index.\n"
-            "  --turn-relation=<rel>   * the turn relation with the selected index.\n"
+            "  --node=<node>             * the node with the selected index.\n"
+            "  --segment=<segment>       * the segment with the selected index.\n"
+            "  --way=<way>               * the way with the selected index.\n"
+            "  --turn-relation=<rel>     * the turn relation with the selected index.\n"
+            "  --errorlog=<number>       * the error log with the selected index.\n"
             "                          Use 'all' instead of a number to get all of them.\n"
             "\n"
             "--dump-osm                Dump all or part of the database as an XML file.\n"
-            "  --no-super              * exclude the super-segments.\n"
-            "  --latmin=<latmin>       * the minimum latitude (degrees N).\n"
-            "  --latmax=<latmax>       * the maximum latitude (degrees N).\n"
-            "  --lonmin=<lonmin>       * the minimum longitude (degrees E).\n"
-            "  --lonmax=<lonmax>       * the maximum longitude (degrees E).\n");
+            "  --no-super                * exclude the super-segments.\n"
+            "  --latmin=<latmin>         * the minimum latitude (degrees N).\n"
+            "  --latmax=<latmax>         * the maximum latitude (degrees N).\n"
+            "  --lonmin=<lonmin>         * the minimum longitude (degrees E).\n"
+            "  --lonmax=<lonmax>         * the maximum longitude (degrees E).\n"
+            "\n"
+            "--dump-visualiser         Dump selected contents of the database in HTML.\n"
+            "  --data=node<node>         * the node with the selected index.\n"
+            "  --data=segment<segment>   * the segment with the selected index.\n"
+            "  --data=turn-relation<rel> * the turn relation with the selected index.\n"
+            "  --data=errorlog<number>   * the error log with the selected index.\n");
 
  exit(!detail);
 }
diff --git a/src/filedumperx.c b/src/filedumperx.c
index 5043284..6a77314 100644
--- a/src/filedumperx.c
+++ b/src/filedumperx.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -27,7 +27,6 @@
 
 #include "typesx.h"
 #include "nodesx.h"
-#include "segmentsx.h"
 #include "waysx.h"
 #include "relationsx.h"
 
@@ -38,7 +37,6 @@
 /* Local functions */
 
 static void print_nodes(const char *filename);
-static void print_segments(const char *filename);
 static void print_ways(const char *filename);
 static void print_route_relations(const char *filename);
 static void print_turn_relations(const char *filename);
@@ -54,7 +52,7 @@ int main(int argc,char** argv)
 {
  int   arg;
  char *dirname=NULL,*prefix=NULL;
- char *nodes_filename,*segments_filename,*ways_filename,*route_relations_filename,*turn_relations_filename;
+ char *nodes_filename,*ways_filename,*route_relations_filename,*turn_relations_filename;
  int   option_dump;
 
  /* Parse the command line arguments */
@@ -71,8 +69,6 @@ int main(int argc,char** argv)
        option_dump=1;
     else if(!strcmp(argv[arg],"--nodes"))
        ;
-    else if(!strcmp(argv[arg],"--segments"))
-       ;
     else if(!strcmp(argv[arg],"--ways"))
        ;
     else if(!strcmp(argv[arg],"--route-relations"))
@@ -90,8 +86,6 @@ int main(int argc,char** argv)
 
  nodes_filename=FileName(dirname,prefix,"nodesx.parsed.mem");
 
- segments_filename=FileName(dirname,prefix,"segmentsx.parsed.mem");
-
  ways_filename=FileName(dirname,prefix,"waysx.parsed.mem");
 
  route_relations_filename=FileName(dirname,prefix,"relationsx.route.parsed.mem");
@@ -107,10 +101,6 @@ int main(int argc,char** argv)
          {
           print_nodes(nodes_filename);
          }
-       else if(!strcmp(argv[arg],"--segments"))
-         {
-          print_segments(segments_filename);
-         }
        else if(!strcmp(argv[arg],"--ways"))
          {
           print_ways(ways_filename);
@@ -137,104 +127,72 @@ int main(int argc,char** argv)
 
 static void print_nodes(const char *filename)
 {
- off_t size,position=0;
  int fd;
+ NodeX nodex;
 
- size=SizeFile(filename);
-
- fd=ReOpenFile(filename);
+ fd=ReOpenFileBuffered(filename);
 
- while(position<size)
+ while(!ReadFileBuffered(fd,&nodex,sizeof(NodeX)))
    {
-    NodeX nodex;
-
-    ReadFile(fd,&nodex,sizeof(NodeX));
-
     printf("Node %"Pnode_t"\n",nodex.id);
     printf("  lat=%d lon=%d\n",nodex.latitude,nodex.longitude);
     printf("  allow=%02x\n",nodex.allow);
     printf("  flags=%02x\n",nodex.flags);
-
-    position+=sizeof(NodeX);
    }
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out all of the segments.
+  Print out all of the ways.
 
   const char *filename The name of the file containing the data.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void print_segments(const char *filename)
+static void print_ways(const char *filename)
 {
- off_t size,position=0;
+ FILESORT_VARINT waysize;
  int fd;
 
- size=SizeFile(filename);
-
- fd=ReOpenFile(filename);
+ fd=ReOpenFileBuffered(filename);
 
- while(position<size)
+ while(!ReadFileBuffered(fd,&waysize,FILESORT_VARSIZE))
    {
-    SegmentX segmentx;
-
-    ReadFile(fd,&segmentx,sizeof(SegmentX));
-
-    printf("Segment\n");
-    printf("  node1=%"Pnode_t" node2=%"Pnode_t"\n",segmentx.node1,segmentx.node2);
-    printf("  way=%"Pway_t"\n",segmentx.way);
-    if(segmentx.distance&SEGMENT_AREA)
-       printf("  Part of area\n");
-    if(segmentx.distance&ONEWAY_1TO2)
-       printf("  One-way (forward)\n");
-    if(segmentx.distance&ONEWAY_2TO1)
-       printf("  One-way (reverse)\n");
-
-    position+=sizeof(SegmentX);
-   }
-
- CloseFile(fd);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Print out all of the ways.
+    WayX wayx;
+    node_t node;
+    char *name=NULL;
+    size_t malloced=0;
+    int first=1;
 
-  const char *filename The name of the file containing the data.
-  ++++++++++++++++++++++++++++++++++++++*/
+    ReadFileBuffered(fd,&wayx,sizeof(WayX));
 
-static void print_ways(const char *filename)
-{
- off_t size,position=0;
- int fd;
+    printf("Way %"Pway_t"\n",wayx.id);
 
- size=SizeFile(filename);
+    while(!ReadFileBuffered(fd,&node,sizeof(node_t)) && node!=NO_NODE_ID)
+      {
+       if(first)
+          printf("  nodes=%"Pnode_t,node);
+       else
+          printf(",%"Pnode_t,node);
 
- fd=ReOpenFile(filename);
+       waysize-=sizeof(node_t);
 
- while(position<size)
-   {
-    FILESORT_VARINT waysize;
-    WayX wayx;
-    char *name=NULL;
-    int malloced=0;
+       first=0;
+      }
 
-    ReadFile(fd,&waysize,FILESORT_VARSIZE);
+    printf("\n");
 
-    ReadFile(fd,&wayx,sizeof(WayX));
+    waysize-=sizeof(node_t)+sizeof(WayX);
 
-    if(malloced<(waysize-sizeof(WayX)))
+    if(malloced<waysize)
       {
-       malloced=(waysize-sizeof(WayX));
+       malloced=waysize;
        name=(char*)realloc((void*)name,malloced);
       }
 
-    ReadFile(fd,name,(waysize-sizeof(WayX)));
+    ReadFileBuffered(fd,name,waysize);
 
-    printf("Way %"Pway_t"\n",wayx.id);
     if(*name)
        printf("  name=%s\n",name);
     printf("  type=%02x\n",wayx.way.type);
@@ -251,11 +209,9 @@ static void print_ways(const char *filename)
        printf("  width=%d\n",wayx.way.width);
     if(wayx.way.length)
        printf("  length=%d\n",wayx.way.length);
-
-    position+=waysize+FILESORT_VARSIZE;
    }
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 }
 
 
@@ -267,47 +223,34 @@ static void print_ways(const char *filename)
 
 static void print_route_relations(const char *filename)
 {
- off_t size,position=0;
+ FILESORT_VARINT relationsize;
  int fd;
 
- size=SizeFile(filename);
+ fd=ReOpenFileBuffered(filename);
 
- fd=ReOpenFile(filename);
-
- while(position<size)
+ while(!ReadFileBuffered(fd,&relationsize,FILESORT_VARSIZE))
    {
-    FILESORT_VARINT relationsize;
     RouteRelX relationx;
+    node_t nodeid;
     way_t wayid;
     relation_t relationid;
 
-    ReadFile(fd,&relationsize,FILESORT_VARSIZE);
-
-    ReadFile(fd,&relationx,sizeof(RouteRelX));
+    ReadFileBuffered(fd,&relationx,sizeof(RouteRelX));
 
     printf("Relation %"Prelation_t"\n",relationx.id);
     printf("  routes=%02x\n",relationx.routes);
 
-    do
-      {
-       ReadFile(fd,&wayid,sizeof(way_t));
+    while(!ReadFileBuffered(fd,&nodeid,sizeof(node_t)) && nodeid!=NO_NODE_ID)
+       printf("  node=%"Pnode_t"\n",nodeid);
 
+    while(!ReadFileBuffered(fd,&wayid,sizeof(way_t)) && wayid!=NO_WAY_ID);
        printf("  way=%"Pway_t"\n",wayid);
-      }
-    while(wayid!=NO_WAY_ID);
-
-    do
-      {
-       ReadFile(fd,&relationid,sizeof(relation_t));
 
+    while(!ReadFileBuffered(fd,&relationid,sizeof(relation_t)) && relationid!=NO_RELATION_ID);
        printf("  relation=%"Prelation_t"\n",relationid);
-      }
-    while(relationid!=NO_RELATION_ID);
-
-    position+=relationsize+FILESORT_VARSIZE;
    }
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 }
 
 
@@ -319,19 +262,13 @@ static void print_route_relations(const char *filename)
 
 static void print_turn_relations(const char *filename)
 {
- off_t size,position=0;
  int fd;
+ TurnRelX relationx;
 
- size=SizeFile(filename);
-
- fd=ReOpenFile(filename);
+ fd=ReOpenFileBuffered(filename);
 
- while(position<size)
+ while(!ReadFileBuffered(fd,&relationx,sizeof(TurnRelX)))
    {
-    TurnRelX relationx;
-
-    ReadFile(fd,&relationx,sizeof(TurnRelX));
-
     printf("Relation %"Prelation_t"\n",relationx.id);
     printf("  from=%"Pway_t"\n",relationx.from);
     printf("  via=%"Pnode_t"\n",relationx.via);
@@ -339,11 +276,9 @@ static void print_turn_relations(const char *filename)
     printf("  type=%d\n",relationx.restriction);
     if(relationx.except)
        printf("  except=%02x\n",relationx.except);
-
-    position+=sizeof(TurnRelX);
    }
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 }
 
 
@@ -360,13 +295,12 @@ static void print_turn_relations(const char *filename)
 static void print_usage(int detail,const char *argerr,const char *err)
 {
  fprintf(stderr,
-         "Usage: filedumper [--help]\n"
-         "                  [--dir=<dirname>] [--prefix=<name>]\n"
-         "                  [--dump [--nodes]\n"
-         "                          [--segments]\n"
-         "                          [--ways]\n"
-         "                          [--route-relations]\n"
-         "                          [--turn-relations]]\n");
+         "Usage: filedumperx [--help]\n"
+         "                   [--dir=<dirname>] [--prefix=<name>]\n"
+         "                   [--dump [--nodes]\n"
+         "                           [--ways]\n"
+         "                           [--route-relations]\n"
+         "                           [--turn-relations]]\n");
 
  if(argerr)
     fprintf(stderr,
@@ -388,7 +322,6 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "\n"
             "--dump                    Dump the intermediate files after parsing.\n"
             "  --nodes                 * all of the nodes.\n"
-            "  --segments              * all of the segments.\n"
             "  --ways                  * all of the ways.\n"
             "  --route-relations       * all of the route relations.\n"
             "  --turn-relations        * all of the turn relations.\n");
diff --git a/src/files.c b/src/files.c
index ec78d5e..94fee7d 100644
--- a/src/files.c
+++ b/src/files.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -49,6 +49,30 @@ static struct mmapinfo *mappedfiles;
 static int nmappedfiles=0;
 
 
+#define BUFFLEN 4096
+
+/*+ A structure to contain the list of file buffers. +*/
+struct filebuffer
+{
+ int    fd;                     /*+ The file descriptor used when it was opened. +*/
+ char   buffer[BUFFLEN];        /*+ The data buffer. +*/
+ size_t pointer;                /*+ The read/write pointer for the file buffer. +*/
+ size_t length;                 /*+ The read pointer for the file buffer. +*/
+ int    reading;                /*+ A flag to indicate if the file is for reading. +*/
+};
+
+/*+ The list of file buffers. +*/
+static struct filebuffer **filebuffers=NULL;
+
+/*+ The number of allocated file buffer pointers. +*/
+static int nfilebuffers=0;
+
+
+/* Local functions */
+
+static void CreateFileBuffer(int fd,int read_write);
+
+
 /*++++++++++++++++++++++++++++++++++++++
   Return a filename composed of the dirname, prefix and name.
 
@@ -82,14 +106,29 @@ char *FileName(const char *dirname,const char *prefix, const char *name)
 void *MapFile(const char *filename)
 {
  int fd;
+ struct stat buf;
  off_t size;
  void *address;
 
- /* Open the file and get its size */
+ /* Open the file */
 
- fd=ReOpenFile(filename);
+ fd=open(filename,O_RDONLY);
 
- size=SizeFile(filename);
+ if(fd<0)
+   {
+    fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ /* Get its size */
+
+ if(stat(filename,&buf))
+   {
+    fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ size=buf.st_size;
 
  /* Map the file */
 
@@ -129,14 +168,29 @@ void *MapFile(const char *filename)
 void *MapFileWriteable(const char *filename)
 {
  int fd;
+ struct stat buf;
  off_t size;
  void *address;
 
- /* Open the file and get its size */
+ /* Open the file */
 
- fd=ReOpenFileWriteable(filename);
+ fd=open(filename,O_RDWR);
 
- size=SizeFile(filename);
+ if(fd<0)
+   {
+    fprintf(stderr,"Cannot open file '%s' for reading and writing [%s].\n",filename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ /* Get its size */
+
+ if(stat(filename,&buf))
+   {
+    fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ size=buf.st_size;
 
  /* Map the file */
 
@@ -207,20 +261,92 @@ void *UnmapFile(const void *address)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open a new file on disk for writing.
+  Open an existing file on disk for reading.
+
+  int SlimMapFile Returns the file descriptor if OK or exits in case of an error.
+
+  const char *filename The name of the file to open.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int SlimMapFile(const char *filename)
+{
+ int fd;
+
+ /* Open the file */
+
+ fd=open(filename,O_RDONLY);
+
+ if(fd<0)
+   {
+    fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ CreateFileBuffer(fd,0);
 
-  int OpenFileNew Returns the file descriptor if OK or exits in case of an error.
+ return(fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Open an existing file on disk for reading or writing.
+
+  int SlimMapFileWriteable Returns the file descriptor if OK or exits in case of an error.
+
+  const char *filename The name of the file to open.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int SlimMapFileWriteable(const char *filename)
+{
+ int fd;
+
+ /* Open the file */
+
+ fd=open(filename,O_RDWR);
+
+ if(fd<0)
+   {
+    fprintf(stderr,"Cannot open file '%s' for reading and writing [%s].\n",filename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ CreateFileBuffer(fd,0);
+
+ return(fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Close a file on disk.
+
+  int SlimUnmapFile returns -1 (for similarity to the UnmapFile function).
+
+  int fd The file descriptor to close.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int SlimUnmapFile(int fd)
+{
+ close(fd);
+
+ return(-1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Open a new file on disk for writing (with buffering).
+
+  int OpenFileBufferedNew Returns the file descriptor if OK or exits in case of an error.
 
   const char *filename The name of the file to create.
   ++++++++++++++++++++++++++++++++++++++*/
 
-int OpenFileNew(const char *filename)
+int OpenFileBufferedNew(const char *filename)
 {
  int fd;
 
  /* Open the file */
 
- fd=open(filename,O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
 
  if(fd<0)
    {
@@ -228,25 +354,27 @@ int OpenFileNew(const char *filename)
     exit(EXIT_FAILURE);
    }
 
+ CreateFileBuffer(fd,-1);
+
  return(fd);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open a new or existing file on disk for reading and appending.
+  Open a new or existing file on disk for appending (with buffering).
 
-  int OpenFileAppend Returns the file descriptor if OK or exits in case of an error.
+  int OpenFileBufferedAppend Returns the file descriptor if OK or exits in case of an error.
 
   const char *filename The name of the file to create or open.
   ++++++++++++++++++++++++++++++++++++++*/
 
-int OpenFileAppend(const char *filename)
+int OpenFileBufferedAppend(const char *filename)
 {
  int fd;
 
  /* Open the file */
 
- fd=open(filename,O_RDWR|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
 
  if(fd<0)
    {
@@ -254,19 +382,21 @@ int OpenFileAppend(const char *filename)
     exit(EXIT_FAILURE);
    }
 
+ CreateFileBuffer(fd,-1);
+
  return(fd);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open an existing file on disk for reading.
+  Open an existing file on disk for reading (with buffering).
 
-  int ReOpenFile Returns the file descriptor if OK or exits in case of an error.
+  int ReOpenFileBuffered Returns the file descriptor if OK or exits in case of an error.
 
   const char *filename The name of the file to open.
   ++++++++++++++++++++++++++++++++++++++*/
 
-int ReOpenFile(const char *filename)
+int ReOpenFileBuffered(const char *filename)
 {
  int fd;
 
@@ -280,33 +410,189 @@ int ReOpenFile(const char *filename)
     exit(EXIT_FAILURE);
    }
 
+ CreateFileBuffer(fd,1);
+
  return(fd);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open an existing file on disk for reading or writing.
+  Write data to a file descriptor (via a buffer).
 
-  int ReOpenFileWriteable Returns the file descriptor if OK or exits in case of an error.
+  int WriteFileBuffered Returns 0 if OK or something else in case of an error.
 
-  const char *filename The name of the file to open.
+  int fd The file descriptor to write to.
+
+  const void *address The address of the data to be written.
+
+  size_t length The length of data to write.
   ++++++++++++++++++++++++++++++++++++++*/
 
-int ReOpenFileWriteable(const char *filename)
+int WriteFileBuffered(int fd,const void *address,size_t length)
 {
- int fd;
+ logassert(fd!=-1,"File descriptor is in error - report a bug");
 
- /* Open the file */
+ logassert(fd<nfilebuffers && filebuffers[fd],"File descriptor has no buffer - report a bug");
 
- fd=open(filename,O_RDWR);
+ logassert(!filebuffers[fd]->reading,"File descriptor was not opened for writing - report a bug");
 
- if(fd<0)
+ /* Write the data */
+
+ if((filebuffers[fd]->pointer+length)>BUFFLEN)
    {
-    fprintf(stderr,"Cannot open file '%s' for reading and writing [%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
+    if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
+       return(-1);
+
+    filebuffers[fd]->pointer=0;
    }
 
- return(fd);
+ if(length>=BUFFLEN)
+   {
+    if(write(fd,address,length)!=(ssize_t)length)
+       return(-1);
+
+    return(0);
+   }
+
+ memcpy(filebuffers[fd]->buffer+filebuffers[fd]->pointer,address,length);
+
+ filebuffers[fd]->pointer+=length;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Read data from a file descriptor (via a buffer).
+
+  int ReadFileBuffered Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor to read from.
+
+  void *address The address the data is to be read into.
+
+  size_t length The length of data to read.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ReadFileBuffered(int fd,void *address,size_t length)
+{
+ logassert(fd!=-1,"File descriptor is in error - report a bug");
+
+ logassert(fd<nfilebuffers && filebuffers[fd],"File descriptor has no buffer - report a bug");
+
+ logassert(filebuffers[fd]->reading,"File descriptor was not opened for reading - report a bug");
+
+ /* Read the data */
+
+ if((filebuffers[fd]->pointer+length)>filebuffers[fd]->length)
+    if(filebuffers[fd]->pointer<filebuffers[fd]->length)
+      {
+       memcpy(address,filebuffers[fd]->buffer+filebuffers[fd]->pointer,filebuffers[fd]->length-filebuffers[fd]->pointer);
+
+       address+=filebuffers[fd]->length-filebuffers[fd]->pointer;
+       length-=filebuffers[fd]->length-filebuffers[fd]->pointer;
+
+       filebuffers[fd]->pointer=0;
+       filebuffers[fd]->length=0;
+      }
+
+ if(length>=BUFFLEN)
+   {
+    if(read(fd,address,length)!=(ssize_t)length)
+       return(-1);
+
+    return(0);
+   }
+
+ if(filebuffers[fd]->pointer==filebuffers[fd]->length)
+   {
+    ssize_t len=read(fd,filebuffers[fd]->buffer,BUFFLEN);
+
+    if(len<=0)
+       return(-1);
+
+
+    filebuffers[fd]->length=len;
+    filebuffers[fd]->pointer=0;
+   }
+
+ if(filebuffers[fd]->length==0)
+    return(-1);
+
+ memcpy(address,filebuffers[fd]->buffer+filebuffers[fd]->pointer,length);
+
+ filebuffers[fd]->pointer+=length;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Seek to a position in a file descriptor that uses a buffer.
+
+  int SeekFileBuffered Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor to seek within.
+
+  off_t position The position to seek to.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int SeekFileBuffered(int fd,off_t position)
+{
+ logassert(fd!=-1,"File descriptor is in error - report a bug");
+
+ logassert(fd<nfilebuffers && filebuffers[fd],"File descriptor has no buffer - report a bug");
+
+ /* Seek the data - doesn't need to be highly optimised */
+
+ if(!filebuffers[fd]->reading)
+    if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
+       return(-1);
+
+ filebuffers[fd]->pointer=0;
+ filebuffers[fd]->length=0;
+
+ if(lseek(fd,position,SEEK_SET)!=position)
+    return(-1);
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Skip forward by an offset in a file descriptor that uses a buffer.
+
+  int SkipFileBuffered Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor to skip within.
+
+  off_t skip The amount to skip forward.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int SkipFileBuffered(int fd,off_t skip)
+{
+ logassert(fd!=-1,"File descriptor is in error - report a bug");
+
+ logassert(fd<nfilebuffers && filebuffers[fd],"File descriptor has no buffer - report a bug");
+
+ logassert(filebuffers[fd]->reading,"File descriptor was not opened for reading - report a bug");
+
+ /* Skip the data - needs to be optimised */
+
+ if((filebuffers[fd]->pointer+skip)>filebuffers[fd]->length)
+   {
+    skip-=filebuffers[fd]->length-filebuffers[fd]->pointer;
+
+    filebuffers[fd]->pointer=0;
+    filebuffers[fd]->length=0;
+
+    if(lseek(fd,skip,SEEK_CUR)==-1)
+       return(-1);
+   }
+ else
+    filebuffers[fd]->pointer+=skip;
+
+ return(0);
 }
 
 
@@ -352,22 +638,70 @@ int ExistsFile(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Close a file on disk.
+  Close a file on disk (and flush the buffer).
 
-  int CloseFile returns -1 (for similarity to the *OpenFile* functions).
+  int CloseFileBuffered returns -1 (for similarity to the *OpenFileBuffered* functions).
 
   int fd The file descriptor to close.
   ++++++++++++++++++++++++++++++++++++++*/
 
-int CloseFile(int fd)
+int CloseFileBuffered(int fd)
 {
+ logassert(fd<nfilebuffers && filebuffers[fd],"File descriptor has no buffer - report a bug");
+
+ if(!filebuffers[fd]->reading)
+    if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
+       return(-1);
+
  close(fd);
 
+ free(filebuffers[fd]);
+
+ filebuffers[fd]=NULL;
+
  return(-1);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Open an existing file on disk for reading (in a simple mode).
+
+  int OpenFile Returns the file descriptor if OK or exits in case of an error.
+
+  const char *filename The name of the file to open.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int OpenFile(const char *filename)
+{
+ int fd;
+
+ /* Open the file */
+
+ fd=open(filename,O_RDONLY);
+
+ if(fd<0)
+   {
+    fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ return(fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Close a file on disk (that was opened in simple mode).
+
+  int fd The file descriptor to close.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void CloseFile(int fd)
+{
+ close(fd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Delete a file from disk.
 
   int DeleteFile Returns 0 if OK.
@@ -399,3 +733,34 @@ int RenameFile(const char *oldfilename,const char *newfilename)
 
  return(0);
 }
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create a file buffer.
+
+  int fd The file descriptor.
+
+  int read_write A flag set to 1 for reading, -1 for writing and 0 for unbuffered.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void CreateFileBuffer(int fd,int read_write)
+{
+ if(nfilebuffers<=fd)
+   {
+    int i;
+
+    filebuffers=(struct filebuffer**)realloc((void*)filebuffers,(fd+1)*sizeof(struct filebuffer*));
+
+    for(i=nfilebuffers;i<=fd;i++)
+       filebuffers[i]=NULL;
+
+    nfilebuffers=fd+1;
+   }
+
+ if(read_write)
+   {
+    filebuffers[fd]=(struct filebuffer*)calloc(sizeof(struct filebuffer),1);
+
+    filebuffers[fd]->reading=(read_write==1);
+   }
+}
diff --git a/src/files.h b/src/files.h
index 1f66c87..1d76dbd 100644
--- a/src/files.h
+++ b/src/files.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -41,110 +41,50 @@ char *FileName(const char *dirname,const char *prefix, const char *name);
 
 void *MapFile(const char *filename);
 void *MapFileWriteable(const char *filename);
-void *UnmapFile(const void *address);
-
-int OpenFileNew(const char *filename);
-int OpenFileAppend(const char *filename);
-int ReOpenFile(const char *filename);
-int ReOpenFileWriteable(const char *filename);
-
-static int WriteFile(int fd,const void *address,size_t length);
-static int ReadFile(int fd,void *address,size_t length);
-
-static int SeekWriteFile(int fd,const void *address,size_t length,off_t position);
-static int SeekReadFile(int fd,void *address,size_t length,off_t position);
-
-off_t SizeFile(const char *filename);
-int ExistsFile(const char *filename);
-
-static int SeekFile(int fd,off_t position);
-
-int CloseFile(int fd);
-
-int DeleteFile(const char *filename);
-
-int RenameFile(const char *oldfilename,const char *newfilename);
-
-
-/* Inline the frequently called functions */
-
-/*++++++++++++++++++++++++++++++++++++++
-  Write data to a file descriptor.
-
-  int WriteFile Returns 0 if OK or something else in case of an error.
-
-  int fd The file descriptor to write to.
-
-  const void *address The address of the data to be written.
-
-  size_t length The length of data to write.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static inline int WriteFile(int fd,const void *address,size_t length)
-{
- logassert(fd!=-1,"File descriptor is in error - report a bug");
-
- /* Write the data */
-
- if(write(fd,address,length)!=length)
-    return(-1);
 
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Read data from a file descriptor.
+void *UnmapFile(const void *address);
 
-  int ReadFile Returns 0 if OK or something else in case of an error.
+int SlimMapFile(const char *filename);
+int SlimMapFileWriteable(const char *filename);
 
-  int fd The file descriptor to read from.
+int SlimUnmapFile(int fd);
 
-  void *address The address the data is to be read into.
+int OpenFileBufferedNew(const char *filename);
+int OpenFileBufferedAppend(const char *filename);
 
-  size_t length The length of data to read.
-  ++++++++++++++++++++++++++++++++++++++*/
+int ReOpenFileBuffered(const char *filename);
 
-static inline int ReadFile(int fd,void *address,size_t length)
-{
- logassert(fd!=-1,"File descriptor is in error - report a bug");
+int WriteFileBuffered(int fd,const void *address,size_t length);
+int ReadFileBuffered(int fd,void *address,size_t length);
 
- /* Read the data */
+int SeekFileBuffered(int fd,off_t position);
+int SkipFileBuffered(int fd,off_t skip);
 
- if(read(fd,address,length)!=length)
-    return(-1);
+int CloseFileBuffered(int fd);
 
- return(0);
-}
+int OpenFile(const char *filename);
 
+void CloseFile(int fd);
 
-/*++++++++++++++++++++++++++++++++++++++
-  Seek to a position in a file descriptor.
+off_t SizeFile(const char *filename);
+int ExistsFile(const char *filename);
 
-  int SeekFile Returns 0 if OK or something else in case of an error.
+int DeleteFile(const char *filename);
 
-  int fd The file descriptor to seek within.
+int RenameFile(const char *oldfilename,const char *newfilename);
 
-  off_t position The position to seek to.
-  ++++++++++++++++++++++++++++++++++++++*/
+/* Functions in files.h */
 
-static inline int SeekFile(int fd,off_t position)
-{
- logassert(fd!=-1,"File descriptor is in error - report a bug");
+static inline int SlimReplace(int fd,const void *address,size_t length,off_t position);
+static inline int SlimFetch(int fd,void *address,size_t length,off_t position);
 
- /* Seek the data */
-
- if(lseek(fd,position,SEEK_SET)!=position)
-    return(-1);
-
- return(0);
-}
 
+/* Inline the frequently called functions */
 
 /*++++++++++++++++++++++++++++++++++++++
-  Write data to a file descriptor after seeking to a position.
+  Write data to a file that has been opened for slim mode access.
 
-  int SeekWriteFile Returns 0 if OK or something else in case of an error.
+  int SlimReplace Returns 0 if OK or something else in case of an error.
 
   int fd The file descriptor to write to.
 
@@ -152,18 +92,16 @@ static inline int SeekFile(int fd,off_t position)
 
   size_t length The length of data to write.
 
-  off_t position The position to seek to.
+  off_t position The position in the file to seek to.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline int SeekWriteFile(int fd,const void *address,size_t length,off_t position)
+static inline int SlimReplace(int fd,const void *address,size_t length,off_t position)
 {
- logassert(fd!=-1,"File descriptor is in error - report a bug");
-
  /* Seek and write the data */
 
 #if HAVE_PREAD_PWRITE
 
- if(pwrite(fd,address,length,position)!=length)
+ if(pwrite(fd,address,length,position)!=(ssize_t)length)
     return(-1);
 
 #else
@@ -181,9 +119,9 @@ static inline int SeekWriteFile(int fd,const void *address,size_t length,off_t p
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Read data from a file descriptor after seeking to a position.
+  Read data from a file that has been opened for slim mode access.
 
-  int SeekReadFile Returns 0 if OK or something else in case of an error.
+  int SlimFetch Returns 0 if OK or something else in case of an error.
 
   int fd The file descriptor to read from.
 
@@ -191,18 +129,16 @@ static inline int SeekWriteFile(int fd,const void *address,size_t length,off_t p
 
   size_t length The length of data to read.
 
-  off_t position The position to seek to.
+  off_t position The position in the file to seek to.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline int SeekReadFile(int fd,void *address,size_t length,off_t position)
+static inline int SlimFetch(int fd,void *address,size_t length,off_t position)
 {
- logassert(fd!=-1,"File descriptor is in error - report a bug");
-
  /* Seek and read the data */
 
 #if HAVE_PREAD_PWRITE
 
- if(pread(fd,address,length,position)!=length)
+ if(pread(fd,address,length,position)!=(ssize_t)length)
     return(-1);
 
 #else
diff --git a/src/functions.h b/src/functions.h
index c584b81..8aa7c27 100644
--- a/src/functions.h
+++ b/src/functions.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2011 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -35,7 +35,9 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
 Results *FindMiddleRoute(Nodes *supernodes,Segments *supersegments,Ways *superways,Relations *relations,Profile *profile,Results *begin,Results *end);
 
-Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t prev_segment,index_t finish_node,int *nsuper);
+Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t prev_segment,index_t finish_node);
+
+Results *ExtendStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,Results *begin,index_t finish_node);
 
 Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t finish_node);
 
diff --git a/src/logerror.c b/src/logerror.c
new file mode 100644
index 0000000..d9d18a8
--- /dev/null
+++ b/src/logerror.c
@@ -0,0 +1,219 @@
+/***************************************
+ Error logging functions
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "typesx.h"
+
+#include "files.h"
+#include "logging.h"
+
+
+/* Global variables */
+
+/*+ The name of the error log file. +*/
+char *errorlogfilename=NULL;
+
+/*+ The name of the binary error log file. +*/
+char *errorbinfilename=NULL;
+
+
+/* Local variables */
+
+/*+ The file handle for the error log file. +*/
+static FILE *errorlogfile=NULL;
+
+/*+ The file descriptor for the binary error log file. +*/
+static int errorbinfile=-1;
+
+/*+ The offset of the error message in the error log file. +*/
+static off_t errorfileoffset=0;
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create the error log file.
+
+  const char *filename The name of the file to create.
+
+  int append The option to append to an existing file.
+
+  int bin The option to enable a binary log file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void open_errorlog(const char *filename,int append,int bin)
+{
+ /* Text log file */
+
+ errorlogfilename=(char*)malloc(strlen(filename)+8);
+
+ strcpy(errorlogfilename,filename);
+
+ errorlogfile=fopen(errorlogfilename,append?"a":"w");
+
+ if(!errorlogfile)
+   {
+    fprintf(stderr,"Cannot open file '%s' for writing [%s].\n",errorlogfilename,strerror(errno));
+    exit(EXIT_FAILURE);
+   }
+
+ /* Binary log file */
+
+ if(bin)
+   {
+    errorbinfilename=(char*)malloc(strlen(filename)+8);
+
+    sprintf(errorbinfilename,"%s.tmp",filename);
+
+    errorfileoffset=0;
+
+    if(append)
+      {
+       if(ExistsFile(filename))
+          errorfileoffset=SizeFile(filename);
+
+       errorbinfile=OpenFileBufferedAppend(errorbinfilename);
+      }
+    else
+       errorbinfile=OpenFileBufferedNew(errorbinfilename);
+   }
+ else
+    errorbinfile=-1;
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Close the error log file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void close_errorlog(void)
+{
+ if(errorlogfile)
+   {
+    fclose(errorlogfile);
+
+    if(errorbinfile!=-1)
+       CloseFileBuffered(errorbinfile);
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Log a message to the error log file.
+
+  const char *format The format string.
+
+  ... The other arguments.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void logerror(const char *format, ...)
+{
+ va_list ap;
+
+ if(!errorlogfile)
+    return;
+
+ va_start(ap,format);
+
+ errorfileoffset+=vfprintf(errorlogfile,format,ap);
+
+ va_end(ap);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Store the node information in the binary log file for this message.
+
+  node_t logerror_node Returns the node identifier.
+
+  node_t id The node identifier.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+node_t logerror_node(node_t id)
+{
+ if(errorbinfile!=-1)
+   {
+    ErrorLogObject error={0};
+
+    error.id=id;
+    error.type='N';
+
+    error.offset=errorfileoffset;
+
+    WriteFileBuffered(errorbinfile,&error,sizeof(ErrorLogObject));
+   }
+
+ return(id);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Store the way information in the binary log file for this message.
+
+  way_t logerror_way Returns the way identifier.
+
+  way_t id The way identifier.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+way_t logerror_way(way_t id)
+{
+ if(errorbinfile!=-1)
+   {
+    ErrorLogObject error={0};
+
+    error.id=id;
+    error.type='W';
+
+    error.offset=errorfileoffset;
+
+    WriteFileBuffered(errorbinfile,&error,sizeof(ErrorLogObject));
+   }
+
+ return(id);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Store the relation information in the binary log file for this message.
+
+  relation_t logerror_relation Returns the relation identifier.
+
+  relation_t id The relation identifier.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+relation_t logerror_relation(relation_t id)
+{
+ if(errorbinfile!=-1)
+   {
+    ErrorLogObject error={0};
+
+    error.id=id;
+    error.type='R';
+
+    error.offset=errorfileoffset;
+
+    WriteFileBuffered(errorbinfile,&error,sizeof(ErrorLogObject));
+   }
+
+ return(id);
+}
diff --git a/src/logging.c b/src/logging.c
index 94174ae..0a3c027 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -48,14 +48,40 @@ static void vfprintf_last(FILE *file,const char *format,va_list ap);
 
 /* Local variables */
 
-/*+ The time that printf_first was called. +*/
-static struct timeval start_time;
+/*+ The time that program_start() was called. +*/
+static struct timeval program_start_time;
+
+/*+ The time that printf_first() was called. +*/
+static struct timeval function_start_time;
 
 /*+ The length of the string printed out last time. +*/
 static int printed_length=0;
 
-/*+ The file handle for the error log file. +*/
-static FILE *errorlogfile;
+
+/*++++++++++++++++++++++++++++++++++++++
+  Record the time that the program started.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void printf_program_start(void)
+{
+ gettimeofday(&program_start_time,NULL);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Record the time that the program started.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void printf_program_end(void)
+{
+ if(option_logtime)
+   {
+    printf("\n");
+    fprintf_elapsed_time(stdout,&program_start_time);
+    printf("Complete\n");
+    fflush(stdout);
+   }
+}
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -71,7 +97,7 @@ void printf_first(const char *format, ...)
  va_list ap;
 
  if(option_logtime)
-    gettimeofday(&start_time,NULL);
+    gettimeofday(&function_start_time,NULL);
 
  if(option_loggable)
     return;
@@ -142,7 +168,7 @@ void fprintf_first(FILE *file,const char *format, ...)
  va_list ap;
 
  if(option_logtime)
-    gettimeofday(&start_time,NULL);
+    gettimeofday(&function_start_time,NULL);
 
  if(option_loggable)
     return;
@@ -217,7 +243,7 @@ static void vfprintf_first(FILE *file,const char *format,va_list ap)
  int retval;
 
  if(option_logtime)
-    fprintf_elapsed_time(file,&start_time);
+    fprintf_elapsed_time(file,&function_start_time);
 
  retval=vfprintf(file,format,ap);
  fflush(file);
@@ -244,7 +270,7 @@ static void vfprintf_middle(FILE *file,const char *format,va_list ap)
  fputc('\r',file);
 
  if(option_logtime)
-    fprintf_elapsed_time(file,&start_time);
+    fprintf_elapsed_time(file,&function_start_time);
 
  retval=vfprintf(file,format,ap);
  fflush(file);
@@ -279,7 +305,7 @@ static void vfprintf_last(FILE *file,const char *format,va_list ap)
     fputc('\r',file);
 
  if(option_logtime)
-    fprintf_elapsed_time(file,&start_time);
+    fprintf_elapsed_time(file,&function_start_time);
 
  retval=vfprintf(file,format,ap);
 
@@ -314,61 +340,7 @@ void fprintf_elapsed_time(FILE *file,struct timeval *start)
     elapsed.tv_usec+=1000000;
    }
 
- fprintf(file,"[%2ld:%02ld.%03ld] ",elapsed.tv_sec/60,elapsed.tv_sec%60,elapsed.tv_usec/10000);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Create the error log file.
-
-  const char *filename The name of the file to create.
-
-  int append The option to append to an existing file.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void open_errorlog(const char *filename,int append)
-{
- errorlogfile=fopen(filename,append?"a":"w");
-
- if(!errorlogfile)
-   {
-    fprintf(stderr,"Cannot open file '%s' for writing [%s].\n",filename,strerror(errno));
-    exit(EXIT_FAILURE);
-   }
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Close the error log file.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void close_errorlog(void)
-{
- if(errorlogfile)
-    fclose(errorlogfile);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Log a message to the error log file.
-
-  const char *format The format string.
-
-  ... The other arguments.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void logerror(const char *format, ...)
-{
- va_list ap;
-
- if(!errorlogfile)
-    return;
-
- va_start(ap,format);
-
- vfprintf(errorlogfile,format,ap);
-
- va_end(ap);
+ fprintf(file,"[%2ld:%02ld.%03ld] ",elapsed.tv_sec/60,elapsed.tv_sec%60,elapsed.tv_usec/1000);
 }
 
 
diff --git a/src/logging.h b/src/logging.h
index 8857316..cc61bf6 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -26,6 +26,22 @@
 #include <stdio.h>
 #include <sys/time.h>
 
+#include "typesx.h"
+
+
+/* Data structures */
+
+/*+ A structure containing a single object as written by the logerror_*() functions. +*/
+typedef struct _ErrorLogObject
+{
+ char      type;             /*+ The type of the object. +*/
+
+ uint64_t  id;               /*+ The id of the object. +*/
+
+ uint32_t  offset;           /*+ The offset of the error message from the beginning of the text file. +*/
+}
+ ErrorLogObject;
+
 
 /* Variables */
 
@@ -35,6 +51,10 @@ extern int option_logtime;
 
 /* Runtime progress logging functions in logging.c */
 
+void printf_program_start(void);
+void printf_program_end(void);
+
+
 #ifdef __GNUC__
 
 void printf_first(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
@@ -59,9 +79,10 @@ void fprintf_last(FILE *file,const char *format, ...);
 
 void fprintf_elapsed_time(FILE *file,struct timeval *start);
 
-/* Parsing/processing error logging functions in logging.c */
 
-void open_errorlog(const char *filename,int append);
+/* Error logging functions in logerror.c */
+
+void open_errorlog(const char *filename,int append,int bin);
 void close_errorlog(void);
 
 #ifdef __GNUC__
@@ -74,10 +95,14 @@ void logerror(const char *format, ...);
 
 #endif
 
+node_t     logerror_node    (node_t     id);
+way_t      logerror_way     (way_t      id);
+relation_t logerror_relation(relation_t id);
+
 
-/* Runtime fatal error assertion */
+/* Runtime fatal error assertion in logging.c */
 
-#define logassert(xx,yy) do{ if(!(xx)) _logassert(yy,__FILE__,__LINE__); } while(0);
+#define logassert(xx,yy) do { if(!(xx)) _logassert(yy,__FILE__,__LINE__); } while(0)
 
 void _logassert(const char *message,const char *file,int line);
 
diff --git a/src/nodes.c b/src/nodes.c
index 9b693a1..e021f0b 100644
--- a/src/nodes.c
+++ b/src/nodes.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -50,7 +50,6 @@ Nodes *LoadNodeList(const char *filename)
  Nodes *nodes;
 #if SLIM
  size_t sizeoffsets;
- int i;
 #endif
 
  nodes=(Nodes*)malloc(sizeof(Nodes));
@@ -70,22 +69,21 @@ Nodes *LoadNodeList(const char *filename)
 
 #else
 
- nodes->fd=ReOpenFile(filename);
+ nodes->fd=SlimMapFile(filename);
 
  /* Copy the NodesFile header structure from the loaded data */
 
- ReadFile(nodes->fd,&nodes->file,sizeof(NodesFile));
+ SlimFetch(nodes->fd,&nodes->file,sizeof(NodesFile),0);
 
  sizeoffsets=(nodes->file.latbins*nodes->file.lonbins+1)*sizeof(index_t);
 
  nodes->offsets=(index_t*)malloc(sizeoffsets);
 
- ReadFile(nodes->fd,nodes->offsets,sizeoffsets);
+ SlimFetch(nodes->fd,nodes->offsets,sizeoffsets,sizeof(NodesFile));
 
  nodes->nodesoffset=sizeof(NodesFile)+sizeoffsets;
 
- for(i=0;i<sizeof(nodes->cached)/sizeof(nodes->cached[0]);i++)
-    nodes->incache[i]=NO_NODE;
+ nodes->cache=NewNodeCache();
 
 #endif
 
@@ -94,6 +92,32 @@ Nodes *LoadNodeList(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Destroy the node list.
+
+  Nodes *nodes The node list to destroy.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DestroyNodeList(Nodes *nodes)
+{
+#if !SLIM
+
+ nodes->data=UnmapFile(nodes->data);
+
+#else
+
+ nodes->fd=SlimUnmapFile(nodes->fd);
+
+ free(nodes->offsets);
+
+ DeleteNodeCache(nodes->cache);
+
+#endif
+
+ free(nodes);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Find the closest node given its latitude, longitude and the profile of the
   mode of transport that must be able to move to/from this node.
 
@@ -372,7 +396,7 @@ index_t FindClosestSegment(Nodes *nodes,Segments *segments,Ways *ways,double lat
                       distance_t dist2,dist3;
                       double lat2,lon2,dist3a,dist3b,distp;
 
-                      GetLatLong(nodes,OtherNode(segmentp,i),&lat2,&lon2);
+                      GetLatLong(nodes,OtherNode(segmentp,i),NULL,&lat2,&lon2);
 
                       dist2=Distance(lat2,lon2,latitude,longitude);
 
@@ -380,26 +404,37 @@ index_t FindClosestSegment(Nodes *nodes,Segments *segments,Ways *ways,double lat
 
                       /* Use law of cosines (assume flat Earth) */
 
-                      dist3a=((double)dist1*(double)dist1-(double)dist2*(double)dist2+(double)dist3*(double)dist3)/(2.0*(double)dist3);
-                      dist3b=(double)dist3-dist3a;
-
-                      if((dist1+dist2)<dist3)
+                      if(dist3==0)
                         {
-                         distp=0;
+                         distp=dist1;  /* == dist2 */
+                         dist3a=dist1; /* == dist2 */
+                         dist3b=dist2; /* == dist1 */
                         }
-                      else if(dist3a>=0 && dist3b>=0)
-                         distp=sqrt((double)dist1*(double)dist1-dist3a*dist3a);
-                      else if(dist3a>0)
+                      else if((dist1+dist2)<dist3)
                         {
-                         distp=dist2;
-                         dist3a=dist3;
-                         dist3b=0;
+                         distp=0;
+                         dist3a=dist1;
+                         dist3b=dist2;
                         }
-                      else /* if(dist3b>0) */
+                      else
                         {
-                         distp=dist1;
-                         dist3a=0;
-                         dist3b=dist3;
+                         dist3a=((double)dist1*(double)dist1-(double)dist2*(double)dist2+(double)dist3*(double)dist3)/(2.0*(double)dist3);
+                         dist3b=(double)dist3-dist3a;
+
+                         if(dist3a>=0 && dist3b>=0)
+                            distp=sqrt((double)dist1*(double)dist1-dist3a*dist3a);
+                         else if(dist3a>0)
+                           {
+                            distp=dist2;
+                            dist3a=dist3;
+                            dist3b=0;
+                           }
+                         else /* if(dist3b>0) */
+                           {
+                            distp=dist1;
+                            dist3a=0;
+                            dist3b=dist3;
+                           }
                         }
 
                       if(distp<(double)bestd)
@@ -510,16 +545,18 @@ static int valid_segment_for_profile(Ways *ways,Segment *segmentp,Profile *profi
 
   index_t index The node index.
 
+  Node *nodep A pointer to the node if already available.
+
   double *latitude Returns the latitude.
 
   double *longitude Returns the logitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude)
+void GetLatLong(Nodes *nodes,index_t index,Node *nodep,double *latitude,double *longitude)
 {
- Node *nodep=LookupNode(nodes,index,4);
- ll_bin_t latbin=-1,lonbin=-1;
- ll_bin_t start,end,mid;
+ ll_bin_t latbin,lonbin;
+ ll_bin2_t bin=-1;
+ ll_bin2_t start,end,mid;
  index_t offset;
 
  /* Binary search - search key nearest match below is required.
@@ -533,76 +570,48 @@ void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude)
   *  # <- end    |  start or end is the wanted one.
   */
 
- /* Search for longitude */
+ /* Search for offset */
 
  start=0;
- end=nodes->file.lonbins-1;
+ end=nodes->file.lonbins*nodes->file.latbins;
 
  do
    {
     mid=(start+end)/2;                  /* Choose mid point */
 
-    offset=LookupNodeOffset(nodes,nodes->file.latbins*mid);
+    offset=LookupNodeOffset(nodes,mid);
 
     if(offset<index)                    /* Mid point is too low for an exact match but could be lower bound */
        start=mid;
     else if(offset>index)               /* Mid point is too high */
        end=mid?(mid-1):mid;
     else                                /* Mid point is correct */
-      {lonbin=mid;break;}
+      {bin=mid;break;}
    }
  while((end-start)>1);
 
- if(lonbin==-1)
+ if(bin==-1)
    {
-    offset=LookupNodeOffset(nodes,nodes->file.latbins*end);
+    offset=LookupNodeOffset(nodes,end);
 
     if(offset>index)
-       lonbin=start;
+       bin=start;
     else
-       lonbin=end;
+       bin=end;
    }
 
- while(lonbin<nodes->file.lonbins && 
-       LookupNodeOffset(nodes,lonbin*nodes->file.latbins)==LookupNodeOffset(nodes,(lonbin+1)*nodes->file.latbins))
-    lonbin++;
+ while(bin<=(nodes->file.lonbins*nodes->file.latbins) && 
+       LookupNodeOffset(nodes,bin)==LookupNodeOffset(nodes,bin+1))
+    bin++;
 
- /* Search for latitude */
-
- start=0;
- end=nodes->file.latbins-1;
-
- do
-   {
-    mid=(start+end)/2;                  /* Choose mid point */
-
-    offset=LookupNodeOffset(nodes,lonbin*nodes->file.latbins+mid);
-
-    if(offset<index)                    /* Mid point is too low for an exact match but could be lower bound */
-       start=mid;
-    else if(offset>index)               /* Mid point is too high */
-       end=mid?(mid-1):mid;
-    else                                /* Mid point is correct */
-      {latbin=mid;break;}
-   }
- while((end-start)>1);
-
- if(latbin==-1)
-   {
-    offset=LookupNodeOffset(nodes,lonbin*nodes->file.latbins+end);
-
-    if(offset>index)
-       latbin=start;
-    else
-       latbin=end;
-   }
-
- while(latbin<nodes->file.latbins &&
-       LookupNodeOffset(nodes,lonbin*nodes->file.latbins+latbin)==LookupNodeOffset(nodes,lonbin*nodes->file.latbins+latbin+1))
-    latbin++;
+ latbin=bin%nodes->file.latbins;
+ lonbin=bin/nodes->file.latbins;
 
  /* Return the values */
 
+ if(nodep==NULL)
+    nodep=LookupNode(nodes,index,4);
+
  *latitude =latlong_to_radians(bin_to_latlong(nodes->file.latzero+latbin)+off_to_latlong(nodep->latoffset));
  *longitude=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonbin)+off_to_latlong(nodep->lonoffset));
 }
diff --git a/src/nodes.h b/src/nodes.h
index faf416a..01832aa 100644
--- a/src/nodes.h
+++ b/src/nodes.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -27,6 +27,7 @@
 #include <sys/types.h>
 
 #include "types.h"
+#include "cache.h"
 
 #include "files.h"
 #include "profiles.h"
@@ -85,7 +86,8 @@ struct _Nodes
  off_t     nodesoffset;         /*+ The offset of the nodes within the file. +*/
 
  Node      cached[6];           /*+ Some cached nodes read from the file in slim mode. +*/
- index_t   incache[6];          /*+ The indexes of the cached nodes. +*/
+
+ NodeCache *cache;              /*+ A RAM cache of nodes read from the file. +*/
 
 #endif
 };
@@ -95,6 +97,8 @@ struct _Nodes
 
 Nodes *LoadNodeList(const char *filename);
 
+void DestroyNodeList(Nodes *nodes);
+
 index_t FindClosestNode(Nodes *nodes,Segments *segments,Ways *ways,double latitude,double longitude,
                         distance_t distance,Profile *profile,distance_t *bestdist);
 
@@ -102,7 +106,7 @@ index_t FindClosestSegment(Nodes *nodes,Segments *segments,Ways *ways,double lat
                            distance_t distance,Profile *profile, distance_t *bestdist,
                            index_t *bestnode1,index_t *bestnode2,distance_t *bestdist1,distance_t *bestdist2);
 
-void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude);
+void GetLatLong(Nodes *nodes,index_t index,Node *nodep,double *latitude,double *longitude);
 
 
 /* Macros and inline functions */
@@ -127,7 +131,23 @@ void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude);
 
 #else
 
-static Node *LookupNode(Nodes *nodes,index_t index,int position);
+/* Prototypes */
+
+static inline Node *LookupNode(Nodes *nodes,index_t index,int position);
+
+CACHE_NEWCACHE_PROTO(Node)
+CACHE_DELETECACHE_PROTO(Node)
+CACHE_FETCHCACHE_PROTO(Node)
+CACHE_INVALIDATECACHE_PROTO(Node)
+
+
+/* Inline functions */
+
+CACHE_STRUCTURE(Node)
+CACHE_NEWCACHE(Node)
+CACHE_DELETECACHE(Node)
+CACHE_FETCHCACHE(Node)
+CACHE_INVALIDATECACHE(Node)
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -144,12 +164,7 @@ static Node *LookupNode(Nodes *nodes,index_t index,int position);
 
 static inline Node *LookupNode(Nodes *nodes,index_t index,int position)
 {
- if(nodes->incache[position-1]!=index)
-   {
-    SeekReadFile(nodes->fd,&nodes->cached[position-1],sizeof(Node),nodes->nodesoffset+(off_t)index*sizeof(Node));
-
-    nodes->incache[position-1]=index;
-   }
+ nodes->cached[position-1]=*FetchCachedNode(nodes->cache,index,nodes->fd,nodes->nodesoffset);
 
  return(&nodes->cached[position-1]);
 }
diff --git a/src/nodesx.c b/src/nodesx.c
index 1b74bba..7474ba7 100644
--- a/src/nodesx.c
+++ b/src/nodesx.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -52,6 +52,7 @@ static latlong_t lat_min,lat_max,lon_min,lon_max;
 static int sort_by_id(NodeX *a,NodeX *b);
 static int deduplicate_and_index_by_id(NodeX *nodex,index_t index);
 
+static int update_id(NodeX *nodex,index_t index);
 static int sort_by_lat_long(NodeX *a,NodeX *b);
 static int index_by_lat_long(NodeX *nodex,index_t index);
 
@@ -93,12 +94,16 @@ NodesX *NewNodeList(int append,int readonly)
       }
 
  if(append)
-    nodesx->fd=OpenFileAppend(nodesx->filename_tmp);
+    nodesx->fd=OpenFileBufferedAppend(nodesx->filename_tmp);
  else if(!readonly)
-    nodesx->fd=OpenFileNew(nodesx->filename_tmp);
+    nodesx->fd=OpenFileBufferedNew(nodesx->filename_tmp);
  else
     nodesx->fd=-1;
 
+#if SLIM
+ nodesx->cache=NewNodeXCache();
+#endif
+
  return(nodesx);
 }
 
@@ -133,6 +138,10 @@ void FreeNodeList(NodesX *nodesx,int keep)
  if(nodesx->super)
     free(nodesx->super);
 
+#if SLIM
+ DeleteNodeXCache(nodesx->cache);
+#endif
+
  free(nodesx);
 }
 
@@ -163,7 +172,7 @@ void AppendNodeList(NodesX *nodesx,node_t id,double latitude,double longitude,tr
  nodex.allow=allow;
  nodex.flags=flags;
 
- WriteFile(nodesx->fd,&nodex,sizeof(NodeX));
+ WriteFileBuffered(nodesx->fd,&nodex,sizeof(NodeX));
 
  nodesx->number++;
 
@@ -180,7 +189,7 @@ void AppendNodeList(NodesX *nodesx,node_t id,double latitude,double longitude,tr
 void FinishNodeList(NodesX *nodesx)
 {
  if(nodesx->fd!=-1)
-    nodesx->fd=CloseFile(nodesx->fd);
+    nodesx->fd=CloseFileBuffered(nodesx->fd);
 }
 
 
@@ -260,11 +269,11 @@ void SortNodeList(NodesX *nodesx)
 
  /* Re-open the file read-only and a new file writeable */
 
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
+ nodesx->fd=ReOpenFileBuffered(nodesx->filename_tmp);
 
  DeleteFile(nodesx->filename_tmp);
 
- fd=OpenFileNew(nodesx->filename_tmp);
+ fd=OpenFileBufferedNew(nodesx->filename_tmp);
 
  /* Allocate the array of indexes */
 
@@ -282,10 +291,12 @@ void SortNodeList(NodesX *nodesx)
                                                            (int (*)(const void*,const void*))sort_by_id,
                                                            (int (*)(void*,index_t))deduplicate_and_index_by_id);
 
+ nodesx->knumber=nodesx->number;
+
  /* Close the files */
 
- nodesx->fd=CloseFile(nodesx->fd);
- CloseFile(fd);
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
@@ -354,44 +365,97 @@ static int deduplicate_and_index_by_id(NodeX *nodex,index_t index)
 
   NodesX *nodesx The set of nodes to modify.
 
-  SegmentsX *segmentsx The set of segments to use.
+  WaysX *waysx The set of ways to use.
 
   int keep If set to 1 then keep the old data file otherwise delete it.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,int keep)
+void RemoveNonHighwayNodes(NodesX *nodesx,WaysX *waysx,int keep)
 {
+ BitMask *usednode;
  NodeX nodex;
- index_t total=0,highway=0,nothighway=0;
+ index_t i,total=0,highway=0,nothighway=0;
  int fd;
 
  /* Print the start message */
 
- printf_first("Checking Nodes: Nodes=0");
+ printf_first("Checking Ways for unused Nodes: Ways=0");
+
+ /* Allocate the node usage bitmask */
+
+ usednode=AllocBitMask(nodesx->number);
+
+ logassert(usednode,"Failed to allocate memory (try using slim mode?)"); /* Check AllocBitMask() worked */
+
+ /* Re-open the file read-only */
+
+ waysx->fd=ReOpenFileBuffered(waysx->filename_tmp);
+
+ /* Loop through the ways and mark the used nodes */
+
+ for(i=0;i<waysx->number;i++)
+   {
+    WayX wayx;
+    FILESORT_VARINT waysize;
+    node_t node;
+
+    ReadFileBuffered(waysx->fd,&waysize,FILESORT_VARSIZE);
+
+    ReadFileBuffered(waysx->fd,&wayx,sizeof(WayX));
+
+    while(!ReadFileBuffered(waysx->fd,&node,sizeof(node_t)) && node!=NO_NODE_ID)
+      {
+       index_t index=IndexNodeX(nodesx,node);
+
+       waysize-=sizeof(node_t);
+
+       if(index!=NO_NODE)
+          SetBit(usednode,index);
+      }
+
+    waysize-=sizeof(node_t)+sizeof(WayX);
+
+    SkipFileBuffered(waysx->fd,waysize);
+
+    if(!((i+1)%1000))
+       printf_middle("Checking Ways for unused Nodes: Ways=%"Pindex_t,i+1);
+   }
+
+ /* Close the file */
+
+ waysx->fd=CloseFileBuffered(waysx->fd);
+
+ /* Print the final message */
+
+ printf_last("Checked Ways for unused Nodes: Ways=%"Pindex_t,waysx->number);
+
+
+ /* Print the start message */
+
+ printf_first("Removing unused Nodes: Nodes=0");
 
  /* Re-open the file read-only and a new file writeable */
 
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
+ nodesx->fd=ReOpenFileBuffered(nodesx->filename_tmp);
 
  if(keep)
     RenameFile(nodesx->filename_tmp,nodesx->filename);
  else
     DeleteFile(nodesx->filename_tmp);
 
- fd=OpenFileNew(nodesx->filename_tmp);
+ fd=OpenFileBufferedNew(nodesx->filename_tmp);
 
  /* Modify the on-disk image */
 
- while(!ReadFile(nodesx->fd,&nodex,sizeof(NodeX)))
+ while(!ReadFileBuffered(nodesx->fd,&nodex,sizeof(NodeX)))
    {
-    if(!IsBitSet(segmentsx->usednode,total))
+    if(!IsBitSet(usednode,total))
        nothighway++;
     else
       {
-       nodex.id=highway;
-       nodesx->idata[highway]=nodesx->idata[total];
+       nodesx->idata[highway]=nodex.id;
 
-       WriteFile(fd,&nodex,sizeof(NodeX));
+       WriteFileBuffered(fd,&nodex,sizeof(NodeX));
 
        highway++;
       }
@@ -399,24 +463,23 @@ void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,int keep)
     total++;
 
     if(!(total%10000))
-       printf_middle("Checking Nodes: Nodes=%"Pindex_t" Highway=%"Pindex_t" not-Highway=%"Pindex_t,total,highway,nothighway);
+       printf_middle("Removing unused Nodes: Nodes=%"Pindex_t" Highway=%"Pindex_t" not-Highway=%"Pindex_t,total,highway,nothighway);
    }
 
  nodesx->number=highway;
 
  /* Close the files */
 
- nodesx->fd=CloseFile(nodesx->fd);
- CloseFile(fd);
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
 
  /* Free the now-unneeded index */
 
- free(segmentsx->usednode);
- segmentsx->usednode=NULL;
+ free(usednode);
 
  /* Print the final message */
 
- printf_last("Checked Nodes: Nodes=%"Pindex_t" Highway=%"Pindex_t" not-Highway=%"Pindex_t,total,highway,nothighway);
+ printf_last("Removed unused Nodes: Nodes=%"Pindex_t" Highway=%"Pindex_t" not-Highway=%"Pindex_t,total,highway,nothighway);
 }
 
 
@@ -449,15 +512,15 @@ void RemovePrunedNodes(NodesX *nodesx,SegmentsX *segmentsx)
 
  /* Re-open the file read-only and a new file writeable */
 
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
+ nodesx->fd=ReOpenFileBuffered(nodesx->filename_tmp);
 
  DeleteFile(nodesx->filename_tmp);
 
- fd=OpenFileNew(nodesx->filename_tmp);
+ fd=OpenFileBufferedNew(nodesx->filename_tmp);
 
  /* Modify the on-disk image */
 
- while(!ReadFile(nodesx->fd,&nodex,sizeof(NodeX)))
+ while(!ReadFileBuffered(nodesx->fd,&nodex,sizeof(NodeX)))
    {
     if(segmentsx->firstnode[total]==NO_SEGMENT)
       {
@@ -467,10 +530,9 @@ void RemovePrunedNodes(NodesX *nodesx,SegmentsX *segmentsx)
       }
     else
       {
-       nodex.id=notpruned;
        nodesx->pdata[total]=notpruned;
 
-       WriteFile(fd,&nodex,sizeof(NodeX));
+       WriteFileBuffered(fd,&nodex,sizeof(NodeX));
 
        notpruned++;
       }
@@ -485,8 +547,8 @@ void RemovePrunedNodes(NodesX *nodesx,SegmentsX *segmentsx)
 
  /* Close the files */
 
- nodesx->fd=CloseFile(nodesx->fd);
- CloseFile(fd);
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
@@ -508,17 +570,17 @@ void SortNodeListGeographically(NodesX *nodesx)
  if(nodesx->number==0)
     return;
 
- /* While we are here we can work out the range of data */
+ /* Print the start message */
+
+ printf_first("Sorting Nodes Geographically");
+
+ /* Work out the range of data */
 
  lat_min=radians_to_latlong( 2);
  lat_max=radians_to_latlong(-2);
  lon_min=radians_to_latlong( 4);
  lon_max=radians_to_latlong(-4);
 
- /* Print the start message */
-
- printf_first("Sorting Nodes Geographically");
-
  /* Allocate the memory for the geographical index array */
 
  nodesx->gdata=(index_t*)malloc(nodesx->number*sizeof(index_t));
@@ -527,24 +589,24 @@ void SortNodeListGeographically(NodesX *nodesx)
 
  /* Re-open the file read-only and a new file writeable */
 
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
+ nodesx->fd=ReOpenFileBuffered(nodesx->filename_tmp);
 
  DeleteFile(nodesx->filename_tmp);
 
- fd=OpenFileNew(nodesx->filename_tmp);
+ fd=OpenFileBufferedNew(nodesx->filename_tmp);
 
  /* Sort nodes geographically and index them */
 
  sortnodesx=nodesx;
 
- filesort_fixed(nodesx->fd,fd,sizeof(NodeX),NULL,
+ filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(void*,index_t))update_id,
                                             (int (*)(const void*,const void*))sort_by_lat_long,
                                             (int (*)(void*,index_t))index_by_lat_long);
 
  /* Close the files */
 
- nodesx->fd=CloseFile(nodesx->fd);
- CloseFile(fd);
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+ CloseFileBuffered(fd);
 
  /* Free the memory */
 
@@ -571,6 +633,27 @@ void SortNodeListGeographically(NodesX *nodesx)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Update the node ids.
+
+  int update_id Return 1 if the value is to be kept, otherwise 0.
+
+  NodeX *nodex The extended node.
+
+  index_t index The number of unsorted nodes that have been read from the input file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int update_id(NodeX *nodex,index_t index)
+{
+ nodex->id=index;
+
+ if(IsBitSet(sortnodesx->super,index))
+    nodex->flags|=NODE_SUPER;
+
+ return(1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Sort the nodes into latitude and longitude order (first by longitude bin
   number, then by latitude bin number and then by exact longitude and then by
   exact latitude).
@@ -634,9 +717,6 @@ static int index_by_lat_long(NodeX *nodex,index_t index)
 {
  sortnodesx->gdata[nodex->id]=index;
 
- if(IsBitSet(sortnodesx->super,nodex->id))
-    nodex->flags|=NODE_SUPER;
-
  if(nodex->latitude<lat_min)
     lat_min=nodex->latitude;
  if(nodex->latitude>lat_max)
@@ -683,13 +763,13 @@ void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx)
 
  /* Re-open the file */
 
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
+ nodesx->fd=ReOpenFileBuffered(nodesx->filename_tmp);
 
  /* Write out the nodes data */
 
- fd=OpenFileNew(filename);
+ fd=OpenFileBufferedNew(filename);
 
- SeekFile(fd,sizeof(NodesFile)+(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
+ SeekFileBuffered(fd,sizeof(NodesFile)+(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
 
  for(i=0;i<nodesx->number;i++)
    {
@@ -698,13 +778,13 @@ void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx)
     ll_bin_t latbin,lonbin;
     ll_bin2_t llbin;
 
-    ReadFile(nodesx->fd,&nodex,sizeof(NodeX));
+    ReadFileBuffered(nodesx->fd,&nodex,sizeof(NodeX));
 
     /* Create the Node */
 
     node.latoffset=latlong_to_off(nodex.latitude);
     node.lonoffset=latlong_to_off(nodex.longitude);
-    node.firstseg=segmentsx->firstnode[nodesx->gdata[nodex.id]];
+    node.firstseg=segmentsx->firstnode[i];
     node.allow=nodex.allow;
     node.flags=nodex.flags;
 
@@ -722,7 +802,7 @@ void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx)
 
     /* Write the data */
 
-    WriteFile(fd,&node,sizeof(Node));
+    WriteFileBuffered(fd,&node,sizeof(Node));
 
     if(!((i+1)%10000))
        printf_middle("Writing Nodes: Nodes=%"Pindex_t,i+1);
@@ -730,7 +810,7 @@ void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx)
 
  /* Close the file */
 
- nodesx->fd=CloseFile(nodesx->fd);
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
 
  /* Finish off the offset indexing and write them out */
 
@@ -739,8 +819,8 @@ void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx)
  for(;latlonbin<=maxlatlonbins;latlonbin++)
     offsets[latlonbin]=nodesx->number;
 
- SeekFile(fd,sizeof(NodesFile));
- WriteFile(fd,offsets,(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
+ SeekFileBuffered(fd,sizeof(NodesFile));
+ WriteFileBuffered(fd,offsets,(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
 
  free(offsets);
 
@@ -755,10 +835,10 @@ void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx)
  nodesfile.latzero=nodesx->latzero;
  nodesfile.lonzero=nodesx->lonzero;
 
- SeekFile(fd,0);
- WriteFile(fd,&nodesfile,sizeof(NodesFile));
+ SeekFileBuffered(fd,0);
+ WriteFileBuffered(fd,&nodesfile,sizeof(NodesFile));
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
diff --git a/src/nodesx.h b/src/nodesx.h
index a2dfc34..84bd380 100644
--- a/src/nodesx.h
+++ b/src/nodesx.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -30,6 +30,7 @@
 
 #include "typesx.h"
 
+#include "cache.h"
 #include "files.h"
 
 
@@ -57,6 +58,7 @@ struct _NodesX
  int       fd;                  /*+ The file descriptor of the open file (for the NodesX). +*/
 
  index_t   number;              /*+ The number of extended nodes still being considered. +*/
+ index_t   knumber;             /*+ The number of extended nodes kept for next time. +*/
 
 #if !SLIM
 
@@ -67,6 +69,8 @@ struct _NodesX
  NodeX     cached[3];           /*+ Three cached extended nodes read from the file in slim mode. +*/
  index_t   incache[3];          /*+ The indexes of the cached extended nodes. +*/
 
+ NodeXCache *cache;             /*+ A RAM cache of extended nodes read from the file. +*/
+
 #endif
 
  node_t   *idata;               /*+ The extended node IDs (sorted by ID). +*/
@@ -97,7 +101,7 @@ index_t IndexNodeX(NodesX *nodesx,node_t id);
 
 void SortNodeList(NodesX *nodesx);
 
-void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,int keep);
+void RemoveNonHighwayNodes(NodesX *nodesx,WaysX *waysx,int keep);
 
 void RemovePrunedNodes(NodesX *nodesx,SegmentsX *segmentsx);
 
@@ -112,13 +116,31 @@ void SaveNodeList(NodesX *nodesx,const char *filename,SegmentsX *segmentsx);
 
 #define LookupNodeX(nodesx,index,position)      &(nodesx)->data[index]
   
-#define PutBackNodeX(nodesx,nodex)              /* nop */
+#define PutBackNodeX(nodesx,nodex)              while(0) { /* nop */ }
 
 #else
 
-static NodeX *LookupNodeX(NodesX *nodesx,index_t index,int position);
+/* Prototypes */
+
+static inline NodeX *LookupNodeX(NodesX *nodesx,index_t index,int position);
+
+static inline void PutBackNodeX(NodesX *nodesx,NodeX *nodex);
+
+CACHE_NEWCACHE_PROTO(NodeX)
+CACHE_DELETECACHE_PROTO(NodeX)
+CACHE_FETCHCACHE_PROTO(NodeX)
+CACHE_REPLACECACHE_PROTO(NodeX)
+CACHE_INVALIDATECACHE_PROTO(NodeX)
+
+
+/* Inline functions */
 
-static void PutBackNodeX(NodesX *nodesx,NodeX *nodex);
+CACHE_STRUCTURE(NodeX)
+CACHE_NEWCACHE(NodeX)
+CACHE_DELETECACHE(NodeX)
+CACHE_FETCHCACHE(NodeX)
+CACHE_REPLACECACHE(NodeX)
+CACHE_INVALIDATECACHE(NodeX)
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -135,7 +157,7 @@ static void PutBackNodeX(NodesX *nodesx,NodeX *nodex);
 
 static inline NodeX *LookupNodeX(NodesX *nodesx,index_t index,int position)
 {
- SeekReadFile(nodesx->fd,&nodesx->cached[position-1],sizeof(NodeX),(off_t)index*sizeof(NodeX));
+ nodesx->cached[position-1]=*FetchCachedNodeX(nodesx->cache,index,nodesx->fd,0);
 
  nodesx->incache[position-1]=index;
 
@@ -155,7 +177,7 @@ static inline void PutBackNodeX(NodesX *nodesx,NodeX *nodex)
 {
  int position1=nodex-&nodesx->cached[0];
 
- SeekWriteFile(nodesx->fd,&nodesx->cached[position1],sizeof(NodeX),(off_t)nodesx->incache[position1]*sizeof(NodeX));
+ ReplaceCachedNodeX(nodesx->cache,nodex,nodesx->incache[position1],nodesx->fd,0);
 }
 
 #endif /* SLIM */
diff --git a/src/optimiser.c b/src/optimiser.c
index fc7cdd7..1e8dfa3 100644
--- a/src/optimiser.c
+++ b/src/optimiser.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -32,6 +32,10 @@
 #include "results.h"
 
 
+/*+ To help when debugging +*/
+#define DEBUG 0
+
+
 /* Global variables */
 
 /*+ The option not to print any progress information. +*/
@@ -43,8 +47,8 @@ extern int option_quickest;
 
 /* Local functions */
 
-static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,index_t finish_node,index_t finish_segment);
-static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,index_t start_node,index_t finish_node);
+static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t finish_node,index_t finish_segment);
+static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t finish_node);
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -79,6 +83,10 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
  Result  *result1,*result2;
  int     force_uturn=0;
 
+#if DEBUG
+ printf("  FindNormalRoute(...,start_node=%"Pindex_t" prev_segment=%"Pindex_t" finish_node=%"Pindex_t")\n",start_node,prev_segment,finish_node);
+#endif
+
  /* Set up the finish conditions */
 
  finish_score=INF_SCORE;
@@ -87,19 +95,19 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
  if(IsFakeNode(finish_node))
     GetFakeLatLong(finish_node,&finish_lat,&finish_lon);
  else
-    GetLatLong(nodes,finish_node,&finish_lat,&finish_lon);
+    GetLatLong(nodes,finish_node,NULL,&finish_lat,&finish_lon);
 
  /* Create the list of results and insert the first node into the queue */
 
- results=NewResultsList(64);
- queue=NewQueueList();
+ results=NewResultsList(8);
+ queue=NewQueueList(8);
 
  results->start_node=start_node;
  results->prev_segment=prev_segment;
 
  result1=InsertResult(results,results->start_node,results->prev_segment);
 
- InsertInQueue(queue,result1);
+ InsertInQueue(queue,result1,0);
 
  /* Check for barrier at start waypoint - must perform U-turn */
 
@@ -162,7 +170,15 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
        /* must obey one-way restrictions (unless profile allows) */
        if(profile->oneway && IsOnewayTo(segmentp,node1))
-          goto endloop;
+         {
+          if(profile->allow!=Transports_Bicycle)
+             goto endloop;
+
+          wayp=LookupWay(ways,segmentp->way,1);
+
+          if(!(wayp->props&Properties_CycleBothWays))
+             goto endloop;
+         }
 
        if(IsFakeNode(node1) || IsFakeNode(node2))
          {
@@ -220,13 +236,10 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
           goto endloop;
 
        for(i=1;i<Property_Count;i++)
-          if(ways->file.props & PROPERTIES(i))
-            {
-             if(wayp->props & PROPERTIES(i))
-                segment_pref*=profile->props_yes[i];
-             else
-                segment_pref*=profile->props_no[i];
-            }
+          if(wayp->props & PROPERTIES(i))
+             segment_pref*=profile->props_yes[i];
+          else
+             segment_pref*=profile->props_no[i];
 
        /* profile preferences must allow this highway */
        if(segment_pref==0)
@@ -254,35 +267,23 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
           result2=InsertResult(results,node2,seg2);
           result2->prev=result1;
           result2->score=cumulative_score;
-
-          if(node2==finish_node)
-            {
-             finish_score=cumulative_score;
-             finish_result=result2;
-            }
-          else
-            {
-             result2->sortby=result2->score;
-             InsertInQueue(queue,result2);
-            }
          }
        else if(cumulative_score<result2->score) /* New score for end node/segment combination is better */
          {
           result2->prev=result1;
           result2->score=cumulative_score;
           result2->segment=seg2;
+         }
+       else
+          goto endloop;
 
-          if(node2==finish_node)
-            {
-             finish_score=cumulative_score;
-             finish_result=result2;
-            }
-          else
-            {
-             result2->sortby=result2->score;
-             InsertInQueue(queue,result2);
-            }
+       if(node2==finish_node)
+         {
+          finish_score=cumulative_score;
+          finish_result=result2;
          }
+       else
+          InsertInQueue(queue,result2,result2->score);
 
       endloop:
 
@@ -306,12 +307,27 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
  if(!finish_result)
    {
+#if DEBUG
+    printf("    Failed\n");
+#endif
+
     FreeResultsList(results);
     return(NULL);
    }
 
  FixForwardRoute(results,finish_result);
 
+#if DEBUG
+ Result *r=FindResult(results,results->start_node,results->prev_segment);
+
+ while(r)
+   {
+    printf("    node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+    r=r->next;
+   }
+#endif
+
  return(results);
 }
 
@@ -346,8 +362,14 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
  Result  *result1,*result2,*result3,*result4;
  int     force_uturn=0;
 
+#if DEBUG
+ printf("  FindMiddleRoute(...,[begin has %d nodes],[end has %d nodes])\n",begin->number,end->number);
+#endif
+
+#if !DEBUG
  if(!option_quiet)
     printf_first("Routing: Super-Nodes checked = 0");
+#endif
 
  /* Set up the finish conditions */
 
@@ -357,12 +379,12 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
  if(IsFakeNode(end->finish_node))
     GetFakeLatLong(end->finish_node,&finish_lat,&finish_lon);
  else
-    GetLatLong(nodes,end->finish_node,&finish_lat,&finish_lon);
+    GetLatLong(nodes,end->finish_node,NULL,&finish_lat,&finish_lon);
 
  /* Create the list of results and insert the first node into the queue */
 
- results=NewResultsList(65536);
- queue=NewQueueList();
+ results=NewResultsList(20);
+ queue=NewQueueList(12);
 
  results->start_node=begin->start_node;
  results->prev_segment=begin->prev_segment;
@@ -373,7 +395,7 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
        results->prev_segment=NO_SEGMENT;
     else
       {
-       index_t superseg=FindSuperSegment(nodes,segments,ways,relations,begin->start_node,begin->prev_segment);
+       index_t superseg=FindSuperSegment(nodes,segments,ways,relations,profile,begin->start_node,begin->prev_segment);
 
        results->prev_segment=superseg;
       }
@@ -392,7 +414,7 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
        !IsFakeNode(result3->node) && IsSuperNode(LookupNode(nodes,result3->node,5)))
       {
        Result *result5=result1;
-       index_t superseg=FindSuperSegment(nodes,segments,ways,relations,result3->node,result3->segment);
+       index_t superseg=FindSuperSegment(nodes,segments,ways,relations,profile,result3->node,result3->segment);
 
        if(superseg!=result3->segment)
          {
@@ -407,9 +429,8 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
           result2->prev=result5;
 
           result2->score=result3->score;
-          result2->sortby=result3->score;
 
-          InsertInQueue(queue,result2);
+          InsertInQueue(queue,result2,result3->score);
 
           if((result4=FindResult(end,result2->node,result2->segment)))
             {
@@ -426,7 +447,7 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
    }
 
  if(begin->number==1)
-    InsertInQueue(queue,result1);
+    InsertInQueue(queue,result1,0);
 
  /* Check for barrier at start waypoint - must perform U-turn */
 
@@ -478,7 +499,15 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
        /* must obey one-way restrictions (unless profile allows) */
        if(profile->oneway && IsOnewayTo(segmentp,node1))
-          goto endloop;
+         {
+          if(profile->allow!=Transports_Bicycle)
+             goto endloop;
+
+          wayp=LookupWay(ways,segmentp->way,1);
+
+          if(!(wayp->props&Properties_CycleBothWays))
+             goto endloop;
+         }
 
        seg2=IndexSegment(segments,segmentp); /* segment cannot be a fake segment (must be a super-segment) */
 
@@ -520,13 +549,10 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
           goto endloop;
 
        for(i=1;i<Property_Count;i++)
-          if(ways->file.props & PROPERTIES(i))
-            {
-             if(wayp->props & PROPERTIES(i))
-                segment_pref*=profile->props_yes[i];
-             else
-                segment_pref*=profile->props_no[i];
-            }
+          if(wayp->props & PROPERTIES(i))
+             segment_pref*=profile->props_yes[i];
+          else
+             segment_pref*=profile->props_no[i];
 
        /* profile preferences must allow this highway */
        if(segment_pref==0)
@@ -558,67 +584,46 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
           result2=InsertResult(results,node2,seg2);
           result2->prev=result1;
           result2->score=cumulative_score;
-
-          if((result3=FindResult(end,node2,seg2)))
-            {
-             if((result2->score+result3->score)<finish_score)
-               {
-                finish_score=result2->score+result3->score;
-                finish_result=result2;
-               }
-            }
-          else
-            {
-             double lat,lon;
-             distance_t direct;
-
-             GetLatLong(nodes,node2,&lat,&lon); /* node2 cannot be a fake node (must be a super-node) */
-
-             direct=Distance(lat,lon,finish_lat,finish_lon);
-
-             if(option_quickest==0)
-                result2->sortby=result2->score+(score_t)direct/profile->max_pref;
-             else
-                result2->sortby=result2->score+(score_t)distance_speed_to_duration(direct,profile->max_speed)/profile->max_pref;
-
-             if(result2->sortby<finish_score)
-                InsertInQueue(queue,result2);
-            }
          }
        else if(cumulative_score<result2->score) /* New end node/segment pair is better */
          {
           result2->prev=result1;
           result2->score=cumulative_score;
+         }
+       else
+          goto endloop;
 
-          if((result3=FindResult(end,node2,seg2)))
+       if((result3=FindResult(end,node2,seg2)))
+         {
+          if((result2->score+result3->score)<finish_score)
             {
-             if((result2->score+result3->score)<finish_score)
-               {
-                finish_score=result2->score+result3->score;
-                finish_result=result2;
-               }
+             finish_score=result2->score+result3->score;
+             finish_result=result2;
             }
-          else if(result2->score<finish_score)
-            {
-             double lat,lon;
-             distance_t direct;
+         }
+       else
+         {
+          double lat,lon;
+          distance_t direct;
+          score_t potential_score;
 
-             GetLatLong(nodes,node2,&lat,&lon); /* node2 cannot be a fake node (must be a super-node) */
+          GetLatLong(nodes,node2,node2p,&lat,&lon); /* node2 cannot be a fake node (must be a super-node) */
 
-             direct=Distance(lat,lon,finish_lat,finish_lon);
+          direct=Distance(lat,lon,finish_lat,finish_lon);
 
-             if(option_quickest==0)
-                result2->sortby=result2->score+(score_t)direct/profile->max_pref;
-             else
-                result2->sortby=result2->score+(score_t)distance_speed_to_duration(direct,profile->max_speed)/profile->max_pref;
+          if(option_quickest==0)
+             potential_score=result2->score+(score_t)direct/profile->max_pref;
+          else
+             potential_score=result2->score+(score_t)distance_speed_to_duration(direct,profile->max_speed)/profile->max_pref;
 
-             if(result2->sortby<finish_score)
-                InsertInQueue(queue,result2);
-            }
+          if(potential_score<finish_score)
+             InsertInQueue(queue,result2,potential_score);
          }
 
+#if !DEBUG
        if(!option_quiet && !(results->number%1000))
           printf_middle("Routing: Super-Nodes checked = %d",results->number);
+#endif
 
       endloop:
 
@@ -626,8 +631,10 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
       }
    }
 
+#if !DEBUG
  if(!option_quiet)
     printf_last("Routing: Super-Nodes checked = %d",results->number);
+#endif
 
  FreeQueueList(queue);
 
@@ -635,6 +642,10 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
  if(!finish_result)
    {
+#if DEBUG
+    printf("    Failed\n");
+#endif
+
     FreeResultsList(results);
     return(NULL);
    }
@@ -653,6 +664,17 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
  FixForwardRoute(results,finish_result);
 
+#if DEBUG
+ Result *r=FindResult(results,results->start_node,results->prev_segment);
+
+ while(r)
+   {
+    printf("    node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+    r=r->next;
+   }
+#endif
+
  return(results);
 }
 
@@ -670,12 +692,14 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
   Relations *relations The set of relations to use.
 
+  Profile *profile The profile containing the transport type, speeds and allowed highways.
+
   index_t finish_node The super-node that the route ends at.
 
   index_t finish_segment The segment that the route ends with.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,index_t finish_node,index_t finish_segment)
+static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t finish_node,index_t finish_segment)
 {
  Node *supernodep;
  Segment *supersegmentp;
@@ -703,7 +727,7 @@ static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relat
 
        start_node=OtherNode(supersegmentp,finish_node);
 
-       results=FindSuperRoute(nodes,segments,ways,relations,start_node,finish_node);
+       results=FindSuperRoute(nodes,segments,ways,relations,profile,start_node,finish_node);
 
        if(!results)
           continue;
@@ -741,28 +765,34 @@ static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relat
 
   Relations *relations The set of relations to use.
 
+  Profile *profile The profile containing the transport type, speeds and allowed highways.
+
   index_t start_node The start node.
 
   index_t finish_node The finish node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,index_t start_node,index_t finish_node)
+static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t finish_node)
 {
  Results *results;
  Queue   *queue;
  Result  *result1,*result2;
 
+#if DEBUG
+ printf("    FindSuperRoute(...,start_node=%"Pindex_t" finish_node=%"Pindex_t")\n",start_node,finish_node);
+#endif
+
  /* Create the list of results and insert the first node into the queue */
 
- results=NewResultsList(64);
- queue=NewQueueList();
+ results=NewResultsList(8);
+ queue=NewQueueList(8);
 
  results->start_node=start_node;
  results->prev_segment=NO_SEGMENT;
 
  result1=InsertResult(results,results->start_node,results->prev_segment);
 
- InsertInQueue(queue,result1);
+ InsertInQueue(queue,result1,0);
 
  /* Loop across all nodes in the queue */
 
@@ -793,7 +823,17 @@ static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relati
 
        /* must obey one-way restrictions */
        if(IsOnewayTo(segmentp,node1))
-          goto endloop;
+         {
+          Way *wayp;
+
+          if(profile->allow!=Transports_Bicycle)
+             goto endloop;
+
+          wayp=LookupWay(ways,segmentp->way,1);
+
+          if(!(wayp->props&Properties_CycleBothWays))
+             goto endloop;
+         }
 
        seg2=IndexSegment(segments,segmentp);
 
@@ -819,23 +859,18 @@ static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relati
           result2=InsertResult(results,node2,seg2);
           result2->prev=result1;
           result2->score=cumulative_score;
-          result2->sortby=result2->score;
-
-          /* don't route beyond a super-node. */
-          if(!IsSuperNode(node2p))
-             InsertInQueue(queue,result2);
          }
        else if(cumulative_score<result2->score) /* New score for end node/segment combination is better */
          {
           result2->prev=result1;
           result2->segment=seg2;
           result2->score=cumulative_score;
-          result2->sortby=result2->score;
-
-          /* don't route beyond a super-node. */
-          if(!IsSuperNode(node2p))
-             InsertInQueue(queue,result2);
          }
+       else goto endloop;
+
+       /* don't route beyond a super-node. */
+       if(!IsSuperNode(node2p))
+          InsertInQueue(queue,result2,result2->score);
 
       endloop:
 
@@ -845,6 +880,17 @@ static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relati
 
  FreeQueueList(queue);
 
+#if DEBUG
+ Result *r=FindResult(results,results->start_node,results->prev_segment);
+
+ while(r)
+   {
+    printf("      node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+    r=r->next;
+   }
+#endif
+
  return(results);
 }
 
@@ -869,28 +915,30 @@ static Results *FindSuperRoute(Nodes *nodes,Segments *segments,Ways *ways,Relati
   index_t prev_segment The previous segment before the start node.
 
   index_t finish_node The finish node.
-
-  int *nsuper Returns the number of super-nodes seen.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t prev_segment,index_t finish_node,int *nsuper)
+Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t prev_segment,index_t finish_node)
 {
  Results *results;
  Queue   *queue;
  Result  *result1,*result2;
- int     found_finish=0,force_uturn=0;
+ int     nsuper=0,force_uturn=0;
+
+#if DEBUG
+ printf("  FindStartRoutes(...,start_node=%"Pindex_t" prev_segment=%"Pindex_t" finish_node=%"Pindex_t")\n",start_node,prev_segment,finish_node);
+#endif
 
  /* Create the list of results and insert the first node into the queue */
 
- results=NewResultsList(64);
- queue=NewQueueList();
+ results=NewResultsList(8);
+ queue=NewQueueList(8);
 
  results->start_node=start_node;
  results->prev_segment=prev_segment;
 
  result1=InsertResult(results,results->start_node,results->prev_segment);
 
- InsertInQueue(queue,result1);
+ InsertInQueue(queue,result1,0);
 
  /* Check for barrier at start waypoint - must perform U-turn */
 
@@ -949,7 +997,15 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
        /* must obey one-way restrictions (unless profile allows) */
        if(profile->oneway && IsOnewayTo(segmentp,node1))
-          goto endloop;
+         {
+          if(profile->allow!=Transports_Bicycle)
+             goto endloop;
+
+          wayp=LookupWay(ways,segmentp->way,1);
+
+          if(!(wayp->props&Properties_CycleBothWays))
+             goto endloop;
+         }
 
        if(IsFakeNode(node1) || IsFakeNode(node2))
          {
@@ -1000,13 +1056,10 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
           goto endloop;
 
        for(i=1;i<Property_Count;i++)
-          if(ways->file.props & PROPERTIES(i))
-            {
-             if(wayp->props & PROPERTIES(i))
-                segment_pref*=profile->props_yes[i];
-             else
-                segment_pref*=profile->props_no[i];
-            }
+          if(wayp->props & PROPERTIES(i))
+             segment_pref*=profile->props_yes[i];
+          else
+             segment_pref*=profile->props_no[i];
 
        /* profile preferences must allow this highway */
        if(segment_pref==0)
@@ -1035,28 +1088,33 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
           result2->score=cumulative_score;
 
           if(node2p && IsSuperNode(node2p))
-             (*nsuper)++;
+             nsuper++;
 
-          if(node2p && !IsSuperNode(node2p))
-            {
-             result2->sortby=result2->score;
-             InsertInQueue(queue,result2);
-            }
+          if(node2==finish_node)
+             nsuper++;
 
           if(node2==finish_node)
-             found_finish=1;
+            {
+             results->finish_node=finish_node;
+             results->last_segment=seg2;
+            }
          }
        else if(cumulative_score<result2->score) /* New score for end node/segment combination is better */
          {
           result2->prev=result1;
           result2->score=cumulative_score;
 
-          if(node2p && !IsSuperNode(node2p))
+          if(node2==finish_node)
             {
-             result2->sortby=result2->score;
-             InsertInQueue(queue,result2);
+             results->finish_node=finish_node;
+             results->last_segment=seg2;
             }
          }
+       else
+          goto endloop;
+
+       if(node2p && !IsSuperNode(node2p))
+          InsertInQueue(queue,result2,result2->score);
 
       endloop:
 
@@ -1078,12 +1136,304 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *r
 
  /* Check it worked */
 
- if(results->number==1 || (*nsuper==0 && found_finish==0))
+ if(results->number==1 || nsuper==0)
    {
+#if DEBUG
+    printf("    Failed (%d results, %d super)\n",results->number,nsuper);
+#endif
+
     FreeResultsList(results);
     return(NULL);
    }
 
+#if DEBUG
+ Result *s=FirstResult(results);
+
+ while(s)
+   {
+    if(s->node==finish_node)
+      {
+       Result *r=FindResult(results,s->node,s->segment);
+
+       printf("    -------- route to finish node\n");
+
+       while(r)
+         {
+          printf("    node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+          r=r->prev;
+         }
+      }
+
+    if(!IsFakeNode(s->node))
+      {
+       Node *n=LookupNode(nodes,s->node,1);
+
+       if(IsSuperNode(n))
+         {
+          Result *r=FindResult(results,s->node,s->segment);
+
+          printf("    -------- route to super node\n");
+
+          while(r)
+            {
+             printf("    node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+             r=r->prev;
+            }
+         }
+      }
+
+    s=NextResult(results,s);
+   }
+#endif
+
+ return(results);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Continue finding routes from a set of super-nodes to a finish point.
+
+  Results *ExtendStartRoutes Returns the set of results that were passed in.
+
+  Nodes *nodes The set of nodes to use.
+
+  Segments *segments The set of segments to use.
+
+  Ways *ways The set of ways to use.
+
+  Relations *relations The set of relations to use.
+
+  Profile *profile The profile containing the transport type, speeds and allowed highways.
+
+  Results *begin The partial set of routes already computed.
+
+  index_t finish_node The finish node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+Results *ExtendStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,Results *begin,index_t finish_node)
+{
+ Results *results=begin;
+ Queue   *queue;
+ Result  *result1,*result2,*result3;
+ Result  *finish_result=NULL;
+ score_t finish_score=INF_SCORE;
+
+#if DEBUG
+ printf("  ExtendStartRoutes(...,[begin has %d nodes],finish_node=%"Pindex_t")\n",begin->number,finish_node);
+#endif
+
+ /* Check the list of results and insert the super nodes into the queue */
+
+ queue=NewQueueList(8);
+
+ result3=FirstResult(begin);
+
+ while(result3)
+   {
+    if(result3->node==finish_node)
+       if(result3->score<finish_score)
+         {
+          finish_score=result3->score;
+          finish_result=result3;
+         }
+
+    if(!IsFakeNode(result3->node))
+       if(IsSuperNode(LookupNode(nodes,result3->node,5)))
+          InsertInQueue(queue,result3,result3->score);
+
+    result3=NextResult(results,result3);
+   }
+
+ /* Loop across all nodes in the queue */
+
+ while((result1=PopFromQueue(queue)))
+   {
+    Node *node1p=NULL;
+    Segment *segmentp;
+    index_t node1,seg1,seg1r;
+    index_t turnrelation=NO_RELATION;
+
+    /* score must be better than current best score */
+    if(result1->score>=finish_score)
+       continue;
+
+    node1=result1->node;
+    seg1=result1->segment;
+
+    if(IsFakeSegment(seg1))
+       seg1r=IndexRealSegment(seg1);
+    else
+       seg1r=seg1;
+
+    if(!IsFakeNode(node1))
+       node1p=LookupNode(nodes,node1,1);
+
+    /* lookup if a turn restriction applies */
+    if(profile->turns && node1p && IsTurnRestrictedNode(node1p))
+       turnrelation=FindFirstTurnRelation2(relations,node1,seg1r);
+
+    /* Loop across all segments */
+
+    if(IsFakeNode(node1))
+       segmentp=FirstFakeSegment(node1);
+    else
+       segmentp=FirstSegment(segments,node1p,1);
+
+    while(segmentp)
+      {
+       Node *node2p=NULL;
+       Way *wayp;
+       index_t node2,seg2,seg2r;
+       score_t segment_pref,segment_score,cumulative_score;
+       int i;
+
+       node2=OtherNode(segmentp,node1); /* need this here because we use node2 at the end of the loop */
+
+       /* must be a normal segment */
+       if(!IsNormalSegment(segmentp))
+          goto endloop;
+
+       /* must obey one-way restrictions (unless profile allows) */
+       if(profile->oneway && IsOnewayTo(segmentp,node1))
+         {
+          if(profile->allow!=Transports_Bicycle)
+             goto endloop;
+
+          wayp=LookupWay(ways,segmentp->way,1);
+
+          if(!(wayp->props&Properties_CycleBothWays))
+             goto endloop;
+         }
+
+       if(IsFakeNode(node1) || IsFakeNode(node2))
+         {
+          seg2 =IndexFakeSegment(segmentp);
+          seg2r=IndexRealSegment(seg2);
+         }
+       else
+         {
+          seg2 =IndexSegment(segments,segmentp);
+          seg2r=seg2;
+         }
+
+       /* must not perform U-turn (unless profile allows) */
+       if(profile->turns && (seg1==seg2 || seg1==seg2r || seg1r==seg2 || (seg1r==seg2r && IsFakeUTurn(seg1,seg2))))
+          goto endloop;
+
+       /* must obey turn relations */
+       if(turnrelation!=NO_RELATION && !IsTurnAllowed(relations,turnrelation,node1,seg1r,seg2r,profile->allow))
+          goto endloop;
+
+       wayp=LookupWay(ways,segmentp->way,1);
+
+       /* mode of transport must be allowed on the highway */
+       if(!(wayp->allow&profile->allow))
+          goto endloop;
+
+       /* must obey weight restriction (if exists) */
+       if(wayp->weight && wayp->weight<profile->weight)
+          goto endloop;
+
+       /* must obey height/width/length restriction (if exists) */
+       if((wayp->height && wayp->height<profile->height) ||
+          (wayp->width  && wayp->width <profile->width ) ||
+          (wayp->length && wayp->length<profile->length))
+          goto endloop;
+
+       segment_pref=profile->highway[HIGHWAY(wayp->type)];
+
+       /* highway preferences must allow this highway */
+       if(segment_pref==0)
+          goto endloop;
+
+       for(i=1;i<Property_Count;i++)
+          if(wayp->props & PROPERTIES(i))
+             segment_pref*=profile->props_yes[i];
+          else
+             segment_pref*=profile->props_no[i];
+
+       /* profile preferences must allow this highway */
+       if(segment_pref==0)
+          goto endloop;
+
+       if(!IsFakeNode(node2))
+          node2p=LookupNode(nodes,node2,2);
+
+       /* mode of transport must be allowed through node2 unless it is the final node */
+       if(node2p && node2!=finish_node && !(node2p->allow&profile->allow))
+          goto endloop;
+
+       if(option_quickest==0)
+          segment_score=(score_t)DISTANCE(segmentp->distance)/segment_pref;
+       else
+          segment_score=(score_t)Duration(segmentp,wayp,profile)/segment_pref;
+
+       cumulative_score=result1->score+segment_score;
+
+       /* score must be better than current best score */
+       if(cumulative_score>=finish_score)
+          goto endloop;
+
+       result2=FindResult(results,node2,seg2);
+
+       if(!result2) /* New end node/segment combination */
+         {
+          result2=InsertResult(results,node2,seg2);
+          result2->prev=result1;
+          result2->score=cumulative_score;
+         }
+       else if(cumulative_score<result2->score) /* New score for end node/segment combination is better */
+         {
+          result2->prev=result1;
+          result2->score=cumulative_score;
+         }
+       else
+          goto endloop;
+
+       if(node2==finish_node)
+         {
+          if(cumulative_score<finish_score)
+            {
+             finish_score=cumulative_score;
+             finish_result=result2;
+            }
+         }
+       else
+          InsertInQueue(queue,result2,result2->score);
+
+      endloop:
+
+       if(IsFakeNode(node1))
+          segmentp=NextFakeSegment(segmentp,node1);
+       else if(IsFakeNode(node2))
+          segmentp=NULL; /* cannot call NextSegment() with a fake segment */
+       else
+         {
+          segmentp=NextSegment(segments,segmentp,node1);
+
+          if(!segmentp && IsFakeNode(finish_node))
+             segmentp=ExtraFakeSegment(node1,finish_node);
+         }
+      }
+   }
+
+ FreeQueueList(queue);
+
+ FixForwardRoute(results,finish_result);
+
+#if DEBUG
+ Result *r=FindResult(results,results->start_node,results->prev_segment);
+
+ while(r)
+   {
+    printf("    node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+    r=r->next;
+   }
+#endif
+
  return(results);
 }
 
@@ -1112,16 +1462,20 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *
  Queue   *queue;
  Result  *result1,*result2,*result3;
 
+#if DEBUG
+ printf("  FindFinishRoutes(...,finish_node=%"Pindex_t")\n",finish_node);
+#endif
+
  /* Create the results and insert the finish node into the queue */
 
- results=NewResultsList(64);
- queue=NewQueueList();
+ results=NewResultsList(8);
+ queue=NewQueueList(8);
 
  results->finish_node=finish_node;
 
  result1=InsertResult(results,finish_node,NO_SEGMENT);
 
- InsertInQueue(queue,result1);
+ InsertInQueue(queue,result1,0);
 
  /* Loop across all nodes in the queue */
 
@@ -1172,7 +1526,15 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *
 
        /* must obey one-way restrictions (unless profile allows) */
        if(profile->oneway && IsOnewayFrom(segmentp,node1)) /* working backwards => disallow oneway *from* node1 */
-          goto endloop;
+         {
+          if(profile->allow!=Transports_Bicycle)
+             goto endloop;
+
+          wayp=LookupWay(ways,segmentp->way,1);
+
+          if(!(wayp->props&Properties_CycleBothWays))
+             goto endloop;
+         }
 
        node2=OtherNode(segmentp,node1);
 
@@ -1223,13 +1585,10 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *
           goto endloop;
 
        for(i=1;i<Property_Count;i++)
-          if(ways->file.props & PROPERTIES(i))
-            {
-             if(wayp->props & PROPERTIES(i))
-                segment_pref*=profile->props_yes[i];
-             else
-                segment_pref*=profile->props_no[i];
-            }
+          if(wayp->props & PROPERTIES(i))
+             segment_pref*=profile->props_yes[i];
+          else
+             segment_pref*=profile->props_no[i];
 
        /* profile preferences must allow this highway */
        if(segment_pref==0)
@@ -1256,24 +1615,17 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *
           result2=InsertResult(results,node2,seg2);
           result2->next=result1;   /* working backwards */
           result2->score=cumulative_score;
-
-          if(IsFakeNode(node1) || !IsSuperNode(node1p))
-            {
-             result2->sortby=result2->score;
-             InsertInQueue(queue,result2);
-            }
          }
        else if(cumulative_score<result2->score) /* New end node is better */
          {
           result2->next=result1; /* working backwards */
           result2->score=cumulative_score;
-
-          if(IsFakeNode(node1) || !IsSuperNode(node1p))
-            {
-             result2->sortby=result2->score;
-             InsertInQueue(queue,result2);
-            }
          }
+       else
+          goto endloop;
+
+       if(IsFakeNode(node1) || !IsSuperNode(node1p))
+          InsertInQueue(queue,result2,result2->score);
 
       endloop:
 
@@ -1290,13 +1642,17 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *
 
  if(results->number==1)
    {
+#if DEBUG
+    printf("    Failed\n");
+#endif
+
     FreeResultsList(results);
     return(NULL);
    }
 
  /* Create a results structure with the node at the end of the segment opposite the start */
 
- results2=NewResultsList(64);
+ results2=NewResultsList(8);
 
  results2->finish_node=results->finish_node;
 
@@ -1333,6 +1689,34 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *
 
  FreeResultsList(results);
 
+#if DEBUG
+ Result *s=FirstResult(results2);
+
+ while(s)
+   {
+    if(!IsFakeNode(s->node))
+      {
+       Node *n=LookupNode(nodes,s->node,1);
+
+       if(IsSuperNode(n))
+         {
+          Result *r=FindResult(results2,s->node,s->segment);
+
+          printf("    --------\n");
+
+          while(r)
+            {
+             printf("    node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+             r=r->next;
+            }
+         }
+      }
+
+    s=NextResult(results2,s);
+   }
+#endif
+
  return(results2);
 }
 
@@ -1362,7 +1746,11 @@ Results *CombineRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *rel
  Result *midres,*comres1;
  Results *combined;
 
- combined=NewResultsList(256);
+#if DEBUG
+ printf("  CombineRoutes(...,[begin has %d nodes],[middle has %d nodes])\n",begin->number,middle->number);
+#endif
+
+ combined=NewResultsList(10);
 
  combined->start_node=begin->start_node;
  combined->prev_segment=begin->prev_segment;
@@ -1398,7 +1786,7 @@ Results *CombineRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *rel
 
        comres2=InsertResult(combined,begres->node,begres->segment);
 
-       comres2->score=begres->score+comres1->score;
+       comres2->score=begres->score;
        comres2->prev=comres1;
 
        begres=begres->next;
@@ -1445,7 +1833,7 @@ Results *CombineRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *rel
 
           comres2=InsertResult(combined,result->node,result->segment);
 
-          comres2->score=result->score+comres1->score;
+          comres2->score=midres->score+result->score;
           comres2->prev=comres1;
 
           result=result->next;
@@ -1463,6 +1851,17 @@ Results *CombineRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *rel
 
  FixForwardRoute(combined,comres1);
 
+#if DEBUG
+ Result *r=FindResult(combined,combined->start_node,combined->prev_segment);
+
+ while(r)
+   {
+    printf("    node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+    r=r->next;
+   }
+#endif
+
  return(combined);
 }
 
diff --git a/src/osmo5mparse.c b/src/osmo5mparse.c
new file mode 100644
index 0000000..26c7467
--- /dev/null
+++ b/src/osmo5mparse.c
@@ -0,0 +1,856 @@
+/***************************************
+ A simple o5m/o5c parser.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2012-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include "osmparser.h"
+#include "tagging.h"
+#include "logging.h"
+
+
+/* At the top level */
+
+#define O5M_FILE_NODE           0x10
+#define O5M_FILE_WAY            0x11
+#define O5M_FILE_RELATION       0x12
+#define O5M_FILE_BOUNDING_BOX   0xdb
+#define O5M_FILE_TIMESTAMP      0xdc
+#define O5M_FILE_HEADER         0xe0
+#define O5M_FILE_SYNC           0xee
+#define O5M_FILE_JUMP           0xef
+#define O5M_FILE_END            0xfe
+#define O5M_FILE_RESET          0xff
+
+/* Errors */
+
+#define O5M_EOF                     0
+
+#define O5M_ERROR_UNEXP_EOF        100
+#define O5M_ERROR_RESET_NOT_FIRST  101
+#define O5M_ERROR_HEADER_NOT_FIRST 102
+#define O5M_ERROR_EXPECTED_O5M     103
+#define O5M_ERROR_EXPECTED_O5C     104
+#define O5M_ERROR_FILE_LEVEL       105
+
+
+
+/* Parsing variables and functions */
+
+static uint64_t byteno=0;
+static uint64_t nnodes=0,nways=0,nrelations=0;
+
+static int64_t id=0;
+static int32_t lat=0;
+static int32_t lon=0;
+static int64_t timestamp=0;
+static int64_t node_refid=0,way_refid=0,relation_refid=0;
+
+static int mode_change=MODE_NORMAL;
+
+static uint32_t buffer_allocated;
+static unsigned char *buffer=NULL;
+static unsigned char *buffer_ptr,*buffer_end;
+
+static int string_table_start=0;
+static unsigned char **string_table=NULL;
+
+#define STRING_TABLE_ALLOCATED 15000
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Refill the data buffer and set the pointers.
+
+  int buffer_refill Return 0 if everything is OK or 1 for EOF.
+
+  int fd The file descriptor to read from.
+
+  uint32_t bytes The number of bytes to read.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int buffer_refill(int fd,uint32_t bytes)
+{
+ ssize_t n,m;
+ uint32_t totalbytes;
+
+ m=buffer_end-buffer_ptr;
+
+ if(m)
+    memmove(buffer,buffer_ptr,m);
+
+ totalbytes=bytes+m;
+
+ if(totalbytes>buffer_allocated)
+    buffer=(unsigned char *)realloc(buffer,buffer_allocated=totalbytes);
+
+ byteno+=bytes;
+
+ buffer_ptr=buffer;
+ buffer_end=buffer+m;
+
+ do
+   {
+    n=read(fd,buffer_end,bytes);
+
+    if(n<=0)
+       return(1);
+
+    buffer_end+=n;
+    bytes-=n;
+   }
+ while(bytes>0);
+
+ return(0);
+}
+
+static void process_node(void);
+static void process_way(void);
+static void process_relation(void);
+static void process_info(void);
+static unsigned char *process_string(int pair,unsigned char **buf_ptr,unsigned char **string1,unsigned char **string2);
+
+
+/* Macros to simplify the parser (and make it look more like the XML parser) */
+
+#define BEGIN(xx)            do{ state=(xx); goto finish_parsing; } while(0)
+
+#define BUFFER_CHARS(xx)     do{ if(buffer_refill(fd,(xx))) BEGIN(O5M_ERROR_UNEXP_EOF); } while(0)
+
+
+/* O5M decoding */
+
+#define O5M_LATITUDE(xx)  (double)(1E-7*(xx))
+#define O5M_LONGITUDE(xx) (double)(1E-7*(xx))
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an O5M int32 data value.
+
+  uint32_t o5m_int32 Returns the integer value.
+
+  unsigned char **ptr The pointer to read the data from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline uint32_t o5m_int32(unsigned char **ptr)
+{
+ uint32_t result=(**ptr)&0x7F;
+
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<7;
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<14;
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<21;
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<28;
+
+ (*ptr)++;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an O5M int32 data value.
+
+  int32_t o5m_sint32 Returns the integer value.
+
+  unsigned char **ptr The pointer to read the data from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int32_t o5m_sint32(unsigned char **ptr)
+{
+ int64_t result=((**ptr)&0x7E)>>1;
+ int sign=(**ptr)&0x01;
+
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<6;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<13;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<20;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<27;
+
+ (*ptr)++;
+
+ if(sign)
+    result=-result-1;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an O5M int64 data value.
+
+  int64_t o5m_int64 Returns the integer value.
+
+  unsigned char **ptr The pointer to read the data from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int64_t o5m_int64(unsigned char **ptr)
+{
+ uint64_t result=(**ptr)&0x7F;
+
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<7;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<14;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<21;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<28;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<35;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<42;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<49;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<56;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<63;
+
+ (*ptr)++;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an O5M sint64 data value.
+
+  int64_t o5m_sint64 Returns the integer value.
+
+  unsigned char **ptr The pointer to read the data from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int64_t o5m_sint64(unsigned char **ptr)
+{
+ int64_t result=((**ptr)&0x7E)>>1;
+ int sign=(**ptr)&0x01;
+
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<6;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<13;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<20;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<27;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<34;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<41;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<48;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<55;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<62;
+
+ (*ptr)++;
+
+ if(sign)
+    result=-result-1;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse the O5M and call the functions for each OSM item as seen.
+
+  int ParseO5M Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to parse.
+
+  int changes Set to 1 if this is expected to be a changes file, otherwise zero.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int ParseO5M(int fd,int changes)
+{
+ int i;
+ int state;
+ int number_reset=0;
+ int error;
+
+ /* Print the initial message */
+
+ printf_first("Reading: Bytes=0 Nodes=0 Ways=0 Relations=0");
+
+ /* The actual parser. */
+
+ nnodes=0,nways=0,nrelations=0;
+
+ if(changes)
+    mode_change=MODE_MODIFY;
+
+ string_table_start=0;
+ string_table=(unsigned char **)malloc(STRING_TABLE_ALLOCATED*sizeof(unsigned char *));
+ for(i=0;i<STRING_TABLE_ALLOCATED;i++)
+    string_table[i]=(unsigned char*)malloc(252);
+
+ buffer_allocated=4096;
+ buffer=(unsigned char*)malloc(buffer_allocated);
+
+ buffer_ptr=buffer_end=buffer;
+
+ while(1)
+   {
+    uint32_t dataset_length=0;
+
+    /* ================ Parsing states ================ */
+
+    BUFFER_CHARS(1);
+
+    state=*buffer_ptr++;
+
+    if(state!=O5M_FILE_END && state!=O5M_FILE_RESET)
+      {
+       uint32_t length;
+       unsigned char *ptr;
+
+       if(number_reset==0)
+          BEGIN(O5M_ERROR_RESET_NOT_FIRST);
+
+       BUFFER_CHARS(4);
+
+       ptr=buffer_ptr;
+       dataset_length=o5m_int32(&buffer_ptr);
+
+       length=dataset_length-4+(buffer_ptr-ptr);
+
+       BUFFER_CHARS(length);
+      }
+    else if(state==O5M_FILE_END)
+       ;
+    else if(state==O5M_FILE_RESET)
+       number_reset++;
+
+    switch(state)
+      {
+      case O5M_FILE_NODE:
+
+       process_node();
+
+       break;
+
+      case O5M_FILE_WAY:
+
+       process_way();
+
+       break;
+
+      case O5M_FILE_RELATION:
+
+       process_relation();
+
+       break;
+
+      case O5M_FILE_BOUNDING_BOX:
+
+       buffer_ptr+=dataset_length;
+
+       break;
+
+      case O5M_FILE_TIMESTAMP:
+
+       buffer_ptr+=dataset_length;
+
+       break;
+
+      case O5M_FILE_HEADER:
+
+       if(number_reset!=1)
+          BEGIN(O5M_ERROR_HEADER_NOT_FIRST);
+
+       if(!changes && strncmp((char*)buffer_ptr,"o5m2",4))
+          BEGIN(O5M_ERROR_EXPECTED_O5M);
+
+       if( changes && strncmp((char*)buffer_ptr,"o5c2",4))
+          BEGIN(O5M_ERROR_EXPECTED_O5C);
+
+       buffer_ptr+=dataset_length;
+
+       break;
+
+      case O5M_FILE_SYNC:
+
+       buffer_ptr+=dataset_length;
+
+       break;
+
+      case O5M_FILE_JUMP:
+
+       buffer_ptr+=dataset_length;
+
+       break;
+
+      case O5M_FILE_END:
+
+       BEGIN(O5M_EOF);
+
+       break;
+
+      case O5M_FILE_RESET:
+
+       string_table_start=0;
+       id=0;
+       lat=0;
+       lon=0;
+       timestamp=0;
+       node_refid=0,way_refid=0,relation_refid=0;
+
+       break;
+
+      default:
+
+       error=state;
+       BEGIN(O5M_ERROR_FILE_LEVEL);
+      }
+   }
+
+
+ finish_parsing:
+
+ switch(state)
+   {
+    /* End of file */
+
+   case O5M_EOF:
+    break;
+
+
+    /* ================ Error states ================ */
+
+
+   case O5M_ERROR_UNEXP_EOF:
+    fprintf(stderr,"O5M Parser: Error at byte %"PRIu64": unexpected end of file seen.\n",byteno);
+    break;
+
+   case O5M_ERROR_RESET_NOT_FIRST:
+    fprintf(stderr,"O5M Parser: Error at byte %"PRIu64": Reset was not the first byte.\n",byteno);
+    break;
+
+   case O5M_ERROR_HEADER_NOT_FIRST:
+    fprintf(stderr,"O5M Parser: Error at byte %"PRIu64": Header was not the first section.\n",byteno);
+    break;
+
+   case O5M_ERROR_EXPECTED_O5M:
+    fprintf(stderr,"O5M Parser: Error at byte %"PRIu64": Expected O5M format but header disagrees.\n",byteno);
+    break;
+
+   case O5M_ERROR_EXPECTED_O5C:
+    fprintf(stderr,"O5M Parser: Error at byte %"PRIu64": Expected O5C format but header disagrees.\n",byteno);
+    break;
+
+   case O5M_ERROR_FILE_LEVEL:
+    fprintf(stderr,"O5M Parser: Error at byte %"PRIu64": Unexpected dataset type %02x.\n",byteno,error);
+    break;
+   }
+
+ /* Free the parser variables */
+
+ for(i=0;i<STRING_TABLE_ALLOCATED;i++)
+    free(string_table[i]);
+ free(string_table);
+
+ free(buffer);
+
+ /* Print the final message */
+
+ printf_last("Read: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ return(state);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process an O5M Node dataset.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_node(void)
+{
+ int64_t delta_id;
+ int32_t delta_lat;
+ int32_t delta_lon;
+ TagList *tags=NULL,*result=NULL;
+ int mode=mode_change;
+
+ delta_id=o5m_sint64(&buffer_ptr);
+ id+=delta_id;
+
+ if(buffer_ptr<buffer_end)
+   {
+    if(*buffer_ptr!=0)
+       process_info();
+    else
+       buffer_ptr++;
+   }
+
+ if(buffer_ptr<buffer_end)
+   {
+    delta_lon=o5m_sint32(&buffer_ptr);
+    lon+=delta_lon;
+
+    delta_lat=o5m_sint32(&buffer_ptr);
+    lat+=delta_lat;
+   }
+ else
+    mode=MODE_DELETE;
+
+ /* Mangle the data and send it to the OSM parser */
+
+ nnodes++;
+
+ if(!(nnodes%10000))
+    printf_middle("Reading: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ tags=NewTagList();
+
+ while(buffer_ptr<buffer_end)
+   {
+    unsigned char *key,*val;
+
+    process_string(2,&buffer_ptr,&key,&val);
+
+    AppendTag(tags,(char*)key,(char*)val);
+   }
+
+ result=ApplyNodeTaggingRules(tags,id);
+
+ ProcessNodeTags(result,id,O5M_LATITUDE(lat),O5M_LONGITUDE(lon),mode);
+
+ DeleteTagList(tags);
+ DeleteTagList(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process an O5M Way dataset.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_way(void)
+{
+ int64_t delta_id;
+ TagList *tags=NULL,*result=NULL;
+ int mode=mode_change;
+ unsigned char *refs=NULL,*refs_end;
+
+ delta_id=o5m_sint64(&buffer_ptr);
+ id+=delta_id;
+
+ if(buffer_ptr<buffer_end)
+   {
+    if(*buffer_ptr!=0)
+       process_info();
+    else
+       buffer_ptr++;
+   }
+
+ if(buffer_ptr<buffer_end)
+   {
+    uint32_t length;
+
+    length=o5m_int32(&buffer_ptr);
+
+    if(length)
+      {
+       refs=buffer_ptr;
+       refs_end=buffer_ptr+length;
+
+       buffer_ptr=refs_end;
+      }
+   }
+ else
+    mode=MODE_DELETE;
+
+ /* Mangle the data and send it to the OSM parser */
+
+ nways++;
+
+ if(!(nways%1000))
+    printf_middle("Reading: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ AddWayRefs(0);
+
+ if(refs)
+    while(refs<refs_end)
+      {
+       int64_t delta_ref;
+
+       delta_ref=o5m_sint64(&refs);
+       node_refid+=delta_ref;
+
+       AddWayRefs(node_refid);
+      }
+
+ tags=NewTagList();
+
+ while(buffer_ptr<buffer_end)
+   {
+    unsigned char *key,*val;
+
+    process_string(2,&buffer_ptr,&key,&val);
+
+    AppendTag(tags,(char*)key,(char*)val);
+   }
+
+ result=ApplyWayTaggingRules(tags,id);
+
+ ProcessWayTags(result,id,mode);
+
+ DeleteTagList(tags);
+ DeleteTagList(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process an O5M Relation dataset.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_relation()
+{
+ int64_t delta_id;
+ TagList *tags=NULL,*result=NULL;
+ int mode=mode_change;
+ unsigned char *refs=NULL,*refs_end;
+
+ delta_id=o5m_sint64(&buffer_ptr);
+ id+=delta_id;
+
+ if(buffer_ptr<buffer_end)
+   {
+    if(*buffer_ptr!=0)
+       process_info();
+    else
+       buffer_ptr++;
+   }
+
+ if(buffer_ptr<buffer_end)
+   {
+    uint32_t length;
+
+    length=o5m_int32(&buffer_ptr);
+
+    if(length)
+      {
+       refs=buffer_ptr;
+       refs_end=buffer_ptr+length;
+
+       buffer_ptr=refs_end;
+      }
+   }
+ else
+    mode=MODE_DELETE;
+
+ /* Mangle the data and send it to the OSM parser */
+
+ nrelations++;
+
+ if(!(nrelations%1000))
+    printf_middle("Reading: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ AddRelationRefs(0,0,0,NULL);
+
+ if(refs)
+    while(refs<refs_end)
+      {
+       int64_t delta_ref;
+       unsigned char *typerole=NULL;
+
+       delta_ref=o5m_sint64(&refs);
+
+       typerole=process_string(1,&refs,NULL,NULL);
+
+       if(*typerole=='0')
+         {
+          node_refid+=delta_ref;
+
+          AddRelationRefs(node_refid,0,0,(char*)(typerole+1));
+         }
+       else if(*typerole=='1')
+         {
+          way_refid+=delta_ref;
+
+          AddRelationRefs(0,way_refid,0,(char*)(typerole+1));
+         }
+       else if(*typerole=='2')
+         {
+          relation_refid+=delta_ref;
+
+          AddRelationRefs(0,0,relation_refid,(char*)(typerole+1));
+         }
+      }
+
+ tags=NewTagList();
+
+ while(buffer_ptr<buffer_end)
+   {
+    unsigned char *key,*val;
+
+    process_string(2,&buffer_ptr,&key,&val);
+
+    AppendTag(tags,(char*)key,(char*)val);
+   }
+
+ result=ApplyRelationTaggingRules(tags,id);
+
+ ProcessRelationTags(result,id,mode);
+
+ DeleteTagList(tags);
+ DeleteTagList(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process an O5M info message.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_info(void)
+{
+ int64_t timestamp_delta;
+
+ o5m_int32(&buffer_ptr);        /* version */
+
+ timestamp_delta=o5m_sint64(&buffer_ptr);
+ timestamp+=timestamp_delta;
+
+ if(timestamp!=0)
+   {
+    o5m_sint32(&buffer_ptr);     /* changeset */
+
+    process_string(2,&buffer_ptr,NULL,NULL);  /* user */
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process an O5M string and take care of maintaining the string table.
+
+  unsigned char *process_string_pair Return a pointer to the concatenation of the two strings.
+
+  int pair Set to 2 for a pair or 1 for a single string.
+
+  unsigned char **buf_ptr The pointer to the buffer that is to be updated.
+
+  unsigned char **string1 Returns a pointer to the first of the strings in the pair.
+
+  unsigned char **string2 Returns a pointer to the second of the strings in the pair.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static unsigned char *process_string(int pair,unsigned char **buf_ptr,unsigned char **string1,unsigned char **string2)
+{
+ int lookup=0;
+ unsigned char *string;
+ unsigned char *p;
+
+ if(**buf_ptr==0)
+    string=*buf_ptr+1;
+ else
+   {
+    uint32_t position=o5m_int32(buf_ptr);
+
+    string=string_table[(string_table_start-position+STRING_TABLE_ALLOCATED)%STRING_TABLE_ALLOCATED];
+
+    lookup=1;
+   }
+
+ p=string;
+
+ if(pair==2)
+   {
+    if(string1)
+       *string1=p;
+
+    while(*p) p++;
+
+    p++;
+
+    if(string2)
+       *string2=p;
+   }
+
+ while(*p) p++;
+
+ if(!lookup)
+   {
+    if((p-string)<252)
+      {
+       memcpy(string_table[string_table_start],string,p-string+1);
+
+       string_table_start=(string_table_start+1)%STRING_TABLE_ALLOCATED;
+      }
+
+    *buf_ptr=p+1;
+   }
+
+ return(string);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an O5M format OSM file (from planet download).
+
+  int ParseO5MFile Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to read from.
+
+  NodesX *OSMNodes The data structure of nodes to fill in.
+
+  WaysX *OSMWays The data structure of ways to fill in.
+
+  RelationsX *OSMRelations The data structure of relations to fill in.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParseO5MFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations)
+{
+ int retval;
+
+ /* Initialise the parser */
+
+ InitialiseParser(OSMNodes,OSMWays,OSMRelations);
+
+ /* Parse the file */
+
+ retval=ParseO5M(fd,0);
+
+ /* Cleanup the parser */
+
+ CleanupParser();
+
+ return(retval);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an O5C format OSM file (from planet download).
+
+  int ParseO5CFile Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to read from.
+
+  NodesX *OSMNodes The data structure of nodes to fill in.
+
+  WaysX *OSMWays The data structure of ways to fill in.
+
+  RelationsX *OSMRelations The data structure of relations to fill in.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParseO5CFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations)
+{
+ int retval;
+
+ /* Initialise the parser */
+
+ InitialiseParser(OSMNodes,OSMWays,OSMRelations);
+
+ /* Parse the file */
+
+ retval=ParseO5M(fd,1);
+
+ /* Cleanup the parser */
+
+ CleanupParser();
+
+ return(retval);
+}
diff --git a/src/osmparser.c b/src/osmparser.c
index ec9692e..3dfd4f4 100644
--- a/src/osmparser.c
+++ b/src/osmparser.c
@@ -1,9 +1,9 @@
 /***************************************
- OSM XML file parser (either JOSM or planet)
+ OSM file parser (either JOSM or planet)
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -20,22 +20,20 @@
  ***************************************/
 
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdint.h>
 #include <ctype.h>
 
+#include "types.h"
 #include "typesx.h"
+
 #include "nodesx.h"
-#include "segmentsx.h"
 #include "waysx.h"
 #include "relationsx.h"
 
 #include "osmparser.h"
-
-#include "xmlparse.h"
 #include "tagging.h"
-
 #include "logging.h"
 
 
@@ -47,24 +45,11 @@
 /*+ Checks if a value in the XML is one of the allowed values for false. +*/
 #define ISFALSE(xx) (!strcmp(xx,"false") || !strcmp(xx,"no") || !strcmp(xx,"0"))
 
-
-/* Constants */
-
-#define MODE_NORMAL  3
-#define MODE_CREATE  2
-#define MODE_MODIFY  1
-#define MODE_DELETE -1
-
-
 /* Local variables */
 
-static int mode=MODE_NORMAL;
-
-static index_t nnodes=0;
-static index_t nways=0;
-static index_t nrelations=0;
-
-static TagList *current_tags=NULL;
+static NodesX     *nodes;
+static WaysX      *ways;
+static RelationsX *relations;
 
 static node_t *way_nodes=NULL;
 static int     way_nnodes=0;
@@ -79,747 +64,156 @@ static way_t       relation_from=NO_WAY_ID;
 static way_t       relation_to=NO_WAY_ID;
 static node_t      relation_via=NO_NODE_ID;
 
-static NodesX     *nodes;
-static SegmentsX  *segments;
-static WaysX      *ways;
-static RelationsX *relations;
-
-
 /* Local functions */
 
-static void process_node_tags(TagList *tags,node_t id,double latitude,double longitude);
-static void process_way_tags(TagList *tags,way_t id);
-static void process_relation_tags(TagList *tags,relation_t id);
-
 static double parse_speed(way_t id,const char *k,const char *v);
 static double parse_weight(way_t id,const char *k,const char *v);
 static double parse_length(way_t id,const char *k,const char *v);
 
 
-/* The XML tag processing function prototypes */
-
-//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding);
-static int osmType_function(const char *_tag_,int _type_,const char *version);
-static int osmChangeType_function(const char *_tag_,int _type_,const char *version);
-static int deleteType_function(const char *_tag_,int _type_);
-static int createType_function(const char *_tag_,int _type_);
-static int modifyType_function(const char *_tag_,int _type_);
-static int relationType_function(const char *_tag_,int _type_,const char *id);
-static int wayType_function(const char *_tag_,int _type_,const char *id);
-static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role);
-static int ndType_function(const char *_tag_,int _type_,const char *ref);
-static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon);
-static int changesetType_function(const char *_tag_,int _type_);
-static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v);
-//static int boundType_function(const char *_tag_,int _type_);
-//static int boundsType_function(const char *_tag_,int _type_);
-
-
-/* The XML tag definitions */
-
-/*+ The boundsType type tag. +*/
-static xmltag boundsType_tag=
-              {"bounds",
-               0, {NULL},
-               NULL,
-               {NULL}};
-
-/*+ The boundType type tag. +*/
-static xmltag boundType_tag=
-              {"bound",
-               0, {NULL},
-               NULL,
-               {NULL}};
-
-/*+ The tagType type tag. +*/
-static xmltag tagType_tag=
-              {"tag",
-               2, {"k","v"},
-               tagType_function,
-               {NULL}};
-
-/*+ The changesetType type tag. +*/
-static xmltag changesetType_tag=
-              {"changeset",
-               0, {NULL},
-               changesetType_function,
-               {&tagType_tag,NULL}};
-
-/*+ The nodeType type tag. +*/
-static xmltag nodeType_tag=
-              {"node",
-               3, {"id","lat","lon"},
-               nodeType_function,
-               {&tagType_tag,NULL}};
-
-/*+ The ndType type tag. +*/
-static xmltag ndType_tag=
-              {"nd",
-               1, {"ref"},
-               ndType_function,
-               {NULL}};
-
-/*+ The memberType type tag. +*/
-static xmltag memberType_tag=
-              {"member",
-               3, {"type","ref","role"},
-               memberType_function,
-               {NULL}};
-
-/*+ The wayType type tag. +*/
-static xmltag wayType_tag=
-              {"way",
-               1, {"id"},
-               wayType_function,
-               {&ndType_tag,&tagType_tag,NULL}};
-
-/*+ The relationType type tag. +*/
-static xmltag relationType_tag=
-              {"relation",
-               1, {"id"},
-               relationType_function,
-               {&memberType_tag,&tagType_tag,NULL}};
-
-/*+ The deleteType type tag. +*/
-static xmltag deleteType_tag=
-              {"delete",
-               0, {NULL},
-               deleteType_function,
-               {&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
-
-/*+ The createType type tag. +*/
-static xmltag createType_tag=
-              {"create",
-               0, {NULL},
-               createType_function,
-               {&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
-
-/*+ The modifyType type tag. +*/
-static xmltag modifyType_tag=
-              {"modify",
-               0, {NULL},
-               modifyType_function,
-               {&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
-
-/*+ The osmChangeType type tag. +*/
-static xmltag osmChangeType_tag=
-              {"osmChange",
-               1, {"version"},
-               osmChangeType_function,
-               {&boundsType_tag,&modifyType_tag,&createType_tag,&deleteType_tag,NULL}};
-
-/*+ The osmType type tag. +*/
-static xmltag osmType_tag=
-              {"osm",
-               1, {"version"},
-               osmType_function,
-               {&boundsType_tag,&boundType_tag,&changesetType_tag,&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
-
-/*+ The xmlDeclaration type tag. +*/
-static xmltag xmlDeclaration_tag=
-              {"xml",
-               2, {"version","encoding"},
-               NULL,
-               {NULL}};
-
-
-/*+ The complete set of tags at the top level for OSM. +*/
-static xmltag *xml_osm_toplevel_tags[]={&xmlDeclaration_tag,&osmType_tag,NULL};
-
-/*+ The complete set of tags at the top level for OSC. +*/
-static xmltag *xml_osc_toplevel_tags[]={&xmlDeclaration_tag,&osmChangeType_tag,NULL};
-
-
-/* The XML tag processing functions */
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the boundsType XSD type is seen
-
-  int boundsType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int boundsType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the boundType XSD type is seen
+  Initialise the OSM parser by initialising the local variables.
 
-  int boundType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int boundType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the tagType XSD type is seen
-
-  int tagType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  NodesX *OSMNodes The data structure of nodes to fill in.
 
-  const char *k The contents of the 'k' attribute (or NULL if not defined).
+  WaysX *OSMWays The data structure of ways to fill in.
 
-  const char *v The contents of the 'v' attribute (or NULL if not defined).
+  RelationsX *OSMRelations The data structure of relations to fill in.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v)
+void InitialiseParser(NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations)
 {
- if(_type_&XMLPARSE_TAG_START && current_tags)
-   {
-    XMLPARSE_ASSERT_STRING(_tag_,k);
-    XMLPARSE_ASSERT_STRING(_tag_,v);
+ /* Copy the function parameters and initialise the variables */
 
-    AppendTag(current_tags,k,v);
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the changesetType XSD type is seen
-
-  int changesetType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
+ nodes=OSMNodes;
+ ways=OSMWays;
+ relations=OSMRelations;
 
-static int changesetType_function(const char *_tag_,int _type_)
-{
- current_tags=NULL;
+ way_nodes=(node_t*)malloc(256*sizeof(node_t));
 
- return(0);
+ relation_nodes    =(node_t    *)malloc(256*sizeof(node_t));
+ relation_ways     =(way_t     *)malloc(256*sizeof(way_t));
+ relation_relations=(relation_t*)malloc(256*sizeof(relation_t));
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the nodeType XSD type is seen
-
-  int nodeType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *id The contents of the 'id' attribute (or NULL if not defined).
-
-  const char *lat The contents of the 'lat' attribute (or NULL if not defined).
-
-  const char *lon The contents of the 'lon' attribute (or NULL if not defined).
+  Clean up the memory after parsing.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon)
+void CleanupParser(void)
 {
- static node_t node_id;
- static double latitude,longitude;
-
- if(_type_&XMLPARSE_TAG_START)
-   {
-    long long llid;
-
-    nnodes++;
-
-    if(!(nnodes%10000))
-       printf_middle("Reading: Lines=%llu Nodes=%"Pindex_t" Ways=%"Pindex_t" Relations=%"Pindex_t,ParseXML_LineNumber(),nnodes,nways,nrelations);
-
-    current_tags=NewTagList();
-
-    /* Handle the node information */
-
-    XMLPARSE_ASSERT_INTEGER(_tag_,id);   llid=atoll(id); /* need long long conversion */
-    node_id=(node_t)llid;
-    logassert((long long)node_id==llid,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
-
-    if(mode!=MODE_DELETE)
-      {
-       XMLPARSE_ASSERT_FLOATING(_tag_,lat); latitude =atof(lat);
-       XMLPARSE_ASSERT_FLOATING(_tag_,lon); longitude=atof(lon);
-      }
-   }
+ /* Free the variables */
 
- if(_type_&XMLPARSE_TAG_END)
-   {
-    TagList *result=ApplyTaggingRules(&NodeRules,current_tags,node_id);
-
-    process_node_tags(result,node_id,latitude,longitude);
-
-    DeleteTagList(current_tags); current_tags=NULL;
-    DeleteTagList(result);
-   }
+ free(way_nodes);
 
- return(0);
+ free(relation_nodes);
+ free(relation_ways);
+ free(relation_relations);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the ndType XSD type is seen
-
-  int ndType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
+  Add node references to a way.
 
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *ref The contents of the 'ref' attribute (or NULL if not defined).
+  int64_t node_id The node ID to add or zero to clear the list.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int ndType_function(const char *_tag_,int _type_,const char *ref)
+void AddWayRefs(int64_t node_id)
 {
- if(_type_&XMLPARSE_TAG_START)
+ if(node_id==0)
+    way_nnodes=0;
+ else
    {
-    long long llid;
-    node_t node_id;
-
-    XMLPARSE_ASSERT_INTEGER(_tag_,ref); llid=atoll(ref); /* need long long conversion */
-    node_id=(node_t)llid;
-    logassert((long long)node_id==llid,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
+    node_t id;
 
     if(way_nnodes && (way_nnodes%256)==0)
        way_nodes=(node_t*)realloc((void*)way_nodes,(way_nnodes+256)*sizeof(node_t));
 
-    way_nodes[way_nnodes++]=node_id;
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the memberType XSD type is seen
-
-  int memberType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *type The contents of the 'type' attribute (or NULL if not defined).
-
-  const char *ref The contents of the 'ref' attribute (or NULL if not defined).
-
-  const char *role The contents of the 'role' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role)
-{
- if(_type_&XMLPARSE_TAG_START)
-   {
-    long long llid;
-
-    XMLPARSE_ASSERT_STRING(_tag_,type);
-    XMLPARSE_ASSERT_INTEGER(_tag_,ref); llid=atoll(ref); /* need long long conversion */
-
-    if(!strcmp(type,"node"))
-      {
-       node_t node_id;
-
-       node_id=(node_t)llid;
-       logassert((long long)node_id==llid,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
-
-       if(relation_nnodes && (relation_nnodes%256)==0)
-          relation_nodes=(node_t*)realloc((void*)relation_nodes,(relation_nnodes+256)*sizeof(node_t));
-
-       relation_nodes[relation_nnodes++]=node_id;
-
-       if(role)
-         {
-          if(!strcmp(role,"via"))
-             relation_via=node_id;
-         }
-      }
-    else if(!strcmp(type,"way"))
-      {
-       way_t way_id;
-
-       way_id=(way_t)llid;
-       logassert((long long)way_id==llid,"Way ID too large (change way_t to 64-bits?)"); /* check way id can be stored in way_t data type. */
-
-       if(relation_nways && (relation_nways%256)==0)
-          relation_ways=(way_t*)realloc((void*)relation_ways,(relation_nways+256)*sizeof(way_t));
+    id=(node_t)node_id;
+    logassert((int64_t)id==node_id,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
 
-       relation_ways[relation_nways++]=way_id;
-
-       if(role)
-         {
-          if(!strcmp(role,"from"))
-             relation_from=way_id;
-          if(!strcmp(role,"to"))
-             relation_to=way_id;
-         }
-      }
-    else if(!strcmp(type,"relation"))
-      {
-       relation_t relation_id;
-
-       relation_id=(relation_t)llid;
-       logassert((long long)relation_id==llid,"Relation ID too large (change relation_t to 64-bits?)"); /* check relation id can be stored in relation_t data type. */
-
-       if(relation_nrelations && (relation_nrelations%256)==0)
-          relation_relations=(relation_t*)realloc((void*)relation_relations,(relation_nrelations+256)*sizeof(relation_t));
-
-       relation_relations[relation_nrelations++]=relation_id;
-      }
+    way_nodes[way_nnodes++]=id;
    }
-
- return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the wayType XSD type is seen
+  Add node, way or relation references to a relation.
 
-  int wayType_function Returns 0 if no error occured or something else otherwise.
+  int64_t node_id The node ID to add or zero if it is not a node.
 
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
+  int64_t way_id The way ID to add or zero if it is not a way.
 
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  int64_t relation_id The relation ID to add or zero if it is not a relation.
 
-  const char *id The contents of the 'id' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int wayType_function(const char *_tag_,int _type_,const char *id)
-{
- static way_t way_id;
+  const char *role The role played by this referenced item or NULL.
 
- if(_type_&XMLPARSE_TAG_START)
-   {
-    long long llid;
-
-    nways++;
-
-    if(!(nways%1000))
-       printf_middle("Reading: Lines=%llu Nodes=%"Pindex_t" Ways=%"Pindex_t" Relations=%"Pindex_t,ParseXML_LineNumber(),nnodes,nways,nrelations);
-
-    current_tags=NewTagList();
-
-    way_nnodes=0;
-
-    /* Handle the way information */
-
-    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need long long conversion */
-
-    way_id=(way_t)llid;
-    logassert((long long)way_id==llid,"Way ID too large (change way_t to 64-bits?)"); /* check way id can be stored in way_t data type. */
-   }
-
- if(_type_&XMLPARSE_TAG_END)
-   {
-    TagList *result=ApplyTaggingRules(&WayRules,current_tags,way_id);
-
-    process_way_tags(result,way_id);
-
-    DeleteTagList(current_tags); current_tags=NULL;
-    DeleteTagList(result);
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the relationType XSD type is seen
-
-  int relationType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *id The contents of the 'id' attribute (or NULL if not defined).
+  If all of node_id, way_id and relation_id are zero then the list is cleared.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int relationType_function(const char *_tag_,int _type_,const char *id)
+void AddRelationRefs(int64_t node_id,int64_t way_id,int64_t relation_id,const char *role)
 {
- static relation_t relation_id;
-
- if(_type_&XMLPARSE_TAG_START)
+ if(node_id==0 && way_id==0 && relation_id==0)
    {
-    long long llid;
-
-    nrelations++;
-
-    if(!(nrelations%1000))
-       printf_middle("Reading: Lines=%llu Nodes=%"Pindex_t" Ways=%"Pindex_t" Relations=%"Pindex_t,ParseXML_LineNumber(),nnodes,nways,nrelations);
-
-    current_tags=NewTagList();
-
-    relation_nnodes=relation_nways=relation_nrelations=0;
+    relation_nnodes=0;
+    relation_nways=0;
+    relation_nrelations=0;
 
     relation_from=NO_WAY_ID;
-    relation_to=NO_WAY_ID;
     relation_via=NO_NODE_ID;
-
-    /* Handle the relation information */
-
-    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need long long conversion */
-
-    relation_id=(relation_t)llid;
-    logassert((long long)relation_id==llid,"Relation ID too large (change relation_t to 64-bits?)"); /* check relation id can be stored in relation_t data type. */
+    relation_to=NO_WAY_ID;
    }
-
- if(_type_&XMLPARSE_TAG_END)
+ else if(node_id!=0)
    {
-    TagList *result=ApplyTaggingRules(&RelationRules,current_tags,relation_id);
-
-    process_relation_tags(result,relation_id);
-
-    DeleteTagList(current_tags); current_tags=NULL;
-    DeleteTagList(result);
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the deleteType XSD type is seen
-
-  int deleteType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int deleteType_function(const char *_tag_,int _type_)
-{
- if(_type_&XMLPARSE_TAG_START)
-    mode=MODE_DELETE;
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the createType XSD type is seen
-
-  int createType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int createType_function(const char *_tag_,int _type_)
-{
- if(_type_&XMLPARSE_TAG_START)
-    mode=MODE_CREATE;
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the modifyType XSD type is seen
-
-  int modifyType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int modifyType_function(const char *_tag_,int _type_)
-{
- if(_type_&XMLPARSE_TAG_START)
-    mode=MODE_MODIFY;
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the osmChangeType XSD type is seen
-
-  int osmChangeType_function Returns 0 if no error occured or something else otherwise.
+    node_t id;
 
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
+    id=(node_t)node_id;
+    logassert((int64_t)id==node_id,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
 
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+    if(relation_nnodes && (relation_nnodes%256)==0)
+       relation_nodes=(node_t*)realloc((void*)relation_nodes,(relation_nnodes+256)*sizeof(node_t));
 
-  const char *version The contents of the 'version' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
+    relation_nodes[relation_nnodes++]=id;
 
-static int osmChangeType_function(const char *_tag_,int _type_,const char *version)
-{
- if(_type_&XMLPARSE_TAG_START)
-   {
-    if(!version || strcmp(version,"0.6"))
-       XMLPARSE_MESSAGE(_tag_,"Invalid value for 'version' (only '0.6' accepted)");
+    if(role)
+      {
+       if(!strcmp(role,"via"))
+          relation_via=id;
+      }
    }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the osmType XSD type is seen
-
-  int osmType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *version The contents of the 'version' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int osmType_function(const char *_tag_,int _type_,const char *version)
-{
- if(_type_&XMLPARSE_TAG_START)
+ else if(way_id!=0)
    {
-    mode=MODE_NORMAL;
-
-    if(!version || strcmp(version,"0.6"))
-       XMLPARSE_MESSAGE(_tag_,"Invalid value for 'version' (only '0.6' accepted)");
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the XML declaration is seen
-
-  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *version The contents of the 'version' attribute (or NULL if not defined).
-
-  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Parse an OSM XML file (from JOSM or planet download).
-
-  int ParseOSM Returns 0 if OK or something else in case of an error.
-
-  FILE *file The file to read from.
-
-  NodesX *OSMNodes The data structure of nodes to fill in.
-
-  SegmentsX *OSMSegments The data structure of segments to fill in.
-
-  WaysX *OSMWays The data structure of ways to fill in.
-
-  RelationsX *OSMRelations The data structure of relations to fill in.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,RelationsX *OSMRelations)
-{
- int retval;
-
- /* Copy the function parameters and initialise the variables. */
-
- nodes=OSMNodes;
- segments=OSMSegments;
- ways=OSMWays;
- relations=OSMRelations;
-
- way_nodes=(node_t*)malloc(256*sizeof(node_t));
+    way_t id;
 
- relation_nodes    =(node_t    *)malloc(256*sizeof(node_t));
- relation_ways     =(way_t     *)malloc(256*sizeof(way_t));
- relation_relations=(relation_t*)malloc(256*sizeof(relation_t));
-
- /* Parse the file */
-
- nnodes=0,nways=0,nrelations=0;
-
- printf_first("Reading: Lines=0 Nodes=0 Ways=0 Relations=0");
-
- retval=ParseXML(file,xml_osm_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
-
- printf_last("Read: Lines=%llu Nodes=%"Pindex_t" Ways=%"Pindex_t" Relations=%"Pindex_t,ParseXML_LineNumber(),nnodes,nways,nrelations);
-
- free(way_nodes);
-
- free(relation_nodes);
- free(relation_ways);
- free(relation_relations);
+    id=(way_t)way_id;
+    logassert((int64_t)id==way_id,"Way ID too large (change way_t to 64-bits?)"); /* check way id can be stored in way_t data type. */
 
- return(retval);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Parse an OSC XML file (from planet download).
-
-  int ParseOSC Returns 0 if OK or something else in case of an error.
-
-  FILE *file The file to read from.
-
-  NodesX *OSMNodes The data structure of nodes to fill in.
-
-  SegmentsX *OSMSegments The data structure of segments to fill in.
+    if(relation_nways && (relation_nways%256)==0)
+       relation_ways=(way_t*)realloc((void*)relation_ways,(relation_nways+256)*sizeof(way_t));
 
-  WaysX *OSMWays The data structure of ways to fill in.
+    relation_ways[relation_nways++]=id;
 
-  RelationsX *OSMRelations The data structure of relations to fill in.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-int ParseOSC(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,RelationsX *OSMRelations)
-{
- int retval;
-
- /* Copy the function parameters and initialise the variables. */
-
- nodes=OSMNodes;
- segments=OSMSegments;
- ways=OSMWays;
- relations=OSMRelations;
-
- way_nodes=(node_t*)malloc(256*sizeof(node_t));
-
- relation_nodes    =(node_t    *)malloc(256*sizeof(node_t));
- relation_ways     =(way_t     *)malloc(256*sizeof(way_t));
- relation_relations=(relation_t*)malloc(256*sizeof(relation_t));
-
- /* Parse the file */
-
- nnodes=0,nways=0,nrelations=0;
-
- printf_first("Reading: Lines=0 Nodes=0 Ways=0 Relations=0");
-
- retval=ParseXML(file,xml_osc_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
+    if(role)
+      {
+       if(!strcmp(role,"from"))
+          relation_from=id;
+       else if(!strcmp(role,"to"))
+          relation_to=id;
+      }
+   }
+ else /* if(relation_id!=0) */
+   {
+    relation_t id;
 
- printf_last("Read: Lines=%llu Nodes=%"Pindex_t" Ways=%"Pindex_t" Relations=%"Pindex_t,ParseXML_LineNumber(),nnodes,nways,nrelations);
+    id=(relation_t)relation_id;
+    logassert((int64_t)id==relation_id,"Relation ID too large (change relation_t to 64-bits?)"); /* check relation id can be stored in relation_t data type. */
 
- free(way_nodes);
+    if(relation_nrelations && (relation_nrelations%256)==0)
+       relation_relations=(relation_t*)realloc((void*)relation_relations,(relation_nrelations+256)*sizeof(relation_t));
 
- free(relation_nodes);
- free(relation_ways);
- free(relation_relations);
-
- return(retval);
+    relation_relations[relation_nrelations++]=relation_id;
+   }
 }
 
 
@@ -828,19 +222,27 @@ int ParseOSC(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,R
 
   TagList *tags The list of node tags.
 
-  node_t id The id of the node.
+  int64_t node_id The id of the node.
 
   double latitude The latitude of the node.
 
   double longitude The longitude of the node.
+
+  int mode The mode of operation to take (create, modify, delete).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void process_node_tags(TagList *tags,node_t id,double latitude,double longitude)
+void ProcessNodeTags(TagList *tags,int64_t node_id,double latitude,double longitude,int mode)
 {
  transports_t allow=Transports_ALL;
  nodeflags_t flags=0;
+ node_t id;
  int i;
 
+ /* Convert id */
+
+ id=(node_t)node_id;
+ logassert((int64_t)id==node_id,"Node ID too large (change node_t to 64-bits?)"); /* check node id can be stored in node_t data type. */
+
  /* Delete */
 
  if(mode==MODE_DELETE)
@@ -866,7 +268,7 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_Bicycle;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'bicycle' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'bicycle' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
@@ -878,7 +280,7 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_Foot;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'foot' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'foot' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
@@ -890,26 +292,19 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_Goods;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'goods' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'goods' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
        break;
 
       case 'h':
-       if(!strcmp(k,"highway"))
-          if(!strcmp(v,"mini_roundabout"))
-            {
-             flags|=NODE_MINIRNDBT;
-             recognised=1; break;
-            }
-
        if(!strcmp(k,"horse"))
          {
           if(ISFALSE(v))
              allow&=~Transports_Horse;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'horse' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'horse' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
@@ -918,7 +313,7 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_HGV;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'hgv' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'hgv' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
@@ -930,16 +325,16 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_Moped;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'moped' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'moped' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
-       if(!strcmp(k,"motorbike"))
+       if(!strcmp(k,"motorcycle"))
          {
           if(ISFALSE(v))
-             allow&=~Transports_Motorbike;
+             allow&=~Transports_Motorcycle;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'motorbike' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'motorcycle' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
@@ -948,7 +343,7 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_Motorcar;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'motorcar' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'motorcar' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
@@ -960,7 +355,19 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_PSV;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'psv' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'psv' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
+          recognised=1; break;
+         }
+
+       break;
+
+      case 'r':
+       if(!strcmp(k,"roundabout"))
+         {
+          if(ISTRUE(v))
+             flags|=NODE_MINIRNDBT;
+          else
+             logerror("Node %"Pnode_t" has an unrecognised tag 'roundabout' = '%s' (after tagging rules); using 'no'.\n",id,v);
           recognised=1; break;
          }
 
@@ -972,7 +379,7 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
           if(ISFALSE(v))
              allow&=~Transports_Wheelchair;
           else if(!ISTRUE(v))
-             logerror("Node %"Pnode_t" has an unrecognised tag value 'wheelchair' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+             logerror("Node %"Pnode_t" has an unrecognised tag 'wheelchair' = '%s' (after tagging rules); using 'yes'.\n",logerror_node(id),v);
           recognised=1; break;
          }
 
@@ -983,7 +390,7 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
       }
 
     if(!recognised)
-       logerror("Node %"Pnode_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+       logerror("Node %"Pnode_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_node(id),k,v);
    }
 
  /* Create the node */
@@ -997,16 +404,24 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
 
   TagList *tags The list of way tags.
 
-  way_t id The id of the way.
+  int64_t way_id The id of the way.
+
+  int mode The mode of operation to take (create, modify, delete).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void process_way_tags(TagList *tags,way_t id)
+void ProcessWayTags(TagList *tags,int64_t way_id,int mode)
 {
  Way way={0};
- distance_t oneway=0,area=0;
- int roundabout=0;
+ int oneway=0,area=0;
+ int roundabout=0,lanes=0;
  char *name=NULL,*ref=NULL,*refname=NULL;
- int i,j;
+ way_t id;
+ int i;
+
+ /* Convert id */
+
+ id=(way_t)way_id;
+ logassert((int64_t)id==way_id,"Way ID too large (change way_t to 64-bits?)"); /* check way id can be stored in way_t data type. */
 
  /* Delete */
 
@@ -1014,11 +429,7 @@ static void process_way_tags(TagList *tags,way_t id)
    {
     way.type=WAY_DELETED;
 
-    AppendWayList(ways,id,&way,"");
-
-    way.type=Highway_None;
-
-    AppendSegmentList(segments,id,NO_NODE_ID,NO_NODE_ID,0);
+    AppendWayList(ways,id,&way,way_nodes,way_nnodes,"");
    }
 
  if(mode==MODE_DELETE)
@@ -1028,13 +439,14 @@ static void process_way_tags(TagList *tags,way_t id)
 
  if(way_nnodes==0)
    {
-    logerror("Way %"Pway_t" has no nodes.\n",id);
+    logerror("Way %"Pway_t" has no nodes.\n",logerror_way(id));
     return;
    }
 
  if(way_nnodes==1)
    {
-    logerror("Way %"Pway_t" has only one node.\n",id);
+    logerror_node(way_nodes[0]); /* Extra logerror information since way isn't stored */
+    logerror("Way %"Pway_t" has only one node.\n",logerror_way(id));
     return;
    }
 
@@ -1050,7 +462,7 @@ static void process_way_tags(TagList *tags,way_t id)
        way.type=HighwayType(v);
 
        if(way.type==Highway_None)
-          logerror("Way %"Pway_t" has an unrecognised highway type '%s' (after tagging rules); ignoring it.\n",id,v);
+          logerror("Way %"Pway_t" has an unrecognised highway type '%s' (after tagging rules); ignoring it.\n",logerror_way(id),v);
 
        break;
       }
@@ -1075,9 +487,9 @@ static void process_way_tags(TagList *tags,way_t id)
        if(!strcmp(k,"area"))
          {
           if(ISTRUE(v))
-             area=SEGMENT_AREA;
+             area=1;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'area' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'area' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1089,7 +501,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_Bicycle;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'bicycle' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'bicycle' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1098,7 +510,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.props|=Properties_BicycleRoute;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'bicycleroute' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'bicycleroute' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1107,19 +519,30 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.props|=Properties_Bridge;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'bridge' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'bridge' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
        break;
 
+      case 'c':
+       if(!strcmp(k,"cyclebothways"))
+         {
+          if(ISTRUE(v))
+             way.props|=Properties_CycleBothWays;
+          else if(!ISFALSE(v))
+             logerror("Way %"Pway_t" has an unrecognised tag 'cyclebothways' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
+          recognised=1; break;
+         }
+       break;
+
       case 'f':
        if(!strcmp(k,"foot"))
          {
           if(ISTRUE(v))
              way.allow|=Transports_Foot;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'foot' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'foot' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1128,7 +551,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.props|=Properties_FootRoute;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'footroute' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'footroute' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1140,7 +563,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_Goods;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'goods' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'goods' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1155,7 +578,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_Horse;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'horse' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'horse' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1164,7 +587,21 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_HGV;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'hgv' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'hgv' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
+          recognised=1; break;
+         }
+
+       break;
+
+      case 'l':
+       if(!strcmp(k,"lanes"))
+         {
+          int en=0;
+          float lanesf;
+          if(sscanf(v,"%f%n",&lanesf,&en)==1 && en && !v[en])
+             lanes=(int)lanesf;
+          else
+             logerror("Way %"Pway_t" has an unrecognised tag 'lanes' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1209,16 +646,16 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_Moped;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'moped' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'moped' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
-       if(!strcmp(k,"motorbike"))
+       if(!strcmp(k,"motorcycle"))
          {
           if(ISTRUE(v))
-             way.allow|=Transports_Motorbike;
+             way.allow|=Transports_Motorcycle;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'motorbike' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'motorcycle' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1227,7 +664,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_Motorcar;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'motorcar' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'motorcar' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1236,7 +673,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.props|=Properties_Multilane;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'multilane' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'multilane' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1255,11 +692,11 @@ static void process_way_tags(TagList *tags,way_t id)
        if(!strcmp(k,"oneway"))
          {
           if(ISTRUE(v))
-             oneway=ONEWAY_1TO2;
+             oneway=1;
           else if(!strcmp(v,"-1"))
-             oneway=ONEWAY_2TO1;
+             oneway=-1;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'oneway' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'oneway' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1271,7 +708,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.props|=Properties_Paved;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'paved' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'paved' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1280,7 +717,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_PSV;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'psv' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'psv' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1298,7 +735,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              roundabout=1;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'roundabout' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'roundabout' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1310,7 +747,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.props|=Properties_Tunnel;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'tunnel' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'tunnel' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1322,7 +759,7 @@ static void process_way_tags(TagList *tags,way_t id)
           if(ISTRUE(v))
              way.allow|=Transports_Wheelchair;
           else if(!ISFALSE(v))
-             logerror("Way %"Pway_t" has an unrecognised tag value 'wheelchair' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Way %"Pway_t" has an unrecognised tag 'wheelchair' = '%s' (after tagging rules); using 'no'.\n",logerror_way(id),v);
           recognised=1; break;
          }
 
@@ -1333,20 +770,61 @@ static void process_way_tags(TagList *tags,way_t id)
       }
 
     if(!recognised)
-       logerror("Way %"Pway_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+       logerror("Way %"Pway_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),k,v);
    }
 
  /* Create the way */
 
+ if(area && oneway)
+   {
+    logerror("Way %"Pway_t" is an area and oneway; ignoring area tagging.\n",logerror_way(id));
+    area=0;
+   }
+
+ if(!oneway && way.props&Properties_CycleBothWays)
+   {
+    logerror("Way %"Pway_t" is not oneway and cyclebothways; ignoring cyclebothways tagging.\n",logerror_way(id));
+    way.props&=~Properties_CycleBothWays;
+   }
+
  if(!way.allow)
     return;
 
  if(oneway)
+   {
     way.type|=Highway_OneWay;
 
+    if(oneway==-1)
+       for(i=0;i<way_nnodes/2;i++)
+         {
+          node_t temp;
+
+          temp=way_nodes[i];
+          way_nodes[i]=way_nodes[way_nnodes-i-1];
+          way_nodes[way_nnodes-i-1]=temp;
+         }
+   }
+
  if(roundabout)
     way.type|=Highway_Roundabout;
 
+ if(area)
+   {
+    way.type|=Highway_Area;
+
+    if(way_nodes[0]!=way_nodes[way_nnodes-1])
+       logerror("Way %"Pway_t" is an area but not closed.\n",logerror_way(id));
+   }
+
+ if(lanes)
+   {
+    if(oneway || (lanes/2)>1)
+       way.props|=Properties_Multilane;
+
+    if(oneway && lanes==1)
+       way.props&=~Properties_Multilane;
+   }
+
  if(ref && name)
    {
     refname=(char*)malloc(strlen(ref)+strlen(name)+4);
@@ -1359,27 +837,10 @@ static void process_way_tags(TagList *tags,way_t id)
  else /* if(!ref && !name) */
     refname="";
 
- AppendWayList(ways,id,&way,refname);
+ AppendWayList(ways,id,&way,way_nodes,way_nnodes,refname);
 
  if(ref && name)
     free(refname);
-
- for(i=1;i<way_nnodes;i++)
-   {
-    node_t from=way_nodes[i-1];
-    node_t to  =way_nodes[i];
-
-    for(j=1;j<i;j++)
-      {
-       node_t n1=way_nodes[j-1];
-       node_t n2=way_nodes[j];
-
-       if((n1==from && n2==to) || (n2==from && n1==to))
-          logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" in way %"Pway_t" is duplicated.\n",n1,n2,id);
-      }
-
-    AppendSegmentList(segments,id,from,to,area+oneway);
-   }
 }
 
 
@@ -1388,22 +849,31 @@ static void process_way_tags(TagList *tags,way_t id)
 
   TagList *tags The list of relation tags.
 
-  relation_t id The id of the relation.
+  int64_t relation_id The id of the relation.
+
+  int mode The mode of operation to take (create, modify, delete).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void process_relation_tags(TagList *tags,relation_t id)
+void ProcessRelationTags(TagList *tags,int64_t relation_id,int mode)
 {
  transports_t routes=Transports_None;
  transports_t except=Transports_None;
  int relation_turn_restriction=0;
  TurnRestriction restriction=TurnRestrict_None;
+ relation_t id;
  int i;
 
+ /* Convert id */
+
+ id=(relation_t)relation_id;
+ logassert((int64_t)id==relation_id,"Relation ID too large (change relation_t to 64-bits?)"); /* check relation id can be stored in relation_t data type. */
+
  /* Delete */
 
  if(mode==MODE_DELETE || mode==MODE_MODIFY)
    {
     AppendRouteRelationList(relations,id,RELATION_DELETED,
+                            relation_nodes,relation_nnodes,
                             relation_ways,relation_nways,
                             relation_relations,relation_nrelations);
 
@@ -1419,7 +889,7 @@ static void process_relation_tags(TagList *tags,relation_t id)
 
  if(relation_nnodes==0 && relation_nways==0 && relation_nrelations==0)
    {
-    logerror("Relation %"Prelation_t" has no nodes, ways or relations.\n",id);
+    logerror("Relation %"Prelation_t" has no nodes, ways or relations.\n",logerror_relation(id));
     return;
    }
 
@@ -1439,7 +909,7 @@ static void process_relation_tags(TagList *tags,relation_t id)
           if(ISTRUE(v))
              routes|=Transports_Bicycle;
           else if(!ISFALSE(v))
-             logerror("Relation %"Prelation_t" has an unrecognised tag value 'bicycleroute' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Relation %"Prelation_t" has an unrecognised tag 'bicycleroute' = '%s' (after tagging rules); using 'no'.\n",logerror_relation(id),v);
           recognised=1; break;
          }
 
@@ -1453,7 +923,7 @@ static void process_relation_tags(TagList *tags,relation_t id)
                 except|=TRANSPORTS(i);
 
           if(except==Transports_None)
-             logerror("Relation %"Prelation_t" has an unrecognised tag value 'except' = '%s' (after tagging rules); ignoring it.\n",id,v);
+             logerror("Relation %"Prelation_t" has an unrecognised tag 'except' = '%s' (after tagging rules); ignoring it.\n",logerror_relation(id),v);
 
           recognised=1; break;
          }
@@ -1466,7 +936,7 @@ static void process_relation_tags(TagList *tags,relation_t id)
           if(ISTRUE(v))
              routes|=Transports_Foot;
           else if(!ISFALSE(v))
-             logerror("Relation %"Prelation_t" has an unrecognised tag value 'footroute' = '%s' (after tagging rules); using 'no'.\n",id,v);
+             logerror("Relation %"Prelation_t" has an unrecognised tag 'footroute' = '%s' (after tagging rules); using 'no'.\n",logerror_relation(id),v);
           recognised=1; break;
          }
 
@@ -1484,7 +954,7 @@ static void process_relation_tags(TagList *tags,relation_t id)
           if(!strcmp(v,"only_straight_on")) restriction=TurnRestrict_only_straight_on;
 
           if(restriction==TurnRestrict_None)
-             logerror("Relation %"Prelation_t" has an unrecognised tag value 'restriction' = '%s' (after tagging rules); ignoring it.\n",id,v);
+             logerror("Relation %"Prelation_t" has an unrecognised tag 'restriction' = '%s' (after tagging rules); ignoring it.\n",logerror_relation(id),v);
 
           recognised=1; break;
          }
@@ -1508,7 +978,7 @@ static void process_relation_tags(TagList *tags,relation_t id)
       }
 
     if(!recognised)
-       logerror("Relation %"Prelation_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+       logerror("Relation %"Prelation_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_relation(id),k,v);
    }
 
  /* Create the route relation (must store all relations that have ways or
@@ -1517,6 +987,7 @@ static void process_relation_tags(TagList *tags,relation_t id)
 
  if((relation_nways || relation_nrelations) && !relation_turn_restriction)
     AppendRouteRelationList(relations,id,routes,
+                            relation_nodes,relation_nnodes,
                             relation_ways,relation_nways,
                             relation_relations,relation_nrelations);
 
@@ -1525,12 +996,28 @@ static void process_relation_tags(TagList *tags,relation_t id)
  if(relation_turn_restriction && restriction!=TurnRestrict_None)
    {
     if(relation_from==NO_WAY_ID)
-       logerror("Relation %"Prelation_t" is a turn restriction but has no 'from' way.\n",id);
-    else if(relation_to==NO_WAY_ID)
-       logerror("Relation %"Prelation_t" is a turn restriction but has no 'to' way.\n",id);
-    else if(relation_via==NO_NODE_ID)
-       logerror("Relation %"Prelation_t" is a turn restriction but has no 'via' node.\n",id);
-    else
+      {
+       /* Extra logerror information since relation isn't stored */
+       if(relation_to!=NO_WAY_ID) logerror_way(relation_to);
+       if(relation_via!=NO_NODE_ID) logerror_node(relation_via);
+       logerror("Relation %"Prelation_t" is a turn restriction but has no 'from' way.\n",logerror_relation(id));
+      }
+    if(relation_to==NO_WAY_ID)
+      {
+       /* Extra logerror information since relation isn't stored */
+       if(relation_via!=NO_NODE_ID) logerror_node(relation_via);
+       if(relation_from!=NO_WAY_ID) logerror_way(relation_from);
+       logerror("Relation %"Prelation_t" is a turn restriction but has no 'to' way.\n",logerror_relation(id));
+      }
+    if(relation_via==NO_NODE_ID)
+      {
+       /* Extra logerror information since relation isn't stored */
+       if(relation_to!=NO_WAY_ID) logerror_way(relation_to);
+       if(relation_from!=NO_WAY_ID) logerror_way(relation_from);
+       logerror("Relation %"Prelation_t" is a turn restriction but has no 'via' node.\n",logerror_relation(id));
+      }
+
+    if(relation_from!=NO_WAY_ID && relation_to!=NO_WAY_ID && relation_via!=NO_NODE_ID)
        AppendTurnRelationList(relations,id,
                               relation_from,relation_to,relation_via,
                               restriction,except);
@@ -1556,18 +1043,24 @@ static double parse_speed(way_t id,const char *k,const char *v)
  double value=strtod(v,&ev);
 
  if(v==ev)
-    logerror("Way %"Pway_t" has an unrecognised tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+    logerror("Way %"Pway_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),k,v);
  else
    {
     while(isspace(*ev)) ev++;
 
+    if(*ev==0)
+       return(value);
+
+    if(!strcmp(ev,"km/h") || !strcmp(ev,"kph") || !strcmp(ev,"kmph"))
+       return(value);
+
     if(!strcmp(ev,"mph"))
        return(1.609*value);
 
-    if(*ev==0 || !strcmp(ev,"kph"))
-       return(value);
+    if(!strcmp(ev,"knots"))
+       return(1.852*value);
 
-    logerror("Way %"Pway_t" has an un-parseable tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+    logerror("Way %"Pway_t" has an un-parseable tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),k,v);
    }
 
  return(0);
@@ -1592,20 +1085,23 @@ static double parse_weight(way_t id,const char *k,const char *v)
  double value=strtod(v,&ev);
 
  if(v==ev)
-    logerror("Way %"Pway_t" has an unrecognised tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+    logerror("Way %"Pway_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),k,v);
  else
    {
     while(isspace(*ev)) ev++;
 
+    if(*ev==0)
+       return(value);
+
     if(!strcmp(ev,"kg"))
        return(value/1000.0);
 
-    if(*ev==0 || !strcmp(ev,"T") || !strcmp(ev,"t")
-              || !strcmp(ev,"ton") || !strcmp(ev,"tons")
-              || !strcmp(ev,"tonne") || !strcmp(ev,"tonnes"))
+    if(!strcmp(ev,"T") || !strcmp(ev,"t") ||
+       !strcmp(ev,"ton") || !strcmp(ev,"tons") ||
+       !strcmp(ev,"tonne") || !strcmp(ev,"tonnes"))
        return(value);
 
-    logerror("Way %"Pway_t" has an un-parseable tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+    logerror("Way %"Pway_t" has an un-parseable tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),k,v);
    }
 
  return(0);
@@ -1630,12 +1126,29 @@ static double parse_length(way_t id,const char *k,const char *v)
  double value=strtod(v,&ev);
 
  if(v==ev)
-    logerror("Way %"Pway_t" has an unrecognised tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+    logerror("Way %"Pway_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),k,v);
  else
    {
     int en=0;
     int feet=0,inches=0;
 
+    while(isspace(*ev)) ev++;
+
+    if(*ev==0)
+       return(value);
+
+    if(!strcmp(ev,"m") || !strcmp(ev,"metre") || !strcmp(ev,"metres") || !strcmp(ev,"meter") || !strcmp(ev,"meters"))
+       return(value);
+
+    if(!strcmp(ev,"'"))
+       return(value*0.254);
+
+    if(!strcmp(ev,"′"))
+       return(value*0.254);
+
+    if(!strcmp(ev,"ft") || !strcmp(ev,"feet"))
+       return(value*0.254);
+
     if(sscanf(v,"%d' %d\"%n",&feet,&inches,&en)==2 && en && !v[en])
        return((feet+(double)inches/12.0)*0.254);
 
@@ -1645,27 +1158,25 @@ static double parse_length(way_t id,const char *k,const char *v)
     if(sscanf(v,"%d'-%d\"%n",&feet,&inches,&en)==2 && en && !v[en])
        return((feet+(double)inches/12.0)*0.254);
 
-    if(sscanf(v,"%d - %d%n",&feet,&inches,&en)==2 && en && !v[en])
+    if(sscanf(v,"%d′ %d″%n",&feet,&inches,&en)==2 && en && !v[en])
        return((feet+(double)inches/12.0)*0.254);
 
-    if(sscanf(v,"%d ft %d in%n",&feet,&inches,&en)==2 && en && !v[en])
+    if(sscanf(v,"%d′%d″%n",&feet,&inches,&en)==2 && en && !v[en])
        return((feet+(double)inches/12.0)*0.254);
 
-    if(sscanf(v,"%d feet %d inches%n",&feet,&inches,&en)==2 && en && !v[en])
+    if(sscanf(v,"%d′-%d″%n",&feet,&inches,&en)==2 && en && !v[en])
        return((feet+(double)inches/12.0)*0.254);
 
-    if(!strcmp(ev,"'"))
-       return(feet*0.254);
-
-    while(isspace(*ev)) ev++;
+    if(sscanf(v,"%d - %d%n",&feet,&inches,&en)==2 && en && !v[en])
+       return((feet+(double)inches/12.0)*0.254);
 
-    if(!strcmp(ev,"ft") || !strcmp(ev,"feet"))
-       return(value*0.254);
+    if(sscanf(v,"%d ft %d in%n",&feet,&inches,&en)==2 && en && !v[en])
+       return((feet+(double)inches/12.0)*0.254);
 
-    if(*ev==0 || !strcmp(ev,"m") || !strcmp(ev,"metre") || !strcmp(ev,"metres"))
-       return(value);
+    if(sscanf(v,"%d feet %d inches%n",&feet,&inches,&en)==2 && en && !v[en])
+       return((feet+(double)inches/12.0)*0.254);
 
-    logerror("Way %"Pway_t" has an un-parseable tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+    logerror("Way %"Pway_t" has an un-parseable tag '%s' = '%s' (after tagging rules); ignoring it.\n",logerror_way(id),k,v);
    }
 
  return(0);
diff --git a/src/osmparser.h b/src/osmparser.h
index ba91717..02d89e3 100644
--- a/src/osmparser.h
+++ b/src/osmparser.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -23,16 +23,51 @@
 #ifndef OSMPARSER_H
 #define OSMPARSER_H    /*+ To stop multiple inclusions. +*/
 
-#include <stdio.h>
+#include <stdint.h>
 
 #include "typesx.h"
+#include "xmlparse.h"
+#include "tagging.h"
+
+
+/* Constants */
+
+#define MODE_NORMAL  3
+#define MODE_CREATE  2
+#define MODE_MODIFY  1
+#define MODE_DELETE -1
+
+
+/* Functions in osmxmlparse.c */
+
+int ParseOSMFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations);
+
+int ParseOSCFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations);
+
+
+/* Functions in osmpbfparse.c */
+
+int ParsePBFFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations);
+
+
+/* Functions in osmo5mparse.c */
+
+int ParseO5MFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations);
+
+int ParseO5CFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations);
 
 
 /* Functions in osmparser.c */
 
-int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,RelationsX *OSMRelations);
+void InitialiseParser(NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations);
+void CleanupParser(void);
+
+void AddWayRefs(int64_t node_id);
+void AddRelationRefs(int64_t node_id,int64_t way_id,int64_t relation_id,const char *role);
 
-int ParseOSC(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,RelationsX *OSMRelations);
+void ProcessNodeTags(TagList *tags,int64_t node_id,double latitude,double longitude,int mode);
+void ProcessWayTags(TagList *tags,int64_t way_id, int mode);
+void ProcessRelationTags(TagList *tags,int64_t relation_id,int mode);
 
 
 #endif /* OSMPARSER_H */
diff --git a/src/osmpbfparse.c b/src/osmpbfparse.c
new file mode 100644
index 0000000..c388151
--- /dev/null
+++ b/src/osmpbfparse.c
@@ -0,0 +1,1234 @@
+/***************************************
+ A simple osm-specific PBF parser where the structure is hard-coded.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2012-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#if defined(USE_GZIP) && USE_GZIP
+#include <zlib.h>
+#endif
+
+#include "osmparser.h"
+#include "tagging.h"
+#include "logging.h"
+
+
+/* Inside a BlobHeader message */
+
+#define PBF_VAL_BLOBHEADER_TYPE    1
+#define PBF_VAL_BLOBHEADER_SIZE    3
+
+/* Inside a Blob message */
+
+#define PBF_VAL_BLOB_RAW_DATA      1
+#define PBF_VAL_BLOB_RAW_SIZE      2
+#define PBF_VAL_BLOB_ZLIB_DATA     3
+
+/* Inside a HeaderBlock message */
+
+#define PBF_VAL_REQUIRED_FEATURES  4
+#define PBF_VAL_OPTIONAL_FEATURES  5
+
+/* Inside a PrimitiveBlock message */
+
+#define PBF_VAL_STRING_TABLE       1
+#define PBF_VAL_PRIMITIVE_GROUP    2
+#define PBF_VAL_GRANULARITY       17
+#define PBF_VAL_LAT_OFFSET        19
+#define PBF_VAL_LON_OFFSET        20
+
+/* Inside a PrimitiveGroup message */
+
+#define PBF_VAL_NODES              1
+#define PBF_VAL_DENSE_NODES        2
+#define PBF_VAL_WAYS               3
+#define PBF_VAL_RELATIONS          4
+
+/* Inside a StringTable message */
+
+#define PBF_VAL_STRING             1
+
+/* Inside a Node message */
+
+#define PBF_VAL_NODE_ID            1
+#define PBF_VAL_NODE_KEYS          2
+#define PBF_VAL_NODE_VALS          3
+#define PBF_VAL_NODE_LAT           8
+#define PBF_VAL_NODE_LON           9
+
+/* Inside a DenseNode message */
+
+#define PBF_VAL_DENSE_NODE_ID         1
+#define PBF_VAL_DENSE_NODE_LAT        8
+#define PBF_VAL_DENSE_NODE_LON        9
+#define PBF_VAL_DENSE_NODE_KEYS_VALS 10
+
+/* Inside a Way message */
+
+#define PBF_VAL_WAY_ID             1
+#define PBF_VAL_WAY_KEYS           2
+#define PBF_VAL_WAY_VALS           3
+#define PBF_VAL_WAY_REFS           8
+
+/* Inside a Relation message */
+
+#define PBF_VAL_RELATION_ID        1
+#define PBF_VAL_RELATION_KEYS      2
+#define PBF_VAL_RELATION_VALS      3
+#define PBF_VAL_RELATION_ROLES     8
+#define PBF_VAL_RELATION_MEMIDS    9
+#define PBF_VAL_RELATION_TYPES    10
+
+/* Errors */
+
+#define PBF_EOF                     0
+
+#define PBF_ERROR_UNEXP_EOF       100
+#define PBF_ERROR_BLOB_HEADER_LEN 101
+#define PBF_ERROR_BLOB_LEN        102
+#define PBF_ERROR_NOT_OSM         103
+#define PBF_ERROR_BLOB_BOTH       104
+#define PBF_ERROR_BLOB_NEITHER    105
+#define PBF_ERROR_NO_GZIP         106
+#define PBF_ERROR_GZIP_INIT       107
+#define PBF_ERROR_GZIP_INFLATE    108
+#define PBF_ERROR_GZIP_WRONG_LEN  109
+#define PBF_ERROR_GZIP_END        110
+#define PBF_ERROR_UNSUPPORTED     111
+#define PBF_ERROR_TOO_MANY_GROUPS 112
+
+
+/* Parsing variables and functions */
+
+static uint64_t byteno=0;
+static uint64_t nnodes=0,nways=0,nrelations=0;
+
+static uint32_t buffer_allocated,zbuffer_allocated;
+static unsigned char *buffer=NULL,*zbuffer=NULL;
+static unsigned char *buffer_ptr,*buffer_end;
+
+static int string_table_length=0,string_table_allocated=0;
+static unsigned char **string_table=NULL;
+static uint32_t *string_table_string_lengths=NULL;
+
+static int32_t granularity=100;
+static int64_t lat_offset=0,lon_offset=0;
+
+#define LENGTH_32M (32*1024*1024)
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Refill the data buffer and set the pointers.
+
+  int buffer_refill Return 0 if everything is OK or 1 for EOF.
+
+  int fd The file descriptor to read from.
+
+  uint32_t bytes The number of bytes to read.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int buffer_refill(int fd,uint32_t bytes)
+{
+ ssize_t n;
+
+ if(bytes>buffer_allocated)
+    buffer=(unsigned char *)realloc(buffer,buffer_allocated=bytes);
+
+ byteno+=bytes;
+
+ buffer_ptr=buffer;
+ buffer_end=buffer;
+
+ do
+   {
+    n=read(fd,buffer_end,bytes);
+
+    if(n<=0)
+       return(1);
+
+    buffer_end+=n;
+    bytes-=n;
+   }
+ while(bytes>0);
+
+ return(0);
+}
+
+#if defined(USE_GZIP) && USE_GZIP
+static int uncompress_pbf(unsigned char *data,uint32_t compressed,uint32_t uncompressed);
+#endif /* USE_GZIP */
+
+static void process_string_table(unsigned char *data,uint32_t length);
+static void process_primitive_group(unsigned char *data,uint32_t length);
+static void process_nodes(unsigned char *data,uint32_t length);
+static void process_dense_nodes(unsigned char *data,uint32_t length);
+static void process_ways(unsigned char *data,uint32_t length);
+static void process_relations(unsigned char *data,uint32_t length);
+
+
+/* Macros to simplify the parser (and make it look more like the XML parser) */
+
+#define BEGIN(xx)            do{ state=(xx); goto finish_parsing; } while(0)
+
+#define BUFFER_CHARS_EOF(xx) do{ if(buffer_refill(fd,(xx))) BEGIN(PBF_EOF); } while(0)
+
+#define BUFFER_CHARS(xx)     do{ if(buffer_refill(fd,(xx))) BEGIN(PBF_ERROR_UNEXP_EOF); } while(0)
+
+
+/* PBF decoding */
+
+#define PBF_FIELD(xx)   (int)(((xx)&0xFFF8)>>3)
+#define PBF_TYPE(xx)    (int)((xx)&0x0007)
+
+#define PBF_LATITUDE(xx)  (double)(1E-9*(granularity*(xx)+lat_offset))
+#define PBF_LONGITUDE(xx) (double)(1E-9*(granularity*(xx)+lon_offset))
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse a PBF int32 data value.
+
+  uint32_t pbf_int32 Returns the integer value.
+
+  unsigned char **ptr The pointer to read the data from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline uint32_t pbf_int32(unsigned char **ptr)
+{
+ uint32_t result=(**ptr)&0x7F;
+
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<7;
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<14;
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<21;
+ if((**ptr)&0x80) result+=((*++(*ptr))&0x7F)<<28;
+
+ (*ptr)++;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse a PBF int64 data value.
+
+  int64_t pbf_int64 Returns the integer value.
+
+  unsigned char **ptr The pointer to read the data from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int64_t pbf_int64(unsigned char **ptr)
+{
+ uint64_t result=(**ptr)&0x7F;
+
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<7;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<14;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<21;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<28;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<35;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<42;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<49;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<56;
+ if((**ptr)&0x80) result+=(uint64_t)((*++(*ptr))&0x7F)<<63;
+
+ (*ptr)++;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse a PBF sint64 data value.
+
+  int64_t pbf_sint64 Returns the integer value.
+
+  unsigned char **ptr The pointer to read the data from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int64_t pbf_sint64(unsigned char **ptr)
+{
+ int64_t result=((**ptr)&0x7E)>>1;
+ int sign=(**ptr)&0x01;
+
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<6;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<13;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<20;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<27;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<34;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<41;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<48;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<55;
+ if((**ptr)&0x80) result+=(int64_t)((*++(*ptr))&0x7F)<<62;
+
+ (*ptr)++;
+
+ if(sign)
+    result=-result-1;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse a PBF length delimited data value.
+
+  unsigned char *pbf_length_delimited Returns a pointer to the start of the data.
+
+  unsigned char **ptr The pointer to read the data from.
+
+  uint32_t *length Returns the length of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline unsigned char *pbf_length_delimited(unsigned char **ptr,uint32_t *length)
+{
+ uint32_t len=pbf_int32(ptr);
+
+ if(length)
+    *length=len;
+
+ *ptr+=len;
+
+ return(*ptr-len);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Skip any pbf field from a message.
+
+  unsigned char **ptr The pointer to read the data from.
+
+  int type The type of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline void pbf_skip(unsigned char **ptr,int type)
+{
+ uint32_t length;
+
+ switch(type)
+   {
+   case 0: /* varint */
+    while((**ptr)&0x80) (*ptr)++;
+    (*ptr)++;
+    break;
+   case 1: /* 64-bit */
+    *ptr+=8;
+    break;
+   case 2: /* length delimited */
+    length=pbf_int32(ptr);
+    *ptr+=length;
+    break;
+   case 3: /* deprecated */
+    break;
+   case 4: /* deprecated */
+    break;
+   case 5: /* 32-bit */
+    *ptr+=4;
+    break;
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse the PBF and call the functions for each OSM item as seen.
+
+  int ParsePBF Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to parse.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int ParsePBF(int fd)
+{
+ int state;
+ unsigned char *error=NULL;
+
+ /* Print the initial message */
+
+ printf_first("Reading: Bytes=0 Nodes=0 Ways=0 Relations=0");
+
+ /* The actual parser. */
+
+ nnodes=0,nways=0,nrelations=0;
+
+ string_table_allocated=16384;
+ string_table_length=0;
+ string_table=(unsigned char **)malloc(string_table_allocated*sizeof(unsigned char *));
+ string_table_string_lengths=(uint32_t *)malloc(string_table_allocated*sizeof(uint32_t));
+
+ zbuffer_allocated=0;
+ zbuffer=NULL;
+
+ buffer_allocated=65536;
+ buffer=(unsigned char*)malloc(buffer_allocated);
+
+ buffer_ptr=buffer_end=buffer;
+
+ while(1)
+   {
+    int32_t blob_header_length=0;
+    int osm_data=0,osm_header=0;
+    int32_t blob_length=0;
+    uint32_t raw_size=0,compressed_size=0,uncompressed_size=0;
+    unsigned char *raw_data=NULL,*zlib_data=NULL;
+    uint32_t length;
+    unsigned char *data;
+
+    /* ================ Parsing states ================ */
+
+
+    BUFFER_CHARS_EOF(4);
+
+    blob_header_length=(256*(256*(256*(int)buffer_ptr[0])+(int)buffer_ptr[1])+(int)buffer_ptr[2])+buffer_ptr[3];
+    buffer_ptr+=4;
+
+    if(blob_header_length==0 || blob_header_length>LENGTH_32M)
+       BEGIN(PBF_ERROR_BLOB_HEADER_LEN);
+
+
+    BUFFER_CHARS(blob_header_length);
+
+    osm_header=0;
+    osm_data=0;
+
+    while(buffer_ptr<buffer_end)
+      {
+       int fieldtype=pbf_int32(&buffer_ptr);
+       int field=PBF_FIELD(fieldtype);
+
+       switch(field)
+         {
+         case PBF_VAL_BLOBHEADER_TYPE: /* string */
+          {
+           uint32_t length=0;
+           unsigned char *type=NULL;
+
+           type=pbf_length_delimited(&buffer_ptr,&length);
+
+           if(length==9 && !strncmp((char*)type,"OSMHeader",9))
+              osm_header=1;
+
+           if(length==7 && !strncmp((char*)type,"OSMData",7))
+              osm_data=1;
+          }
+          break;
+
+         case PBF_VAL_BLOBHEADER_SIZE: /* int32 */
+          blob_length=pbf_int32(&buffer_ptr);
+          break;
+
+         default:
+          pbf_skip(&buffer_ptr,PBF_TYPE(fieldtype));
+         }
+      }
+
+    if(blob_length==0 || blob_length>LENGTH_32M)
+       BEGIN(PBF_ERROR_BLOB_LEN);
+
+    if(!osm_data && !osm_header)
+       BEGIN(PBF_ERROR_NOT_OSM);
+
+
+    BUFFER_CHARS(blob_length);
+
+    while(buffer_ptr<buffer_end)
+      {
+       int fieldtype=pbf_int32(&buffer_ptr);
+       int field=PBF_FIELD(fieldtype);
+
+       switch(field)
+         {
+         case PBF_VAL_BLOB_RAW_DATA: /* bytes */
+          raw_data=pbf_length_delimited(&buffer_ptr,&raw_size);
+          break;
+
+         case PBF_VAL_BLOB_RAW_SIZE: /* int32 */
+          uncompressed_size=pbf_int32(&buffer_ptr);
+          break;
+
+         case PBF_VAL_BLOB_ZLIB_DATA: /* bytes */
+          zlib_data=pbf_length_delimited(&buffer_ptr,&compressed_size);
+          break;
+
+         default:
+          pbf_skip(&buffer_ptr,PBF_TYPE(fieldtype));
+         }
+      }
+
+    if(raw_data && zlib_data)
+       BEGIN(PBF_ERROR_BLOB_BOTH);
+
+    if(!raw_data && !zlib_data)
+       BEGIN(PBF_ERROR_BLOB_NEITHER);
+
+    if(zlib_data)
+      {
+#if defined(USE_GZIP) && USE_GZIP
+       int newstate=uncompress_pbf(zlib_data,compressed_size,uncompressed_size);
+
+       if(newstate)
+          BEGIN(newstate);
+#else
+       BEGIN(PBF_ERROR_NO_GZIP);
+#endif
+      }
+    else
+      {
+       buffer_ptr=raw_data;
+       buffer_end=raw_data+raw_size;
+      }
+
+
+    if(osm_header)
+      {
+       while(buffer_ptr<buffer_end)
+         {
+          int fieldtype=pbf_int32(&buffer_ptr);
+          int field=PBF_FIELD(fieldtype);
+
+          switch(field)
+            {
+            case PBF_VAL_REQUIRED_FEATURES: /* string */
+             {
+              uint32_t length=0;
+              unsigned char *feature=NULL;
+
+              feature=pbf_length_delimited(&buffer_ptr,&length);
+
+              if(strncmp((char*)feature,"OsmSchema-V0.6",14) &&
+                 strncmp((char*)feature,"DenseNodes",10))
+                {
+                 feature[length]=0;
+                 error=feature;
+                 BEGIN(PBF_ERROR_UNSUPPORTED);
+                }
+             }
+             break;
+
+            case PBF_VAL_OPTIONAL_FEATURES: /* string */
+             pbf_length_delimited(&buffer_ptr,NULL);
+             break;
+
+            default:
+             pbf_skip(&buffer_ptr,PBF_TYPE(fieldtype));
+            }
+         }
+      }
+
+
+    if(osm_data)
+      {
+       unsigned char *primitive_group[8]={NULL};
+       uint32_t primitive_group_length[8]={0};
+       uint32_t nprimitive_groups=0,i;
+
+       granularity=100;
+       lat_offset=lon_offset=0;
+
+       while(buffer_ptr<buffer_end)
+         {
+          int fieldtype=pbf_int32(&buffer_ptr);
+          int field=PBF_FIELD(fieldtype);
+
+          switch(field)
+            {
+            case PBF_VAL_STRING_TABLE: /* bytes */
+             data=pbf_length_delimited(&buffer_ptr,&length);
+             process_string_table(data,length);
+             break;
+
+            case PBF_VAL_PRIMITIVE_GROUP: /* bytes */
+             primitive_group[nprimitive_groups]=pbf_length_delimited(&buffer_ptr,&primitive_group_length[nprimitive_groups]);
+
+             if(++nprimitive_groups>(sizeof(primitive_group)/sizeof(primitive_group[0])))
+                BEGIN(PBF_ERROR_TOO_MANY_GROUPS);
+             break;
+
+            case PBF_VAL_GRANULARITY: /* int32 */
+             granularity=pbf_int32(&buffer_ptr);
+             break;
+
+            case PBF_VAL_LAT_OFFSET: /* int64 */
+             lat_offset=pbf_int64(&buffer_ptr);
+             break;
+
+            case PBF_VAL_LON_OFFSET: /* int64 */
+             lon_offset=pbf_int64(&buffer_ptr);
+             break;
+
+            default:
+             pbf_skip(&buffer_ptr,PBF_TYPE(fieldtype));
+            }
+         }
+
+       if(nprimitive_groups)
+          for(i=0;i<nprimitive_groups;i++)
+             process_primitive_group(primitive_group[i],primitive_group_length[i]);
+      }
+   }
+
+
+ finish_parsing:
+
+ switch(state)
+   {
+    /* End of file */
+
+   case PBF_EOF:
+    break;
+
+
+    /* ================ Error states ================ */
+
+
+   case PBF_ERROR_UNEXP_EOF:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": unexpected end of file seen.\n",byteno);
+    break;
+
+   case PBF_ERROR_BLOB_HEADER_LEN:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": BlobHeader length is wrong (0<x<=32M).\n",byteno);
+    break;
+
+   case PBF_ERROR_BLOB_LEN:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob length is wrong (0<x<=32M).\n",byteno);
+    break;
+
+   case PBF_ERROR_NOT_OSM:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": BlobHeader is neither 'OSMData' or 'OSMHeader'.\n",byteno);
+    break;
+
+   case PBF_ERROR_BLOB_BOTH:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob has both zlib compressed and raw uncompressed data.\n",byteno);
+    break;
+
+   case PBF_ERROR_BLOB_NEITHER:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob has neither zlib compressed or raw uncompressed data.\n",byteno);
+    break;
+
+   case PBF_ERROR_NO_GZIP:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob is compressed but no gzip support is available.\n",byteno);
+    break;
+
+   case PBF_ERROR_GZIP_INIT:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob is compressed but failed to initialise decompression.\n",byteno);
+    break;
+
+   case PBF_ERROR_GZIP_INFLATE:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob is compressed but failed to uncompress it.\n",byteno);
+    break;
+
+   case PBF_ERROR_GZIP_WRONG_LEN:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob is compressed and wrong size when uncompressed.\n",byteno);
+    break;
+
+   case PBF_ERROR_GZIP_END:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Blob is compressed but failed to finalise decompression.\n",byteno);
+    break;
+
+   case PBF_ERROR_UNSUPPORTED:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": Unsupported required feature '%s'.\n",byteno,error);
+    break;
+
+   case PBF_ERROR_TOO_MANY_GROUPS:
+    fprintf(stderr,"PBF Parser: Error at byte %"PRIu64": OsmData message contains too many PrimitiveGroup messages.\n",byteno);
+    break;
+   }
+
+ /* Free the parser variables */
+
+ free(string_table);
+ free(string_table_string_lengths);
+
+ free(buffer);
+ if(zbuffer)
+    free(zbuffer);
+
+ /* Print the final message */
+
+ printf_last("Read: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ return(state);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a PBF StringTable message.
+
+  unsigned char *data The data to process.
+
+  uint32_t length The length of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_string_table(unsigned char *data,uint32_t length)
+{
+ unsigned char *end=data+length;
+ unsigned char *string;
+ uint32_t string_length;
+
+ string_table_length=0;
+
+ while(data<end)
+   {
+    int fieldtype=pbf_int32(&data);
+    int field=PBF_FIELD(fieldtype);
+
+    switch(field)
+      {
+      case PBF_VAL_STRING:      /* string */
+       string=pbf_length_delimited(&data,&string_length);
+
+       if(string_table_length==string_table_allocated)
+         {
+          string_table_allocated+=8192;
+          string_table=(unsigned char **)realloc(string_table,string_table_allocated*sizeof(unsigned char *));
+          string_table_string_lengths=(uint32_t *)realloc(string_table_string_lengths,string_table_allocated*sizeof(uint32_t));
+         }
+
+       string_table[string_table_length]=string;
+       string_table_string_lengths[string_table_length]=string_length;
+
+       string_table_length++;
+       break;
+
+      default:
+       pbf_skip(&data,PBF_TYPE(fieldtype));
+      }
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a PBF PrimitiveGroup message.
+
+  unsigned char *data The data to process.
+
+  uint32_t length The length of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_primitive_group(unsigned char *data,uint32_t length)
+{
+ unsigned char *end=data+length;
+ unsigned char *subdata;
+ uint32_t sublength;
+ int i;
+
+ /* Fixup the strings (not null terminated in buffer) */
+
+ for(i=0;i<string_table_length;i++)
+    string_table[i][string_table_string_lengths[i]]=0;
+
+
+ while(data<end)
+   {
+    int fieldtype=pbf_int32(&data);
+    int field=PBF_FIELD(fieldtype);
+
+    switch(field)
+      {
+      case PBF_VAL_NODES:       /* message */
+       subdata=pbf_length_delimited(&data,&sublength);
+       process_nodes(subdata,sublength);
+       break;
+
+      case PBF_VAL_DENSE_NODES: /* message */
+       subdata=pbf_length_delimited(&data,&sublength);
+       process_dense_nodes(subdata,sublength);
+       break;
+
+      case PBF_VAL_WAYS:        /* message */
+       subdata=pbf_length_delimited(&data,&sublength);
+       process_ways(subdata,sublength);
+       break;
+
+      case PBF_VAL_RELATIONS:   /* message */
+       subdata=pbf_length_delimited(&data,&sublength);
+       process_relations(subdata,sublength);
+       break;
+
+      default:
+       pbf_skip(&data,PBF_TYPE(fieldtype));
+      }
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a PBF Node message.
+
+  unsigned char *data The data to process.
+
+  uint32_t length The length of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_nodes(unsigned char *data,uint32_t length)
+{
+ unsigned char *end=data+length;
+ int64_t id=0;
+ unsigned char *keys=NULL,*vals=NULL;
+ unsigned char *keys_end=NULL,*vals_end=NULL;
+ uint32_t keylen=0,vallen=0;
+ int64_t lat=0,lon=0;
+ TagList *tags=NULL,*result=NULL;
+
+ while(data<end)
+   {
+    int fieldtype=pbf_int32(&data);
+    int field=PBF_FIELD(fieldtype);
+
+    switch(field)
+      {
+      case PBF_VAL_NODE_ID:     /* sint64 */
+       id=pbf_sint64(&data);
+       break;
+
+      case PBF_VAL_NODE_KEYS:   /* packed int32 */
+       keys=pbf_length_delimited(&data,&keylen);
+       keys_end=keys+keylen;
+       break;
+
+      case PBF_VAL_NODE_VALS:   /* packed int32 */
+       vals=pbf_length_delimited(&data,&vallen);
+       vals_end=vals+vallen;
+       break;
+
+      case PBF_VAL_NODE_LAT:    /* sint64 */
+       lat=pbf_sint64(&data);
+       break;
+
+      case PBF_VAL_NODE_LON:    /* sint64 */
+       lon=pbf_sint64(&data);
+       break;
+
+      default:
+       pbf_skip(&data,PBF_TYPE(fieldtype));
+      }
+   }
+
+ /* Mangle the data and send it to the OSM parser */
+
+ nnodes++;
+
+ if(!(nnodes%10000))
+    printf_middle("Reading: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ tags=NewTagList();
+
+ if(keys && vals)
+   {
+    while(keys<keys_end && vals<vals_end)
+      {
+       uint32_t key=pbf_int32(&keys);
+       uint32_t val=pbf_int32(&vals);
+
+       AppendTag(tags,(char*)string_table[key],(char*)string_table[val]);
+      }
+   }
+
+ result=ApplyNodeTaggingRules(tags,id);
+
+ ProcessNodeTags(result,id,PBF_LATITUDE(lat),PBF_LONGITUDE(lon),MODE_NORMAL);
+
+ DeleteTagList(tags);
+ DeleteTagList(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a PBF DenseNode message.
+
+  unsigned char *data The data to process.
+
+  uint32_t length The length of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_dense_nodes(unsigned char *data,uint32_t length)
+{
+ unsigned char *end=data+length;
+ unsigned char *ids=NULL,*keys_vals=NULL,*lats=NULL,*lons=NULL;
+ unsigned char *ids_end=NULL;
+ uint32_t idlen=0;
+ int64_t id=0;
+ int64_t lat=0,lon=0;
+ TagList *tags=NULL,*result;
+
+ while(data<end)
+   {
+    int fieldtype=pbf_int32(&data);
+    int field=PBF_FIELD(fieldtype);
+
+    switch(field)
+      {
+      case PBF_VAL_DENSE_NODE_ID: /* packed sint64 */
+       ids=pbf_length_delimited(&data,&idlen);
+       ids_end=ids+idlen;
+       break;
+
+      case PBF_VAL_DENSE_NODE_LAT: /* packed sint64 */
+       lats=pbf_length_delimited(&data,NULL);
+       break;
+
+      case PBF_VAL_DENSE_NODE_LON: /* packed sint64 */
+       lons=pbf_length_delimited(&data,NULL);
+       break;
+
+      case PBF_VAL_DENSE_NODE_KEYS_VALS: /* packed int32 */
+       keys_vals=pbf_length_delimited(&data,NULL);
+       break;
+
+      default:
+       pbf_skip(&data,PBF_TYPE(fieldtype));
+      }
+   }
+
+ while(ids<ids_end)
+   {
+    int64_t delta_id;
+    int64_t delta_lat,delta_lon;
+
+    delta_id=pbf_sint64(&ids);
+    delta_lat=pbf_sint64(&lats);
+    delta_lon=pbf_sint64(&lons);
+
+    id+=delta_id;
+    lat+=delta_lat;
+    lon+=delta_lon;
+
+    /* Mangle the data and send it to the OSM parser */
+
+    nnodes++;
+
+    if(!(nnodes%10000))
+       printf_middle("Reading: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+    tags=NewTagList();
+
+    if(keys_vals)
+      {
+       while(1)
+         {
+          uint32_t key=pbf_int32(&keys_vals),val;
+
+          if(key==0)
+             break;
+
+          val=pbf_int32(&keys_vals);
+
+          AppendTag(tags,(char*)string_table[key],(char*)string_table[val]);
+         }
+      }
+
+    result=ApplyNodeTaggingRules(tags,id);
+
+    ProcessNodeTags(result,id,PBF_LATITUDE(lat),PBF_LONGITUDE(lon),MODE_NORMAL);
+
+    DeleteTagList(tags);
+    DeleteTagList(result);
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a PBF Way message.
+
+  unsigned char *data The data to process.
+
+  uint32_t length The length of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_ways(unsigned char *data,uint32_t length)
+{
+ unsigned char *end=data+length;
+ int64_t id=0;
+ unsigned char *keys=NULL,*vals=NULL,*refs=NULL;
+ unsigned char *keys_end=NULL,*vals_end=NULL,*refs_end=NULL;
+ uint32_t keylen=0,vallen=0,reflen=0;
+ int64_t ref=0;
+ TagList *tags=NULL,*result;
+
+ while(data<end)
+   {
+    int fieldtype=pbf_int32(&data);
+    int field=PBF_FIELD(fieldtype);
+
+    switch(field)
+      {
+      case PBF_VAL_WAY_ID:      /* int64 */
+       id=pbf_int64(&data);
+       break;
+
+      case PBF_VAL_WAY_KEYS:    /* packed int32 */
+       keys=pbf_length_delimited(&data,&keylen);
+       keys_end=keys+keylen;
+       break;
+
+      case PBF_VAL_WAY_VALS:    /* packed int32 */
+       vals=pbf_length_delimited(&data,&vallen);
+       vals_end=vals+vallen;
+       break;
+
+      case PBF_VAL_WAY_REFS:    /* packed sint64 */
+       refs=pbf_length_delimited(&data,&reflen);
+       refs_end=refs+reflen;
+       break;
+
+      default:
+       pbf_skip(&data,PBF_TYPE(fieldtype));
+      }
+   }
+
+ /* Mangle the data and send it to the OSM parser */
+
+ nways++;
+
+ if(!(nways%1000))
+    printf_middle("Reading: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ tags=NewTagList();
+
+ if(keys && vals)
+   {
+    while(keys<keys_end && vals<vals_end)
+      {
+       uint32_t key=pbf_int32(&keys);
+       uint32_t val=pbf_int32(&vals);
+
+       AppendTag(tags,(char*)string_table[key],(char*)string_table[val]);
+      }
+   }
+
+ AddWayRefs(0);
+
+ if(refs)
+    while(refs<refs_end)
+      {
+       int64_t delta_ref;
+
+       delta_ref=pbf_sint64(&refs);
+
+       ref+=delta_ref;
+
+       if(ref==0)
+          break;
+
+       AddWayRefs(ref);
+      }
+
+ result=ApplyWayTaggingRules(tags,id);
+
+ ProcessWayTags(result,id,MODE_NORMAL);
+
+ DeleteTagList(tags);
+ DeleteTagList(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a PBF Relation message.
+
+  unsigned char *data The data to process.
+
+  uint32_t length The length of the data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void process_relations(unsigned char *data,uint32_t length)
+{
+ unsigned char *end=data+length;
+ int64_t id=0;
+ unsigned char *keys=NULL,*vals=NULL,*roles=NULL,*memids=NULL,*types=NULL;
+ unsigned char *keys_end=NULL,*vals_end=NULL,*memids_end=NULL,*types_end=NULL;
+ uint32_t keylen=0,vallen=0,rolelen=0,memidlen=0,typelen=0;
+ int64_t memid=0;
+ TagList *tags=NULL,*result;
+
+ while(data<end)
+   {
+    int fieldtype=pbf_int32(&data);
+    int field=PBF_FIELD(fieldtype);
+
+    switch(field)
+      {
+      case PBF_VAL_RELATION_ID: /* int64 */
+       id=pbf_int64(&data);
+       break;
+
+      case PBF_VAL_RELATION_KEYS: /* packed string */
+       keys=pbf_length_delimited(&data,&keylen);
+       keys_end=keys+keylen;
+       break;
+
+      case PBF_VAL_RELATION_VALS: /* packed string */
+       vals=pbf_length_delimited(&data,&vallen);
+       vals_end=vals+vallen;
+       break;
+
+      case PBF_VAL_RELATION_ROLES: /* packed int32 */
+       roles=pbf_length_delimited(&data,&rolelen);
+       break;
+
+      case PBF_VAL_RELATION_MEMIDS: /* packed sint64 */
+       memids=pbf_length_delimited(&data,&memidlen);
+       memids_end=memids+memidlen;
+       break;
+
+      case PBF_VAL_RELATION_TYPES: /* packed enum */
+       types=pbf_length_delimited(&data,&typelen);
+       types_end=types+typelen;
+       break;
+
+      default:
+       pbf_skip(&data,PBF_TYPE(fieldtype));
+      }
+   }
+
+ /* Mangle the data and send it to the OSM parser */
+
+ nrelations++;
+
+ if(!(nrelations%1000))
+    printf_middle("Reading: Bytes=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,byteno,nnodes,nways,nrelations);
+
+ AddRelationRefs(0,0,0,NULL);
+
+ tags=NewTagList();
+
+ if(keys && vals)
+   {
+    while(keys<keys_end && vals<vals_end)
+      {
+       uint32_t key=pbf_int32(&keys);
+       uint32_t val=pbf_int32(&vals);
+
+       AppendTag(tags,(char*)string_table[key],(char*)string_table[val]);
+      }
+   }
+
+ if(memids && types)
+    while(memids<memids_end && types<types_end)
+      {
+       int64_t delta_memid;
+       unsigned char *role=NULL;
+       int type;
+
+       delta_memid=pbf_sint64(&memids);
+       type=pbf_int32(&types);
+
+       if(roles)
+          role=string_table[pbf_int32(&roles)];
+
+       memid+=delta_memid;
+
+       if(type==0)
+          AddRelationRefs(memid,0,0,(char*)role);
+       else if(type==1)
+          AddRelationRefs(0,memid,0,(char*)role);
+       else if(type==2)
+          AddRelationRefs(0,0,memid,(char*)role);
+      }
+
+ result=ApplyRelationTaggingRules(tags,id);
+
+ ProcessRelationTags(result,id,MODE_NORMAL);
+
+ DeleteTagList(tags);
+ DeleteTagList(result);
+}
+
+
+#if defined(USE_GZIP) && USE_GZIP
+
+/*++++++++++++++++++++++++++++++++++++++
+  Uncompress the part of the PBF data that is compressed.
+
+  int uncompress_pbf Returns the error state or 0 if OK.
+
+  unsigned char *data The data to uncompress.
+
+  uint32_t compressed The number of bytes to uncompress.
+
+  uint32_t uncompressed The number of bytes expected when uncompressed.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int uncompress_pbf(unsigned char *data,uint32_t compressed,uint32_t uncompressed)
+{
+ z_stream z={0};
+
+ if(uncompressed>zbuffer_allocated)
+    zbuffer=(unsigned char *)realloc(zbuffer,zbuffer_allocated=uncompressed);
+
+ if(inflateInit2(&z,15+32)!=Z_OK)
+    return(PBF_ERROR_GZIP_INIT);
+
+ z.next_in=data;
+ z.avail_in=compressed;
+
+ z.next_out=zbuffer;
+ z.avail_out=uncompressed;
+
+ if(inflate(&z,Z_FINISH)!=Z_STREAM_END)
+    return(PBF_ERROR_GZIP_INFLATE);
+
+ if(z.avail_out!=0)
+    return(PBF_ERROR_GZIP_WRONG_LEN);
+
+ if(inflateEnd(&z)!=Z_OK)
+    return(PBF_ERROR_GZIP_END);
+
+ buffer_ptr=zbuffer;
+ buffer_end=zbuffer+uncompressed;
+
+ return(0);
+}
+
+#endif /* USE_GZIP */
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse a PBF format OSM file (from planet download).
+
+  int ParsePBFFile Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to read from.
+
+  NodesX *OSMNodes The data structure of nodes to fill in.
+
+  WaysX *OSMWays The data structure of ways to fill in.
+
+  RelationsX *OSMRelations The data structure of relations to fill in.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParsePBFFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations)
+{
+ int retval;
+
+ /* Initialise the parser */
+
+ InitialiseParser(OSMNodes,OSMWays,OSMRelations);
+
+ /* Parse the file */
+
+ retval=ParsePBF(fd);
+
+ /* Cleanup the parser */
+
+ CleanupParser();
+
+ return(retval);
+}
diff --git a/src/osmxmlparse.c b/src/osmxmlparse.c
new file mode 100644
index 0000000..39af899
--- /dev/null
+++ b/src/osmxmlparse.c
@@ -0,0 +1,703 @@
+/***************************************
+ OSM XML file parser (either JOSM or planet)
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2013 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include "osmparser.h"
+#include "xmlparse.h"
+#include "tagging.h"
+#include "logging.h"
+
+
+/* Local variables */
+
+static int current_mode=MODE_NORMAL;
+
+static uint64_t nnodes=0,nways=0,nrelations=0;
+
+static TagList *current_tags=NULL;
+
+
+/* The XML tag processing function prototypes */
+
+//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding);
+static int osmType_function(const char *_tag_,int _type_,const char *version);
+static int osmChangeType_function(const char *_tag_,int _type_,const char *version);
+//static int boundsType_function(const char *_tag_,int _type_);
+//static int boundType_function(const char *_tag_,int _type_);
+static int changesetType_function(const char *_tag_,int _type_);
+static int modifyType_function(const char *_tag_,int _type_);
+static int createType_function(const char *_tag_,int _type_);
+static int deleteType_function(const char *_tag_,int _type_);
+static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon);
+static int wayType_function(const char *_tag_,int _type_,const char *id);
+static int relationType_function(const char *_tag_,int _type_,const char *id);
+static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v);
+static int ndType_function(const char *_tag_,int _type_,const char *ref);
+static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role);
+
+
+/* The XML tag definitions (forward declarations) */
+
+static xmltag xmlDeclaration_tag;
+static xmltag osmType_tag;
+static xmltag osmChangeType_tag;
+static xmltag boundsType_tag;
+static xmltag boundType_tag;
+static xmltag changesetType_tag;
+static xmltag modifyType_tag;
+static xmltag createType_tag;
+static xmltag deleteType_tag;
+static xmltag nodeType_tag;
+static xmltag wayType_tag;
+static xmltag relationType_tag;
+static xmltag tagType_tag;
+static xmltag ndType_tag;
+static xmltag memberType_tag;
+
+
+/* The XML tag definition values */
+
+/*+ The complete set of tags at the top level for OSM. +*/
+static xmltag *xml_osm_toplevel_tags[]={&xmlDeclaration_tag,&osmType_tag,NULL};
+
+/*+ The complete set of tags at the top level for OSC. +*/
+static xmltag *xml_osc_toplevel_tags[]={&xmlDeclaration_tag,&osmChangeType_tag,NULL};
+
+/*+ The xmlDeclaration type tag. +*/
+static xmltag xmlDeclaration_tag=
+              {"xml",
+               2, {"version","encoding"},
+               NULL,
+               {NULL}};
+
+/*+ The osmType type tag. +*/
+static xmltag osmType_tag=
+              {"osm",
+               1, {"version"},
+               osmType_function,
+               {&boundsType_tag,&boundType_tag,&changesetType_tag,&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
+
+/*+ The osmChangeType type tag. +*/
+static xmltag osmChangeType_tag=
+              {"osmChange",
+               1, {"version"},
+               osmChangeType_function,
+               {&boundsType_tag,&modifyType_tag,&createType_tag,&deleteType_tag,NULL}};
+
+/*+ The boundsType type tag. +*/
+static xmltag boundsType_tag=
+              {"bounds",
+               0, {NULL},
+               NULL,
+               {NULL}};
+
+/*+ The boundType type tag. +*/
+static xmltag boundType_tag=
+              {"bound",
+               0, {NULL},
+               NULL,
+               {NULL}};
+
+/*+ The changesetType type tag. +*/
+static xmltag changesetType_tag=
+              {"changeset",
+               0, {NULL},
+               changesetType_function,
+               {&tagType_tag,NULL}};
+
+/*+ The modifyType type tag. +*/
+static xmltag modifyType_tag=
+              {"modify",
+               0, {NULL},
+               modifyType_function,
+               {&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
+
+/*+ The createType type tag. +*/
+static xmltag createType_tag=
+              {"create",
+               0, {NULL},
+               createType_function,
+               {&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
+
+/*+ The deleteType type tag. +*/
+static xmltag deleteType_tag=
+              {"delete",
+               0, {NULL},
+               deleteType_function,
+               {&nodeType_tag,&wayType_tag,&relationType_tag,NULL}};
+
+/*+ The nodeType type tag. +*/
+static xmltag nodeType_tag=
+              {"node",
+               3, {"id","lat","lon"},
+               nodeType_function,
+               {&tagType_tag,NULL}};
+
+/*+ The wayType type tag. +*/
+static xmltag wayType_tag=
+              {"way",
+               1, {"id"},
+               wayType_function,
+               {&ndType_tag,&tagType_tag,NULL}};
+
+/*+ The relationType type tag. +*/
+static xmltag relationType_tag=
+              {"relation",
+               1, {"id"},
+               relationType_function,
+               {&memberType_tag,&tagType_tag,NULL}};
+
+/*+ The tagType type tag. +*/
+static xmltag tagType_tag=
+              {"tag",
+               2, {"k","v"},
+               tagType_function,
+               {NULL}};
+
+/*+ The ndType type tag. +*/
+static xmltag ndType_tag=
+              {"nd",
+               1, {"ref"},
+               ndType_function,
+               {NULL}};
+
+/*+ The memberType type tag. +*/
+static xmltag memberType_tag=
+              {"member",
+               3, {"type","ref","role"},
+               memberType_function,
+               {NULL}};
+
+
+/* The XML tag processing functions */
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the XML declaration is seen
+
+  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *version The contents of the 'version' attribute (or NULL if not defined).
+
+  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the osmType XSD type is seen
+
+  int osmType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *version The contents of the 'version' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int osmType_function(const char *_tag_,int _type_,const char *version)
+{
+ /* Print the initial message */
+
+ if(_type_&XMLPARSE_TAG_START)
+    printf_first("Read: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes=0,nways=0,nrelations=0);
+
+ /* Check the tag values */
+
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    current_mode=MODE_NORMAL;
+
+    if(!version || strcmp(version,"0.6"))
+       XMLPARSE_MESSAGE(_tag_,"Invalid value for 'version' (only '0.6' accepted)");
+   }
+
+ /* Print the final message */
+
+ if(_type_&XMLPARSE_TAG_END)
+    printf_last("Read: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the osmChangeType XSD type is seen
+
+  int osmChangeType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *version The contents of the 'version' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int osmChangeType_function(const char *_tag_,int _type_,const char *version)
+{
+ /* Print the initial message */
+
+ if(_type_&XMLPARSE_TAG_START)
+    printf_first("Read: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes=0,nways=0,nrelations=0);
+
+ /* Check the tag values */
+
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    if(!version || strcmp(version,"0.6"))
+       XMLPARSE_MESSAGE(_tag_,"Invalid value for 'version' (only '0.6' accepted)");
+   }
+
+ /* Print the final message */
+
+ if(_type_&XMLPARSE_TAG_END)
+    printf_last("Read: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the boundsType XSD type is seen
+
+  int boundsType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int boundsType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the boundType XSD type is seen
+
+  int boundType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int boundType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the changesetType XSD type is seen
+
+  int changesetType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int changesetType_function(const char *_tag_,int _type_)
+{
+ current_tags=NULL;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the modifyType XSD type is seen
+
+  int modifyType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int modifyType_function(const char *_tag_,int _type_)
+{
+ if(_type_&XMLPARSE_TAG_START)
+    current_mode=MODE_MODIFY;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the createType XSD type is seen
+
+  int createType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int createType_function(const char *_tag_,int _type_)
+{
+ if(_type_&XMLPARSE_TAG_START)
+    current_mode=MODE_CREATE;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the deleteType XSD type is seen
+
+  int deleteType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int deleteType_function(const char *_tag_,int _type_)
+{
+ if(_type_&XMLPARSE_TAG_START)
+    current_mode=MODE_DELETE;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the nodeType XSD type is seen
+
+  int nodeType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *id The contents of the 'id' attribute (or NULL if not defined).
+
+  const char *lat The contents of the 'lat' attribute (or NULL if not defined).
+
+  const char *lon The contents of the 'lon' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int nodeType_function(const char *_tag_,int _type_,const char *id,const char *lat,const char *lon)
+{
+ static int64_t llid;
+ static double latitude,longitude;
+
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    nnodes++;
+
+    if(!(nnodes%10000))
+       printf_middle("Reading: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
+
+    current_tags=NewTagList();
+
+    /* Handle the node information */
+
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need int64_t conversion */
+
+    if(current_mode!=MODE_DELETE)
+      {
+       XMLPARSE_ASSERT_FLOATING(_tag_,lat); latitude =atof(lat);
+       XMLPARSE_ASSERT_FLOATING(_tag_,lon); longitude=atof(lon);
+      }
+   }
+
+ if(_type_&XMLPARSE_TAG_END)
+   {
+    TagList *result=ApplyNodeTaggingRules(current_tags,llid);
+
+    ProcessNodeTags(result,llid,latitude,longitude,current_mode);
+
+    DeleteTagList(current_tags); current_tags=NULL;
+    DeleteTagList(result);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the wayType XSD type is seen
+
+  int wayType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *id The contents of the 'id' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int wayType_function(const char *_tag_,int _type_,const char *id)
+{
+ static int64_t llid;
+
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    nways++;
+
+    if(!(nways%1000))
+       printf_middle("Reading: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
+
+    current_tags=NewTagList();
+
+    AddWayRefs(0);
+
+    /* Handle the way information */
+
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need int64_t conversion */
+   }
+
+ if(_type_&XMLPARSE_TAG_END)
+   {
+    TagList *result=ApplyWayTaggingRules(current_tags,llid);
+
+    ProcessWayTags(result,llid,current_mode);
+
+    DeleteTagList(current_tags); current_tags=NULL;
+    DeleteTagList(result);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the relationType XSD type is seen
+
+  int relationType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *id The contents of the 'id' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int relationType_function(const char *_tag_,int _type_,const char *id)
+{
+ static int64_t llid;
+
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    nrelations++;
+
+    if(!(nrelations%1000))
+       printf_middle("Reading: Lines=%"PRIu64" Nodes=%"PRIu64" Ways=%"PRIu64" Relations=%"PRIu64,ParseXML_LineNumber(),nnodes,nways,nrelations);
+
+    current_tags=NewTagList();
+
+    AddRelationRefs(0,0,0,NULL);
+
+    /* Handle the relation information */
+
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need int64_t conversion */
+   }
+
+ if(_type_&XMLPARSE_TAG_END)
+   {
+    TagList *result=ApplyRelationTaggingRules(current_tags,llid);
+
+    ProcessRelationTags(result,llid,current_mode);
+
+    DeleteTagList(current_tags); current_tags=NULL;
+    DeleteTagList(result);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the tagType XSD type is seen
+
+  int tagType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *k The contents of the 'k' attribute (or NULL if not defined).
+
+  const char *v The contents of the 'v' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int tagType_function(const char *_tag_,int _type_,const char *k,const char *v)
+{
+ if(_type_&XMLPARSE_TAG_START && current_tags)
+   {
+    XMLPARSE_ASSERT_STRING(_tag_,k);
+    XMLPARSE_ASSERT_STRING(_tag_,v);
+
+    AppendTag(current_tags,k,v);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the ndType XSD type is seen
+
+  int ndType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *ref The contents of the 'ref' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int ndType_function(const char *_tag_,int _type_,const char *ref)
+{
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    int64_t llid;
+
+    XMLPARSE_ASSERT_INTEGER(_tag_,ref); llid=atoll(ref); /* need int64_t conversion */
+
+    AddWayRefs(llid);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the memberType XSD type is seen
+
+  int memberType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *type The contents of the 'type' attribute (or NULL if not defined).
+
+  const char *ref The contents of the 'ref' attribute (or NULL if not defined).
+
+  const char *role The contents of the 'role' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int memberType_function(const char *_tag_,int _type_,const char *type,const char *ref,const char *role)
+{
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    int64_t llid;
+
+    XMLPARSE_ASSERT_STRING(_tag_,type);
+    XMLPARSE_ASSERT_INTEGER(_tag_,ref); llid=atoll(ref); /* need int64_t conversion */
+
+    if(!strcmp(type,"node"))
+       AddRelationRefs(llid,0,0,role);
+    else if(!strcmp(type,"way"))
+       AddRelationRefs(0,llid,0,role);
+    else if(!strcmp(type,"relation"))
+       AddRelationRefs(0,0,llid,role);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an OSM XML file (from JOSM or planet download).
+
+  int ParseOSMFile Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to read from.
+
+  NodesX *OSMNodes The data structure of nodes to fill in.
+
+  WaysX *OSMWays The data structure of ways to fill in.
+
+  RelationsX *OSMRelations The data structure of relations to fill in.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParseOSMFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations)
+{
+ int retval;
+
+ /* Initialise the parser */
+
+ InitialiseParser(OSMNodes,OSMWays,OSMRelations);
+
+ /* Parse the file */
+
+ retval=ParseXML(fd,xml_osm_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
+
+ /* Cleanup the parser */
+
+ CleanupParser();
+
+ return(retval);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse an OSC XML file (from planet download).
+
+  int ParseOSCFile Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to read from.
+
+  NodesX *OSMNodes The data structure of nodes to fill in.
+
+  WaysX *OSMWays The data structure of ways to fill in.
+
+  RelationsX *OSMRelations The data structure of relations to fill in.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParseOSCFile(int fd,NodesX *OSMNodes,WaysX *OSMWays,RelationsX *OSMRelations)
+{
+ int retval;
+
+ /* Initialise the parser */
+
+ InitialiseParser(OSMNodes,OSMWays,OSMRelations);
+
+ /* Parse the file */
+
+ retval=ParseXML(fd,xml_osc_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
+
+ /* Cleanup the parser */
+
+ CleanupParser();
+
+ return(retval);
+}
diff --git a/src/output.c b/src/output.c
index 2616936..e464f20 100644
--- a/src/output.c
+++ b/src/output.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -61,7 +61,7 @@
 extern int option_quickest;
 
 /*+ The options to select the format of the output. +*/
-extern int option_html,option_gpx_track,option_gpx_route,option_text,option_text_all;
+extern int option_html,option_gpx_track,option_gpx_route,option_text,option_text_all,option_stdout;
 
 
 /* Local variables */
@@ -88,7 +88,7 @@ static char junction_other_way[Highway_Count][Highway_Count]=
 /*++++++++++++++++++++++++++++++++++++++
   Print the optimum route between two nodes.
 
-  Results **results The set of results to print (some may be NULL - ignore them).
+  Results **results The set of results to print (consecutive in array even if not consecutive waypoints).
 
   int nresults The number of results in the list.
 
@@ -109,84 +109,100 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
  distance_t cum_distance=0;
  duration_t cum_duration=0;
 
- int point=1;
+ int point=0;
  int segment_count=0,route_count=0;
  int point_count=0;
  int roundabout=0;
 
  /* Open the files */
 
- if(option_quickest==0)
+ if(option_stdout)
    {
-    /* Print the result for the shortest route */
-
     if(option_html)
-       htmlfile    =fopen("shortest.html","w");
+       htmlfile    =stdout;
     if(option_gpx_track)
-       gpxtrackfile=fopen("shortest-track.gpx","w");
+       gpxtrackfile=stdout;
     if(option_gpx_route)
-       gpxroutefile=fopen("shortest-route.gpx","w");
+       gpxroutefile=stdout;
     if(option_text)
-       textfile    =fopen("shortest.txt","w");
+       textfile    =stdout;
     if(option_text_all)
-       textallfile =fopen("shortest-all.txt","w");
-
-    if(option_html && !htmlfile)
-       fprintf(stderr,"Warning: Cannot open file 'shortest.html' for writing [%s].\n",strerror(errno));
-    if(option_gpx_track && !gpxtrackfile)
-       fprintf(stderr,"Warning: Cannot open file 'shortest-track.gpx' for writing [%s].\n",strerror(errno));
-    if(option_gpx_route && !gpxroutefile)
-       fprintf(stderr,"Warning: Cannot open file 'shortest-route.gpx' for writing [%s].\n",strerror(errno));
-    if(option_text && !textfile)
-       fprintf(stderr,"Warning: Cannot open file 'shortest.txt' for writing [%s].\n",strerror(errno));
-    if(option_text_all && !textallfile)
-       fprintf(stderr,"Warning: Cannot open file 'shortest-all.txt' for writing [%s].\n",strerror(errno));
+       textallfile =stdout;
    }
  else
    {
-    /* Print the result for the quickest route */
-
-    if(option_html)
-       htmlfile    =fopen("quickest.html","w");
-    if(option_gpx_track)
-       gpxtrackfile=fopen("quickest-track.gpx","w");
-    if(option_gpx_route)
-       gpxroutefile=fopen("quickest-route.gpx","w");
-    if(option_text)
-       textfile    =fopen("quickest.txt","w");
-    if(option_text_all)
-       textallfile =fopen("quickest-all.txt","w");
-
-    if(option_html && !htmlfile)
-       fprintf(stderr,"Warning: Cannot open file 'quickest.html' for writing [%s].\n",strerror(errno));
-    if(option_gpx_track && !gpxtrackfile)
-       fprintf(stderr,"Warning: Cannot open file 'quickest-track.gpx' for writing [%s].\n",strerror(errno));
-    if(option_gpx_route && !gpxroutefile)
-       fprintf(stderr,"Warning: Cannot open file 'quickest-route.gpx' for writing [%s].\n",strerror(errno));
-    if(option_text && !textfile)
-       fprintf(stderr,"Warning: Cannot open file 'quickest.txt' for writing [%s].\n",strerror(errno));
-    if(option_text_all && !textallfile)
-       fprintf(stderr,"Warning: Cannot open file 'quickest-all.txt' for writing [%s].\n",strerror(errno));
+    if(option_quickest==0)
+      {
+       /* Print the result for the shortest route */
+
+       if(option_html)
+          htmlfile    =fopen("shortest.html","w");
+       if(option_gpx_track)
+          gpxtrackfile=fopen("shortest-track.gpx","w");
+       if(option_gpx_route)
+          gpxroutefile=fopen("shortest-route.gpx","w");
+       if(option_text)
+          textfile    =fopen("shortest.txt","w");
+       if(option_text_all)
+          textallfile =fopen("shortest-all.txt","w");
+
+       if(option_html && !htmlfile)
+          fprintf(stderr,"Warning: Cannot open file 'shortest.html' for writing [%s].\n",strerror(errno));
+       if(option_gpx_track && !gpxtrackfile)
+          fprintf(stderr,"Warning: Cannot open file 'shortest-track.gpx' for writing [%s].\n",strerror(errno));
+       if(option_gpx_route && !gpxroutefile)
+          fprintf(stderr,"Warning: Cannot open file 'shortest-route.gpx' for writing [%s].\n",strerror(errno));
+       if(option_text && !textfile)
+          fprintf(stderr,"Warning: Cannot open file 'shortest.txt' for writing [%s].\n",strerror(errno));
+       if(option_text_all && !textallfile)
+          fprintf(stderr,"Warning: Cannot open file 'shortest-all.txt' for writing [%s].\n",strerror(errno));
+      }
+    else
+      {
+       /* Print the result for the quickest route */
+
+       if(option_html)
+          htmlfile    =fopen("quickest.html","w");
+       if(option_gpx_track)
+          gpxtrackfile=fopen("quickest-track.gpx","w");
+       if(option_gpx_route)
+          gpxroutefile=fopen("quickest-route.gpx","w");
+       if(option_text)
+          textfile    =fopen("quickest.txt","w");
+       if(option_text_all)
+          textallfile =fopen("quickest-all.txt","w");
+
+       if(option_html && !htmlfile)
+          fprintf(stderr,"Warning: Cannot open file 'quickest.html' for writing [%s].\n",strerror(errno));
+       if(option_gpx_track && !gpxtrackfile)
+          fprintf(stderr,"Warning: Cannot open file 'quickest-track.gpx' for writing [%s].\n",strerror(errno));
+       if(option_gpx_route && !gpxroutefile)
+          fprintf(stderr,"Warning: Cannot open file 'quickest-route.gpx' for writing [%s].\n",strerror(errno));
+       if(option_text && !textfile)
+          fprintf(stderr,"Warning: Cannot open file 'quickest.txt' for writing [%s].\n",strerror(errno));
+       if(option_text_all && !textallfile)
+          fprintf(stderr,"Warning: Cannot open file 'quickest-all.txt' for writing [%s].\n",strerror(errno));
+      }
    }
 
  /* Print the head of the files */
 
  if(htmlfile)
    {
-    fprintf(htmlfile,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-    fprintf(htmlfile,"<HTML>\n");
+    fprintf(htmlfile,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n");
+    fprintf(htmlfile,"<html>\n");
     if(translate_xml_copyright_creator[0] && translate_xml_copyright_creator[1])
        fprintf(htmlfile,"<!-- %s : %s -->\n",translate_xml_copyright_creator[0],translate_xml_copyright_creator[1]);
     if(translate_xml_copyright_source[0] && translate_xml_copyright_source[1])
        fprintf(htmlfile,"<!-- %s : %s -->\n",translate_xml_copyright_source[0],translate_xml_copyright_source[1]);
     if(translate_xml_copyright_license[0] && translate_xml_copyright_license[1])
        fprintf(htmlfile,"<!-- %s : %s -->\n",translate_xml_copyright_license[0],translate_xml_copyright_license[1]);
-    fprintf(htmlfile,"<HEAD>\n");
-    fprintf(htmlfile,"<TITLE>");
+    fprintf(htmlfile,"<head>\n");
+    fprintf(htmlfile,"<title>");
     fprintf(htmlfile,translate_html_title,option_quickest?translate_xml_route_quickest:translate_xml_route_shortest);
-    fprintf(htmlfile,"</TITLE>\n");
-    fprintf(htmlfile,"<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
-    fprintf(htmlfile,"<STYLE type=\"text/css\">\n");
+    fprintf(htmlfile,"</title>\n");
+    fprintf(htmlfile,"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
+    fprintf(htmlfile,"<style type=\"text/css\">\n");
     fprintf(htmlfile,"<!--\n");
     fprintf(htmlfile,"   table   {table-layout: fixed; border: none; border-collapse: collapse;}\n");
     fprintf(htmlfile,"   table.c {color: grey; font-size: x-small;} /* copyright */\n");
@@ -204,12 +220,12 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
     fprintf(htmlfile,"   span.t  {font-variant: small-caps;} /* turn */\n");
     fprintf(htmlfile,"   span.b  {font-variant: small-caps;} /* bearing */\n");
     fprintf(htmlfile,"-->\n");
-    fprintf(htmlfile,"</STYLE>\n");
-    fprintf(htmlfile,"</HEAD>\n");
-    fprintf(htmlfile,"<BODY>\n");
-    fprintf(htmlfile,"<H1>");
+    fprintf(htmlfile,"</style>\n");
+    fprintf(htmlfile,"</head>\n");
+    fprintf(htmlfile,"<body>\n");
+    fprintf(htmlfile,"<h1>");
     fprintf(htmlfile,translate_html_title,option_quickest?translate_xml_route_quickest:translate_xml_route_shortest);
-    fprintf(htmlfile,"</H1>\n");
+    fprintf(htmlfile,"</h1>\n");
     fprintf(htmlfile,"<table>\n");
    }
 
@@ -305,10 +321,7 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
 
  /* Loop through all the sections of the route and print them */
 
- while(!results[point])
-    point++;
-
- while(point<=nresults)
+ do
    {
     int next_point=point;
     distance_t junc_distance=0;
@@ -346,23 +359,26 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
        if(IsFakeNode(result->node))
           GetFakeLatLong(result->node,&latitude,&longitude);
        else
-          GetLatLong(nodes,result->node,&latitude,&longitude);
-
-       if(!IsFakeNode(result->node))
+         {
           resultnodep=LookupNode(nodes,result->node,6);
 
+          GetLatLong(nodes,result->node,resultnodep,&latitude,&longitude);
+         }
+
        /* Calculate the next result */
 
        next_result=result->next;
 
        if(!next_result)
-          for(next_point=point+1;next_point<=nresults;next_point++)
-             if(results[next_point])
-               {
-                next_result=FindResult(results[next_point],results[next_point]->start_node,results[next_point]->prev_segment);
-                next_result=next_result->next;
-                break;
-               }
+         {
+          next_point++;
+
+          if(next_point<nresults)
+            {
+             next_result=FindResult(results[next_point],results[next_point]->start_node,results[next_point]->prev_segment);
+             next_result=next_result->next;
+            }
+         }
 
        /* Calculate the information about this segment */
 
@@ -793,6 +809,7 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
 
     point=next_point;
    }
+ while(point<nresults);
 
  /* Print the tail of the files */
 
@@ -815,8 +832,8 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
        fprintf(htmlfile,"</table>\n");
       }
 
-    fprintf(htmlfile,"</BODY>\n");
-    fprintf(htmlfile,"</HTML>\n");
+    fprintf(htmlfile,"</body>\n");
+    fprintf(htmlfile,"</html>\n");
    }
 
  if(gpxtrackfile)
@@ -833,14 +850,17 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
 
  /* Close the files */
 
- if(htmlfile)
-    fclose(htmlfile);
- if(gpxtrackfile)
-    fclose(gpxtrackfile);
- if(gpxroutefile)
-    fclose(gpxroutefile);
- if(textfile)
-    fclose(textfile);
- if(textallfile)
-    fclose(textallfile);
+ if(!option_stdout)
+   {
+    if(htmlfile)
+       fclose(htmlfile);
+    if(gpxtrackfile)
+       fclose(gpxtrackfile);
+    if(gpxroutefile)
+       fclose(gpxroutefile);
+    if(textfile)
+       fclose(textfile);
+    if(textallfile)
+       fclose(textallfile);
+   }
 }
diff --git a/src/planetsplitter.c b/src/planetsplitter.c
index 0105ef5..22bf6d3 100644
--- a/src/planetsplitter.c
+++ b/src/planetsplitter.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,9 +22,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <string.h>
 #include <errno.h>
-#include <sys/time.h>
 
 #include "types.h"
 #include "ways.h"
@@ -39,9 +39,11 @@
 
 #include "files.h"
 #include "logging.h"
+#include "errorlogx.h"
 #include "functions.h"
 #include "osmparser.h"
 #include "tagging.h"
+#include "uncompress.h"
 
 
 /* Global variables */
@@ -67,11 +69,10 @@ static void print_usage(int detail,const char *argerr,const char *err);
 
 int main(int argc,char** argv)
 {
- struct timeval start_time;
- NodesX     *Nodes;
- SegmentsX  *Segments,*SuperSegments=NULL,*MergedSegments=NULL;
- WaysX      *Ways;
- RelationsX *Relations;
+ NodesX     *OSMNodes;
+ SegmentsX  *OSMSegments,*SuperSegments=NULL,*MergedSegments=NULL;
+ WaysX      *OSMWays;
+ RelationsX *OSMRelations;
  int         iteration=0,quit=0;
  int         max_iterations=5;
  char       *dirname=NULL,*prefix=NULL,*tagging=NULL,*errorlog=NULL;
@@ -81,7 +82,7 @@ int main(int argc,char** argv)
  int         option_prune_isolated=500,option_prune_short=5,option_prune_straight=3;
  int         arg;
 
- gettimeofday(&start_time,NULL);
+ printf_program_start();
 
  /* Parse the command line arguments */
 
@@ -89,18 +90,28 @@ int main(int argc,char** argv)
    {
     if(!strcmp(argv[arg],"--help"))
        print_usage(1,NULL,NULL);
+    else if(!strncmp(argv[arg],"--dir=",6))
+       dirname=&argv[arg][6];
+    else if(!strncmp(argv[arg],"--prefix=",9))
+       prefix=&argv[arg][9];
     else if(!strncmp(argv[arg],"--sort-ram-size=",16))
        option_filesort_ramsize=atoi(&argv[arg][16]);
 #if defined(USE_PTHREADS) && USE_PTHREADS
     else if(!strncmp(argv[arg],"--sort-threads=",15))
        option_filesort_threads=atoi(&argv[arg][15]);
 #endif
-    else if(!strncmp(argv[arg],"--dir=",6))
-       dirname=&argv[arg][6];
     else if(!strncmp(argv[arg],"--tmpdir=",9))
        option_tmpdirname=&argv[arg][9];
-    else if(!strncmp(argv[arg],"--prefix=",9))
-       prefix=&argv[arg][9];
+    else if(!strncmp(argv[arg],"--tagging=",10))
+       tagging=&argv[arg][10];
+    else if(!strcmp(argv[arg],"--loggable"))
+       option_loggable=1;
+    else if(!strcmp(argv[arg],"--logtime"))
+       option_logtime=1;
+    else if(!strcmp(argv[arg],"--errorlog"))
+       errorlog="error.log";
+    else if(!strncmp(argv[arg],"--errorlog=",11))
+       errorlog=&argv[arg][11];
     else if(!strcmp(argv[arg],"--parse-only"))
        option_parse_only=1;
     else if(!strcmp(argv[arg],"--process-only"))
@@ -111,18 +122,8 @@ int main(int argc,char** argv)
        option_keep=1;
     else if(!strcmp(argv[arg],"--changes"))
        option_changes=1;
-    else if(!strcmp(argv[arg],"--loggable"))
-       option_loggable=1;
-    else if(!strcmp(argv[arg],"--logtime"))
-       option_logtime=1;
-    else if(!strcmp(argv[arg],"--errorlog"))
-       errorlog="error.log";
-    else if(!strncmp(argv[arg],"--errorlog=",11))
-       errorlog=&argv[arg][11];
     else if(!strncmp(argv[arg],"--max-iterations=",17))
        max_iterations=atoi(&argv[arg][17]);
-    else if(!strncmp(argv[arg],"--tagging=",10))
-       tagging=&argv[arg][10];
     else if(!strncmp(argv[arg],"--prune",7))
       {
        if(!strcmp(&argv[arg][7],"-none"))
@@ -153,6 +154,9 @@ int main(int argc,char** argv)
  if(option_filenames && option_process_only)
     print_usage(0,NULL,"Cannot use '--process-only' and filenames at the same time.");
 
+ if(!option_filenames && !option_process_only)
+    print_usage(0,NULL,"File names must be specified unless using '--process-only'");
+
  if(!option_filesort_ramsize)
    {
 #if SLIM
@@ -179,7 +183,7 @@ int main(int argc,char** argv)
        if(!ExistsFile(tagging))
          {
           fprintf(stderr,"Error: The '--tagging' option specifies a file that does not exist.\n");
-          return(1);
+          exit(EXIT_FAILURE);
          }
       }
     else
@@ -191,107 +195,123 @@ int main(int argc,char** argv)
        else
          {
           fprintf(stderr,"Error: The '--tagging' option was not used and the default 'tagging.xml' does not exist.\n");
-          return(1);
+          exit(EXIT_FAILURE);
          }
       }
 
     if(ParseXMLTaggingRules(tagging))
       {
        fprintf(stderr,"Error: Cannot read the tagging rules in the file '%s'.\n",tagging);
-       return(1);
+       exit(EXIT_FAILURE);
       }
    }
 
  /* Create new node, segment, way and relation variables */
 
- Nodes=NewNodeList(option_append||option_changes,option_process_only);
+ OSMNodes=NewNodeList(option_append||option_changes,option_process_only);
 
- Segments=NewSegmentList(option_append||option_changes,option_process_only);
+ OSMWays=NewWayList(option_append||option_changes,option_process_only);
 
- Ways=NewWayList(option_append||option_changes,option_process_only);
-
- Relations=NewRelationList(option_append||option_changes,option_process_only);
+ OSMRelations=NewRelationList(option_append||option_changes,option_process_only);
 
  /* Create the error log file */
 
  if(errorlog)
-    open_errorlog(FileName(dirname,prefix,errorlog),option_append||option_changes||option_process_only);
+    open_errorlog(FileName(dirname,prefix,errorlog),option_append||option_changes||option_process_only,option_keep);
 
  /* Parse the file */
 
 if(!option_process_only)
   {
-   if(option_filenames)
+   for(arg=1;arg<argc;arg++)
      {
-      for(arg=1;arg<argc;arg++)
+      int fd;
+      char *filename,*p;
+
+      if(argv[arg][0]=='-' && argv[arg][1]=='-')
+         continue;
+
+      filename=strcpy(malloc(strlen(argv[arg])+1),argv[arg]);
+
+      fd=OpenFile(filename);
+
+      if((p=strstr(filename,".bz2")) && !strcmp(p,".bz2"))
         {
-         FILE *file;
+         fd=Uncompress_Bzip2(fd);
+         *p=0;
+        }
 
-         if(argv[arg][0]=='-' && argv[arg][1]=='-')
-            continue;
+      if((p=strstr(filename,".gz")) && !strcmp(p,".gz"))
+        {
+         fd=Uncompress_Gzip(fd);
+         *p=0;
+        }
 
-         file=fopen(argv[arg],"rb");
+      if((p=strstr(filename,".xz")) && !strcmp(p,".xz"))
+        {
+         fd=Uncompress_Xz(fd);
+         *p=0;
+        }
+
+      if(option_changes)
+        {
+         printf("\nParse OSC Data [%s]\n==============\n\n",filename);
+         fflush(stdout);
 
-         if(!file)
+         if((p=strstr(filename,".pbf")) && !strcmp(p,".pbf"))
            {
-            fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",argv[arg],strerror(errno));
-            exit(EXIT_FAILURE);
+            logassert(0,"Unable to read a PBF file to apply changes (format does not permit this)");
            }
-
-         if(option_changes)
+         else if((p=strstr(filename,".o5c")) && !strcmp(p,".o5c"))
            {
-            printf("\nParse OSC Data [%s]\n==============\n\n",argv[arg]);
-            fflush(stdout);
-
-            if(ParseOSC(file,Nodes,Segments,Ways,Relations))
+            if(ParseO5CFile(fd,OSMNodes,OSMWays,OSMRelations))
                exit(EXIT_FAILURE);
            }
          else
            {
-            printf("\nParse OSM Data [%s]\n==============\n\n",argv[arg]);
-            fflush(stdout);
-
-            if(ParseOSM(file,Nodes,Segments,Ways,Relations))
+            if(ParseOSCFile(fd,OSMNodes,OSMWays,OSMRelations))
                exit(EXIT_FAILURE);
            }
-
-         fclose(file);
-        }
-     }
-   else
-     {
-      if(option_changes)
-        {
-         printf("\nParse OSC Data\n==============\n\n");
-         fflush(stdout);
-
-         if(ParseOSC(stdin,Nodes,Segments,Ways,Relations))
-            exit(EXIT_FAILURE);
         }
       else
         {
-         printf("\nParse OSM Data\n==============\n\n");
+         printf("\nParse OSM Data [%s]\n==============\n\n",filename);
          fflush(stdout);
 
-         if(ParseOSM(stdin,Nodes,Segments,Ways,Relations))
-            exit(EXIT_FAILURE);
+         if((p=strstr(filename,".pbf")) && !strcmp(p,".pbf"))
+           {
+            if(ParsePBFFile(fd,OSMNodes,OSMWays,OSMRelations))
+               exit(EXIT_FAILURE);
+           }
+         else if((p=strstr(filename,".o5m")) && !strcmp(p,".o5m"))
+           {
+            if(ParseO5MFile(fd,OSMNodes,OSMWays,OSMRelations))
+               exit(EXIT_FAILURE);
+           }
+         else
+           {
+            if(ParseOSMFile(fd,OSMNodes,OSMWays,OSMRelations))
+               exit(EXIT_FAILURE);
+           }
         }
+
+      CloseFile(fd);
+
+      free(filename);
      }
 
    DeleteXMLTaggingRules();
   }
 
- FinishNodeList(Nodes);
- FinishSegmentList(Segments);
- FinishWayList(Ways);
- FinishRelationList(Relations);
+ FinishNodeList(OSMNodes);
+ FinishWayList(OSMWays);
+ FinishRelationList(OSMRelations);
 
  if(option_parse_only)
    {
-    FreeNodeList(Nodes,1);
-    FreeSegmentList(Segments,1);
-    FreeWayList(Ways,1);
-    FreeRelationList(Relations,1);
+    FreeNodeList(OSMNodes,1);
+    FreeWayList(OSMWays,1);
+    FreeRelationList(OSMRelations,1);
 
     return(0);
    }
@@ -302,61 +322,50 @@ if(!option_process_only)
  printf("\nSort OSM Data\n=============\n\n");
  fflush(stdout);
 
- /* Sort the nodes, segments, ways and relations */
+ /* Sort the nodes, ways and relations */
 
- SortNodeList(Nodes);
+ SortNodeList(OSMNodes);
 
- if(option_changes)
-    ApplySegmentChanges(Segments);
+ SortWayList(OSMWays);
 
- SortSegmentList(Segments);
-
- SortWayList(Ways);
-
- SortRelationList(Relations);
+ SortRelationList(OSMRelations);
 
  /* Process the data */
 
  printf("\nProcess OSM Data\n================\n\n");
  fflush(stdout);
 
- /* Extract the way names (must be before using the ways) */
-
- ExtractWayNames(Ways,option_keep||option_changes);
+ /* Remove non-highway nodes by looking through the ways */
 
- /* Remove bad segments (must be after sorting the nodes, segments and ways) */
+ RemoveNonHighwayNodes(OSMNodes,OSMWays,option_keep||option_changes);
 
- RemoveBadSegments(Segments,Nodes,Ways,option_keep||option_changes);
+ /* Separate the segments and way names and sort them. */
 
- /* Remove non-highway nodes (must be after removing the bad segments) */
+ OSMSegments=SplitWays(OSMWays,OSMNodes,option_keep||option_changes);
 
- RemoveNonHighwayNodes(Nodes,Segments,option_keep||option_changes);
+ SortWayNames(OSMWays);
 
- /* Process the route relations and first part of turn relations (must be before compacting the ways) */
+ SortSegmentList(OSMSegments);
 
- ProcessRouteRelations(Relations,Ways,option_keep||option_changes);
+ /* Process the segments and index them */
 
- ProcessTurnRelations1(Relations,Nodes,Ways,option_keep||option_changes);
+ ProcessSegments(OSMSegments,OSMNodes,OSMWays);
 
- /* Measure the segments and replace node/way id with index (must be after removing non-highway nodes) */
+ IndexSegments(OSMSegments,OSMNodes,OSMWays);
 
- MeasureSegments(Segments,Nodes,Ways);
+ /* Process the route relations and turn relations (must be before compacting the ways) */
 
- /* Index the segments */
-
- IndexSegments(Segments,Nodes,Ways);
-
- /* Convert the turn relations from ways into nodes */
+ ProcessRouteRelations(OSMRelations,OSMWays,option_keep||option_changes);
 
- ProcessTurnRelations2(Relations,Nodes,Segments,Ways);
+ ProcessTurnRelations(OSMRelations,OSMNodes,OSMSegments,OSMWays,option_keep||option_changes);
 
- /* Compact the ways (must be after turn relations 2) */
+ /* Compact the ways (must be after processing turn relations) */
 
- CompactWayList(Ways,Segments);
+ CompactWayList(OSMWays,OSMSegments);
 
  /* Index the segments */
 
- IndexSegments(Segments,Nodes,Ways);
+ IndexSegments(OSMSegments,OSMNodes,OSMWays);
 
  /* Prune unwanted nodes/segments. */
 
@@ -365,33 +374,34 @@ if(!option_process_only)
     printf("\nPrune Unneeded Data\n===================\n\n");
     fflush(stdout);
 
-    StartPruning(Nodes,Segments,Ways);
+    StartPruning(OSMNodes,OSMSegments,OSMWays);
 
     if(option_prune_straight)
-       PruneStraightHighwayNodes(Nodes,Segments,Ways,option_prune_straight);
+       PruneStraightHighwayNodes(OSMNodes,OSMSegments,OSMWays,option_prune_straight);
 
     if(option_prune_isolated)
-       PruneIsolatedRegions(Nodes,Segments,Ways,option_prune_isolated);
+       PruneIsolatedRegions(OSMNodes,OSMSegments,OSMWays,option_prune_isolated);
 
     if(option_prune_short)
-       PruneShortSegments(Nodes,Segments,Ways,option_prune_short);
+       PruneShortSegments(OSMNodes,OSMSegments,OSMWays,option_prune_short);
 
-    FinishPruning(Nodes,Segments,Ways);
+    FinishPruning(OSMNodes,OSMSegments,OSMWays);
 
-    /* Remove the pruned nodes and segments and update the indexes */
+    /* Remove the pruned nodes, segments, ways and relations and update the indexes */
 
-    RemovePrunedNodes(Nodes,Segments);
-    RemovePrunedSegments(Segments,Ways);
-    CompactWayList(Ways,Segments);
-    RemovePrunedTurnRelations(Relations,Nodes);
-    IndexSegments(Segments,Nodes,Ways);
+    RemovePrunedNodes(OSMNodes,OSMSegments);
+    RemovePrunedSegments(OSMSegments,OSMWays);
+    CompactWayList(OSMWays,OSMSegments);
+    RemovePrunedTurnRelations(OSMRelations,OSMNodes);
+
+    IndexSegments(OSMSegments,OSMNodes,OSMWays);
    }
 
  /* Repeated iteration on Super-Nodes and Super-Segments */
 
  do
    {
-    int nsuper;
+    index_t nsuper;
 
     printf("\nProcess Super-Data (iteration %d)\n================================%s\n\n",iteration,iteration>9?"=":"");
     fflush(stdout);
@@ -400,13 +410,13 @@ if(!option_process_only)
       {
        /* Select the super-nodes */
 
-       ChooseSuperNodes(Nodes,Segments,Ways);
+       ChooseSuperNodes(OSMNodes,OSMSegments,OSMWays);
 
        /* Select the super-segments */
 
-       SuperSegments=CreateSuperSegments(Nodes,Segments,Ways);
+       SuperSegments=CreateSuperSegments(OSMNodes,OSMSegments,OSMWays);
 
-       nsuper=Segments->number;
+       nsuper=OSMSegments->number;
       }
     else
       {
@@ -414,26 +424,26 @@ if(!option_process_only)
 
        /* Select the super-nodes */
 
-       ChooseSuperNodes(Nodes,SuperSegments,Ways);
+       ChooseSuperNodes(OSMNodes,SuperSegments,OSMWays);
 
        /* Select the super-segments */
 
-       SuperSegments2=CreateSuperSegments(Nodes,SuperSegments,Ways);
+       SuperSegments2=CreateSuperSegments(OSMNodes,SuperSegments,OSMWays);
 
        nsuper=SuperSegments->number;
 
-       FreeSegmentList(SuperSegments,0);
+       FreeSegmentList(SuperSegments);
 
        SuperSegments=SuperSegments2;
       }
 
     /* Sort the super-segments and remove duplicates */
 
-    DeduplicateSuperSegments(SuperSegments,Ways);
+    DeduplicateSuperSegments(SuperSegments,OSMWays);
 
     /* Index the segments */
 
-    IndexSegments(SuperSegments,Nodes,Ways);
+    IndexSegments(SuperSegments,OSMNodes,OSMWays);
 
     /* Check for end condition */
 
@@ -454,17 +464,13 @@ if(!option_process_only)
 
  /* Merge the super-segments */
 
- MergedSegments=MergeSuperSegments(Segments,SuperSegments);
-
- FreeSegmentList(Segments,0);
-
- FreeSegmentList(SuperSegments,0);
+ MergedSegments=MergeSuperSegments(OSMSegments,SuperSegments);
 
- Segments=MergedSegments;
+ FreeSegmentList(OSMSegments);
 
- /* Re-index the merged segments */
+ FreeSegmentList(SuperSegments);
 
- IndexSegments(Segments,Nodes,Ways);
+ OSMSegments=MergedSegments;
 
  /* Cross reference the nodes and segments */
 
@@ -473,17 +479,17 @@ if(!option_process_only)
 
  /* Sort the nodes and segments geographically */
 
- SortNodeListGeographically(Nodes);
+ SortNodeListGeographically(OSMNodes);
 
- SortSegmentListGeographically(Segments,Nodes);
+ SortSegmentListGeographically(OSMSegments,OSMNodes);
 
  /* Re-index the segments */
 
- IndexSegments(Segments,Nodes,Ways);
+ IndexSegments(OSMSegments,OSMNodes,OSMWays);
 
  /* Sort the turn relations geographically */
 
- SortTurnRelationListGeographically(Relations,Nodes,Segments);
+ SortTurnRelationListGeographically(OSMRelations,OSMNodes,OSMSegments);
 
  /* Output the results */
 
@@ -492,43 +498,55 @@ if(!option_process_only)
 
  /* Write out the nodes */
 
- SaveNodeList(Nodes,FileName(dirname,prefix,"nodes.mem"),Segments);
-
- FreeNodeList(Nodes,0);
+ SaveNodeList(OSMNodes,FileName(dirname,prefix,"nodes.mem"),OSMSegments);
 
  /* Write out the segments */
 
- SaveSegmentList(Segments,FileName(dirname,prefix,"segments.mem"));
-
- FreeSegmentList(Segments,0);
+ SaveSegmentList(OSMSegments,FileName(dirname,prefix,"segments.mem"));
 
  /* Write out the ways */
 
- SaveWayList(Ways,FileName(dirname,prefix,"ways.mem"));
-
- FreeWayList(Ways,0);
+ SaveWayList(OSMWays,FileName(dirname,prefix,"ways.mem"));
 
  /* Write out the relations */
 
- SaveRelationList(Relations,FileName(dirname,prefix,"relations.mem"));
+ SaveRelationList(OSMRelations,FileName(dirname,prefix,"relations.mem"));
 
- FreeRelationList(Relations,0);
-
- /* Close the error log file */
+ /* Close the error log file and process the data */
 
  if(errorlog)
+   {
     close_errorlog();
 
- /* Print the total time */
+    if(option_keep)
+      {
+       ErrorLogsX *OSMErrorLogs;
+
+       printf("\nCreate Error Log\n================\n\n");
+       fflush(stdout);
 
- if(option_logtime)
-   {
-    printf("\n");
-    fprintf_elapsed_time(stdout,&start_time);
-    printf("Complete\n");
-    fflush(stdout);
+       OSMErrorLogs=NewErrorLogList();
+
+       ProcessErrorLogs(OSMErrorLogs,OSMNodes,OSMWays,OSMRelations);
+
+       SortErrorLogsGeographically(OSMErrorLogs);
+
+       SaveErrorLogs(OSMErrorLogs,FileName(dirname,prefix,"errorlogs.mem"));
+
+       FreeErrorLogList(OSMErrorLogs);
+      }
    }
 
+ /* Free the memory (delete the temporary files) */
+
+ FreeNodeList(OSMNodes,0);
+ FreeWayList(OSMWays,0);
+ FreeRelationList(OSMRelations,0);
+
+ FreeSegmentList(OSMSegments);
+
+ printf_program_end();
+
  return(0);
 }
 
@@ -564,7 +582,19 @@ static void print_usage(int detail,const char *argerr,const char *err)
          "                      [--prune-isolated=<len>]\n"
          "                      [--prune-short=<len>]\n"
          "                      [--prune-straight=<len>]\n"
-         "                      [<filename.osm> ...]\n");
+         "                      [<filename.osm> ... | <filename.osc> ...\n"
+         "                       | <filename.pbf> ...\n"
+         "                       | <filename.o5m> ... | <filename.o5c> ..."
+#if defined(USE_BZIP2) && USE_BZIP2
+         "\n                       | <filename.(osm|osc|o5m|o5c).bz2> ..."
+#endif
+#if defined(USE_GZIP) && USE_GZIP
+         "\n                       | <filename.(osm|osc|o5m|o5c).gz> ..."
+#endif
+#if defined(USE_XZ) && USE_XZ
+         "\n                       | <filename.(osm|osc|o5m|o5c).xz> ..."
+#endif
+         "]\n");
 
  if(argerr)
     fprintf(stderr,
@@ -625,8 +655,19 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "--prune-straight=<len>    Remove nodes in almost straight highways (defaults to\n"
             "                          removing nodes up to 3m offset from a straight line).\n"
             "\n"
-            "<filename.osm> ...        The name(s) of the file(s) to process (by default\n"
-            "                          data is read from standard input).\n"
+            "<filename.osm>, <filename.osc>, <filename.pbf>, <filename.o5m>, <filename.o5c>\n"
+            "                          The name(s) of the file(s) to read and parse.\n"
+            "                          Filenames ending '.pbf' read as PBF, filenames ending\n"
+            "                          '.o5m' or '.o5c' read as O5M/O5C, others as XML.\n"
+#if defined(USE_BZIP2) && USE_BZIP2
+            "                          Filenames ending '.bz2' will be bzip2 uncompressed.\n"
+#endif
+#if defined(USE_GZIP) && USE_GZIP
+            "                          Filenames ending '.gz' will be gzip uncompressed.\n"
+#endif
+#if defined(USE_XZ) && USE_XZ
+            "                          Filenames ending '.xz' will be xz uncompressed.\n"
+#endif
             "\n"
             "<transport> defaults to all but can be set to:\n"
             "%s"
diff --git a/src/profiles.c b/src/profiles.c
index db21742..3ce66bb 100644
--- a/src/profiles.c
+++ b/src/profiles.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -47,30 +47,67 @@ static int nloaded_profiles=0;
 //static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding);
 //static int RoutinoProfilesType_function(const char *_tag_,int _type_);
 static int profileType_function(const char *_tag_,int _type_,const char *name,const char *transport);
-//static int restrictionsType_function(const char *_tag_,int _type_);
-static int lengthType_function(const char *_tag_,int _type_,const char *limit);
-static int widthType_function(const char *_tag_,int _type_,const char *limit);
-static int heightType_function(const char *_tag_,int _type_,const char *limit);
-static int weightType_function(const char *_tag_,int _type_,const char *limit);
-static int turnsType_function(const char *_tag_,int _type_,const char *obey);
-//static int propertiesType_function(const char *_tag_,int _type_);
-static int onewayType_function(const char *_tag_,int _type_,const char *obey);
-static int propertyType_function(const char *_tag_,int _type_,const char *type,const char *percent);
-//static int preferencesType_function(const char *_tag_,int _type_);
-static int preferenceType_function(const char *_tag_,int _type_,const char *highway,const char *percent);
 //static int speedsType_function(const char *_tag_,int _type_);
+//static int preferencesType_function(const char *_tag_,int _type_);
+//static int propertiesType_function(const char *_tag_,int _type_);
+//static int restrictionsType_function(const char *_tag_,int _type_);
 static int speedType_function(const char *_tag_,int _type_,const char *highway,const char *kph);
+static int preferenceType_function(const char *_tag_,int _type_,const char *highway,const char *percent);
+static int propertyType_function(const char *_tag_,int _type_,const char *type,const char *percent);
+static int onewayType_function(const char *_tag_,int _type_,const char *obey);
+static int turnsType_function(const char *_tag_,int _type_,const char *obey);
+static int weightType_function(const char *_tag_,int _type_,const char *limit);
+static int heightType_function(const char *_tag_,int _type_,const char *limit);
+static int widthType_function(const char *_tag_,int _type_,const char *limit);
+static int lengthType_function(const char *_tag_,int _type_,const char *limit);
 
 
-/* The XML tag definitions */
+/* The XML tag definitions (forward declarations) */
 
-/*+ The speedType type tag. +*/
-static xmltag speedType_tag=
-              {"speed",
-               2, {"highway","kph"},
-               speedType_function,
+static xmltag xmlDeclaration_tag;
+static xmltag RoutinoProfilesType_tag;
+static xmltag profileType_tag;
+static xmltag speedsType_tag;
+static xmltag preferencesType_tag;
+static xmltag propertiesType_tag;
+static xmltag restrictionsType_tag;
+static xmltag speedType_tag;
+static xmltag preferenceType_tag;
+static xmltag propertyType_tag;
+static xmltag onewayType_tag;
+static xmltag turnsType_tag;
+static xmltag weightType_tag;
+static xmltag heightType_tag;
+static xmltag widthType_tag;
+static xmltag lengthType_tag;
+
+
+/* The XML tag definition values */
+
+/*+ The complete set of tags at the top level. +*/
+static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoProfilesType_tag,NULL};
+
+/*+ The xmlDeclaration type tag. +*/
+static xmltag xmlDeclaration_tag=
+              {"xml",
+               2, {"version","encoding"},
+               NULL,
                {NULL}};
 
+/*+ The RoutinoProfilesType type tag. +*/
+static xmltag RoutinoProfilesType_tag=
+              {"routino-profiles",
+               0, {NULL},
+               NULL,
+               {&profileType_tag,NULL}};
+
+/*+ The profileType type tag. +*/
+static xmltag profileType_tag=
+              {"profile",
+               2, {"name","transport"},
+               profileType_function,
+               {&speedsType_tag,&preferencesType_tag,&propertiesType_tag,&restrictionsType_tag,NULL}};
+
 /*+ The speedsType type tag. +*/
 static xmltag speedsType_tag=
               {"speeds",
@@ -78,13 +115,6 @@ static xmltag speedsType_tag=
                NULL,
                {&speedType_tag,NULL}};
 
-/*+ The preferenceType type tag. +*/
-static xmltag preferenceType_tag=
-              {"preference",
-               2, {"highway","percent"},
-               preferenceType_function,
-               {NULL}};
-
 /*+ The preferencesType type tag. +*/
 static xmltag preferencesType_tag=
               {"preferences",
@@ -92,6 +122,34 @@ static xmltag preferencesType_tag=
                NULL,
                {&preferenceType_tag,NULL}};
 
+/*+ The propertiesType type tag. +*/
+static xmltag propertiesType_tag=
+              {"properties",
+               0, {NULL},
+               NULL,
+               {&propertyType_tag,NULL}};
+
+/*+ The restrictionsType type tag. +*/
+static xmltag restrictionsType_tag=
+              {"restrictions",
+               0, {NULL},
+               NULL,
+               {&onewayType_tag,&turnsType_tag,&weightType_tag,&heightType_tag,&widthType_tag,&lengthType_tag,NULL}};
+
+/*+ The speedType type tag. +*/
+static xmltag speedType_tag=
+              {"speed",
+               2, {"highway","kph"},
+               speedType_function,
+               {NULL}};
+
+/*+ The preferenceType type tag. +*/
+static xmltag preferenceType_tag=
+              {"preference",
+               2, {"highway","percent"},
+               preferenceType_function,
+               {NULL}};
+
 /*+ The propertyType type tag. +*/
 static xmltag propertyType_tag=
               {"property",
@@ -106,13 +164,6 @@ static xmltag onewayType_tag=
                onewayType_function,
                {NULL}};
 
-/*+ The propertiesType type tag. +*/
-static xmltag propertiesType_tag=
-              {"properties",
-               0, {NULL},
-               NULL,
-               {&propertyType_tag,NULL}};
-
 /*+ The turnsType type tag. +*/
 static xmltag turnsType_tag=
               {"turns",
@@ -148,73 +199,88 @@ static xmltag lengthType_tag=
                lengthType_function,
                {NULL}};
 
-/*+ The restrictionsType type tag. +*/
-static xmltag restrictionsType_tag=
-              {"restrictions",
-               0, {NULL},
-               NULL,
-               {&onewayType_tag,&turnsType_tag,&weightType_tag,&heightType_tag,&widthType_tag,&lengthType_tag,NULL}};
 
-/*+ The profileType type tag. +*/
-static xmltag profileType_tag=
-              {"profile",
-               2, {"name","transport"},
-               profileType_function,
-               {&speedsType_tag,&preferencesType_tag,&propertiesType_tag,&restrictionsType_tag,NULL}};
+/* The XML tag processing functions */
 
-/*+ The RoutinoProfilesType type tag. +*/
-static xmltag RoutinoProfilesType_tag=
-              {"routino-profiles",
-               0, {NULL},
-               NULL,
-               {&profileType_tag,NULL}};
 
-/*+ The xmlDeclaration type tag. +*/
-static xmltag xmlDeclaration_tag=
-              {"xml",
-               2, {"version","encoding"},
-               NULL,
-               {NULL}};
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the XML declaration is seen
 
+  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
 
-/*+ The complete set of tags at the top level. +*/
-static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoProfilesType_tag,NULL};
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
 
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-/* The XML tag processing functions */
+  const char *version The contents of the 'version' attribute (or NULL if not defined).
+
+  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
+//{
+// return(0);
+//}
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the speedType XSD type is seen
+  The function that is called when the RoutinoProfilesType XSD type is seen
 
-  int speedType_function Returns 0 if no error occured or something else otherwise.
+  int RoutinoProfilesType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
 
-  const char *highway The contents of the 'highway' attribute (or NULL if not defined).
+//static int RoutinoProfilesType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
 
-  const char *kph The contents of the 'kph' attribute (or NULL if not defined).
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the profileType XSD type is seen
+
+  int profileType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *name The contents of the 'name' attribute (or NULL if not defined).
+
+  const char *transport The contents of the 'transport' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int speedType_function(const char *_tag_,int _type_,const char *highway,const char *kph)
+static int profileType_function(const char *_tag_,int _type_,const char *name,const char *transport)
 {
  if(_type_&XMLPARSE_TAG_START)
    {
-    double speed;
-    Highway highwaytype;
+    Transport transporttype;
+    int i;
 
-    XMLPARSE_ASSERT_STRING(_tag_,highway);
+    XMLPARSE_ASSERT_STRING(_tag_,name);
+    XMLPARSE_ASSERT_STRING(_tag_,transport);
 
-    highwaytype=HighwayType(highway);
+    for(i=0;i<nloaded_profiles;i++)
+       if(!strcmp(name,loaded_profiles[i]->name))
+          XMLPARSE_MESSAGE(_tag_,"profile name must be unique");
 
-    if(highwaytype==Highway_None)
-       XMLPARSE_INVALID(_tag_,highway);
+    transporttype=TransportType(transport);
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,kph); speed=atof(kph);
+    if(transporttype==Transport_None)
+       XMLPARSE_INVALID(_tag_,transport);
 
-    loaded_profiles[nloaded_profiles-1]->speed[highwaytype]=kph_to_speed(speed);
+    if((nloaded_profiles%16)==0)
+       loaded_profiles=(Profile**)realloc((void*)loaded_profiles,(nloaded_profiles+16)*sizeof(Profile*));
+
+    nloaded_profiles++;
+
+    loaded_profiles[nloaded_profiles-1]=(Profile*)calloc(1,sizeof(Profile));
+
+    loaded_profiles[nloaded_profiles-1]->name=strcpy(malloc(strlen(name)+1),name);
+    loaded_profiles[nloaded_profiles-1]->transport=transporttype;
    }
 
  return(0);
@@ -238,9 +304,57 @@ static int speedType_function(const char *_tag_,int _type_,const char *highway,c
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the preferenceType XSD type is seen
+  The function that is called when the preferencesType XSD type is seen
 
-  int preferenceType_function Returns 0 if no error occured or something else otherwise.
+  int preferencesType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int preferencesType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the propertiesType XSD type is seen
+
+  int propertiesType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int propertiesType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the restrictionsType XSD type is seen
+
+  int restrictionsType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int restrictionsType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the speedType XSD type is seen
+
+  int speedType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
@@ -248,15 +362,15 @@ static int speedType_function(const char *_tag_,int _type_,const char *highway,c
 
   const char *highway The contents of the 'highway' attribute (or NULL if not defined).
 
-  const char *percent The contents of the 'percent' attribute (or NULL if not defined).
+  const char *kph The contents of the 'kph' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int preferenceType_function(const char *_tag_,int _type_,const char *highway,const char *percent)
+static int speedType_function(const char *_tag_,int _type_,const char *highway,const char *kph)
 {
  if(_type_&XMLPARSE_TAG_START)
    {
+    double speed;
     Highway highwaytype;
-    double p;
 
     XMLPARSE_ASSERT_STRING(_tag_,highway);
 
@@ -265,9 +379,9 @@ static int preferenceType_function(const char *_tag_,int _type_,const char *high
     if(highwaytype==Highway_None)
        XMLPARSE_INVALID(_tag_,highway);
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,percent); p=atof(percent);
+    XMLPARSE_ASSERT_FLOATING(_tag_,kph); speed=atof(kph);
 
-    loaded_profiles[nloaded_profiles-1]->highway[highwaytype]=p;
+    loaded_profiles[nloaded_profiles-1]->speed[highwaytype]=kph_to_speed(speed);
    }
 
  return(0);
@@ -275,19 +389,40 @@ static int preferenceType_function(const char *_tag_,int _type_,const char *high
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the preferencesType XSD type is seen
+  The function that is called when the preferenceType XSD type is seen
 
-  int preferencesType_function Returns 0 if no error occured or something else otherwise.
+  int preferenceType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *highway The contents of the 'highway' attribute (or NULL if not defined).
+
+  const char *percent The contents of the 'percent' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-//static int preferencesType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
+static int preferenceType_function(const char *_tag_,int _type_,const char *highway,const char *percent)
+{
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    Highway highwaytype;
+    double p;
+
+    XMLPARSE_ASSERT_STRING(_tag_,highway);
+
+    highwaytype=HighwayType(highway);
+
+    if(highwaytype==Highway_None)
+       XMLPARSE_INVALID(_tag_,highway);
+
+    XMLPARSE_ASSERT_FLOATING(_tag_,percent); p=atof(percent);
+
+    loaded_profiles[nloaded_profiles-1]->highway[highwaytype]=p;
+   }
+
+ return(0);
+}
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -355,22 +490,6 @@ static int onewayType_function(const char *_tag_,int _type_,const char *obey)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the propertiesType XSD type is seen
-
-  int propertiesType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int propertiesType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   The function that is called when the turnsType XSD type is seen
 
   int turnsType_function Returns 0 if no error occured or something else otherwise.
@@ -506,106 +625,6 @@ static int lengthType_function(const char *_tag_,int _type_,const char *limit)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the restrictionsType XSD type is seen
-
-  int restrictionsType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int restrictionsType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the profileType XSD type is seen
-
-  int profileType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *name The contents of the 'name' attribute (or NULL if not defined).
-
-  const char *transport The contents of the 'transport' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int profileType_function(const char *_tag_,int _type_,const char *name,const char *transport)
-{
- if(_type_&XMLPARSE_TAG_START)
-   {
-    Transport transporttype;
-    int i;
-
-    XMLPARSE_ASSERT_STRING(_tag_,name);
-    XMLPARSE_ASSERT_STRING(_tag_,transport);
-
-    for(i=0;i<nloaded_profiles;i++)
-       if(!strcmp(name,loaded_profiles[i]->name))
-          XMLPARSE_MESSAGE(_tag_,"profile name must be unique");
-
-    transporttype=TransportType(transport);
-
-    if(transporttype==Transport_None)
-       XMLPARSE_INVALID(_tag_,transport);
-
-    if((nloaded_profiles%16)==0)
-       loaded_profiles=(Profile**)realloc((void*)loaded_profiles,(nloaded_profiles+16)*sizeof(Profile*));
-
-    nloaded_profiles++;
-
-    loaded_profiles[nloaded_profiles-1]=(Profile*)calloc(1,sizeof(Profile));
-
-    loaded_profiles[nloaded_profiles-1]->name=strcpy(malloc(strlen(name)+1),name);
-    loaded_profiles[nloaded_profiles-1]->transport=transporttype;
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the RoutinoProfilesType XSD type is seen
-
-  int RoutinoProfilesType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int RoutinoProfilesType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the XML declaration is seen
-
-  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *version The contents of the 'version' attribute (or NULL if not defined).
-
-  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   The XML profile parser.
 
   int ParseXMLProfiles Returns 0 if OK or something else in case of an error.
@@ -615,7 +634,7 @@ static int profileType_function(const char *_tag_,int _type_,const char *name,co
 
 int ParseXMLProfiles(const char *filename)
 {
- FILE *file;
+ int fd;
  int retval;
 
  if(!ExistsFile(filename))
@@ -624,17 +643,11 @@ int ParseXMLProfiles(const char *filename)
     return(1);
    }
 
- file=fopen(filename,"r");
-
- if(!file)
-   {
-    fprintf(stderr,"Error: Cannot open profiles file '%s' for reading.\n",filename);
-    return(1);
-   }
+ fd=OpenFile(filename);
 
- retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME);
+ retval=ParseXML(fd,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME);
 
- fclose(file);
+ CloseFile(fd);
 
  if(retval)
    {
@@ -878,7 +891,7 @@ void PrintProfilesJSON(void)
  printf("\n");
 
  printf("  // Default transport type\n");
- printf("  transport: 'motorcar',\n");
+ printf("  transport: \"motorcar\",\n");
  printf("\n");
 
  printf("  // Transport types\n");
@@ -934,7 +947,7 @@ void PrintProfilesJSON(void)
  printf("  profile_property: {\n");
  for(i=1;i<Property_Count;i++)
    {
-    printf("    %12s: { ",PropertyName(i));
+    printf("    %13s: { ",PropertyName(i));
     for(j=0;j<nloaded_profiles;j++)
        printf("%s%s: %3d",j==0?"":", ",TransportName(loaded_profiles[j]->transport),(int)loaded_profiles[j]->props_yes[i]);
     printf(" }%s\n",i==(Property_Count-1)?"":",");
@@ -987,7 +1000,7 @@ void PrintProfilesPerl(void)
  printf("\n");
 
  printf("  # Default transport type\n");
- printf("  transport => 'motorcar',\n");
+ printf("  transport => \"motorcar\",\n");
  printf("\n");
 
  printf("  # Transport types\n");
@@ -1043,7 +1056,7 @@ void PrintProfilesPerl(void)
  printf("  profile_property => {\n");
  for(i=1;i<Property_Count;i++)
    {
-    printf("  %12s => {",PropertyName(i));
+    printf("  %13s => {",PropertyName(i));
     for(j=0;j<nloaded_profiles;j++)
        printf("%s %s => %3d",j==0?"":", ",TransportName(loaded_profiles[j]->transport),(int)loaded_profiles[j]->props_yes[i]);
     printf(" }%s\n",i==(Property_Count-1)?"":",");
diff --git a/src/prunex.c b/src/prunex.c
index ffd7be5..a7d1d11 100644
--- a/src/prunex.c
+++ b/src/prunex.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2011-2012 Andrew M. Bishop
+ This file Copyright 2011-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -82,11 +82,11 @@ void StartPruning(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
  /* Open the file read-only */
 
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
 
  /* Read the on-disk image */
 
- while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
+ while(!ReadFileBuffered(segmentsx->fd,&segmentx,sizeof(SegmentX)))
    {
     index_t node1=segmentx.node1;
 
@@ -108,7 +108,7 @@ void StartPruning(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
  /* Close the file */
 
- segmentsx->fd=CloseFile(segmentsx->fd);
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
 
  /* Print the final message */
 
@@ -150,12 +150,12 @@ void FinishPruning(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
 void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distance_t minimum)
 {
+ WaysX *newwaysx;
+ WayX tmpwayx;
  transport_t transport;
  BitMask *connected,*region;
  index_t *regionsegments,*othersegments;
- int nallocregionsegments,nallocothersegments;
- index_t nnewways=0;
- int fd;
+ index_t nallocregionsegments,nallocothersegments;
 
  if(nodesx->number==0 || segmentsx->number==0)
     return;
@@ -167,12 +167,19 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
  segmentsx->data=MapFileWriteable(segmentsx->filename_tmp);
  waysx->data=MapFile(waysx->filename_tmp);
 #else
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
- segmentsx->fd=ReOpenFileWriteable(segmentsx->filename_tmp);
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ nodesx->fd=SlimMapFile(nodesx->filename_tmp);
+ segmentsx->fd=SlimMapFileWriteable(segmentsx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
+
+ InvalidateNodeXCache(nodesx->cache);
+ InvalidateSegmentXCache(segmentsx->cache);
+ InvalidateWayXCache(waysx->cache);
 #endif
 
- fd=ReOpenFileWriteable(waysx->filename_tmp);
+ newwaysx=NewWayList(0,0);
+ CloseFileBuffered(newwaysx->fd);
+
+ newwaysx->fd=SlimMapFileWriteable(newwaysx->filename_tmp);
 
  connected=AllocBitMask(segmentsx->number);
  region   =AllocBitMask(segmentsx->number);
@@ -195,6 +202,9 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
     const char *transport_str=TransportName(transport);
     transports_t transports=TRANSPORTS(transport);
 
+    if(!(waysx->allow&transports))
+       continue;
+
     /* Print the start message */
 
     printf_first("Pruning Isolated Regions (%s): Segments=0 Adjusted=0 Pruned=0",transport_str);
@@ -206,10 +216,10 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
 
     for(i=0;i<segmentsx->number;i++)
       {
-       int nregionsegments=0,nothersegments=0;
+       index_t nregionsegments=0,nothersegments=0;
        distance_t total=0;
        SegmentX *segmentx;
-       WayX *wayx,tmpwayx;
+       WayX *wayx;
 
        if(IsBitSet(connected,i))
           goto endloop;
@@ -222,7 +232,7 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
        if(segmentx->way<waysx->number)
           wayx=LookupWayX(waysx,segmentx->way,1);
        else
-          SeekReadFile(fd,(wayx=&tmpwayx),sizeof(WayX),segmentx->way*sizeof(WayX));
+          SlimFetch(newwaysx->fd,(wayx=&tmpwayx),sizeof(WayX),(segmentx->way-waysx->number)*sizeof(WayX));
 
        if(!(wayx->way.allow&transports))
           goto endloop;
@@ -265,7 +275,7 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
                    if(segmentx->way<waysx->number)
                       wayx=LookupWayX(waysx,segmentx->way,1);
                    else
-                      SeekReadFile(fd,(wayx=&tmpwayx),sizeof(WayX),segmentx->way*sizeof(WayX));
+                      SlimFetch(newwaysx->fd,(wayx=&tmpwayx),sizeof(WayX),(segmentx->way-waysx->number)*sizeof(WayX));
 
                    if(wayx->way.allow&transports)
                      {
@@ -317,7 +327,7 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
              if(segmentx->way<waysx->number)
                 wayx=LookupWayX(waysx,segmentx->way,1);
              else
-                SeekReadFile(fd,(wayx=&tmpwayx),sizeof(WayX),segmentx->way*sizeof(WayX));
+                SlimFetch(newwaysx->fd,(wayx=&tmpwayx),sizeof(WayX),(segmentx->way-waysx->number)*sizeof(WayX));
 
              if(wayx->way.allow==transports)
                {
@@ -333,11 +343,11 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
 
                    tmpwayx.way.allow&=~transports;
 
-                   segmentx->way=waysx->number+nnewways;
+                   segmentx->way=waysx->number+newwaysx->number;
 
-                   SeekWriteFile(fd,&tmpwayx,sizeof(WayX),segmentx->way*sizeof(WayX));
+                   SlimReplace(newwaysx->fd,&tmpwayx,sizeof(WayX),(segmentx->way-waysx->number)*sizeof(WayX));
 
-                   nnewways++;
+                   newwaysx->number++;
 
                    PutBackSegmentX(segmentsx,segmentx);
                   }
@@ -345,7 +355,7 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
                   {
                    tmpwayx.way.allow&=~transports;
 
-                   SeekWriteFile(fd,&tmpwayx,sizeof(WayX),segmentx->way*sizeof(WayX));
+                   SlimReplace(newwaysx->fd,&tmpwayx,sizeof(WayX),(segmentx->way-waysx->number)*sizeof(WayX));
                   }
 
                 nadjusted++;
@@ -391,14 +401,26 @@ void PruneIsolatedRegions(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,dista
  segmentsx->data=UnmapFile(segmentsx->data);
  waysx->data=UnmapFile(waysx->data);
 #else
- nodesx->fd=CloseFile(nodesx->fd);
- segmentsx->fd=CloseFile(segmentsx->fd);
- waysx->fd=CloseFile(waysx->fd);
+ nodesx->fd=SlimUnmapFile(nodesx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
- CloseFile(fd);
+ SlimUnmapFile(newwaysx->fd);
+
+ waysx->number+=newwaysx->number;
+
+ waysx->fd=OpenFileBufferedAppend(waysx->filename_tmp);
 
- waysx->number+=nnewways;
+ newwaysx->fd=ReOpenFileBuffered(newwaysx->filename_tmp);
+
+ while(!ReadFileBuffered(newwaysx->fd,&tmpwayx,sizeof(WayX)))
+    WriteFileBuffered(waysx->fd,&tmpwayx,sizeof(WayX));
+
+ CloseFileBuffered(waysx->fd);
+ CloseFileBuffered(newwaysx->fd);
+
+ FreeWayList(newwaysx,0);
 }
 
 
@@ -433,9 +455,13 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
  segmentsx->data=MapFileWriteable(segmentsx->filename_tmp);
  waysx->data=MapFile(waysx->filename_tmp);
 #else
- nodesx->fd=ReOpenFileWriteable(nodesx->filename_tmp);
- segmentsx->fd=ReOpenFileWriteable(segmentsx->filename_tmp);
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ nodesx->fd=SlimMapFileWriteable(nodesx->filename_tmp);
+ segmentsx->fd=SlimMapFileWriteable(segmentsx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
+
+ InvalidateNodeXCache(nodesx->cache);
+ InvalidateSegmentXCache(segmentsx->cache);
+ InvalidateWayXCache(waysx->cache);
 #endif
 
  /* Loop through the segments and find the short ones for possible modification */
@@ -453,8 +479,8 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
                        :     S2
 
                        :
-      Final state:   ..N3        N2 --
-                       :            S2
+      Final state:   ..N3
+                       :
 
       = OR =
 
@@ -463,14 +489,15 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
                        :     S1        S2        S3    :
 
                        :                               :
-      Final state:   ..N1 ------------ N3 ------------ N4..   N2 --
-                       :       S1               S3     :         S2
+      Final state:   ..N1 ------------ N3 ------------ N4..
+                       :       S1               S3     :
 
       Not if N1 is the same as N4.
-      Not if S2 has different one-way properties from S1 and S3.
+      Must not delete N2 (or N3) if S2 (or S3) has different one-way properties from S1.
+      Must not delete N2 (or N3) if S2 (or S3) has different highway properties from S1.
       Must combine N2, S2 and N3 disallowed transports into new N3.
-      Must not delete N2 or N3 if they are mini-roundabouts.
-      Must not delete N2 or N3 if they are part of turn relations.
+      Must not delete N2 (or N3) if it is a mini-roundabout.
+      Must not delete N2 (or N3) if it is involved in a turn restriction.
 
       = OR =
 
@@ -479,14 +506,14 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
                        :     S1        S2  :
 
                        :               :
-      Final state:   ..N1 ------------ N3..   N2 --
-                       :       S1      :         S2
+      Final state:   ..N1 ------------ N3..
+                       :       S1      :
 
       Not if N1 is the same as N3.
       Not if S1 has different one-way properties from S2.
       Not if S1 has different highway properties from S2.
       Not if N2 disallows transports allowed on S1 and S2.
-      Not if N2 is a mini-roundabouts.
+      Not if N2 is a mini-roundabout.
       Not if N2 is involved in a turn restriction.
      */
 
@@ -502,7 +529,7 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
        node2=segmentx2->node1;
        node3=segmentx2->node2;
 
-       /* Count the segments connected to N2 and N3 */
+       /* Count the segments connected to N2 */
 
        segmentx=FirstSegmentX(segmentsx,node2,4);
 
@@ -526,6 +553,8 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
           segmentx=NextSegmentX(segmentsx,segmentx,node2);
          }
 
+       /* Count the segments connected to N3 */
+
        segmentx=FirstSegmentX(segmentsx,node3,4);
 
        while(segmentx)
@@ -605,6 +634,21 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
           if(!join12 && !join23)
              goto endloop;
 
+          /* Check if allowed due to highway properties */
+
+          wayx1=LookupWayX(waysx,segmentx1->way,1);
+          wayx2=LookupWayX(waysx,segmentx2->way,2);
+          wayx3=LookupWayX(waysx,segmentx3->way,3);
+
+          if(WaysCompare(&wayx1->way,&wayx2->way))
+             join12=0;
+
+          if(WaysCompare(&wayx3->way,&wayx2->way))
+             join23=0;
+
+          if(!join12 && !join23)
+             goto endloop;
+
           /* Check if allowed due to mini-roundabout and turn restriction */
 
           nodex2=LookupNodeX(nodesx,node2,2);
@@ -616,6 +660,9 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
           if(nodex3->flags&NODE_MINIRNDBT)
              join23=0;
 
+          if(!join12 && !join23)
+             goto endloop;
+
           if(nodex2->flags&NODE_TURNRSTRCT2 || nodex2->flags&NODE_TURNRSTRCT)
              join12=0;
 
@@ -638,10 +685,6 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
              newnodex=nodex2;
             }
 
-          wayx1=LookupWayX(waysx,segmentx1->way,1);
-          wayx2=LookupWayX(waysx,segmentx2->way,2);
-          wayx3=LookupWayX(waysx,segmentx3->way,3);
-
           newnodex->allow=nodex2->allow&nodex3->allow; /* combine the restrictions of the two nodes */
           newnodex->allow&=~((~wayx2->way.allow)&wayx3->way.allow); /* disallow anything blocked by segment2 */
           newnodex->allow&=~((~wayx2->way.allow)&wayx1->way.allow); /* disallow anything blocked by segment2 */
@@ -783,9 +826,9 @@ void PruneShortSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,distanc
  segmentsx->data=UnmapFile(segmentsx->data);
  waysx->data=UnmapFile(waysx->data);
 #else
- nodesx->fd=CloseFile(nodesx->fd);
- segmentsx->fd=CloseFile(segmentsx->fd);
- waysx->fd=CloseFile(waysx->fd);
+ nodesx->fd=SlimUnmapFile(nodesx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
  /* Print the final message */
@@ -810,8 +853,8 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 {
  index_t i;
  index_t npruned=0;
+ index_t nalloc;
  BitMask *checked;
- int nalloc;
  index_t *nodes,*segments;
  double *lats,*lons;
  double maximumf;
@@ -819,8 +862,6 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
  if(nodesx->number==0 || segmentsx->number==0 || waysx->number==0)
     return;
 
- maximumf=distance_to_km(maximum);
-
  /* Print the start message */
 
  printf_first("Pruning Straight Highway Nodes: Nodes=0 Pruned=0");
@@ -832,9 +873,13 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
  segmentsx->data=MapFileWriteable(segmentsx->filename_tmp);
  waysx->data=MapFile(waysx->filename_tmp);
 #else
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
- segmentsx->fd=ReOpenFileWriteable(segmentsx->filename_tmp);
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ nodesx->fd=SlimMapFile(nodesx->filename_tmp);
+ segmentsx->fd=SlimMapFileWriteable(segmentsx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
+
+ InvalidateNodeXCache(nodesx->cache);
+ InvalidateSegmentXCache(segmentsx->cache);
+ InvalidateWayXCache(waysx->cache);
 #endif
 
  checked=AllocBitMask(nodesx->number);
@@ -853,7 +898,9 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
  logassert(lats,"Failed to allocate memory (try using slim mode?)");    /* Check malloc() worked */
  logassert(lons,"Failed to allocate memory (try using slim mode?)");    /* Check malloc() worked */
 
- /* Loop through the nodes and find stretchs of simple highway for possible modification */
+ /* Loop through the nodes and find stretches of simple highway for possible modification */
+
+ maximumf=distance_to_km(maximum);
 
  for(i=0;i<nodesx->number;i++)
    {
@@ -899,7 +946,7 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 
           /* Count the segments connected to the node */
 
-          segmentx=FirstSegmentX(segmentsx,nodes[current],1);
+          segmentx=FirstSegmentX(segmentsx,nodes[current],3);
 
           while(segmentx)
             {
@@ -973,6 +1020,8 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 
        if(segcount==2)
          {
+          /* Make space in the lists */
+
           if(upper==(nalloc-1))
             {
              nodes   =(index_t*)realloc(nodes   ,(nalloc+=1024)*sizeof(index_t));
@@ -1006,6 +1055,7 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 
              nodes[upper]=node2;
              segments[upper-1]=segment2;
+             segments[upper]=NO_SEGMENT;
 
              current--;
             }
@@ -1041,16 +1091,29 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
                 segments[upper-1]=segment2;
                }
 
+             segments[upper]=NO_SEGMENT;
+
              current++;
             }
 
           if(nodes[upper]==nodes[lower])
             {
+             if(!lowerbounded && !upperbounded)
+               {
+                nodex=LookupNodeX(nodesx,nodes[lower],1);
+
+                lats[lower]=latlong_to_radians(nodex->latitude);
+                lons[lower]=latlong_to_radians(nodex->longitude);
+               }
+
+             lats[upper]=lats[lower];
+             lons[upper]=lons[lower];
+
              lowerbounded=1;
              upperbounded=1;
             }
          }
-       else
+       else /* if(segment!=2) */
          {
           if(current==upper)
              upperbounded=1;
@@ -1071,13 +1134,13 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 
     /* Check for straight highway */
 
-    while((upper-lower)>=2)
+    for(;lower<(upper-1);lower++)
       {
-       index_t bestc=lower;
-
-       for(current=lower+2;current<=upper;current++)
+       for(current=upper;current>(lower+1);current--)
          {
-          double dist1,dist2,dist3,dist3a,dist3b,distp;
+          SegmentX *segmentx;
+          distance_t dist=0;
+          double dist1,dist2,dist3,distp;
           index_t c;
 
           dist3=distance(lats[lower],lons[lower],lats[current],lons[current]);
@@ -1089,75 +1152,63 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 
              /* Use law of cosines (assume flat Earth) */
 
-             dist3a=(dist1*dist1-dist2*dist2+dist3*dist3)/(2.0*dist3);
-             dist3b=dist3-dist3a;
-
-             if((dist1+dist2)<dist3)
+             if(dist3==0)
+                distp=dist1; /* == dist2 */
+             else if((dist1+dist2)<dist3)
                 distp=0;
-             else if(dist3a>=0 && dist3b>=0)
-                distp=sqrt(dist1*dist1-dist3a*dist3a);
-             else if(dist3a>0)
-                distp=dist2;
-             else /* if(dist3b>0) */
-                distp=dist1;
+             else
+               {
+                double dist3a=(dist1*dist1-dist2*dist2+dist3*dist3)/(2.0*dist3);
+                double dist3b=dist3-dist3a;
+
+                if(dist3a>=0 && dist3b>=0)
+                   distp=sqrt(dist1*dist1-dist3a*dist3a);
+                else if(dist3a>0)
+                   distp=dist2;
+                else /* if(dist3b>0) */
+                   distp=dist1;
+               }
 
              if(distp>maximumf) /* gone too far */
                 break;
             }
 
-          if(c>bestc)
-             bestc=c;
-
-          if(bestc>c)
-             c=bestc;
-
-          if(c==current && current!=upper) /* Can replace at least this far (not finished yet) */
+          if(c<current) /* not finished */
              continue;
 
-          if((c-lower)<2)       /* first three points are not straight */
-            {
-             lower=c;
-             break;
-            }
-          else                  /* delete some segments and shift along */
-            {
-             SegmentX *segmentx;
-             distance_t distance=0;
-
-             current=c;
-
-             for(c=lower+1;c<current;c++)
-               {
-                segmentx=LookupSegmentX(segmentsx,segments[c],1);
+          /* Delete some segments and shift along */
 
-                distance+=DISTANCE(segmentx->distance);
+          for(c=lower+1;c<current;c++)
+            {
+             segmentx=LookupSegmentX(segmentsx,segments[c],1);
 
-                prune_segment(segmentsx,segmentx);
+             dist+=DISTANCE(segmentx->distance);
 
-                npruned++;
-               }
+             prune_segment(segmentsx,segmentx);
 
-             segmentx=LookupSegmentX(segmentsx,segments[lower],1);
+             npruned++;
+            }
 
-             if(nodes[lower]==nodes[current]) /* loop; all within maximum distance */
-               {
-                prune_segment(segmentsx,segmentx);
+          segmentx=LookupSegmentX(segmentsx,segments[lower],1);
 
-                npruned++;
-               }
-             else
-               {
-                segmentx->distance+=distance;
+          if(nodes[lower]==nodes[current]) /* loop; all within maximum distance */
+            {
+             prune_segment(segmentsx,segmentx);
 
-                if(segmentx->node1==nodes[lower])
-                   modify_segment(segmentsx,segmentx,nodes[lower],nodes[current]);
-                else /* if(segmentx->node2==nodes[lower]) */
-                   modify_segment(segmentsx,segmentx,nodes[current],nodes[lower]);
-               }
+             npruned++;
+            }
+          else
+            {
+             segmentx->distance+=dist;
 
-             lower=current;
-             break;
+             if(segmentx->node1==nodes[lower])
+                modify_segment(segmentsx,segmentx,nodes[lower],nodes[current]);
+             else /* if(segmentx->node2==nodes[lower]) */
+                modify_segment(segmentsx,segmentx,nodes[current],nodes[lower]);
             }
+
+          lower=current-1;
+          break;
          }
       }
 
@@ -1182,9 +1233,9 @@ void PruneStraightHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
  segmentsx->data=UnmapFile(segmentsx->data);
  waysx->data=UnmapFile(waysx->data);
 #else
- nodesx->fd=CloseFile(nodesx->fd);
- segmentsx->fd=CloseFile(segmentsx->fd);
- waysx->fd=CloseFile(waysx->fd);
+ nodesx->fd=SlimUnmapFile(nodesx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
  /* Print the final message */
diff --git a/src/queue.c b/src/queue.c
index ec84ee1..226b729 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -26,17 +26,14 @@
 #include "results.h"
 
 
-/*+ The size of the increment to the allocated memory. +*/
-#define QUEUE_INCREMENT 1024
-
-
 /*+ A queue of results. +*/
 struct _Queue
 {
+ int      nincrement;           /*+ The amount to increment the queue when full. +*/
  int      nallocated;           /*+ The number of entries allocated. +*/
  int      noccupied;            /*+ The number of entries occupied. +*/
 
- Result **data;                 /*+ The queue of pointers to results. +*/
+ Result **results;              /*+ The queue of pointers to results. +*/
 };
 
 
@@ -44,24 +41,40 @@ struct _Queue
   Allocate a new queue.
 
   Queue *NewQueueList Returns the queue.
+
+  uint8_t log2bins The base 2 logarithm of the initial number of bins in the queue.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Queue *NewQueueList(void)
+Queue *NewQueueList(uint8_t log2bins)
 {
  Queue *queue;
 
  queue=(Queue*)malloc(sizeof(Queue));
 
- queue->nallocated=QUEUE_INCREMENT;
+ queue->nincrement=1<<log2bins;
+
+ queue->nallocated=queue->nincrement;
  queue->noccupied=0;
 
- queue->data=(Result**)malloc(queue->nallocated*sizeof(Result*));
+ queue->results=(Result**)malloc(queue->nallocated*sizeof(Result*));
 
  return(queue);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Re-use an existing queue.
+
+  Queue *queue The queue to reset for re-use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ResetQueueList(Queue *queue)
+{
+ queue->noccupied=0;
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Free a queue.
 
   Queue *queue The queue to be freed.
@@ -69,7 +82,7 @@ Queue *NewQueueList(void)
 
 void FreeQueueList(Queue *queue)
 {
- free(queue->data);
+ free(queue->results);
 
  free(queue);
 }
@@ -84,9 +97,11 @@ void FreeQueueList(Queue *queue)
   Queue *queue The queue to insert the result into.
 
   Result *result The result to insert into the queue.
+
+  score_t score The score to use for sorting the node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void InsertInQueue(Queue *queue,Result *result)
+void InsertInQueue(Queue *queue,Result *result,score_t score)
 {
  int index;
 
@@ -97,34 +112,36 @@ void InsertInQueue(Queue *queue,Result *result)
 
     if(queue->noccupied==queue->nallocated)
       {
-       queue->nallocated=queue->nallocated+QUEUE_INCREMENT;
-       queue->data=(Result**)realloc((void*)queue->data,queue->nallocated*sizeof(Result*));
+       queue->nallocated=queue->nallocated+queue->nincrement;
+       queue->results=(Result**)realloc((void*)queue->results,queue->nallocated*sizeof(Result*));
       }
 
-    queue->data[index]=result;
-    queue->data[index]->queued=index;
+    queue->results[index]=result;
+    queue->results[index]->queued=index;
    }
  else
-   {
     index=result->queued;
-   }
+
+ queue->results[index]->sortby=score;
 
  /* Bubble up the new value */
 
- while(index>1 &&
-       queue->data[index]->sortby<queue->data[index/2]->sortby)
+ while(index>1)
    {
     int newindex;
     Result *temp;
 
     newindex=index/2;
 
-    temp=queue->data[index];
-    queue->data[index]=queue->data[newindex];
-    queue->data[newindex]=temp;
+    if(queue->results[index]->sortby>=queue->results[newindex]->sortby)
+       break;
 
-    queue->data[index]->queued=index;
-    queue->data[newindex]->queued=newindex;
+    temp=queue->results[index];
+    queue->results[index]=queue->results[newindex];
+    queue->results[newindex]=temp;
+
+    queue->results[index]->queued=index;
+    queue->results[newindex]->queued=newindex;
 
     index=newindex;
    }
@@ -150,52 +167,58 @@ Result *PopFromQueue(Queue *queue)
  if(queue->noccupied==0)
     return(NULL);
 
- retval=queue->data[1];
+ retval=queue->results[1];
  retval->queued=NOT_QUEUED;
 
  index=1;
 
- queue->data[index]=queue->data[queue->noccupied];
+ queue->results[index]=queue->results[queue->noccupied];
+
  queue->noccupied--;
 
  /* Bubble down the newly promoted value */
 
- while((2*index)<queue->noccupied &&
-       (queue->data[index]->sortby>queue->data[2*index  ]->sortby ||
-        queue->data[index]->sortby>queue->data[2*index+1]->sortby))
+ while((2*index)<queue->noccupied)
    {
     int newindex;
     Result *temp;
 
-    if(queue->data[2*index]->sortby<queue->data[2*index+1]->sortby)
-       newindex=2*index;
-    else
-       newindex=2*index+1;
+    newindex=2*index;
+
+    if(queue->results[newindex]->sortby>queue->results[newindex+1]->sortby)
+       newindex=newindex+1;
+
+    if(queue->results[index]->sortby<=queue->results[newindex]->sortby)
+       break;
 
-    temp=queue->data[newindex];
-    queue->data[newindex]=queue->data[index];
-    queue->data[index]=temp;
+    temp=queue->results[newindex];
+    queue->results[newindex]=queue->results[index];
+    queue->results[index]=temp;
 
-    queue->data[index]->queued=index;
-    queue->data[newindex]->queued=newindex;
+    queue->results[index]->queued=index;
+    queue->results[newindex]->queued=newindex;
 
     index=newindex;
    }
 
- if((2*index)==queue->noccupied &&
-    queue->data[index]->sortby>queue->data[2*index]->sortby)
+ if((2*index)==queue->noccupied)
    {
     int newindex;
     Result *temp;
 
     newindex=2*index;
 
-    temp=queue->data[newindex];
-    queue->data[newindex]=queue->data[index];
-    queue->data[index]=temp;
+    if(queue->results[index]->sortby<=queue->results[newindex]->sortby)
+       ; /* break */
+    else
+      {
+       temp=queue->results[newindex];
+       queue->results[newindex]=queue->results[index];
+       queue->results[index]=temp;
 
-    queue->data[index]->queued=index;
-    queue->data[newindex]->queued=newindex;
+       queue->results[index]->queued=index;
+       queue->results[newindex]->queued=newindex;
+      }
    }
 
  return(retval);
diff --git a/src/relations.c b/src/relations.c
index 4102f71..94ec1d8 100644
--- a/src/relations.c
+++ b/src/relations.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -26,6 +26,7 @@
 #include "relations.h"
 #include "fakes.h"
 
+#include "cache.h"
 #include "files.h"
 
 
@@ -40,9 +41,6 @@
 Relations *LoadRelationList(const char *filename)
 {
  Relations *relations;
-#if SLIM
- int i;
-#endif
 
  relations=(Relations*)malloc(sizeof(Relations));
 
@@ -60,16 +58,15 @@ Relations *LoadRelationList(const char *filename)
 
 #else
 
- relations->fd=ReOpenFile(filename);
+ relations->fd=SlimMapFile(filename);
 
  /* Copy the RelationsFile header structure from the loaded data */
 
- ReadFile(relations->fd,&relations->file,sizeof(RelationsFile));
+ SlimFetch(relations->fd,&relations->file,sizeof(RelationsFile),0);
 
  relations->troffset=sizeof(RelationsFile);
 
- for(i=0;i<sizeof(relations->cached)/sizeof(relations->cached[0]);i++)
-    relations->incache[i]=NO_RELATION;
+ relations->cache=NewTurnRelationCache();
 
 #endif
 
@@ -91,6 +88,30 @@ Relations *LoadRelationList(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Destroy the relation list.
+
+  Relations *relations The relation list to destroy.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DestroyRelationList(Relations *relations)
+{
+#if !SLIM
+
+ relations->data=UnmapFile(relations->data);
+
+#else
+
+ relations->fd=SlimUnmapFile(relations->fd);
+
+ DeleteTurnRelationCache(relations->cache);
+
+#endif
+
+ free(relations);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Find the first turn relation in the file whose 'via' matches a specific node.
 
   index_t FindFirstTurnRelation1 Returns the index of the first turn relation matching.
@@ -106,7 +127,7 @@ index_t FindFirstTurnRelation1(Relations *relations,index_t via)
  index_t start=0;
  index_t end=relations->file.trnumber-1;
  index_t mid;
- index_t match=-1;
+ index_t match=NO_RELATION;
 
  /* Binary search - search key any exact match is required.
   *
@@ -137,7 +158,7 @@ index_t FindFirstTurnRelation1(Relations *relations,index_t via)
    }
  while((end-start)>1);
 
- if(match==-1)                      /* Check if start matches */
+ if(match==NO_RELATION)             /* Check if start matches */
    {
     relation=LookupTurnRelation(relations,start,1);
 
@@ -145,7 +166,7 @@ index_t FindFirstTurnRelation1(Relations *relations,index_t via)
        match=start;
    }
 
- if(match==-1)                      /* Check if end matches */
+ if(match==NO_RELATION)             /* Check if end matches */
    {
     relation=LookupTurnRelation(relations,end,1);
 
@@ -153,8 +174,8 @@ index_t FindFirstTurnRelation1(Relations *relations,index_t via)
        match=end;
    }
 
- if(match==-1)
-    return(NO_RELATION);
+ if(match==NO_RELATION)
+    return(match);
 
  while(match>0)                     /* Search backwards for the first match */
    {
@@ -221,7 +242,7 @@ index_t FindFirstTurnRelation2(Relations *relations,index_t via,index_t from)
  index_t start=0;
  index_t end=relations->file.trnumber-1;
  index_t mid;
- index_t match=-1;
+ index_t match=NO_RELATION;
 
  if(IsFakeSegment(from))
     from=IndexRealSegment(from);
@@ -262,7 +283,7 @@ index_t FindFirstTurnRelation2(Relations *relations,index_t via,index_t from)
    }
  while((end-start)>1);
 
- if(match==-1)                      /* Check if start matches */
+ if(match==NO_RELATION)             /* Check if start matches */
    {
     relation=LookupTurnRelation(relations,start,1);
 
@@ -270,7 +291,7 @@ index_t FindFirstTurnRelation2(Relations *relations,index_t via,index_t from)
        match=start;
    }
 
- if(match==-1)                      /* Check if end matches */
+ if(match==NO_RELATION)             /* Check if end matches */
    {
     relation=LookupTurnRelation(relations,end,1);
 
@@ -278,8 +299,8 @@ index_t FindFirstTurnRelation2(Relations *relations,index_t via,index_t from)
        match=end;
    }
 
- if(match==-1)
-    return(NO_RELATION);
+ if(match==NO_RELATION)
+    return(match);
 
  while(match>0)                     /* Search backwards for the first match */
    {
diff --git a/src/relations.h b/src/relations.h
index 7c0e9d0..039bd1c 100644
--- a/src/relations.h
+++ b/src/relations.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -28,6 +28,7 @@
 
 #include "types.h"
 
+#include "cache.h"
 #include "files.h"
 #include "profiles.h"
 
@@ -72,7 +73,8 @@ struct _Relations
  off_t         troffset;        /*+ The offset of the turn relations in the file. +*/
 
  TurnRelation  cached[2];       /*+ Two cached relations read from the file in slim mode. +*/
- index_t       incache[2];      /*+ The indexes of the cached relations. +*/
+
+ TurnRelationCache *cache;      /*+ A RAM cache of turn relations read from the file. +*/
 
 #endif
 
@@ -85,6 +87,8 @@ struct _Relations
 
 Relations *LoadRelationList(const char *filename);
 
+void DestroyRelationList(Relations *relations);
+
 index_t FindFirstTurnRelation1(Relations *relations,index_t via);
 index_t FindNextTurnRelation1(Relations *relations,index_t current);
 
@@ -103,7 +107,23 @@ int IsTurnAllowed(Relations *relations,index_t index,index_t via,index_t from,in
 
 #else
 
-static TurnRelation *LookupTurnRelation(Relations *relations,index_t index,int position);
+/* Prototypes */
+
+static inline TurnRelation *LookupTurnRelation(Relations *relations,index_t index,int position);
+
+CACHE_NEWCACHE_PROTO(TurnRelation)
+CACHE_DELETECACHE_PROTO(TurnRelation)
+CACHE_FETCHCACHE_PROTO(TurnRelation)
+CACHE_INVALIDATECACHE_PROTO(TurnRelation)
+
+
+/* Inline functions */
+
+CACHE_STRUCTURE(TurnRelation)
+CACHE_NEWCACHE(TurnRelation)
+CACHE_DELETECACHE(TurnRelation)
+CACHE_FETCHCACHE(TurnRelation)
+CACHE_INVALIDATECACHE(TurnRelation)
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -120,12 +140,7 @@ static TurnRelation *LookupTurnRelation(Relations *relations,index_t index,int p
 
 static inline TurnRelation *LookupTurnRelation(Relations *relations,index_t index,int position)
 {
- if(relations->incache[position-1]!=index)
-   {
-    SeekReadFile(relations->fd,&relations->cached[position-1],sizeof(TurnRelation),relations->troffset+(off_t)index*sizeof(TurnRelation));
-
-    relations->incache[position-1]=index;
-   }
+ relations->cached[position-1]=*FetchCachedTurnRelation(relations->cache,index,relations->fd,relations->troffset);
 
  return(&relations->cached[position-1]);
 }
diff --git a/src/relationsx.c b/src/relationsx.c
index a55def6..4c9a61c 100644
--- a/src/relationsx.c
+++ b/src/relationsx.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2012 Andrew M. Bishop
+ This file Copyright 2010-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -81,43 +81,38 @@ RelationsX *NewRelationList(int append,int readonly)
 
  /* Route Relations */
 
- relationsx->rfilename    =(char*)malloc(strlen(option_tmpdirname)+32);
- relationsx->rfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
+ relationsx->rrfilename    =(char*)malloc(strlen(option_tmpdirname)+32);
+ relationsx->rrfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
 
- sprintf(relationsx->rfilename    ,"%s/relationsx.route.parsed.mem",option_tmpdirname);
- sprintf(relationsx->rfilename_tmp,"%s/relationsx.route.%p.tmp"    ,option_tmpdirname,(void*)relationsx);
+ sprintf(relationsx->rrfilename    ,"%s/relationsx.route.parsed.mem",option_tmpdirname);
+ sprintf(relationsx->rrfilename_tmp,"%s/relationsx.route.%p.tmp"    ,option_tmpdirname,(void*)relationsx);
 
  if(append || readonly)
-    if(ExistsFile(relationsx->rfilename))
+    if(ExistsFile(relationsx->rrfilename))
       {
-       off_t size,position=0;
-       int rfd;
+       FILESORT_VARINT relationsize;
+       int rrfd;
 
-       size=SizeFile(relationsx->rfilename);
+       rrfd=ReOpenFileBuffered(relationsx->rrfilename);
 
-       rfd=ReOpenFile(relationsx->rfilename);
-
-       while(position<size)
+       while(!ReadFileBuffered(rrfd,&relationsize,FILESORT_VARSIZE))
          {
-          FILESORT_VARINT relationsize;
-
-          SeekReadFile(rfd,&relationsize,FILESORT_VARSIZE,position);
+          SkipFileBuffered(rrfd,relationsize);
 
-          relationsx->rnumber++;
-          position+=relationsize+FILESORT_VARSIZE;
+          relationsx->rrnumber++;
          }
 
-       CloseFile(rfd);
+       CloseFileBuffered(rrfd);
 
-       RenameFile(relationsx->rfilename ,relationsx->rfilename_tmp);
+       RenameFile(relationsx->rrfilename,relationsx->rrfilename_tmp);
       }
 
  if(append)
-    relationsx->rfd=OpenFileAppend(relationsx->rfilename_tmp);
+    relationsx->rrfd=OpenFileBufferedAppend(relationsx->rrfilename_tmp);
  else if(!readonly)
-    relationsx->rfd=OpenFileNew(relationsx->rfilename_tmp);
+    relationsx->rrfd=OpenFileBufferedNew(relationsx->rrfilename_tmp);
  else
-    relationsx->rfd=-1;
+    relationsx->rrfd=-1;
 
 
  /* Turn Restriction Relations */
@@ -141,9 +136,9 @@ RelationsX *NewRelationList(int append,int readonly)
       }
 
  if(append)
-    relationsx->trfd=OpenFileAppend(relationsx->trfilename_tmp);
+    relationsx->trfd=OpenFileBufferedAppend(relationsx->trfilename_tmp);
  else if(!readonly)
-    relationsx->trfd=OpenFileNew(relationsx->trfilename_tmp);
+    relationsx->trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
  else
     relationsx->trfd=-1;
 
@@ -164,12 +159,18 @@ void FreeRelationList(RelationsX *relationsx,int keep)
  /* Route relations */
 
  if(keep)
-    RenameFile(relationsx->rfilename_tmp,relationsx->rfilename);
+    RenameFile(relationsx->rrfilename_tmp,relationsx->rrfilename);
  else
-    DeleteFile(relationsx->rfilename_tmp);
+    DeleteFile(relationsx->rrfilename_tmp);
+
+ free(relationsx->rrfilename);
+ free(relationsx->rrfilename_tmp);
 
- free(relationsx->rfilename);
- free(relationsx->rfilename_tmp);
+ if(relationsx->rridata)
+    free(relationsx->rridata);
+
+ if(relationsx->rrodata)
+    free(relationsx->rrodata);
 
 
  /* Turn Restriction relations */
@@ -182,6 +183,10 @@ void FreeRelationList(RelationsX *relationsx,int keep)
  free(relationsx->trfilename);
  free(relationsx->trfilename_tmp);
 
+ if(relationsx->tridata)
+    free(relationsx->tridata);
+
+
  free(relationsx);
 }
 
@@ -195,6 +200,10 @@ void FreeRelationList(RelationsX *relationsx,int keep)
 
   transports_t routes The types of routes that this relation is for.
 
+  node_t *nodes The array of nodes that are members of the relation.
+
+  int nnodes The number of nodes that are members of the relation.
+
   way_t *ways The array of ways that are members of the relation.
 
   int nways The number of ways that are members of the relation.
@@ -206,31 +215,36 @@ void FreeRelationList(RelationsX *relationsx,int keep)
 
 void AppendRouteRelationList(RelationsX* relationsx,relation_t id,
                              transports_t routes,
+                             node_t *nodes,int nnodes,
                              way_t *ways,int nways,
                              relation_t *relations,int nrelations)
 {
  RouteRelX relationx={0};
  FILESORT_VARINT size;
+ node_t nonode=NO_NODE_ID;
  way_t noway=NO_WAY_ID;
  relation_t norelation=NO_RELATION_ID;
 
  relationx.id=id;
  relationx.routes=routes;
 
- size=sizeof(RouteRelX)+(nways+1)*sizeof(way_t)+(nrelations+1)*sizeof(relation_t);
+ size=sizeof(RouteRelX)+(nnodes+1)*sizeof(node_t)+(nways+1)*sizeof(way_t)+(nrelations+1)*sizeof(relation_t);
 
- WriteFile(relationsx->rfd,&size,FILESORT_VARSIZE);
- WriteFile(relationsx->rfd,&relationx,sizeof(RouteRelX));
+ WriteFileBuffered(relationsx->rrfd,&size,FILESORT_VARSIZE);
+ WriteFileBuffered(relationsx->rrfd,&relationx,sizeof(RouteRelX));
 
- WriteFile(relationsx->rfd,ways  ,nways*sizeof(way_t));
- WriteFile(relationsx->rfd,&noway,      sizeof(way_t));
+ WriteFileBuffered(relationsx->rrfd,nodes  ,nnodes*sizeof(node_t));
+ WriteFileBuffered(relationsx->rrfd,&nonode,       sizeof(node_t));
 
- WriteFile(relationsx->rfd,relations  ,nrelations*sizeof(relation_t));
- WriteFile(relationsx->rfd,&norelation,           sizeof(relation_t));
+ WriteFileBuffered(relationsx->rrfd,ways  ,nways*sizeof(way_t));
+ WriteFileBuffered(relationsx->rrfd,&noway,      sizeof(way_t));
 
- relationsx->rnumber++;
+ WriteFileBuffered(relationsx->rrfd,relations  ,nrelations*sizeof(relation_t));
+ WriteFileBuffered(relationsx->rrfd,&norelation,           sizeof(relation_t));
 
- logassert(relationsx->rnumber!=0,"Too many route relations (change index_t to 64-bits?)"); /* Zero marks the high-water mark for relations. */
+ relationsx->rrnumber++;
+
+ logassert(relationsx->rrnumber!=0,"Too many route relations (change index_t to 64-bits?)"); /* Zero marks the high-water mark for relations. */
 }
 
 
@@ -265,7 +279,7 @@ void AppendTurnRelationList(RelationsX* relationsx,relation_t id,
  relationx.restriction=restriction;
  relationx.except=except;
 
- WriteFile(relationsx->trfd,&relationx,sizeof(TurnRelX));
+ WriteFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
 
  relationsx->trnumber++;
 
@@ -281,11 +295,129 @@ void AppendTurnRelationList(RelationsX* relationsx,relation_t id,
 
 void FinishRelationList(RelationsX *relationsx)
 {
- if(relationsx->rfd!=-1)
-    relationsx->rfd =CloseFile(relationsx->rfd);
+ if(relationsx->rrfd!=-1)
+    relationsx->rrfd =CloseFileBuffered(relationsx->rrfd);
 
  if(relationsx->trfd!=-1)
-    relationsx->trfd=CloseFile(relationsx->trfd);
+    relationsx->trfd=CloseFileBuffered(relationsx->trfd);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find a particular route relation index.
+
+  index_t IndexRouteRelX Returns the index of the route relation with the specified id.
+
+  RelationsX *relationsx The set of relations to process.
+
+  relation_t id The relation id to look for.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t IndexRouteRelX(RelationsX *relationsx,relation_t id)
+{
+ index_t start=0;
+ index_t end=relationsx->rrnumber-1;
+ index_t mid;
+
+ if(relationsx->rrnumber==0)             /* There are no route relations */
+    return(NO_RELATION);
+
+ if(id<relationsx->rridata[start])       /* Key is before start */
+    return(NO_RELATION);
+
+ if(id>relationsx->rridata[end])         /* Key is after end */
+    return(NO_RELATION);
+
+ /* Binary search - search key exact match only is required.
+  *
+  *  # <- start  |  Check mid and move start or end if it doesn't match
+  *  #           |
+  *  #           |  Since an exact match is wanted we can set end=mid-1
+  *  # <- mid    |  or start=mid+1 because we know that mid doesn't match.
+  *  #           |
+  *  #           |  Eventually either end=start or end=start+1 and one of
+  *  # <- end    |  start or end is the wanted one.
+  */
+
+ do
+   {
+    mid=(start+end)/2;                  /* Choose mid point */
+
+    if(relationsx->rridata[mid]<id)      /* Mid point is too low */
+       start=mid+1;
+    else if(relationsx->rridata[mid]>id) /* Mid point is too high */
+       end=mid?(mid-1):mid;
+    else                                /* Mid point is correct */
+       return(mid);
+   }
+ while((end-start)>1);
+
+ if(relationsx->rridata[start]==id)      /* Start is correct */
+    return(start);
+
+ if(relationsx->rridata[end]==id)        /* End is correct */
+    return(end);
+
+ return(NO_RELATION);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find a particular route relation index.
+
+  index_t IndexTurnRelX Returns the index of the turn relation with the specified id.
+
+  RelationsX *relationsx The set of relations to process.
+
+  relation_t id The relation id to look for.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t IndexTurnRelX(RelationsX *relationsx,relation_t id)
+{
+ index_t start=0;
+ index_t end=relationsx->trnumber-1;
+ index_t mid;
+
+ if(relationsx->trnumber==0)            /* There are no route relations */
+    return(NO_RELATION);
+
+ if(id<relationsx->tridata[start])      /* Key is before start */
+    return(NO_RELATION);
+
+ if(id>relationsx->tridata[end])        /* Key is after end */
+    return(NO_RELATION);
+
+ /* Binary search - search key exact match only is required.
+  *
+  *  # <- start  |  Check mid and move start or end if it doesn't match
+  *  #           |
+  *  #           |  Since an exact match is wanted we can set end=mid-1
+  *  # <- mid    |  or start=mid+1 because we know that mid doesn't match.
+  *  #           |
+  *  #           |  Eventually either end=start or end=start+1 and one of
+  *  # <- end    |  start or end is the wanted one.
+  */
+
+ do
+   {
+    mid=(start+end)/2;                   /* Choose mid point */
+
+    if(relationsx->tridata[mid]<id)      /* Mid point is too low */
+       start=mid+1;
+    else if(relationsx->tridata[mid]>id) /* Mid point is too high */
+       end=mid?(mid-1):mid;
+    else                                 /* Mid point is correct */
+       return(mid);
+   }
+ while((end-start)>1);
+
+ if(relationsx->tridata[start]==id)      /* Start is correct */
+    return(start);
+
+ if(relationsx->tridata[end]==id)        /* End is correct */
+    return(end);
+
+ return(NO_RELATION);
 }
 
 
@@ -299,10 +431,10 @@ void SortRelationList(RelationsX* relationsx)
 {
  /* Route Relations */
 
- if(relationsx->rnumber)
+ if(relationsx->rrnumber)
    {
-    index_t rxnumber;
-    int rfd;
+    index_t rrxnumber;
+    int rrfd;
 
     /* Print the start message */
 
@@ -310,28 +442,30 @@ void SortRelationList(RelationsX* relationsx)
 
     /* Re-open the file read-only and a new file writeable */
 
-    relationsx->rfd=ReOpenFile(relationsx->rfilename_tmp);
+    relationsx->rrfd=ReOpenFileBuffered(relationsx->rrfilename_tmp);
 
-    DeleteFile(relationsx->rfilename_tmp);
+    DeleteFile(relationsx->rrfilename_tmp);
 
-    rfd=OpenFileNew(relationsx->rfilename_tmp);
+    rrfd=OpenFileBufferedNew(relationsx->rrfilename_tmp);
 
     /* Sort the relations */
 
-    rxnumber=relationsx->rnumber;
+    rrxnumber=relationsx->rrnumber;
 
-    relationsx->rnumber=filesort_vary(relationsx->rfd,rfd,NULL,
-                                                          (int (*)(const void*,const void*))sort_route_by_id,
-                                                          (int (*)(void*,index_t))deduplicate_route_by_id);
+    relationsx->rrnumber=filesort_vary(relationsx->rrfd,rrfd,NULL,
+                                                           (int (*)(const void*,const void*))sort_route_by_id,
+                                                           (int (*)(void*,index_t))deduplicate_route_by_id);
+
+    relationsx->rrknumber=relationsx->rrnumber;
 
     /* Close the files */
 
-    relationsx->rfd=CloseFile(relationsx->rfd);
-    CloseFile(rfd);
+    relationsx->rrfd=CloseFileBuffered(relationsx->rrfd);
+    CloseFileBuffered(rrfd);
 
     /* Print the final message */
 
-    printf_last("Sorted Route Relations: Relations=%"Pindex_t" Duplicates=%"Pindex_t,rxnumber,rxnumber-relationsx->rnumber);
+    printf_last("Sorted Route Relations: Relations=%"Pindex_t" Duplicates=%"Pindex_t,rrxnumber,rrxnumber-relationsx->rrnumber);
    }
 
  /* Turn Restriction Relations. */
@@ -347,11 +481,11 @@ void SortRelationList(RelationsX* relationsx)
 
     /* Re-open the file read-only and a new file writeable */
 
-    relationsx->trfd=ReOpenFile(relationsx->trfilename_tmp);
+    relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
 
     DeleteFile(relationsx->trfilename_tmp);
 
-    trfd=OpenFileNew(relationsx->trfilename_tmp);
+    trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
 
     /* Sort the relations */
 
@@ -361,10 +495,12 @@ void SortRelationList(RelationsX* relationsx)
                                                                                (int (*)(const void*,const void*))sort_turn_by_id,
                                                                                (int (*)(void*,index_t))deduplicate_turn_by_id);
 
+    relationsx->trknumber=relationsx->trnumber;
+
     /* Close the files */
 
-    relationsx->trfd=CloseFile(relationsx->trfd);
-    CloseFile(trfd);
+    relationsx->trfd=CloseFileBuffered(relationsx->trfd);
+    CloseFileBuffered(trfd);
 
     /* Print the final message */
 
@@ -500,12 +636,14 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
 #if !SLIM
  waysx->data=MapFileWriteable(waysx->filename_tmp);
 #else
- waysx->fd=ReOpenFileWriteable(waysx->filename_tmp);
+ waysx->fd=SlimMapFileWriteable(waysx->filename_tmp);
+
+ InvalidateWayXCache(waysx->cache);
 #endif
 
  /* Re-open the file read-only */
 
- relationsx->rfd=ReOpenFile(relationsx->rfilename_tmp);
+ relationsx->rrfd=ReOpenFileBuffered(relationsx->rrfilename_tmp);
 
  /* Read through the file. */
 
@@ -514,24 +652,25 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
     int ways=0,relations=0;
     index_t i;
 
-    SeekFile(relationsx->rfd,0);
-
     /* Print the start message */
 
     printf_first("Processing Route Relations (%d): Relations=0 Modified Ways=0",iteration);
 
-    for(i=0;i<relationsx->rnumber;i++)
+    SeekFileBuffered(relationsx->rrfd,0);
+
+    for(i=0;i<relationsx->rrnumber;i++)
       {
        FILESORT_VARINT size;
        RouteRelX relationx;
        way_t wayid;
+       node_t nodeid;
        relation_t relationid;
        transports_t routes=Transports_None;
 
        /* Read each route relation */
 
-       ReadFile(relationsx->rfd,&size,FILESORT_VARSIZE);
-       ReadFile(relationsx->rfd,&relationx,sizeof(RouteRelX));
+       ReadFileBuffered(relationsx->rrfd,&size,FILESORT_VARSIZE);
+       ReadFileBuffered(relationsx->rrfd,&relationx,sizeof(RouteRelX));
 
        /* Decide what type of route it is */
 
@@ -558,17 +697,17 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
                }
          }
 
+       /* Skip the nodes */
+
+       while(!ReadFileBuffered(relationsx->rrfd,&nodeid,sizeof(node_t)) && nodeid!=NO_NODE_ID)
+          ;
+
        /* Loop through the ways */
 
-       do
+       while(!ReadFileBuffered(relationsx->rrfd,&wayid,sizeof(way_t)) && wayid!=NO_WAY_ID)
          {
-          ReadFile(relationsx->rfd,&wayid,sizeof(way_t));
-
           /* Update the ways that are listed for the relation */
 
-          if(wayid==NO_WAY_ID)
-             continue;
-
           if(routes)
             {
              index_t way=IndexWayX(waysx,wayid);
@@ -581,7 +720,7 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
                   {
                    if(!(wayx->way.allow&Transports_Foot))
                      {
-                      logerror("Route Relation %"Prelation_t" for Foot contains Way %"Pway_t" that does not allow Foot transport; overriding.\n",relationx.id,wayid);
+                      logerror("Route Relation %"Prelation_t" for Foot contains Way %"Pway_t" that does not allow Foot transport; overriding.\n",logerror_relation(relationx.id),logerror_way(wayid));
                       wayx->way.allow|=Transports_Foot;
                      }
                    wayx->way.props|=Properties_FootRoute;
@@ -591,7 +730,7 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
                   {
                    if(!(wayx->way.allow&Transports_Bicycle))
                      {
-                      logerror("Route Relation %"Prelation_t" for Bicycle contains Way %"Pway_t" that does not allow Bicycle transport; overriding.\n",relationx.id,wayid);
+                      logerror("Route Relation %"Prelation_t" for Bicycle contains Way %"Pway_t" that does not allow Bicycle transport; overriding.\n",logerror_relation(relationx.id),logerror_way(wayid));
                       wayx->way.allow|=Transports_Bicycle;
                      }
                    wayx->way.props|=Properties_BicycleRoute;
@@ -602,24 +741,18 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
                 ways++;
                }
              else
-                logerror("Route Relation %"Prelation_t" contains Way %"Pway_t" but it does not exist in the Routino database.\n",relationx.id,wayid);
+                logerror("Route Relation %"Prelation_t" contains Way %"Pway_t" that does not exist in the Routino database (not a highway?).\n",logerror_relation(relationx.id),logerror_way(wayid));
             }
          }
-       while(wayid!=NO_WAY_ID);
 
        /* Loop through the relations */
 
-       do
+       while(!ReadFileBuffered(relationsx->rrfd,&relationid,sizeof(relation_t)) && relationid!=NO_RELATION_ID)
          {
-          ReadFile(relationsx->rfd,&relationid,sizeof(relation_t));
-
           /* Add the relations that are listed for this relation to the list for next time */
 
-          if(relationid==NO_RELATION_ID)
-             continue;
-
           if(relationid==relationx.id)
-             logerror("Relation %"Prelation_t" contains itself.\n",relationx.id);
+             logerror("Relation %"Prelation_t" contains itself.\n",logerror_relation(relationx.id));
           else if(routes)
             {
              if(nunmatched%256==0)
@@ -631,7 +764,6 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
              nunmatched++;
             }
          }
-       while(relationid!=NO_RELATION_ID);
 
        if(!((i+1)%1000))
           printf_middle("Processing Route Relations (%d): Relations=%"Pindex_t" Modified Ways=%"Pindex_t,iteration,relations,ways);
@@ -657,153 +789,114 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
 
  /* Close the file */
 
- relationsx->rfd=CloseFile(relationsx->rfd);
+ relationsx->rrfd=CloseFileBuffered(relationsx->rrfd);
 
  if(keep)
-    RenameFile(relationsx->rfilename_tmp,relationsx->rfilename);
+    RenameFile(relationsx->rrfilename_tmp,relationsx->rrfilename);
 
  /* Unmap from memory / close the files */
 
 #if !SLIM
  waysx->data=UnmapFile(waysx->data);
 #else
- waysx->fd=CloseFile(waysx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Process the turn relations (first part) to update them with the node/way information.
+  Process the turn relations to update them with node/segment information.
 
   RelationsX *relationsx The set of relations to modify.
 
   NodesX *nodesx The set of nodes to use.
 
+  SegmentsX *segmentsx The set of segments to use.
+
   WaysX *waysx The set of ways to use.
 
   int keep If set to 1 then keep the old data file otherwise delete it.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void ProcessTurnRelations1(RelationsX *relationsx,NodesX *nodesx,WaysX *waysx,int keep)
+void ProcessTurnRelations(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int keep)
 {
  int trfd;
- index_t i,deleted=0;
+ index_t i,total=0,deleted=0;
+
+ if(nodesx->number==0 || segmentsx->number==0)
+    return;
 
  /* Print the start message */
 
- printf_first("Processing Turn Relations (1): Relations=0");
+ printf_first("Processing Turn Relations: Relations=0 Deleted=0 Added=0");
+
+ /* Map into memory / open the files */
+
+#if !SLIM
+ nodesx->data=MapFileWriteable(nodesx->filename_tmp);
+ segmentsx->data=MapFile(segmentsx->filename_tmp);
+ waysx->data=MapFile(waysx->filename_tmp);
+#else
+ nodesx->fd=SlimMapFileWriteable(nodesx->filename_tmp);
+ segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
+
+ InvalidateNodeXCache(nodesx->cache);
+ InvalidateSegmentXCache(segmentsx->cache);
+ InvalidateWayXCache(waysx->cache);
+#endif
 
  /* Re-open the file read-only and a new file writeable */
 
- relationsx->trfd=ReOpenFile(relationsx->trfilename_tmp);
+ relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
 
  if(keep)
     RenameFile(relationsx->trfilename_tmp,relationsx->trfilename);
  else
     DeleteFile(relationsx->trfilename_tmp);
 
- trfd=OpenFileNew(relationsx->trfilename_tmp);
+ trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
 
  /* Process all of the relations */
 
  for(i=0;i<relationsx->trnumber;i++)
    {
     TurnRelX relationx;
+    NodeX *nodex;
+    SegmentX *segmentx;
     index_t via,from,to;
 
-    ReadFile(relationsx->trfd,&relationx,sizeof(TurnRelX));
+    ReadFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
 
     via =IndexNodeX(nodesx,relationx.via);
     from=IndexWayX(waysx,relationx.from);
     to  =IndexWayX(waysx,relationx.to);
 
     if(via==NO_NODE)
-       logerror("Turn Relation %"Prelation_t" contains Node %"Pnode_t" but it does not exist in the Routino database.\n",relationx.id,relationx.via);
+      {
+       logerror("Turn Relation %"Prelation_t" contains Node %"Pnode_t" that does not exist in the Routino database (not a highway node?).\n",logerror_relation(relationx.id),logerror_node(relationx.via));
+       deleted++;
+       goto endloop;
+      }
 
     if(from==NO_WAY)
-       logerror("Turn Relation %"Prelation_t" contains Way %"Pway_t" but it does not exist in the Routino database.\n",relationx.id,relationx.from);
+      {
+       logerror("Turn Relation %"Prelation_t" contains Way %"Pway_t" that does not exist in the Routino database (not a highway?).\n",logerror_relation(relationx.id),logerror_way(relationx.from));
+       deleted++;
+       goto endloop;
+      }
 
     if(to==NO_WAY)
-       logerror("Turn Relation %"Prelation_t" contains Way %"Pway_t" but it does not exist in the Routino database.\n",relationx.id,relationx.to);
+      {
+       logerror("Turn Relation %"Prelation_t" contains Way %"Pway_t" that does not exist in the Routino database (not a highway?).\n",logerror_relation(relationx.id),logerror_way(relationx.to));
+       deleted++;
+       goto endloop;
+      }
 
     relationx.via =via;
     relationx.from=from;
     relationx.to  =to;
 
-    if(relationx.via==NO_NODE || relationx.from==NO_WAY || relationx.to==NO_WAY)
-       deleted++;
-    else
-       WriteFile(trfd,&relationx,sizeof(TurnRelX));
-
-    if(!((i+1)%1000))
-       printf_middle("Processing Turn Relations (1): Relations=%"Pindex_t" Deleted=%"Pindex_t,i+1,deleted);
-   }
-
- /* Close the files */
-
- relationsx->trfd=CloseFile(relationsx->trfd);
- CloseFile(trfd);
-
- /* Print the final message */
-
- printf_last("Processed Turn Relations (1): Relations=%"Pindex_t" Deleted=%"Pindex_t,relationsx->trnumber,deleted);
-
- relationsx->trnumber-=deleted;
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Process the turn relations (second part) to convert them to nodes.
-
-  RelationsX *relationsx The set of relations to modify.
-
-  NodesX *nodesx The set of nodes to use.
-
-  SegmentsX *segmentsx The set of segments to use.
-
-  WaysX *waysx The set of ways to use.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
-{
- TurnRelX relationx;
- int trfd;
- index_t total=0,deleted=0;
-
- if(nodesx->number==0 || segmentsx->number==0)
-    return;
-
- /* Print the start message */
-
- printf_first("Processing Turn Relations (2): Relations=0");
-
- /* Map into memory / open the files */
-
-#if !SLIM
- nodesx->data=MapFileWriteable(nodesx->filename_tmp);
- segmentsx->data=MapFile(segmentsx->filename_tmp);
- waysx->data=MapFile(waysx->filename_tmp);
-#else
- nodesx->fd=ReOpenFileWriteable(nodesx->filename_tmp);
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
- waysx->fd=ReOpenFile(waysx->filename_tmp);
-#endif
-
- /* Re-open the file read-only and a new file writeable */
-
- relationsx->trfd=ReOpenFile(relationsx->trfilename_tmp);
-
- DeleteFile(relationsx->trfilename_tmp);
-
- trfd=OpenFileNew(relationsx->trfilename_tmp);
-
- /* Process all of the relations */
-
- while(!ReadFile(relationsx->trfd,&relationx,sizeof(TurnRelX)))
-   {
-    NodeX *nodex;
-    SegmentX *segmentx;
-
     if(relationx.restriction==TurnRestrict_no_right_turn ||
        relationx.restriction==TurnRestrict_no_left_turn ||
        relationx.restriction==TurnRestrict_no_u_turn ||
@@ -824,7 +917,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
 
              if(node_from!=NO_NODE) /* Only one segment can be on the 'from' way */
                {
-                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'from' way.\n",relationx.id);
+                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'from' way.\n",logerror_relation(relationx.id));
                 deleted++;
                 goto endloop;
                }
@@ -834,7 +927,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
              if(IsOnewayFrom(segmentx,relationx.via))
                 oneway_from=1;  /* not allowed */
 
-             if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorbike|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
+             if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
                 vehicles_from=0;  /* not allowed */
             }
 
@@ -844,7 +937,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
 
              if(node_to!=NO_NODE) /* Only one segment can be on the 'to' way */
                {
-                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'to' way.\n",relationx.id);
+                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'to' way.\n",logerror_relation(relationx.id));
                 deleted++;
                 goto endloop;
                }
@@ -854,7 +947,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
              if(IsOnewayTo(segmentx,relationx.via))
                 oneway_to=1;  /* not allowed */
 
-             if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorbike|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
+             if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
                 vehicles_to=0;  /* not allowed */
             }
 
@@ -862,22 +955,22 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
          }
 
        if(node_from==NO_NODE)
-          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'from' way.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'from' way.\n",logerror_relation(relationx.id));
 
        if(node_to==NO_NODE)
-          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'to' way.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'to' way.\n",logerror_relation(relationx.id));
 
        if(oneway_from)
-          logerror("Turn Relation %"Prelation_t" is not stored because the 'from' way is oneway away from the 'via' node.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way is oneway away from the 'via' node.\n",logerror_relation(relationx.id));
 
        if(oneway_to)
-          logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way is oneway towards the 'via' node.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way is oneway towards the 'via' node.\n",logerror_relation(relationx.id));
 
        if(!vehicles_from)
-          logerror("Turn Relation %"Prelation_t" is not stored because the 'from' way does not allow vehicles.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way does not allow vehicles.\n",logerror_relation(relationx.id));
 
        if(!vehicles_to)
-          logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way does not allow vehicles.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way does not allow vehicles.\n",logerror_relation(relationx.id));
 
        if(oneway_from || oneway_to || !vehicles_from || !vehicles_to || node_from==NO_NODE || node_to==NO_NODE)
          {
@@ -890,12 +983,9 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
        relationx.from=node_from;
        relationx.to  =node_to;
 
-       WriteFile(trfd,&relationx,sizeof(TurnRelX));
+       WriteFileBuffered(trfd,&relationx,sizeof(TurnRelX));
 
        total++;
-
-       if(!(total%1000))
-          printf_middle("Processing Turn Relations (2): Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,total,deleted,total-relationsx->trnumber+deleted);
       }
     else
       {
@@ -915,7 +1005,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
 
              if(node_from!=NO_NODE) /* Only one segment can be on the 'from' way */
                {
-                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'from' way.\n",relationx.id);
+                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'from' way.\n",logerror_relation(relationx.id));
                 deleted++;
                 goto endloop;
                }
@@ -925,7 +1015,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
              if(IsOnewayFrom(segmentx,relationx.via))
                 oneway_from=1;  /* not allowed */
 
-             if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorbike|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
+             if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
                 vehicles_from=0;  /* not allowed */
             }
 
@@ -933,7 +1023,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
             {
              if(node_to!=NO_NODE) /* Only one segment can be on the 'to' way */
                {
-                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'to' way.\n",relationx.id);
+                logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'to' way.\n",logerror_relation(relationx.id));
                 deleted++;
                 goto endloop;
                }
@@ -947,7 +1037,7 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
 
              if(IsOnewayTo(segmentx,relationx.via))
                 ;  /* not allowed */
-             else if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorbike|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
+             else if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
                 ;  /* not allowed */
              else
                {
@@ -961,19 +1051,19 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
          }
 
        if(node_from==NO_NODE)
-          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'from' way.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'from' way.\n",logerror_relation(relationx.id));
 
        if(node_to==NO_NODE)
-          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'to' way.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'to' way.\n",logerror_relation(relationx.id));
 
        if(nnodes_other==0)
-          logerror("Turn Relation %"Prelation_t" is not needed because the only allowed exit from the 'via' node is the 'to' way.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not needed because the only allowed exit from the 'via' node is the 'to' way.\n",logerror_relation(relationx.id));
 
        if(oneway_from)
-          logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way is oneway away from the 'via' node.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way is oneway away from the 'via' node.\n",logerror_relation(relationx.id));
 
        if(!vehicles_from)
-          logerror("Turn Relation %"Prelation_t" is not stored because the 'from' way does not allow vehicles.\n",relationx.id);
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way does not allow vehicles.\n",logerror_relation(relationx.id));
 
        if(oneway_from || !vehicles_from || node_from==NO_NODE || node_to==NO_NODE || nnodes_other==0)
          {
@@ -988,12 +1078,9 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
           relationx.from=node_from;
           relationx.to  =node_other[i];
 
-          WriteFile(trfd,&relationx,sizeof(TurnRelX));
+          WriteFileBuffered(trfd,&relationx,sizeof(TurnRelX));
 
           total++;
-
-          if(!(total%1000))
-             printf_middle("Processing Turn Relations (2): Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,total,deleted,total-relationsx->trnumber+deleted);
          }
       }
 
@@ -1016,13 +1103,24 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
        segmentx=NextSegmentX(segmentsx,segmentx,relationx.via);
       }
 
-   endloop: ;
+   endloop:
+
+    if(!((i+1)%1000))
+       printf_middle("Processing Turn Relations: Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,i+1,deleted,total-relationsx->trnumber+deleted);
    }
 
  /* Close the files */
 
- relationsx->trfd=CloseFile(relationsx->trfd);
- CloseFile(trfd);
+ relationsx->trfd=CloseFileBuffered(relationsx->trfd);
+ CloseFileBuffered(trfd);
+
+ /* Free the now-unneeded indexes */
+
+ free(nodesx->idata);
+ nodesx->idata=NULL;
+
+ free(waysx->idata);
+ waysx->idata=NULL;
 
  /* Unmap from memory / close the files */
 
@@ -1031,14 +1129,14 @@ void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segm
  segmentsx->data=UnmapFile(segmentsx->data);
  waysx->data=UnmapFile(waysx->data);
 #else
- nodesx->fd=CloseFile(nodesx->fd);
- segmentsx->fd=CloseFile(segmentsx->fd);
- waysx->fd=CloseFile(waysx->fd);
+ nodesx->fd=SlimUnmapFile(nodesx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Processed Turn Relations (2): Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,total,deleted,total-relationsx->trnumber+deleted);
+ printf_last("Processed Turn Relations: Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,total,deleted,total-relationsx->trnumber+deleted);
 
  relationsx->trnumber=total;
 }
@@ -1067,15 +1165,15 @@ void RemovePrunedTurnRelations(RelationsX *relationsx,NodesX *nodesx)
 
  /* Re-open the file read-only and a new file writeable */
 
- relationsx->trfd=ReOpenFile(relationsx->trfilename_tmp);
+ relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
 
  DeleteFile(relationsx->trfilename_tmp);
 
- trfd=OpenFileNew(relationsx->trfilename_tmp);
+ trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
 
  /* Process all of the relations */
 
- while(!ReadFile(relationsx->trfd,&relationx,sizeof(TurnRelX)))
+ while(!ReadFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX)))
    {
     relationx.from=nodesx->pdata[relationx.from];
     relationx.via =nodesx->pdata[relationx.via];
@@ -1085,7 +1183,7 @@ void RemovePrunedTurnRelations(RelationsX *relationsx,NodesX *nodesx)
        pruned++;
     else
       {
-       WriteFile(trfd,&relationx,sizeof(TurnRelX));
+       WriteFileBuffered(trfd,&relationx,sizeof(TurnRelX));
 
        notpruned++;
       }
@@ -1100,8 +1198,8 @@ void RemovePrunedTurnRelations(RelationsX *relationsx,NodesX *nodesx)
 
  /* Close the files */
 
- relationsx->trfd=CloseFile(relationsx->trfd);
- CloseFile(trfd);
+ relationsx->trfd=CloseFileBuffered(relationsx->trfd);
+ CloseFileBuffered(trfd);
 
  /* Print the final message */
 
@@ -1135,16 +1233,18 @@ void SortTurnRelationListGeographically(RelationsX *relationsx,NodesX *nodesx,Se
 #if !SLIM
  segmentsx->data=MapFile(segmentsx->filename_tmp);
 #else
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
+
+ InvalidateSegmentXCache(segmentsx->cache);
 #endif
 
  /* Re-open the file read-only and a new file writeable */
 
- relationsx->trfd=ReOpenFile(relationsx->trfilename_tmp);
+ relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
 
  DeleteFile(relationsx->trfilename_tmp);
 
- trfd=OpenFileNew(relationsx->trfilename_tmp);
+ trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
 
  /* Update the segments with geographically sorted node indexes and sort them */
 
@@ -1157,17 +1257,25 @@ void SortTurnRelationListGeographically(RelationsX *relationsx,NodesX *nodesx,Se
 
  /* Close the files */
 
- relationsx->trfd=CloseFile(relationsx->trfd);
- CloseFile(trfd);
+ relationsx->trfd=CloseFileBuffered(relationsx->trfd);
+ CloseFileBuffered(trfd);
 
  /* Unmap from memory / close the files */
 
 #if !SLIM
  segmentsx->data=UnmapFile(segmentsx->data);
 #else
- segmentsx->fd=CloseFile(segmentsx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
 #endif
 
+ /* Free the memory */
+
+ if(nodesx->gdata)
+   {
+    free(nodesx->gdata);
+    nodesx->gdata=NULL;
+   }
+
  /* Print the final message */
 
  printf_last("Sorted Turn Relations Geographically: Turn Relations=%"Pindex_t,relationsx->trnumber);
@@ -1277,27 +1385,27 @@ void SaveRelationList(RelationsX* relationsx,const char *filename)
 
  /* Re-open the file read-only */
 
- relationsx->trfd=ReOpenFile(relationsx->trfilename_tmp);
+ relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
 
  /* Write out the relations data */
 
- fd=OpenFileNew(filename);
+ fd=OpenFileBufferedNew(filename);
 
- SeekFile(fd,sizeof(RelationsFile));
+ SeekFileBuffered(fd,sizeof(RelationsFile));
 
  for(i=0;i<relationsx->trnumber;i++)
    {
     TurnRelX relationx;
     TurnRelation relation={0};
 
-    ReadFile(relationsx->trfd,&relationx,sizeof(TurnRelX));
+    ReadFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
 
     relation.from=relationx.from;
     relation.via=relationx.via;
     relation.to=relationx.to;
     relation.except=relationx.except;
 
-    WriteFile(fd,&relation,sizeof(TurnRelation));
+    WriteFileBuffered(fd,&relation,sizeof(TurnRelation));
 
     if(!((i+1)%1000))
        printf_middle("Writing Relations: Turn Relations=%"Pindex_t,i+1);
@@ -1307,14 +1415,14 @@ void SaveRelationList(RelationsX* relationsx,const char *filename)
 
  relationsfile.trnumber=relationsx->trnumber;
 
- SeekFile(fd,0);
- WriteFile(fd,&relationsfile,sizeof(RelationsFile));
+ SeekFileBuffered(fd,0);
+ WriteFileBuffered(fd,&relationsfile,sizeof(RelationsFile));
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 
  /* Close the file */
 
- relationsx->trfd=CloseFile(relationsx->trfd);
+ relationsx->trfd=CloseFileBuffered(relationsx->trfd);
 
  /* Print the final message */
 
diff --git a/src/relationsx.h b/src/relationsx.h
index 23440a7..d405980 100644
--- a/src/relationsx.h
+++ b/src/relationsx.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2012 Andrew M. Bishop
+ This file Copyright 2010-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -47,9 +47,9 @@ struct _TurnRelX
 {
  relation_t      id;           /*+ The relation identifier. +*/
 
- way_t           from;         /*+ The id of the starting way; initially the OSM value, later the NodeX index then the SegmentX index. +*/
+ way_t           from;         /*+ The id of the starting way; initially the OSM value, later the SegmentX index. +*/
  node_t          via;          /*+ The id of the via node; initially the OSM value, later the NodeX index. +*/
- way_t           to;           /*+ The id of the ending way; initially the OSM value, later the NodeX index then the SegmentX index. +*/
+ way_t           to;           /*+ The id of the ending way; initially the OSM value, later the SegmentX index. +*/
 
  TurnRestriction restriction;  /*+ The type of restriction. +*/
  transports_t    except;       /*+ The types of transports that that this relation does not apply to. +*/
@@ -61,21 +61,28 @@ struct _RelationsX
 {
  /* Route relations */
 
- char      *rfilename;         /*+ The name of the intermediate file (for the RouteRelX). +*/
- char      *rfilename_tmp;     /*+ The name of the temporary file (for the RouteRelX). +*/
+ char       *rrfilename;       /*+ The name of the intermediate file (for the RouteRelX). +*/
+ char       *rrfilename_tmp;   /*+ The name of the temporary file (for the RouteRelX). +*/
 
- int        rfd;               /*+ The file descriptor of the open file (for the RouteRelX). +*/
+ int         rrfd;             /*+ The file descriptor of the open file (for the RouteRelX). +*/
 
- index_t    rnumber;           /*+ The number of extended route relations. +*/
+ index_t     rrnumber;         /*+ The number of extended route relations. +*/
+ index_t     rrknumber;        /*+ The number of extended route relations kept for next time. +*/
+
+ relation_t *rridata;          /*+ The extended relation IDs (sorted by ID). +*/
+ off_t      *rrodata;          /*+ The offset of the route relation in the file (used for error log). +*/
 
  /* Turn restriction relations */
 
- char      *trfilename;        /*+ The name of the intermediate file (for the TurnRelX). +*/
- char      *trfilename_tmp;    /*+ The name of the temporary file (for the TurnRelX). +*/
+ char       *trfilename;       /*+ The name of the intermediate file (for the TurnRelX). +*/
+ char       *trfilename_tmp;   /*+ The name of the temporary file (for the TurnRelX). +*/
+
+ int         trfd;             /*+ The file descriptor of the temporary file (for the TurnRelX). +*/
 
- int        trfd;              /*+ The file descriptor of the temporary file (for the TurnRelX). +*/
+ index_t     trnumber;         /*+ The number of extended turn restriction relations. +*/
+ index_t     trknumber;        /*+ The number of extended turn relations kept for next time. +*/
 
- index_t    trnumber;          /*+ The number of extended turn restriction relations. +*/
+ relation_t *tridata;          /*+ The extended relation IDs (sorted by ID). +*/
 };
 
 
@@ -86,6 +93,7 @@ void FreeRelationList(RelationsX *relationsx,int keep);
 
 void AppendRouteRelationList(RelationsX* relationsx,relation_t id,
                              transports_t routes,
+                             node_t *nodes,int nnodes,
                              way_t *ways,int nways,
                              relation_t *relations,int nrelations);
 void AppendTurnRelationList(RelationsX* relationsx,relation_t id,
@@ -93,12 +101,14 @@ void AppendTurnRelationList(RelationsX* relationsx,relation_t id,
                             TurnRestriction restriction,transports_t except);
 void FinishRelationList(RelationsX *relationsx);
 
+index_t IndexRouteRelX(RelationsX *relationsx,relation_t id);
+index_t IndexTurnRelX(RelationsX *relationsx,relation_t id);
+
 void SortRelationList(RelationsX *relationsx);
 
 void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep);
 
-void ProcessTurnRelations1(RelationsX *relationsx,NodesX *nodesx,WaysX *waysx,int keep);
-void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx);
+void ProcessTurnRelations(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int keep);
 
 void RemovePrunedTurnRelations(RelationsX *relationsx,NodesX *nodesx);
 
diff --git a/src/results.c b/src/results.c
index f295a3b..e46d9d4 100644
--- a/src/results.c
+++ b/src/results.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -28,44 +28,35 @@
 #include "logging.h"
 
 
-/*+ The maximum number of collisions in a bin for the Results 'point' arrays before worrying. +*/
-#define MAX_COLLISIONS 32
- 
+#define HASH_NODE_SEGMENT(node,segment) ((node)^(segment<<4))
+
 
 /*++++++++++++++++++++++++++++++++++++++
   Allocate a new results list.
 
   Results *NewResultsList Returns the results list.
 
-  int nbins The initial number of bins in the results array.
+  uint8_t log2bins The base 2 logarithm of the initial number of bins in the results array.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Results *NewResultsList(int nbins)
+Results *NewResultsList(uint8_t log2bins)
 {
  Results *results;
 
  results=(Results*)malloc(sizeof(Results));
 
- results->nbins=1;
- results->mask=~0;
-
- while(nbins>>=1)
-   {
-    results->mask<<=1;
-    results->nbins<<=1;
-   }
-
- results->mask=~results->mask;
+ results->nbins=1<<log2bins;
+ results->mask=results->nbins-1;
+ results->ncollisions=log2bins-4;
 
  results->number=0;
 
- results->npoint1=0;
-
  results->count=(uint8_t*)calloc(results->nbins,sizeof(uint8_t));
- results->point=(Result***)malloc(MAX_COLLISIONS*sizeof(Result**));
+ results->point=(Result**)calloc(results->nbins,sizeof(Result*));
 
  results->ndata1=0;
- results->ndata2=results->nbins;
+ results->nallocdata1=0;
+ results->ndata2=results->nbins>>2;
 
  results->data=NULL;
 
@@ -75,11 +66,41 @@ Results *NewResultsList(int nbins)
  results->finish_node=NO_NODE;
  results->last_segment=NO_SEGMENT;
 
+ results->start_waypoint=NO_WAYPOINT;
+ results->finish_waypoint=NO_WAYPOINT;
+
  return(results);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Allocate a new results list.
+
+  Results *results The results list to be reset.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ResetResultsList(Results *results)
+{
+ uint32_t i;
+
+ results->number=0;
+ results->ndata1=0;
+
+ for(i=0;i<results->nbins;i++)
+   {
+    results->point[i]=NULL;
+    results->count[i]=0;
+   }
+
+ results->start_node=NO_NODE;
+ results->prev_segment=NO_SEGMENT;
+
+ results->finish_node=NO_NODE;
+ results->last_segment=NO_SEGMENT;
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Free a results list.
 
   Results *results The results list to be destroyed.
@@ -87,16 +108,13 @@ Results *NewResultsList(int nbins)
 
 void FreeResultsList(Results *results)
 {
- int i;
+ uint32_t i;
 
- for(i=0;i<results->ndata1;i++)
+ for(i=0;i<results->nallocdata1;i++)
     free(results->data[i]);
 
  free(results->data);
 
- for(i=0;i<results->npoint1;i++)
-    free(results->point[i]);
-
  free(results->point);
 
  free(results->count);
@@ -120,84 +138,83 @@ void FreeResultsList(Results *results)
 Result *InsertResult(Results *results,index_t node,index_t segment)
 {
  Result *result;
- int bin=node&results->mask;
+ uint32_t bin=HASH_NODE_SEGMENT(node,segment)&results->mask;
 
  /* Check if we have hit the limit on the number of collisions per bin */
 
- if(results->count[bin]>MAX_COLLISIONS && results->count[bin]==results->npoint1)
+ if(results->count[bin]==results->ncollisions)
    {
-    int i,j,k;
+    uint32_t i;
 
     results->nbins<<=1;
-    results->mask=(results->mask<<1)|1;
+    results->mask=results->nbins-1;
+    results->ncollisions++;
 
     results->count=(uint8_t*)realloc((void*)results->count,results->nbins*sizeof(uint8_t));
-
-    for(i=0;i<results->npoint1;i++)
-       results->point[i]=(Result**)realloc((void*)results->point[i],results->nbins*sizeof(Result*));
+    results->point=(Result**)realloc((void*)results->point,results->nbins*sizeof(Result*));
 
     for(i=0;i<results->nbins/2;i++)
       {
-       int c=results->count[i];
+       Result *r=results->point[i];
+       Result **bin1,**bin2;
 
+       results->count[i]                 =0;
        results->count[i+results->nbins/2]=0;
 
-       for(j=0,k=0;j<c;j++)
+       bin1=&results->point[i];
+       bin2=&results->point[i+results->nbins/2];
+
+       *bin1=NULL;
+       *bin2=NULL;
+
+       while(r)
          {
-          int newbin=results->point[j][i]->node&results->mask;
+          Result *rh=r->hashnext;
+          uint32_t newbin=HASH_NODE_SEGMENT(r->node,r->segment)&results->mask;
+
+          r->hashnext=NULL;
 
           if(newbin==i)
-            {
-             if(k!=j)
-                results->point[k][i]=results->point[j][i];
-             k++;
-            }
+            { *bin1=r; bin1=&r->hashnext; }
           else
-            {
-             results->point[results->count[newbin]][newbin]=results->point[j][i];
+            { *bin2=r; bin2=&r->hashnext; }
+
+          results->count[newbin]++;
 
-             results->count[newbin]++;
-             results->count[i]--;
-            }
+          r=rh;
          }
       }
 
-    bin=node&results->mask;
+    bin=HASH_NODE_SEGMENT(node,segment)&results->mask;
    }
 
- /* Check that the arrays have enough space or allocate more. */
-
- if(results->count[bin]==results->npoint1)
-   {
-    logassert(results->npoint1<255,"Results are more numerous than expected (report a bug)");
-
-    results->npoint1++;
-
-    if(results->npoint1>MAX_COLLISIONS)
-       results->point=(Result***)realloc((void*)results->point,results->npoint1*sizeof(Result**));
-
-    results->point[results->npoint1-1]=(Result**)malloc(results->nbins*sizeof(Result*));
-   }
+ /* Check if we need more data space allocated */
 
  if((results->number%results->ndata2)==0)
    {
     results->ndata1++;
 
-    results->data=(Result**)realloc((void*)results->data,results->ndata1*sizeof(Result*));
-    results->data[results->ndata1-1]=(Result*)malloc(results->ndata2*sizeof(Result));
+    if(results->ndata1>=results->nallocdata1)
+      {
+       results->nallocdata1++;
+       results->data=(Result**)realloc((void*)results->data,results->nallocdata1*sizeof(Result*));
+       results->data[results->nallocdata1-1]=(Result*)malloc(results->ndata2*sizeof(Result));
+      }
    }
 
  /* Insert the new entry */
 
- results->point[results->count[bin]][bin]=&results->data[results->ndata1-1][results->number%results->ndata2];
+ result=&results->data[results->ndata1-1][results->number%results->ndata2];
 
- results->number++;
+ result->hashnext=results->point[bin];
+
+ results->point[bin]=result;
 
  results->count[bin]++;
 
- /* Initialise the result */
+ results->number++;
 
- result=results->point[results->count[bin]-1][bin];
+ /* Initialise the result */
 
  result->node=node;
  result->segment=segment;
@@ -215,34 +232,6 @@ Result *InsertResult(Results *results,index_t node,index_t segment)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find a result; search by node only (don't care about the segment but find the shortest).
-
-  Result *FindResult1 Returns the result that has been found.
-
-  Results *results The results structure to search.
-
-  index_t node The node that is to be found.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-Result *FindResult1(Results *results,index_t node)
-{
- int bin=node&results->mask;
- score_t best_score=INF_SCORE;
- Result *best_result=NULL;
- int i;
-
- for(i=results->count[bin]-1;i>=0;i--)
-    if(results->point[i][bin]->node==node && results->point[i][bin]->score<best_score)
-      {
-       best_score=results->point[i][bin]->score;
-       best_result=results->point[i][bin];
-      }
-
- return(best_result);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   Find a result; search by node and segment.
 
   Result *FindResult Returns the result that has been found.
@@ -256,14 +245,20 @@ Result *FindResult1(Results *results,index_t node)
 
 Result *FindResult(Results *results,index_t node,index_t segment)
 {
- int bin=node&results->mask;
- int i;
+ Result *r;
+ uint32_t bin=HASH_NODE_SEGMENT(node,segment)&results->mask;
+
+ r=results->point[bin];
 
- for(i=results->count[bin]-1;i>=0;i--)
-    if(results->point[i][bin]->segment==segment && results->point[i][bin]->node==node)
-       return(results->point[i][bin]);
+ while(r)
+   {
+    if(r->segment==segment && r->node==node)
+       break;
 
- return(NULL);
+    r=r->hashnext;
+   }
+
+ return(r);
 }
 
 
@@ -293,15 +288,17 @@ Result *FirstResult(Results *results)
 
 Result *NextResult(Results *results,Result *result)
 {
- int i,j=0;
+ uint32_t i;
+ size_t j=0;
 
  for(i=0;i<results->ndata1;i++)
-   {
-    j=result-results->data[i];
+    if(result>=results->data[i])
+      {
+       j=result-results->data[i];
 
-    if(j>=0 && j<results->ndata2)
-       break;
-   }
+       if(j<results->ndata2)
+          break;
+      }
 
  if(++j>=results->ndata2)
    {i++;j=0;}
diff --git a/src/results.h b/src/results.h
index 3878221..736211b 100644
--- a/src/results.h
+++ b/src/results.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2011 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -51,25 +51,28 @@ struct _Result
  score_t   sortby;              /*+ The best possible weighted distance or duration score from the start to the finish. +*/
 
  uint32_t  queued;              /*+ The position of this result in the queue. +*/
+
+ Result   *hashnext;            /*+ The next result in the linked list for this hash bin. +*/
 };
 
 /*+ A list of results. +*/
 typedef struct _Results
 {
- uint32_t  nbins;               /*+ The number of bins. +*/
- uint32_t  mask;                /*+ A bit mask to select the bottom 'nbins' bits. +*/
+ uint32_t  nbins;               /*+ The number of bins in the has table. +*/
+ uint32_t  mask;                /*+ A bit mask to select the bottom log2(nbins) bits. +*/
 
  uint32_t  number;              /*+ The total number of occupied results. +*/
 
- uint8_t   npoint1;             /*+ The amount of space allocated for results
-                                    (the first dimension of the 'point' array). +*/
+ uint8_t   ncollisions;         /*+ The number of results allowed in each hash bin. +*/
+ uint8_t  *count;               /*+ An array of nbins counters of results in each hash bin. +*/
 
- uint8_t  *count;               /*+ An array of nbins counters of results in each array. +*/
- Result ***point;               /*+ An array of nbins arrays of pointers to actual results. +*/
+ Result  **point;               /*+ An array of nbins linked lists of results for one hash bin. +*/
 
  uint32_t  ndata1;              /*+ The size of the first dimension of the 'data' array. +*/
  uint32_t  ndata2;              /*+ The size of the second dimension of the 'data' array. +*/
 
+ uint32_t  nallocdata1;         /*+ The amount of allocated space in the first dimension of the 'data' array. +*/
+
  Result  **data;                /*+ An array of arrays containing the actual results, the first
                                     dimension is reallocated but the second dimension is not.
                                     Most importantly pointers into the real data don't change
@@ -80,6 +83,9 @@ typedef struct _Results
 
  index_t finish_node;           /*+ The finish node. +*/
  index_t last_segment;          /*+ The last segment (to arrive at the finish node). +*/
+
+ waypoint_t start_waypoint;     /*+ The number of the starting waypoint. +*/
+ waypoint_t finish_waypoint;    /*+ The number of the finish waypoint. +*/
 }
  Results;
 
@@ -91,12 +97,12 @@ typedef struct _Queue Queue;
 
 /* Results functions in results.c */
 
-Results *NewResultsList(int nbins);
+Results *NewResultsList(uint8_t log2bins);
+void ResetResultsList(Results *results);
 void FreeResultsList(Results *results);
 
 Result *InsertResult(Results *results,index_t node,index_t segment);
 
-Result *FindResult1(Results *results,index_t node);
 Result *FindResult(Results *results,index_t node,index_t segment);
 
 Result *FirstResult(Results *results);
@@ -105,10 +111,11 @@ Result *NextResult(Results *results,Result *result);
 
 /* Queue functions in queue.c */
 
-Queue *NewQueueList(void);
+Queue *NewQueueList(uint8_t log2bins);
+void ResetQueueList(Queue *queue);
 void FreeQueueList(Queue *queue);
 
-void InsertInQueue(Queue *queue,Result *result);
+void InsertInQueue(Queue *queue,Result *result,score_t score);
 Result *PopFromQueue(Queue *queue);
 
 
diff --git a/src/router.c b/src/router.c
index f798949..f9f8100 100644
--- a/src/router.c
+++ b/src/router.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -39,6 +39,9 @@
 #include "profiles.h"
 
 
+/*+ To help when debugging +*/
+#define DEBUG 0
+
 /*+ The maximum distance from the specified point to search for a node or segment (in km). +*/
 #define MAXSEARCH  1
 
@@ -49,7 +52,7 @@
 int option_quiet=0;
 
 /*+ The options to select the format of the output. +*/
-int option_html=0,option_gpx_track=0,option_gpx_route=0,option_text=0,option_text_all=0,option_none=0;
+int option_html=0,option_gpx_track=0,option_gpx_route=0,option_text=0,option_text_all=0,option_none=0,option_stdout=0;
 
 /*+ The option to calculate the quickest route insted of the shortest. +*/
 int option_quickest=0;
@@ -58,6 +61,7 @@ int option_quickest=0;
 /* Local functions */
 
 static void print_usage(int detail,const char *argerr,const char *err);
+static Results *CalculateRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t prev_segment,index_t finish_node);
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -66,24 +70,26 @@ static void print_usage(int detail,const char *argerr,const char *err);
 
 int main(int argc,char** argv)
 {
- Nodes    *OSMNodes;
- Segments *OSMSegments;
- Ways     *OSMWays;
- Relations*OSMRelations;
- Results  *results[NWAYPOINTS+1]={NULL};
- int       point_used[NWAYPOINTS+1]={0};
- double    point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
- double    heading=-999;
- int       help_profile=0,help_profile_xml=0,help_profile_json=0,help_profile_pl=0;
- char     *dirname=NULL,*prefix=NULL;
- char     *profiles=NULL,*profilename=NULL;
- char     *translations=NULL,*language=NULL;
- int       exactnodes=0;
- Transport transport=Transport_None;
- Profile  *profile=NULL;
- index_t   start_node=NO_NODE,finish_node=NO_NODE;
- index_t   join_segment=NO_SEGMENT;
- int       arg,point;
+ Nodes     *OSMNodes;
+ Segments  *OSMSegments;
+ Ways      *OSMWays;
+ Relations *OSMRelations;
+ Results   *results[NWAYPOINTS+1]={NULL};
+ int        point_used[NWAYPOINTS+1]={0};
+ double     point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
+ double     heading=-999;
+ int        help_profile=0,help_profile_xml=0,help_profile_json=0,help_profile_pl=0;
+ char      *dirname=NULL,*prefix=NULL;
+ char      *profiles=NULL,*profilename=NULL;
+ char      *translations=NULL,*language=NULL;
+ int        exactnodes=0,reverse=0,loop=0;
+ Transport  transport=Transport_None;
+ Profile   *profile=NULL;
+ index_t    start_node,finish_node=NO_NODE,first_node=NO_NODE;
+ index_t    join_segment=NO_SEGMENT;
+ int        arg,nresults=0;
+ waypoint_t start_waypoint,finish_waypoint=NO_WAYPOINT;
+ waypoint_t first_waypoint=NWAYPOINTS,last_waypoint=1,inc_dec_waypoint,waypoint;
 
  /* Parse the command line arguments */
 
@@ -114,6 +120,10 @@ int main(int argc,char** argv)
        translations=&argv[arg][15];
     else if(!strcmp(argv[arg],"--exact-nodes-only"))
        exactnodes=1;
+    else if(!strcmp(argv[arg],"--reverse"))
+       reverse=1;
+    else if(!strcmp(argv[arg],"--loop"))
+       loop=1;
     else if(!strcmp(argv[arg],"--quiet"))
        option_quiet=1;
     else if(!strcmp(argv[arg],"--loggable"))
@@ -130,6 +140,8 @@ int main(int argc,char** argv)
        option_text_all=1;
     else if(!strcmp(argv[arg],"--output-none"))
        option_none=1;
+    else if(!strcmp(argv[arg],"--output-stdout"))
+      { option_stdout=1; option_quiet=1; }
     else if(!strncmp(argv[arg],"--profile=",10))
        profilename=&argv[arg][10];
     else if(!strncmp(argv[arg],"--language=",11))
@@ -147,6 +159,12 @@ int main(int argc,char** argv)
     argv[arg]=NULL;
    }
 
+ if(option_stdout && (option_html+option_gpx_track+option_gpx_route+option_text+option_text_all)!=1)
+   {
+    fprintf(stderr,"Error: The '--output-stdout' option requires exactly one other output option (but not '--output-none').\n");
+    exit(EXIT_FAILURE);
+   }
+
  /* Load in the profiles */
 
  if(transport==Transport_None)
@@ -157,7 +175,7 @@ int main(int argc,char** argv)
     if(!ExistsFile(profiles))
       {
        fprintf(stderr,"Error: The '--profiles' option specifies a file that does not exist.\n");
-       return(1);
+       exit(EXIT_FAILURE);
       }
    }
  else
@@ -169,14 +187,14 @@ int main(int argc,char** argv)
     else
       {
        fprintf(stderr,"Error: The '--profiles' option was not used and the default 'profiles.xml' does not exist.\n");
-       return(1);
+       exit(EXIT_FAILURE);
       }
    }
 
  if(ParseXMLProfiles(profiles))
    {
     fprintf(stderr,"Error: Cannot read the profiles in the file '%s'.\n",profiles);
-    return(1);
+    exit(EXIT_FAILURE);
    }
 
  /* Choose the selected profile. */
@@ -188,7 +206,7 @@ int main(int argc,char** argv)
     if(!profile)
       {
        fprintf(stderr,"Error: Cannot find a profile called '%s' in '%s'.\n",profilename,profiles);
-       return(1);
+       exit(EXIT_FAILURE);
       }
    }
  else
@@ -210,52 +228,47 @@ int main(int argc,char** argv)
        option_quickest=0;
     else if(!strcmp(argv[arg],"--quickest"))
        option_quickest=1;
-    else if(isdigit(argv[arg][0]) ||
-       ((argv[arg][0]=='-' || argv[arg][0]=='+') && isdigit(argv[arg][1])))
+    else if(!strncmp(argv[arg],"--lon",5) && isdigit(argv[arg][5]))
       {
-       for(point=1;point<=NWAYPOINTS;point++)
-          if(point_used[point]!=3)
-            {
-             if(point_used[point]==0)
-               {
-                point_lon[point]=degrees_to_radians(atof(argv[arg]));
-                point_used[point]=1;
-               }
-             else /* if(point_used[point]==1) */
-               {
-                point_lat[point]=degrees_to_radians(atof(argv[arg]));
-                point_used[point]=3;
-               }
-             break;
-            }
-      }
-     else if(!strncmp(argv[arg],"--lon",5) && isdigit(argv[arg][5]))
-       {
-        char *p=&argv[arg][6];
-        while(isdigit(*p)) p++;
-        if(*p++!='=')
-           print_usage(0,argv[arg],NULL);
+       int point;
+       char *p=&argv[arg][6];
+
+       while(isdigit(*p)) p++;
+       if(*p++!='=')
+          print_usage(0,argv[arg],NULL);
  
-        point=atoi(&argv[arg][5]);
-        if(point>NWAYPOINTS || point_used[point]&1)
-           print_usage(0,argv[arg],NULL);
+       point=atoi(&argv[arg][5]);
+       if(point>NWAYPOINTS || point_used[point]&1)
+          print_usage(0,argv[arg],NULL);
  
        point_lon[point]=degrees_to_radians(atof(p));
        point_used[point]+=1;
+
+       if(point<first_waypoint)
+          first_waypoint=point;
+       if(point>last_waypoint)
+          last_waypoint=point;
       }
-     else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5]))
-       {
-        char *p=&argv[arg][6];
-        while(isdigit(*p)) p++;
-        if(*p++!='=')
-           print_usage(0,argv[arg],NULL);
+    else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5]))
+      {
+       int point;
+       char *p=&argv[arg][6];
+
+       while(isdigit(*p)) p++;
+       if(*p++!='=')
+          print_usage(0,argv[arg],NULL);
  
-        point=atoi(&argv[arg][5]);
-        if(point>NWAYPOINTS || point_used[point]&2)
-           print_usage(0,argv[arg],NULL);
+       point=atoi(&argv[arg][5]);
+       if(point>NWAYPOINTS || point_used[point]&2)
+          print_usage(0,argv[arg],NULL);
  
        point_lat[point]=degrees_to_radians(atof(p));
        point_used[point]+=2;
+
+       if(point<first_waypoint)
+          first_waypoint=point;
+       if(point>last_waypoint)
+          last_waypoint=point;
       }
     else if(!strncmp(argv[arg],"--heading=",10))
       {
@@ -349,8 +362,8 @@ int main(int argc,char** argv)
        print_usage(0,argv[arg],NULL);
    }
 
- for(point=1;point<=NWAYPOINTS;point++)
-    if(point_used[point]==1 || point_used[point]==2)
+ for(waypoint=1;waypoint<=NWAYPOINTS;waypoint++)
+    if(point_used[waypoint]==1 || point_used[waypoint]==2)
        print_usage(0,NULL,"All waypoints must have latitude and longitude.");
 
  /* Print one of the profiles if requested */
@@ -392,7 +405,7 @@ int main(int argc,char** argv)
        if(!ExistsFile(translations))
          {
           fprintf(stderr,"Error: The '--translations' option specifies a file that does not exist.\n");
-          return(1);
+          exit(EXIT_FAILURE);
          }
       }
     else
@@ -404,14 +417,14 @@ int main(int argc,char** argv)
        else
          {
           fprintf(stderr,"Error: The '--translations' option was not used and the default 'translations.xml' does not exist.\n");
-          return(1);
+          exit(EXIT_FAILURE);
          }
       }
 
     if(ParseXMLTranslations(translations,language))
       {
        fprintf(stderr,"Error: Cannot read the translations in the file '%s'.\n",translations);
-       return(1);
+       exit(EXIT_FAILURE);
       }
    }
 
@@ -428,50 +441,71 @@ int main(int argc,char** argv)
  if(UpdateProfile(profile,OSMWays))
    {
     fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n");
-    return(1);
+    exit(EXIT_FAILURE);
    }
 
- /* Loop through all pairs of points */
+ /* Check for reverse direction */
+
+ if(reverse)
+   {
+    waypoint_t temp;
+
+    temp=first_waypoint;
+    first_waypoint=last_waypoint;
+    last_waypoint=temp;
+
+    last_waypoint--;
 
- for(point=1;point<=NWAYPOINTS;point++)
+    inc_dec_waypoint=-1;
+   }
+ else
+   {
+    last_waypoint++;
+
+    inc_dec_waypoint=1;
+   }
+
+ /* Loop through all pairs of waypoints */
+
+ for(waypoint=first_waypoint;waypoint!=last_waypoint;waypoint+=inc_dec_waypoint)
    {
-    Results *begin,*end;
-    Result *finish_result;
     distance_t distmax=km_to_distance(MAXSEARCH);
     distance_t distmin;
     index_t segment=NO_SEGMENT;
     index_t node1,node2;
-    int     nsuper=0;
 
-    if(point_used[point]!=3)
+    if(point_used[waypoint]!=3)
        continue;
 
     /* Find the closest point */
 
     start_node=finish_node;
+    start_waypoint=finish_waypoint;
 
     if(exactnodes)
       {
-       finish_node=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin);
+       finish_node=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[waypoint],point_lon[waypoint],distmax,profile,&distmin);
       }
     else
       {
        distance_t dist1,dist2;
 
-       segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2);
+       segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[waypoint],point_lon[waypoint],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2);
 
        if(segment!=NO_SEGMENT)
-          finish_node=CreateFakes(OSMNodes,OSMSegments,point,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
+          finish_node=CreateFakes(OSMNodes,OSMSegments,waypoint,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
        else
           finish_node=NO_NODE;
       }
 
     if(finish_node==NO_NODE)
       {
-       fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",point);
-       return(1);
+       fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",waypoint);
+       exit(EXIT_FAILURE);
       }
 
+    finish_waypoint=waypoint;
+
     if(!option_quiet)
       {
        double lat,lon;
@@ -479,148 +513,214 @@ int main(int argc,char** argv)
        if(IsFakeNode(finish_node))
           GetFakeLatLong(finish_node,&lat,&lon);
        else
-          GetLatLong(OSMNodes,finish_node,&lat,&lon);
+          GetLatLong(OSMNodes,finish_node,NULL,&lat,&lon);
 
        if(IsFakeNode(finish_node))
-          printf("Point %d is segment %"Pindex_t" (node %"Pindex_t" -> %"Pindex_t"): %3.6f %4.6f = %2.3f km\n",point,segment,node1,node2,
+          printf("Waypoint %d is segment %"Pindex_t" (node %"Pindex_t" -> %"Pindex_t"): %3.6f %4.6f = %2.3f km\n",waypoint,segment,node1,node2,
                  radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
        else
-          printf("Point %d is node %"Pindex_t": %3.6f %4.6f = %2.3f km\n",point,finish_node,
+          printf("Waypoint %d is node %"Pindex_t": %3.6f %4.6f = %2.3f km\n",waypoint,finish_node,
                  radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
       }
 
+    /* Check the nodes */
+
     if(start_node==NO_NODE)
        continue;
 
     if(start_node==finish_node)
        continue;
 
+    if(first_node==NO_NODE)
+       first_node=start_node;
+
     if(heading!=-999 && join_segment==NO_SEGMENT)
        join_segment=FindClosestSegmentHeading(OSMNodes,OSMSegments,OSMWays,start_node,heading,profile);
 
-    /* Calculate the beginning of the route */
+    /* Calculate the route */
+
+    results[nresults]=CalculateRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node);
+
+    results[nresults]->start_waypoint=start_waypoint;
+    results[nresults]->finish_waypoint=finish_waypoint;
+
+    join_segment=results[nresults]->last_segment;
+
+    nresults++;
+   }
+
+ /* Finish the loop */
+
+ if(loop && finish_node!=NO_NODE)
+   {
+    results[nresults]=CalculateRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,finish_node,join_segment,first_node);
+
+    results[nresults]->start_waypoint=last_waypoint;
+    results[nresults]->finish_waypoint=first_waypoint;
+
+    nresults++;
+   }
+
+ if(!option_quiet)
+   {
+    printf("Routed OK\n");
+    fflush(stdout);
+   }
+
+ /* Print out the combined route */
+
+ if(!option_none)
+    PrintRoute(results,nresults,OSMNodes,OSMSegments,OSMWays,profile);
+
+ /* Destroy the remaining results lists and data structures */
+
+#if 0
+
+ for(waypoint=0;waypoint<=nresults;waypoint++)
+    FreeResultsList(results[waypoint]);
+
+ DestroyNodeList(OSMNodes);
+ DestroySegmentList(OSMSegments);
+ DestroyWayList(OSMWays);
+ DestroyRelationList(OSMRelations);
+
+#endif
+
+ return(0);
+}
+
 
-    begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,&nsuper);
+/*++++++++++++++++++++++++++++++++++++++
+  Find a complete route from a specified node to another node.
+
+  Results *CalculateRoute Returns a set of results.
+
+  Nodes *nodes The set of nodes to use.
+
+  Segments *segments The set of segments to use.
+
+  Ways *ways The set of ways to use.
+
+  Relations *relations The set of relations to use.
+
+  Profile *profile The profile containing the transport type, speeds and allowed highways.
+
+  index_t start_node The start node.
 
-    if(!begin && join_segment!=NO_SEGMENT)
+  index_t prev_segment The previous segment before the start node.
+
+  index_t finish_node The finish node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static Results *CalculateRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t prev_segment,index_t finish_node)
+{
+ Results *begin,*end,*complete=NULL;
+
+ /* Calculate the beginning of the route */
+
+ begin=FindStartRoutes(nodes,segments,ways,relations,profile,start_node,prev_segment,finish_node);
+
+ if(begin)
+   {
+    /* Check if the end of the route was reached */
+
+    if(begin->finish_node!=NO_NODE)
+       complete=ExtendStartRoutes(nodes,segments,ways,relations,profile,begin,finish_node);
+   }
+ else
+   {
+    if(prev_segment!=NO_SEGMENT)
       {
        /* Try again but allow a U-turn at the start waypoint -
           this solves the problem of facing a dead-end that contains no super-nodes. */
 
-       join_segment=NO_SEGMENT;
+       prev_segment=NO_SEGMENT;
 
-       begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,&nsuper);
+       begin=FindStartRoutes(nodes,segments,ways,relations,profile,start_node,prev_segment,finish_node);
       }
 
-    if(!begin)
+    if(begin)
       {
-       fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n");
-       return(1);
-      }
+       /* Check if the end of the route was reached */
 
-    finish_result=FindResult1(begin,finish_node);
-
-    if(nsuper || !finish_result)
+       if(begin->finish_node!=NO_NODE)
+          complete=ExtendStartRoutes(nodes,segments,ways,relations,profile,begin,finish_node);
+      }
+    else
       {
-       /* The route may include super-nodes but there may also be a route
-          without passing any super-nodes to fall back on */
-
-       Results *middle;
+       fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n");
+       exit(EXIT_FAILURE);
+      }
+   }
 
-       /* Calculate the end of the route */
+ /* Calculate the rest of the route */
 
-       end=FindFinishRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,finish_node);
+ if(!complete)
+   {
+    Results *middle;
 
-       if(!end)
-         {
-          fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n");
-          return(1);
-         }
+    /* Calculate the end of the route */
 
-       /* Calculate the middle of the route */
+    end=FindFinishRoutes(nodes,segments,ways,relations,profile,finish_node);
 
-       middle=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,begin,end);
+    if(!end)
+      {
+       fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n");
+       exit(EXIT_FAILURE);
+      }
 
-       if(!middle && join_segment!=NO_SEGMENT && !finish_result)
-         {
-          /* Try again but allow a U-turn at the start waypoint -
-             this solves the problem of facing a dead-end that contains some super-nodes. */
+    /* Calculate the middle of the route */
 
-          FreeResultsList(begin);
+    middle=FindMiddleRoute(nodes,segments,ways,relations,profile,begin,end);
 
-          begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,NO_SEGMENT,finish_node,&nsuper);
+    if(!middle && prev_segment!=NO_SEGMENT)
+      {
+       /* Try again but allow a U-turn at the start waypoint -
+          this solves the problem of facing a dead-end that contains some super-nodes. */
 
-          middle=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,begin,end);
-         }
+       FreeResultsList(begin);
 
-       FreeResultsList(end);
+       begin=FindStartRoutes(nodes,segments,ways,relations,profile,start_node,NO_SEGMENT,finish_node);
 
-       if(!middle)
-         {
-          if(!finish_result)
-            {
-             fprintf(stderr,"Error: Cannot find super-route compatible with profile.\n");
-             return(1);
-            }
-         }
-       else
-         {
-          results[point]=CombineRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,begin,middle);
-
-          if(!results[point])
-            {
-             if(!finish_result)
-               {
-                fprintf(stderr,"Error: Cannot create combined route following super-route.\n");
-                return(1);
-               }
-            }
-
-          if(results[point] && finish_result)
-            {
-             /* If the direct route without passing super-nodes is shorter than
-                the route that does pass super-nodes then fall back to it */
-
-             Result *last_result=FindResult(results[point],results[point]->finish_node,results[point]->last_segment);
-
-             if(last_result->score>finish_result->score)
-               {
-                FreeResultsList(results[point]);
-                results[point]=NULL;
-               }
-            }
-
-          FreeResultsList(middle);
-         }
+       if(begin)
+          middle=FindMiddleRoute(nodes,segments,ways,relations,profile,begin,end);
       }
 
-    if(finish_result && !results[point])
+    FreeResultsList(end);
+
+    if(!middle)
       {
-       /* Use the direct route without passing any super-nodes if there was no
-          other route. */
+       fprintf(stderr,"Error: Cannot find super-route compatible with profile.\n");
+       exit(EXIT_FAILURE);
+      }
 
-       FixForwardRoute(begin,finish_result);
+    complete=CombineRoutes(nodes,segments,ways,relations,profile,begin,middle);
 
-       results[point]=begin;
+    if(!complete)
+      {
+       fprintf(stderr,"Error: Cannot create combined route following super-route.\n");
+       exit(EXIT_FAILURE);
       }
-    else
-       FreeResultsList(begin);
 
-    join_segment=results[point]->last_segment;
-   }
+    FreeResultsList(begin);
 
- if(!option_quiet)
-   {
-    printf("Routed OK\n");
-    fflush(stdout);
+    FreeResultsList(middle);
    }
 
- /* Print out the combined route */
+#if DEBUG
+ Result *r=FindResult(complete,complete->start_node,complete->prev_segment);
 
- if(!option_none)
-    PrintRoute(results,NWAYPOINTS,OSMNodes,OSMSegments,OSMWays,profile);
+ printf("The final route is:\n");
 
- return(0);
+ while(r)
+   {
+    printf("  node=%"Pindex_t" segment=%"Pindex_t" score=%f\n",r->node,r->segment,r->score);
+
+    r=r->next;
+   }
+#endif
+
+ return(complete);
 }
 
 
@@ -647,13 +747,14 @@ static void print_usage(int detail,const char *argerr,const char *err)
          "              [--output-html]\n"
          "              [--output-gpx-track] [--output-gpx-route]\n"
          "              [--output-text] [--output-text-all]\n"
-         "              [--output-none]\n"
+         "              [--output-none] [--output-stdout]\n"
          "              [--profile=<name>]\n"
          "              [--transport=<transport>]\n"
          "              [--shortest | --quickest]\n"
          "              --lon1=<longitude> --lat1=<latitude>\n"
          "              --lon2=<longitude> --lon2=<latitude>\n"
          "              [ ... --lon99=<longitude> --lon99=<latitude>]\n"
+         "              [--reverse] [--loop]\n"
          "              [--highway-<highway>=<preference> ...]\n"
          "              [--speed-<highway>=<speed> ...]\n"
          "              [--property-<property>=<preference> ...]\n"
@@ -704,6 +805,8 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "--output-text-all       Write a plain test file with all route points.\n"
             "--output-none           Don't write any output files or read any translations.\n"
             "                        (If no output option is given then all are written.)\n"
+            "--output-stdout         Write to stdout instead of a file (requires exactly\n"
+            "                        one output format option, implies '--quiet').\n"
             "\n"
             "--profile=<name>        Select the loaded profile with this name.\n"
             "--transport=<transport> Select the transport to use (selects the profile\n"
@@ -715,6 +818,9 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "--lon<n>=<longitude>    Specify the longitude of the n'th waypoint.\n"
             "--lat<n>=<latitude>     Specify the latitude of the n'th waypoint.\n"
             "\n"
+            "--reverse               Find a route between the waypoints in reverse order.\n"
+            "--loop                  Find a route that returns to the first waypoint.\n"
+            "\n"
             "--heading=<bearing>     Initial compass bearing at lowest numbered waypoint.\n"
             "\n"
             "                                   Routing preference options\n"
diff --git a/src/segments.c b/src/segments.c
index 7fb83e5..425af4d 100644
--- a/src/segments.c
+++ b/src/segments.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -28,6 +28,7 @@
 #include "segments.h"
 #include "ways.h"
 
+#include "cache.h"
 #include "fakes.h"
 #include "files.h"
 #include "profiles.h"
@@ -44,9 +45,6 @@
 Segments *LoadSegmentList(const char *filename)
 {
  Segments *segments;
-#if SLIM
- int i;
-#endif
 
  segments=(Segments*)malloc(sizeof(Segments));
 
@@ -64,14 +62,13 @@ Segments *LoadSegmentList(const char *filename)
 
 #else
 
- segments->fd=ReOpenFile(filename);
+ segments->fd=SlimMapFile(filename);
 
  /* Copy the SegmentsFile header structure from the loaded data */
 
- ReadFile(segments->fd,&segments->file,sizeof(SegmentsFile));
+ SlimFetch(segments->fd,&segments->file,sizeof(SegmentsFile),0);
 
- for(i=0;i<sizeof(segments->cached)/sizeof(segments->cached[0]);i++)
-    segments->incache[i]=NO_SEGMENT;
+ segments->cache=NewSegmentCache();
 
 #endif
 
@@ -80,6 +77,30 @@ Segments *LoadSegmentList(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Destroy the segment list.
+
+  Segments *segments The segment list to destroy.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DestroySegmentList(Segments *segments)
+{
+#if !SLIM
+
+ segments->data=UnmapFile(segments->data);
+
+#else
+
+ segments->fd=SlimUnmapFile(segments->fd);
+
+ DeleteSegmentCache(segments->cache);
+
+#endif
+
+ free(segments);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Find the closest segment from a specified node heading in a particular direction and optionally profile.
 
   index_t FindClosestSegmentHeading Returns the closest heading segment index.
@@ -123,9 +144,6 @@ index_t FindClosestSegmentHeading(Nodes *nodes,Segments *segments,Ways *ways,ind
     if(!IsNormalSegment(segmentp))
        goto endloop;
 
-    if(profile->oneway && IsOnewayFrom(segmentp,node1))
-       goto endloop;
-
     if(IsFakeNode(node1) || IsFakeNode(node2))
        seg2=IndexFakeSegment(segmentp);
     else
@@ -136,6 +154,15 @@ index_t FindClosestSegmentHeading(Nodes *nodes,Segments *segments,Ways *ways,ind
     if(!(wayp->allow&profile->allow))
        goto endloop;
 
+    if(profile->oneway && IsOnewayFrom(segmentp,node1))
+      {
+       if(profile->allow!=Transports_Bicycle)
+          goto endloop;
+
+       if(!(wayp->props&Properties_CycleBothWays))
+          goto endloop;
+      }
+
     bearing=BearingAngle(nodes,segmentp,node1);
 
     difference=(heading-bearing);
@@ -270,17 +297,17 @@ double TurnAngle(Nodes *nodes,Segment *segment1p,Segment *segment2p,index_t node
  if(IsFakeNode(node1))
     GetFakeLatLong(node1,&lat1,&lon1);
  else
-    GetLatLong(nodes,node1,&lat1,&lon1);
+    GetLatLong(nodes,node1,NULL,&lat1,&lon1);
 
  if(IsFakeNode(node))
     GetFakeLatLong(node,&latm,&lonm);
  else
-    GetLatLong(nodes,node,&latm,&lonm);
+    GetLatLong(nodes,node,NULL,&latm,&lonm);
 
  if(IsFakeNode(node2))
     GetFakeLatLong(node2,&lat2,&lon2);
  else
-    GetLatLong(nodes,node2,&lat2,&lon2);
+    GetLatLong(nodes,node2,NULL,&lat2,&lon2);
 
  angle1=atan2((lonm-lon1)*cos(latm),(latm-lat1));
  angle2=atan2((lon2-lonm)*cos(latm),(lat2-latm));
@@ -323,12 +350,12 @@ double BearingAngle(Nodes *nodes,Segment *segmentp,index_t node)
  if(IsFakeNode(node1))
     GetFakeLatLong(node1,&lat1,&lon1);
  else
-    GetLatLong(nodes,node1,&lat1,&lon1);
+    GetLatLong(nodes,node1,NULL,&lat1,&lon1);
 
  if(IsFakeNode(node2))
     GetFakeLatLong(node2,&lat2,&lon2);
  else
-    GetLatLong(nodes,node2,&lat2,&lon2);
+    GetLatLong(nodes,node2,NULL,&lat2,&lon2);
 
  angle=atan2((lat2-lat1),(lon2-lon1)*cos(lat1));
 
diff --git a/src/segments.h b/src/segments.h
index f6f70d6..ab0572a 100644
--- a/src/segments.h
+++ b/src/segments.h
@@ -5,7 +5,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -29,6 +29,7 @@
 
 #include "types.h"
 
+#include "cache.h"
 #include "files.h"
 #include "profiles.h"
 
@@ -78,6 +79,8 @@ struct _Segments
  Segment      cached[3];        /*+ Three cached segments read from the file in slim mode. +*/
  index_t      incache[3];       /*+ The indexes of the cached segments. +*/
 
+ SegmentCache *cache;           /*+ A RAM cache of segments read from the file. +*/
+
 #endif
 };
 
@@ -86,6 +89,8 @@ struct _Segments
 
 Segments *LoadSegmentList(const char *filename);
 
+void DestroySegmentList(Segments *segments);
+
 index_t FindClosestSegmentHeading(Nodes *nodes,Segments *segments,Ways *ways,index_t node1,double heading,Profile *profile);
 
 distance_t Distance(double lat1,double lon1,double lat2,double lon2);
@@ -119,6 +124,7 @@ static inline Segment *NextSegment(Segments *segments,Segment *segmentp,index_t
 /*+ Return the other node in the segment that is not the specified node. +*/
 #define OtherNode(xxx,yyy)     ((xxx)->node1==(yyy)?(xxx)->node2:(xxx)->node1)
 
+
 #if !SLIM
 
 /*+ Return a segment pointer given a set of segments and an index. +*/
@@ -162,9 +168,25 @@ static inline Segment *NextSegment(Segments *segments,Segment *segmentp,index_t
 
 #else
 
-static Segment *LookupSegment(Segments *segments,index_t index,int position);
+/* Prototypes */
+
+static inline Segment *LookupSegment(Segments *segments,index_t index,int position);
+
+static inline index_t IndexSegment(Segments *segments,Segment *segmentp);
 
-static index_t IndexSegment(Segments *segments,Segment *segmentp);
+CACHE_NEWCACHE_PROTO(Segment)
+CACHE_DELETECACHE_PROTO(Segment)
+CACHE_FETCHCACHE_PROTO(Segment)
+CACHE_INVALIDATECACHE_PROTO(Segment)
+
+
+/* Inline functions */
+
+CACHE_STRUCTURE(Segment)
+CACHE_NEWCACHE(Segment)
+CACHE_DELETECACHE(Segment)
+CACHE_FETCHCACHE(Segment)
+CACHE_INVALIDATECACHE(Segment)
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -181,12 +203,9 @@ static index_t IndexSegment(Segments *segments,Segment *segmentp);
 
 static inline Segment *LookupSegment(Segments *segments,index_t index,int position)
 {
- if(segments->incache[position-1]!=index)
-   {
-    SeekReadFile(segments->fd,&segments->cached[position-1],sizeof(Segment),sizeof(SegmentsFile)+(off_t)index*sizeof(Segment));
+ segments->cached[position-1]=*FetchCachedSegment(segments->cache,index,segments->fd,sizeof(SegmentsFile));
 
-    segments->incache[position-1]=index;
-   }
+ segments->incache[position-1]=index;
 
  return(&segments->cached[position-1]);
 }
diff --git a/src/segmentsx.c b/src/segmentsx.c
index 130aca2..e70bb46 100644
--- a/src/segmentsx.c
+++ b/src/segmentsx.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -52,11 +52,7 @@ static WaysX *sortwaysx;
 
 /* Local functions */
 
-static int sort_by_way_id(SegmentX *a,SegmentX *b);
-static int apply_changes(SegmentX *segmentx,index_t index);
-
 static int sort_by_id(SegmentX *a,SegmentX *b);
-static int deduplicate(SegmentX *segmentx,index_t index);
 
 static int delete_pruned(SegmentX *segmentx,index_t index);
 
@@ -71,13 +67,9 @@ static distance_t DistanceX(NodeX *nodex1,NodeX *nodex2);
   Allocate a new segment list (create a new file or open an existing one).
 
   SegmentsX *NewSegmentList Returns the segment list.
-
-  int append Set to 1 if the file is to be opened for appending.
-
-  int readonly Set to 1 if the file is to be opened for reading.
   ++++++++++++++++++++++++++++++++++++++*/
 
-SegmentsX *NewSegmentList(int append,int readonly)
+SegmentsX *NewSegmentList(void)
 {
  SegmentsX *segmentsx;
 
@@ -85,30 +77,15 @@ SegmentsX *NewSegmentList(int append,int readonly)
 
  logassert(segmentsx,"Failed to allocate memory (try using slim mode?)"); /* Check calloc() worked */
 
- segmentsx->filename    =(char*)malloc(strlen(option_tmpdirname)+32);
  segmentsx->filename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
 
- sprintf(segmentsx->filename    ,"%s/segmentsx.parsed.mem",option_tmpdirname);
- sprintf(segmentsx->filename_tmp,"%s/segmentsx.%p.tmp"    ,option_tmpdirname,(void*)segmentsx);
-
- if(append || readonly)
-    if(ExistsFile(segmentsx->filename))
-      {
-       off_t size;
-
-       size=SizeFile(segmentsx->filename);
+ sprintf(segmentsx->filename_tmp,"%s/segmentsx.%p.tmp",option_tmpdirname,(void*)segmentsx);
 
-       segmentsx->number=size/sizeof(SegmentX);
-
-       RenameFile(segmentsx->filename,segmentsx->filename_tmp);
-      }
+ segmentsx->fd=OpenFileBufferedNew(segmentsx->filename_tmp);
 
- if(append)
-    segmentsx->fd=OpenFileAppend(segmentsx->filename_tmp);
- else if(!readonly)
-    segmentsx->fd=OpenFileNew(segmentsx->filename_tmp);
- else
-    segmentsx->fd=-1;
+#if SLIM
+ segmentsx->cache=NewSegmentXCache();
+#endif
 
  return(segmentsx);
 }
@@ -118,18 +95,12 @@ SegmentsX *NewSegmentList(int append,int readonly)
   Free a segment list.
 
   SegmentsX *segmentsx The set of segments to be freed.
-
-  int keep If set then the results file is to be kept.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void FreeSegmentList(SegmentsX *segmentsx,int keep)
+void FreeSegmentList(SegmentsX *segmentsx)
 {
- if(keep)
-    RenameFile(segmentsx->filename_tmp,segmentsx->filename);
- else
-    DeleteFile(segmentsx->filename_tmp);
+ DeleteFile(segmentsx->filename_tmp);
 
- free(segmentsx->filename);
  free(segmentsx->filename_tmp);
 
  if(segmentsx->firstnode)
@@ -138,8 +109,9 @@ void FreeSegmentList(SegmentsX *segmentsx,int keep)
  if(segmentsx->next1)
     free(segmentsx->next1);
 
- if(segmentsx->usednode)
-    free(segmentsx->usednode);
+#if SLIM
+ DeleteSegmentXCache(segmentsx->cache);
+#endif
 
  free(segmentsx);
 }
@@ -150,22 +122,22 @@ void FreeSegmentList(SegmentsX *segmentsx,int keep)
 
   SegmentsX *segmentsx The set of segments to modify.
 
-  way_t way The way that the segment belongs to.
+  index_t way The index of the way that the segment belongs to.
 
-  node_t node1 The first node in the segment.
+  index_t node1 The index of the first node in the segment.
 
-  node_t node2 The second node in the segment.
+  index_t node2 The index of the second node in the segment.
 
   distance_t distance The distance between the nodes (or just the flags).
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendSegmentList(SegmentsX *segmentsx,way_t way,node_t node1,node_t node2,distance_t distance)
+void AppendSegmentList(SegmentsX *segmentsx,index_t way,index_t node1,index_t node2,distance_t distance)
 {
  SegmentX segmentx;
 
  if(node1>node2)
    {
-    node_t temp;
+    index_t temp;
 
     temp=node1;
     node1=node2;
@@ -181,7 +153,7 @@ void AppendSegmentList(SegmentsX *segmentsx,way_t way,node_t node1,node_t node2,
  segmentx.way=way;
  segmentx.distance=distance;
 
- WriteFile(segmentsx->fd,&segmentx,sizeof(SegmentX));
+ WriteFileBuffered(segmentsx->fd,&segmentx,sizeof(SegmentX));
 
  segmentsx->number++;
 
@@ -198,7 +170,7 @@ void AppendSegmentList(SegmentsX *segmentsx,way_t way,node_t node1,node_t node2,
 void FinishSegmentList(SegmentsX *segmentsx)
 {
  if(segmentsx->fd!=-1)
-    segmentsx->fd=CloseFile(segmentsx->fd);
+    segmentsx->fd=CloseFileBuffered(segmentsx->fd);
 }
 
 
@@ -293,113 +265,14 @@ SegmentX *NextSegmentX(SegmentsX *segmentsx,SegmentX *segmentx,index_t nodeindex
  
  
 /*++++++++++++++++++++++++++++++++++++++
-  Apply the changes to the segments (no unique id to use).
-
-  SegmentsX *segmentsx The set of segments to sort and modify.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void ApplySegmentChanges(SegmentsX *segmentsx)
-{
- int fd;
- index_t xnumber;
-
- /* Print the start message */
-
- printf_first("Applying Segment Changes");
-
- /* Re-open the file read-only and a new file writeable */
-
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
-
- DeleteFile(segmentsx->filename_tmp);
-
- fd=OpenFileNew(segmentsx->filename_tmp);
-
- /* Sort by node indexes */
-
- xnumber=segmentsx->number;
-
- segmentsx->number=filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),NULL,
-                                                                    (int (*)(const void*,const void*))sort_by_way_id,
-                                                                    (int (*)(void*,index_t))apply_changes);
-
- /* Close the files */
-
- segmentsx->fd=CloseFile(segmentsx->fd);
- CloseFile(fd);
-
- /* Print the final message */
-
- printf_last("Applying Segment Changes: Segments=%"Pindex_t" Changed=%"Pindex_t,xnumber,xnumber-segmentsx->number);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Sort the segments into way id order.
-
-  int sort_by_way_id Returns the comparison of the way fields.
-
-  SegmentX *a The first segment.
-
-  SegmentX *b The second segment.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int sort_by_way_id(SegmentX *a,SegmentX *b)
-{
- way_t a_id=a->way;
- way_t b_id=b->way;
-
- if(a_id<b_id)
-    return(-1);
- else if(a_id>b_id)
-    return(1);
- else /* if(a_id==b_id) */
-    return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Apply the changes to the segments.
-
-  int apply_changes Return 1 if the value is to be kept, otherwise 0.
+  Sort the segment list.
 
-  SegmentX *segmentx The extended segment.
-
-  index_t index The number of sorted segments that have already been written to the output file.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int apply_changes(SegmentX *segmentx,index_t index)
-{
- static way_t prevway=NO_WAY_ID;
- static int deleted=0;
-
- if(prevway!=segmentx->way)
-   {
-    prevway=segmentx->way;
-    deleted=0;
-   }
-
- if(!deleted)
-    if(segmentx->node1==NO_NODE_ID)
-       deleted=1;
-
- if(deleted)
-    return(0);
- else
-    return(1);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Sort the segment list and deduplicate it.
-
-  SegmentsX *segmentsx The set of segments to sort and modify.
+  SegmentsX *segmentsx The set of segments to sort.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void SortSegmentList(SegmentsX *segmentsx)
 {
  int fd;
- index_t xnumber;
 
  /* Print the start message */
 
@@ -407,28 +280,26 @@ void SortSegmentList(SegmentsX *segmentsx)
 
  /* Re-open the file read-only and a new file writeable */
 
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
 
  DeleteFile(segmentsx->filename_tmp);
 
- fd=OpenFileNew(segmentsx->filename_tmp);
+ fd=OpenFileBufferedNew(segmentsx->filename_tmp);
 
  /* Sort by node indexes */
 
- xnumber=segmentsx->number;
-
  segmentsx->number=filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),NULL,
                                                                     (int (*)(const void*,const void*))sort_by_id,
-                                                                    (int (*)(void*,index_t))deduplicate);
+                                                                    NULL);
 
  /* Close the files */
 
- segmentsx->fd=CloseFile(segmentsx->fd);
- CloseFile(fd);
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
- printf_last("Sorted Segments: Segments=%"Pindex_t" Duplicates=%"Pindex_t,xnumber,xnumber-segmentsx->number);
+ printf_last("Sorted Segments: Segments=%"Pindex_t,segmentsx->number);
 }
 
 
@@ -444,8 +315,8 @@ void SortSegmentList(SegmentsX *segmentsx)
 
 static int sort_by_id(SegmentX *a,SegmentX *b)
 {
- node_t a_id1=a->node1;
- node_t b_id1=b->node1;
+ index_t a_id1=a->node1;
+ index_t b_id1=b->node1;
 
  if(a_id1<b_id1)
     return(-1);
@@ -453,8 +324,8 @@ static int sort_by_id(SegmentX *a,SegmentX *b)
     return(1);
  else /* if(a_id1==b_id1) */
    {
-    node_t a_id2=a->node2;
-    node_t b_id2=b->node2;
+    index_t a_id2=a->node2;
+    index_t b_id2=b->node2;
 
     if(a_id2<b_id2)
        return(-1);
@@ -487,268 +358,136 @@ static int sort_by_id(SegmentX *a,SegmentX *b)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Discard duplicate segments.
-
-  int deduplicate Return 1 if the value is to be kept, otherwise 0.
-
-  SegmentX *segmentx The extended segment.
-
-  index_t index The number of sorted segments that have already been written to the output file.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int deduplicate(SegmentX *segmentx,index_t index)
-{
- static node_t prevnode1=NO_NODE_ID,prevnode2=NO_NODE_ID;
- static way_t prevway=NO_WAY_ID;
- static distance_t prevdist=0;
-
- if(prevnode1!=segmentx->node1 || prevnode2!=segmentx->node2 || prevway!=segmentx->way || prevdist!=segmentx->distance)
-   {
-    prevnode1=segmentx->node1;
-    prevnode2=segmentx->node2;
-    prevway=segmentx->way;
-    prevdist=segmentx->distance;
-
-    return(1);
-   }
- else
-    return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Remove bad segments (duplicated, zero length or with missing nodes).
+  Process segments (non-trivial duplicates).
 
   SegmentsX *segmentsx The set of segments to modify.
 
   NodesX *nodesx The set of nodes to use.
 
   WaysX *waysx The set of ways to use.
-
-  int keep If set to 1 then keep the old data file otherwise delete it.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void RemoveBadSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx,int keep)
+void ProcessSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 {
- index_t noway=0,loop=0,nonode=0,duplicate=0,good=0,total=0;
- node_t prevnode1=NO_NODE_ID,prevnode2=NO_NODE_ID;
- way_t prevway=NO_WAY_ID;
+ index_t duplicate=0,good=0,total=0;
+ index_t prevnode1=NO_NODE,prevnode2=NO_NODE;
+ index_t prevway=NO_WAY;
  distance_t prevdist=0;
  SegmentX segmentx;
  int fd;
 
  /* Print the start message */
 
- printf_first("Checking Segments: Segments=0 Loop=0 No-Way=0 No-Node=0 Duplicate=0");
-
- /* Allocate the node usage bitmask */
-
- segmentsx->usednode=AllocBitMask(nodesx->number);
+ printf_first("Processing Segments: Segments=0 Duplicates=0");
 
- logassert(segmentsx->usednode,"Failed to allocate memory (try using slim mode?)"); /* Check AllocBitMask() worked */
+ /* Map into memory /  open the file */
 
- /* Re-open the file read-only and a new file writeable */
+#if !SLIM
+ nodesx->data=MapFile(nodesx->filename_tmp);
+ waysx->data=MapFile(waysx->filename_tmp);
+#else
+ nodesx->fd=SlimMapFile(nodesx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
 
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ InvalidateNodeXCache(nodesx->cache);
+ InvalidateWayXCache(waysx->cache);
+#endif
 
- if(keep)
-    RenameFile(segmentsx->filename_tmp,segmentsx->filename);
- else
-    DeleteFile(segmentsx->filename_tmp);
+ /* Allocate the way usage bitmask */
 
- fd=OpenFileNew(segmentsx->filename_tmp);
+ segmentsx->usedway=AllocBitMask(waysx->number);
 
- /* Modify the on-disk image */
+ logassert(segmentsx->usedway,"Failed to allocate memory (try using slim mode?)"); /* Check AllocBitMask() worked */
 
- while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
-   {
-    index_t index1=IndexNodeX(nodesx,segmentx.node1);
-    index_t index2=IndexNodeX(nodesx,segmentx.node2);
-    index_t indexw=IndexWayX(waysx,segmentx.way);
+ /* Re-open the file read-only and a new file writeable */
 
-    if(indexw==NO_WAY)
-      {
-       logerror("Segment belongs to way %"Pway_t" but it doesn't exist.\n",segmentx.way);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
 
-       noway++;
-      }
-    else if(segmentx.node1==segmentx.node2)
-      {
-       logerror("Segment connects node %"Pnode_t" to itself.\n",segmentx.node1);
+ DeleteFile(segmentsx->filename_tmp);
 
-       loop++;
-      }
-    else if(index1==NO_NODE || index2==NO_NODE)
-      {
-       if(index1==NO_NODE && index2==NO_NODE)
-          logerror("Segment connects nodes %"Pnode_t" and %"Pnode_t" but neither exist.\n",segmentx.node1,segmentx.node2);
+ fd=OpenFileBufferedNew(segmentsx->filename_tmp);
 
-       if(index1==NO_NODE && index2!=NO_NODE)
-          logerror("Segment connects nodes %"Pnode_t" and %"Pnode_t" but the first one does not exist.\n",segmentx.node1,segmentx.node2);
+ /* Modify the on-disk image */
 
-       if(index1!=NO_NODE && index2==NO_NODE)
-          logerror("Segment connects nodes %"Pnode_t" and %"Pnode_t" but the second one does not exist.\n",segmentx.node1,segmentx.node2);
+ while(!ReadFileBuffered(segmentsx->fd,&segmentx,sizeof(SegmentX)))
+   {
+    NodeX *nodex1=LookupNodeX(nodesx,segmentx.node1,1);
+    NodeX *nodex2=LookupNodeX(nodesx,segmentx.node2,2);
 
-       nonode++;
-      }
-    else if(prevnode1==segmentx.node1 && prevnode2==segmentx.node2)
+    if(prevnode1==segmentx.node1 && prevnode2==segmentx.node2)
       {
        if(prevway==segmentx.way)
-          ; /* already logged an error - only possible to get here for oneway opposite direction segments */
+         {
+          WayX *wayx=LookupWayX(waysx,segmentx.way,1);
+
+          logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" in way %"Pway_t" is duplicated.\n",logerror_node(nodex1->id),logerror_node(nodex2->id),logerror_way(wayx->id));
+         }
        else
          {
           if(!(prevdist&SEGMENT_AREA) && !(segmentx.distance&SEGMENT_AREA))
-             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated.\n",segmentx.node1,segmentx.node2);
+             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated.\n",logerror_node(nodex1->id),logerror_node(nodex2->id));
 
           if(!(prevdist&SEGMENT_AREA) && (segmentx.distance&SEGMENT_AREA))
-             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (discarded the area).\n",segmentx.node1,segmentx.node2);
+             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (discarded the area).\n",logerror_node(nodex1->id),logerror_node(nodex2->id));
 
           if((prevdist&SEGMENT_AREA) && !(segmentx.distance&SEGMENT_AREA))
-             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (discarded the non-area).\n",segmentx.node1,segmentx.node2);
+             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (discarded the non-area).\n",logerror_node(nodex1->id),logerror_node(nodex2->id));
 
           if((prevdist&SEGMENT_AREA) && (segmentx.distance&SEGMENT_AREA))
-             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (both are areas).\n",segmentx.node1,segmentx.node2);
+             logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (both are areas).\n",logerror_node(nodex1->id),logerror_node(nodex2->id));
          }
 
        duplicate++;
       }
     else
       {
-       WriteFile(fd,&segmentx,sizeof(SegmentX));
-
-       SetBit(segmentsx->usednode,index1);
-       SetBit(segmentsx->usednode,index2);
-
        prevnode1=segmentx.node1;
        prevnode2=segmentx.node2;
        prevway=segmentx.way;
        prevdist=DISTANCE(segmentx.distance);
 
-       good++;
-      }
-
-    total++;
-
-    if(!(total%10000))
-       printf_middle("Checking Segments: Segments=%"Pindex_t" Loop=%"Pindex_t" No-Way=%"Pindex_t" No-Node=%"Pindex_t" Duplicate=%"Pindex_t,total,loop,noway,nonode,duplicate);
-   }
-
- segmentsx->number=good;
-
- /* Close the files */
-
- segmentsx->fd=CloseFile(segmentsx->fd);
- CloseFile(fd);
-
- /* Print the final message */
-
- printf_last("Checked Segments: Segments=%"Pindex_t" Loop=%"Pindex_t" No-Way=%"Pindex_t" No-Node=%"Pindex_t" Duplicate=%"Pindex_t,total,loop,noway,nonode,duplicate);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Measure the segments and replace node/way ids with indexes.
-
-  SegmentsX *segmentsx The set of segments to process.
-
-  NodesX *nodesx The set of nodes to use.
-
-  WaysX *waysx The set of ways to use.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void MeasureSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
-{
- index_t index=0;
- int fd;
- SegmentX segmentx;
-
- if(segmentsx->number==0)
-    return;
-
- /* Print the start message */
+       /* Mark the ways which are used */
 
- printf_first("Measuring Segments: Segments=0");
+       SetBit(segmentsx->usedway,segmentx.way);
 
- /* Map into memory /  open the file */
+       /* Set the distance but keep the other flags except for area */
 
-#if !SLIM
- nodesx->data=MapFile(nodesx->filename_tmp);
-#else
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
-#endif
+       segmentx.distance=DISTANCE(DistanceX(nodex1,nodex2))|DISTFLAG(segmentx.distance);
+       segmentx.distance&=~SEGMENT_AREA;
 
- /* Allocate the way usage bitmask */
+       /* Write the modified segment */
 
- segmentsx->usedway=AllocBitMask(waysx->number);
-
- logassert(segmentsx->usedway,"Failed to allocate memory (try using slim mode?)"); /* Check AllocBitMask() worked */
+       WriteFileBuffered(fd,&segmentx,sizeof(SegmentX));
 
- /* Re-open the file read-only and a new file writeable */
-
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
-
- DeleteFile(segmentsx->filename_tmp);
-
- fd=OpenFileNew(segmentsx->filename_tmp);
-
- /* Modify the on-disk image */
-
- while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
-   {
-    index_t node1=IndexNodeX(nodesx,segmentx.node1);
-    index_t node2=IndexNodeX(nodesx,segmentx.node2);
-    index_t way  =IndexWayX (waysx ,segmentx.way);
-
-    NodeX *nodex1=LookupNodeX(nodesx,node1,1);
-    NodeX *nodex2=LookupNodeX(nodesx,node2,2);
-
-    /* Replace the node and way ids with their indexes */
-
-    segmentx.node1=node1;
-    segmentx.node2=node2;
-    segmentx.way  =way;
-
-    SetBit(segmentsx->usedway,segmentx.way);
-
-    /* Set the distance but keep the other flags except for area */
-
-    segmentx.distance=DISTANCE(DistanceX(nodex1,nodex2))|DISTFLAG(segmentx.distance);
-    segmentx.distance&=~SEGMENT_AREA;
-
-    /* Write the modified segment */
-
-    WriteFile(fd,&segmentx,sizeof(SegmentX));
+       good++;
+      }
 
-    index++;
+    total++;
 
-    if(!(index%10000))
-       printf_middle("Measuring Segments: Segments=%"Pindex_t,index);
+    if(!(total%10000))
+       printf_middle("Processing Segments: Segments=%"Pindex_t" Duplicates=%"Pindex_t,total,duplicate);
    }
 
- /* Close the files */
-
- segmentsx->fd=CloseFile(segmentsx->fd);
- CloseFile(fd);
-
- /* Free the other now-unneeded indexes */
+ segmentsx->number=good;
 
- free(nodesx->idata);
- nodesx->idata=NULL;
+ /* Close the files */
 
- free(waysx->idata);
- waysx->idata=NULL;
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
+ CloseFileBuffered(fd);
 
  /* Unmap from memory / close the file */
 
 #if !SLIM
  nodesx->data=UnmapFile(nodesx->data);
+ waysx->data=UnmapFile(waysx->data);
 #else
- nodesx->fd=CloseFile(nodesx->fd);
+ nodesx->fd=SlimUnmapFile(nodesx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Measured Segments: Segments=%"Pindex_t,segmentsx->number);
+ printf_last("Processed Segments: Segments=%"Pindex_t" Duplicates=%"Pindex_t,total,duplicate);
 }
 
 
@@ -764,7 +503,11 @@ void MeasureSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 
 void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 {
- index_t index,i;
+ index_t index,start=0,i;
+ SegmentX *segmentx_list=NULL;
+#if SLIM
+ index_t length=0;
+#endif
 
  if(segmentsx->number==0)
     return;
@@ -790,14 +533,31 @@ void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 #if !SLIM
  segmentsx->data=MapFileWriteable(segmentsx->filename_tmp);
 #else
- segmentsx->fd=ReOpenFileWriteable(segmentsx->filename_tmp);
+ segmentsx->fd=SlimMapFileWriteable(segmentsx->filename_tmp);
+
+ segmentx_list=(SegmentX*)malloc(1024*sizeof(SegmentX));
 #endif
 
- /* Read through the segments in reverse order */
+ /* Read through the segments in reverse order (in chunks to help slim mode) */
 
  for(index=segmentsx->number-1;index!=NO_SEGMENT;index--)
    {
-    SegmentX *segmentx=LookupSegmentX(segmentsx,index,1);
+    SegmentX *segmentx;
+
+    if((index%1024)==1023 || index==(segmentsx->number-1))
+      {
+       start=1024*(index/1024);
+
+#if !SLIM
+       segmentx_list=LookupSegmentX(segmentsx,start,1);
+#else
+       length=index-start+1;
+
+       SlimFetch(segmentsx->fd,segmentx_list,length*sizeof(SegmentX),start*sizeof(SegmentX));
+#endif
+      }
+
+    segmentx=segmentx_list+(index-start);
 
     if(nodesx->pdata)
       {
@@ -810,13 +570,16 @@ void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 
     segmentx->next2=segmentsx->firstnode[segmentx->node2];
 
-    PutBackSegmentX(segmentsx,segmentx);
-
     segmentsx->firstnode[segmentx->node1]=index;
     segmentsx->firstnode[segmentx->node2]=index;
 
     if(!(index%10000))
        printf_middle("Indexing Segments: Segments=%"Pindex_t,segmentsx->number-index);
+
+#if SLIM
+    if(index==start)
+       SlimReplace(segmentsx->fd,segmentx_list,length*sizeof(SegmentX),start*sizeof(SegmentX));
+#endif
    }
 
  /* Unmap from memory / close the files */
@@ -824,7 +587,9 @@ void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 #if !SLIM
  segmentsx->data=UnmapFile(segmentsx->data);
 #else
- segmentsx->fd=CloseFile(segmentsx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
+
+ free(segmentx_list);
 #endif
 
  /* Free the memory */
@@ -875,11 +640,11 @@ void RemovePrunedSegments(SegmentsX *segmentsx,WaysX *waysx)
 
  /* Re-open the file read-only and a new file writeable */
 
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
 
  DeleteFile(segmentsx->filename_tmp);
 
- fd=OpenFileNew(segmentsx->filename_tmp);
+ fd=OpenFileBufferedNew(segmentsx->filename_tmp);
 
  /* Sort by node indexes */
 
@@ -893,8 +658,8 @@ void RemovePrunedSegments(SegmentsX *segmentsx,WaysX *waysx)
 
  /* Close the files */
 
- segmentsx->fd=CloseFile(segmentsx->fd);
- CloseFile(fd);
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
@@ -948,16 +713,18 @@ void DeduplicateSuperSegments(SegmentsX *segmentsx,WaysX *waysx)
 #if !SLIM
  waysx->data=MapFile(waysx->filename_tmp);
 #else
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
+
+ InvalidateWayXCache(waysx->cache);
 #endif
 
  /* Re-open the file read-only and a new file writeable */
 
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
 
  DeleteFile(segmentsx->filename_tmp);
 
- fd=OpenFileNew(segmentsx->filename_tmp);
+ fd=OpenFileBufferedNew(segmentsx->filename_tmp);
 
  /* Sort by node indexes */
 
@@ -972,15 +739,15 @@ void DeduplicateSuperSegments(SegmentsX *segmentsx,WaysX *waysx)
 
  /* Close the files */
 
- segmentsx->fd=CloseFile(segmentsx->fd);
- CloseFile(fd);
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
+ CloseFileBuffered(fd);
 
  /* Unmap from memory / close the file */
 
 #if !SLIM
  waysx->data=UnmapFile(waysx->data);
 #else
- waysx->fd=CloseFile(waysx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
  /* Print the final message */
@@ -1077,11 +844,11 @@ void SortSegmentListGeographically(SegmentsX *segmentsx,NodesX *nodesx)
 
  /* Re-open the file read-only and a new file writeable */
 
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
 
  DeleteFile(segmentsx->filename_tmp);
 
- fd=OpenFileNew(segmentsx->filename_tmp);
+ fd=OpenFileBufferedNew(segmentsx->filename_tmp);
 
  /* Update the segments with geographically sorted node indexes and sort them */
 
@@ -1092,8 +859,8 @@ void SortSegmentListGeographically(SegmentsX *segmentsx,NodesX *nodesx)
                                                   NULL);
  /* Close the files */
 
- segmentsx->fd=CloseFile(segmentsx->fd);
- CloseFile(fd);
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
@@ -1153,20 +920,20 @@ void SaveSegmentList(SegmentsX *segmentsx,const char *filename)
 
  /* Re-open the file */
 
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
 
  /* Write out the segments data */
 
- fd=OpenFileNew(filename);
+ fd=OpenFileBufferedNew(filename);
 
- SeekFile(fd,sizeof(SegmentsFile));
+ SeekFileBuffered(fd,sizeof(SegmentsFile));
 
  for(i=0;i<segmentsx->number;i++)
    {
     SegmentX segmentx;
     Segment  segment={0};
 
-    ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX));
+    ReadFileBuffered(segmentsx->fd,&segmentx,sizeof(SegmentX));
 
     segment.node1   =segmentx.node1;
     segment.node2   =segmentx.node2;
@@ -1179,7 +946,7 @@ void SaveSegmentList(SegmentsX *segmentsx,const char *filename)
     if(IsNormalSegment(&segment))
        normal_number++;
 
-    WriteFile(fd,&segment,sizeof(Segment));
+    WriteFileBuffered(fd,&segment,sizeof(Segment));
 
     if(!((i+1)%10000))
        printf_middle("Writing Segments: Segments=%"Pindex_t,i+1);
@@ -1191,14 +958,14 @@ void SaveSegmentList(SegmentsX *segmentsx,const char *filename)
  segmentsfile.snumber=super_number;
  segmentsfile.nnumber=normal_number;
 
- SeekFile(fd,0);
- WriteFile(fd,&segmentsfile,sizeof(SegmentsFile));
+ SeekFileBuffered(fd,0);
+ WriteFileBuffered(fd,&segmentsfile,sizeof(SegmentsFile));
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 
  /* Close the file */
 
- segmentsx->fd=CloseFile(segmentsx->fd);
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
 
  /* Print the final message */
 
diff --git a/src/segmentsx.h b/src/segmentsx.h
index 0071f20..df04c62 100644
--- a/src/segmentsx.h
+++ b/src/segmentsx.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -29,6 +29,7 @@
 
 #include "typesx.h"
 
+#include "cache.h"
 #include "files.h"
 
 
@@ -38,12 +39,12 @@
 /*+ An extended structure used for processing. +*/
 struct _SegmentX
 {
- node_t     node1;              /*+ The id of the starting node; initially the OSM value, later the NodeX index. +*/
- node_t     node2;              /*+ The id of the finishing node; initially the OSM value, later the NodeX index. +*/
+ index_t    node1;              /*+ The NodeX index of the starting node. +*/
+ index_t    node2;              /*+ The NodeX index of the finishing node. +*/
 
  index_t    next2;              /*+ The index of the next segment with the same node2. +*/
 
- way_t      way;                /*+ The id of the way; initially the OSM value, later the WayX index. +*/
+ index_t    way;                /*+ The WayX index of the way. +*/
 
  distance_t distance;           /*+ The distance between the nodes. +*/
 };
@@ -52,7 +53,6 @@ struct _SegmentX
 /*+ A structure containing a set of segments (memory format). +*/
 struct _SegmentsX
 {
- char      *filename;           /*+ The name of the intermediate file (for the SegmentsX). +*/
  char      *filename_tmp;       /*+ The name of the temporary file (for the SegmentsX). +*/
 
  int        fd;                 /*+ The file descriptor of the open file (for the SegmentsX). +*/
@@ -68,38 +68,34 @@ struct _SegmentsX
  SegmentX   cached[4];          /*+ Four cached extended segments read from the file in slim mode. +*/
  index_t    incache[4];         /*+ The indexes of the cached extended segments. +*/
 
+ SegmentXCache *cache;          /*+ A RAM cache of extended segments read from the file. +*/
+
 #endif
 
  index_t   *firstnode;          /*+ The first segment index for each node. +*/
 
  index_t   *next1;              /*+ The index of the next segment with the same node1 (used while pruning). +*/
 
- BitMask   *usednode;           /*+ A flag to indicate if a node is used (used for removing bad segments). +*/
-
  BitMask   *usedway;            /*+ A flag to indicate if a way is used (used for removing pruned ways). +*/
 };
 
 
 /* Functions in segmentsx.c */
 
-SegmentsX *NewSegmentList(int append,int readonly);
-void FreeSegmentList(SegmentsX *segmentsx,int keep);
+SegmentsX *NewSegmentList(void);
+void FreeSegmentList(SegmentsX *segmentsx);
 
-void AppendSegmentList(SegmentsX *segmentsx,way_t way,node_t node1,node_t node2,distance_t distance);
+void AppendSegmentList(SegmentsX *segmentsx,index_t way,index_t node1,index_t node2,distance_t distance);
 void FinishSegmentList(SegmentsX *segmentsx);
 
 SegmentX *FirstSegmentX(SegmentsX *segmentsx,index_t nodeindex,int position);
 SegmentX *NextSegmentX(SegmentsX *segmentsx,SegmentX *segmentx,index_t nodeindex);
 
-void ApplySegmentChanges(SegmentsX *segmentsx);
-
 void SortSegmentList(SegmentsX *segmentsx);
 
 void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx);
 
-void RemoveBadSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx,int keep);
-
-void MeasureSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx);
+void ProcessSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx);
 
 void RemovePrunedSegments(SegmentsX *segmentsx,WaysX *waysx);
 
@@ -122,19 +118,37 @@ void SaveSegmentList(SegmentsX *segmentsx,const char *filename);
 
 #define IndexSegmentX(segmentsx,segmentx)                (index_t)((segmentx)-&(segmentsx)->data[0])
 
-#define PutBackSegmentX(segmentsx,segmentx)              /* nop */
+#define PutBackSegmentX(segmentsx,segmentx)              while(0) { /* nop */ }
 
-#define ReLookupSegmentX(segmentsx,segmentx)             /* nop */
+#define ReLookupSegmentX(segmentsx,segmentx)             while(0) { /* nop */ }
   
 #else
 
-static SegmentX *LookupSegmentX(SegmentsX *segmentsx,index_t index,int position);
+/* Prototypes */
+
+static inline SegmentX *LookupSegmentX(SegmentsX *segmentsx,index_t index,int position);
+
+static inline index_t IndexSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
+
+static inline void PutBackSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
+
+static inline void ReLookupSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
+
+CACHE_NEWCACHE_PROTO(SegmentX)
+CACHE_DELETECACHE_PROTO(SegmentX)
+CACHE_FETCHCACHE_PROTO(SegmentX)
+CACHE_REPLACECACHE_PROTO(SegmentX)
+CACHE_INVALIDATECACHE_PROTO(SegmentX)
 
-static index_t IndexSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
 
-static void PutBackSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
+/* Inline functions */
 
-static void ReLookupSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
+CACHE_STRUCTURE(SegmentX)
+CACHE_NEWCACHE(SegmentX)
+CACHE_DELETECACHE(SegmentX)
+CACHE_FETCHCACHE(SegmentX)
+CACHE_REPLACECACHE(SegmentX)
+CACHE_INVALIDATECACHE(SegmentX)
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -151,7 +165,7 @@ static void ReLookupSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
 
 static inline SegmentX *LookupSegmentX(SegmentsX *segmentsx,index_t index,int position)
 {
- SeekReadFile(segmentsx->fd,&segmentsx->cached[position-1],sizeof(SegmentX),(off_t)index*sizeof(SegmentX));
+ segmentsx->cached[position-1]=*FetchCachedSegmentX(segmentsx->cache,index,segmentsx->fd,0);
 
  segmentsx->incache[position-1]=index;
 
@@ -189,7 +203,7 @@ static inline void PutBackSegmentX(SegmentsX *segmentsx,SegmentX *segmentx)
 {
  int position1=segmentx-&segmentsx->cached[0];
 
- SeekWriteFile(segmentsx->fd,&segmentsx->cached[position1],sizeof(SegmentX),(off_t)segmentsx->incache[position1]*sizeof(SegmentX));
+ ReplaceCachedSegmentX(segmentsx->cache,segmentx,segmentsx->incache[position1],segmentsx->fd,0);
 }
 
 
@@ -205,7 +219,7 @@ static inline void ReLookupSegmentX(SegmentsX *segmentsx,SegmentX *segmentx)
 {
  int position1=segmentx-&segmentsx->cached[0];
 
- SeekReadFile(segmentsx->fd,&segmentsx->cached[position1],sizeof(SegmentX),(off_t)segmentsx->incache[position1]*sizeof(SegmentX));
+ segmentsx->cached[position1]=*FetchCachedSegmentX(segmentsx->cache,segmentsx->incache[position1],segmentsx->fd,0);
 }
 
 #endif /* SLIM */
diff --git a/src/sorting.c b/src/sorting.c
index 2562db8..51ec0bb 100644
--- a/src/sorting.c
+++ b/src/sorting.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2009-2012 Andrew M. Bishop
+ This file Copyright 2009-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -124,6 +124,7 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
  size_t nitems=option_filesort_ramsize/(option_filesort_threads*(itemsize+sizeof(void*)));
  void *data,**datap;
  thread_data *threads;
+ size_t item;
  int i,more=1;
 #if defined(USE_PTHREADS) && USE_PTHREADS
  int nthreads=0;
@@ -171,26 +172,26 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
 
     /* Read in the data and create pointers */
 
-    for(i=0;i<nitems;)
+    for(item=0;item<nitems;)
       {
-       threads[thread].datap[i]=threads[thread].data+i*itemsize;
+       threads[thread].datap[item]=threads[thread].data+item*itemsize;
 
-       if(ReadFile(fd_in,threads[thread].datap[i],itemsize))
+       if(ReadFileBuffered(fd_in,threads[thread].datap[item],itemsize))
          {
           more=0;
           break;
          }
 
-       if(!pre_sort_function || pre_sort_function(threads[thread].datap[i],count_in))
+       if(!pre_sort_function || pre_sort_function(threads[thread].datap[item],count_in))
          {
-          i++;
+          item++;
           total++;
          }
 
        count_in++;
       }
 
-    threads[thread].n=i;
+    threads[thread].n=item;
 
     /* Shortcut if there is no previous data and no more data (i.e. no data at all) */
 
@@ -283,11 +284,11 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
 
  if(nfiles==1)
    {
-    for(i=0;i<threads[0].n;i++)
+    for(item=0;item<threads[0].n;item++)
       {
-       if(!post_sort_function || post_sort_function(threads[0].datap[i],count_out))
+       if(!post_sort_function || post_sort_function(threads[0].datap[item],count_out))
          {
-          WriteFile(fd_out,threads[0].datap[i],itemsize);
+          WriteFileBuffered(fd_out,threads[0].datap[item],itemsize);
           count_out++;
          }
       }
@@ -299,7 +300,7 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
 
  /* Check that number of files is less than file size */
 
- logassert(nfiles<nitems,"Too many temporary files (use more sorting memory?)");
+ logassert((unsigned)nfiles<nitems,"Too many temporary files (use more sorting memory?)");
 
  /* Open all of the temporary files */
 
@@ -311,7 +312,7 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
 
     sprintf(filename,"%s/filesort.%d.tmp",option_tmpdirname,i);
 
-    fds[i]=ReOpenFile(filename);
+    fds[i]=ReOpenFileBuffered(filename);
 
     DeleteFile(filename);
    }
@@ -331,7 +332,7 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
 
     datap[i]=data+i*itemsize;
 
-    ReadFile(fds[i],datap[i],itemsize);
+    ReadFileBuffered(fds[i],datap[i],itemsize);
 
     index=i+1;
 
@@ -367,11 +368,11 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
 
     if(!post_sort_function || post_sort_function(datap[heap[index]],count_out))
       {
-       WriteFile(fd_out,datap[heap[index]],itemsize);
+       WriteFileBuffered(fd_out,datap[heap[index]],itemsize);
        count_out++;
       }
 
-    if(ReadFile(fds[heap[index]],datap[heap[index]],itemsize))
+    if(ReadFileBuffered(fds[heap[index]],datap[heap[index]],itemsize))
       {
        heap[index]=heap[ndata];
        ndata--;
@@ -425,7 +426,7 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
  if(fds)
    {
     for(i=0;i<nfiles;i++)
-       CloseFile(fds[i]);
+       CloseFileBuffered(fds[i]);
     free(fds);
    }
 
@@ -440,6 +441,8 @@ index_t filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*pre_sort_funct
     free(threads[i].filename);
    }
 
+ free(threads);
+
  return(count_out);
 }
 
@@ -485,6 +488,7 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
  FILESORT_VARINT nextitemsize,largestitemsize=0;
  void *data,**datap;
  thread_data *threads;
+ size_t item;
  int i,more=1;
 #if defined(USE_PTHREADS) && USE_PTHREADS
  int nthreads=0;
@@ -508,7 +512,7 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
 
  /* Loop around, fill the buffer, sort the data and write a temporary file */
 
- if(ReadFile(fd_in,&nextitemsize,FILESORT_VARSIZE))    /* Always have the next item size known in advance */
+ if(ReadFileBuffered(fd_in,&nextitemsize,FILESORT_VARSIZE))    /* Always have the next item size known in advance */
     goto tidy_and_exit;
 
  do
@@ -539,23 +543,23 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
 
     /* Read in the data and create pointers */
 
-    while((ramused+FILESORT_VARSIZE+nextitemsize)<=((void*)threads[thread].datap-sizeof(void*)-threads[thread].data))
+    while((ramused+FILESORT_VARSIZE+nextitemsize)<=(unsigned)((void*)threads[thread].datap-sizeof(void*)-threads[thread].data))
       {
        FILESORT_VARINT itemsize=nextitemsize;
 
-       if(itemsize>largestitemsize)
-          largestitemsize=itemsize;
-
        *(FILESORT_VARINT*)(threads[thread].data+ramused)=itemsize;
 
        ramused+=FILESORT_VARSIZE;
 
-       ReadFile(fd_in,threads[thread].data+ramused,itemsize);
+       ReadFileBuffered(fd_in,threads[thread].data+ramused,itemsize);
 
        if(!pre_sort_function || pre_sort_function(threads[thread].data+ramused,count_in))
          {
           *--threads[thread].datap=threads[thread].data+ramused; /* points to real data */
 
+          if(itemsize>largestitemsize)
+             largestitemsize=itemsize;
+
           ramused+=itemsize;
 
           ramused =FILESORT_VARALIGN*((ramused+FILESORT_VARSIZE-1)/FILESORT_VARALIGN);
@@ -564,10 +568,12 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
           total++;
           threads[thread].n++;
          }
+       else
+          ramused-=FILESORT_VARSIZE;
 
        count_in++;
 
-       if(ReadFile(fd_in,&nextitemsize,FILESORT_VARSIZE))
+       if(ReadFileBuffered(fd_in,&nextitemsize,FILESORT_VARSIZE))
          {
           more=0;
           break;
@@ -663,13 +669,13 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
 
  if(nfiles==1)
    {
-    for(i=0;i<threads[0].n;i++)
+    for(item=0;item<threads[0].n;item++)
       {
-       if(!post_sort_function || post_sort_function(threads[0].datap[i],count_out))
+       if(!post_sort_function || post_sort_function(threads[0].datap[item],count_out))
          {
-          FILESORT_VARINT itemsize=*(FILESORT_VARINT*)(threads[0].datap[i]-FILESORT_VARSIZE);
+          FILESORT_VARINT itemsize=*(FILESORT_VARINT*)(threads[0].datap[item]-FILESORT_VARSIZE);
 
-          WriteFile(fd_out,threads[0].datap[i]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
+          WriteFileBuffered(fd_out,threads[0].datap[item]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
           count_out++;
          }
       }
@@ -683,7 +689,7 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
 
  largestitemsize=FILESORT_VARALIGN*(1+(largestitemsize+FILESORT_VARALIGN-FILESORT_VARSIZE)/FILESORT_VARALIGN);
 
- logassert(nfiles<((datasize-nfiles*sizeof(void*))/largestitemsize),"Too many temporary files (use more sorting memory?)");
+ logassert((unsigned)nfiles<((datasize-nfiles*sizeof(void*))/largestitemsize),"Too many temporary files (use more sorting memory?)");
 
  /* Open all of the temporary files */
 
@@ -695,7 +701,7 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
 
     sprintf(filename,"%s/filesort.%d.tmp",option_tmpdirname,i);
 
-    fds[i]=ReOpenFile(filename);
+    fds[i]=ReOpenFileBuffered(filename);
 
     DeleteFile(filename);
    }
@@ -716,11 +722,11 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
 
     datap[i]=data+FILESORT_VARALIGN-FILESORT_VARSIZE+i*largestitemsize;
 
-    ReadFile(fds[i],&itemsize,FILESORT_VARSIZE);
+    ReadFileBuffered(fds[i],&itemsize,FILESORT_VARSIZE);
 
     *(FILESORT_VARINT*)(datap[i]-FILESORT_VARSIZE)=itemsize;
 
-    ReadFile(fds[i],datap[i],itemsize);
+    ReadFileBuffered(fds[i],datap[i],itemsize);
 
     index=i+1;
 
@@ -759,11 +765,11 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
       {
        itemsize=*(FILESORT_VARINT*)(datap[heap[index]]-FILESORT_VARSIZE);
 
-       WriteFile(fd_out,datap[heap[index]]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
+       WriteFileBuffered(fd_out,datap[heap[index]]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
        count_out++;
       }
 
-    if(ReadFile(fds[heap[index]],&itemsize,FILESORT_VARSIZE))
+    if(ReadFileBuffered(fds[heap[index]],&itemsize,FILESORT_VARSIZE))
       {
        heap[index]=heap[ndata];
        ndata--;
@@ -772,7 +778,7 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
       {
        *(FILESORT_VARINT*)(datap[heap[index]]-FILESORT_VARSIZE)=itemsize;
 
-       ReadFile(fds[heap[index]],datap[heap[index]],itemsize);
+       ReadFileBuffered(fds[heap[index]],datap[heap[index]],itemsize);
       }
 
     /* Bubble down the new value */
@@ -823,7 +829,7 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
  if(fds)
    {
     for(i=0;i<nfiles;i++)
-       CloseFile(fds[i]);
+       CloseFileBuffered(fds[i]);
     free(fds);
    }
 
@@ -837,6 +843,8 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
     free(threads[i].filename);
    }
 
+ free(threads);
+
  return(count_out);
 }
 
@@ -851,7 +859,8 @@ index_t filesort_vary(int fd_in,int fd_out,int (*pre_sort_function)(void*,index_
 
 static void *filesort_fixed_heapsort_thread(thread_data *thread)
 {
- int fd,i;
+ int fd;
+ size_t item;
 
  /* Sort the data pointers using a heap sort */
 
@@ -859,12 +868,12 @@ static void *filesort_fixed_heapsort_thread(thread_data *thread)
 
  /* Create a temporary file and write the result */
 
- fd=OpenFileNew(thread->filename);
+ fd=OpenFileBufferedNew(thread->filename);
 
- for(i=0;i<thread->n;i++)
-    WriteFile(fd,thread->datap[i],thread->itemsize);
+ for(item=0;item<thread->n;item++)
+    WriteFileBuffered(fd,thread->datap[item],thread->itemsize);
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 
 #if defined(USE_PTHREADS) && USE_PTHREADS
 
@@ -895,7 +904,8 @@ static void *filesort_fixed_heapsort_thread(thread_data *thread)
 
 static void *filesort_vary_heapsort_thread(thread_data *thread)
 {
- int fd,i;
+ int fd;
+ size_t item;
 
  /* Sort the data pointers using a heap sort */
 
@@ -903,16 +913,16 @@ static void *filesort_vary_heapsort_thread(thread_data *thread)
 
  /* Create a temporary file and write the result */
 
- fd=OpenFileNew(thread->filename);
+ fd=OpenFileBufferedNew(thread->filename);
 
- for(i=0;i<thread->n;i++)
+ for(item=0;item<thread->n;item++)
    {
-    FILESORT_VARINT itemsize=*(FILESORT_VARINT*)(thread->datap[i]-FILESORT_VARSIZE);
+    FILESORT_VARINT itemsize=*(FILESORT_VARINT*)(thread->datap[item]-FILESORT_VARSIZE);
 
-    WriteFile(fd,thread->datap[i]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
+    WriteFileBuffered(fd,thread->datap[item]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
    }
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 
 #if defined(USE_PTHREADS) && USE_PTHREADS
 
@@ -951,13 +961,13 @@ static void *filesort_vary_heapsort_thread(thread_data *thread)
 void filesort_heapsort(void **datap,size_t nitems,int(*compare_function)(const void*, const void*))
 {
  void **datap1=&datap[-1];
- int i;
+ size_t item;
 
  /* Fill the heap by pretending to insert the data that is already there */
 
- for(i=2;i<=nitems;i++)
+ for(item=2;item<=nitems;item++)
    {
-    int index=i;
+    size_t index=item;
 
     /* Bubble up the new value (upside-down, put largest at top) */
 
@@ -981,18 +991,18 @@ void filesort_heapsort(void **datap,size_t nitems,int(*compare_function)(const v
 
  /* Repeatedly pull out the root of the heap and swap with the bottom item */
 
- for(i=nitems;i>1;i--)
+ for(item=nitems;item>1;item--)
    {
-    int index=1;
+    size_t index=1;
     void *temp;
 
     temp=datap1[index];
-    datap1[index]=datap1[i];
-    datap1[i]=temp;
+    datap1[index]=datap1[item];
+    datap1[item]=temp;
 
     /* Bubble down the new value (upside-down, put largest at top) */
 
-    while((2*index)<(i-1))
+    while((2*index)<(item-1))
       {
        int newindex;
        void *temp;
@@ -1012,7 +1022,7 @@ void filesort_heapsort(void **datap,size_t nitems,int(*compare_function)(const v
        index=newindex;
       }
 
-    if((2*index)==(i-1))
+    if((2*index)==(item-1))
       {
        int newindex;
        void *temp;
diff --git a/src/superx.c b/src/superx.c
index 4404a3b..d179475 100644
--- a/src/superx.c
+++ b/src/superx.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -77,26 +77,32 @@ void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
  /* Map into memory / open the files */
 
+ nodesx->fd=ReOpenFileBuffered(nodesx->filename_tmp);
+
 #if !SLIM
- nodesx->data=MapFile(nodesx->filename_tmp);
  segmentsx->data=MapFile(segmentsx->filename_tmp);
  waysx->data=MapFile(waysx->filename_tmp);
 #else
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
+
+ InvalidateSegmentXCache(segmentsx->cache);
+ InvalidateWayXCache(waysx->cache);
 #endif
 
  /* Find super-nodes */
 
  for(i=0;i<nodesx->number;i++)
    {
+    NodeX nodex;
+
+    ReadFileBuffered(nodesx->fd,&nodex,sizeof(NodeX));
+
     if(IsBitSet(nodesx->super,i))
       {
        int issuper=0;
-       NodeX *nodex=LookupNodeX(nodesx,i,1);
 
-       if(nodex->flags&(NODE_TURNRSTRCT|NODE_TURNRSTRCT2))
+       if(nodex.flags&(NODE_TURNRSTRCT|NODE_TURNRSTRCT2))
           issuper=1;
        else
          {
@@ -123,7 +129,7 @@ void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
              /* If the node allows less traffic types than any connecting way then it is super if it allows anything */
 
-             if((wayx->way.allow&nodex->allow)!=wayx->way.allow && nodex->allow!=Transports_None)
+             if((wayx->way.allow&nodex.allow)!=wayx->way.allow && nodex.allow!=Transports_None)
                {
                 issuper=1;
                 break;
@@ -176,15 +182,15 @@ void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
  /* Unmap from memory / close the files */
 
 #if !SLIM
- nodesx->data=UnmapFile(nodesx->data);
  segmentsx->data=UnmapFile(segmentsx->data);
  waysx->data=UnmapFile(waysx->data);
 #else
- nodesx->fd=CloseFile(nodesx->fd);
- segmentsx->fd=CloseFile(segmentsx->fd);
- waysx->fd=CloseFile(waysx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
+ nodesx->fd=CloseFileBuffered(nodesx->fd);
+
  /* Print the final message */
 
  printf_last("Found Super-Nodes: Nodes=%"Pindex_t" Super-Nodes=%"Pindex_t,nodesx->number,nnodes);
@@ -209,7 +215,7 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
  SegmentsX *supersegmentsx;
  index_t sn=0,ss=0;
 
- supersegmentsx=NewSegmentList(0,0);
+ supersegmentsx=NewSegmentList();
 
  if(segmentsx->number==0 || waysx->number==0)
    {
@@ -229,9 +235,13 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
  segmentsx->data=MapFile(segmentsx->filename_tmp);
  waysx->data=MapFile(waysx->filename_tmp);
 #else
- nodesx->fd=ReOpenFile(nodesx->filename_tmp);
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ nodesx->fd=SlimMapFile(nodesx->filename_tmp);
+ segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
+ waysx->fd=SlimMapFile(waysx->filename_tmp);
+
+ InvalidateNodeXCache(nodesx->cache);
+ InvalidateSegmentXCache(segmentsx->cache);
+ InvalidateWayXCache(waysx->cache);
 #endif
 
  /* Create super-segments for each super-node. */
@@ -291,8 +301,6 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
                 result=NextResult(results,result);
                }
-
-             FreeResultsList(results);
             }
 
           segmentx=NextSegmentX(segmentsx,segmentx,i);
@@ -305,6 +313,8 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
       }
    }
 
+ FinishSegmentList(supersegmentsx);
+
  /* Unmap from memory / close the files */
 
 #if !SLIM
@@ -312,17 +322,15 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
  segmentsx->data=UnmapFile(segmentsx->data);
  waysx->data=UnmapFile(waysx->data);
 #else
- nodesx->fd=CloseFile(nodesx->fd);
- segmentsx->fd=CloseFile(segmentsx->fd);
- waysx->fd=CloseFile(waysx->fd);
+ nodesx->fd=SlimUnmapFile(nodesx->fd);
+ segmentsx->fd=SlimUnmapFile(segmentsx->fd);
+ waysx->fd=SlimUnmapFile(waysx->fd);
 #endif
 
  /* Print the final message */
 
  printf_last("Created Super-Segments: Super-Nodes=%"Pindex_t" Super-Segments=%"Pindex_t,sn,ss);
 
- FinishSegmentList(supersegmentsx);
-
  return(supersegmentsx);
 }
 
@@ -339,11 +347,11 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
 SegmentsX *MergeSuperSegments(SegmentsX *segmentsx,SegmentsX *supersegmentsx)
 {
- index_t i,j;
+ index_t i,j,lastj;
  index_t merged=0,added=0;
  SegmentsX *mergedsegmentsx;
 
- mergedsegmentsx=NewSegmentList(0,0);
+ mergedsegmentsx=NewSegmentList();
 
  if(segmentsx->number==0)
    {
@@ -356,32 +364,37 @@ SegmentsX *MergeSuperSegments(SegmentsX *segmentsx,SegmentsX *supersegmentsx)
 
  printf_first("Merging Segments: Segments=0 Super=0 Merged=0 Added=0");
 
- /* Map into memory / open the files */
+ /* Open the files */
 
-#if !SLIM
- segmentsx->data=MapFile(segmentsx->filename_tmp);
- if(supersegmentsx->number>0)
-    supersegmentsx->data=MapFile(supersegmentsx->filename_tmp);
-#else
- segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
+ segmentsx->fd=ReOpenFileBuffered(segmentsx->filename_tmp);
  if(supersegmentsx->number>0)
-    supersegmentsx->fd=ReOpenFile(supersegmentsx->filename_tmp);
-#endif
+    supersegmentsx->fd=ReOpenFileBuffered(supersegmentsx->filename_tmp);
 
  /* Loop through and create a new list of combined segments */
 
- for(i=0,j=0;i<segmentsx->number;i++)
+ lastj=-1;
+ j=0;
+
+ for(i=0;i<segmentsx->number;i++)
    {
     int super=0;
-    SegmentX *segmentx=LookupSegmentX(segmentsx,i,1);
+    SegmentX segmentx;
+
+    ReadFileBuffered(segmentsx->fd,&segmentx,sizeof(SegmentX));
 
     while(j<supersegmentsx->number)
       {
-       SegmentX *supersegmentx=LookupSegmentX(supersegmentsx,j,1);
+       SegmentX supersegmentx;
 
-       if(segmentx->node1   ==supersegmentx->node1 &&
-          segmentx->node2   ==supersegmentx->node2 &&
-          segmentx->distance==supersegmentx->distance)
+       if(j!=lastj)
+         {
+          ReadFileBuffered(supersegmentsx->fd,&supersegmentx,sizeof(SegmentX));
+          lastj=j;
+         }
+
+       if(segmentx.node1   ==supersegmentx.node1 &&
+          segmentx.node2   ==supersegmentx.node2 &&
+          segmentx.distance==supersegmentx.distance)
          {
           merged++;
           j++;
@@ -389,14 +402,14 @@ SegmentsX *MergeSuperSegments(SegmentsX *segmentsx,SegmentsX *supersegmentsx)
           super=1;
           break;
          }
-       else if((segmentx->node1==supersegmentx->node1 &&
-                segmentx->node2==supersegmentx->node2) ||
-               (segmentx->node1==supersegmentx->node1 &&
-                segmentx->node2>supersegmentx->node2) ||
-               (segmentx->node1>supersegmentx->node1))
+       else if((segmentx.node1==supersegmentx.node1 &&
+                segmentx.node2==supersegmentx.node2) ||
+               (segmentx.node1==supersegmentx.node1 &&
+                segmentx.node2>supersegmentx.node2) ||
+               (segmentx.node1>supersegmentx.node1))
          {
           /* mark as super-segment */
-          AppendSegmentList(mergedsegmentsx,supersegmentx->way,supersegmentx->node1,supersegmentx->node2,supersegmentx->distance|SEGMENT_SUPER);
+          AppendSegmentList(mergedsegmentsx,supersegmentx.way,supersegmentx.node1,supersegmentx.node2,supersegmentx.distance|SEGMENT_SUPER);
           added++;
           j++;
          }
@@ -408,32 +421,37 @@ SegmentsX *MergeSuperSegments(SegmentsX *segmentsx,SegmentsX *supersegmentsx)
       }
 
     if(super)
-       AppendSegmentList(mergedsegmentsx,segmentx->way,segmentx->node1,segmentx->node2,segmentx->distance|SEGMENT_SUPER|SEGMENT_NORMAL);
+       AppendSegmentList(mergedsegmentsx,segmentx.way,segmentx.node1,segmentx.node2,segmentx.distance|SEGMENT_SUPER|SEGMENT_NORMAL);
     else
-       AppendSegmentList(mergedsegmentsx,segmentx->way,segmentx->node1,segmentx->node2,segmentx->distance|SEGMENT_NORMAL);
+       AppendSegmentList(mergedsegmentsx,segmentx.way,segmentx.node1,segmentx.node2,segmentx.distance|SEGMENT_NORMAL);
 
     if(!((i+1)%10000))
        printf_middle("Merging Segments: Segments=%"Pindex_t" Super=%"Pindex_t" Merged=%"Pindex_t" Added=%"Pindex_t,i+1,j,merged,added);
    }
 
- /* Unmap from memory / close the files */
+ while(j<supersegmentsx->number)
+   {
+    SegmentX supersegmentx;
 
-#if !SLIM
- segmentsx->data=UnmapFile(segmentsx->data);
- if(supersegmentsx->number>0)
-    supersegmentsx->data=UnmapFile(supersegmentsx->data);
-#else
- segmentsx->fd=CloseFile(segmentsx->fd);
+    ReadFileBuffered(supersegmentsx->fd,&supersegmentx,sizeof(SegmentX));
+
+    AppendSegmentList(mergedsegmentsx,supersegmentx.way,supersegmentx.node1,supersegmentx.node2,supersegmentx.distance|SEGMENT_SUPER);
+    added++;
+    j++;
+   }
+
+ FinishSegmentList(mergedsegmentsx);
+
+ /* Close the files */
+
+ segmentsx->fd=CloseFileBuffered(segmentsx->fd);
  if(supersegmentsx->number>0)
-    supersegmentsx->fd=CloseFile(supersegmentsx->fd);
-#endif
+    supersegmentsx->fd=CloseFileBuffered(supersegmentsx->fd);
 
  /* Print the final message */
 
  printf_last("Merged Segments: Segments=%"Pindex_t" Super=%"Pindex_t" Merged=%"Pindex_t" Added=%"Pindex_t,segmentsx->number,supersegmentsx->number,merged,added);
 
- FinishSegmentList(mergedsegmentsx);
-
  return(mergedsegmentsx);
 }
 
@@ -456,20 +474,26 @@ SegmentsX *MergeSuperSegments(SegmentsX *segmentsx,SegmentsX *supersegmentsx)
 
 static Results *FindSuperRoutes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,node_t start,Way *match)
 {
- Results *results;
- Queue *queue;
+ static Results *results=NULL;
+ static Queue *queue=NULL;
  Result *result1,*result2;
  WayX *wayx;
 
  /* Insert the first node into the queue */
 
- results=NewResultsList(64);
+ if(!results)
+    results=NewResultsList(8);
+ else
+    ResetResultsList(results);
 
- queue=NewQueueList();
+ if(!queue)
+    queue=NewQueueList(8);
+ else
+    ResetQueueList(queue);
 
  result1=InsertResult(results,start,NO_SEGMENT);
 
- InsertInQueue(queue,result1);
+ InsertInQueue(queue,result1,0);
 
  /* Loop across all nodes in the queue */
 
@@ -521,21 +545,19 @@ static Results *FindSuperRoutes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx
           result2=InsertResult(results,node2,seg2);
           result2->prev=result1;
           result2->score=cumulative_distance;
-          result2->sortby=cumulative_distance;
 
           /* don't route beyond a super-node. */
           if(!IsBitSet(nodesx->super,node2))
-             InsertInQueue(queue,result2);
+             InsertInQueue(queue,result2,cumulative_distance);
          }
        else if(cumulative_distance<result2->score)
          {
           result2->prev=result1;
           result2->score=cumulative_distance;
-          result2->sortby=cumulative_distance;
 
           /* don't route beyond a super-node. */
           if(!IsBitSet(nodesx->super,node2))
-             InsertInQueue(queue,result2);
+             InsertInQueue(queue,result2,cumulative_distance);
          }
 
       endloop:
@@ -544,7 +566,5 @@ static Results *FindSuperRoutes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx
       }
    }
 
- FreeQueueList(queue);
-
  return(results);
 }
diff --git a/src/tagging.c b/src/tagging.c
index 47ec539..cba2e68 100644
--- a/src/tagging.c
+++ b/src/tagging.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2012 Andrew M. Bishop
+ This file Copyright 2010-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -23,6 +23,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <inttypes.h>
+#include <stdint.h>
 
 #include "files.h"
 #include "tagging.h"
@@ -30,39 +32,126 @@
 #include "logging.h"
 
 
-/* Global variables */
+/* Constants */
 
-TaggingRuleList NodeRules={NULL,0};
-TaggingRuleList WayRules={NULL,0};
-TaggingRuleList RelationRules={NULL,0};
+#define TAGACTION_IF       1
+#define TAGACTION_IFNOT    2
+
+#define TAGACTION_INHERIT  3    /* Not a real action, just a marker */
+
+#define TAGACTION_SET      4
+#define TAGACTION_UNSET    5
+#define TAGACTION_OUTPUT   6
+#define TAGACTION_LOGERROR 7
 
 
 /* Local variables */
 
-TaggingRuleList *current_list=NULL;
-TaggingRule     *current_rule=NULL;
+static TaggingRuleList NodeRules={NULL,0};
+static TaggingRuleList WayRules={NULL,0};
+static TaggingRuleList RelationRules={NULL,0};
+
+static int current_list_stack_depth=0;
+static TaggingRuleList **current_list_stack=NULL;
+static TaggingRuleList *current_list=NULL;
+
+static int64_t current_id;
+
+static char *default_logerror_message="ignoring it";
 
 
 /* Local functions */
 
-static void apply_actions(TaggingRuleList *rules,TaggingRule *rule,int match,TagList *input,TagList *output,node_t id);
+static TaggingRuleList *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *v,int action);
+static void AppendTaggingAction(TaggingRuleList *rules,const char *k,const char *v,int action,const char *message);
+static void DeleteTaggingRuleList(TaggingRuleList *rules);
+
+static void ApplyRules(TaggingRuleList *rules,TagList *input,TagList *output,const char *match_k,const char *match_v);
 
 
 /* The XML tag processing function prototypes */
 
 //static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding);
 //static int RoutinoTaggingType_function(const char *_tag_,int _type_);
-static int RelationType_function(const char *_tag_,int _type_);
-static int WayType_function(const char *_tag_,int _type_);
 static int NodeType_function(const char *_tag_,int _type_);
+static int WayType_function(const char *_tag_,int _type_);
+static int RelationType_function(const char *_tag_,int _type_);
 static int IfType_function(const char *_tag_,int _type_,const char *k,const char *v);
-static int LogErrorType_function(const char *_tag_,int _type_,const char *k,const char *v);
-static int OutputType_function(const char *_tag_,int _type_,const char *k,const char *v);
-static int UnsetType_function(const char *_tag_,int _type_,const char *k);
+static int IfNotType_function(const char *_tag_,int _type_,const char *k,const char *v);
 static int SetType_function(const char *_tag_,int _type_,const char *k,const char *v);
+static int UnsetType_function(const char *_tag_,int _type_,const char *k);
+static int OutputType_function(const char *_tag_,int _type_,const char *k,const char *v);
+static int LogErrorType_function(const char *_tag_,int _type_,const char *k,const char *v,const char *message);
+
+
+/* The XML tag definitions (forward declarations) */
+
+static xmltag xmlDeclaration_tag;
+static xmltag RoutinoTaggingType_tag;
+static xmltag NodeType_tag;
+static xmltag WayType_tag;
+static xmltag RelationType_tag;
+static xmltag IfType_tag;
+static xmltag IfNotType_tag;
+static xmltag SetType_tag;
+static xmltag UnsetType_tag;
+static xmltag OutputType_tag;
+static xmltag LogErrorType_tag;
+
+
+/* The XML tag definition values */
+
+/*+ The complete set of tags at the top level. +*/
+static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoTaggingType_tag,NULL};
+
+/*+ The xmlDeclaration type tag. +*/
+static xmltag xmlDeclaration_tag=
+              {"xml",
+               2, {"version","encoding"},
+               NULL,
+               {NULL}};
+
+/*+ The RoutinoTaggingType type tag. +*/
+static xmltag RoutinoTaggingType_tag=
+              {"routino-tagging",
+               0, {NULL},
+               NULL,
+               {&NodeType_tag,&WayType_tag,&RelationType_tag,NULL}};
+
+/*+ The NodeType type tag. +*/
+static xmltag NodeType_tag=
+              {"node",
+               0, {NULL},
+               NodeType_function,
+               {&IfType_tag,&IfNotType_tag,NULL}};
+
+/*+ The WayType type tag. +*/
+static xmltag WayType_tag=
+              {"way",
+               0, {NULL},
+               WayType_function,
+               {&IfType_tag,&IfNotType_tag,NULL}};
 
+/*+ The RelationType type tag. +*/
+static xmltag RelationType_tag=
+              {"relation",
+               0, {NULL},
+               RelationType_function,
+               {&IfType_tag,&IfNotType_tag,NULL}};
+
+/*+ The IfType type tag. +*/
+static xmltag IfType_tag=
+              {"if",
+               2, {"k","v"},
+               IfType_function,
+               {&IfType_tag,&IfNotType_tag,&SetType_tag,&UnsetType_tag,&OutputType_tag,&LogErrorType_tag,NULL}};
 
-/* The XML tag definitions */
+/*+ The IfNotType type tag. +*/
+static xmltag IfNotType_tag=
+              {"ifnot",
+               2, {"k","v"},
+               IfNotType_function,
+               {&IfType_tag,&IfNotType_tag,&SetType_tag,&UnsetType_tag,&OutputType_tag,&LogErrorType_tag,NULL}};
 
 /*+ The SetType type tag. +*/
 static xmltag SetType_tag=
@@ -88,145 +177,111 @@ static xmltag OutputType_tag=
 /*+ The LogErrorType type tag. +*/
 static xmltag LogErrorType_tag=
               {"logerror",
-               2, {"k","v"},
+               3, {"k","v","message"},
                LogErrorType_function,
                {NULL}};
 
-/*+ The IfType type tag. +*/
-static xmltag IfType_tag=
-              {"if",
-               2, {"k","v"},
-               IfType_function,
-               {&SetType_tag,&UnsetType_tag,&OutputType_tag,&LogErrorType_tag,NULL}};
 
-/*+ The NodeType type tag. +*/
-static xmltag NodeType_tag=
-              {"node",
-               0, {NULL},
-               NodeType_function,
-               {&IfType_tag,NULL}};
+/* The XML tag processing functions */
 
-/*+ The WayType type tag. +*/
-static xmltag WayType_tag=
-              {"way",
-               0, {NULL},
-               WayType_function,
-               {&IfType_tag,NULL}};
 
-/*+ The RelationType type tag. +*/
-static xmltag RelationType_tag=
-              {"relation",
-               0, {NULL},
-               RelationType_function,
-               {&IfType_tag,NULL}};
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the XML declaration is seen
 
-/*+ The RoutinoTaggingType type tag. +*/
-static xmltag RoutinoTaggingType_tag=
-              {"routino-tagging",
-               0, {NULL},
-               NULL,
-               {&NodeType_tag,&WayType_tag,&RelationType_tag,NULL}};
+  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
 
-/*+ The xmlDeclaration type tag. +*/
-static xmltag xmlDeclaration_tag=
-              {"xml",
-               2, {"version","encoding"},
-               NULL,
-               {NULL}};
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
 
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-/*+ The complete set of tags at the top level. +*/
-static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoTaggingType_tag,NULL};
+  const char *version The contents of the 'version' attribute (or NULL if not defined).
 
+  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
 
-/* The XML tag processing functions */
+//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
+//{
+// return(0);
+//}
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the SetType XSD type is seen
+  The function that is called when the RoutinoTaggingType XSD type is seen
 
-  int SetType_function Returns 0 if no error occured or something else otherwise.
+  int RoutinoTaggingType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *k The contents of the 'k' attribute (or NULL if not defined).
-
-  const char *v The contents of the 'v' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int SetType_function(const char *_tag_,int _type_,const char *k,const char *v)
-{
- if(_type_&XMLPARSE_TAG_START)
-    AppendTaggingAction(current_rule,k,v,TAGACTION_SET);
-
- return(0);
-}
+//static int RoutinoTaggingType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the UnsetType XSD type is seen
+  The function that is called when the NodeType XSD type is seen
 
-  int UnsetType_function Returns 0 if no error occured or something else otherwise.
+  int NodeType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *k The contents of the 'k' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int UnsetType_function(const char *_tag_,int _type_,const char *k)
+static int NodeType_function(const char *_tag_,int _type_)
 {
  if(_type_&XMLPARSE_TAG_START)
-    AppendTaggingAction(current_rule,k,NULL,TAGACTION_UNSET);
+   {
+    current_list_stack_depth=0;
+    current_list=&NodeRules;
+   }
 
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the OutputType XSD type is seen
+  The function that is called when the WayType XSD type is seen
 
-  int OutputType_function Returns 0 if no error occured or something else otherwise.
+  int WayType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *k The contents of the 'k' attribute (or NULL if not defined).
-
-  const char *v The contents of the 'v' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int OutputType_function(const char *_tag_,int _type_,const char *k,const char *v)
+static int WayType_function(const char *_tag_,int _type_)
 {
  if(_type_&XMLPARSE_TAG_START)
-    AppendTaggingAction(current_rule,k,v,TAGACTION_OUTPUT);
+   {
+    current_list_stack_depth=0;
+    current_list=&WayRules;
+   }
 
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the LogErrorType XSD type is seen
+  The function that is called when the RelationType XSD type is seen
 
-  int LogErrorType_function Returns 0 if no error occured or something else otherwise.
+  int RelationType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *k The contents of the 'k' attribute (or NULL if not defined).
-
-  const char *v The contents of the 'v' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int LogErrorType_function(const char *_tag_,int _type_,const char *k,const char *v)
+static int RelationType_function(const char *_tag_,int _type_)
 {
  if(_type_&XMLPARSE_TAG_START)
-    AppendTaggingAction(current_rule,k,v,TAGACTION_LOGERROR);
+   {
+    current_list_stack_depth=0;
+    current_list=&RelationRules;
+   }
 
  return(0);
 }
@@ -250,104 +305,144 @@ static int IfType_function(const char *_tag_,int _type_,const char *k,const char
 {
  if(_type_&XMLPARSE_TAG_START)
    {
-    current_rule=AppendTaggingRule(current_list,k,v);
+    if(!current_list_stack || (current_list_stack_depth%8)==7)
+       current_list_stack=(TaggingRuleList**)realloc((void*)current_list_stack,(current_list_stack_depth+8)*sizeof(TaggingRuleList*));
+
+    current_list_stack[current_list_stack_depth++]=current_list;
+
+    current_list=AppendTaggingRule(current_list,k,v,TAGACTION_IF);
    }
 
+ if(_type_&XMLPARSE_TAG_END)
+    current_list=current_list_stack[--current_list_stack_depth];
+
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the NodeType XSD type is seen
+  The function that is called when the IfNotType XSD type is seen
 
-  int NodeType_function Returns 0 if no error occured or something else otherwise.
+  int IfNotType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *k The contents of the 'k' attribute (or NULL if not defined).
+
+  const char *v The contents of the 'v' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int NodeType_function(const char *_tag_,int _type_)
+static int IfNotType_function(const char *_tag_,int _type_,const char *k,const char *v)
 {
  if(_type_&XMLPARSE_TAG_START)
-    current_list=&NodeRules;
+   {
+    if(!current_list_stack || (current_list_stack_depth%8)==7)
+       current_list_stack=(TaggingRuleList**)realloc((void*)current_list_stack,(current_list_stack_depth+8)*sizeof(TaggingRuleList*));
+
+    current_list_stack[current_list_stack_depth++]=current_list;
+
+    current_list=AppendTaggingRule(current_list,k,v,TAGACTION_IFNOT);
+   }
+
+ if(_type_&XMLPARSE_TAG_END)
+    current_list=current_list_stack[--current_list_stack_depth];
 
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the WayType XSD type is seen
+  The function that is called when the SetType XSD type is seen
 
-  int WayType_function Returns 0 if no error occured or something else otherwise.
+  int SetType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *k The contents of the 'k' attribute (or NULL if not defined).
+
+  const char *v The contents of the 'v' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int WayType_function(const char *_tag_,int _type_)
+static int SetType_function(const char *_tag_,int _type_,const char *k,const char *v)
 {
  if(_type_&XMLPARSE_TAG_START)
-    current_list=&WayRules;
+    AppendTaggingAction(current_list,k,v,TAGACTION_SET,NULL);
 
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the RelationType XSD type is seen
+  The function that is called when the UnsetType XSD type is seen
 
-  int RelationType_function Returns 0 if no error occured or something else otherwise.
+  int UnsetType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *k The contents of the 'k' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int RelationType_function(const char *_tag_,int _type_)
+static int UnsetType_function(const char *_tag_,int _type_,const char *k)
 {
  if(_type_&XMLPARSE_TAG_START)
-    current_list=&RelationRules;
+    AppendTaggingAction(current_list,k,NULL,TAGACTION_UNSET,NULL);
 
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the RoutinoTaggingType XSD type is seen
+  The function that is called when the OutputType XSD type is seen
 
-  int RoutinoTaggingType_function Returns 0 if no error occured or something else otherwise.
+  int OutputType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *k The contents of the 'k' attribute (or NULL if not defined).
+
+  const char *v The contents of the 'v' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-//static int RoutinoTaggingType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
+static int OutputType_function(const char *_tag_,int _type_,const char *k,const char *v)
+{
+ if(_type_&XMLPARSE_TAG_START)
+    AppendTaggingAction(current_list,k,v,TAGACTION_OUTPUT,NULL);
+
+ return(0);
+}
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the XML declaration is seen
+  The function that is called when the LogErrorType XSD type is seen
 
-  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
+  int LogErrorType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *version The contents of the 'version' attribute (or NULL if not defined).
+  const char *k The contents of the 'k' attribute (or NULL if not defined).
 
-  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
+  const char *v The contents of the 'v' attribute (or NULL if not defined).
+
+  const char *message The contents of the 'message' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
-//{
-// return(0);
-//}
+static int LogErrorType_function(const char *_tag_,int _type_,const char *k,const char *v,const char *message)
+{
+ if(_type_&XMLPARSE_TAG_START)
+    AppendTaggingAction(current_list,k,v,TAGACTION_LOGERROR,message);
+
+ return(0);
+}
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -360,7 +455,7 @@ static int RelationType_function(const char *_tag_,int _type_)
 
 int ParseXMLTaggingRules(const char *filename)
 {
- FILE *file;
+ int fd;
  int retval;
 
  if(!ExistsFile(filename))
@@ -369,17 +464,14 @@ int ParseXMLTaggingRules(const char *filename)
     return(1);
    }
 
- file=fopen(filename,"r");
+ fd=OpenFile(filename);
 
- if(!file)
-   {
-    fprintf(stderr,"Error: Cannot open tagging rules file '%s' for reading.\n",filename);
-    return(1);
-   }
+ retval=ParseXML(fd,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME);
 
- retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME);
+ CloseFile(fd);
 
- fclose(file);
+ if(current_list_stack)
+    free(current_list_stack);
 
  if(retval)
     return(1);
@@ -403,22 +495,26 @@ void DeleteXMLTaggingRules(void)
 /*++++++++++++++++++++++++++++++++++++++
   Append a tagging rule to the list of rules.
 
-  TaggingRule *AppendTaggingRule Returns the latest rule (the just added one).
+  TaggingRuleList *AppendTaggingRule Returns the new TaggingRuleList inside the new TaggingRule.
 
   TaggingRuleList *rules The list of rules to add to.
 
   const char *k The tag key.
 
   const char *v The tag value.
+
+  int action Set to the type of action.
   ++++++++++++++++++++++++++++++++++++++*/
 
-TaggingRule *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *v)
+TaggingRuleList *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *v,int action)
 {
  if((rules->nrules%16)==0)
     rules->rules=(TaggingRule*)realloc((void*)rules->rules,(rules->nrules+16)*sizeof(TaggingRule));
 
  rules->nrules++;
 
+ rules->rules[rules->nrules-1].action=action;
+
  if(k)
     rules->rules[rules->nrules-1].k=strcpy(malloc(strlen(k)+1),k);
  else
@@ -429,43 +525,53 @@ TaggingRule *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *
  else
     rules->rules[rules->nrules-1].v=NULL;
 
- rules->rules[rules->nrules-1].nactions=0;
- rules->rules[rules->nrules-1].actions=NULL;
+ rules->rules[rules->nrules-1].message=NULL;
+
+ rules->rules[rules->nrules-1].rulelist=(TaggingRuleList*)calloc(sizeof(TaggingRuleList),1);
 
- return(&rules->rules[rules->nrules-1]);
+ return(rules->rules[rules->nrules-1].rulelist);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Append a tagging action to a tagging rule.
+  Append a tagging action to the list of rules.
 
-  TaggingRule *rule The rule to add the action to.
+  TaggingRuleList *rules The list of rules to add to.
 
   const char *k The tag key.
 
   const char *v The tag value.
 
   int action Set to the type of action.
+
+  const char *message The message to use for the logerror action.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int action)
+static void AppendTaggingAction(TaggingRuleList *rules,const char *k,const char *v,int action,const char *message)
 {
- if((rule->nactions%16)==0)
-    rule->actions=(TaggingAction*)realloc((void*)rule->actions,(rule->nactions+16)*sizeof(TaggingAction));
+ if((rules->nrules%16)==0)
+    rules->rules=(TaggingRule*)realloc((void*)rules->rules,(rules->nrules+16)*sizeof(TaggingRule));
 
- rule->nactions++;
+ rules->nrules++;
 
- rule->actions[rule->nactions-1].action=action;
+ rules->rules[rules->nrules-1].action=action;
 
  if(k)
-    rule->actions[rule->nactions-1].k=strcpy(malloc(strlen(k)+1),k);
+    rules->rules[rules->nrules-1].k=strcpy(malloc(strlen(k)+1),k);
  else
-    rule->actions[rule->nactions-1].k=NULL;
+    rules->rules[rules->nrules-1].k=NULL;
 
  if(v)
-    rule->actions[rule->nactions-1].v=strcpy(malloc(strlen(v)+1),v);
+    rules->rules[rules->nrules-1].v=strcpy(malloc(strlen(v)+1),v);
+ else
+    rules->rules[rules->nrules-1].v=NULL;
+
+ if(message)
+    rules->rules[rules->nrules-1].message=strcpy(malloc(strlen(message)+1),message);
  else
-    rule->actions[rule->nactions-1].v=NULL;
+    rules->rules[rules->nrules-1].message=default_logerror_message;
+
+ rules->rules[rules->nrules-1].rulelist=NULL;
 }
 
 
@@ -477,7 +583,7 @@ void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int actio
 
 void DeleteTaggingRuleList(TaggingRuleList *rules)
 {
- int i,j;
+ int i;
 
  for(i=0;i<rules->nrules;i++)
    {
@@ -485,17 +591,14 @@ void DeleteTaggingRuleList(TaggingRuleList *rules)
        free(rules->rules[i].k);
     if(rules->rules[i].v)
        free(rules->rules[i].v);
+    if(rules->rules[i].message && rules->rules[i].message!=default_logerror_message)
+       free(rules->rules[i].message);
 
-    for(j=0;j<rules->rules[i].nactions;j++)
+    if(rules->rules[i].rulelist)
       {
-       if(rules->rules[i].actions[j].k)
-          free(rules->rules[i].actions[j].k);
-       if(rules->rules[i].actions[j].v)
-          free(rules->rules[i].actions[j].v);
+       DeleteTaggingRuleList(rules->rules[i].rulelist);
+       free(rules->rules[i].rulelist);
       }
-
-    if(rules->rules[i].actions)
-       free(rules->rules[i].actions);
    }
 
  if(rules->rules)
@@ -628,101 +731,240 @@ void DeleteTag(TagList *tags,const char *k)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Apply a set of tagging rules to a set of tags.
+  Create a string containing all of the tags formatted as if HTML.
+
+  char *StringifyTag Returns a static pointer to the created string.
+
+  TagList *tags The list of tags to convert.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+char *StringifyTag(TagList *tags)
+{
+ static char *string=NULL;
+ int i,length=0,used=0;
+
+ for(i=0;i<tags->ntags;i++)
+   {
+    length+=strlen(tags->k[i]);
+    length+=strlen(tags->v[i]);
+
+    length+=16;
+   }
+
+ string=realloc((char*)string,length);
+
+ for(i=0;i<tags->ntags;i++)
+    used+=sprintf(string+used,"<tag k='%s' v='%s'>",tags->k[i],tags->v[i]);
+
+ return(string);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Apply a set of tagging rules to a set of node tags.
+
+  TagList *ApplyNodeTaggingRules Returns the list of output tags after modification.
+
+  TagList *tags The tags to be modified.
+
+  int64_t id The ID of the node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+TagList *ApplyNodeTaggingRules(TagList *tags,int64_t id)
+{
+ TagList *result=NewTagList();
+
+ current_id=id;
+ current_list=&NodeRules;
+
+ ApplyRules(current_list,tags,result,NULL,NULL);
+
+ return(result);
+}
+
 
-  TagList *ApplyTaggingRules Returns the list of output tags after modification.
+/*++++++++++++++++++++++++++++++++++++++
+  Apply a set of tagging rules to a set of way tags.
 
-  TaggingRuleList *rules The tagging rules to apply.
+  TagList *ApplyWayTaggingRules Returns the list of output tags after modification.
 
   TagList *tags The tags to be modified.
 
-  node_t id The ID of the node, way or relation.
+  int64_t id The ID of the way.
   ++++++++++++++++++++++++++++++++++++++*/
 
-TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags,node_t id)
+TagList *ApplyWayTaggingRules(TagList *tags,int64_t id)
 {
  TagList *result=NewTagList();
- int i,j;
 
- for(i=0;i<rules->nrules;i++)
-   {
-    if(rules->rules[i].k && rules->rules[i].v)
-      {
-       for(j=0;j<tags->ntags;j++)
-          if(!strcmp(tags->k[j],rules->rules[i].k) && !strcmp(tags->v[j],rules->rules[i].v))
-             apply_actions(rules,&rules->rules[i],j,tags,result,id);
-      }
-    else if(rules->rules[i].k && !rules->rules[i].v)
-      {
-       for(j=0;j<tags->ntags;j++)
-          if(!strcmp(tags->k[j],rules->rules[i].k))
-             apply_actions(rules,&rules->rules[i],j,tags,result,id);
-      }
-    else if(!rules->rules[i].k && rules->rules[i].v)
-      {
-       for(j=0;j<tags->ntags;j++)
-          if(!strcmp(tags->v[j],rules->rules[i].v))
-             apply_actions(rules,&rules->rules[i],j,tags,result,id);
-      }
-    else /* if(!rules->rules[i].k && !rules->rules[i].v) */
-      {
-       for(j=0;j<tags->ntags;j++)
-          apply_actions(rules,&rules->rules[i],j,tags,result,id);
-      }
-   }
+ current_id=id;
+ current_list=&WayRules;
+
+ ApplyRules(current_list,tags,result,NULL,NULL);
 
  return(result);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Apply a set of actions to a matching tag.
+  Apply a set of tagging rules to a set of relation tags.
+
+  TagList *ApplyRelationTaggingRules Returns the list of output tags after modification.
+
+  TagList *tags The tags to be modified.
+
+  int64_t id The ID of the relation.
+  ++++++++++++++++++++++++++++++++++++++*/
 
-  TaggingRuleList *rules The tagging rules to apply.
+TagList *ApplyRelationTaggingRules(TagList *tags,int64_t id)
+{
+ TagList *result=NewTagList();
+
+ current_id=id;
+ current_list=&RelationRules;
 
-  TaggingRule *rule The rule that matched (containing the actions).
+ ApplyRules(current_list,tags,result,NULL,NULL);
+
+ return(result);
+}
 
-  int match The matching tag number.
+
+/*++++++++++++++++++++++++++++++++++++++
+  Apply a set of rules to a matching tag.
+
+  TaggingRuleList *rules The rules that are to be matched.
 
   TagList *input The input tags.
 
   TagList *output The output tags.
 
-  node_t id The ID of the node, way or relation.
+  const char *match_k The key matched at the higher level rule.
+
+  const char *match_v The value matched at the higher level rule.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void apply_actions(TaggingRuleList *rules,TaggingRule *rule,int match,TagList *input,TagList *output,node_t id)
+static void ApplyRules(TaggingRuleList *rules,TagList *input,TagList *output,const char *match_k,const char *match_v)
 {
- int i;
+ int i,j;
+ char *match_k_copy=NULL,*match_v_copy=NULL;
  
- for(i=0;i<rule->nactions;i++)
+ if(match_k)
+    match_k_copy=strcpy(malloc(strlen(match_k)+1),match_k);
+
+ if(match_v)
+    match_v_copy=strcpy(malloc(strlen(match_v)+1),match_v);
+
+ for(i=0;i<rules->nrules;i++)
    {
-    char *k,*v;
+    const char *k,*v;
+
+    k=rules->rules[i].k;
+
+    if(!k && rules->rules[i].action >= TAGACTION_INHERIT)
+       k=match_k_copy;
+
+    v=rules->rules[i].v;
+
+    if(!v && rules->rules[i].action >= TAGACTION_INHERIT)
+       v=match_v_copy;
+
+    switch(rules->rules[i].action)
+      {
+      case TAGACTION_IF:
+       if(k && v)
+         {
+          for(j=0;j<input->ntags;j++)
+             if(!strcmp(input->k[j],k) && !strcmp(input->v[j],v))
+                ApplyRules(rules->rules[i].rulelist,input,output,input->k[j],input->v[j]);
+         }
+       else if(k && !v)
+         {
+          for(j=0;j<input->ntags;j++)
+             if(!strcmp(input->k[j],k))
+                ApplyRules(rules->rules[i].rulelist,input,output,input->k[j],input->v[j]);
+         }
+       else if(!k && v)
+         {
+          for(j=0;j<input->ntags;j++)
+             if(!strcmp(input->v[j],v))
+                ApplyRules(rules->rules[i].rulelist,input,output,input->k[j],input->v[j]);
+         }
+       else /* if(!k && !v) */
+         {
+          if(!input->ntags)
+             ApplyRules(rules->rules[i].rulelist,input,output,"","");
+          else
+             for(j=0;j<input->ntags;j++)
+                ApplyRules(rules->rules[i].rulelist,input,output,input->k[j],input->v[j]);
+         }
+       break;
+
+      case TAGACTION_IFNOT:
+       if(k && v)
+         {
+          for(j=0;j<input->ntags;j++)
+             if(!strcmp(input->k[j],k) && !strcmp(input->v[j],v))
+                break;
+
+          if(j!=input->ntags)
+             break;
+         }
+       else if(k && !v)
+         {
+          for(j=0;j<input->ntags;j++)
+             if(!strcmp(input->k[j],k))
+                break;
 
-    if(rule->actions[i].k)
-       k=rule->actions[i].k;
-    else
-       k=input->k[match];
+          if(j!=input->ntags)
+             break;
+         }
+       else if(!k && v)
+         {
+          for(j=0;j<input->ntags;j++)
+             if(!strcmp(input->v[j],v))
+                break;
 
-    if(rule->actions[i].v)
-       v=rule->actions[i].v;
-    else
-       v=input->v[match];
+          if(j!=input->ntags)
+             break;
+         }
+       else /* if(!k && !v) */
+         {
+          break;
+         }
 
-    if(rule->actions[i].action==TAGACTION_SET)
+       ApplyRules(rules->rules[i].rulelist,input,output,k,v);
+       break;
+
+      case TAGACTION_SET:
        ModifyTag(input,k,v);
-    if(rule->actions[i].action==TAGACTION_UNSET)
+       break;
+
+      case TAGACTION_UNSET:
        DeleteTag(input,k);
-    if(rule->actions[i].action==TAGACTION_OUTPUT)
+       break;
+
+      case TAGACTION_OUTPUT:
        ModifyTag(output,k,v);
-    if(rule->actions[i].action==TAGACTION_LOGERROR)
-      {
-       if(rules==&NodeRules)
-          logerror("Node %"Pnode_t" has an unrecognised tag value '%s' = '%s' (in tagging rules); ignoring it.\n",id,k,v);
-       if(rules==&WayRules)
-          logerror("Way %"Pway_t" has an unrecognised tag value '%s' = '%s' (in tagging rules); ignoring it.\n",id,k,v);
-       if(rules==&RelationRules)
-          logerror("Relation %"Prelation_t" has an unrecognised tag value '%s' = '%s' (in tagging rules); ignoring it.\n",id,k,v);
+       break;
+
+      case TAGACTION_LOGERROR:
+       if(rules->rules[i].k && !rules->rules[i].v)
+          for(j=0;j<input->ntags;j++)
+             if(!strcmp(input->k[j],rules->rules[i].k))
+               {
+                v=input->v[j];
+                break;
+               }
+
+       if(current_list==&NodeRules)
+          logerror("Node %"Pnode_t" has an unrecognised tag '%s' = '%s' (in tagging rules); %s.\n",logerror_node(current_id),k,v,rules->rules[i].message);
+       if(current_list==&WayRules)
+          logerror("Way %"Pway_t" has an unrecognised tag '%s' = '%s' (in tagging rules); %s.\n",logerror_way(current_id),k,v,rules->rules[i].message);
+       if(current_list==&RelationRules)
+          logerror("Relation %"Prelation_t" has an unrecognised tag '%s' = '%s' (in tagging rules); %s.\n",logerror_relation(current_id),k,v,rules->rules[i].message);
       }
    }
+
+ if(match_k_copy) free(match_k_copy);
+ if(match_v_copy) free(match_v_copy);
 }
diff --git a/src/tagging.h b/src/tagging.h
index 189e9d7..e745718 100644
--- a/src/tagging.h
+++ b/src/tagging.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2011 Andrew M. Bishop
+ This file Copyright 2010-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,49 +22,34 @@
 #ifndef TAGGING_H
 #define TAGGING_H    /*+ To stop multiple inclusions. +*/
 
-#include "typesx.h"
-
-
-/* Constants */
-
-#define TAGACTION_SET      0
-#define TAGACTION_UNSET    1
-#define TAGACTION_OUTPUT   2
-#define TAGACTION_LOGERROR 3
+#include <stdint.h>
 
 
 /* Data types */
 
-/*+ A structure to contain the tagging action. +*/
-typedef struct _TaggingAction
-{
- int action;                    /*+ A flag to indicate the type of action. +*/
-
- char *k;                       /*+ The tag key (or NULL). +*/
- char *v;                       /*+ The tag value (or NULL). +*/
-}
- TaggingAction;
+typedef struct _TaggingRuleList TaggingRuleList;
 
 
-/*+ A structure to contain the tagging rule. +*/
+/*+ A structure to contain the tagging rule/action. +*/
 typedef struct _TaggingRule
 {
+ int action;                    /*+ A flag to indicate the type of action. +*/
+
  char *k;                       /*+ The tag key (or NULL). +*/
  char *v;                       /*+ The tag value (or NULL). +*/
+ char *message;                 /*+ The message string for logerror (or NULL). +*/
 
- TaggingAction *actions;        /*+ The actions to take. +*/
- int            nactions;       /*+ The number of actions. +*/
+ TaggingRuleList *rulelist;     /*+ The sub-rules belonging to this rule. +*/
 }
  TaggingRule;
 
 
 /*+ A structure to contain the list of rules and associated information. +*/
-typedef struct _TaggingRuleList
+struct _TaggingRuleList
 {
  TaggingRule *rules;            /*+ The array of rules. +*/
  int          nrules;           /*+ The number of rules. +*/
-}
- TaggingRuleList;
+};
 
 
 /*+ A structure to hold a list of tags to be processed. +*/
@@ -78,22 +63,11 @@ typedef struct _TagList
  TagList;
 
 
-/* Global variables */
-
-extern TaggingRuleList NodeRules;
-extern TaggingRuleList WayRules;
-extern TaggingRuleList RelationRules;
-
-
 /* Functions in tagging.c */
 
 int ParseXMLTaggingRules(const char *filename);
 void DeleteXMLTaggingRules(void);
 
-TaggingRule *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *v);
-void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int action);
-void DeleteTaggingRuleList(TaggingRuleList *rules);
-
 TagList *NewTagList(void);
 void DeleteTagList(TagList *tags);
 
@@ -101,7 +75,11 @@ void AppendTag(TagList *tags,const char *k,const char *v);
 void ModifyTag(TagList *tags,const char *k,const char *v);
 void DeleteTag(TagList *tags,const char *k);
 
-TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags,node_t id);
+char *StringifyTag(TagList *tags);
+
+TagList *ApplyNodeTaggingRules(TagList *tags,int64_t id);
+TagList *ApplyWayTaggingRules(TagList *tags,int64_t id);
+TagList *ApplyRelationTaggingRules(TagList *tags,int64_t id);
 
 
 #endif /* TAGGING_H */
diff --git a/src/test/Makefile b/src/test/Makefile
index 4db6c6c..7db3d58 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -2,7 +2,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2011-2012 Andrew M. Bishop
+# This file Copyright 2011-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -18,11 +18,17 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Routino executables
+# All configuration is in the top-level Makefile.conf
 
-EXE=../planetsplitter ../planetsplitter-slim \
-    ../router ../router-slim \
-    ../filedumper ../filedumper-slim
+include ../../Makefile.conf
+
+# executables
+
+ROUTINO_EXE=../planetsplitter ../planetsplitter-slim \
+            ../router ../router-slim \
+            ../filedumper ../filedumper-slim
+
+EXE=is-fast-math
 
 # Compilation targets
 
@@ -32,17 +38,13 @@ S=$(foreach f,$(O),$(addsuffix .sh,$(basename $f)))
 ########
 
 all :
-	@true
 
 ########
 
-exe :
-	cd .. && $(MAKE)
-
-########
-
-test : exe
+test : $(ROUTINO_EXE) $(EXE)
 	@status=true ;\
+	echo ""; \
+	./is-fast-math message; \
 	for script in $(S); do \
 	   echo "" ;\
 	   echo "Testing: $$script (non-slim, no pruning) ... " ;\
@@ -83,6 +85,21 @@ test : exe
 
 ########
 
+$(ROUTINO_EXE) :
+	cd .. && $(MAKE) $(notdir $@)
+
+is-fast-math : is-fast-math.o
+	$(LD) $< -o $@ $(LDFLAGS)
+
+is-fast-math.o : is-fast-math.c
+	$(CC) -c $(CFLAGS) $< -o $@
+
+########
+
+install:
+
+########
+
 clean:
 	rm -rf fat
 	rm -rf slim
@@ -90,13 +107,14 @@ clean:
 	rm -rf slim-pruned
 	rm -f *.log
 	rm -f *~
+	rm -f *.o
 	rm -f core
 	rm -f *.gcda *.gcno *.gcov gmon.out
 
 ########
 
 distclean: clean
-	@true
+	rm -f is-fast-math
 
 ########
 
diff --git a/src/test/a-b-c.sh b/src/test/a-b-c.sh
index 1c74f22..f13f51b 100755
--- a/src/test/a-b-c.sh
+++ b/src/test/a-b-c.sh
@@ -93,11 +93,12 @@ for waypoint in $waypoints; do
 
     mv shortest* $dir/$name-$waypoint
 
-    if [ "$pruned" = "" ]; then
+    echo diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
 
-        echo diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
+    if ./is-fast-math; then
+        diff -U 0 expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt | 2>&1 egrep '^[-+] ' || true
+    else
         diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
-
     fi
 
 done
diff --git a/src/test/a-b.sh b/src/test/a-b.sh
index 3e7426e..9064d79 100755
--- a/src/test/a-b.sh
+++ b/src/test/a-b.sh
@@ -92,11 +92,12 @@ for waypoint in $waypoints; do
 
     mv shortest* $dir/$name-$waypoint
 
-    if [ "$pruned" = "" ]; then
+    echo diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
 
-        echo diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
+    if ./is-fast-math; then
+        diff -U 0 expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt | 2>&1 egrep '^[-+] ' || true
+    else
         diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
-
     fi
 
 done
diff --git a/src/test/dead-ends.osm b/src/test/dead-ends.osm
index f3b1684..70a4a22 100644
--- a/src/test/dead-ends.osm
+++ b/src/test/dead-ends.osm
@@ -8,7 +8,6 @@
   <node id='263' visible='true' version='1' lat='-0.2199233360272462' lon='-0.5191790778221413'>
     <tag k='name' v='WP02' />
   </node>
-  <node id='265' visible='true' version='1' lat='-0.21887734290530528' lon='-0.5193788335075634' />
   <node id='267' visible='true' version='1' lat='-0.21923442817890232' lon='-0.519554450499408' />
   <node id='269' visible='true' version='1' lat='-0.21955455458130574' lon='-0.5193510859959124'>
     <tag k='name' v='WP03' />
@@ -45,7 +44,7 @@
     <tag k='name' v='WP05' />
   </node>
   <node id='325' visible='true' version='1' lat='-0.22022296242215664' lon='-0.5210519045762619' />
-  <node id='329' visible='true' version='1' lat='-0.218492980018636' lon='-0.5157886455630277' >
+  <node id='329' visible='true' version='1' lat='-0.218492980018636' lon='-0.5157886455630277'>
     <tag k='name' v='WP11' />
   </node>
   <node id='331' visible='true' version='1' lat='-0.21934091992851262' lon='-0.5162056682348201' />
@@ -76,7 +75,6 @@
     <nd ref='261' />
     <nd ref='263' />
     <nd ref='267' />
-    <nd ref='265' />
     <nd ref='271' />
     <tag k='highway' v='residential' />
     <tag k='name' v='dead-end 1' />
diff --git a/src/test/expected/dead-ends-WP01.txt b/src/test/expected/dead-ends-WP01.txt
index d50bacc..72c402f 100644
--- a/src/test/expected/dead-ends-WP01.txt
+++ b/src/test/expected/dead-ends-WP01.txt
@@ -8,10 +8,10 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220263	  -0.519309	      14*	Waypt	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
+ -0.220263	  -0.519309	      13*	Waypt	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
  -0.220702	  -0.519478	      12*	Junct	0.052	 0.07	 0.41	  0.4	 48	 201	dead-end 1
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.59	  0.7	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.78	  0.9	 48	  91	high street
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.01	  1.2	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.06	  1.2	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.59	  0.7	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.78	  0.9	 48	  91	high street
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.01	  1.2	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.06	  1.2	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.15	  1.3	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP02.txt b/src/test/expected/dead-ends-WP02.txt
index 49d8042..0becf02 100644
--- a/src/test/expected/dead-ends-WP02.txt
+++ b/src/test/expected/dead-ends-WP02.txt
@@ -8,12 +8,12 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220263	  -0.519309	      14*	Junct-	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
- -0.219924	  -0.519179	      15 	Waypt	0.040	 0.05	 0.39	  0.4	 48	  20	dead-end 1
- -0.220263	  -0.519309	      14*	Junct-	0.040	 0.05	 0.43	  0.5	 48	 200	dead-end 1
+ -0.220263	  -0.519309	      13*	Junct-	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
+ -0.219924	  -0.519179	      14 	Waypt	0.040	 0.05	 0.39	  0.4	 48	  20	dead-end 1
+ -0.220263	  -0.519309	      13*	Junct-	0.040	 0.05	 0.43	  0.5	 48	 200	dead-end 1
  -0.220702	  -0.519478	      12*	Junct	0.052	 0.07	 0.48	  0.5	 48	 201	dead-end 1
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.67	  0.8	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.86	  1.0	 48	  91	high street
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.09	  1.3	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.14	  1.3	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.67	  0.8	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.86	  1.0	 48	  91	high street
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.09	  1.3	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.14	  1.3	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.23	  1.4	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP03.txt b/src/test/expected/dead-ends-WP03.txt
index 45c7610..52cdacc 100644
--- a/src/test/expected/dead-ends-WP03.txt
+++ b/src/test/expected/dead-ends-WP03.txt
@@ -8,14 +8,14 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220263	  -0.519309	      14*	Junct-	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
- -0.219924	  -0.519179	      15 	Inter	0.040	 0.05	 0.39	  0.4	 48	  20	dead-end 1
+ -0.220263	  -0.519309	      13*	Junct-	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
+ -0.219924	  -0.519179	      14 	Inter	0.040	 0.05	 0.39	  0.4	 48	  20	dead-end 1
  -0.219567	  -0.519373	      -2 	Waypt	0.045	 0.06	 0.44	  0.5	 48	 331	dead-end 1
- -0.219924	  -0.519179	      15 	Inter	0.045	 0.06	 0.48	  0.5	 48	 151	dead-end 1
- -0.220263	  -0.519309	      14*	Junct-	0.040	 0.05	 0.52	  0.6	 48	 200	dead-end 1
+ -0.219924	  -0.519179	      14 	Inter	0.045	 0.06	 0.48	  0.5	 48	 151	dead-end 1
+ -0.220263	  -0.519309	      13*	Junct-	0.040	 0.05	 0.52	  0.6	 48	 200	dead-end 1
  -0.220702	  -0.519478	      12*	Junct	0.052	 0.07	 0.57	  0.6	 48	 201	dead-end 1
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.76	  0.9	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.95	  1.1	 48	  91	high street
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.18	  1.4	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.23	  1.4	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.76	  0.9	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.95	  1.1	 48	  91	high street
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.18	  1.4	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.23	  1.4	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.32	  1.5	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP04.txt b/src/test/expected/dead-ends-WP04.txt
index d35b11f..941340a 100644
--- a/src/test/expected/dead-ends-WP04.txt
+++ b/src/test/expected/dead-ends-WP04.txt
@@ -8,18 +8,16 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220263	  -0.519309	      14*	Junct-	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
- -0.219924	  -0.519179	      15 	Inter	0.040	 0.05	 0.39	  0.4	 48	  20	dead-end 1
+ -0.220263	  -0.519309	      13*	Junct-	0.052	 0.07	 0.35	  0.4	 48	  21	dead-end 1
+ -0.219924	  -0.519179	      14 	Inter	0.040	 0.05	 0.39	  0.4	 48	  20	dead-end 1
  -0.219235	  -0.519555	      11 	Inter	0.087	 0.11	 0.48	  0.5	 48	 331	dead-end 1
- -0.218878	  -0.519379	      13 	Inter	0.044	 0.06	 0.52	  0.6	 48	  26	dead-end 1
- -0.218387	  -0.519137	      16 	Waypt	0.060	 0.07	 0.58	  0.7	 48	  26	dead-end 1
- -0.218878	  -0.519379	      13 	Inter	0.060	 0.07	 0.64	  0.7	 48	 206	dead-end 1
- -0.219235	  -0.519555	      11 	Inter	0.044	 0.06	 0.69	  0.8	 48	 206	dead-end 1
- -0.219924	  -0.519179	      15 	Inter	0.087	 0.11	 0.78	  0.9	 48	 151	dead-end 1
- -0.220263	  -0.519309	      14*	Junct-	0.040	 0.05	 0.81	  0.9	 48	 200	dead-end 1
+ -0.218387	  -0.519137	      15 	Waypt	0.105	 0.13	 0.58	  0.7	 48	  26	dead-end 1
+ -0.219235	  -0.519555	      11 	Inter	0.105	 0.13	 0.69	  0.8	 48	 206	dead-end 1
+ -0.219924	  -0.519179	      14 	Inter	0.087	 0.11	 0.78	  0.9	 48	 151	dead-end 1
+ -0.220263	  -0.519309	      13*	Junct-	0.040	 0.05	 0.82	  0.9	 48	 200	dead-end 1
  -0.220702	  -0.519478	      12*	Junct	0.052	 0.07	 0.87	  1.0	 48	 201	dead-end 1
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 1.05	  1.2	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 1.24	  1.5	 48	  91	high street
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.47	  1.8	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.52	  1.8	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 1.05	  1.2	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 1.24	  1.5	 48	  91	high street
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.47	  1.8	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.52	  1.8	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.61	  1.8	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP05.txt b/src/test/expected/dead-ends-WP05.txt
index 05bc0af..c243705 100644
--- a/src/test/expected/dead-ends-WP05.txt
+++ b/src/test/expected/dead-ends-WP05.txt
@@ -8,10 +8,10 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
- -0.220296	  -0.517634	      21*	Waypt	0.052	 0.07	 0.54	  0.6	 48	  21	dead-end 2
- -0.220739	  -0.517804	      19*	Junct	0.052	 0.07	 0.59	  0.7	 48	 201	dead-end 2
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.78	  0.9	 48	  91	high street
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.01	  1.2	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.06	  1.2	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
+ -0.220296	  -0.517634	      20*	Waypt	0.052	 0.07	 0.54	  0.6	 48	  21	dead-end 2
+ -0.220739	  -0.517804	      18*	Junct	0.052	 0.07	 0.59	  0.7	 48	 201	dead-end 2
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.78	  0.9	 48	  91	high street
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.01	  1.2	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.06	  1.2	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.15	  1.3	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP06.txt b/src/test/expected/dead-ends-WP06.txt
index 780c373..086b2fe 100644
--- a/src/test/expected/dead-ends-WP06.txt
+++ b/src/test/expected/dead-ends-WP06.txt
@@ -8,12 +8,12 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
- -0.220296	  -0.517634	      21*	Junct-	0.052	 0.07	 0.54	  0.6	 48	  21	dead-end 2
- -0.219991	  -0.517512	      22 	Waypt	0.036	 0.04	 0.57	  0.6	 48	  21	dead-end 2
- -0.220296	  -0.517634	      21*	Junct-	0.036	 0.04	 0.61	  0.7	 48	 201	dead-end 2
- -0.220739	  -0.517804	      19*	Junct	0.052	 0.07	 0.66	  0.7	 48	 201	dead-end 2
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.85	  1.0	 48	  91	high street
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.08	  1.3	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.13	  1.3	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
+ -0.220296	  -0.517634	      20*	Junct-	0.052	 0.07	 0.54	  0.6	 48	  21	dead-end 2
+ -0.219991	  -0.517512	      21 	Waypt	0.036	 0.04	 0.57	  0.6	 48	  21	dead-end 2
+ -0.220296	  -0.517634	      20*	Junct-	0.036	 0.04	 0.61	  0.7	 48	 201	dead-end 2
+ -0.220739	  -0.517804	      18*	Junct	0.052	 0.07	 0.66	  0.7	 48	 201	dead-end 2
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.85	  1.0	 48	  91	high street
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.08	  1.3	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.13	  1.3	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.22	  1.4	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP07.txt b/src/test/expected/dead-ends-WP07.txt
index 2f80486..946c8a9 100644
--- a/src/test/expected/dead-ends-WP07.txt
+++ b/src/test/expected/dead-ends-WP07.txt
@@ -8,14 +8,14 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
- -0.220296	  -0.517634	      21*	Junct-	0.052	 0.07	 0.54	  0.6	 48	  21	dead-end 2
- -0.219991	  -0.517512	      22 	Inter	0.036	 0.04	 0.57	  0.6	 48	  21	dead-end 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
+ -0.220296	  -0.517634	      20*	Junct-	0.052	 0.07	 0.54	  0.6	 48	  21	dead-end 2
+ -0.219991	  -0.517512	      21 	Inter	0.036	 0.04	 0.57	  0.6	 48	  21	dead-end 2
  -0.219635	  -0.517707	      -2 	Waypt	0.045	 0.06	 0.62	  0.7	 48	 331	dead-end 2
- -0.219991	  -0.517512	      22 	Inter	0.045	 0.06	 0.67	  0.7	 48	 151	dead-end 2
- -0.220296	  -0.517634	      21*	Junct-	0.036	 0.04	 0.70	  0.8	 48	 201	dead-end 2
- -0.220739	  -0.517804	      19*	Junct	0.052	 0.07	 0.75	  0.9	 48	 201	dead-end 2
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.94	  1.1	 48	  91	high street
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.17	  1.4	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.22	  1.4	 96	   2	main 2
+ -0.219991	  -0.517512	      21 	Inter	0.045	 0.06	 0.67	  0.7	 48	 151	dead-end 2
+ -0.220296	  -0.517634	      20*	Junct-	0.036	 0.04	 0.70	  0.8	 48	 201	dead-end 2
+ -0.220739	  -0.517804	      18*	Junct	0.052	 0.07	 0.75	  0.9	 48	 201	dead-end 2
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.94	  1.1	 48	  91	high street
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.17	  1.4	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.22	  1.4	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.31	  1.5	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP08.txt b/src/test/expected/dead-ends-WP08.txt
index a0d1efa..eedd51d 100644
--- a/src/test/expected/dead-ends-WP08.txt
+++ b/src/test/expected/dead-ends-WP08.txt
@@ -8,10 +8,10 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
- -0.220361	  -0.515961	      26*	Waypt	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
- -0.220782	  -0.516137	      25*	Junct	0.050	 0.06	 0.77	  0.9	 48	 202	dead-end 3
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.00	  1.2	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.05	  1.2	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
+ -0.220361	  -0.515961	      25*	Waypt	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
+ -0.220782	  -0.516137	      24*	Junct	0.050	 0.06	 0.77	  0.9	 48	 202	dead-end 3
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.00	  1.2	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.05	  1.2	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.14	  1.3	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP09.txt b/src/test/expected/dead-ends-WP09.txt
index 8c5eae6..97db084 100644
--- a/src/test/expected/dead-ends-WP09.txt
+++ b/src/test/expected/dead-ends-WP09.txt
@@ -8,12 +8,12 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
- -0.220361	  -0.515961	      26*	Junct-	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
- -0.220019	  -0.515847	      27 	Waypt	0.040	 0.05	 0.76	  0.9	 48	  18	dead-end 3
- -0.220361	  -0.515961	      26*	Junct-	0.040	 0.05	 0.80	  0.9	 48	 198	dead-end 3
- -0.220782	  -0.516137	      25*	Junct	0.050	 0.06	 0.85	  1.0	 48	 202	dead-end 3
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.08	  1.3	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.13	  1.3	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
+ -0.220361	  -0.515961	      25*	Junct-	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
+ -0.220019	  -0.515847	      26 	Waypt	0.040	 0.05	 0.76	  0.9	 48	  18	dead-end 3
+ -0.220361	  -0.515961	      25*	Junct-	0.040	 0.05	 0.80	  0.9	 48	 198	dead-end 3
+ -0.220782	  -0.516137	      24*	Junct	0.050	 0.06	 0.85	  1.0	 48	 202	dead-end 3
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.08	  1.3	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.13	  1.3	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.22	  1.4	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP10.txt b/src/test/expected/dead-ends-WP10.txt
index a879f2c..b75124f 100644
--- a/src/test/expected/dead-ends-WP10.txt
+++ b/src/test/expected/dead-ends-WP10.txt
@@ -8,14 +8,14 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
- -0.220361	  -0.515961	      26*	Junct-	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
- -0.220019	  -0.515847	      27 	Inter	0.040	 0.05	 0.76	  0.9	 48	  18	dead-end 3
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
+ -0.220361	  -0.515961	      25*	Junct-	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
+ -0.220019	  -0.515847	      26 	Inter	0.040	 0.05	 0.76	  0.9	 48	  18	dead-end 3
  -0.219672	  -0.516031	      -2 	Waypt	0.043	 0.05	 0.81	  0.9	 48	 332	dead-end 3
- -0.220019	  -0.515847	      27 	Inter	0.043	 0.05	 0.85	  1.0	 48	 152	dead-end 3
- -0.220361	  -0.515961	      26*	Junct-	0.040	 0.05	 0.89	  1.0	 48	 198	dead-end 3
- -0.220782	  -0.516137	      25*	Junct	0.050	 0.06	 0.94	  1.1	 48	 202	dead-end 3
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.17	  1.4	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.22	  1.4	 96	   2	main 2
+ -0.220019	  -0.515847	      26 	Inter	0.043	 0.05	 0.85	  1.0	 48	 152	dead-end 3
+ -0.220361	  -0.515961	      25*	Junct-	0.040	 0.05	 0.89	  1.0	 48	 198	dead-end 3
+ -0.220782	  -0.516137	      24*	Junct	0.050	 0.06	 0.94	  1.1	 48	 202	dead-end 3
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.17	  1.4	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.22	  1.4	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.31	  1.5	 96	   2	main 2
diff --git a/src/test/expected/dead-ends-WP11.txt b/src/test/expected/dead-ends-WP11.txt
index 3267bf3..3cbc217 100644
--- a/src/test/expected/dead-ends-WP11.txt
+++ b/src/test/expected/dead-ends-WP11.txt
@@ -8,16 +8,16 @@
  -0.220223	  -0.521052	       5*	Junct-	0.076	 0.05	 0.08	  0.0	 96	 182	main 1
  -0.220666	  -0.521060	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 180	main 1
  -0.220702	  -0.519478	      12*	Junct	0.176	 0.22	 0.30	  0.3	 48	  91	high street
- -0.220739	  -0.517804	      19*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
- -0.220782	  -0.516137	      25*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
- -0.220361	  -0.515961	      26*	Junct-	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
- -0.220019	  -0.515847	      27 	Inter	0.040	 0.05	 0.76	  0.9	 48	  18	dead-end 3
- -0.219341	  -0.516206	      24 	Inter	0.085	 0.10	 0.85	  1.0	 48	 332	dead-end 3
- -0.218493	  -0.515789	      28*	Waypt	0.105	 0.13	 0.95	  1.1	 48	  26	dead-end 3
- -0.219341	  -0.516206	      24 	Inter	0.105	 0.13	 1.06	  1.2	 48	 206	dead-end 3
- -0.220019	  -0.515847	      27 	Inter	0.085	 0.10	 1.14	  1.3	 48	 152	dead-end 3
- -0.220361	  -0.515961	      26*	Junct-	0.040	 0.05	 1.18	  1.4	 48	 198	dead-end 3
- -0.220782	  -0.516137	      25*	Junct	0.050	 0.06	 1.23	  1.5	 48	 202	dead-end 3
- -0.220817	  -0.514062	      33*	Junct	0.230	 0.29	 1.46	  1.7	 48	  90	high street
- -0.220344	  -0.514042	      34*	Junct-	0.052	 0.03	 1.51	  1.8	 96	   2	main 2
+ -0.220739	  -0.517804	      18*	Junct	0.186	 0.23	 0.49	  0.5	 48	  91	high street
+ -0.220782	  -0.516137	      24*	Junct	0.185	 0.23	 0.67	  0.8	 48	  91	high street
+ -0.220361	  -0.515961	      25*	Junct-	0.050	 0.06	 0.72	  0.8	 48	  22	dead-end 3
+ -0.220019	  -0.515847	      26 	Inter	0.040	 0.05	 0.76	  0.9	 48	  18	dead-end 3
+ -0.219341	  -0.516206	      23 	Inter	0.085	 0.10	 0.85	  1.0	 48	 332	dead-end 3
+ -0.218493	  -0.515789	      27*	Waypt	0.105	 0.13	 0.95	  1.1	 48	  26	dead-end 3
+ -0.219341	  -0.516206	      23 	Inter	0.105	 0.13	 1.06	  1.2	 48	 206	dead-end 3
+ -0.220019	  -0.515847	      26 	Inter	0.085	 0.10	 1.14	  1.3	 48	 152	dead-end 3
+ -0.220361	  -0.515961	      25*	Junct-	0.040	 0.05	 1.18	  1.4	 48	 198	dead-end 3
+ -0.220782	  -0.516137	      24*	Junct	0.050	 0.06	 1.23	  1.5	 48	 202	dead-end 3
+ -0.220817	  -0.514062	      32*	Junct	0.230	 0.29	 1.46	  1.7	 48	  90	high street
+ -0.220344	  -0.514042	      33*	Junct-	0.052	 0.03	 1.51	  1.8	 96	   2	main 2
  -0.219539	  -0.514007	      -3 	Waypt	0.089	 0.06	 1.60	  1.8	 96	   2	main 2
diff --git a/src/test/expected/node-restrictions-WP08.txt b/src/test/expected/node-restrictions-WP08.txt
index 6bfadb5..d9df640 100644
--- a/src/test/expected/node-restrictions-WP08.txt
+++ b/src/test/expected/node-restrictions-WP08.txt
@@ -7,16 +7,16 @@
  -0.219564	  -0.520846	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
  -0.220666	  -0.520893	       4*	Junct	0.122	 0.07	 0.12	  0.1	 96	 182	main 1
  -0.220682	  -0.520141	       9*	Junct	0.083	 0.08	 0.20	  0.2	 64	  91	high street
- -0.219054	  -0.520077	      -2 	Waypt	0.180	 0.17	 0.39	  0.3	 64	   2	dead end road
- -0.220682	  -0.520141	       9*	Junct	0.180	 0.17	 0.56	  0.5	 64	 182	dead end road
- -0.220695	  -0.519489	      12*	Change	0.072	 0.07	 0.64	  0.6	 64	  91	high street
- -0.220694	  -0.519227	      15 	Inter	0.029	 0.04	 0.67	  0.6	 48	  89	long road
- -0.220386	  -0.519076	      16 	Inter	0.038	 0.05	 0.70	  0.6	 48	  26	long road
- -0.220961	  -0.518928	      17 	Inter	0.066	 0.08	 0.77	  0.7	 48	 165	long road
- -0.220427	  -0.518622	      18 	Inter	0.068	 0.09	 0.84	  0.8	 48	  29	long road
- -0.220949	  -0.518422	      21 	Inter	0.062	 0.08	 0.90	  0.9	 48	 159	long road
- -0.220455	  -0.518238	      22 	Inter	0.058	 0.07	 0.96	  1.0	 48	  20	long road
- -0.220746	  -0.518070	      23 	Inter	0.037	 0.04	 0.99	  1.0	 48	 149	long road
- -0.220739	  -0.517801	      26*	Junct	0.029	 0.04	 1.02	  1.0	 48	  88	long road
- -0.220784	  -0.516035	      31*	Junct	0.196	 0.18	 1.22	  1.2	 64	  91	high street
- -0.219593	  -0.515985	      -3 	Waypt	0.132	 0.08	 1.35	  1.3	 96	   2	main 2
+ -0.219009	  -0.520075	      10*	Waypt	0.186	 0.17	 0.39	  0.3	 64	   2	dead end road
+ -0.220682	  -0.520141	       9*	Junct	0.186	 0.17	 0.58	  0.5	 64	 182	dead end road
+ -0.220695	  -0.519489	      12*	Change	0.072	 0.07	 0.65	  0.6	 64	  91	high street
+ -0.220694	  -0.519227	      15 	Inter	0.029	 0.04	 0.68	  0.6	 48	  89	long road
+ -0.220386	  -0.519076	      16 	Inter	0.038	 0.05	 0.72	  0.6	 48	  26	long road
+ -0.220961	  -0.518928	      17 	Inter	0.066	 0.08	 0.78	  0.7	 48	 165	long road
+ -0.220427	  -0.518622	      18 	Inter	0.068	 0.09	 0.85	  0.8	 48	  29	long road
+ -0.220949	  -0.518422	      21 	Inter	0.062	 0.08	 0.91	  0.9	 48	 159	long road
+ -0.220455	  -0.518238	      22 	Inter	0.058	 0.07	 0.97	  1.0	 48	  20	long road
+ -0.220746	  -0.518070	      23 	Inter	0.037	 0.04	 1.01	  1.0	 48	 149	long road
+ -0.220739	  -0.517801	      26*	Junct	0.029	 0.04	 1.04	  1.0	 48	  88	long road
+ -0.220784	  -0.516035	      31*	Junct	0.196	 0.18	 1.23	  1.2	 64	  91	high street
+ -0.219593	  -0.515985	      -3 	Waypt	0.132	 0.08	 1.36	  1.3	 96	   2	main 2
diff --git a/src/test/is-fast-math.c b/src/test/is-fast-math.c
new file mode 100644
index 0000000..78c667b
--- /dev/null
+++ b/src/test/is-fast-math.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+
+int main(int argc,char **argv)
+{
+#ifdef __FAST_MATH__
+
+ if(argc>1)
+    printf("Compiled with -ffast-math => results may differ slightly.\n");
+
+ return 0;
+
+#else
+
+ if(argc>1)
+    printf("Not compiled with -ffast-math => results should match exactly.\n");
+
+ return 1;
+
+#endif
+}
diff --git a/src/test/node-restrictions.osm b/src/test/node-restrictions.osm
index cfe608d..5682e14 100644
--- a/src/test/node-restrictions.osm
+++ b/src/test/node-restrictions.osm
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<osm version='0.6' upload='true' generator='JOSM'>
+<osm version='0.6' generator='JOSM'>
   <node id='3' visible='true' version='1' lat='-0.2217201380129468' lon='-0.5209275966384278' />
   <node id='4' visible='true' version='1' lat='-0.21828070777942268' lon='-0.5207912709437164' />
   <node id='6' visible='true' version='1' lat='-0.21804206074333024' lon='-0.5205359496121407' />
diff --git a/src/test/oneway-loop.osm b/src/test/oneway-loop.osm
index 57ef1ab..78eaf6f 100644
--- a/src/test/oneway-loop.osm
+++ b/src/test/oneway-loop.osm
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<osm version='0.6' upload='true' generator='JOSM'>
+<osm version='0.6' generator='JOSM'>
   <node id='97' visible='true' version='1' lat='-0.2203330521761548' lon='-0.5194857377924652' />
   <node id='95' visible='true' version='1' lat='-0.22040525704431851' lon='-0.517799295180183' />
   <node id='3' visible='true' version='1' lat='-0.2217201380129468' lon='-0.5209275966384278' />
diff --git a/src/test/only-split.sh b/src/test/only-split.sh
index 3942322..b823d8c 100755
--- a/src/test/only-split.sh
+++ b/src/test/only-split.sh
@@ -21,7 +21,14 @@ fi
 # Pruned or non-pruned
 
 if [ "$2" = "prune" ]; then
-    prune=""
+
+    case $name in
+        prune-isolated) prune="--prune-none --prune-isolated=100";;
+        prune-short)    prune="--prune-none --prune-short=5";;
+        prune-straight) prune="--prune-none --prune-straight=5";;
+        *)              prune="";;
+    esac
+
     pruned="-pruned"
 else
     prune="--prune-none"
diff --git a/src/test/prune-short.osm b/src/test/prune-short.osm
new file mode 100644
index 0000000..d78ef6e
--- /dev/null
+++ b/src/test/prune-short.osm
@@ -0,0 +1,629 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='3595' visible='true' version='1' lat='-0.22104660009157598' lon='-0.5159975959146365'>
+    <tag k='barrier' v='bollard' />
+  </node>
+  <node id='3594' visible='true' version='1' lat='-0.22045049050193835' lon='-0.5159942163952338' />
+  <node id='3593' visible='true' version='1' lat='-0.2201788312678428' lon='-0.5159976002642083' />
+  <node id='3592' visible='true' version='1' lat='-0.22045462692616266' lon='-0.5163068279854052' />
+  <node id='3591' visible='true' version='1' lat='-0.22044209075998147' lon='-0.5156633000250767' />
+  <node id='3288' visible='true' version='1' lat='-0.22109734126804137' lon='-0.5152506229571332' />
+  <node id='3217' visible='true' version='1' lat='-0.22113406029065996' lon='-0.5140671692051982' />
+  <node id='3179' visible='true' version='1' lat='-0.2210294380331181' lon='-0.5167574844040262'>
+    <tag k='barrier' v='bollard' />
+    <tag k='goods' v='yes' />
+    <tag k='motorcar' v='yes' />
+  </node>
+  <node id='3177' visible='true' version='1' lat='-0.2204158596326279' lon='-0.5178043240356551' />
+  <node id='3176' visible='true' version='1' lat='-0.22046453886858514' lon='-0.5152482570652049' />
+  <node id='3175' visible='true' version='1' lat='-0.22048093018165196' lon='-0.5145050025515154' />
+  <node id='3173' visible='true' version='1' lat='-0.22036977472541366' lon='-0.5200841499753365' />
+  <node id='3172' visible='true' version='1' lat='-0.22037260530871416' lon='-0.5186863255910119' />
+  <node id='3171' visible='true' version='1' lat='-0.22019287963474385' lon='-0.5152516409341795' />
+  <node id='3170' visible='true' version='1' lat='-0.22107893616237437' lon='-0.5145084240485217' />
+  <node id='3169' visible='true' version='1' lat='-0.22100970613768825' lon='-0.5174950919648864'>
+    <tag k='barrier' v='bollard' />
+  </node>
+  <node id='3168' visible='true' version='1' lat='-0.22012693295915078' lon='-0.5182599378763078' />
+  <node id='3166' visible='true' version='1' lat='-0.2203377650859623' lon='-0.5202133610231608' />
+  <node id='3165' visible='true' version='1' lat='-0.22034616420484693' lon='-0.5205477105564476' />
+  <node id='3164' visible='true' version='1' lat='-0.22101540883355839' lon='-0.519020790240951' />
+  <node id='3162' visible='true' version='1' lat='-0.2203656383011626' lon='-0.5197715383851653' />
+  <node id='3160' visible='true' version='1' lat='-0.22108342504232312' lon='-0.5159972875495841' />
+  <node id='3159' visible='true' version='1' lat='-0.22047253043973514' lon='-0.5141740861813582' />
+  <node id='3158' visible='true' version='1' lat='-0.22038100505069896' lon='-0.5190172419611692' />
+  <node id='3155' visible='true' version='1' lat='-0.22096555172390003' lon='-0.5197776970962421' />
+  <node id='3154' visible='true' version='1' lat='-0.22103299366341264' lon='-0.5182603413162163' />
+  <node id='3153' visible='true' version='1' lat='-0.22098056861233564' lon='-0.5205478256731001' />
+  <node id='3152' visible='true' version='1' lat='-0.22015988957621005' lon='-0.5167574467759948' />
+  <node id='3151' visible='true' version='1' lat='-0.2211153339602629' lon='-0.5145085508312978' />
+  <node id='3150' visible='true' version='1' lat='-0.220435685234862' lon='-0.5170666744971915' />
+  <node id='3148' visible='true' version='1' lat='-0.2201093458153487' lon='-0.5190206258301437' />
+  <node id='3147' visible='true' version='1' lat='-0.22104625848691606' lon='-0.5174952321326493' />
+  <node id='3146' visible='true' version='1' lat='-0.22094525908123056' lon='-0.5205478161580427' />
+  <node id='3145' visible='true' version='1' lat='-0.22035723855918068' lon='-0.5194406220150082' />
+  <node id='3144' visible='true' version='1' lat='-0.22097925560094275' lon='-0.5190206214805714' />
+  <node id='3143' visible='true' version='1' lat='-0.220468675292795' lon='-0.5155608686553763' />
+  <node id='3142' visible='true' version='1' lat='-0.22097252594183314' lon='-0.5210600600464496' />
+  <node id='3141' visible='true' version='1' lat='-0.22099644131416435' lon='-0.5182602011484532' />
+  <node id='3140' visible='true' version='1' lat='-0.22039859219419322' lon='-0.518256554007333' />
+  <node id='3139' visible='true' version='1' lat='-0.22007450559198807' lon='-0.5205476612622925' />
+  <node id='3137' visible='true' version='1' lat='-0.22042314906869445' lon='-0.5164231465368627' />
+  <node id='3136' visible='true' version='1' lat='-0.22045613912663242' lon='-0.5149173406950477' />
+  <node id='3135' visible='true' version='1' lat='-0.2210659903823021' lon='-0.5167576245717892' />
+  <node id='3134' visible='true' version='1' lat='-0.2202092709481208' lon='-0.5145083864204898' />
+  <node id='3133' visible='true' version='1' lat='-0.22106078891889977' lon='-0.5152504827893704'>
+    <tag k='junction' v='roundabout' />
+  </node>
+  <node id='3132' visible='true' version='1' lat='-0.2203851414749569' lon='-0.5193298535513403' />
+  <node id='3131' visible='true' version='1' lat='-0.22041172320838295' lon='-0.5174917124454843' />
+  <node id='3129' visible='true' version='1' lat='-0.2204027286184389' lon='-0.5185691655975043' />
+  <node id='3127' visible='true' version='1' lat='-0.22035030125222277' lon='-0.5208568889834893' />
+  <node id='3126' visible='true' version='1' lat='-0.22043154881062796' lon='-0.5167540629070206' />
+  <node id='3123' visible='true' version='1' lat='-0.2201400639735915' lon='-0.5174950963144586' />
+  <node id='3122' visible='true' version='1' lat='-0.220093979065534' lon='-0.5197749222541397' />
+  <node id='3120' visible='true' version='1' lat='-0.2204850666058679' lon='-0.5148176141416868' />
+  <node id='3119' visible='true' version='1' lat='-0.22040332346643296' lon='-0.5171607960753265' />
+  <node id='3118' visible='true' version='1' lat='-0.22039019245221977' lon='-0.5179256376371758' />
+  <node id='3116' visible='true' version='1' lat='-0.22100130433783766' lon='-0.5197781875878197' />
+  <node id='103' visible='true' version='1' lat='-0.2199921202808201' lon='-0.5215014225990243' />
+  <node id='105' visible='true' version='1' lat='-0.2200061092774588' lon='-0.5215256957971467' />
+  <node id='111' visible='true' version='1' lat='-0.21999591111254738' lon='-0.5213655177256614' />
+  <node id='113' visible='true' version='1' lat='-0.2199053434653081' lon='-0.5213683099539047' />
+  <node id='115' visible='true' version='1' lat='-0.2200003872856441' lon='-0.5212054858376033' />
+  <node id='117' visible='true' version='1' lat='-0.21995987449775212' lon='-0.5212056899383996' />
+  <node id='122' visible='true' version='1' lat='-0.21930388302075823' lon='-0.5204844403665726' />
+  <node id='124' visible='true' version='1' lat='-0.2183978198987466' lon='-0.520484275955765' />
+  <node id='126' visible='true' version='1' lat='-0.2186736155898939' lon='-0.5207935036769619' />
+  <node id='127' visible='true' version='1' lat='-0.21866947916516918' lon='-0.5204808920867906' />
+  <node id='130' visible='true' version='1' lat='-0.21866107942222732' lon='-0.5201499757166331' />
+  <node id='140' visible='true' version='1' lat='-0.21889092115998834' lon='-0.520482130632451' />
+  <node id='142' visible='true' version='1' lat='-0.21892747351440356' lon='-0.520482270800214' />
+  <node id='175' visible='true' version='1' lat='-0.21894694698945158' lon='-0.5197095317920613' />
+  <node id='176' visible='true' version='1' lat='-0.2184172933744773' lon='-0.5197115369476123' />
+  <node id='177' visible='true' version='1' lat='-0.21868895264054233' lon='-0.5197081530786378' />
+  <node id='178' visible='true' version='1' lat='-0.21869308906526194' lon='-0.520020764668809' />
+  <node id='179' visible='true' version='1' lat='-0.21868055289761731' lon='-0.5193772367084805' />
+  <node id='180' visible='true' version='1' lat='-0.21891039463507056' lon='-0.5197093916242983' />
+  <node id='183' visible='true' version='1' lat='-0.21932461874859735' lon='-0.5197148022812921' />
+  <node id='203' visible='true' version='1' lat='-0.21933872324591014' lon='-0.5189574049344235' />
+  <node id='204' visible='true' version='1' lat='-0.21896231374044123' lon='-0.5189552353680651' />
+  <node id='205' visible='true' version='1' lat='-0.21843266012600215' lon='-0.518957240523616' />
+  <node id='206' visible='true' version='1' lat='-0.21870431939180376' lon='-0.5189538566546416' />
+  <node id='207' visible='true' version='1' lat='-0.21870845581652365' lon='-0.5192664682448129' />
+  <node id='208' visible='true' version='1' lat='-0.21869591964887972' lon='-0.5186229402844842' />
+  <node id='209' visible='true' version='1' lat='-0.21892576138609787' lon='-0.518955095200302' />
+  <node id='242' visible='true' version='1' lat='-0.2187135067943563' lon='-0.5178622523306481' />
+  <node id='243' visible='true' version='1' lat='-0.21935631039062914' lon='-0.5181967169805877' />
+  <node id='244' visible='true' version='1' lat='-0.21845024727178483' lon='-0.5181965525697801' />
+  <node id='245' visible='true' version='1' lat='-0.21894334853131397' lon='-0.5181944072464661' />
+  <node id='246' visible='true' version='1' lat='-0.21897990088560967' lon='-0.5181945474142293' />
+  <node id='247' visible='true' version='1' lat='-0.21872604296198137' lon='-0.5185057802909768' />
+  <node id='248' visible='true' version='1' lat='-0.21872190653727555' lon='-0.5181931687008056' />
+  <node id='263' visible='true' version='1' lat='-0.22000344006187805' lon='-0.5209828473984814' />
+  <node id='265' visible='true' version='1' lat='-0.219420576342371' lon='-0.5151870004920865' />
+  <node id='278' visible='true' version='1' lat='-0.21904569740319257' lon='-0.5151860679810479'>
+    <tag k='junction' v='roundabout' />
+  </node>
+  <node id='279' visible='true' version='1' lat='-0.21878770305599066' lon='-0.5151846892676242' />
+  <node id='281' visible='true' version='1' lat='-0.21877930331311232' lon='-0.5148537728974668' />
+  <node id='282' visible='true' version='1' lat='-0.21900914504905303' lon='-0.5151859278132848' />
+  <node id='283' visible='true' version='1' lat='-0.21851604379168485' lon='-0.5151880731365988' />
+  <node id='284' visible='true' version='1' lat='-0.21879183948067282' lon='-0.5154973008577955' />
+  <node id='306' visible='true' version='1' lat='-0.2194386483838444' lon='-0.5144451655247703' />
+  <node id='307' visible='true' version='1' lat='-0.21906223888087323' lon='-0.5144429959584117' />
+  <node id='308' visible='true' version='1' lat='-0.21853258526998015' lon='-0.5144450011139624' />
+  <node id='309' visible='true' version='1' lat='-0.21880424453396874' lon='-0.514441617244988' />
+  <node id='310' visible='true' version='1' lat='-0.21880838095864102' lon='-0.5147542288351594' />
+  <node id='311' visible='true' version='1' lat='-0.21879584479109798' lon='-0.5141107008748307' />
+  <node id='312' visible='true' version='1' lat='-0.2190256865268027' lon='-0.5144428557906486' />
+  <node id='335' visible='true' version='1' lat='-0.21936944140574963' lon='-0.5174318754187385' />
+  <node id='336' visible='true' version='1' lat='-0.21899303190104727' lon='-0.51742970585238' />
+  <node id='337' visible='true' version='1' lat='-0.2184633782877009' lon='-0.5174317110079311' />
+  <node id='338' visible='true' version='1' lat='-0.21873503755293217' lon='-0.5174283271389567' />
+  <node id='339' visible='true' version='1' lat='-0.21873917397764245' lon='-0.5177409387291276' />
+  <node id='340' visible='true' version='1' lat='-0.21872663781003568' lon='-0.517097410768799' />
+  <node id='341' visible='true' version='1' lat='-0.21944646904283585' lon='-0.5139990438249045' />
+  <node id='342' visible='true' version='1' lat='-0.21895647954678618' lon='-0.517429565684617'>
+    <tag k='barrier' v='bollard' />
+  </node>
+  <node id='343' visible='true' version='1' lat='-0.21929584034936425' lon='-0.520996674739922' />
+  <node id='345' visible='true' version='1' lat='-0.22192916244766508' lon='-0.514332326947849' />
+  <node id='347' visible='true' version='1' lat='-0.2219482141512544' lon='-0.5138620338011963' />
+  <node id='349' visible='true' version='1' lat='-0.22217484074642313' lon='-0.5141041642151607' />
+  <node id='351' visible='true' version='1' lat='-0.22170436926410486' lon='-0.5140901916750656' />
+  <node id='353' visible='true' version='1' lat='-0.21801516712553526' lon='-0.5141909410905267' />
+  <node id='355' visible='true' version='1' lat='-0.2177803096434024' lon='-0.5139345260591944' />
+  <node id='357' visible='true' version='1' lat='-0.2180357522245965' lon='-0.5136983548940309' />
+  <node id='359' visible='true' version='1' lat='-0.21827293796270247' lon='-0.5139541080318566' />
+  <node id='361' visible='true' version='1' lat='-0.22193543526189635' lon='-0.5213371153581924' />
+  <node id='363' visible='true' version='1' lat='-0.22195451857008858' lon='-0.5208670528071448' />
+  <node id='365' visible='true' version='1' lat='-0.2221908395649969' lon='-0.5211083521360607' />
+  <node id='367' visible='true' version='1' lat='-0.21802147020530807' lon='-0.5211953349284685' />
+  <node id='369' visible='true' version='1' lat='-0.21778806952505525' lon='-0.520938503435257' />
+  <node id='371' visible='true' version='1' lat='-0.21804206074333024' lon='-0.5207027586808577' />
+  <node id='373' visible='true' version='1' lat='-0.21828070777942268' lon='-0.5209580800124335' />
+  <node id='375' visible='true' version='1' lat='-0.2217201380129468' lon='-0.5210944057071448' />
+  <node id='1819' visible='true' version='1' lat='-0.21874646341451853' lon='-0.5163597612303352' />
+  <node id='1820' visible='true' version='1' lat='-0.21848320389252793' lon='-0.5166940614694673' />
+  <node id='1821' visible='true' version='1' lat='-0.21875486315739293' lon='-0.516690677600493' />
+  <node id='1822' visible='true' version='1' lat='-0.21901285750516775' lon='-0.5166920563139161'>
+    <tag k='barrier' v='bollard' />
+    <tag k='goods' v='yes' />
+    <tag k='motorcar' v='yes' />
+  </node>
+  <node id='1823' visible='true' version='1' lat='-0.2189763051509611' lon='-0.5166919161461533'>
+    <tag k='barrier' v='bollard' />
+    <tag k='hgv' v='yes' />
+    <tag k='psv' v='yes' />
+  </node>
+  <node id='1824' visible='true' version='1' lat='-0.21875899958209588' lon='-0.517003289190664' />
+  <node id='1825' visible='true' version='1' lat='-0.21938926700936567' lon='-0.5166942258802748' />
+  <node id='1855' visible='true' version='1' lat='-0.21876553724829867' lon='-0.515600619980971' />
+  <node id='1856' visible='true' version='1' lat='-0.2189953789844518' lon='-0.515932774896789'>
+    <tag k='barrier' v='bollard' />
+  </node>
+  <node id='1857' visible='true' version='1' lat='-0.21877393699118441' lon='-0.5159315363511282' />
+  <node id='1858' visible='true' version='1' lat='-0.21940681027814252' lon='-0.5159338475755908' />
+  <node id='1859' visible='true' version='1' lat='-0.2190319313386278' lon='-0.515932915064552'>
+    <tag k='barrier' v='bollard' />
+  </node>
+  <node id='1860' visible='true' version='1' lat='-0.21850227772663344' lon='-0.5159349202201028' />
+  <node id='1861' visible='true' version='1' lat='-0.21877807341587224' lon='-0.5162441479412996' />
+  <node id='1875' visible='true' version='1' lat='-0.220001904692577' lon='-0.5210251383845648' />
+  <way id='3600' visible='true' version='1'>
+    <nd ref='3592' />
+    <nd ref='3594' />
+    <nd ref='3591' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3599' visible='true' version='1'>
+    <nd ref='3595' />
+    <nd ref='3594' />
+    <nd ref='3593' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3598' visible='true' version='1'>
+    <nd ref='3160' />
+    <nd ref='3595' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='3294' visible='true' version='1'>
+    <nd ref='3151' />
+    <nd ref='3170' />
+    <nd ref='3175' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3246' visible='true' version='1'>
+    <nd ref='3179' />
+    <nd ref='3126' />
+    <nd ref='3152' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3245' visible='true' version='1'>
+    <nd ref='3135' />
+    <nd ref='3179' />
+    <tag k='goods' v='no' />
+    <tag k='highway' v='residential' />
+    <tag k='motorcar' v='no' />
+  </way>
+  <way id='3240' visible='true' version='1'>
+    <nd ref='1823' />
+    <nd ref='1821' />
+    <nd ref='1820' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3239' visible='true' version='1'>
+    <nd ref='1822' />
+    <nd ref='1823' />
+    <tag k='highway' v='residential' />
+    <tag k='moped' v='no' />
+    <tag k='motorcycle' v='no' />
+  </way>
+  <way id='3234' visible='true' version='1'>
+    <nd ref='3147' />
+    <nd ref='3169' />
+    <nd ref='3131' />
+    <nd ref='3123' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3211' visible='true' version='1'>
+    <nd ref='3132' />
+    <nd ref='3158' />
+    <nd ref='3172' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3210' visible='true' version='1'>
+    <nd ref='3164' />
+    <nd ref='3144' />
+    <tag k='highway' v='residential' />
+    <tag k='motor_vehicle' v='no' />
+    <tag k='name' v='different transport types' />
+  </way>
+  <way id='3209' visible='true' version='1'>
+    <nd ref='3153' />
+    <nd ref='3146' />
+    <nd ref='3165' />
+    <nd ref='3139' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3207' visible='true' version='1'>
+    <nd ref='3177' />
+    <nd ref='3131' />
+    <nd ref='3119' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3206' visible='true' version='1'>
+    <nd ref='3288' />
+    <nd ref='3133' />
+    <nd ref='3176' />
+    <nd ref='3171' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3205' visible='true' version='1'>
+    <nd ref='3155' />
+    <nd ref='3162' />
+    <nd ref='3122' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3204' visible='true' version='1'>
+    <nd ref='3175' />
+    <nd ref='3159' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3203' visible='true' version='1'>
+    <nd ref='3129' />
+    <nd ref='3140' />
+    <nd ref='3118' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3202' visible='true' version='1'>
+    <nd ref='3116' />
+    <nd ref='3155' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='different oneway property' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='3201' visible='true' version='1'>
+    <nd ref='3154' />
+    <nd ref='3141' />
+    <tag k='highway' v='residential' />
+    <tag k='maxwidth' v='3m' />
+    <tag k='name' v='different highway properties' />
+  </way>
+  <way id='3200' visible='true' version='1'>
+    <nd ref='3175' />
+    <nd ref='3134' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3198' visible='true' version='1'>
+    <nd ref='3144' />
+    <nd ref='3158' />
+    <nd ref='3148' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3196' visible='true' version='1'>
+    <nd ref='3173' />
+    <nd ref='3162' />
+    <nd ref='3145' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3195' visible='true' version='1'>
+    <nd ref='3141' />
+    <nd ref='3140' />
+    <nd ref='3168' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3193' visible='true' version='1'>
+    <nd ref='3120' />
+    <nd ref='3175' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3191' visible='true' version='1'>
+    <nd ref='3127' />
+    <nd ref='3165' />
+    <nd ref='3166' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3190' visible='true' version='1'>
+    <nd ref='3142' />
+    <nd ref='3153' />
+    <nd ref='3116' />
+    <nd ref='3164' />
+    <nd ref='3154' />
+    <nd ref='3147' />
+    <nd ref='3135' />
+    <nd ref='3160' />
+    <nd ref='3288' />
+    <nd ref='3151' />
+    <nd ref='3217' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='3186' visible='true' version='1'>
+    <nd ref='3143' />
+    <nd ref='3176' />
+    <nd ref='3136' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='3182' visible='true' version='1'>
+    <nd ref='3150' />
+    <nd ref='3126' />
+    <nd ref='3137' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='104' visible='true' version='1'>
+    <nd ref='263' />
+    <nd ref='1875' />
+    <nd ref='115' />
+    <nd ref='111' />
+    <nd ref='103' />
+    <nd ref='105' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='114' visible='true' version='1'>
+    <nd ref='111' />
+    <nd ref='113' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='118' visible='true' version='1'>
+    <nd ref='115' />
+    <nd ref='117' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='125' visible='true' version='1'>
+    <nd ref='122' />
+    <nd ref='142' />
+    <nd ref='140' />
+    <nd ref='127' />
+    <nd ref='124' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='128' visible='true' version='1'>
+    <nd ref='126' />
+    <nd ref='127' />
+    <nd ref='130' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='181' visible='true' version='1'>
+    <nd ref='183' />
+    <nd ref='175' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='182' visible='true' version='1'>
+    <nd ref='178' />
+    <nd ref='177' />
+    <nd ref='179' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='187' visible='true' version='1'>
+    <nd ref='175' />
+    <nd ref='180' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='different oneway property' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='188' visible='true' version='1'>
+    <nd ref='180' />
+    <nd ref='177' />
+    <nd ref='176' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='210' visible='true' version='1'>
+    <nd ref='203' />
+    <nd ref='204' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='211' visible='true' version='1'>
+    <nd ref='207' />
+    <nd ref='206' />
+    <nd ref='208' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='214' visible='true' version='1'>
+    <nd ref='204' />
+    <nd ref='209' />
+    <tag k='highway' v='residential' />
+    <tag k='motor_vehicle' v='no' />
+    <tag k='name' v='different transport types' />
+  </way>
+  <way id='215' visible='true' version='1'>
+    <nd ref='209' />
+    <nd ref='206' />
+    <nd ref='205' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='249' visible='true' version='1'>
+    <nd ref='247' />
+    <nd ref='248' />
+    <nd ref='242' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='250' visible='true' version='1'>
+    <nd ref='243' />
+    <nd ref='246' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='251' visible='true' version='1'>
+    <nd ref='245' />
+    <nd ref='248' />
+    <nd ref='244' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='252' visible='true' version='1'>
+    <nd ref='246' />
+    <nd ref='245' />
+    <tag k='highway' v='residential' />
+    <tag k='maxwidth' v='3m' />
+    <tag k='name' v='different highway properties' />
+  </way>
+  <way id='285' visible='true' version='1'>
+    <nd ref='284' />
+    <nd ref='279' />
+    <nd ref='281' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='286' visible='true' version='1'>
+    <nd ref='282' />
+    <nd ref='279' />
+    <nd ref='283' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='287' visible='true' version='1'>
+    <nd ref='265' />
+    <nd ref='278' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='288' visible='true' version='1'>
+    <nd ref='278' />
+    <nd ref='282' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='313' visible='true' version='1'>
+    <nd ref='306' />
+    <nd ref='307' />
+    <nd ref='312' />
+    <nd ref='309' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='314' visible='true' version='1'>
+    <nd ref='310' />
+    <nd ref='309' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='317' visible='true' version='1'>
+    <nd ref='309' />
+    <nd ref='311' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='319' visible='true' version='1'>
+    <nd ref='309' />
+    <nd ref='308' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='342' visible='true' version='1'>
+    <nd ref='335' />
+    <nd ref='336' />
+    <nd ref='342' />
+    <nd ref='338' />
+    <nd ref='337' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='343' visible='true' version='1'>
+    <nd ref='339' />
+    <nd ref='338' />
+    <nd ref='340' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='393' visible='true' version='1'>
+    <nd ref='349' />
+    <nd ref='347' />
+    <nd ref='351' />
+    <nd ref='345' />
+    <nd ref='349' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='395' visible='true' version='1'>
+    <nd ref='351' />
+    <nd ref='3217' />
+    <nd ref='341' />
+    <nd ref='359' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 2' />
+  </way>
+  <way id='397' visible='true' version='1'>
+    <nd ref='359' />
+    <nd ref='357' />
+    <nd ref='355' />
+    <nd ref='353' />
+    <nd ref='359' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='399' visible='true' version='1'>
+    <nd ref='365' />
+    <nd ref='363' />
+    <nd ref='375' />
+    <nd ref='361' />
+    <nd ref='365' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='401' visible='true' version='1'>
+    <nd ref='373' />
+    <nd ref='371' />
+    <nd ref='369' />
+    <nd ref='367' />
+    <nd ref='373' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='403' visible='true' version='1'>
+    <nd ref='375' />
+    <nd ref='3142' />
+    <nd ref='1875' />
+    <nd ref='343' />
+    <nd ref='373' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 1' />
+  </way>
+  <way id='1826' visible='true' version='1'>
+    <nd ref='1825' />
+    <nd ref='1822' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='1827' visible='true' version='1'>
+    <nd ref='1824' />
+    <nd ref='1821' />
+    <nd ref='1819' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='1862' visible='true' version='1'>
+    <nd ref='1859' />
+    <nd ref='1856' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='1863' visible='true' version='1'>
+    <nd ref='1858' />
+    <nd ref='1859' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='1864' visible='true' version='1'>
+    <nd ref='1861' />
+    <nd ref='1857' />
+    <nd ref='1855' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='1865' visible='true' version='1'>
+    <nd ref='1856' />
+    <nd ref='1857' />
+    <nd ref='1860' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='1872' visible='true' version='1'>
+    <nd ref='343' />
+    <nd ref='122' />
+    <nd ref='183' />
+    <nd ref='203' />
+    <nd ref='243' />
+    <nd ref='335' />
+    <nd ref='1825' />
+    <nd ref='1858' />
+    <nd ref='265' />
+    <nd ref='306' />
+    <nd ref='341' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <relation id='3300' visible='true' version='1'>
+    <member type='way' ref='3294' role='from' />
+    <member type='way' ref='3204' role='to' />
+    <member type='node' ref='3175' role='via' />
+    <tag k='restriction' v='no_right_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='323' visible='true' version='1'>
+    <member type='way' ref='313' role='from' />
+    <member type='node' ref='309' role='via' />
+    <member type='way' ref='317' role='to' />
+    <tag k='restriction' v='no_right_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+</osm>
diff --git a/src/test/prune-short.sh b/src/test/prune-short.sh
new file mode 120000
index 0000000..bc8f799
--- /dev/null
+++ b/src/test/prune-short.sh
@@ -0,0 +1 @@
+only-split.sh
\ No newline at end of file
diff --git a/src/test/prune-straight.osm b/src/test/prune-straight.osm
new file mode 100644
index 0000000..c100ddc
--- /dev/null
+++ b/src/test/prune-straight.osm
@@ -0,0 +1,167 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' upload='true' generator='JOSM'>
+  <node id='122' visible='true' version='1' lat='-0.2202806356012029' lon='-0.5205283172729586' />
+  <node id='183' visible='true' version='1' lat='-0.22026900997513588' lon='-0.5197565898111836' />
+  <node id='203' visible='true' version='1' lat='-0.22025358651939986' lon='-0.5189993952842524' />
+  <node id='243' visible='true' version='1' lat='-0.22030070161517012' lon='-0.518238504510479' />
+  <node id='265' visible='true' version='1' lat='-0.22036496756284105' lon='-0.5152287880219779' />
+  <node id='306' visible='true' version='1' lat='-0.2204170113536037' lon='-0.5145041750343666' />
+  <node id='335' visible='true' version='1' lat='-0.22034764780366026' lon='-0.5174764910312685' />
+  <node id='341' visible='true' version='1' lat='-0.22039086026167154' lon='-0.514040831354796' />
+  <node id='343' visible='true' version='1' lat='-0.22024023157772424' lon='-0.5210384622698134' />
+  <node id='345' visible='true' version='1' lat='-0.22192916244766508' lon='-0.514332326947849' />
+  <node id='347' visible='true' version='1' lat='-0.2219482141512544' lon='-0.5138620338011963' />
+  <node id='349' visible='true' version='1' lat='-0.22217484074642313' lon='-0.5141041642151607' />
+  <node id='351' visible='true' version='1' lat='-0.22170436926410486' lon='-0.5140901916750656' />
+  <node id='353' visible='true' version='1' lat='-0.21801516712553526' lon='-0.5141909410905267' />
+  <node id='355' visible='true' version='1' lat='-0.2177803096434024' lon='-0.5139345260591944' />
+  <node id='357' visible='true' version='1' lat='-0.2180357522245965' lon='-0.5136983548940309' />
+  <node id='359' visible='true' version='1' lat='-0.21827293796270247' lon='-0.5139541080318566' />
+  <node id='361' visible='true' version='1' lat='-0.22193543526189635' lon='-0.5213371153581924' />
+  <node id='363' visible='true' version='1' lat='-0.22195451857008858' lon='-0.5208670528071448' />
+  <node id='365' visible='true' version='1' lat='-0.2221908395649969' lon='-0.5211083521360607' />
+  <node id='367' visible='true' version='1' lat='-0.21802147020530807' lon='-0.5211953349284685' />
+  <node id='369' visible='true' version='1' lat='-0.21778806952505525' lon='-0.520938503435257' />
+  <node id='371' visible='true' version='1' lat='-0.21804206074333024' lon='-0.5207027586808577' />
+  <node id='373' visible='true' version='1' lat='-0.21828070777942268' lon='-0.5209580800124335' />
+  <node id='375' visible='true' version='1' lat='-0.2217201380129468' lon='-0.5210944057071448' />
+  <node id='514' visible='true' version='1' lat='-0.21911648466865563' lon='-0.5204548928241738' />
+  <node id='515' visible='true' version='1' lat='-0.21931060134759067' lon='-0.5197122066404057' />
+  <node id='516' visible='true' version='1' lat='-0.21951085842814533' lon='-0.518931170696933' />
+  <node id='517' visible='true' version='1' lat='-0.21935410979490197' lon='-0.5181411739474401' />
+  <node id='518' visible='true' version='1' lat='-0.2192550922021173' lon='-0.5174174318869332' />
+  <node id='519' visible='true' version='1' lat='-0.21937528739938633' lon='-0.516691643624375' />
+  <node id='520' visible='true' version='1' lat='-0.21954923963370723' lon='-0.5159137876476338' />
+  <node id='521' visible='true' version='1' lat='-0.21940663828868845' lon='-0.5151846420097191' />
+  <node id='522' visible='true' version='1' lat='-0.21930348406326042' lon='-0.5144366602347327' />
+  <node id='524' visible='true' version='1' lat='-0.2192811120690243' lon='-0.520994928100946' />
+  <node id='529' visible='true' version='1' lat='-0.219443360641783' lon='-0.5140020337469828' />
+  <node id='537' visible='true' version='1' lat='-0.2193329757546545' lon='-0.5142619714135797' />
+  <node id='539' visible='true' version='1' lat='-0.21930594099502726' lon='-0.5146628218111022' />
+  <node id='541' visible='true' version='1' lat='-0.21952184242087056' lon='-0.5156033304324728' />
+  <node id='543' visible='true' version='1' lat='-0.21950279450540236' lon='-0.516181349042567' />
+  <node id='545' visible='true' version='1' lat='-0.219277127340726' lon='-0.5171568636654659' />
+  <node id='547' visible='true' version='1' lat='-0.2192703091822638' lon='-0.5176842973588277' />
+  <node id='549' visible='true' version='1' lat='-0.2194822077936952' lon='-0.5186881745363406' />
+  <node id='551' visible='true' version='1' lat='-0.21947150748538968' lon='-0.5192131869020143' />
+  <node id='553' visible='true' version='1' lat='-0.21916581010325425' lon='-0.5202070286137637' />
+  <node id='555' visible='true' version='1' lat='-0.2191502071646719' lon='-0.5206793428462938' />
+  <node id='1825' visible='true' version='1' lat='-0.2203336582318136' lon='-0.5167360134101662' />
+  <node id='1858' visible='true' version='1' lat='-0.22031968292631743' lon='-0.5159743875972973' />
+  <node id='3116' visible='true' version='1' lat='-0.22100130433783766' lon='-0.5197781875878197' />
+  <node id='3135' visible='true' version='1' lat='-0.2210659903823021' lon='-0.5167576245717892' />
+  <node id='3142' visible='true' version='1' lat='-0.22097252594183314' lon='-0.5210600600464496' />
+  <node id='3147' visible='true' version='1' lat='-0.22104625848691606' lon='-0.5174952321326493' />
+  <node id='3151' visible='true' version='1' lat='-0.2211153339602629' lon='-0.5145085508312978' />
+  <node id='3153' visible='true' version='1' lat='-0.22098056861233564' lon='-0.5205478256731001' />
+  <node id='3154' visible='true' version='1' lat='-0.22103299366341264' lon='-0.5182603413162163' />
+  <node id='3160' visible='true' version='1' lat='-0.22108342504232312' lon='-0.5159972875495841' />
+  <node id='3164' visible='true' version='1' lat='-0.22101540883355839' lon='-0.519020790240951' />
+  <node id='3217' visible='true' version='1' lat='-0.22113406029065996' lon='-0.5140671692051982' />
+  <node id='3288' visible='true' version='1' lat='-0.22109734126804137' lon='-0.5152506229571332' />
+  <way id='393' visible='true' version='1'>
+    <nd ref='349' />
+    <nd ref='347' />
+    <nd ref='351' />
+    <nd ref='345' />
+    <nd ref='349' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='395' visible='true' version='1'>
+    <nd ref='351' />
+    <nd ref='3217' />
+    <nd ref='341' />
+    <nd ref='529' />
+    <nd ref='359' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 2' />
+  </way>
+  <way id='397' visible='true' version='1'>
+    <nd ref='359' />
+    <nd ref='357' />
+    <nd ref='355' />
+    <nd ref='353' />
+    <nd ref='359' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='399' visible='true' version='1'>
+    <nd ref='365' />
+    <nd ref='363' />
+    <nd ref='375' />
+    <nd ref='361' />
+    <nd ref='365' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='401' visible='true' version='1'>
+    <nd ref='373' />
+    <nd ref='371' />
+    <nd ref='369' />
+    <nd ref='367' />
+    <nd ref='373' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='403' visible='true' version='1'>
+    <nd ref='375' />
+    <nd ref='3142' />
+    <nd ref='343' />
+    <nd ref='524' />
+    <nd ref='373' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 1' />
+  </way>
+  <way id='512' visible='true' version='1'>
+    <nd ref='524' />
+    <nd ref='555' />
+    <nd ref='514' />
+    <nd ref='553' />
+    <nd ref='515' />
+    <nd ref='551' />
+    <nd ref='516' />
+    <nd ref='549' />
+    <nd ref='517' />
+    <nd ref='547' />
+    <nd ref='518' />
+    <nd ref='545' />
+    <nd ref='519' />
+    <nd ref='543' />
+    <nd ref='520' />
+    <nd ref='541' />
+    <nd ref='521' />
+    <nd ref='539' />
+    <nd ref='522' />
+    <nd ref='537' />
+    <nd ref='529' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='very wiggly road' />
+  </way>
+  <way id='1872' visible='true' version='1'>
+    <nd ref='343' />
+    <nd ref='122' />
+    <nd ref='183' />
+    <nd ref='203' />
+    <nd ref='243' />
+    <nd ref='335' />
+    <nd ref='1825' />
+    <nd ref='1858' />
+    <nd ref='265' />
+    <nd ref='306' />
+    <nd ref='341' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='wiggly road' />
+  </way>
+  <way id='3190' visible='true' version='1'>
+    <nd ref='3142' />
+    <nd ref='3153' />
+    <nd ref='3116' />
+    <nd ref='3164' />
+    <nd ref='3154' />
+    <nd ref='3147' />
+    <nd ref='3135' />
+    <nd ref='3160' />
+    <nd ref='3288' />
+    <nd ref='3151' />
+    <nd ref='3217' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='straight road' />
+  </way>
+</osm>
diff --git a/src/test/prune-straight.sh b/src/test/prune-straight.sh
new file mode 120000
index 0000000..bc8f799
--- /dev/null
+++ b/src/test/prune-straight.sh
@@ -0,0 +1 @@
+only-split.sh
\ No newline at end of file
diff --git a/src/test/start-1-finish.sh b/src/test/start-1-finish.sh
index e8905a1..a448943 100755
--- a/src/test/start-1-finish.sh
+++ b/src/test/start-1-finish.sh
@@ -92,11 +92,12 @@ for waypoint in $waypoints; do
 
     mv shortest* $dir/$name-$waypoint
 
-    if [ "$pruned" = "" ]; then
+    echo diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
 
-        echo diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
+    if ./is-fast-math; then
+        diff -U 0 expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt | 2>&1 egrep '^[-+] ' || true
+    else
         diff -u expected/$name-$waypoint.txt $dir/$name-$waypoint/shortest-all.txt >> $log
-
     fi
 
 done
diff --git a/src/test/waypoints.pl b/src/test/waypoints.pl
index 681a689..fefe3a6 100755
--- a/src/test/waypoints.pl
+++ b/src/test/waypoints.pl
@@ -1,4 +1,26 @@
 #!/usr/bin/perl
+#
+# Routing test case generator tool.
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2011-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
 
 # Command line
 
@@ -12,11 +34,11 @@ if($#ARGV<1 || $ARGV>2 || ! -f $ARGV[0])
 
 open(FILE,"<$ARGV[0]") || die "Cannot open '$ARGV[0]'\n";
 
-%waypoints=();
- at waypoints=();
- at waypoint_lat=();
- at waypoint_lon=();
-$innode=0;
+my %waypoints=();
+my @waypoints=();
+my @waypoint_lat=();
+my @waypoint_lon=();
+my $innode=0;
 
 while(<FILE>)
   {
diff --git a/src/translations.c b/src/translations.c
index daff2db..ff2796a 100644
--- a/src/translations.c
+++ b/src/translations.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2012 Andrew M. Bishop
+ This file Copyright 2010-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -48,22 +48,22 @@ char *translate_raw_highway[Highway_Count]={"","motorway","trunk road","primary
 char *translate_xml_route_shortest="Shortest";
 char *translate_xml_route_quickest="Quickest";
 
-char *translate_html_waypoint  ="<span class='w'>Waypoint</span>";
+char *translate_html_waypoint  ="<span class='w'>Waypoint</span>"; /* when reading XML translations file span is added */
 char *translate_html_junction  ="Junction";
 char *translate_html_roundabout="Roundabout";
 
 char *translate_html_title     ="%s Route";
-char *translate_html_start[2]  ={"Start","At %s, head %s"};
+char *translate_html_start[2]  ={"Start" ,"At %s, head %s"};
+char *translate_html_node[2]   ={"At"    ,"%s, go %s heading %s"};
+char *translate_html_rbnode[2] ={"Leave" ,"%s, take the %s exit heading %s"};
 char *translate_html_segment[2]={"Follow","%s for %.3f km, %.1f min"};
-char *translate_html_node[2]   ={"At","%s, go %s heading %s"};
-char *translate_html_rbnode[2] ={"Leave","%s, take the %s exit heading %s"};
-char *translate_html_stop[2]   ={"Stop","At %s"};
-char *translate_html_total[2]  ={"Total","%.1f km, %.0f minutes"};
+char *translate_html_stop[2]   ={"Stop"  ,"At %s"};
+char *translate_html_total[2]  ={"Total" ,"%.1f km, %.0f minutes"};
 
-char *translate_gpx_desc ="%s between 'start' and 'finish' waypoints";
-char *translate_gpx_name ="%s Route";
-char *translate_gpx_step ="%s on '%s' for %.3f km, %.1 min";
-char *translate_gpx_final="Total Journey %.1f km, %d minutes";
+char *translate_gpx_desc ="%s route between 'start' and 'finish' waypoints";
+char *translate_gpx_name ="%s route";
+char *translate_gpx_step ="%s on '%s' for %.3f km, %.1f min";
+char *translate_gpx_final="Total Journey %.1f km, %.0f minutes";
 
 char *translate_gpx_start ="START";
 char *translate_gpx_inter ="INTER";
@@ -87,55 +87,96 @@ static int stored=0;
 
 //static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding);
 //static int RoutinoTranslationsType_function(const char *_tag_,int _type_);
-static int languageType_function(const char *_tag_,int _type_,const char *lang);
-//static int GPXType_function(const char *_tag_,int _type_);
-static int GPXFinalType_function(const char *_tag_,int _type_,const char *text);
-static int GPXStepType_function(const char *_tag_,int _type_,const char *text);
-static int GPXNameType_function(const char *_tag_,int _type_,const char *text);
-static int GPXDescType_function(const char *_tag_,int _type_,const char *text);
-//static int HTMLType_function(const char *_tag_,int _type_);
-static int HTMLTotalType_function(const char *_tag_,int _type_,const char *string,const char *text);
-static int HTMLStopType_function(const char *_tag_,int _type_,const char *string,const char *text);
-static int HTMLSegmentType_function(const char *_tag_,int _type_,const char *string,const char *text);
-static int HTMLRBNodeType_function(const char *_tag_,int _type_,const char *string,const char *text);
-static int HTMLNodeType_function(const char *_tag_,int _type_,const char *string,const char *text);
-static int HTMLStartType_function(const char *_tag_,int _type_,const char *string,const char *text);
-static int HTMLTitleType_function(const char *_tag_,int _type_,const char *text);
+static int LanguageType_function(const char *_tag_,int _type_,const char *lang);
 //static int CopyrightType_function(const char *_tag_,int _type_);
-static int GPXWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string);
-static int HTMLWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string);
-static int RouteType_function(const char *_tag_,int _type_,const char *type,const char *string);
-static int HighwayType_function(const char *_tag_,int _type_,const char *type,const char *string);
-static int OrdinalType_function(const char *_tag_,int _type_,const char *number,const char *string);
-static int HeadingType_function(const char *_tag_,int _type_,const char *direction,const char *string);
 static int TurnType_function(const char *_tag_,int _type_,const char *direction,const char *string);
-static int CopyrightLicenseType_function(const char *_tag_,int _type_,const char *string,const char *text);
-static int CopyrightSourceType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int HeadingType_function(const char *_tag_,int _type_,const char *direction,const char *string);
+static int OrdinalType_function(const char *_tag_,int _type_,const char *number,const char *string);
+static int HighwayType_function(const char *_tag_,int _type_,const char *type,const char *string);
+static int RouteType_function(const char *_tag_,int _type_,const char *type,const char *string);
+//static int HTMLType_function(const char *_tag_,int _type_);
+//static int GPXType_function(const char *_tag_,int _type_);
 static int CopyrightCreatorType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int CopyrightSourceType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int CopyrightLicenseType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int HTMLWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string);
+static int HTMLTitleType_function(const char *_tag_,int _type_,const char *text);
+static int HTMLStartType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int HTMLNodeType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int HTMLRBNodeType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int HTMLSegmentType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int HTMLStopType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int HTMLTotalType_function(const char *_tag_,int _type_,const char *string,const char *text);
+static int GPXWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string);
+static int GPXDescType_function(const char *_tag_,int _type_,const char *text);
+static int GPXNameType_function(const char *_tag_,int _type_,const char *text);
+static int GPXStepType_function(const char *_tag_,int _type_,const char *text);
+static int GPXFinalType_function(const char *_tag_,int _type_,const char *text);
 
 
-/* The XML tag definitions */
+/* The XML tag definitions (forward declarations) */
+
+static xmltag xmlDeclaration_tag;
+static xmltag RoutinoTranslationsType_tag;
+static xmltag LanguageType_tag;
+static xmltag CopyrightType_tag;
+static xmltag TurnType_tag;
+static xmltag HeadingType_tag;
+static xmltag OrdinalType_tag;
+static xmltag HighwayType_tag;
+static xmltag RouteType_tag;
+static xmltag HTMLType_tag;
+static xmltag GPXType_tag;
+static xmltag CopyrightCreatorType_tag;
+static xmltag CopyrightSourceType_tag;
+static xmltag CopyrightLicenseType_tag;
+static xmltag HTMLWaypointType_tag;
+static xmltag HTMLTitleType_tag;
+static xmltag HTMLStartType_tag;
+static xmltag HTMLNodeType_tag;
+static xmltag HTMLRBNodeType_tag;
+static xmltag HTMLSegmentType_tag;
+static xmltag HTMLStopType_tag;
+static xmltag HTMLTotalType_tag;
+static xmltag GPXWaypointType_tag;
+static xmltag GPXDescType_tag;
+static xmltag GPXNameType_tag;
+static xmltag GPXStepType_tag;
+static xmltag GPXFinalType_tag;
+
+
+/* The XML tag definition values */
 
-/*+ The CopyrightCreatorType type tag. +*/
-static xmltag CopyrightCreatorType_tag=
-              {"creator",
-               2, {"string","text"},
-               CopyrightCreatorType_function,
-               {NULL}};
+/*+ The complete set of tags at the top level. +*/
+static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoTranslationsType_tag,NULL};
 
-/*+ The CopyrightSourceType type tag. +*/
-static xmltag CopyrightSourceType_tag=
-              {"source",
-               2, {"string","text"},
-               CopyrightSourceType_function,
+/*+ The xmlDeclaration type tag. +*/
+static xmltag xmlDeclaration_tag=
+              {"xml",
+               2, {"version","encoding"},
+               NULL,
                {NULL}};
 
-/*+ The CopyrightLicenseType type tag. +*/
-static xmltag CopyrightLicenseType_tag=
-              {"license",
-               2, {"string","text"},
-               CopyrightLicenseType_function,
-               {NULL}};
+/*+ The RoutinoTranslationsType type tag. +*/
+static xmltag RoutinoTranslationsType_tag=
+              {"routino-translations",
+               0, {NULL},
+               NULL,
+               {&LanguageType_tag,NULL}};
+
+/*+ The LanguageType type tag. +*/
+static xmltag LanguageType_tag=
+              {"language",
+               1, {"lang"},
+               LanguageType_function,
+               {&CopyrightType_tag,&TurnType_tag,&HeadingType_tag,&OrdinalType_tag,&HighwayType_tag,&RouteType_tag,&HTMLType_tag,&GPXType_tag,NULL}};
+
+/*+ The CopyrightType type tag. +*/
+static xmltag CopyrightType_tag=
+              {"copyright",
+               0, {NULL},
+               NULL,
+               {&CopyrightCreatorType_tag,&CopyrightSourceType_tag,&CopyrightLicenseType_tag,NULL}};
 
 /*+ The TurnType type tag. +*/
 static xmltag TurnType_tag=
@@ -172,6 +213,41 @@ static xmltag RouteType_tag=
                RouteType_function,
                {NULL}};
 
+/*+ The HTMLType type tag. +*/
+static xmltag HTMLType_tag=
+              {"output-html",
+               0, {NULL},
+               NULL,
+               {&HTMLWaypointType_tag,&HTMLTitleType_tag,&HTMLStartType_tag,&HTMLNodeType_tag,&HTMLRBNodeType_tag,&HTMLSegmentType_tag,&HTMLStopType_tag,&HTMLTotalType_tag,NULL}};
+
+/*+ The GPXType type tag. +*/
+static xmltag GPXType_tag=
+              {"output-gpx",
+               0, {NULL},
+               NULL,
+               {&GPXWaypointType_tag,&GPXDescType_tag,&GPXNameType_tag,&GPXStepType_tag,&GPXFinalType_tag,NULL}};
+
+/*+ The CopyrightCreatorType type tag. +*/
+static xmltag CopyrightCreatorType_tag=
+              {"creator",
+               2, {"string","text"},
+               CopyrightCreatorType_function,
+               {NULL}};
+
+/*+ The CopyrightSourceType type tag. +*/
+static xmltag CopyrightSourceType_tag=
+              {"source",
+               2, {"string","text"},
+               CopyrightSourceType_function,
+               {NULL}};
+
+/*+ The CopyrightLicenseType type tag. +*/
+static xmltag CopyrightLicenseType_tag=
+              {"license",
+               2, {"string","text"},
+               CopyrightLicenseType_function,
+               {NULL}};
+
 /*+ The HTMLWaypointType type tag. +*/
 static xmltag HTMLWaypointType_tag=
               {"waypoint",
@@ -179,20 +255,6 @@ static xmltag HTMLWaypointType_tag=
                HTMLWaypointType_function,
                {NULL}};
 
-/*+ The GPXWaypointType type tag. +*/
-static xmltag GPXWaypointType_tag=
-              {"waypoint",
-               2, {"type","string"},
-               GPXWaypointType_function,
-               {NULL}};
-
-/*+ The CopyrightType type tag. +*/
-static xmltag CopyrightType_tag=
-              {"copyright",
-               0, {NULL},
-               NULL,
-               {&CopyrightCreatorType_tag,&CopyrightSourceType_tag,&CopyrightLicenseType_tag,NULL}};
-
 /*+ The HTMLTitleType type tag. +*/
 static xmltag HTMLTitleType_tag=
               {"title",
@@ -242,12 +304,12 @@ static xmltag HTMLTotalType_tag=
                HTMLTotalType_function,
                {NULL}};
 
-/*+ The HTMLType type tag. +*/
-static xmltag HTMLType_tag=
-              {"output-html",
-               0, {NULL},
-               NULL,
-               {&HTMLWaypointType_tag,&HTMLTitleType_tag,&HTMLStartType_tag,&HTMLNodeType_tag,&HTMLRBNodeType_tag,&HTMLSegmentType_tag,&HTMLStopType_tag,&HTMLTotalType_tag,NULL}};
+/*+ The GPXWaypointType type tag. +*/
+static xmltag GPXWaypointType_tag=
+              {"waypoint",
+               2, {"type","string"},
+               GPXWaypointType_function,
+               {NULL}};
 
 /*+ The GPXDescType type tag. +*/
 static xmltag GPXDescType_tag=
@@ -277,73 +339,134 @@ static xmltag GPXFinalType_tag=
                GPXFinalType_function,
                {NULL}};
 
-/*+ The GPXType type tag. +*/
-static xmltag GPXType_tag=
-              {"output-gpx",
-               0, {NULL},
-               NULL,
-               {&GPXWaypointType_tag,&GPXDescType_tag,&GPXNameType_tag,&GPXStepType_tag,&GPXFinalType_tag,NULL}};
 
-/*+ The languageType type tag. +*/
-static xmltag languageType_tag=
-              {"language",
-               1, {"lang"},
-               languageType_function,
-               {&CopyrightType_tag,&TurnType_tag,&HeadingType_tag,&OrdinalType_tag,&HighwayType_tag,&RouteType_tag,&HTMLType_tag,&GPXType_tag,NULL}};
+/* The XML tag processing functions */
 
-/*+ The RoutinoTranslationsType type tag. +*/
-static xmltag RoutinoTranslationsType_tag=
-              {"routino-translations",
-               0, {NULL},
-               NULL,
-               {&languageType_tag,NULL}};
 
-/*+ The xmlDeclaration type tag. +*/
-static xmltag xmlDeclaration_tag=
-              {"xml",
-               2, {"version","encoding"},
-               NULL,
-               {NULL}};
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the XML declaration is seen
 
+  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
 
-/*+ The complete set of tags at the top level. +*/
-static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoTranslationsType_tag,NULL};
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
 
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-/* The XML tag processing functions */
+  const char *version The contents of the 'version' attribute (or NULL if not defined).
+
+  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
+//{
+// return(0);
+//}
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the CopyrightCreatorType XSD type is seen
+  The function that is called when the RoutinoTranslationsType XSD type is seen
 
-  int CopyrightCreatorType_function Returns 0 if no error occured or something else otherwise.
+  int RoutinoTranslationsType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
 
-  const char *string The contents of the 'string' attribute (or NULL if not defined).
+//static int RoutinoTranslationsType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
 
-  const char *text The contents of the 'text' attribute (or NULL if not defined).
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the LanguageType XSD type is seen
+
+  int LanguageType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *lang The contents of the 'lang' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int CopyrightCreatorType_function(const char *_tag_,int _type_,const char *string,const char *text)
+static int LanguageType_function(const char *_tag_,int _type_,const char *lang)
+{
+ static int first=1;
+
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    XMLPARSE_ASSERT_STRING(_tag_,lang);
+
+    if(!store_lang && first)
+       store=1;
+    else if(store_lang && !strcmp(store_lang,lang))
+       store=1;
+    else
+       store=0;
+
+    first=0;
+   }
+
+ if(_type_&XMLPARSE_TAG_END && store)
+   {
+    store=0;
+    stored=1;
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the CopyrightType XSD type is seen
+
+  int CopyrightType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int CopyrightType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the TurnType XSD type is seen
+
+  int TurnType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *direction The contents of the 'direction' attribute (or NULL if not defined).
+
+  const char *string The contents of the 'string' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int TurnType_function(const char *_tag_,int _type_,const char *direction,const char *string)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
-    char *xmlstring,*xmltext;
+    char *xmlstring;
+    int d;
 
+    XMLPARSE_ASSERT_INTEGER(_tag_,direction); d=atoi(direction);
     XMLPARSE_ASSERT_STRING(_tag_,string);
-    XMLPARSE_ASSERT_STRING(_tag_,text);
 
-    translate_raw_copyright_creator[0]=strcpy(malloc(strlen(string)+1),string);
-    translate_raw_copyright_creator[1]=strcpy(malloc(strlen(text)+1)  ,text);
+    d+=4;
+
+    if(d<0 || d>8)
+       XMLPARSE_INVALID(_tag_,direction);
 
     xmlstring=ParseXML_Encode_Safe_XML(string);
-    xmltext  =ParseXML_Encode_Safe_XML(text);
 
-    translate_xml_copyright_creator[0]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    translate_xml_copyright_creator[1]=strcpy(malloc(strlen(xmltext)+1)  ,xmltext);
+    translate_xml_turn[d]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
    }
 
  return(0);
@@ -351,36 +474,37 @@ static int CopyrightCreatorType_function(const char *_tag_,int _type_,const char
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the CopyrightSourceType XSD type is seen
+  The function that is called when the HeadingType XSD type is seen
 
-  int CopyrightSourceType_function Returns 0 if no error occured or something else otherwise.
+  int HeadingType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *string The contents of the 'string' attribute (or NULL if not defined).
+  const char *direction The contents of the 'direction' attribute (or NULL if not defined).
 
-  const char *text The contents of the 'text' attribute (or NULL if not defined).
+  const char *string The contents of the 'string' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int CopyrightSourceType_function(const char *_tag_,int _type_,const char *string,const char *text)
+static int HeadingType_function(const char *_tag_,int _type_,const char *direction,const char *string)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
-    char *xmlstring,*xmltext;
+    char *xmlstring;
+    int d;
 
+    XMLPARSE_ASSERT_INTEGER(_tag_,direction); d=atoi(direction);
     XMLPARSE_ASSERT_STRING(_tag_,string);
-    XMLPARSE_ASSERT_STRING(_tag_,text);
 
-    translate_raw_copyright_source[0]=strcpy(malloc(strlen(string)+1),string);
-    translate_raw_copyright_source[1]=strcpy(malloc(strlen(text)+1)  ,text);
+    d+=4;
+
+    if(d<0 || d>8)
+       XMLPARSE_INVALID(_tag_,direction);
 
     xmlstring=ParseXML_Encode_Safe_XML(string);
-    xmltext  =ParseXML_Encode_Safe_XML(text);
 
-    translate_xml_copyright_source[0]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    translate_xml_copyright_source[1]=strcpy(malloc(strlen(xmltext)+1)  ,xmltext);
+    translate_xml_heading[d]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
    }
 
  return(0);
@@ -388,36 +512,35 @@ static int CopyrightSourceType_function(const char *_tag_,int _type_,const char
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the CopyrightLicenseType XSD type is seen
+  The function that is called when the OrdinalType XSD type is seen
 
-  int CopyrightLicenseType_function Returns 0 if no error occured or something else otherwise.
+  int OrdinalType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *string The contents of the 'string' attribute (or NULL if not defined).
+  const char *number The contents of the 'number' attribute (or NULL if not defined).
 
-  const char *text The contents of the 'text' attribute (or NULL if not defined).
+  const char *string The contents of the 'string' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int CopyrightLicenseType_function(const char *_tag_,int _type_,const char *string,const char *text)
+static int OrdinalType_function(const char *_tag_,int _type_,const char *number,const char *string)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
-    char *xmlstring,*xmltext;
+    char *xmlstring;
+    int n;
 
+    XMLPARSE_ASSERT_INTEGER(_tag_,number); n=atoi(number);
     XMLPARSE_ASSERT_STRING(_tag_,string);
-    XMLPARSE_ASSERT_STRING(_tag_,text);
 
-    translate_raw_copyright_license[0]=strcpy(malloc(strlen(string)+1),string);
-    translate_raw_copyright_license[1]=strcpy(malloc(strlen(text)+1)  ,text);
+    if(n<1 || n>10)
+       XMLPARSE_INVALID(_tag_,number);
 
     xmlstring=ParseXML_Encode_Safe_XML(string);
-    xmltext  =ParseXML_Encode_Safe_XML(text);
 
-    translate_xml_copyright_license[0]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    translate_xml_copyright_license[1]=strcpy(malloc(strlen(xmltext)+1)  ,xmltext);
+    translate_xml_ordinal[n-1]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
    }
 
  return(0);
@@ -425,37 +548,34 @@ static int CopyrightLicenseType_function(const char *_tag_,int _type_,const char
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the TurnType XSD type is seen
+  The function that is called when the HighwayType XSD type is seen
 
-  int TurnType_function Returns 0 if no error occured or something else otherwise.
+  int HighwayType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *direction The contents of the 'direction' attribute (or NULL if not defined).
+  const char *type The contents of the 'type' attribute (or NULL if not defined).
 
   const char *string The contents of the 'string' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int TurnType_function(const char *_tag_,int _type_,const char *direction,const char *string)
+static int HighwayType_function(const char *_tag_,int _type_,const char *type,const char *string)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
-    char *xmlstring;
-    int d;
+    Highway highway;
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,direction); d=atoi(direction);
+    XMLPARSE_ASSERT_STRING(_tag_,type);
     XMLPARSE_ASSERT_STRING(_tag_,string);
 
-    d+=4;
-
-    if(d<0 || d>8)
-       XMLPARSE_INVALID(_tag_,direction);
+    highway=HighwayType(type);
 
-    xmlstring=ParseXML_Encode_Safe_XML(string);
+    if(highway==Highway_None)
+       XMLPARSE_INVALID(_tag_,type);
 
-    translate_xml_turn[d]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    translate_raw_highway[highway]=strcpy(malloc(strlen(string)+1),string);
    }
 
  return(0);
@@ -463,37 +583,36 @@ static int TurnType_function(const char *_tag_,int _type_,const char *direction,
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the HeadingType XSD type is seen
+  The function that is called when the RouteType XSD type is seen
 
-  int HeadingType_function Returns 0 if no error occured or something else otherwise.
+  int RouteType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *direction The contents of the 'direction' attribute (or NULL if not defined).
+  const char *type The contents of the 'type' attribute (or NULL if not defined).
 
   const char *string The contents of the 'string' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int HeadingType_function(const char *_tag_,int _type_,const char *direction,const char *string)
+static int RouteType_function(const char *_tag_,int _type_,const char *type,const char *string)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
     char *xmlstring;
-    int d;
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,direction); d=atoi(direction);
+    XMLPARSE_ASSERT_STRING(_tag_,type);
     XMLPARSE_ASSERT_STRING(_tag_,string);
 
-    d+=4;
-
-    if(d<0 || d>8)
-       XMLPARSE_INVALID(_tag_,direction);
-
     xmlstring=ParseXML_Encode_Safe_XML(string);
 
-    translate_xml_heading[d]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    if(!strcmp(type,"shortest"))
+       translate_xml_route_shortest=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    else if(!strcmp(type,"quickest"))
+       translate_xml_route_quickest=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    else
+       XMLPARSE_INVALID(_tag_,type);
    }
 
  return(0);
@@ -501,35 +620,68 @@ static int HeadingType_function(const char *_tag_,int _type_,const char *directi
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the OrdinalType XSD type is seen
+  The function that is called when the HTMLType XSD type is seen
 
-  int OrdinalType_function Returns 0 if no error occured or something else otherwise.
+  int HTMLType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int HTMLType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the GPXType XSD type is seen
+
+  int GPXType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+//static int GPXType_function(const char *_tag_,int _type_)
+//{
+// return(0);
+//}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the CopyrightCreatorType XSD type is seen
+
+  int CopyrightCreatorType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *number The contents of the 'number' attribute (or NULL if not defined).
-
   const char *string The contents of the 'string' attribute (or NULL if not defined).
+
+  const char *text The contents of the 'text' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int OrdinalType_function(const char *_tag_,int _type_,const char *number,const char *string)
+static int CopyrightCreatorType_function(const char *_tag_,int _type_,const char *string,const char *text)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
-    char *xmlstring;
-    int n;
+    char *xmlstring,*xmltext;
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,number); n=atoi(number);
     XMLPARSE_ASSERT_STRING(_tag_,string);
+    XMLPARSE_ASSERT_STRING(_tag_,text);
 
-    if(n<1 || n>10)
-       XMLPARSE_INVALID(_tag_,number);
+    translate_raw_copyright_creator[0]=strcpy(malloc(strlen(string)+1),string);
+    translate_raw_copyright_creator[1]=strcpy(malloc(strlen(text)+1)  ,text);
 
     xmlstring=ParseXML_Encode_Safe_XML(string);
+    xmltext  =ParseXML_Encode_Safe_XML(text);
 
-    translate_xml_ordinal[n-1]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    translate_xml_copyright_creator[0]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    translate_xml_copyright_creator[1]=strcpy(malloc(strlen(xmltext)+1)  ,xmltext);
    }
 
  return(0);
@@ -537,34 +689,36 @@ static int OrdinalType_function(const char *_tag_,int _type_,const char *number,
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the HighwayType XSD type is seen
+  The function that is called when the CopyrightSourceType XSD type is seen
 
-  int HighwayType_function Returns 0 if no error occured or something else otherwise.
+  int CopyrightSourceType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *type The contents of the 'type' attribute (or NULL if not defined).
-
   const char *string The contents of the 'string' attribute (or NULL if not defined).
+
+  const char *text The contents of the 'text' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int HighwayType_function(const char *_tag_,int _type_,const char *type,const char *string)
+static int CopyrightSourceType_function(const char *_tag_,int _type_,const char *string,const char *text)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
-    Highway highway;
+    char *xmlstring,*xmltext;
 
-    XMLPARSE_ASSERT_STRING(_tag_,type);
     XMLPARSE_ASSERT_STRING(_tag_,string);
+    XMLPARSE_ASSERT_STRING(_tag_,text);
 
-    highway=HighwayType(type);
+    translate_raw_copyright_source[0]=strcpy(malloc(strlen(string)+1),string);
+    translate_raw_copyright_source[1]=strcpy(malloc(strlen(text)+1)  ,text);
 
-    if(highway==Highway_None)
-       XMLPARSE_INVALID(_tag_,type);
+    xmlstring=ParseXML_Encode_Safe_XML(string);
+    xmltext  =ParseXML_Encode_Safe_XML(text);
 
-    translate_raw_highway[highway]=strcpy(malloc(strlen(string)+1),string);
+    translate_xml_copyright_source[0]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    translate_xml_copyright_source[1]=strcpy(malloc(strlen(xmltext)+1)  ,xmltext);
    }
 
  return(0);
@@ -572,36 +726,36 @@ static int HighwayType_function(const char *_tag_,int _type_,const char *type,co
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the RouteType XSD type is seen
+  The function that is called when the CopyrightLicenseType XSD type is seen
 
-  int RouteType_function Returns 0 if no error occured or something else otherwise.
+  int CopyrightLicenseType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *type The contents of the 'type' attribute (or NULL if not defined).
-
   const char *string The contents of the 'string' attribute (or NULL if not defined).
+
+  const char *text The contents of the 'text' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int RouteType_function(const char *_tag_,int _type_,const char *type,const char *string)
+static int CopyrightLicenseType_function(const char *_tag_,int _type_,const char *string,const char *text)
 {
  if(_type_&XMLPARSE_TAG_START && store)
    {
-    char *xmlstring;
+    char *xmlstring,*xmltext;
 
-    XMLPARSE_ASSERT_STRING(_tag_,type);
     XMLPARSE_ASSERT_STRING(_tag_,string);
+    XMLPARSE_ASSERT_STRING(_tag_,text);
+
+    translate_raw_copyright_license[0]=strcpy(malloc(strlen(string)+1),string);
+    translate_raw_copyright_license[1]=strcpy(malloc(strlen(text)+1)  ,text);
 
     xmlstring=ParseXML_Encode_Safe_XML(string);
+    xmltext  =ParseXML_Encode_Safe_XML(text);
 
-    if(!strcmp(type,"shortest"))
-       translate_xml_route_shortest=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    else if(!strcmp(type,"quickest"))
-       translate_xml_route_quickest=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    else
-       XMLPARSE_INVALID(_tag_,type);
+    translate_xml_copyright_license[0]=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    translate_xml_copyright_license[1]=strcpy(malloc(strlen(xmltext)+1)  ,xmltext);
    }
 
  return(0);
@@ -651,63 +805,6 @@ static int HTMLWaypointType_function(const char *_tag_,int _type_,const char *ty
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the GPXWaypointType XSD type is seen
-
-  int GPXWaypointType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *type The contents of the 'type' attribute (or NULL if not defined).
-
-  const char *string The contents of the 'string' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int GPXWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string)
-{
- if(_type_&XMLPARSE_TAG_START && store)
-   {
-    char *xmlstring;
-
-    XMLPARSE_ASSERT_STRING(_tag_,type);
-    XMLPARSE_ASSERT_STRING(_tag_,string);
-
-    xmlstring=ParseXML_Encode_Safe_XML(string);
-
-    if(!strcmp(type,"start"))
-       translate_gpx_start=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    else if(!strcmp(type,"inter"))
-       translate_gpx_inter=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    else if(!strcmp(type,"trip"))
-       translate_gpx_trip=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    else if(!strcmp(type,"finish"))
-       translate_gpx_finish=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
-    else
-       XMLPARSE_INVALID(_tag_,type);
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the CopyrightType XSD type is seen
-
-  int CopyrightType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int CopyrightType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   The function that is called when the HTMLTitleType XSD type is seen
 
   int HTMLTitleType_function Returns 0 if no error occured or something else otherwise.
@@ -963,19 +1060,44 @@ static int HTMLTotalType_function(const char *_tag_,int _type_,const char *strin
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the HTMLType XSD type is seen
+  The function that is called when the GPXWaypointType XSD type is seen
 
-  int HTMLType_function Returns 0 if no error occured or something else otherwise.
+  int GPXWaypointType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *type The contents of the 'type' attribute (or NULL if not defined).
+
+  const char *string The contents of the 'string' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-//static int HTMLType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
+static int GPXWaypointType_function(const char *_tag_,int _type_,const char *type,const char *string)
+{
+ if(_type_&XMLPARSE_TAG_START && store)
+   {
+    char *xmlstring;
+
+    XMLPARSE_ASSERT_STRING(_tag_,type);
+    XMLPARSE_ASSERT_STRING(_tag_,string);
+
+    xmlstring=ParseXML_Encode_Safe_XML(string);
+
+    if(!strcmp(type,"start"))
+       translate_gpx_start=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    else if(!strcmp(type,"inter"))
+       translate_gpx_inter=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    else if(!strcmp(type,"trip"))
+       translate_gpx_trip=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    else if(!strcmp(type,"finish"))
+       translate_gpx_finish=strcpy(malloc(strlen(xmlstring)+1),xmlstring);
+    else
+       XMLPARSE_INVALID(_tag_,type);
+   }
+
+ return(0);
+}
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -1095,98 +1217,6 @@ static int GPXFinalType_function(const char *_tag_,int _type_,const char *text)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the GPXType XSD type is seen
-
-  int GPXType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int GPXType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the languageType XSD type is seen
-
-  int languageType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *lang The contents of the 'lang' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int languageType_function(const char *_tag_,int _type_,const char *lang)
-{
- static int first=1;
-
- if(_type_&XMLPARSE_TAG_START)
-   {
-    XMLPARSE_ASSERT_STRING(_tag_,lang);
-
-    if(!store_lang && first)
-       store=1;
-    else if(store_lang && !strcmp(store_lang,lang))
-       store=1;
-    else
-       store=0;
-
-    first=0;
-   }
-
- if(_type_&XMLPARSE_TAG_END && store)
-   {
-    store=0;
-    stored=1;
-   }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the RoutinoTranslationsType XSD type is seen
-
-  int RoutinoTranslationsType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int RoutinoTranslationsType_function(const char *_tag_,int _type_)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the XML declaration is seen
-
-  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *version The contents of the 'version' attribute (or NULL if not defined).
-
-  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-//static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
-//{
-// return(0);
-//}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   The XML translation parser.
 
   int ParseXMLTranslations Returns 0 if OK or something else in case of an error.
@@ -1198,7 +1228,7 @@ static int languageType_function(const char *_tag_,int _type_,const char *lang)
 
 int ParseXMLTranslations(const char *filename,const char *language)
 {
- FILE *file;
+ int fd;
  int retval;
 
  store_lang=language;
@@ -1209,17 +1239,11 @@ int ParseXMLTranslations(const char *filename,const char *language)
     return(1);
    }
 
- file=fopen(filename,"r");
-
- if(!file)
-   {
-    fprintf(stderr,"Error: Cannot open translations file '%s' for reading.\n",filename);
-    return(1);
-   }
+ fd=OpenFile(filename);
 
- retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME|XMLPARSE_RETURN_ATTR_ENCODED);
+ retval=ParseXML(fd,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_ERRNONAME|XMLPARSE_RETURN_ATTR_ENCODED);
 
- fclose(file);
+ CloseFile(fd);
 
  if(retval)
     return(1);
diff --git a/src/types.c b/src/types.c
index d7842cf..44850af 100644
--- a/src/types.c
+++ b/src/types.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -119,8 +119,8 @@ Transport TransportType(const char *transport)
    case 'm':
     if(!strcmp(transport,"moped"))
        return(Transport_Moped);
-    if(!strcmp(transport,"motorbike"))
-       return(Transport_Motorbike);
+    if(!strcmp(transport,"motorcycle"))
+       return(Transport_Motorcycle);
     if(!strcmp(transport,"motorcar"))
        return(Transport_Motorcar);
     break;
@@ -163,6 +163,11 @@ Property PropertyType(const char *property)
        return(Property_Bridge);
     break;
 
+   case 'c':
+    if(!strcmp(property,"cyclebothways"))
+       return(Property_CycleBothWays);
+    break;
+
    case 'f':
     if(!strcmp(property,"footroute"))
        return(Property_FootRoute);
@@ -240,6 +245,8 @@ const char *HighwayName(Highway highway)
     ;
    case Highway_Roundabout:
     ;
+   case Highway_Area:
+    ;
    }
 
  return(NULL);
@@ -271,8 +278,8 @@ const char *TransportName(Transport transport)
     return("bicycle");
    case Transport_Moped:
     return("moped");
-   case Transport_Motorbike:
-    return("motorbike");
+   case Transport_Motorcycle:
+    return("motorcycle");
    case Transport_Motorcar:
     return("motorcar");
    case Transport_Goods:
@@ -323,6 +330,9 @@ const char *PropertyName(Property property)
    case Property_BicycleRoute:
     return("bicycleroute");
 
+   case Property_CycleBothWays:
+    return("cyclebothways");
+
    case Property_Count:
     ;
   }
@@ -459,10 +469,10 @@ const char *AllowedNameList(transports_t allowed)
     strcat(string,"moped");
    }
 
- if(allowed & Transports_Motorbike)
+ if(allowed & Transports_Motorcycle)
    {
     if(*string) strcat(string,", ");
-    strcat(string,"motorbike");
+    strcat(string,"motorcycle");
    }
 
  if(allowed & Transports_Motorcar)
@@ -584,8 +594,8 @@ const char *TransportList(void)
         "    bicycle    = Bicycle\n"
         "    wheelchair = Wheelchair\n"
         "    horse      = Horse\n"
-        "    moped      = Moped     (Small motorbike, limited speed)\n"
-        "    motorbike  = Motorbike\n"
+        "    moped      = Moped     (Small motorcycle, limited speed)\n"
+        "    motorcycle = Motorcycle\n"
         "    motorcar   = Motorcar\n"
         "    goods      = Goods     (Small lorry, van)\n"
         "    hgv        = HGV       (Heavy Goods Vehicle - large lorry)\n"
diff --git a/src/types.h b/src/types.h
index 44a1b74..69a7928 100644
--- a/src/types.h
+++ b/src/types.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -39,6 +39,9 @@
 #define NWAYPOINTS 99
 
 
+/*+ An undefined waypoint index. +*/
+#define NO_WAYPOINT    ((waypoint_t)~0)
+
 /*+ An undefined node index. +*/
 #define NO_NODE        ((index_t)~0)
 
@@ -128,6 +131,10 @@
 /* Simple Types */
 
 
+/*+ A waypoint index. +*/
+typedef uint16_t waypoint_t;
+
+
 /*+ A node, segment, way or relation index. +*/
 typedef uint32_t index_t;
 
@@ -179,10 +186,10 @@ typedef uint16_t ll_off_t;
 /*+ Node flags. +*/
 typedef uint16_t nodeflags_t;
 
-/*+ A distance, measured in metres. +*/
+/*+ A distance, measured in metres (will not overflow for any earth-based distance). +*/
 typedef uint32_t distance_t;
 
-/*+ A duration, measured in 1/10th seconds. +*/
+/*+ A duration, measured in 1/10th seconds (will not overflow for 13 years). +*/
 typedef uint32_t duration_t;
 
 /*+ A routing optimisation score. +*/
@@ -232,8 +239,9 @@ typedef enum _Highway
 
   Highway_Count        = 14,       /* One more than the number of highway types. */
 
-  Highway_OneWay       = 32,
-  Highway_Roundabout   = 64
+  Highway_OneWay       =  32,
+  Highway_Roundabout   =  64,
+  Highway_Area         = 128
  }
  Highway;
 
@@ -279,7 +287,7 @@ typedef enum _Transport
   Transport_Wheelchair =  3,
   Transport_Bicycle    =  4,
   Transport_Moped      =  5,
-  Transport_Motorbike  =  6,
+  Transport_Motorcycle =  6,
   Transport_Motorcar   =  7,
   Transport_Goods      =  8,
   Transport_HGV        =  9,
@@ -305,7 +313,7 @@ typedef enum _Transports
   Transports_Wheelchair = TRANSPORTS(Transport_Wheelchair),
   Transports_Bicycle    = TRANSPORTS(Transport_Bicycle   ),
   Transports_Moped      = TRANSPORTS(Transport_Moped     ),
-  Transports_Motorbike  = TRANSPORTS(Transport_Motorbike ),
+  Transports_Motorcycle = TRANSPORTS(Transport_Motorcycle),
   Transports_Motorcar   = TRANSPORTS(Transport_Motorcar  ),
   Transports_Goods      = TRANSPORTS(Transport_Goods     ),
   Transports_HGV        = TRANSPORTS(Transport_HGV       ),
@@ -330,8 +338,9 @@ typedef enum _Property
   Property_Tunnel       = 4,
   Property_FootRoute    = 5,
   Property_BicycleRoute = 6,
+  Property_CycleBothWays= 7,
 
-  Property_Count        = 7       /* One more than the number of property types. */
+  Property_Count        = 8       /* One more than the number of property types. */
  }
  Property;
 
@@ -346,14 +355,15 @@ typedef enum _Properties
  {
   Properties_None         = 0,
 
-  Properties_Paved        = PROPERTIES(Property_Paved       ),
-  Properties_Multilane    = PROPERTIES(Property_Multilane   ),
-  Properties_Bridge       = PROPERTIES(Property_Bridge      ),
-  Properties_Tunnel       = PROPERTIES(Property_Tunnel      ),
-  Properties_FootRoute    = PROPERTIES(Property_FootRoute   ),
-  Properties_BicycleRoute = PROPERTIES(Property_BicycleRoute),
+  Properties_Paved        = PROPERTIES(Property_Paved        ),
+  Properties_Multilane    = PROPERTIES(Property_Multilane    ),
+  Properties_Bridge       = PROPERTIES(Property_Bridge       ),
+  Properties_Tunnel       = PROPERTIES(Property_Tunnel       ),
+  Properties_FootRoute    = PROPERTIES(Property_FootRoute    ),
+  Properties_BicycleRoute = PROPERTIES(Property_BicycleRoute ),
+  Properties_CycleBothWays= PROPERTIES(Property_CycleBothWays),
 
-  Properties_ALL          = PROPERTIES(Property_Count       )-1
+  Properties_ALL          = PROPERTIES(Property_Count        )-1
  }
  Properties;
 
@@ -374,32 +384,37 @@ typedef uint8_t width_t;
 typedef uint8_t length_t;
 
 
-/*+ Conversion of km/hr to speed_t. +*/
-#define kph_to_speed(xxx)      (speed_t)(xxx)
+/*+ Conversion of km/hr to speed_t - simple inline function with error checking. +*/
+inline static speed_t kph_to_speed(double xxx);
+inline static speed_t kph_to_speed(double xxx) { if(xxx>255) return(255); if(xxx<0) return(0); return((speed_t)xxx); }
 
 /*+ Conversion of speed_t to km/hr. +*/
 #define speed_to_kph(xxx)      (int)(xxx)
 
-/*+ Conversion of tonnes to weight_t. +*/
-#define tonnes_to_weight(xxx)  (weight_t)((xxx)*5)
+/*+ Conversion of tonnes to weight_t - simple inline function with error checking. +*/
+inline static weight_t tonnes_to_weight(double xxx);
+inline static weight_t tonnes_to_weight(double xxx) { if(xxx>51) return(255); if(xxx<0) return(0); return((weight_t)(xxx*5)); }
 
 /*+ Conversion of weight_t to tonnes. +*/
 #define weight_to_tonnes(xxx)  ((double)(xxx)/5.0)
 
-/*+ Conversion of metres to height_t. +*/
-#define metres_to_height(xxx)  (height_t)((xxx)*10)
+/*+ Conversion of metres to height_t - simple inline function with error checking. +*/
+inline static height_t metres_to_height(double xxx);
+inline static height_t metres_to_height(double xxx) { if(xxx>25.5) return(255); if(xxx<0) return(0); return((height_t)(xxx*10)); }
 
 /*+ Conversion of height_t to metres. +*/
 #define height_to_metres(xxx)  ((double)(xxx)/10.0)
 
-/*+ Conversion of metres to width_t. +*/
-#define metres_to_width(xxx)   (width_t)((xxx)*10)
+/*+ Conversion of metres to width_t - simple inline function with error checking. +*/
+inline static width_t metres_to_width(double xxx);
+inline static width_t metres_to_width(double xxx) { if(xxx>25.5) return(255); if(xxx<0) return(0); return((width_t)(xxx*10)); }
 
 /*+ Conversion of width_t to metres. +*/
 #define width_to_metres(xxx)   ((double)(xxx)/10.0)
 
-/*+ Conversion of metres to length_t. +*/
-#define metres_to_length(xxx)  (length_t)((xxx)*10)
+/*+ Conversion of metres to length_t - simple inline function with error checking. +*/
+inline static length_t metres_to_length(double xxx);
+inline static length_t metres_to_length(double xxx) { if(xxx>25.5) return(255); if(xxx<0) return(0); return((length_t)(xxx*10)); }
 
 /*+ Conversion of length_t to metres. +*/
 #define length_to_metres(xxx)  ((double)(xxx)/10.0)
diff --git a/src/uncompress.c b/src/uncompress.c
new file mode 100644
index 0000000..aee16d1
--- /dev/null
+++ b/src/uncompress.c
@@ -0,0 +1,450 @@
+/***************************************
+ File uncompression.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2012-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+#if defined(USE_BZIP2) && USE_BZIP2
+#define BZ_NO_STDIO
+#include <bzlib.h>
+#endif
+
+#if defined(USE_GZIP) && USE_GZIP
+#include <zlib.h>
+#endif
+
+#if defined(USE_XZ) && USE_XZ
+#include <lzma.h>
+#endif
+
+#include "logging.h"
+#include "uncompress.h"
+
+
+/* Local functions */
+
+static int pipe_and_fork(int filefd,int *pipefd);
+
+#if defined(USE_BZIP2) && USE_BZIP2
+static void uncompress_bzip2_pipe(int filefd,int pipefd);
+#endif
+
+#if defined(USE_GZIP) && USE_GZIP
+static void uncompress_gzip_pipe(int filefd,int pipefd);
+#endif
+
+#if defined(USE_XZ) && USE_XZ
+static void uncompress_xz_pipe(int filefd,int pipefd);
+#endif
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create a child process to uncompress data on a file descriptor as if it were a pipe.
+
+  int Uncompress_Bzip2 Returns the file descriptor of the uncompressed end of the pipe.
+
+  int filefd The file descriptor of the compressed end of the pipe.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int Uncompress_Bzip2(int filefd)
+{
+#if defined(USE_BZIP2) && USE_BZIP2
+
+ int pipefd=-1;
+
+ if(pipe_and_fork(filefd,&pipefd))
+    return(pipefd);
+
+ uncompress_bzip2_pipe(filefd,pipefd);
+
+ exit(EXIT_SUCCESS);
+
+#else /* USE_BZIP2 */
+
+ logassert(0,"No bzip2 compression support available (re-compile and try again)");
+
+ return(0);
+
+#endif /* USE_BZIP2 */
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create a child process to uncompress data on a file descriptor as if it were a pipe.
+
+  int Uncompress_Gzip Returns the file descriptor of the uncompressed end of the pipe.
+
+  int filefd The file descriptor of the compressed end of the pipe.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int Uncompress_Gzip(int filefd)
+{
+#if defined(USE_GZIP) && USE_GZIP
+
+ int pipefd=-1;
+
+ if(pipe_and_fork(filefd,&pipefd))
+    return(pipefd);
+
+ uncompress_gzip_pipe(filefd,pipefd);
+
+ exit(EXIT_SUCCESS);
+
+#else /* USE_GZIP */
+
+ logassert(0,"No gzip compression support available (re-compile and try again)");
+
+ return(0);
+
+#endif /* USE_GZIP */
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create a child process to uncompress data on a file descriptor as if it were a pipe.
+
+  int Uncompress_Xz Returns the file descriptor of the uncompressed end of the pipe.
+
+  int filefd The file descriptor of the compressed end of the pipe.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int Uncompress_Xz(int filefd)
+{
+#if defined(USE_XZ) && USE_XZ
+
+ int pipefd=-1;
+
+ if(pipe_and_fork(filefd,&pipefd))
+    return(pipefd);
+
+ uncompress_xz_pipe(filefd,pipefd);
+
+ exit(EXIT_SUCCESS);
+
+#else /* USE_XZ */
+
+ logassert(0,"No xz compression support available (re-compile and try again)");
+
+ return(0);
+
+#endif /* USE_XZ */
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Create a pipe and then fork returning in the parent and child with a different end of the pipe.
+
+  int pipe_and_fork Returns 1 for the reading (parent) end of the pipe and 0 for the writing (child) end.
+
+  int filefd The file descriptor of the file.
+
+  int *pipefd Returns the file descriptor for the end of the pipe.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int pipe_and_fork(int filefd,int *pipefd)
+{
+ int pipe_fd[2]={-1,-1};
+ pid_t childpid;
+
+#define PIPE_READER 0
+#define PIPE_WRITER 1
+
+ if(pipe(pipe_fd))
+   {
+    logassert(0,"Cannot create pipe for uncompressor (try without using a compressed file)");
+    return(1);
+   }
+
+ if((childpid=fork()) == -1)
+   {
+    logassert(0,"Cannot create new process for uncompressor (try without using a compressed file)");
+    return(1);
+   }
+
+ if(childpid==0)   /* The child */
+   {
+    int i;
+
+    *pipefd=pipe_fd[PIPE_WRITER];
+
+    /* Close all unneeded file descriptors */
+
+    for(i=0;i<255;i++)
+       if(i!=filefd && i!=*pipefd)
+          close(i);
+
+    return(0);    
+   }
+ else   /* The parent */
+   {
+    struct sigaction action;
+
+    *pipefd=pipe_fd[PIPE_READER];
+
+    /* Close all unneeded file descriptors */
+
+    close(pipe_fd[PIPE_WRITER]);
+    close(filefd);
+
+    /* Ignore child exiting and pipe signals */
+
+    /* SIGCHLD */
+    action.sa_handler=SIG_IGN;
+    sigemptyset(&action.sa_mask);
+    action.sa_flags=0;
+    sigaction(SIGCHLD,&action,NULL);
+
+    /* SIGPIPE */
+    action.sa_handler=SIG_IGN;
+    sigemptyset(&action.sa_mask);
+    action.sa_flags=0;
+    sigaction(SIGPIPE,&action,NULL);
+
+    return(1);
+   }
+}
+
+
+#if defined(USE_BZIP2) && USE_BZIP2
+
+/*++++++++++++++++++++++++++++++++++++++
+  Uncompress a file using bzip2 as a pipeline.
+
+  int filefd The incoming, compressed, data.
+
+  int pipefd The outgoing, uncompressed, data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void uncompress_bzip2_pipe(int filefd,int pipefd)
+{
+ bz_stream bz={0};
+ char inbuffer[16384],outbuffer[16384];
+ int infinished=0;
+ int state;
+
+ if(BZ2_bzDecompressInit(&bz,0,0)!=BZ_OK)
+    exit(EXIT_FAILURE);
+
+ do
+   {
+    if(bz.avail_in==0 && !infinished)
+      {
+       ssize_t n=read(filefd,inbuffer,sizeof(inbuffer));
+
+       if(n<=0)
+          infinished=1;
+       else
+         {
+          bz.next_in=inbuffer;
+          bz.avail_in=n;
+         }
+      }
+
+    bz.next_out=outbuffer;
+    bz.avail_out=sizeof(outbuffer);
+
+    state=BZ2_bzDecompress(&bz);
+
+    if(state!=BZ_OK && state!=BZ_STREAM_END)
+       exit(EXIT_FAILURE);
+
+    if(bz.avail_out<sizeof(outbuffer))
+      {
+       char *p;
+       ssize_t m,n;
+
+       p=outbuffer;
+       n=sizeof(outbuffer)-bz.avail_out;
+
+       while(n>0)
+         {
+          m=write(pipefd,p,n);
+
+          if(m<=0)
+             exit(EXIT_FAILURE);
+
+          p+=m;
+          n-=m;
+         }
+      }
+   }
+ while(state!=BZ_STREAM_END);
+
+ if(BZ2_bzDecompressEnd(&bz)!=BZ_OK)
+    exit(EXIT_FAILURE);
+
+ exit(EXIT_SUCCESS);
+}
+
+#endif /* USE_BZIP2 */
+
+
+#if defined(USE_GZIP) && USE_GZIP
+
+/*++++++++++++++++++++++++++++++++++++++
+  Uncompress a file using gzip as a pipeline.
+
+  int filefd The incoming, compressed, data.
+
+  int pipefd The outgoing, uncompressed, data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void uncompress_gzip_pipe(int filefd,int pipefd)
+{
+ z_stream z={0};
+ unsigned char inbuffer[16384],outbuffer[16384];
+ int infinished=0;
+ int state;
+
+ if(inflateInit2(&z,15+32)!=Z_OK)
+    exit(EXIT_FAILURE);
+
+ do
+   {
+    if(z.avail_in==0 && !infinished)
+      {
+       ssize_t n=read(filefd,inbuffer,sizeof(inbuffer));
+
+       if(n<=0)
+          infinished=1;
+       else
+         {
+          z.next_in=inbuffer;
+          z.avail_in=n;
+         }
+      }
+
+    z.next_out=outbuffer;
+    z.avail_out=sizeof(outbuffer);
+
+    state=inflate(&z,Z_NO_FLUSH);
+
+    if(state!=Z_OK && state!=Z_STREAM_END)
+      {
+       exit(EXIT_FAILURE);
+      }
+
+    if(z.avail_out<sizeof(outbuffer))
+      {
+       unsigned char *p;
+       ssize_t n,m;
+
+       p=outbuffer;
+       n=sizeof(outbuffer)-z.avail_out;
+
+       while(n>0)
+         {
+          m=write(pipefd,p,n);
+
+          if(m<=0)
+             exit(EXIT_FAILURE);
+
+          p+=m;
+          n-=m;
+         }
+      }
+   }
+ while(state!=Z_STREAM_END);
+
+ if(inflateEnd(&z)!=Z_OK)
+    exit(EXIT_FAILURE);
+
+ exit(EXIT_SUCCESS);
+}
+
+#endif /* USE_GZIP */
+
+
+#if defined(USE_XZ) && USE_XZ
+
+/*++++++++++++++++++++++++++++++++++++++
+  Uncompress a file using xz as a pipeline.
+
+  int filefd The incoming, compressed, data.
+
+  int pipefd The outgoing, uncompressed, data.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void uncompress_xz_pipe(int filefd,int pipefd)
+{
+ lzma_stream lzma=LZMA_STREAM_INIT;
+ unsigned char inbuffer[16384],outbuffer[16384];
+ int infinished=0;
+ lzma_ret retval;
+
+ if(lzma_stream_decoder(&lzma,UINT64_MAX,0)!=LZMA_OK)
+    exit(EXIT_FAILURE);
+
+ do
+   {
+    if(lzma.avail_in==0 && !infinished)
+      {
+       ssize_t n=read(filefd,inbuffer,sizeof(inbuffer));
+
+       if(n<=0)
+          infinished=1;
+       else
+         {
+          lzma.next_in=inbuffer;
+          lzma.avail_in=n;
+         }
+      }
+
+    lzma.next_out=outbuffer;
+    lzma.avail_out=sizeof(outbuffer);
+
+    retval=lzma_code(&lzma,LZMA_RUN);
+
+    if(retval!=LZMA_OK && retval!=LZMA_STREAM_END)
+      {
+       exit(EXIT_FAILURE);
+      }
+
+    if(lzma.avail_out<sizeof(outbuffer))
+      {
+       unsigned char *p;
+       ssize_t n,m;
+
+       p=outbuffer;
+       n=sizeof(outbuffer)-lzma.avail_out;
+
+       while(n>0)
+         {
+          m=write(pipefd,p,n);
+
+          if(m<=0)
+             exit(EXIT_FAILURE);
+
+          p+=m;
+          n-=m;
+         }
+      }
+   }
+ while(retval!=LZMA_STREAM_END);
+
+ lzma_end(&lzma);
+
+ exit(EXIT_SUCCESS);
+}
+
+#endif /* USE_XZ */
diff --git a/src/osmparser.h b/src/uncompress.h
similarity index 62%
copy from src/osmparser.h
copy to src/uncompress.h
index ba91717..5c850ca 100644
--- a/src/osmparser.h
+++ b/src/uncompress.h
@@ -1,9 +1,9 @@
 /***************************************
- Header file for OSM parser function prototype.
+ Function prototypes for file uncompression.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2012-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -20,19 +20,13 @@
  ***************************************/
 
 
-#ifndef OSMPARSER_H
-#define OSMPARSER_H    /*+ To stop multiple inclusions. +*/
+#ifndef UNCOMPRESS_H
+#define UNCOMPRESS_H    /*+ To stop multiple inclusions. +*/
 
-#include <stdio.h>
+int Uncompress_Bzip2(int filefd);
 
-#include "typesx.h"
+int Uncompress_Gzip(int filefd);
 
+int Uncompress_Xz(int filefd);
 
-/* Functions in osmparser.c */
-
-int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,RelationsX *OSMRelations);
-
-int ParseOSC(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,RelationsX *OSMRelations);
-
-
-#endif /* OSMPARSER_H */
+#endif /* UNCOMPRESS_H */
diff --git a/src/visualiser.c b/src/visualiser.c
index 0d1403c..eed8bc1 100644
--- a/src/visualiser.c
+++ b/src/visualiser.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -29,6 +29,7 @@
 #include "segments.h"
 #include "ways.h"
 #include "relations.h"
+#include "errorlog.h"
 
 #include "visualiser.h"
 
@@ -63,10 +64,12 @@ static double LonMax;
 static int limit_type=0;
 static Highway highways=Highway_None;
 static Transports transports=Transports_None;
+static Properties properties=Properties_None;
 
 /* Local functions */
 
 static void find_all_nodes(Nodes *nodes,callback_t callback);
+
 static void output_junctions(index_t node,double latitude,double longitude);
 static void output_super(index_t node,double latitude,double longitude);
 static void output_oneway(index_t node,double latitude,double longitude);
@@ -75,6 +78,7 @@ static void output_transport(index_t node,double latitude,double longitude);
 static void output_barrier(index_t node,double latitude,double longitude);
 static void output_turnrestriction(index_t node,double latitude,double longitude);
 static void output_limits(index_t node,double latitude,double longitude);
+static void output_property(index_t node,double latitude,double longitude);
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -152,7 +156,7 @@ static void output_junctions(index_t node,double latitude,double longitude)
  while(segmentp);
 
  if(count!=2 || difference)
-    printf("%.6f %.6f %d\n",radians_to_degrees(latitude),radians_to_degrees(longitude),count);
+    printf("node%"Pindex_t" %.6f %.6f %d\n",node,radians_to_degrees(latitude),radians_to_degrees(longitude),count);
 }
 
 
@@ -214,7 +218,7 @@ static void output_super(index_t node,double latitude,double longitude)
  if(!IsSuperNode(nodep))
     return;
 
- printf("%.6f %.6f n\n",radians_to_degrees(latitude),radians_to_degrees(longitude));
+ printf("node%"Pindex_t" %.6f %.6f\n",node,radians_to_degrees(latitude),radians_to_degrees(longitude));
 
  segmentp=FirstSegment(OSMSegments,nodep,1);
 
@@ -225,10 +229,10 @@ static void output_super(index_t node,double latitude,double longitude)
        index_t othernode=OtherNode(segmentp,node);
        double lat,lon;
 
-       GetLatLong(OSMNodes,othernode,&lat,&lon);
+       GetLatLong(OSMNodes,othernode,NULL,&lat,&lon);
 
        if(node>othernode || (lat<LatMin || lat>LatMax || lon<LonMin || lon>LonMax))
-          printf("%.6f %.6f s\n",radians_to_degrees(lat),radians_to_degrees(lon));
+          printf("segment%"Pindex_t" %.6f %.6f\n",IndexSegment(OSMSegments,segmentp),radians_to_degrees(lat),radians_to_degrees(lon));
       }
 
     segmentp=NextSegment(OSMSegments,segmentp,node);
@@ -304,12 +308,12 @@ static void output_oneway(index_t node,double latitude,double longitude)
          {
           double lat,lon;
 
-          GetLatLong(OSMNodes,othernode,&lat,&lon);
+          GetLatLong(OSMNodes,othernode,NULL,&lat,&lon);
 
           if(IsOnewayFrom(segmentp,node))
-             printf("%.6f %.6f %.6f %.6f\n",radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon));
+             printf("segment%"Pindex_t" %.6f %.6f %.6f %.6f\n",IndexSegment(OSMSegments,segmentp),radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon));
           else if(IsOnewayFrom(segmentp,othernode))
-             printf("%.6f %.6f %.6f %.6f\n",radians_to_degrees(lat),radians_to_degrees(lon),radians_to_degrees(latitude),radians_to_degrees(longitude));
+             printf("segment%"Pindex_t" %.6f %.6f %.6f %.6f\n",IndexSegment(OSMSegments,segmentp),radians_to_degrees(lat),radians_to_degrees(lon),radians_to_degrees(latitude),radians_to_degrees(longitude));
          }
       }
 
@@ -394,9 +398,9 @@ static void output_highway(index_t node,double latitude,double longitude)
             {
              double lat,lon;
 
-             GetLatLong(OSMNodes,othernode,&lat,&lon);
+             GetLatLong(OSMNodes,othernode,NULL,&lat,&lon);
 
-             printf("%.6f %.6f %.6f %.6f\n",radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon));
+             printf("segment%"Pindex_t" %.6f %.6f %.6f %.6f\n",IndexSegment(OSMSegments,segmentp),radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon));
             }
          }
       }
@@ -408,7 +412,7 @@ static void output_highway(index_t node,double latitude,double longitude)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Output the data for segments allowed for a paticular type of traffic.
+  Output the data for segments allowed for a particular type of traffic.
 
   Nodes *nodes The set of nodes to use.
 
@@ -482,9 +486,9 @@ static void output_transport(index_t node,double latitude,double longitude)
             {
              double lat,lon;
 
-             GetLatLong(OSMNodes,othernode,&lat,&lon);
+             GetLatLong(OSMNodes,othernode,NULL,&lat,&lon);
 
-             printf("%.6f %.6f %.6f %.6f\n",radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon));
+             printf("segment%"Pindex_t" %.6f %.6f %.6f %.6f\n",IndexSegment(OSMSegments,segmentp),radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon));
             }
          }
       }
@@ -496,7 +500,7 @@ static void output_transport(index_t node,double latitude,double longitude)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Output the data for nodes disallowed for a paticular type of traffic.
+  Output the data for nodes disallowed for a particular type of traffic.
 
   Nodes *nodes The set of nodes to use.
 
@@ -554,7 +558,7 @@ static void output_barrier(index_t node,double latitude,double longitude)
  Node *nodep=LookupNode(OSMNodes,node,1);
 
  if(!(nodep->allow&transports))
-    printf("%.6f %.6f\n",radians_to_degrees(latitude),radians_to_degrees(longitude));
+    printf("node%"Pindex_t" %.6f %.6f\n",node,radians_to_degrees(latitude),radians_to_degrees(longitude));
 }
 
 
@@ -631,14 +635,16 @@ static void output_turnrestriction(index_t node,double latitude,double longitude
     to_segmentp  =LookupSegment(OSMSegments,relation->to  ,2);
 
     from_node=OtherNode(from_segmentp,node);
-    to_node=OtherNode(to_segmentp,node);
+    to_node  =OtherNode(to_segmentp  ,node);
 
-    GetLatLong(OSMNodes,from_node,&from_lat,&from_lon);
-    GetLatLong(OSMNodes,to_node,&to_lat,&to_lon);
+    GetLatLong(OSMNodes,from_node,NULL,&from_lat,&from_lon);
+    GetLatLong(OSMNodes,to_node  ,NULL,&to_lat  ,&to_lon);
 
-    printf("%.6f %.6f %.6f %.6f %.6f %.6f\n",radians_to_degrees(from_lat),radians_to_degrees(from_lon),
-                                             radians_to_degrees(latitude),radians_to_degrees(longitude),
-                                             radians_to_degrees(to_lat),radians_to_degrees(to_lon));
+    printf("turn-relation%"Pindex_t" %.6f %.6f %.6f %.6f %.6f %.6f\n",
+           turnrelation,
+           radians_to_degrees(from_lat),radians_to_degrees(from_lon),
+           radians_to_degrees(latitude),radians_to_degrees(longitude),
+           radians_to_degrees(to_lat),radians_to_degrees(to_lon));
 
     turnrelation=FindNextTurnRelation1(OSMRelations,turnrelation);
    }
@@ -870,6 +876,7 @@ static void output_limits(index_t node,double latitude,double longitude)
 {
  Node *nodep=LookupNode(OSMNodes,node,1);
  Segment *segmentp,segmentps[MAX_SEG_PER_NODE];
+ index_t segments[MAX_SEG_PER_NODE];
  int limits[MAX_SEG_PER_NODE];
  int count=0;
  int i,j,same=0;
@@ -883,6 +890,7 @@ static void output_limits(index_t node,double latitude,double longitude)
        Way *wayp=LookupWay(OSMWays,segmentp->way,1);
 
        segmentps[count]=*segmentp;
+       segments [count]=IndexSegment(OSMSegments,segmentp);
 
        switch(limit_type)
          {
@@ -918,7 +926,7 @@ static void output_limits(index_t node,double latitude,double longitude)
 
  /* Display the interesting limits */
 
- printf("%.6f %.6f\n",radians_to_degrees(latitude),radians_to_degrees(longitude));
+ printf("node%"Pindex_t" %.6f %.6f\n",node,radians_to_degrees(latitude),radians_to_degrees(longitude));
 
  for(i=0;i<count;i++)
    {
@@ -931,24 +939,24 @@ static void output_limits(index_t node,double latitude,double longitude)
       {
        double lat,lon;
 
-       GetLatLong(OSMNodes,OtherNode(&segmentps[i],node),&lat,&lon);
+       GetLatLong(OSMNodes,OtherNode(&segmentps[i],node),NULL,&lat,&lon);
 
        switch(limit_type)
          {
          case SPEED_LIMIT:
-          printf("%.6f %.6f %d\n",radians_to_degrees(lat),radians_to_degrees(lon),speed_to_kph(limits[i]));
+          printf("segment%"Pindex_t" %.6f %.6f %d\n",segments[i],radians_to_degrees(lat),radians_to_degrees(lon),speed_to_kph(limits[i]));
           break;
          case WEIGHT_LIMIT:
-          printf("%.6f %.6f %.1f\n",radians_to_degrees(lat),radians_to_degrees(lon),weight_to_tonnes(limits[i]));
+          printf("segment%"Pindex_t" %.6f %.6f %.1f\n",segments[i],radians_to_degrees(lat),radians_to_degrees(lon),weight_to_tonnes(limits[i]));
           break;
          case HEIGHT_LIMIT:
-          printf("%.6f %.6f %.1f\n",radians_to_degrees(lat),radians_to_degrees(lon),height_to_metres(limits[i]));
+          printf("segment%"Pindex_t" %.6f %.6f %.1f\n",segments[i],radians_to_degrees(lat),radians_to_degrees(lon),height_to_metres(limits[i]));
           break;
          case WIDTH_LIMIT:
-          printf("%.6f %.6f %.1f\n",radians_to_degrees(lat),radians_to_degrees(lon),width_to_metres(limits[i]));
+          printf("segment%"Pindex_t" %.6f %.6f %.1f\n",segments[i],radians_to_degrees(lat),radians_to_degrees(lon),width_to_metres(limits[i]));
           break;
          case LENGTH_LIMIT:
-          printf("%.6f %.6f %.1f\n",radians_to_degrees(lat),radians_to_degrees(lon),length_to_metres(limits[i]));
+          printf("segment%"Pindex_t" %.6f %.6f %.1f\n",segments[i],radians_to_degrees(lat),radians_to_degrees(lon),length_to_metres(limits[i]));
           break;
          }
       }
@@ -957,6 +965,94 @@ static void output_limits(index_t node,double latitude,double longitude)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Output the data for segments that have a particular property.
+
+  Nodes *nodes The set of nodes to use.
+
+  Segments *segments The set of segments to use.
+
+  Ways *ways The set of ways to use.
+
+  Relations *relations The set of relations to use.
+
+  double latmin The minimum latitude.
+
+  double latmax The maximum latitude.
+
+  double lonmin The minimum longitude.
+
+  double lonmax The maximum longitude.
+
+  Property property The type of property.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void OutputProperty(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax,Property property)
+{
+ /* Use local variables so that the callback doesn't need to pass them backwards and forwards */
+
+ OSMNodes=nodes;
+ OSMSegments=segments;
+ OSMWays=ways;
+ OSMRelations=relations;
+
+ LatMin=latmin;
+ LatMax=latmax;
+ LonMin=lonmin;
+ LonMax=lonmax;
+
+ /* Iterate through the nodes and process them */
+
+ properties=PROPERTIES(property);
+
+ find_all_nodes(nodes,(callback_t)output_property);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a single node and all connected one-way segments (called as a callback).
+
+  index_t node The node to output.
+
+  double latitude The latitude of the node.
+
+  double longitude The longitude of the node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void output_property(index_t node,double latitude,double longitude)
+{
+ Node *nodep=LookupNode(OSMNodes,node,1);
+ Segment *segmentp;
+
+ segmentp=FirstSegment(OSMSegments,nodep,1);
+
+ do
+   {
+    if(IsNormalSegment(segmentp))
+      {
+       index_t othernode=OtherNode(segmentp,node);
+
+       if(node>othernode)
+         {
+          Way *wayp=LookupWay(OSMWays,segmentp->way,1);
+
+          if(wayp->props&properties)
+            {
+             double lat,lon;
+
+             GetLatLong(OSMNodes,othernode,NULL,&lat,&lon);
+
+             printf("segment%"Pindex_t" %.6f %.6f %.6f %.6f\n",IndexSegment(OSMSegments,segmentp),radians_to_degrees(latitude),radians_to_degrees(longitude),radians_to_degrees(lat),radians_to_degrees(lon));
+            }
+         }
+      }
+
+    segmentp=NextSegment(OSMSegments,segmentp,node);
+   }
+ while(segmentp);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   A function to iterate through all nodes and call a callback function for each one.
 
   Nodes *nodes The set of nodes to use.
@@ -975,6 +1071,12 @@ static void find_all_nodes(Nodes *nodes,callback_t callback)
 
  /* Loop through all of the nodes. */
 
+ if(latminbin<0)                   latminbin=0;
+ if(latmaxbin>nodes->file.latbins) latmaxbin=nodes->file.latbins-1;
+
+ if(lonminbin<0)                   lonminbin=0;
+ if(lonmaxbin>nodes->file.lonbins) lonmaxbin=nodes->file.lonbins-1;
+
  for(latb=latminbin;latb<=latmaxbin;latb++)
     for(lonb=lonminbin;lonb<=lonmaxbin;lonb++)
       {
@@ -998,3 +1100,56 @@ static void find_all_nodes(Nodes *nodes,callback_t callback)
          }
       }
 }
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Output the data for error logs within the region.
+
+  ErrorLogs *errorlogs The set of error logs to use.
+
+  double latmin The minimum latitude.
+
+  double latmax The maximum latitude.
+
+  double lonmin The minimum longitude.
+
+  double lonmax The maximum longitude.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void OutputErrorLog(ErrorLogs *errorlogs,double latmin,double latmax,double lonmin,double lonmax)
+{
+ ll_bin_t latminbin=latlong_to_bin(radians_to_latlong(latmin))-errorlogs->file.latzero;
+ ll_bin_t latmaxbin=latlong_to_bin(radians_to_latlong(latmax))-errorlogs->file.latzero;
+ ll_bin_t lonminbin=latlong_to_bin(radians_to_latlong(lonmin))-errorlogs->file.lonzero;
+ ll_bin_t lonmaxbin=latlong_to_bin(radians_to_latlong(lonmax))-errorlogs->file.lonzero;
+ ll_bin_t latb,lonb;
+ index_t i,index1,index2;
+
+ /* Loop through all of the error logs. */
+
+ for(latb=latminbin;latb<=latmaxbin;latb++)
+    for(lonb=lonminbin;lonb<=lonmaxbin;lonb++)
+      {
+       ll_bin2_t llbin=lonb*errorlogs->file.latbins+latb;
+
+       if(llbin<0 || llbin>(errorlogs->file.latbins*errorlogs->file.lonbins))
+          continue;
+
+       index1=LookupErrorLogOffset(errorlogs,llbin);
+       index2=LookupErrorLogOffset(errorlogs,llbin+1);
+
+       if(index2>errorlogs->file.number_geo)
+          index2=errorlogs->file.number_geo;
+
+       for(i=index1;i<index2;i++)
+         {
+          ErrorLog *errorlogp=LookupErrorLog(errorlogs,i,1);
+
+          double lat=latlong_to_radians(bin_to_latlong(errorlogs->file.latzero+latb)+off_to_latlong(errorlogp->latoffset));
+          double lon=latlong_to_radians(bin_to_latlong(errorlogs->file.lonzero+lonb)+off_to_latlong(errorlogp->lonoffset));
+
+          if(lat>latmin && lat<latmax && lon>lonmin && lon<lonmax)
+             printf("errorlog%"Pindex_t" %.6f %.6f\n",i,radians_to_degrees(lat),radians_to_degrees(lon));
+         }
+      }
+}
diff --git a/src/visualiser.h b/src/visualiser.h
index f05ca69..05d15e7 100644
--- a/src/visualiser.h
+++ b/src/visualiser.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -24,6 +24,7 @@
 #define VISUALISER_H    /*+ To stop multiple inclusions. +*/
 
 #include "types.h"
+#include "errorlog.h"
 
 
 /* Functions in visualiser.c */
@@ -50,5 +51,9 @@ void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *re
 void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
 void OutputLengthLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
 
+void OutputProperty(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax,Property property);
+
+void OutputErrorLog(ErrorLogs *errorlogs,double latmin,double latmax,double lonmin,double lonmax);
+
 
 #endif /* VISUALISER_H */
diff --git a/src/ways.c b/src/ways.c
index f911887..60cb08d 100644
--- a/src/ways.c
+++ b/src/ways.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -24,6 +24,7 @@
 
 #include "ways.h"
 
+#include "cache.h"
 #include "files.h"
 
 
@@ -38,9 +39,6 @@
 Ways *LoadWayList(const char *filename)
 {
  Ways *ways;
-#if SLIM
- int i;
-#endif
 
  ways=(Ways*)malloc(sizeof(Ways));
 
@@ -59,19 +57,15 @@ Ways *LoadWayList(const char *filename)
 
 #else
 
- ways->fd=ReOpenFile(filename);
+ ways->fd=SlimMapFile(filename);
 
  /* Copy the WaysFile header structure from the loaded data */
 
- ReadFile(ways->fd,&ways->file,sizeof(WaysFile));
-
- for(i=0;i<sizeof(ways->cached)/sizeof(ways->cached[0]);i++)
-    ways->incache[i]=NO_WAY;
+ SlimFetch(ways->fd,&ways->file,sizeof(WaysFile),0);
 
  ways->namesoffset=sizeof(WaysFile)+ways->file.number*sizeof(Way);
 
- for(i=0;i<sizeof(ways->cached)/sizeof(ways->cached[0]);i++)
-    ways->ncached[i]=NULL;
+ ways->cache=NewWayCache();
 
 #endif
 
@@ -80,6 +74,30 @@ Ways *LoadWayList(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Destroy the way list.
+
+  Ways *ways The way list to destroy.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DestroyWayList(Ways *ways)
+{
+#if !SLIM
+
+ ways->data=UnmapFile(ways->data);
+
+#else
+
+ ways->fd=SlimUnmapFile(ways->fd);
+
+ DeleteWayCache(ways->cache);
+
+#endif
+
+ free(ways);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Return 0 if the two ways are the same (in respect of their types and limits),
            otherwise return positive or negative to allow sorting.
 
diff --git a/src/ways.h b/src/ways.h
index 6650370..b4f4066 100644
--- a/src/ways.h
+++ b/src/ways.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -28,6 +28,7 @@
 
 #include "types.h"
 
+#include "cache.h"
 #include "files.h"
 
 
@@ -84,10 +85,11 @@ struct _Ways
  off_t      namesoffset;        /*+ The offset of the names within the file. +*/
 
  Way        cached[3];          /*+ Two cached nodes read from the file in slim mode. +*/
- index_t    incache[3];         /*+ The indexes of the cached ways. +*/
 
  char      *ncached[3];         /*+ The cached way name. +*/
 
+ WayCache  *cache;              /*+ A RAM cache of ways read from the file. +*/
+
 #endif
 };
 
@@ -96,6 +98,8 @@ struct _Ways
 
 Ways *LoadWayList(const char *filename);
 
+void DestroyWayList(Ways *ways);
+
 int WaysCompare(Way *way1p,Way *way2p);
 
 
@@ -111,9 +115,23 @@ int WaysCompare(Way *way1p,Way *way2p);
 
 #else
 
-static Way *LookupWay(Ways *ways,index_t index,int position);
+static inline Way *LookupWay(Ways *ways,index_t index,int position);
+
+static inline char *WayName(Ways *ways,Way *wayp);
+
+CACHE_NEWCACHE_PROTO(Way)
+CACHE_DELETECACHE_PROTO(Way)
+CACHE_FETCHCACHE_PROTO(Way)
+CACHE_INVALIDATECACHE_PROTO(Way)
+
+
+/* Inline functions */
 
-static char *WayName(Ways *ways,Way *wayp);
+CACHE_STRUCTURE(Way)
+CACHE_NEWCACHE(Way)
+CACHE_DELETECACHE(Way)
+CACHE_FETCHCACHE(Way)
+CACHE_INVALIDATECACHE(Way)
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -130,12 +148,7 @@ static char *WayName(Ways *ways,Way *wayp);
 
 static inline Way *LookupWay(Ways *ways,index_t index,int position)
 {
- if(ways->incache[position-1]!=index)
-   {
-    SeekReadFile(ways->fd,&ways->cached[position-1],sizeof(Way),sizeof(WaysFile)+(off_t)index*sizeof(Way));
-
-    ways->incache[position-1]=index;
-   }
+ ways->cached[position-1]=*FetchCachedWay(ways->cache,index,ways->fd,sizeof(WaysFile));
 
  return(&ways->cached[position-1]);
 }
@@ -154,29 +167,22 @@ static inline Way *LookupWay(Ways *ways,index_t index,int position)
 static inline char *WayName(Ways *ways,Way *wayp)
 {
  int position=wayp-&ways->cached[-1];
-
  int n=0;
 
- SeekFile(ways->fd,ways->namesoffset+wayp->name);
-
  if(!ways->ncached[position-1])
-    ways->ncached[position-1]=(char*)malloc(32);
+    ways->ncached[position-1]=(char*)malloc(64);
 
- while(1)
+ while(!SlimFetch(ways->fd,ways->ncached[position-1]+n,64,ways->namesoffset+wayp->name+n))
    {
     int i;
-    int m=ReadFile(ways->fd,ways->ncached[position-1]+n,32);
 
-    if(m<0)
-       break;
-    
-    for(i=n;i<n+32;i++)
+    for(i=n;i<n+64;i++)
        if(ways->ncached[position-1][i]==0)
           goto exitloop;
 
-    n+=32;
+    n+=64;
 
-    ways->ncached[position-1]=(char*)realloc((void*)ways->ncached[position-1],n+32);
+    ways->ncached[position-1]=(char*)realloc((void*)ways->ncached[position-1],n+64);
    }
 
  exitloop:
diff --git a/src/waysx.c b/src/waysx.c
index 584cd19..d7d737d 100644
--- a/src/waysx.c
+++ b/src/waysx.c
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -27,6 +27,7 @@
 #include "ways.h"
 
 #include "typesx.h"
+#include "nodesx.h"
 #include "segmentsx.h"
 #include "waysx.h"
 
@@ -49,10 +50,9 @@ static SegmentsX *sortsegmentsx;
 /* Local functions */
 
 static int sort_by_id(WayX *a,WayX *b);
-static int deduplicate_by_id(WayX *wayx,index_t index);
+static int deduplicate_and_index_by_id(WayX *wayx,index_t index);
 
-static int sort_by_name(WayX *a,WayX *b);
-static int index_by_id(WayX *wayx,index_t index);
+static int sort_by_name(char *a,char *b);
 
 static int delete_unused(WayX *wayx,index_t index);
 static int sort_by_name_and_prop_and_id(WayX *a,WayX *b);
@@ -86,35 +86,34 @@ WaysX *NewWayList(int append,int readonly)
  if(append || readonly)
     if(ExistsFile(waysx->filename))
       {
-       off_t size,position=0;
+       FILESORT_VARINT waysize;
        int fd;
 
-       size=SizeFile(waysx->filename);
+       fd=ReOpenFileBuffered(waysx->filename);
 
-       fd=ReOpenFile(waysx->filename);
-
-       while(position<size)
+       while(!ReadFileBuffered(fd,&waysize,FILESORT_VARSIZE))
          {
-          FILESORT_VARINT waysize;
-
-          SeekReadFile(fd,&waysize,FILESORT_VARSIZE,position);
+          SkipFileBuffered(fd,waysize);
 
           waysx->number++;
-          position+=waysize+FILESORT_VARSIZE;
          }
 
-       CloseFile(fd);
+       CloseFileBuffered(fd);
 
        RenameFile(waysx->filename,waysx->filename_tmp);
       }
 
  if(append)
-    waysx->fd=OpenFileAppend(waysx->filename_tmp);
+    waysx->fd=OpenFileBufferedAppend(waysx->filename_tmp);
  else if(!readonly)
-    waysx->fd=OpenFileNew(waysx->filename_tmp);
+    waysx->fd=OpenFileBufferedNew(waysx->filename_tmp);
  else
     waysx->fd=-1;
 
+#if SLIM
+ waysx->cache=NewWayXCache();
+#endif
+
 
  waysx->nfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
 
@@ -145,6 +144,9 @@ void FreeWayList(WaysX *waysx,int keep)
  if(waysx->idata)
     free(waysx->idata);
 
+ if(waysx->odata)
+    free(waysx->odata);
+
  if(waysx->cdata)
     free(waysx->cdata);
 
@@ -152,6 +154,10 @@ void FreeWayList(WaysX *waysx,int keep)
 
  free(waysx->nfilename_tmp);
 
+#if SLIM
+ DeleteWayXCache(waysx->cache);
+#endif
+
  free(waysx);
 }
 
@@ -165,22 +171,31 @@ void FreeWayList(WaysX *waysx,int keep)
 
   Way *way The way data itself.
 
+  node_t *nodes The list of nodes for this way.
+
+  int nnodes The number of nodes for this way.
+
   const char *name The name or reference of the way.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendWayList(WaysX *waysx,way_t id,Way *way,const char *name)
+void AppendWayList(WaysX *waysx,way_t id,Way *way,node_t *nodes,int nnodes,const char *name)
 {
  WayX wayx;
  FILESORT_VARINT size;
+ node_t nonode=NO_NODE_ID;
 
  wayx.id=id;
  wayx.way=*way;
 
- size=sizeof(WayX)+strlen(name)+1;
+ size=sizeof(WayX)+(nnodes+1)*sizeof(node_t)+strlen(name)+1;
+
+ WriteFileBuffered(waysx->fd,&size,FILESORT_VARSIZE);
+ WriteFileBuffered(waysx->fd,&wayx,sizeof(WayX));
 
- WriteFile(waysx->fd,&size,FILESORT_VARSIZE);
- WriteFile(waysx->fd,&wayx,sizeof(WayX));
- WriteFile(waysx->fd,name,strlen(name)+1);
+ WriteFileBuffered(waysx->fd,nodes  ,nnodes*sizeof(node_t));
+ WriteFileBuffered(waysx->fd,&nonode,       sizeof(node_t));
+
+ WriteFileBuffered(waysx->fd,name,strlen(name)+1);
 
  waysx->number++;
 
@@ -197,7 +212,7 @@ void AppendWayList(WaysX *waysx,way_t id,Way *way,const char *name)
 void FinishWayList(WaysX *waysx)
 {
  if(waysx->fd!=-1)
-    waysx->fd=CloseFile(waysx->fd);
+    waysx->fd=CloseFileBuffered(waysx->fd);
 }
 
 
@@ -277,24 +292,34 @@ void SortWayList(WaysX *waysx)
 
  /* Re-open the file read-only and a new file writeable */
 
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ waysx->fd=ReOpenFileBuffered(waysx->filename_tmp);
 
  DeleteFile(waysx->filename_tmp);
 
- fd=OpenFileNew(waysx->filename_tmp);
+ fd=OpenFileBufferedNew(waysx->filename_tmp);
+
+ /* Allocate the array of indexes */
+
+ waysx->idata=(way_t*)malloc(waysx->number*sizeof(way_t));
+
+ logassert(waysx->idata,"Failed to allocate memory (try using slim mode?)"); /* Check malloc() worked */
 
  /* Sort the ways by ID and index them */
 
+ sortwaysx=waysx;
+
  xnumber=waysx->number;
 
  waysx->number=filesort_vary(waysx->fd,fd,NULL,
                                           (int (*)(const void*,const void*))sort_by_id,
-                                          (int (*)(void*,index_t))deduplicate_by_id);
+                                          (int (*)(void*,index_t))deduplicate_and_index_by_id);
+
+ waysx->knumber=waysx->number;
 
  /* Close the files */
 
- waysx->fd=CloseFile(waysx->fd);
- CloseFile(fd);
+ waysx->fd=CloseFileBuffered(waysx->fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
@@ -327,16 +352,16 @@ static int sort_by_id(WayX *a,WayX *b)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Discard duplicate ways.
+  Discard duplicate ways and create and index of ids.
 
-  int deduplicate_by_id Return 1 if the value is to be kept, otherwise 0.
+  int deduplicate_and_index_by_id Return 1 if the value is to be kept, otherwise 0.
 
   WayX *wayx The extended way.
 
   index_t index The number of sorted ways that have already been written to the output file.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int deduplicate_by_id(WayX *wayx,index_t index)
+static int deduplicate_and_index_by_id(WayX *wayx,index_t index)
 {
  static way_t previd=NO_WAY_ID;
 
@@ -347,7 +372,11 @@ static int deduplicate_by_id(WayX *wayx,index_t index)
     if(wayx->way.type==WAY_DELETED)
        return(0);
     else
+      {
+       sortwaysx->idata[index]=wayx->id;
+
        return(1);
+      }
    }
  else
     return(0);
@@ -355,167 +384,269 @@ static int deduplicate_by_id(WayX *wayx,index_t index)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Extract the way names from the ways and reference the list of names from the ways.
+  Split the ways into segments and way names.
+
+  SegmentsX *SplitWays Returns the set of segments that have been created.
 
   WaysX *waysx The set of ways to process.
 
+  NodesX *nodesx The set of nodes to use.
+
   int keep If set to 1 then keep the old data file otherwise delete it.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void ExtractWayNames(WaysX *waysx,int keep)
+SegmentsX *SplitWays(WaysX *waysx,NodesX *nodesx,int keep)
 {
+ SegmentsX *segmentsx;
  index_t i;
- int fd;
- char *names[2]={NULL,NULL};
- int namelen[2]={0,0};
- int nnames=0;
- uint32_t lastlength=0;
+ int fd,nfd;
+ char *name=NULL;
+ int namelen=0;
 
  /* Print the start message */
 
- printf_first("Sorting Ways by Name");
+ printf_first("Splitting Ways: Ways=0 Segments=0");
+
+ segmentsx=NewSegmentList();
 
  /* Re-open the file read-only and a new file writeable */
 
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ waysx->fd=ReOpenFileBuffered(waysx->filename_tmp);
 
  if(keep)
     RenameFile(waysx->filename_tmp,waysx->filename);
  else
     DeleteFile(waysx->filename_tmp);
 
- fd=OpenFileNew(waysx->filename_tmp);
+ fd=OpenFileBufferedNew(waysx->filename_tmp);
 
- /* Sort the ways to allow separating the names */
+ nfd=OpenFileBufferedNew(waysx->nfilename_tmp);
 
- filesort_vary(waysx->fd,fd,NULL,
-                            (int (*)(const void*,const void*))sort_by_name,
-                            NULL);
+ /* Loop through the ways and create the segments and way names */
 
- /* Close the files */
+ for(i=0;i<waysx->number;i++)
+   {
+    WayX wayx;
+    FILESORT_VARINT size;
+    node_t node,prevnode=NO_NODE_ID;
+    index_t index,previndex=NO_NODE;
 
- waysx->fd=CloseFile(waysx->fd);
- CloseFile(fd);
+    ReadFileBuffered(waysx->fd,&size,FILESORT_VARSIZE);
 
- /* Print the final message */
+    ReadFileBuffered(waysx->fd,&wayx,sizeof(WayX));
 
- printf_last("Sorted Ways by Name: Ways=%"Pindex_t,waysx->number);
+    waysx->allow|=wayx.way.allow;
 
+    while(!ReadFileBuffered(waysx->fd,&node,sizeof(node_t)) && node!=NO_NODE_ID)
+      {
+       index=IndexNodeX(nodesx,node);
 
- /* Print the start message */
+       if(prevnode==node)
+         {
+          logerror("Way %"Pway_t" contains node %"Pnode_t" that is connected to itself.\n",logerror_way(wayx.id),logerror_node(node));
+         }
+       else if(index==NO_NODE)
+         {
+          logerror("Way %"Pway_t" contains node %"Pnode_t" that does not exist in the Routino database.\n",logerror_way(wayx.id),logerror_node(node));
+         }
+       else if(previndex==NO_NODE)
+          ;
+       else
+         {
+          distance_t segment_flags=0;
 
- printf_first("Separating Way Names: Ways=0 Names=0");
+          if(wayx.way.type&Highway_OneWay)
+             segment_flags|=ONEWAY_1TO2;
 
- /* Re-open the file read-only and new files writeable */
+          if(wayx.way.type&Highway_Area)
+             segment_flags|=SEGMENT_AREA;
 
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+          AppendSegmentList(segmentsx,i,previndex,index,segment_flags);
+         }
 
- DeleteFile(waysx->filename_tmp);
+       prevnode=node;
+       previndex=index;
 
- fd=OpenFileNew(waysx->filename_tmp);
+       size-=sizeof(node_t);
+      }
 
- waysx->nfd=OpenFileNew(waysx->nfilename_tmp);
+    size-=sizeof(node_t)+sizeof(WayX);
 
- /* Copy from the single file into two files */
+    if(namelen<size)
+       name=(char*)realloc((void*)name,namelen=size);
 
- for(i=0;i<waysx->number;i++)
-   {
-    WayX wayx;
-    FILESORT_VARINT size;
+    ReadFileBuffered(waysx->fd,name,size);
 
-    ReadFile(waysx->fd,&size,FILESORT_VARSIZE);
+    WriteFileBuffered(fd,&wayx,sizeof(WayX));
 
-    if(namelen[nnames%2]<size)
-       names[nnames%2]=(char*)realloc((void*)names[nnames%2],namelen[nnames%2]=size);
+    size+=sizeof(index_t);
 
-    ReadFile(waysx->fd,&wayx,sizeof(WayX));
-    ReadFile(waysx->fd,names[nnames%2],size-sizeof(WayX));
+    WriteFileBuffered(nfd,&size,FILESORT_VARSIZE);
+    WriteFileBuffered(nfd,&i,sizeof(index_t));
+    WriteFileBuffered(nfd,name,size-sizeof(index_t));
 
-    if(nnames==0 || strcmp(names[0],names[1]))
-      {
-       WriteFile(waysx->nfd,names[nnames%2],size-sizeof(WayX));
+    if(!((i+1)%1000))
+       printf_middle("Splitting Ways: Ways=%"Pindex_t" Segments=%"Pindex_t,i+1,segmentsx->number);
+   }
 
-       lastlength=waysx->nlength;
-       waysx->nlength+=size-sizeof(WayX);
+ FinishSegmentList(segmentsx);
 
-       nnames++;
-      }
+ if(name) free(name);
 
-    wayx.way.name=lastlength;
+ /* Close the files */
 
-    WriteFile(fd,&wayx,sizeof(WayX));
+ waysx->fd=CloseFileBuffered(waysx->fd);
+ CloseFileBuffered(fd);
 
-    if(!((i+1)%1000))
-       printf_middle("Separating Way Names: Ways=%"Pindex_t" Names=%"Pindex_t,i+1,nnames);
-   }
+ CloseFileBuffered(nfd);
 
- if(names[0]) free(names[0]);
- if(names[1]) free(names[1]);
+ /* Print the final message */
+
+ printf_last("Split Ways: Ways=%"Pindex_t" Segments=%"Pindex_t,waysx->number,segmentsx->number);
+
+ return(segmentsx);
+}
 
- /* Close the files */
 
- waysx->fd=CloseFile(waysx->fd);
- CloseFile(fd);
 
- waysx->nfd=CloseFile(waysx->nfd);
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the way names and assign the offsets to the ways.
+
+  WaysX *waysx The set of ways to process.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SortWayNames(WaysX *waysx)
+{
+ index_t i;
+ int nfd;
+ char *names[2]={NULL,NULL};
+ int namelen[2]={0,0};
+ int nnames=0;
+ uint32_t lastlength=0;
+
+ /* Print the start message */
+
+ printf_first("Sorting Way Names");
+
+ /* Re-open the file read-only and new file writeable */
+
+ waysx->nfd=ReOpenFileBuffered(waysx->nfilename_tmp);
+
+ DeleteFile(waysx->nfilename_tmp);
+
+ nfd=OpenFileBufferedNew(waysx->nfilename_tmp);
+
+ /* Sort the way names */
+
+ waysx->nlength=0;
+
+ filesort_vary(waysx->nfd,nfd,NULL,
+                              (int (*)(const void*,const void*))sort_by_name,
+                              NULL);
+
+ /* Close the files */
+
+ waysx->nfd=CloseFileBuffered(waysx->nfd);
+ CloseFileBuffered(nfd);
 
  /* Print the final message */
 
- printf_last("Separated Way Names: Ways=%"Pindex_t" Names=%"Pindex_t,waysx->number,nnames);
+ printf_last("Sorted Way Names: Ways=%"Pindex_t,waysx->number);
 
 
  /* Print the start message */
 
- printf_first("Sorting Ways");
+ printf_first("Updating Ways with Names: Ways=0 Names=0");
 
- /* Re-open the file read-only and a new file writeable */
+ /* Map into memory /  open the file */
 
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+#if !SLIM
+ waysx->data=MapFileWriteable(waysx->filename_tmp);
+#else
+ waysx->fd=SlimMapFileWriteable(waysx->filename_tmp);
+#endif
 
- DeleteFile(waysx->filename_tmp);
+ /* Re-open the file read-only and new file writeable */
 
- fd=OpenFileNew(waysx->filename_tmp);
+ waysx->nfd=ReOpenFileBuffered(waysx->nfilename_tmp);
 
- /* Allocate the array of indexes */
+ DeleteFile(waysx->nfilename_tmp);
 
- waysx->idata=(way_t*)malloc(waysx->number*sizeof(way_t));
+ nfd=OpenFileBufferedNew(waysx->nfilename_tmp);
 
- logassert(waysx->idata,"Failed to allocate memory (try using slim mode?)"); /* Check malloc() worked */
+ /* Update the ways and de-duplicate the names */
+
+ for(i=0;i<waysx->number;i++)
+   {
+    WayX *wayx;
+    index_t index;
+    FILESORT_VARINT size;
 
- /* Sort the ways by ID */
+    ReadFileBuffered(waysx->nfd,&size,FILESORT_VARSIZE);
 
- sortwaysx=waysx;
+    if(namelen[nnames%2]<size)
+       names[nnames%2]=(char*)realloc((void*)names[nnames%2],namelen[nnames%2]=size);
 
- filesort_fixed(waysx->fd,fd,sizeof(WayX),NULL,
-                                          (int (*)(const void*,const void*))sort_by_id,
-                                          (int (*)(void*,index_t))index_by_id);
+    ReadFileBuffered(waysx->nfd,&index,sizeof(index_t));
+    ReadFileBuffered(waysx->nfd,names[nnames%2],size-sizeof(index_t));
+
+    if(nnames==0 || strcmp(names[0],names[1]))
+      {
+       WriteFileBuffered(nfd,names[nnames%2],size-sizeof(index_t));
+
+       lastlength=waysx->nlength;
+       waysx->nlength+=size-sizeof(index_t);
+
+       nnames++;
+      }
+
+    wayx=LookupWayX(waysx,index,1);
+
+    wayx->way.name=lastlength;
+
+    PutBackWayX(waysx,wayx);
+
+    if(!((i+1)%1000))
+       printf_middle("Updating Ways with Names: Ways=%"Pindex_t" Names=%"Pindex_t,i+1,nnames);
+   }
+
+ if(names[0]) free(names[0]);
+ if(names[1]) free(names[1]);
 
  /* Close the files */
 
- waysx->fd=CloseFile(waysx->fd);
- CloseFile(fd);
+ waysx->nfd=CloseFileBuffered(waysx->nfd);
+ CloseFileBuffered(nfd);
+
+ /* Unmap from memory / close the files */
+
+#if !SLIM
+ waysx->data=UnmapFile(waysx->data);
+#else
+ waysx->fd=SlimUnmapFile(waysx->fd);
+#endif
 
  /* Print the final message */
 
- printf_last("Sorted Ways: Ways=%"Pindex_t,waysx->number);
+ printf_last("Updated Ways with Names: Ways=%"Pindex_t" Names=%"Pindex_t,waysx->number,nnames);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Sort the ways into name order and then id order.
+  Sort the ways into name order.
 
   int sort_by_name Returns the comparison of the name fields.
 
-  WayX *a The first extended Way.
+  char *a The first way name.
 
-  WayX *b The second extended Way.
+  char *b The second way name.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int sort_by_name(WayX *a,WayX *b)
+static int sort_by_name(char *a,char *b)
 {
  int compare;
- char *a_name=(char*)a+sizeof(WayX);
- char *b_name=(char*)b+sizeof(WayX);
+ char *a_name=a+sizeof(index_t);
+ char *b_name=b+sizeof(index_t);
 
  compare=strcmp(a_name,b_name);
 
@@ -527,24 +658,6 @@ static int sort_by_name(WayX *a,WayX *b)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Create the index of identifiers.
-
-  int index_by_id Return 1 if the value is to be kept, otherwise 0.
-
-  WayX *wayx The extended way.
-
-  index_t index The number of sorted ways that have already been written to the output file.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int index_by_id(WayX *wayx,index_t index)
-{
- sortwaysx->idata[index]=wayx->id;
-
- return(1);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   Compact the way list, removing duplicated ways and unused ways.
 
   WaysX *waysx The set of ways to process.
@@ -572,11 +685,11 @@ void CompactWayList(WaysX *waysx,SegmentsX *segmentsx)
 
  /* Re-open the file read-only and a new file writeable */
 
- waysx->fd=ReOpenFile(waysx->filename_tmp);
+ waysx->fd=ReOpenFileBuffered(waysx->filename_tmp);
 
  DeleteFile(waysx->filename_tmp);
 
- fd=OpenFileNew(waysx->filename_tmp);
+ fd=OpenFileBufferedNew(waysx->filename_tmp);
 
  /* Sort the ways to allow compacting according to the properties */
 
@@ -589,16 +702,18 @@ void CompactWayList(WaysX *waysx,SegmentsX *segmentsx)
 
  /* Close the files */
 
- waysx->fd=CloseFile(waysx->fd);
- CloseFile(fd);
+ waysx->fd=CloseFileBuffered(waysx->fd);
+ CloseFileBuffered(fd);
+
+ /* Free the data */
+
+ free(segmentsx->usedway);
+ segmentsx->usedway=NULL;
 
  /* Print the final message */
 
  printf_last("Sorted and Compacted Ways: Ways=%"Pindex_t" Unique=%"Pindex_t,waysx->number,cnumber);
  waysx->number=cnumber;
-
- free(segmentsx->usedway);
- segmentsx->usedway=NULL;
 }
 
 
@@ -704,7 +819,7 @@ void SaveWayList(WaysX *waysx,const char *filename)
 {
  index_t i;
  int fd;
- int position=0;
+ index_t position=0;
  WayX wayx;
  WaysFile waysfile={0};
  highways_t   highways=0;
@@ -717,24 +832,24 @@ void SaveWayList(WaysX *waysx,const char *filename)
 
  /* Re-open the files */
 
- waysx->fd=ReOpenFile(waysx->filename_tmp);
- waysx->nfd=ReOpenFile(waysx->nfilename_tmp);
+ waysx->fd=ReOpenFileBuffered(waysx->filename_tmp);
+ waysx->nfd=ReOpenFileBuffered(waysx->nfilename_tmp);
 
  /* Write out the ways data */
 
- fd=OpenFileNew(filename);
+ fd=OpenFileBufferedNew(filename);
 
- SeekFile(fd,sizeof(WaysFile));
+ SeekFileBuffered(fd,sizeof(WaysFile));
 
  for(i=0;i<waysx->number;i++)
    {
-    ReadFile(waysx->fd,&wayx,sizeof(WayX));
+    ReadFileBuffered(waysx->fd,&wayx,sizeof(WayX));
 
     highways|=HIGHWAYS(wayx.way.type);
     allow   |=wayx.way.allow;
     props   |=wayx.way.props;
 
-    WriteFile(fd,&wayx.way,sizeof(Way));
+    WriteFileBuffered(fd,&wayx.way,sizeof(Way));
 
     if(!((i+1)%1000))
        printf_middle("Writing Ways: Ways=%"Pindex_t,i+1);
@@ -742,27 +857,27 @@ void SaveWayList(WaysX *waysx,const char *filename)
 
  /* Write out the ways names */
 
- SeekFile(fd,sizeof(WaysFile)+(off_t)waysx->number*sizeof(Way));
+ SeekFileBuffered(fd,sizeof(WaysFile)+(off_t)waysx->number*sizeof(Way));
 
  while(position<waysx->nlength)
    {
-    int len=1024;
+    size_t len=1024;
     char temp[1024];
 
     if((waysx->nlength-position)<1024)
        len=waysx->nlength-position;
 
-    ReadFile(waysx->nfd,temp,len);
+    ReadFileBuffered(waysx->nfd,temp,len);
 
-    WriteFile(fd,temp,len);
+    WriteFileBuffered(fd,temp,len);
 
     position+=len;
    }
 
  /* Close the files */
 
- waysx->fd=CloseFile(waysx->fd);
- waysx->nfd=CloseFile(waysx->nfd);
+ waysx->fd=CloseFileBuffered(waysx->fd);
+ waysx->nfd=CloseFileBuffered(waysx->nfd);
 
  /* Write out the header structure */
 
@@ -772,10 +887,10 @@ void SaveWayList(WaysX *waysx,const char *filename)
  waysfile.allow   =allow;
  waysfile.props   =props;
 
- SeekFile(fd,0);
- WriteFile(fd,&waysfile,sizeof(WaysFile));
+ SeekFileBuffered(fd,0);
+ WriteFileBuffered(fd,&waysfile,sizeof(WaysFile));
 
- CloseFile(fd);
+ CloseFileBuffered(fd);
 
  /* Print the final message */
 
diff --git a/src/waysx.h b/src/waysx.h
index f4ed51a..0a75f8d 100644
--- a/src/waysx.h
+++ b/src/waysx.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2012 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -30,6 +30,7 @@
 #include "typesx.h"
 #include "ways.h"
 
+#include "cache.h"
 #include "files.h"
 
 
@@ -54,6 +55,9 @@ struct _WaysX
  int      fd;                   /*+ The file descriptor of the open file (for the WaysX). +*/
 
  index_t  number;               /*+ The number of extended ways still being considered. +*/
+ index_t  knumber;              /*+ The number of extended ways kept for next time. +*/
+
+ transports_t allow;            /*+ The types of traffic that were seen when parsing. +*/
 
 #if !SLIM
 
@@ -64,9 +68,12 @@ struct _WaysX
  WayX     cached[3];            /*+ Three cached extended ways read from the file in slim mode. +*/
  index_t  incache[3];           /*+ The indexes of the cached extended ways. +*/
 
+ WayXCache *cache;              /*+ A RAM cache of extended ways read from the file. +*/
+
 #endif
 
  way_t   *idata;                /*+ The extended way IDs (sorted by ID). +*/
+ off_t   *odata;                /*+ The offset of the way in the file (used for error log). +*/
 
  index_t *cdata;                /*+ The compacted way IDs (same order as sorted ways). +*/
 
@@ -84,14 +91,16 @@ struct _WaysX
 WaysX *NewWayList(int append,int readonly);
 void FreeWayList(WaysX *waysx,int keep);
 
-void AppendWayList(WaysX *waysx,way_t id,Way *way,const char *name);
+void AppendWayList(WaysX *waysx,way_t id,Way *way,node_t *nodes,int nnodes,const char *name);
 void FinishWayList(WaysX *waysx);
 
 index_t IndexWayX(WaysX *waysx,way_t id);
 
 void SortWayList(WaysX *waysx);
 
-void ExtractWayNames(WaysX *waysx,int keep);
+SegmentsX *SplitWays(WaysX *waysx,NodesX *nodesx,int keep);
+
+void SortWayNames(WaysX *waysx);
 
 void CompactWayList(WaysX *waysx,SegmentsX *segmentsx);
 
@@ -104,13 +113,31 @@ void SaveWayList(WaysX *waysx,const char *filename);
 
 #define LookupWayX(waysx,index,position)  &(waysx)->data[index]
   
-#define PutBackWayX(waysx,wayx)           /* nop */
+#define PutBackWayX(waysx,wayx)           while(0) { /* nop */ }
 
 #else
 
-static WayX *LookupWayX(WaysX *waysx,index_t index,int position);
+/* Prototypes */
+
+static inline WayX *LookupWayX(WaysX *waysx,index_t index,int position);
+
+static inline void PutBackWayX(WaysX *waysx,WayX *wayx);
+
+CACHE_NEWCACHE_PROTO(WayX)
+CACHE_DELETECACHE_PROTO(WayX)
+CACHE_FETCHCACHE_PROTO(WayX)
+CACHE_REPLACECACHE_PROTO(WayX)
+CACHE_INVALIDATECACHE_PROTO(WayX)
+
+
+/* Inline functions */
 
-static void PutBackWayX(WaysX *waysx,WayX *wayx);
+CACHE_STRUCTURE(WayX)
+CACHE_NEWCACHE(WayX)
+CACHE_DELETECACHE(WayX)
+CACHE_FETCHCACHE(WayX)
+CACHE_REPLACECACHE(WayX)
+CACHE_INVALIDATECACHE(WayX)
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -127,7 +154,7 @@ static void PutBackWayX(WaysX *waysx,WayX *wayx);
 
 static inline WayX *LookupWayX(WaysX *waysx,index_t index,int position)
 {
- SeekReadFile(waysx->fd,&waysx->cached[position-1],sizeof(WayX),(off_t)index*sizeof(WayX));
+ waysx->cached[position-1]=*FetchCachedWayX(waysx->cache,index,waysx->fd,0);
 
  waysx->incache[position-1]=index;
 
@@ -147,7 +174,7 @@ static inline void PutBackWayX(WaysX *waysx,WayX *wayx)
 {
  int position1=wayx-&waysx->cached[0];
 
- SeekWriteFile(waysx->fd,&waysx->cached[position1],sizeof(WayX),(off_t)waysx->incache[position1]*sizeof(WayX));
+ ReplaceCachedWayX(waysx->cache,wayx,waysx->incache[position1],waysx->fd,0);
 }
 
 #endif /* SLIM */
diff --git a/src/xml/Makefile b/src/xml/Makefile
index dc2d003..c4ddf87 100644
--- a/src/xml/Makefile
+++ b/src/xml/Makefile
@@ -2,7 +2,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2010-2012 Andrew M. Bishop
+# This file Copyright 2010-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -18,21 +18,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Programs
+# All configuration is in the top-level Makefile.conf
 
-CC=gcc
-LD=gcc
-
-# Program options
-
-CFLAGS=-Wall -Wmissing-prototypes -std=c99
-#CFLAGS+=-Wextra -pedantic
-LDFLAGS=-lm -lc
-
-CFLAGS+=-O3
-
-# Required to use stdio with files > 2GiB on 32-bit system.
-CFLAGS+=-D_FILE_OFFSET_BITS=64
+include ../../Makefile.conf
 
 # Compilation targets
 
@@ -49,7 +37,6 @@ EXE=xsd-to-xmlparser
 ########
 
 all: $(EXE) $(C) $(E)
-	@true
 
 ########
 
@@ -72,9 +59,6 @@ xsd-to-xmlparser : xsd-to-xmlparser.o ../xmlparse.o
 ../xmlparse.o : ../xmlparse.c ../xmlparse.h
 	cd .. && $(MAKE) xmlparse.o
 
-../xmlparse.c : ../xmlparse.l
-	cd .. && $(MAKE) xmlparse.c
-
 ########
 
 %.o : %.c
@@ -83,7 +67,7 @@ xsd-to-xmlparser : xsd-to-xmlparser.o ../xmlparse.o
 
 ########
 
-test : all test-skeleton
+test: test-skeleton
 	@status=true ;\
 	echo "" ;\
 	for good in test/good*.xml; do \
diff --git a/src/xml/xsd-to-xmlparser.c b/src/xml/xsd-to-xmlparser.c
index df2b6a4..7f27a35 100644
--- a/src/xml/xsd-to-xmlparser.c
+++ b/src/xml/xsd-to-xmlparser.c
@@ -5,7 +5,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2012 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -23,6 +23,7 @@
 
 
 #include <stdio.h>
+#include <unistd.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
@@ -61,13 +62,40 @@ static char *safe(const char *name);
 
 static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding);
 static int schemaType_function(const char *_tag_,int _type_,const char *elementFormDefault,const char *xmlns_xsd);
+static int elementType_function(const char *_tag_,int _type_,const char *name,const char *type,const char *minOccurs,const char *maxOccurs);
 static int complexType_function(const char *_tag_,int _type_,const char *name);
-static int attributeType_function(const char *_tag_,int _type_,const char *name,const char *type);
 static int sequenceType_function(const char *_tag_,int _type_);
-static int elementType_function(const char *_tag_,int _type_,const char *name,const char *type,const char *minOccurs,const char *maxOccurs);
+static int attributeType_function(const char *_tag_,int _type_,const char *name,const char *type);
 
 
-/* The XML tag definitions */
+/* The XML tag definitions (forward declarations) */
+
+static xmltag xmlDeclaration_tag;
+static xmltag schemaType_tag;
+static xmltag elementType_tag;
+static xmltag complexType_tag;
+static xmltag sequenceType_tag;
+static xmltag attributeType_tag;
+
+
+/* The XML tag definition values */
+
+/*+ The complete set of tags at the top level. +*/
+static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&schemaType_tag,NULL};
+
+/*+ The xmlDeclaration type tag. +*/
+static xmltag xmlDeclaration_tag=
+              {"xml",
+               2, {"version","encoding"},
+               xmlDeclaration_function,
+               {NULL}};
+
+/*+ The schemaType type tag. +*/
+static xmltag schemaType_tag=
+              {"xsd:schema",
+               2, {"elementFormDefault","xmlns:xsd"},
+               schemaType_function,
+               {&elementType_tag,&complexType_tag,NULL}};
 
 /*+ The elementType type tag. +*/
 static xmltag elementType_tag=
@@ -76,6 +104,13 @@ static xmltag elementType_tag=
                elementType_function,
                {NULL}};
 
+/*+ The complexType type tag. +*/
+static xmltag complexType_tag=
+              {"xsd:complexType",
+               1, {"name"},
+               complexType_function,
+               {&sequenceType_tag,&attributeType_tag,NULL}};
+
 /*+ The sequenceType type tag. +*/
 static xmltag sequenceType_tag=
               {"xsd:sequence",
@@ -90,33 +125,58 @@ static xmltag attributeType_tag=
                attributeType_function,
                {NULL}};
 
-/*+ The complexType type tag. +*/
-static xmltag complexType_tag=
-              {"xsd:complexType",
-               1, {"name"},
-               complexType_function,
-               {&sequenceType_tag,&attributeType_tag,NULL}};
 
-/*+ The schemaType type tag. +*/
-static xmltag schemaType_tag=
-              {"xsd:schema",
-               2, {"elementFormDefault","xmlns:xsd"},
-               schemaType_function,
-               {&elementType_tag,&complexType_tag,NULL}};
+/* The XML tag processing functions */
 
-/*+ The xmlDeclaration type tag. +*/
-static xmltag xmlDeclaration_tag=
-              {"xml",
-               2, {"version","encoding"},
-               xmlDeclaration_function,
-               {NULL}};
 
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the XML declaration is seen
 
-/*+ The complete set of tags at the top level. +*/
-static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&schemaType_tag,NULL};
+  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
 
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
 
-/* The XML tag processing functions */
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *version The contents of the 'version' attribute (or NULL if not defined).
+
+  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
+{
+ /* Add the XML declaration as a tag. */
+
+ currenttype=NULL;
+ elementType_function("xsd:element",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"xml","xmlDeclaration",NULL,NULL);
+ complexType_function("xsd:complexType",XMLPARSE_TAG_START,"xmlDeclaration");
+ attributeType_function("xsd:attribute",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"version",NULL);
+ attributeType_function("xsd:attribute",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"encoding",NULL);
+ complexType_function("xsd:complexType",XMLPARSE_TAG_END,NULL);
+ currenttype=NULL;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the schemaType XSD type is seen
+
+  int schemaType_function Returns 0 if no error occured or something else otherwise.
+
+  const char *_tag_ Set to the name of the element tag that triggered this function call.
+
+  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
+
+  const char *elementFormDefault The contents of the 'elementFormDefault' attribute (or NULL if not defined).
+
+  const char *xmlns_xsd The contents of the 'xmlns:xsd' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int schemaType_function(const char *_tag_,int _type_,const char *elementFormDefault,const char *xmlns_xsd)
+{
+ return(0);
+}
 
 
 /*++++++++++++++++++++++++++++++++++++++
@@ -179,57 +239,6 @@ static int elementType_function(const char *_tag_,int _type_,const char *name,co
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the sequenceType XSD type is seen
-
-  int sequenceType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int sequenceType_function(const char *_tag_,int _type_)
-{
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the attributeType XSD type is seen
-
-  int attributeType_function Returns 0 if no error occured or something else otherwise.
-
-  const char *_tag_ Set to the name of the element tag that triggered this function call.
-
-  int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *name The contents of the 'name' attribute (or NULL if not defined).
-
-  const char *type The contents of the 'type' attribute (or NULL if not defined).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int attributeType_function(const char *_tag_,int _type_,const char *name,const char *type)
-{
- int i;
-
- if(_type_==XMLPARSE_TAG_END)
-    return(0);
-
- for(i=0;i<ntagsx;i++)
-    if(!strcmp(tagsx[i]->type,currenttype))
-      {
-       tagsx[i]->attributes[tagsx[i]->nattributes]=strcpy(malloc(strlen(name)+1),name);
-       tagsx[i]->nattributes++;
-
-       if(tagsx[i]->nattributes==XMLPARSE_MAX_ATTRS)
-         {fprintf(stderr,"Too many attributes seen for type '%s'.\n",currenttype); exit(1);}
-      }
-
- return(0);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
   The function that is called when the complexType XSD type is seen
 
   int complexType_function Returns 0 if no error occured or something else otherwise.
@@ -253,41 +262,52 @@ static int complexType_function(const char *_tag_,int _type_,const char *name)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the schemaType XSD type is seen
+  The function that is called when the sequenceType XSD type is seen
 
-  int schemaType_function Returns 0 if no error occured or something else otherwise.
+  int sequenceType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
-
-  const char *elementFormDefault The contents of the 'elementFormDefault' attribute (or NULL if not defined).
-
-  const char *xmlns_xsd The contents of the 'xmlns:xsd' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int schemaType_function(const char *_tag_,int _type_,const char *elementFormDefault,const char *xmlns_xsd)
+static int sequenceType_function(const char *_tag_,int _type_)
 {
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the XML declaration is seen
+  The function that is called when the attributeType XSD type is seen
 
-  int xmlDeclaration_function Returns 0 if no error occured or something else otherwise.
+  int attributeType_function Returns 0 if no error occured or something else otherwise.
 
   const char *_tag_ Set to the name of the element tag that triggered this function call.
 
   int _type_ Set to XMLPARSE_TAG_START at the start of a tag and/or XMLPARSE_TAG_END at the end of a tag.
 
-  const char *version The contents of the 'version' attribute (or NULL if not defined).
+  const char *name The contents of the 'name' attribute (or NULL if not defined).
 
-  const char *encoding The contents of the 'encoding' attribute (or NULL if not defined).
+  const char *type The contents of the 'type' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int xmlDeclaration_function(const char *_tag_,int _type_,const char *version,const char *encoding)
+static int attributeType_function(const char *_tag_,int _type_,const char *name,const char *type)
 {
+ int i;
+
+ if(_type_==XMLPARSE_TAG_END)
+    return(0);
+
+ for(i=0;i<ntagsx;i++)
+    if(!strcmp(tagsx[i]->type,currenttype))
+      {
+       tagsx[i]->attributes[tagsx[i]->nattributes]=strcpy(malloc(strlen(name)+1),name);
+       tagsx[i]->nattributes++;
+
+       if(tagsx[i]->nattributes==XMLPARSE_MAX_ATTRS)
+         {fprintf(stderr,"Too many attributes seen for type '%s'.\n",currenttype); exit(1);}
+      }
+
  return(0);
 }
 
@@ -298,48 +318,16 @@ static int xmlDeclaration_function(const char *_tag_,int _type_,const char *vers
 
 int main(int argc,char **argv)
 {
- int i,j,k;
+ int i,j;
 
- if(ParseXML(stdin,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE))
+ /* Parse the XSD file */
+
+ if(ParseXML(STDIN_FILENO,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE))
    {
     fprintf(stderr,"Cannot parse XML file - exiting.\n");
     exit(1);
    }
 
- /* Add the XML declaration as a tag. */
-
- currenttype=NULL;
- elementType_function("xsd:element",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"xml","xmlDeclaration",NULL,NULL);
- complexType_function("xsd:complexType",XMLPARSE_TAG_START,"xmlDeclaration");
- attributeType_function("xsd:attribute",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"version",NULL);
- attributeType_function("xsd:attribute",XMLPARSE_TAG_START|XMLPARSE_TAG_END,"encoding",NULL);
- complexType_function("xsd:complexType",XMLPARSE_TAG_END,NULL);
-
- /* Sort the tags */
-
- sorttags:
-
- for(i=0;i<ntagsx;i++)
-   {
-    for(j=0;j<tagsx[i]->nsubtagsx;j++)
-      {
-       for(k=0;k<ntagsx;k++)
-          if(tagsx[i]->subtagsx[j]==tagsx[k])
-             break;
-
-       if(i<k)
-         {
-          xmltagx *temp=tagsx[i];
-
-          tagsx[i]=tagsx[k];
-
-          tagsx[k]=temp;
-
-          goto sorttags;
-         }
-      }
-   }
-
  /* Print the header */
 
  printf("/***************************************\n");
@@ -350,6 +338,7 @@ int main(int argc,char **argv)
  printf("\n");
  printf("\n");
  printf("#include <stdio.h>\n");
+ printf("#include <unistd.h>\n");
  printf("\n");
  printf("#include \"xmlparse.h\"\n");
 
@@ -360,7 +349,7 @@ int main(int argc,char **argv)
  printf("/* The XML tag processing function prototypes */\n");
  printf("\n");
 
- for(i=ntagsx-1;i>=0;i--)
+ for(i=0;i<ntagsx;i++)
    {
     printf("static int %s_function(const char *_tag_,int _type_",safe(tagsx[i]->type));
 
@@ -374,7 +363,23 @@ int main(int argc,char **argv)
 
  printf("\n");
  printf("\n");
- printf("/* The XML tag definitions */\n");
+ printf("/* The XML tag definitions (forward declarations) */\n");
+
+ printf("\n");
+
+ for(i=0;i<ntagsx;i++)
+    printf("static xmltag %s_tag;\n",safe(tagsx[i]->type));
+
+ printf("\n");
+ printf("\n");
+ printf("/* The XML tag definition values */\n");
+
+ printf("\n");
+ printf("/*+ The complete set of tags at the top level. +*/\n");
+ printf("static xmltag *xml_toplevel_tags[]={");
+ printf("&%s_tag,",safe(tagsx[0]->type));
+ printf("&%s_tag,",safe(tagsx[1]->type));
+ printf("NULL};\n");
 
  for(i=0;i<ntagsx;i++)
    {
@@ -396,14 +401,6 @@ int main(int argc,char **argv)
     printf("NULL}};\n");
    }
 
- printf("\n");
- printf("\n");
- printf("/*+ The complete set of tags at the top level. +*/\n");
- printf("static xmltag *xml_toplevel_tags[]={");
- printf("&%s_tag,",safe(tagsx[ntagsx-1]->type));
- printf("&%s_tag,",safe(tagsx[ntagsx-2]->type));
- printf("NULL};\n");
-
  /* Print the functions */
 
  printf("\n");
@@ -415,7 +412,7 @@ int main(int argc,char **argv)
     printf("\n");
     printf("\n");
     printf("/*++++++++++++++++++++++++++++++++++++++\n");
-    if(i==(ntagsx-1))            /* XML tag */
+    if(i==0) /* XML tag */
        printf("  The function that is called when the XML declaration is seen\n");
     else
        printf("  The function that is called when the %s XSD type is seen\n",tagsx[i]->type);
@@ -477,7 +474,7 @@ int main(int argc,char **argv)
  printf("\n");
  printf("int main(int argc,char **argv)\n");
  printf("{\n");
- printf(" if(ParseXML(stdin,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_WARN))\n");
+ printf(" if(ParseXML(STDIN_FILENO,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_WARN))\n");
  printf("    return(1);\n");
  printf(" else\n");
  printf("    return(0);\n");
diff --git a/src/xmlparse.c b/src/xmlparse.c
new file mode 100644
index 0000000..a2ed901
--- /dev/null
+++ b/src/xmlparse.c
@@ -0,0 +1,1706 @@
+/***************************************
+ A simple generic XML parser where the structure comes from the function parameters.
+ Not intended to be fully conforming to XML standard or a validating parser but
+ sufficient to parse OSM XML and simple program configuration files.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2010-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ ***************************************/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+
+#include "xmlparse.h"
+
+
+/* Parser states */
+
+#define LEX_EOF                    0
+
+#define LEX_FUNC_TAG_BEGIN         1
+#define LEX_FUNC_XML_DECL_BEGIN    2
+#define LEX_FUNC_TAG_POP           3
+#define LEX_FUNC_TAG_PUSH          4
+#define LEX_FUNC_XML_DECL_FINISH   5
+#define LEX_FUNC_TAG_FINISH        6
+#define LEX_FUNC_ATTR_KEY          7
+#define LEX_FUNC_ATTR_VAL          8
+
+#define LEX_STATE_INITIAL         10
+#define LEX_STATE_BANGTAG         11
+#define LEX_STATE_COMMENT         12
+#define LEX_STATE_XML_DECL_START  13
+#define LEX_STATE_XML_DECL        14
+#define LEX_STATE_TAG_START       15
+#define LEX_STATE_TAG             16
+#define LEX_STATE_ATTR_KEY        17
+#define LEX_STATE_ATTR_VAL        18
+#define LEX_STATE_END_TAG1        19
+#define LEX_STATE_END_TAG2        20
+#define LEX_STATE_DQUOTED         21
+#define LEX_STATE_SQUOTED         22
+
+#define LEX_ERROR_TAG_START      101
+#define LEX_ERROR_XML_DECL_START 102
+#define LEX_ERROR_TAG            103
+#define LEX_ERROR_XML_DECL       104
+#define LEX_ERROR_ATTR           105
+#define LEX_ERROR_END_TAG        106
+#define LEX_ERROR_COMMENT        107
+#define LEX_ERROR_CLOSE          108
+#define LEX_ERROR_ATTR_VAL       109
+#define LEX_ERROR_ENTITY_REF     110
+#define LEX_ERROR_CHAR_REF       111
+#define LEX_ERROR_TEXT_OUTSIDE   112
+
+#define LEX_ERROR_UNEXP_TAG      201
+#define LEX_ERROR_UNBALANCED     202
+#define LEX_ERROR_NO_START       203
+#define LEX_ERROR_UNEXP_ATT      204
+#define LEX_ERROR_UNEXP_EOF      205
+#define LEX_ERROR_XML_NOT_FIRST  206
+
+#define LEX_ERROR_OUT_OF_MEMORY  254
+#define LEX_ERROR_CALLBACK       255
+
+
+/* Parsing variables and functions */
+
+static uint64_t lineno;
+
+static unsigned char buffer[2][16384];
+static unsigned char *buffer_token,*buffer_end,*buffer_ptr;
+static int buffer_active=0;
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Refill the data buffer making sure that the string starting at buffer_token is contiguous.
+
+  int buffer_refill Return 0 if everything is OK or 1 for EOF.
+
+  int fd The file descriptor to read from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int buffer_refill(int fd)
+{
+ ssize_t n;
+ size_t m=0;
+
+ m=(buffer_end-buffer[buffer_active])+1;
+
+ if(m>(sizeof(buffer[0])/2))    /* more than half full */
+   {
+    m=0;
+
+    buffer_active=!buffer_active;
+
+    if(buffer_token)
+      {
+       m=(buffer_end-buffer_token)+1;
+
+       memcpy(buffer[buffer_active],buffer_token,m);
+
+       buffer_token=buffer[buffer_active];
+      }
+   }
+
+ n=read(fd,buffer[buffer_active]+m,sizeof(buffer[0])-m);
+
+ buffer_ptr=buffer[buffer_active]+m;
+ buffer_end=buffer[buffer_active]+m+n-1;
+
+ if(n<=0)
+    return(1);
+ else
+    return(0);
+}
+
+
+/* Macros to simplify the parser (and make it look more like lex) */
+
+#define BEGIN(xx) do{ state=(xx); goto new_state; } while(0)
+#define NEXT(xx)  next_state=(xx)
+
+#define START_TOKEN buffer_token=buffer_ptr
+#define END_TOKEN   buffer_token=NULL
+
+#define NEXT_CHAR                                                       \
+ do{                                                                    \
+  if(buffer_ptr==buffer_end)                                            \
+    { if(buffer_refill(fd)) BEGIN(LEX_EOF); }                           \
+    else                                                                \
+       buffer_ptr++;                                                    \
+   } while(0)
+
+
+ /* -------- equivalent flex definition --------
+
+    S               [ \t\r]
+    N               (\n)
+
+    U1              [\x09\x0A\x0D\x20-\x7F]
+    U2              [\xC2-\xDF][\x80-\xBF]
+    U3a             \xE0[\xA0-\xBF][\x80-\xBF]
+    U3b             [\xE1-\xEC][\x80-\xBF][\x80-\xBF]
+    U3c             \xED[\x80-\x9F][\x80-\xBF]
+    U3d             [\xEE-\xEF][\x80-\xBF][\x80-\xBF]
+    U3              {U3a}|{U3b}|{U3c}|{U3d}
+    U4a             \xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]
+    U4b             [\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]
+    U4c             \xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]
+    U4              {U4a}|{U4b}|{U4c}
+
+    U               ({U1}|{U2}|{U3}|{U4})
+
+    U1_xml          ([\x09\x0A\x0D\x20-\x25\x27-\x3B\x3D\x3F-\x7F])
+
+    U1quotedS_xml   ([\x09\x0A\x0D\x20-\x25\x28-\x3B\x3D\x3F-\x7F])
+    U1quotedD_xml   ([\x09\x0A\x0D\x20-\x21\x23-\x25\x27-\x3B\x3D\x3F-\x7F])
+
+    UquotedS        ({U1quotedS_xml}|{U2}|{U3}|{U4})
+    UquotedD        ({U1quotedD_xml}|{U2}|{U3}|{U4})
+
+    letter          [a-zA-Z]
+    digit           [0-9]
+    xdigit          [a-fA-F0-9]
+
+    namechar        ({letter}|{digit}|[-._:])
+    namestart       ({letter}|[_:])
+    name            ({namestart}{namechar}*)
+
+    entityref       (&{name};)
+    charref         (&#({digit}+|x{xdigit}+);)
+
+    -------- equivalent flex definition -------- */
+
+/* Tables containing character class defintions (advance declaration for data at end of file). */
+static const unsigned char quotedD[256],quotedS[256];
+static const unsigned char *U2[1],*U3a[2],*U3b[2],*U3c[2],*U3d[2],*U4a[3],*U4b[3],*U4c[3];
+static const unsigned char namestart[256],namechar[256],whitespace[256],digit[256],xdigit[256];
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  A function to call the callback function with the parameters needed.
+
+  int call_callback Returns 1 if the callback returned with an error.
+
+  const char *name The name of the tag.
+
+  int (*callback)() The callback function.
+
+  int type The type of tag (start and/or end).
+
+  int nattributes The number of attributes collected.
+
+  unsigned char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline int call_callback(const char *name,int (*callback)(),int type,int nattributes,unsigned char *attributes[XMLPARSE_MAX_ATTRS])
+{
+ switch(nattributes)
+   {
+   case  0: return (*callback)(name,type);
+   case  1: return (*callback)(name,type,attributes[0]);
+   case  2: return (*callback)(name,type,attributes[0],attributes[1]);
+   case  3: return (*callback)(name,type,attributes[0],attributes[1],attributes[2]);
+   case  4: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3]);
+   case  5: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]);
+   case  6: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]);
+   case  7: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]);
+   case  8: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]);
+   case  9: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]);
+   case 10: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9]);
+   case 11: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10]);
+   case 12: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11]);
+   case 13: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12]);
+   case 14: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13]);
+   case 15: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14]);
+   case 16: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14],attributes[15]);
+
+   default:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": too many attributes for tag '%s' source code needs changing.\n",lineno,name);
+    exit(1);
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Parse the XML and call the functions for each tag as seen.
+
+  int ParseXML Returns 0 if OK or something else in case of an error.
+
+  int fd The file descriptor of the file to parse.
+
+  xmltag **tags The array of pointers to tags for the top level.
+
+  int options A list of XML Parser options OR-ed together.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParseXML(int fd,xmltag **tags,int options)
+{
+ int i;
+ int state,next_state,after_attr;
+ unsigned char saved_buffer_ptr=0;
+ const unsigned char *quoted;
+
+ unsigned char *attributes[XMLPARSE_MAX_ATTRS]={NULL};
+ int attribute=0;
+
+ int stackdepth=0,stackused=0;
+ xmltag ***tags_stack=NULL;
+ xmltag **tag_stack=NULL;
+ xmltag *tag=NULL;
+
+ /* The actual parser. */
+
+ lineno=1;
+
+ buffer_end=buffer[buffer_active]+sizeof(buffer[0])-1;
+ buffer_token=NULL;
+
+ buffer_refill(fd);
+
+ BEGIN(LEX_STATE_INITIAL);
+
+ new_state:
+
+ switch(state)
+   {
+    /* ================ Parsing states ================ */
+
+
+    /* -------- equivalent flex definition --------
+
+       <INITIAL>"<!"                        { BEGIN(BANGTAG); }
+       <INITIAL>"</"                        { BEGIN(END_TAG1); }
+       <INITIAL>"<?"                        { BEGIN(XML_DECL_START); }
+       <INITIAL>"<"                         { BEGIN(TAG_START); }
+
+       <INITIAL>">"                         { return(LEX_ERROR_CLOSE); }
+
+       <INITIAL>{N}                         { lineno++; }
+       <INITIAL>{S}+                        { }
+       <INITIAL>.                           { return(LEX_ERROR_TEXT_OUTSIDE); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_INITIAL:
+
+    while(1)
+      {
+       while(whitespace[(int)*buffer_ptr])
+          NEXT_CHAR;
+
+       if(*buffer_ptr=='\n')
+         {
+          NEXT_CHAR;
+
+          lineno++;
+         }
+       else if(*buffer_ptr=='<')
+         {
+          NEXT_CHAR;
+
+          if(*buffer_ptr=='/')
+            {
+             NEXT_CHAR;
+             BEGIN(LEX_STATE_END_TAG1);
+            }
+          else if(*buffer_ptr=='!')
+            {
+             NEXT_CHAR;
+             BEGIN(LEX_STATE_BANGTAG);
+            }
+          else if(*buffer_ptr=='?')
+            {
+             NEXT_CHAR;
+             BEGIN(LEX_STATE_XML_DECL_START);
+            }
+          else
+             BEGIN(LEX_STATE_TAG_START);
+         }
+       else if(*buffer_ptr=='>')
+          BEGIN(LEX_ERROR_CLOSE);
+       else
+          BEGIN(LEX_ERROR_TEXT_OUTSIDE);
+      }
+
+    break;
+
+    /* -------- equivalent flex definition --------
+
+       <BANGTAG>"--"               { BEGIN(COMMENT); }
+       <BANGTAG>{N}                { return(LEX_ERROR_TAG_START); }
+       <BANGTAG>.                  { return(LEX_ERROR_TAG_START); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_BANGTAG:
+
+    if(*buffer_ptr!='-')
+       BEGIN(LEX_ERROR_TAG_START);
+
+    NEXT_CHAR;
+
+    if(*buffer_ptr!='-')
+       BEGIN(LEX_ERROR_TAG_START);
+
+    NEXT_CHAR;
+    BEGIN(LEX_STATE_COMMENT);
+
+    break;
+
+    /* -------- equivalent flex definition --------
+
+       <COMMENT>"-->"              { BEGIN(INITIAL); }
+       <COMMENT>"--"[^>]           { return(LEX_ERROR_COMMENT); }
+       <COMMENT>"-"                { }
+       <COMMENT>{N}                { lineno++; }
+       <COMMENT>[^-\n]+            { }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_COMMENT:
+
+    while(1)
+      {
+       while(*buffer_ptr!='-' && *buffer_ptr!='\n')
+          NEXT_CHAR;
+
+       if(*buffer_ptr=='-')
+         {
+          NEXT_CHAR;
+
+          if(*buffer_ptr!='-')
+             continue;
+
+          NEXT_CHAR;
+          if(*buffer_ptr=='>')
+            {
+             NEXT_CHAR;
+             BEGIN(LEX_STATE_INITIAL);
+            }
+
+          BEGIN(LEX_ERROR_COMMENT);
+         }
+       else /* if(*buffer_ptr=='\n') */
+         {
+          NEXT_CHAR;
+
+          lineno++;
+         }
+      }
+
+    break;
+
+    /* -------- equivalent flex definition --------
+
+       <XML_DECL_START>xml         { BEGIN(XML_DECL); return(LEX_XML_DECL_BEGIN); }
+       <XML_DECL_START>{N}         { return(LEX_ERROR_XML_DECL_START); }
+       <XML_DECL_START>.           { return(LEX_ERROR_XML_DECL_START); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_XML_DECL_START:
+
+    START_TOKEN;
+
+    if(*buffer_ptr=='x')
+      {
+       NEXT_CHAR;
+       if(*buffer_ptr=='m')
+         {
+          NEXT_CHAR;
+          if(*buffer_ptr=='l')
+            {
+             NEXT_CHAR;
+
+             saved_buffer_ptr=*buffer_ptr;
+             *buffer_ptr=0;
+
+             NEXT(LEX_STATE_XML_DECL);
+             BEGIN(LEX_FUNC_XML_DECL_BEGIN);
+            }
+         }
+      }
+
+    BEGIN(LEX_ERROR_XML_DECL_START);
+
+    /* -------- equivalent flex definition --------
+
+       <XML_DECL>"?>"              { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); }
+       <XML_DECL>{S}+              { }
+       <XML_DECL>{N}               { lineno++; }
+       <XML_DECL>{name}            { after_attr=XML_DECL; BEGIN(ATTR_KEY); return(LEX_ATTR_KEY); }
+       <XML_DECL>.                 { return(LEX_ERROR_XML_DECL); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_XML_DECL:
+
+    while(1)
+      {
+       while(whitespace[(int)*buffer_ptr])
+          NEXT_CHAR;
+
+       if(namestart[(int)*buffer_ptr])
+         {
+          START_TOKEN;
+
+          NEXT_CHAR;
+          while(namechar[(int)*buffer_ptr])
+             NEXT_CHAR;
+
+          saved_buffer_ptr=*buffer_ptr;
+          *buffer_ptr=0;
+
+          after_attr=LEX_STATE_XML_DECL;
+          NEXT(LEX_STATE_ATTR_KEY);
+          BEGIN(LEX_FUNC_ATTR_KEY);
+         }
+       else if(*buffer_ptr=='?')
+         {
+          NEXT_CHAR;
+          if(*buffer_ptr=='>')
+            {
+             NEXT_CHAR;
+             NEXT(LEX_STATE_INITIAL);
+             BEGIN(LEX_FUNC_XML_DECL_FINISH);
+            }
+
+          BEGIN(LEX_ERROR_XML_DECL);
+         }
+       else if(*buffer_ptr=='\n')
+         {
+          NEXT_CHAR;
+          lineno++;
+         }
+       else
+          BEGIN(LEX_ERROR_XML_DECL);
+      }
+
+    break;
+
+    /* -------- equivalent flex definition --------
+
+       <TAG_START>{name}           { BEGIN(TAG); return(LEX_TAG_BEGIN); }
+       <TAG_START>{N}              { return(LEX_ERROR_TAG_START); }
+       <TAG_START>.                { return(LEX_ERROR_TAG_START); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_TAG_START:
+
+    if(namestart[(int)*buffer_ptr])
+      {
+       START_TOKEN;
+
+       NEXT_CHAR;
+       while(namechar[(int)*buffer_ptr])
+          NEXT_CHAR;
+
+       saved_buffer_ptr=*buffer_ptr;
+       *buffer_ptr=0;
+
+       NEXT(LEX_STATE_TAG);
+       BEGIN(LEX_FUNC_TAG_BEGIN);
+      }
+
+    BEGIN(LEX_ERROR_TAG_START);
+
+    /* -------- equivalent flex definition --------
+
+       <END_TAG1>{name}            { BEGIN(END_TAG2); return(LEX_TAG_POP); }
+       <END_TAG1>{N}               { return(LEX_ERROR_END_TAG); }
+       <END_TAG1>.                 { return(LEX_ERROR_END_TAG); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_END_TAG1:
+
+    if(namestart[(int)*buffer_ptr])
+      {
+       START_TOKEN;
+
+       NEXT_CHAR;
+       while(namechar[(int)*buffer_ptr])
+          NEXT_CHAR;
+
+       saved_buffer_ptr=*buffer_ptr;
+       *buffer_ptr=0;
+
+       NEXT(LEX_STATE_END_TAG2);
+       BEGIN(LEX_FUNC_TAG_POP);
+      }
+
+    BEGIN(LEX_ERROR_END_TAG);
+
+    /* -------- equivalent flex definition --------
+
+       <END_TAG2>">"               { BEGIN(INITIAL); }
+       <END_TAG2>{N}               { return(LEX_ERROR_END_TAG); }
+       <END_TAG2>.                 { return(LEX_ERROR_END_TAG); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_END_TAG2:
+
+    if(*buffer_ptr=='>')
+      {
+       NEXT_CHAR;
+
+       BEGIN(LEX_STATE_INITIAL);
+      }
+
+    BEGIN(LEX_ERROR_END_TAG);
+
+    /* -------- equivalent flex definition --------
+
+       <TAG>"/>"                   { BEGIN(INITIAL); return(LEX_TAG_FINISH); }
+       <TAG>">"                    { BEGIN(INITIAL); return(LEX_TAG_PUSH); }
+       <TAG>{S}+                   { }
+       <TAG>{N}                    { lineno++; }
+       <TAG>{name}                 { after_attr=TAG; BEGIN(ATTR_KEY); return(LEX_ATTR_KEY); }
+       <TAG>.                      { return(LEX_ERROR_TAG); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_TAG:
+
+    while(1)
+      {
+       while(whitespace[(int)*buffer_ptr])
+          NEXT_CHAR;
+
+       if(namestart[(int)*buffer_ptr])
+         {
+          START_TOKEN;
+
+          NEXT_CHAR;
+          while(namechar[(int)*buffer_ptr])
+             NEXT_CHAR;
+
+          saved_buffer_ptr=*buffer_ptr;
+          *buffer_ptr=0;
+
+          after_attr=LEX_STATE_TAG;
+          NEXT(LEX_STATE_ATTR_KEY);
+          BEGIN(LEX_FUNC_ATTR_KEY);
+         }
+       else if(*buffer_ptr=='/')
+         {
+          NEXT_CHAR;
+          if(*buffer_ptr=='>')
+            {
+             NEXT_CHAR;
+             NEXT(LEX_STATE_INITIAL);
+             BEGIN(LEX_FUNC_TAG_FINISH);
+            }
+
+          BEGIN(LEX_ERROR_TAG);
+         }
+       else if(*buffer_ptr=='>')
+         {
+          NEXT_CHAR;
+          NEXT(LEX_STATE_INITIAL);
+          BEGIN(LEX_FUNC_TAG_PUSH);
+         }
+       else if(*buffer_ptr=='\n')
+         {
+          NEXT_CHAR;
+          lineno++;
+         }
+       else
+          BEGIN(LEX_ERROR_TAG);
+      }
+
+    break;
+
+    /* -------- equivalent flex definition --------
+
+       <ATTR_KEY>=                 { BEGIN(ATTR_VAL); }
+       <ATTR_KEY>{N}               { return(LEX_ERROR_ATTR); }
+       <ATTR_KEY>.                 { return(LEX_ERROR_ATTR); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_ATTR_KEY:
+
+    if(*buffer_ptr=='=')
+      {
+       NEXT_CHAR;
+       BEGIN(LEX_STATE_ATTR_VAL);
+      }
+
+    BEGIN(LEX_ERROR_ATTR);
+
+    /* -------- equivalent flex definition --------
+
+       <ATTR_VAL>\"                { BEGIN(DQUOTED); }
+       <ATTR_VAL>\'                { BEGIN(SQUOTED); }
+       <ATTR_VAL>{N}               { return(LEX_ERROR_ATTR); }
+       <ATTR_VAL>.                 { return(LEX_ERROR_ATTR); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_ATTR_VAL:
+
+    if(*buffer_ptr=='"')
+      {
+       NEXT_CHAR;
+       BEGIN(LEX_STATE_DQUOTED);
+      }
+    else if(*buffer_ptr=='\'')
+      {
+       NEXT_CHAR;
+       BEGIN(LEX_STATE_SQUOTED);
+      }
+
+    BEGIN(LEX_ERROR_ATTR);
+
+    /* -------- equivalent flex definition --------
+
+       <DQUOTED>\"                 { BEGIN(after_attr); return(LEX_ATTR_VAL); }
+       <DQUOTED>{entityref}        { if(options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
+                                     else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {return(LEX_ERROR_ENTITY_REF);} } }
+       <DQUOTED>{charref}          { if(options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
+                                     else { const char *str=ParseXML_Decode_Char_Ref(yytext);   if(str) {append_string(str);} else {return(LEX_ERROR_CHAR_REF);} } }
+       <DQUOTED>{UquotedD}         { }
+       <DQUOTED>[<>&]              { return(LEX_ERROR_ATTR_VAL); }
+       <DQUOTED>.                  { return(LEX_ERROR_ATTR_VAL); }
+
+       <SQUOTED>\'                 { BEGIN(after_attr); return(LEX_ATTR_VAL); }
+       <SQUOTED>{entityref}        { if(options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
+                                     else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {return(LEX_ERROR_ENTITY_REF);} } }
+       <SQUOTED>{charref}          { if(options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
+                                     else { const char *str=ParseXML_Decode_Char_Ref(yytext);   if(str) {append_string(str);} else {return(LEX_ERROR_CHAR_REF);} } }
+       <SQUOTED>{UquotedS}         { append_string(yytext); }
+       <SQUOTED>[<>&]              { return(LEX_ERROR_ATTR_VAL); }
+       <SQUOTED>.                  { return(LEX_ERROR_ATTR_VAL); }
+
+       -------- equivalent flex definition -------- */
+
+   case LEX_STATE_DQUOTED:
+   case LEX_STATE_SQUOTED:
+
+    if(state==LEX_STATE_DQUOTED)
+       quoted=quotedD;
+    else
+       quoted=quotedS;
+
+    START_TOKEN;
+
+    while(1)
+      {
+       switch(quoted[(int)*buffer_ptr])
+         {
+         case 10:            /* U1 - used by all tag keys and many values */
+          do
+            {
+             NEXT_CHAR;
+            }
+          while(quoted[(int)*buffer_ptr]==10);
+          break;
+
+         case 20:            /* U2 */
+          NEXT_CHAR;
+          if(!U2[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 31:            /* U3a */
+          NEXT_CHAR;
+          if(!U3a[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U3a[1][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 32:            /* U3b */
+          NEXT_CHAR;
+          if(!U3b[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U3b[1][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 33:            /* U3c */
+          NEXT_CHAR;
+          if(!U3c[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U3c[1][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 34:            /* U3d */
+          NEXT_CHAR;
+          if(!U3d[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U3d[1][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 41:            /* U4a */
+          NEXT_CHAR;
+          if(!U4a[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U4a[1][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U4a[2][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 42:            /* U4b */
+          NEXT_CHAR;
+          if(!U4b[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U4b[1][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U4b[2][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 43:            /* U4c */
+          NEXT_CHAR;
+          if(!U4c[0][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U4c[1][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          if(!U4c[2][(int)*buffer_ptr])
+             BEGIN(LEX_ERROR_ATTR_VAL);
+          NEXT_CHAR;
+          break;
+
+         case 50:            /* entityref or charref */
+          NEXT_CHAR;
+
+          if(*buffer_ptr=='#') /* charref */
+            {
+             int charref_len=3;
+
+             NEXT_CHAR;
+             if(digit[(int)*buffer_ptr]) /* decimal */
+               {
+                NEXT_CHAR;
+                charref_len++;
+
+                while(digit[(int)*buffer_ptr])
+                  {
+                   NEXT_CHAR;
+                   charref_len++;
+                  }
+
+                if(*buffer_ptr!=';')
+                   BEGIN(LEX_ERROR_ATTR_VAL);
+               }
+             else if(*buffer_ptr=='x') /* hex */
+               {
+                NEXT_CHAR;
+                charref_len++;
+
+                while(xdigit[(int)*buffer_ptr])
+                  {
+                   NEXT_CHAR;
+                   charref_len++;
+                  }
+
+                if(*buffer_ptr!=';')
+                   BEGIN(LEX_ERROR_ATTR_VAL);
+               }
+             else            /* other */
+                BEGIN(LEX_ERROR_ATTR_VAL);
+
+             NEXT_CHAR;
+
+             if(!(options&XMLPARSE_RETURN_ATTR_ENCODED))
+               {
+                const char *str;
+
+                saved_buffer_ptr=*buffer_ptr;
+                *buffer_ptr=0;
+
+                str=ParseXML_Decode_Char_Ref((char*)(buffer_ptr-charref_len));
+
+                if(!str)
+                  {
+                   buffer_ptr-=charref_len;
+                   BEGIN(LEX_ERROR_CHAR_REF);
+                  }
+
+                buffer_token=memmove(buffer_token+(charref_len-strlen(str)),buffer_token,buffer_ptr-buffer_token-charref_len);
+                memcpy(buffer_ptr-strlen(str),str,strlen(str));
+
+                *buffer_ptr=saved_buffer_ptr;
+               }
+            }
+          else if(namestart[(int)*buffer_ptr]) /* entityref */
+            {
+             int entityref_len=3;
+
+             NEXT_CHAR;
+             while(namechar[(int)*buffer_ptr])
+               {
+                NEXT_CHAR;
+                entityref_len++;
+               }
+
+             if(*buffer_ptr!=';')
+                BEGIN(LEX_ERROR_ATTR_VAL);
+
+             NEXT_CHAR;
+
+             if(!(options&XMLPARSE_RETURN_ATTR_ENCODED))
+               {
+                const char *str;
+
+                saved_buffer_ptr=*buffer_ptr;
+                *buffer_ptr=0;
+
+                str=ParseXML_Decode_Entity_Ref((char*)(buffer_ptr-entityref_len));
+
+                if(!str)
+                  {
+                   buffer_ptr-=entityref_len;
+                   BEGIN(LEX_ERROR_ENTITY_REF);
+                  }
+
+                buffer_token=memmove(buffer_token+(entityref_len-strlen(str)),buffer_token,buffer_ptr-buffer_token-entityref_len);
+                memcpy(buffer_ptr-strlen(str),str,strlen(str));
+
+                *buffer_ptr=saved_buffer_ptr;
+               }
+            }
+          else               /* other */
+             BEGIN(LEX_ERROR_ATTR_VAL);
+
+          break;
+
+         case 99:            /* quote */
+          *buffer_ptr=0;
+          NEXT_CHAR;
+
+          NEXT(after_attr);
+          BEGIN(LEX_FUNC_ATTR_VAL);
+
+         default:            /* other */
+          BEGIN(LEX_ERROR_ATTR_VAL);
+         }
+      }
+
+    break;
+
+
+    /* ================ Functional states ================ */
+
+
+    /* The start of a tag for an XML declaration */
+
+   case LEX_FUNC_XML_DECL_BEGIN:
+
+    if(tag_stack)
+       BEGIN(LEX_ERROR_XML_NOT_FIRST);
+
+    /* The start of a tag for an element */
+
+   case LEX_FUNC_TAG_BEGIN:
+
+    tag=NULL;
+
+    for(i=0;tags[i];i++)
+       if(buffer_token[0]==tags[i]->name[0] || tolower(buffer_token[0])==tags[i]->name[0])
+          if(!strcasecmp((char*)buffer_token+1,tags[i]->name+1))
+            {
+             tag=tags[i];
+
+             for(i=0;i<tag->nattributes;i++)
+                attributes[i]=NULL;
+
+             break;
+            }
+
+    if(tag==NULL)
+       BEGIN(LEX_ERROR_UNEXP_TAG);
+
+    END_TOKEN;
+
+    *buffer_ptr=saved_buffer_ptr;
+    BEGIN(next_state);
+
+    /* The end of the start-tag for an element */
+
+   case LEX_FUNC_TAG_PUSH:
+
+    if(stackused==stackdepth)
+      {
+       tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*));
+       tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**));
+      }
+
+    tag_stack [stackused]=tag;
+    tags_stack[stackused]=tags;
+    stackused++;
+
+    if(tag->callback)
+       if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,tag->nattributes,attributes))
+          BEGIN(LEX_ERROR_CALLBACK);
+
+    tags=tag->subtags;
+
+    BEGIN(next_state);
+
+    /* The end of the empty-element-tag for an XML declaration */
+
+   case LEX_FUNC_XML_DECL_FINISH:
+
+    /* The end of the empty-element-tag for an element */
+
+   case LEX_FUNC_TAG_FINISH:
+
+    if(tag->callback)
+       if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,tag->nattributes,attributes))
+          BEGIN(LEX_ERROR_CALLBACK);
+
+    if(stackused>0)
+       tag=tag_stack[stackused-1];
+    else
+       tag=NULL;
+
+    BEGIN(next_state);
+
+    /* The end of the end-tag for an element */
+
+   case LEX_FUNC_TAG_POP:
+
+    stackused--;
+    tags=tags_stack[stackused];
+    tag =tag_stack [stackused];
+
+    if(strcmp((char*)buffer_token,tag->name))
+       BEGIN(LEX_ERROR_UNBALANCED);
+
+    if(stackused<0)
+       BEGIN(LEX_ERROR_NO_START);
+
+    for(i=0;i<tag->nattributes;i++)
+       attributes[i]=NULL;
+
+    if(tag->callback)
+       if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,tag->nattributes,attributes))
+          BEGIN(LEX_ERROR_CALLBACK);
+
+    if(stackused>0)
+       tag=tag_stack[stackused-1];
+    else
+       tag=NULL;
+
+    END_TOKEN;
+
+    *buffer_ptr=saved_buffer_ptr;
+    BEGIN(next_state);
+
+    /* An attribute key */
+
+   case LEX_FUNC_ATTR_KEY:
+
+    attribute=-1;
+
+    for(i=0;i<tag->nattributes;i++)
+       if(buffer_token[0]==tag->attributes[i][0] || tolower(buffer_token[0])==tag->attributes[i][0])
+          if(!strcasecmp((char*)buffer_token+1,tag->attributes[i]+1))
+            {
+             attribute=i;
+
+             break;
+            }
+
+    if(attribute==-1)
+      {
+       if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERROR ||
+          ((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERRNONAME && !strchr((char*)buffer_token,':')))
+          BEGIN(LEX_ERROR_UNEXP_ATT);
+       else if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_WARN)
+          fprintf(stderr,"XML Parser: Warning on line %"PRIu64": unexpected attribute '%s' for tag '%s'.\n",lineno,buffer_token,tag->name);
+      }
+
+    END_TOKEN;
+
+    *buffer_ptr=saved_buffer_ptr;
+    BEGIN(next_state);
+
+    /* An attribute value */
+
+   case LEX_FUNC_ATTR_VAL:
+
+    if(tag->callback && attribute!=-1)
+       attributes[attribute]=buffer_token;
+
+    END_TOKEN;
+
+    BEGIN(next_state);
+
+    /* End of file */
+
+   case LEX_EOF:
+
+    if(tag)
+       BEGIN(LEX_ERROR_UNEXP_EOF);
+
+    break;
+
+
+    /* ================ Error states ================ */
+
+
+   case LEX_ERROR_TAG_START:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": character '<' seen not at start of tag.\n",lineno);
+    break;
+
+   case LEX_ERROR_XML_DECL_START:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": characters '<?' seen not at start of XML declaration.\n",lineno);
+    break;
+
+   case LEX_ERROR_TAG:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid character seen inside tag '<%s...>'.\n",lineno,tag->name);
+    break;
+
+   case LEX_ERROR_XML_DECL:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid character seen inside XML declaration '<?xml...>'.\n",lineno);
+    break;
+
+   case LEX_ERROR_ATTR:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid attribute definition seen in tag.\n",lineno);
+    break;
+    
+   case LEX_ERROR_END_TAG:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid character seen in end-tag.\n",lineno);
+    break;
+
+   case LEX_ERROR_COMMENT:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid comment seen.\n",lineno);
+    break;
+
+   case LEX_ERROR_CLOSE:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": character '>' seen not at end of tag.\n",lineno);
+    break;
+
+   case LEX_ERROR_ATTR_VAL:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid character '%c' seen in attribute value.\n",lineno,*buffer_ptr);
+    break;
+
+   case LEX_ERROR_ENTITY_REF:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid entity reference '%s' seen in attribute value.\n",lineno,buffer_ptr);
+    break;
+
+   case LEX_ERROR_CHAR_REF:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": invalid character reference '%s' seen in attribute value.\n",lineno,buffer_ptr);
+    break;
+
+   case LEX_ERROR_TEXT_OUTSIDE:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": non-whitespace '%c' seen outside tag.\n",lineno,*buffer_ptr);
+    break;
+
+   case LEX_ERROR_UNEXP_TAG:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": unexpected tag '%s'.\n",lineno,buffer_token);
+    break;
+
+   case LEX_ERROR_UNBALANCED:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": end tag '</%s>' doesn't match start tag '<%s ...>'.\n",lineno,buffer_token,tag->name);
+    break;
+
+   case LEX_ERROR_NO_START:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": end tag '</%s>' seen but there was no start tag '<%s ...>'.\n",lineno,buffer_token,buffer_token);
+    break;
+
+   case LEX_ERROR_UNEXP_ATT:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": unexpected attribute '%s' for tag '%s'.\n",lineno,buffer_token,tag->name);
+    break;
+
+   case LEX_ERROR_UNEXP_EOF:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": end of file seen without end tag '</%s>'.\n",lineno,tag->name);
+    break;
+
+   case LEX_ERROR_XML_NOT_FIRST:
+    fprintf(stderr,"XML Parser: Error on line %"PRIu64": XML declaration '<?xml...>' not before all other tags.\n",lineno);
+    break;
+   }
+
+ /* Delete the tagdata */
+
+ if(stackdepth)
+   {
+    free(tag_stack);
+    free(tags_stack);
+   }
+
+ return(state);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Return the current parser line number.
+
+  uint64_t ParseXML_LineNumber Returns the line number.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+uint64_t ParseXML_LineNumber(void)
+{
+ return(lineno);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Convert an XML entity reference into an ASCII string.
+
+  char *ParseXML_Decode_Entity_Ref Returns a pointer to the replacement decoded string.
+
+  const char *string The entity reference string.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+char *ParseXML_Decode_Entity_Ref(const char *string)
+{
+ if(!strcmp(string,"&"))  return("&");
+ if(!strcmp(string,"<"))   return("<");
+ if(!strcmp(string,">"))   return(">");
+ if(!strcmp(string,"'")) return("'");
+ if(!strcmp(string,""")) return("\"");
+ return(NULL);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Convert an XML character reference into an ASCII string.
+
+  char *ParseXML_Decode_Char_Ref Returns a pointer to the replacement decoded string.
+
+  const char *string The character reference string.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+char *ParseXML_Decode_Char_Ref(const char *string)
+{
+ static char result[5]="";
+ long int unicode;
+
+ if(string[2]=='x') unicode=strtol(string+3,NULL,16);
+ else               unicode=strtol(string+2,NULL,10);
+
+ if(unicode<0x80)
+   {
+    /* 0000 0000-0000 007F  =>  0xxxxxxx */
+    result[0]=unicode;
+    result[1]=0;
+   }
+ else if(unicode<0x07FF)
+   {
+    /* 0000 0080-0000 07FF  =>  110xxxxx 10xxxxxx */
+    result[0]=0xC0+((unicode&0x07C0)>>6);
+    result[1]=0x80+ (unicode&0x003F);
+    result[2]=0;
+   }
+ else if(unicode<0xFFFF)
+   {
+    /* 0000 0800-0000 FFFF  =>  1110xxxx 10xxxxxx 10xxxxxx */
+    result[0]=0xE0+((unicode&0xF000)>>12);
+    result[1]=0x80+((unicode&0x0FC0)>>6);
+    result[2]=0x80+ (unicode&0x003F);
+    result[3]=0;
+   }
+ else if(unicode<0x1FFFFF)
+   {
+    /* 0001 0000-001F FFFF  =>  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+    result[0]=0xF0+((unicode&0x1C0000)>>18);
+    result[1]=0x80+((unicode&0x03F000)>>12);
+    result[2]=0x80+((unicode&0x000FC0)>>6);
+    result[3]=0x80+ (unicode&0x00003F);
+    result[4]=0;
+   }
+ else
+   {
+    result[0]=0xFF;
+    result[1]=0xFD;
+    result[2]=0;
+   }
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Convert a string into something that is safe to output in an XML file.
+
+  char *ParseXML_Encode_Safe_XML Returns a pointer to the replacement encoded string (or the original if no change needed).
+
+  const char *string The string to convert.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+char *ParseXML_Encode_Safe_XML(const char *string)
+{
+ static const char hexstring[17]="0123456789ABCDEF";
+ int i=0,j=0,len;
+ char *result;
+
+ for(i=0;string[i];i++)
+    if(string[i]=='<' || string[i]=='>' || string[i]=='&' || string[i]=='\'' || string[i]=='"' || string[i]<32 || (unsigned char)string[i]>127)
+       break;
+
+ if(!string[i])
+    return((char*)string);
+
+ len=i+256-6;
+
+ result=(char*)malloc(len+7);
+ strncpy(result,string,j=i);
+
+ do
+   {
+    for(;j<len && string[i];i++)
+       if(string[i]=='\'')
+         {
+          /* XML, HTML5 and XHTML1 allow ' but HTML4 doesn't. */
+          result[j++]='&';
+          result[j++]='#';
+          result[j++]='3';
+          result[j++]='9';
+          result[j++]=';';
+         }
+       else if(string[i]=='&')
+         {
+          result[j++]='&';
+          result[j++]='a';
+          result[j++]='m';
+          result[j++]='p';
+          result[j++]=';';
+         }
+       else if(string[i]=='"')
+         {
+          result[j++]='&';
+          result[j++]='q';
+          result[j++]='u';
+          result[j++]='o';
+          result[j++]='t';
+          result[j++]=';';
+         }
+       else if(string[i]=='<')
+         {
+          result[j++]='&';
+          result[j++]='l';
+          result[j++]='t';
+          result[j++]=';';
+         }
+       else if(string[i]=='>')
+         {
+          result[j++]='&';
+          result[j++]='g';
+          result[j++]='t';
+          result[j++]=';';
+         }
+       else if(string[i]>=32 && (unsigned char)string[i]<=127)
+          result[j++]=string[i];
+       else
+         {
+          unsigned int unicode;
+
+          /* Decode the UTF-8 */
+
+          if((string[i]&0x80)==0)
+            {
+             /* 0000 0000-0000 007F  =>  0xxxxxxx */
+             unicode=string[i];
+            }
+          else if((string[i]&0xE0)==0xC0 && (string[i]&0x1F)>=2 && (string[i+1]&0xC0)==0x80)
+            {
+             /* 0000 0080-0000 07FF  =>  110xxxxx 10xxxxxx */
+             unicode =(string[i++]&0x1F)<<6;
+             unicode|= string[i  ]&0x3F;
+            }
+          else if((string[i]&0xF0)==0xE0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80)
+            {
+             /* 0000 0800-0000 FFFF  =>  1110xxxx 10xxxxxx 10xxxxxx */
+             unicode =(string[i++]&0x0F)<<12;
+             unicode|=(string[i++]&0x3F)<<6;
+             unicode|= string[i  ]&0x3F;
+            }
+          else if((string[i]&0xF8)==0xF0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80 && (string[i+3]&0xC0)==0x80)
+            {
+             /* 0001 0000-001F FFFF  =>  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+             unicode =(string[i++]&0x07)<<18;
+             unicode|=(string[i++]&0x3F)<<12;
+             unicode|=(string[i++]&0x3F)<<6;
+             unicode|= string[i  ]&0x3F;
+            }
+          else
+             unicode=0xFFFD;
+
+          /* Output the character entity */
+
+          result[j++]='&';
+          result[j++]='#';
+          result[j++]='x';
+
+          if(unicode&0x00FF0000)
+            {
+             result[j++]=hexstring[((unicode>>16)&0xf0)>>4];
+             result[j++]=hexstring[((unicode>>16)&0x0f)   ];
+            }
+          if(unicode&0x00FFFF00)
+            {
+             result[j++]=hexstring[((unicode>>8)&0xf0)>>4];
+             result[j++]=hexstring[((unicode>>8)&0x0f)   ];
+            }
+          result[j++]=hexstring[(unicode&0xf0)>>4];
+          result[j++]=hexstring[(unicode&0x0f)   ];
+
+          result[j++]=';';
+         }
+
+    if(string[i])                  /* Not finished */
+      {
+       len+=256;
+       result=(char*)realloc((void*)result,len+7);
+      }
+   }
+ while(string[i]);
+
+ result[j]=0;
+
+ return(result);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Check that a string really is an integer.
+
+  int ParseXML_IsInteger Returns 1 if an integer could be found or 0 otherwise.
+
+  const char *string The string to be parsed.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParseXML_IsInteger(const char *string)
+{
+ const unsigned char *p=(unsigned char*)string;
+
+ if(*p=='-' || *p=='+')
+    p++;
+
+ while(digit[(int)*p])
+    p++;
+
+ if(*p)
+    return(0);
+ else
+    return(1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Check that a string really is a floating point number.
+
+  int ParseXML_IsFloating Returns 1 if a floating point number could be found or 0 otherwise.
+
+  const char *string The string to be parsed.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int ParseXML_IsFloating(const char *string)
+{
+ const unsigned char *p=(unsigned char*)string;
+
+ if(*p=='-' || *p=='+')
+    p++;
+
+ while(digit[(int)*p] || *p=='.')
+    p++;
+
+ if(*p=='e' || *p=='E')
+   {
+    p++;
+
+    if(*p=='-' || *p=='+')
+       p++;
+
+    while(digit[*p])
+       p++;
+   }
+
+ if(*p)
+    return(0);
+ else
+    return(1);
+}
+
+
+/* Table for checking for double-quoted characters. */
+static const unsigned char quotedD[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0,10,10, 0, 0,10, 0, 0,  /* 0x00-0x0f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                         10,10,99,10,10,10,50,10,10,10,10,10,10,10,10,10,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10, 0,10, 0,10,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                          0, 0,20,20,20,20,20,20,20,20,20,20,20,20,20,20,  /* 0xc0-0xcf "                " */
+                                         20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,  /* 0xd0-0xdf "                " */
+                                         31,32,32,32,32,32,32,32,32,32,32,32,32,33,34,34,  /* 0xe0-0xef "                " */
+                                         41,42,42,42,43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for single-quoted characters. */
+static const unsigned char quotedS[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0,10,10, 0, 0,10, 0, 0,  /* 0x00-0x0f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                         10,10,10,10,10,10,50,99,10,10,10,10,10,10,10,10,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10, 0,10, 0,10,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                         10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                          0, 0,20,20,20,20,20,20,20,20,20,20,20,20,20,20,  /* 0xc0-0xcf "                " */
+                                         20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,  /* 0xd0-0xdf "                " */
+                                         31,32,32,32,32,32,32,32,32,32,32,32,32,33,34,34,  /* 0xe0-0xef "                " */
+                                         41,42,42,42,43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for characters between 0x80 and 0x8f. */
+static const unsigned char U_80_8F[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x80-0x8f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for characters between 0x80 and 0x9f. */
+static const unsigned char U_80_9F[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x80-0x8f "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x90-0x9f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for characters between 0x80 and 0xbf. */
+static const unsigned char U_80_BF[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x80-0x8f "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x90-0x9f "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0xa0-0xaf "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0xb0-0xbf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for characters between 0x90 and 0xbf. */
+static const unsigned char U_90_BF[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x90-0x9f "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0xa0-0xaf "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0xb0-0xbf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for characters between 0xa0 and 0xbf. */
+static const unsigned char U_A0_BF[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0xa0-0xaf "                " */
+                                          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0xb0-0xbf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for U2 characters = C2-DF,80-BF = U+0080-U+07FF. */
+static const unsigned char *U2[1]={ U_80_BF };
+
+/* Table for checking for U3a characters = E0,A0-BF,80-BF = U+0800-U+0FFF. */
+static const unsigned char *U3a[2]={ U_A0_BF, U_80_BF };
+
+/* Table for checking for U3b characters = E1-EC,80-BF,80-BF = U+1000-U+CFFF. */
+static const unsigned char *U3b[2]={ U_80_BF, U_80_BF };
+
+/* Table for checking for U3c characters = ED,80-9F,80-BF = U+D000-U+D7FF (U+D800-U+DFFF are not legal in XML). */
+static const unsigned char *U3c[2]={ U_80_9F, U_80_BF };
+
+/* Table for checking for U3d characters = EE-EF,80-BF,80-BF = U+E000-U+FFFF (U+FFFE-U+FFFF are not legal in XML but handled). */
+static const unsigned char *U3d[2]={ U_80_BF, U_80_BF };
+
+/* Table for checking for U4a characters = F0,90-BF,80-BF,80-BF = U+10000-U+3FFFF. */
+static const unsigned char *U4a[3]={ U_90_BF, U_80_BF, U_80_BF };
+
+/* Table for checking for U4b characters = F1-F3,80-BF,80-BF,80-BF = U+40000-U+FFFFF. */
+static const unsigned char *U4b[3]={ U_80_BF, U_80_BF, U_80_BF };
+
+/* Table for checking for U4c characters = F4,80-8F,80-BF,80-BF = U+100000-U+10FFFF (U+110000- are not legal in XML). */
+static const unsigned char *U4c[3]={ U_80_8F, U_80_BF, U_80_BF };
+
+/* Table for checking for namestart characters. */
+static const unsigned char namestart[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for namechar characters. */
+static const unsigned char namechar[256] ={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for whitespace characters. */
+static const unsigned char whitespace[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,  /* 0x00-0x0f "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                             1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for digit characters. */
+static const unsigned char digit[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
+
+/* Table for checking for xdigit characters. */
+static const unsigned char xdigit[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x00-0x0f "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x10-0x1f "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x20-0x2f " !"#$%&'()*+,-./" */
+                                         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  /* 0x30-0x3f "0123456789:;<=>?" */
+                                         0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x40-0x4f "@ABCDEFGHIJKLMNO" */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x50-0x5f "PQRSTUVWXYZ[\]^_" */
+                                         0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x60-0x6f "`abcdefghijklmno" */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x70-0x7f "pqrstuvwxyz{|}~ " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x80-0x8f "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x90-0x9f "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xa0-0xaf "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xb0-0xbf "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xc0-0xcf "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xd0-0xdf "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0xe0-0xef "                " */
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 0xf0-0xff "                " */
diff --git a/src/xmlparse.h b/src/xmlparse.h
index 8c43cad..f4cb595 100644
--- a/src/xmlparse.h
+++ b/src/xmlparse.h
@@ -3,7 +3,7 @@
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010-2011 Andrew M. Bishop
+ This file Copyright 2010-2014 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -23,7 +23,8 @@
 #ifndef XMLPARSE_H
 #define XMLPARSE_H    /*+ To stop multiple inclusions. +*/
 
-#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
 
 
 /*+ The maximum number of attributes per tag. +*/
@@ -44,7 +45,7 @@ typedef struct _xmltag xmltag;
 /*+ A structure to hold the definition of a tag. +*/
 struct _xmltag
 {
- char *name;                            /*+ The name of the tag. +*/
+ char *name;                            /*+ The name of the tag - must be in lower case. +*/
 
  int  nattributes;                      /*+ The number of valid attributes for the tag. +*/
  char *attributes[XMLPARSE_MAX_ATTRS];  /*+ The valid attributes for the tag. +*/
@@ -68,9 +69,9 @@ struct _xmltag
 
 /* XML parser functions */
 
-int ParseXML(FILE *file,xmltag **tags,int options);
+int ParseXML(int fd,xmltag **tags,int options);
 
-unsigned long long ParseXML_LineNumber(void);
+uint64_t ParseXML_LineNumber(void);
 
 char *ParseXML_Decode_Entity_Ref(const char *string);
 char *ParseXML_Decode_Char_Ref(const char *string);
@@ -84,7 +85,7 @@ int ParseXML_IsFloating(const char *string);
 #define XMLPARSE_MESSAGE(tag,message) \
  do \
    { \
-    fprintf(stderr,"XML Parser: Error on line %llu: " message " in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+    fprintf(stderr,"XML Parser: Error on line %" PRIu64 ": " message " in <%s> tag.\n",ParseXML_LineNumber(),tag); \
     return(1); \
    } \
     while(0)
@@ -92,7 +93,7 @@ int ParseXML_IsFloating(const char *string);
 #define XMLPARSE_INVALID(tag,attribute) \
  do \
    { \
-    fprintf(stderr,"XML Parser: Error on line %llu: Invalid value for '" #attribute "' attribute in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+    fprintf(stderr,"XML Parser: Error on line %" PRIu64 ": Invalid value for '" #attribute "' attribute in <%s> tag.\n",ParseXML_LineNumber(),tag); \
     return(1); \
    } \
     while(0)
@@ -102,7 +103,7 @@ int ParseXML_IsFloating(const char *string);
    { \
     if(!attribute) \
       { \
-       fprintf(stderr,"XML Parser: Error on line %llu: '" #attribute "' attribute must be specified in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+       fprintf(stderr,"XML Parser: Error on line %" PRIu64 ": '" #attribute "' attribute must be specified in <%s> tag.\n",ParseXML_LineNumber(),tag); \
        return(1); \
       } \
    } \
@@ -113,7 +114,7 @@ int ParseXML_IsFloating(const char *string);
    { \
     if(!attribute || !*attribute || !ParseXML_IsInteger(attribute)) \
       { \
-       fprintf(stderr,"XML Parser: Error on line %llu: '" #attribute "' attribute must be a integer in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+       fprintf(stderr,"XML Parser: Error on line %" PRIu64 ": '" #attribute "' attribute must be a integer in <%s> tag.\n",ParseXML_LineNumber(),tag); \
        return(1); \
       } \
    } \
@@ -124,7 +125,7 @@ int ParseXML_IsFloating(const char *string);
    { \
     if(!attribute || !*attribute || !ParseXML_IsFloating(attribute)) \
       { \
-       fprintf(stderr,"XML Parser: Error on line %llu: '" #attribute "' attribute must be a number in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+       fprintf(stderr,"XML Parser: Error on line %" PRIu64 ": '" #attribute "' attribute must be a number in <%s> tag.\n",ParseXML_LineNumber(),tag); \
        return(1); \
       } \
    } \
diff --git a/src/xmlparse.l b/src/xmlparse.l
deleted file mode 100644
index 4bed772..0000000
--- a/src/xmlparse.l
+++ /dev/null
@@ -1,931 +0,0 @@
-%{
-/***************************************
- A simple generic XML parser where the structure comes from the function parameters.
- Not intended to be fully conforming to XML standard or a validating parser but
- sufficient to parse OSM XML and simple program configuration files.
-
- Part of the Routino routing software.
- ******************/ /******************
- This file Copyright 2010-2012 Andrew M. Bishop
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
- ***************************************/
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include <strings.h>
-
-#include "xmlparse.h"
-
-
-/* Parser outputs */
-
-#define LEX_EOF                    0
-
-#define LEX_TAG_BEGIN              1
-#define LEX_XML_DECL_BEGIN         2
-#define LEX_TAG_POP                3
-#define LEX_TAG_PUSH               4
-#define LEX_XML_DECL_FINISH        6
-#define LEX_TAG_FINISH             7
-#define LEX_ATTR_KEY               8
-#define LEX_ATTR_VAL               9
-
-#define LEX_ERROR                100
-
-#define LEX_ERROR_TAG_START      101
-#define LEX_ERROR_XML_DECL_START 102
-#define LEX_ERROR_TAG            103
-#define LEX_ERROR_XML_DECL       104
-#define LEX_ERROR_ATTR           105
-#define LEX_ERROR_END_TAG        106
-#define LEX_ERROR_COMMENT        107
-#define LEX_ERROR_CLOSE          108
-#define LEX_ERROR_ATTR_VAL       109
-#define LEX_ERROR_ENTITY_REF     110
-#define LEX_ERROR_CHAR_REF       111
-#define LEX_ERROR_TEXT_OUTSIDE   112
-
-#define LEX_ERROR_UNEXP_TAG      201
-#define LEX_ERROR_UNBALANCED     202
-#define LEX_ERROR_NO_START       203
-#define LEX_ERROR_UNEXP_ATT      204
-#define LEX_ERROR_UNEXP_EOF      205
-#define LEX_ERROR_XML_NOT_FIRST  206
-
-#define LEX_ERROR_CALLBACK       255
-
-
-/* Lexer definitions */
-
-/*+ Reset the current string. +*/
-#define reset_string \
- stringnum=-1;
-
-/*+ Prepare for the next string. +*/
-#define next_string \
- stringnum++; \
- if(stringnum>=numstrings) \
-   { \
-    int i; \
-    numstrings+=32; \
-    string=(char**)realloc((void*)string,numstrings*sizeof(char*)); \
-    stringlen=(unsigned long*)realloc((void*)stringlen,numstrings*sizeof(unsigned long)); \
-    stringused=(unsigned long*)realloc((void*)stringused,numstrings*sizeof(unsigned long)); \
-    for(i=stringnum;i<numstrings;i++) \
-      {string[i]=NULL;stringlen[i]=0;stringused[i]=0;} \
-   } \
- if(!string[stringnum]) string[stringnum]=(char*)malloc(stringlen[stringnum]=256); \
- *string[stringnum]=0; \
- stringused[stringnum]=0;
-
-/*+ Append information to the current string. +*/
-#define append_string(xx) \
- newlen=strlen(xx); \
- if((stringused[stringnum]+newlen)>=stringlen[stringnum]) \
-    string[stringnum]=(char*)realloc((void*)string[stringnum],stringlen[stringnum]=(stringused[stringnum]+newlen+256)); \
- strcpy(string[stringnum]+stringused[stringnum],xx); \
- stringused[stringnum]+=newlen;
-
-
-/* Lexer functions and variables */
-
-extern int yylex(void);
-
-static char *yylval=NULL;
-
-static int xmlparse_options;
-
-static unsigned long long lineno;
-
-%}
-
-%option 8bit
-%option pointer
-%option batch
-%option never-interactive
-
-%option perf-report perf-report
-%option warn
-%option verbose
-
-%option nodefault
-%option fast
-%option noread
-
-%option noreject
-%option nounput
-%option noinput
-%option noyywrap
-%option noyymore
-%option noyylineno
-
-%option ansi-definitions
-%option ansi-prototypes
-
- /* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII tags not Unicode. */
-
-S               [ \t]
-
-U1              [\x09\x0A\x0D\x20-\x7F]
-U2              [\xC2-\xDF][\x80-\xBF]
-U3a             \xE0[\xA0-\xBF][\x80-\xBF]
-U3b             [\xE1-\xEC][\x80-\xBF][\x80-\xBF]
-U3c             \xED[\x80-\x9F][\x80-\xBF]
-U3d             [\xEE-\xEF][\x80-\xBF][\x80-\xBF]
-U3              {U3a}|{U3b}|{U3c}|{U3d}
-U4a             \xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]
-U4b             [\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]
-U4c             \xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]
-U4              {U4a}|{U4b}|{U4c}
-
-U               ({U1}|{U2}|{U3}|{U4})
-
-U2_bup          [\xC2-\xDF].
-U3a_bup         \xE0.{1,2}
-U3b_bup         [\xE1-\xEC].{1,2}
-U3c_bup         \xED.{1,2}
-U3d_bup         [\xEE-\xEF].{1,2}
-U3_bup          {U3a_bup}|{U3b_bup}|{U3c_bup}|{U3d_bup}
-U4a_bup         \xF0.{1,3}
-U4b_bup         [\xF1-\xF3].{1,3}
-U4c_bup         \xF4.{1,3}
-U4_bup          {U4a_bup}|{U4b_bup}|{U4c_bup}
-
-U1_xml          ([\x09\x0A\x0D\x20-\x25\x27-\x3B\x3D\x3F-\x7F])
-
-U1quotedS_xml   ([\x09\x0A\x0D\x20-\x25\x28-\x3B\x3D\x3F-\x7F])
-U1quotedD_xml   ([\x09\x0A\x0D\x20-\x21\x23-\x25\x27-\x3B\x3D\x3F-\x7F])
-
-UquotedS        ({U1quotedS_xml}|{U2}|{U3}|{U4})
-UquotedD        ({U1quotedD_xml}|{U2}|{U3}|{U4})
-
-UquotedS_bup    ({U2_bup}|{U3_bup}|{U4_bup})
-UquotedD_bup    ({U2_bup}|{U3_bup}|{U4_bup})
-
-N               (\n|\r\n)
-
-letter          [a-zA-Z]
-digit           [0-9]
-xdigit          [a-fA-F0-9]
-
-namechar        ({letter}|{digit}|[-._:])
-namestart       ({letter}|[_:])
-name            ({namestart}{namechar}*)
-
-entityref       (&{name};)
-charref         (&#({digit}+|x{xdigit}+);)
-
-entityref_bup   (&{namestart}{namechar}*[^-.0-9:;A-Z_a-z]*)
-charref_bup     (&#({digit}*[^0-9;]*|x{xdigit}*[^a-fA-F0-9;]*))
-
-
-%x BANGTAG
-%x COMMENT
-%x XML_DECL_START XML_DECL
-%x TAG_START TAG
-%x ATTR_KEY ATTR_VAL
-%x END_TAG1 END_TAG2
-%x DQUOTED SQUOTED
-
-%%
- /* Must use static variables since the parser returns often. */
- static int numstrings=0,stringnum=0;
- static char **string=NULL;
- static unsigned long *stringlen=NULL,*stringused=NULL;
- static int after_attr=0;
- int newlen;
-
- /* Handle top level entities */
-
-"<!"                        { BEGIN(BANGTAG); }
-"</"                        { BEGIN(END_TAG1); }
-"<?"                        { BEGIN(XML_DECL_START); }
-"<"                         { BEGIN(TAG_START); }
-
-">"                         { return(LEX_ERROR_CLOSE); }
-
-{N}                         { lineno++; }
-{S}+                        { }
-.                           { yylval=yytext; return(LEX_ERROR_TEXT_OUTSIDE); }
-
- /* Tags beginning with '!' */
-
-<BANGTAG>"--"               { BEGIN(COMMENT); }
-<BANGTAG>{N}                { /* lineno++; */ return(LEX_ERROR_TAG_START); }
-<BANGTAG>.                  { return(LEX_ERROR_TAG_START); }
-
- /* Comments */
-
-<COMMENT>"-->"              { BEGIN(INITIAL); }
-<COMMENT>"--"[^>]           { return(LEX_ERROR_COMMENT); }
-<COMMENT>"-".               { /* avoid backing up */ }
-<COMMENT>"-"                { }
-<COMMENT>{N}                { lineno++; }
-<COMMENT>[^-\n]+            { }
-
- /* XML declaration start */
-
-<XML_DECL_START>xml         { BEGIN(XML_DECL); reset_string; yylval=yytext; return(LEX_XML_DECL_BEGIN); }
-<XML_DECL_START>x.{1,2}     { return(LEX_ERROR_XML_DECL_START); /* avoid backing up */ }
-<XML_DECL_START>{N}         { /* lineno++; */ return(LEX_ERROR_XML_DECL_START); }
-<XML_DECL_START>.           { return(LEX_ERROR_XML_DECL_START); }
-
- /* XML declaration middle */
-
-<XML_DECL>"?>"              { BEGIN(INITIAL); return(LEX_XML_DECL_FINISH); }
-<XML_DECL>{S}+              { }
-<XML_DECL>{N}               { lineno++; }
-<XML_DECL>{name}            { after_attr=XML_DECL; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
-<XML_DECL>.                 { return(LEX_ERROR_XML_DECL); }
-
- /* Any tag start */
-
-<TAG_START>{name}           { BEGIN(TAG); reset_string; yylval=yytext; return(LEX_TAG_BEGIN); }
-<TAG_START>{N}              { /* lineno++; */ return(LEX_ERROR_TAG_START); }
-<TAG_START>.                { return(LEX_ERROR_TAG_START); }
-
- /* End-tag start */
-
-<END_TAG1>{name}            { BEGIN(END_TAG2); yylval=yytext; return(LEX_TAG_POP); }
-<END_TAG1>{N}               { /* lineno++; */ return(LEX_ERROR_END_TAG); }
-<END_TAG1>.                 { return(LEX_ERROR_END_TAG); }
-
-<END_TAG2>">"               { BEGIN(INITIAL); }
-<END_TAG2>{N}               { /* lineno++; */ return(LEX_ERROR_END_TAG); }
-<END_TAG2>.                 { return(LEX_ERROR_END_TAG); }
-
- /* Any tag middle */
-
-<TAG>"/>"                   { BEGIN(INITIAL); return(LEX_TAG_FINISH); }
-<TAG>">"                    { BEGIN(INITIAL); return(LEX_TAG_PUSH); }
-<TAG>{S}+                   { }
-<TAG>{N}                    { lineno++; }
-<TAG>{name}                 { after_attr=TAG; BEGIN(ATTR_KEY); yylval=yytext; return(LEX_ATTR_KEY); }
-<TAG>.                      { return(LEX_ERROR_TAG); }
-
- /* Attributes */
-
-<ATTR_KEY>=                 { BEGIN(ATTR_VAL); }
-<ATTR_KEY>{N}               { /* lineno++; */ return(LEX_ERROR_ATTR); }
-<ATTR_KEY>.                 { return(LEX_ERROR_ATTR); }
-
-<ATTR_VAL>\"                { BEGIN(DQUOTED); next_string; }
-<ATTR_VAL>\'                { BEGIN(SQUOTED); next_string; }
-<ATTR_VAL>{N}               { /* lineno++; */ return(LEX_ERROR_ATTR); }
-<ATTR_VAL>.                 { return(LEX_ERROR_ATTR); }
-
- /* Quoted strings */
-
-<DQUOTED>\"                 { BEGIN(after_attr); yylval=string[stringnum]; return(LEX_ATTR_VAL); }
-<DQUOTED>{entityref}        { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
-                              else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } }
-<DQUOTED>{entityref_bup}    { yylval=yytext; return(LEX_ERROR_ATTR_VAL); /* avoid backing up */ }
-<DQUOTED>{charref}          { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
-                              else { const char *str=ParseXML_Decode_Char_Ref(yytext);   if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } }
-<DQUOTED>{charref_bup}      { yylval=yytext; return(LEX_ERROR_ATTR_VAL); /* avoid backing up */ }
-<DQUOTED>{U1quotedD_xml}+   { append_string(yytext); /* optimise after avoiding backing up */ }
-<DQUOTED>{UquotedD}         { append_string(yytext); }
-<DQUOTED>{UquotedD_bup}     { yylval=yytext; return(LEX_ERROR_ATTR_VAL); /* avoid backing up */ }
-<DQUOTED>[<>&]              { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
-<DQUOTED>.                  { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
-
-<SQUOTED>\'                 { BEGIN(after_attr); yylval=string[stringnum]; return(LEX_ATTR_VAL); }
-<SQUOTED>{entityref}        { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
-                              else { const char *str=ParseXML_Decode_Entity_Ref(yytext); if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_ENTITY_REF);} } }
-<SQUOTED>{entityref_bup}    { yylval=yytext; return(LEX_ERROR_ATTR_VAL); /* avoid backing up */ }
-<SQUOTED>{charref}          { if(xmlparse_options&XMLPARSE_RETURN_ATTR_ENCODED) {append_string(yytext);}
-                              else { const char *str=ParseXML_Decode_Char_Ref(yytext);   if(str) {append_string(str);} else {yylval=yytext; return(LEX_ERROR_CHAR_REF);} } }
-<SQUOTED>{charref_bup}      { yylval=yytext; return(LEX_ERROR_ATTR_VAL); /* avoid backing up */ }
-<SQUOTED>{U1quotedS_xml}+   { append_string(yytext); /* optimise after avoiding backing up */ }
-<SQUOTED>{UquotedS}         { append_string(yytext); }
-<SQUOTED>{UquotedS_bup}     { yylval=yytext; return(LEX_ERROR_ATTR_VAL); /* avoid backing up */ }
-<SQUOTED>[<>&]              { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
-<SQUOTED>.                  { yylval=yytext; return(LEX_ERROR_ATTR_VAL); }
-
- /* End of file */
-
-<<EOF>>                     { for(stringnum=0;stringnum<numstrings;stringnum++) if(string[stringnum]) free(string[stringnum]);
-                              if(string) free(string); string=NULL;
-                              if(stringlen) free(stringlen); stringlen=NULL;
-                              if(stringlen) free(stringlen); stringlen=NULL;
-                              if(stringused) free(stringused); stringused=NULL;
-                              numstrings=0;
-                              BEGIN(INITIAL); return(LEX_EOF); }
-
-%%
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  A function to call the callback function with the parameters needed.
-
-  int call_callback Returns 1 if the callback returned with an error.
-
-  const char *name The name of the tag.
-
-  int (*callback)() The callback function.
-
-  int type The type of tag (start and/or end).
-
-  int nattributes The number of attributes collected.
-
-  char *attributes[XMLPARSE_MAX_ATTRS] The list of attributes.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static inline int call_callback(const char *name,int (*callback)(),int type,int nattributes,char *attributes[XMLPARSE_MAX_ATTRS])
-{
- switch(nattributes)
-   {
-   case  0: return (*callback)(name,type);
-   case  1: return (*callback)(name,type,attributes[0]);
-   case  2: return (*callback)(name,type,attributes[0],attributes[1]);
-   case  3: return (*callback)(name,type,attributes[0],attributes[1],attributes[2]);
-   case  4: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3]);
-   case  5: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4]);
-   case  6: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5]);
-   case  7: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6]);
-   case  8: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]);
-   case  9: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8]);
-   case 10: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9]);
-   case 11: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10]);
-   case 12: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11]);
-   case 13: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12]);
-   case 14: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13]);
-   case 15: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14]);
-   case 16: return (*callback)(name,type,attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7],attributes[8],attributes[9],attributes[10],attributes[11],attributes[12],attributes[13],attributes[14],attributes[15]);
-
-   default:
-    fprintf(stderr,"XML Parser: Error on line %llu: too many attributes for tag '%s' source code needs changing.\n",lineno,name);
-    exit(1);
-   }
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Parse the XML and call the functions for each tag as seen.
-
-  int ParseXML Returns 0 if OK or something else in case of an error.
-
-  FILE *file The file to parse.
-
-  xmltag **tags The array of pointers to tags for the top level.
-
-  int options A list of XML Parser options OR-ed together.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-int ParseXML(FILE *file,xmltag **tags,int options)
-{
- int yychar,i;
-
- char *attributes[XMLPARSE_MAX_ATTRS]={NULL};
- int attribute=0;
-
- int stackdepth=0,stackused=0;
- xmltag ***tags_stack=NULL;
- xmltag **tag_stack=NULL;
- xmltag *tag=NULL;
-
- /* The actual parser. */
-
- xmlparse_options=options;
-
- yyin=file;
-
- yyrestart(yyin);
-
- lineno=1;
-
- BEGIN(INITIAL);
-
- do
-   {
-    yychar=yylex();
-
-    switch(yychar)
-      {
-       /* The start of a tag for an XML declaration */
-
-      case LEX_XML_DECL_BEGIN:
-
-       if(tag_stack)
-         {
-          fprintf(stderr,"XML Parser: Error on line %llu: XML declaration not before all other tags.\n",lineno);
-          yychar=LEX_ERROR_XML_NOT_FIRST;
-          break;
-         }
-
-       /* The start of a tag for an element */
-
-      case LEX_TAG_BEGIN:
-
-       tag=NULL;
-
-       for(i=0;tags[i];i++)
-          if(!strcasecmp(yylval,tags[i]->name))
-            {
-             tag=tags[i];
-
-             for(i=0;i<tag->nattributes;i++)
-                attributes[i]=NULL;
-
-             break;
-            }
-
-       if(tag==NULL)
-         {
-          fprintf(stderr,"XML Parser: Error on line %llu: unexpected tag '%s'.\n",lineno,yylval);
-          yychar=LEX_ERROR_UNEXP_TAG;
-         }
-
-       break;
-
-       /* The end of the start-tag for an element */
-
-      case LEX_TAG_PUSH:
-
-       if(stackused==stackdepth)
-         {
-          tag_stack =(xmltag**) realloc((void*)tag_stack ,(stackdepth+=8)*sizeof(xmltag*));
-          tags_stack=(xmltag***)realloc((void*)tags_stack,(stackdepth+=8)*sizeof(xmltag**));
-         }
-
-       tag_stack [stackused]=tag;
-       tags_stack[stackused]=tags;
-       stackused++;
-
-       if(tag->callback)
-          if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START,tag->nattributes,attributes))
-             yychar=LEX_ERROR_CALLBACK;
-
-       tags=tag->subtags;
-
-       break;
-
-       /* The end of the empty-element-tag for an XML declaration */
-
-      case LEX_XML_DECL_FINISH:
-
-       /* The end of the empty-element-tag for an element */
-
-      case LEX_TAG_FINISH:
-
-       if(tag->callback)
-          if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_START|XMLPARSE_TAG_END,tag->nattributes,attributes))
-             yychar=LEX_ERROR_CALLBACK;
-
-       if(stackused>0)
-          tag=tag_stack[stackused-1];
-       else
-          tag=NULL;
-
-       break;
-
-       /* The end of the end-tag for an element */
-
-      case LEX_TAG_POP:
-
-       stackused--;
-       tags=tags_stack[stackused];
-       tag =tag_stack [stackused];
-
-       if(strcmp(tag->name,yylval))
-         {
-          fprintf(stderr,"XML Parser: Error on line %llu: end tag '</%s>' doesn't match start tag '<%s ...>'.\n",lineno,yylval,tag->name);
-          yychar=LEX_ERROR_UNBALANCED;
-         }
-
-       if(stackused<0)
-         {
-          fprintf(stderr,"XML Parser: Error on line %llu: end tag '</%s>' seen but there was no start tag '<%s ...>'.\n",lineno,yylval,yylval);
-          yychar=LEX_ERROR_NO_START;
-         }
-
-       for(i=0;i<tag->nattributes;i++)
-          attributes[i]=NULL;
-
-       if(tag->callback)
-          if(call_callback(tag->name,tag->callback,XMLPARSE_TAG_END,tag->nattributes,attributes))
-             yychar=LEX_ERROR_CALLBACK;
-
-       if(stackused>0)
-          tag=tag_stack[stackused-1];
-       else
-          tag=NULL;
-
-       break;
-
-       /* An attribute key */
-
-      case LEX_ATTR_KEY:
-
-       attribute=-1;
-
-       for(i=0;i<tag->nattributes;i++)
-          if(!strcasecmp(yylval,tag->attributes[i]))
-            {
-             attribute=i;
-
-             break;
-            }
-
-       if(attribute==-1)
-         {
-          if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERROR ||
-             ((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_ERRNONAME && !strchr(yylval,':')))
-            {
-             fprintf(stderr,"XML Parser: Error on line %llu: unexpected attribute '%s' for tag '%s'.\n",lineno,yylval,tag->name);
-             yychar=LEX_ERROR_UNEXP_ATT;
-            }
-          else if((options&XMLPARSE_UNKNOWN_ATTRIBUTES)==XMLPARSE_UNKNOWN_ATTR_WARN)
-             fprintf(stderr,"XML Parser: Warning on line %llu: unexpected attribute '%s' for tag '%s'.\n",lineno,yylval,tag->name);
-         }
-
-       break;
-
-       /* An attribute value */
-
-      case LEX_ATTR_VAL:
-
-       if(tag->callback && attribute!=-1 && yylval)
-          attributes[attribute]=yylval;
-
-       break;
-
-       /* End of file */
-
-      case LEX_EOF:
-
-       if(tag)
-         {
-          fprintf(stderr,"XML Parser: Error on line %llu: end of file seen without end tag '</%s>'.\n",lineno,tag->name);
-          yychar=LEX_ERROR_UNEXP_EOF;
-         }
-
-       break;
-
-      case LEX_ERROR_TAG_START:
-       fprintf(stderr,"XML Parser: Error on line %llu: character '<' seen not at start of tag.\n",lineno);
-       break;
-
-      case LEX_ERROR_XML_DECL_START:
-       fprintf(stderr,"XML Parser: Error on line %llu: characters '<?' seen not at start of XML declaration.\n",lineno);
-       break;
-
-      case LEX_ERROR_TAG:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid character seen inside tag '<%s...>'.\n",lineno,tag->name);
-       break;
-
-      case LEX_ERROR_XML_DECL:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid character seen inside XML declaration '<?%s...>'.\n",lineno,tag->name);
-       break;
-
-      case LEX_ERROR_ATTR:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid attribute definition seen in tag.\n",lineno);
-       break;
-
-      case LEX_ERROR_END_TAG:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid character seen in end-tag.\n",lineno);
-       break;
-
-      case LEX_ERROR_COMMENT:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid comment seen.\n",lineno);
-       break;
-
-      case LEX_ERROR_CLOSE:
-       fprintf(stderr,"XML Parser: Error on line %llu: character '>' seen not at end of tag.\n",lineno);
-       break;
-
-      case LEX_ERROR_ATTR_VAL:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid character '%s' seen in attribute value.\n",lineno,yylval);
-       break;
-
-      case LEX_ERROR_ENTITY_REF:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid entity reference '%s' seen in attribute value.\n",lineno,yylval);
-       break;
-
-      case LEX_ERROR_CHAR_REF:
-       fprintf(stderr,"XML Parser: Error on line %llu: invalid character reference '%s' seen in attribute value.\n",lineno,yylval);
-       break;
-
-      case LEX_ERROR_TEXT_OUTSIDE:
-       fprintf(stderr,"XML Parser: Error on line %llu: non-whitespace '%s' seen outside tag.\n",lineno,yylval);
-       break;
-      }
-   }
- while(yychar>LEX_EOF && yychar<LEX_ERROR);
-
- /* Delete the tagdata */
-
- if(stackdepth)
-   {
-    free(tag_stack);
-    free(tags_stack);
-   }
-
- return(yychar);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Return the current parser line number.
-
-  unsigned long long ParseXML_LineNumber Returns the line number.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-unsigned long long ParseXML_LineNumber(void)
-{
- return(lineno);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Convert an XML entity reference into an ASCII string.
-
-  char *ParseXML_Decode_Entity_Ref Returns a pointer to the replacement decoded string.
-
-  const char *string The entity reference string.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-char *ParseXML_Decode_Entity_Ref(const char *string)
-{
- if(!strcmp(string,"&"))  return("&");
- if(!strcmp(string,"<"))   return("<");
- if(!strcmp(string,">"))   return(">");
- if(!strcmp(string,"'")) return("'");
- if(!strcmp(string,""")) return("\"");
- return(NULL);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Convert an XML character reference into an ASCII string.
-
-  char *ParseXML_Decode_Char_Ref Returns a pointer to the replacement decoded string.
-
-  const char *string The character reference string.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-char *ParseXML_Decode_Char_Ref(const char *string)
-{
- static char result[5]="";
- long int unicode;
-
- if(string[2]=='x') unicode=strtol(string+3,NULL,16);
- else               unicode=strtol(string+2,NULL,10);
-
- if(unicode<0x80)
-   {
-    /* 0000 0000-0000 007F  =>  0xxxxxxx */
-    result[0]=unicode;
-    result[1]=0;
-   }
- else if(unicode<0x07FF)
-   {
-    /* 0000 0080-0000 07FF  =>  110xxxxx 10xxxxxx */
-    result[0]=0xC0+((unicode&0x07C0)>>6);
-    result[1]=0x80+ (unicode&0x003F);
-    result[2]=0;
-   }
- else if(unicode<0xFFFF)
-   {
-    /* 0000 0800-0000 FFFF  =>  1110xxxx 10xxxxxx 10xxxxxx */
-    result[0]=0xE0+((unicode&0xF000)>>12);
-    result[1]=0x80+((unicode&0x0FC0)>>6);
-    result[2]=0x80+ (unicode&0x003F);
-    result[3]=0;
-   }
- else if(unicode<0x1FFFFF)
-   {
-    /* 0001 0000-001F FFFF  =>  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
-    result[0]=0xF0+((unicode&0x1C0000)>>18);
-    result[1]=0x80+((unicode&0x03F000)>>12);
-    result[2]=0x80+((unicode&0x000FC0)>>6);
-    result[3]=0x80+ (unicode&0x00003F);
-    result[4]=0;
-   }
- else
-   {
-    result[0]=0xFF;
-    result[1]=0xFD;
-    result[2]=0;
-   }
-
- return(result);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Convert a string into something that is safe to output in an XML file.
-
-  char *ParseXML_Encode_Safe_XML Returns a pointer to the replacement encoded string (or the original if no change needed).
-
-  const char *string The string to convert.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-char *ParseXML_Encode_Safe_XML(const char *string)
-{
- static const char hexstring[17]="0123456789ABCDEF";
- int i=0,j=0,len;
- char *result;
-
- for(i=0;string[i];i++)
-    if(string[i]=='<' || string[i]=='>' || string[i]=='&' || string[i]=='\'' || string[i]=='"' || string[i]<32 || (unsigned char)string[i]>127)
-       break;
-
- if(!string[i])
-    return((char*)string);
-
- len=i+256-6;
-
- result=(char*)malloc(len+7);
- strncpy(result,string,j=i);
-
- do
-   {
-    for(;j<len && string[i];i++)
-       if(string[i]=='<')
-         {
-          result[j++]='&';
-          result[j++]='l';
-          result[j++]='t';
-          result[j++]=';';
-         }
-       else if(string[i]=='>')
-         {
-          result[j++]='&';
-          result[j++]='g';
-          result[j++]='t';
-          result[j++]=';';
-         }
-       else if(string[i]=='&')
-         {
-          result[j++]='&';
-          result[j++]='a';
-          result[j++]='m';
-          result[j++]='p';
-          result[j++]=';';
-         }
-       else if(string[i]=='\'')
-         {
-          result[j++]='&';
-          result[j++]='a';
-          result[j++]='p';
-          result[j++]='o';
-          result[j++]='s';
-          result[j++]=';';
-         }
-       else if(string[i]=='"')
-         {
-          result[j++]='&';
-          result[j++]='q';
-          result[j++]='u';
-          result[j++]='o';
-          result[j++]='t';
-          result[j++]=';';
-         }
-       else if(string[i]>=32 && (unsigned char)string[i]<=127)
-          result[j++]=string[i];
-       else
-         {
-          unsigned int unicode;
-
-          /* Decode the UTF-8 */
-
-          if((string[i]&0x80)==0)
-            {
-             /* 0000 0000-0000 007F  =>  0xxxxxxx */
-             unicode=string[i];
-            }
-          else if((string[i]&0xE0)==0xC0 && (string[i]&0x1F)>=2 && (string[i+1]&0xC0)==0x80)
-            {
-             /* 0000 0080-0000 07FF  =>  110xxxxx 10xxxxxx */
-             unicode =(string[i++]&0x1F)<<6;
-             unicode|= string[i  ]&0x3F;
-            }
-          else if((string[i]&0xF0)==0xE0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80)
-            {
-             /* 0000 0800-0000 FFFF  =>  1110xxxx 10xxxxxx 10xxxxxx */
-             unicode =(string[i++]&0x0F)<<12;
-             unicode|=(string[i++]&0x3F)<<6;
-             unicode|= string[i  ]&0x3F;
-            }
-          else if((string[i]&0xF8)==0xF0 && (string[i+1]&0xC0)==0x80 && (string[i+2]&0xC0)==0x80 && (string[i+3]&0xC0)==0x80)
-            {
-             /* 0001 0000-001F FFFF  =>  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
-             unicode =(string[i++]&0x07)<<18;
-             unicode|=(string[i++]&0x3F)<<12;
-             unicode|=(string[i++]&0x3F)<<6;
-             unicode|= string[i  ]&0x3F;
-            }
-          else
-             unicode=0xFFFD;
-
-          /* Output the character entity */
-
-          result[j++]='&';
-          result[j++]='#';
-          result[j++]='x';
-
-          if(unicode&0x00FF0000)
-            {
-             result[j++]=hexstring[((unicode>>16)&0xf0)>>4];
-             result[j++]=hexstring[((unicode>>16)&0x0f)   ];
-            }
-          if(unicode&0x00FFFF00)
-            {
-             result[j++]=hexstring[((unicode>>8)&0xf0)>>4];
-             result[j++]=hexstring[((unicode>>8)&0x0f)   ];
-            }
-          result[j++]=hexstring[(unicode&0xf0)>>4];
-          result[j++]=hexstring[(unicode&0x0f)   ];
-
-          result[j++]=';';
-         }
-
-    if(string[i])                  /* Not finished */
-      {
-       len+=256;
-       result=(char*)realloc((void*)result,len+7);
-      }
-   }
- while(string[i]);
-
- result[j]=0;
-
- return(result);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Check that a string really is an integer.
-
-  int ParseXML_IsInteger Returns 1 if an integer could be found or 0 otherwise.
-
-  const char *string The string to be parsed.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-int ParseXML_IsInteger(const char *string)
-{
- const char *p=string;
-
- if(*p=='-' || *p=='+')
-    p++;
-
- while(isdigit(*p))
-    p++;
-
- if(*p)
-    return(0);
- else
-    return(1);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Check that a string really is a floating point number.
-
-  int ParseXML_IsFloating Returns 1 if a floating point number could be found or 0 otherwise.
-
-  const char *string The string to be parsed.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-int ParseXML_IsFloating(const char *string)
-{
- const char *p=string;
-
- if(*p=='-' || *p=='+')
-    p++;
-
- while(isdigit(*p) || *p=='.')
-    p++;
-
- if(*p=='e' || *p=='E')
-   {
-    p++;
-
-    if(*p=='-' || *p=='+')
-       p++;
-
-    while(isdigit(*p))
-       p++;
-   }
-
- if(*p)
-    return(0);
- else
-    return(1);
-}
diff --git a/web/Makefile b/web/Makefile
new file mode 100644
index 0000000..d9a0f82
--- /dev/null
+++ b/web/Makefile
@@ -0,0 +1,149 @@
+# web directory Makefile
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2010-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# All configuration is in the top-level Makefile.conf
+
+include ../Makefile.conf
+
+# Web file paths and other paths
+
+WEBBINDIR=bin
+WEBDATADIR=data
+WEBTRANSDIR=translations
+WEBWWWDIR=www/routino
+WEBICONDIR=www/routino/icons
+WEBDOCDIR=www/routino/documentation
+
+XMLDIR=../xml
+DOCDIR=../doc
+SRCDIR=../src
+
+# Files to install
+
+STANDARD_XML_FILES=profiles.xml \
+	           translations.xml \
+	           tagging.xml
+
+SPECIAL_XML_FILES=tagging-drive.xml \
+	          tagging-ride.xml \
+	          tagging-walk.xml
+
+PROFILE_FILES=profiles.pl \
+	      profiles.js
+
+DOC_FILES=$(notdir $(wildcard $(DOCDIR)/html/*.html)) $(notdir $(wildcard $(DOCDIR)/html/*.css))
+
+EXE_FILES=planetsplitter planetsplitter-slim \
+	  router         router-slim \
+	  filedumper     filedumper-slim \
+	  filedumperx
+
+########
+
+all: all-bin all-data all-doc all-profiles all-icons all-translations
+
+all-bin: all-exe
+	@[ -d $(WEBBINDIR) ] || mkdir -p $(WEBBINDIR)
+	@for file in $(EXE_FILES); do \
+	    if [ ! -f $(WEBBINDIR)/$$file ] || [ $(SRCDIR)/$$file -nt $(WEBBINDIR)/$$file ]; then \
+	       echo cp $(SRCDIR)/$$file $(WEBBINDIR) ;\
+	       cp -f $(SRCDIR)/$$file $(WEBBINDIR) ;\
+	    fi ;\
+	 done
+
+all-data: all-xml
+	@[ -d $(WEBDATADIR) ] || mkdir -p $(WEBDATADIR)
+	@for file in $(STANDARD_XML_FILES); do \
+	    if [ ! -f $(WEBDATADIR)/$$file ] || [ routino-$$file -nt $(WEBDATADIR)/$$file ]; then \
+	       echo cp $(XMLDIR)/routino-$$file $(WEBDATADIR)/$$file ;\
+	       cp -f $(XMLDIR)/routino-$$file $(WEBDATADIR)/$$file ;\
+	    fi ;\
+	 done
+	@for file in $(SPECIAL_XML_FILES); do \
+	    if [ ! -f $(WEBDATADIR)/$$file ] || [ $$file -nt $(WEBDATADIR)/$$file ]; then \
+	       echo cp $(XMLDIR)/$$file $(WEBDATADIR)/$$file ;\
+	       cp -f $(XMLDIR)/$$file $(WEBDATADIR)/$$file ;\
+	    fi ;\
+	 done
+
+all-doc:
+	@[ -d $(WEBDOCDIR) ] || mkdir -p $(WEBDOCDIR)
+	@for file in $(DOC_FILES); do \
+	    if [ ! -f $(WEBDOCDIR)/$$file ] || [ $(DOCDIR)/html/$$file -nt $(WEBDOCDIR)/$$file ]; then \
+	       echo cp $(DOCDIR)/html/$$file $(WEBDOCDIR) ;\
+	       cp -f $(DOCDIR)/html/$$file $(WEBDOCDIR) ;\
+	    fi ;\
+	 done
+
+all-profiles: all-exe all-data
+	@if [ ! -f $(WEBWWWDIR)/profiles.js ] || [ ! -f $(WEBWWWDIR)/profiles.pl ] || \
+	     [ $(WEBDATADIR)/profiles.xml -nt $(WEBWWWDIR)/profiles.pl ] || \
+	     [ $(WEBDATADIR)/profiles.xml -nt $(WEBWWWDIR)/profiles.js ]; then \
+	    echo update-profiles.pl ;\
+	    ( cd $(WEBWWWDIR) ; perl update-profiles.pl ) ;\
+	 fi
+
+all-icons: $(WEBICONDIR)/ball-0.png
+
+$(WEBICONDIR)/ball-0.png:
+	echo create-icons.pl
+	@cd $(WEBICONDIR) && perl create-icons.pl
+
+all-translations: $(WEBWWWDIR)/router.html.en
+
+$(WEBWWWDIR)/router.html.en:
+	echo translate.pl
+	@cd $(WEBTRANSDIR) && perl translate.pl
+
+all-exe:
+	cd $(SRCDIR) && $(MAKE) $(EXE_FILES)
+
+all-xml:
+	cd $(XMLDIR) && $(MAKE) all
+
+########
+
+test:
+
+########
+
+install: all
+	@echo "******************************************************"
+	@echo "* Note: web directory is not installed automatically *"
+	@echo "******************************************************"
+
+########
+
+clean:
+	rm -f *~
+
+########
+
+distclean: clean
+	-cd $(WEBBINDIR)  && rm -f $(EXE_FILES)
+	-cd $(WEBDATADIR) && rm -f $(STANDARD_XML_FILES) $(SPECIAL_XML_FILES)
+	-cd $(WEBDOCDIR)  && rm -f $(DOC_FILES)
+	-cd $(WEBWWWDIR)  && rm -f $(PROFILE_FILES)
+
+########
+
+.PHONY:: all test install clean distclean
+
+.PHONY:: all-bin all-data all-doc all-profiles all-icons all-translations all-exe all-xml
diff --git a/web/data/create.sh b/web/data/create.sh
index 87a0959..19d5a8e 100755
--- a/web/data/create.sh
+++ b/web/data/create.sh
@@ -1,30 +1,20 @@
 #!/bin/sh -x
 
-# This script can download either from GeoFabrik or Cloudmade.
+# This script can download from the GeoFabrik server.
 
 
-# EDIT THIS to set the names of the files to download.
-files="europe/great_britain.osm.bz2 europe/ireland.osm.bz2 europe/isle_of_man.osm.bz2"
+# EDIT THIS to set the names of the files to download from GeoFabrik.
+files="europe/great-britain-latest.osm.bz2 europe/ireland-and-northern-ireland-latest.osm.bz2"
+server="download.geofabrik.de"
+
 
 # Download the files
 
 for file in $files; do
-   wget -N http://download.geofabrik.de/osm/$file
+   wget -N http://$server/$file
 done
 
 
-## EDIT THIS to set the names of the files to download.
-#files="europe/united_kingdom/united_kingdom.osm.bz2 europe/ireland/ireland.osm.bz2 europe/isle_of_man/isle_of_man.osm.bz2"
-#
-## Download the files
-#
-#for file in $files; do
-#   wget -N http://downloads.cloudmade.com/$file
-#done
-
-
 # Process the data
 
-bunzip2 *.bz2
-
-../bin/planetsplitter --errorlog *.osm
+../bin/planetsplitter --errorlog *.osm.bz2
diff --git a/web/translations/router.html b/web/translations/router.html
new file mode 100644
index 0000000..63498e3
--- /dev/null
+++ b/web/translations/router.html
@@ -0,0 +1,396 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routing route planner">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
+
+<title>Routino : @@ROUTER-TITLE@@</title>
+
+<!--
+ Routino router web page.
+
+ Part of the Routino routing software.
+
+ This file Copyright 2008-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see http://www.gnu.org/licenses/.
+-->
+
+<!-- Page elements -->
+<script src="page-elements.js" type="text/javascript"></script>
+<link href="page-elements.css" type="text/css" rel="stylesheet">
+
+<!-- Router and visualiser shared features -->
+<link href="maplayout.css" type="text/css" rel="stylesheet">
+
+<!-- Router specific features -->
+<script src="profiles.js" type="text/javascript"></script>
+<link href="router.css" type="text/css" rel="stylesheet">
+
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('html_init();map_init();form_init();');">
+
+<!-- Left hand side of window - data panel -->
+
+<div class="left_panel">
+
+  <div class="tab_box">
+    <span id="tab_options" onclick="tab_select('options');" class="tab_selected"   title="@@OPTION-TAB-HELP@@">@@OPTION-TAB@@</span>
+    <span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="@@RESULTS-TAB-HELP@@">@@RESULTS-TAB@@</span>
+    <span id="tab_data"    onclick="tab_select('data');"    class="tab_unselected" title="@@DATA-TAB-HELP@@">@@DATA-TAB@@</span>
+  </div>
+
+  <div class="tab_content" id="tab_options_div">
+
+    <form name="form" id="form" action="#" method="get" onsubmit="return false;">
+      <div class="hideshow_box">
+        <span class="hideshow_title">@@ROUTINO-ROUTER@@</span>
+        @@ROUTER-INFO@@
+        <div class="center">
+          <a target="other" href="http://www.routino.org/">@@ROUTINO-WEBSITE@@</a>
+          |
+          <a target="other" href="documentation/">@@DOCUMENTATION@@</a>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+        <span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+        <span class="hideshow_title">@@LANGUAGE-BOX@@</span>
+
+        <div id="hideshow_language_div" style="display: none;">
+          <table>
+           $$LANGUAGES-META$$
+            <tr>
+              <td><a id="lang_xx_url" href="router.html.xx" title="@@LANGUAGE-WEBPAGE@@">@@LANGUAGE@@</a>
+              <td>(XX)
+              <td><input name="language" type="radio" value="xx" onchange="formSetLanguage();" CHECKED-XX>
+           $$LANGUAGES-META$$
+          </table>
+          <a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
+        <span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
+        <span class="hideshow_title">@@WAYPOINTS-BOX@@</span>
+        <div id="hideshow_waypoint_div">
+          <table id="waypoints">
+            <colgroup>
+              <col style="width: 22px;">
+              <col>
+              <col style="width: 76px;">
+            </colgroup>
+            <tr id="waypointXXX" style="display: none;">
+              <td>
+                <img id="iconXXX" src="icons/marker-XXX-grey.png" title="@@WAYPOINT-POSITION@@" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX);">
+              <td>
+                <span id="coordsXXX">
+                  <input name="lonXXX" type="text" size="7" title="@@WAYPOINT-LONGITUDE@@" onchange="formSetCoords(XXX);">E
+                  <input name="latXXX" type="text" size="7" title="@@WAYPOINT-LATITUDE@@"  onchange="formSetCoords(XXX);">N
+                </span>
+                <span id="searchXXX" style="display: none;">
+                  <input name="searchXXX" type="text" size="18" title="@@WAYPOINT-LOCATION@@"> <!-- uses Javascript event for triggering -->
+                </span>
+              <td>
+                <img alt="?" src="icons/waypoint-search.png"   title="@@WAYPOINT-SEARCH@@"  onmousedown="markerSearch(XXX);"  >
+                <img alt="G" src="icons/waypoint-locate.png"   title="@@WAYPOINT-GET@@"     onmousedown="markerLocate(XXX);"  >
+                <img alt="O" src="icons/waypoint-recentre.png" title="@@WAYPOINT-CENTRE1@@" onmousedown="markerRecentre(XXX);">
+                <img alt="^" src="icons/waypoint-up.png"       title="@@WAYPOINT-UP@@"      onmousedown="markerMoveUp(XXX);"  >
+                <img alt="+" src="icons/waypoint-add.png"      title="@@WAYPOINT-ADD@@"     onmousedown="markerAddAfter(XXX);">
+                <br>
+                <img alt="#" src="icons/waypoint-coords.png"   title="@@WAYPOINT-COORDS@@"  onmousedown="markerCoords(XXX);"  >
+                <img alt="~" src="icons/waypoint-home.png"     title="@@WAYPOINT-HOME@@"    onmousedown="markerHome(XXX);"    >
+                <img alt="o" src="icons/waypoint-centre.png"   title="@@WAYPOINT-CENTRE2@@" onmousedown="markerCentre(XXX);"  >
+                <img alt="v" src="icons/waypoint-down.png"     title="@@WAYPOINT-DOWN@@"    onmousedown="markerMoveDown(XXX);">
+                <img alt="-" src="icons/waypoint-remove.png"   title="@@WAYPOINT-REMOVE@@"  onmousedown="markerRemove(XXX);"  >
+            <tr id="searchresultsXXX" style="display: none;">
+              <td colspan="3">
+            <!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js.  -->
+            <tr>
+              <td colspan="3" class="center">
+                <input type="button" title="@@WAYPOINT-REVERSE@@" value="@@WAYPOINT-REVERSE-BUTTON@@" onmousedown="markersReverse();">
+                <input type="button" title="@@WAYPOINT-LOOP@@"    value="@@WAYPOINT-LOOP-BUTTON@@"    onmousedown="markersLoop();">
+          </table>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
+        <span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
+        <span class="hideshow_title">@@TRANSPORT-TYPE-BOX@@</span>
+        <div id="hideshow_transport_div">
+          <table>
+            <tr><td>@@TRANSPORT-FOOT@@:      <td><input name="transport" type="radio" value="foot"       onchange="formSetTransport('foot'      );">
+            <tr><td>@@TRANSPORT-HORSE@@:     <td><input name="transport" type="radio" value="horse"      onchange="formSetTransport('horse'     );">
+            <tr><td>@@TRANSPORT-WHEELCHAIR@@:<td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair');">
+            <tr><td>@@TRANSPORT-BICYCLE@@:   <td><input name="transport" type="radio" value="bicycle"    onchange="formSetTransport('bicycle'   );">
+            <tr><td>@@TRANSPORT-MOPED@@:     <td><input name="transport" type="radio" value="moped"      onchange="formSetTransport('moped'     );">
+            <tr><td>@@TRANSPORT-MOTORCYCLE@@:<td><input name="transport" type="radio" value="motorcycle" onchange="formSetTransport('motorcycle');">
+            <tr><td>@@TRANSPORT-MOTORCAR@@:  <td><input name="transport" type="radio" value="motorcar"   onchange="formSetTransport('motorcar'  );">
+            <tr><td>@@TRANSPORT-GOODS@@:     <td><input name="transport" type="radio" value="goods"      onchange="formSetTransport('goods'     );">
+            <tr><td>@@TRANSPORT-HGV@@:       <td><input name="transport" type="radio" value="hgv"        onchange="formSetTransport('hgv'       );">
+            <tr><td>@@TRANSPORT-PSV@@:       <td><input name="transport" type="radio" value="psv"        onchange="formSetTransport('psv'       );">
+          </table>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+        <span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+        <span class="hideshow_title">@@HIGHWAY-PREFERENCES-BOX@@</span>
+        <div id="hideshow_highway_div" style="display: none;">
+          <table>
+            <tr><td>@@HIGHWAY-MOTORWAY@@:    <td><input name="highway-motorway"     type="text" size="3" onchange="formSetHighway('motorway'    ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('motorway'    ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('motorway'    ,'+');">
+            <tr><td>@@HIGHWAY-TRUNK@@:       <td><input name="highway-trunk"        type="text" size="3" onchange="formSetHighway('trunk'       ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('trunk'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('trunk'       ,'+');">
+            <tr><td>@@HIGHWAY-PRIMARY@@:     <td><input name="highway-primary"      type="text" size="3" onchange="formSetHighway('primary'     ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('primary'     ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('primary'     ,'+');">
+            <tr><td>@@HIGHWAY-SECONDARY@@:   <td><input name="highway-secondary"    type="text" size="3" onchange="formSetHighway('secondary'   ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('secondary'   ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('secondary'   ,'+');">
+            <tr><td>@@HIGHWAY-TERTIARY@@:    <td><input name="highway-tertiary"     type="text" size="3" onchange="formSetHighway('tertiary'    ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('tertiary'    ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('tertiary'    ,'+');">
+            <tr><td>@@HIGHWAY-UNCLASSIFIED@@:<td><input name="highway-unclassified" type="text" size="3" onchange="formSetHighway('unclassified','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('unclassified','+');">
+            <tr><td>@@HIGHWAY-RESIDENTIAL@@: <td><input name="highway-residential"  type="text" size="3" onchange="formSetHighway('residential' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('residential' ,'+');">
+            <tr><td>@@HIGHWAY-SERVICE@@:     <td><input name="highway-service"      type="text" size="3" onchange="formSetHighway('service'     ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('service'     ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('service'     ,'+');">
+            <tr><td>@@HIGHWAY-TRACK@@:       <td><input name="highway-track"        type="text" size="3" onchange="formSetHighway('track'       ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('track'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('track'       ,'+');">
+            <tr><td>@@HIGHWAY-CYCLEWAY@@:    <td><input name="highway-cycleway"     type="text" size="3" onchange="formSetHighway('cycleway'    ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('cycleway'    ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('cycleway'    ,'+');">
+            <tr><td>@@HIGHWAY-PATH@@:        <td><input name="highway-path"         type="text" size="3" onchange="formSetHighway('path'        ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('path'        ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('path'        ,'+');">
+            <tr><td>@@HIGHWAY-STEPS@@:       <td><input name="highway-steps"        type="text" size="3" onchange="formSetHighway('steps'       ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('steps'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('steps'       ,'+');">
+            <tr><td>@@HIGHWAY-FERRY@@:       <td><input name="highway-ferry"        type="text" size="3" onchange="formSetHighway('ferry'       ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('ferry'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('ferry'       ,'+');">
+          </table>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+        <span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+        <span class="hideshow_title">@@SPEED-LIMITS-BOX@@</span>
+        <div id="hideshow_speed_div" style="display: none;">
+          <table>
+            <tr><td>@@HIGHWAY-MOTORWAY@@:    <td><input name="speed-motorway"     type="text" size="3" onchange="formSetSpeed('motorway'    ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('motorway'    ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('motorway'    ,'+');">
+            <tr><td>@@HIGHWAY-TRUNK@@:       <td><input name="speed-trunk"        type="text" size="3" onchange="formSetSpeed('trunk'       ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('trunk'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('trunk'       ,'+');">
+            <tr><td>@@HIGHWAY-PRIMARY@@:     <td><input name="speed-primary"      type="text" size="3" onchange="formSetSpeed('primary'     ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('primary'     ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('primary'     ,'+');">
+            <tr><td>@@HIGHWAY-SECONDARY@@:   <td><input name="speed-secondary"    type="text" size="3" onchange="formSetSpeed('secondary'   ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('secondary'   ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('secondary'   ,'+');">
+            <tr><td>@@HIGHWAY-TERTIARY@@:    <td><input name="speed-tertiary"     type="text" size="3" onchange="formSetSpeed('tertiary'    ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('tertiary'    ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('tertiary'    ,'+');">
+            <tr><td>@@HIGHWAY-UNCLASSIFIED@@:<td><input name="speed-unclassified" type="text" size="3" onchange="formSetSpeed('unclassified','=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('unclassified','+');">
+            <tr><td>@@HIGHWAY-RESIDENTIAL@@: <td><input name="speed-residential"  type="text" size="3" onchange="formSetSpeed('residential' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('residential' ,'+');">
+            <tr><td>@@HIGHWAY-SERVICE@@:     <td><input name="speed-service"      type="text" size="3" onchange="formSetSpeed('service'     ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('service'     ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('service'     ,'+');">
+            <tr><td>@@HIGHWAY-TRACK@@:       <td><input name="speed-track"        type="text" size="3" onchange="formSetSpeed('track'       ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('track'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('track'       ,'+');">
+            <tr><td>@@HIGHWAY-CYCLEWAY@@:    <td><input name="speed-cycleway"     type="text" size="3" onchange="formSetSpeed('cycleway'    ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('cycleway'    ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('cycleway'    ,'+');">
+            <tr><td>@@HIGHWAY-PATH@@:        <td><input name="speed-path"         type="text" size="3" onchange="formSetSpeed('path'        ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('path'        ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('path'        ,'+');">
+            <tr><td>@@HIGHWAY-STEPS@@:       <td><input name="speed-steps"        type="text" size="3" onchange="formSetSpeed('steps'       ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('steps'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('steps'       ,'+');">
+            <tr><td>@@HIGHWAY-FERRY@@:       <td><input name="speed-ferry"        type="text" size="3" onchange="formSetSpeed('ferry'       ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('ferry'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('ferry'       ,'+');">
+          </table>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+        <span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+        <span class="hideshow_title">@@PROPERTY-PREFERENCES-BOX@@</span>
+        <div id="hideshow_property_div" style="display: none;">
+          <table>
+            <tr><td>@@PROPERTY-PAVED@@:       <td><input name="property-paved"        type="text" size="3" onchange="formSetProperty('paved'       ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('paved'       ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('paved'       ,'+');">
+            <tr><td>@@PROPERTY-MULTILANE@@:   <td><input name="property-multilane"    type="text" size="3" onchange="formSetProperty('multilane'   ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('multilane'   ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('multilane'   ,'+');">
+            <tr><td>@@PROPERTY-BRIDGE@@:      <td><input name="property-bridge"       type="text" size="3" onchange="formSetProperty('bridge'      ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bridge'      ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bridge'      ,'+');">
+            <tr><td>@@PROPERTY-TUNNEL@@:      <td><input name="property-tunnel"       type="text" size="3" onchange="formSetProperty('tunnel'      ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('tunnel'      ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('tunnel'      ,'+');">
+            <tr><td>@@PROPERTY-WALKINGROUTE@@:<td><input name="property-footroute"    type="text" size="3" onchange="formSetProperty('footroute'   ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('footroute'   ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('footroute'   ,'+');">
+            <tr><td>@@PROPERTY-BICYCLEROUTE@@:<td><input name="property-bicycleroute" type="text" size="3" onchange="formSetProperty('bicycleroute','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bicycleroute','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bicycleroute','+');">
+          </table>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
+        <span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
+        <span class="hideshow_title">@@OTHER-RESTRICTIONS-BOX@@</span>
+        <div id="hideshow_restriction_div" style="display: none;">
+          <table>
+            <tr><td>@@RESTRICT-ONEWAY@@:   <td><input name="restrict-oneway" type="checkbox" onchange="formSetRestriction('oneway');">
+            <tr><td>@@RESTRICT-TURNS@@:<td><input name="restrict-turns"  type="checkbox" onchange="formSetRestriction('turns' );">
+          </table>
+          <table>
+            <tr><td>@@RESTRICT-WEIGHT@@:<td><input name="restrict-weight" type="text" size="3" onchange="formSetRestriction('weight','=');"><td>tonnes<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('weight','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('weight','+');">
+            <tr><td>@@RESTRICT-HEIGHT@@:<td><input name="restrict-height" type="text" size="3" onchange="formSetRestriction('height','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('height','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('height','+');">
+            <tr><td>@@RESTRICT-WIDTH@@: <td><input name="restrict-width"  type="text" size="3" onchange="formSetRestriction('width' ,'=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('width' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('width' ,'+');">
+            <tr><td>@@RESTRICT-LENGTH@@:<td><input name="restrict-length" type="text" size="3" onchange="formSetRestriction('length','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('length','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('length','+');">
+          </table>
+        </div>
+      </div>
+
+      <div class="hideshow_box">
+        <span class="hideshow_title">@@FIND-BOX@@</span>
+        <input type="button" title="@@FIND-SHORTEST-ROUTE@@" id="shortest" value="@@SHORTEST-ROUTE@@" onclick="findRoute('shortest');">
+        <input type="button" title="@@FIND-QUICKEST-ROUTE@@" id="quickest" value="@@QUICKEST-ROUTE@@" onclick="findRoute('quickest');">
+      </div>
+
+      <div class="hideshow_box">
+        <span class="hideshow_title">@@LINKS-BOX@@</span>
+        <a id="permalink_url" href="router.html">@@MAP-VIEW-LINK@@</a>
+        <br>
+        <a id="edit_url" target="edit" style="display: none;">@@EDIT-OSM-DATA@@</a>
+      </div>
+
+      <div class="hideshow_box">
+        <span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+        <span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+        <span class="hideshow_title">@@HELP-BOX@@</span>
+        <div id="hideshow_help_options_div">
+          <div class="scrollable">
+            @@ROUTER-OPTIONS-HELP@@
+          </div>
+        </div>
+      </div>
+    </form>
+  </div>
+
+
+  <div class="tab_content" id="tab_results_div" style="display: none;">
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@STATUS-BOX@@</span>
+      <div id="result_status">
+        <div id="result_status_not_run">
+          <b><i>@@ROUTER-NOT-RUN@@</i></b>
+        </div>
+        <div id="result_status_running"  style="display: none;">
+          <b>@@ROUTER-RUNNING@@</b>
+        </div>
+        <div id="result_status_complete" style="display: none;">
+          <b>@@ROUTER-COMPLETED@@</b>
+          <br>
+          <a id="router_log_complete" target="router_log" href="#">@@VIEW-DETAILS@@</a>
+        </div>
+        <div id="result_status_error"    style="display: none;">
+          <b>@@ROUTER-ERROR@@</b>
+          <br>
+          <a id="router_log_error" target="router_log" href="#">@@VIEW-DETAILS@@</a>
+        </div>
+        <div id="result_status_failed"   style="display: none;">
+          <b>@@ROUTER-FAILED@@</b>
+        </div>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
+      <span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
+      <span class="hideshow_title">@@SHORTEST-ROUTE@@</span>
+      <div id="shortest_status">
+        <div id="shortest_status_no_info">
+          <b><i>@@NO-INFORMATION@@</i></b>
+        </div>
+        <div id="shortest_status_info" style="display: none;">
+        </div>
+      </div>
+      <div id="hideshow_shortest_div" style="display: none;">
+        <div id="shortest_links" style="display: none;">
+          <table>
+            <tr><td>@@HTML-ROUTE@@:      <td><a id="shortest_html"      target="shortest_html"      href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@GPX-TRACK-ROUTE@@: <td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@GPX-ROUTE@@:       <td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@FULL-TEXT-ROUTE@@: <td><a id="shortest_text_all"  target="shortest_text_all"  href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@TEXT-ROUTE@@:      <td><a id="shortest_text"      target="shortest_text"      href="#">@@OPEN-POPUP@@</a>
+          </table>
+          <hr>
+        </div>
+        <div id="shortest_route">
+        </div>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
+      <span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
+      <span class="hideshow_title">@@QUICKEST-ROUTE@@</span>
+      <div id="quickest_status">
+        <div id="quickest_status_no_info">
+          <b><i>@@NO-INFORMATION@@</i></b>
+        </div>
+        <div id="quickest_status_info" style="display: none;">
+        </div>
+      </div>
+      <div id="hideshow_quickest_div" style="display: none;">
+        <div id="quickest_links" style="display: none;">
+          <table>
+            <tr><td>@@HTML-ROUTE@@:      <td><a id="quickest_html"      target="quickest_html"      href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@GPX-TRACK-ROUTE@@: <td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@GPX-ROUTE@@:       <td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@FULL-TEXT-ROUTE@@: <td><a id="quickest_text_all"  target="quickest_text_all"  href="#">@@OPEN-POPUP@@</a>
+            <tr><td>@@TEXT-ROUTE@@:      <td><a id="quickest_text"      target="quickest_text"      href="#">@@OPEN-POPUP@@</a>
+          </table>
+          <hr>
+        </div>
+        <div id="quickest_route">
+        </div>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
+      <span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
+      <span class="hideshow_title">@@HELP-BOX@@</span>
+      <div id="hideshow_help_route_div">
+        <div class="scrollable">
+          @@ROUTER-RESULTS-HELP@@
+        </div>
+      </div>
+    </div>
+  </div>
+
+
+  <div class="tab_content" id="tab_data_div" style="display: none;">
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@STATISTICS-BOX@@</span>
+      <div id="statistics_data"></div>
+      <a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">@@DISPLAY-STATISTICS@@</a>
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@VISUALISER-BOX@@</span>
+      @@ROUTER-VISUALISER-INFO@@
+      <br>
+      <a id="visualiser_url" href="visualiser.html" target="visualiser">@@MAP-VIEW-LINK@@</a>
+    </div>
+  </div>
+
+</div>
+
+<!-- Right hand side of window - map -->
+
+<div class="right_panel">
+  <div class="map" id="map">
+    <noscript>
+      <p>
+        @@JAVASCRIPT-REQUIRED@@
+    </noscript>
+  </div>
+  <div class="attribution">
+    @@ROUTER@@: <a href="http://www.routino.org/" target="routino">Routino</a>
+    |
+    @@GEO-DATA@@: <span id="attribution_data"></span>
+    |
+    @@TILES@@: <span id="attribution_tile"></span>
+  </div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/translations/translate.pl b/web/translations/translate.pl
new file mode 100755
index 0000000..0023945
--- /dev/null
+++ b/web/translations/translate.pl
@@ -0,0 +1,327 @@
+#!/usr/bin/perl
+#
+# Routino translation replacement Perl script
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Use the perl encoding/decoding functions
+use Encode qw(decode encode);
+
+use strict;
+
+# Constants
+
+my @translation_files=(<translation.*.txt>);
+my $xml_file="../data/translations.xml";
+my $output_dir="../www/routino";
+
+my @html_template_files=(<*.html>);
+
+my %languages=();
+my %translations=();
+
+
+# Read in the translations (HTML)
+
+foreach my $translation_file (@translation_files)
+  {
+   $translation_file =~ m%translation.([^.]+).txt%;
+
+   # Add to list of languages
+
+   my $language=$1;
+
+   if(! defined $languages{$language})
+     {
+      $languages{$language}=1;
+
+      $translations{$language}={};
+      $translations{$language}->{codes}={};
+      $translations{$language}->{html}=0;
+      $translations{$language}->{xml}=0;
+     }
+
+   # Process the file
+
+   open(FILE,"<$translation_file");
+
+   while(<FILE>)
+     {
+      s%\r*\n%%;
+
+      next if(m%^#%);
+      next if(m%^$%);
+
+      # Single line entries
+
+      if(m%\@\@%)
+        {
+         my($code,$text)=split("\t");
+
+         if(defined $translations{$language}->{codes}->{$code})
+           {
+            print STDERR "Language: $language DUPLICATED codeword '$code'\n";
+           }
+         else
+           {
+            $translations{$language}->{html}++;
+            $translations{$language}->{codes}->{$code}={};
+            $translations{$language}->{codes}->{$code}->{text}=encode('ascii',decode('UTF-8',$text),Encode::FB_HTMLCREF);
+            $translations{$language}->{codes}->{$code}->{used}=0;
+           }
+        }
+
+      # Multi-line entries
+
+      if(m%\$\$([^\$]+)\$\$%)
+        {
+         my($code,$text)=("@@".$1."@@","");
+
+         while(<FILE>)
+           {
+            last if(m%\$\$%);
+
+            $text=$text.$_;
+           }
+
+         $text =~ s%\r*\n$%%;
+
+         if(defined $translations{$language}->{codes}->{$code})
+           {
+            print STDERR "Language: $language DUPLICATED codeword '$code'\n";
+           }
+         else
+           {
+            $translations{$language}->{html}++;
+            $translations{$language}->{codes}->{$code}={};
+            $translations{$language}->{codes}->{$code}->{text}=encode('ascii',decode('UTF-8',$text),Encode::FB_HTMLCREF);
+            $translations{$language}->{codes}->{$code}->{used}=0;
+           }
+        }
+     }
+
+   close(FILE);
+  }
+
+
+# Read in the translations (XML)
+
+open(XML,"<$xml_file");
+
+my $language="";
+
+while(<XML>)
+  {
+   if(m%<language +lang=["']([^"']+)["'] *>%)
+     {
+      # Add to list of languages
+
+      $language=$1;
+
+      if(! defined $languages{$language})
+        {
+         $languages{$language}=1;
+
+         $translations{$language}={};
+         $translations{$language}->{codes}={};
+         $translations{$language}->{html}=0;
+         $translations{$language}->{xml}=0;
+        }
+
+      next;
+     }
+
+   $language="" if(m%</language *>%);
+
+   next if(!$language);
+
+   # Count the phrases
+
+   if(m%<[a-z]+% && m% +(text|string)=["']([^"']+)["']%)
+     {
+      $translations{$language}->{xml}++;
+     }
+  }
+
+close(XML);
+
+
+# Sort out the languages
+
+my @languages=();
+
+push(@languages,"en");
+
+foreach my $language (sort (keys %languages))
+  {
+   push(@languages,$language) if($language ne "en");
+  }
+
+
+# Create the files
+
+foreach my $html_template_file (@html_template_files)
+  {
+   foreach my $language (@languages)
+     {
+      next if(!$translations{$language}->{html});
+
+      print "Language: $language File: $html_template_file\n";
+
+      my $language_meta=0;
+      my $language_meta_string="";
+
+      open(HTML_IN ,"<$html_template_file");
+      open(HTML_OUT,">$output_dir/$html_template_file.$language");
+
+      while(<HTML_IN>)
+        {
+         my $line=$_;
+
+         # Language selection - special handling
+
+         if($line =~ m%\$\$LANGUAGES-META\$\$%)
+           {
+            $language_meta=1-$language_meta;
+
+            if($language_meta==0)
+              {
+               foreach my $language2 (@languages)
+                 {
+                  my $LANGUAGE2=$language2;
+                  $LANGUAGE2 =~ tr%a-z%A-Z%;
+
+                  $line=$language_meta_string;
+
+                  if($language eq $language2)
+                    {
+                     $line =~ s%CHECKED-XX%checked%g;
+                    }
+                  else
+                    {
+                     $line =~ s%CHECKED-XX%%g;
+                    }
+
+                  $line =~ s%xx%$language2%g;
+                  $line =~ s%XX%$LANGUAGE2%g;
+
+                  if(!$translations{$language2}->{html})
+                    {
+                     $line =~ s%<a.+</a>%%;
+                    }
+
+                  if(!$translations{$language2}->{xml})
+                    {
+                     $line =~ s%<input .+>%%;
+                    }
+
+                  foreach my $code (keys $translations{$language}->{codes})
+                    {
+                     if($line =~ s%$code%$translations{$language2}->{codes}->{$code}->{text}%g)
+                       {$translations{$language2}->{codes}->{$code}->{used} = 1;}
+                    }
+
+                  # Remove un-needed spaces
+
+                  $line =~ s%[\t ]+% %g;
+                  $line =~ s%\n %\n%g;
+                  $line =~ s%^ %%g;
+
+                  print HTML_OUT $line;
+                 }
+              }
+
+            next;
+           }
+
+         if($language_meta)
+           {
+            $language_meta_string.=$line;
+            next;
+           }
+
+         # Replace with translated phrases
+
+         foreach my $code (keys $translations{$language}->{codes})
+           {
+            if($line =~ s%$code%$translations{$language}->{codes}->{$code}->{text}%g)
+              {$translations{$language}->{codes}->{$code}->{used} = 1;}
+           }
+
+         # Replace what is left with English phrases
+
+         foreach my $code (keys $translations{$languages[0]}->{codes})
+           {
+            $line =~ s%$code%$translations{$languages[0]}->{codes}->{$code}->{text}%g;
+           }
+
+         if($line =~ m%\@\@%)
+           {
+            print STDERR "   Unmatched codeword in line: $line";
+           }
+
+         # Remove un-needed spaces
+
+         $line =~ s%[\t ]+% %g;
+         $line =~ s%\n %\n%g;
+         $line =~ s%^ %%g;
+
+         print HTML_OUT $line;
+        }
+
+      close(HTML_IN);
+      close(HTML_OUT);
+     }
+  }
+
+
+# Check the languages
+
+foreach my $language (@languages)
+  {
+   foreach my $code (keys $translations{$language}->{codes})
+     {
+      if(! $translations{$language}->{codes}->{$code}->{used})
+        {
+         print STDERR "Language: $language UNUSED codeword: $code\n";
+        }
+     }
+  }
+
+
+# Print the translation coverage
+
+print "\n";
+
+print "Translation Coverage\n";
+print "====================\n";
+
+print "\n";
+print "            Number      Fraction\n";
+print "Language  HTML   XML   HTML   XML\n";
+print "--------  ----   ---   ----   ---\n";
+
+foreach my $language (@languages)
+  {
+   printf("%-6s     %3d   %3d  %4.0f%% %4.0f%%\n",$language,
+                                                  $translations{$language}->{html},
+                                                  $translations{$language}->{xml},
+                                                  100.0*$translations{$language}->{html}/$translations{$languages[0]}->{html},
+                                                  100.0*$translations{$language}->{xml} /$translations{$languages[0]}->{xml});
+  }
diff --git a/web/translations/translation.de.txt b/web/translations/translation.de.txt
new file mode 100644
index 0000000..9f5e9a7
--- /dev/null
+++ b/web/translations/translation.de.txt
@@ -0,0 +1,204 @@
+#
+# German language web-page translation phrases
+#
+
+@@LANGUAGE@@	Deutsche
+@@LANGUAGE-WEBPAGE@@	Deutsche Webseite
+
+#
+# Router (and some shared) translations
+#
+
+@@ROUTER-TITLE@@	Routen Planer für OpenStreetMap Daten
+
+@@OPTION-TAB@@	Optionen
+@@OPTION-TAB-HELP@@	Setze Routing-Optionen
+
+@@RESULTS-TAB@@	Ergebnisse
+@@RESULTS-TAB-HELP@@	Sieh die Ergebnisse
+
+@@DATA-TAB@@	Daten
+@@DATA-TAB-HELP@@	Sieh die Datenbankinformationen
+
+@@ROUTINO-ROUTER@@	Routino OpenStreetMap Router
+@@ROUTINO-WEBSITE@@	Routino Website
+@@DOCUMENTATION@@	Dokumentation
+
+@@LANGUAGE-BOX@@	Sprache
+@@WAYPOINTS-BOX@@	Wegpunkte
+@@TRANSPORT-TYPE-BOX@@	Fortbewegungsart
+@@HIGHWAY-PREFERENCES-BOX@@	Vorgaben zur Wegnutzung
+@@SPEED-LIMITS-BOX@@	Geschwindigkeitsvorgaben
+@@PROPERTY-PREFERENCES-BOX@@	Vorgaben zur Wegbeschaffenheit
+@@OTHER-RESTRICTIONS-BOX@@	andere Vorgaben
+@@FIND-BOX@@	Suche
+@@LINKS-BOX@@	Links
+@@HELP-BOX@@	Hilfe
+
+@@STATUS-BOX@@	Status
+@@SHORTEST-ROUTE@@	kürzester Weg
+@@QUICKEST-ROUTE@@	schnellste Route
+@@STATISTICS-BOX@@	Routino Statistik
+@@VISUALISER-BOX@@	Routino Ansichten
+
+@@WAYPOINT-REVERSE@@	Rückwärts
+@@WAYPOINT-REVERSE-BUTTON@@	Rückwärts
+
+@@TRANSPORT-FOOT@@	Fußgänger
+@@TRANSPORT-HORSE@@	Reiter
+@@TRANSPORT-WHEELCHAIR@@	Rollstuhl
+@@TRANSPORT-BICYCLE@@	Fahrrad
+@@TRANSPORT-MOPED@@	Moped
+@@TRANSPORT-MOTORCYCLE@@	Motorrad
+@@TRANSPORT-MOTORCAR@@	Auto
+@@TRANSPORT-GOODS@@	LKW
+@@TRANSPORT-HGV@@	Schwertransport
+@@TRANSPORT-PSV@@	Personenverkehr
+
+@@HIGHWAY-MOTORWAY@@	Autobahn
+@@HIGHWAY-TRUNK@@	Schnellstraße
+@@HIGHWAY-PRIMARY@@	Bundesstraße
+@@HIGHWAY-SECONDARY@@	Landesstraße
+@@HIGHWAY-TERTIARY@@	Hauptstraße
+@@HIGHWAY-UNCLASSIFIED@@	Straße
+@@HIGHWAY-RESIDENTIAL@@	Wohnstraße
+@@HIGHWAY-SERVICE@@	Zufahrtsweg
+@@HIGHWAY-TRACK@@	Feld-(Wald-)weg
+@@HIGHWAY-CYCLEWAY@@	Fahrradweg
+@@HIGHWAY-PATH@@	Weg
+@@HIGHWAY-STEPS@@	Fußweg
+@@HIGHWAY-FERRY@@	Fähre
+
+@@PROPERTY-PAVED@@	befestigt
+@@PROPERTY-MULTILANE@@	mehrspurig
+@@PROPERTY-BRIDGE@@	Brücken
+@@PROPERTY-TUNNEL@@	Tunnel
+@@PROPERTY-WALKINGROUTE@@	Wanderweg
+@@PROPERTY-BICYCLEROUTE@@	Radweg
+
+@@RESTRICT-ONEWAY@@	beachte Einbahnstraßen
+@@RESTRICT-TURNS@@	beachte Abbiegeverbot
+@@RESTRICT-WEIGHT@@	Gewicht
+@@RESTRICT-HEIGHT@@	Höhe
+@@RESTRICT-WIDTH@@	Breite
+@@RESTRICT-LENGTH@@	Länge
+
+@@MAP-VIEW-LINK@@	anpassen dieser Kartenansicht
+@@EDIT-OSM-DATA@@	Bearbeitet die OSM-Daten
+
+@@ROUTER-NOT-RUN@@	Router läuft nicht
+@@ROUTER-RUNNING@@	Router läuft...
+@@ROUTER-COMPLETED@@	Routing fertig
+@@ROUTER-ERROR@@	Router Fehler
+@@ROUTER-FAILED@@	Router funktioniert nicht
+@@VIEW-DETAILS@@	zeige Details
+
+@@NO-INFORMATION@@	keine Information
+@@HTML-ROUTE@@	HTML
+@@GPX-TRACK-ROUTE@@	GPX Track-Datei
+@@GPX-ROUTE@@	GPX Routen-Datei
+@@FULL-TEXT-ROUTE@@	Volltext-Datei
+@@TEXT-ROUTE@@	Text-Datei
+@@OPEN-POPUP@@	öffne Popup
+
+@@DISPLAY-STATISTICS@@	zeige die Statistik
+
+#
+# Visualiser specific translations
+#
+
+#
+# Multi-line descriptive translations (router)
+#
+
+$$ROUTER-INFO$$
+Diese Website erlaubt Routing mit den Daten, die OpenStreetMap gesammelt hat.
+Wähle Start- und Endpunkt (klicke auf die Marker-Symbole unten), wähle die Routing-Vorgaben und dann finde den Weg.
+$$ROUTER-INFO$$
+
+$$ROUTER-OPTIONS-HELP$$
+<b>Schnellanleitung</b>
+<br>
+Klicke auf die Marker-Bildchen (oben), um sie in der Mitte der Karte (rechts) zu positionieren. Dann 
+ziehe das Bildchen auf die genaue Position. Das Zoomen der Karte vor der Patzierung ist vermutlich am einfachsten.
+Alternativ kann man die geografische Breite und Länge in den Kästchen eintragen.
+<p>
+Wähle die Fortbewegungsart, die Vorgaben zur Wegnutzung, die Geschwindigkeitsvorgaben, 
+die Vorgaben zur Wegbeschaffenheit und die anderen Vorgaben von den obigen Auswahlfeldern.
+Ein Klick auf "kürzeste" oder "schnellste" ermittelt die entsprechende Verbindung und zeigt sie in der Karte an.
+<p>
+<b>Wegpunkte</b>
+<br>
+Ein Klick auf das Marker-Bildchen (oben) schaltet die Sichbarkeit in der Karte ein bzw. aus.
+Die Berechnung Route erfolgt in der Reihenfolge der Wegpunkte (so gut, wie es für die 
+gewählte Fortbewegungsart möglich ist).
+<p>
+<b>Fortbewegungsart</b>
+<br>
+Die Auswahl der Fortbewegungsart bestimmt die bei der Routenberechnung erlaubten Wegtypen und die 
+Vorgabeeinstellungen aller anderen Parameter.
+<p>
+<b>Vorgaben zur Wegnutzung</b>
+<br>
+Die Vorgaben zur Wegnutzung bestimmen die Priorisierung von Wegarten.
+Wenn z. B. Schnellstraßen mit 110% und Bundesstraßen mit 100% angegeben werden, wird 
+bei zwei möglichen Wegwahlen die Schnellstraße solange bevorzugt wird, wie der 
+Längen(oder Zeit-)unterschied 10% nicht überschreitet.
+<p>
+<b>Geschwindigkeitsvorgaben</b>
+<br>
+Die hier geannten Geschwindigkeiten werden für den jeweiligen Wegtyp finden Anwendung wenn keine
+andere Geschwindkeitsbegrenzung mit geringerem Wert bekannt ist.
+<p>
+<b>Vorgaben zur Wegbeschaffenheit</b>
+<br>
+Die Vorgaben zur Wegbeschaffenheit werden als Prozentangaben verwendet, um die Verhältnisse 
+der Wegbenutzung zu steuern.
+Wenn z. B. befestigte Wege mit 75% angegeben sind, werden unbefestigte automatisch mit 25% angenommen, so
+werden Wege ausgewählt, die mindestens drei mal länger auf befestigten Wegen verlaufen.
+<p>
+<b>andere Vorgaben</b>
+<br>
+Die Berücksichtigung von Benutzungs-Begrenzungen durch Gewicht, Höhe, Länge und 
+Breite ist möglich. Genauso können Einbahnstraßenbeschräkungen ignoriert werden 
+(z. B. als Fußgänger). 
+$$ROUTER-OPTIONS-HELP$$
+
+$$ROUTER-RESULTS-HELP$$
+<b>Schnellanleitung</b>
+<br>
+Nach der Routenberechnung kann man eine GPX oder eine einfache Textdatei (Kurz- oder Langfassung)
+herunterladen. Ebenso kann man die Routenbeschreibung ansehen und in ausgewälte Bereiche zoomen.
+<p>
+<b>Problemlösung</b>
+<br>
+Wenn der Router einen Fehler meldet liegt es meistens daran, dass kein Weg zwischen den gewälten Punkten unter
+Beachtung der Vorgaben gefunden werden kann. Das Bewegen eines oder mehrere Punkte oder das verändern von
+Vorgaben sollte es erlauben eine Route zu finden.
+<p>
+<b>Ausgabe-Formate</b>
+<br>
+<dl>
+  <dt>HTMLs
+  <dd>Eine Beschreibung der Route mit Anweisungen für jede wichtige Abzweigung.
+  <dt>GPX Track-Datei
+  <dd>Die gleichen Informationen, die in der Karte angezeigt werden mit Punkten für jeden Abzweig 
+  und Linien für jedes Teilstück.
+  <dt>GPX Routen-Datei
+  <dd>Die gleichen Informationen, die im Text angezeigt werden mit einem Wegpunkt für 
+  jede wichtige Richtungsänderung.
+  <dt>Volltext-Datei
+  <dd>Eine aller Knoten und die Abstände zwischen ihnen, sowie die Gesamtentfernung vom i
+      Startpunkt zum jeweiligen Konten.
+  <dt>Text-Datei
+  <dd>Die gleiche Information, die als Text angezeigt wird.
+</dl>
+$$ROUTER-RESULTS-HELP$$
+
+$$ROUTER-VISUALISER-INFO$$
+Die Anzeige der Daten kann auf verschiedene Weise angepasst werden.	
+$$ROUTER-VISUALISER-INFO$$
+
+#
+# Multi-line descriptive translations (visualiser)
+#
diff --git a/web/translations/translation.en.txt b/web/translations/translation.en.txt
new file mode 100644
index 0000000..a3bcb84
--- /dev/null
+++ b/web/translations/translation.en.txt
@@ -0,0 +1,390 @@
+#
+# English language web-page translation phrases
+#
+
+@@LANGUAGE@@	English
+@@LANGUAGE-WEBPAGE@@	English language webpage
+
+#
+# Router (and some shared) translations
+#
+
+@@ROUTER-TITLE@@	Route Planner for OpenStreetMap Data
+
+@@OPTION-TAB@@	Options
+@@OPTION-TAB-HELP@@	Set routing options
+
+@@RESULTS-TAB@@	Results
+@@RESULTS-TAB-HELP@@	See routing results
+
+@@DATA-TAB@@	Data
+@@DATA-TAB-HELP@@	View database information
+
+@@ROUTINO-ROUTER@@	Routino OpenStreetMap Router
+@@ROUTINO-WEBSITE@@	Routino Website
+@@DOCUMENTATION@@	Documentation
+
+@@LANGUAGE-BOX@@	Language
+@@WAYPOINTS-BOX@@	Waypoints
+@@TRANSPORT-TYPE-BOX@@	Transport Type
+@@HIGHWAY-PREFERENCES-BOX@@	Highway Preferences
+@@SPEED-LIMITS-BOX@@	Speed Limits
+@@PROPERTY-PREFERENCES-BOX@@	Property Preferences
+@@OTHER-RESTRICTIONS-BOX@@	Other Restrictions
+@@FIND-BOX@@	Find
+@@LINKS-BOX@@	Links
+@@HELP-BOX@@	Help
+
+@@STATUS-BOX@@	Status
+@@SHORTEST-ROUTE@@	Shortest Route
+@@QUICKEST-ROUTE@@	Quickest Route
+
+@@STATISTICS-BOX@@	Routino Statistics
+@@VISUALISER-BOX@@	Routino Visualiser
+
+@@WAYPOINT-POSITION@@	Waypoint XXX Position - (click to add/remove on map)
+@@WAYPOINT-LONGITUDE@@	Waypoint XXX Longitude
+@@WAYPOINT-LATITUDE@@	Waypoint XXX Latitude
+@@WAYPOINT-LOCATION@@	Waypoint XXX Location
+@@WAYPOINT-SEARCH@@	Search for location
+@@WAYPOINT-GET@@	Get current location
+@@WAYPOINT-CENTRE1@@	Centre map on this waypoint
+@@WAYPOINT-UP@@	Move this waypoint up
+@@WAYPOINT-ADD@@	Add waypoint after this one
+@@WAYPOINT-COORDS@@	Coordinates for location
+@@WAYPOINT-HOME@@	Toggle as home location
+@@WAYPOINT-CENTRE2@@	Centre this waypoint on map
+@@WAYPOINT-DOWN@@	Move this waypoint down
+@@WAYPOINT-REMOVE@@	Remove this waypoint
+@@WAYPOINT-REVERSE@@	Reverse order of waypoints
+@@WAYPOINT-REVERSE-BUTTON@@	Reverse order
+@@WAYPOINT-LOOP@@	Add a new waypoint to make a loop
+@@WAYPOINT-LOOP-BUTTON@@	Close loop
+
+@@TRANSPORT-FOOT@@	Foot
+@@TRANSPORT-HORSE@@	Horse
+@@TRANSPORT-WHEELCHAIR@@	Wheelchair
+@@TRANSPORT-BICYCLE@@	Bicycle
+@@TRANSPORT-MOPED@@	Moped
+@@TRANSPORT-MOTORCYCLE@@	Motorcycle
+@@TRANSPORT-MOTORCAR@@	Motorcar
+@@TRANSPORT-GOODS@@	Goods
+@@TRANSPORT-HGV@@	HGV
+@@TRANSPORT-PSV@@	PSV
+
+@@HIGHWAY-MOTORWAY@@	Motorway
+@@HIGHWAY-TRUNK@@	Trunk
+@@HIGHWAY-PRIMARY@@	Primary
+@@HIGHWAY-SECONDARY@@	Secondary
+@@HIGHWAY-TERTIARY@@	Tertiary
+@@HIGHWAY-UNCLASSIFIED@@	Unclassified
+@@HIGHWAY-RESIDENTIAL@@	Residential
+@@HIGHWAY-SERVICE@@	Service
+@@HIGHWAY-TRACK@@	Track
+@@HIGHWAY-CYCLEWAY@@	Cycleway
+@@HIGHWAY-PATH@@	Path
+@@HIGHWAY-STEPS@@	Steps
+@@HIGHWAY-FERRY@@	Ferry
+
+@@PROPERTY-PAVED@@	Paved
+@@PROPERTY-MULTILANE@@	Multiple Lanes
+@@PROPERTY-BRIDGE@@	Bridge
+@@PROPERTY-TUNNEL@@	Tunnel
+@@PROPERTY-WALKINGROUTE@@	Walking Route
+@@PROPERTY-BICYCLEROUTE@@	Bicycle Route
+@@PROPERTY-CYCLE-BOTHWAYS@@	Cycle Both Ways
+
+@@RESTRICT-ONEWAY@@	Obey oneway streets
+@@RESTRICT-TURNS@@	Obey turn restrictions
+@@RESTRICT-WEIGHT@@	Weight
+@@RESTRICT-HEIGHT@@	Height
+@@RESTRICT-WIDTH@@	Width
+@@RESTRICT-LENGTH@@	Length
+
+@@FIND-SHORTEST-ROUTE@@	Find shortest route
+@@FIND-QUICKEST-ROUTE@@	Find quickest route
+
+@@MAP-VIEW-LINK@@	Link to this map view
+@@EDIT-OSM-DATA@@	Edit this OSM data
+
+@@ROUTER-NOT-RUN@@	Router not run
+@@ROUTER-RUNNING@@	Router running...
+@@ROUTER-COMPLETED@@	Routing completed
+@@ROUTER-ERROR@@	Router error
+@@ROUTER-FAILED@@	Router failed to run
+@@VIEW-DETAILS@@	View Details
+
+@@NO-INFORMATION@@	No Information
+@@HTML-ROUTE@@	HTML directions
+@@GPX-TRACK-ROUTE@@	GPX track file
+@@GPX-ROUTE@@	GPX route file
+@@FULL-TEXT-ROUTE@@	Full text file
+@@TEXT-ROUTE@@	Text file
+@@OPEN-POPUP@@	Open Popup
+
+@@DISPLAY-STATISTICS@@	Display data statistics
+
+@@JAVASCRIPT-REQUIRED@@	Javascript is <em>required</em> to use this web page because of the interactive map.
+@@ROUTER@@	Router
+@@GEO-DATA@@	Geo Data
+@@TILES@@	Tiles
+
+#
+# Visualiser specific translations
+#
+
+@@VISUALISER-TITLE@@	Data Visualiser for Routing Data
+
+@@INSTRUCTIONS-BOX@@	Instructions
+@@ROUTER-BOX@@	Routino Router
+
+@@NO-DATA-DISPLAYED@@	No data displayed
+
+@@VISUALISER-FAILED@@	Failed to get visualiser data!
+@@VISUALISER-NUM-JUNCTIONS@@	Processed # junctions
+@@VISUALISER-NUM-SUPER@@	Processed # super-nodes/segments
+@@VISUALISER-NUM-ONEWAY@@	Processed # oneway segments
+@@VISUALISER-NUM-SEGMENTS@@	Processed # segments
+@@VISUALISER-NUM-NODES@@	Processed # nodes
+@@VISUALISER-NUM-TURNS@@	Processed # turn restrictions
+@@VISUALISER-NUM-LIMITS@@	Processed # limit changes
+@@VISUALISER-NUM-ERRORS@@	Processed # error logs
+
+@@JUNCTIONS-BUTTON@@	Display Junctions
+$$JUNCTIONS-INFO$$
+Each node that is a dead-end, a junction of two highways of different
+types (or different properties) or a junction where more than two segments
+join are shown colour-coded:
+$$JUNCTIONS-INFO$$
+@@JUNCTIONS-1@@	only one highway - a dead-end.
+@@JUNCTIONS-2@@	two highways of different types meet.
+@@JUNCTIONS-3@@	three highways meet.
+@@JUNCTIONS-4@@	four highways meet.
+@@JUNCTIONS-5@@	five highways meet.
+@@JUNCTIONS-6@@	six highways meet.
+@@JUNCTIONS-MORE@@	seven (or more) highways meet.
+
+@@SUPER-BUTTON@@	Display Super Segments
+$$SUPER-INFO$$
+Each super-node and the associated super-segments are shown (see
+algorithm page for description).
+$$SUPER-INFO$$
+
+@@ONEWAY-BUTTON@@	Display One-way Segments
+$$ONEWAY-INFO$$
+Each one-way segment is shown with a coloured triangle indicating the
+allowed direction.  The colours of the triangles depend on the bearing
+of the highway segment.
+$$ONEWAY-INFO$$
+
+@@HIGHWAY-BUTTON@@	Display Highway Segments
+$$HIGHWAY-INFO$$
+Each segment of the chosen type of highway is drawn.
+$$HIGHWAY-INFO$$
+
+@@TRANSPORT-BUTTON@@	Display Transport Segments
+$$TRANSPORT-INFO$$
+Each segment allowed for the chosen type of transport is drawn.
+$$TRANSPORT-INFO$$
+
+@@BARRIER-BUTTON@@	Display Barrier Nodes
+$$BARRIER-INFO$$
+Each barrier blocking the chosen type of transport is drawn.
+$$BARRIER-INFO$$
+
+@@TURNS-BUTTON@@	Display Turn Restrictions
+$$TURNS-INFO$$
+Each turn restrictions is shown with a line indicating the disallowed turn.
+$$TURNS-INFO$$
+
+@@SPEED-BUTTON@@	Display Speed Limits
+$$SPEED-INFO$$
+Each node that joins segments with different speed limits is shown
+along with the speed limit on relevant segments.
+$$SPEED-INFO$$
+
+@@LIMIT-CHANGE@@	Change of limit
+@@LIMIT-NONE@@	No specified limit
+@@SPEED-LIMIT-80@@	80 km/hour speed limit
+
+@@WEIGHT-BUTTON@@	Display Weight Limits
+$$WEIGHT-INFO$$
+Each node that joins segments with different weight limits is shown
+along with the weight limit on relevant segments.
+$$WEIGHT-INFO$$
+
+@@WEIGHT-LIMIT-8@@	8.0 tonnes weight limit
+
+@@HEIGHT-BUTTON@@	Display Height Limits
+$$HEIGHT-INFO$$
+Each node that joins segments with different height limits is shown
+along with the height limit on relevant segments.
+$$HEIGHT-INFO$$
+
+@@HEIGHT-LIMIT-4@@	4.0 m height limit
+
+@@WIDTH-BUTTON@@	Display Width Limits
+$$WIDTH-INFO$$
+Each node that joins segments with different width limits is shown
+along with the width limit on relevant segments.
+$$WIDTH-INFO$$
+
+@@WIDTH-LIMIT-3@@	3.0 m width limit
+
+@@LENGTH-BUTTON@@	Display Length Limits
+$$LENGTH-INFO$$
+Each node that joins segments with different length limits is shown
+along with the length limit on relevant segments.
+$$LENGTH-INFO$$
+
+@@LENGTH-LIMIT-9@@	9.0 m length limit
+
+@@PROPERTY-BUTTON@@	Display Highway Properties
+$$PROPERTY-INFO$$
+Each segment of the highways with a particular property is drawn.
+$$PROPERTY-INFO$$
+
+@@ERROR-LOG-BUTTON@@	Display Error Logs
+$$ERROR-LOG-INFO$$
+Potential problems found by Routino when processing the input data.
+$$ERROR-LOG-INFO$$
+
+@@CLEAR-DATA-BUTTON@@	Clear data
+
+#
+# Multi-line descriptive translations (router)
+#
+
+$$ROUTER-INFO$$
+This web page allows routing within the data collected by OpenStreetMap.
+Select start and end points (click on the marker icons below), select routing preferences then find a route.
+$$ROUTER-INFO$$
+
+$$ROUTER-OPTIONS-HELP$$
+<b>Quick Start</b>
+<br>
+Click on marker icons (above) to place them on the map (right).  Then
+drag them to the correct position.  Zooming the map before placing the
+markers is probably easiest.  Alternatively type the latitude and
+longitude into the boxes above.
+<p>
+Select the transport type, allowed highway types, speed limits, highway
+properties and other restrictions from the options above.
+Select "Shortest" or "Quickest" to calculate the route and display it
+on the map.
+<p>
+<b>Waypoints</b>
+<br>
+Clicking on the marker icons will toggle the display of them on the map.
+When a route is calculated it will visit (as close as possible
+for the selected transport type) each of the waypoints that have
+markers on the map in the order given.
+<p>
+<b>Transport Type</b>
+<br>
+Selecting a transport type will restrict the chosen route to
+those on which it is allowed and set default values for the
+other parameters.
+<p>
+<b>Highway Preferences</b>
+<br>
+The highway preference is selected as a percentage and routes are chosen that
+try to follow the preferred highways.
+For example if a "Primary" road is given a "110%" preference and a "Secondary"
+road is given a "100%" preference then it means that a route on a Primary road
+can be up to 10% longer than on a secondary road and still be selected.
+<p>
+<b>Speed Limits</b>
+<br>
+The speed limits chosen here for the different types of highway apply if the
+highway has no other speed limit marked or it is higher than the chosen one.
+<p>
+<b>Property Preferences</b>
+<br>
+The property preference is selected as a percentage and routes are chosen that
+try to follow highways with the preferred property.
+For example if a "Paved" highway is given a "75%" preference then it means that
+an unpaved highway is automatically given a "25%" preference so that a route on
+a paved highway can be 3 times the length of an unpaved one and still be
+selected.
+<p>
+<b>Other Restrictions</b>
+<br>
+These allow a route to be found that avoids marked limits on
+weight, height, width or length.  It is also possible to ignore
+one-way restrictions (e.g. if walking).
+$$ROUTER-OPTIONS-HELP$$
+
+$$ROUTER-RESULTS-HELP$$
+<b>Quick Start</b>
+<br>
+After calculating a route you can download the GPX file or plain
+text route description (summary or detailed version). Also you
+can view the route description and zoom in to selected parts.
+<p>
+<b>Problem Solving</b>
+<br>
+If the router completes with an error then the most likely cause is
+that it is not possible to find a route between the selected points.
+Moving one or more markers or changing the routing options should
+allow a route to be found.
+<p>
+<b>Output Formats</b>
+<br>
+<dl>
+  <dt>HTML instructions
+  <dd>A description of the route to take with directions at each
+    important junction.
+  <dt>GPX track file
+  <dd>The same information that is displayed on the map with points
+    for every node and lines for every segment.
+  <dt>GPX route file
+  <dd>The same information that is displayed in text for the route
+    with a waypoint for each important junction in the route.
+  <dt>Full text file
+  <dd>A list of all of the nodes visited as well as the distance
+    between them and the cumulative distance for each step of the
+    route.
+  <dt>Text file
+  <dd>The same information that is displayed in text for the route.
+</dl>
+$$ROUTER-RESULTS-HELP$$
+
+$$ROUTER-VISUALISER-INFO$$
+To see Routino's view of the data there is a data visualiser that allows
+displaying of the underlying data in various ways.
+$$ROUTER-VISUALISER-INFO$$
+
+#
+# Multi-line descriptive translations (visualiser)
+#
+
+$$VISUALISER-INFO$$
+This web page allows visualisation of the data that Routino uses for routing.
+Only data relevant for routing is displayed and some will therefore be excluded.
+$$VISUALISER-INFO$$
+
+$$VISUALISER-INSTRUCTIONS$$
+Zoom in and then use the buttons below to download the data.  The
+server will only return data if the selected area is small enough.
+$$VISUALISER-INSTRUCTIONS$$
+
+$$VISUALISER-HELP$$
+<b>Quick Start</b>
+<br>
+Zoom to an area and select one of the buttons to display that type of
+data.
+<br>
+More data options can be found by expanding the details below each
+button.
+<p>
+<b>Data Failure</b>
+<br>
+If the area selected is too large (depends on the data type) then the
+status will say "Failed to get visualiser data" - zoom in and try
+again.
+$$VISUALISER-HELP$$
+
+$$VISUALISER-ROUTER-INFO$$
+To perform routing on the map use the link below.
+$$VISUALISER-ROUTER-INFO$$
diff --git a/web/translations/translation.fr.txt b/web/translations/translation.fr.txt
new file mode 100644
index 0000000..f19e530
--- /dev/null
+++ b/web/translations/translation.fr.txt
@@ -0,0 +1,240 @@
+#
+# French language web-page translation phrases
+#
+
+@@LANGUAGE@@	Francais
+@@LANGUAGE-WEBPAGE@@	Francais
+
+#
+# Router (and some shared) translations
+#
+
+@@ROUTER-TITLE@@	Calculateur d'itinéraire pour OpenStreetMap
+
+@@OPTION-TAB@@	Options
+@@OPTION-TAB-HELP@@	définir les options
+
+@@RESULTS-TAB@@	Résultats
+@@RESULTS-TAB-HELP@@	Voir les resultats
+
+@@DATA-TAB@@	Données
+@@DATA-TAB-HELP@@	Voir les informations de la base de donnée
+
+@@ROUTINO-ROUTER@@	Itinéraires pour Openstreetmap Routino
+@@ROUTINO-WEBSITE@@	site web Routino
+
+@@DOCUMENTATION@@	Documentation
+
+@@LANGUAGE-BOX@@	Langue
+@@WAYPOINTS-BOX@@	Etapes de l'itinéraire
+@@TRANSPORT-TYPE-BOX@@	Mode de déplacement
+@@HIGHWAY-PREFERENCES-BOX@@	Préférences routières
+@@SPEED-LIMITS-BOX@@	Limitations de vitesse
+@@PROPERTY-PREFERENCES-BOX@@	Préférences des propriétés
+@@OTHER-RESTRICTIONS-BOX@@	Autres Restrictions
+@@FIND-BOX@@	Rechercher
+@@LINKS-BOX@@	Liens
+@@HELP-BOX@@	Aide
+
+@@SHORTEST-ROUTE@@	Le plus court
+@@QUICKEST-ROUTE@@	Le plus rapide
+
+@@STATISTICS-BOX@@	Routino Statistiques
+@@VISUALISER-BOX@@	Routino Visualiser
+
+@@WAYPOINT-POSITION@@	Etape XXX de l'itinéraire - (cliquer pour ajouter/enlever de la carte)
+@@WAYPOINT-LONGITUDE@@	Etape XXX Longitude
+@@WAYPOINT-LATITUDE@@	Etape XXX Latitude
+@@WAYPOINT-LOCATION@@	position de l'étape XXX
+@@WAYPOINT-SEARCH@@	Rechercher la position
+@@WAYPOINT-GET@@	obtenir la position actuelle
+@@WAYPOINT-CENTRE1@@	Centrer la carte sur cette étape
+@@WAYPOINT-UP@@	Placer cette étape avant
+@@WAYPOINT-ADD@@	Ajouter une étape après celle-ci
+@@WAYPOINT-COORDS@@	Coordonnées de position
+@@WAYPOINT-HOME@@	Changer en position de départ
+@@WAYPOINT-CENTRE2@@	Centrer cette étape sur la carte
+@@WAYPOINT-DOWN@@	Placer cette étape après
+@@WAYPOINT-REMOVE@@	supprimer cette étape
+@@WAYPOINT-REVERSE-BUTTON@@	Inverser l'ordre
+@@WAYPOINT-REVERSE@@	Inverser l'ordre des étapes
+@@WAYPOINT-LOOP-BUTTON@@	Faire une boucle
+@@WAYPOINT-LOOP@@	Ajouter une nouvelle étape pour faire une boucle
+
+@@TRANSPORT-FOOT@@	À pied
+@@TRANSPORT-HORSE@@	À cheval
+@@TRANSPORT-WHEELCHAIR@@	Fauteuil roulant
+@@TRANSPORT-BICYCLE@@	Bicyclette
+@@TRANSPORT-MOPED@@	Mobilette
+@@TRANSPORT-MOTORCYCLE@@	Moto
+@@TRANSPORT-MOTORCAR@@	Voiture
+@@TRANSPORT-GOODS@@	Camionette
+@@TRANSPORT-HGV@@	Camion(15t)
+@@TRANSPORT-PSV@@	Camion(10t)
+
+@@HIGHWAY-MOTORWAY@@	Autoroute
+@@HIGHWAY-TRUNK@@	Trunk
+@@HIGHWAY-PRIMARY@@	Primaire
+@@HIGHWAY-SECONDARY@@	Secondaire
+@@HIGHWAY-TERTIARY@@	Tertiaire
+@@HIGHWAY-UNCLASSIFIED@@	Non classée
+@@HIGHWAY-RESIDENTIAL@@	Résidentiel
+@@HIGHWAY-SERVICE@@	Service
+@@HIGHWAY-TRACK@@	Chemin
+@@HIGHWAY-CYCLEWAY@@	Voie cyclable
+@@HIGHWAY-PATH@@	Sentier
+@@HIGHWAY-STEPS@@	Escaliers
+@@HIGHWAY-FERRY@@	Ferry
+
+@@PROPERTY-PAVED@@	Pavée
+@@PROPERTY-MULTILANE@@	Voies multiples
+@@PROPERTY-BRIDGE@@	Pont
+@@PROPERTY-TUNNEL@@	Tunnel
+@@PROPERTY-WALKINGROUTE@@	Itinér. piéton
+@@PROPERTY-BICYCLEROUTE@@	Itinér. cycle
+
+@@RESTRICT-ONEWAY@@	Respecter les sens uniques
+@@RESTRICT-TURNS@@	Respecter les obligations de tourner
+@@RESTRICT-WEIGHT@@	Poids
+@@RESTRICT-HEIGHT@@	Hauteur
+@@RESTRICT-WIDTH@@	Largeur
+@@RESTRICT-LENGTH@@	Longueur
+
+@@FIND-SHORTEST-ROUTE@@	Chercher l'itinéraire le plus court
+@@FIND-QUICKEST-ROUTE@@	Chercher l'itinéraire le plus rapide
+
+@@MAP-VIEW-LINK@@	Lien vers cet outil de visualisation
+@@EDIT-OSM-DATA@@	Editer cette donnée OSM
+
+@@ROUTER-NOT-RUN@@	Routage non lancé
+@@ROUTER-RUNNING@@	Routage en cours...
+@@ROUTER-COMPLETED@@	Routage terminé
+@@ROUTER-ERROR@@	Erreur de Routage
+@@ROUTER-FAILED@@	Le Routage n'a pas été lancé correctement
+@@VIEW-DETAILS@@	Voir les Détails
+
+@@NO-INFORMATION@@	Pas d'information
+@@HTML-ROUTE@@	Itinéraire HTML
+@@GPX-TRACK-ROUTE@@	Fichier chemin GPX
+@@GPX-ROUTE@@	Fichier route GPX
+@@FULL-TEXT-ROUTE@@	Fichier texte complet
+@@TEXT-ROUTE@@	Fichier texte
+@@OPEN-POPUP@@	Ouvrir Popup
+
+@@DISPLAY-STATISTICS@@	Afficher les données statistiques
+
+@@JAVASCRIPT-REQUIRED@@	Javascript est <em>nécessaire</em> pour cette page web à cause de la carte intéractive.
+@@ROUTER@@	Routeur
+@@GEO-DATA@@	Geo Data
+@@TILES@@	Tuiles
+
+#
+# Visualiser specific translations
+#
+
+#
+# Multi-line descriptive translations (router)
+#
+
+$$ROUTER-INFO$$
+Cette page web permet de calculer des itinéraires à l'aide des données collectées par OpenStreetMap.
+Sélectionner les points de départ et d'arrivée (cliquer sur les icones ci-dessous), sélectionner les préférences, puis rechercher un itinéraire.
+$$ROUTER-INFO$$
+
+$$ROUTER-OPTIONS-HELP$$
+<b>Aide simplifiée</b>
+<br>
+Cliquer sur les icones de balises (ci-dessus) pour les placer sur la carte (droite).  Puis
+les déplacer à la position choisie. Il sera sûrement plus facile de zoomer sur la carte 
+avant de placer les balises.  Autre solution, taper la latitude et
+la longitude dans les cases ci-dessus.
+<p>
+Selectionner le mode de déplacement, les types de voies autorisées, les limitations de vitesse, 
+les propriétés des voies et les autres restrictions dans les options ci-dessus.
+Selectionner "Le plus court" ou "Le plus rapide" pour calculer l'itinéraire et le visualiser
+sur la carte.
+<p>
+<b>Etapes</b>
+<br>
+Cliquer sur les balises affichera ou supprimera leur apparition sur la carte.
+Quand un itinéraire est calculé, il affichera (le plus près possible
+pour le mode de déplacement sélectionné) chacune des étapes qui ont une
+balise sur la carte dans l'ordre défini.
+<p>
+<b>Mode de déplacement</b>
+<br>
+Selectionner un mode de déplacement restreindra l'itinéraire choisi aux
+voies sur lesquelles il est autorisé et définira les valeurs par défaut pour
+les autres paramètres.
+<p>
+<b>Préferences des voies</b>
+<br>
+La préférence de voies est définie par un pourcentage et des itinéraires sont choisis 
+qui essaient de suivre les voies préferrées.
+Par exemple, si une voie "Primaire" a une préférence de "110%" et une voie "Secondaire"
+une préférence de "100%", alors cela signifie qu'un itinéraire sur une voie primaire
+peut être jusqu'à 10% plus long que sur une voie secondaire et être sélectionné.
+<p>
+<b>Limites de vitesse</b>
+<br>
+Les limites de vitesse choisies ici pour les differents types de voies s'appliquent si la
+voie n'a pas d'autre limite de vitesse définie ou si celle-ci est supérieure à celle choisie.
+<p>
+<b>Préférences de propriétés</b>
+<br>
+La préférence de propriété est définie par un pourcentage et des itinéraires sont choisis
+qui essaient de suivre les voies ayant cette propriété préférée.
+Par exemple, si une voie goudronnée a une préférence de "75%", alors cela signifie que
+une voie non goudronnée obtient automatiquement une préférence de "25%" ce qui fait que 
+un itinéraire sur une voie goudronnée peut avoir 3 fois la longueur d'une non goudronnée 
+et être sélectionnée.
+<p>
+<b>Autres restrictions</b>
+<br>
+Celles-ci permettent de touver un itinéraire qui respecte les limites définies pour
+le poids, la hauteur, la largeur ou la longueur.  Il est également possible d'ignorer
+les restrictions de sens unique (e. pour la marche).
+$$ROUTER-OPTIONS-HELP$$
+
+$$ROUTER-RESULTS-HELP$$
+<b>Aide rapide</b>
+<br>
+Après le calcul de l'itinéraire, vous pouvez télécharger le fichier GPX ou
+la description au format texte (résumé ou version détaillée). Vous pouvez également
+visualiser la description de l'itinéraire et zoomer sur des tronçons sélectionnés.
+<p>
+<b>Résoudre un problème</b>
+<br>
+Si le calculateur aboutie à une erreur, la cause la plus probable est que
+il n'est pas possible de trouver un itinéraire entre les points sélectionnés.
+Por permettre de trouver un itinéraire, déplacer une ou des balises 
+ou changer les options de recherche.
+<p>
+<b>Formats d'affichage</b>
+<br>
+<dl>
+  <dt>Instructions HTML
+  <dd>une description de l'itinéraire à prendre 
+    à chaque intersection importante.
+  <dt>Fichier chemin GPX
+  <dd>La même information qui est affichée sur la carte avec des points
+    pour chaque noeud et des lignes pour tous les sègments.
+  <dt>Fichier route GPX
+  <dd>La même information qui est affichée en texte pour l'itinéraire
+    avec une étape pour chaque intersection importante.
+  <dt>Fichier texte complet
+  <dd>Une liste de tous les noeuds traversés ainsi que la distance 
+    entre eux et la distance cumulée pour chaque étape de l'itinéraire.
+  <dt>Fichier texte
+  <dd>La même information qui est affichée en texte pour l'itinéraire.
+</dl>
+$$ROUTER-RESULTS-HELP$$
+
+$$ROUTER-VISUALISER-INFO$$
+Pour comprendre comment Routino voit les données, il y a un outil de visualisation
+qui permet d'afficher les données soujacentes de multiples manières.
+$$ROUTER-VISUALISER-INFO$$
+
+#
+# Multi-line descriptive translations (visualiser)
+#
diff --git a/web/translations/translation.nl.txt b/web/translations/translation.nl.txt
new file mode 100644
index 0000000..bf9e70e
--- /dev/null
+++ b/web/translations/translation.nl.txt
@@ -0,0 +1,206 @@
+#
+# Dutch language web-page translation phrases
+#
+
+@@LANGUAGE@@	Nederlands
+@@LANGUAGE-WEBPAGE@@	Nederlandse web pagina
+
+#
+# Router (and some shared) translations
+#
+
+@@OPTION-TAB@@	Opties
+
+@@RESULTS-TAB@@	Resultaten
+
+@@DATA-TAB@@	Data
+
+@@LANGUAGE-BOX@@	Taal
+@@WAYPOINTS-BOX@@	Coordinaten
+@@TRANSPORT-TYPE-BOX@@	Transport Type
+@@HIGHWAY-PREFERENCES-BOX@@	Voorkeur Wegtype
+@@SPEED-LIMITS-BOX@@	Snelheidslimieten
+@@PROPERTY-PREFERENCES-BOX@@	Weg Eigenschappen
+@@OTHER-RESTRICTIONS-BOX@@	Andere Beperkingen
+@@FIND-BOX@@	Zoek Route
+@@LINKS-BOX@@	Links
+@@HELP-BOX@@	Help
+
+@@STATUS-BOX@@	Status
+@@SHORTEST-ROUTE@@	Kortste Route
+@@QUICKEST-ROUTE@@	Snelste Route
+
+@@WAYPOINT-POSITION@@	Waypoint XXX Position - (click voor plaatsen/verwijderen op map)
+@@WAYPOINT-UP@@	Beweeg dit punt naar boven
+@@WAYPOINT-ADD@@	Voeg hierna punt toe
+@@WAYPOINT-HOME@@	Toggle als thuis locatie
+@@WAYPOINT-CENTRE2@@	Centreer dit punt op map
+@@WAYPOINT-DOWN@@	Beweeg dit punt naar beneden
+@@WAYPOINT-REMOVE@@	Verwijder dit punt
+@@WAYPOINT-REVERSE@@	Keer volgorde punten om
+@@WAYPOINT-REVERSE-BUTTON@@	Keer volgorde punten om
+
+@@TRANSPORT-FOOT@@	Te voet
+@@TRANSPORT-HORSE@@	Paard
+@@TRANSPORT-WHEELCHAIR@@	Rolstoel
+@@TRANSPORT-BICYCLE@@	Fiets
+@@TRANSPORT-MOPED@@	Brommer
+@@TRANSPORT-MOTORCYCLE@@	Motorfiets
+@@TRANSPORT-MOTORCAR@@	Auto
+@@TRANSPORT-GOODS@@	Goederen
+@@TRANSPORT-HGV@@	Zwaar transport
+@@TRANSPORT-PSV@@	Publiek transport
+
+@@HIGHWAY-MOTORWAY@@	Autostrade
+@@HIGHWAY-TRUNK@@	Autoweg
+@@HIGHWAY-PRIMARY@@	Provinciale wegen
+@@HIGHWAY-SECONDARY@@	Nationale wegen
+@@HIGHWAY-TERTIARY@@	Doorgangsweg
+@@HIGHWAY-UNCLASSIFIED@@	Niet geclassificeerd
+@@HIGHWAY-RESIDENTIAL@@	Woongebied
+@@HIGHWAY-SERVICE@@	Toegangsweg
+@@HIGHWAY-TRACK@@	Veldweg
+@@HIGHWAY-CYCLEWAY@@	Fietspad
+@@HIGHWAY-PATH@@	Pad
+@@HIGHWAY-STEPS@@	Trap
+@@HIGHWAY-FERRY@@	Ferry
+
+@@PROPERTY-PAVED@@	Verhard
+@@PROPERTY-MULTILANE@@	Meerdere Stroken
+@@PROPERTY-BRIDGE@@	Brug
+@@PROPERTY-TUNNEL@@	Tunnel
+
+@@RESTRICT-ONEWAY@@	Volg Eenrichtingsverkeer
+@@RESTRICT-WEIGHT@@	Gewicht
+@@RESTRICT-HEIGHT@@	Hoogte
+@@RESTRICT-WIDTH@@	Breedte
+@@RESTRICT-LENGTH@@	Lengte
+
+@@FIND-SHORTEST-ROUTE@@	Zoek de kortste route
+@@FIND-QUICKEST-ROUTE@@	Zoek de snelste route
+
+@@EDIT-OSM-DATA@@	Lees hoe je OSM data kan inbrengen
+
+@@ROUTER-NOT-RUN@@	Router niet in gebruik
+@@ROUTER-RUNNING@@	Router werkt...
+@@ROUTER-COMPLETED@@	Routing voltooid
+@@ROUTER-ERROR@@	Router error
+@@ROUTER-FAILED@@	Router werkt niet
+
+@@HTML-ROUTE@@	HTML directions
+@@GPX-TRACK-ROUTE@@	GPX track bestand
+@@GPX-ROUTE@@	GPX route bestand
+@@FULL-TEXT-ROUTE@@	Full text bestand
+@@TEXT-ROUTE@@	Text bestand
+
+#
+# Visualiser specific translations
+#
+
+#
+# Multi-line descriptive translations (router)
+#
+
+$$ROUTER-INFO$$
+Zoom naar straatniveau.
+Selecteer start- and eindpunten onder Coordinaten. (click op het marker
+icoon links, schuif het op map naar gewenste positie).
+$$ROUTER-INFO$$
+
+$$ROUTER-OPTIONS-HELP$$
+<b>Quick Start</b>
+<br>
+Click op marker-icoontje (Waypoints) om ze op de map te plaatsen (rechts).
+Sleep ze vervolgens naar de gewenste positie. 
+Het is best om eerst naar straat niveau te zoomen op de kaart. 
+Selecteer het transport type, toegestane weg-types,
+snelheidslimieten, wegeigenschappen en andere restricties uit de
+opties.
+Selecteer  "Kortste" of "Snelste" om de route te berekenen en te tekenen op de map. 
+<p>
+<b>Coordinaten (Waypoints)</b>
+<br>
+Click op het marker icoontje, nog eens clicken voor aan/uit.
+Wanneer de route berekend wordt, zal dit nauwkeurig aansluiten bij de volgorde van deze punten. (rekening houdend met transport type)
+<p>
+<b>Transport Type</b>
+<br>
+Wanneer je een bepaald transport type kiest wordt bij berekenen
+route hiermede rekening gehouden.
+Het transport type bestaat uit een lijst met default waarden voor
+ieder wegtype.
+Deze percentages kunnen ook nog eens manueel aangepast worden.
+<p>
+<b>Voorkeur Wegtype</b>
+<br>
+De voorkeur voor een bepaald type weg wordt uitgedrukt in een percentage.  
+Bijvoorbeeld wanneer u het Transport Type "Fiets" kiest, dan zal er
+voor Autostrade 0% staan, en voor Fietspad 100%.
+Wanneer u Autowegen, Nationale wegen wil vermijden of beperken bij
+het maken van een fietsroute, kan u percentage naar beneden
+aanpassen.
+<p>
+<b>Snelheid limieten</b>
+<br>
+De snelheidslimieten worden afgeleid van het type weg. Het is
+mogelijk dat er voor een bepaalde weg andere beperkingen gelden. In
+dat geval worden die gekoezen. (het geval dat deze lager zijn dan de
+default)
+<p>
+<b>Weg Eigenschappen</b>
+<br>
+Voor het berekenen van de route, kan de de voorkeur gegeven worden
+aan een bepaalde wegeigenschap.
+Wanneer u kiest voor 25% verhard, zal er automatisch de voorkeur aan
+75% onverhard worden gegeven.
+Ook al is het onverharde stuk 3 X langer, toch kan er dan de
+voorkeur aan gegeven worden.
+<p>
+<b>Andere Beperkingen</b>
+<br>
+Deze zullen toelaten dat er een route berekend wordt die rekening
+houdt met gewicht, hoogte, breedte of lengte.
+Het is ook mogelijk geen rekening te houden met eenrichtingsverkeer
+(bijvoorbeeld als voetganger of fietser)
+$$ROUTER-OPTIONS-HELP$$
+
+$$ROUTER-RESULTS-HELP$$
+<b>Quick Start</b>
+<br>
+Na het berekenen van een route, kan het GPX bestand, of de beschrijving als tekstbestand downloaden.
+Door met muis over de beschrijving te bewegen, ziet u die ook op de kaart gesitueerd.
+<p>
+<b>Problem Solving</b>
+<br>
+Als de router eindigt met een fout, dan is de meest waarschijnlijke
+oorzaak, dat er geen route mogelijk is tussen de gekozen punten.
+Het verplaatsen van de punten, of het aanpassen van weg-eigenschappen
+of voertuigtype kan een oplossing bieden.
+<p>
+<b>Output Formats</b>
+<br>
+<dl>
+  <dt>HTML instructies
+  <dd>Een beschrijving van de route, met de te nemen afslag aan iedere splitsing.
+  <dt>GPX track bestand
+  <dd>Dezelfde informatie die op de kaart wordt weergegeven. Met
+    coordinaten voor ieder knooppunt, en een track voor ieder segment.
+  <dt>GPX route bestand
+  <dd>Dezelfde informatie dat is opgenomen in de tekst van de route,
+    met een coordinaat voor iedere belangrijke splitsing.
+  <dt>Full text bestand
+  <dd>Een lijst met alle coordinaten, met de afstand hier tussen. En
+    een cumulatieve afstand voor iedere stap op de route.
+  <dt>Text bestand
+  <dd>Dezelfde informatie als wordt weergegeven in de tekst voor de route.
+</dl>
+$$ROUTER-RESULTS-HELP$$
+
+$$ROUTER-VISUALISER-INFO$$
+Om te kijken hoe Routino omgaat met de basisdata,
+is er een tooltje dat de onderliggende data toont op verschillende manieren.
+$$ROUTER-VISUALISER-INFO$$
+
+#
+# Multi-line descriptive translations (visualiser)
+#
diff --git a/web/translations/visualiser.html b/web/translations/visualiser.html
new file mode 100644
index 0000000..299973b
--- /dev/null
+++ b/web/translations/visualiser.html
@@ -0,0 +1,424 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routino verifier">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
+
+<title>Routino : @@VISUALISER-TITLE@@</title>
+
+<!--
+ Routino data visualiser web page.
+
+ Part of the Routino routing software.
+
+ This file Copyright 2008-2014 Andrew M. Bishop
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see http://www.gnu.org/licenses/.
+-->
+
+<!-- Page elements -->
+<script src="page-elements.js" type="text/javascript"></script>
+<link href="page-elements.css" type="text/css" rel="stylesheet">
+
+<!-- Router and visualiser shared features -->
+<link href="maplayout.css" type="text/css" rel="stylesheet">
+
+<!-- Visualiser specific features -->
+<script src="mapprops.js" type="text/javascript"></script>
+<link href="visualiser.css" type="text/css" rel="stylesheet">
+
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('map_init();');">
+
+<!-- Left hand side of window - data panel -->
+
+<div class="left_panel">
+
+  <div class="tab_box">
+    <span id="tab_visualiser" onclick="tab_select('visualiser');" class="tab_selected"   title="Select data options">Visualiser</span>
+    <span id="tab_router"     onclick="tab_select('router');"     class="tab_unselected" title="Plan a route">Router</span>
+    <span id="tab_data"       onclick="tab_select('data');"       class="tab_unselected" title="View database information">Data</span>
+  </div>
+
+  <div class="tab_content" id="tab_visualiser_div">
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@VISUALISER-BOX@@</span>
+      @@VISUALISER-INFO@@
+      <div class="center">
+        <a target="other" href="http://www.routino.org/">@@ROUTINO-WEBSITE@@</a>
+        |
+        <a target="other" href="documentation/">@@DOCUMENTATION@@</a>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+      <span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+      <span class="hideshow_title">@@LANGUAGE-BOX@@</span>
+
+      <div id="hideshow_language_div" style="display: none;">
+        <table>
+          $$LANGUAGES-META$$
+          <tr>
+            <td><a id="lang_xx_url" href="visualiser.html.xx" title="@@LANGUAGE-WEBPAGE@@">@@LANGUAGE@@</a>
+            <td>(XX)
+          $$LANGUAGES-META$$
+        </table>
+        <a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@INSTRUCTIONS-BOX@@</span>
+      @@VISUALISER-INSTRUCTIONS@@
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@STATUS-BOX@@</span>
+      <div id="result_status">
+        <div id="result_status_no_data">
+          <b><i>@@NO-DATA-DISPLAYED@@</i></b>
+        </div>
+        <div id="result_status_data"      style="display: none;">
+        </div>
+        <div id="result_status_failed"    style="display: none;">
+          <b>@@VISUALISER-FAILED@@</b>
+        </div>
+        <div id="result_status_junctions" style="display: none;">
+          <b>@@VISUALISER-NUM-JUNCTIONS@@</b>
+        </div>
+        <div id="result_status_super"     style="display: none;">
+          <b>@@VISUALISER-NUM-SUPER@@</b>
+        </div>
+        <div id="result_status_oneway"    style="display: none;">
+          <b>@@VISUALISER-NUM-ONEWAY@@</b>
+        </div>
+        <div id="result_status_highway"   style="display: none;">
+          <b>@@VISUALISER-NUM-SEGMENTS@@</b>
+        </div>
+        <div id="result_status_transport" style="display: none;">
+          <b>@@VISUALISER-NUM-SEGMENTS@@</b>
+        </div>
+        <div id="result_status_barrier"   style="display: none;">
+          <b>@@VISUALISER-NUM-NODES@@</b>
+        </div>
+        <div id="result_status_turns"     style="display: none;">
+          <b>@@VISUALISER-NUM-TURNS@@</b>
+        </div>
+        <div id="result_status_limit"     style="display: none;">
+          <b>@@VISUALISER-NUM-LIMITS@@</b>
+        </div>
+        <div id="result_status_property"  style="display: none;">
+          <b>@@VISUALISER-NUM-SEGMENTS@@</b>
+        </div>
+        <div id="result_status_errorlogs" style="display: none;">
+          <b>@@VISUALISER-NUM-ERRORS@@</b>
+        </div>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_junctions_show" onclick="hideshow_show('junctions');" class="hideshow_show">+</span>
+      <span id="hideshow_junctions_hide" onclick="hideshow_hide('junctions');" class="hideshow_hide">-</span>
+      <input type="button" id="junctions" onclick="displayData('junctions');" value="@@JUNCTIONS-BUTTON@@">
+      <div id="hideshow_junctions_div" style="display: none;">
+        @@JUNCTIONS-INFO@@
+        <br>
+        <table>
+          <tr><td><img src="icons/ball-1.png" alt="1" ><td>@@JUNCTIONS-1@@
+          <tr><td><img src="icons/ball-2.png" alt="2" ><td>@@JUNCTIONS-2@@
+          <tr><td><img src="icons/ball-3.png" alt="3" ><td>@@JUNCTIONS-3@@
+          <tr><td><img src="icons/ball-4.png" alt="4" ><td>@@JUNCTIONS-4@@
+          <tr><td><img src="icons/ball-5.png" alt="5" ><td>@@JUNCTIONS-5@@
+          <tr><td><img src="icons/ball-6.png" alt="6" ><td>@@JUNCTIONS-6@@
+          <tr><td><img src="icons/ball-7.png" alt="7+"><td>@@JUNCTIONS-MORE@@
+        </table>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_super_show" onclick="hideshow_show('super');" class="hideshow_show">+</span>
+      <span id="hideshow_super_hide" onclick="hideshow_hide('super');" class="hideshow_hide">-</span>
+      <input type="button" id="super" onclick="displayData('super');" value="@@SUPER-BUTTON@@">
+      <div id="hideshow_super_div" style="display: none;">
+        @@SUPER-INFO@@
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_oneway_show" onclick="hideshow_show('oneway');" class="hideshow_show">+</span>
+      <span id="hideshow_oneway_hide" onclick="hideshow_hide('oneway');" class="hideshow_hide">-</span>
+      <input type="button" id="oneway" onclick="displayData('oneway');" value="@@ONEWAY-BUTTON@@">
+      <div id="hideshow_oneway_div" style="display: none;">
+        @@ONEWAY-INFO@@
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+      <span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+      <input type="button" id="highway" onclick="displayData('highway');" value="@@HIGHWAY-BUTTON@@">
+      <div id="hideshow_highway_div" style="display: none;">
+        @@HIGHWAY-INFO@@
+        <form name="highways" id="highways" action="#" method="get" onsubmit="return false;">
+          <table>
+            <tr><td>@@HIGHWAY-MOTORWAY@@:    <td><input name="highway" type="radio" value="motorway"     onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-TRUNK@@:       <td><input name="highway" type="radio" value="trunk"        onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-PRIMARY@@:     <td><input name="highway" type="radio" value="primary"      onchange="displayData('highway');" checked>
+            <tr><td>@@HIGHWAY-SECONDARY@@:   <td><input name="highway" type="radio" value="secondary"    onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-TERTIARY@@:    <td><input name="highway" type="radio" value="tertiary"     onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-UNCLASSIFIED@@:<td><input name="highway" type="radio" value="unclassified" onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-RESIDENTIAL@@: <td><input name="highway" type="radio" value="residential"  onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-SERVICE@@:     <td><input name="highway" type="radio" value="service"      onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-TRACK@@:       <td><input name="highway" type="radio" value="track"        onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-CYCLEWAY@@:    <td><input name="highway" type="radio" value="cycleway"     onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-PATH@@:        <td><input name="highway" type="radio" value="path"         onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-STEPS@@:       <td><input name="highway" type="radio" value="steps"        onchange="displayData('highway');">
+            <tr><td>@@HIGHWAY-FERRY@@:       <td><input name="highway" type="radio" value="ferry"        onchange="displayData('highway');">
+          </table>
+        </form>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_show">+</span>
+      <span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_hide">-</span>
+      <input type="button" id="transport" onclick="displayData('transport');" value="@@TRANSPORT-BUTTON@@">
+      <div id="hideshow_transport_div" style="display: none;">
+        @@TRANSPORT-INFO@@
+        <form name="transports" id="transports" action="#" method="get" onsubmit="return false;">
+          <table>
+            <tr><td>@@TRANSPORT-FOOT@@:      <td><input name="transport" type="radio" value="foot"       onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-HORSE@@:     <td><input name="transport" type="radio" value="horse"      onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-WHEELCHAIR@@:<td><input name="transport" type="radio" value="wheelchair" onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-BICYCLE@@:   <td><input name="transport" type="radio" value="bicycle"    onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-MOPED@@:     <td><input name="transport" type="radio" value="moped"      onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-MOTORCYCLE@@:<td><input name="transport" type="radio" value="motorcycle" onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-MOTORCAR@@:  <td><input name="transport" type="radio" value="motorcar"   onchange="displayData('transport');" checked>
+            <tr><td>@@TRANSPORT-GOODS@@:     <td><input name="transport" type="radio" value="goods"      onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-HGV@@:       <td><input name="transport" type="radio" value="hgv"        onchange="displayData('transport');">
+            <tr><td>@@TRANSPORT-PSV@@:       <td><input name="transport" type="radio" value="psv"        onchange="displayData('transport');">
+          </table>
+        </form>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_barrier_show" onclick="hideshow_show('barrier');" class="hideshow_show">+</span>
+      <span id="hideshow_barrier_hide" onclick="hideshow_hide('barrier');" class="hideshow_hide">-</span>
+      <input type="button" id="barrier" onclick="displayData('barrier');" value="@@BARRIER-BUTTON@@">
+      <div id="hideshow_barrier_div" style="display: none;">
+        @@BARRIER-INFO@@
+        <form name="barriers" id="barriers" action="#" method="get" onsubmit="return false;">
+          <table>
+            <tr><td>@@TRANSPORT-FOOT@@:      <td><input name="barrier" type="radio" value="foot"       onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-HORSE@@:     <td><input name="barrier" type="radio" value="horse"      onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-WHEELCHAIR@@:<td><input name="barrier" type="radio" value="wheelchair" onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-BICYCLE@@:   <td><input name="barrier" type="radio" value="bicycle"    onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-MOPED@@:     <td><input name="barrier" type="radio" value="moped"      onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-MOTORCYCLE@@:<td><input name="barrier" type="radio" value="motorcycle" onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-MOTORCAR@@:  <td><input name="barrier" type="radio" value="motorcar"   onchange="displayData('barrier');" checked>
+            <tr><td>@@TRANSPORT-GOODS@@:     <td><input name="barrier" type="radio" value="goods"      onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-HGV@@:       <td><input name="barrier" type="radio" value="hgv"        onchange="displayData('barrier');">
+            <tr><td>@@TRANSPORT-PSV@@:       <td><input name="barrier" type="radio" value="psv"        onchange="displayData('barrier');">
+          </table>
+        </form>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_turns_show" onclick="hideshow_show('turns');" class="hideshow_show">+</span>
+      <span id="hideshow_turns_hide" onclick="hideshow_hide('turns');" class="hideshow_hide">-</span>
+      <input type="button" id="turns" onclick="displayData('turns');" value="@@TURNS-BUTTON@@">
+      <div id="hideshow_turns_div" style="display: none;">
+        @@TURNS-INFO@@
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+      <span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+      <input type="button" id="speed" onclick="displayData('speed');" value="@@SPEED-BUTTON@@">
+      <div id="hideshow_speed_div" style="display: none;">
+        @@SPEED-INFO@@
+        <br>
+        <table>
+          <tr><td><img src="icons/ball-1.png"   alt="."   ><td>@@LIMIT-CHANGE@@
+          <tr><td><img src="icons/limit-no.png" alt="()"  ><td>@@LIMIT-NONE@@
+          <tr><td><img src="icons/limit-80.png" alt="(80)"><td>@@SPEED-LIMIT-80@@
+        </table>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_weight_show" onclick="hideshow_show('weight');" class="hideshow_show">+</span>
+      <span id="hideshow_weight_hide" onclick="hideshow_hide('weight');" class="hideshow_hide">-</span>
+      <input type="button" id="weight" onclick="displayData('weight');" value="@@WEIGHT-BUTTON@@">
+      <div id="hideshow_weight_div" style="display: none;">
+        @@WEIGHT-INFO@@
+        <br>
+        <table>
+          <tr><td><img src="icons/ball-1.png"    alt="."    ><td>@@LIMIT-CHANGE@@
+          <tr><td><img src="icons/limit-no.png"  alt="()"   ><td>@@LIMIT-NONE@@
+          <tr><td><img src="icons/limit-8.0.png" alt="(8.0)"><td>@@WEIGHT-LIMIT-8@@
+        </table>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_height_show" onclick="hideshow_show('height');" class="hideshow_show">+</span>
+      <span id="hideshow_height_hide" onclick="hideshow_hide('height');" class="hideshow_hide">-</span>
+      <input type="button" id="height" onclick="displayData('height');" value="@@HEIGHT-BUTTON@@">
+      <div id="hideshow_height_div" style="display: none;">
+        @@HEIGHT-INFO@@
+        <br>
+        <table>
+          <tr><td><img src="icons/ball-1.png"    alt="."    ><td>@@LIMIT-CHANGE@@
+          <tr><td><img src="icons/limit-no.png"  alt="()"   ><td>@@LIMIT-NONE@@
+          <tr><td><img src="icons/limit-4.0.png" alt="(4.0)"><td>@@HEIGHT-LIMIT-4@@
+        </table>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_width_show" onclick="hideshow_show('width');" class="hideshow_show">+</span>
+      <span id="hideshow_width_hide" onclick="hideshow_hide('width');" class="hideshow_hide">-</span>
+      <input type="button" id="width" onclick="displayData('width');" value="@@WIDTH-BUTTON@@">
+      <div id="hideshow_width_div" style="display: none;">
+        @@WIDTH-INFO@@
+        <br>
+        <table>
+          <tr><td><img src="icons/ball-1.png"    alt="."    ><td>@@LIMIT-CHANGE@@
+          <tr><td><img src="icons/limit-no.png"  alt="()"   ><td>@@LIMIT-NONE@@
+          <tr><td><img src="icons/limit-3.0.png" alt="(3.0)"><td>@@WIDTH-LIMIT-3@@
+        </table>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_length_show" onclick="hideshow_show('length');" class="hideshow_show">+</span>
+      <span id="hideshow_length_hide" onclick="hideshow_hide('length');" class="hideshow_hide">-</span>
+      <input type="button" id="length" onclick="displayData('length');" value="@@LENGTH-BUTTON@@">
+      <div id="hideshow_length_div" style="display: none;">
+        @@LENGTH-INFO@@
+        <br>
+        <table>
+          <tr><td><img src="icons/ball-1.png"    alt="."    ><td>@@LIMIT-CHANGE@@
+          <tr><td><img src="icons/limit-no.png"  alt="()"   ><td>@@LIMIT-NONE@@
+          <tr><td><img src="icons/limit-9.0.png" alt="(9.0)"><td>@@LENGTH-LIMIT-9@@
+        </table>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+      <span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+      <input type="button" id="property" onclick="displayData('property');" value="@@PROPERTY-BUTTON@@">
+      <div id="hideshow_property_div" style="display: none;">
+        @@PROPERTY-INFO@@
+        <form name="properties" id="properties" action="#" method="get" onsubmit="return false;">
+          <table>
+            <tr><td>@@PROPERTY-PAVED@@:         <td><input name="property" type="radio" value="paved"         onchange="displayData('property');" checked>
+            <tr><td>@@PROPERTY-MULTILANE@@:     <td><input name="property" type="radio" value="multilane"     onchange="displayData('property');">
+            <tr><td>@@PROPERTY-BRIDGE@@:        <td><input name="property" type="radio" value="bridge"        onchange="displayData('property');">
+            <tr><td>@@PROPERTY-TUNNEL@@:        <td><input name="property" type="radio" value="tunnel"        onchange="displayData('property');">
+            <tr><td>@@PROPERTY-WALKINGROUTE@@:  <td><input name="property" type="radio" value="footroute"     onchange="displayData('property');">
+            <tr><td>@@PROPERTY-BICYCLEROUTE@@:  <td><input name="property" type="radio" value="bicycleroute"  onchange="displayData('property');">
+            <tr><td>@@PROPERTY-CYCLE-BOTHWAYS@@:<td><input name="property" type="radio" value="cyclebothways" onchange="displayData('property');">
+          </table>
+        </form>
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_errorlogs_show" onclick="hideshow_show('errorlogs');" class="hideshow_show">+</span>
+      <span id="hideshow_errorlogs_hide" onclick="hideshow_hide('errorlogs');" class="hideshow_hide">-</span>
+      <input type="button" id="errorlogs" onclick="displayData('errorlogs');" value="@@ERROR-LOG-BUTTON@@">
+      <div id="hideshow_errorlogs_div" style="display: none;">
+        @@ERROR-LOG-INFO@@
+      </div>
+    </div>
+
+    <div class="hideshow_box">
+      <input type="button" id="clear" onclick="displayData('');" value="@@CLEAR-DATA-BUTTON@@">
+    </div>
+
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@LINKS-BOX@@</span>
+      <a id="permalink_url" href="visualiser.html">@@MAP-VIEW-LINK@@</a>
+      <br>
+      <a id="edit_url" target="edit" style="display: none;">@@EDIT-OSM-DATA@@</a>
+    </div>
+
+    <div class="hideshow_box">
+      <span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+      <span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+      <span class="hideshow_title">@@HELP-BOX@@</span>
+      <div id="hideshow_help_options_div">
+        <div class="scrollable">
+          @@VISUALISER-HELP@@
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <div class="tab_content" id="tab_router_div" style="display: none;">
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@ROUTER-BOX@@</span>
+      @@VISUALISER-ROUTER-INFO@@
+      <br>
+      <a id="router_url" href="router.html" target="router">@@MAP-VIEW-LINK@@</a>
+    </div>
+  </div>
+
+  <div class="tab_content" id="tab_data_div" style="display: none;">
+    <div class="hideshow_box">
+      <span class="hideshow_title">@@STATISTICS-BOX@@</span>
+      <div id="statistics_data"></div>
+      <a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">@@DISPLAY-STATISTICS@@</a>
+    </div>
+  </div>
+
+</div>
+
+<!-- Right hand side of window - map -->
+
+<div class="right_panel">
+  <div class="map" id="map">
+    <noscript>
+      <p>
+        @@JAVASCRIPT-REQUIRED@@
+    </noscript>
+  </div>
+  <div class="attribution">
+    @@ROUTER@@: <a href="http://www.routino.org/" target="routino">Routino</a>
+    |
+    @@GEO-DATA@@: <span id="attribution_data"></span>
+    |
+    @@TILES@@: <span id="attribution_tile"></span>
+  </div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/leaflet/install.sh b/web/www/leaflet/install.sh
new file mode 100755
index 0000000..30d95bd
--- /dev/null
+++ b/web/www/leaflet/install.sh
@@ -0,0 +1,11 @@
+#!/bin/sh -x
+
+version=0.7.2
+
+# Download the file.
+
+wget http://leaflet-cdn.s3.amazonaws.com/build/leaflet-$version.zip
+
+# Uncompress it.
+
+unzip leaflet-$version.zip
diff --git a/web/www/openlayers/install.sh b/web/www/openlayers/install.sh
index 830a4ef..01cc302 100755
--- a/web/www/openlayers/install.sh
+++ b/web/www/openlayers/install.sh
@@ -1,6 +1,6 @@
 #!/bin/sh -x
 
-version=2.11
+version=2.13.1
 
 # Download the file.
 
diff --git a/web/www/openlayers/routino.cfg b/web/www/openlayers/routino.cfg
index 23e68bd..278e0c6 100644
--- a/web/www/openlayers/routino.cfg
+++ b/web/www/openlayers/routino.cfg
@@ -17,6 +17,8 @@ OpenLayers/Control/DragFeature.js
 OpenLayers/Control/LayerSwitcher.js
 OpenLayers/Control/Navigation.js
 OpenLayers/Control/PanZoomBar.js
+OpenLayers/Control/PinchZoom.js
+OpenLayers/Control/SelectFeature.js
 OpenLayers/Control/ScaleLine.js
 OpenLayers/Feature/Vector.js
 OpenLayers/Format/GPX.js
diff --git a/web/www/routino/icons/waypoint-left.png b/web/www/routino/icons/waypoint-left.png
new file mode 100644
index 0000000..17c972f
Binary files /dev/null and b/web/www/routino/icons/waypoint-left.png differ
diff --git a/web/www/routino/icons/waypoint-right.png b/web/www/routino/icons/waypoint-right.png
new file mode 100644
index 0000000..8a0f516
Binary files /dev/null and b/web/www/routino/icons/waypoint-right.png differ
diff --git a/web/www/routino/index.html b/web/www/routino/index.html
index 831ef02..317d734 100644
--- a/web/www/routino/index.html
+++ b/web/www/routino/index.html
@@ -1,12 +1,18 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta http-equiv="refresh" content="1; URL=router.html">
+
+<title>Routino : Route Planner for OpenStreetMap Data</title>
 
 <!--
  Routino redirect web page.
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2013 Andrew M. Bishop
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
@@ -22,18 +28,14 @@
  along with this program.  If not, see http://www.gnu.org/licenses/.
 -->
 
-<HEAD>
-<TITLE>Routino : Route Planner for OpenStreetMap Data</TITLE>
-<META http-equiv="refresh" content="1; URL=router.html">
-<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<LINK href="../documentation/style.css" type="text/css" rel="stylesheet">
-</HEAD>
+<link href="documentation/style.css" type="text/css" rel="stylesheet">
+</head>
 
-<BODY>
+<body>
 
 <!-- Header Start -->
 
-<div class="header" align="center">
+<div class="header">
 
 <h1>Routino : Route Planner for OpenStreetMap Data</h1>
 
@@ -54,17 +56,17 @@
 
 <!-- Footer Start -->
 
-<div class="footer" align="center">
+<div class="footer">
 <hr>
 
 <address>
-© Andrew M. Bishop = <amb "at" gedanken.demon.co.uk>
+© Andrew M. Bishop - <a href="http://www.routino.org/">http://www.routino.org/</a>
 </address>
 
 </div>
 
 <!-- Footer End -->
 
-</BODY>
+</body>
 
-</HTML>
+</html>
diff --git a/web/www/routino/maplayout-ie6-bugfixes.css b/web/www/routino/maplayout-ie6-bugfixes.css
deleted file mode 100644
index 0760507..0000000
--- a/web/www/routino/maplayout-ie6-bugfixes.css
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-// Routino Internet Explorer 6 map layout web page style sheet.
-//
-// Part of the Routino routing software.
-//
-// This file Copyright 2010 Andrew M. Bishop
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-/*----------------------------------*/
-/* Body HTML formatting             */
-/*----------------------------------*/
-
-/*
-  Internet Explorer 6 doesn't understand 'postion: fixed' styles.  The best that
-  can be done is the following to make it the equivalent of absolute positioning.
-  This is "well-known" problem, you can find details on the internet.
-*/
-
-* HTML
-{
- overflow-x: auto;
-}
-
-* HTML BODY
-{
- height: 100%;
- width:  100%;
- overflow: auto;
-}
-
-
-/*-------------*/
-/* Right panel */
-/*-------------*/
-
-
-/*
-  Internet Explorer 6 ignores the fact that the map and attribution divs are
-  within the right_panel and positions them all over everything (probably due
-  to the previous hacks).  The fix for this is to make the left edges of these
-  divs line up with where the edge of the right_panel is.
-*/
-
-DIV.map
-{
- left:  23.5em !important;
-}
-
-DIV.attribution
-{
- left:  23.5em !important;
-}
-
-
-/*
-  In addition to the poor positioning we need to set a height and width of the
-  map so we guess what fits in the user's window.
-*/
-
-DIV.map
-{
- width:  65%;
- height: 90%;
-}
-
-DIV.attribution
-{
- width:  65%;
-}
diff --git a/web/www/routino/maplayout-ie7-bugfixes.css b/web/www/routino/maplayout-ie7-bugfixes.css
deleted file mode 100644
index 705056e..0000000
--- a/web/www/routino/maplayout-ie7-bugfixes.css
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-// Routino Internet Explorer 7 map layout web page style sheet.
-//
-// Part of the Routino routing software.
-//
-// This file Copyright 2010 Andrew M. Bishop
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-
-/*-------------*/
-/* Right panel */
-/*-------------*/
-
-/*
-  What seems to happen is that the map div in the right panel picks up the size
-  of the right_panel div itself but won't fill the area unless a width and
-  height are given.  Using 100% width and height is then the whole size of the
-  right_panel and the border makes it bigger still.
-
-  This fix makes the right_panel smaller all round, the map div has its edges
-  moved out to allow the border to be visible all round and has 100% size.  The
-  attribution needs to be given a position outside of the right_panel to make
-  sure that is isn't covered by the map.
-*/
-
-DIV.right_panel
-{
- top:    3px    !important;
- bottom: 1.7em  !important;
- right:  3px    !important;
- left:   23.7em !important;
-}
-
-DIV.map
-{
- top:    -3px !important;
- bottom: -3px !important;
- right:  -3px !important;
- left:   -3px !important;
-
- width:  100% !important;
- height: 100% !important;
-}
-
-DIV.attribution
-{
- bottom: -1.7em !important;
-
- width:  100% !important;
-}
diff --git a/web/www/routino/maplayout.css b/web/www/routino/maplayout.css
index 2c5b2c6..511a9c4 100644
--- a/web/www/routino/maplayout.css
+++ b/web/www/routino/maplayout.css
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2012 Andrew M. Bishop
+// This file Copyright 2008-2014 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -99,3 +99,13 @@ DIV.attribution
  white-space: nowrap;
  overflow:    hidden;
 }
+
+
+/*-----------------------------*/
+/* Leaflet base layer selector */
+/*-----------------------------*/
+
+FORM.leaflet-control-layers-list
+{
+ text-align: left;
+}
diff --git a/web/www/routino/maploader.js b/web/www/routino/maploader.js
new file mode 100644
index 0000000..dba3c54
--- /dev/null
+++ b/web/www/routino/maploader.js
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////////
+///////////////////// Map loader (OpenLayers or Leaflet) ///////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+
+function map_load(callbacks)
+{
+ var pending=1;
+ var head = document.getElementsByTagName("head")[0];
+
+ /* Call the callbacks when everything is loaded. */
+
+ function call_callbacks()
+ {
+  if(!--pending)
+     eval(callbacks);
+ }
+
+ /* Javascript loader */
+
+ function load_js(url)
+ {
+  var script = document.createElement("script");
+  script.src = url;
+  script.type = "text/javascript";
+
+  script.onload = call_callbacks;
+
+  pending++;
+
+  head.appendChild(script);
+ }
+
+ /* CSS loader */
+
+ function load_css(url)
+ {
+  var link = document.createElement("link");
+  link.href = url;
+  link.type = "text/css";
+  link.rel = "stylesheet";
+
+  head.appendChild(link);
+ }
+
+ /* Load the external library and local code */
+
+ if(mapprops.library == "leaflet")
+   {
+    load_css("../leaflet/leaflet.css");
+    load_js("../leaflet/leaflet.js");
+
+    load_js(location.pathname.replace(/\.html.*/,".leaflet.js"));
+   }
+ else
+   {
+    load_js("../openlayers/OpenLayers.js");
+
+    load_js(location.pathname.replace(/\.html.*/,".openlayers.js"));
+   }
+
+ call_callbacks();
+}
diff --git a/web/www/routino/mapprops.js b/web/www/routino/mapprops.js
index b600e8d..d2a19c9 100644
--- a/web/www/routino/mapprops.js
+++ b/web/www/routino/mapprops.js
@@ -4,43 +4,80 @@
 
 var mapprops={ // contains all properties for the map to be displayed.
 
- // Default configuration:
- // UK coordinate range
- // West -11.0, South 49.5, East 2.0, North 61.0
- // Zoom level 4 to 15
+ // EDIT THIS below to change the map library (either 'openlayers' or 'leaflet').
+
+  library: "openlayers",
+  //library: "leaflet",
+
+ // EDIT THIS above to change the map library (either 'openlayers' or 'leaflet').
+
 
  // EDIT THIS below to change the visible map limits
 
-    westedge:  -11.0,          // Minimum longitude (degrees)
-    eastedge:    2.0,          // Maximum longitude (degrees)
+  westedge:  -11.0,          // Minimum longitude (degrees)
+  eastedge:    2.0,          // Maximum longitude (degrees)
 
-    southedge:  49.5,          // Minimum latitude (degrees)
-    northedge:  61.0,          // Maximum latitude (degrees)
+  southedge:  49.5,          // Minimum latitude (degrees)
+  northedge:  61.0,          // Maximum latitude (degrees)
 
-    zoomout:       4,          // Minimum zoom
-    zoomin:       15,          // Maximum zoom
+  zoomout:       4,          // Minimum zoom
+  zoomin:       15,          // Maximum zoom
 
  // EDIT THIS above to change the visible map limits
 
 
- // EDIT THIS below to change the map URL(s)
+ // EDIT THIS below to change the map URL(s) and copyright notices
+
+  mapdata: [
+           {
+            label: "OpenStreetMap",
+            tiles: {
+                    url: "http://${s}.tile.openstreetmap.org/${z}/${x}/${y}.png",
+                    subdomains: ["a","b","c"]
+                   },
+            attribution: {
+                          data_url:  "http://www.openstreetmap.org/copyright",
+                          data_text: "© OpenStreetMap contributors",
+                          tile_url:  "http://www.openstreetmap.org/copyright",
+                          tile_text: "© OpenStreetMap"
+                         }
+           },
+           {
+            label: "MapQuest",
+            tiles: {
+                    url: "http://otile${s}.mqcdn.com/tiles/1.0.0/map/${z}/${x}/${y}.jpg",
+                    subdomains: ["1","2","3","4"]
+                   },
+            attribution: {
+                          data_url:  "http://www.openstreetmap.org/copyright",
+                          data_text: "© OpenStreetMap contributors",
+                          tile_url:  "http://www.mapquest.com/",
+                          tile_text: "© MapQuest <img src=\"http://developer.mapquest.com/content/osm/mq_logo.png\">"
+                         }
+           }
+           ],
+
+ // EDIT THIS above to change the map URL(s) and copyright notices
+
+
+ // EDIT THIS below to change the map source data editing URL (or leave blank for no link)
+
+  editurl: "http://www.openstreetmap.org/edit",
+
+ // EDIT THIS above to change the map source data editing URL (or leave blank for no link)
+
+
+ // EDIT THIS below to change the map source data browsing URL (or leave blank for no link)
 
-    mapdata: [
-        {
-            label:    "OSM map",
-            baseurl:  "http://tile.openstreetmap.org/",
-            errorurl: "http://openstreetmap.org/openlayers/img/404.png"
-        }
-    ],
+  browseurl: "http://www.openstreetmap.org/browse",
 
- // EDIT THIS above to change the map URL(s)
+ // EDIT THIS above to change the map source data browsing URL (or leave blank for no link)
 
 
- // EDIT THIS below to change the maximum number of markers
+ // EDIT THIS below to change the maximum number of markers to include in the HTML
 
- // The number of waypoints to include in the HTML
-    maxmarkers: 9
+  maxmarkers: 9
 
- // EDIT THIS above to change the maximum number of markers
+ // EDIT THIS above to change the maximum number of markers to include in the HTML
 
 }; // end of map properties
diff --git a/web/www/routino/page-elements.css b/web/www/routino/page-elements.css
index 076bd0d..449593c 100644
--- a/web/www/routino/page-elements.css
+++ b/web/www/routino/page-elements.css
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2011 Andrew M. Bishop
+// This file Copyright 2008-2014 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -42,7 +42,10 @@ DIV.tab_box SPAN.tab_selected
 
  z-index: 5;
 
- padding: 3px;
+ padding-top:    3px;
+ padding-bottom: 3px;
+ padding-right:  5px;
+ padding-left:   5px;
 
  font-weight: bold;
  font-variant: small-caps;
@@ -56,7 +59,10 @@ DIV.tab_box SPAN.tab_unselected
  border-left:  1px solid;
  border-right: 1px solid;
 
- padding: 3px;
+ padding-top:    3px;
+ padding-bottom: 3px;
+ padding-right:  5px;
+ padding-left:   5px;
 
  cursor: pointer;
 
@@ -112,8 +118,8 @@ DIV.hideshow_box SPAN.hideshow_show
 
  width: 1em;
 
- padding-right:  2px;
- padding-left:   1px;
+ padding-right:  5px;
+ padding-left:   5px;
  padding-top:    1px;
  padding-bottom: 1px;
 
diff --git a/web/www/routino/page-elements.js b/web/www/routino/page-elements.js
index 61d4a4c..427f9be 100644
--- a/web/www/routino/page-elements.js
+++ b/web/www/routino/page-elements.js
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2012 Andrew M. Bishop
+// This file Copyright 2008-2014 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -48,7 +48,7 @@ function tab_select(name)
 
     child=child.nextSibling;
    }
- while(child!=null);
+ while(child!==null);
 
  // Display the newly selected tab and DIV
 
@@ -79,3 +79,16 @@ function hideshow_hide(name)
  document.getElementById("hideshow_" + name + "_hide").className="hideshow_hide";
  document.getElementById("hideshow_" + name + "_div").style.display="none";
 }
+
+
+//
+// Toggle the associated DIV
+//
+
+function hideshow_toggle(name)
+{
+ if(document.getElementById("hideshow_" + name + "_div").style.display=="none")
+    hideshow_show(name);
+ else
+    hideshow_hide(name);
+}
diff --git a/web/www/routino/results.cgi b/web/www/routino/results.cgi
index 2047cfe..bbd564d 100755
--- a/web/www/routino/results.cgi
+++ b/web/www/routino/results.cgi
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2012 Andrew M. Bishop
+# This file Copyright 2008-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,6 +20,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the generic router script
 require "router.pl";
 
@@ -29,13 +31,13 @@ use CGI ':cgi';
 
 # Create the query and get the parameters
 
-$query=new CGI;
+my $query=new CGI;
 
- at rawparams=$query->param;
+my @rawparams=$query->param;
 
 # Legal CGI parameters with regexp validity check
 
-%legalparams=(
+my %legalparams=(
               "type"   => "(shortest|quickest|router)",
               "format" => "(html|gpx-route|gpx-track|text|text-all|log)",
 
@@ -44,6 +46,8 @@ $query=new CGI;
 
 # Validate the CGI parameters, ignore invalid ones
 
+my %cgiparams=();
+
 foreach my $key (@rawparams)
   {
    foreach my $test (keys (%legalparams))
@@ -63,9 +67,9 @@ foreach my $key (@rawparams)
 
 # Parse the parameters
 
-$uuid  =$cgiparams{"uuid"};
-$type  =$cgiparams{"type"};
-$format=$cgiparams{"format"};
+my $uuid  =$cgiparams{"uuid"};
+my $type  =$cgiparams{"type"};
+my $format=$cgiparams{"format"};
 
 # Return the file
 
diff --git a/web/www/routino/router.cgi b/web/www/routino/router.cgi
index 6e6efc8..f92a210 100755
--- a/web/www/routino/router.cgi
+++ b/web/www/routino/router.cgi
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2012 Andrew M. Bishop
+# This file Copyright 2008-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,6 +20,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the generic router script
 require "router.pl";
 
@@ -29,15 +31,15 @@ use CGI ':cgi';
 
 # Create the query and get the parameters
 
-$query=new CGI;
+my $query=new CGI;
 
- at rawparams=$query->param;
+my @rawparams=$query->param;
 
 # Legal CGI parameters with regexp validity check
 
-%legalparams=(
-              "lon[1-9]"        => "[-0-9.]+",
-              "lat[1-9]"        => "[-0-9.]+",
+my %legalparams=(
+              "lon[1-9][0-9]*"  => "[-0-9.]+",
+              "lat[1-9][0-9]*"  => "[-0-9.]+",
               "heading"         => "[-0-9.]+",
               "transport"       => "[a-z]+",
               "highway-[a-z]+"  => "[0-9.]+",
@@ -53,11 +55,16 @@ $query=new CGI;
 
               "language"        => "[-a-zA-Z]+",
               "type"            => "(shortest|quickest)",
-              "format"          => "(html|gpx-route|gpx-track|text|text-all)"
+              "format"          => "(html|gpx-route|gpx-track|text|text-all)",
+
+              "reverse"         => "(1|0|true|false|on|off)",
+              "loop"            => "(1|0|true|false|on|off)"
              );
 
 # Validate the CGI parameters, ignore invalid ones
 
+my %cgiparams=();
+
 foreach my $key (@rawparams)
   {
    foreach my $test (keys (%legalparams))
@@ -77,6 +84,9 @@ foreach my $key (@rawparams)
 
 # Get the important parameters
 
+my $type;
+my $format;
+
 $type=$cgiparams{type};
 delete $cgiparams{type};
 
@@ -87,7 +97,7 @@ delete $cgiparams{format};
 
 # Fill in the default parameters
 
-%fullparams=FillInDefaults(%cgiparams);
+my %fullparams=FillInDefaults(%cgiparams);
 
 # Run the router
 
diff --git a/web/www/routino/router.css b/web/www/routino/router.css
index f3e1254..71f9d45 100644
--- a/web/www/routino/router.css
+++ b/web/www/routino/router.css
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2012 Andrew M. Bishop
+// This file Copyright 2008-2013 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -64,6 +64,11 @@ DIV#tab_options_div TABLE
  margin:  0;
 }
 
+DIV#tab_options_div DIV.center
+{
+ text-align: center;
+}
+
 DIV#tab_options_div TABLE TD
 {
  padding: 0;
@@ -71,6 +76,11 @@ DIV#tab_options_div TABLE TD
  margin:  0;
 }
 
+DIV#tab_options_div TABLE TD.center
+{
+ text-align: center;
+}
+
 DIV#tab_options_div TABLE#waypoints
 {
  width: 100%;
diff --git a/web/www/routino/router.html.de b/web/www/routino/router.html.de
index 9186ee0..940187c 100644
--- a/web/www/routino/router.html.de
+++ b/web/www/routino/router.html.de
@@ -1,36 +1,33 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<html>
 
-<!--
- Routino router web page.
-
- Part of the Routino routing software.
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routing route planner">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
 
- This file Copyright 2008-2012 Andrew M. Bishop
+<title>Routino : Routen Planer für OpenStreetMap Daten</title>
 
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+<!--
+Routino router web page.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
+Part of the Routino routing software.
 
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see http://www.gnu.org/licenses/.
+This file Copyright 2008-2014 Andrew M. Bishop
 
- Deutsche UEbersetzung von Andreas Matthus
--->
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
-<HEAD>
-<TITLE>Routino : Routen Planer für OpenStreetMap Daten</TITLE>
-<META name="keywords" content="openstreetmap routing route planer">
-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
 
-<!-- OpenLayers Javascript library -->
-<script src="../openlayers/OpenLayers.js" type="text/javascript"></script>
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
 
 <!-- Page elements -->
 <script src="page-elements.js" type="text/javascript"></script>
@@ -38,449 +35,448 @@
 
 <!-- Router and visualiser shared features -->
 <link href="maplayout.css" type="text/css" rel="stylesheet">
-<!--[if IE 6]>
-  <link href="maplayout-ie6-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
-<!--[if IE 7]>
-  <link href="maplayout-ie7-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
 
 <!-- Router specific features -->
 <script src="profiles.js" type="text/javascript"></script>
-<script src="mapprops.js" type="text/javascript"></script>
-<script src="router.js" type="text/javascript"></script>
 <link href="router.css" type="text/css" rel="stylesheet">
 
-</HEAD>
-<BODY onload="html_init();map_init();form_init();">
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('html_init();map_init();form_init();');">
 
 <!-- Left hand side of window - data panel -->
 
 <div class="left_panel">
 
-  <div class="tab_box">
-    <span id="tab_options" onclick="tab_select('options');" class="tab_selected"   title="Setze Routing-Optionen">Optionen</span>
-    <span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="Sieh die Ergebnisse">Ergebnisse</span>
-    <span id="tab_data"    onclick="tab_select('data');"    class="tab_unselected" title="Sieh die Datenbankinformationen">Daten</span>
-  </div>
-
-  <div class="tab_content" id="tab_options_div">
-
-    <form name="form" id="form" action="" method="get" onsubmit="return false;">
-      <div class="hideshow_box">
-        <span class="hideshow_title">Routino OpenStreetMap Router</span>
-        Diese Website erlaubt Routing mit den Daten, die OpenStreetMap gesammelt hat.
-        Wähle Start- und Endpunkt (klicke auf die Marker-Symbole unten), wähle die Routing-Vorgaben und dann finde den Weg.
-        <div align="center">
-          <a target="other" href="http://www.routino.org/">Routino Website</a>
-          |
-          <a target="other" href="documentation/">Dokumentation</a>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
-        <span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Sprache</span>
-
-        <!-- Note for translations: Only this HTML file needs to be translated, the Javascript has
-             no language specific information in it.  Only the body text and title attributes should
-             be changed, the values passed to the JavaScript and the element names must not be changed.
-             The selection below changes the language option passed to the router and selects the
-             output language not the web page language, the links are for that.  The router itself uses
-             the translations.xml file for the translated versions of the output. -->
-
-        <div id="hideshow_language_div" style="display: none;">
-          <table>
-            <tr>
-              <td><a id="lang_de_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);"  href="router.html.de" title="Deutsche Webseite">German</a>
-              <td>(DE)
-              <td><input name="language" type="radio" value="de" onchange="formSetLanguage()" checked>
-	    <tr>
-              <td><a id="lang_en_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);"  href="router.html.en" title="English language web page">English</a>
-              <td>(EN)
-              <td><input name="language" type="radio" value="en" onchange="formSetLanguage()">
-            <tr>
-              <td><a id="lang_nl_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);"  href="router.html.nl" title="Dutch language web page">Dutch</a>
-              <td>(NL)
-              <td><input name="language" type="radio" value="nl" onchange="formSetLanguage()">
-            <tr>
-              <td>Russian
-              <td>(RU)
-              <td><input name="language" type="radio" value="ru" onchange="formSetLanguage()">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
-        <span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Wegpunkte</span>
-        <div id="hideshow_waypoint_div">
-          <table id="waypoints">
-            <colgroup>
-              <col style="width: 25px;">
-              <col>
-              <col style="width: 76px;">
-            </colgroup>
-            <tr id="waypointXXX" style="display: none;">
-              <td>
-                <img name="waypointXXX" src="icons/marker-XXX-grey.png" title="Waypoint XXX Position - (click to add/remove on map)" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX)"> 
-              <td>
-                <span id="coordsXXX">
-                  <input name="lonXXX" type="text" size="7" title="Waypoint XXX Longitude" onchange="formSetCoords(XXX);">E
-                  <input name="latXXX" type="text" size="7" title="Waypoint XXX Latitude"  onchange="formSetCoords(XXX);">N
-                </span>
-                <span id="searchXXX" style="display: none;">
-                  <input name="searchXXX" type="text" size="18" title="Waypoint XXX Location"> <!-- uses Javascript event for triggering -->
-                </span>
-              <td>
-                <img alt="?" src="icons/waypoint-search.png"   title="Search for location"         onmousedown="markerSearch(XXX);"  >
-                <img alt="G" src="icons/waypoint-locate.png"   title="Get current location"        onmousedown="markerLocate(XXX);"  >
-                <img alt="O" src="icons/waypoint-recentre.png" title="Centre map on this waypoint" onmousedown="markerRecentre(XXX);">
-                <img alt="^" src="icons/waypoint-up.png"       title="Move this waypoint up"       onmousedown="markerMoveUp(XXX);"  >
-                <img alt="+" src="icons/waypoint-add.png"      title="Add waypoint after this one" onmousedown="markerAddAfter(XXX);">
-                <br>
-                <img alt="#" src="icons/waypoint-coords.png"   title="Coordinates for location"    onmousedown="markerCoords(XXX);"  >
-                <img alt="~" src="icons/waypoint-home.png"     title="Toggle as home location"     onmousedown="markerHome(XXX);"    >
-                <img alt="o" src="icons/waypoint-centre.png"   title="Centre this waypoint on map" onmousedown="markerCentre(XXX);"  >
-                <img alt="v" src="icons/waypoint-down.png"     title="Move this waypoint down"     onmousedown="markerMoveDown(XXX);">
-                <img alt="-" src="icons/waypoint-remove.png"   title="Remove this waypoint"        onmousedown="markerRemove(XXX);"  >
-            <tr id="searchresultsXXX" style="display: none;">
-              <td colspan="3">
-            <!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js.  -->
-            <tr>
-              <td colspan="3" align="center">
-                <input type="button" title="Rückwärts" value="Rückwärts" onmousedown="markersReverse();">
-                <input type="button" title="Add a new waypoint to make a loop" value="Close loop" onmousedown="markersLoop();">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
-        <span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Fortbewegungsart</span>
-        <div id="hideshow_transport_div">
-          <table>
-            <tr><td>Fußgänger <td><input name="transport" type="radio" value="foot"       onchange="formSetTransport('foot'      )">
-            <tr><td>Reiter               <td><input name="transport" type="radio" value="horse"      onchange="formSetTransport('horse'     )">
-            <tr><td>Rollstuhl            <td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair')">
-            <tr><td>Fahrrad              <td><input name="transport" type="radio" value="bicycle"    onchange="formSetTransport('bicycle'   )">
-            <tr><td>Moped                <td><input name="transport" type="radio" value="moped"      onchange="formSetTransport('moped'     )">
-            <tr><td>Motorrad             <td><input name="transport" type="radio" value="motorbike"  onchange="formSetTransport('motorbike' )">
-            <tr><td>Auto                 <td><input name="transport" type="radio" value="motorcar"   onchange="formSetTransport('motorcar'  )">
-            <tr><td>LKW                  <td><input name="transport" type="radio" value="goods"      onchange="formSetTransport('goods'     )">
-            <tr><td>Schwertransport      <td><input name="transport" type="radio" value="hgv"        onchange="formSetTransport('hgv'       )">
-            <tr><td>Personenverkehr      <td><input name="transport" type="radio" value="psv"        onchange="formSetTransport('psv'       )">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
-        <span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Vorgaben zur Wegnutzung</span>
-        <div id="hideshow_highway_div" style="display: none;">
-          <table>
-            <tr><td>Autobahn:            <td><input name="highway-motorway"     type="text" size=3 onchange="formSetHighway('motorway'    )"><td>%
-            <tr><td>Schnellstraße: <td><input name="highway-trunk"        type="text" size=3 onchange="formSetHighway('trunk'       )"><td>%
-            <tr><td>Bundesstraße:  <td><input name="highway-primary"      type="text" size=3 onchange="formSetHighway('primary'     )"><td>%
-            <tr><td>Landesstraße:  <td><input name="highway-secondary"    type="text" size=3 onchange="formSetHighway('secondary'   )"><td>%
-            <tr><td>Hauptstraße:   <td><input name="highway-tertiary"     type="text" size=3 onchange="formSetHighway('tertiary'    )"><td>%
-            <tr><td>Straße:        <td><input name="highway-unclassified" type="text" size=3 onchange="formSetHighway('unclassified')"><td>%
-            <tr><td>Wohnstraße:    <td><input name="highway-residential"  type="text" size=3 onchange="formSetHighway('residential' )"><td>%
-            <tr><td>Zufahrtsweg:         <td><input name="highway-service"      type="text" size=3 onchange="formSetHighway('service'     )"><td>%
-            <tr><td>Feld-(Wald-)weg:     <td><input name="highway-track"        type="text" size=3 onchange="formSetHighway('track'       )"><td>%
-            <tr><td>Fahrradweg:          <td><input name="highway-cycleway"     type="text" size=3 onchange="formSetHighway('cycleway'    )"><td>%
-            <tr><td>Weg:                 <td><input name="highway-path"         type="text" size=3 onchange="formSetHighway('path'        )"><td>%
-            <tr><td>Fußweg:        <td><input name="highway-steps"        type="text" size=3 onchange="formSetHighway('steps'       )"><td>%
-            <tr><td>Fähre:          <td><input name="highway-ferry"        type="text" size=3 onchange="formSetHighway('ferry'       )"><td>%
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
-        <span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Geschwindigkeitsvorgaben</span>
-        <div id="hideshow_speed_div" style="display: none;">
-          <table>
-            <tr><td>Autobahn:            <td><input name="speed-motorway"     type="text" size=3 onchange="formSetSpeed('motorway'    )"><td>km/hr
-            <tr><td>Schnellstraße: <td><input name="speed-trunk"        type="text" size=3 onchange="formSetSpeed('trunk'       )"><td>km/hr
-            <tr><td>Bundesstraße:  <td><input name="speed-primary"      type="text" size=3 onchange="formSetSpeed('primary'     )"><td>km/hr
-            <tr><td>Landesstraße:  <td><input name="speed-secondary"    type="text" size=3 onchange="formSetSpeed('secondary'   )"><td>km/hr
-            <tr><td>Hauptstraße:   <td><input name="speed-tertiary"     type="text" size=3 onchange="formSetSpeed('tertiary'    )"><td>km/hr
-            <tr><td>Straße:        <td><input name="speed-unclassified" type="text" size=3 onchange="formSetSpeed('unclassified')"><td>km/hr
-            <tr><td>Wohnstraße:    <td><input name="speed-residential"  type="text" size=3 onchange="formSetSpeed('residential' )"><td>km/hr
-            <tr><td>Zufahrtsweg:         <td><input name="speed-service"      type="text" size=3 onchange="formSetSpeed('service'     )"><td>km/hr
-            <tr><td>Feld-(Wald-)weg:     <td><input name="speed-track"        type="text" size=3 onchange="formSetSpeed('track'       )"><td>km/hr
-            <tr><td>Fahrradweg:          <td><input name="speed-cycleway"     type="text" size=3 onchange="formSetSpeed('cycleway'    )"><td>km/hr
-            <tr><td>Weg:                 <td><input name="speed-path"         type="text" size=3 onchange="formSetSpeed('path'        )"><td>km/hr
-            <tr><td>Fußweg:        <td><input name="speed-steps"        type="text" size=3 onchange="formSetSpeed('steps'       )"><td>km/hr
-            <tr><td>Fähre:          <td><input name="speed-ferry"        type="text" size=3 onchange="formSetSpeed('ferry'       )"><td>km/hr
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
-        <span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Vorgaben zur Wegbeschaffenheit</span>
-        <div id="hideshow_property_div" style="display: none;">
-          <table>
-            <tr><td>befestigt:    <td><input name="property-paved"        type="text" size=3 onchange="formSetProperty('paved'       )"><td>%
-            <tr><td>mehrspurig:   <td><input name="property-multilane"    type="text" size=3 onchange="formSetProperty('multilane'   )"><td>%
-            <tr><td>Brücken: <td><input name="property-bridge"       type="text" size=3 onchange="formSetProperty('bridge'      )"><td>%
-            <tr><td>Tunnel:       <td><input name="property-tunnel"       type="text" size=3 onchange="formSetProperty('tunnel'      )"><td>%
-            <tr><td>Wanderweg:    <td><input name="property-footroute"    type="text" size=3 onchange="formSetProperty('footroute'   )"><td>%
-            <tr><td>Radweg:       <td><input name="property-bicycleroute" type="text" size=3 onchange="formSetProperty('bicycleroute')"><td>%
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
-        <span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">andere Vorgaben</span>
-        <div id="hideshow_restriction_div" style="display: none;">
-          <table>
-            <tr><td>beachte Einbahnstraßen:<td><input name="restrict-oneway" type="checkbox"    onchange="formSetRestriction('oneway')"><td>
-            <tr><td>beachte Abbiegeverbot:       <td><input name="restrict-turns"  type="checkbox"    onchange="formSetRestriction('turns' )"><td>
-            <tr><td>Gewicht:                     <td><input name="restrict-weight" type="text" size=3 onchange="formSetRestriction('weight')"><td>Tonnen
-            <tr><td>Höhe:                   <td><input name="restrict-height" type="text" size=3 onchange="formSetRestriction('height')"><td>Meter
-            <tr><td>Breite:                      <td><input name="restrict-width"  type="text" size=3 onchange="formSetRestriction('width' )"><td>Meter
-            <tr><td>Länge:                  <td><input name="restrict-length" type="text" size=3 onchange="formSetRestriction('length')"><td>Meter
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span class="hideshow_title">Suche</span>
-        <input type="button" title="Find shortest route" id="shortest" value="kürzeste" onclick="findRoute('shortest');">
-        <input type="button" title="Find quickest route" id="quickest" value="schnellste" onclick="findRoute('quickest');">
-      </div>
-
-      <div class="hideshow_box">
-        <span class="hideshow_title">Links</span>
-        <a id="permalink_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html">Dauerhafter Link zu diesen Vorgaben</a>
-        <br>
-        <a id="edit_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="http://www.openstreetmap.org/" target="edit">Bearbeitet die OSM-Daten in Potlatch</a>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
-        <span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Hilfe</span>
-        <div id="hideshow_help_options_div">
-          <div class="scrollable">
-            <p>
-            <b>Schnellanleitung</b>
-            <br>
-            Klicke auf die Marker-Bildchen (oben), um sie in der Mitte der Karte (rechts) zu positionieren. Dann 
-	    ziehe das Bildchen auf die genaue Position. Das Zoomen der Karte vor der Patzierung ist vermutlich am einfachsten.
-	    Alternativ kann man die geografische Breite und Länge in den Kästchen eintragen.
-            <p>
-	    Wähle die Fortbewegungsart, die Vorgaben zur Wegnutzung, die Geschwindigkeitsvorgaben, 
-	    die Vorgaben zur Wegbeschaffenheit und die anderen Vorgaben von den obigen Auswahlfeldern.
-	    Ein Klick auf "kürzeste" oder "schnellste" ermittelt die entsprechende Verbindung und zeigt sie in der Karte an.
-            <p>
-            <b>Wegpunkte</b>
-            <br>
-	    Ein Klick auf das Marker-Bildchen (oben) schaltet die Sichbarkeit in der Karte ein bzw. aus.
-	    Die Berechnung Route erfolgt in der Reihenfolge der Wegpunkte (so gut, wie es für die 
-	    gewählte Fortbewegungsart möglich ist).
-            <p>
-            <b>Fortbewegungsart</b>
-            <br>
-	    Die Auswahl der Fortbewegungsart bestimmt die bei der Routenberechnung erlaubten Wegtypen und die 
-	    Vorgabeeinstellungen aller anderen Parameter.
-            <p>
-            <b>Vorgaben zur Wegnutzung</b>
-            <br>
-	    Die Vorgaben zur Wegnutzung bestimmen die Priorisierung von Wegarten.
-	    Wenn z. B. Schnellstraßen mit 110% und Bundesstraßen mit 100% angegeben werden, wird 
-	    bei zwei möglichen Wegwahlen die Schnellstraße solange bevorzugt wird, wie der 
-	    Längen(oder Zeit-)unterschied 10% nicht überschreitet.
-            <p>
-            <b>Geschwindigkeitsvorgaben</b>
-            <br>
-	    Die hier geannten Geschwindigkeiten werden für den jeweiligen Wegtyp finden Anwendung wenn keine
-	    andere Geschwindkeitsbegrenzung mit geringerem Wert bekannt ist.
-            <p>
-            <b>Vorgaben zur Wegbeschaffenheit</b>
-            <br>
-	    Die Vorgaben zur Wegbeschaffenheit werden als Prozentangaben verwendet, um die Verhältnisse 
-	    der Wegbenutzung zu steuern.
-	    Wenn z. B. befestigte Wege mit 75% angegeben sind, werden unbefestigte automatisch mit 25% angenommen, so
-	    werden Wege ausgewählt, die mindestens drei mal länger auf befestigten Wegen verlaufen.
-            <p>
-            <b>andere Vorgaben</b>
-            <br>
-	    Die Berücksichtigung von Benutzungs-Begrenzungen durch Gewicht, Höhe, Länge und 
-	    Breite ist möglich. Genauso können Einbahnstraßenbeschräkungen ignoriert werden 
-	    (z. B. als Fußgänger). 
-          </div>
-        </div>
-      </div>
-    </form>
-  </div>
-
-
-  <div class="tab_content" id="tab_results_div" style="display: none;">
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Status</span>
-      <div id="result_status">
-        <div id="result_status_not_run">
-          <b><i>Router läuft nicht</i></b>
-        </div>
-        <div id="result_status_running"  style="display: none;">
-          <b>Router läuft...</b>
-        </div>
-        <div id="result_status_complete" style="display: none;">
-          <b>Routing fertig</b>
-          <br>
-          <a id="router_log_complete" target="router_log" href="#">zeige Details</a>
-        </div>
-        <div id="result_status_error"    style="display: none;">
-          <b>Router Fehler</b>
-          <br>
-          <a id="router_log_error" target="router_log" href="#">zeige Details</a>
-        </div>
-        <div id="result_status_failed"   style="display: none;">
-          <b>Router funktioniert nicht</b>
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
-      <span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
-      <span class="hideshow_title">kürzester Weg</span>
-      <div id="shortest_status">
-        <div id="shortest_status_no_info">
-          <b><i>keine Information</i></b>
-        </div>
-        <div id="shortest_status_info" style="display: none;">
-        </div>
-      </div>
-      <div id="hideshow_shortest_div" style="display: none;">
-        <div id="shortest_links" style="display: none;">
-          <table>
-            <tr><td>HTML:            <td><a id="shortest_html"      target="shortest_html"      href="#">öffne Popup</a>
-            <tr><td>GPX Track-Datei: <td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">öffne Popup</a>
-            <tr><td>GPX Routen-Datei:<td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">öffne Popup</a>
-            <tr><td>Volltext-Datei:  <td><a id="shortest_text_all"  target="shortest_text_all"  href="#">öffne Popup</a>
-            <tr><td>Text-Datei:      <td><a id="shortest_text"      target="shortest_text"      href="#">öffne Popup</a>
-          </table>
-          <hr>
-        </div>
-        <div id="shortest_route">
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
-      <span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
-      <span class="hideshow_title">schnellste Route</span>
-      <div id="quickest_status">
-        <div id="quickest_status_no_info">
-          <b><i>keine Information</i></b>
-        </div>
-        <div id="quickest_status_info" style="display: none;">
-        </div>
-      </div>
-      <div id="hideshow_quickest_div" style="display: none;">
-        <div id="quickest_links" style="display: none;">
-          <table>
-            <tr><td>HTML:            <td><a id="quickest_html"      target="quickest_html"      href="#">öffne Popup</a>
-            <tr><td>GPX Track-Datei: <td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">öffne Popup</a>
-            <tr><td>GPX Routen-Datei:<td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">öffne Popup</a>
-            <tr><td>Volltext-Datei:  <td><a id="quickest_text_all"  target="quickest_text_all"  href="#">öffne Popup</a>
-            <tr><td>Text-Datei:      <td><a id="quickest_text"      target="quickest_text"      href="#">öffne Popup</a>
-          </table>
-          <hr>
-        </div>
-        <div id="quickest_route">
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
-      <span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
-      <span class="hideshow_title">Hilfe</span>
-      <div id="hideshow_help_route_div">
-        <div class="scrollable">
-          <p>
-          <b>Schnellanleitung</b>
-          <br>
-	  Nach der Routenberechnung kann man eine GPX oder eine einfache Textdatei (Kurz- oder Langfassung)
-	  herunterladen. Ebenso kann man die Routenbeschreibung ansehen und in ausgewälte Bereiche zoomen.
-          <p>
-          <b>Problemlösung</b>
-          <br>
-	  Wenn der Router einen Fehler meldet liegt es meistens daran, dass kein Weg zwischen den gewälten Punkten unter
-	  Beachtung der Vorgaben gefunden werden kann. Das Bewegen eines oder mehrere Punkte oder das verändern von
-	  Vorgaben sollte es erlauben eine Route zu finden.
-          <p>
-          <b>Ausgabe-Formate</b>
-          <br>
-          <dl>
-            <dt>HTMLs
-            <dd>Eine Beschreibung der Route mit Anweisungen für jede wichtige Abzweigung.
-            <dt>GPX Track-Datei
-            <dd>Die gleichen Informationen, die in der Karte angezeigt werden mit Punkten für jeden Abzweig 
-	    und Linien für jedes Teilstück.
-            <dt>GPX Routen-Datei
-            <dd>Die gleichen Informationen, die im Text angezeigt werden mit einem Wegpunkt für 
-	    jede wichtige Richtungsänderung.
-            <dt>Volltext-Datei
-            <dd>Eine aller Knoten und die Abstände zwischen ihnen, sowie die Gesamtentfernung vom i
-		Startpunkt zum jeweiligen Konten.
-            <dt>Text-Datei
-            <dd>Die gleiche Information, die als Text angezeigt wird.
-          </dl>
-        </div>
-      </div>
-    </div>
-  </div>
-
-
-  <div class="tab_content" id="tab_data_div" style="display: none;">
-    <div class="hideshow_box">
-      <span class="hideshow_title">Statistik</span>
-      <div id="statistics_data"></div>
-      <a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">zeige die Statistik</a>
-    </div>
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Ansichten</span>
-	Die Anzeige der Daten kann auf verschiedene Weise angepasst werden.	
-      <br>
-      <a id="visualiser_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="visualiser.html" target="visualiser">anpassen dieser Kartenansicht</a>
-    </div>
-  </div>
+<div class="tab_box">
+<span id="tab_options" onclick="tab_select('options');" class="tab_selected" title="Setze Routing-Optionen">Optionen</span>
+<span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="Sieh die Ergebnisse">Ergebnisse</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="Sieh die Datenbankinformationen">Daten</span>
+</div>
+
+<div class="tab_content" id="tab_options_div">
+
+<form name="form" id="form" action="#" method="get" onsubmit="return false;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino OpenStreetMap Router</span>
+Diese Website erlaubt Routing mit den Daten, die OpenStreetMap gesammelt hat.
+Wähle Start- und Endpunkt (klicke auf die Marker-Symbole unten), wähle die Routing-Vorgaben und dann finde den Weg.
+<div class="center">
+<a target="other" href="http://www.routino.org/">Routino Website</a>
+|
+<a target="other" href="../documentation/">Dokumentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Sprache</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="router.html.en" title="English language webpage">English</a>
+<td>(EN)
+<td><input name="language" type="radio" value="en" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_de_url" href="router.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<td><input name="language" type="radio" value="de" onchange="formSetLanguage();" checked>
+<tr>
+<td><a id="lang_fr_url" href="router.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<td><input name="language" type="radio" value="fr" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_nl_url" href="router.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<td><input name="language" type="radio" value="nl" onchange="formSetLanguage();" >
+<tr>
+<td>
+<td>(RU)
+<td><input name="language" type="radio" value="ru" onchange="formSetLanguage();" >
+</table>
+<a target="translation" href="../translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
+<span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
+<span class="hideshow_title">Wegpunkte</span>
+<div id="hideshow_waypoint_div">
+<table id="waypoints">
+<colgroup>
+<col style="width: 22px;">
+<col>
+<col style="width: 76px;">
+</colgroup>
+<tr id="waypointXXX" style="display: none;">
+<td>
+<img id="iconXXX" src="icons/marker-XXX-grey.png" title="Waypoint XXX Position - (click to add/remove on map)" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX);">
+<td>
+<span id="coordsXXX">
+<input name="lonXXX" type="text" size="7" title="Waypoint XXX Longitude" onchange="formSetCoords(XXX);">E
+<input name="latXXX" type="text" size="7" title="Waypoint XXX Latitude" onchange="formSetCoords(XXX);">N
+</span>
+<span id="searchXXX" style="display: none;">
+<input name="searchXXX" type="text" size="18" title="Waypoint XXX Location"> <!-- uses Javascript event for triggering -->
+</span>
+<td>
+<img alt="?" src="icons/waypoint-search.png" title="Search for location" onmousedown="markerSearch(XXX);" >
+<img alt="G" src="icons/waypoint-locate.png" title="Get current location" onmousedown="markerLocate(XXX);" >
+<img alt="O" src="icons/waypoint-recentre.png" title="Centre map on this waypoint" onmousedown="markerRecentre(XXX);">
+<img alt="^" src="icons/waypoint-up.png" title="Move this waypoint up" onmousedown="markerMoveUp(XXX);" >
+<img alt="+" src="icons/waypoint-add.png" title="Add waypoint after this one" onmousedown="markerAddAfter(XXX);">
+<br>
+<img alt="#" src="icons/waypoint-coords.png" title="Coordinates for location" onmousedown="markerCoords(XXX);" >
+<img alt="~" src="icons/waypoint-home.png" title="Toggle as home location" onmousedown="markerHome(XXX);" >
+<img alt="o" src="icons/waypoint-centre.png" title="Centre this waypoint on map" onmousedown="markerCentre(XXX);" >
+<img alt="v" src="icons/waypoint-down.png" title="Move this waypoint down" onmousedown="markerMoveDown(XXX);">
+<img alt="-" src="icons/waypoint-remove.png" title="Remove this waypoint" onmousedown="markerRemove(XXX);" >
+<tr id="searchresultsXXX" style="display: none;">
+<td colspan="3">
+<!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js. -->
+<tr>
+<td colspan="3" class="center">
+<input type="button" title="Rückwärts" value="Rückwärts" onmousedown="markersReverse();">
+<input type="button" title="Add a new waypoint to make a loop" value="Close loop" onmousedown="markersLoop();">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
+<span class="hideshow_title">Fortbewegungsart</span>
+<div id="hideshow_transport_div">
+<table>
+<tr><td>Fußgänger: <td><input name="transport" type="radio" value="foot" onchange="formSetTransport('foot' );">
+<tr><td>Reiter: <td><input name="transport" type="radio" value="horse" onchange="formSetTransport('horse' );">
+<tr><td>Rollstuhl:<td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair');">
+<tr><td>Fahrrad: <td><input name="transport" type="radio" value="bicycle" onchange="formSetTransport('bicycle' );">
+<tr><td>Moped: <td><input name="transport" type="radio" value="moped" onchange="formSetTransport('moped' );">
+<tr><td>Motorrad:<td><input name="transport" type="radio" value="motorcycle" onchange="formSetTransport('motorcycle');">
+<tr><td>Auto: <td><input name="transport" type="radio" value="motorcar" onchange="formSetTransport('motorcar' );">
+<tr><td>LKW: <td><input name="transport" type="radio" value="goods" onchange="formSetTransport('goods' );">
+<tr><td>Schwertransport: <td><input name="transport" type="radio" value="hgv" onchange="formSetTransport('hgv' );">
+<tr><td>Personenverkehr: <td><input name="transport" type="radio" value="psv" onchange="formSetTransport('psv' );">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Vorgaben zur Wegnutzung</span>
+<div id="hideshow_highway_div" style="display: none;">
+<table>
+<tr><td>Autobahn: <td><input name="highway-motorway" type="text" size="3" onchange="formSetHighway('motorway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('motorway' ,'+');">
+<tr><td>Schnellstraße: <td><input name="highway-trunk" type="text" size="3" onchange="formSetHighway('trunk' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('trunk' ,'+');">
+<tr><td>Bundesstraße: <td><input name="highway-primary" type="text" size="3" onchange="formSetHighway('primary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('primary' ,'+');">
+<tr><td>Landesstraße: <td><input name="highway-secondary" type="text" size="3" onchange="formSetHighway('secondary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('secondary' ,'+');">
+<tr><td>Hauptstraße: <td><input name="highway-tertiary" type="text" size="3" onchange="formSetHighway('tertiary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('tertiary' ,'+');">
+<tr><td>Straße:<td><input name="highway-unclassified" type="text" size="3" onchange="formSetHighway('unclassified','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('unclassified','+');">
+<tr><td>Wohnstraße: <td><input name="highway-residential" type="text" size="3" onchange="formSetHighway('residential' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('residential' ,'+');">
+<tr><td>Zufahrtsweg: <td><input name="highway-service" type="text" size="3" onchange="formSetHighway('service' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('service' ,'+');">
+<tr><td>Feld-(Wald-)weg: <td><input name="highway-track" type="text" size="3" onchange="formSetHighway('track' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('track' ,'+');">
+<tr><td>Fahrradweg: <td><input name="highway-cycleway" type="text" size="3" onchange="formSetHighway('cycleway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('cycleway' ,'+');">
+<tr><td>Weg: <td><input name="highway-path" type="text" size="3" onchange="formSetHighway('path' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('path' ,'+');">
+<tr><td>Fußweg: <td><input name="highway-steps" type="text" size="3" onchange="formSetHighway('steps' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('steps' ,'+');">
+<tr><td>Fähre: <td><input name="highway-ferry" type="text" size="3" onchange="formSetHighway('ferry' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Geschwindigkeitsvorgaben</span>
+<div id="hideshow_speed_div" style="display: none;">
+<table>
+<tr><td>Autobahn: <td><input name="speed-motorway" type="text" size="3" onchange="formSetSpeed('motorway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('motorway' ,'+');">
+<tr><td>Schnellstraße: <td><input name="speed-trunk" type="text" size="3" onchange="formSetSpeed('trunk' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('trunk' ,'+');">
+<tr><td>Bundesstraße: <td><input name="speed-primary" type="text" size="3" onchange="formSetSpeed('primary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('primary' ,'+');">
+<tr><td>Landesstraße: <td><input name="speed-secondary" type="text" size="3" onchange="formSetSpeed('secondary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('secondary' ,'+');">
+<tr><td>Hauptstraße: <td><input name="speed-tertiary" type="text" size="3" onchange="formSetSpeed('tertiary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('tertiary' ,'+');">
+<tr><td>Straße:<td><input name="speed-unclassified" type="text" size="3" onchange="formSetSpeed('unclassified','=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('unclassified','+');">
+<tr><td>Wohnstraße: <td><input name="speed-residential" type="text" size="3" onchange="formSetSpeed('residential' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('residential' ,'+');">
+<tr><td>Zufahrtsweg: <td><input name="speed-service" type="text" size="3" onchange="formSetSpeed('service' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('service' ,'+');">
+<tr><td>Feld-(Wald-)weg: <td><input name="speed-track" type="text" size="3" onchange="formSetSpeed('track' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('track' ,'+');">
+<tr><td>Fahrradweg: <td><input name="speed-cycleway" type="text" size="3" onchange="formSetSpeed('cycleway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('cycleway' ,'+');">
+<tr><td>Weg: <td><input name="speed-path" type="text" size="3" onchange="formSetSpeed('path' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('path' ,'+');">
+<tr><td>Fußweg: <td><input name="speed-steps" type="text" size="3" onchange="formSetSpeed('steps' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('steps' ,'+');">
+<tr><td>Fähre: <td><input name="speed-ferry" type="text" size="3" onchange="formSetSpeed('ferry' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Vorgaben zur Wegbeschaffenheit</span>
+<div id="hideshow_property_div" style="display: none;">
+<table>
+<tr><td>befestigt: <td><input name="property-paved" type="text" size="3" onchange="formSetProperty('paved' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('paved' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('paved' ,'+');">
+<tr><td>mehrspurig: <td><input name="property-multilane" type="text" size="3" onchange="formSetProperty('multilane' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('multilane' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('multilane' ,'+');">
+<tr><td>Brücken: <td><input name="property-bridge" type="text" size="3" onchange="formSetProperty('bridge' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bridge' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bridge' ,'+');">
+<tr><td>Tunnel: <td><input name="property-tunnel" type="text" size="3" onchange="formSetProperty('tunnel' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('tunnel' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('tunnel' ,'+');">
+<tr><td>Wanderweg:<td><input name="property-footroute" type="text" size="3" onchange="formSetProperty('footroute' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('footroute' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('footroute' ,'+');">
+<tr><td>Radweg:<td><input name="property-bicycleroute" type="text" size="3" onchange="formSetProperty('bicycleroute','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bicycleroute','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bicycleroute','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
+<span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
+<span class="hideshow_title">andere Vorgaben</span>
+<div id="hideshow_restriction_div" style="display: none;">
+<table>
+<tr><td>beachte Einbahnstraßen: <td><input name="restrict-oneway" type="checkbox" onchange="formSetRestriction('oneway');">
+<tr><td>beachte Abbiegeverbot:<td><input name="restrict-turns" type="checkbox" onchange="formSetRestriction('turns' );">
+</table>
+<table>
+<tr><td>Gewicht:<td><input name="restrict-weight" type="text" size="3" onchange="formSetRestriction('weight','=');"><td>tonnes<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('weight','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('weight','+');">
+<tr><td>Höhe:<td><input name="restrict-height" type="text" size="3" onchange="formSetRestriction('height','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('height','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('height','+');">
+<tr><td>Breite: <td><input name="restrict-width" type="text" size="3" onchange="formSetRestriction('width' ,'=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('width' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('width' ,'+');">
+<tr><td>Länge:<td><input name="restrict-length" type="text" size="3" onchange="formSetRestriction('length','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('length','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('length','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Suche</span>
+<input type="button" title="Find shortest route" id="shortest" value="kürzester Weg" onclick="findRoute('shortest');">
+<input type="button" title="Find quickest route" id="quickest" value="schnellste Route" onclick="findRoute('quickest');">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Links</span>
+<a id="permalink_url" href="router.html">anpassen dieser Kartenansicht</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Bearbeitet die OSM-Daten</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Hilfe</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Schnellanleitung</b>
+<br>
+Klicke auf die Marker-Bildchen (oben), um sie in der Mitte der Karte (rechts) zu positionieren. Dann 
+ziehe das Bildchen auf die genaue Position. Das Zoomen der Karte vor der Patzierung ist vermutlich am einfachsten.
+Alternativ kann man die geografische Breite und Länge in den Kästchen eintragen.
+<p>
+Wähle die Fortbewegungsart, die Vorgaben zur Wegnutzung, die Geschwindigkeitsvorgaben, 
+die Vorgaben zur Wegbeschaffenheit und die anderen Vorgaben von den obigen Auswahlfeldern.
+Ein Klick auf "kürzeste" oder "schnellste" ermittelt die entsprechende Verbindung und zeigt sie in der Karte an.
+<p>
+<b>Wegpunkte</b>
+<br>
+Ein Klick auf das Marker-Bildchen (oben) schaltet die Sichbarkeit in der Karte ein bzw. aus.
+Die Berechnung Route erfolgt in der Reihenfolge der Wegpunkte (so gut, wie es für die 
+gewählte Fortbewegungsart möglich ist).
+<p>
+<b>Fortbewegungsart</b>
+<br>
+Die Auswahl der Fortbewegungsart bestimmt die bei der Routenberechnung erlaubten Wegtypen und die 
+Vorgabeeinstellungen aller anderen Parameter.
+<p>
+<b>Vorgaben zur Wegnutzung</b>
+<br>
+Die Vorgaben zur Wegnutzung bestimmen die Priorisierung von Wegarten.
+Wenn z. B. Schnellstraßen mit 110% und Bundesstraßen mit 100% angegeben werden, wird 
+bei zwei möglichen Wegwahlen die Schnellstraße solange bevorzugt wird, wie der 
+Längen(oder Zeit-)unterschied 10% nicht überschreitet.
+<p>
+<b>Geschwindigkeitsvorgaben</b>
+<br>
+Die hier geannten Geschwindigkeiten werden für den jeweiligen Wegtyp finden Anwendung wenn keine
+andere Geschwindkeitsbegrenzung mit geringerem Wert bekannt ist.
+<p>
+<b>Vorgaben zur Wegbeschaffenheit</b>
+<br>
+Die Vorgaben zur Wegbeschaffenheit werden als Prozentangaben verwendet, um die Verhältnisse 
+der Wegbenutzung zu steuern.
+Wenn z. B. befestigte Wege mit 75% angegeben sind, werden unbefestigte automatisch mit 25% angenommen, so
+werden Wege ausgewählt, die mindestens drei mal länger auf befestigten Wegen verlaufen.
+<p>
+<b>andere Vorgaben</b>
+<br>
+Die Berücksichtigung von Benutzungs-Begrenzungen durch Gewicht, Höhe, Länge und 
+Breite ist möglich. Genauso können Einbahnstraßenbeschräkungen ignoriert werden 
+(z. B. als Fußgänger). 
+</div>
+</div>
+</div>
+</form>
+</div>
+
+
+<div class="tab_content" id="tab_results_div" style="display: none;">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_not_run">
+<b><i>Router läuft nicht</i></b>
+</div>
+<div id="result_status_running" style="display: none;">
+<b>Router läuft...</b>
+</div>
+<div id="result_status_complete" style="display: none;">
+<b>Routing fertig</b>
+<br>
+<a id="router_log_complete" target="router_log" href="#">zeige Details</a>
+</div>
+<div id="result_status_error" style="display: none;">
+<b>Router Fehler</b>
+<br>
+<a id="router_log_error" target="router_log" href="#">zeige Details</a>
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Router funktioniert nicht</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
+<span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">kürzester Weg</span>
+<div id="shortest_status">
+<div id="shortest_status_no_info">
+<b><i>keine Information</i></b>
+</div>
+<div id="shortest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_shortest_div" style="display: none;">
+<div id="shortest_links" style="display: none;">
+<table>
+<tr><td>HTML: <td><a id="shortest_html" target="shortest_html" href="#">öffne Popup</a>
+<tr><td>GPX Track-Datei: <td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">öffne Popup</a>
+<tr><td>GPX Routen-Datei: <td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">öffne Popup</a>
+<tr><td>Volltext-Datei: <td><a id="shortest_text_all" target="shortest_text_all" href="#">öffne Popup</a>
+<tr><td>Text-Datei: <td><a id="shortest_text" target="shortest_text" href="#">öffne Popup</a>
+</table>
+<hr>
+</div>
+<div id="shortest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
+<span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">schnellste Route</span>
+<div id="quickest_status">
+<div id="quickest_status_no_info">
+<b><i>keine Information</i></b>
+</div>
+<div id="quickest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_quickest_div" style="display: none;">
+<div id="quickest_links" style="display: none;">
+<table>
+<tr><td>HTML: <td><a id="quickest_html" target="quickest_html" href="#">öffne Popup</a>
+<tr><td>GPX Track-Datei: <td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">öffne Popup</a>
+<tr><td>GPX Routen-Datei: <td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">öffne Popup</a>
+<tr><td>Volltext-Datei: <td><a id="quickest_text_all" target="quickest_text_all" href="#">öffne Popup</a>
+<tr><td>Text-Datei: <td><a id="quickest_text" target="quickest_text" href="#">öffne Popup</a>
+</table>
+<hr>
+</div>
+<div id="quickest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
+<span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
+<span class="hideshow_title">Hilfe</span>
+<div id="hideshow_help_route_div">
+<div class="scrollable">
+<b>Schnellanleitung</b>
+<br>
+Nach der Routenberechnung kann man eine GPX oder eine einfache Textdatei (Kurz- oder Langfassung)
+herunterladen. Ebenso kann man die Routenbeschreibung ansehen und in ausgewälte Bereiche zoomen.
+<p>
+<b>Problemlösung</b>
+<br>
+Wenn der Router einen Fehler meldet liegt es meistens daran, dass kein Weg zwischen den gewälten Punkten unter
+Beachtung der Vorgaben gefunden werden kann. Das Bewegen eines oder mehrere Punkte oder das verändern von
+Vorgaben sollte es erlauben eine Route zu finden.
+<p>
+<b>Ausgabe-Formate</b>
+<br>
+<dl>
+<dt>HTMLs
+<dd>Eine Beschreibung der Route mit Anweisungen für jede wichtige Abzweigung.
+<dt>GPX Track-Datei
+<dd>Die gleichen Informationen, die in der Karte angezeigt werden mit Punkten für jeden Abzweig 
+und Linien für jedes Teilstück.
+<dt>GPX Routen-Datei
+<dd>Die gleichen Informationen, die im Text angezeigt werden mit einem Wegpunkt für 
+jede wichtige Richtungsänderung.
+<dt>Volltext-Datei
+<dd>Eine aller Knoten und die Abstände zwischen ihnen, sowie die Gesamtentfernung vom i
+Startpunkt zum jeweiligen Konten.
+<dt>Text-Datei
+<dd>Die gleiche Information, die als Text angezeigt wird.
+</dl>
+</div>
+</div>
+</div>
+</div>
+
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistik</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">zeige die Statistik</a>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Ansichten</span>
+Die Anzeige der Daten kann auf verschiedene Weise angepasst werden. 
+<br>
+<a id="visualiser_url" href="visualiser.html" target="visualiser">anpassen dieser Kartenansicht</a>
+</div>
+</div>
 
 </div>
 
 <!-- Right hand side of window - map -->
 
 <div class="right_panel">
-  <div class="map" id="map">
-    <noscript>
-      Javascript is <em>required</em> to use this web page because of the
-      interactive map.
-    </noscript>
-  </div>
-  <div class="attribution">
-    <a target="other" href="http://www.routino.org/" title="Routino">Router: Routino</a>
-    |
-    <a target="other" href="http://www.openstreetmap.org/" title="Copyright: OpenStreetMap.org; License: Creative Commons Attribution-Share Alike 2.0">Geo Data: OpenStreetMap</a>
-  </div>
-</div>
-
-</BODY>
-</HTML>
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript is <em>required</em> to use this web page because of the interactive map.
+</noscript>
+</div>
+<div class="attribution">
+Router: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/router.html.en b/web/www/routino/router.html.en
index 3aaf5e2..0cb348a 100644
--- a/web/www/routino/router.html.en
+++ b/web/www/routino/router.html.en
@@ -1,34 +1,33 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<html>
 
-<!--
- Routino router web page.
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routing route planner">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
 
- Part of the Routino routing software.
+<title>Routino : Route Planner for OpenStreetMap Data</title>
 
- This file Copyright 2008-2012 Andrew M. Bishop
+<!--
+Routino router web page.
 
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+Part of the Routino routing software.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
+This file Copyright 2008-2014 Andrew M. Bishop
 
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see http://www.gnu.org/licenses/.
--->
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
-<HEAD>
-<TITLE>Routino : Route Planner for OpenStreetMap Data</TITLE>
-<META name="keywords" content="openstreetmap routing route planner">
-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
 
-<!-- OpenLayers Javascript library -->
-<script src="../openlayers/OpenLayers.js" type="text/javascript"></script>
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
 
 <!-- Page elements -->
 <script src="page-elements.js" type="text/javascript"></script>
@@ -36,461 +35,460 @@
 
 <!-- Router and visualiser shared features -->
 <link href="maplayout.css" type="text/css" rel="stylesheet">
-<!--[if IE 6]>
-  <link href="maplayout-ie6-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
-<!--[if IE 7]>
-  <link href="maplayout-ie7-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
 
 <!-- Router specific features -->
 <script src="profiles.js" type="text/javascript"></script>
-<script src="mapprops.js" type="text/javascript"></script>
-<script src="router.js" type="text/javascript"></script>
 <link href="router.css" type="text/css" rel="stylesheet">
 
-</HEAD>
-<BODY onload="html_init();map_init();form_init();">
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('html_init();map_init();form_init();');">
 
 <!-- Left hand side of window - data panel -->
 
 <div class="left_panel">
 
-  <div class="tab_box">
-    <span id="tab_options" onclick="tab_select('options');" class="tab_selected"   title="Set routing options">Options</span>
-    <span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="See routing results">Results</span>
-    <span id="tab_data"    onclick="tab_select('data');"    class="tab_unselected" title="View database information">Data</span>
-  </div>
-
-  <div class="tab_content" id="tab_options_div">
-
-    <form name="form" id="form" action="" method="get" onsubmit="return false;">
-      <div class="hideshow_box">
-        <span class="hideshow_title">Routino OpenStreetMap Router</span>
-        This web page allows routing within the data collected by OpenStreetMap.
-        Select start and end points (click on the marker icons below), select routing preferences then find a route.
-        <div align="center">
-          <a target="other" href="http://www.routino.org/">Routino Website</a>
-          |
-          <a target="other" href="documentation/">Documentation</a>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
-        <span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Language</span>
-
-        <!-- Note for translations: Only this HTML file needs to be translated, the Javascript has
-             no language specific information in it.  Only the body text and title attributes should
-             be changed, the values passed to the JavaScript and the element names must not be changed.
-             The selection below changes the language option passed to the router and selects the
-             output language not the web page language, the links are for that.  The router itself uses
-             the translations.xml file for the translated versions of the output. -->
-
-        <div id="hideshow_language_div" style="display: none;">
-          <table>
-            <tr>
-              <td><a id="lang_en_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html.en" title="English language web page">English</a>
-              <td>(EN)
-              <td><input name="language" type="radio" value="en" onchange="formSetLanguage()" checked>
-            <tr>
-              <td><a id="lang_de_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html.de" title="Deutsche Webseite">German</a>
-              <td>(DE)
-              <td><input name="language" type="radio" value="de" onchange="formSetLanguage()">
-            <tr>
-              <td><a id="lang_nl_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html.nl" title="Dutch language web page">Dutch</a>
-              <td>(NL)
-              <td><input name="language" type="radio" value="nl" onchange="formSetLanguage()">
-            <tr>
-              <td>Russian
-              <td>(RU)
-              <td><input name="language" type="radio" value="ru" onchange="formSetLanguage()">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
-        <span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Waypoints</span>
-        <div id="hideshow_waypoint_div">
-          <table id="waypoints">
-            <colgroup>
-              <col style="width: 25px;">
-              <col>
-              <col style="width: 76px;">
-            </colgroup>
-            <tr id="waypointXXX" style="display: none;">
-              <td>
-                <img name="waypointXXX" src="icons/marker-XXX-grey.png" title="Waypoint XXX Position - (click to add/remove on map)" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX)"> 
-              <td>
-                <span id="coordsXXX">
-                  <input name="lonXXX" type="text" size="7" title="Waypoint XXX Longitude" onchange="formSetCoords(XXX);">E
-                  <input name="latXXX" type="text" size="7" title="Waypoint XXX Latitude"  onchange="formSetCoords(XXX);">N
-                </span>
-                <span id="searchXXX" style="display: none;">
-                  <input name="searchXXX" type="text" size="18" title="Waypoint XXX Location"> <!-- uses Javascript event for triggering -->
-                </span>
-              <td>
-                <img alt="?" src="icons/waypoint-search.png"   title="Search for location"         onmousedown="markerSearch(XXX);"  >
-                <img alt="G" src="icons/waypoint-locate.png"   title="Get current location"        onmousedown="markerLocate(XXX);"  >
-                <img alt="O" src="icons/waypoint-recentre.png" title="Centre map on this waypoint" onmousedown="markerRecentre(XXX);">
-                <img alt="^" src="icons/waypoint-up.png"       title="Move this waypoint up"       onmousedown="markerMoveUp(XXX);"  >
-                <img alt="+" src="icons/waypoint-add.png"      title="Add waypoint after this one" onmousedown="markerAddAfter(XXX);">
-                <br>
-                <img alt="#" src="icons/waypoint-coords.png"   title="Coordinates for location"    onmousedown="markerCoords(XXX);"  >
-                <img alt="~" src="icons/waypoint-home.png"     title="Toggle as home location"     onmousedown="markerHome(XXX);"    >
-                <img alt="o" src="icons/waypoint-centre.png"   title="Centre this waypoint on map" onmousedown="markerCentre(XXX);"  >
-                <img alt="v" src="icons/waypoint-down.png"     title="Move this waypoint down"     onmousedown="markerMoveDown(XXX);">
-                <img alt="-" src="icons/waypoint-remove.png"   title="Remove this waypoint"        onmousedown="markerRemove(XXX);"  >
-            <tr id="searchresultsXXX" style="display: none;">
-              <td colspan="3">
-            <!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js.  -->
-            <tr>
-              <td colspan="3" align="center">
-                <input type="button" title="Reverse order of waypoints"        value="Reverse order" onmousedown="markersReverse();">
-                <input type="button" title="Add a new waypoint to make a loop" value="Close loop"    onmousedown="markersLoop();">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
-        <span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Transport Type</span>
-        <div id="hideshow_transport_div">
-          <table>
-            <tr><td>Foot      <td><input name="transport" type="radio" value="foot"       onchange="formSetTransport('foot'      )">
-            <tr><td>Horse     <td><input name="transport" type="radio" value="horse"      onchange="formSetTransport('horse'     )">
-            <tr><td>Wheelchair<td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair')">
-            <tr><td>Bicycle   <td><input name="transport" type="radio" value="bicycle"    onchange="formSetTransport('bicycle'   )">
-            <tr><td>Moped     <td><input name="transport" type="radio" value="moped"      onchange="formSetTransport('moped'     )">
-            <tr><td>Motorbike <td><input name="transport" type="radio" value="motorbike"  onchange="formSetTransport('motorbike' )">
-            <tr><td>Motorcar  <td><input name="transport" type="radio" value="motorcar"   onchange="formSetTransport('motorcar'  )">
-            <tr><td>Goods     <td><input name="transport" type="radio" value="goods"      onchange="formSetTransport('goods'     )">
-            <tr><td>HGV       <td><input name="transport" type="radio" value="hgv"        onchange="formSetTransport('hgv'       )">
-            <tr><td>PSV       <td><input name="transport" type="radio" value="psv"        onchange="formSetTransport('psv'       )">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
-        <span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Highway Preferences</span>
-        <div id="hideshow_highway_div" style="display: none;">
-          <table>
-            <tr><td>Motorway:    <td><input name="highway-motorway"     type="text" size=3 onchange="formSetHighway('motorway'    )"><td>%
-            <tr><td>Trunk:       <td><input name="highway-trunk"        type="text" size=3 onchange="formSetHighway('trunk'       )"><td>%
-            <tr><td>Primary:     <td><input name="highway-primary"      type="text" size=3 onchange="formSetHighway('primary'     )"><td>%
-            <tr><td>Secondary:   <td><input name="highway-secondary"    type="text" size=3 onchange="formSetHighway('secondary'   )"><td>%
-            <tr><td>Tertiary:    <td><input name="highway-tertiary"     type="text" size=3 onchange="formSetHighway('tertiary'    )"><td>%
-            <tr><td>Unclassified:<td><input name="highway-unclassified" type="text" size=3 onchange="formSetHighway('unclassified')"><td>%
-            <tr><td>Residential: <td><input name="highway-residential"  type="text" size=3 onchange="formSetHighway('residential' )"><td>%
-            <tr><td>Service:     <td><input name="highway-service"      type="text" size=3 onchange="formSetHighway('service'     )"><td>%
-            <tr><td>Track:       <td><input name="highway-track"        type="text" size=3 onchange="formSetHighway('track'       )"><td>%
-            <tr><td>Cycleway:    <td><input name="highway-cycleway"     type="text" size=3 onchange="formSetHighway('cycleway'    )"><td>%
-            <tr><td>Path:        <td><input name="highway-path"         type="text" size=3 onchange="formSetHighway('path'        )"><td>%
-            <tr><td>Steps:       <td><input name="highway-steps"        type="text" size=3 onchange="formSetHighway('steps'       )"><td>%
-            <tr><td>Ferry:       <td><input name="highway-ferry"        type="text" size=3 onchange="formSetHighway('ferry'       )"><td>%
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
-        <span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Speed Limits</span>
-        <div id="hideshow_speed_div" style="display: none;">
-          <table>
-            <tr><td>Motorway:    <td><input name="speed-motorway"     type="text" size=3 onchange="formSetSpeed('motorway'    )"><td>km/hr
-            <tr><td>Trunk:       <td><input name="speed-trunk"        type="text" size=3 onchange="formSetSpeed('trunk'       )"><td>km/hr
-            <tr><td>Primary:     <td><input name="speed-primary"      type="text" size=3 onchange="formSetSpeed('primary'     )"><td>km/hr
-            <tr><td>Secondary:   <td><input name="speed-secondary"    type="text" size=3 onchange="formSetSpeed('secondary'   )"><td>km/hr
-            <tr><td>Tertiary:    <td><input name="speed-tertiary"     type="text" size=3 onchange="formSetSpeed('tertiary'    )"><td>km/hr
-            <tr><td>Unclassified:<td><input name="speed-unclassified" type="text" size=3 onchange="formSetSpeed('unclassified')"><td>km/hr
-            <tr><td>Residential: <td><input name="speed-residential"  type="text" size=3 onchange="formSetSpeed('residential' )"><td>km/hr
-            <tr><td>Service:     <td><input name="speed-service"      type="text" size=3 onchange="formSetSpeed('service'     )"><td>km/hr
-            <tr><td>Track:       <td><input name="speed-track"        type="text" size=3 onchange="formSetSpeed('track'       )"><td>km/hr
-            <tr><td>Cycleway:    <td><input name="speed-cycleway"     type="text" size=3 onchange="formSetSpeed('cycleway'    )"><td>km/hr
-            <tr><td>Path:        <td><input name="speed-path"         type="text" size=3 onchange="formSetSpeed('path'        )"><td>km/hr
-            <tr><td>Steps:       <td><input name="speed-steps"        type="text" size=3 onchange="formSetSpeed('steps'       )"><td>km/hr
-            <tr><td>Ferry:       <td><input name="speed-ferry"        type="text" size=3 onchange="formSetSpeed('ferry'       )"><td>km/hr
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
-        <span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Property Preferences</span>
-        <div id="hideshow_property_div" style="display: none;">
-          <table>
-            <tr><td>Paved:         <td><input name="property-paved"        type="text" size=3 onchange="formSetProperty('paved'       )"><td>%
-            <tr><td>Multiple Lanes:<td><input name="property-multilane"    type="text" size=3 onchange="formSetProperty('multilane'   )"><td>%
-            <tr><td>Bridge:        <td><input name="property-bridge"       type="text" size=3 onchange="formSetProperty('bridge'      )"><td>%
-            <tr><td>Tunnel:        <td><input name="property-tunnel"       type="text" size=3 onchange="formSetProperty('tunnel'      )"><td>%
-            <tr><td>Walking Route: <td><input name="property-footroute"    type="text" size=3 onchange="formSetProperty('footroute'   )"><td>%
-            <tr><td>Bicycle Route: <td><input name="property-bicycleroute" type="text" size=3 onchange="formSetProperty('bicycleroute')"><td>%
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
-        <span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Other Restrictions</span>
-        <div id="hideshow_restriction_div" style="display: none;">
-          <table>
-            <tr><td>Obey oneway:<td><input name="restrict-oneway" type="checkbox"    onchange="formSetRestriction('oneway')"><td>
-            <tr><td>Obey turns: <td><input name="restrict-turns"  type="checkbox"    onchange="formSetRestriction('turns' )"><td>
-            <tr><td>Weight:     <td><input name="restrict-weight" type="text" size=3 onchange="formSetRestriction('weight')"><td>tonnes
-            <tr><td>Height:     <td><input name="restrict-height" type="text" size=3 onchange="formSetRestriction('height')"><td>metres
-            <tr><td>Width:      <td><input name="restrict-width"  type="text" size=3 onchange="formSetRestriction('width' )"><td>metres
-            <tr><td>Length:     <td><input name="restrict-length" type="text" size=3 onchange="formSetRestriction('length')"><td>metres
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span class="hideshow_title">Find</span>
-        <input type="button" title="Find shortest route" id="shortest" value="Shortest" onclick="findRoute('shortest');">
-        <input type="button" title="Find quickest route" id="quickest" value="Quickest" onclick="findRoute('quickest');">
-      </div>
-
-      <div class="hideshow_box">
-        <span class="hideshow_title">Links</span>
-        <a id="permalink_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html">Permanent link to these parameters</a>
-        <br>
-        <a id="edit_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="http://www.openstreetmap.org/" target="edit">Edit OSM data in Potlatch</a>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
-        <span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Help</span>
-        <div id="hideshow_help_options_div">
-          <div class="scrollable">
-            <p>
-            <b>Quick Start</b>
-            <br>
-            Click on marker icons (above) to place them on the map (right).  Then
-            drag them to the correct position.  Zooming the map before placing the
-            markers is probably easiest.  Alternatively type the latitude and
-            longitude into the boxes above.
-            <p>
-            Select the transport type, allowed highway types, speed limits, highway
-            properties and other restrictions from the options above.
-            Select "Shortest" or "Quickest" to calculate the route and display it
-            on the map.
-            <p>
-            <b>Waypoints</b>
-            <br>
-            Clicking on the marker icons will toggle the display of them on the map.
-            When a route is calculated it will visit (as close as possible
-            for the selected transport type) each of the waypoints that have
-            markers on the map in the order given.
-            <p>
-            <b>Transport Type</b>
-            <br>
-            Selecting a transport type will restrict the chosen route to
-            those on which it is allowed and set default values for the
-            other parameters.
-            <p>
-            <b>Highway Preferences</b>
-            <br>
-            The highway preference is selected as a percentage and routes are chosen that
-            try to follow the preferred highways.
-            For example if a "Primary" road is given a "110%" preference and a "Secondary"
-            road is given a "100%" preference then it means that a route on a Primary road
-            can be up to 10% longer than on a secondary road and still be selected.
-            <p>
-            <b>Speed Limits</b>
-            <br>
-            The speed limits chosen here for the different types of highway apply if the
-            highway has no other speed limit marked or it is higher than the chosen one.
-            <p>
-            <b>Property Preferences</b>
-            <br>
-            The property preference is selected as a percentage and routes are chosen that
-            try to follow highways with the preferred property.
-            For example if a "Paved" highway is given a "75%" preference then it means that
-            an unpaved highway is automatically given a "25%" preference so that a route on
-            a paved highway can be 3 times the length of an unpaved one and still be
-            selected.
-            <p>
-            <b>Other Restrictions</b>
-            <br>
-            These allow a route to be found that avoids marked limits on
-            weight, height, width or length.  It is also possible to ignore
-            one-way restrictions (e.g. if walking).
-          </div>
-        </div>
-      </div>
-    </form>
-  </div>
-
-
-  <div class="tab_content" id="tab_results_div" style="display: none;">
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Status</span>
-      <div id="result_status">
-        <div id="result_status_not_run">
-          <b><i>Router not run</i></b>
-        </div>
-        <div id="result_status_running"  style="display: none;">
-          <b>Router running...</b>
-        </div>
-        <div id="result_status_complete" style="display: none;">
-          <b>Routing completed</b>
-          <br>
-          <a id="router_log_complete" target="router_log" href="#">View Details</a>
-        </div>
-        <div id="result_status_error"    style="display: none;">
-          <b>Router error</b>
-          <br>
-          <a id="router_log_error" target="router_log" href="#">View Details</a>
-        </div>
-        <div id="result_status_failed"   style="display: none;">
-          <b>Router failed to run</b>
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
-      <span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
-      <span class="hideshow_title">Shortest Route</span>
-      <div id="shortest_status">
-        <div id="shortest_status_no_info">
-          <b><i>No Information</i></b>
-        </div>
-        <div id="shortest_status_info" style="display: none;">
-        </div>
-      </div>
-      <div id="hideshow_shortest_div" style="display: none;">
-        <div id="shortest_links" style="display: none;">
-          <table>
-            <tr><td>HTML directions:<td><a id="shortest_html"      target="shortest_html"      href="#">Open Popup</a>
-            <tr><td>GPX track file: <td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">Open Popup</a>
-            <tr><td>GPX route file: <td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">Open Popup</a>
-            <tr><td>Full text file: <td><a id="shortest_text_all"  target="shortest_text_all"  href="#">Open Popup</a>
-            <tr><td>Text file:      <td><a id="shortest_text"      target="shortest_text"      href="#">Open Popup</a>
-          </table>
-          <hr>
-        </div>
-        <div id="shortest_route">
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
-      <span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
-      <span class="hideshow_title">Quickest Route</span>
-      <div id="quickest_status">
-        <div id="quickest_status_no_info">
-          <b><i>No Information</i></b>
-        </div>
-        <div id="quickest_status_info" style="display: none;">
-        </div>
-      </div>
-      <div id="hideshow_quickest_div" style="display: none;">
-        <div id="quickest_links" style="display: none;">
-          <table>
-            <tr><td>HTML directions:<td><a id="quickest_html"      target="quickest_html"      href="#">Open Popup</a>
-            <tr><td>GPX track file: <td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">Open Popup</a>
-            <tr><td>GPX route file: <td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">Open Popup</a>
-            <tr><td>Full text file: <td><a id="quickest_text_all"  target="quickest_text_all"  href="#">Open Popup</a>
-            <tr><td>Text file:      <td><a id="quickest_text"      target="quickest_text"      href="#">Open Popup</a>
-          </table>
-          <hr>
-        </div>
-        <div id="quickest_route">
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
-      <span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
-      <span class="hideshow_title">Help</span>
-      <div id="hideshow_help_route_div">
-        <div class="scrollable">
-          <p>
-          <b>Quick Start</b>
-          <br>
-          After calculating a route you can download the GPX file or plain
-          text route description (summary or detailed version). Also you
-          can view the route description and zoom in to selected parts.
-          <p>
-          <b>Problem Solving</b>
-          <br>
-          If the router completes with an error then the most likely cause is
-          that it is not possible to find a route between the selected points.
-          Moving one or more markers or changing the routing options should
-          allow a route to be found.
-          <p>
-          <b>Output Formats</b>
-          <br>
-          <dl>
-            <dt>HTML instructions
-            <dd>A description of the route to take with directions at each
-              important junction.
-            <dt>GPX track file
-            <dd>The same information that is displayed on the map with points
-              for every node and lines for every segment.
-            <dt>GPX route file
-            <dd>The same information that is displayed in text for the route
-              with a waypoint for each important junction in the route.
-            <dt>Full text file
-            <dd>A list of all of the nodes visited as well as the distance
-              between them and the cumulative distance for each step of the
-              route.
-            <dt>Text file
-            <dd>The same information that is displayed in text for the route.
-          </dl>
-        </div>
-      </div>
-    </div>
-  </div>
-
-
-  <div class="tab_content" id="tab_data_div" style="display: none;">
-    <div class="hideshow_box">
-      <span class="hideshow_title">Statistics</span>
-      <div id="statistics_data"></div>
-      <a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
-    </div>
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Visualiser</span>
-      To see Routino's view of the data there is a data visualiser that allows
-      displaying of the underlying data in various ways.
-      <br>
-      <a id="visualiser_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="visualiser.html" target="visualiser">Custom link to this map view</a>
-    </div>
-  </div>
+<div class="tab_box">
+<span id="tab_options" onclick="tab_select('options');" class="tab_selected" title="Set routing options">Options</span>
+<span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="See routing results">Results</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="View database information">Data</span>
+</div>
+
+<div class="tab_content" id="tab_options_div">
+
+<form name="form" id="form" action="#" method="get" onsubmit="return false;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino OpenStreetMap Router</span>
+This web page allows routing within the data collected by OpenStreetMap.
+Select start and end points (click on the marker icons below), select routing preferences then find a route.
+<div class="center">
+<a target="other" href="http://www.routino.org/">Routino Website</a>
+|
+<a target="other" href="documentation/">Documentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Language</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="router.html.en" title="English language webpage">English</a>
+<td>(EN)
+<td><input name="language" type="radio" value="en" onchange="formSetLanguage();" checked>
+<tr>
+<td><a id="lang_de_url" href="router.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<td><input name="language" type="radio" value="de" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_fr_url" href="router.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<td><input name="language" type="radio" value="fr" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_nl_url" href="router.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<td><input name="language" type="radio" value="nl" onchange="formSetLanguage();" >
+<tr>
+<td>
+<td>(RU)
+<td><input name="language" type="radio" value="ru" onchange="formSetLanguage();" >
+</table>
+<a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
+<span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
+<span class="hideshow_title">Waypoints</span>
+<div id="hideshow_waypoint_div">
+<table id="waypoints">
+<colgroup>
+<col style="width: 22px;">
+<col>
+<col style="width: 76px;">
+</colgroup>
+<tr id="waypointXXX" style="display: none;">
+<td>
+<img id="iconXXX" src="icons/marker-XXX-grey.png" title="Waypoint XXX Position - (click to add/remove on map)" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX);">
+<td>
+<span id="coordsXXX">
+<input name="lonXXX" type="text" size="7" title="Waypoint XXX Longitude" onchange="formSetCoords(XXX);">E
+<input name="latXXX" type="text" size="7" title="Waypoint XXX Latitude" onchange="formSetCoords(XXX);">N
+</span>
+<span id="searchXXX" style="display: none;">
+<input name="searchXXX" type="text" size="18" title="Waypoint XXX Location"> <!-- uses Javascript event for triggering -->
+</span>
+<td>
+<img alt="?" src="icons/waypoint-search.png" title="Search for location" onmousedown="markerSearch(XXX);" >
+<img alt="G" src="icons/waypoint-locate.png" title="Get current location" onmousedown="markerLocate(XXX);" >
+<img alt="O" src="icons/waypoint-recentre.png" title="Centre map on this waypoint" onmousedown="markerRecentre(XXX);">
+<img alt="^" src="icons/waypoint-up.png" title="Move this waypoint up" onmousedown="markerMoveUp(XXX);" >
+<img alt="+" src="icons/waypoint-add.png" title="Add waypoint after this one" onmousedown="markerAddAfter(XXX);">
+<br>
+<img alt="#" src="icons/waypoint-coords.png" title="Coordinates for location" onmousedown="markerCoords(XXX);" >
+<img alt="~" src="icons/waypoint-home.png" title="Toggle as home location" onmousedown="markerHome(XXX);" >
+<img alt="o" src="icons/waypoint-centre.png" title="Centre this waypoint on map" onmousedown="markerCentre(XXX);" >
+<img alt="v" src="icons/waypoint-down.png" title="Move this waypoint down" onmousedown="markerMoveDown(XXX);">
+<img alt="-" src="icons/waypoint-remove.png" title="Remove this waypoint" onmousedown="markerRemove(XXX);" >
+<tr id="searchresultsXXX" style="display: none;">
+<td colspan="3">
+<!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js. -->
+<tr>
+<td colspan="3" class="center">
+<input type="button" title="Reverse order of waypoints" value="Reverse order" onmousedown="markersReverse();">
+<input type="button" title="Add a new waypoint to make a loop" value="Close loop" onmousedown="markersLoop();">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
+<span class="hideshow_title">Transport Type</span>
+<div id="hideshow_transport_div">
+<table>
+<tr><td>Foot: <td><input name="transport" type="radio" value="foot" onchange="formSetTransport('foot' );">
+<tr><td>Horse: <td><input name="transport" type="radio" value="horse" onchange="formSetTransport('horse' );">
+<tr><td>Wheelchair:<td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair');">
+<tr><td>Bicycle: <td><input name="transport" type="radio" value="bicycle" onchange="formSetTransport('bicycle' );">
+<tr><td>Moped: <td><input name="transport" type="radio" value="moped" onchange="formSetTransport('moped' );">
+<tr><td>Motorcycle:<td><input name="transport" type="radio" value="motorcycle" onchange="formSetTransport('motorcycle');">
+<tr><td>Motorcar: <td><input name="transport" type="radio" value="motorcar" onchange="formSetTransport('motorcar' );">
+<tr><td>Goods: <td><input name="transport" type="radio" value="goods" onchange="formSetTransport('goods' );">
+<tr><td>HGV: <td><input name="transport" type="radio" value="hgv" onchange="formSetTransport('hgv' );">
+<tr><td>PSV: <td><input name="transport" type="radio" value="psv" onchange="formSetTransport('psv' );">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Highway Preferences</span>
+<div id="hideshow_highway_div" style="display: none;">
+<table>
+<tr><td>Motorway: <td><input name="highway-motorway" type="text" size="3" onchange="formSetHighway('motorway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('motorway' ,'+');">
+<tr><td>Trunk: <td><input name="highway-trunk" type="text" size="3" onchange="formSetHighway('trunk' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('trunk' ,'+');">
+<tr><td>Primary: <td><input name="highway-primary" type="text" size="3" onchange="formSetHighway('primary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('primary' ,'+');">
+<tr><td>Secondary: <td><input name="highway-secondary" type="text" size="3" onchange="formSetHighway('secondary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('secondary' ,'+');">
+<tr><td>Tertiary: <td><input name="highway-tertiary" type="text" size="3" onchange="formSetHighway('tertiary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('tertiary' ,'+');">
+<tr><td>Unclassified:<td><input name="highway-unclassified" type="text" size="3" onchange="formSetHighway('unclassified','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('unclassified','+');">
+<tr><td>Residential: <td><input name="highway-residential" type="text" size="3" onchange="formSetHighway('residential' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('residential' ,'+');">
+<tr><td>Service: <td><input name="highway-service" type="text" size="3" onchange="formSetHighway('service' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('service' ,'+');">
+<tr><td>Track: <td><input name="highway-track" type="text" size="3" onchange="formSetHighway('track' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('track' ,'+');">
+<tr><td>Cycleway: <td><input name="highway-cycleway" type="text" size="3" onchange="formSetHighway('cycleway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('cycleway' ,'+');">
+<tr><td>Path: <td><input name="highway-path" type="text" size="3" onchange="formSetHighway('path' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('path' ,'+');">
+<tr><td>Steps: <td><input name="highway-steps" type="text" size="3" onchange="formSetHighway('steps' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('steps' ,'+');">
+<tr><td>Ferry: <td><input name="highway-ferry" type="text" size="3" onchange="formSetHighway('ferry' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Speed Limits</span>
+<div id="hideshow_speed_div" style="display: none;">
+<table>
+<tr><td>Motorway: <td><input name="speed-motorway" type="text" size="3" onchange="formSetSpeed('motorway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('motorway' ,'+');">
+<tr><td>Trunk: <td><input name="speed-trunk" type="text" size="3" onchange="formSetSpeed('trunk' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('trunk' ,'+');">
+<tr><td>Primary: <td><input name="speed-primary" type="text" size="3" onchange="formSetSpeed('primary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('primary' ,'+');">
+<tr><td>Secondary: <td><input name="speed-secondary" type="text" size="3" onchange="formSetSpeed('secondary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('secondary' ,'+');">
+<tr><td>Tertiary: <td><input name="speed-tertiary" type="text" size="3" onchange="formSetSpeed('tertiary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('tertiary' ,'+');">
+<tr><td>Unclassified:<td><input name="speed-unclassified" type="text" size="3" onchange="formSetSpeed('unclassified','=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('unclassified','+');">
+<tr><td>Residential: <td><input name="speed-residential" type="text" size="3" onchange="formSetSpeed('residential' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('residential' ,'+');">
+<tr><td>Service: <td><input name="speed-service" type="text" size="3" onchange="formSetSpeed('service' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('service' ,'+');">
+<tr><td>Track: <td><input name="speed-track" type="text" size="3" onchange="formSetSpeed('track' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('track' ,'+');">
+<tr><td>Cycleway: <td><input name="speed-cycleway" type="text" size="3" onchange="formSetSpeed('cycleway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('cycleway' ,'+');">
+<tr><td>Path: <td><input name="speed-path" type="text" size="3" onchange="formSetSpeed('path' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('path' ,'+');">
+<tr><td>Steps: <td><input name="speed-steps" type="text" size="3" onchange="formSetSpeed('steps' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('steps' ,'+');">
+<tr><td>Ferry: <td><input name="speed-ferry" type="text" size="3" onchange="formSetSpeed('ferry' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Property Preferences</span>
+<div id="hideshow_property_div" style="display: none;">
+<table>
+<tr><td>Paved: <td><input name="property-paved" type="text" size="3" onchange="formSetProperty('paved' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('paved' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('paved' ,'+');">
+<tr><td>Multiple Lanes: <td><input name="property-multilane" type="text" size="3" onchange="formSetProperty('multilane' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('multilane' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('multilane' ,'+');">
+<tr><td>Bridge: <td><input name="property-bridge" type="text" size="3" onchange="formSetProperty('bridge' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bridge' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bridge' ,'+');">
+<tr><td>Tunnel: <td><input name="property-tunnel" type="text" size="3" onchange="formSetProperty('tunnel' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('tunnel' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('tunnel' ,'+');">
+<tr><td>Walking Route:<td><input name="property-footroute" type="text" size="3" onchange="formSetProperty('footroute' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('footroute' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('footroute' ,'+');">
+<tr><td>Bicycle Route:<td><input name="property-bicycleroute" type="text" size="3" onchange="formSetProperty('bicycleroute','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bicycleroute','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bicycleroute','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
+<span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Other Restrictions</span>
+<div id="hideshow_restriction_div" style="display: none;">
+<table>
+<tr><td>Obey oneway streets: <td><input name="restrict-oneway" type="checkbox" onchange="formSetRestriction('oneway');">
+<tr><td>Obey turn restrictions:<td><input name="restrict-turns" type="checkbox" onchange="formSetRestriction('turns' );">
+</table>
+<table>
+<tr><td>Weight:<td><input name="restrict-weight" type="text" size="3" onchange="formSetRestriction('weight','=');"><td>tonnes<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('weight','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('weight','+');">
+<tr><td>Height:<td><input name="restrict-height" type="text" size="3" onchange="formSetRestriction('height','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('height','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('height','+');">
+<tr><td>Width: <td><input name="restrict-width" type="text" size="3" onchange="formSetRestriction('width' ,'=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('width' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('width' ,'+');">
+<tr><td>Length:<td><input name="restrict-length" type="text" size="3" onchange="formSetRestriction('length','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('length','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('length','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Find</span>
+<input type="button" title="Find shortest route" id="shortest" value="Shortest Route" onclick="findRoute('shortest');">
+<input type="button" title="Find quickest route" id="quickest" value="Quickest Route" onclick="findRoute('quickest');">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Links</span>
+<a id="permalink_url" href="router.html">Link to this map view</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Edit this OSM data</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Help</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+Click on marker icons (above) to place them on the map (right). Then
+drag them to the correct position. Zooming the map before placing the
+markers is probably easiest. Alternatively type the latitude and
+longitude into the boxes above.
+<p>
+Select the transport type, allowed highway types, speed limits, highway
+properties and other restrictions from the options above.
+Select "Shortest" or "Quickest" to calculate the route and display it
+on the map.
+<p>
+<b>Waypoints</b>
+<br>
+Clicking on the marker icons will toggle the display of them on the map.
+When a route is calculated it will visit (as close as possible
+for the selected transport type) each of the waypoints that have
+markers on the map in the order given.
+<p>
+<b>Transport Type</b>
+<br>
+Selecting a transport type will restrict the chosen route to
+those on which it is allowed and set default values for the
+other parameters.
+<p>
+<b>Highway Preferences</b>
+<br>
+The highway preference is selected as a percentage and routes are chosen that
+try to follow the preferred highways.
+For example if a "Primary" road is given a "110%" preference and a "Secondary"
+road is given a "100%" preference then it means that a route on a Primary road
+can be up to 10% longer than on a secondary road and still be selected.
+<p>
+<b>Speed Limits</b>
+<br>
+The speed limits chosen here for the different types of highway apply if the
+highway has no other speed limit marked or it is higher than the chosen one.
+<p>
+<b>Property Preferences</b>
+<br>
+The property preference is selected as a percentage and routes are chosen that
+try to follow highways with the preferred property.
+For example if a "Paved" highway is given a "75%" preference then it means that
+an unpaved highway is automatically given a "25%" preference so that a route on
+a paved highway can be 3 times the length of an unpaved one and still be
+selected.
+<p>
+<b>Other Restrictions</b>
+<br>
+These allow a route to be found that avoids marked limits on
+weight, height, width or length. It is also possible to ignore
+one-way restrictions (e.g. if walking).
+</div>
+</div>
+</div>
+</form>
+</div>
+
+
+<div class="tab_content" id="tab_results_div" style="display: none;">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_not_run">
+<b><i>Router not run</i></b>
+</div>
+<div id="result_status_running" style="display: none;">
+<b>Router running...</b>
+</div>
+<div id="result_status_complete" style="display: none;">
+<b>Routing completed</b>
+<br>
+<a id="router_log_complete" target="router_log" href="#">View Details</a>
+</div>
+<div id="result_status_error" style="display: none;">
+<b>Router error</b>
+<br>
+<a id="router_log_error" target="router_log" href="#">View Details</a>
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Router failed to run</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
+<span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Shortest Route</span>
+<div id="shortest_status">
+<div id="shortest_status_no_info">
+<b><i>No Information</i></b>
+</div>
+<div id="shortest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_shortest_div" style="display: none;">
+<div id="shortest_links" style="display: none;">
+<table>
+<tr><td>HTML directions: <td><a id="shortest_html" target="shortest_html" href="#">Open Popup</a>
+<tr><td>GPX track file: <td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">Open Popup</a>
+<tr><td>GPX route file: <td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">Open Popup</a>
+<tr><td>Full text file: <td><a id="shortest_text_all" target="shortest_text_all" href="#">Open Popup</a>
+<tr><td>Text file: <td><a id="shortest_text" target="shortest_text" href="#">Open Popup</a>
+</table>
+<hr>
+</div>
+<div id="shortest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
+<span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Quickest Route</span>
+<div id="quickest_status">
+<div id="quickest_status_no_info">
+<b><i>No Information</i></b>
+</div>
+<div id="quickest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_quickest_div" style="display: none;">
+<div id="quickest_links" style="display: none;">
+<table>
+<tr><td>HTML directions: <td><a id="quickest_html" target="quickest_html" href="#">Open Popup</a>
+<tr><td>GPX track file: <td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">Open Popup</a>
+<tr><td>GPX route file: <td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">Open Popup</a>
+<tr><td>Full text file: <td><a id="quickest_text_all" target="quickest_text_all" href="#">Open Popup</a>
+<tr><td>Text file: <td><a id="quickest_text" target="quickest_text" href="#">Open Popup</a>
+</table>
+<hr>
+</div>
+<div id="quickest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
+<span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
+<span class="hideshow_title">Help</span>
+<div id="hideshow_help_route_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+After calculating a route you can download the GPX file or plain
+text route description (summary or detailed version). Also you
+can view the route description and zoom in to selected parts.
+<p>
+<b>Problem Solving</b>
+<br>
+If the router completes with an error then the most likely cause is
+that it is not possible to find a route between the selected points.
+Moving one or more markers or changing the routing options should
+allow a route to be found.
+<p>
+<b>Output Formats</b>
+<br>
+<dl>
+<dt>HTML instructions
+<dd>A description of the route to take with directions at each
+important junction.
+<dt>GPX track file
+<dd>The same information that is displayed on the map with points
+for every node and lines for every segment.
+<dt>GPX route file
+<dd>The same information that is displayed in text for the route
+with a waypoint for each important junction in the route.
+<dt>Full text file
+<dd>A list of all of the nodes visited as well as the distance
+between them and the cumulative distance for each step of the
+route.
+<dt>Text file
+<dd>The same information that is displayed in text for the route.
+</dl>
+</div>
+</div>
+</div>
+</div>
+
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistics</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Visualiser</span>
+To see Routino's view of the data there is a data visualiser that allows
+displaying of the underlying data in various ways.
+<br>
+<a id="visualiser_url" href="visualiser.html" target="visualiser">Link to this map view</a>
+</div>
+</div>
 
 </div>
 
 <!-- Right hand side of window - map -->
 
 <div class="right_panel">
-  <div class="map" id="map">
-    <noscript>
-      Javascript is <em>required</em> to use this web page because of the
-      interactive map.
-    </noscript>
-  </div>
-  <div class="attribution">
-    <a target="other" href="http://www.routino.org/" title="Routino">Router: Routino</a>
-    |
-    <a target="other" href="http://www.openstreetmap.org/" title="Copyright: OpenStreetMap.org; License: Creative Commons Attribution-Share Alike 2.0">Geo Data: OpenStreetMap</a>
-  </div>
-</div>
-
-</BODY>
-</HTML>
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript is <em>required</em> to use this web page because of the interactive map.
+</noscript>
+</div>
+<div class="attribution">
+Router: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/router.html.fr b/web/www/routino/router.html.fr
new file mode 100644
index 0000000..2e5054c
--- /dev/null
+++ b/web/www/routino/router.html.fr
@@ -0,0 +1,493 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routing route planner">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
+
+<title>Routino : Calculateur d'itinéraire pour OpenStreetMap</title>
+
+<!--
+Routino router web page.
+
+Part of the Routino routing software.
+
+This file Copyright 2008-2014 Andrew M. Bishop
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
+
+<!-- Page elements -->
+<script src="page-elements.js" type="text/javascript"></script>
+<link href="page-elements.css" type="text/css" rel="stylesheet">
+
+<!-- Router and visualiser shared features -->
+<link href="maplayout.css" type="text/css" rel="stylesheet">
+
+<!-- Router specific features -->
+<script src="profiles.js" type="text/javascript"></script>
+<link href="router.css" type="text/css" rel="stylesheet">
+
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('html_init();map_init();form_init();');">
+
+<!-- Left hand side of window - data panel -->
+
+<div class="left_panel">
+
+<div class="tab_box">
+<span id="tab_options" onclick="tab_select('options');" class="tab_selected" title="définir les options">Options</span>
+<span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="Voir les resultats">Résultats</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="Voir les informations de la base de donnée">Données</span>
+</div>
+
+<div class="tab_content" id="tab_options_div">
+
+<form name="form" id="form" action="#" method="get" onsubmit="return false;">
+<div class="hideshow_box">
+<span class="hideshow_title">Itinéraires pour Openstreetmap Routino</span>
+Cette page web permet de calculer des itinéraires à l'aide des données collectées par OpenStreetMap.
+Sélectionner les points de départ et d'arrivée (cliquer sur les icones ci-dessous), sélectionner les préférences, puis rechercher un itinéraire.
+<div class="center">
+<a target="other" href="http://www.routino.org/">site web Routino</a>
+|
+<a target="other" href="documentation/">Documentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Langue</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="router.html.en" title="English language webpage">English</a>
+<td>(EN)
+<td><input name="language" type="radio" value="en" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_de_url" href="router.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<td><input name="language" type="radio" value="de" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_fr_url" href="router.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<td><input name="language" type="radio" value="fr" onchange="formSetLanguage();" checked>
+<tr>
+<td><a id="lang_nl_url" href="router.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<td><input name="language" type="radio" value="nl" onchange="formSetLanguage();" >
+<tr>
+<td>
+<td>(RU)
+<td><input name="language" type="radio" value="ru" onchange="formSetLanguage();" >
+</table>
+<a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
+<span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
+<span class="hideshow_title">Etapes de l'itinéraire</span>
+<div id="hideshow_waypoint_div">
+<table id="waypoints">
+<colgroup>
+<col style="width: 22px;">
+<col>
+<col style="width: 76px;">
+</colgroup>
+<tr id="waypointXXX" style="display: none;">
+<td>
+<img id="iconXXX" src="icons/marker-XXX-grey.png" title="Etape XXX de l'itinéraire - (cliquer pour ajouter/enlever de la carte)" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX);">
+<td>
+<span id="coordsXXX">
+<input name="lonXXX" type="text" size="7" title="Etape XXX Longitude" onchange="formSetCoords(XXX);">E
+<input name="latXXX" type="text" size="7" title="Etape XXX Latitude" onchange="formSetCoords(XXX);">N
+</span>
+<span id="searchXXX" style="display: none;">
+<input name="searchXXX" type="text" size="18" title="position de l'étape XXX"> <!-- uses Javascript event for triggering -->
+</span>
+<td>
+<img alt="?" src="icons/waypoint-search.png" title="Rechercher la position" onmousedown="markerSearch(XXX);" >
+<img alt="G" src="icons/waypoint-locate.png" title="obtenir la position actuelle" onmousedown="markerLocate(XXX);" >
+<img alt="O" src="icons/waypoint-recentre.png" title="Centrer la carte sur cette étape" onmousedown="markerRecentre(XXX);">
+<img alt="^" src="icons/waypoint-up.png" title="Placer cette étape avant" onmousedown="markerMoveUp(XXX);" >
+<img alt="+" src="icons/waypoint-add.png" title="Ajouter une étape après celle-ci" onmousedown="markerAddAfter(XXX);">
+<br>
+<img alt="#" src="icons/waypoint-coords.png" title="Coordonnées de position" onmousedown="markerCoords(XXX);" >
+<img alt="~" src="icons/waypoint-home.png" title="Changer en position de départ" onmousedown="markerHome(XXX);" >
+<img alt="o" src="icons/waypoint-centre.png" title="Centrer cette étape sur la carte" onmousedown="markerCentre(XXX);" >
+<img alt="v" src="icons/waypoint-down.png" title="Placer cette étape après" onmousedown="markerMoveDown(XXX);">
+<img alt="-" src="icons/waypoint-remove.png" title="supprimer cette étape" onmousedown="markerRemove(XXX);" >
+<tr id="searchresultsXXX" style="display: none;">
+<td colspan="3">
+<!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js. -->
+<tr>
+<td colspan="3" class="center">
+<input type="button" title="Inverser l'ordre des étapes" value="Inverser l'ordre" onmousedown="markersReverse();">
+<input type="button" title="Ajouter une nouvelle étape pour faire une boucle" value="Faire une boucle" onmousedown="markersLoop();">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
+<span class="hideshow_title">Mode de déplacement</span>
+<div id="hideshow_transport_div">
+<table>
+<tr><td>À pied: <td><input name="transport" type="radio" value="foot" onchange="formSetTransport('foot' );">
+<tr><td>À cheval: <td><input name="transport" type="radio" value="horse" onchange="formSetTransport('horse' );">
+<tr><td>Fauteuil roulant:<td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair');">
+<tr><td>Bicyclette: <td><input name="transport" type="radio" value="bicycle" onchange="formSetTransport('bicycle' );">
+<tr><td>Mobilette: <td><input name="transport" type="radio" value="moped" onchange="formSetTransport('moped' );">
+<tr><td>Moto:<td><input name="transport" type="radio" value="motorcycle" onchange="formSetTransport('motorcycle');">
+<tr><td>Voiture: <td><input name="transport" type="radio" value="motorcar" onchange="formSetTransport('motorcar' );">
+<tr><td>Camionette: <td><input name="transport" type="radio" value="goods" onchange="formSetTransport('goods' );">
+<tr><td>Camion(15t): <td><input name="transport" type="radio" value="hgv" onchange="formSetTransport('hgv' );">
+<tr><td>Camion(10t): <td><input name="transport" type="radio" value="psv" onchange="formSetTransport('psv' );">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Préférences routières</span>
+<div id="hideshow_highway_div" style="display: none;">
+<table>
+<tr><td>Autoroute: <td><input name="highway-motorway" type="text" size="3" onchange="formSetHighway('motorway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('motorway' ,'+');">
+<tr><td>Trunk: <td><input name="highway-trunk" type="text" size="3" onchange="formSetHighway('trunk' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('trunk' ,'+');">
+<tr><td>Primaire: <td><input name="highway-primary" type="text" size="3" onchange="formSetHighway('primary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('primary' ,'+');">
+<tr><td>Secondaire: <td><input name="highway-secondary" type="text" size="3" onchange="formSetHighway('secondary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('secondary' ,'+');">
+<tr><td>Tertiaire: <td><input name="highway-tertiary" type="text" size="3" onchange="formSetHighway('tertiary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('tertiary' ,'+');">
+<tr><td>Non classée:<td><input name="highway-unclassified" type="text" size="3" onchange="formSetHighway('unclassified','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('unclassified','+');">
+<tr><td>Résidentiel: <td><input name="highway-residential" type="text" size="3" onchange="formSetHighway('residential' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('residential' ,'+');">
+<tr><td>Service: <td><input name="highway-service" type="text" size="3" onchange="formSetHighway('service' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('service' ,'+');">
+<tr><td>Chemin: <td><input name="highway-track" type="text" size="3" onchange="formSetHighway('track' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('track' ,'+');">
+<tr><td>Voie cyclable: <td><input name="highway-cycleway" type="text" size="3" onchange="formSetHighway('cycleway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('cycleway' ,'+');">
+<tr><td>Sentier: <td><input name="highway-path" type="text" size="3" onchange="formSetHighway('path' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('path' ,'+');">
+<tr><td>Escaliers: <td><input name="highway-steps" type="text" size="3" onchange="formSetHighway('steps' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('steps' ,'+');">
+<tr><td>Ferry: <td><input name="highway-ferry" type="text" size="3" onchange="formSetHighway('ferry' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Limitations de vitesse</span>
+<div id="hideshow_speed_div" style="display: none;">
+<table>
+<tr><td>Autoroute: <td><input name="speed-motorway" type="text" size="3" onchange="formSetSpeed('motorway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('motorway' ,'+');">
+<tr><td>Trunk: <td><input name="speed-trunk" type="text" size="3" onchange="formSetSpeed('trunk' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('trunk' ,'+');">
+<tr><td>Primaire: <td><input name="speed-primary" type="text" size="3" onchange="formSetSpeed('primary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('primary' ,'+');">
+<tr><td>Secondaire: <td><input name="speed-secondary" type="text" size="3" onchange="formSetSpeed('secondary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('secondary' ,'+');">
+<tr><td>Tertiaire: <td><input name="speed-tertiary" type="text" size="3" onchange="formSetSpeed('tertiary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('tertiary' ,'+');">
+<tr><td>Non classée:<td><input name="speed-unclassified" type="text" size="3" onchange="formSetSpeed('unclassified','=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('unclassified','+');">
+<tr><td>Résidentiel: <td><input name="speed-residential" type="text" size="3" onchange="formSetSpeed('residential' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('residential' ,'+');">
+<tr><td>Service: <td><input name="speed-service" type="text" size="3" onchange="formSetSpeed('service' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('service' ,'+');">
+<tr><td>Chemin: <td><input name="speed-track" type="text" size="3" onchange="formSetSpeed('track' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('track' ,'+');">
+<tr><td>Voie cyclable: <td><input name="speed-cycleway" type="text" size="3" onchange="formSetSpeed('cycleway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('cycleway' ,'+');">
+<tr><td>Sentier: <td><input name="speed-path" type="text" size="3" onchange="formSetSpeed('path' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('path' ,'+');">
+<tr><td>Escaliers: <td><input name="speed-steps" type="text" size="3" onchange="formSetSpeed('steps' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('steps' ,'+');">
+<tr><td>Ferry: <td><input name="speed-ferry" type="text" size="3" onchange="formSetSpeed('ferry' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Préférences des propriétés</span>
+<div id="hideshow_property_div" style="display: none;">
+<table>
+<tr><td>Pavée: <td><input name="property-paved" type="text" size="3" onchange="formSetProperty('paved' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('paved' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('paved' ,'+');">
+<tr><td>Voies multiples: <td><input name="property-multilane" type="text" size="3" onchange="formSetProperty('multilane' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('multilane' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('multilane' ,'+');">
+<tr><td>Pont: <td><input name="property-bridge" type="text" size="3" onchange="formSetProperty('bridge' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bridge' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bridge' ,'+');">
+<tr><td>Tunnel: <td><input name="property-tunnel" type="text" size="3" onchange="formSetProperty('tunnel' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('tunnel' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('tunnel' ,'+');">
+<tr><td>Itinér. piéton:<td><input name="property-footroute" type="text" size="3" onchange="formSetProperty('footroute' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('footroute' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('footroute' ,'+');">
+<tr><td>Itinér. cycle:<td><input name="property-bicycleroute" type="text" size="3" onchange="formSetProperty('bicycleroute','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bicycleroute','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bicycleroute','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
+<span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Autres Restrictions</span>
+<div id="hideshow_restriction_div" style="display: none;">
+<table>
+<tr><td>Respecter les sens uniques: <td><input name="restrict-oneway" type="checkbox" onchange="formSetRestriction('oneway');">
+<tr><td>Respecter les obligations de tourner:<td><input name="restrict-turns" type="checkbox" onchange="formSetRestriction('turns' );">
+</table>
+<table>
+<tr><td>Poids:<td><input name="restrict-weight" type="text" size="3" onchange="formSetRestriction('weight','=');"><td>tonnes<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('weight','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('weight','+');">
+<tr><td>Hauteur:<td><input name="restrict-height" type="text" size="3" onchange="formSetRestriction('height','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('height','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('height','+');">
+<tr><td>Largeur: <td><input name="restrict-width" type="text" size="3" onchange="formSetRestriction('width' ,'=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('width' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('width' ,'+');">
+<tr><td>Longueur:<td><input name="restrict-length" type="text" size="3" onchange="formSetRestriction('length','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('length','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('length','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Rechercher</span>
+<input type="button" title="Chercher l'itinéraire le plus court" id="shortest" value="Le plus court" onclick="findRoute('shortest');">
+<input type="button" title="Chercher l'itinéraire le plus rapide" id="quickest" value="Le plus rapide" onclick="findRoute('quickest');">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Liens</span>
+<a id="permalink_url" href="router.html">Lien vers cet outil de visualisation</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Editer cette donnée OSM</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Aide</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Aide simplifiée</b>
+<br>
+Cliquer sur les icones de balises (ci-dessus) pour les placer sur la carte (droite). Puis
+les déplacer à la position choisie. Il sera sûrement plus facile de zoomer sur la carte 
+avant de placer les balises. Autre solution, taper la latitude et
+la longitude dans les cases ci-dessus.
+<p>
+Selectionner le mode de déplacement, les types de voies autorisées, les limitations de vitesse, 
+les propriétés des voies et les autres restrictions dans les options ci-dessus.
+Selectionner "Le plus court" ou "Le plus rapide" pour calculer l'itinéraire et le visualiser
+sur la carte.
+<p>
+<b>Etapes</b>
+<br>
+Cliquer sur les balises affichera ou supprimera leur apparition sur la carte.
+Quand un itinéraire est calculé, il affichera (le plus près possible
+pour le mode de déplacement sélectionné) chacune des étapes qui ont une
+balise sur la carte dans l'ordre défini.
+<p>
+<b>Mode de déplacement</b>
+<br>
+Selectionner un mode de déplacement restreindra l'itinéraire choisi aux
+voies sur lesquelles il est autorisé et définira les valeurs par défaut pour
+les autres paramètres.
+<p>
+<b>Préferences des voies</b>
+<br>
+La préférence de voies est définie par un pourcentage et des itinéraires sont choisis 
+qui essaient de suivre les voies préferrées.
+Par exemple, si une voie "Primaire" a une préférence de "110%" et une voie "Secondaire"
+une préférence de "100%", alors cela signifie qu'un itinéraire sur une voie primaire
+peut être jusqu'à 10% plus long que sur une voie secondaire et être sélectionné.
+<p>
+<b>Limites de vitesse</b>
+<br>
+Les limites de vitesse choisies ici pour les differents types de voies s'appliquent si la
+voie n'a pas d'autre limite de vitesse définie ou si celle-ci est supérieure à celle choisie.
+<p>
+<b>Préférences de propriétés</b>
+<br>
+La préférence de propriété est définie par un pourcentage et des itinéraires sont choisis
+qui essaient de suivre les voies ayant cette propriété préférée.
+Par exemple, si une voie goudronnée a une préférence de "75%", alors cela signifie que
+une voie non goudronnée obtient automatiquement une préférence de "25%" ce qui fait que 
+un itinéraire sur une voie goudronnée peut avoir 3 fois la longueur d'une non goudronnée 
+et être sélectionnée.
+<p>
+<b>Autres restrictions</b>
+<br>
+Celles-ci permettent de touver un itinéraire qui respecte les limites définies pour
+le poids, la hauteur, la largeur ou la longueur. Il est également possible d'ignorer
+les restrictions de sens unique (e. pour la marche).
+</div>
+</div>
+</div>
+</form>
+</div>
+
+
+<div class="tab_content" id="tab_results_div" style="display: none;">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_not_run">
+<b><i>Routage non lancé</i></b>
+</div>
+<div id="result_status_running" style="display: none;">
+<b>Routage en cours...</b>
+</div>
+<div id="result_status_complete" style="display: none;">
+<b>Routage terminé</b>
+<br>
+<a id="router_log_complete" target="router_log" href="#">Voir les Détails</a>
+</div>
+<div id="result_status_error" style="display: none;">
+<b>Erreur de Routage</b>
+<br>
+<a id="router_log_error" target="router_log" href="#">Voir les Détails</a>
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Le Routage n'a pas été lancé correctement</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
+<span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Le plus court</span>
+<div id="shortest_status">
+<div id="shortest_status_no_info">
+<b><i>Pas d'information</i></b>
+</div>
+<div id="shortest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_shortest_div" style="display: none;">
+<div id="shortest_links" style="display: none;">
+<table>
+<tr><td>Itinéraire HTML: <td><a id="shortest_html" target="shortest_html" href="#">Ouvrir Popup</a>
+<tr><td>Fichier chemin GPX: <td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">Ouvrir Popup</a>
+<tr><td>Fichier route GPX: <td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">Ouvrir Popup</a>
+<tr><td>Fichier texte complet: <td><a id="shortest_text_all" target="shortest_text_all" href="#">Ouvrir Popup</a>
+<tr><td>Fichier texte: <td><a id="shortest_text" target="shortest_text" href="#">Ouvrir Popup</a>
+</table>
+<hr>
+</div>
+<div id="shortest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
+<span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Le plus rapide</span>
+<div id="quickest_status">
+<div id="quickest_status_no_info">
+<b><i>Pas d'information</i></b>
+</div>
+<div id="quickest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_quickest_div" style="display: none;">
+<div id="quickest_links" style="display: none;">
+<table>
+<tr><td>Itinéraire HTML: <td><a id="quickest_html" target="quickest_html" href="#">Ouvrir Popup</a>
+<tr><td>Fichier chemin GPX: <td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">Ouvrir Popup</a>
+<tr><td>Fichier route GPX: <td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">Ouvrir Popup</a>
+<tr><td>Fichier texte complet: <td><a id="quickest_text_all" target="quickest_text_all" href="#">Ouvrir Popup</a>
+<tr><td>Fichier texte: <td><a id="quickest_text" target="quickest_text" href="#">Ouvrir Popup</a>
+</table>
+<hr>
+</div>
+<div id="quickest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
+<span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
+<span class="hideshow_title">Aide</span>
+<div id="hideshow_help_route_div">
+<div class="scrollable">
+<b>Aide rapide</b>
+<br>
+Après le calcul de l'itinéraire, vous pouvez télécharger le fichier GPX ou
+la description au format texte (résumé ou version détaillée). Vous pouvez également
+visualiser la description de l'itinéraire et zoomer sur des tronçons sélectionnés.
+<p>
+<b>Résoudre un problème</b>
+<br>
+Si le calculateur aboutie à une erreur, la cause la plus probable est que
+il n'est pas possible de trouver un itinéraire entre les points sélectionnés.
+Por permettre de trouver un itinéraire, déplacer une ou des balises 
+ou changer les options de recherche.
+<p>
+<b>Formats d'affichage</b>
+<br>
+<dl>
+<dt>Instructions HTML
+<dd>une description de l'itinéraire à prendre 
+à chaque intersection importante.
+<dt>Fichier chemin GPX
+<dd>La même information qui est affichée sur la carte avec des points
+pour chaque noeud et des lignes pour tous les sègments.
+<dt>Fichier route GPX
+<dd>La même information qui est affichée en texte pour l'itinéraire
+avec une étape pour chaque intersection importante.
+<dt>Fichier texte complet
+<dd>Une liste de tous les noeuds traversés ainsi que la distance 
+entre eux et la distance cumulée pour chaque étape de l'itinéraire.
+<dt>Fichier texte
+<dd>La même information qui est affichée en texte pour l'itinéraire.
+</dl>
+</div>
+</div>
+</div>
+</div>
+
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistiques</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Afficher les données statistiques</a>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Visualiser</span>
+Pour comprendre comment Routino voit les données, il y a un outil de visualisation
+qui permet d'afficher les données soujacentes de multiples manières.
+<br>
+<a id="visualiser_url" href="visualiser.html" target="visualiser">Lien vers cet outil de visualisation</a>
+</div>
+</div>
+
+</div>
+
+<!-- Right hand side of window - map -->
+
+<div class="right_panel">
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript est <em>nécessaire</em> pour cette page web à cause de la carte intéractive.
+</noscript>
+</div>
+<div class="attribution">
+Routeur: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tuiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/router.html.nl b/web/www/routino/router.html.nl
index 4478cba..26c5908 100644
--- a/web/www/routino/router.html.nl
+++ b/web/www/routino/router.html.nl
@@ -1,36 +1,33 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<html>
 
-<!--
- Routino router web page.
-
- Part of the Routino routing software.
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routing route planner">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
 
- This file Copyright 2008-2012 Andrew M. Bishop
+<title>Routino : Route Planner for OpenStreetMap Data</title>
 
- Dutch translation by Jan Jansen (August 2010).
+<!--
+Routino router web page.
 
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+Part of the Routino routing software.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
+This file Copyright 2008-2014 Andrew M. Bishop
 
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see http://www.gnu.org/licenses/.
--->
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
-<HEAD>
-<TITLE>Routino : Route Planner for OpenStreetMap Data</TITLE>
-<META name="keywords" content="openstreetmap routing route planner">
-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
 
-<!-- OpenLayers Javascript library -->
-<script src="../openlayers/OpenLayers.js" type="text/javascript"></script>
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
 
 <!-- Page elements -->
 <script src="page-elements.js" type="text/javascript"></script>
@@ -38,460 +35,460 @@
 
 <!-- Router and visualiser shared features -->
 <link href="maplayout.css" type="text/css" rel="stylesheet">
-<!--[if IE 6]>
-  <link href="maplayout-ie6-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
-<!--[if IE 7]>
-  <link href="maplayout-ie7-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
 
 <!-- Router specific features -->
 <script src="profiles.js" type="text/javascript"></script>
-<script src="mapprops.js" type="text/javascript"></script>
-<script src="router.js" type="text/javascript"></script>
 <link href="router.css" type="text/css" rel="stylesheet">
 
-</HEAD>
-<BODY onload="html_init();map_init();form_init();">
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('html_init();map_init();form_init();');">
 
 <!-- Left hand side of window - data panel -->
 
 <div class="left_panel">
 
-  <div class="tab_box">
-    <span id="tab_options" onclick="tab_select('options');" class="tab_selected"   title="Set routing options">Opties</span>
-    <span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="See routing results">Resultaten</span>
-    <span id="tab_data"    onclick="tab_select('data');"    class="tab_unselected" title="View database information">Data</span>
-  </div>
-
-  <div class="tab_content" id="tab_options_div">
-
-    <form name="form" id="form" action="" method="get" onsubmit="return false;">
-      <div class="hideshow_box">
-        <span class="hideshow_title">Routino OpenStreetMap Router</span>
-        Zoom naar straatniveau.
-        Selecteer start- and eindpunten onder Coordinaten. (click op het marker
-        icoon links, schuif het op map naar gewenste positie).
-        <div align="center">
-          <a target="other" href="http://www.routino.org/">Routino Website</a>
-          |
-          <a target="other" href="documentation/">Documentation</a>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
-        <span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Taal (Language)</span>
-
-        <!-- Note for translations: Only this HTML file needs to be translated, the Javascript has
-             no language specific information in it.  Only the body text and title attributes should
-             be changed, the values passed to the JavaScript and the element names must not be changed.
-             The selection below changes the language option passed to the router and selects the
-             output language not the web page language, the links are for that.  The router itself uses
-             the translations.xml file for the translated versions of the output. -->
-
-        <div id="hideshow_language_div" style="display: none;">
-          <table>
-            <tr>
-              <td><a id="lang_nl_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html.nl" title="Nederlandse web pagina">Nederlands</a>
-              <td>(NL)
-              <td><input name="language" type="radio" value="nl" onchange="formSetLanguage()" checked>
-            <tr>
-              <td><a id="lang_en_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html.en" title="Engelstalige web pagina">English</a>
-              <td>(EN)
-              <td><input name="language" type="radio" value="en" onchange="formSetLanguage()">
-            <tr>
-              <td><a id="lang_de_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html.de" title="Deutsche Webseite">German</a>
-              <td>(DE)
-              <td><input name="language" type="radio" value="de" onchange="formSetLanguage()">
-            <tr>
-              <td>Russian
-              <td>(RU)
-              <td><input name="language" type="radio" value="ru" onchange="formSetLanguage()">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
-        <span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Coordinaten (waypoints)</span>
-        <div id="hideshow_waypoint_div">
-          <table id="waypoints">
-            <colgroup>
-              <col style="width: 25px;">
-              <col>
-              <col style="width: 76px;">
-            </colgroup>
-            <tr id="waypointXXX" style="display: none;">
-              <td>
-                <img name="waypointXXX" src="icons/marker-XXX-grey.png" title="Waypoint XXX Position - (click voor plaatsen/verwijderen op map)" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX)"> 
-              <td>
-                <span id="coordsXXX">
-                  <input name="lonXXX" type="text" size="7" title="Waypoint XXX Longitude" onchange="formSetCoords(XXX);">E
-                  <input name="latXXX" type="text" size="7" title="Waypoint XXX Latitude"  onchange="formSetCoords(XXX);">N
-                </span>
-                <span id="searchXXX" style="display: none;">
-                  <input name="searchXXX" type="text" size="18" title="Waypoint XXX Location"> <!-- uses Javascript event for triggering -->
-                </span>
-              <td>
-                <img alt="?" src="icons/waypoint-search.png"   title="Search for location"         onmousedown="markerSearch(XXX);"  >
-                <img alt="G" src="icons/waypoint-locate.png"   title="Get current location"        onmousedown="markerLocate(XXX);"  >
-                <img alt="O" src="icons/waypoint-recentre.png" title="Centre map on this waypoint"  onmousedown="markerRecentre(XXX);">
-                <img alt="^" src="icons/waypoint-up.png"       title="Beweeg dit punt naar boven"   onmousedown="markerMoveUp(XXX);"  >
-                <img alt="+" src="icons/waypoint-add.png"      title="Voeg hierna punt toe"         onmousedown="markerAddAfter(XXX);">
-                <br>
-                <img alt="#" src="icons/waypoint-coords.png"   title="Coordinates for location"    onmousedown="markerCoords(XXX);"  >
-                <img alt="~" src="icons/waypoint-home.png"     title="Toggle als thuis locatie"     onmousedown="markerHome(XXX);"    >
-                <img alt="o" src="icons/waypoint-centre.png"   title="Centreer dit punt op map"     onmousedown="markerCentre(XXX);"  >
-                <img alt="v" src="icons/waypoint-down.png"     title="Beweeg dit punt naar beneden" onmousedown="markerMoveDown(XXX);">
-                <img alt="-" src="icons/waypoint-remove.png"   title="Verwijder dit punt"           onmousedown="markerRemove(XXX);"  >
-            <tr id="searchresultsXXX" style="display: none;">
-              <td colspan="3">
-            <!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js.  -->
-            <tr>
-              <td colspan="3" align="center">
-                <input type="button" title="Keer volgorde punten om" value="Keer volgorde punten om" onmousedown="markersReverse();">
-                <input type="button" title="Add a new waypoint to make a loop" value="Close loop"    onmousedown="markersLoop();">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
-        <span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Transport Type</span>
-        <div id="hideshow_transport_div">
-          <table>
-            <tr><td>Te voet          <td><input name="transport" type="radio" value="foot"       onchange="formSetTransport('foot'      )">
-            <tr><td>Paard            <td><input name="transport" type="radio" value="horse"      onchange="formSetTransport('horse'     )">
-            <tr><td>Rolstoel         <td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair')">
-            <tr><td>Fiets            <td><input name="transport" type="radio" value="bicycle"    onchange="formSetTransport('bicycle'   )">
-            <tr><td>Brommer          <td><input name="transport" type="radio" value="moped"      onchange="formSetTransport('moped'     )">
-            <tr><td>Motorfiets       <td><input name="transport" type="radio" value="motorbike"  onchange="formSetTransport('motorbike' )">
-            <tr><td>Auto             <td><input name="transport" type="radio" value="motorcar"   onchange="formSetTransport('motorcar'  )">
-            <tr><td>Goederen         <td><input name="transport" type="radio" value="goods"      onchange="formSetTransport('goods'     )">
-            <tr><td>Zwaar transport  <td><input name="transport" type="radio" value="hgv"        onchange="formSetTransport('hgv'       )">
-            <tr><td>Publiek transport<td><input name="transport" type="radio" value="psv"        onchange="formSetTransport('psv'       )">
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
-        <span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Voorkeur Wegtype</span>
-        <div id="hideshow_highway_div" style="display: none;">
-          <table>
-            <tr><td>Autostrade           <td><input name="highway-motorway"     type="text" size=3 onchange="formSetHighway('motorway'    )"><td>%
-            <tr><td>Autoweg:             <td><input name="highway-trunk"        type="text" size=3 onchange="formSetHighway('trunk'       )"><td>%
-            <tr><td>Provinciale wegen:   <td><input name="highway-primary"      type="text" size=3 onchange="formSetHighway('primary'     )"><td>%
-            <tr><td>Nationale wegen:     <td><input name="highway-secondary"    type="text" size=3 onchange="formSetHighway('secondary'   )"><td>%
-            <tr><td>Doorgangsweg:        <td><input name="highway-tertiary"     type="text" size=3 onchange="formSetHighway('tertiary'    )"><td>%
-            <tr><td>Niet geclassificeerd:<td><input name="highway-unclassified" type="text" size=3 onchange="formSetHighway('unclassified')"><td>%
-            <tr><td>Woongebied:          <td><input name="highway-residential"  type="text" size=3 onchange="formSetHighway('residential' )"><td>%
-            <tr><td>Toegangsweg:         <td><input name="highway-service"      type="text" size=3 onchange="formSetHighway('service'     )"><td>%
-            <tr><td>Veldweg:             <td><input name="highway-track"        type="text" size=3 onchange="formSetHighway('track'       )"><td>%
-            <tr><td>Fietspad:            <td><input name="highway-cycleway"     type="text" size=3 onchange="formSetHighway('cycleway'    )"><td>%
-            <tr><td>Pad:                 <td><input name="highway-path"         type="text" size=3 onchange="formSetHighway('path'        )"><td>%
-            <tr><td>Trap:                <td><input name="highway-steps"        type="text" size=3 onchange="formSetHighway('steps'       )"><td>%
-            <tr><td>Ferry:               <td><input name="highway-ferry"        type="text" size=3 onchange="formSetHighway('ferry'       )"><td>%
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
-        <span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Snelheidslimieten</span>
-        <div id="hideshow_speed_div" style="display: none;">
-          <table>
-            <tr><td>Autostrade           <td><input name="speed-motorway"     type="text" size=3 onchange="formSetSpeed('motorway'    )"><td>km/hr
-            <tr><td>Autoweg:             <td><input name="speed-trunk"        type="text" size=3 onchange="formSetSpeed('trunk'       )"><td>km/hr
-            <tr><td>Provinciale wegen:   <td><input name="speed-primary"      type="text" size=3 onchange="formSetSpeed('primary'     )"><td>km/hr
-            <tr><td>Nationale wegen:     <td><input name="speed-secondary"    type="text" size=3 onchange="formSetSpeed('secondary'   )"><td>km/hr
-            <tr><td>Doorgangsweg:        <td><input name="speed-tertiary"     type="text" size=3 onchange="formSetSpeed('tertiary'    )"><td>km/hr
-            <tr><td>Niet geclassificeerd:<td><input name="speed-unclassified" type="text" size=3 onchange="formSetSpeed('unclassified')"><td>km/hr
-            <tr><td>Woongebied:          <td><input name="speed-residential"  type="text" size=3 onchange="formSetSpeed('residential' )"><td>km/hr
-            <tr><td>Toegangsweg:         <td><input name="speed-service"      type="text" size=3 onchange="formSetSpeed('service'     )"><td>km/hr
-            <tr><td>Veldweg:             <td><input name="speed-track"        type="text" size=3 onchange="formSetSpeed('track'       )"><td>km/hr
-            <tr><td>Fietspad:            <td><input name="speed-cycleway"     type="text" size=3 onchange="formSetSpeed('cycleway'    )"><td>km/hr
-            <tr><td>Pad:                 <td><input name="speed-path"         type="text" size=3 onchange="formSetSpeed('path'        )"><td>km/hr
-            <tr><td>Trap:                <td><input name="speed-steps"        type="text" size=3 onchange="formSetSpeed('steps'       )"><td>km/hr
-            <tr><td>Ferry:               <td><input name="speed-ferry"        type="text" size=3 onchange="formSetSpeed('ferry'       )"><td>km/hr
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
-        <span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Weg Eigenschappen</span>
-        <div id="hideshow_property_div" style="display: none;">
-          <table>
-            <tr><td>Verhard:         <td><input name="property-paved"        type="text" size=3 onchange="formSetProperty('paved'       )"><td>%
-            <tr><td>Meerdere Stroken:<td><input name="property-multilane"    type="text" size=3 onchange="formSetProperty('multilane'   )"><td>%
-            <tr><td>Brug:            <td><input name="property-bridge"       type="text" size=3 onchange="formSetProperty('bridge'      )"><td>%
-            <tr><td>Tunnel:          <td><input name="property-tunnel"       type="text" size=3 onchange="formSetProperty('tunnel'      )"><td>%
-            <tr><td>Walking Route:   <td><input name="property-footroute"    type="text" size=3 onchange="formSetProperty('footroute'   )"><td>%
-            <tr><td>Bicycle Route:   <td><input name="property-bicycleroute" type="text" size=3 onchange="formSetProperty('bicycleroute')"><td>%
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
-        <span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
-        <span class="hideshow_title">Andere Beperkingen</span>
-        <div id="hideshow_restriction_div" style="display: none;">
-          <table>
-            <tr><td>Volg Eenrichtingsverkeer:<td><input name="restrict-oneway" type="checkbox"    onchange="formSetRestriction('oneway')"><td>
-            <tr><td>Obey turn restrictions:  <td><input name="restrict-turns"  type="checkbox"    onchange="formSetRestriction('turns' )"><td>
-            <tr><td>Gewicht:                 <td><input name="restrict-weight" type="text" size=3 onchange="formSetRestriction('weight')"><td> ton
-            <tr><td>Hoogte:                  <td><input name="restrict-height" type="text" size=3 onchange="formSetRestriction('height')"><td> meter
-            <tr><td>Breedte:                 <td><input name="restrict-width"  type="text" size=3 onchange="formSetRestriction('width' )"><td> meter
-            <tr><td>Lengte:                  <td><input name="restrict-length" type="text" size=3 onchange="formSetRestriction('length')"><td> meter
-          </table>
-        </div>
-      </div>
-
-      <div class="hideshow_box">
-        <span class="hideshow_title">Zoek Route</span>
-        <input type="button" title="Zoek de kortste route" id="shortest" value="Kortste" onclick="findRoute('shortest');">
-        <input type="button" title="Zoek de snelste route" id="quickest" value="Snelste" onclick="findRoute('quickest');">
-      </div>
-
-      <div class="hideshow_box">
-        <span class="hideshow_title">Links</span>
-        <a id="permalink_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html">Permanente link naar deze parameters</a>
-        <br>
-        <a id="edit_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="http://wiki.openstreetmap.org/wiki/NL:Mapper" target="edit">Lees hoe je OSM data kan inbrengen met Potlatch</a>
-      </div>
-
-      <div class="hideshow_box">
-        <span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
-        <span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
-        <span class="hideshow_title">Help</span>
-        <div id="hideshow_help_options_div">
-          <div class="scrollable">
-            <p>
-            <b>Quick Start</b>
-            <br>
-            Click op marker-icoontje (Waypoints) om ze op de map te plaatsen (rechts).
-            Sleep ze vervolgens naar de gewenste positie. 
-            Het is best om eerst naar straat niveau te zoomen op de kaart. 
-            Selecteer het transport type, toegestane weg-types,
-            snelheidslimieten, wegeigenschappen en andere restricties uit de
-            opties.
-            Selecteer  "Kortste" of "Snelste" om de route te berekenen en te tekenen op de map. 
-            <p>
-            <b>Coordinaten (Waypoints)</b>
-            <br>
-            Click op het marker icoontje, nog eens clicken voor aan/uit.
-            Wanneer de route berekend wordt, zal dit nauwkeurig aansluiten bij de volgorde van deze punten. (rekening houdend met transport type)
-            <p>
-            <b>Transport Type</b>
-            <br>
-            Wanneer je een bepaald transport type kiest wordt bij berekenen
-            route hiermede rekening gehouden.
-            Het transport type bestaat uit een lijst met default waarden voor
-            ieder wegtype.
-            Deze percentages kunnen ook nog eens manueel aangepast worden.
-            <p>
-            <b>Voorkeur Wegtype</b>
-            <br>
-            De voorkeur voor een bepaald type weg wordt uitgedrukt in een percentage.  
-            Bijvoorbeeld wanneer u het Transport Type "Fiets" kiest, dan zal er
-            voor Autostrade 0% staan, en voor Fietspad 100%.
-            Wanneer u Autowegen, Nationale wegen wil vermijden of beperken bij
-            het maken van een fietsroute, kan u percentage naar beneden
-            aanpassen.
-            <p>
-            <b>Snelheid limieten</b>
-            <br>
-            De snelheidslimieten worden afgeleid van het type weg. Het is
-            mogelijk dat er voor een bepaalde weg andere beperkingen gelden. In
-            dat geval worden die gekoezen. (het geval dat deze lager zijn dan de
-            default)
-            <p>
-            <b>Weg Eigenschappen</b>
-            <br>
-            Voor het berekenen van de route, kan de de voorkeur gegeven worden
-            aan een bepaalde wegeigenschap.
-            Wanneer u kiest voor 25% verhard, zal er automatisch de voorkeur aan
-            75% onverhard worden gegeven.
-            Ook al is het onverharde stuk 3 X langer, toch kan er dan de
-            voorkeur aan gegeven worden.
-            <p>
-            <b>Andere Beperkingen</b>
-            <br>
-            Deze zullen toelaten dat er een route berekend wordt die rekening
-            houdt met gewicht, hoogte, breedte of lengte.
-            Het is ook mogelijk geen rekening te houden met eenrichtingsverkeer
-            (bijvoorbeeld als voetganger of fietser)
-          </div>
-        </div>
-      </div>
-    </form>
-  </div>
-
-
-  <div class="tab_content" id="tab_results_div" style="display: none;">
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Status</span>
-      <div id="result_status">
-        <div id="result_status_not_run">
-          <b><i>Router niet in gebruik</i></b>
-        </div>
-        <div id="result_status_running"  style="display: none;">
-          <b>Router werkt...</b>
-        </div>
-        <div id="result_status_complete" style="display: none;">
-          <b>Routing voltooid</b>
-          <br>
-          <a id="router_log_complete" target="router_log" href="#">View Details</a>
-        </div>
-        <div id="result_status_error"    style="display: none;">
-          <b>Router error</b>
-          <br>
-          <a id="router_log_error" target="router_log" href="#">View Details</a>
-        </div>
-        <div id="result_status_failed"   style="display: none;">
-          <b>Router werkt niet</b>
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
-      <span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
-      <span class="hideshow_title">Kortste Route</span>
-      <div id="shortest_status">
-        <div id="shortest_status_no_info">
-          <b><i>No Information</i></b>
-        </div>
-        <div id="shortest_status_info" style="display: none;">
-        </div>
-      </div>
-      <div id="hideshow_shortest_div" style="display: none;">
-        <div id="shortest_links" style="display: none;">
-          <table>
-            <tr><td>HTML directions:  <td><a id="shortest_html"      target="shortest_html"      href="#">Open Popup</a>
-            <tr><td>GPX track bestand:<td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">Open Popup</a>
-            <tr><td>GPX route bestand:<td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">Open Popup</a>
-            <tr><td>Full text bestand:<td><a id="shortest_text_all"  target="shortest_text_all"  href="#">Open Popup</a>
-            <tr><td>Text bestand:     <td><a id="shortest_text"      target="shortest_text"      href="#">Open Popup</a>
-          </table>
-          <hr>
-        </div>
-        <div id="shortest_route">
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
-      <span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
-      <span class="hideshow_title">Snelste Route</span>
-      <div id="quickest_status">
-        <div id="quickest_status_no_info">
-          <b><i>No Information</i></b>
-        </div>
-        <div id="quickest_status_info" style="display: none;">
-        </div>
-      </div>
-      <div id="hideshow_quickest_div" style="display: none;">
-        <div id="quickest_links" style="display: none;">
-          <table>
-            <tr><td>HTML directions:  <td><a id="quickest_html"      target="quickest_html"      href="#">Open Popup</a>
-            <tr><td>GPX track bestand:<td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">Open Popup</a>
-            <tr><td>GPX route bestand:<td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">Open Popup</a>
-            <tr><td>Full text bestand:<td><a id="quickest_text_all"  target="quickest_text_all"  href="#">Open Popup</a>
-            <tr><td>Text bestand:     <td><a id="quickest_text"      target="quickest_text"      href="#">Open Popup</a>
-          </table>
-          <hr>
-        </div>
-        <div id="quickest_route">
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
-      <span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
-      <span class="hideshow_title">Help</span>
-      <div id="hideshow_help_route_div">
-        <div class="scrollable">
-          <p>
-          <b>Quick Start</b>
-          <br>
-          Na het berekenen van een route, kan het GPX bestand, of de beschrijving als tekstbestand downloaden.
-          Door met muis over de beschrijving te bewegen, ziet u die ook op de kaart gesitueerd.
-          <p>
-          <b>Problem Solving</b>
-          <br>
-          Als de router eindigt met een fout, dan is de meest waarschijnlijke
-          oorzaak, dat er geen route mogelijk is tussen de gekozen punten.
-          Het verplaatsen van de punten, of het aanpassen van weg-eigenschappen
-          of voertuigtype kan een oplossing bieden.
-          <p>
-          <b>Output Formats</b>
-          <br>
-          <dl>
-            <dt>HTML instructies
-            <dd>Een beschrijving van de route, met de te nemen afslag aan iedere splitsing.
-            <dt>GPX track bestand
-            <dd>Dezelfde informatie die op de kaart wordt weergegeven. Met
-              coordinaten voor ieder knooppunt, en een track voor ieder segment.
-            <dt>GPX route bestand
-            <dd>Dezelfde informatie dat is opgenomen in de tekst van de route,
-              met een coordinaat voor iedere belangrijke splitsing.
-            <dt>Full text bestand
-            <dd>Een lijst met alle coordinaten, met de afstand hier tussen. En
-              een cumulatieve afstand voor iedere stap op de route.
-            <dt>Text bestand
-            <dd>Dezelfde informatie als wordt weergegeven in de tekst voor de route.
-          </dl>
-        </div>
-      </div>
-    </div>
-  </div>
-
-
-  <div class="tab_content" id="tab_data_div" style="display: none;">
-    <div class="hideshow_box">
-      <span class="hideshow_title">Statistics</span>
-      <div id="statistics_data"></div>
-      <a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
-    </div>
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Visualiser</span>
-      Om te kijken hoe routino omgaat met de basisdata, is er een tooltje dat de onderliggende data toont op verschillende manieren.
-      <br>
-      <a id="visualiser_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="visualiser.html" target="visualiser">Custom link to this map view</a>
-    </div>
-  </div>
+<div class="tab_box">
+<span id="tab_options" onclick="tab_select('options');" class="tab_selected" title="Set routing options">Opties</span>
+<span id="tab_results" onclick="tab_select('results');" class="tab_unselected" title="See routing results">Resultaten</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="View database information">Data</span>
+</div>
+
+<div class="tab_content" id="tab_options_div">
+
+<form name="form" id="form" action="#" method="get" onsubmit="return false;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino OpenStreetMap Router</span>
+Zoom naar straatniveau.
+Selecteer start- and eindpunten onder Coordinaten. (click op het marker
+icoon links, schuif het op map naar gewenste positie).
+<div class="center">
+<a target="other" href="http://www.routino.org/">Routino Website</a>
+|
+<a target="other" href="documentation/">Documentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Taal</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="router.html.en" title="English language webpage">English</a>
+<td>(EN)
+<td><input name="language" type="radio" value="en" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_de_url" href="router.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<td><input name="language" type="radio" value="de" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_fr_url" href="router.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<td><input name="language" type="radio" value="fr" onchange="formSetLanguage();" >
+<tr>
+<td><a id="lang_nl_url" href="router.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<td><input name="language" type="radio" value="nl" onchange="formSetLanguage();" checked>
+<tr>
+<td>
+<td>(RU)
+<td><input name="language" type="radio" value="ru" onchange="formSetLanguage();" >
+</table>
+<a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_waypoint_show" onclick="hideshow_show('waypoint');" class="hideshow_hide">+</span>
+<span id="hideshow_waypoint_hide" onclick="hideshow_hide('waypoint');" class="hideshow_show">-</span>
+<span class="hideshow_title">Coordinaten</span>
+<div id="hideshow_waypoint_div">
+<table id="waypoints">
+<colgroup>
+<col style="width: 22px;">
+<col>
+<col style="width: 76px;">
+</colgroup>
+<tr id="waypointXXX" style="display: none;">
+<td>
+<img id="iconXXX" src="icons/marker-XXX-grey.png" title="Waypoint XXX Position - (click voor plaatsen/verwijderen op map)" alt="Waypoint XXX" onmousedown="markerToggleMap(XXX);">
+<td>
+<span id="coordsXXX">
+<input name="lonXXX" type="text" size="7" title="Waypoint XXX Longitude" onchange="formSetCoords(XXX);">E
+<input name="latXXX" type="text" size="7" title="Waypoint XXX Latitude" onchange="formSetCoords(XXX);">N
+</span>
+<span id="searchXXX" style="display: none;">
+<input name="searchXXX" type="text" size="18" title="Waypoint XXX Location"> <!-- uses Javascript event for triggering -->
+</span>
+<td>
+<img alt="?" src="icons/waypoint-search.png" title="Search for location" onmousedown="markerSearch(XXX);" >
+<img alt="G" src="icons/waypoint-locate.png" title="Get current location" onmousedown="markerLocate(XXX);" >
+<img alt="O" src="icons/waypoint-recentre.png" title="Centre map on this waypoint" onmousedown="markerRecentre(XXX);">
+<img alt="^" src="icons/waypoint-up.png" title="Beweeg dit punt naar boven" onmousedown="markerMoveUp(XXX);" >
+<img alt="+" src="icons/waypoint-add.png" title="Voeg hierna punt toe" onmousedown="markerAddAfter(XXX);">
+<br>
+<img alt="#" src="icons/waypoint-coords.png" title="Coordinates for location" onmousedown="markerCoords(XXX);" >
+<img alt="~" src="icons/waypoint-home.png" title="Toggle als thuis locatie" onmousedown="markerHome(XXX);" >
+<img alt="o" src="icons/waypoint-centre.png" title="Centreer dit punt op map" onmousedown="markerCentre(XXX);" >
+<img alt="v" src="icons/waypoint-down.png" title="Beweeg dit punt naar beneden" onmousedown="markerMoveDown(XXX);">
+<img alt="-" src="icons/waypoint-remove.png" title="Verwijder dit punt" onmousedown="markerRemove(XXX);" >
+<tr id="searchresultsXXX" style="display: none;">
+<td colspan="3">
+<!-- The waypoints are inserted by the JavaScript, see the "maxmarkers" variable in router.js. -->
+<tr>
+<td colspan="3" class="center">
+<input type="button" title="Keer volgorde punten om" value="Keer volgorde punten om" onmousedown="markersReverse();">
+<input type="button" title="Add a new waypoint to make a loop" value="Close loop" onmousedown="markersLoop();">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_hide">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_show">-</span>
+<span class="hideshow_title">Transport Type</span>
+<div id="hideshow_transport_div">
+<table>
+<tr><td>Te voet: <td><input name="transport" type="radio" value="foot" onchange="formSetTransport('foot' );">
+<tr><td>Paard: <td><input name="transport" type="radio" value="horse" onchange="formSetTransport('horse' );">
+<tr><td>Rolstoel:<td><input name="transport" type="radio" value="wheelchair" onchange="formSetTransport('wheelchair');">
+<tr><td>Fiets: <td><input name="transport" type="radio" value="bicycle" onchange="formSetTransport('bicycle' );">
+<tr><td>Brommer: <td><input name="transport" type="radio" value="moped" onchange="formSetTransport('moped' );">
+<tr><td>Motorfiets:<td><input name="transport" type="radio" value="motorcycle" onchange="formSetTransport('motorcycle');">
+<tr><td>Auto: <td><input name="transport" type="radio" value="motorcar" onchange="formSetTransport('motorcar' );">
+<tr><td>Goederen: <td><input name="transport" type="radio" value="goods" onchange="formSetTransport('goods' );">
+<tr><td>Zwaar transport: <td><input name="transport" type="radio" value="hgv" onchange="formSetTransport('hgv' );">
+<tr><td>Publiek transport: <td><input name="transport" type="radio" value="psv" onchange="formSetTransport('psv' );">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Voorkeur Wegtype</span>
+<div id="hideshow_highway_div" style="display: none;">
+<table>
+<tr><td>Autostrade: <td><input name="highway-motorway" type="text" size="3" onchange="formSetHighway('motorway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('motorway' ,'+');">
+<tr><td>Autoweg: <td><input name="highway-trunk" type="text" size="3" onchange="formSetHighway('trunk' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('trunk' ,'+');">
+<tr><td>Provinciale wegen: <td><input name="highway-primary" type="text" size="3" onchange="formSetHighway('primary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('primary' ,'+');">
+<tr><td>Nationale wegen: <td><input name="highway-secondary" type="text" size="3" onchange="formSetHighway('secondary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('secondary' ,'+');">
+<tr><td>Doorgangsweg: <td><input name="highway-tertiary" type="text" size="3" onchange="formSetHighway('tertiary' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('tertiary' ,'+');">
+<tr><td>Niet geclassificeerd:<td><input name="highway-unclassified" type="text" size="3" onchange="formSetHighway('unclassified','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('unclassified','+');">
+<tr><td>Woongebied: <td><input name="highway-residential" type="text" size="3" onchange="formSetHighway('residential' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('residential' ,'+');">
+<tr><td>Toegangsweg: <td><input name="highway-service" type="text" size="3" onchange="formSetHighway('service' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('service' ,'+');">
+<tr><td>Veldweg: <td><input name="highway-track" type="text" size="3" onchange="formSetHighway('track' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('track' ,'+');">
+<tr><td>Fietspad: <td><input name="highway-cycleway" type="text" size="3" onchange="formSetHighway('cycleway' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('cycleway' ,'+');">
+<tr><td>Pad: <td><input name="highway-path" type="text" size="3" onchange="formSetHighway('path' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('path' ,'+');">
+<tr><td>Trap: <td><input name="highway-steps" type="text" size="3" onchange="formSetHighway('steps' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('steps' ,'+');">
+<tr><td>Ferry: <td><input name="highway-ferry" type="text" size="3" onchange="formSetHighway('ferry' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetHighway('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetHighway('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Snelheidslimieten</span>
+<div id="hideshow_speed_div" style="display: none;">
+<table>
+<tr><td>Autostrade: <td><input name="speed-motorway" type="text" size="3" onchange="formSetSpeed('motorway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('motorway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('motorway' ,'+');">
+<tr><td>Autoweg: <td><input name="speed-trunk" type="text" size="3" onchange="formSetSpeed('trunk' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('trunk' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('trunk' ,'+');">
+<tr><td>Provinciale wegen: <td><input name="speed-primary" type="text" size="3" onchange="formSetSpeed('primary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('primary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('primary' ,'+');">
+<tr><td>Nationale wegen: <td><input name="speed-secondary" type="text" size="3" onchange="formSetSpeed('secondary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('secondary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('secondary' ,'+');">
+<tr><td>Doorgangsweg: <td><input name="speed-tertiary" type="text" size="3" onchange="formSetSpeed('tertiary' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('tertiary' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('tertiary' ,'+');">
+<tr><td>Niet geclassificeerd:<td><input name="speed-unclassified" type="text" size="3" onchange="formSetSpeed('unclassified','=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('unclassified','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('unclassified','+');">
+<tr><td>Woongebied: <td><input name="speed-residential" type="text" size="3" onchange="formSetSpeed('residential' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('residential' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('residential' ,'+');">
+<tr><td>Toegangsweg: <td><input name="speed-service" type="text" size="3" onchange="formSetSpeed('service' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('service' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('service' ,'+');">
+<tr><td>Veldweg: <td><input name="speed-track" type="text" size="3" onchange="formSetSpeed('track' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('track' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('track' ,'+');">
+<tr><td>Fietspad: <td><input name="speed-cycleway" type="text" size="3" onchange="formSetSpeed('cycleway' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('cycleway' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('cycleway' ,'+');">
+<tr><td>Pad: <td><input name="speed-path" type="text" size="3" onchange="formSetSpeed('path' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('path' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('path' ,'+');">
+<tr><td>Trap: <td><input name="speed-steps" type="text" size="3" onchange="formSetSpeed('steps' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('steps' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('steps' ,'+');">
+<tr><td>Ferry: <td><input name="speed-ferry" type="text" size="3" onchange="formSetSpeed('ferry' ,'=');"><td>km/hr<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetSpeed('ferry' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetSpeed('ferry' ,'+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Weg Eigenschappen</span>
+<div id="hideshow_property_div" style="display: none;">
+<table>
+<tr><td>Verhard: <td><input name="property-paved" type="text" size="3" onchange="formSetProperty('paved' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('paved' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('paved' ,'+');">
+<tr><td>Meerdere Stroken: <td><input name="property-multilane" type="text" size="3" onchange="formSetProperty('multilane' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('multilane' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('multilane' ,'+');">
+<tr><td>Brug: <td><input name="property-bridge" type="text" size="3" onchange="formSetProperty('bridge' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bridge' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bridge' ,'+');">
+<tr><td>Tunnel: <td><input name="property-tunnel" type="text" size="3" onchange="formSetProperty('tunnel' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('tunnel' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('tunnel' ,'+');">
+<tr><td>Walking Route:<td><input name="property-footroute" type="text" size="3" onchange="formSetProperty('footroute' ,'=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('footroute' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('footroute' ,'+');">
+<tr><td>Bicycle Route:<td><input name="property-bicycleroute" type="text" size="3" onchange="formSetProperty('bicycleroute','=');"><td>%<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetProperty('bicycleroute','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetProperty('bicycleroute','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_restriction_show" onclick="hideshow_show('restriction');" class="hideshow_show">+</span>
+<span id="hideshow_restriction_hide" onclick="hideshow_hide('restriction');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Andere Beperkingen</span>
+<div id="hideshow_restriction_div" style="display: none;">
+<table>
+<tr><td>Volg Eenrichtingsverkeer: <td><input name="restrict-oneway" type="checkbox" onchange="formSetRestriction('oneway');">
+<tr><td>Obey turn restrictions:<td><input name="restrict-turns" type="checkbox" onchange="formSetRestriction('turns' );">
+</table>
+<table>
+<tr><td>Gewicht:<td><input name="restrict-weight" type="text" size="3" onchange="formSetRestriction('weight','=');"><td>tonnes<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('weight','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('weight','+');">
+<tr><td>Hoogte:<td><input name="restrict-height" type="text" size="3" onchange="formSetRestriction('height','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('height','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('height','+');">
+<tr><td>Breedte: <td><input name="restrict-width" type="text" size="3" onchange="formSetRestriction('width' ,'=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('width' ,'-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('width' ,'+');">
+<tr><td>Lengte:<td><input name="restrict-length" type="text" size="3" onchange="formSetRestriction('length','=');"><td>metres<td><img alt="<" src="icons/waypoint-left.png" title="-" onmousedown="formSetRestriction('length','-');">–/+<img alt=">" src="icons/waypoint-right.png" title="+" onmousedown="formSetRestriction('length','+');">
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Zoek Route</span>
+<input type="button" title="Zoek de kortste route" id="shortest" value="Kortste Route" onclick="findRoute('shortest');">
+<input type="button" title="Zoek de snelste route" id="quickest" value="Snelste Route" onclick="findRoute('quickest');">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Links</span>
+<a id="permalink_url" href="router.html">Link to this map view</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Lees hoe je OSM data kan inbrengen</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Help</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+Click op marker-icoontje (Waypoints) om ze op de map te plaatsen (rechts).
+Sleep ze vervolgens naar de gewenste positie. 
+Het is best om eerst naar straat niveau te zoomen op de kaart. 
+Selecteer het transport type, toegestane weg-types,
+snelheidslimieten, wegeigenschappen en andere restricties uit de
+opties.
+Selecteer "Kortste" of "Snelste" om de route te berekenen en te tekenen op de map. 
+<p>
+<b>Coordinaten (Waypoints)</b>
+<br>
+Click op het marker icoontje, nog eens clicken voor aan/uit.
+Wanneer de route berekend wordt, zal dit nauwkeurig aansluiten bij de volgorde van deze punten. (rekening houdend met transport type)
+<p>
+<b>Transport Type</b>
+<br>
+Wanneer je een bepaald transport type kiest wordt bij berekenen
+route hiermede rekening gehouden.
+Het transport type bestaat uit een lijst met default waarden voor
+ieder wegtype.
+Deze percentages kunnen ook nog eens manueel aangepast worden.
+<p>
+<b>Voorkeur Wegtype</b>
+<br>
+De voorkeur voor een bepaald type weg wordt uitgedrukt in een percentage. 
+Bijvoorbeeld wanneer u het Transport Type "Fiets" kiest, dan zal er
+voor Autostrade 0% staan, en voor Fietspad 100%.
+Wanneer u Autowegen, Nationale wegen wil vermijden of beperken bij
+het maken van een fietsroute, kan u percentage naar beneden
+aanpassen.
+<p>
+<b>Snelheid limieten</b>
+<br>
+De snelheidslimieten worden afgeleid van het type weg. Het is
+mogelijk dat er voor een bepaalde weg andere beperkingen gelden. In
+dat geval worden die gekoezen. (het geval dat deze lager zijn dan de
+default)
+<p>
+<b>Weg Eigenschappen</b>
+<br>
+Voor het berekenen van de route, kan de de voorkeur gegeven worden
+aan een bepaalde wegeigenschap.
+Wanneer u kiest voor 25% verhard, zal er automatisch de voorkeur aan
+75% onverhard worden gegeven.
+Ook al is het onverharde stuk 3 X langer, toch kan er dan de
+voorkeur aan gegeven worden.
+<p>
+<b>Andere Beperkingen</b>
+<br>
+Deze zullen toelaten dat er een route berekend wordt die rekening
+houdt met gewicht, hoogte, breedte of lengte.
+Het is ook mogelijk geen rekening te houden met eenrichtingsverkeer
+(bijvoorbeeld als voetganger of fietser)
+</div>
+</div>
+</div>
+</form>
+</div>
+
+
+<div class="tab_content" id="tab_results_div" style="display: none;">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_not_run">
+<b><i>Router niet in gebruik</i></b>
+</div>
+<div id="result_status_running" style="display: none;">
+<b>Router werkt...</b>
+</div>
+<div id="result_status_complete" style="display: none;">
+<b>Routing voltooid</b>
+<br>
+<a id="router_log_complete" target="router_log" href="#">View Details</a>
+</div>
+<div id="result_status_error" style="display: none;">
+<b>Router error</b>
+<br>
+<a id="router_log_error" target="router_log" href="#">View Details</a>
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Router werkt niet</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_shortest_show" onclick="hideshow_show('shortest');" class="hideshow_show">+</span>
+<span id="hideshow_shortest_hide" onclick="hideshow_hide('shortest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Kortste Route</span>
+<div id="shortest_status">
+<div id="shortest_status_no_info">
+<b><i>No Information</i></b>
+</div>
+<div id="shortest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_shortest_div" style="display: none;">
+<div id="shortest_links" style="display: none;">
+<table>
+<tr><td>HTML directions: <td><a id="shortest_html" target="shortest_html" href="#">Open Popup</a>
+<tr><td>GPX track bestand: <td><a id="shortest_gpx_track" target="shortest_gpx_track" href="#">Open Popup</a>
+<tr><td>GPX route bestand: <td><a id="shortest_gpx_route" target="shortest_gpx_route" href="#">Open Popup</a>
+<tr><td>Full text bestand: <td><a id="shortest_text_all" target="shortest_text_all" href="#">Open Popup</a>
+<tr><td>Text bestand: <td><a id="shortest_text" target="shortest_text" href="#">Open Popup</a>
+</table>
+<hr>
+</div>
+<div id="shortest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_quickest_show" onclick="hideshow_show('quickest');" class="hideshow_show">+</span>
+<span id="hideshow_quickest_hide" onclick="hideshow_hide('quickest');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Snelste Route</span>
+<div id="quickest_status">
+<div id="quickest_status_no_info">
+<b><i>No Information</i></b>
+</div>
+<div id="quickest_status_info" style="display: none;">
+</div>
+</div>
+<div id="hideshow_quickest_div" style="display: none;">
+<div id="quickest_links" style="display: none;">
+<table>
+<tr><td>HTML directions: <td><a id="quickest_html" target="quickest_html" href="#">Open Popup</a>
+<tr><td>GPX track bestand: <td><a id="quickest_gpx_track" target="quickest_gpx_track" href="#">Open Popup</a>
+<tr><td>GPX route bestand: <td><a id="quickest_gpx_route" target="quickest_gpx_route" href="#">Open Popup</a>
+<tr><td>Full text bestand: <td><a id="quickest_text_all" target="quickest_text_all" href="#">Open Popup</a>
+<tr><td>Text bestand: <td><a id="quickest_text" target="quickest_text" href="#">Open Popup</a>
+</table>
+<hr>
+</div>
+<div id="quickest_route">
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_route_show" onclick="hideshow_show('help_route');" class="hideshow_hide">+</span>
+<span id="hideshow_help_route_hide" onclick="hideshow_hide('help_route');" class="hideshow_show">-</span>
+<span class="hideshow_title">Help</span>
+<div id="hideshow_help_route_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+Na het berekenen van een route, kan het GPX bestand, of de beschrijving als tekstbestand downloaden.
+Door met muis over de beschrijving te bewegen, ziet u die ook op de kaart gesitueerd.
+<p>
+<b>Problem Solving</b>
+<br>
+Als de router eindigt met een fout, dan is de meest waarschijnlijke
+oorzaak, dat er geen route mogelijk is tussen de gekozen punten.
+Het verplaatsen van de punten, of het aanpassen van weg-eigenschappen
+of voertuigtype kan een oplossing bieden.
+<p>
+<b>Output Formats</b>
+<br>
+<dl>
+<dt>HTML instructies
+<dd>Een beschrijving van de route, met de te nemen afslag aan iedere splitsing.
+<dt>GPX track bestand
+<dd>Dezelfde informatie die op de kaart wordt weergegeven. Met
+coordinaten voor ieder knooppunt, en een track voor ieder segment.
+<dt>GPX route bestand
+<dd>Dezelfde informatie dat is opgenomen in de tekst van de route,
+met een coordinaat voor iedere belangrijke splitsing.
+<dt>Full text bestand
+<dd>Een lijst met alle coordinaten, met de afstand hier tussen. En
+een cumulatieve afstand voor iedere stap op de route.
+<dt>Text bestand
+<dd>Dezelfde informatie als wordt weergegeven in de tekst voor de route.
+</dl>
+</div>
+</div>
+</div>
+</div>
+
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistics</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Visualiser</span>
+Om te kijken hoe Routino omgaat met de basisdata,
+is er een tooltje dat de onderliggende data toont op verschillende manieren.
+<br>
+<a id="visualiser_url" href="visualiser.html" target="visualiser">Link to this map view</a>
+</div>
+</div>
 
 </div>
 
 <!-- Right hand side of window - map -->
 
 <div class="right_panel">
-  <div class="map" id="map">
-    <noscript>
-      Javascript is <em>required</em> to use this web page because of the
-      interactive map.
-    </noscript>
-  </div>
-  <div class="attribution">
-    <a target="other" href="http://www.routino.org/" title="Routino">Router: Routino</a>
-    |
-    <a target="other" href="http://www.openstreetmap.org/" title="Copyright: OpenStreetMap.org; License: Creative Commons Attribution-Share Alike 2.0">Geo Data: OpenStreetMap</a>
-  </div>
-</div>
-
-</BODY>
-</HTML>
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript is <em>required</em> to use this web page because of the interactive map.
+</noscript>
+</div>
+<div class="attribution">
+Router: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/router.js b/web/www/routino/router.leaflet.js
similarity index 67%
copy from web/www/routino/router.js
copy to web/www/routino/router.leaflet.js
index 0da42d9..3c93f32 100644
--- a/web/www/routino/router.js
+++ b/web/www/routino/router.leaflet.js
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2012 Andrew M. Bishop
+// This file Copyright 2008-2014 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -20,7 +20,7 @@
 //
 
 
-var vismarkers, markers, markersmoved, paramschanged;
+var vismarkers, markers, icons, markersmoved, paramschanged;
 var homelat=null, homelon=null;
 
 
@@ -32,13 +32,13 @@ var homelat=null, homelon=null;
 
 var routino_default={};
 for(var l1 in routino)
-   if(typeof(routino[l1])!='object')
+   if(typeof(routino[l1])!="object")
       routino_default[l1]=routino[l1];
    else
      {
       routino_default[l1]={};
       for(var l2 in routino[l1])
-         if(typeof(routino[l1][l2])!='object')
+         if(typeof(routino[l1][l2])!="object")
             routino_default[l1][l2]=Number(routino[l1][l2]);
          else
            {
@@ -92,15 +92,15 @@ if(location.search.length>1)
    var query,queries;
 
    query=location.search.replace(/^\?/,"");
-   query=query.replace(/;/g,'&');
-   queries=query.split('&');
+   query=query.replace(/;/g,"&");
+   queries=query.split("&");
 
    for(var i=0;i<queries.length;i++)
      {
       queries[i].match(/^([^=]+)(=(.*))?$/);
 
-      k=RegExp.$1;
-      v=unescape(RegExp.$3);
+      var k=RegExp.$1;
+      var v=decodeURIComponent(RegExp.$3);
 
       for(var l in legal)
         {
@@ -131,13 +131,13 @@ function html_init()            // called from router.html
 
     searchresults.style.display="none";
     searchresults.id="searchresults" + marker;
-    searchresults.innerHTML=searchresults_html.split('XXX').join(marker);
+    searchresults.innerHTML=searchresults_html.split("XXX").join(marker);
 
     var waypoint=waypoints.insertRow(0);
 
     waypoint.style.display="none";
     waypoint.id="waypoint" + marker;
-    waypoint.innerHTML=waypoint_html.split('XXX').join(marker);
+    waypoint.innerHTML=waypoint_html.split("XXX").join(marker);
    }
 }
 
@@ -162,7 +162,7 @@ function form_init()            // called from router.html
     var lat=args["lat" + marker];
     var search=args["search" + marker];
 
-    if(lon != undefined && lat != undefined && search != undefined && lon != "" && lat != "" && search != "")
+    if(lon !== undefined && lat !== undefined && search !== undefined && lon !== "" && lat !== "" && search !== "")
       {
        markerAddForm(marker);
 
@@ -175,7 +175,7 @@ function form_init()            // called from router.html
 
        vismarkers++;
       }
-    else if(lon != undefined && lat != undefined && lon != "" && lat != "")
+    else if(lon !== undefined && lat !== undefined && lon !== "" && lat !== "")
       {
        markerAddForm(marker);
 
@@ -187,7 +187,7 @@ function form_init()            // called from router.html
 
        vismarkers++;
       }
-    else if(search != undefined && search != "")
+    else if(search !== undefined && search !== "")
       {
        markerAddForm(marker);
 
@@ -209,54 +209,54 @@ function form_init()            // called from router.html
     var searchfield=document.forms["form"].elements["search" + marker];
 
     if(searchfield.addEventListener)
-       searchfield.addEventListener('keyup', searchOnReturnKey, false);
+       searchfield.addEventListener("keyup", searchOnReturnKey, false);
     else if(searchfield.attachEvent)
-       searchfield.attachEvent('keyup', searchOnReturnKey); // Internet Explorer
+       searchfield.attachEvent("keyup", searchOnReturnKey); // Internet Explorer
    }
 
  // Update the transport type with the URL settings which updates all HTML forms to defaults.
 
  var transport=routino.transport;
 
- if(args["transport"] != undefined)
+ if(args["transport"] !== undefined)
     transport=args["transport"];
 
  formSetTransport(transport);
 
  // Update the HTML with the URL settings
 
- if(args["language"] != undefined)
+ if(args["language"] !== undefined)
     formSetLanguage(args["language"]);
 
  for(var key in routino.profile_highway)
-    if(args["highway-" + key] != undefined)
+    if(args["highway-" + key] !== undefined)
        formSetHighway(key,args["highway-" + key]);
 
  for(var key in routino.profile_speed)
-    if(args["speed-" + key] != undefined)
+    if(args["speed-" + key] !== undefined)
        formSetSpeed(key,args["speed-" + key]);
 
  for(var key in routino.profile_property)
-    if(args["property-" + key] != undefined)
+    if(args["property-" + key] !== undefined)
        formSetProperty(key,args["property-" + key]);
 
  for(var key in routino.restrictions)
    {
     if(key=="oneway" || key=="turns")
       {
-       if(args[key] != undefined)
+       if(args[key] !== undefined)
           formSetRestriction(key,args[key]);
       }
     else
       {
-       if(args["restrict-" + key] != undefined)
+       if(args["restrict-" + key] !== undefined)
           formSetRestriction(key,args["restrict-" + key]);
       }
    }
 
  // Get the home location cookie and compare to each waypoint
 
- var cookies=document.cookie.split('; ');
+ var cookies=document.cookie.split("; ");
 
  for(var cookie=0;cookie<cookies.length;cookie++)
     if(cookies[cookie].substr(0,"Routino-home".length)=="Routino-home")
@@ -267,7 +267,7 @@ function form_init()            // called from router.html
        if(data[3]=="lat") homelat=Number(data[4]);
       }
 
- if(homelon!=null && homelat!=null)
+ if(homelon!==null && homelat!==null)
    {
     for(var m=1;m<=vismarkers;m++)
        markerCheckHome(m);
@@ -277,6 +277,8 @@ function form_init()            // called from router.html
     if(!routino.point[1].used)
        markerMoveHome(1);
    }
+
+ updateURLs();
 }
 
 
@@ -301,7 +303,7 @@ function searchOnReturnKey(ev)
 
 function formSetLanguage(value) // called from router.html (with no arguments)
 {
- if(value == undefined)
+ if(value === undefined)
    {
     for(var lang=0;lang<document.forms["form"].elements["language"].length;lang++)
        if(document.forms["form"].elements["language"][lang].checked)
@@ -317,6 +319,8 @@ function formSetLanguage(value) // called from router.html (with no arguments)
 
     routino.language=value;
    }
+
+ updateURLs();
 }
 
 
@@ -349,6 +353,8 @@ function formSetTransport(value) // called from router.html
    }
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -358,15 +364,30 @@ function formSetTransport(value) // called from router.html
 
 function formSetHighway(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
-    routino.profile_highway[type][routino.transport]=document.forms["form"].elements["highway-" + type].value;
- else
+ if(value == "+")
    {
-    document.forms["form"].elements["highway-" + type].value=value;
-    routino.profile_highway[type][routino.transport]=value;
+    value=routino.profile_highway[type][routino.transport];
+    value=10*Math.floor(value/10)+10;
    }
+ else if(value == "-")
+   {
+    value=routino.profile_highway[type][routino.transport]-10;
+    value=10*Math.ceil(value/10)-10;
+   }
+ else if(value == "=")
+    value=document.forms["form"].elements["highway-" + type].value;
+
+ value=Number(value);
+ if(isNaN(value)) value= 50;
+ if(value>100)    value=100;
+ if(value<  0)    value=  0;
+
+ document.forms["form"].elements["highway-" + type].value=value;
+ routino.profile_highway[type][routino.transport]=value;
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -376,15 +397,34 @@ function formSetHighway(type,value) // called from router.html (with one argumen
 
 function formSetSpeed(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
-    routino.profile_speed[type][routino.transport]=document.forms["form"].elements["speed-" + type].value;
- else
+ if(value == "+")
+   {
+    value=routino.profile_speed[type][routino.transport];
+    if(value<10) value=2*Math.floor(value/2)+2;
+    else if(value<30) value=5*Math.floor(value/5)+5;
+    else value=10*Math.floor(value/10)+10;
+   }
+ else if(value == "-")
    {
-    document.forms["form"].elements["speed-" + type].value=value;
-    routino.profile_speed[type][routino.transport]=value;
+    value=routino.profile_speed[type][routino.transport];
+    if(value<=10) value=2*Math.ceil(value/2)-2;
+    else if(value<=30) value=5*Math.ceil(value/5)-5;
+    else value=10*Math.ceil(value/10)-10;
    }
+ else if(value == "=")
+    value=document.forms["form"].elements["speed-" + type].value;
+
+ value=Number(value);
+ if(isNaN(value)) value= 60;
+ if(value>150)    value=150;
+ if(value<  0)    value=  0;
+
+ document.forms["form"].elements["speed-" + type].value=value;
+ routino.profile_speed[type][routino.transport]=value;
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -394,15 +434,32 @@ function formSetSpeed(type,value) // called from router.html (with one argument)
 
 function formSetProperty(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
-    routino.profile_property[type][routino.transport]=document.forms["form"].elements["property-" + type].value;
- else
+ if(value == "+")
+   {
+    value=routino.profile_property[type][routino.transport];
+    if(value>=40 && value<60) value=2*Math.floor(value/2)+2;
+    else value=5*Math.floor(value/5)+5;
+   }
+ else if(value == "-")
    {
-    document.forms["form"].elements["property-" + type].value=value;
-    routino.profile_property[type][routino.transport]=value;
+    value=routino.profile_property[type][routino.transport];
+    if(value>40 && value<=60) value=2*Math.ceil(value/2)-2;
+    else value=5*Math.ceil(value/5)-5;
    }
+ else if(value == "=")
+    value=document.forms["form"].elements["property-" + type].value;
+
+ value=Number(value);
+ if(isNaN(value)) value= 50;
+ if(value>100)    value=100;
+ if(value<  0)    value=  0;
+
+ document.forms["form"].elements["property-" + type].value=value;
+ routino.profile_property[type][routino.transport]=value;
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -412,24 +469,53 @@ function formSetProperty(type,value) // called from router.html (with one argume
 
 function formSetRestriction(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
+ if(type=="oneway" || type=="turns")
    {
-    if(type=="oneway" || type=="turns")
+    if(value === undefined)
        routino.profile_restrictions[type][routino.transport]=document.forms["form"].elements["restrict-" + type].checked;
     else
-       routino.profile_restrictions[type][routino.transport]=document.forms["form"].elements["restrict-" + type].value;
-   }
- else
-   {
-    if(type=="oneway" || type=="turns")
        document.forms["form"].elements["restrict-" + type].checked=value;
-    else
-       document.forms["form"].elements["restrict-" + type].value=value;
 
     routino.profile_restrictions[type][routino.transport]=value;
    }
+ else if(type=="weight")
+   {
+    if(value == "+")
+       value=routino.profile_restrictions[type][routino.transport]+5;
+    else if(value == "-")
+       value=routino.profile_restrictions[type][routino.transport]-5;
+    else if(value == "=")
+       value=document.forms["form"].elements["restrict-" + type].value;
+
+    value=Number(value);
+    if(isNaN(value)) value= 0;
+    if(value>50)     value=50;
+    if(value< 0)     value= 0;
+
+    document.forms["form"].elements["restrict-" + type].value=value;
+    routino.profile_restrictions[type][routino.transport]=value;
+   }
+ else /* if(type=="height" || type=="width" || type=="length") */
+   {
+    if(value == "+")
+       value=routino.profile_restrictions[type][routino.transport]+1;
+    else if(value == "-")
+       value=routino.profile_restrictions[type][routino.transport]-1;
+    else if(value == "=")
+       value=document.forms["form"].elements["restrict-" + type].value;
+
+    value=Number(value);
+    if(isNaN(value)) value= 0;
+    if(value>25)     value=25;
+    if(value< 0)     value= 0;
+
+    document.forms["form"].elements["restrict-" + type].value=value;
+    routino.profile_restrictions[type][routino.transport]=value;
+   }
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -441,26 +527,29 @@ function formSetCoords(marker,lon,lat) // called from router.html (with one argu
 {
  clearSearchResult(marker);
 
- if(lon == undefined && lat == undefined)
+ if(lon === undefined && lat === undefined)
    {
     lon=document.forms["form"].elements["lon" + marker].value;
     lat=document.forms["form"].elements["lat" + marker].value;
    }
 
- if(lon == "" && lat == "")
+ if(lon === "" && lat === "")
    {
     document.forms["form"].elements["lon" + marker].value="";
     document.forms["form"].elements["lat" + marker].value="";
 
     routino.point[marker].lon="";
-    routino.point[marker].lat=""
+    routino.point[marker].lat="";
+
+    updateURLs();
    }
  else
    {
-    if(lon=="")
+    var lonlat;
+
+    if(lon==="")
       {
-       var lonlat=map.getCenter().clone();
-       lonlat.transform(epsg900913,epsg4326);
+       lonlat=map.getCenter();
 
        lon=lonlat.lon;
       }
@@ -468,10 +557,9 @@ function formSetCoords(marker,lon,lat) // called from router.html (with one argu
     if(lon<-180) lon=-180;
     if(lon>+180) lon=+180;
 
-    if(lat=="")
+    if(lat==="")
       {
-       var lonlat=map.getCenter().clone();
-       lonlat.transform(epsg900913,epsg4326);
+       lonlat=map.getCenter();
 
        lat=lonlat.lat;
       }
@@ -479,10 +567,9 @@ function formSetCoords(marker,lon,lat) // called from router.html (with one argu
     if(lat<-90 ) lat=-90 ;
     if(lat>+90 ) lat=+90 ;
 
-    var lonlat = new OpenLayers.LonLat(lon,lat);
-    lonlat.transform(epsg4326,epsg900913);
+    lonlat = L.latLng(lat,lon);
 
-    markers[marker].move(lonlat);
+    markers[marker].setLatLng(lonlat);
 
     markersmoved=true;
 
@@ -506,7 +593,7 @@ function formSetSearch(marker,search) // called from event handler linked to rou
 {
  clearSearchResult(marker);
 
- if(search == undefined)
+ if(search === undefined)
    {
     routino.point[marker].search=document.forms["form"].elements["search" + marker].value;
 
@@ -558,7 +645,7 @@ function buildURLArguments(lang)
       {
        url=url + ";lon" + marker + "=" + routino.point[marker].lon;
        url=url + ";lat" + marker + "=" + routino.point[marker].lat;
-       if(routino.point[marker].search != "")
+       if(routino.point[marker].search !== "")
           url=url + ";search" + marker + "=" + encodeURIComponent(routino.point[marker].search);
       }
 
@@ -591,32 +678,42 @@ function buildURLArguments(lang)
 
 function buildMapArguments()
 {
- var lonlat = map.getCenter().clone();
- lonlat.transform(epsg900913,epsg4326);
+ var lonlat = map.getCenter();
 
- var zoom = map.getZoom() + map.minZoomLevel;
+ var zoom = map.getZoom();
 
- return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lon) + ";zoom=" + zoom;
+ return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lng) + ";zoom=" + zoom;
 }
 
 
 //
-// Update a URL
+// Update the URLs
 //
 
-function updateURL(element)     // called from router.html
+function updateURLs()
 {
- if(element.id == "permalink_url")
-    element.href=location.pathname + "?" + buildURLArguments(true) + ";" + buildMapArguments();
+ var urlargs1=buildURLArguments(true);
+ var urlargs2=buildURLArguments(false);
+ var mapargs=buildMapArguments();
+
+ var links=document.getElementsByTagName("a");
+
+ for(var i=0; i<links.length; i++)
+   {
+    var element=links[i];
+
+    if(element.id == "permalink_url")
+       element.href=location.pathname + "?" + urlargs1 + ";" + mapargs;
 
- if(element.id == "visualiser_url")
-    element.href="visualiser.html" + "?" + buildMapArguments();
+    if(element.id == "visualiser_url")
+       element.href="visualiser.html" + "?" + mapargs;
 
- if(element.id == "edit_url")
-    element.href="http://www.openstreetmap.org/edit" + "?" + buildMapArguments();
+    if(element.id == "edit_url")
+       element.href=mapprops.editurl + "?" + mapargs;
 
- if(element.id.match(/^lang_([a-zA-Z-]+)_url$/))
-    element.href="router.html" + "." + RegExp.$1 + "?" + buildURLArguments(false) + ";" + buildMapArguments();
+    if(element.id.match(/^lang_([a-zA-Z-]+)_url$/))
+       element.href="router.html" + "." + RegExp.$1 + "?" + urlargs2 + ";" + mapargs;
+   }
 }
 
 
@@ -626,7 +723,7 @@ function updateURL(element)     // called from router.html
 
 var map;
 var layerMap=[], layerVectors, layerGPX;
-var epsg4326, epsg900913;
+var routing_type;
 
 //
 // Initialise the 'map' object
@@ -634,136 +731,106 @@ var epsg4326, epsg900913;
 
 function map_init()             // called from router.html
 {
- lon =args["lon"];
- lat =args["lat"];
- zoom=args["zoom"];
-
- // Map properties (North/South and East/West limits and zoom in/out limits) are now in mapprops.js
- // Map URLs are now in mapprops.js
+ // Create the map (Map URLs and limits are in mapprops.js)
 
- //
- // Create the map
- //
+ map = L.map("map",
+             {
+              attributionControl: false,
+              zoomControl: false,
 
- epsg4326=new OpenLayers.Projection("EPSG:4326");
- epsg900913=new OpenLayers.Projection("EPSG:900913");
+              minZoom: mapprops.zoomout,
+              maxZoom: mapprops.zoomin,
 
- map = new OpenLayers.Map ("map",
-                           {
-                            controls:[
-                                      new OpenLayers.Control.Navigation(),
-                                      new OpenLayers.Control.PanZoomBar(),
-                                      new OpenLayers.Control.ScaleLine(),
-                                      new OpenLayers.Control.LayerSwitcher()
-                                      ],
+              maxBounds: L.latLngBounds(L.latLng(mapprops.southedge,mapprops.westedge),L.latLng(mapprops.northedge,mapprops.eastedge))
+              });
 
-                            projection: epsg900913,
-                            displayProjection: epsg4326,
+ // Add map tile layers
 
-                            minZoomLevel: mapprops.zoomout,
-                            numZoomLevels: mapprops.zoomin-mapprops.zoomout+1,
-                            maxResolution: 156543.03390625 / Math.pow(2,mapprops.zoomout),
+ var baselayers={};
 
-                            // These two lines are not needed with OpenLayers 2.12
-                            units: "m",
-                            maxExtent:        new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
+ for(var l=0; l<mapprops.mapdata.length; l++)
+   {
+    var urls=mapprops.mapdata[l].tiles.url.replace(/\${/g,"{");
 
-                            restrictedExtent: new OpenLayers.Bounds(mapprops.westedge,mapprops.southedge,mapprops.eastedge,mapprops.northedge).transform(epsg4326,epsg900913)
-                           });
+    if(mapprops.mapdata[l].tiles.subdomains===undefined)
+       layerMap[l] = L.tileLayer(urls);
+    else
+       layerMap[l] = L.tileLayer(urls, {subdomains: mapprops.mapdata[l].tiles.subdomains});
 
- // Add map tile layers
+    baselayers[mapprops.mapdata[l].label]=layerMap[l];
 
- for(var l=0;l < mapprops.mapdata.length;l++)
-   {
-    layerMap[l] = new OpenLayers.Layer.TMS(mapprops.mapdata[l].label,
-                                           mapprops.mapdata[l].baseurl,
-                                           {
-                                            emptyUrl: mapprops.mapdata[l].errorurl,
-                                            type: 'png',
-                                            getURL: limitedUrl,
-                                            displayOutsideMaxExtent: true,
-                                            buffer: 1
-                                           });
-    map.addLayer(layerMap[l]);
+    if(l===0)
+       map.addLayer(layerMap[l]);
    }
 
- // Get a URL for the tile; limited to map restricted extent.
+ // Add the controls
 
- function limitedUrl(bounds)
- {
-  var z = map.getZoom() + map.minZoomLevel;
+ map.addControl(L.control.zoom());
+ map.addControl(L.control.scale());
+ map.addControl(L.control.layers(baselayers));
 
-  if (z>=7 && (bounds.right  < map.restrictedExtent.left ||
-               bounds.left   > map.restrictedExtent.right ||
-               bounds.top    < map.restrictedExtent.bottom ||
-               bounds.bottom > map.restrictedExtent.top))
-     return this.emptyUrl;
+ // Update the attribution if the layer changes
 
-  var res = map.getResolution();
-  var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
-  var limit = Math.pow(2, z);
+ function change_attribution_event(event)
+ {
+  for(var l=0; l<mapprops.mapdata.length; l++)
+     if(layerMap[l] == event.layer)
+        change_attribution(l);
+ }
 
-  if (y < 0 || y >= limit)
-    return this.emptyUrl;
+ map.on("baselayerchange",change_attribution_event);
 
-  var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
+ function change_attribution(l)
+ {
+  var data_url =mapprops.mapdata[l].attribution.data_url;
+  var data_text=mapprops.mapdata[l].attribution.data_text;
+  var tile_url =mapprops.mapdata[l].attribution.tile_url;
+  var tile_text=mapprops.mapdata[l].attribution.tile_text;
 
-  x = ((x % limit) + limit) % limit;
-  return this.url + z + "/" + x + "/" + y + "." + this.type;
+  document.getElementById("attribution_data").innerHTML="<a href=\"" + data_url + "\" target=\"data_attribution\">" + data_text + "</a>";
+  document.getElementById("attribution_tile").innerHTML="<a href=\"" + tile_url + "\" target=\"tile_attribution\">" + tile_text + "</a>";
  }
 
+ change_attribution(0);
+
  // Define a GPX layer but don't add it yet
 
  layerGPX={shortest: null, quickest: null};
 
- gpx_style={shortest: new OpenLayers.Style({},{strokeWidth: 3, strokeColor: "#00FF00"}),
-            quickest: new OpenLayers.Style({},{strokeWidth: 3, strokeColor: "#0000FF"})};
-
- // Add a vectors layer
+ // Add a markers vectors layer
 
- layerVectors = new OpenLayers.Layer.Vector("Markers");
+ layerVectors = L.layerGroup();
  map.addLayer(layerVectors);
 
  // A set of markers
 
  markers={};
+ icons={};
  markersmoved=false;
  paramschanged=false;
 
  for(var marker=1;marker<=mapprops.maxmarkers;marker++)
    {
-    markers[marker] = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0),{},
-                                                    new OpenLayers.Style({},{externalGraphic: 'icons/marker-' + marker + '-red.png',
-                                                                             fillColor: "white",
-                                                                             graphicYOffset: -25,
-                                                                             graphicWidth: 21,
-                                                                             graphicHeight: 25,
-                                                                             display: "none"}));
-
-    layerVectors.addFeatures([markers[marker]]);
-   }
+    icons[marker]=L.icon({iconUrl: "icons/marker-" + marker + "-red.png",
+                          iconSize: L.point(21,25),
+                          iconAnchor: L.point(10,25)});
 
- // A function to drag the markers
+    markers[marker]=L.marker(L.point(0,0), {clickable: true, draggable: true, icon: icons[marker]});
 
- var drag = new OpenLayers.Control.DragFeature(layerVectors,
-                                               {onDrag:     dragMove,
-                                                onComplete: dragComplete });
- map.addControl(drag);
- drag.activate();
+    markers[marker].on("drag"   , (function(m) { return function(evt) { dragMove    (m,evt); }; }(marker)));
+    markers[marker].on("dragend", (function(m) { return function(evt) { dragComplete(m,evt); }; }(marker)));
+   }
+
+ icons.home=L.icon({iconUrl: "icons/marker-home-red.png",
+                    iconSize: L.point(21,25),
+                    iconAnchor: L.point(11,-25)});
 
  // Markers to highlight a selected point
 
  for(var highlight in highlights)
    {
-    highlights[highlight] = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0),{},
-                                                          new OpenLayers.Style({},{strokeColor: route_dark_colours[highlight],
-                                                                                   fillColor: "white",
-                                                                                   pointRadius: 10,
-                                                                                   strokeWidth: 4,
-                                                                                   fillOpacity: 0,
-                                                                                   display: "none"}));
-
-    layerVectors.addFeatures([highlights[highlight]]);
+    highlights[highlight]=L.circleMarker(L.latLng(0,0), {radius: 10, stroke: true, weight: 4, color: route_dark_colours[highlight], opacity: 1.0,
+                                                         fill: false});
    }
 
  // A popup for routing results
@@ -771,14 +838,15 @@ function map_init()             // called from router.html
  for(var popup in popups)
     popups[popup] = createPopup(popup);
 
- // Set the map centre to the limited range specified
+ // Move the map
 
- map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true));
- map.maxResolution = map.getResolution();
+ map.on("moveend", updateURLs);
 
- // Move the map
+ var lon =args["lon"];
+ var lat =args["lat"];
+ var zoom=args["zoom"];
 
- if(lon != undefined && lat != undefined && zoom != undefined)
+ if(lon !== undefined && lat !== undefined && zoom !== undefined)
    {
     if(lon<mapprops.westedge) lon=mapprops.westedge;
     if(lon>mapprops.eastedge) lon=mapprops.eastedge;
@@ -789,35 +857,44 @@ function map_init()             // called from router.html
     if(zoom<mapprops.zoomout) zoom=mapprops.zoomout;
     if(zoom>mapprops.zoomin)  zoom=mapprops.zoomin;
 
-    var lonlat = new OpenLayers.LonLat(lon,lat);
-    lonlat.transform(epsg4326,epsg900913);
+    map.setView(L.latLng(lat,lon),zoom);
+   }
+ else
+    map.fitBounds(map.options.maxBounds);
+
+ // Unhide editing URL if variable set
 
-    map.moveTo(lonlat,zoom-map.minZoomLevel);
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var edit_url=document.getElementById("edit_url");
+
+    edit_url.style.display="";
+    edit_url.href=mapprops.editurl;
    }
+
+ updateURLs();
 }
 
 
 //
-// OpenLayers.Control.DragFeature callback for a drag occuring.
+// Callback for a drag occuring.
 //
 
-function dragMove(feature,pixel)
+function dragMove(marker,event)
 {
- for(var marker in markers)
-    if(feature==markers[marker])
-       dragSetForm(marker);
+ dragSetForm(marker);
 }
 
 
 //
-// OpenLayers.Control.DragFeature callback for completing a drag.
+// Callback for completing a drag.
 //
 
-function dragComplete(feature,pixel)
+function dragComplete(marker,event)
 {
- for(var marker in markers)
-    if(feature==markers[marker])
-       dragSetForm(marker);
+ dragSetForm(marker);
+
+ updateURLs();
 }
 
 
@@ -827,10 +904,9 @@ function dragComplete(feature,pixel)
 
 function dragSetForm(marker)
 {
- var lonlat = new OpenLayers.LonLat(markers[marker].geometry.x, markers[marker].geometry.y);
- lonlat.transform(epsg900913,epsg4326);
+ var lonlat = markers[marker].getLatLng();
 
- var lon=format5f(lonlat.lon);
+ var lon=format5f(lonlat.lng);
  var lat=format5f(lonlat.lat);
 
  formSetCoords(marker,lon,lat);
@@ -880,13 +956,15 @@ function markerAddMap(marker)
 {
  clearSearchResult(marker);
 
- markers[marker].style.display = "";
+ layerVectors.addLayer(markers[marker]);
  routino.point[marker].active=true;
  routino.point[marker].used=true;
 
  updateIcon(marker);
 
  markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -898,12 +976,14 @@ function markerRemoveMap(marker)
 {
  clearSearchResult(marker);
 
- markers[marker].style.display = "none";
+ layerVectors.removeLayer(markers[marker]);
  routino.point[marker].active=false;
 
  updateIcon(marker);
 
  markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -944,10 +1024,9 @@ function markerCentre(marker)   // called from router.html
 
  clearSearchResult(marker);
 
- var lonlat=map.getCenter().clone();
- lonlat.transform(epsg900913,epsg4326);
+ var lonlat=map.getCenter();
 
- formSetCoords(marker,lonlat.lon,lonlat.lat);
+ formSetCoords(marker,format5f(lonlat.lng),format5f(lonlat.lat));
 }
 
 
@@ -962,11 +1041,10 @@ function markerRecentre(marker) // called from router.html
 
  clearSearchResult(marker);
 
- lon=routino.point[marker].lon;
- lat=routino.point[marker].lat;
+ var lon=routino.point[marker].lon;
+ var lat=routino.point[marker].lat;
 
- var lonlat = new OpenLayers.LonLat(lon,lat);
- lonlat.transform(epsg4326,epsg900913);
+ var lonlat = L.latLng(lat,lon);
 
  map.panTo(lonlat);
 }
@@ -1075,23 +1153,23 @@ function updateIcon(marker)
  if(routino.point[marker].home)
    {
     if(routino.point[marker].active)
-       document.images["waypoint" + marker].src="icons/marker-home-red.png";
+       document.getElementById("icon" + marker).src="icons/marker-home-red.png";
     else
-       document.images["waypoint" + marker].src="icons/marker-home-grey.png";
+       document.getElementById("icon" + marker).src="icons/marker-home-grey.png";
 
-    markers[marker].style.externalGraphic="icons/marker-home-red.png";
+    markers[marker].setIcon(icons.home);
    }
  else
    {
     if(routino.point[marker].active)
-       document.images["waypoint" + marker].src="icons/marker-" + marker + "-red.png";
+       document.getElementById("icon" + marker).src="icons/marker-" + marker + "-red.png";
     else
-       document.images["waypoint" + marker].src="icons/marker-" + marker + "-grey.png";
+       document.getElementById("icon" + marker).src="icons/marker-" + marker + "-grey.png";
 
-    markers[marker].style.externalGraphic="icons/marker-" + marker + "-red.png";
+    markers[marker].setIcon(icons[marker]);
    }
 
- layerVectors.drawFeature(markers[marker]);
+ markers[marker].update();
 }
 
 
@@ -1101,7 +1179,7 @@ function updateIcon(marker)
 
 function markerMoveHome(marker)
 {
- if(homelon==null || homelat==null)
+ if(homelon===null || homelat===null)
     return;
 
  routino.point[marker].home=true;
@@ -1148,7 +1226,7 @@ function markerSetClearHome(marker,home)
 
  updateIcon(marker);
 
- for(m=1;m<=mapprops.maxmarkers;m++)
+ for(var m=1;m<=mapprops.maxmarkers;m++)
     markerCheckHome(m);
 }
 
@@ -1265,6 +1343,10 @@ function markersReverse()       // called from router.html
 {
  for(var marker=1;marker<=vismarkers/2;marker++)
     markerSwap(marker,vismarkers+1-marker);
+
+ markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -1284,6 +1366,10 @@ function markersLoop()          // called from router.html
     markerAddForm(++vismarkers);
 
  markerCopy(vismarkers,1);
+
+ markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -1338,61 +1424,51 @@ var route_dark_colours ={shortest: "#408040", quickest: "#404080"};
 var highlights={shortest: null, quickest: null};
 var popups={shortest: null, quickest: null};
 var routepoints={shortest: {}, quickest: {}};
-var gpx_style={shortest: null, quickest: null};
-
-//
-// Zoom to a specific item in the route
-//
-
-function zoomTo(type,line)
-{
- var lonlat = new OpenLayers.LonLat(routepoints[type][line].lon,routepoints[type][line].lat);
- lonlat.transform(epsg4326,epsg900913);
-
- map.moveTo(lonlat,map.numZoomLevels-2);
-}
-
 
 //
 // Highlight a specific item in the route
 //
 
-function highlight(type,line)
+function highlight(type,line,action)
 {
- if(line==-1)
+ if(action == "clear")
    {
-    highlights[type].style.display = "none";
+    layerVectors.removeLayer(highlights[type]);
 
-    drawPopup(popups[type],null);
+    drawPopup(type,null);
+   }
+ else if(action == "zoom")
+   {
+    var lonlat = L.latLng(routepoints[type][line].lat,routepoints[type][line].lon);
+
+    map.setView(lonlat,mapprops.zoomin-2);
    }
  else
    {
     // Marker
 
-    var lonlat = new OpenLayers.LonLat(routepoints[type][line].lon,routepoints[type][line].lat);
-    lonlat.transform(epsg4326,epsg900913);
+    var lonlat = L.latLng(routepoints[type][line].lat,routepoints[type][line].lon);
 
-    highlights[type].move(lonlat);
+    highlights[type].setLatLng(lonlat);
 
-    if(highlights[type].style.display = "none")
-       highlights[type].style.display = "";
+    layerVectors.addLayer(highlights[type]);
 
     // Popup
 
-    drawPopup(popups[type],"<table>" + routepoints[type][line].html + "</table>");
+    drawPopup(type,"<table>" + routepoints[type][line].html + "</table>");
    }
 
- layerVectors.drawFeature(highlights[type]);
+ highlights[type].redraw();
 }
 
 
 //
-// Create a popup - not using OpenLayers because want it fixed on screen not fixed on map.
+// Create a popup - independent of map because want it fixed on screen not fixed on map.
 //
 
 function createPopup(type)
 {
- var popup=document.createElement('div');
+ var popup=document.createElement("div");
 
  popup.className = "popup";
 
@@ -1418,12 +1494,14 @@ function createPopup(type)
 
 
 //
-// Draw a popup - not using OpenLayers because want it fixed on screen not fixed on map.
+// Draw a popup - independent of map because want it fixed on screen not fixed on map.
 //
 
-function drawPopup(popup,html)
+function drawPopup(type,html)
 {
- if(html==null)
+ var popup=popups[type];
+
+ if(html===null)
    {
     popup.style.display="none";
     return;
@@ -1435,12 +1513,14 @@ function drawPopup(popup,html)
 
     popup.style.left  =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px";
     popup.style.top   =                                map_div.offsetTop +30 + "px";
-    popup.style.width =map_div.clientWidth-100 + "px";
+    popup.style.width =map_div.clientWidth-120 + "px";
 
     popup.style.display="";
    }
 
- popup.innerHTML=html;
+ var close="<span style='float: right; cursor: pointer;' onclick='highlight(\""+type+"\",-1,\"clear\")'>X</span>";
+
+ popup.innerHTML=close+html;
 }
 
 
@@ -1451,7 +1531,6 @@ function drawPopup(popup,html)
 function removeGPXTrace(type)
 {
  map.removeLayer(layerGPX[type]);
- layerGPX[type].destroy();
  layerGPX[type]=null;
 
  displayStatus(type,"no_info");
@@ -1469,6 +1548,28 @@ function removeGPXTrace(type)
 ////////////////////////////////////////////////////////////////////////////////
 
 //
+// Define an AJAX request object
+//
+
+function ajaxGET(url,success,failure,state)
+{
+ var ajaxRequest=new XMLHttpRequest();
+
+ function ajaxGOT(options) {
+  if(this.readyState==4)
+     if(this.status==200)
+       { if(typeof(options.success)=="function") options.success(this,options.state); }
+     else
+       { if(typeof(options.failure)=="function") options.failure(this,options.state); }
+ }
+
+ ajaxRequest.onreadystatechange = function(){ ajaxGOT.call(ajaxRequest,{success: success, failure: failure, state: state}); };
+ ajaxRequest.open("GET", url, true);
+ ajaxRequest.send(null);
+}
+
+
+//
 // Display data statistics
 //
 
@@ -1476,7 +1577,7 @@ function displayStatistics() // called from router.html
 {
  // Use AJAX to get the statistics
 
- OpenLayers.Request.GET({url: "statistics.cgi", success: runStatisticsSuccess});
+ ajaxGET("statistics.cgi", runStatisticsSuccess);
 }
 
 
@@ -1499,9 +1600,9 @@ function findRoute(type) // called from router.html
 {
  tab_select("results");
 
- hideshow_hide('help_options');
- hideshow_hide('shortest');
- hideshow_hide('quickest');
+ hideshow_hide("help_options");
+ hideshow_hide("shortest");
+ hideshow_hide("quickest");
 
  displayStatus("result","running");
 
@@ -1509,23 +1610,26 @@ function findRoute(type) // called from router.html
 
  // Destroy the existing layer(s)
 
+ highlight("shortest",-1,"clear");
+ highlight("quickest",-1,"clear");
+
  if(markersmoved || paramschanged)
    {
-    if(layerGPX.shortest!=null)
+    if(layerGPX.shortest!==null)
        removeGPXTrace("shortest");
-    if(layerGPX.quickest!=null)
+    if(layerGPX.quickest!==null)
        removeGPXTrace("quickest");
     markersmoved=false;
     paramschanged=false;
    }
- else if(layerGPX[type]!=null)
+ else if(layerGPX[type]!==null)
     removeGPXTrace(type);
 
  // Use AJAX to run the router
 
  routing_type=type;
 
- OpenLayers.Request.GET({url: url, success: runRouterSuccess, failure: runRouterFailure});
+ ajaxGET(url, runRouterSuccess, runRouterFailure);
 }
 
 
@@ -1535,7 +1639,7 @@ function findRoute(type) // called from router.html
 
 function runRouterSuccess(response)
 {
- var lines=response.responseText.split('\n');
+ var lines=response.responseText.split("\n");
 
  var uuid=lines[0];
  var success=lines[1];
@@ -1547,7 +1651,7 @@ function runRouterSuccess(response)
  if(success=="ERROR")
    {
     displayStatus("result","error");
-    hideshow_show('help_route');
+    hideshow_show("help_route");
 
     link=document.getElementById("router_log_error");
     link.href="results.cgi?uuid=" + uuid + ";type=router;format=log";
@@ -1557,7 +1661,7 @@ function runRouterSuccess(response)
  else
    {
     displayStatus("result","complete");
-    hideshow_hide('help_route');
+    hideshow_hide("help_route");
 
     link=document.getElementById("router_log_complete");
     link.href="results.cgi?uuid=" + uuid + ";type=router;format=log";
@@ -1580,22 +1684,14 @@ function runRouterSuccess(response)
  link=document.getElementById(routing_type + "_text");
  link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=text";
 
- links=document.getElementById(routing_type + "_links").style.display = "";
+ document.getElementById(routing_type + "_links").style.display = "";
 
  // Add a GPX layer
 
  var url="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=gpx-track";
 
- layerGPX[routing_type] = new OpenLayers.Layer.Vector("GPX (" + routing_type + ")",
-                                                      {
-                                                       protocol:   new OpenLayers.Protocol.HTTP({url: url, format: new OpenLayers.Format.GPX()}),
-                                                       strategies: [new OpenLayers.Strategy.Fixed()],
-                                                       style:      gpx_style[routing_type],
-                                                       projection: map.displayProjection
-                                                      });
-
- map.addLayer(layerGPX[routing_type]);
-
+ ajaxGET(url, runGPXSuccess);
+ 
  hideshow_show(routing_type);
 
  displayResult(routing_type,uuid);
@@ -1603,6 +1699,47 @@ function runRouterSuccess(response)
 
 
 //
+// Success in getting GPX.
+//
+
+function runGPXSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var coords=[];
+ var segment=-1;
+
+ for(var line=0;line<lines.length;line++)
+   {
+    if(lines[line].match(/^<trkseg>/))
+       {
+        segment++;
+        coords[segment]=[];
+       }
+    if(lines[line].match(/^<trkpt lat="([-0-9.]+)" lon="([-0-9.]+)"/))
+      {
+       var lat=RegExp.$1;
+       var lon=RegExp.$2;
+
+       coords[segment].push(L.latLng(lat,lon));
+      }
+   }
+
+ var colour;
+
+ if(routing_type == "shortest")
+    colour="#00FF00";
+ else
+    colour="#0000FF";
+
+ layerGPX[routing_type] = L.multiPolyline(coords,{weight: 3, stroke: true, color: colour, opacity: 1.0,
+                                                  fill: false});
+
+ map.addLayer(layerGPX[routing_type]);
+}
+
+
+//
 // Failure in running router.
 //
 
@@ -1622,18 +1759,18 @@ function displayStatus(type,subtype,content)
 
  do
    {
-    if(child.id != undefined)
+    if(child.id !== undefined)
        child.style.display="none";
 
     child=child.nextSibling;
    }
- while(child != undefined);
+ while(child !== null);
 
  var chosen_status=document.getElementById(type + "_status_" + subtype);
 
  chosen_status.style.display="";
 
- if(content != null)
+ if(content !== undefined)
     chosen_status.innerHTML=content;
 }
 
@@ -1652,7 +1789,7 @@ function displayResult(type,uuid)
 
  // Use AJAX to get the route
 
- OpenLayers.Request.GET({url: url, success: getRouteSuccess, failure: getRouteFailure});
+ ajaxGET(url, getRouteSuccess, getRouteFailure);
 }
 
 
@@ -1662,7 +1799,7 @@ function displayResult(type,uuid)
 
 function getRouteSuccess(response)
 {
- var lines=response.responseText.split('\n');
+ var lines=response.responseText.split("\n");
 
  routepoints[routing_type]=[];
 
@@ -1676,58 +1813,58 @@ function getRouteSuccess(response)
    {
     var thisline=lines[line];
 
-    if(table==0)
+    if(table===0)
       {
-       if(thisline.match('<table>'))
+       if(thisline.match("<table>"))
           table=1;
        else
           continue;
       }
 
-    if(thisline.match('</table>'))
+    if(thisline.match("</table>"))
        break;
 
-    if(thisline.match('<tr class=\'([a-z])\'>'))
+    if(thisline.match("<tr class='([a-z])'>"))
       {
        var rowtype=RegExp.$1;
 
-       if(rowtype=='c')
+       if(rowtype=="c")
          {
-          thisline.match('<td class=\'r\'> *([-0-9.]+) *([-0-9.]+)');
+          thisline.match("<td class='r'> *([-0-9.]+) *([-0-9.]+)");
           points[point]={lat: Number(RegExp.$1), lon: Number(RegExp.$2), html: "", highway: "", distance: "", total: ""};
 
           point++;
          }
-       else if(rowtype=='n')
+       else if(rowtype=="n")
          {
           points[point-1].html += thisline;
          }
-       else if(rowtype=='s')
+       else if(rowtype=="s")
          {
-          thisline.match('<span class=\'h\'>([^<]+)</span>');
+          thisline.match("<span class='h'>([^<]+)</span>");
           points[point-1].highway = RegExp.$1;
 
-          thisline.match('<span class=\'d\'>([^<]+)</span>');
+          thisline.match("<span class='d'>([^<]+)</span>");
           points[point-1].distance = RegExp.$1;
 
-          thisline.match('(<span class=\'j\'>[^<]+</span>)');
+          thisline.match("(<span class='j'>[^<]+</span>)");
           points[point-1].total = RegExp.$1;
 
-          thisline.match('^(.*).<span class=\'j\'>');
+          thisline.match("^(.*).<span class='j'>");
 
           points[point-1].html += RegExp.$1;
          }
-       else if(rowtype=='t')
+       else if(rowtype=="t")
          {
           points[point-1].html += thisline;
 
-          thisline.match('^(.*<td class=\'r\'>)');
+          thisline.match("^(.*<td class='r'>)");
           total_table = RegExp.$1;
 
-          thisline.match('<td class=\'l\'>([^<]+)<');
+          thisline.match("<td class='l'>([^<]+)<");
           total_word = RegExp.$1;
 
-          thisline.match('<span class=\'j\'>([^<]+)</span>');
+          thisline.match("<span class='j'>([^<]+)</span>");
           points[point-1].total = RegExp.$1;
          }
       }
@@ -1735,21 +1872,22 @@ function getRouteSuccess(response)
 
  displayStatus(routing_type,"info",points[point-1].total.bold());
 
- var result="<table onmouseout='highlight(\"" + routing_type + "\",-1)'>";
+ var result="<table onmouseout='highlight(\"" + routing_type + "\",-1,\"clear\")'>";
 
  for(var p=0;p<point-1;p++)
    {
     points[p].html += total_table + points[p].total;
 
-    result=result + "<tr onclick='zoomTo(\"" + routing_type + "\"," + p + ")'" +
-                    " onmouseover='highlight(\"" + routing_type + "\"," + p + ")'>" +
-                    "<td class='distance' title='" + points[p].distance + "'>#" + (p+1) +
+    result=result + "<tr onmouseover='highlight(\"" + routing_type + "\"," + p + ",\"show\")'>" +
+                    "<td onclick='highlight(\"" + routing_type + "\"," + p + ",\"zoom\")'" +
+                    " class='distance' title='" + points[p].distance + "'>#" + (p+1) +
                     "<td class='highway'>" + points[p].highway;
    }
 
- result=result + "<tr onclick='zoomTo(\"" + routing_type + "\"," + p + ")'" +
-                 " onmouseover='highlight(\"" + routing_type + "\"," + p + ")'>" +
-                 "<td colspan='2'>" + total_word + " " + points[p].total;
+ result=result + "<tr onmouseover='highlight(\"" + routing_type + "\"," + p + ",\"show\")'>" +
+                 "<td onclick='highlight(\"" + routing_type + "\"," + p + ",\"zoom\")'" +
+                 " class='distance'>#" + (p+1) +
+                 "<td class='highway'>" + total_word + " " + points[p].total;
 
  result=result + "</table>";
 
@@ -1777,17 +1915,18 @@ function DoSearch(marker)
 
  var search=routino.point[marker].search;
 
- var bounds=map.getExtent().clone();
- bounds.transform(epsg900913,epsg4326);
+ var mapbounds=map.getBounds();
+
+ var url="search.cgi";
 
- var url="search.cgi?marker=" + marker +
-         ";left=" + format5f(bounds.left) +
-         ";top="  + format5f(bounds.top) +
-         ";right="  + format5f(bounds.right) +
-         ";bottom=" + format5f(bounds.bottom) +
-         ";search=" + encodeURIComponent(search);
+ url=url + "?marker=" + marker;
+ url=url + ";lonmin=" + format5f(mapbounds.getWest());
+ url=url + ";latmin=" + format5f(mapbounds.getSouth());
+ url=url + ";lonmax=" + format5f(mapbounds.getEast());
+ url=url + ";latmax=" + format5f(mapbounds.getNorth());
+ url=url + ";search=" + encodeURIComponent(search);
 
- OpenLayers.Request.GET({url: url, success: runSearchSuccess});
+ ajaxGET(url,runSearchSuccess);
 }
 
 
@@ -1799,13 +1938,13 @@ var searchresults=[];
 
 function runSearchSuccess(response)
 {
- var lines=response.responseText.split('\n');
+ var lines=response.responseText.split("\n");
 
  var marker=lines[0];
  var cpuinfo=lines[1];  // not used
- var message=lines[2];  // not used
+ var message=lines[2];
 
- if(message != "")
+ if(message !== "")
    {
     alert(message);
     return;
@@ -1817,10 +1956,10 @@ function runSearchSuccess(response)
    {
     var thisline=lines[line];
 
-    if(thisline=="")
+    if(thisline==="")
        break;
 
-    thisline.match('([-.0-9]+) ([-.0-9]+) (.*)');
+    thisline.match("([-.0-9]+) ([-.0-9]+) (.*)");
 
     searchresults[marker][searchresults[marker].length]={lat: Number(RegExp.$1), lon: Number(RegExp.$2), name: RegExp.$3};
    }
diff --git a/web/www/routino/router.js b/web/www/routino/router.openlayers.js
similarity index 74%
rename from web/www/routino/router.js
rename to web/www/routino/router.openlayers.js
index 0da42d9..ec65f58 100644
--- a/web/www/routino/router.js
+++ b/web/www/routino/router.openlayers.js
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2012 Andrew M. Bishop
+// This file Copyright 2008-2014 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -32,13 +32,13 @@ var homelat=null, homelon=null;
 
 var routino_default={};
 for(var l1 in routino)
-   if(typeof(routino[l1])!='object')
+   if(typeof(routino[l1])!="object")
       routino_default[l1]=routino[l1];
    else
      {
       routino_default[l1]={};
       for(var l2 in routino[l1])
-         if(typeof(routino[l1][l2])!='object')
+         if(typeof(routino[l1][l2])!="object")
             routino_default[l1][l2]=Number(routino[l1][l2]);
          else
            {
@@ -92,15 +92,15 @@ if(location.search.length>1)
    var query,queries;
 
    query=location.search.replace(/^\?/,"");
-   query=query.replace(/;/g,'&');
-   queries=query.split('&');
+   query=query.replace(/;/g,"&");
+   queries=query.split("&");
 
    for(var i=0;i<queries.length;i++)
      {
       queries[i].match(/^([^=]+)(=(.*))?$/);
 
-      k=RegExp.$1;
-      v=unescape(RegExp.$3);
+      var k=RegExp.$1;
+      var v=decodeURIComponent(RegExp.$3);
 
       for(var l in legal)
         {
@@ -131,13 +131,13 @@ function html_init()            // called from router.html
 
     searchresults.style.display="none";
     searchresults.id="searchresults" + marker;
-    searchresults.innerHTML=searchresults_html.split('XXX').join(marker);
+    searchresults.innerHTML=searchresults_html.split("XXX").join(marker);
 
     var waypoint=waypoints.insertRow(0);
 
     waypoint.style.display="none";
     waypoint.id="waypoint" + marker;
-    waypoint.innerHTML=waypoint_html.split('XXX').join(marker);
+    waypoint.innerHTML=waypoint_html.split("XXX").join(marker);
    }
 }
 
@@ -162,7 +162,7 @@ function form_init()            // called from router.html
     var lat=args["lat" + marker];
     var search=args["search" + marker];
 
-    if(lon != undefined && lat != undefined && search != undefined && lon != "" && lat != "" && search != "")
+    if(lon !== undefined && lat !== undefined && search !== undefined && lon !== "" && lat !== "" && search !== "")
       {
        markerAddForm(marker);
 
@@ -175,7 +175,7 @@ function form_init()            // called from router.html
 
        vismarkers++;
       }
-    else if(lon != undefined && lat != undefined && lon != "" && lat != "")
+    else if(lon !== undefined && lat !== undefined && lon !== "" && lat !== "")
       {
        markerAddForm(marker);
 
@@ -187,7 +187,7 @@ function form_init()            // called from router.html
 
        vismarkers++;
       }
-    else if(search != undefined && search != "")
+    else if(search !== undefined && search !== "")
       {
        markerAddForm(marker);
 
@@ -209,54 +209,54 @@ function form_init()            // called from router.html
     var searchfield=document.forms["form"].elements["search" + marker];
 
     if(searchfield.addEventListener)
-       searchfield.addEventListener('keyup', searchOnReturnKey, false);
+       searchfield.addEventListener("keyup", searchOnReturnKey, false);
     else if(searchfield.attachEvent)
-       searchfield.attachEvent('keyup', searchOnReturnKey); // Internet Explorer
+       searchfield.attachEvent("keyup", searchOnReturnKey); // Internet Explorer
    }
 
  // Update the transport type with the URL settings which updates all HTML forms to defaults.
 
  var transport=routino.transport;
 
- if(args["transport"] != undefined)
+ if(args["transport"] !== undefined)
     transport=args["transport"];
 
  formSetTransport(transport);
 
  // Update the HTML with the URL settings
 
- if(args["language"] != undefined)
+ if(args["language"] !== undefined)
     formSetLanguage(args["language"]);
 
  for(var key in routino.profile_highway)
-    if(args["highway-" + key] != undefined)
+    if(args["highway-" + key] !== undefined)
        formSetHighway(key,args["highway-" + key]);
 
  for(var key in routino.profile_speed)
-    if(args["speed-" + key] != undefined)
+    if(args["speed-" + key] !== undefined)
        formSetSpeed(key,args["speed-" + key]);
 
  for(var key in routino.profile_property)
-    if(args["property-" + key] != undefined)
+    if(args["property-" + key] !== undefined)
        formSetProperty(key,args["property-" + key]);
 
  for(var key in routino.restrictions)
    {
     if(key=="oneway" || key=="turns")
       {
-       if(args[key] != undefined)
+       if(args[key] !== undefined)
           formSetRestriction(key,args[key]);
       }
     else
       {
-       if(args["restrict-" + key] != undefined)
+       if(args["restrict-" + key] !== undefined)
           formSetRestriction(key,args["restrict-" + key]);
       }
    }
 
  // Get the home location cookie and compare to each waypoint
 
- var cookies=document.cookie.split('; ');
+ var cookies=document.cookie.split("; ");
 
  for(var cookie=0;cookie<cookies.length;cookie++)
     if(cookies[cookie].substr(0,"Routino-home".length)=="Routino-home")
@@ -267,7 +267,7 @@ function form_init()            // called from router.html
        if(data[3]=="lat") homelat=Number(data[4]);
       }
 
- if(homelon!=null && homelat!=null)
+ if(homelon!==null && homelat!==null)
    {
     for(var m=1;m<=vismarkers;m++)
        markerCheckHome(m);
@@ -277,6 +277,8 @@ function form_init()            // called from router.html
     if(!routino.point[1].used)
        markerMoveHome(1);
    }
+
+ updateURLs();
 }
 
 
@@ -301,7 +303,7 @@ function searchOnReturnKey(ev)
 
 function formSetLanguage(value) // called from router.html (with no arguments)
 {
- if(value == undefined)
+ if(value === undefined)
    {
     for(var lang=0;lang<document.forms["form"].elements["language"].length;lang++)
        if(document.forms["form"].elements["language"][lang].checked)
@@ -317,6 +319,8 @@ function formSetLanguage(value) // called from router.html (with no arguments)
 
     routino.language=value;
    }
+
+ updateURLs();
 }
 
 
@@ -349,6 +353,8 @@ function formSetTransport(value) // called from router.html
    }
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -358,15 +364,30 @@ function formSetTransport(value) // called from router.html
 
 function formSetHighway(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
-    routino.profile_highway[type][routino.transport]=document.forms["form"].elements["highway-" + type].value;
- else
+ if(value == "+")
    {
-    document.forms["form"].elements["highway-" + type].value=value;
-    routino.profile_highway[type][routino.transport]=value;
+    value=routino.profile_highway[type][routino.transport];
+    value=10*Math.floor(value/10)+10;
    }
+ else if(value == "-")
+   {
+    value=routino.profile_highway[type][routino.transport]-10;
+    value=10*Math.ceil(value/10)-10;
+   }
+ else if(value == "=")
+    value=document.forms["form"].elements["highway-" + type].value;
+
+ value=Number(value);
+ if(isNaN(value)) value= 50;
+ if(value>100)    value=100;
+ if(value<  0)    value=  0;
+
+ document.forms["form"].elements["highway-" + type].value=value;
+ routino.profile_highway[type][routino.transport]=value;
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -376,15 +397,34 @@ function formSetHighway(type,value) // called from router.html (with one argumen
 
 function formSetSpeed(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
-    routino.profile_speed[type][routino.transport]=document.forms["form"].elements["speed-" + type].value;
- else
+ if(value == "+")
+   {
+    value=routino.profile_speed[type][routino.transport];
+    if(value<10) value=2*Math.floor(value/2)+2;
+    else if(value<30) value=5*Math.floor(value/5)+5;
+    else value=10*Math.floor(value/10)+10;
+   }
+ else if(value == "-")
    {
-    document.forms["form"].elements["speed-" + type].value=value;
-    routino.profile_speed[type][routino.transport]=value;
+    value=routino.profile_speed[type][routino.transport];
+    if(value<=10) value=2*Math.ceil(value/2)-2;
+    else if(value<=30) value=5*Math.ceil(value/5)-5;
+    else value=10*Math.ceil(value/10)-10;
    }
+ else if(value == "=")
+    value=document.forms["form"].elements["speed-" + type].value;
+
+ value=Number(value);
+ if(isNaN(value)) value= 60;
+ if(value>150)    value=150;
+ if(value<  0)    value=  0;
+
+ document.forms["form"].elements["speed-" + type].value=value;
+ routino.profile_speed[type][routino.transport]=value;
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -394,15 +434,32 @@ function formSetSpeed(type,value) // called from router.html (with one argument)
 
 function formSetProperty(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
-    routino.profile_property[type][routino.transport]=document.forms["form"].elements["property-" + type].value;
- else
+ if(value == "+")
+   {
+    value=routino.profile_property[type][routino.transport];
+    if(value>=40 && value<60) value=2*Math.floor(value/2)+2;
+    else value=5*Math.floor(value/5)+5;
+   }
+ else if(value == "-")
    {
-    document.forms["form"].elements["property-" + type].value=value;
-    routino.profile_property[type][routino.transport]=value;
+    value=routino.profile_property[type][routino.transport];
+    if(value>40 && value<=60) value=2*Math.ceil(value/2)-2;
+    else value=5*Math.ceil(value/5)-5;
    }
+ else if(value == "=")
+    value=document.forms["form"].elements["property-" + type].value;
+
+ value=Number(value);
+ if(isNaN(value)) value= 50;
+ if(value>100)    value=100;
+ if(value<  0)    value=  0;
+
+ document.forms["form"].elements["property-" + type].value=value;
+ routino.profile_property[type][routino.transport]=value;
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -412,24 +469,53 @@ function formSetProperty(type,value) // called from router.html (with one argume
 
 function formSetRestriction(type,value) // called from router.html (with one argument)
 {
- if(value == undefined)
+ if(type=="oneway" || type=="turns")
    {
-    if(type=="oneway" || type=="turns")
+    if(value === undefined)
        routino.profile_restrictions[type][routino.transport]=document.forms["form"].elements["restrict-" + type].checked;
     else
-       routino.profile_restrictions[type][routino.transport]=document.forms["form"].elements["restrict-" + type].value;
-   }
- else
-   {
-    if(type=="oneway" || type=="turns")
        document.forms["form"].elements["restrict-" + type].checked=value;
-    else
-       document.forms["form"].elements["restrict-" + type].value=value;
 
     routino.profile_restrictions[type][routino.transport]=value;
    }
+ else if(type=="weight")
+   {
+    if(value == "+")
+       value=routino.profile_restrictions[type][routino.transport]+5;
+    else if(value == "-")
+       value=routino.profile_restrictions[type][routino.transport]-5;
+    else if(value == "=")
+       value=document.forms["form"].elements["restrict-" + type].value;
+
+    value=Number(value);
+    if(isNaN(value)) value= 0;
+    if(value>50)     value=50;
+    if(value< 0)     value= 0;
+
+    document.forms["form"].elements["restrict-" + type].value=value;
+    routino.profile_restrictions[type][routino.transport]=value;
+   }
+ else /* if(type=="height" || type=="width" || type=="length") */
+   {
+    if(value == "+")
+       value=routino.profile_restrictions[type][routino.transport]+1;
+    else if(value == "-")
+       value=routino.profile_restrictions[type][routino.transport]-1;
+    else if(value == "=")
+       value=document.forms["form"].elements["restrict-" + type].value;
+
+    value=Number(value);
+    if(isNaN(value)) value= 0;
+    if(value>25)     value=25;
+    if(value< 0)     value= 0;
+
+    document.forms["form"].elements["restrict-" + type].value=value;
+    routino.profile_restrictions[type][routino.transport]=value;
+   }
 
  paramschanged=true;
+
+ updateURLs();
 }
 
 
@@ -441,25 +527,29 @@ function formSetCoords(marker,lon,lat) // called from router.html (with one argu
 {
  clearSearchResult(marker);
 
- if(lon == undefined && lat == undefined)
+ if(lon === undefined && lat === undefined)
    {
     lon=document.forms["form"].elements["lon" + marker].value;
     lat=document.forms["form"].elements["lat" + marker].value;
    }
 
- if(lon == "" && lat == "")
+ if(lon === "" && lat === "")
    {
     document.forms["form"].elements["lon" + marker].value="";
     document.forms["form"].elements["lat" + marker].value="";
 
     routino.point[marker].lon="";
-    routino.point[marker].lat=""
+    routino.point[marker].lat="";
+
+    updateURLs();
    }
  else
    {
-    if(lon=="")
+    var lonlat;
+
+    if(lon==="")
       {
-       var lonlat=map.getCenter().clone();
+       lonlat=map.getCenter().clone();
        lonlat.transform(epsg900913,epsg4326);
 
        lon=lonlat.lon;
@@ -468,9 +558,9 @@ function formSetCoords(marker,lon,lat) // called from router.html (with one argu
     if(lon<-180) lon=-180;
     if(lon>+180) lon=+180;
 
-    if(lat=="")
+    if(lat==="")
       {
-       var lonlat=map.getCenter().clone();
+       lonlat=map.getCenter().clone();
        lonlat.transform(epsg900913,epsg4326);
 
        lat=lonlat.lat;
@@ -479,7 +569,7 @@ function formSetCoords(marker,lon,lat) // called from router.html (with one argu
     if(lat<-90 ) lat=-90 ;
     if(lat>+90 ) lat=+90 ;
 
-    var lonlat = new OpenLayers.LonLat(lon,lat);
+    lonlat = new OpenLayers.LonLat(lon,lat);
     lonlat.transform(epsg4326,epsg900913);
 
     markers[marker].move(lonlat);
@@ -506,7 +596,7 @@ function formSetSearch(marker,search) // called from event handler linked to rou
 {
  clearSearchResult(marker);
 
- if(search == undefined)
+ if(search === undefined)
    {
     routino.point[marker].search=document.forms["form"].elements["search" + marker].value;
 
@@ -558,7 +648,7 @@ function buildURLArguments(lang)
       {
        url=url + ";lon" + marker + "=" + routino.point[marker].lon;
        url=url + ";lat" + marker + "=" + routino.point[marker].lat;
-       if(routino.point[marker].search != "")
+       if(routino.point[marker].search !== "")
           url=url + ";search" + marker + "=" + encodeURIComponent(routino.point[marker].search);
       }
 
@@ -601,22 +691,33 @@ function buildMapArguments()
 
 
 //
-// Update a URL
+// Update the URLs
 //
 
-function updateURL(element)     // called from router.html
+function updateURLs()
 {
- if(element.id == "permalink_url")
-    element.href=location.pathname + "?" + buildURLArguments(true) + ";" + buildMapArguments();
+ var urlargs1=buildURLArguments(true);
+ var urlargs2=buildURLArguments(false);
+ var mapargs=buildMapArguments();
+
+ var links=document.getElementsByTagName("a");
+
+ for(var i=0; i<links.length; i++)
+   {
+    var element=links[i];
 
- if(element.id == "visualiser_url")
-    element.href="visualiser.html" + "?" + buildMapArguments();
+    if(element.id == "permalink_url")
+       element.href=location.pathname + "?" + urlargs1 + ";" + mapargs;
 
- if(element.id == "edit_url")
-    element.href="http://www.openstreetmap.org/edit" + "?" + buildMapArguments();
+    if(element.id == "visualiser_url")
+       element.href="visualiser.html" + "?" + mapargs;
 
- if(element.id.match(/^lang_([a-zA-Z-]+)_url$/))
-    element.href="router.html" + "." + RegExp.$1 + "?" + buildURLArguments(false) + ";" + buildMapArguments();
+    if(element.id == "edit_url")
+       element.href=mapprops.editurl + "?" + mapargs;
+
+    if(element.id.match(/^lang_([a-zA-Z-]+)_url$/))
+       element.href="router.html" + "." + RegExp.$1 + "?" + urlargs2 + ";" + mapargs;
+   }
 }
 
 
@@ -627,6 +728,7 @@ function updateURL(element)     // called from router.html
 var map;
 var layerMap=[], layerVectors, layerGPX;
 var epsg4326, epsg900913;
+var routing_type;
 
 //
 // Initialise the 'map' object
@@ -634,16 +736,7 @@ var epsg4326, epsg900913;
 
 function map_init()             // called from router.html
 {
- lon =args["lon"];
- lat =args["lat"];
- zoom=args["zoom"];
-
- // Map properties (North/South and East/West limits and zoom in/out limits) are now in mapprops.js
- // Map URLs are now in mapprops.js
-
- //
- // Create the map
- //
+ // Create the map (Map URLs and limits are in mapprops.js)
 
  epsg4326=new OpenLayers.Projection("EPSG:4326");
  epsg900913=new OpenLayers.Projection("EPSG:900913");
@@ -664,22 +757,53 @@ function map_init()             // called from router.html
                             numZoomLevels: mapprops.zoomin-mapprops.zoomout+1,
                             maxResolution: 156543.03390625 / Math.pow(2,mapprops.zoomout),
 
-                            // These two lines are not needed with OpenLayers 2.12
-                            units: "m",
-                            maxExtent:        new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
-
                             restrictedExtent: new OpenLayers.Bounds(mapprops.westedge,mapprops.southedge,mapprops.eastedge,mapprops.northedge).transform(epsg4326,epsg900913)
                            });
 
+ // Get a URL for the tile (mostly copied from OpenLayers/Layer/XYZ.js).
+
+ function limitedUrl(bounds)
+ {
+  var res = this.map.getResolution();
+
+  var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
+  var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
+  var z = this.map.getZoom() + this.map.minZoomLevel;
+
+  var limit = Math.pow(2, z);
+  x = ((x % limit) + limit) % limit;
+
+  var xyz = {"x": x, "y": y, "z": z};
+  var url = this.url;
+
+  if (OpenLayers.Util.isArray(url))
+    {
+     var s = "" + xyz.x + xyz.y + xyz.z;
+     url = this.selectUrl(s, url);
+    }
+
+  return OpenLayers.String.format(url, xyz);
+ }
+
  // Add map tile layers
 
- for(var l=0;l < mapprops.mapdata.length;l++)
+ for(var l=0; l<mapprops.mapdata.length; l++)
    {
+    var urls;
+
+    if(OpenLayers.Util.isArray(mapprops.mapdata[l].tiles.subdomains))
+      {
+       urls=[];
+
+       for(var s=0; s<mapprops.mapdata[l].tiles.subdomains.length; s++)
+          urls.push(mapprops.mapdata[l].tiles.url.replace(/\${s}/,mapprops.mapdata[l].tiles.subdomains[s]));
+      }
+    else
+       urls=mapprops.mapdata[l].tiles.url;
+
     layerMap[l] = new OpenLayers.Layer.TMS(mapprops.mapdata[l].label,
-                                           mapprops.mapdata[l].baseurl,
+                                           urls,
                                            {
-                                            emptyUrl: mapprops.mapdata[l].errorurl,
-                                            type: 'png',
                                             getURL: limitedUrl,
                                             displayOutsideMaxExtent: true,
                                             buffer: 1
@@ -687,31 +811,30 @@ function map_init()             // called from router.html
     map.addLayer(layerMap[l]);
    }
 
- // Get a URL for the tile; limited to map restricted extent.
+ // Update the attribution if the layer changes
 
- function limitedUrl(bounds)
+ function change_attribution_event(event)
  {
-  var z = map.getZoom() + map.minZoomLevel;
-
-  if (z>=7 && (bounds.right  < map.restrictedExtent.left ||
-               bounds.left   > map.restrictedExtent.right ||
-               bounds.top    < map.restrictedExtent.bottom ||
-               bounds.bottom > map.restrictedExtent.top))
-     return this.emptyUrl;
-
-  var res = map.getResolution();
-  var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
-  var limit = Math.pow(2, z);
+  for(var l=0; l<mapprops.mapdata.length; l++)
+     if(layerMap[l] == event.layer)
+        change_attribution(l);
+ }
 
-  if (y < 0 || y >= limit)
-    return this.emptyUrl;
+ map.events.register("changelayer",layerMap,change_attribution_event);
 
-  var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
+ function change_attribution(l)
+ {
+  var data_url =mapprops.mapdata[l].attribution.data_url;
+  var data_text=mapprops.mapdata[l].attribution.data_text;
+  var tile_url =mapprops.mapdata[l].attribution.tile_url;
+  var tile_text=mapprops.mapdata[l].attribution.tile_text;
 
-  x = ((x % limit) + limit) % limit;
-  return this.url + z + "/" + x + "/" + y + "." + this.type;
+  document.getElementById("attribution_data").innerHTML="<a href=\"" + data_url + "\" target=\"data_attribution\">" + data_text + "</a>";
+  document.getElementById("attribution_tile").innerHTML="<a href=\"" + tile_url + "\" target=\"tile_attribution\">" + tile_text + "</a>";
  }
 
+ change_attribution(0);
+
  // Define a GPX layer but don't add it yet
 
  layerGPX={shortest: null, quickest: null};
@@ -721,7 +844,7 @@ function map_init()             // called from router.html
 
  // Add a vectors layer
 
- layerVectors = new OpenLayers.Layer.Vector("Markers");
+ layerVectors = new OpenLayers.Layer.Vector("Markers",{displayInLayerSwitcher: false});
  map.addLayer(layerVectors);
 
  // A set of markers
@@ -733,7 +856,7 @@ function map_init()             // called from router.html
  for(var marker=1;marker<=mapprops.maxmarkers;marker++)
    {
     markers[marker] = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0,0),{},
-                                                    new OpenLayers.Style({},{externalGraphic: 'icons/marker-' + marker + '-red.png',
+                                                    new OpenLayers.Style({},{externalGraphic: "icons/marker-" + marker + "-red.png",
                                                                              fillColor: "white",
                                                                              graphicYOffset: -25,
                                                                              graphicWidth: 21,
@@ -771,14 +894,15 @@ function map_init()             // called from router.html
  for(var popup in popups)
     popups[popup] = createPopup(popup);
 
- // Set the map centre to the limited range specified
+ // Move the map
 
- map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true));
- map.maxResolution = map.getResolution();
+ map.events.register("moveend", map, updateURLs);
 
- // Move the map
+ var lon =args["lon"];
+ var lat =args["lat"];
+ var zoom=args["zoom"];
 
- if(lon != undefined && lat != undefined && zoom != undefined)
+ if(lon !== undefined && lat !== undefined && zoom !== undefined)
    {
     if(lon<mapprops.westedge) lon=mapprops.westedge;
     if(lon>mapprops.eastedge) lon=mapprops.eastedge;
@@ -794,11 +918,28 @@ function map_init()             // called from router.html
 
     map.moveTo(lonlat,zoom-map.minZoomLevel);
    }
+ else
+   {
+    map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true));
+    map.maxResolution = map.getResolution();
+   }
+
+ // Unhide editing URL if variable set
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var edit_url=document.getElementById("edit_url");
+
+    edit_url.style.display="";
+    edit_url.href=mapprops.editurl;
+   }
+
+ updateURLs();
 }
 
 
 //
-// OpenLayers.Control.DragFeature callback for a drag occuring.
+// Callback for a drag occuring.
 //
 
 function dragMove(feature,pixel)
@@ -810,7 +951,7 @@ function dragMove(feature,pixel)
 
 
 //
-// OpenLayers.Control.DragFeature callback for completing a drag.
+// Callback for completing a drag.
 //
 
 function dragComplete(feature,pixel)
@@ -818,6 +959,8 @@ function dragComplete(feature,pixel)
  for(var marker in markers)
     if(feature==markers[marker])
        dragSetForm(marker);
+
+ updateURLs();
 }
 
 
@@ -887,6 +1030,8 @@ function markerAddMap(marker)
  updateIcon(marker);
 
  markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -904,6 +1049,8 @@ function markerRemoveMap(marker)
  updateIcon(marker);
 
  markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -947,7 +1094,7 @@ function markerCentre(marker)   // called from router.html
  var lonlat=map.getCenter().clone();
  lonlat.transform(epsg900913,epsg4326);
 
- formSetCoords(marker,lonlat.lon,lonlat.lat);
+ formSetCoords(marker,format5f(lonlat.lon),format5f(lonlat.lat));
 }
 
 
@@ -962,8 +1109,8 @@ function markerRecentre(marker) // called from router.html
 
  clearSearchResult(marker);
 
- lon=routino.point[marker].lon;
- lat=routino.point[marker].lat;
+ var lon=routino.point[marker].lon;
+ var lat=routino.point[marker].lat;
 
  var lonlat = new OpenLayers.LonLat(lon,lat);
  lonlat.transform(epsg4326,epsg900913);
@@ -1075,18 +1222,18 @@ function updateIcon(marker)
  if(routino.point[marker].home)
    {
     if(routino.point[marker].active)
-       document.images["waypoint" + marker].src="icons/marker-home-red.png";
+       document.getElementById("icon" + marker).src="icons/marker-home-red.png";
     else
-       document.images["waypoint" + marker].src="icons/marker-home-grey.png";
+       document.getElementById("icon" + marker).src="icons/marker-home-grey.png";
 
     markers[marker].style.externalGraphic="icons/marker-home-red.png";
    }
  else
    {
     if(routino.point[marker].active)
-       document.images["waypoint" + marker].src="icons/marker-" + marker + "-red.png";
+       document.getElementById("icon" + marker).src="icons/marker-" + marker + "-red.png";
     else
-       document.images["waypoint" + marker].src="icons/marker-" + marker + "-grey.png";
+       document.getElementById("icon" + marker).src="icons/marker-" + marker + "-grey.png";
 
     markers[marker].style.externalGraphic="icons/marker-" + marker + "-red.png";
    }
@@ -1101,7 +1248,7 @@ function updateIcon(marker)
 
 function markerMoveHome(marker)
 {
- if(homelon==null || homelat==null)
+ if(homelon===null || homelat===null)
     return;
 
  routino.point[marker].home=true;
@@ -1148,7 +1295,7 @@ function markerSetClearHome(marker,home)
 
  updateIcon(marker);
 
- for(m=1;m<=mapprops.maxmarkers;m++)
+ for(var m=1;m<=mapprops.maxmarkers;m++)
     markerCheckHome(m);
 }
 
@@ -1265,6 +1412,10 @@ function markersReverse()       // called from router.html
 {
  for(var marker=1;marker<=vismarkers/2;marker++)
     markerSwap(marker,vismarkers+1-marker);
+
+ markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -1284,6 +1435,10 @@ function markersLoop()          // called from router.html
     markerAddForm(++vismarkers);
 
  markerCopy(vismarkers,1);
+
+ markersmoved=true;
+
+ updateURLs();
 }
 
 
@@ -1340,30 +1495,25 @@ var popups={shortest: null, quickest: null};
 var routepoints={shortest: {}, quickest: {}};
 var gpx_style={shortest: null, quickest: null};
 
-//
-// Zoom to a specific item in the route
-//
-
-function zoomTo(type,line)
-{
- var lonlat = new OpenLayers.LonLat(routepoints[type][line].lon,routepoints[type][line].lat);
- lonlat.transform(epsg4326,epsg900913);
-
- map.moveTo(lonlat,map.numZoomLevels-2);
-}
-
 
 //
 // Highlight a specific item in the route
 //
 
-function highlight(type,line)
+function highlight(type,line,action)
 {
- if(line==-1)
+ if(action == "clear")
    {
     highlights[type].style.display = "none";
 
-    drawPopup(popups[type],null);
+    drawPopup(type,null);
+   }
+ else if(action == "zoom")
+   {
+    var lonlat = new OpenLayers.LonLat(routepoints[type][line].lon,routepoints[type][line].lat);
+    lonlat.transform(epsg4326,epsg900913);
+
+    map.moveTo(lonlat,map.numZoomLevels-2);
    }
  else
    {
@@ -1374,12 +1524,12 @@ function highlight(type,line)
 
     highlights[type].move(lonlat);
 
-    if(highlights[type].style.display = "none")
+    if(highlights[type].style.display == "none")
        highlights[type].style.display = "";
 
     // Popup
 
-    drawPopup(popups[type],"<table>" + routepoints[type][line].html + "</table>");
+    drawPopup(type,"<table>" + routepoints[type][line].html + "</table>");
    }
 
  layerVectors.drawFeature(highlights[type]);
@@ -1387,12 +1537,12 @@ function highlight(type,line)
 
 
 //
-// Create a popup - not using OpenLayers because want it fixed on screen not fixed on map.
+// Create a popup - independent of map because want it fixed on screen not fixed on map.
 //
 
 function createPopup(type)
 {
- var popup=document.createElement('div');
+ var popup=document.createElement("div");
 
  popup.className = "popup";
 
@@ -1418,12 +1568,14 @@ function createPopup(type)
 
 
 //
-// Draw a popup - not using OpenLayers because want it fixed on screen not fixed on map.
+// Draw a popup - independent of map because want it fixed on screen not fixed on map.
 //
 
-function drawPopup(popup,html)
+function drawPopup(type,html)
 {
- if(html==null)
+ var popup=popups[type];
+
+ if(html===null)
    {
     popup.style.display="none";
     return;
@@ -1435,12 +1587,14 @@ function drawPopup(popup,html)
 
     popup.style.left  =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px";
     popup.style.top   =                                map_div.offsetTop +30 + "px";
-    popup.style.width =map_div.clientWidth-100 + "px";
+    popup.style.width =map_div.clientWidth-120 + "px";
 
     popup.style.display="";
    }
 
- popup.innerHTML=html;
+ var close="<span style='float: right; cursor: pointer;' onclick='highlight(\""+type+"\",-1,\"clear\")'>X</span>";
+
+ popup.innerHTML=close+html;
 }
 
 
@@ -1469,6 +1623,28 @@ function removeGPXTrace(type)
 ////////////////////////////////////////////////////////////////////////////////
 
 //
+// Define an AJAX request object
+//
+
+function ajaxGET(url,success,failure,state)
+{
+ var ajaxRequest=new XMLHttpRequest();
+
+ function ajaxGOT(options) {
+  if(this.readyState==4)
+     if(this.status==200)
+       { if(typeof(options.success)=="function") options.success(this,options.state); }
+     else
+       { if(typeof(options.failure)=="function") options.failure(this,options.state); }
+ }
+
+ ajaxRequest.onreadystatechange = function(){ ajaxGOT.call(ajaxRequest,{success: success, failure: failure, state: state}); };
+ ajaxRequest.open("GET", url, true);
+ ajaxRequest.send(null);
+}
+
+
+//
 // Display data statistics
 //
 
@@ -1476,7 +1652,7 @@ function displayStatistics() // called from router.html
 {
  // Use AJAX to get the statistics
 
- OpenLayers.Request.GET({url: "statistics.cgi", success: runStatisticsSuccess});
+ ajaxGET("statistics.cgi", runStatisticsSuccess);
 }
 
 
@@ -1499,9 +1675,9 @@ function findRoute(type) // called from router.html
 {
  tab_select("results");
 
- hideshow_hide('help_options');
- hideshow_hide('shortest');
- hideshow_hide('quickest');
+ hideshow_hide("help_options");
+ hideshow_hide("shortest");
+ hideshow_hide("quickest");
 
  displayStatus("result","running");
 
@@ -1509,23 +1685,26 @@ function findRoute(type) // called from router.html
 
  // Destroy the existing layer(s)
 
+ highlight("shortest",-1,"clear");
+ highlight("quickest",-1,"clear");
+
  if(markersmoved || paramschanged)
    {
-    if(layerGPX.shortest!=null)
+    if(layerGPX.shortest!==null)
        removeGPXTrace("shortest");
-    if(layerGPX.quickest!=null)
+    if(layerGPX.quickest!==null)
        removeGPXTrace("quickest");
     markersmoved=false;
     paramschanged=false;
    }
- else if(layerGPX[type]!=null)
+ else if(layerGPX[type]!==null)
     removeGPXTrace(type);
 
  // Use AJAX to run the router
 
  routing_type=type;
 
- OpenLayers.Request.GET({url: url, success: runRouterSuccess, failure: runRouterFailure});
+ ajaxGET(url, runRouterSuccess, runRouterFailure);
 }
 
 
@@ -1535,7 +1714,7 @@ function findRoute(type) // called from router.html
 
 function runRouterSuccess(response)
 {
- var lines=response.responseText.split('\n');
+ var lines=response.responseText.split("\n");
 
  var uuid=lines[0];
  var success=lines[1];
@@ -1547,7 +1726,7 @@ function runRouterSuccess(response)
  if(success=="ERROR")
    {
     displayStatus("result","error");
-    hideshow_show('help_route');
+    hideshow_show("help_route");
 
     link=document.getElementById("router_log_error");
     link.href="results.cgi?uuid=" + uuid + ";type=router;format=log";
@@ -1557,7 +1736,7 @@ function runRouterSuccess(response)
  else
    {
     displayStatus("result","complete");
-    hideshow_hide('help_route');
+    hideshow_hide("help_route");
 
     link=document.getElementById("router_log_complete");
     link.href="results.cgi?uuid=" + uuid + ";type=router;format=log";
@@ -1580,7 +1759,7 @@ function runRouterSuccess(response)
  link=document.getElementById(routing_type + "_text");
  link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=text";
 
- links=document.getElementById(routing_type + "_links").style.display = "";
+ document.getElementById(routing_type + "_links").style.display = "";
 
  // Add a GPX layer
 
@@ -1588,6 +1767,7 @@ function runRouterSuccess(response)
 
  layerGPX[routing_type] = new OpenLayers.Layer.Vector("GPX (" + routing_type + ")",
                                                       {
+                                                       displayInLayerSwitcher: false,
                                                        protocol:   new OpenLayers.Protocol.HTTP({url: url, format: new OpenLayers.Format.GPX()}),
                                                        strategies: [new OpenLayers.Strategy.Fixed()],
                                                        style:      gpx_style[routing_type],
@@ -1622,18 +1802,18 @@ function displayStatus(type,subtype,content)
 
  do
    {
-    if(child.id != undefined)
+    if(child.id !== undefined)
        child.style.display="none";
 
     child=child.nextSibling;
    }
- while(child != undefined);
+ while(child !== null);
 
  var chosen_status=document.getElementById(type + "_status_" + subtype);
 
  chosen_status.style.display="";
 
- if(content != null)
+ if(content !== undefined)
     chosen_status.innerHTML=content;
 }
 
@@ -1652,7 +1832,7 @@ function displayResult(type,uuid)
 
  // Use AJAX to get the route
 
- OpenLayers.Request.GET({url: url, success: getRouteSuccess, failure: getRouteFailure});
+ ajaxGET(url, getRouteSuccess, getRouteFailure);
 }
 
 
@@ -1662,7 +1842,7 @@ function displayResult(type,uuid)
 
 function getRouteSuccess(response)
 {
- var lines=response.responseText.split('\n');
+ var lines=response.responseText.split("\n");
 
  routepoints[routing_type]=[];
 
@@ -1676,58 +1856,58 @@ function getRouteSuccess(response)
    {
     var thisline=lines[line];
 
-    if(table==0)
+    if(table===0)
       {
-       if(thisline.match('<table>'))
+       if(thisline.match("<table>"))
           table=1;
        else
           continue;
       }
 
-    if(thisline.match('</table>'))
+    if(thisline.match("</table>"))
        break;
 
-    if(thisline.match('<tr class=\'([a-z])\'>'))
+    if(thisline.match("<tr class='([a-z])'>"))
       {
        var rowtype=RegExp.$1;
 
-       if(rowtype=='c')
+       if(rowtype=="c")
          {
-          thisline.match('<td class=\'r\'> *([-0-9.]+) *([-0-9.]+)');
+          thisline.match("<td class='r'> *([-0-9.]+) *([-0-9.]+)");
           points[point]={lat: Number(RegExp.$1), lon: Number(RegExp.$2), html: "", highway: "", distance: "", total: ""};
 
           point++;
          }
-       else if(rowtype=='n')
+       else if(rowtype=="n")
          {
           points[point-1].html += thisline;
          }
-       else if(rowtype=='s')
+       else if(rowtype=="s")
          {
-          thisline.match('<span class=\'h\'>([^<]+)</span>');
+          thisline.match("<span class='h'>([^<]+)</span>");
           points[point-1].highway = RegExp.$1;
 
-          thisline.match('<span class=\'d\'>([^<]+)</span>');
+          thisline.match("<span class='d'>([^<]+)</span>");
           points[point-1].distance = RegExp.$1;
 
-          thisline.match('(<span class=\'j\'>[^<]+</span>)');
+          thisline.match("(<span class='j'>[^<]+</span>)");
           points[point-1].total = RegExp.$1;
 
-          thisline.match('^(.*).<span class=\'j\'>');
+          thisline.match("^(.*).<span class='j'>");
 
           points[point-1].html += RegExp.$1;
          }
-       else if(rowtype=='t')
+       else if(rowtype=="t")
          {
           points[point-1].html += thisline;
 
-          thisline.match('^(.*<td class=\'r\'>)');
+          thisline.match("^(.*<td class='r'>)");
           total_table = RegExp.$1;
 
-          thisline.match('<td class=\'l\'>([^<]+)<');
+          thisline.match("<td class='l'>([^<]+)<");
           total_word = RegExp.$1;
 
-          thisline.match('<span class=\'j\'>([^<]+)</span>');
+          thisline.match("<span class='j'>([^<]+)</span>");
           points[point-1].total = RegExp.$1;
          }
       }
@@ -1735,21 +1915,22 @@ function getRouteSuccess(response)
 
  displayStatus(routing_type,"info",points[point-1].total.bold());
 
- var result="<table onmouseout='highlight(\"" + routing_type + "\",-1)'>";
+ var result="<table onmouseout='highlight(\"" + routing_type + "\",-1,\"clear\")'>";
 
  for(var p=0;p<point-1;p++)
    {
     points[p].html += total_table + points[p].total;
 
-    result=result + "<tr onclick='zoomTo(\"" + routing_type + "\"," + p + ")'" +
-                    " onmouseover='highlight(\"" + routing_type + "\"," + p + ")'>" +
-                    "<td class='distance' title='" + points[p].distance + "'>#" + (p+1) +
+    result=result + "<tr onmouseover='highlight(\"" + routing_type + "\"," + p + ",\"show\")'>" +
+                    "<td onclick='highlight(\"" + routing_type + "\"," + p + ",\"zoom\")'" +
+                    " class='distance' title='" + points[p].distance + "'>#" + (p+1) +
                     "<td class='highway'>" + points[p].highway;
    }
 
- result=result + "<tr onclick='zoomTo(\"" + routing_type + "\"," + p + ")'" +
-                 " onmouseover='highlight(\"" + routing_type + "\"," + p + ")'>" +
-                 "<td colspan='2'>" + total_word + " " + points[p].total;
+ result=result + "<tr onmouseover='highlight(\"" + routing_type + "\"," + p + ",\"show\")'>" +
+                 "<td onclick='highlight(\"" + routing_type + "\"," + p + ",\"zoom\")'" +
+                 " class='distance'>#" + (p+1) +
+                 "<td class='highway'>" + total_word + " " + points[p].total;
 
  result=result + "</table>";
 
@@ -1777,17 +1958,19 @@ function DoSearch(marker)
 
  var search=routino.point[marker].search;
 
- var bounds=map.getExtent().clone();
- bounds.transform(epsg900913,epsg4326);
+ var mapbounds=map.getExtent().clone();
+ mapbounds.transform(epsg900913,epsg4326);
+
+ var url="search.cgi";
 
- var url="search.cgi?marker=" + marker +
-         ";left=" + format5f(bounds.left) +
-         ";top="  + format5f(bounds.top) +
-         ";right="  + format5f(bounds.right) +
-         ";bottom=" + format5f(bounds.bottom) +
-         ";search=" + encodeURIComponent(search);
+ url=url + "?marker=" + marker;
+ url=url + ";lonmin=" + format5f(mapbounds.left);
+ url=url + ";latmin=" + format5f(mapbounds.bottom);
+ url=url + ";lonmax=" + format5f(mapbounds.right);
+ url=url + ";latmax=" + format5f(mapbounds.top);
+ url=url + ";search=" + encodeURIComponent(search);
 
- OpenLayers.Request.GET({url: url, success: runSearchSuccess});
+ ajaxGET(url,runSearchSuccess);
 }
 
 
@@ -1799,13 +1982,13 @@ var searchresults=[];
 
 function runSearchSuccess(response)
 {
- var lines=response.responseText.split('\n');
+ var lines=response.responseText.split("\n");
 
  var marker=lines[0];
  var cpuinfo=lines[1];  // not used
- var message=lines[2];  // not used
+ var message=lines[2];
 
- if(message != "")
+ if(message !== "")
    {
     alert(message);
     return;
@@ -1817,10 +2000,10 @@ function runSearchSuccess(response)
    {
     var thisline=lines[line];
 
-    if(thisline=="")
+    if(thisline==="")
        break;
 
-    thisline.match('([-.0-9]+) ([-.0-9]+) (.*)');
+    thisline.match("([-.0-9]+) ([-.0-9]+) (.*)");
 
     searchresults[marker][searchresults[marker].length]={lat: Number(RegExp.$1), lon: Number(RegExp.$2), name: RegExp.$3};
    }
diff --git a/web/www/routino/router.pl b/web/www/routino/router.pl
index b06e449..b2d39ba 100644
--- a/web/www/routino/router.pl
+++ b/web/www/routino/router.pl
@@ -3,7 +3,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2012 Andrew M. Bishop
+# This file Copyright 2008-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -19,6 +19,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the directory paths script
 require "paths.pl";
 
@@ -28,7 +30,7 @@ require "profiles.pl";
 # Use the perl Time::HiRes module
 use Time::HiRes qw(gettimeofday tv_interval);
 
-$t0 = [gettimeofday];
+my $t0 = [gettimeofday];
 
 
 #
@@ -39,25 +41,25 @@ sub FillInDefaults
   {
    my(%params)=@_;
 
-   $params{transport}=$routino->{transport} if(!defined $params{transport});
+   $params{transport}=$main::routino->{transport} if(!defined $params{transport});
 
    my $transport=$params{transport};
 
-   foreach my $highway (keys %{$routino->{highways}})
+   foreach my $highway (keys %{$main::routino->{highways}})
      {
       my $key="highway-$highway";
-      my $value=$routino->{profile_highway}->{$highway}->{$transport};
+      my $value=$main::routino->{profile_highway}->{$highway}->{$transport};
       $params{$key}=$value if(!defined $params{$key});
 
       $key="speed-$highway";
-      $value=$routino->{profile_speed}->{$highway}->{$transport};
+      $value=$main::routino->{profile_speed}->{$highway}->{$transport};
       $params{$key}=$value if(!defined $params{$key});
      }
 
-   foreach my $property (keys %{$routino->{properties}})
+   foreach my $property (keys %{$main::routino->{properties}})
      {
       my $key="property-$property";
-      my $value=$routino->{profile_property}->{$property}->{$transport};
+      my $value=$main::routino->{profile_property}->{$property}->{$transport};
       $params{$key}=$value if(!defined $params{$key});
      }
 
@@ -67,10 +69,10 @@ sub FillInDefaults
    $params{turns} =~ s/(true|on)/1/;
    $params{turns} =~ s/(false|off)/0/;
 
-   foreach my $restriction (keys %{$routino->{restrictions}})
+   foreach my $restriction (keys %{$main::routino->{restrictions}})
      {
       my $key="$restriction";
-      my $value=$routino->{profile_restrictions}->{$restriction}->{$transport};
+      my $value=$main::routino->{profile_restrictions}->{$restriction}->{$transport};
       $params{$key}=$value if(!defined $params{$key});
      }
 
@@ -97,8 +99,8 @@ sub RunRouter
 
    # Change directory
 
-   mkdir $results_dir,0755 if(! -d $results_dir);
-   chdir $results_dir;
+   mkdir $main::results_dir,0755 if(! -d $main::results_dir);
+   chdir $main::results_dir;
 
    # Create a unique output directory
 
@@ -119,25 +121,25 @@ sub RunRouter
 
    # Run the router
 
-   my($safe_params)="";
-   if($data_dir)
+   my $safe_params ="";
+   if($main::data_dir)
      {
-      my(@pathparts)=split('/',$data_dir);
+      my @pathparts=split('/',$main::data_dir);
       $safe_params.=" --dir=".pop(@pathparts);
      }
    # This works in newer Perl versions, but not older ones.
-   #$safe_params.=" --dir=".pop([split('/',$data_dir)]) if($data_dir);
-   $safe_params.=" --prefix=$data_prefix" if($data_prefix);
+   #$safe_params.=" --dir=".pop([split('/',$main::data_dir)]) if($main::data_dir);
+   $safe_params.=" --prefix=$main::data_prefix" if($main::data_prefix);
 
    open(LOG,">router.log");
-   print LOG "$router_exe $params$safe_params\n\n"; # Don't put the full pathnames in the logfile.
+   print LOG "$main::router_exe $params$safe_params\n\n"; # Don't put the full pathnames in the logfile.
    close(LOG);
 
-   $params.=" --dir=$data_dir" if($data_dir);
-   $params.=" --prefix=$data_prefix" if($data_prefix);
+   $params.=" --dir=$main::data_dir" if($main::data_dir);
+   $params.=" --prefix=$main::data_prefix" if($main::data_prefix);
    $params.=" --loggable";
 
-   system "$bin_dir/$router_exe $params >> router.log 2>&1";
+   system "$main::bin_dir/$main::router_exe $params >> router.log 2>&1";
 
    my $status="OK";
    $status="ERROR" if($? != 0);
@@ -160,7 +162,7 @@ sub RunRouter
 
 # Possible file formats
 
-%suffixes=(
+my %suffixes=(
            "html"      => ".html",
            "gpx-route" => "-route.gpx",
            "gpx-track" => "-track.gpx",
@@ -171,7 +173,7 @@ sub RunRouter
 
 # Possible MIME types
 
-%mimetypes=(
+my %mimetypes=(
             "html"      => "text/html",
             "gpx-route" => "text/xml",
             "gpx-track" => "text/xml",
@@ -189,7 +191,7 @@ sub ReturnOutput
    my $suffix=$suffixes{$format};
    my $mime  =$mimetypes{$format};
 
-   my $file="$results_dir/$uuid/$type$suffix";
+   my $file="$main::results_dir/$uuid/$type$suffix";
 
    # Return the output
 
diff --git a/web/www/routino/search.cgi b/web/www/routino/search.cgi
index 1ec3082..7189d39 100755
--- a/web/www/routino/search.cgi
+++ b/web/www/routino/search.cgi
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2012 Andrew M. Bishop
+# This file Copyright 2012-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,6 +20,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the generic search script
 require "search.pl";
 
@@ -29,25 +31,27 @@ use CGI ':cgi';
 
 # Create the query and get the parameters
 
-$query=new CGI;
+my $query=new CGI;
 
- at rawparams=$query->param;
+my @rawparams=$query->param;
 
 # Legal CGI parameters with regexp validity check
 
-%legalparams=(
+my %legalparams=(
               "marker"  => "[0-9]+",
 
-              "left"    => "[-0-9.]+",
-              "right"   => "[-0-9.]+",
-              "top"     => "[-0-9.]+",
-              "bottom"  => "[-0-9.]+",
+              "lonmin"  => "[-0-9.]+",
+              "lonmax"  => "[-0-9.]+",
+              "latmax"  => "[-0-9.]+",
+              "latmin"  => "[-0-9.]+",
 
               "search"  => ".+"
              );
 
 # Validate the CGI parameters, ignore invalid ones
 
+my %cgiparams=();
+
 foreach my $key (@rawparams)
   {
    foreach my $test (keys (%legalparams))
@@ -67,17 +71,17 @@ foreach my $key (@rawparams)
 
 # Parse the parameters
 
-$marker=$cgiparams{marker};
-$search=$cgiparams{search};
+my $marker=$cgiparams{marker};
+my $search=$cgiparams{search};
 
-$left  =$cgiparams{left};
-$right =$cgiparams{right};
-$top   =$cgiparams{top};
-$bottom=$cgiparams{bottom};
+my $lonmin=$cgiparams{lonmin};
+my $lonmax=$cgiparams{lonmax};
+my $latmax=$cgiparams{latmax};
+my $latmin=$cgiparams{latmin};
 
 # Run the search
 
-($search_time,$search_message, at places)=RunSearch($search,$left,$right,$top,$bottom);
+my($search_time,$search_message, at places)=RunSearch($search,$lonmin,$lonmax,$latmax,$latmin);
 
 # Return the output
 
@@ -86,7 +90,7 @@ print header('text/plain');
 print "$marker\n";
 print "$search_time\n";
 print "$search_message\n";
-foreach $place (@places)
+foreach my $place (@places)
   {
    print "$place\n";
   }
diff --git a/web/www/routino/search.pl b/web/www/routino/search.pl
index a9dc1a7..18bb20f 100644
--- a/web/www/routino/search.pl
+++ b/web/www/routino/search.pl
@@ -3,7 +3,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2012 Andrew M. Bishop
+# This file Copyright 2012-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -19,6 +19,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the directory paths script
 require "paths.pl";
 
@@ -34,7 +36,7 @@ use JSON::PP;
 # Use the perl Time::HiRes module
 use Time::HiRes qw(gettimeofday tv_interval);
 
-$t0 = [gettimeofday];
+my $t0 = [gettimeofday];
 
 
 #
@@ -43,19 +45,20 @@ $t0 = [gettimeofday];
 
 sub RunSearch
   {
-   my($search,$left,$right,$top,$bottom)=@_;
+   my($search,$lonmin,$lonmax,$latmax,$latmin)=@_;
 
    # Perform the search based on the type
 
-   my(@places)=[];
+   my $message="";
+   my @places=[];
 
-   if($search_type eq "nominatim")
+   if($main::search_type eq "nominatim")
      {
-      ($message, at places)=DoNominatimSearch($search,$left,$right,$top,$bottom);
+      ($message, at places)=DoNominatimSearch($search,$lonmin,$lonmax,$latmax,$latmin);
      }
    else
      {
-      $message="Unknown search type '$search_type'";
+      $message="Unknown search type '$main::search_type'";
      }
 
    my(undef,undef,$cuser,$csystem) = times;
@@ -73,19 +76,19 @@ sub RunSearch
 
 sub DoNominatimSearch
   {
-   my($search,$left,$right,$top,$bottom)=@_;
+   my($search,$lonmin,$lonmax,$latmax,$latmin)=@_;
 
    $search = uri_escape($search);
 
    my $url;
 
-   if($left && $right && $top && $bottom)
+   if($lonmin && $lonmax && $latmax && $latmin)
      {
-      $url="$search_baseurl?format=json&viewbox=$left,$top,$right,$bottom&q=$search";
+      $url="$main::search_baseurl?format=json&viewbox=$lonmin,$latmax,$lonmax,$latmin&q=$search";
      }
    else
      {
-      $url="$search_baseurl?format=json&q=$search";
+      $url="$main::search_baseurl?format=json&q=$search";
      }
 
    my $ua=LWP::UserAgent->new;
@@ -97,15 +100,15 @@ sub DoNominatimSearch
       return($res->status_line);
      }
 
-   my($result)=decode_json($res->content);
+   my $result=decode_json($res->content);
 
-   my(@places);
+   my @places=();
 
    foreach my $place (@$result)
      {
-      my($lat)=$place->{"lat"};
-      my($lon)=$place->{"lon"};
-      my($name)=$place->{"display_name"};
+      my $lat=$place->{"lat"};
+      my $lon=$place->{"lon"};
+      my $name=$place->{"display_name"};
 
       push(@places,"$lat $lon $name");
      }
diff --git a/web/www/routino/statistics.cgi b/web/www/routino/statistics.cgi
index fefa9fc..991adee 100755
--- a/web/www/routino/statistics.cgi
+++ b/web/www/routino/statistics.cgi
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2012 Andrew M. Bishop
+# This file Copyright 2008-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,6 +20,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the directory paths script
 require "paths.pl";
 
@@ -33,8 +35,10 @@ print header('text/plain');
 
 # Run the filedumper
 
-$params.=" --dir=$data_dir" if($data_dir);
-$params.=" --prefix=$data_prefix" if($data_prefix);
+my $params="";
+
+$params.=" --dir=$main::data_dir" if($main::data_dir);
+$params.=" --prefix=$main::data_prefix" if($main::data_prefix);
 $params.=" --statistics";
 
-system "$bin_dir/$filedumper_exe $params 2>&1";
+system "$main::bin_dir/$main::filedumper_exe $params 2>&1";
diff --git a/web/www/routino/update-profiles.pl b/web/www/routino/update-profiles.pl
index 9a64306..48e06d3 100755
--- a/web/www/routino/update-profiles.pl
+++ b/web/www/routino/update-profiles.pl
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2011 Andrew M. Bishop
+# This file Copyright 2011-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,14 +20,18 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the directory paths script
 require "paths.pl";
 
 
 # The parameters for the execution
 
-$params.=" --dir=$data_dir" if($data_dir);
-$params.=" --prefix=$data_prefix" if($data_prefix);
+my $params="";
+
+$params.=" --dir=$main::data_dir" if($main::data_dir);
+$params.=" --prefix=$main::data_prefix" if($main::data_prefix);
 
 
 # Generate the Perl profiles.
@@ -39,7 +43,7 @@ print PROFILE "########################### Routino default profile #############
 print PROFILE "################################################################################\n";
 print PROFILE "\n";
 
-open(EXECUTE,"$bin_dir/$router_exe $params --help-profile-perl |") || die "Failed to execute router to generate profiles.\n";
+open(EXECUTE,"$main::bin_dir/$main::router_exe $params --help-profile-perl |") || die "Failed to execute router to generate profiles.\n";
 
 while(<EXECUTE>)
   {
@@ -63,7 +67,7 @@ print PROFILE "/////////////////////////// Routino default profile /////////////
 print PROFILE "////////////////////////////////////////////////////////////////////////////////\n";
 print PROFILE "\n";
 
-open(EXECUTE,"$bin_dir/$router_exe $params --help-profile-json |") || die "Failed to execute router to generate profiles.\n";
+open(EXECUTE,"$main::bin_dir/$main::router_exe $params --help-profile-json |") || die "Failed to execute router to generate profiles.\n";
 
 while(<EXECUTE>)
   {
diff --git a/web/www/routino/visualiser.cgi b/web/www/routino/visualiser.cgi
index a10929a..54b91cf 100755
--- a/web/www/routino/visualiser.cgi
+++ b/web/www/routino/visualiser.cgi
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2012 Andrew M. Bishop
+# This file Copyright 2008-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,6 +20,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+use strict;
+
 # Use the directory paths script
 require "paths.pl";
 
@@ -29,22 +31,25 @@ use CGI ':cgi';
 
 # Create the query and get the parameters
 
-$query=new CGI;
+my $query=new CGI;
 
- at rawparams=$query->param;
+my @rawparams=$query->param;
 
 # Legal CGI parameters with regexp validity check
 
-%legalparams=(
-              "latmin" => "[-0-9.]+",
-              "latmax" => "[-0-9.]+",
-              "lonmin" => "[-0-9.]+",
-              "lonmax" => "[-0-9.]+",
-              "data"   => "(junctions|super|oneway|highway-.*|transport-.*|barrier-.*|turns|speed|weight|height|width|length)"
-             );
+my %legalparams=(
+                 "latmin" => "[-0-9.]+",
+                 "latmax" => "[-0-9.]+",
+                 "lonmin" => "[-0-9.]+",
+                 "lonmax" => "[-0-9.]+",
+                 "data"   => "(junctions|super|oneway|highway-.*|transport-.*|barrier-.*|turns|speed|weight|height|width|length|property-.*|errorlogs)",
+                 "dump"   => "(node|segment|turn-relation|errorlog)[0-9]+"
+                );
 
 # Validate the CGI parameters, ignore invalid ones
 
+my %cgiparams=();
+
 foreach my $key (@rawparams)
   {
    foreach my $test (keys (%legalparams))
@@ -62,59 +67,90 @@ foreach my $key (@rawparams)
      }
   }
 
-# Parameters to limit range selected
-
-%limits=(
-         "junctions" => 0.2,
-         "speed"     => 0.2,
-         "super"     => 0.2,
-         "oneway"    => 0.2,
-         "highway"   => 0.2,
-         "transport" => 0.2,
-         "barrier"   => 0.2,
-         "turns"     => 0.3,
-         "weight"    => 0.3,
-         "height"    => 0.3,
-         "width"     => 0.3,
-         "length"    => 0.3
-        );
-
-# Check the parameters
-
-$latmin=$cgiparams{"latmin"};
-$latmax=$cgiparams{"latmax"};
-$lonmin=$cgiparams{"lonmin"};
-$lonmax=$cgiparams{"lonmax"};
-$data  =$cgiparams{"data"};
-
-if($latmin eq "" || $latmax eq "" || $lonmin eq "" || $lonmax eq "" || $data eq "")
+# Data or dump?
+
+my $params="";
+
+my $data=$cgiparams{"data"};
+my $dump=$cgiparams{"dump"};
+
+if(!defined $data && !defined $dump)
   {
    print header(-status => '500 Invalid CGI parameters');
    exit;
   }
 
-$subdata=$data;
-$subdata="highway"   if($data =~ m%highway-%);
-$subdata="transport" if($data =~ m%transport-%);
-$subdata="barrier"   if($data =~ m%barrier-%);
-
-if(($latmax-$latmin)>$limits{$subdata} || ($lonmax-$lonmin)>$limits{$subdata})
+if(defined $data)
   {
-   print header(-status => '500 Selected area too large');
-   exit;
+   # Parameters to limit range selected
+
+   my %limits=(
+               "junctions" => 0.2,
+               "super"     => 0.2,
+               "oneway"    => 0.2,
+               "highway"   => 0.2,
+               "transport" => 0.2,
+               "barrier"   => 0.3,
+               "turns"     => 0.3,
+               "speed"     => 0.3,
+               "weight"    => 0.3,
+               "height"    => 0.3,
+               "width"     => 0.3,
+               "length"    => 0.3,
+               "property"  => 0.3,
+               "errorlogs" => 0.5
+              );
+
+   # Check the parameters
+
+   my $latmin=$cgiparams{"latmin"};
+   my $latmax=$cgiparams{"latmax"};
+   my $lonmin=$cgiparams{"lonmin"};
+   my $lonmax=$cgiparams{"lonmax"};
+
+   if($latmin eq "" || $latmax eq "" || $lonmin eq "" || $lonmax eq "" || $data eq "")
+     {
+      print header(-status => '500 Invalid CGI parameters');
+      exit;
+     }
+
+   my $subdata=$data;
+   $subdata="highway"   if($data =~ m%highway-%);
+   $subdata="transport" if($data =~ m%transport-%);
+   $subdata="barrier"   if($data =~ m%barrier-%);
+   $subdata="property"  if($data =~ m%property-%);
+
+   if(($latmax-$latmin)>$limits{$subdata} || ($lonmax-$lonmin)>$limits{$subdata})
+     {
+      print header(-status => '500 Selected area too large');
+      exit;
+     }
+
+   # Print the output
+
+   print header('text/plain');
+
+   print "$latmin $lonmin $latmax $lonmax\n";
+
+   # Set the parameters
+
+   $params.=" --visualiser --data=$data";
+   $params.=" --latmin=$latmin --latmax=$latmax --lonmin=$lonmin --lonmax=$lonmax";
   }
+else
+  {
+   # Print the output
 
-# Print the output
+   print header('text/plain');
 
-print header('text/plain');
+   # Set the parameters
 
-print "$latmin $lonmin $latmax $lonmax\n";
+   $params.=" --dump-visualiser --data=$dump";
+  }
 
 # Run the filedumper
 
-$params.=" --dir=$data_dir" if($data_dir);
-$params.=" --prefix=$data_prefix" if($data_prefix);
-$params.=" --visualiser --data=$data";
-$params.=" --latmin=$latmin --latmax=$latmax --lonmin=$lonmin --lonmax=$lonmax";
+$params.=" --dir=$main::data_dir" if($main::data_dir);
+$params.=" --prefix=$main::data_prefix" if($main::data_prefix);
 
-system "$bin_dir/$filedumper_exe $params 2>&1";
+system "$main::bin_dir/$main::filedumper_exe $params 2>&1";
diff --git a/web/www/routino/visualiser.css b/web/www/routino/visualiser.css
index 23a7017..aca80c3 100644
--- a/web/www/routino/visualiser.css
+++ b/web/www/routino/visualiser.css
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2012 Andrew M. Bishop
+// This file Copyright 2008-2013 Andrew M. Bishop
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as published by
@@ -55,6 +55,11 @@ DIV#tab_visualiser_div TABLE
  margin:  0;
 }
 
+DIV#tab_visualiser_div DIV.center
+{
+ text-align: center;
+}
+
 DIV#tab_visualiser_div TABLE TD
 {
  padding: 0;
@@ -68,3 +73,14 @@ DIV#tab_visualiser_div INPUT
  border:  1px solid;
  margin:  0;
 }
+
+
+/*-------*/
+/* Popup */
+/*-------*/
+
+DIV.popup
+{
+ font-family: monospace;
+ font-size:   10px;
+}
diff --git a/web/www/routino/visualiser.html.de b/web/www/routino/visualiser.html.de
new file mode 100644
index 0000000..8b8782a
--- /dev/null
+++ b/web/www/routino/visualiser.html.de
@@ -0,0 +1,458 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routino verifier">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
+
+<title>Routino : Data Visualiser for Routing Data</title>
+
+<!--
+Routino data visualiser web page.
+
+Part of the Routino routing software.
+
+This file Copyright 2008-2014 Andrew M. Bishop
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
+
+<!-- Page elements -->
+<script src="page-elements.js" type="text/javascript"></script>
+<link href="page-elements.css" type="text/css" rel="stylesheet">
+
+<!-- Router and visualiser shared features -->
+<link href="maplayout.css" type="text/css" rel="stylesheet">
+
+<!-- Visualiser specific features -->
+<script src="mapprops.js" type="text/javascript"></script>
+<link href="visualiser.css" type="text/css" rel="stylesheet">
+
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('map_init();');">
+
+<!-- Left hand side of window - data panel -->
+
+<div class="left_panel">
+
+<div class="tab_box">
+<span id="tab_visualiser" onclick="tab_select('visualiser');" class="tab_selected" title="Select data options">Visualiser</span>
+<span id="tab_router" onclick="tab_select('router');" class="tab_unselected" title="Plan a route">Router</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="View database information">Data</span>
+</div>
+
+<div class="tab_content" id="tab_visualiser_div">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Ansichten</span>
+This web page allows visualisation of the data that Routino uses for routing.
+Only data relevant for routing is displayed and some will therefore be excluded.
+<div class="center">
+<a target="other" href="http://www.routino.org/">Routino Website</a>
+|
+<a target="other" href="documentation/">Dokumentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Sprache</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="visualiser.html.en" title="English language webpage">English</a>
+<td>(EN)
+<tr>
+<td><a id="lang_de_url" href="visualiser.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<tr>
+<td><a id="lang_fr_url" href="visualiser.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<tr>
+<td><a id="lang_nl_url" href="visualiser.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<tr>
+<td>
+<td>(RU)
+</table>
+<a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Instructions</span>
+Zoom in and then use the buttons below to download the data. The
+server will only return data if the selected area is small enough.
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_no_data">
+<b><i>No data displayed</i></b>
+</div>
+<div id="result_status_data" style="display: none;">
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Failed to get visualiser data!</b>
+</div>
+<div id="result_status_junctions" style="display: none;">
+<b>Processed # junctions</b>
+</div>
+<div id="result_status_super" style="display: none;">
+<b>Processed # super-nodes/segments</b>
+</div>
+<div id="result_status_oneway" style="display: none;">
+<b>Processed # oneway segments</b>
+</div>
+<div id="result_status_highway" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_transport" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_barrier" style="display: none;">
+<b>Processed # nodes</b>
+</div>
+<div id="result_status_turns" style="display: none;">
+<b>Processed # turn restrictions</b>
+</div>
+<div id="result_status_limit" style="display: none;">
+<b>Processed # limit changes</b>
+</div>
+<div id="result_status_property" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_errorlogs" style="display: none;">
+<b>Processed # error logs</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_junctions_show" onclick="hideshow_show('junctions');" class="hideshow_show">+</span>
+<span id="hideshow_junctions_hide" onclick="hideshow_hide('junctions');" class="hideshow_hide">-</span>
+<input type="button" id="junctions" onclick="displayData('junctions');" value="Display Junctions">
+<div id="hideshow_junctions_div" style="display: none;">
+Each node that is a dead-end, a junction of two highways of different
+types (or different properties) or a junction where more than two segments
+join are shown colour-coded:
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="1" ><td>only one highway - a dead-end.
+<tr><td><img src="icons/ball-2.png" alt="2" ><td>two highways of different types meet.
+<tr><td><img src="icons/ball-3.png" alt="3" ><td>three highways meet.
+<tr><td><img src="icons/ball-4.png" alt="4" ><td>four highways meet.
+<tr><td><img src="icons/ball-5.png" alt="5" ><td>five highways meet.
+<tr><td><img src="icons/ball-6.png" alt="6" ><td>six highways meet.
+<tr><td><img src="icons/ball-7.png" alt="7+"><td>seven (or more) highways meet.
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_super_show" onclick="hideshow_show('super');" class="hideshow_show">+</span>
+<span id="hideshow_super_hide" onclick="hideshow_hide('super');" class="hideshow_hide">-</span>
+<input type="button" id="super" onclick="displayData('super');" value="Display Super Segments">
+<div id="hideshow_super_div" style="display: none;">
+Each super-node and the associated super-segments are shown (see
+algorithm page for description).
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_oneway_show" onclick="hideshow_show('oneway');" class="hideshow_show">+</span>
+<span id="hideshow_oneway_hide" onclick="hideshow_hide('oneway');" class="hideshow_hide">-</span>
+<input type="button" id="oneway" onclick="displayData('oneway');" value="Display One-way Segments">
+<div id="hideshow_oneway_div" style="display: none;">
+Each one-way segment is shown with a coloured triangle indicating the
+allowed direction. The colours of the triangles depend on the bearing
+of the highway segment.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<input type="button" id="highway" onclick="displayData('highway');" value="Display Highway Segments">
+<div id="hideshow_highway_div" style="display: none;">
+Each segment of the chosen type of highway is drawn.
+<form name="highways" id="highways" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Autobahn: <td><input name="highway" type="radio" value="motorway" onchange="displayData('highway');">
+<tr><td>Schnellstraße: <td><input name="highway" type="radio" value="trunk" onchange="displayData('highway');">
+<tr><td>Bundesstraße: <td><input name="highway" type="radio" value="primary" onchange="displayData('highway');" checked>
+<tr><td>Landesstraße: <td><input name="highway" type="radio" value="secondary" onchange="displayData('highway');">
+<tr><td>Hauptstraße: <td><input name="highway" type="radio" value="tertiary" onchange="displayData('highway');">
+<tr><td>Straße:<td><input name="highway" type="radio" value="unclassified" onchange="displayData('highway');">
+<tr><td>Wohnstraße: <td><input name="highway" type="radio" value="residential" onchange="displayData('highway');">
+<tr><td>Zufahrtsweg: <td><input name="highway" type="radio" value="service" onchange="displayData('highway');">
+<tr><td>Feld-(Wald-)weg: <td><input name="highway" type="radio" value="track" onchange="displayData('highway');">
+<tr><td>Fahrradweg: <td><input name="highway" type="radio" value="cycleway" onchange="displayData('highway');">
+<tr><td>Weg: <td><input name="highway" type="radio" value="path" onchange="displayData('highway');">
+<tr><td>Fußweg: <td><input name="highway" type="radio" value="steps" onchange="displayData('highway');">
+<tr><td>Fähre: <td><input name="highway" type="radio" value="ferry" onchange="displayData('highway');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_show">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_hide">-</span>
+<input type="button" id="transport" onclick="displayData('transport');" value="Display Transport Segments">
+<div id="hideshow_transport_div" style="display: none;">
+Each segment allowed for the chosen type of transport is drawn.
+<form name="transports" id="transports" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Fußgänger: <td><input name="transport" type="radio" value="foot" onchange="displayData('transport');">
+<tr><td>Reiter: <td><input name="transport" type="radio" value="horse" onchange="displayData('transport');">
+<tr><td>Rollstuhl:<td><input name="transport" type="radio" value="wheelchair" onchange="displayData('transport');">
+<tr><td>Fahrrad: <td><input name="transport" type="radio" value="bicycle" onchange="displayData('transport');">
+<tr><td>Moped: <td><input name="transport" type="radio" value="moped" onchange="displayData('transport');">
+<tr><td>Motorrad:<td><input name="transport" type="radio" value="motorcycle" onchange="displayData('transport');">
+<tr><td>Auto: <td><input name="transport" type="radio" value="motorcar" onchange="displayData('transport');" checked>
+<tr><td>LKW: <td><input name="transport" type="radio" value="goods" onchange="displayData('transport');">
+<tr><td>Schwertransport: <td><input name="transport" type="radio" value="hgv" onchange="displayData('transport');">
+<tr><td>Personenverkehr: <td><input name="transport" type="radio" value="psv" onchange="displayData('transport');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_barrier_show" onclick="hideshow_show('barrier');" class="hideshow_show">+</span>
+<span id="hideshow_barrier_hide" onclick="hideshow_hide('barrier');" class="hideshow_hide">-</span>
+<input type="button" id="barrier" onclick="displayData('barrier');" value="Display Barrier Nodes">
+<div id="hideshow_barrier_div" style="display: none;">
+Each barrier blocking the chosen type of transport is drawn.
+<form name="barriers" id="barriers" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Fußgänger: <td><input name="barrier" type="radio" value="foot" onchange="displayData('barrier');">
+<tr><td>Reiter: <td><input name="barrier" type="radio" value="horse" onchange="displayData('barrier');">
+<tr><td>Rollstuhl:<td><input name="barrier" type="radio" value="wheelchair" onchange="displayData('barrier');">
+<tr><td>Fahrrad: <td><input name="barrier" type="radio" value="bicycle" onchange="displayData('barrier');">
+<tr><td>Moped: <td><input name="barrier" type="radio" value="moped" onchange="displayData('barrier');">
+<tr><td>Motorrad:<td><input name="barrier" type="radio" value="motorcycle" onchange="displayData('barrier');">
+<tr><td>Auto: <td><input name="barrier" type="radio" value="motorcar" onchange="displayData('barrier');" checked>
+<tr><td>LKW: <td><input name="barrier" type="radio" value="goods" onchange="displayData('barrier');">
+<tr><td>Schwertransport: <td><input name="barrier" type="radio" value="hgv" onchange="displayData('barrier');">
+<tr><td>Personenverkehr: <td><input name="barrier" type="radio" value="psv" onchange="displayData('barrier');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_turns_show" onclick="hideshow_show('turns');" class="hideshow_show">+</span>
+<span id="hideshow_turns_hide" onclick="hideshow_hide('turns');" class="hideshow_hide">-</span>
+<input type="button" id="turns" onclick="displayData('turns');" value="Display Turn Restrictions">
+<div id="hideshow_turns_div" style="display: none;">
+Each turn restrictions is shown with a line indicating the disallowed turn.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<input type="button" id="speed" onclick="displayData('speed');" value="Display Speed Limits">
+<div id="hideshow_speed_div" style="display: none;">
+Each node that joins segments with different speed limits is shown
+along with the speed limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-80.png" alt="(80)"><td>80 km/hour speed limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_weight_show" onclick="hideshow_show('weight');" class="hideshow_show">+</span>
+<span id="hideshow_weight_hide" onclick="hideshow_hide('weight');" class="hideshow_hide">-</span>
+<input type="button" id="weight" onclick="displayData('weight');" value="Display Weight Limits">
+<div id="hideshow_weight_div" style="display: none;">
+Each node that joins segments with different weight limits is shown
+along with the weight limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-8.0.png" alt="(8.0)"><td>8.0 tonnes weight limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_height_show" onclick="hideshow_show('height');" class="hideshow_show">+</span>
+<span id="hideshow_height_hide" onclick="hideshow_hide('height');" class="hideshow_hide">-</span>
+<input type="button" id="height" onclick="displayData('height');" value="Display Height Limits">
+<div id="hideshow_height_div" style="display: none;">
+Each node that joins segments with different height limits is shown
+along with the height limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-4.0.png" alt="(4.0)"><td>4.0 m height limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_width_show" onclick="hideshow_show('width');" class="hideshow_show">+</span>
+<span id="hideshow_width_hide" onclick="hideshow_hide('width');" class="hideshow_hide">-</span>
+<input type="button" id="width" onclick="displayData('width');" value="Display Width Limits">
+<div id="hideshow_width_div" style="display: none;">
+Each node that joins segments with different width limits is shown
+along with the width limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-3.0.png" alt="(3.0)"><td>3.0 m width limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_length_show" onclick="hideshow_show('length');" class="hideshow_show">+</span>
+<span id="hideshow_length_hide" onclick="hideshow_hide('length');" class="hideshow_hide">-</span>
+<input type="button" id="length" onclick="displayData('length');" value="Display Length Limits">
+<div id="hideshow_length_div" style="display: none;">
+Each node that joins segments with different length limits is shown
+along with the length limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-9.0.png" alt="(9.0)"><td>9.0 m length limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<input type="button" id="property" onclick="displayData('property');" value="Display Highway Properties">
+<div id="hideshow_property_div" style="display: none;">
+Each segment of the highways with a particular property is drawn.
+<form name="properties" id="properties" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>befestigt: <td><input name="property" type="radio" value="paved" onchange="displayData('property');" checked>
+<tr><td>mehrspurig: <td><input name="property" type="radio" value="multilane" onchange="displayData('property');">
+<tr><td>Brücken: <td><input name="property" type="radio" value="bridge" onchange="displayData('property');">
+<tr><td>Tunnel: <td><input name="property" type="radio" value="tunnel" onchange="displayData('property');">
+<tr><td>Wanderweg: <td><input name="property" type="radio" value="footroute" onchange="displayData('property');">
+<tr><td>Radweg: <td><input name="property" type="radio" value="bicycleroute" onchange="displayData('property');">
+<tr><td>Cycle Both Ways:<td><input name="property" type="radio" value="cyclebothways" onchange="displayData('property');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_errorlogs_show" onclick="hideshow_show('errorlogs');" class="hideshow_show">+</span>
+<span id="hideshow_errorlogs_hide" onclick="hideshow_hide('errorlogs');" class="hideshow_hide">-</span>
+<input type="button" id="errorlogs" onclick="displayData('errorlogs');" value="Display Error Logs">
+<div id="hideshow_errorlogs_div" style="display: none;">
+Potential problems found by Routino when processing the input data.
+</div>
+</div>
+
+<div class="hideshow_box">
+<input type="button" id="clear" onclick="displayData('');" value="Clear data">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Links</span>
+<a id="permalink_url" href="visualiser.html">anpassen dieser Kartenansicht</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Bearbeitet die OSM-Daten</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Hilfe</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+Zoom to an area and select one of the buttons to display that type of
+data.
+<br>
+More data options can be found by expanding the details below each
+button.
+<p>
+<b>Data Failure</b>
+<br>
+If the area selected is too large (depends on the data type) then the
+status will say "Failed to get visualiser data" - zoom in and try
+again.
+</div>
+</div>
+</div>
+</div>
+
+<div class="tab_content" id="tab_router_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Router</span>
+To perform routing on the map use the link below.
+<br>
+<a id="router_url" href="router.html" target="router">anpassen dieser Kartenansicht</a>
+</div>
+</div>
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistik</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">zeige die Statistik</a>
+</div>
+</div>
+
+</div>
+
+<!-- Right hand side of window - map -->
+
+<div class="right_panel">
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript is <em>required</em> to use this web page because of the interactive map.
+</noscript>
+</div>
+<div class="attribution">
+Router: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/visualiser.html.en b/web/www/routino/visualiser.html.en
index 24aa157..c39f289 100644
--- a/web/www/routino/visualiser.html.en
+++ b/web/www/routino/visualiser.html.en
@@ -1,34 +1,33 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
+<html>
 
-<!--
- Routino data visualiser web page.
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routino verifier">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
 
- Part of the Routino routing software.
+<title>Routino : Data Visualiser for Routing Data</title>
 
- This file Copyright 2008-2012 Andrew M. Bishop
+<!--
+Routino data visualiser web page.
 
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+Part of the Routino routing software.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
+This file Copyright 2008-2014 Andrew M. Bishop
 
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see http://www.gnu.org/licenses/.
--->
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
-<HEAD>
-<TITLE>Routino : Data Visualiser for Routino OpenStreetMap Data</TITLE>
-<META name="keywords" content="openstreetmap routino verifier">
-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
 
-<!-- OpenLayers Javascript library -->
-<script src="../openlayers/OpenLayers.js" type="text/javascript"></script>
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
 
 <!-- Page elements -->
 <script src="page-elements.js" type="text/javascript"></script>
@@ -36,359 +35,424 @@
 
 <!-- Router and visualiser shared features -->
 <link href="maplayout.css" type="text/css" rel="stylesheet">
-<!--[if IE 6]>
-  <link href="maplayout-ie6-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
-<!--[if IE 7]>
-  <link href="maplayout-ie7-bugfixes.css" type="text/css" rel="stylesheet">
-<![endif]-->
 
 <!-- Visualiser specific features -->
 <script src="mapprops.js" type="text/javascript"></script>
-<script src="visualiser.js" type="text/javascript"></script>
 <link href="visualiser.css" type="text/css" rel="stylesheet">
 
-</HEAD>
-<BODY onload="map_init();">
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('map_init();');">
 
 <!-- Left hand side of window - data panel -->
 
 <div class="left_panel">
 
-  <div class="tab_box">
-    <span id="tab_visualiser" onclick="tab_select('visualiser');" class="tab_selected"   title="Select data options">Visualiser</span>
-    <span id="tab_router"     onclick="tab_select('router');"     class="tab_unselected" title="Plan a route">Router</span>
-    <span id="tab_data"       onclick="tab_select('data');"       class="tab_unselected" title="View database information">Data</span>
-  </div>
-
-  <div class="tab_content" id="tab_visualiser_div">
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Routino Data Visualiser</span>
-      This web page allows visualisation of the data that Routino uses for routing.
-      Only data relevant for routing is displayed and some will therefore be excluded.
-      <div align="center">
-        <a target="other" href="http://www.routino.org/">Routino Website</a>
-        |
-        <a target="other" href="../documentation/">Documentation</a>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
-      <span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
-      <span class="hideshow_title">Language</span>
-
-      <!-- Note for translations: Only this HTML file needs to be translated, the Javascript has
-           no language specific information in it.  Only the body text and title attributes should
-           be changed, the values passed to the JavaScript and the element names must not be changed.
-           The selection below changes the language option passed to the router and selects the
-           output language not the web page language, the links are for that.  The router itself uses
-           the translations.xml file for the translated versions of the output. -->
-
-      <div id="hideshow_language_div" style="display: none;">
-        <table>
-          <tr>
-            <td><a id="lang_en_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="visualiser.html.en" title="English language web page">English</a>
-            <td>(EN)
-        </table>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Instructions</span>
-      Zoom in and then use the buttons below to download the data.  The
-      server will only return data if the selected area is small enough.
-    </div>
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Status</span>
-      <div id="result_status">
-        <div id="result_status_no_data">
-          <b><i>No data displayed</i></b>
-        </div>
-        <div id="result_status_data" style="display: none;">
-        </div>
-        <div id="result_status_error" style="display: none;">
-          <b>Failed to get visualiser data!</b>
-        </div>
-        <div id="result_status_junctions" style="display: none;">
-          <b>Processed # junctions</b>
-        </div>
-        <div id="result_status_super"     style="display: none;">
-          <b>Processed # super-nodes/segments</b>
-        </div>
-        <div id="result_status_oneway"    style="display: none;">
-          <b>Processed # oneway segments</b>
-        </div>
-        <div id="result_status_highway"   style="display: none;">
-          <b>Processed # segments</b>
-        </div>
-        <div id="result_status_transport" style="display: none;">
-          <b>Processed # segments</b>
-        </div>
-        <div id="result_status_barrier"   style="display: none;">
-          <b>Processed # nodes</b>
-        </div>
-        <div id="result_status_turns"     style="display: none;">
-          <b>Processed # turn restrictions</b>
-        </div>
-        <div id="result_status_limit"     style="display: none;">
-          <b>Processed # limits</b>
-        </div>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_junctions_show" onclick="hideshow_show('junctions');" class="hideshow_show">+</span>
-      <span id="hideshow_junctions_hide" onclick="hideshow_hide('junctions');" class="hideshow_hide">-</span>
-      <input type="button" id="junctions" onclick="displayData('junctions');" value="Display Junctions">
-      <div id="hideshow_junctions_div" style="display: none;">
-        Each node that is a dead-end, a junction of two highways of different
-        types (or different properties) or a junction where more than two segments
-        join are shown colour-coded:
-        <br>
-        <table>
-          <tr><td><img src="icons/ball-1.png" alt="Red"   ><td>only one highway - a dead-end.
-          <tr><td><img src="icons/ball-2.png" alt="Yellow"><td>two highways of different types meet.
-          <tr><td><img src="icons/ball-3.png" alt="Green" ><td>three highways meet.
-          <tr><td><img src="icons/ball-4.png" alt="Brown" ><td>four highways meet.
-          <tr><td><img src="icons/ball-5.png" alt="Blue"  ><td>five highways meet.
-          <tr><td><img src="icons/ball-6.png" alt="Pink"  ><td>six highways meet.
-          <tr><td><img src="icons/ball-7.png" alt="Black" ><td>seven (or more) highways meet.
-        </table>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_super_show" onclick="hideshow_show('super');" class="hideshow_show">+</span>
-      <span id="hideshow_super_hide" onclick="hideshow_hide('super');" class="hideshow_hide">-</span>
-      <input type="button" id="super" onclick="displayData('super');" value="Display Super Segments">
-      <div id="hideshow_super_div" style="display: none;">
-        Each super-node and the associated super-segments are shown (see
-        algorithm page for description).
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_oneway_show" onclick="hideshow_show('oneway');" class="hideshow_show">+</span>
-      <span id="hideshow_oneway_hide" onclick="hideshow_hide('oneway');" class="hideshow_hide">-</span>
-      <input type="button" id="oneway" onclick="displayData('oneway');" value="Display One-way Segments">
-      <div id="hideshow_oneway_div" style="display: none;">
-        Each one-way segment is shown with a coloured triangle indicating the
-        allowed direction.  The colours of the triangles depend on the bearing
-        of the highway segment.
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
-      <span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
-      <input type="button" id="highway" onclick="displayData('highway');" value="Display Highway Segments">
-      <div id="hideshow_highway_div" style="display: none;">
-        Each segment of the chosen type of highway is drawn.
-        <form name="highways" id="highways" action="" method="get" onsubmit="return false;">
-          <table>
-            <tr><td>Motorway:    <td><input name="highway" type="radio" value="motorway"    >
-            <tr><td>Trunk:       <td><input name="highway" type="radio" value="trunk"       >
-            <tr><td>Primary:     <td><input name="highway" type="radio" value="primary"     checked>
-            <tr><td>Secondary:   <td><input name="highway" type="radio" value="secondary"   >
-            <tr><td>Tertiary:    <td><input name="highway" type="radio" value="tertiary"    >
-            <tr><td>Unclassified:<td><input name="highway" type="radio" value="unclassified">
-            <tr><td>Residential: <td><input name="highway" type="radio" value="residential" >
-            <tr><td>Service:     <td><input name="highway" type="radio" value="service"     >
-            <tr><td>Track:       <td><input name="highway" type="radio" value="track"       >
-            <tr><td>Cycleway:    <td><input name="highway" type="radio" value="cycleway"    >
-            <tr><td>Path:        <td><input name="highway" type="radio" value="path"        >
-            <tr><td>Steps:       <td><input name="highway" type="radio" value="steps"       >
-            <tr><td>Ferry:       <td><input name="highway" type="radio" value="ferry"       >
-          </table>
-        </form>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_show">+</span>
-      <span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_hide">-</span>
-      <input type="button" id="transport" onclick="displayData('transport');" value="Display Transport Segments">
-      <div id="hideshow_transport_div" style="display: none;">
-        Each segment allowed for the chosen type of transport is drawn.
-        <form name="transports" id="transports" action="" method="get" onsubmit="return false;">
-          <table>
-            <tr><td>Foot      <td><input name="transport" type="radio" value="foot"      >
-            <tr><td>Horse     <td><input name="transport" type="radio" value="horse"     >
-            <tr><td>Wheelchair<td><input name="transport" type="radio" value="wheelchair">
-            <tr><td>Bicycle   <td><input name="transport" type="radio" value="bicycle"   >
-            <tr><td>Moped     <td><input name="transport" type="radio" value="moped"     >
-            <tr><td>Motorbike <td><input name="transport" type="radio" value="motorbike" >
-            <tr><td>Motorcar  <td><input name="transport" type="radio" value="motorcar"  checked>
-            <tr><td>Goods     <td><input name="transport" type="radio" value="goods"     >
-            <tr><td>HGV       <td><input name="transport" type="radio" value="hgv"       >
-            <tr><td>PSV       <td><input name="transport" type="radio" value="psv"       >
-          </table>
-        </form>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_barrier_show" onclick="hideshow_show('barrier');" class="hideshow_show">+</span>
-      <span id="hideshow_barrier_hide" onclick="hideshow_hide('barrier');" class="hideshow_hide">-</span>
-      <input type="button" id="barrier" onclick="displayData('barrier');" value="Display Barrier Nodes">
-      <div id="hideshow_barrier_div" style="display: none;">
-        Each barrier blocking the chosen type of transport is drawn.
-        <form name="barriers" id="barriers" action="" method="get" onsubmit="return false;">
-          <table>
-            <tr><td>Foot      <td><input name="barrier" type="radio" value="foot"      >
-            <tr><td>Horse     <td><input name="barrier" type="radio" value="horse"     >
-            <tr><td>Wheelchair<td><input name="barrier" type="radio" value="wheelchair">
-            <tr><td>Bicycle   <td><input name="barrier" type="radio" value="bicycle"   >
-            <tr><td>Moped     <td><input name="barrier" type="radio" value="moped"     >
-            <tr><td>Motorbike <td><input name="barrier" type="radio" value="motorbike" >
-            <tr><td>Motorcar  <td><input name="barrier" type="radio" value="motorcar"  checked>
-            <tr><td>Goods     <td><input name="barrier" type="radio" value="goods"     >
-            <tr><td>HGV       <td><input name="barrier" type="radio" value="hgv"       >
-            <tr><td>PSV       <td><input name="barrier" type="radio" value="psv"       >
-          </table>
-        </form>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_turns_show" onclick="hideshow_show('turns');" class="hideshow_show">+</span>
-      <span id="hideshow_turns_hide" onclick="hideshow_hide('turns');" class="hideshow_hide">-</span>
-      <input type="button" id="turns" onclick="displayData('turns');" value="Display Turn Restrictions">
-      <div id="hideshow_turns_div" style="display: none;">
-        Each turn restrictions is shown with a line indicating the disallowed
-        turn.
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
-      <span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
-      <input type="button" id="speed" onclick="displayData('speed');" value="Display Speed Limits">
-      <div id="hideshow_speed_div" style="display: none;">
-        Each node that joins segments with different speed limits is shown
-        along with the speed limit on relevant segments.
-        <br>
-        <table>
-          <tr><td><img src="icons/ball-1.png"   alt="Red dot"><td>Change of limit
-          <tr><td><img src="icons/limit-no.png" alt="(no)"   ><td>No specified speed limit
-          <tr><td><img src="icons/limit-80.png" alt="(80)"   ><td>80 km/hour speed limit
-        </table>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_weight_show" onclick="hideshow_show('weight');" class="hideshow_show">+</span>
-      <span id="hideshow_weight_hide" onclick="hideshow_hide('weight');" class="hideshow_hide">-</span>
-      <input type="button" id="weight" onclick="displayData('weight');" value="Display Weight Limits">
-      <div id="hideshow_weight_div" style="display: none;">
-        Each node that joins segments with different weight limits is shown
-        along with the weight limit on relevant segments.  For example:
-        <br>
-        <table>
-          <tr><td><img src="icons/ball-1.png"    alt="Red dot"><td>Change of limit
-          <tr><td><img src="icons/limit-no.png"  alt="(no)"   ><td>No specified weight limit
-          <tr><td><img src="icons/limit-8.0.png" alt="(8.0)"  ><td>8.0 tonnes weight limit
-        </table>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_height_show" onclick="hideshow_show('height');" class="hideshow_show">+</span>
-      <span id="hideshow_height_hide" onclick="hideshow_hide('height');" class="hideshow_hide">-</span>
-      <input type="button" id="height" onclick="displayData('height');" value="Display Height Limits">
-      <div id="hideshow_height_div" style="display: none;">
-        Each node that joins segments with different height limits is shown
-        along with the height limit on relevant segments.  For example:
-        <br>
-        <table>
-          <tr><td><img src="icons/ball-1.png"    alt="Red dot"><td>Change of limit
-          <tr><td><img src="icons/limit-no.png"  alt="(no)"   ><td>No specified height limit
-          <tr><td><img src="icons/limit-4.0.png" alt="(4.0)"  ><td>4.0 m height limit
-        </table>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_width_show" onclick="hideshow_show('width');" class="hideshow_show">+</span>
-      <span id="hideshow_width_hide" onclick="hideshow_hide('width');" class="hideshow_hide">-</span>
-      <input type="button" id="width" onclick="displayData('width');" value="Display Width Limits">
-      <div id="hideshow_width_div" style="display: none;">
-        Each node that joins segments with different width limits is shown
-        along with the width limit on relevant segments.  For example:
-        <br>
-        <table>
-          <tr><td><img src="icons/ball-1.png"    alt="Red dot"><td>Change of limit
-          <tr><td><img src="icons/limit-no.png"  alt="(no)"   ><td>No specified width limit
-          <tr><td><img src="icons/limit-3.0.png" alt="(3.0)"  ><td>3.0 m width limit
-        </table>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <span id="hideshow_length_show" onclick="hideshow_show('length');" class="hideshow_show">+</span>
-      <span id="hideshow_length_hide" onclick="hideshow_hide('length');" class="hideshow_hide">-</span>
-      <input type="button" id="length" onclick="displayData('length');" value="Display Length Limits">
-      <div id="hideshow_length_div" style="display: none;">
-        Each node that joins segments with different length limits is shown
-        along with the length limit on relevant segments.  For example:
-        <br>
-        <table>
-          <tr><td><img src="icons/ball-1.png"    alt="Red dot"><td>Change of limit
-          <tr><td><img src="icons/limit-no.png"  alt="(no)"   ><td>No specified length limit
-          <tr><td><img src="icons/limit-9.0.png" alt="(9.0)"  ><td>9.0 m length limit
-        </table>
-      </div>
-    </div>
-
-    <div class="hideshow_box">
-      <input type="button" id="clear" onclick="displayData('');" value="Clear data">
-    </div>
-
-    <div class="hideshow_box">
-      <span class="hideshow_title">Links</span>
-      <a id="permalink_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="visualiser.html">Permanent link to this view</a>
-      <br>
-      <a id="edit_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="http://www.openstreetmap.org/" target="edit">Edit OSM data in Potlatch</a>
-    </div>
-  </div>
-
-  <div class="tab_content" id="tab_router_div" style="display: none;">
-    <div class="hideshow_box">
-      <span class="hideshow_title">Router</span>
-      To perform routing on the map use the link below.
-      <br>
-      <a id="router_url" onmouseover="updateURL(this);" onfocus="updateURL(this);" onclick="updateURL(this);" href="router.html" target="router">Custom link to this map view</a>
-    </div>
-  </div>
-
-  <div class="tab_content" id="tab_data_div" style="display: none;">
-    <div class="hideshow_box">
-      <span class="hideshow_title">Statistics</span>
-      <div id="statistics_data"></div>
-      <a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
-    </div>
-  </div>
+<div class="tab_box">
+<span id="tab_visualiser" onclick="tab_select('visualiser');" class="tab_selected" title="Select data options">Visualiser</span>
+<span id="tab_router" onclick="tab_select('router');" class="tab_unselected" title="Plan a route">Router</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="View database information">Data</span>
+</div>
+
+<div class="tab_content" id="tab_visualiser_div">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Visualiser</span>
+This web page allows visualisation of the data that Routino uses for routing.
+Only data relevant for routing is displayed and some will therefore be excluded.
+<div class="center">
+<a target="other" href="http://www.routino.org/">Routino Website</a>
+|
+<a target="other" href="documentation/">Documentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Language</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="visualiser.html.en" title="English language webpage">English</a>
+<td>(EN)
+<tr>
+<td><a id="lang_de_url" href="visualiser.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<tr>
+<td><a id="lang_fr_url" href="visualiser.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<tr>
+<td><a id="lang_nl_url" href="visualiser.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<tr>
+<td>
+<td>(RU)
+</table>
+<a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Instructions</span>
+Zoom in and then use the buttons below to download the data. The
+server will only return data if the selected area is small enough.
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_no_data">
+<b><i>No data displayed</i></b>
+</div>
+<div id="result_status_data" style="display: none;">
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Failed to get visualiser data!</b>
+</div>
+<div id="result_status_junctions" style="display: none;">
+<b>Processed # junctions</b>
+</div>
+<div id="result_status_super" style="display: none;">
+<b>Processed # super-nodes/segments</b>
+</div>
+<div id="result_status_oneway" style="display: none;">
+<b>Processed # oneway segments</b>
+</div>
+<div id="result_status_highway" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_transport" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_barrier" style="display: none;">
+<b>Processed # nodes</b>
+</div>
+<div id="result_status_turns" style="display: none;">
+<b>Processed # turn restrictions</b>
+</div>
+<div id="result_status_limit" style="display: none;">
+<b>Processed # limit changes</b>
+</div>
+<div id="result_status_property" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_errorlogs" style="display: none;">
+<b>Processed # error logs</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_junctions_show" onclick="hideshow_show('junctions');" class="hideshow_show">+</span>
+<span id="hideshow_junctions_hide" onclick="hideshow_hide('junctions');" class="hideshow_hide">-</span>
+<input type="button" id="junctions" onclick="displayData('junctions');" value="Display Junctions">
+<div id="hideshow_junctions_div" style="display: none;">
+Each node that is a dead-end, a junction of two highways of different
+types (or different properties) or a junction where more than two segments
+join are shown colour-coded:
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="1" ><td>only one highway - a dead-end.
+<tr><td><img src="icons/ball-2.png" alt="2" ><td>two highways of different types meet.
+<tr><td><img src="icons/ball-3.png" alt="3" ><td>three highways meet.
+<tr><td><img src="icons/ball-4.png" alt="4" ><td>four highways meet.
+<tr><td><img src="icons/ball-5.png" alt="5" ><td>five highways meet.
+<tr><td><img src="icons/ball-6.png" alt="6" ><td>six highways meet.
+<tr><td><img src="icons/ball-7.png" alt="7+"><td>seven (or more) highways meet.
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_super_show" onclick="hideshow_show('super');" class="hideshow_show">+</span>
+<span id="hideshow_super_hide" onclick="hideshow_hide('super');" class="hideshow_hide">-</span>
+<input type="button" id="super" onclick="displayData('super');" value="Display Super Segments">
+<div id="hideshow_super_div" style="display: none;">
+Each super-node and the associated super-segments are shown (see
+algorithm page for description).
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_oneway_show" onclick="hideshow_show('oneway');" class="hideshow_show">+</span>
+<span id="hideshow_oneway_hide" onclick="hideshow_hide('oneway');" class="hideshow_hide">-</span>
+<input type="button" id="oneway" onclick="displayData('oneway');" value="Display One-way Segments">
+<div id="hideshow_oneway_div" style="display: none;">
+Each one-way segment is shown with a coloured triangle indicating the
+allowed direction. The colours of the triangles depend on the bearing
+of the highway segment.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<input type="button" id="highway" onclick="displayData('highway');" value="Display Highway Segments">
+<div id="hideshow_highway_div" style="display: none;">
+Each segment of the chosen type of highway is drawn.
+<form name="highways" id="highways" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Motorway: <td><input name="highway" type="radio" value="motorway" onchange="displayData('highway');">
+<tr><td>Trunk: <td><input name="highway" type="radio" value="trunk" onchange="displayData('highway');">
+<tr><td>Primary: <td><input name="highway" type="radio" value="primary" onchange="displayData('highway');" checked>
+<tr><td>Secondary: <td><input name="highway" type="radio" value="secondary" onchange="displayData('highway');">
+<tr><td>Tertiary: <td><input name="highway" type="radio" value="tertiary" onchange="displayData('highway');">
+<tr><td>Unclassified:<td><input name="highway" type="radio" value="unclassified" onchange="displayData('highway');">
+<tr><td>Residential: <td><input name="highway" type="radio" value="residential" onchange="displayData('highway');">
+<tr><td>Service: <td><input name="highway" type="radio" value="service" onchange="displayData('highway');">
+<tr><td>Track: <td><input name="highway" type="radio" value="track" onchange="displayData('highway');">
+<tr><td>Cycleway: <td><input name="highway" type="radio" value="cycleway" onchange="displayData('highway');">
+<tr><td>Path: <td><input name="highway" type="radio" value="path" onchange="displayData('highway');">
+<tr><td>Steps: <td><input name="highway" type="radio" value="steps" onchange="displayData('highway');">
+<tr><td>Ferry: <td><input name="highway" type="radio" value="ferry" onchange="displayData('highway');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_show">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_hide">-</span>
+<input type="button" id="transport" onclick="displayData('transport');" value="Display Transport Segments">
+<div id="hideshow_transport_div" style="display: none;">
+Each segment allowed for the chosen type of transport is drawn.
+<form name="transports" id="transports" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Foot: <td><input name="transport" type="radio" value="foot" onchange="displayData('transport');">
+<tr><td>Horse: <td><input name="transport" type="radio" value="horse" onchange="displayData('transport');">
+<tr><td>Wheelchair:<td><input name="transport" type="radio" value="wheelchair" onchange="displayData('transport');">
+<tr><td>Bicycle: <td><input name="transport" type="radio" value="bicycle" onchange="displayData('transport');">
+<tr><td>Moped: <td><input name="transport" type="radio" value="moped" onchange="displayData('transport');">
+<tr><td>Motorcycle:<td><input name="transport" type="radio" value="motorcycle" onchange="displayData('transport');">
+<tr><td>Motorcar: <td><input name="transport" type="radio" value="motorcar" onchange="displayData('transport');" checked>
+<tr><td>Goods: <td><input name="transport" type="radio" value="goods" onchange="displayData('transport');">
+<tr><td>HGV: <td><input name="transport" type="radio" value="hgv" onchange="displayData('transport');">
+<tr><td>PSV: <td><input name="transport" type="radio" value="psv" onchange="displayData('transport');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_barrier_show" onclick="hideshow_show('barrier');" class="hideshow_show">+</span>
+<span id="hideshow_barrier_hide" onclick="hideshow_hide('barrier');" class="hideshow_hide">-</span>
+<input type="button" id="barrier" onclick="displayData('barrier');" value="Display Barrier Nodes">
+<div id="hideshow_barrier_div" style="display: none;">
+Each barrier blocking the chosen type of transport is drawn.
+<form name="barriers" id="barriers" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Foot: <td><input name="barrier" type="radio" value="foot" onchange="displayData('barrier');">
+<tr><td>Horse: <td><input name="barrier" type="radio" value="horse" onchange="displayData('barrier');">
+<tr><td>Wheelchair:<td><input name="barrier" type="radio" value="wheelchair" onchange="displayData('barrier');">
+<tr><td>Bicycle: <td><input name="barrier" type="radio" value="bicycle" onchange="displayData('barrier');">
+<tr><td>Moped: <td><input name="barrier" type="radio" value="moped" onchange="displayData('barrier');">
+<tr><td>Motorcycle:<td><input name="barrier" type="radio" value="motorcycle" onchange="displayData('barrier');">
+<tr><td>Motorcar: <td><input name="barrier" type="radio" value="motorcar" onchange="displayData('barrier');" checked>
+<tr><td>Goods: <td><input name="barrier" type="radio" value="goods" onchange="displayData('barrier');">
+<tr><td>HGV: <td><input name="barrier" type="radio" value="hgv" onchange="displayData('barrier');">
+<tr><td>PSV: <td><input name="barrier" type="radio" value="psv" onchange="displayData('barrier');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_turns_show" onclick="hideshow_show('turns');" class="hideshow_show">+</span>
+<span id="hideshow_turns_hide" onclick="hideshow_hide('turns');" class="hideshow_hide">-</span>
+<input type="button" id="turns" onclick="displayData('turns');" value="Display Turn Restrictions">
+<div id="hideshow_turns_div" style="display: none;">
+Each turn restrictions is shown with a line indicating the disallowed turn.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<input type="button" id="speed" onclick="displayData('speed');" value="Display Speed Limits">
+<div id="hideshow_speed_div" style="display: none;">
+Each node that joins segments with different speed limits is shown
+along with the speed limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-80.png" alt="(80)"><td>80 km/hour speed limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_weight_show" onclick="hideshow_show('weight');" class="hideshow_show">+</span>
+<span id="hideshow_weight_hide" onclick="hideshow_hide('weight');" class="hideshow_hide">-</span>
+<input type="button" id="weight" onclick="displayData('weight');" value="Display Weight Limits">
+<div id="hideshow_weight_div" style="display: none;">
+Each node that joins segments with different weight limits is shown
+along with the weight limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-8.0.png" alt="(8.0)"><td>8.0 tonnes weight limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_height_show" onclick="hideshow_show('height');" class="hideshow_show">+</span>
+<span id="hideshow_height_hide" onclick="hideshow_hide('height');" class="hideshow_hide">-</span>
+<input type="button" id="height" onclick="displayData('height');" value="Display Height Limits">
+<div id="hideshow_height_div" style="display: none;">
+Each node that joins segments with different height limits is shown
+along with the height limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-4.0.png" alt="(4.0)"><td>4.0 m height limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_width_show" onclick="hideshow_show('width');" class="hideshow_show">+</span>
+<span id="hideshow_width_hide" onclick="hideshow_hide('width');" class="hideshow_hide">-</span>
+<input type="button" id="width" onclick="displayData('width');" value="Display Width Limits">
+<div id="hideshow_width_div" style="display: none;">
+Each node that joins segments with different width limits is shown
+along with the width limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-3.0.png" alt="(3.0)"><td>3.0 m width limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_length_show" onclick="hideshow_show('length');" class="hideshow_show">+</span>
+<span id="hideshow_length_hide" onclick="hideshow_hide('length');" class="hideshow_hide">-</span>
+<input type="button" id="length" onclick="displayData('length');" value="Display Length Limits">
+<div id="hideshow_length_div" style="display: none;">
+Each node that joins segments with different length limits is shown
+along with the length limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-9.0.png" alt="(9.0)"><td>9.0 m length limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<input type="button" id="property" onclick="displayData('property');" value="Display Highway Properties">
+<div id="hideshow_property_div" style="display: none;">
+Each segment of the highways with a particular property is drawn.
+<form name="properties" id="properties" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Paved: <td><input name="property" type="radio" value="paved" onchange="displayData('property');" checked>
+<tr><td>Multiple Lanes: <td><input name="property" type="radio" value="multilane" onchange="displayData('property');">
+<tr><td>Bridge: <td><input name="property" type="radio" value="bridge" onchange="displayData('property');">
+<tr><td>Tunnel: <td><input name="property" type="radio" value="tunnel" onchange="displayData('property');">
+<tr><td>Walking Route: <td><input name="property" type="radio" value="footroute" onchange="displayData('property');">
+<tr><td>Bicycle Route: <td><input name="property" type="radio" value="bicycleroute" onchange="displayData('property');">
+<tr><td>Cycle Both Ways:<td><input name="property" type="radio" value="cyclebothways" onchange="displayData('property');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_errorlogs_show" onclick="hideshow_show('errorlogs');" class="hideshow_show">+</span>
+<span id="hideshow_errorlogs_hide" onclick="hideshow_hide('errorlogs');" class="hideshow_hide">-</span>
+<input type="button" id="errorlogs" onclick="displayData('errorlogs');" value="Display Error Logs">
+<div id="hideshow_errorlogs_div" style="display: none;">
+Potential problems found by Routino when processing the input data.
+</div>
+</div>
+
+<div class="hideshow_box">
+<input type="button" id="clear" onclick="displayData('');" value="Clear data">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Links</span>
+<a id="permalink_url" href="visualiser.html">Link to this map view</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Edit this OSM data</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Help</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+Zoom to an area and select one of the buttons to display that type of
+data.
+<br>
+More data options can be found by expanding the details below each
+button.
+<p>
+<b>Data Failure</b>
+<br>
+If the area selected is too large (depends on the data type) then the
+status will say "Failed to get visualiser data" - zoom in and try
+again.
+</div>
+</div>
+</div>
+</div>
+
+<div class="tab_content" id="tab_router_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Router</span>
+To perform routing on the map use the link below.
+<br>
+<a id="router_url" href="router.html" target="router">Link to this map view</a>
+</div>
+</div>
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistics</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
+</div>
+</div>
 
 </div>
 
 <!-- Right hand side of window - map -->
 
 <div class="right_panel">
-  <div class="map" id="map">
-    <noscript>
-      Javascript is <em>required</em> to use this web page because of the
-      interactive map.
-    </noscript>
-  </div>
-  <div class="attribution">
-    <a target="other" href="http://www.routino.org/" title="Routino">Data Manipulation: Routino</a>
-    |
-    <a target="other" href="http://www.openstreetmap.org/" title="Copyright: OpenStreetMap.org; License: Creative Commons Attribution-Share Alike 2.0">Geo Data: OpenStreetMap</a>
-  </div>
-</div>
-
-</BODY>
-</HTML>
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript is <em>required</em> to use this web page because of the interactive map.
+</noscript>
+</div>
+<div class="attribution">
+Router: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/visualiser.html.fr b/web/www/routino/visualiser.html.fr
new file mode 100644
index 0000000..e49b6ee
--- /dev/null
+++ b/web/www/routino/visualiser.html.fr
@@ -0,0 +1,458 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routino verifier">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
+
+<title>Routino : Data Visualiser for Routing Data</title>
+
+<!--
+Routino data visualiser web page.
+
+Part of the Routino routing software.
+
+This file Copyright 2008-2014 Andrew M. Bishop
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
+
+<!-- Page elements -->
+<script src="page-elements.js" type="text/javascript"></script>
+<link href="page-elements.css" type="text/css" rel="stylesheet">
+
+<!-- Router and visualiser shared features -->
+<link href="maplayout.css" type="text/css" rel="stylesheet">
+
+<!-- Visualiser specific features -->
+<script src="mapprops.js" type="text/javascript"></script>
+<link href="visualiser.css" type="text/css" rel="stylesheet">
+
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('map_init();');">
+
+<!-- Left hand side of window - data panel -->
+
+<div class="left_panel">
+
+<div class="tab_box">
+<span id="tab_visualiser" onclick="tab_select('visualiser');" class="tab_selected" title="Select data options">Visualiser</span>
+<span id="tab_router" onclick="tab_select('router');" class="tab_unselected" title="Plan a route">Router</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="View database information">Data</span>
+</div>
+
+<div class="tab_content" id="tab_visualiser_div">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Visualiser</span>
+This web page allows visualisation of the data that Routino uses for routing.
+Only data relevant for routing is displayed and some will therefore be excluded.
+<div class="center">
+<a target="other" href="http://www.routino.org/">site web Routino</a>
+|
+<a target="other" href="documentation/">Documentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Langue</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="visualiser.html.en" title="English language webpage">English</a>
+<td>(EN)
+<tr>
+<td><a id="lang_de_url" href="visualiser.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<tr>
+<td><a id="lang_fr_url" href="visualiser.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<tr>
+<td><a id="lang_nl_url" href="visualiser.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<tr>
+<td>
+<td>(RU)
+</table>
+<a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Instructions</span>
+Zoom in and then use the buttons below to download the data. The
+server will only return data if the selected area is small enough.
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_no_data">
+<b><i>No data displayed</i></b>
+</div>
+<div id="result_status_data" style="display: none;">
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Failed to get visualiser data!</b>
+</div>
+<div id="result_status_junctions" style="display: none;">
+<b>Processed # junctions</b>
+</div>
+<div id="result_status_super" style="display: none;">
+<b>Processed # super-nodes/segments</b>
+</div>
+<div id="result_status_oneway" style="display: none;">
+<b>Processed # oneway segments</b>
+</div>
+<div id="result_status_highway" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_transport" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_barrier" style="display: none;">
+<b>Processed # nodes</b>
+</div>
+<div id="result_status_turns" style="display: none;">
+<b>Processed # turn restrictions</b>
+</div>
+<div id="result_status_limit" style="display: none;">
+<b>Processed # limit changes</b>
+</div>
+<div id="result_status_property" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_errorlogs" style="display: none;">
+<b>Processed # error logs</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_junctions_show" onclick="hideshow_show('junctions');" class="hideshow_show">+</span>
+<span id="hideshow_junctions_hide" onclick="hideshow_hide('junctions');" class="hideshow_hide">-</span>
+<input type="button" id="junctions" onclick="displayData('junctions');" value="Display Junctions">
+<div id="hideshow_junctions_div" style="display: none;">
+Each node that is a dead-end, a junction of two highways of different
+types (or different properties) or a junction where more than two segments
+join are shown colour-coded:
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="1" ><td>only one highway - a dead-end.
+<tr><td><img src="icons/ball-2.png" alt="2" ><td>two highways of different types meet.
+<tr><td><img src="icons/ball-3.png" alt="3" ><td>three highways meet.
+<tr><td><img src="icons/ball-4.png" alt="4" ><td>four highways meet.
+<tr><td><img src="icons/ball-5.png" alt="5" ><td>five highways meet.
+<tr><td><img src="icons/ball-6.png" alt="6" ><td>six highways meet.
+<tr><td><img src="icons/ball-7.png" alt="7+"><td>seven (or more) highways meet.
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_super_show" onclick="hideshow_show('super');" class="hideshow_show">+</span>
+<span id="hideshow_super_hide" onclick="hideshow_hide('super');" class="hideshow_hide">-</span>
+<input type="button" id="super" onclick="displayData('super');" value="Display Super Segments">
+<div id="hideshow_super_div" style="display: none;">
+Each super-node and the associated super-segments are shown (see
+algorithm page for description).
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_oneway_show" onclick="hideshow_show('oneway');" class="hideshow_show">+</span>
+<span id="hideshow_oneway_hide" onclick="hideshow_hide('oneway');" class="hideshow_hide">-</span>
+<input type="button" id="oneway" onclick="displayData('oneway');" value="Display One-way Segments">
+<div id="hideshow_oneway_div" style="display: none;">
+Each one-way segment is shown with a coloured triangle indicating the
+allowed direction. The colours of the triangles depend on the bearing
+of the highway segment.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<input type="button" id="highway" onclick="displayData('highway');" value="Display Highway Segments">
+<div id="hideshow_highway_div" style="display: none;">
+Each segment of the chosen type of highway is drawn.
+<form name="highways" id="highways" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Autoroute: <td><input name="highway" type="radio" value="motorway" onchange="displayData('highway');">
+<tr><td>Trunk: <td><input name="highway" type="radio" value="trunk" onchange="displayData('highway');">
+<tr><td>Primaire: <td><input name="highway" type="radio" value="primary" onchange="displayData('highway');" checked>
+<tr><td>Secondaire: <td><input name="highway" type="radio" value="secondary" onchange="displayData('highway');">
+<tr><td>Tertiaire: <td><input name="highway" type="radio" value="tertiary" onchange="displayData('highway');">
+<tr><td>Non classée:<td><input name="highway" type="radio" value="unclassified" onchange="displayData('highway');">
+<tr><td>Résidentiel: <td><input name="highway" type="radio" value="residential" onchange="displayData('highway');">
+<tr><td>Service: <td><input name="highway" type="radio" value="service" onchange="displayData('highway');">
+<tr><td>Chemin: <td><input name="highway" type="radio" value="track" onchange="displayData('highway');">
+<tr><td>Voie cyclable: <td><input name="highway" type="radio" value="cycleway" onchange="displayData('highway');">
+<tr><td>Sentier: <td><input name="highway" type="radio" value="path" onchange="displayData('highway');">
+<tr><td>Escaliers: <td><input name="highway" type="radio" value="steps" onchange="displayData('highway');">
+<tr><td>Ferry: <td><input name="highway" type="radio" value="ferry" onchange="displayData('highway');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_show">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_hide">-</span>
+<input type="button" id="transport" onclick="displayData('transport');" value="Display Transport Segments">
+<div id="hideshow_transport_div" style="display: none;">
+Each segment allowed for the chosen type of transport is drawn.
+<form name="transports" id="transports" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>À pied: <td><input name="transport" type="radio" value="foot" onchange="displayData('transport');">
+<tr><td>À cheval: <td><input name="transport" type="radio" value="horse" onchange="displayData('transport');">
+<tr><td>Fauteuil roulant:<td><input name="transport" type="radio" value="wheelchair" onchange="displayData('transport');">
+<tr><td>Bicyclette: <td><input name="transport" type="radio" value="bicycle" onchange="displayData('transport');">
+<tr><td>Mobilette: <td><input name="transport" type="radio" value="moped" onchange="displayData('transport');">
+<tr><td>Moto:<td><input name="transport" type="radio" value="motorcycle" onchange="displayData('transport');">
+<tr><td>Voiture: <td><input name="transport" type="radio" value="motorcar" onchange="displayData('transport');" checked>
+<tr><td>Camionette: <td><input name="transport" type="radio" value="goods" onchange="displayData('transport');">
+<tr><td>Camion(15t): <td><input name="transport" type="radio" value="hgv" onchange="displayData('transport');">
+<tr><td>Camion(10t): <td><input name="transport" type="radio" value="psv" onchange="displayData('transport');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_barrier_show" onclick="hideshow_show('barrier');" class="hideshow_show">+</span>
+<span id="hideshow_barrier_hide" onclick="hideshow_hide('barrier');" class="hideshow_hide">-</span>
+<input type="button" id="barrier" onclick="displayData('barrier');" value="Display Barrier Nodes">
+<div id="hideshow_barrier_div" style="display: none;">
+Each barrier blocking the chosen type of transport is drawn.
+<form name="barriers" id="barriers" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>À pied: <td><input name="barrier" type="radio" value="foot" onchange="displayData('barrier');">
+<tr><td>À cheval: <td><input name="barrier" type="radio" value="horse" onchange="displayData('barrier');">
+<tr><td>Fauteuil roulant:<td><input name="barrier" type="radio" value="wheelchair" onchange="displayData('barrier');">
+<tr><td>Bicyclette: <td><input name="barrier" type="radio" value="bicycle" onchange="displayData('barrier');">
+<tr><td>Mobilette: <td><input name="barrier" type="radio" value="moped" onchange="displayData('barrier');">
+<tr><td>Moto:<td><input name="barrier" type="radio" value="motorcycle" onchange="displayData('barrier');">
+<tr><td>Voiture: <td><input name="barrier" type="radio" value="motorcar" onchange="displayData('barrier');" checked>
+<tr><td>Camionette: <td><input name="barrier" type="radio" value="goods" onchange="displayData('barrier');">
+<tr><td>Camion(15t): <td><input name="barrier" type="radio" value="hgv" onchange="displayData('barrier');">
+<tr><td>Camion(10t): <td><input name="barrier" type="radio" value="psv" onchange="displayData('barrier');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_turns_show" onclick="hideshow_show('turns');" class="hideshow_show">+</span>
+<span id="hideshow_turns_hide" onclick="hideshow_hide('turns');" class="hideshow_hide">-</span>
+<input type="button" id="turns" onclick="displayData('turns');" value="Display Turn Restrictions">
+<div id="hideshow_turns_div" style="display: none;">
+Each turn restrictions is shown with a line indicating the disallowed turn.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<input type="button" id="speed" onclick="displayData('speed');" value="Display Speed Limits">
+<div id="hideshow_speed_div" style="display: none;">
+Each node that joins segments with different speed limits is shown
+along with the speed limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-80.png" alt="(80)"><td>80 km/hour speed limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_weight_show" onclick="hideshow_show('weight');" class="hideshow_show">+</span>
+<span id="hideshow_weight_hide" onclick="hideshow_hide('weight');" class="hideshow_hide">-</span>
+<input type="button" id="weight" onclick="displayData('weight');" value="Display Weight Limits">
+<div id="hideshow_weight_div" style="display: none;">
+Each node that joins segments with different weight limits is shown
+along with the weight limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-8.0.png" alt="(8.0)"><td>8.0 tonnes weight limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_height_show" onclick="hideshow_show('height');" class="hideshow_show">+</span>
+<span id="hideshow_height_hide" onclick="hideshow_hide('height');" class="hideshow_hide">-</span>
+<input type="button" id="height" onclick="displayData('height');" value="Display Height Limits">
+<div id="hideshow_height_div" style="display: none;">
+Each node that joins segments with different height limits is shown
+along with the height limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-4.0.png" alt="(4.0)"><td>4.0 m height limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_width_show" onclick="hideshow_show('width');" class="hideshow_show">+</span>
+<span id="hideshow_width_hide" onclick="hideshow_hide('width');" class="hideshow_hide">-</span>
+<input type="button" id="width" onclick="displayData('width');" value="Display Width Limits">
+<div id="hideshow_width_div" style="display: none;">
+Each node that joins segments with different width limits is shown
+along with the width limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-3.0.png" alt="(3.0)"><td>3.0 m width limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_length_show" onclick="hideshow_show('length');" class="hideshow_show">+</span>
+<span id="hideshow_length_hide" onclick="hideshow_hide('length');" class="hideshow_hide">-</span>
+<input type="button" id="length" onclick="displayData('length');" value="Display Length Limits">
+<div id="hideshow_length_div" style="display: none;">
+Each node that joins segments with different length limits is shown
+along with the length limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-9.0.png" alt="(9.0)"><td>9.0 m length limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<input type="button" id="property" onclick="displayData('property');" value="Display Highway Properties">
+<div id="hideshow_property_div" style="display: none;">
+Each segment of the highways with a particular property is drawn.
+<form name="properties" id="properties" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Pavée: <td><input name="property" type="radio" value="paved" onchange="displayData('property');" checked>
+<tr><td>Voies multiples: <td><input name="property" type="radio" value="multilane" onchange="displayData('property');">
+<tr><td>Pont: <td><input name="property" type="radio" value="bridge" onchange="displayData('property');">
+<tr><td>Tunnel: <td><input name="property" type="radio" value="tunnel" onchange="displayData('property');">
+<tr><td>Itinér. piéton: <td><input name="property" type="radio" value="footroute" onchange="displayData('property');">
+<tr><td>Itinér. cycle: <td><input name="property" type="radio" value="bicycleroute" onchange="displayData('property');">
+<tr><td>Cycle Both Ways:<td><input name="property" type="radio" value="cyclebothways" onchange="displayData('property');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_errorlogs_show" onclick="hideshow_show('errorlogs');" class="hideshow_show">+</span>
+<span id="hideshow_errorlogs_hide" onclick="hideshow_hide('errorlogs');" class="hideshow_hide">-</span>
+<input type="button" id="errorlogs" onclick="displayData('errorlogs');" value="Display Error Logs">
+<div id="hideshow_errorlogs_div" style="display: none;">
+Potential problems found by Routino when processing the input data.
+</div>
+</div>
+
+<div class="hideshow_box">
+<input type="button" id="clear" onclick="displayData('');" value="Clear data">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Liens</span>
+<a id="permalink_url" href="visualiser.html">Lien vers cet outil de visualisation</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Editer cette donnée OSM</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Aide</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+Zoom to an area and select one of the buttons to display that type of
+data.
+<br>
+More data options can be found by expanding the details below each
+button.
+<p>
+<b>Data Failure</b>
+<br>
+If the area selected is too large (depends on the data type) then the
+status will say "Failed to get visualiser data" - zoom in and try
+again.
+</div>
+</div>
+</div>
+</div>
+
+<div class="tab_content" id="tab_router_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Router</span>
+To perform routing on the map use the link below.
+<br>
+<a id="router_url" href="router.html" target="router">Lien vers cet outil de visualisation</a>
+</div>
+</div>
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistiques</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Afficher les données statistiques</a>
+</div>
+</div>
+
+</div>
+
+<!-- Right hand side of window - map -->
+
+<div class="right_panel">
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript est <em>nécessaire</em> pour cette page web à cause de la carte intéractive.
+</noscript>
+</div>
+<div class="attribution">
+Routeur: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tuiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/visualiser.html.nl b/web/www/routino/visualiser.html.nl
new file mode 100644
index 0000000..c262bc7
--- /dev/null
+++ b/web/www/routino/visualiser.html.nl
@@ -0,0 +1,458 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="keywords" content="openstreetmap routino verifier">
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
+
+<title>Routino : Data Visualiser for Routing Data</title>
+
+<!--
+Routino data visualiser web page.
+
+Part of the Routino routing software.
+
+This file Copyright 2008-2014 Andrew M. Bishop
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+-->
+
+<!-- Page elements -->
+<script src="page-elements.js" type="text/javascript"></script>
+<link href="page-elements.css" type="text/css" rel="stylesheet">
+
+<!-- Router and visualiser shared features -->
+<link href="maplayout.css" type="text/css" rel="stylesheet">
+
+<!-- Visualiser specific features -->
+<script src="mapprops.js" type="text/javascript"></script>
+<link href="visualiser.css" type="text/css" rel="stylesheet">
+
+<!-- Map parameters -->
+<script src="mapprops.js" type="text/javascript"></script>
+
+<!-- Map loader -->
+<script src="maploader.js" type="text/javascript"></script>
+
+</head>
+<body onload="map_load('map_init();');">
+
+<!-- Left hand side of window - data panel -->
+
+<div class="left_panel">
+
+<div class="tab_box">
+<span id="tab_visualiser" onclick="tab_select('visualiser');" class="tab_selected" title="Select data options">Visualiser</span>
+<span id="tab_router" onclick="tab_select('router');" class="tab_unselected" title="Plan a route">Router</span>
+<span id="tab_data" onclick="tab_select('data');" class="tab_unselected" title="View database information">Data</span>
+</div>
+
+<div class="tab_content" id="tab_visualiser_div">
+
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Visualiser</span>
+This web page allows visualisation of the data that Routino uses for routing.
+Only data relevant for routing is displayed and some will therefore be excluded.
+<div class="center">
+<a target="other" href="http://www.routino.org/">Routino Website</a>
+|
+<a target="other" href="documentation/">Documentation</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_language_show" onclick="hideshow_show('language');" class="hideshow_show">+</span>
+<span id="hideshow_language_hide" onclick="hideshow_hide('language');" class="hideshow_hide">-</span>
+<span class="hideshow_title">Taal</span>
+
+<div id="hideshow_language_div" style="display: none;">
+<table>
+<tr>
+<td><a id="lang_en_url" href="visualiser.html.en" title="English language webpage">English</a>
+<td>(EN)
+<tr>
+<td><a id="lang_de_url" href="visualiser.html.de" title="Deutsche Webseite">Deutsche</a>
+<td>(DE)
+<tr>
+<td><a id="lang_fr_url" href="visualiser.html.fr" title="Francais">Francais</a>
+<td>(FR)
+<tr>
+<td><a id="lang_nl_url" href="visualiser.html.nl" title="Nederlandse web pagina">Nederlands</a>
+<td>(NL)
+<tr>
+<td>
+<td>(RU)
+</table>
+<a target="translation" href="http://www.routino.org/translations/">Routino Translations</a>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Instructions</span>
+Zoom in and then use the buttons below to download the data. The
+server will only return data if the selected area is small enough.
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Status</span>
+<div id="result_status">
+<div id="result_status_no_data">
+<b><i>No data displayed</i></b>
+</div>
+<div id="result_status_data" style="display: none;">
+</div>
+<div id="result_status_failed" style="display: none;">
+<b>Failed to get visualiser data!</b>
+</div>
+<div id="result_status_junctions" style="display: none;">
+<b>Processed # junctions</b>
+</div>
+<div id="result_status_super" style="display: none;">
+<b>Processed # super-nodes/segments</b>
+</div>
+<div id="result_status_oneway" style="display: none;">
+<b>Processed # oneway segments</b>
+</div>
+<div id="result_status_highway" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_transport" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_barrier" style="display: none;">
+<b>Processed # nodes</b>
+</div>
+<div id="result_status_turns" style="display: none;">
+<b>Processed # turn restrictions</b>
+</div>
+<div id="result_status_limit" style="display: none;">
+<b>Processed # limit changes</b>
+</div>
+<div id="result_status_property" style="display: none;">
+<b>Processed # segments</b>
+</div>
+<div id="result_status_errorlogs" style="display: none;">
+<b>Processed # error logs</b>
+</div>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_junctions_show" onclick="hideshow_show('junctions');" class="hideshow_show">+</span>
+<span id="hideshow_junctions_hide" onclick="hideshow_hide('junctions');" class="hideshow_hide">-</span>
+<input type="button" id="junctions" onclick="displayData('junctions');" value="Display Junctions">
+<div id="hideshow_junctions_div" style="display: none;">
+Each node that is a dead-end, a junction of two highways of different
+types (or different properties) or a junction where more than two segments
+join are shown colour-coded:
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="1" ><td>only one highway - a dead-end.
+<tr><td><img src="icons/ball-2.png" alt="2" ><td>two highways of different types meet.
+<tr><td><img src="icons/ball-3.png" alt="3" ><td>three highways meet.
+<tr><td><img src="icons/ball-4.png" alt="4" ><td>four highways meet.
+<tr><td><img src="icons/ball-5.png" alt="5" ><td>five highways meet.
+<tr><td><img src="icons/ball-6.png" alt="6" ><td>six highways meet.
+<tr><td><img src="icons/ball-7.png" alt="7+"><td>seven (or more) highways meet.
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_super_show" onclick="hideshow_show('super');" class="hideshow_show">+</span>
+<span id="hideshow_super_hide" onclick="hideshow_hide('super');" class="hideshow_hide">-</span>
+<input type="button" id="super" onclick="displayData('super');" value="Display Super Segments">
+<div id="hideshow_super_div" style="display: none;">
+Each super-node and the associated super-segments are shown (see
+algorithm page for description).
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_oneway_show" onclick="hideshow_show('oneway');" class="hideshow_show">+</span>
+<span id="hideshow_oneway_hide" onclick="hideshow_hide('oneway');" class="hideshow_hide">-</span>
+<input type="button" id="oneway" onclick="displayData('oneway');" value="Display One-way Segments">
+<div id="hideshow_oneway_div" style="display: none;">
+Each one-way segment is shown with a coloured triangle indicating the
+allowed direction. The colours of the triangles depend on the bearing
+of the highway segment.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_highway_show" onclick="hideshow_show('highway');" class="hideshow_show">+</span>
+<span id="hideshow_highway_hide" onclick="hideshow_hide('highway');" class="hideshow_hide">-</span>
+<input type="button" id="highway" onclick="displayData('highway');" value="Display Highway Segments">
+<div id="hideshow_highway_div" style="display: none;">
+Each segment of the chosen type of highway is drawn.
+<form name="highways" id="highways" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Autostrade: <td><input name="highway" type="radio" value="motorway" onchange="displayData('highway');">
+<tr><td>Autoweg: <td><input name="highway" type="radio" value="trunk" onchange="displayData('highway');">
+<tr><td>Provinciale wegen: <td><input name="highway" type="radio" value="primary" onchange="displayData('highway');" checked>
+<tr><td>Nationale wegen: <td><input name="highway" type="radio" value="secondary" onchange="displayData('highway');">
+<tr><td>Doorgangsweg: <td><input name="highway" type="radio" value="tertiary" onchange="displayData('highway');">
+<tr><td>Niet geclassificeerd:<td><input name="highway" type="radio" value="unclassified" onchange="displayData('highway');">
+<tr><td>Woongebied: <td><input name="highway" type="radio" value="residential" onchange="displayData('highway');">
+<tr><td>Toegangsweg: <td><input name="highway" type="radio" value="service" onchange="displayData('highway');">
+<tr><td>Veldweg: <td><input name="highway" type="radio" value="track" onchange="displayData('highway');">
+<tr><td>Fietspad: <td><input name="highway" type="radio" value="cycleway" onchange="displayData('highway');">
+<tr><td>Pad: <td><input name="highway" type="radio" value="path" onchange="displayData('highway');">
+<tr><td>Trap: <td><input name="highway" type="radio" value="steps" onchange="displayData('highway');">
+<tr><td>Ferry: <td><input name="highway" type="radio" value="ferry" onchange="displayData('highway');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_transport_show" onclick="hideshow_show('transport');" class="hideshow_show">+</span>
+<span id="hideshow_transport_hide" onclick="hideshow_hide('transport');" class="hideshow_hide">-</span>
+<input type="button" id="transport" onclick="displayData('transport');" value="Display Transport Segments">
+<div id="hideshow_transport_div" style="display: none;">
+Each segment allowed for the chosen type of transport is drawn.
+<form name="transports" id="transports" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Te voet: <td><input name="transport" type="radio" value="foot" onchange="displayData('transport');">
+<tr><td>Paard: <td><input name="transport" type="radio" value="horse" onchange="displayData('transport');">
+<tr><td>Rolstoel:<td><input name="transport" type="radio" value="wheelchair" onchange="displayData('transport');">
+<tr><td>Fiets: <td><input name="transport" type="radio" value="bicycle" onchange="displayData('transport');">
+<tr><td>Brommer: <td><input name="transport" type="radio" value="moped" onchange="displayData('transport');">
+<tr><td>Motorfiets:<td><input name="transport" type="radio" value="motorcycle" onchange="displayData('transport');">
+<tr><td>Auto: <td><input name="transport" type="radio" value="motorcar" onchange="displayData('transport');" checked>
+<tr><td>Goederen: <td><input name="transport" type="radio" value="goods" onchange="displayData('transport');">
+<tr><td>Zwaar transport: <td><input name="transport" type="radio" value="hgv" onchange="displayData('transport');">
+<tr><td>Publiek transport: <td><input name="transport" type="radio" value="psv" onchange="displayData('transport');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_barrier_show" onclick="hideshow_show('barrier');" class="hideshow_show">+</span>
+<span id="hideshow_barrier_hide" onclick="hideshow_hide('barrier');" class="hideshow_hide">-</span>
+<input type="button" id="barrier" onclick="displayData('barrier');" value="Display Barrier Nodes">
+<div id="hideshow_barrier_div" style="display: none;">
+Each barrier blocking the chosen type of transport is drawn.
+<form name="barriers" id="barriers" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Te voet: <td><input name="barrier" type="radio" value="foot" onchange="displayData('barrier');">
+<tr><td>Paard: <td><input name="barrier" type="radio" value="horse" onchange="displayData('barrier');">
+<tr><td>Rolstoel:<td><input name="barrier" type="radio" value="wheelchair" onchange="displayData('barrier');">
+<tr><td>Fiets: <td><input name="barrier" type="radio" value="bicycle" onchange="displayData('barrier');">
+<tr><td>Brommer: <td><input name="barrier" type="radio" value="moped" onchange="displayData('barrier');">
+<tr><td>Motorfiets:<td><input name="barrier" type="radio" value="motorcycle" onchange="displayData('barrier');">
+<tr><td>Auto: <td><input name="barrier" type="radio" value="motorcar" onchange="displayData('barrier');" checked>
+<tr><td>Goederen: <td><input name="barrier" type="radio" value="goods" onchange="displayData('barrier');">
+<tr><td>Zwaar transport: <td><input name="barrier" type="radio" value="hgv" onchange="displayData('barrier');">
+<tr><td>Publiek transport: <td><input name="barrier" type="radio" value="psv" onchange="displayData('barrier');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_turns_show" onclick="hideshow_show('turns');" class="hideshow_show">+</span>
+<span id="hideshow_turns_hide" onclick="hideshow_hide('turns');" class="hideshow_hide">-</span>
+<input type="button" id="turns" onclick="displayData('turns');" value="Display Turn Restrictions">
+<div id="hideshow_turns_div" style="display: none;">
+Each turn restrictions is shown with a line indicating the disallowed turn.
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_speed_show" onclick="hideshow_show('speed');" class="hideshow_show">+</span>
+<span id="hideshow_speed_hide" onclick="hideshow_hide('speed');" class="hideshow_hide">-</span>
+<input type="button" id="speed" onclick="displayData('speed');" value="Display Speed Limits">
+<div id="hideshow_speed_div" style="display: none;">
+Each node that joins segments with different speed limits is shown
+along with the speed limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-80.png" alt="(80)"><td>80 km/hour speed limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_weight_show" onclick="hideshow_show('weight');" class="hideshow_show">+</span>
+<span id="hideshow_weight_hide" onclick="hideshow_hide('weight');" class="hideshow_hide">-</span>
+<input type="button" id="weight" onclick="displayData('weight');" value="Display Weight Limits">
+<div id="hideshow_weight_div" style="display: none;">
+Each node that joins segments with different weight limits is shown
+along with the weight limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-8.0.png" alt="(8.0)"><td>8.0 tonnes weight limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_height_show" onclick="hideshow_show('height');" class="hideshow_show">+</span>
+<span id="hideshow_height_hide" onclick="hideshow_hide('height');" class="hideshow_hide">-</span>
+<input type="button" id="height" onclick="displayData('height');" value="Display Height Limits">
+<div id="hideshow_height_div" style="display: none;">
+Each node that joins segments with different height limits is shown
+along with the height limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-4.0.png" alt="(4.0)"><td>4.0 m height limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_width_show" onclick="hideshow_show('width');" class="hideshow_show">+</span>
+<span id="hideshow_width_hide" onclick="hideshow_hide('width');" class="hideshow_hide">-</span>
+<input type="button" id="width" onclick="displayData('width');" value="Display Width Limits">
+<div id="hideshow_width_div" style="display: none;">
+Each node that joins segments with different width limits is shown
+along with the width limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-3.0.png" alt="(3.0)"><td>3.0 m width limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_length_show" onclick="hideshow_show('length');" class="hideshow_show">+</span>
+<span id="hideshow_length_hide" onclick="hideshow_hide('length');" class="hideshow_hide">-</span>
+<input type="button" id="length" onclick="displayData('length');" value="Display Length Limits">
+<div id="hideshow_length_div" style="display: none;">
+Each node that joins segments with different length limits is shown
+along with the length limit on relevant segments.
+<br>
+<table>
+<tr><td><img src="icons/ball-1.png" alt="." ><td>Change of limit
+<tr><td><img src="icons/limit-no.png" alt="()" ><td>No specified limit
+<tr><td><img src="icons/limit-9.0.png" alt="(9.0)"><td>9.0 m length limit
+</table>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_property_show" onclick="hideshow_show('property');" class="hideshow_show">+</span>
+<span id="hideshow_property_hide" onclick="hideshow_hide('property');" class="hideshow_hide">-</span>
+<input type="button" id="property" onclick="displayData('property');" value="Display Highway Properties">
+<div id="hideshow_property_div" style="display: none;">
+Each segment of the highways with a particular property is drawn.
+<form name="properties" id="properties" action="#" method="get" onsubmit="return false;">
+<table>
+<tr><td>Verhard: <td><input name="property" type="radio" value="paved" onchange="displayData('property');" checked>
+<tr><td>Meerdere Stroken: <td><input name="property" type="radio" value="multilane" onchange="displayData('property');">
+<tr><td>Brug: <td><input name="property" type="radio" value="bridge" onchange="displayData('property');">
+<tr><td>Tunnel: <td><input name="property" type="radio" value="tunnel" onchange="displayData('property');">
+<tr><td>Walking Route: <td><input name="property" type="radio" value="footroute" onchange="displayData('property');">
+<tr><td>Bicycle Route: <td><input name="property" type="radio" value="bicycleroute" onchange="displayData('property');">
+<tr><td>Cycle Both Ways:<td><input name="property" type="radio" value="cyclebothways" onchange="displayData('property');">
+</table>
+</form>
+</div>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_errorlogs_show" onclick="hideshow_show('errorlogs');" class="hideshow_show">+</span>
+<span id="hideshow_errorlogs_hide" onclick="hideshow_hide('errorlogs');" class="hideshow_hide">-</span>
+<input type="button" id="errorlogs" onclick="displayData('errorlogs');" value="Display Error Logs">
+<div id="hideshow_errorlogs_div" style="display: none;">
+Potential problems found by Routino when processing the input data.
+</div>
+</div>
+
+<div class="hideshow_box">
+<input type="button" id="clear" onclick="displayData('');" value="Clear data">
+</div>
+
+<div class="hideshow_box">
+<span class="hideshow_title">Links</span>
+<a id="permalink_url" href="visualiser.html">Link to this map view</a>
+<br>
+<a id="edit_url" target="edit" style="display: none;">Lees hoe je OSM data kan inbrengen</a>
+</div>
+
+<div class="hideshow_box">
+<span id="hideshow_help_options_show" onclick="hideshow_show('help_options');" class="hideshow_hide">+</span>
+<span id="hideshow_help_options_hide" onclick="hideshow_hide('help_options');" class="hideshow_show">-</span>
+<span class="hideshow_title">Help</span>
+<div id="hideshow_help_options_div">
+<div class="scrollable">
+<b>Quick Start</b>
+<br>
+Zoom to an area and select one of the buttons to display that type of
+data.
+<br>
+More data options can be found by expanding the details below each
+button.
+<p>
+<b>Data Failure</b>
+<br>
+If the area selected is too large (depends on the data type) then the
+status will say "Failed to get visualiser data" - zoom in and try
+again.
+</div>
+</div>
+</div>
+</div>
+
+<div class="tab_content" id="tab_router_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Router</span>
+To perform routing on the map use the link below.
+<br>
+<a id="router_url" href="router.html" target="router">Link to this map view</a>
+</div>
+</div>
+
+<div class="tab_content" id="tab_data_div" style="display: none;">
+<div class="hideshow_box">
+<span class="hideshow_title">Routino Statistics</span>
+<div id="statistics_data"></div>
+<a id="statistics_link" href="statistics.cgi" onclick="displayStatistics();return(false);">Display data statistics</a>
+</div>
+</div>
+
+</div>
+
+<!-- Right hand side of window - map -->
+
+<div class="right_panel">
+<div class="map" id="map">
+<noscript>
+<p>
+Javascript is <em>required</em> to use this web page because of the interactive map.
+</noscript>
+</div>
+<div class="attribution">
+Router: <a href="http://www.routino.org/" target="routino">Routino</a>
+|
+Geo Data: <span id="attribution_data"></span>
+|
+Tiles: <span id="attribution_tile"></span>
+</div>
+</div>
+
+</body>
+
+</html>
diff --git a/web/www/routino/visualiser.js b/web/www/routino/visualiser.js
deleted file mode 100644
index f844f29..0000000
--- a/web/www/routino/visualiser.js
+++ /dev/null
@@ -1,920 +0,0 @@
-//
-// Routino data visualiser web page Javascript
-//
-// Part of the Routino routing software.
-//
-// This file Copyright 2008-2012 Andrew M. Bishop
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-
-//
-// Data types
-//
-
-var data_types=[
-                "junctions",
-                "super",
-                "oneway",
-                "highway",
-                "transport",
-                "barrier",
-                "turns",
-                "speed",
-                "weight",
-                "height",
-                "width",
-                "length"
-               ];
-
-
-//
-// Junction styles
-//
-
-var junction_colours={
-                      0: "#FFFFFF",
-                      1: "#FF0000",
-                      2: "#FFFF00",
-                      3: "#00FF00",
-                      4: "#8B4513",
-                      5: "#00BFFF",
-                      6: "#FF69B4",
-                      7: "#000000",
-                      8: "#000000",
-                      9: "#000000"
-                     };
-
-var junction_styles={};
-
-
-//
-// Super styles
-//
-
-var super_node_style,super_segment_style;
-
-
-//
-// Oneway and turn restriction styles
-//
-
-var hex={0: "00", 1: "11",  2: "22",  3: "33",  4: "44",  5: "55",  6: "66",  7: "77",
-         8: "88", 9: "99", 10: "AA", 11: "BB", 12: "CC", 13: "DD", 14: "EE", 15: "FF"};
-
-var turn_restriction_style;
-
-
-////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////// Initialisation /////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-// Process the URL query string and extract the arguments
-
-var legal={"^lon"  : "^[-0-9.]+$",
-           "^lat"  : "^[-0-9.]+$",
-           "^zoom" : "^[0-9]+$"};
-
-var args={};
-
-if(location.search.length>1)
-  {
-   var query,queries;
-
-   query=location.search.replace(/^\?/,"");
-   query=query.replace(/;/g,'&');
-   queries=query.split('&');
-
-   for(var i=0;i<queries.length;i++)
-     {
-      queries[i].match(/^([^=]+)(=(.*))?$/);
-
-      k=RegExp.$1;
-      v=unescape(RegExp.$3);
-
-      for(var l in legal)
-        {
-         if(k.match(RegExp(l)) && v.match(RegExp(legal[l])))
-            args[k]=v;
-        }
-     }
-  }
-
-
-////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////// Map handling /////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-var map;
-var layerMap=[], layerVectors, layerBoxes;
-var epsg4326, epsg900913;
-
-var box;
-
-// 
-// Initialise the 'map' object
-//
-
-function map_init()             // called from visualiser.html
-{
- lon =args["lon"];
- lat =args["lat"];
- zoom=args["zoom"];
-
- // Map properties (North/South and East/West limits and zoom in/out limits) are now in mapprops.js
- // Map URLs are now in mapprops.js
-
- //
- // Create the map
- //
-
- epsg4326=new OpenLayers.Projection("EPSG:4326");
- epsg900913=new OpenLayers.Projection("EPSG:900913");
-
- map = new OpenLayers.Map ("map",
-                           {
-                            controls:[
-                                      new OpenLayers.Control.Navigation(),
-                                      new OpenLayers.Control.PanZoomBar(),
-                                      new OpenLayers.Control.ScaleLine(),
-                                      new OpenLayers.Control.LayerSwitcher()
-                                      ],
-
-                            projection: epsg900913,
-                            displayProjection: epsg4326,
-
-                            minZoomLevel: mapprops.zoomout,
-                            numZoomLevels: mapprops.zoomin-mapprops.zoomout+1,
-                            maxResolution: 156543.03390625 / Math.pow(2,mapprops.zoomout),
-
-                            // These two lines are not needed with OpenLayers 2.12
-                            units: "m",
-                            maxExtent:        new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
-
-                            restrictedExtent: new OpenLayers.Bounds(mapprops.westedge,mapprops.southedge,mapprops.eastedge,mapprops.northedge).transform(epsg4326,epsg900913)
-                           });
-
- // Add map tile layers
-
- for(var l=0;l < mapprops.mapdata.length;l++)
-   {
-    layerMap[l] = new OpenLayers.Layer.TMS(mapprops.mapdata[l].label,
-                                           mapprops.mapdata[l].baseurl,
-                                           {
-                                            emptyUrl: mapprops.mapdata[l].errorurl,
-                                            type: 'png',
-                                            getURL: limitedUrl,
-                                            displayOutsideMaxExtent: true,
-                                            buffer: 1
-                                           });
-    map.addLayer(layerMap[l]);
-   }
-
- // Get a URL for the tile; limited to map restricted extent.
-
- function limitedUrl(bounds)
- {
-  var z = map.getZoom() + map.minZoomLevel;
-
-  if (z>=7 && (bounds.right  < map.restrictedExtent.left ||
-               bounds.left   > map.restrictedExtent.right ||
-               bounds.top    < map.restrictedExtent.bottom ||
-               bounds.bottom > map.restrictedExtent.top))
-     return this.emptyUrl;
-
-  var res = map.getResolution();
-  var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
-  var limit = Math.pow(2, z);
-
-  if (y < 0 || y >= limit)
-    return this.emptyUrl;
-
-  var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
-
-  x = ((x % limit) + limit) % limit;
-  return this.url + z + "/" + x + "/" + y + "." + this.type;
- }
-
- // Add a vectors layer
- 
- layerVectors = new OpenLayers.Layer.Vector("Markers");
- map.addLayer(layerVectors);
-
- for(var colour in junction_colours)
-    junction_styles[colour]=new OpenLayers.Style({},{stroke: false, pointRadius: 2,fillColor: junction_colours[colour]});
-
- super_node_style   =new OpenLayers.Style({},{stroke: false, pointRadius: 3,fillColor  : "#FF0000"});
- super_segment_style=new OpenLayers.Style({},{fill: false  , strokeWidth: 2,strokeColor: "#FF0000"});
-
- turn_restriction_style=new OpenLayers.Style({},{fill: false, strokeWidth: 2,strokeColor: "#FF0000"});
-
- // Add a boxes layer
-
- layerBoxes = new OpenLayers.Layer.Boxes("Boundary");
- map.addLayer(layerBoxes);
-
- box=null;
-
- // Set the map centre to the limited range specified
-
- map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true));
- map.maxResolution = map.getResolution();
-
- // Move the map
-
- if(lon != undefined && lat != undefined && zoom != undefined)
-   {
-    if(lon<mapprops.westedge) lon=mapprops.westedge;
-    if(lon>mapprops.eastedge) lon=mapprops.eastedge;
-
-    if(lat<mapprops.southedge) lat=mapprops.southedge;
-    if(lat>mapprops.northedge) lat=mapprops.northedge;
-
-    if(zoom<mapprops.zoomout) zoom=mapprops.zoomout;
-    if(zoom>mapprops.zoomin)  zoom=mapprops.zoomin;
-
-    var lonlat = new OpenLayers.LonLat(lon,lat);
-    lonlat.transform(epsg4326,epsg900913);
-
-    map.moveTo(lonlat,zoom-map.minZoomLevel);
-   }
-}
-
-
-//
-// Format a number in printf("%.5f") format.
-//
-
-function format5f(number)
-{
- var newnumber=Math.floor(number*100000+0.5);
- var delta=0;
-
- if(newnumber>=0 && newnumber<100000) delta= 100000;
- if(newnumber<0 && newnumber>-100000) delta=-100000;
-
- var string=String(newnumber+delta);
-
- var intpart =string.substring(0,string.length-5);
- var fracpart=string.substring(string.length-5,string.length);
-
- if(delta>0) intpart="0";
- if(delta<0) intpart="-0";
-
- return(intpart + "." + fracpart);
-}
-
-
-//
-// Build a set of URL arguments for the map location
-//
-
-function buildMapArguments()
-{
- var lonlat = map.getCenter().clone();
- lonlat.transform(epsg900913,epsg4326);
-
- var zoom = map.getZoom() + map.minZoomLevel;
-
- return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lon) + ";zoom=" + zoom;
-}
-
-
-//
-// Update a URL
-//
-
-function updateURL(element)     // called from visualiser.html
-{
- if(element.id == "permalink_url")
-    element.href=location.pathname + "?" + buildMapArguments();
-
- if(element.id == "router_url")
-    element.href="router.html" + "?" + buildMapArguments();
-
- if(element.id == "edit_url")
-    element.href="http://www.openstreetmap.org/edit" + "?" + buildMapArguments();
-
- if(element.id.match(/^lang_([a-zA-Z-]+)_url$/))
-    element.href="visualiser.html" + "." + RegExp.$1 + "?" + buildMapArguments();
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////// Server handling ////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-//
-// Display the status
-//
-
-function displayStatus(type,subtype,content)
-{
- var child=document.getElementById("result_status").firstChild;
-
- do
-   {
-    if(child.id != undefined)
-       child.style.display="none";
-
-    child=child.nextSibling;
-   }
- while(child != undefined);
-
- var chosen_status=document.getElementById("result_status_" + type);
-
- chosen_status.style.display="";
-
- if(subtype != null)
-   {
-    var format_status=document.getElementById("result_status_" + subtype).innerHTML;
-
-    chosen_status.innerHTML=format_status.replace('#',String(content));
-   }
-}
-
-
-//
-// Display data statistics
-//
-
-function displayStatistics()
-{
- // Use AJAX to get the statistics
-
- OpenLayers.Request.GET({url: "statistics.cgi", success: runStatisticsSuccess});
-}
-
-
-//
-// Success in running data statistics generation.
-//
-
-function runStatisticsSuccess(response)
-{
- document.getElementById("statistics_data").innerHTML="<pre>" + response.responseText + "</pre>";
- document.getElementById("statistics_link").style.display="none";
-}
-
-
-//
-// Get the requested data
-//
-
-function displayData(datatype)  // called from visualiser.html
-{
- for(var data in data_types)
-    hideshow_hide(data_types[data]);
-
- if(datatype != "")
-    hideshow_show(datatype);
-
- // Delete the old data
-
- layerVectors.destroyFeatures();
-
- if(box != null)
-    layerBoxes.removeMarker(box);
- box=null;
-
- // Print the status
-
- displayStatus("no_data");
-
- // Return if just here to clear the data
-
- if(datatype == "")
-    return;
-
- // Get the new data
-
- var mapbounds=map.getExtent().clone();
- mapbounds.transform(epsg900913,epsg4326);
-
- var url="visualiser.cgi";
-
- url=url + "?lonmin=" + mapbounds.left;
- url=url + ";latmin=" + mapbounds.bottom;
- url=url + ";lonmax=" + mapbounds.right;
- url=url + ";latmax=" + mapbounds.top;
- url=url + ";data=" + datatype;
-
- // Use AJAX to get the data
-
- switch(datatype)
-   {
-   case 'junctions':
-    OpenLayers.Request.GET({url: url, success: runJunctionsSuccess, failure: runFailure});
-    break;
-   case 'super':
-    OpenLayers.Request.GET({url: url, success: runSuperSuccess, faliure: runFailure});
-    break;
-   case 'oneway':
-    OpenLayers.Request.GET({url: url, success: runOnewaySuccess, failure: runFailure});
-    break;
-   case 'highway':
-    var highways=document.forms["highways"].elements["highway"];
-    for(var h in highways)
-       if(highways[h].checked)
-          highway=highways[h].value;
-    url+="-" + highway;
-    OpenLayers.Request.GET({url: url, success: runHighwaySuccess, falure: runFailure});
-    break;
-   case 'transport':
-    var transports=document.forms["transports"].elements["transport"];
-    for(var t in transports)
-       if(transports[t].checked)
-          transport=transports[t].value;
-    url+="-" + transport;
-    OpenLayers.Request.GET({url: url, success: runTransportSuccess, failure: runFailure});
-    break;
-   case 'barrier':
-    var transports=document.forms["barriers"].elements["barrier"];
-    for(var t in transports)
-       if(transports[t].checked)
-          transport=transports[t].value;
-    url+="-" + transport;
-    OpenLayers.Request.GET({url: url, success: runBarrierSuccess, failure: runFailure});
-    break;
-   case 'turns':
-    OpenLayers.Request.GET({url: url, success: runTurnsSuccess, failure: runFailure});
-    break;
-   case 'speed':
-   case 'weight':
-   case 'height':
-   case 'width':
-   case 'length':
-    OpenLayers.Request.GET({url: url, success: runLimitSuccess, failure: runFailure});
-    break;
-   }
-}
-
-
-//
-// Success in getting the junctions.
-//
-
-function runJunctionsSuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat=words[0];
-       var lon=words[1];
-       var count=words[2];
-
-       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
-
-       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
-
-       features.push(new OpenLayers.Feature.Vector(point,{},junction_styles[count]));
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","junctions",lines.length-2);
-}
-
-
-//
-// Success in getting the super-node and super-segments
-//
-
-function runSuperSuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- var nodepoint;
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat=words[0];
-       var lon=words[1];
-       var type=words[2];
-
-       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
-
-       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
-
-       if(type == "n")
-         {
-          nodepoint=point;
-
-          features.push(new OpenLayers.Feature.Vector(point,{},super_node_style));
-         }
-       else
-         {
-          var segment = new OpenLayers.Geometry.LineString([nodepoint,point]);
-
-          features.push(new OpenLayers.Feature.Vector(segment,{},super_segment_style));
-         }
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","super",lines.length-2);
-}
-
-
-//
-// Success in getting the oneway data
-//
-
-function runOnewaySuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
-       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
-
-     //var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
-       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
-
-       var dlat = lonlat2.lat-lonlat1.lat;
-       var dlon = lonlat2.lon-lonlat1.lon;
-       var dist = Math.sqrt(dlat*dlat+dlon*dlon)/10;
-       var ang  = Math.atan2(dlat,dlon);
-
-       var point3 = new OpenLayers.Geometry.Point(lonlat1.lon+dlat/dist,lonlat1.lat-dlon/dist);
-       var point4 = new OpenLayers.Geometry.Point(lonlat1.lon-dlat/dist,lonlat1.lat+dlon/dist);
-
-       var segment = new OpenLayers.Geometry.LineString([point2,point3,point4,point2]);
-
-       var r=Math.round(7.5+7.9*Math.cos(ang));
-       var g=Math.round(7.5+7.9*Math.cos(ang+2.0943951));
-       var b=Math.round(7.5+7.9*Math.cos(ang-2.0943951));
-       var colour = "#" + hex[r] + hex[g] + hex[b];
-
-       var style=new OpenLayers.Style({},{strokeWidth: 2,strokeColor: colour});
-
-       features.push(new OpenLayers.Feature.Vector(segment,{},style));
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","oneway",lines.length-2);
-}
-
-
-//
-// Success in getting the highway data
-//
-
-function runHighwaySuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
-       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
-
-       var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
-       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
-
-       var segment = new OpenLayers.Geometry.LineString([point1,point2]);
-
-       features.push(new OpenLayers.Feature.Vector(segment,{},super_segment_style));
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","highway",lines.length-2);
-}
-
-
-//
-// Success in getting the transport data
-//
-
-function runTransportSuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
-       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
-
-       var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
-       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
-
-       var segment = new OpenLayers.Geometry.LineString([point1,point2]);
-
-       features.push(new OpenLayers.Feature.Vector(segment,{},super_segment_style));
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","transport",lines.length-2);
-}
-
-
-//
-// Success in getting the barrier data
-//
-
-function runBarrierSuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat=words[0];
-       var lon=words[1];
-
-       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
-
-       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
-
-       features.push(new OpenLayers.Feature.Vector(point,{},super_node_style));
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","barrier",lines.length-2);
-}
-
-
-//
-// Success in getting the turn restrictions data
-//
-
-function runTurnsSuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-       var lat3=words[4];
-       var lon3=words[5];
-
-       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
-       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
-       var lonlat3= new OpenLayers.LonLat(lon3,lat3).transform(epsg4326,epsg900913);
-
-       var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
-       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
-       var point3 = new OpenLayers.Geometry.Point(lonlat3.lon,lonlat3.lat);
-
-       var segments = new OpenLayers.Geometry.LineString([point1,point2,point3]);
-
-       features.push(new OpenLayers.Feature.Vector(segments,{},turn_restriction_style));
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","turns",lines.length-2);
-}
-
-
-//
-// Success in getting the speed/weight/height/width/length limits
-//
-
-function runLimitSuccess(response)
-{
- var lines=response.responseText.split('\n');
-
- var features=[];
-
- var nodelonlat;
-
- for(var line=0;line<lines.length;line++)
-   {
-    var words=lines[line].split(' ');
-
-    if(line == 0)
-      {
-       var lat1=words[0];
-       var lon1=words[1];
-       var lat2=words[2];
-       var lon2=words[3];
-
-       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
-
-       box = new OpenLayers.Marker.Box(bounds);
-
-       layerBoxes.addMarker(box);
-      }
-    else if(words[0] != "")
-      {
-       var lat=words[0];
-       var lon=words[1];
-       var number=words[2];
-
-       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
-
-       if(number == undefined)
-         {
-          var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
-
-          nodelonlat=lonlat;
-
-          features.push(new OpenLayers.Feature.Vector(point,{},junction_styles[1]));
-         }
-       else
-         {
-          var dlat = lonlat.lat-nodelonlat.lat;
-          var dlon = lonlat.lon-nodelonlat.lon;
-          var dist = Math.sqrt(dlat*dlat+dlon*dlon)/60;
-
-          var point = new OpenLayers.Geometry.Point(nodelonlat.lon+dlon/dist,nodelonlat.lat+dlat/dist);
-
-          features.push(new OpenLayers.Feature.Vector(point,{},
-                                                      new OpenLayers.Style({},{externalGraphic: 'icons/limit-' + number + '.png',
-                                                                               graphicYOffset: -9,
-                                                                               graphicWidth: 19,
-                                                                               graphicHeight: 19})));
-         }
-      }
-   }
-
- layerVectors.addFeatures(features);
-
- displayStatus("data","limit",lines.length-2);
-}
-
-
-//
-// Failure in getting data.
-//
-
-function runFailure(response)
-{
- displayStatus("error");
-}
diff --git a/web/www/routino/visualiser.leaflet.js b/web/www/routino/visualiser.leaflet.js
new file mode 100644
index 0000000..eb3e91f
--- /dev/null
+++ b/web/www/routino/visualiser.leaflet.js
@@ -0,0 +1,1226 @@
+//
+// Routino data visualiser web page Javascript
+//
+// Part of the Routino routing software.
+//
+// This file Copyright 2008-2014 Andrew M. Bishop
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+//
+// Data types
+//
+
+var data_types=[
+                "junctions",
+                "super",
+                "oneway",
+                "highway",
+                "transport",
+                "barrier",
+                "turns",
+                "speed",
+                "weight",
+                "height",
+                "width",
+                "length",
+                "property",
+                "errorlogs"
+               ];
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Initialisation /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// Process the URL query string and extract the arguments
+
+var legal={"^lon"  : "^[-0-9.]+$",
+           "^lat"  : "^[-0-9.]+$",
+           "^zoom" : "^[0-9]+$"};
+
+var args={};
+
+if(location.search.length>1)
+  {
+   var query,queries;
+
+   query=location.search.replace(/^\?/,"");
+   query=query.replace(/;/g,"&");
+   queries=query.split("&");
+
+   for(var i=0;i<queries.length;i++)
+     {
+      queries[i].match(/^([^=]+)(=(.*))?$/);
+
+      var k=RegExp.$1;
+      var v=decodeURIComponent(RegExp.$3);
+
+      for(var l in legal)
+        {
+         if(k.match(RegExp(l)) && v.match(RegExp(legal[l])))
+            args[k]=v;
+        }
+     }
+  }
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// Map handling /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var map;
+var layerMap=[], layerHighlights, layerVectors, layerBoxes;
+var box;
+
+//
+// Initialise the 'map' object
+//
+
+function map_init()             // called from visualiser.html
+{
+ // Create the map (Map URLs and limits are in mapprops.js)
+
+ map = L.map("map",
+             {
+              attributionControl: false,
+              zoomControl: false,
+
+              minZoom: mapprops.zoomout,
+              maxZoom: mapprops.zoomin,
+
+              maxBounds: L.latLngBounds(L.latLng(mapprops.southedge,mapprops.westedge),L.latLng(mapprops.northedge,mapprops.eastedge))
+              });
+
+ // Add map tile layers
+
+ var baselayers={};
+
+ for(var l=0; l<mapprops.mapdata.length; l++)
+   {
+    var urls=mapprops.mapdata[l].tiles.url.replace(/\${/g,"{");
+
+    if(mapprops.mapdata[l].tiles.subdomains===undefined)
+       layerMap[l] = L.tileLayer(urls);
+    else
+       layerMap[l] = L.tileLayer(urls, {subdomains: mapprops.mapdata[l].tiles.subdomains});
+
+    baselayers[mapprops.mapdata[l].label]=layerMap[l];
+
+    if(l===0)
+       map.addLayer(layerMap[l]);
+   }
+
+ // Add the controls
+
+ map.addControl(L.control.zoom());
+ map.addControl(L.control.scale());
+ map.addControl(L.control.layers(baselayers));
+
+ // Update the attribution if the layer changes
+
+ function change_attribution_event(event)
+ {
+  for(var l=0; l<mapprops.mapdata.length; l++)
+     if(layerMap[l] == event.layer)
+        change_attribution(l);
+ }
+
+ map.on("baselayerchange",change_attribution_event);
+
+ function change_attribution(l)
+ {
+  var data_url =mapprops.mapdata[l].attribution.data_url;
+  var data_text=mapprops.mapdata[l].attribution.data_text;
+  var tile_url =mapprops.mapdata[l].attribution.tile_url;
+  var tile_text=mapprops.mapdata[l].attribution.tile_text;
+
+  document.getElementById("attribution_data").innerHTML="<a href=\"" + data_url + "\" target=\"data_attribution\">" + data_text + "</a>";
+  document.getElementById("attribution_tile").innerHTML="<a href=\"" + tile_url + "\" target=\"tile_attribution\">" + tile_text + "</a>";
+ }
+
+ change_attribution(0);
+
+ // Add two vectors layers (one for highlights that display behind the vectors)
+
+ layerVectors = L.layerGroup();
+ map.addLayer(layerVectors);
+
+ layerHighlights = L.layerGroup();
+ map.addLayer(layerHighlights);
+
+ // Handle popup
+
+ createPopup();
+
+ // Add a boxes layer
+
+ layerBoxes = L.rectangle(map.options.maxBounds,{stroke: false, color: "#f00", weight: 1, opacity: 1.0,
+                                                 fill: false});
+
+ map.addLayer(layerBoxes);
+
+ box=false;
+
+ // Move the map
+
+ map.on("moveend", updateURLs);
+
+ var lon =args["lon"];
+ var lat =args["lat"];
+ var zoom=args["zoom"];
+
+ if(lon !== undefined && lat !== undefined && zoom !== undefined)
+   {
+    if(lon<mapprops.westedge) lon=mapprops.westedge;
+    if(lon>mapprops.eastedge) lon=mapprops.eastedge;
+
+    if(lat<mapprops.southedge) lat=mapprops.southedge;
+    if(lat>mapprops.northedge) lat=mapprops.northedge;
+
+    if(zoom<mapprops.zoomout) zoom=mapprops.zoomout;
+    if(zoom>mapprops.zoomin)  zoom=mapprops.zoomin;
+
+    map.setView(L.latLng(lat,lon),zoom);
+   }
+ else
+    map.fitBounds(map.options.maxBounds);
+
+ // Unhide editing URL if variable set
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var edit_url=document.getElementById("edit_url");
+
+    edit_url.style.display="";
+    edit_url.href=mapprops.editurl;
+   }
+
+ updateURLs();
+}
+
+
+//
+// Format a number in printf("%.5f") format.
+//
+
+function format5f(number)
+{
+ var newnumber=Math.floor(number*100000+0.5);
+ var delta=0;
+
+ if(newnumber>=0 && newnumber<100000) delta= 100000;
+ if(newnumber<0 && newnumber>-100000) delta=-100000;
+
+ var string=String(newnumber+delta);
+
+ var intpart =string.substring(0,string.length-5);
+ var fracpart=string.substring(string.length-5,string.length);
+
+ if(delta>0) intpart="0";
+ if(delta<0) intpart="-0";
+
+ return(intpart + "." + fracpart);
+}
+
+
+//
+// Build a set of URL arguments for the map location
+//
+
+function buildMapArguments()
+{
+ var lonlat = map.getCenter();
+
+ var zoom = map.getZoom();
+
+ return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lng) + ";zoom=" + zoom;
+}
+
+
+//
+// Update the URLs
+//
+
+function updateURLs()
+{
+ var mapargs=buildMapArguments();
+
+ var links=document.getElementsByTagName("a");
+
+ for(var i=0; i<links.length; i++)
+   {
+    var element=links[i];
+
+    if(element.id == "permalink_url")
+       element.href=location.pathname + "?" + mapargs;
+
+    if(element.id == "router_url")
+       element.href="router.html" + "?" + mapargs;
+
+    if(element.id == "edit_url")
+       element.href=mapprops.editurl + "?" + mapargs;
+
+    if(element.id.match(/^lang_([a-zA-Z-]+)_url$/))
+       element.href="visualiser.html" + "." + RegExp.$1 + "?" + mapargs;
+   }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////// Popup and selection handling /////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var popup=null;
+
+//
+// Create a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function createPopup()
+{
+ popup=document.createElement("div");
+
+ popup.className = "popup";
+
+ popup.innerHTML = "<span></span>";
+
+ popup.style.display = "none";
+
+ popup.style.position = "fixed";
+ popup.style.top = "-4000px";
+ popup.style.left = "-4000px";
+ popup.style.zIndex = "100";
+
+ popup.style.padding = "5px";
+
+ popup.style.opacity=0.85;
+ popup.style.backgroundColor="#C0C0C0";
+ popup.style.border="4px solid #404040";
+
+ document.body.appendChild(popup);
+}
+
+
+//
+// Draw a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function drawPopup(html)
+{
+ if(html===null)
+   {
+    popup.style.display="none";
+    return;
+   }
+
+ if(popup.style.display=="none")
+   {
+    var map_div=document.getElementById("map");
+
+    popup.style.left  =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px";
+    popup.style.top   =                                map_div.offsetTop +30 + "px";
+    popup.style.width =map_div.clientWidth-120 + "px";
+
+    popup.style.display="";
+   }
+
+ var close="<span style='float: right; cursor: pointer;' onclick='drawPopup(null)'>X</span>";
+
+ popup.innerHTML=close+html;
+}
+
+
+//
+// Select a circleMarker feature
+//
+
+function selectCircleMarkerFeature(feature,dump,event)
+{
+ if(dump)
+    ajaxGET("visualiser.cgi?dump=" + dump, runDumpSuccess);
+
+ layerHighlights.clearLayers();
+
+ var highlight = L.circleMarker(feature.getLatLng(),{radius: 2*feature.getRadius(), fill: true, fillColor: "#F0F000", fillOpacity: 1.0,
+                                                     stroke: false});
+
+ layerHighlights.addLayer(highlight);
+
+ highlight.bringToBack();
+}
+
+
+//
+// Select a Polyline feature
+//
+
+function selectPolylineFeature(feature,dump,event)
+{
+ if(dump)
+    ajaxGET("visualiser.cgi?dump=" + dump, runDumpSuccess);
+
+ layerHighlights.clearLayers();
+
+ var highlight = L.polyline(feature.getLatLngs(),{weight: 8, stroke: true, color: "#F0F000", opacity: 1.0,
+                                                  fill: false});
+
+ layerHighlights.addLayer(highlight);
+
+ highlight.bringToBack();
+}
+
+
+//
+// Select a Polygon feature
+//
+
+function selectPolygonFeature(feature,dump,event)
+{
+ if(dump)
+    ajaxGET("visualiser.cgi?dump=" + dump, runDumpSuccess);
+
+ layerHighlights.clearLayers();
+
+ var highlight = L.polygon(feature.getLatLngs(),{weight: 8, stroke: true, color: "#F0F000", opacity: 1.0,
+                                                 fill: false});
+
+ layerHighlights.addLayer(highlight);
+
+ highlight.bringToBack();
+}
+
+
+//
+// Un-select a feature
+//
+
+function unselectFeature(feature)
+{
+ layerHighlights.clearLayers();
+
+ drawPopup(null);
+}
+
+
+//
+// Display the dump data
+//
+
+function runDumpSuccess(response)
+{
+ var string=response.responseText;
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var types=["node", "way", "relation"];
+    var Types=["Node", "Way", "Relation"];
+
+    for(var t in types)
+      {
+       var Type=Types[t];
+       var type=types[t];
+
+       var regexp=RegExp(Type + " [0-9]+");
+
+       var match;
+
+       while((match=string.match(regexp)) !== null)
+         {
+          match=String(match);
+
+          var id=match.slice(1+type.length,match.length);
+
+          string=string.replace(regexp,Type + " <a href='" + mapprops.browseurl + "/" + type + "/" + id + "' target='" + type + id + "'>" + id + "</a>");
+         }
+      }
+   }
+
+ drawPopup(string.split("\n").join("<br>"));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Server handling ////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+//
+// Define an AJAX request object
+//
+
+function ajaxGET(url,success,failure,state)
+{
+ var ajaxRequest=new XMLHttpRequest();
+
+ function ajaxGOT(options) {
+  if(this.readyState==4)
+     if(this.status==200)
+       { if(typeof(options.success)=="function") options.success(this,options.state); }
+     else
+       { if(typeof(options.failure)=="function") options.failure(this,options.state); }
+ }
+
+ ajaxRequest.onreadystatechange = function(){ ajaxGOT.call(ajaxRequest,{success: success, failure: failure, state: state}); };
+ ajaxRequest.open("GET", url, true);
+ ajaxRequest.send(null);
+}
+
+
+//
+// Display the status
+//
+
+function displayStatus(type,subtype,content)
+{
+ var child=document.getElementById("result_status").firstChild;
+
+ do
+   {
+    if(child.id !== undefined)
+       child.style.display="none";
+
+    child=child.nextSibling;
+   }
+ while(child !== null);
+
+ var chosen_status=document.getElementById("result_status_" + type);
+
+ chosen_status.style.display="";
+
+ if(subtype !== undefined)
+   {
+    var format_status=document.getElementById("result_status_" + subtype).innerHTML;
+
+    chosen_status.innerHTML=format_status.replace("#",String(content));
+   }
+}
+
+
+//
+// Display data statistics
+//
+
+function displayStatistics()
+{
+ // Use AJAX to get the statistics
+
+ ajaxGET("statistics.cgi", runStatisticsSuccess);
+}
+
+
+//
+// Success in running data statistics generation.
+//
+
+function runStatisticsSuccess(response)
+{
+ document.getElementById("statistics_data").innerHTML="<pre>" + response.responseText + "</pre>";
+ document.getElementById("statistics_link").style.display="none";
+}
+
+
+//
+// Get the requested data
+//
+
+function displayData(datatype)  // called from visualiser.html
+{
+ for(var data in data_types)
+    hideshow_hide(data_types[data]);
+
+ if(datatype !== "")
+    hideshow_show(datatype);
+
+ // Delete the old data
+
+ unselectFeature();
+
+ layerVectors.clearLayers();
+ layerHighlights.clearLayers();
+
+ layerBoxes.setStyle({stroke:false});
+ box=false;
+
+ // Print the status
+
+ displayStatus("no_data");
+
+ // Return if just here to clear the data
+
+ if(datatype === "")
+    return;
+
+ // Get the new data
+
+ var mapbounds=map.getBounds();
+
+ var url="visualiser.cgi";
+
+ url=url + "?lonmin=" + format5f(mapbounds.getWest());
+ url=url + ";latmin=" + format5f(mapbounds.getSouth());
+ url=url + ";lonmax=" + format5f(mapbounds.getEast());
+ url=url + ";latmax=" + format5f(mapbounds.getNorth());
+ url=url + ";data=" + datatype;
+
+ // Use AJAX to get the data
+
+ switch(datatype)
+   {
+   case "junctions":
+    ajaxGET(url, runJunctionsSuccess, runFailure);
+    break;
+   case "super":
+    ajaxGET(url, runSuperSuccess, runFailure);
+    break;
+   case "oneway":
+    ajaxGET(url, runOnewaySuccess, runFailure);
+    break;
+   case "highway":
+    var highway;
+    var highways=document.forms["highways"].elements["highway"];
+    for(var h in highways)
+       if(highways[h].checked)
+          highway=highways[h].value;
+    url+="-" + highway;
+    ajaxGET(url, runHighwaySuccess, runFailure);
+    break;
+   case "transport":
+    var transport;
+    var transports=document.forms["transports"].elements["transport"];
+    for(var t in transports)
+       if(transports[t].checked)
+          transport=transports[t].value;
+    url+="-" + transport;
+    ajaxGET(url, runTransportSuccess, runFailure);
+    break;
+   case "barrier":
+    var transport;
+    var transports=document.forms["barriers"].elements["barrier"];
+    for(var t in transports)
+       if(transports[t].checked)
+          transport=transports[t].value;
+    url+="-" + transport;
+    ajaxGET(url, runBarrierSuccess, runFailure);
+    break;
+   case "turns":
+    ajaxGET(url, runTurnsSuccess, runFailure);
+    break;
+   case "speed":
+   case "weight":
+   case "height":
+   case "width":
+   case "length":
+    ajaxGET(url, runLimitSuccess, runFailure);
+    break;
+   case "property":
+    var property;
+    var properties=document.forms["properties"].elements["property"];
+    for(var p in properties)
+       if(properties[p].checked)
+          property=properties[p].value;
+    url+="-" + property;
+    ajaxGET(url, runPropertySuccess, runFailure);
+    break;
+   case "errorlogs":
+    ajaxGET(url, runErrorlogSuccess, runFailure);
+    break;
+   }
+}
+
+
+//
+// Success in getting the junctions.
+//
+
+function runJunctionsSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var junction_colours={
+                       0: "#FFFFFF",
+                       1: "#FF0000",
+                       2: "#FFFF00",
+                       3: "#00FF00",
+                       4: "#8B4513",
+                       5: "#00BFFF",
+                       6: "#FF69B4",
+                       7: "#000000",
+                       8: "#000000",
+                       9: "#000000"
+                      };
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+       var count=words[3];
+
+       var lonlat = L.latLng(lat,lon);
+
+       var feature = L.circleMarker(lonlat,{radius: 2, fill: true, fillColor: junction_colours[count], fillOpacity: 1.0,
+                                            stroke: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectCircleMarkerFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","junctions",lines.length-2);
+}
+
+
+//
+// Success in getting the super-node and super-segments
+//
+
+function runSuperSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var nodelonlat;
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat = L.latLng(lat,lon);
+
+       if(dump.charAt(0) == "n")
+         {
+          nodelonlat=lonlat;
+
+          var feature = L.circleMarker(lonlat,{radius: 4, fill: true, fillColor: "#FF0000", fillOpacity: 1.0,
+                                               stroke: false});
+
+          feature.on("click", (function(f,d) { return function(evt) { selectCircleMarkerFeature(f,d,evt); }; }(feature,dump)));
+
+          layerVectors.addLayer(feature);
+         }
+       else
+         {
+          var feature = L.polyline([nodelonlat,lonlat],{weight: 2, stroke: true, color: "#FF0000", opacity: 1.0,
+                                                        fill: false});
+
+          feature.on("click", (function(f,d) { return function(evt) { selectPolylineFeature(f,d,evt); }; }(feature,dump)));
+
+          layerVectors.addLayer(feature);
+         }
+      }
+   }
+
+ displayStatus("data","super",lines.length-2);
+}
+
+
+//
+// Success in getting the oneway data
+//
+
+function runOnewaySuccess(response)
+{
+ var hex={0: "00", 1: "11",  2: "22",  3: "33",  4: "44",  5: "55",  6: "66",  7: "77",
+          8: "88", 9: "99", 10: "AA", 11: "BB", 12: "CC", 13: "DD", 14: "EE", 15: "FF"};
+
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1 = L.latLng(lat1,lon1);
+       var lonlat2 = L.latLng(lat2,lon2);
+
+       var point1 = map.options.crs.latLngToPoint(lonlat1,15);
+       var point2 = map.options.crs.latLngToPoint(lonlat2,15);
+
+       var dy = point2.y-point1.y;
+       var dx = point2.x-point1.x;
+       var dist = Math.sqrt(dx*dx+dy*dy)/2;
+       var ang  = Math.atan2(-dy,dx);
+
+       var point3 = L.point(point1.x-dy/dist,point1.y+dx/dist);
+       var point4 = L.point(point1.x+dy/dist,point1.y-dx/dist);
+
+       var lonlat3 = map.options.crs.pointToLatLng(point3,15);
+       var lonlat4 = map.options.crs.pointToLatLng(point4,15);
+
+       var r=Math.round(7.5+7.9*Math.cos(ang));
+       var g=Math.round(7.5+7.9*Math.cos(ang+2.0943951));
+       var b=Math.round(7.5+7.9*Math.cos(ang-2.0943951));
+       var colour = "#" + hex[r] + hex[g] + hex[b];
+
+       var feature = L.polygon([lonlat2,lonlat3,lonlat4],{weight: 2, stroke: true, color: colour, opacity: 1.0,
+                                                          fill: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectPolygonFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","oneway",lines.length-2);
+}
+
+
+//
+// Success in getting the highway data
+//
+
+function runHighwaySuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1 = L.latLng(lat1,lon1);
+       var lonlat2 = L.latLng(lat2,lon2);
+
+       var feature = L.polyline([lonlat1,lonlat2],{weight: 2, stroke: true, color: "#FF0000", opacity: 1.0,
+                                                   fill: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectPolylineFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","highway",lines.length-2);
+}
+
+
+//
+// Success in getting the transport data
+//
+
+function runTransportSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1 = L.latLng(lat1,lon1);
+       var lonlat2 = L.latLng(lat2,lon2);
+
+       var feature = L.polyline([lonlat1,lonlat2],{weight: 2, stroke: true, color: "#FF0000", opacity: 1.0,
+                                                   fill: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectPolylineFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","transport",lines.length-2);
+}
+
+
+//
+// Success in getting the barrier data
+//
+
+function runBarrierSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat = L.latLng(lat,lon);
+
+       var feature = L.circleMarker(lonlat,{radius: 2, fill: true, fillColor: "#FF0000", fillOpacity: 1.0,
+                                            stroke: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectCircleMarkerFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","barrier",lines.length-2);
+}
+
+
+//
+// Success in getting the turn restrictions data
+//
+
+function runTurnsSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+       var lat3=words[5];
+       var lon3=words[6];
+
+       var lonlat1 = L.latLng(lat1,lon1);
+       var lonlat2 = L.latLng(lat2,lon2);
+       var lonlat3 = L.latLng(lat3,lon3);
+
+       var feature = L.polygon([lonlat1,lonlat2,lonlat3],{weight: 2, stroke: true, color: "#FF0000", opacity: 1.0,
+                                                          fill: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectPolygonFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","turns",lines.length-2);
+}
+
+
+//
+// Success in getting the speed/weight/height/width/length limits
+//
+
+function runLimitSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var nodelonlat;
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+       var number=words[3];
+
+       var lonlat = L.latLng(lat,lon);
+
+       if(number === undefined)
+         {
+          nodelonlat=lonlat;
+
+          var feature = L.circleMarker(lonlat,{radius: 3, fill: true, fillColor: "#FF0000", fillOpacity: 1.0,
+                                               stroke: false});
+
+          feature.on("click", (function(f,d) { return function(evt) { selectCircleMarkerFeature(f,d,evt); }; }(feature,dump)));
+
+          layerVectors.addLayer(feature);
+         }
+       else
+         {
+          var lonlat = L.latLng(lat,lon);
+
+          var feature = L.polyline([nodelonlat,lonlat],{weight: 2, stroke: true, color: "#FF0000", opacity: 1.0,
+                                                        fill: false});
+
+          feature.on("click", (function(f,d) { return function(evt) { selectPolylineFeature(f,d,evt); }; }(feature,dump)));
+
+          layerVectors.addLayer(feature);
+
+          var point1 = map.options.crs.latLngToPoint(nodelonlat,15);
+          var point2 = map.options.crs.latLngToPoint(lonlat    ,15);
+
+          var dy = point2.y-point1.y;
+          var dx = point2.x-point1.x;
+          var dist = Math.sqrt(dx*dx+dy*dy)/24;
+
+          var point = L.point(point1.x+dx/dist,point1.y+dy/dist);
+
+          feature=L.marker(map.options.crs.pointToLatLng(point,15), {clickable: false,
+                                                                     icon: L.icon({iconUrl: "icons/limit-" + number + ".png",
+                                                                                   iconSize: L.point(19,19),
+                                                                                   iconAnchor: L.point(9,10)})});
+
+          layerVectors.addLayer(feature);
+         }
+      }
+   }
+
+ displayStatus("data","limit",lines.length-2);
+}
+
+
+//
+// Success in getting the property data
+//
+
+function runPropertySuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1 = L.latLng(lat1,lon1);
+       var lonlat2 = L.latLng(lat2,lon2);
+
+       var feature = L.polyline([lonlat1,lonlat2],{weight: 2, stroke: true, color: "#FF0000", opacity: 1.0,
+                                                   fill: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectPolylineFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","property",lines.length-2);
+}
+
+
+//
+// Success in getting the error log data
+//
+
+function runErrorlogSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = L.latLngBounds(L.latLng(lat1,lon1),L.latLng(lat2,lon2));
+
+       layerBoxes.setBounds(bounds);
+
+       layerBoxes.setStyle({stroke: true});
+       box=true;
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat = L.latLng(lat,lon);
+
+       var feature = L.circleMarker(lonlat,{radius: 3, fill: true, fillColor: "#FF0000", fillOpacity: 1.0,
+                                            stroke: false});
+
+       feature.on("click", (function(f,d) { return function(evt) { selectCircleMarkerFeature(f,d,evt); }; }(feature,dump)));
+
+       layerVectors.addLayer(feature);
+      }
+   }
+
+ displayStatus("data","errorlogs",lines.length-2);
+}
+
+
+//
+// Failure in getting data.
+//
+
+function runFailure(response)
+{
+ displayStatus("failed");
+}
diff --git a/web/www/routino/visualiser.openlayers.js b/web/www/routino/visualiser.openlayers.js
new file mode 100644
index 0000000..7649a6c
--- /dev/null
+++ b/web/www/routino/visualiser.openlayers.js
@@ -0,0 +1,1309 @@
+//
+// Routino data visualiser web page Javascript
+//
+// Part of the Routino routing software.
+//
+// This file Copyright 2008-2014 Andrew M. Bishop
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+//
+// Data types
+//
+
+var data_types=[
+                "junctions",
+                "super",
+                "oneway",
+                "highway",
+                "transport",
+                "barrier",
+                "turns",
+                "speed",
+                "weight",
+                "height",
+                "width",
+                "length",
+                "property",
+                "errorlogs"
+               ];
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Initialisation /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// Process the URL query string and extract the arguments
+
+var legal={"^lon"  : "^[-0-9.]+$",
+           "^lat"  : "^[-0-9.]+$",
+           "^zoom" : "^[0-9]+$"};
+
+var args={};
+
+if(location.search.length>1)
+  {
+   var query,queries;
+
+   query=location.search.replace(/^\?/,"");
+   query=query.replace(/;/g,"&");
+   queries=query.split("&");
+
+   for(var i=0;i<queries.length;i++)
+     {
+      queries[i].match(/^([^=]+)(=(.*))?$/);
+
+      var k=RegExp.$1;
+      var v=decodeURIComponent(RegExp.$3);
+
+      for(var l in legal)
+        {
+         if(k.match(RegExp(l)) && v.match(RegExp(legal[l])))
+            args[k]=v;
+        }
+     }
+  }
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// Map handling /////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var map;
+var layerMap=[], layerHighlights, layerVectors, layerBoxes;
+var epsg4326, epsg900913;
+var box;
+var select;
+
+//
+// Initialise the 'map' object
+//
+
+function map_init()             // called from visualiser.html
+{
+ // Create the map (Map URLs and limits are in mapprops.js)
+
+ epsg4326=new OpenLayers.Projection("EPSG:4326");
+ epsg900913=new OpenLayers.Projection("EPSG:900913");
+
+ map = new OpenLayers.Map ("map",
+                           {
+                            controls:[
+                                      new OpenLayers.Control.Navigation(),
+                                      new OpenLayers.Control.PanZoomBar(),
+                                      new OpenLayers.Control.ScaleLine(),
+                                      new OpenLayers.Control.LayerSwitcher()
+                                      ],
+
+                            projection: epsg900913,
+                            displayProjection: epsg4326,
+
+                            minZoomLevel: mapprops.zoomout,
+                            numZoomLevels: mapprops.zoomin-mapprops.zoomout+1,
+                            maxResolution: 156543.03390625 / Math.pow(2,mapprops.zoomout),
+
+                            restrictedExtent: new OpenLayers.Bounds(mapprops.westedge,mapprops.southedge,mapprops.eastedge,mapprops.northedge).transform(epsg4326,epsg900913)
+                           });
+
+ // Get a URL for the tile (mostly copied from OpenLayers/Layer/XYZ.js).
+
+ function limitedUrl(bounds)
+ {
+  var res = this.map.getResolution();
+
+  var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
+  var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
+  var z = this.map.getZoom() + this.map.minZoomLevel;
+
+  var limit = Math.pow(2, z);
+  x = ((x % limit) + limit) % limit;
+
+  var xyz = {"x": x, "y": y, "z": z};
+  var url = this.url;
+
+  if (OpenLayers.Util.isArray(url))
+    {
+     var s = "" + xyz.x + xyz.y + xyz.z;
+     url = this.selectUrl(s, url);
+    }
+
+  return OpenLayers.String.format(url, xyz);
+ }
+
+ // Add map tile layers
+
+ for(var l=0; l<mapprops.mapdata.length; l++)
+   {
+    var urls;
+
+    if(OpenLayers.Util.isArray(mapprops.mapdata[l].tiles.subdomains))
+      {
+       urls=[];
+
+       for(var s=0; s<mapprops.mapdata[l].tiles.subdomains.length; s++)
+          urls.push(mapprops.mapdata[l].tiles.url.replace(/\${s}/,mapprops.mapdata[l].tiles.subdomains[s]));
+      }
+    else
+       urls=mapprops.mapdata[l].tiles.url;
+
+    layerMap[l] = new OpenLayers.Layer.TMS(mapprops.mapdata[l].label,
+                                           urls,
+                                           {
+                                            getURL: limitedUrl,
+                                            displayOutsideMaxExtent: true,
+                                            buffer: 1
+                                           });
+    map.addLayer(layerMap[l]);
+   }
+
+ // Update the attribution if the layer changes
+
+ function change_attribution_event(event)
+ {
+  for(var l=0; l<mapprops.mapdata.length; l++)
+     if(layerMap[l] == event.layer)
+        change_attribution(l);
+ }
+
+ map.events.register("changelayer",layerMap,change_attribution_event);
+
+ function change_attribution(l)
+ {
+  var data_url =mapprops.mapdata[l].attribution.data_url;
+  var data_text=mapprops.mapdata[l].attribution.data_text;
+  var tile_url =mapprops.mapdata[l].attribution.tile_url;
+  var tile_text=mapprops.mapdata[l].attribution.tile_text;
+
+  document.getElementById("attribution_data").innerHTML="<a href=\"" + data_url + "\" target=\"data_attribution\">" + data_text + "</a>";
+  document.getElementById("attribution_tile").innerHTML="<a href=\"" + tile_url + "\" target=\"tile_attribution\">" + tile_text + "</a>";
+ }
+
+ change_attribution(0);
+
+ // Add two vectors layers (one for highlights that display behind the vectors)
+
+ layerHighlights = new OpenLayers.Layer.Vector("Highlights",{displayInLayerSwitcher: false});
+ map.addLayer(layerHighlights);
+
+ layerVectors = new OpenLayers.Layer.Vector("Markers",{displayInLayerSwitcher: false});
+ map.addLayer(layerVectors);
+
+ // Handle feature selection and popup
+
+ select = new OpenLayers.Control.SelectFeature(layerVectors,
+                                               {onSelect: selectFeature, onUnselect: unselectFeature});
+
+ map.addControl(select);
+ select.activate();
+
+ createPopup();
+
+ // Add a boxes layer
+
+ layerBoxes = new OpenLayers.Layer.Boxes("Boundary",{displayInLayerSwitcher: false});
+ map.addLayer(layerBoxes);
+
+ box=null;
+
+ // Move the map
+
+ map.events.register("moveend", map, updateURLs);
+
+ var lon =args["lon"];
+ var lat =args["lat"];
+ var zoom=args["zoom"];
+
+ if(lon !== undefined && lat !== undefined && zoom !== undefined)
+   {
+    if(lon<mapprops.westedge) lon=mapprops.westedge;
+    if(lon>mapprops.eastedge) lon=mapprops.eastedge;
+
+    if(lat<mapprops.southedge) lat=mapprops.southedge;
+    if(lat>mapprops.northedge) lat=mapprops.northedge;
+
+    if(zoom<mapprops.zoomout) zoom=mapprops.zoomout;
+    if(zoom>mapprops.zoomin)  zoom=mapprops.zoomin;
+
+    var lonlat = new OpenLayers.LonLat(lon,lat);
+    lonlat.transform(epsg4326,epsg900913);
+
+    map.moveTo(lonlat,zoom-map.minZoomLevel);
+   }
+ else
+   {
+    map.setCenter(map.restrictedExtent.getCenterLonLat(), map.getZoomForExtent(map.restrictedExtent,true));
+    map.maxResolution = map.getResolution();
+   }
+
+ // Unhide editing URL if variable set
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var edit_url=document.getElementById("edit_url");
+
+    edit_url.style.display="";
+    edit_url.href=mapprops.editurl;
+   }
+
+ updateURLs();
+}
+
+
+//
+// Format a number in printf("%.5f") format.
+//
+
+function format5f(number)
+{
+ var newnumber=Math.floor(number*100000+0.5);
+ var delta=0;
+
+ if(newnumber>=0 && newnumber<100000) delta= 100000;
+ if(newnumber<0 && newnumber>-100000) delta=-100000;
+
+ var string=String(newnumber+delta);
+
+ var intpart =string.substring(0,string.length-5);
+ var fracpart=string.substring(string.length-5,string.length);
+
+ if(delta>0) intpart="0";
+ if(delta<0) intpart="-0";
+
+ return(intpart + "." + fracpart);
+}
+
+
+//
+// Build a set of URL arguments for the map location
+//
+
+function buildMapArguments()
+{
+ var lonlat = map.getCenter().clone();
+ lonlat.transform(epsg900913,epsg4326);
+
+ var zoom = map.getZoom() + map.minZoomLevel;
+
+ return "lat=" + format5f(lonlat.lat) + ";lon=" + format5f(lonlat.lon) + ";zoom=" + zoom;
+}
+
+
+//
+// Update the URLs
+//
+
+function updateURLs()
+{
+ var mapargs=buildMapArguments();
+
+ var links=document.getElementsByTagName("a");
+
+ for(var i=0; i<links.length; i++)
+   {
+    var element=links[i];
+
+    if(element.id == "permalink_url")
+       element.href=location.pathname + "?" + mapargs;
+
+    if(element.id == "router_url")
+       element.href="router.html" + "?" + mapargs;
+
+    if(element.id == "edit_url")
+       element.href=mapprops.editurl + "?" + mapargs;
+
+    if(element.id.match(/^lang_([a-zA-Z-]+)_url$/))
+       element.href="visualiser.html" + "." + RegExp.$1 + "?" + mapargs;
+   }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////// Popup and selection handling /////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var popup=null;
+
+//
+// Create a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function createPopup()
+{
+ popup=document.createElement("div");
+
+ popup.className = "popup";
+
+ popup.innerHTML = "<span></span>";
+
+ popup.style.display = "none";
+
+ popup.style.position = "fixed";
+ popup.style.top = "-4000px";
+ popup.style.left = "-4000px";
+ popup.style.zIndex = "100";
+
+ popup.style.padding = "5px";
+
+ popup.style.opacity=0.85;
+ popup.style.backgroundColor="#C0C0C0";
+ popup.style.border="4px solid #404040";
+
+ document.body.appendChild(popup);
+}
+
+
+//
+// Draw a popup - independent of map because want it fixed on screen not fixed on map.
+//
+
+function drawPopup(html)
+{
+ if(html===null)
+   {
+    popup.style.display="none";
+    return;
+   }
+
+ if(popup.style.display=="none")
+   {
+    var map_div=document.getElementById("map");
+
+    popup.style.left  =map_div.offsetParent.offsetLeft+map_div.offsetLeft+60 + "px";
+    popup.style.top   =                                map_div.offsetTop +30 + "px";
+    popup.style.width =map_div.clientWidth-120 + "px";
+
+    popup.style.display="";
+   }
+
+ var close="<span style='float: right; cursor: pointer;' onclick='drawPopup(null)'>X</span>";
+
+ popup.innerHTML=close+html;
+}
+
+
+//
+// Select a feature
+//
+
+function selectFeature(feature)
+{
+ if(feature.attributes.dump)
+    ajaxGET("visualiser.cgi?dump=" + feature.attributes.dump, runDumpSuccess);
+
+ layerHighlights.destroyFeatures();
+
+ var highlight_style = new OpenLayers.Style({},{strokeColor: "#F0F000",strokeWidth: 8,
+                                                fillColor: "#F0F000",pointRadius: 4});
+
+ var highlight = new OpenLayers.Feature.Vector(feature.geometry.clone(),{},highlight_style);
+
+ layerHighlights.addFeatures([highlight]);
+}
+
+
+//
+// Un-select a feature
+//
+
+function unselectFeature(feature)
+{
+ layerHighlights.destroyFeatures();
+
+ drawPopup(null);
+}
+
+
+//
+// Display the dump data
+//
+
+function runDumpSuccess(response)
+{
+ var string=response.responseText;
+
+ if(mapprops.editurl !== undefined && mapprops.editurl !== "")
+   {
+    var types=["node", "way", "relation"];
+    var Types=["Node", "Way", "Relation"];
+
+    for(var t in types)
+      {
+       var Type=Types[t];
+       var type=types[t];
+
+       var regexp=RegExp(Type + " [0-9]+");
+
+       var match;
+
+       while((match=string.match(regexp)) !== null)
+         {
+          match=String(match);
+
+          var id=match.slice(1+type.length,match.length);
+
+          string=string.replace(regexp,Type + " <a href='" + mapprops.browseurl + "/" + type + "/" + id + "' target='" + type + id + "'>" + id + "</a>");
+         }
+      }
+   }
+
+ drawPopup(string.split("\n").join("<br>"));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Server handling ////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+//
+// Define an AJAX request object
+//
+
+function ajaxGET(url,success,failure,state)
+{
+ var ajaxRequest=new XMLHttpRequest();
+
+ function ajaxGOT(options) {
+  if(this.readyState==4)
+     if(this.status==200)
+       { if(typeof(options.success)=="function") options.success(this,options.state); }
+     else
+       { if(typeof(options.failure)=="function") options.failure(this,options.state); }
+ }
+
+ ajaxRequest.onreadystatechange = function(){ ajaxGOT.call(ajaxRequest,{success: success, failure: failure, state: state}); };
+ ajaxRequest.open("GET", url, true);
+ ajaxRequest.send(null);
+}
+
+
+//
+// Display the status
+//
+
+function displayStatus(type,subtype,content)
+{
+ var child=document.getElementById("result_status").firstChild;
+
+ do
+   {
+    if(child.id !== undefined)
+       child.style.display="none";
+
+    child=child.nextSibling;
+   }
+ while(child !== null);
+
+ var chosen_status=document.getElementById("result_status_" + type);
+
+ chosen_status.style.display="";
+
+ if(subtype !== undefined)
+   {
+    var format_status=document.getElementById("result_status_" + subtype).innerHTML;
+
+    chosen_status.innerHTML=format_status.replace("#",String(content));
+   }
+}
+
+
+//
+// Display data statistics
+//
+
+function displayStatistics()
+{
+ // Use AJAX to get the statistics
+
+ ajaxGET("statistics.cgi", runStatisticsSuccess);
+}
+
+
+//
+// Success in running data statistics generation.
+//
+
+function runStatisticsSuccess(response)
+{
+ document.getElementById("statistics_data").innerHTML="<pre>" + response.responseText + "</pre>";
+ document.getElementById("statistics_link").style.display="none";
+}
+
+
+//
+// Get the requested data
+//
+
+function displayData(datatype)  // called from visualiser.html
+{
+ for(var data in data_types)
+    hideshow_hide(data_types[data]);
+
+ if(datatype !== "")
+    hideshow_show(datatype);
+
+ // Delete the old data
+
+ unselectFeature();
+
+ select.deactivate();
+
+ layerVectors.destroyFeatures();
+ layerHighlights.destroyFeatures();
+
+ if(box !== null)
+    layerBoxes.removeMarker(box);
+ box=null;
+
+ // Print the status
+
+ displayStatus("no_data");
+
+ // Return if just here to clear the data
+
+ if(datatype === "")
+    return;
+
+ // Get the new data
+
+ var mapbounds=map.getExtent().clone();
+ mapbounds.transform(epsg900913,epsg4326);
+
+ var url="visualiser.cgi";
+
+ url=url + "?lonmin=" + format5f(mapbounds.left);
+ url=url + ";latmin=" + format5f(mapbounds.bottom);
+ url=url + ";lonmax=" + format5f(mapbounds.right);
+ url=url + ";latmax=" + format5f(mapbounds.top);
+ url=url + ";data=" + datatype;
+
+ // Use AJAX to get the data
+
+ switch(datatype)
+   {
+   case "junctions":
+    ajaxGET(url, runJunctionsSuccess, runFailure);
+    break;
+   case "super":
+    ajaxGET(url, runSuperSuccess, runFailure);
+    break;
+   case "oneway":
+    ajaxGET(url, runOnewaySuccess, runFailure);
+    break;
+   case "highway":
+    var highway;
+    var highways=document.forms["highways"].elements["highway"];
+    for(var h in highways)
+       if(highways[h].checked)
+          highway=highways[h].value;
+    url+="-" + highway;
+    ajaxGET(url, runHighwaySuccess, runFailure);
+    break;
+   case "transport":
+    var transport;
+    var transports=document.forms["transports"].elements["transport"];
+    for(var t in transports)
+       if(transports[t].checked)
+          transport=transports[t].value;
+    url+="-" + transport;
+    ajaxGET(url, runTransportSuccess, runFailure);
+    break;
+   case "barrier":
+    var transport;
+    var transports=document.forms["barriers"].elements["barrier"];
+    for(var t in transports)
+       if(transports[t].checked)
+          transport=transports[t].value;
+    url+="-" + transport;
+    ajaxGET(url, runBarrierSuccess, runFailure);
+    break;
+   case "turns":
+    ajaxGET(url, runTurnsSuccess, runFailure);
+    break;
+   case "speed":
+   case "weight":
+   case "height":
+   case "width":
+   case "length":
+    ajaxGET(url, runLimitSuccess, runFailure);
+    break;
+   case "property":
+    var property;
+    var properties=document.forms["properties"].elements["property"];
+    for(var p in properties)
+       if(properties[p].checked)
+          property=properties[p].value;
+    url+="-" + property;
+    ajaxGET(url, runPropertySuccess, runFailure);
+    break;
+   case "errorlogs":
+    ajaxGET(url, runErrorlogSuccess, runFailure);
+    break;
+   }
+}
+
+
+//
+// Success in getting the junctions.
+//
+
+function runJunctionsSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var junction_colours={
+                       0: "#FFFFFF",
+                       1: "#FF0000",
+                       2: "#FFFF00",
+                       3: "#00FF00",
+                       4: "#8B4513",
+                       5: "#00BFFF",
+                       6: "#FF69B4",
+                       7: "#000000",
+                       8: "#000000",
+                       9: "#000000"
+                      };
+
+ var styles={};
+
+ for(var colour in junction_colours)
+    styles[colour]=new OpenLayers.Style({},{stroke: false,
+                                            pointRadius: 2,fillColor: junction_colours[colour],
+                                            cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+       var count=words[3];
+
+       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
+
+       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
+
+       features.push(new OpenLayers.Feature.Vector(point,{dump: dump},styles[count]));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","junctions",lines.length-2);
+}
+
+
+//
+// Success in getting the super-node and super-segments
+//
+
+function runSuperSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var node_style = new OpenLayers.Style({},{stroke: false,
+                                           pointRadius: 4,fillColor: "#FF0000",
+                                           cursor: "pointer"});
+
+ var segment_style = new OpenLayers.Style({},{fill: false,
+                                              strokeWidth: 2,strokeColor: "#FF0000",
+                                              cursor: "pointer"});
+
+ var features=[];
+
+ var nodepoint;
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
+
+       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
+
+       if(dump.charAt(0) == "n")
+         {
+          nodepoint=point;
+
+          features.push(new OpenLayers.Feature.Vector(point,{dump: dump},node_style));
+         }
+       else
+         {
+          var segment = new OpenLayers.Geometry.LineString([nodepoint,point]);
+
+          features.push(new OpenLayers.Feature.Vector(segment,{dump: dump},segment_style));
+         }
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","super",lines.length-2);
+}
+
+
+//
+// Success in getting the oneway data
+//
+
+function runOnewaySuccess(response)
+{
+ var hex={0: "00", 1: "11",  2: "22",  3: "33",  4: "44",  5: "55",  6: "66",  7: "77",
+          8: "88", 9: "99", 10: "AA", 11: "BB", 12: "CC", 13: "DD", 14: "EE", 15: "FF"};
+
+ var lines=response.responseText.split("\n");
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1 = new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
+       var lonlat2 = new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
+
+     //var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
+       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
+
+       var dlat = lonlat2.lat-lonlat1.lat;
+       var dlon = lonlat2.lon-lonlat1.lon;
+       var dist = Math.sqrt(dlat*dlat+dlon*dlon)/10;
+       var ang  = Math.atan2(dlat,dlon);
+
+       var point3 = new OpenLayers.Geometry.Point(lonlat1.lon+dlat/dist,lonlat1.lat-dlon/dist);
+       var point4 = new OpenLayers.Geometry.Point(lonlat1.lon-dlat/dist,lonlat1.lat+dlon/dist);
+
+       var r=Math.round(7.5+7.9*Math.cos(ang));
+       var g=Math.round(7.5+7.9*Math.cos(ang+2.0943951));
+       var b=Math.round(7.5+7.9*Math.cos(ang-2.0943951));
+       var colour = "#" + hex[r] + hex[g] + hex[b];
+
+       var segment = new OpenLayers.Geometry.LineString([point2,point3,point4,point2]);
+
+       var style=new OpenLayers.Style({},{strokeWidth: 2,strokeColor: colour, cursor: "pointer"});
+
+       features.push(new OpenLayers.Feature.Vector(segment,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","oneway",lines.length-2);
+}
+
+
+//
+// Success in getting the highway data
+//
+
+function runHighwaySuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var style = new OpenLayers.Style({},{fill: false,
+                                      strokeWidth: 2,strokeColor: "#FF0000",
+                                      cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
+       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
+
+       var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
+       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
+
+       var segment = new OpenLayers.Geometry.LineString([point1,point2]);
+
+       features.push(new OpenLayers.Feature.Vector(segment,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","highway",lines.length-2);
+}
+
+
+//
+// Success in getting the transport data
+//
+
+function runTransportSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var style = new OpenLayers.Style({},{fill: false,
+                                      strokeWidth: 2,strokeColor: "#FF0000",
+                                      cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
+       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
+
+       var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
+       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
+
+       var segment = new OpenLayers.Geometry.LineString([point1,point2]);
+
+       features.push(new OpenLayers.Feature.Vector(segment,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","transport",lines.length-2);
+}
+
+
+//
+// Success in getting the barrier data
+//
+
+function runBarrierSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var style = new OpenLayers.Style({},{stroke: false,
+                                      pointRadius: 3,fillColor: "#FF0000",
+                                      cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
+
+       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
+
+       features.push(new OpenLayers.Feature.Vector(point,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","barrier",lines.length-2);
+}
+
+
+//
+// Success in getting the turn restrictions data
+//
+
+function runTurnsSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var style = new OpenLayers.Style({},{fill: false,
+                                      strokeWidth: 2,strokeColor: "#FF0000",
+                                      cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+       var lat3=words[5];
+       var lon3=words[6];
+
+       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
+       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
+       var lonlat3= new OpenLayers.LonLat(lon3,lat3).transform(epsg4326,epsg900913);
+
+       var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
+       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
+       var point3 = new OpenLayers.Geometry.Point(lonlat3.lon,lonlat3.lat);
+
+       var segments = new OpenLayers.Geometry.LineString([point1,point2,point3]);
+
+       features.push(new OpenLayers.Feature.Vector(segments,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","turns",lines.length-2);
+}
+
+
+//
+// Success in getting the speed/weight/height/width/length limits
+//
+
+function runLimitSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var node_style = new OpenLayers.Style({},{stroke: false,
+                                           pointRadius: 3,fillColor: "#FF0000",
+                                           cursor: "pointer"});
+
+ var segment_style = new OpenLayers.Style({},{fill: false,
+                                              strokeWidth: 2,strokeColor: "#FF0000",
+                                              cursor: "pointer"});
+
+ var features=[];
+
+ var nodepoint;
+ var nodelonlat;
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+       var number=words[3];
+
+       var lonlat= new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
+
+       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
+
+       if(number === undefined)
+         {
+          nodelonlat=lonlat;
+          nodepoint = point;
+
+          features.push(new OpenLayers.Feature.Vector(point,{dump: dump},node_style));
+         }
+       else
+         {
+          var segment = new OpenLayers.Geometry.LineString([nodepoint,point]);
+
+          features.push(new OpenLayers.Feature.Vector(segment,{dump: dump},segment_style));
+
+          var dlat = lonlat.lat-nodelonlat.lat;
+          var dlon = lonlat.lon-nodelonlat.lon;
+          var dist = Math.sqrt(dlat*dlat+dlon*dlon)/120;
+
+          point = new OpenLayers.Geometry.Point(nodelonlat.lon+dlon/dist,nodelonlat.lat+dlat/dist);
+
+          features.push(new OpenLayers.Feature.Vector(point,{dump: dump},
+                                                      new OpenLayers.Style({},{externalGraphic: "icons/limit-" + number + ".png",
+                                                                               graphicYOffset: -9,
+                                                                               graphicWidth: 19,
+                                                                               graphicHeight: 19})));
+         }
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","limit",lines.length-2);
+}
+
+
+//
+// Success in getting the property data
+//
+
+function runPropertySuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var style = new OpenLayers.Style({},{fill: false,
+                                      strokeWidth: 2,strokeColor: "#FF0000",
+                                      cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat1=words[1];
+       var lon1=words[2];
+       var lat2=words[3];
+       var lon2=words[4];
+
+       var lonlat1= new OpenLayers.LonLat(lon1,lat1).transform(epsg4326,epsg900913);
+       var lonlat2= new OpenLayers.LonLat(lon2,lat2).transform(epsg4326,epsg900913);
+
+       var point1 = new OpenLayers.Geometry.Point(lonlat1.lon,lonlat1.lat);
+       var point2 = new OpenLayers.Geometry.Point(lonlat2.lon,lonlat2.lat);
+
+       var segment = new OpenLayers.Geometry.LineString([point1,point2]);
+
+       features.push(new OpenLayers.Feature.Vector(segment,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","property",lines.length-2);
+}
+
+
+//
+// Success in getting the error log data
+//
+
+function runErrorlogSuccess(response)
+{
+ var lines=response.responseText.split("\n");
+
+ var style = new OpenLayers.Style({},{stroke: false,
+                                      pointRadius: 3,fillColor: "#FF0000",
+                                      cursor: "pointer"});
+
+ var features=[];
+
+ for(var line=0;line<lines.length;line++)
+   {
+    var words=lines[line].split(" ");
+
+    if(line === 0)
+      {
+       var lat1=words[0];
+       var lon1=words[1];
+       var lat2=words[2];
+       var lon2=words[3];
+
+       var bounds = new OpenLayers.Bounds(lon1,lat1,lon2,lat2).transform(epsg4326,epsg900913);
+
+       box = new OpenLayers.Marker.Box(bounds);
+
+       layerBoxes.addMarker(box);
+      }
+    else if(words[0] !== "")
+      {
+       var dump=words[0];
+       var lat=words[1];
+       var lon=words[2];
+
+       var lonlat = new OpenLayers.LonLat(lon,lat).transform(epsg4326,epsg900913);
+
+       var point = new OpenLayers.Geometry.Point(lonlat.lon,lonlat.lat);
+
+       features.push(new OpenLayers.Feature.Vector(point,{dump: dump},style));
+      }
+   }
+
+ select.activate();
+
+ layerVectors.addFeatures(features);
+
+ displayStatus("data","errorlogs",lines.length-2);
+}
+
+
+//
+// Failure in getting data.
+//
+
+function runFailure(response)
+{
+ displayStatus("failed");
+}
diff --git a/xml/Makefile b/xml/Makefile
index 410a3ac..841acfc 100644
--- a/xml/Makefile
+++ b/xml/Makefile
@@ -2,7 +2,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2010-2011 Andrew M. Bishop
+# This file Copyright 2010-2014 Andrew M. Bishop
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -18,10 +18,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# Web file paths
+# All configuration is in the top-level Makefile.conf
 
-WEBDATADIR=../web/data
-WEBWWWDIR=../web/www/routino
+include ../Makefile.conf
 
 # Files to install
 
@@ -33,30 +32,9 @@ SPECIAL_FILES=tagging-drive.xml \
 	      tagging-ride.xml \
               tagging-walk.xml
 
-PROFILE_FILES=$(WEBWWWDIR)/profiles.pl \
-	      $(WEBWWWDIR)/profiles.js
-
 ########
 
 all: $(SPECIAL_FILES)
-	- at for file in $(STANDARD_FILES); do \
-	     if [ ! -f $(WEBDATADIR)/$$file ] || [ routino-$$file -nt $(WEBDATADIR)/$$file ]; then \
-	        echo cp routino-$$file $(WEBDATADIR)/$$file ;\
-	        cp -f routino-$$file $(WEBDATADIR)/$$file ;\
-	     fi ;\
-	  done
-	- at for file in $(SPECIAL_FILES); do \
-	     if [ ! -f $(WEBDATADIR)/$$file ] || [ $$file -nt $(WEBDATADIR)/$$file ]; then \
-	        echo cp $$file $(WEBDATADIR)/$$file ;\
-	        cp -f $$file $(WEBDATADIR)/$$file ;\
-	     fi ;\
-	  done
-	- at if [ ! -f $(WEBWWWDIR)/profiles.js ] || [ ! -f $(WEBWWWDIR)/profiles.pl ] || \
-	     [ $(WEBDATADIR)/profiles.xml -nt $(WEBWWWDIR)/profiles.pl ] || \
-	     [ $(WEBDATADIR)/profiles.xml -nt $(WEBWWWDIR)/profiles.js ]; then \
-	     echo perl update-profiles.pl ;\
-	     ( cd $(WEBWWWDIR) ; perl update-profiles.pl ) ;\
-	  fi
 
 ####
 
@@ -76,15 +54,15 @@ test:
 ########
 
 install: all
-	-[ -d $(DESTDIR)$(datadir) ] || mkdir -p $(DESTDIR)$(datadir)
-	- at for file in $(STANDARD_FILES) ; do \
-	     echo cp routino-$$file $(DESTDIR)$(datadir)/$$file ;\
-	     cp -f routino-$$file $(DESTDIR)$(datadir)/$$file ;\
-	  done
-	- at for file in $(SPECIAL_FILES); do \
-	     echo cp $$file $(DESTDIR)$(datadir)/$$file ;\
-	     cp -f $$file $(DESTDIR)$(datadir)/$$file ;\
-	  done
+	@[ -d $(DESTDIR)$(datadir) ] || mkdir -p $(DESTDIR)$(datadir)
+	@for file in $(STANDARD_FILES) ; do \
+	    echo cp routino-$$file $(DESTDIR)$(datadir)/$$file ;\
+	    cp -f routino-$$file $(DESTDIR)$(datadir)/$$file ;\
+	 done
+	@for file in $(SPECIAL_FILES); do \
+	    echo cp $$file $(DESTDIR)$(datadir)/$$file ;\
+	    cp -f $$file $(DESTDIR)$(datadir)/$$file ;\
+	 done
 
 ########
 
@@ -94,11 +72,8 @@ clean:
 ########
 
 distclean: clean
-	rm -f $(WEBDATADIR)/*.xml
 	rm -f $(SPECIAL_FILES)
-	rm -f $(PROFILE_FILES)
 
 ########
 
-top=-top
-include ../Makefile
+.PHONY:: all test install clean distclean
diff --git a/xml/routino-profiles.xml b/xml/routino-profiles.xml
index 8415d66..4562bc9 100644
--- a/xml/routino-profiles.xml
+++ b/xml/routino-profiles.xml
@@ -5,7 +5,7 @@
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010-2011 Andrew M. Bishop
+     This file Copyright 2010-2014 Andrew M. Bishop
 
      This program is free software: you can redistribute it and/or modify
      it under the terms of the GNU Affero General Public License as published by
@@ -48,12 +48,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="50" />
-      <property type="multilane"    percent="25" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="55" />
-      <property type="bicycleroute" percent="55" />
+      <property type="paved"         percent="50" />
+      <property type="multilane"     percent="25" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="55" />
+      <property type="bicycleroute"  percent="55" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="0" /> 
@@ -97,12 +98,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="20" />
-      <property type="multilane"    percent="25" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="50" />
-      <property type="bicycleroute" percent="50" />
+      <property type="paved"         percent="20" />
+      <property type="multilane"     percent="25" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="50" />
+      <property type="bicycleroute"  percent="50" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
@@ -146,12 +148,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="90" />
-      <property type="multilane"    percent="25" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="55" />
-      <property type="bicycleroute" percent="55" />
+      <property type="paved"         percent="90" />
+      <property type="multilane"     percent="25" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="55" />
+      <property type="bicycleroute"  percent="55" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="0" /> 
@@ -195,12 +198,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="50" />
-      <property type="multilane"    percent="25" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="50" />
-      <property type="bicycleroute" percent="60" />
+      <property type="paved"         percent="50" />
+      <property type="multilane"     percent="25" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="50" />
+      <property type="bicycleroute"  percent="60" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
@@ -244,12 +248,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="100" />
-      <property type="multilane"    percent="35" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="50" />
-      <property type="bicycleroute" percent="50" />
+      <property type="paved"         percent="100" />
+      <property type="multilane"     percent="35" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="50" />
+      <property type="bicycleroute"  percent="50" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
@@ -261,7 +266,7 @@
     </restrictions>
   </profile>
 
-  <profile name="motorbike" transport="motorbike">
+  <profile name="motorcycle" transport="motorcycle">
     <speeds>
       <speed highway="motorway"      kph="112" />
       <speed highway="trunk"         kph="96" />
@@ -293,12 +298,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="100" />
-      <property type="multilane"    percent="60" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="50" />
-      <property type="bicycleroute" percent="50" />
+      <property type="paved"         percent="100" />
+      <property type="multilane"     percent="60" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="50" />
+      <property type="bicycleroute"  percent="50" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
@@ -342,12 +348,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="100" />
-      <property type="multilane"    percent="60" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="45" />
-      <property type="bicycleroute" percent="45" />
+      <property type="paved"         percent="100" />
+      <property type="multilane"     percent="60" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="45" />
+      <property type="bicycleroute"  percent="45" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
@@ -391,12 +398,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="100" />
-      <property type="multilane"    percent="60" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="45" />
-      <property type="bicycleroute" percent="45" />
+      <property type="paved"         percent="100" />
+      <property type="multilane"     percent="60" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="45" />
+      <property type="bicycleroute"  percent="45" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
@@ -440,12 +448,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="100" />
-      <property type="multilane"    percent="60" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="45" />
-      <property type="bicycleroute" percent="45" />
+      <property type="paved"         percent="100" />
+      <property type="multilane"     percent="60" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="45" />
+      <property type="bicycleroute"  percent="45" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
@@ -489,12 +498,13 @@
       <preference highway="ferry"         percent="20" />
     </preferences>
     <properties>
-      <property type="paved"        percent="100" />
-      <property type="multilane"    percent="60" />
-      <property type="bridge"       percent="50" />
-      <property type="tunnel"       percent="50" />
-      <property type="footroute"    percent="45" />
-      <property type="bicycleroute" percent="45" />
+      <property type="paved"         percent="100" />
+      <property type="multilane"     percent="60" />
+      <property type="bridge"        percent="50" />
+      <property type="tunnel"        percent="50" />
+      <property type="footroute"     percent="45" />
+      <property type="bicycleroute"  percent="45" />
+      <property type="cyclebothways" percent="50" />
     </properties>
     <restrictions>
       <oneway obey="1" /> 
diff --git a/xml/routino-tagging.xml b/xml/routino-tagging.xml
index 042c7fc..96153a5 100644
--- a/xml/routino-tagging.xml
+++ b/xml/routino-tagging.xml
@@ -5,7 +5,7 @@
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010-2012 Andrew M. Bishop
+     This file Copyright 2010-2014 Andrew M. Bishop
 
      This program is free software: you can redistribute it and/or modify
      it under the terms of the GNU Affero General Public License as published by
@@ -23,217 +23,256 @@
     <!-- Note: The default is that all transport types are allowed past a barrier;
                access must be specified to disallow each transport type. -->
 
-    <!-- Not useful barrier types (too generic) -->
+    <if k="barrier">
 
-    <if k="barrier" v="gate">     <unset/></if>
-    <if k="barrier" v="entrance"> <unset/></if>
-    <if k="barrier" v="lift_gate"><unset/></if>
+      <!-- Not useful barrier types (too generic) -->
 
-    <!-- Barrier types -->
+      <if k="barrier" v="gate"      > <unset/> </if>
+      <if k="barrier" v="lift_gate" > <unset/> </if>
+      <if k="barrier" v="block"     > <unset/> </if>
 
-    <if k="barrier" v="kissing_gate">
-      <set v="foot_only"/>
-    </if>
+      <!-- Not useful barrier types (generic entrances through barrier ways) -->
 
-    <if k="barrier" v="footgate">
-      <set v="foot_only"/>
-    </if>
+      <if k="barrier" v="entrance"  > <unset/> </if>
+      <if k="barrier" v="sally_port"> <unset/> </if>
+      <if k="barrier" v="toll_booth"> <unset/> </if>
+
+      <!-- Barrier types -->
+
+      <if k="barrier" v="kissing_gate">
+        <set v="foot_only"/>
+      </if>
 
-    <if k="barrier" v="stile">
-      <set v="foot_only"/>
-    </if>
+      <if k="barrier" v="footgate">
+        <set v="foot_only"/>
+      </if>
 
-    <if k="barrier" v="v_stile">
-      <set v="foot_only"/>
-    </if>
-
-    <if k="barrier" v="turnstile">
-      <set v="foot_only"/>
-    </if>
-
-    <if k="barrier" v="squeeze">
-      <set v="foot_only"/>
-    </if>
-
-    <if k="barrier" v="squeeze_stile">
-      <set v="foot_only"/>
-    </if>
-
-    <if k="barrier" v="cycle_barrier">
-      <set v="foot_only"/>
-    </if>
+      <if k="barrier" v="stile">
+        <set v="foot_only"/>
+      </if>
 
-    <if k="barrier" v="bicycle_barrier">
-      <set v="foot_only"/>
-    </if>
+      <if k="barrier" v="v_stile">
+        <set v="foot_only"/>
+      </if>
 
-    <if k="barrier" v="foot_only">
-      <output k="horse"      v="no"/>
-      <output k="wheelchair" v="no"/>
-      <output k="bicycle"    v="no"/>
-      <output k="moped"      v="no"/>
-      <output k="motorbike"  v="no"/>
-      <output k="motorcar"   v="no"/>
-      <output k="goods"      v="no"/>
-      <output k="hgv"        v="no"/>
-      <output k="psv"        v="no"/>
-
-      <unset k="barrier"/>
-    </if>
+      <if k="barrier" v="turnstile">
+        <set v="foot_only"/>
+      </if>
+
+      <if k="barrier" v="squeeze">
+        <set v="foot_only"/>
+      </if>
+
+      <if k="barrier" v="squeeze_stile">
+        <set v="foot_only"/>
+      </if>
+
+      <if k="barrier" v="cycle_barrier">
+        <set v="foot_only"/>
+      </if>
 
-    <if k="barrier" v="horse_stile">
-      <set v="not_wheeled"/>
-    </if>
+      <if k="barrier" v="bicycle_barrier">
+        <set v="foot_only"/>
+      </if>
 
-    <if k="barrier" v="horse_jump">
-      <set v="not_wheeled"/>
-    </if>
+      <if k="barrier" v="foot_only">
+        <output k="horse"      v="no"/>
+        <output k="wheelchair" v="no"/>
+        <output k="bicycle"    v="no"/>
+        <output k="moped"      v="no"/>
+        <output k="motorcycle" v="no"/>
+        <output k="motorcar"   v="no"/>
+        <output k="goods"      v="no"/>
+        <output k="hgv"        v="no"/>
+        <output k="psv"        v="no"/>
 
-    <if k="barrier" v="step_over">
-      <set v="not_wheeled"/>
-    </if>
+        <unset k="barrier"/>
+      </if>
 
-    <if k="barrier" v="not_wheeled">
-      <output k="wheelchair" v="no"/>
-      <output k="bicycle"    v="no"/>
-      <output k="moped"      v="no"/>
-      <output k="motorbike"  v="no"/>
-      <output k="motorcar"   v="no"/>
-      <output k="goods"      v="no"/>
-      <output k="hgv"        v="no"/>
-      <output k="psv"        v="no"/>
-
-      <unset k="barrier"/>
-    </if>
+      <if k="barrier" v="horse_stile">
+        <set v="not_wheeled"/>
+      </if>
 
-    <if k="barrier" v="horse_barrier">
-      <set v="no_horse"/>
-    </if>
+      <if k="barrier" v="horse_jump">
+        <set v="not_wheeled"/>
+      </if>
 
-    <if k="barrier" v="cattle_grid">
-      <set v="no_horse"/>
-    </if>
+      <if k="barrier" v="step_over">
+        <set v="not_wheeled"/>
+      </if>
 
-    <if k="barrier" v="no_horse">
-      <output k="horse"      v="no"/>
+      <if k="barrier" v="not_wheeled">
+        <output k="wheelchair" v="no"/>
+        <output k="bicycle"    v="no"/>
+        <output k="moped"      v="no"/>
+        <output k="motorcycle" v="no"/>
+        <output k="motorcar"   v="no"/>
+        <output k="goods"      v="no"/>
+        <output k="hgv"        v="no"/>
+        <output k="psv"        v="no"/>
 
-      <unset k="barrier"/>
-    </if>
+        <unset k="barrier"/>
+      </if>
 
-    <if k="barrier" v="motorcyle_barrier">
-      <set v="no_motorised"/>
-    </if>
+      <if k="barrier" v="horse_barrier">
+        <set v="no_horse"/>
+      </if>
 
-    <if k="barrier" v="no_motorised">
-      <output k="moped"      v="no"/>
-      <output k="motorbike"  v="no"/>
-      <output k="motorcar"   v="no"/>
-      <output k="goods"      v="no"/>
-      <output k="hgv"        v="no"/>
-      <output k="psv"        v="no"/>
+      <if k="barrier" v="cattle_grid">
+        <set v="no_horse"/>
+      </if>
 
-      <unset k="barrier"/>
-    </if>
+      <if k="barrier" v="no_horse">
+        <output k="horse"      v="no"/>
 
-    <if k="barrier" v="bollard">
-      <set v="not_2plus_wheels"/>
-    </if>
+        <unset k="barrier"/>
+      </if>
+
+      <if k="barrier" v="motorcycle_barrier">
+        <set v="no_motorised"/>
+      </if>
+
+      <if k="barrier" v="no_motorised">
+        <output k="moped"      v="no"/>
+        <output k="motorcycle" v="no"/>
+        <output k="motorcar"   v="no"/>
+        <output k="goods"      v="no"/>
+        <output k="hgv"        v="no"/>
+        <output k="psv"        v="no"/>
+
+        <unset k="barrier"/>
+      </if>
+
+      <if k="barrier" v="bollard">
+        <set v="not_2plus_wheels"/>
+      </if>
+
+      <if k="barrier" v="car_barrier">
+        <set v="not_2plus_wheels"/>
+      </if>
+
+      <if k="barrier" v="car_trap">
+        <set v="not_2plus_wheels"/>
+      </if>
+
+      <if k="barrier" v="not_2plus_wheels">
+        <output k="motorcar"   v="no"/>
+        <output k="goods"      v="no"/>
+        <output k="hgv"        v="no"/>
+        <output k="psv"        v="no"/>
+
+        <unset k="barrier"/>
+      </if>
+
+      <if k="barrier">
+        <logerror message="ignoring it"/>
+      </if>
+
+    </if> <!-- k="barrier" -->
 
-    <if k="barrier" v="car_barrier">
-      <set v="not_2plus_wheels"/>
-    </if>
+    <!-- Normalisation of access tags -->
 
-    <if k="barrier" v="car_trap">
-      <set v="not_2plus_wheels"/>
-    </if>
+    <if v="designated"  > <set v="yes"/> </if>
+    <if v="permissive"  > <set v="yes"/> </if>
+    <if v="destination" > <set v="yes"/> </if>
+    <if v="true"        > <set v="yes"/> </if>
+    <if v="public"      > <set v="yes"/> </if>
+    <if v="official"    > <set v="yes"/> </if>
+                           
+    <if v="unsuitable"  > <set v="no"/> </if>
+    <if v="private"     > <set v="no"/> </if>
+    <if v="limited"     > <set v="no"/> </if>
+    <if v="restricted"  > <set v="no"/> </if>
+    <if v="agricultural"> <set v="no"/> </if>
+    <if v="forestry"    > <set v="no"/> </if>
 
-    <if k="barrier" v="not_2plus_wheels">
-      <output k="motorcar"   v="no"/>
-      <output k="goods"      v="no"/>
-      <output k="hgv"        v="no"/>
-      <output k="psv"        v="no"/>
+    <!-- Generic access permissions for all transport types (to override defaults) -->
 
-      <unset k="barrier"/>
-    </if>
+    <if k="access">
 
-    <if k="barrier">
-      <logerror/>
-    </if>
+      <ifnot k="access" v="yes">
+        <output k="foot"       v="no"/>
+        <output k="horse"      v="no"/>
+        <output k="wheelchair" v="no"/>
+        <output k="bicycle"    v="no"/>
+        <output k="moped"      v="no"/>
+        <output k="motorcycle" v="no"/>
+        <output k="motorcar"   v="no"/>
+        <output k="goods"      v="no"/>
+        <output k="hgv"        v="no"/>
+        <output k="psv"        v="no"/>
+
+        <if k="access" v="foot">
+          <set k="foot" v="yes"/>
+          <logerror k="access" message="using 'access' = 'no' ; 'foot' = 'yes'"/>
+          <set k="access" v="no"/>
+        </if>
+
+        <ifnot k="access" v="no">
+          <logerror k="access" message="using 'no'"/>
+        </ifnot>
+      </ifnot>
+
+    </if> <!-- k="access" -->
 
-    <!-- Normalisation of access tags -->
+    <!-- Generic access permissions for classes of transport types -->
 
-    <if v="designated" ><set v="yes"/></if>
-    <if v="permissive" ><set v="yes"/></if>
-    <if v="destination"><set v="yes"/></if>
-    <if v="true"       ><set v="yes"/></if>
-    <if v="public"     ><set v="yes"/></if>
-    <if v="official"   ><set v="yes"/></if>
+    <if k="vehicle">
 
-    <if v="unsuitable" ><set v="no"/></if>
-    <if v="private"    ><set v="no"/></if>
-    <if v="limited"    ><set v="no"/></if>
+      <ifnot k="vehicle" v="yes">
+        <output k="bicycle"    v="no"/>
+        <output k="moped"      v="no"/>
+        <output k="motorcycle" v="no"/>
+        <output k="motorcar"   v="no"/>
+        <output k="goods"      v="no"/>
+        <output k="hgv"        v="no"/>
+        <output k="psv"        v="no"/>
 
-    <!-- Generic access permissions for all transport types (to override defaults) -->
+        <ifnot k="vehicle" v="no">
+          <logerror k="vehicle" message="using 'no'"/>
+        </ifnot>
+      </ifnot>
 
-    <if k="access">
-      <set k="noaccess" v="yes"/>
-    </if>
+    </if> <!-- k="vehicle" -->
 
-    <if k="access" v="yes">
-      <set k="noaccess" v="no"/>
-    </if>
-
-    <if k="noaccess" v="yes">
-      <output k="foot"       v="no"/>
-      <output k="horse"      v="no"/>
-      <output k="wheelchair" v="no"/>
-      <output k="bicycle"    v="no"/>
-      <output k="moped"      v="no"/>
-      <output k="motorbike"  v="no"/>
-      <output k="motorcar"   v="no"/>
-      <output k="goods"      v="no"/>
-      <output k="hgv"        v="no"/>
-      <output k="psv"        v="no"/>
-    </if>
+    <if k="motor_vehicle">
 
-    <!-- Generic access permissions for classes of transport types -->
+      <ifnot k="motor_vehicle" v="yes">
+        <output k="moped"      v="no"/>
+        <output k="motorcycle" v="no"/>
+        <output k="motorcar"   v="no"/>
+        <output k="goods"      v="no"/>
+        <output k="hgv"        v="no"/>
+        <output k="psv"        v="no"/>
 
-    <if k="motor_vehicle">
-      <output k="moped"/>
-      <output k="motorbike"/>
-      <output k="motorcar"/>
-      <output k="goods"/>
-      <output k="hgv"/>
-      <output k="psv"/>
-    </if>
+        <ifnot k="motor_vehicle" v="no">
+          <logerror k="motor_vehicle" message="using 'no'"/>
+        </ifnot>
+      </ifnot>
 
-    <if k="vehicle">
-      <output k="bicycle"/>
-      <output k="moped"/>
-      <output k="motorbike"/>
-      <output k="motorcar"/>
-      <output k="goods"/>
-      <output k="hgv"/>
-      <output k="psv"/>
-    </if>
+    </if> <!-- k="motor_vehicle" -->
 
     <!-- Specific access rules (to override the generic ones) -->
 
-    <if k="foot"      ><output/></if>
-    <if k="horse"     ><output/></if>
-    <if k="wheelchair"><output/></if>
-    <if k="bicycle"   ><output/></if>
-    <if k="moped"     ><output/></if>
-    <if k="motorbike" ><output/></if>
-    <if k="motorcar"  ><output/></if>
-    <if k="goods"     ><output/></if>
-    <if k="hgv"       ><output/></if>
-    <if k="psv"       ><output/></if>
+    <if k="foot"      > <output/> </if>
+    <if k="horse"     > <output/> </if>
+    <if k="wheelchair"> <output/> </if>
+    <if k="bicycle"   > <output/> </if>
+    <if k="moped"     > <output/> </if>
+    <if k="motorcycle"> <output/> </if>
+    <if k="motorcar"  > <output/> </if>
+    <if k="goods"     > <output/> </if>
+    <if k="hgv"       > <output/> </if>
+    <if k="psv"       > <output/> </if>
 
     <!-- Mini-roundabouts  -->
 
     <if k="highway" v="mini_roundabout">
-      <output/>
+      <output k="roundabout" v="yes"/>
+    </if>
+
+    <if k="junction" v="roundabout">
+      <output k="roundabout" v="yes"/>
     </if>
 
   </node>
@@ -245,587 +284,691 @@
     <!-- Note: The default is that no transport type is allowed on any highway;
                access must be specified to allow each transport type. -->
 
-    <!-- Not useful highway types -->
-
-    <if k="highway" v="proposed">
-      <unset/>
-    </if>
-
-    <if k="highway" v="construction">
-      <unset/>
-    </if>
-
-    <if k="highway" v="abandoned">
-      <unset/>
-    </if>
-
-    <if k="highway" v="raceway">
-      <unset/>
-    </if>
-
-    <!-- Mark ways that are not highways to suppress error logging on non-highways -->
-
-    <if>
-      <set k="not_highway" v="yes"/>
+    <if k="route" v="ferry">
+      <set k="highway" v="ferry"/>
     </if>
 
     <if k="highway">
-      <unset k="not_highway"/>
-    </if>
 
-    <!-- Highway types (includes default access and default properties) -->
+      <!-- Not useful highway types (future highways) -->
 
-    <if k="highway" v="motorway_link">
-      <set v="motorway"/>
-    </if>
+      <if k="highway" v="construction"> <unset/> </if>
+      <if k="highway" v="planned"     > <unset/> </if>
+      <if k="highway" v="proposed"    > <unset/> </if>
 
-    <if k="highway" v="motorway">
-      <output k="highway"/>
+      <!-- Not useful highway types (previous highways) -->
 
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+      <if k="highway" v="no"          > <unset/> </if>
+      <if k="highway" v="abandoned"   > <unset/> </if>
+      <if k="highway" v="disused"     > <unset/> </if>
 
-      <output k="paved"      v="yes"/>
-      <output k="multilane"  v="yes"/>
-      <output k="oneway"     v="yes"/>
+      <!-- Not useful highway types (limited use highways) -->
 
-      <unset k="highway"/>
-    </if>
+      <if k="highway" v="raceway"     > <unset/> </if>
 
-    <if k="highway" v="trunk_link">
-      <set v="trunk"/>
-    </if>
+      <!-- Highway types (includes default access and default properties) -->
 
-    <if k="highway" v="trunk">
-      <output k="highway"/>
+      <if k="highway" v="motorway_link">
+        <set v="motorway"/>
+      </if>
 
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+      <if k="highway" v="motorway">
+        <output k="highway"/>
 
-      <output k="paved"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
 
-      <unset k="highway"/>
-    </if>
+        <output k="paved"      v="yes"/>
+        <output k="multilane"  v="yes"/>
+        <output k="oneway"     v="yes"/>
 
-    <if k="highway" v="primary_link">
-      <set v="primary"/>
-    </if>
+        <unset k="highway"/>
+      </if>
 
-    <if k="highway" v="primary">
-      <output k="highway"/>
+      <if k="highway" v="trunk_link">
+        <set v="trunk"/>
+      </if>
 
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="no"/>
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+      <if k="highway" v="trunk">
+        <output k="highway"/>
 
-      <output k="paved"      v="yes"/>
+        <output k="bicycle"    v="yes"/>
+        <output k="moped"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
 
-      <unset k="highway"/>
-    </if>
+        <output k="paved"      v="yes"/>
 
-    <if k="highway" v="secondary_link">
-      <set v="secondary"/>
-    </if>
+        <unset k="highway"/>
+      </if>
 
-    <if k="highway" v="secondary">
-      <output k="highway"/>
+      <if k="highway" v="primary_link">
+        <set v="primary"/>
+      </if>
 
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+      <if k="highway" v="primary">
+        <output k="highway"/>
 
-      <output k="paved"      v="yes"/>
+        <output k="foot"       v="yes"/>
+        <output k="horse"      v="yes"/>
+        <output k="wheelchair" v="no"/>
+        <output k="bicycle"    v="yes"/>
+        <output k="moped"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
+
+        <output k="paved"      v="yes"/>
 
-      <unset k="highway"/>
-    </if>
+        <unset k="highway"/>
+      </if>
 
-    <if k="highway" v="tertiary_link">
-      <set v="tertiary"/>
-    </if>
+      <if k="highway" v="secondary_link">
+        <set v="secondary"/>
+      </if>
 
-    <if k="highway" v="tertiary">
-      <output k="highway"/>
+      <if k="highway" v="secondary">
+        <output k="highway"/>
+
+        <output k="foot"       v="yes"/>
+        <output k="horse"      v="yes"/>
+        <output k="wheelchair" v="yes"/>
+        <output k="bicycle"    v="yes"/>
+        <output k="moped"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
+
+        <output k="paved"      v="yes"/>
+
+        <unset k="highway"/>
+      </if>
+
+      <if k="highway" v="tertiary_link">
+        <set v="tertiary"/>
+      </if>
+
+      <if k="highway" v="tertiary">
+        <output k="highway"/>
+
+        <output k="foot"       v="yes"/>
+        <output k="horse"      v="yes"/>
+        <output k="wheelchair" v="yes"/>
+        <output k="bicycle"    v="yes"/>
+        <output k="moped"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
+
+        <output k="paved"      v="yes"/>
+
+        <unset k="highway"/>
+      </if>
+
+      <if k="highway" v="minor">
+        <set k="highway" v="unclassified"/>
+      </if>
+
+      <if k="highway" v="road">
+        <set k="highway" v="unclassified"/>
+      </if>
+
+      <if k="highway" v="unclassified">
+        <output k="highway"/>
+
+        <output k="foot"       v="yes"/>
+        <output k="horse"      v="yes"/>
+        <output k="wheelchair" v="yes"/>
+        <output k="bicycle"    v="yes"/>
+        <output k="moped"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
+
+        <output k="paved"      v="yes"/>
+
+        <unset k="highway"/>
+      </if>
+
+      <if k="highway" v="living_street">
+        <set k="highway" v="residential"/>
+      </if>
+
+      <if k="highway" v="residential">
+        <output k="highway"/>
+
+        <output k="foot"       v="yes"/>
+        <output k="horse"      v="yes"/>
+        <output k="wheelchair" v="yes"/>
+        <output k="bicycle"    v="yes"/>
+        <output k="moped"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
+
+        <output k="paved"      v="yes"/>
 
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+        <unset k="highway"/>
+      </if>
 
-      <output k="paved"      v="yes"/>
+      <if k="highway" v="access">
+        <set k="highway" v="service"/>
+      </if>
 
-      <unset k="highway"/>
-    </if>
+      <if k="highway" v="services">
+        <set k="highway" v="service"/>
+      </if>
 
-    <if k="highway" v="minor">
-      <set k="highway" v="unclassified"/>
-    </if>
+      <if k="highway" v="rest_area">
+        <set k="highway" v="service"/>
+      </if>
 
-    <if k="highway" v="road">
-      <set k="highway" v="unclassified"/>
-    </if>
+      <if k="highway" v="layby">
+        <set k="highway" v="service"/>
+      </if>
 
-    <if k="highway" v="unclassified">
-      <output k="highway"/>
+      <if k="highway" v="service">
+        <output k="highway"/>
 
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+        <output k="foot"       v="yes"/>
+        <output k="horse"      v="yes"/>
+        <output k="wheelchair" v="yes"/>
+        <output k="bicycle"    v="yes"/>
+        <output k="moped"      v="yes"/>
+        <output k="motorcycle" v="yes"/>
+        <output k="motorcar"   v="yes"/>
+        <output k="goods"      v="yes"/>
+        <output k="hgv"        v="yes"/>
+        <output k="psv"        v="yes"/>
 
-      <output k="paved"      v="yes"/>
+        <output k="paved"      v="yes"/>
 
-      <unset k="highway"/>
-    </if>
+        <unset k="highway"/>
+      </if>
 
-    <if k="highway" v="living_street">
-      <set k="highway" v="residential"/>
-    </if>
+      <if k="highway" v="byway">
+        <set k="highway" v="track"/>
+      </if>
 
-    <if k="highway" v="residential">
-      <output k="highway"/>
+      <if k="highway" v="unsurfaced">
+        <set k="highway" v="track"/>
+      </if>
 
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+      <if k="highway" v="unpaved">
+        <set k="highway" v="track"/>
+      </if>
 
-      <output k="paved"      v="yes"/>
+      <if k="highway" v="track">
+        <output k="highway"/>
 
-      <unset k="highway"/>
-    </if>
+        <output k="foot"       v="yes"/>
+        <output k="horse"      v="yes"/>
+        <output k="bicycle"    v="yes"/>
 
-    <if k="highway" v="access">
-      <set k="highway" v="service"/>
-    </if>
+        <unset k="highway"/>
+      </if>
 
-    <if k="highway" v="services">
-      <set k="highway" v="service"/>
-    </if>
+      <if k="tracktype" v="grade1">
+        <output k="paved"      v="yes"/>
+      </if>
 
-    <if k="highway" v="layby">
-      <set k="highway" v="service"/>
-    </if>
+      <if k="highway" v="cycleway">
+        <output k="highway"/>
 
-    <if k="highway" v="service">
-      <output k="highway"/>
+        <output k="foot"       v="yes"/>
+        <output k="wheelchair" v="yes"/>
+        <output k="bicycle"    v="yes"/>
 
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-      <output k="goods"      v="yes"/>
-      <output k="hgv"        v="yes"/>
-      <output k="psv"        v="yes"/>
+        <output k="paved"      v="yes"/>
 
-      <output k="paved"      v="yes"/>
+        <unset k="highway"/>
+      </if>
 
-      <unset k="highway"/>
-    </if>
+      <if k="highway" v="footway">
+        <set k="highway" v="path"/>
+      </if>
 
-    <if k="highway" v="byway">
-      <set k="highway" v="track"/>
-    </if>
+      <if k="highway" v="bridleway">
+        <set k="highway" v="path"/>
 
-    <if k="highway" v="unsurfaced">
-      <set k="highway" v="track"/>
-    </if>
+        <output k="horse"      v="yes"/>
+        <output k="bicycle"    v="yes"/>
+      </if>
 
-    <if k="highway" v="unpaved">
-      <set k="highway" v="track"/>
-    </if>
+      <if k="highway" v="pedestrian">
+        <set k="highway" v="path"/>
+
+        <output k="paved"      v="yes"/>
+      </if>
 
-    <if k="highway" v="track">
-      <output k="highway"/>
+      <if k="highway" v="walkway">
+        <set k="highway"  v="path"/>
 
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="bicycle"    v="yes"/>
+        <output k="paved"      v="yes"/>
+      </if>
 
-      <unset k="highway"/>
-    </if>
+      <if k="highway" v="trail">
+        <set k="highway"  v="path"/>
 
-    <if k="tracktype" v="grade1">
-      <output k="paved"      v="yes"/>
-    </if>
+        <output k="paved"      v="no"/>
+      </if>
+
+      <if k="highway" v="path">
+        <output k="highway"/>
+
+        <output k="foot"       v="yes"/>
+        <output k="wheelchair" v="yes"/>
 
-    <if k="highway" v="cycleway">
-      <output k="highway"/>
+        <unset k="highway"/>
+      </if>
 
-      <output k="foot"       v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
+      <if k="highway" v="steps">
+        <output k="highway"/>
 
-      <output k="paved"      v="yes"/>
+        <output k="foot"       v="yes"/>
 
-      <unset k="highway"/>
-    </if>
-
-    <if k="highway" v="footway">
-      <set k="highway" v="path"/>
-    </if>
+        <unset k="highway"/>
+      </if>
 
-    <if k="highway" v="bridleway">
-      <set k="highway" v="path"/>
+      <if k="highway" v="ferry">
+        <output k="highway"/>
+
+        <unset k="highway"/>
+      </if>
 
-      <output k="horse"      v="yes"/>
-      <output k="bicycle"    v="yes"/>
-    </if>
+      <!-- Unrecognised highway type -->
 
-    <if k="highway" v="pedestrian">
-      <set k="highway" v="path"/>
+      <if k="highway">
+        <logerror message="ignoring it"/>
+      </if>
 
-      <output k="paved"      v="yes"/>
-    </if>
+      <!-- Normalisation of access tags -->
 
-    <if k="highway" v="walkway">
-      <set k="highway"  v="path"/>
+      <if v="designated"  > <set v="yes"/> </if>
+      <if v="permissive"  > <set v="yes"/> </if>
+      <if v="destination" > <set v="yes"/> </if>
+      <if v="true"        > <set v="yes"/> </if>
+      <if v="public"      > <set v="yes"/> </if>
+      <if v="customers"   > <set v="yes"/> </if>
+      <if v="customer"    > <set v="yes"/> </if>
+      <if v="official"    > <set v="yes"/> </if>
+                           
+      <if v="unsuitable"  > <set v="no"/> </if>
+      <if v="private"     > <set v="no"/> </if>
+      <if v="limited"     > <set v="no"/> </if>
+      <if v="restricted"  > <set v="no"/> </if>
+      <if v="agricultural"> <set v="no"/> </if>
+      <if v="forestry"    > <set v="no"/> </if>
 
-      <output k="paved"      v="yes"/>
-    </if>
+      <!-- Generic access restrictions for all transport types (to subtract from highway specific defaults) -->
 
-    <if k="highway" v="path">
-      <output k="highway"/>
+      <if k="access">
 
-      <output k="foot"       v="yes"/>
-      <output k="wheelchair" v="yes"/>
+        <ifnot k="access" v="yes">
+          <output k="foot"       v="no"/>
+          <output k="horse"      v="no"/>
+          <output k="wheelchair" v="no"/>
+          <output k="bicycle"    v="no"/>
+          <output k="moped"      v="no"/>
+          <output k="motorcycle" v="no"/>
+          <output k="motorcar"   v="no"/>
+          <output k="goods"      v="no"/>
+          <output k="hgv"        v="no"/>
+          <output k="psv"        v="no"/>
 
-      <unset k="highway"/>
-    </if>
+          <if k="access" v="foot">
+            <set k="foot" v="yes"/>
+            <logerror k="access" message="using 'access' = 'no' ; 'foot' = 'yes'"/>
+            <set k="access" v="no"/>
+          </if>
+
+          <if k="access" v="wheelchair">
+            <set k="wheelchair" v="yes"/>
+            <logerror k="access" message="using 'access' = 'no' ; 'wheelchair' = 'yes'"/>
+            <set k="access" v="no"/>
+          </if>
+
+          <if k="access" v="motor_vehicle">
+            <set k="motor_vehicle" v="yes"/>
+            <logerror k="access" message="using 'access' = 'no' ; 'motor_vehicle' = 'yes'"/>
+            <set k="access" v="no"/>
+          </if>
+
+          <if k="access" v="hgv">
+            <set k="hgv" v="yes"/>
+            <logerror k="access" message="using 'access' = 'no' ; 'hgv' = 'yes'"/>
+            <set k="access" v="no"/>
+          </if>
+
+          <if k="access" v="bus">
+            <set v="psv" />
+          </if>
+
+          <if k="access" v="psv">
+            <set k="psv" v="yes"/>
+            <logerror k="access" message="using 'access' = 'no' ; 'psv' = 'yes'"/>
+            <set k="access" v="no"/>
+          </if>
+
+          <ifnot k="access" v="no">
+            <logerror k="access" message="using 'no'"/>
+          </ifnot>
+        </ifnot>
+
+      </if> <!-- k="access" -->
+
+      <!-- Access restrictions for classes of transport types (to subtract from highway specific defaults) -->
+
+      <if k="vehicle">
+
+        <ifnot k="vehicle" v="yes">
+          <output k="bicycle"    v="no"/>
+          <output k="moped"      v="no"/>
+          <output k="motorcycle" v="no"/>
+          <output k="motorcar"   v="no"/>
+          <output k="goods"      v="no"/>
+          <output k="hgv"        v="no"/>
+          <output k="psv"        v="no"/>
+
+          <ifnot k="vehicle" v="no">
+            <logerror k="vehicle" message="using 'no'"/>
+          </ifnot>
+        </ifnot>
+
+      </if> <!-- k="vehicle" -->
+
+      <if k="motor_vehicle">
+
+        <ifnot k="motor_vehicle" v="yes">
+          <output k="moped"      v="no"/>
+          <output k="motorcycle" v="no"/>
+          <output k="motorcar"   v="no"/>
+          <output k="goods"      v="no"/>
+          <output k="hgv"        v="no"/>
+          <output k="psv"        v="no"/>
+
+          <ifnot k="motor_vehicle" v="no">
+            <logerror k="motor_vehicle" message="using 'no'"/>
+          </ifnot>
+        </ifnot>
+
+      </if> <!-- k="motor_vehicle" -->
+
+      <!-- Other access permissions (to add to highway specific defaults with generic restrictions) -->
+
+      <if k="designation">
+
+        <if k="designation" v="restricted_byway">
+          <output k="foot"       v="yes"/>
+          <output k="horse"      v="yes"/>
+          <output k="wheelchair" v="yes"/>
+          <output k="bicycle"    v="yes"/>
+
+          <unset k="designation"/>
+        </if>
+
+        <if k="designation" v="public_byway">
+          <set v="byway_open_to_all_traffic"/>
+        </if>
+
+        <if k="designation" v="byway">
+          <set v="byway_open_to_all_traffic"/>
+        </if>
+
+        <if k="designation" v="byway_open_to_all_traffic">
+          <output k="foot"       v="yes"/>
+          <output k="horse"      v="yes"/>
+          <output k="wheelchair" v="yes"/>
+          <output k="bicycle"    v="yes"/>
+          <output k="moped"      v="yes"/>
+          <output k="motorcycle" v="yes"/>
+          <output k="motorcar"   v="yes"/>
+
+          <unset k="designation"/>
+        </if>
+
+        <if k="designation" v="permissive_bridleway">
+          <set v="bridleway"/>
+        </if>
+
+        <if k="designation" v="public_bridleway">
+          <set v="bridleway"/>
+        </if>
+
+        <if k="designation" v="bridleway">
+          <output k="foot"       v="yes"/>
+          <output k="horse"      v="yes"/>
+          <output k="wheelchair" v="yes"/>
+          <output k="bicycle"    v="yes"/>
+
+          <unset k="designation"/>
+        </if>
+
+        <if k="designation" v="public_cycleway">
+          <output k="foot"       v="yes"/>
+          <output k="wheelchair" v="yes"/>
+          <output k="bicycle"    v="yes"/>
 
-    <if k="highway" v="steps">
-      <output k="highway"/>
+          <unset k="designation"/>
+        </if>
 
-      <output k="foot"       v="yes"/>
+        <if k="designation" v="permissive_footpath">
+          <set v="footpath"/>
+        </if>
 
-      <unset k="highway"/>
-    </if>
+        <if k="designation" v="public_footpath">
+          <set v="footpath"/>
+        </if>
+
+        <if k="designation" v="footpath">
+          <output k="foot"       v="yes"/>
+          <output k="wheelchair" v="yes"/>
+
+          <unset k="designation"/>
+        </if>
+
+        <if k="designation">
+          <logerror message="ignoring it"/>
+        </if>
+
+      </if> <!-- k="designation" -->
+
+      <!-- Derived access rules (to override the generic ones) -->
+
+      <if k="motorroad" v="yes">
+        <output k="foot"       v="no"/>
+        <output k="horse"      v="no"/>
+        <output k="wheelchair" v="no"/>
+        <output k="bicycle"    v="no"/>
+        <output k="moped"      v="no"/>
+      </if>
+
+      <if k="footway">
+
+        <!-- Tags from http://taginfo.openstreetmap.org/keys/footway#values on 2013-02-09 -->
+
+        <if k="footway" v="sidewalk"> <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+        <if k="footway" v="both">     <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+        <if k="footway" v="left">     <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+        <if k="footway" v="right">    <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+        <if k="footway" v="yes">      <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+      </if>
+
+      <if k="sidewalk">
+
+        <!-- Tags from http://taginfo.openstreetmap.org/keys/sidewalk#values on 2013-02-09 -->
+
+        <if k="sidewalk" v="both">    <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+        <if k="sidewalk" v="left">    <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+        <if k="sidewalk" v="right">   <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+        <if k="sidewalk" v="yes">     <output k="foot" v="yes"/> <output k="wheelchair" v="yes"/> </if>
+      </if>
+
+      <if k="cycleway">
+
+        <!-- Tags from http://taginfo.openstreetmap.org/keys/cycleway#values on 2013-02-09 -->
+
+        <if k="cycleway" v="lane">   <output k="bicycle" v="yes"/> </if>
+        <if k="cycleway" v="track">  <output k="bicycle" v="yes"/> </if>
+        <if k="cycleway" v="shared"> <output k="bicycle" v="yes"/> </if>
+        <if k="cycleway" v="yes">    <output k="bicycle" v="yes"/> </if>
+
+        <if k="cycleway" v="opposite_lane">
+          <output k="bicycle" v="yes"/>
+          <output k="cyclebothways" v="yes"/>
+        </if>
+
+        <if k="cycleway" v="opposite_track">
+          <output k="bicycle" v="yes"/>
+          <output k="cyclebothways" v="yes"/>
+        </if>
+
+        <if k="cycleway" v="opposite">
+          <output k="bicycle" v="yes"/>
+          <output k="cyclebothways" v="yes"/>
+        </if>
+      </if>
+
+      <if k="oneway:bicycle" v="no">
+        <output k="bicycle" v="yes"/>
+        <output k="cyclebothways" v="yes"/>
+      </if>
+
+      <!-- Specific access rules (to override the generic ones) -->
+
+      <if k="foot"      > <output/> </if>
+      <if k="horse"     > <output/> </if>
+      <if k="wheelchair"> <output/> </if>
+      <if k="bicycle"   > <output/> </if>
+      <if k="moped"     > <output/> </if>
+      <if k="motorcycle"> <output/> </if>
+      <if k="motorcar"  > <output/> </if>
+      <if k="goods"     > <output/> </if>
+      <if k="hgv"       > <output/> </if>
+      <if k="psv"       > <output/> </if>
+
+      <!-- Normalisation of property tags -->
+
+      <if k="surface">
+
+        <!-- Tags from http://wiki.openstreetmap.org/wiki/Key:surface on 2012-11-21 -->
+
+        <if k="surface" v="asphalt">               <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="cobblestone">           <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="cobblestone:flattened"> <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="compacted">             <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="concrete">              <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="concrete:lanes">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="concrete:plates">       <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="dirt">                  <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="earth">                 <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="fine_gravel">           <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="grass">                 <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="grass_paver">           <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="gravel">                <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="ground">                <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="metal">                 <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="mud">                   <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="paved">                 <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="paving_stones">         <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="paving_stones:20">      <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="paving_stones:30">      <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="pebblestone">           <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="sand">                  <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="unpaved">               <set k="paved" v="no"/>  <unset k="surface"/> </if>
+        <if k="surface" v="wood">                  <set k="paved" v="no"/>  <unset k="surface"/> </if>
+
+        <!-- Other tags -->
+
+        <if k="surface" v="sealed">       <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="cement">       <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="tarmac">       <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="tar_and_chip"> <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="metalled">     <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="bricks">       <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="brick">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="brick_weave">  <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="setts">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
+        <if k="surface" v="sett">         <set k="paved" v="yes"/> <unset k="surface"/> </if>
+
+        <if k="surface" v="unsealed">     <set k="paved" v="no"/> <unset k="surface"/> </if>
+        <if k="surface" v="soil">         <set k="paved" v="no"/> <unset k="surface"/> </if>
+        <if k="surface" v="stones">       <set k="paved" v="no"/> <unset k="surface"/> </if>
+        <if k="surface" v="stone">        <set k="paved" v="no"/> <unset k="surface"/> </if>
+        <if k="surface" v="pebbles">      <set k="paved" v="no"/> <unset k="surface"/> </if>
+        <if k="surface" v="hardcore">     <set k="paved" v="no"/> <unset k="surface"/> </if>
+        <if k="surface" v="bark">         <set k="paved" v="no"/> <unset k="surface"/> </if>
+
+        <if k="surface">
+          <logerror message="ignoring it"/>
+        </if>
+
+      </if> <!-- k="surface" -->
+
+      <if k="bridge">
+        <ifnot k="bridge" v="no">
+          <set k="bridge" v="yes"/>
+        </ifnot>
+      </if>
+
+      <if k="tunnel">
+        <ifnot k="tunnel" v="no">
+          <set k="tunnel" v="yes"/>
+        </ifnot>
+      </if>
+
+      <if k="junction" v="roundabout">
+        <output k="oneway"     v="yes"/>
+        <output k="roundabout" v="yes"/>
+      </if>
+
+      <!-- Specific property rules (to override the default ones) -->
+
+      <if k="paved"> <output/> </if>
+
+      <if k="lanes"> <output/> </if>
+
+      <if k="bridge"> <output/> </if>
+
+      <if k="tunnel"> <output/> </if>
+
+      <!-- Output the restriction tags -->
+
+      <if k="oneway"> <output/> </if>
+
+      <if k="maxspeed"> <output/> </if>
+
+      <if k="maxweight"> <output/> </if>
+      <if k="maxheight"> <output/> </if>
+      <if k="maxwidth" > <output/> </if>
+      <if k="maxlength"> <output/> </if>
+
+      <!-- Output the name and reference tags -->
 
-    <if k="route" v="ferry">
-      <output k="highway" v="ferry"/>
-    </if>
+      <if k="name"> <output/> </if>
+      <if k="ref" > <output/> </if>
 
-    <if k="highway">
-      <logerror/>
-
-      <set k="not_highway" v="yes"/>
-    </if>
-
-    <!-- Normalisation of access tags -->
-
-    <if v="designated" ><set v="yes"/></if>
-    <if v="permissive" ><set v="yes"/></if>
-    <if v="destination"><set v="yes"/></if>
-    <if v="true"       ><set v="yes"/></if>
-    <if v="public"     ><set v="yes"/></if>
-    <if v="official"   ><set v="yes"/></if>
-
-    <if v="unsuitable" ><set v="no"/></if>
-    <if v="private"    ><set v="no"/></if>
-    <if v="limited"    ><set v="no"/></if>
-
-    <!-- Generic access permissions for all transport types (to override defaults) -->
-
-    <if k="access">
-      <set k="noaccess" v="yes"/>
-    </if>
-
-    <if k="access" v="yes">
-      <set k="noaccess" v="no"/>
-    </if>
-
-    <if k="noaccess" v="yes">
-      <output k="foot"       v="no"/>
-      <output k="horse"      v="no"/>
-      <output k="wheelchair" v="no"/>
-      <output k="bicycle"    v="no"/>
-      <output k="moped"      v="no"/>
-      <output k="motorbike"  v="no"/>
-      <output k="motorcar"   v="no"/>
-      <output k="goods"      v="no"/>
-      <output k="hgv"        v="no"/>
-      <output k="psv"        v="no"/>
-    </if>
-
-    <!-- Generic access permissions for classes of transport types -->
-
-    <if k="motor_vehicle">
-      <output k="moped"/>
-      <output k="motorbike"/>
-      <output k="motorcar"/>
-      <output k="goods"/>
-      <output k="hgv"/>
-      <output k="psv"/>
-    </if>
-
-    <if k="vehicle">
-      <output k="bicycle"/>
-      <output k="moped"/>
-      <output k="motorbike"/>
-      <output k="motorcar"/>
-      <output k="goods"/>
-      <output k="hgv"/>
-      <output k="psv"/>
-    </if>
-
-    <!-- Other access permissions (e.g. UK) -->
-
-    <if k="designation" v="restricted_byway">
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-
-      <unset k="designation"/>
-    </if>
-
-    <if k="designation" v="public_byway">
-      <set v="byway_open_to_all_traffic"/>
-    </if>
-
-    <if k="designation" v="byway">
-      <set v="byway_open_to_all_traffic"/>
-    </if>
-
-    <if k="designation" v="byway_open_to_all_traffic">
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-      <output k="moped"      v="yes"/>
-      <output k="motorbike"  v="yes"/>
-      <output k="motorcar"   v="yes"/>
-
-      <unset k="designation"/>
-    </if>
-
-    <if k="designation" v="permissive_bridleway">
-      <set v="bridleway"/>
-    </if>
-
-    <if k="designation" v="public_bridleway">
-      <set v="bridleway"/>
-    </if>
-
-    <if k="designation" v="bridleway">
-      <output k="foot"       v="yes"/>
-      <output k="horse"      v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-
-      <unset k="designation"/>
-    </if>
-
-    <if k="designation" v="public_cycleway">
-      <output k="foot"       v="yes"/>
-      <output k="wheelchair" v="yes"/>
-      <output k="bicycle"    v="yes"/>
-
-      <unset k="designation"/>
-    </if>
-
-    <if k="designation" v="permissive_footpath">
-      <set v="footpath"/>
-    </if>
-
-    <if k="designation" v="public_footpath">
-      <set v="footpath"/>
-    </if>
-
-    <if k="designation" v="footpath">
-      <output k="foot"       v="yes"/>
-      <output k="wheelchair" v="yes"/>
-
-      <unset k="designation"/>
-    </if>
-
-    <if k="not_highway">
-      <unset k="designation"/>
-    </if>
-
-    <if k="designation">
-      <logerror/>
-    </if>
-
-    <!-- Specific access rules (to override the generic ones) -->
-
-    <if k="bicycle" v="pedestrianShared">
-      <set v="yes"/>
-    </if>
-
-    <if k="bicycle" v="shared">
-      <set v="yes"/>
-    </if>
-
-    <if k="foot"      ><output/></if>
-    <if k="horse"     ><output/></if>
-    <if k="wheelchair"><output/></if>
-    <if k="bicycle"   ><output/></if>
-    <if k="moped"     ><output/></if>
-    <if k="motorbike" ><output/></if>
-    <if k="motorcar"  ><output/></if>
-    <if k="goods"     ><output/></if>
-    <if k="hgv"       ><output/></if>
-    <if k="psv"       ><output/></if>
-
-    <!-- Normalisation of property tags -->
-
-    <!-- Tags from http://wiki.openstreetmap.org/wiki/Key:surface on 2012-11-21 -->
-
-    <if k="surface" v="asphalt">               <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="cobblestone">           <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="cobblestone:flattened"> <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="compacted">             <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="concrete">              <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="concrete:lanes">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="concrete:plates">       <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="dirt">                  <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="earth">                 <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="fine_gravel">           <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="grass">                 <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="grass_paver">           <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="gravel">                <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="ground">                <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="metal">                 <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="mud">                   <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="paved">                 <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="paving_stones">         <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="paving_stones:20">      <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="paving_stones:30">      <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="pebblestone">           <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="sand">                  <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="unpaved">               <set k="paved" v="no"/>  <unset k="surface"/> </if>
-    <if k="surface" v="wood">                  <set k="paved" v="no"/>  <unset k="surface"/> </if>
-
-    <!-- Other tags -->
-
-    <if k="surface" v="sealed">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="cement">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="tarmac">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="tar_and_chip">  <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="metalled">      <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="bricks">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="brick">         <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="brick_weave">   <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="setts">         <set k="paved" v="yes"/> <unset k="surface"/> </if>
-    <if k="surface" v="sett">          <set k="paved" v="yes"/> <unset k="surface"/> </if>
-
-    <if k="surface" v="unsealed">      <set k="paved" v="no"/> <unset k="surface"/> </if>
-    <if k="surface" v="soil">          <set k="paved" v="no"/> <unset k="surface"/> </if>
-    <if k="surface" v="stones">        <set k="paved" v="no"/> <unset k="surface"/> </if>
-    <if k="surface" v="stone">         <set k="paved" v="no"/> <unset k="surface"/> </if>
-    <if k="surface" v="pebbles">       <set k="paved" v="no"/> <unset k="surface"/> </if>
-    <if k="surface" v="hardcore">      <set k="paved" v="no"/> <unset k="surface"/> </if>
-    <if k="surface" v="bark">          <set k="paved" v="no"/> <unset k="surface"/> </if>
-
-    <if k="not_highway">
-      <unset k="surface"/>
-    </if>
-
-    <if k="surface">
-      <logerror/>
-    </if>
-
-    <if k="lanes">
-      <set k="multilane" v="yes"/>
-    </if>
-
-    <if k="lanes" v="1">
-      <set k="multilane" v="no"/>
-    </if>
-
-    <if k="junction" v="roundabout">
-      <output k="oneway"     v="yes"/>
-      <output k="roundabout" v="yes"/>
-    </if>
-
-    <!-- Specific property rules (to override the default ones) -->
-
-    <if k="paved"    ><output/></if>
-    <if k="multilane"><output/></if>
-
-    <if k="bridge" v="arch">            <set v="yes"/></if>
-    <if k="bridge" v="bascule">         <set v="yes"/></if>
-    <if k="bridge" v="drawbridge">      <set v="yes"/></if>
-    <if k="bridge" v="footbridge">      <set v="yes"/></if>
-    <if k="bridge" v="gangway">         <set v="yes"/></if>
-    <if k="bridge" v="humpback">        <set v="yes"/></if>
-    <if k="bridge" v="lifting">         <set v="yes"/></if>
-    <if k="bridge" v="stepping_stones"> <set v="yes"/></if>
-    <if k="bridge" v="suspension">      <set v="yes"/></if>
-    <if k="bridge" v="swing">           <set v="yes"/></if>
-    <if k="bridge" v="viaduct">         <set v="yes"/></if>
-
-    <if k="bridge"><output/></if>
-
-    <if k="tunnel" v="underpass"> <set v="yes"/></if>
-
-    <if k="tunnel"><output/></if>
-
-    <!-- The "footroute" and "bicycleroute" properties can be set here,
-         but normally they are set by the relation rules. -->
-
-    <!-- Output the restriction tags -->
-
-    <if k="oneway"><output/></if>
-
-    <if k="maxspeed"><output/></if>
-
-    <if k="maxweight"><output/></if>
-    <if k="maxheight"><output/></if>
-    <if k="maxwidth" ><output/></if>
-    <if k="maxlength"><output/></if>
-
-    <!-- Output the name and reference tags -->
-
-    <if k="name"><output/></if>
-    <if k="ref" ><output/></if>
-
-    <!-- Output the area tag -->
-
-    <if k="area"><output/></if>
+      <!-- Output the area tag -->
+
+      <if k="area"> <output/> </if>
+
+    </if> <!-- k="highway" -->
 
   </way>
 
@@ -839,31 +982,35 @@
 
     <!-- Copy route relations -->
 
-    <if k="route" v="foot">
-      <output k="footroute" v="yes"/>
-    </if>
+    <if k="route">
 
-    <if k="route" v="walking">
-      <output k="footroute" v="yes"/>
-    </if>
+      <if k="route" v="foot">
+        <output k="footroute" v="yes"/>
+      </if>
 
-    <if k="route" v="hiking">
-      <output k="footroute" v="yes"/>
-    </if>
+      <if k="route" v="walking">
+        <output k="footroute" v="yes"/>
+      </if>
 
-    <if k="route" v="foot;bicycle">
-      <output k="footroute"    v="yes"/>
-      <output k="bicycleroute" v="yes"/>
-    </if>
+      <if k="route" v="hiking">
+        <output k="footroute" v="yes"/>
+      </if>
 
-    <if k="route" v="bicycle;foot">
-      <output k="footroute"    v="yes"/>
-      <output k="bicycleroute" v="yes"/>
-    </if>
+      <if k="route" v="foot;bicycle">
+        <output k="footroute"    v="yes"/>
+        <output k="bicycleroute" v="yes"/>
+      </if>
 
-    <if k="route" v="bicycle">
-      <output k="bicycleroute" v="yes"/>
-    </if>
+      <if k="route" v="bicycle;foot">
+        <output k="footroute"    v="yes"/>
+        <output k="bicycleroute" v="yes"/>
+      </if>
+
+      <if k="route" v="bicycle">
+        <output k="bicycleroute" v="yes"/>
+      </if>
+
+    </if> <!-- k="route" -->
 
     <!-- Pass through turn relations -->
 
@@ -871,12 +1018,14 @@
       <output/>
     </if>
 
-    <if k="except" v="bus">
-      <set v="psv"/>
-    </if>
-
     <if k="except">
-      <output/>
+
+      <if k="except" v="bus"> <set v="psv"/> </if>
+
+      <if k="except">
+        <output/>
+      </if>
+
     </if>
 
   </relation>
diff --git a/xml/routino-tagging.xsd b/xml/routino-tagging.xsd
index d13da19..19962b5 100644
--- a/xml/routino-tagging.xsd
+++ b/xml/routino-tagging.xsd
@@ -5,7 +5,7 @@
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010-2011 Andrew M. Bishop
+     This file Copyright 2010-2013 Andrew M. Bishop
 
      This program is free software: you can redistribute it and/or modify
      it under the terms of the GNU Affero General Public License as published by
@@ -31,19 +31,22 @@
 
   <xsd:complexType name="NodeType">
     <xsd:sequence>
-      <xsd:element name="if" type="IfType" minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="if"    type="IfType"    minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="ifnot" type="IfNotType" minOccurs="0" maxOccurs="unbounded"/>
     </xsd:sequence>
   </xsd:complexType>
 
   <xsd:complexType name="WayType">
     <xsd:sequence>
-      <xsd:element name="if" type="IfType" minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="if"    type="IfType"    minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="ifnot" type="IfNotType" minOccurs="0" maxOccurs="unbounded"/>
     </xsd:sequence>
   </xsd:complexType>
 
   <xsd:complexType name="RelationType">
     <xsd:sequence>
-      <xsd:element name="if" type="IfType" minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="if"    type="IfType"    minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="ifnot" type="IfNotType" minOccurs="0" maxOccurs="unbounded"/>
     </xsd:sequence>
   </xsd:complexType>
 
@@ -51,6 +54,21 @@
 
   <xsd:complexType name="IfType">
     <xsd:sequence>
+      <xsd:element name="if"       type="IfType"       minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="ifnot"    type="IfNotType"    minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="set"      type="SetType"      minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="unset"    type="UnsetType"    minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="output"   type="OutputType"   minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="logerror" type="LogErrorType" minOccurs="0" maxOccurs="1"/>
+    </xsd:sequence>
+    <xsd:attribute name="k" type="xsd:string"/>
+    <xsd:attribute name="v" type="xsd:string"/>
+  </xsd:complexType>
+
+  <xsd:complexType name="IfNotType">
+    <xsd:sequence>
+      <xsd:element name="if"       type="IfType"       minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="ifnot"    type="IfNotType"    minOccurs="0" maxOccurs="unbounded"/>
       <xsd:element name="set"      type="SetType"      minOccurs="0" maxOccurs="unbounded"/>
       <xsd:element name="unset"    type="UnsetType"    minOccurs="0" maxOccurs="unbounded"/>
       <xsd:element name="output"   type="OutputType"   minOccurs="0" maxOccurs="unbounded"/>
@@ -75,8 +93,9 @@
   </xsd:complexType>
 
   <xsd:complexType name="LogErrorType">
-    <xsd:attribute name="k" type="xsd:string"/>
-    <xsd:attribute name="v" type="xsd:string"/>
+    <xsd:attribute name="k"       type="xsd:string"/>
+    <xsd:attribute name="v"       type="xsd:string"/>
+    <xsd:attribute name="message" type="xsd:string"/>
   </xsd:complexType>
 
 </xsd:schema>
diff --git a/xml/routino-translations.xml b/xml/routino-translations.xml
index d928f19..45e7b1a 100644
--- a/xml/routino-translations.xml
+++ b/xml/routino-translations.xml
@@ -5,7 +5,7 @@
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010-2012 Andrew M. Bishop
+     This file Copyright 2010-2014 Andrew M. Bishop
 
      This program is free software: you can redistribute it and/or modify
      it under the terms of the GNU Affero General Public License as published by
@@ -111,7 +111,7 @@
 
   </language>
 
-  <!-- German translation by Christoph Eckert (July 2010) -->
+  <!-- German translation by Christoph Eckert (July 2010) / Alex Treiber (Jan 2013) -->
 
   <language lang="de">
 
@@ -145,16 +145,16 @@
     <heading direction="4"  string="Süd" />
 
     <!-- Ordinals, 1 = first, 2 = second ... -->
-    <!-- TRANSLATION REQUIRED: ordinal number="1"  string="First" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="2"  string="Second" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="3"  string="Third" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="4"  string="Fourth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="5"  string="Fifth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="6"  string="Sixth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="7"  string="Seventh" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="8"  string="Eighth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="9"  string="Ninth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="10" string="Tenth" / -->
+    <ordinal number="1"  string="Erste" />
+    <ordinal number="2"  string="Zweite" />
+    <ordinal number="3"  string="Dritte" />
+    <ordinal number="4"  string="Vierte" />
+    <ordinal number="5"  string="Fünfte" />
+    <ordinal number="6"  string="Sechste" />
+    <ordinal number="7"  string="Siebte" />
+    <ordinal number="8"  string="Achte" />
+    <ordinal number="9"  string="Neunte" />
+    <ordinal number="10" string="Zehnte" />
 
     <!-- Highway names -->
     <highway type="motorway"     string="Autobahn" />
@@ -169,7 +169,7 @@
     <highway type="cycleway"     string="Radweg" />
     <highway type="path"         string="Weg" />
     <highway type="steps"        string="Treppe" />
-    <highway type="ferry"        string="ferry" /> <!-- FIXME - needs translation -->
+    <highway type="ferry"        string="Fähre" /> 
 
     <!-- The type of route -->
     <route type="shortest" string="Kürzeste" />   <!-- For the description and route name -->
@@ -179,13 +179,13 @@
     <output-html>
       <waypoint type="waypoint"  string="Wegpunkt" />        <!-- For the chosen waypoints -->
       <waypoint type="junction"  string="Anschlussstelle" /> <!-- For the interesting junctions -->
-      <!-- TRANSLATION REQUIRED: waypoint type="roundabout" string="Roundabout" / --> <!-- For roundabouts -->
+      <waypoint type="roundabout" string="Kreisverkehr" /> <!-- For roundabouts -->
 
       <title text="%s Route" /> <!-- %s = [shortest|quickest] -->
 
       <start   string="Start"  text="Bei %s halten Sie sich Richtung %s" />          <!-- 1st %s = [waypoint|junction], 2nd %s = [heading] -->
       <node    string="Bei"    text="Bei %s wenden Sie sich nach %s Richtung %s" />  <!-- 1st %s = [waypoint|junction], 2nd %s = [turn], 3rd %s = [heading] -->
-      <!-- TRANSLATION REQUIRED: rbnode  string="Leave"  text="%s, take the %s exit heading %s" / --> <!-- 1st %s = [roundabout], 2nd %s = [first|second|...], 3rd %s = [heading] -->
+      <rbnode  string="Verlassen Sie"  text="%s, nehmen Sie die %s Ausfahrt Richtung %s" /> <!-- 1st %s = [roundabout], 2nd %s = [first|second|...], 3rd %s = [heading] -->
       <segment string="Folgen" text="Folgen Sie der %s für %.3f km bzw. %.1f min" /> <!-- 1st %s = street name -->
       <stop    string="Stop"   text="Sie sind bei %s angekommen" />                  <!-- 1st %s = [waypoint|junction] -->
       <total   string="Gesamt" text="%.1f km, %.0f minuten" />
@@ -206,14 +206,109 @@
 
   </language>
 
-  <!-- Dutch translation by Jan Jansen (August 2010) -->
+  <!-- French translation by Christophe Collard (janvier 2014) -->
+
+ <language lang="fr">
+
+    <!-- Copyright of the data being routed, not of this file  -->
+    <copyright>
+      <creator string="Créateur" text="Routino - http://www.routino.org/" />
+      <source  string="Source"  text="Basé sur les données OpenStreetMap de http://www.openstreetmap.org/" />
+      <license string="License" text="http://www.openstreetmap.org/copyright" />
+    </copyright>
+
+    <!-- Turn directions, 0 = ahead, -2 = left, +/-4 = behind, +2 = right -->
+    <turn direction="-4" string="demi-tour à gauche" />
+    <turn direction="-3" string="Très à gauche" />
+    <turn direction="-2" string="à gauche" />
+    <turn direction="-1" string="Légèrement à gauche" />
+    <turn direction="0"  string="Tout droit" />
+    <turn direction="1"  string="légèrement à droite" />
+    <turn direction="2"  string="à droite" />
+    <turn direction="3"  string="très à droite" />
+    <turn direction="4"  string="demi-tour à droite" />
+
+    <!-- Heading directions, 0 = North, -2 = West, +/-4 = South, +2 = East -->
+    <heading direction="-4" string="Sud" />
+    <heading direction="-3" string="Dud-Ouest" />
+    <heading direction="-2" string="Ouest" />
+    <heading direction="-1" string="Nord-Ouest" />
+    <heading direction="0"  string="Nord" />
+    <heading direction="1"  string="Nord-Est" />
+    <heading direction="2"  string="Est" />
+    <heading direction="3"  string="Sud-Est" />
+    <heading direction="4"  string="Sud" />
+
+    <!-- Ordinals, 1 = first, 2 = second ... -->
+    <ordinal number="1"  string="Premier" />
+    <ordinal number="2"  string="Second" />
+    <ordinal number="3"  string="Troisième" />
+    <ordinal number="4"  string="Quatrième" />
+    <ordinal number="5"  string="Cinquième" />
+    <ordinal number="6"  string="Sixième" />
+    <ordinal number="7"  string="Septième" />
+    <ordinal number="8"  string="huitième" />
+    <ordinal number="9"  string="Neuvième" />
+    <ordinal number="10" string="Dixième" />
+
+    <!-- Highway names -->
+    <highway type="motorway"     string="autoroute" />
+    <highway type="trunk"        string="route de jonction" />
+    <highway type="primary"      string="route nationale" />
+    <highway type="secondary"    string="route départementale" />
+    <highway type="tertiary"     string="route locale" />
+    <highway type="unclassified" string="route non classifiée" />
+    <highway type="residential"  string="rue résidentielle" />
+    <highway type="service"      string="rue de service" />
+    <highway type="track"        string="chemin" />
+    <highway type="cycleway"     string="voie cyclable" />
+    <highway type="path"         string="sentier" />
+    <highway type="steps"        string="escalier" />
+    <highway type="ferry"        string="ferry" />
+
+    <!-- The type of route -->
+    <route type="shortest" string="le plus court" /> <!-- For the description and route name -->
+    <route type="quickest" string="le plus rapide" /> <!-- For the description and route name -->
+
+    <!-- HTML output -->
+    <output-html>
+      <waypoint type="waypoint"   string="Etape" />   <!-- For the chosen waypoints -->
+      <waypoint type="junction"   string="Croisement" />   <!-- For the interesting junctions -->
+      <waypoint type="roundabout" string="rond-point" /> <!-- For roundabouts -->
+
+      <title text="Itinéraire %s" /> <!-- %s = [shortest|quickest] -->
+
+      <start   string="Débute"  text="à %s, direction %s" />                  <!-- 1st %s = [waypoint|junction], 2nd %s = [heading] -->
+      <node    string="à"     text="%s, aller %s direction %s" />            <!-- 1st %s = [waypoint|junction], 2nd %s = [turn], 3rd %s = [heading] -->
+      <rbnode  string="Quitter"  text="%s, prendre le %s sortir direction %s" /> <!-- 1st %s = [roundabout], 2nd %s = [first|second|...], 3rd %s = [heading] -->
+      <segment string="Suivre" text="%s pendant %.3f km, %.1f min" />        <!-- 1st %s = street name -->
+      <stop    string="S'arrêter"   text="à %s" />                           <!-- 1st %s = [waypoint|junction] -->
+      <total   string="Total"  text="%.1f km, %.0f minutes" />
+    </output-html>
+
+    <!-- GPX output -->
+    <output-gpx>
+      <waypoint type="start"  string="DEBUT" /> <!-- For the first route waypoint -->
+      <waypoint type="inter"  string="INTER" /> <!-- For the intermediate route waypoints -->
+      <waypoint type="trip"   string="POINT"  /> <!-- For the other route points -->
+      <waypoint type="finish" string="FINAL"/> <!-- For the last route waypoint -->
+
+      <desc  text="Itinéraire %s entre les étapes 'début' et 'fin' " /> <!-- %s = [shortest|quickest] -->
+      <name  text="Itinéraire %s" />                                        <!-- %s = [shortest|quickest] -->
+      <step  text="%s sur '%s' pendant %.3f km, %.1f min" />                <!-- 1st %s = [turn], 2nd %s = street name -->
+      <final text="Trajet total %.1f km, %.0f minutes" />
+    </output-gpx>
+
+  </language>
+
+  <!-- Dutch translation by Jan Jansen (August 2010) / Glenn Plas (August 2013) -->
 
   <language lang="nl">
 
     <!-- Copyright of the data being routed, not of this file  -->
     <copyright>
       <creator string="Creator" text="Routino - http://www.routino.org/" />
-      <source  string="Source"  text="Basierend auf OpenStreetMap-Daten, erhältlich via http://www.openstreetmap.org/" />
+      <source  string="Source"  text="Gebouwd op OpenStreetMap data van http://www.openstreetmap.org/" />
       <license string="License" text="http://www.openstreetmap.org/copyright" />
     </copyright>
 
@@ -240,16 +335,16 @@
     <heading direction="4"  string="Zuid" />
 
     <!-- Ordinals, 1 = first, 2 = second ... -->
-    <!-- TRANSLATION REQUIRED: ordinal number="1"  string="First" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="2"  string="Second" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="3"  string="Third" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="4"  string="Fourth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="5"  string="Fifth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="6"  string="Sixth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="7"  string="Seventh" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="8"  string="Eighth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="9"  string="Ninth" / -->
-    <!-- TRANSLATION REQUIRED: ordinal number="10" string="Tenth" / -->
+    <ordinal number="1"  string="Eerste" />
+    <ordinal number="2"  string="Tweede" />
+    <ordinal number="3"  string="Derde" />
+    <ordinal number="4"  string="Vierde" />
+    <ordinal number="5"  string="Vijfde" />
+    <ordinal number="6"  string="Zesde" />
+    <ordinal number="7"  string="Zevende" />
+    <ordinal number="8"  string="Achtste" />
+    <ordinal number="9"  string="Negende" />
+    <ordinal number="10" string="Tiende" />
 
     <!-- Highway names -->
     <highway type="motorway"     string="Autostrade" />
@@ -258,13 +353,13 @@
     <highway type="secondary"    string="Nationale weg" />
     <highway type="tertiary"     string="Doorgangsweg" />
     <highway type="unclassified" string="Niet geclassificeerd" />
-    <highway type="residential"  string="Woongebiet" />
+    <highway type="residential"  string="Woongebied" />
     <highway type="service"      string="Toegangsweg" />
     <highway type="track"        string="Veldweg" />
     <highway type="cycleway"     string="Fietspad" />
     <highway type="path"         string="Pad" />
     <highway type="steps"        string="Trap" />
-    <highway type="ferry"        string="Fähre" />
+    <highway type="ferry"        string="Veerboot" />
 
     <!-- The type of route -->
     <route type="shortest" string="Kortste" /> <!-- For the description and route name -->
@@ -274,13 +369,13 @@
     <output-html>
       <waypoint type="waypoint"  string="Punt" />         <!-- For the chosen waypoints -->
       <waypoint type="junction"  string="de splitsing" /> <!-- For the interesting junctions -->
-      <!-- TRANSLATION REQUIRED: waypoint type="roundabout" string="Roundabout" / --> <!-- For roundabouts -->
+      <waypoint type="roundabout" string="rotonde" /> <!-- For roundabouts -->
 
       <title text="%s Route" /> <!-- %s = [shortest|quickest] -->
 
       <start   string="Start"  text="Bij %s neemt u de richting %s" />       <!-- 1st %s = [waypoint|junction], 2nd %s = [heading] -->
       <node    string="Bij"    text="Bij %s gaat u %s richting %s" />        <!-- 1st %s = [waypoint|junction], 2nd %s = [turn], 3rd %s = [heading] -->
-      <!-- TRANSLATION REQUIRED: rbnode  string="Leave"  text="%s, take the %s exit heading %s" / --> <!-- 1st %s = [roundabout], 2nd %s = [first|second|...], 3rd %s = [heading] -->
+      <rbnode  string="Leave"  text="Aan de %s, neem de %s afslag richting %s" /> <!-- 1st %s = [roundabout], 2nd %s = [first|second|...], 3rd %s = [heading] -->
       <segment string="Volg"   text="Volgt u de %s voor %.3f km %.1f min" /> <!-- 1st %s = street name -->
       <stop    string="Stop"   text="U bent bij  %s aangekomen" />           <!-- 1st %s = [waypoint|junction] -->
       <total   string="Totaal" text="%.1f km, %.0f minuten" />
diff --git a/xml/routino-translations.xsd b/xml/routino-translations.xsd
index f701886..1c5646e 100644
--- a/xml/routino-translations.xsd
+++ b/xml/routino-translations.xsd
@@ -5,7 +5,7 @@
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010-2011 Andrew M. Bishop
+     This file Copyright 2010-2012 Andrew M. Bishop
 
      This program is free software: you can redistribute it and/or modify
      it under the terms of the GNU Affero General Public License as published by
@@ -21,11 +21,11 @@
 
   <xsd:complexType name="RoutinoTranslationsType">
     <xsd:sequence>
-      <xsd:element name="language" type="languageType" minOccurs="0" maxOccurs="unbounded"/>
+      <xsd:element name="language" type="LanguageType" minOccurs="0" maxOccurs="unbounded"/>
     </xsd:sequence>
   </xsd:complexType>
 
-  <xsd:complexType name="languageType">
+  <xsd:complexType name="LanguageType">
     <xsd:sequence>
       <xsd:element name="copyright"   type="CopyrightType" minOccurs="0"/>
       <xsd:element name="turn"        type="TurnType"      minOccurs="0" maxOccurs="9"/>
diff --git a/xml/scripts/drive.pl b/xml/scripts/drive.pl
index 08ee2b2..8885e73 100755
--- a/xml/scripts/drive.pl
+++ b/xml/scripts/drive.pl
@@ -1,4 +1,28 @@
 #!/usr/bin/perl
+#
+# Special case tagging rule generator.
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2011-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+# Process the input
 
 while(<STDIN>)
   {
diff --git a/xml/scripts/ride.pl b/xml/scripts/ride.pl
index 1f01909..954aeaa 100755
--- a/xml/scripts/ride.pl
+++ b/xml/scripts/ride.pl
@@ -1,4 +1,28 @@
 #!/usr/bin/perl
+#
+# Special case tagging rule generator.
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2011-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+# Process the input
 
 while(<STDIN>)
   {
@@ -10,7 +34,7 @@ while(<STDIN>)
       print "      <output k=\"foot\"       v=\"no\"/>\n";
       print "      <output k=\"wheelchair\" v=\"no\"/>\n";
       print "      <output k=\"moped\"      v=\"no\"/>\n";
-      print "      <output k=\"motorbike\"  v=\"no\"/>\n";
+      print "      <output k=\"motorcycle\" v=\"no\"/>\n";
       print "      <output k=\"motorcar\"   v=\"no\"/>\n";
       print "      <output k=\"goods\"      v=\"no\"/>\n";
       print "      <output k=\"hgv\"        v=\"no\"/>\n";
diff --git a/xml/scripts/walk.pl b/xml/scripts/walk.pl
index 969fb7a..cbf0f06 100755
--- a/xml/scripts/walk.pl
+++ b/xml/scripts/walk.pl
@@ -1,4 +1,28 @@
 #!/usr/bin/perl
+#
+# Special case tagging rule generator.
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2011-2014 Andrew M. Bishop
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+# Process the input
 
 while(<STDIN>)
   {
@@ -10,7 +34,7 @@ while(<STDIN>)
       print "      <output k=\"horse\"      v=\"no\"/>\n";
       print "      <output k=\"bicycle\"    v=\"no\"/>\n";
       print "      <output k=\"moped\"      v=\"no\"/>\n";
-      print "      <output k=\"motorbike\"  v=\"no\"/>\n";
+      print "      <output k=\"motorcycle\" v=\"no\"/>\n";
       print "      <output k=\"motorcar\"   v=\"no\"/>\n";
       print "      <output k=\"goods\"      v=\"no\"/>\n";
       print "      <output k=\"hgv\"        v=\"no\"/>\n";

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/routino.git



More information about the Pkg-grass-devel mailing list