[SCM] routino branch, master, updated. 59f7dd29369e69f05afb4de29306d16a08197830

Thibaut Gridel tgridel at free.fr
Wed Oct 26 18:59:34 UTC 2011


The following commit has been merged in the master branch:
commit fb9657174e57b36b0427c58b4869a50b075c54b4
Author: Thibaut Gridel <tgridel at free.fr>
Date:   Wed Oct 26 18:23:37 2011 +0200

    Imported Upstream version 2.1.1

diff --git a/ChangeLog b/ChangeLog
index cfa3313..8e323c4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,1552 @@
+2011-10-23  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Version 2.1.1 released
+
+2011-10-23 [r881-882]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/NEWS.txt, FILES, doc/html/readme.html: Update for version
+	  2.1.1.
+
+	* doc/html/configuration.html: Update copyright year.
+
+2011-10-22 [r880]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* Makefile: Fix running 'make test' from the top level.
+
+2011-10-22 [r879]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Add some more typecasts before printing the
+	  values.
+
+2011-10-22 [r878]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/Makefile: Fix the installation of the XML files.
+
+2011-10-22 [r876-877]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/expected/turns-WP09.txt, src/test/turns.osm,
+	  src/test/expected/turns-WP10.txt,
+	  src/test/expected/turns-WP11.txt,
+	  src/test/expected/turns-WP12.txt,
+	  src/test/expected/turns-WP04.txt,
+	  src/test/expected/turns-WP13.txt,
+	  src/test/expected/turns-WP05.txt,
+	  src/test/expected/turns-WP14.txt,
+	  src/test/expected/turns-WP06.txt,
+	  src/test/expected/turns-WP15.txt,
+	  src/test/expected/turns-WP16.txt (added),
+	  src/test/expected/turns-WP07.txt,
+	  src/test/expected/turns-WP08.txt: Test case for 'except' tags on
+	  turn restrictions.
+
+	* src/osmparser.c, xml/routino-tagging.xml: Fix handling of
+	  'except' tags for turn restrictions.
+
+2011-10-22 [r875]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/sorting.c: Revert back to something very close to r869
+	  because it is fastest by a tiny fraction.
+
+2011-10-22 [r874]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.h, src/queue.c: Revert back to r867 because it is
+	  faster (although only by 1%) than any of the other combinations.
+
+2011-10-22 [r873]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/sorting.c, src/queue.c: Revert back to r864 zero-based binary
+	  heap but with r868/r869 refactored code.
+
+2011-10-15 [r872]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/sorting.c, src/queue.c: Change the binary heap to a 3-ary
+	  heap.
+
+2011-10-15 [r871]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/sorting.c: Bug fixes for the previous change.
+
+2011-10-15 [r870]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/sorting.c, src/results.h, src/queue.c: Change the binary heap
+	  to a 4-ary heap.
+
+2011-10-15 [r868-869]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/sorting.c: Refactor the binary heap to reduce the number of
+	  comparisons.
+
+	* src/queue.c: Refactor the binary heap to reduce the number of
+	  comparisons.
+
+2011-10-09 [r867]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/sorting.c: Change to a unity based binary heap rather than
+	  zero based one to save some additions.
+
+2011-10-09 [r866]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/queue.c: Bug fix with previous change.
+
+2011-10-06 [r865]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.h, src/queue.c: Change to a unity based binary heap
+	  rather than zero based one to save some additions.
+
+2011-10-06 [r864]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.c: Swap the order of two parts of an && statement.
+
+2011-10-06 [r863]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.h, src/results.c: Change bin counters to 8-bit
+	  (reduces memory) and pre-allocate first dimension of pointer
+	  array (saves time).
+
+2011-10-06 [r862]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* Makefile, xml/Makefile, doc/Makefile, src/Makefile,
+	  src/xml/Makefile: Makefiles are more consistent with each other
+	  and 'make test' can be run from the top level.
+
+2011-10-06 [r861]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/usage.html, src/planetsplitter.c, doc/USAGE.txt: Change
+	  the default number of iterations to 5 since testing shows that
+	  there is negligible improvement beyond here.
+
+2011-10-05 [r860]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Optimise the number of hash function bins by
+	  trial and error.
+
+2011-10-05 [r859]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/Makefile, src/xml/Makefile: Add the gcc options for profiling
+	  (coverage) and delete the files generated by it.
+
+2011-10-05 [r858]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.c: If there are too many results in one bin then
+	  double the number of bins.
+
+2011-10-05 [r857]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.h, src/results.c: Remove the two RESULTS_*_INCREMENT
+	  constants by swapping the dimensions on the 'point' array so that
+	  both have unity value and are pointless.
+
+2011-10-05 [r856]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c: Optimise the number of hash function bins by trial
+	  and error.
+
+2011-10-04 [r854-855]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/optimiser.c: Increase the size of the hash
+	  array used to store the results.
+
+	* src/results.h, src/results.c: Change the way that allocated
+	  memory is tracked.
+
+2011-10-04 [r853]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.c: Split the data increment constant into two for the
+	  different parts of the data structure.
+
+2011-10-03 [r852]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/router.cgi: Ensure that the shortest or quickest
+	  option is passed to the router.
+
+2011-10-03  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Version 2.1 released
+
+2011-10-03 [r851]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* FILES: Remove another .svn directory.
+
+2011-10-03 [r850]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/NEWS.txt, FILES, doc/html/readme.html: Update for version
+	  2.1.
+
+2011-09-07 [r849]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/super-or-not.osm, src/optimiser.c,
+	  src/test/expected/super-or-not-WP03.txt (added): Handle the
+	  special case where the start point is a super-node and the finish
+	  point is somewhere within one of the super-segments from that
+	  node.
+
+2011-09-07 [r848]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodes.c: Fix for previous binary search change.
+
+2011-09-07 [r847]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Fix bug with earlier change to OSM file
+	  creator.
+
+2011-09-07 [r846]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/router.c: Fix confusing, duplicated, output message.
+
+2011-09-07 [r845]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodes.c: Make stricter checks for closest nodes just like in
+	  v2.0.3 for segments.
+
+2011-09-07 [r844]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Fix formatting problem with dumped OSM file.
+
+2011-09-07 [r842-843]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodes.c, src/waysx.c, src/nodesx.c, src/relations.c: Check
+	  binary search functions and improve comments, fix pathological
+	  case with end point and/or improve start point.
+
+	* src/filedumper.c: Use macro test function rather than direct
+	  check.
+
+2011-09-06 [r841]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/super-or-not.osm (added), src/test/a-b.sh (added),
+	  src/test/expected/super-or-not-WP01.txt (added),
+	  src/test/expected/super-or-not-WP02.txt (added), src/test,
+	  src/test/super-or-not.sh (added): Added a new test case for the
+	  routing bug-fix in version 2.0.3 (route via super-nodes may not
+	  be shortest).
+
+2011-09-06 [r840]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/expected/dead-ends-WP01.txt,
+	  src/test/expected/dead-ends-WP02.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,
+	  src/test/expected/dead-ends-WP08.txt,
+	  src/test/expected/dead-ends-WP09.txt,
+	  src/test/expected/loops-WP10.txt,
+	  src/test/expected/loops-WP11.txt, src/test/copyright.xml (added),
+	  src/test/expected/no-super-WP01.txt,
+	  src/test/expected/no-super-WP02.txt,
+	  src/test/expected/no-super-WP03.txt,
+	  src/test/expected/no-super-WP04.txt,
+	  src/test/expected/turns-WP01.txt,
+	  src/test/expected/turns-WP02.txt,
+	  src/test/expected/turns-WP03.txt, src/test/start-1-finish.sh,
+	  src/test/expected/turns-WP04.txt,
+	  src/test/expected/turns-WP05.txt,
+	  src/test/expected/turns-WP06.txt,
+	  src/test/expected/turns-WP07.txt,
+	  src/test/expected/turns-WP08.txt,
+	  src/test/expected/turns-WP09.txt, src/test/a-b-c.sh,
+	  src/test/expected/dead-ends-WP10.txt,
+	  src/test/expected/dead-ends-WP11.txt,
+	  src/test/expected/loops-WP01.txt,
+	  src/test/expected/loops-WP02.txt,
+	  src/test/expected/loops-WP03.txt,
+	  src/test/expected/loops-WP04.txt,
+	  src/test/expected/loops-WP05.txt,
+	  src/test/expected/loops-WP06.txt,
+	  src/test/expected/loops-WP07.txt,
+	  src/test/expected/loops-WP08.txt,
+	  src/test/expected/loops-WP09.txt,
+	  src/test/expected/turns-WP10.txt,
+	  src/test/expected/turns-WP11.txt,
+	  src/test/expected/turns-WP12.txt,
+	  src/test/expected/turns-WP13.txt,
+	  src/test/expected/turns-WP14.txt,
+	  src/test/expected/turns-WP15.txt: Ensure that test cases have
+	  correct copyright notice (Routino, AGPL3) in generated data and
+	  not the default one (OSM, CC-SA).
+
+2011-09-06 [r838-839]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/expected/dead-ends-WP01.txt (added),
+	  src/test/expected/dead-ends-WP02.txt (added),
+	  src/test/expected/dead-ends-WP03.txt (added),
+	  src/test/expected/dead-ends-WP04.txt (added),
+	  src/test/expected/dead-ends-WP05.txt (added),
+	  src/test/expected/dead-ends-WP06.txt (added),
+	  src/test/expected/dead-ends-WP07.txt (added),
+	  src/test/expected/dead-ends-WP08.txt (added),
+	  src/test/expected/loops-WP10.txt (added),
+	  src/test/expected/dead-ends-WP09.txt (added),
+	  src/test/expected/loops-WP11.txt (added),
+	  src/test/expected/no-super-WP01.txt (added),
+	  src/test/expected/no-super-WP02.txt (added),
+	  src/test/expected/no-super-WP03.txt (added),
+	  src/test/expected/turns-WP01.txt (added),
+	  src/test/expected/no-super-WP04.txt (added),
+	  src/test/expected/turns-WP02.txt (added),
+	  src/test/expected/turns-WP03.txt (added),
+	  src/test/expected/turns-WP04.txt (added),
+	  src/test/expected/turns-WP05.txt (added),
+	  src/test/expected/turns-WP06.txt (added),
+	  src/test/expected/turns-WP07.txt (added),
+	  src/test/expected/turns-WP08.txt (added),
+	  src/test/expected/turns-WP09.txt (added),
+	  src/test/expected/dead-ends-WP10.txt (added),
+	  src/test/expected/dead-ends-WP11.txt (added),
+	  src/test/expected/loops-WP01.txt (added),
+	  src/test/expected/loops-WP02.txt (added),
+	  src/test/expected/loops-WP03.txt (added),
+	  src/test/expected/loops-WP04.txt (added),
+	  src/test/expected/loops-WP05.txt (added),
+	  src/test/expected/loops-WP06.txt (added),
+	  src/test/expected/loops-WP07.txt (added),
+	  src/test/expected/loops-WP08.txt (added),
+	  src/test/expected/loops-WP09.txt (added),
+	  src/test/expected/turns-WP10.txt (added),
+	  src/test/expected/turns-WP11.txt (added),
+	  src/test/expected/turns-WP12.txt (added),
+	  src/test/expected/turns-WP13.txt (added),
+	  src/test/expected/turns-WP14.txt (added),
+	  src/test/expected/turns-WP15.txt (added): Store the expected
+	  results to check for future regressions.
+
+	* src/test/a-b-c.sh, src/test/expected (added),
+	  src/test/start-1-finish.sh: Store the expected results to check
+	  for future regressions.
+
+2011-09-05 [r837]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c: Ignore relations based on all vehicle types
+	  (including bicycles) not just motor vehicles.
+
+2011-09-05 [r836]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/Makefile, xml/scripts/walk.pl (added), xml,
+	  xml/scripts/ride.pl (added), web/data, xml/scripts (added),
+	  xml/scripts/drive.pl (added): Generate special-use sets of
+	  tagging rules for walking, riding and driving
+
+2011-08-27 [r834-835]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/bin/summarise-log.pl (added): A script to process the error
+	  log file and summarise it.
+
+	* xml/routino-tagging.xml: Add lots more tagging rules based on
+	  errors logged from parsing UK, add some more error logging.
+
+2011-08-27 [r832-833]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c: Only log errors for highways.
+
+	* src/relationsx.c: Improve the error messages for bad relations.
+
+2011-08-27 [r830-831]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Change the 'generator' tag in the dumped XML
+	  file.
+
+	* xml/routino-tagging.xsd: Whitespace change.
+
+2011-08-21 [r828]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/NEWS.txt, doc/README.txt, src/nodes.c, FILES,
+	  src/optimiser.c, src/router.c, doc/html/readme.html: Merge
+	  version 2.0.3 into working version.
+
+2011-08-14 [r827]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/tagging.h, src/tagging.c, xml/routino-tagging.xsd: Add an
+	  unset rule in the tagging processing XML file.
+
+2011-08-13 [r826]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/tagging.h, src/tagmodifier.c, src/osmparser.c, src/tagging.c,
+	  xml/routino-tagging.xsd: Add a logerror rule in the tagging
+	  processing XML file.
+
+2011-08-04 [r825]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c: Add more acceptable number suffixes.
+
+2011-07-23 [r813]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c: Better parsing of width/height/length and weight
+	  and more information about value actually used.
+
+2011-07-21 [r812]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c, src/osmparser.c, src/waysx.c, src/segmentsx.c,
+	  src/nodesx.c: Add logging of parsing and processing errors.
+
+2011-07-21 [r810-811]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/a-b-c.sh, src/test/start-1-finish.sh,
+	  src/test/only-split.sh: Use the --errorlog option.
+
+	* doc/html/usage.html, src/planetsplitter.c, doc/USAGE.txt: The
+	  filename is now optional in the --errorlog option.
+
+2011-07-21 [r809]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/planetsplitter.c: Only open/close the error log file if one
+	  was requested.
+
+2011-07-10 [r806-808]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test: Ignore the auto-generated files from the new test case.
+
+	* src/test/invalid-turn-relations.osm (added),
+	  src/test/invalid-turn-relations.sh (added),
+	  src/test/only-split.sh (added): Add test cases for the new turn
+	  relation validity checks.
+
+	* src/relationsx.c: Check turn relations more carefully and discard
+	  them if they are invalid.
+
+2011-07-04 [r805]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c: Change the termination of route relation
+	  way/relation lists.
+
+2011-07-03 [r804]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/logging.c, doc/html/usage.html, src/planetsplitter.c,
+	  src/logging.h, doc/USAGE.txt: Add framework for logging error
+	  during OSM parsing and subsequent processing.
+
+2011-07-02 [r803]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodes.h: Replace over-sized file entry with one of
+	  appropriate size.
+
+2011-08-04  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Version 2.0.3 released
+
+2011-08-04 [r823]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/NEWS.txt, doc/README.txt, FILES, doc/html/readme.html:
+	  Updated for version 2.0.3.
+
+2011-08-04 [r822]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/router.c: If there is a route that passes super-nodes and one
+	  that doesn't then choose the better one.
+
+2011-08-04 [r820-821]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/router.c: If there is a direct route without passing any
+	  super-nodes then keep it as a backup in case the potential route
+	  that does pass super-nodes doesn't work out.
+
+	* src/optimiser.c: Allow calling FixForwardRoute() more than once.
+
+2011-08-04 [r819]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Revert previous change because it breaks the
+	  dead-end handling.
+
+2011-08-03 [r818]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/router.c: Find a valid route if the start and end point are
+	  the same location (it doesn't require a U-turn).
+
+2011-08-03 [r817]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/router.c: Add a new (less confusing) error
+	  message for when the start of the route has no super-nodes and
+	  doesn't include the end node and make clearer the error message
+	  when the combining of routes fails.
+
+2011-08-03 [r816]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodes.c: Make more checks on the closest segment to avoid
+	  choosing one that our profile does not allow us to use.
+
+2011-08-02 [r815]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Handle the case where the start node is a
+	  super-node and there is no previous segment.
+
+2011-06-26  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Version 2.0.2 released
+
+2011-06-26 [r800-r801]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/README.txt, doc/html/readme.html: Update for version 2.0.2.
+
+	* doc/NEWS.txt, FILES, doc/html/readme.html: Update for version
+	  2.0.2.
+
+2011-06-25 [r798-799]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.c: Fix comment associated with results list memory
+	  handling.
+
+	* src/optimiser.c: Free temporary results that are calculated.
+
+2011-06-25 [r795-797]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/tagging.h, src/planetsplitter.c, src/tagging.c: Add some
+	  functions to free the tagging rules when they have been used.
+
+	* src/osmparser.c: Free some memory allocated when parsing the
+	  file.
+
+	* src/nodesx.c: Free some memory allocated when writing the file.
+
+2011-06-19 [r794]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/tagmodifier.c: Change to unsigned long and ensure that printf
+	  format specifiers are correct.
+
+2011-06-19 [r792-793]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: If a node has no segments return a NULL pointer
+	  rather than random junk.
+
+	* xml/routino-tagging.xml: Reinstate the line that makes
+	  roundabouts one-way.
+
+2011-06-18 [r791]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c, src/xmlparse.h, src/xmlparse.l: Don't use the
+	  flex yylineno but keep track with an unsigned long long line
+	  counter instead (if there are more than 2^31 nodes then there are
+	  more than 2^31 lines as well).
+
+2011-06-18 [r790]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/relationsx.c, src/types.h, src/osmparser.c,
+	  src/waysx.c, src/filedumper.c, src/segmentsx.c, src/nodesx.c,
+	  src/router.c, src/typesx.h: Ensure that when printing numbers of
+	  the index_t type that an appropriate printf format specifier is
+	  used (ready for if it is redefined as 64-bit).
+
+2011-06-18 [r788-789]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/Makefile: Use the -std=c99 option by default.
+
+	* src/relationsx.c, src/waysx.c, src/segmentsx.c, src/nodesx.c,
+	  src/segmentsx.h: Fix some more warnings from -Wextra and/or
+	  -pedantic options.
+
+2011-06-18 [r787]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xmlparse.l: Use flex %options instead of #defines, force
+	  clean compilation with C99.
+
+2011-06-18 [r786]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c, src/relationsx.h: Rename structure element
+	  "restrict" to "restriction" to avoid C99 error (reserved word).
+
+2011-06-18 [r785]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c: Removed warning from gcc-4.6.
+
+2011-06-14 [r784]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml: Fix error with handling ferry routes
+	  (patch from Michael Günnewig).
+
+2011-06-07  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Version 2.0.1 released
+
+2011-06-07 [r782]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/readme.html: Update for version 2.0.1.
+
+2011-06-07 [r781]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/NEWS.txt, doc/README.txt, FILES: Update for version 2.0.1.
+
+2011-06-05 [r779-780]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/visualiser.c, src/nodes.c, src/relationsx.c,
+	  src/waysx.c, src/filedumper.c, src/segmentsx.c, src/nodesx.c,
+	  src/relations.c: Replace int with appropriate defined types
+	  (mostly index_t, ll_bin_t and ll_bin2_t).
+
+	* src/types.h: Add some comments to clarify the latitude/longitude
+	  bin types and a new type for latitude/longitude bins (two
+	  dimensions).
+
+2011-06-05 [r777-778]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/profiles.c: Change unsigned int to int for consistency with
+	  the rest of the code.
+
+	* src/optimiser.c: Remove unused variable (hangover from previous
+	  U-turn searching).
+
+2011-06-04 [r776]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/osmparser.c: Add missing header file.
+
+2011-06-04 [r773-775]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c: Convert integer and floating point values
+	  inline. Check that node, way and relation IDs don't need to be
+	  long long types.
+
+	* src/translations.c, src/profiles.c: Convert integer and floating
+	  point values inline.
+
+	* src/xmlparse.h, src/xmlparse.l: The
+	  XMLPARSE_ASSERT_(INTEGER|FLOATING) functions now don't return the
+	  converted type.
+
+2011-06-04 [r770-772]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.h: Add a type cast to a macro.
+
+	* src/segmentsx.c, src/segmentsx.h: Change name of function
+	  parameters to clarify what they are.
+
+	* src/relationsx.c, src/segmentsx.c: Fix some more potential
+	  problems with a transition to 64-bit node_t.
+
+2011-06-03 [r761]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/relationsx.c, src/segmentsx.c: Shorten the
+	  messages when running to avoid going beyond 80 characters.
+
+2011-06-03 [r759-760]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/visualiser.c: Remove hard-coded numeric value and replace
+	  with a #define value.
+
+	* src/superx.c, src/relationsx.c, src/segmentsx.c, src/typesx.h:
+	  Remove hard-coded numeric values and replace with a common
+	  #define value. Handle overflows consistently.
+
+2011-06-03 [r758]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.h, src/typesx.h: Move some macros from nodesx.h to
+	  typesx.h.
+
+2011-06-03 [r757]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c, src/tagmodifier.c, src/optimiser.c,
+	  src/osmparser.c, src/waysx.c: Rationalise the increment of the
+	  numbers used for the output when not --loggable.
+
+2011-06-01 [r756]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c: Delete turn relations that refer to nodes or
+	  ways that don't exist as soon as possible.
+
+2011-05-31 [r755]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.h, src/osmparser.c, src/segmentsx.c, src/waysx.h,
+	  src/typesx.h: Fix some obvious problems with a transition to
+	  64-bit node_t.
+
+2011-05-31 [r754]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/tagging.c, src/translations.c, src/profiles.c: Fix
+	  inconsistent C language version usage.
+
+2011-05-30 Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Version 2.0 released
+
+2011-05-30 [r742]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c: Don't crash on malformed relations, give better
+	  reporting of number when processing them.
+
+2011-05-30 [r741]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* FILES: Update for release.
+
+2011-05-30 [r740]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: Fix spelling mistake in function parameter
+	  comment.
+
+2011-05-30 [r738-739]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/Makefile: Make sure that clean really means it.
+
+	* doc/NEWS.txt, doc/README.txt, doc/html/readme.html: Update for
+	  version 2.0 release.
+
+2011-05-30 [r737]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/usage.html, doc/html/algorithm.html, doc/TAGGING.txt,
+	  doc/html/index.html, doc/html/data.html, doc/USAGE.txt,
+	  doc/ALGORITHM.txt, doc/DATA.txt, doc/html/tagging.html: Run a
+	  spelling check on the documentation.
+
+2011-05-30 [r736]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/algorithm.html: Describe new philosophy of alloing
+	  U-turn at waypoints to avoid dead-ends.
+
+2011-05-30 [r735]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Fix problem with test case loops WP11.
+
+2011-05-30 [r734]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/functions.h, src/router.c: Change the
+	  philosophy on dead ends so that now a U-turn is made at the
+	  waypoint if continuing in the previous direction would lead into
+	  a dead-end. This simplifies the algorithm and removes a lot of
+	  special case handling.
+
+2011-05-30 [r731-733]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/loops.osm: Give the loops unique names.
+
+	* src/test/a-b-c.sh, src/test/start-1-finish.sh: Print less
+	  information when running.
+
+	* src/fakes.c: Fix error with calculating length of fake segment
+	  and optimise the ExtraFakeSegment function.
+
+2011-05-29 [r730]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/a-b-c.sh: Exit on error.
+
+2011-05-21 [r729]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/functions.h, src/router.c: Find all routes
+	  in the no-super.osm test case.
+
+2011-05-20 [r727-728]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/no-super.osm: Add new test cases for fake
+	  nodes/segments.
+
+	* src/fakes.c, src/optimiser.c, src/fakes.h: Add a special function
+	  to handle the detection of U-turns between two fake segments that
+	  sit on the same real segment.
+
+2011-05-18 [r725-726]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/a-b-c.sh (added), src/test, src/test/no-super.sh
+	  (added), src/test/no-super.osm (added): Add new test cases for
+	  very simple routes with no super-nodes.
+
+	* src/fakes.c: Fix routing between two fake nodes on the same
+	  segment (again).
+
+2011-05-18 [r724]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/dead-ends.osm: Add a new waypoint at the very end of a
+	  dead-end (not super-node).
+
+2011-05-18 [r722-723]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/functions.h: Remove the override flag from
+	  FindNormalRoute().
+
+	* src/optimiser.c, src/functions.h, src/router.c: Use the beginning
+	  of the route as the start of the combined route (since it may
+	  have special override segments in it).
+
+2011-05-17 [r721]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/functions.h, src/router.c: Change the order
+	  of the arguments to the routing functions (move profile earlier).
+
+2011-05-17 [r720]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/Makefile: Install the license file in the doc directory.
+
+2011-05-15 [r719]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/router.c: Finally find a way out of
+	  dead-ends, might have some nasty side-effects though.
+
+2011-05-14 [r717-718]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Fix slim/non-slim variation.
+
+	* src/test/dead-ends.osm: Add another waypoint at the terminal
+	  super-node.
+
+2011-05-13 [r716]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/turns.osm: Force waypoint 13 to go round the roundabout
+	  twice.
+
+2011-05-12 [r714-715]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/turns.osm (added), src/test, src/test/turns.sh (added):
+	  Added turn restriction test cases.
+
+	* src/test/start-1-finish.sh: Bug fix for logging.
+
+2011-05-11 [r712-713]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/segmentsx.c: Add comments to assert statements
+	  that don't already have them.
+
+	* src/optimiser.c: Crash out if infinite loop (usually caused by a
+	  bug elsewhere).
+
+2011-05-08 [r708-711]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/start-1-finish.sh: Run filedumper, allow running under a
+	  run-time debugger.
+
+	* src/optimiser.c: Remove clash of cache locations.
+
+	* src/test/Makefile: Print an extra message after comparing the
+	  slim and non-slim results.
+
+	* src/segments.c, src/segments.h: Make the NextSegment function
+	  inline (move from segments.c to segments.h).
+
+2011-05-08 [r707]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.c, src/visualiser.c, src/nodes.c, src/optimiser.c,
+	  src/filedumper.c, src/nodes.h, src/output.c: The FirstSegment
+	  function now takes a cache position argument.
+
+2011-05-08 [r706]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.c, src/nodes.c, src/relations.c, src/ways.c: Ensure
+	  that the correct number of cached nodes, segments, ways or
+	  relations are initialised.
+
+2011-05-08 [r705]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/ways.h, src/filedumper.c, src/ways.c: Remove the unused name
+	  caching for the ways (in slim mode).
+
+2011-05-08 [r704]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.h, src/segmentsx.h: Simplify the lookup of the
+	  segment index in slim mode.
+
+2011-05-07 [r703]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Allow the start of a route to double-back to the
+	  initial node even if a super-node.
+
+2011-05-07 [r700-702]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test/loops.osm: Rename the waypoints.
+
+	* src/files.c: Remove useless assert statement.
+
+	* src/optimiser.c, src/nodes.h, src/segmentsx.c: Fix bugs found by
+	  valgrind.
+
+2011-05-07 [r697-699]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Handle things correctly if the
+	  FindSuperSegment() function is called with a fake segment.
+
+	* src/test/Makefile: Ensure that executables are compiled before
+	  running the tests.
+
+	* src/Makefile: Require slim and non-slim versions of fakes.o.
+
+2011-05-07 [r695-696]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/router.c: Calculate an override version of the start of the
+	  route to get out of dead-ends.
+
+	* src/output.c: Use real segments when making comparisons (not
+	  pointers or non-real segments).
+
+2011-05-06 [r690-694]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/test: Ignore files and directories generated by running 'make
+	  test'.
+
+	* src/xml: Ignore files generated by running 'make test'.
+
+	* src/Makefile: Allow running 'make test' in the source directory.
+
+	* src/test/waypoints.pl (added), src/test/loops.osm (added),
+	  src/test/Makefile (added), src/test/dead-ends.sh (added),
+	  src/test/dead-ends.osm (added), src/test/start-1-finish.sh
+	  (added), src/test/loops.sh (added): Routing test cases.
+
+	* src/test (added): A directory for routing test cases.
+
+2011-05-06 [r689]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/xml/test/bad-attr-character-ref.xml (removed): Remove
+	  false-positive test case (a bug in xmlparse.l previously flagged
+	  this as an error).
+
+2011-04-27 [r688]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Force going straight on if a waypoint is a
+	  super-node.
+
+2011-04-27 [r686-687]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Improve the FindSuperSegment() function when the
+	  existing segment is the right answer.
+
+	* src/optimiser.c, src/router.c: Rename the variables in and around
+	  the CombineRoutes() function for clarity.
+
+2011-04-26 [r685]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: When starting a super-route ensure that all
+	  starting segments are super-segments to avoid u-turns at the
+	  starting super-node.
+
+2011-04-25 [r683-684]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/output.c: Fix error with turn description.
+
+	* src/output.c: Include a point number (hidden) in the HTML file.
+
+2011-04-24 [r682]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/waysx.c, src/waysx.h: Fix error is last semi-automated
+	  update.
+
+2011-04-24 [r681]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.c, src/superx.c, src/visualiser.c, src/relationsx.c,
+	  src/segments.h, src/superx.h, src/filedumper.c, src/nodesx.c,
+	  src/relations.c, src/nodesx.h, src/relations.h, src/nodes.c,
+	  src/waysx.c, src/nodes.h, src/segmentsx.c, src/waysx.h,
+	  src/segmentsx.h, src/ways.c: Make the comments more consistent.
+
+2011-04-24 [r680]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/translations.h, src/fakes.c, src/filedumper.c, src/fakes.h,
+	  src/nodesx.c, src/output.c, src/results.c, src/files.c,
+	  src/nodesx.h, src/results.h, src/files.h, src/nodes.c,
+	  src/planetsplitter.c, src/osmparser.c, src/nodes.h,
+	  src/profiles.c, src/segments.c, src/sorting.c, src/tagging.h,
+	  src/visualiser.c, src/superx.c, src/logging.c, src/ways.h,
+	  src/profiles.h, src/relationsx.c, src/segments.h, src/sorting.h,
+	  src/tagmodifier.c, src/visualiser.h, src/superx.h, src/logging.h,
+	  src/relationsx.h, src/relations.c, src/functionsx.h,
+	  src/relations.h, src/types.h, src/optimiser.c, src/waysx.c,
+	  src/segmentsx.c, src/functions.h, src/waysx.h, src/router.c,
+	  src/segmentsx.h: Update comments throughout the source code.
+
+2011-04-23 [r679]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/algorithm.html, doc/ALGORITHM.txt: Add description of
+	  U-turns at dead-ends.
+
+2011-04-23 [r678]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/functions.h, src/output.c, src/router.c:
+	  Allow U-turns at dead-ends to avoid getting stuck.
+
+2011-04-22 [r677]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/router.c: Handle failure to find route
+	  gracefully.
+
+2011-04-22 [r676]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/visualiser.cgi: Another change related to turn
+	  restrictions (missed in last checkin).
+
+2011-04-22 [r675]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.c, doc/html/usage.html, web/www/routino/router.cgi,
+	  src/segments.h, doc/USAGE.txt, src/router.c: Add in the option to
+	  specify an initial heading.
+
+2011-04-22 [r674]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c, src/nodesx.c: Finish off the geographic sorting
+	  of segments.
+
+2011-04-22 [r673]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Use the common TurnAngle() function from
+	  segments.c instead of a local one.
+
+2011-04-22 [r672]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.c, src/segments.h, src/output.c: Move the
+	  turn_angle() and bearing_angle() functions from output.c into
+	  segments.c.
+
+2011-04-22 [r671]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/algorithm.html, doc/ALGORITHM.txt: Simplify the language
+	  used describing the highway properties.
+
+2011-03-21 [r670]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c: Ignore turn restrictions that ban going the
+	  wrong way down a one-way road.
+
+2011-03-21 [r668-669]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segments.c, src/filedumper.c, src/profiles.c: Include math.h
+	  for files that use math functions.
+
+	* src/types.h: Round the node latitude/longitude rather than
+	  truncating.
+
+2011-03-21 [r667]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Include some of the Routino internal
+	  information when dumping an OSM format output.
+
+2011-03-21 [r666]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: Fix bug with segment deduplication.
+
+2011-03-21 [r665]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c, src/planetsplitter.c, src/relationsx.h,
+	  src/nodesx.c: Sort the segments geographically.
+
+2011-03-20 [r664]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.c: Sort nodes strictly by latitude/longitude within
+	  the bins (helps with regresssion testing).
+
+2011-03-20 [r661-663]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/documentation: Ignore extra image files.
+
+	* src/xmlparse.l: Ensure that UTF-8 is used internally when reading
+	  in a numeric entity.
+
+	* src/router.c: Fix bug found by gcc-4.5.
+
+2011-03-20 [r660]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: Return early from the IndexSegments function if
+	  there are no segments.
+
+2011-03-19 [r659]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/algorithm.html, doc/html/example0.png,
+	  doc/html/example1.png, doc/ALGORITHM.txt, doc/html/example2.png,
+	  doc/html/example3.png (added), doc/html/example4.png (added):
+	  Update the algorithm documents for turn restrictions.
+
+2011-03-19 [r658]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: Deduplicate in pairs only (i.e. if a segment
+	  occurs 4 times then keep 2 of them).
+
+2011-03-19 [r657]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: Cache the recently used ways when de-duplicating
+	  segments.
+
+2011-03-19 [r656]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c: Use previous segment in router rather than looking
+	  at previous node.
+
+2011-03-12 [r655]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c, src/nodesx.c: Make the used nodes marker
+	  bit-wide rather than byte-wide.
+
+2011-03-12 [r654]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.h, src/superx.c, src/nodesx.c: Make the nodes super
+	  marker bit-wide rather than byte-wide.
+
+2011-03-12 [r653]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.h, src/superx.c, src/planetsplitter.c, src/superx.h,
+	  src/nodesx.c: Make the nodes super marker a boolean.
+
+2011-03-12 [r652]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/planetsplitter.c, src/superx.h: Optimise the
+	  search for supernodes, consider traffic when counting segments
+	  that meet at a node.
+
+2011-02-27 [r651]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.h, src/superx.c, src/relationsx.c, src/waysx.c,
+	  src/segmentsx.c, src/waysx.h, src/segmentsx.h: Rename the xdata
+	  and xcached members of the nodesx, segmentsx and waysx
+	  structures.
+
+2011-02-27 [r650]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.h, src/relationsx.c, src/planetsplitter.c,
+	  src/waysx.c, src/relationsx.h, src/segmentsx.c, src/nodesx.c,
+	  src/waysx.h, src/segmentsx.h: Don't have both xnumber and number
+	  in the nodesx, segmentsx, waysx and relationsx structures.
+
+2011-02-27 [r649]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c, src/segmentsx.h: Remove a now unused array of
+	  segment indexes.
+
+2011-02-27 [r648]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/logging.c: Handle the case where the middle string is shorter
+	  than the previous one.
+
+2011-02-26 [r646-647]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c: Use the OtherNode and IsOneWay* macros when
+	  routing.
+
+	* src/superx.c, src/relationsx.c, src/segmentsx.c, src/segmentsx.h:
+	  Remove a pair of functions that are no longer used and rename the
+	  other pair.
+
+2011-02-26 [r645]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c, src/planetsplitter.c, src/relationsx.h: Fixed
+	  the turn relations with a few more functions.
+
+2011-02-26 [r644]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/planetsplitter.c, src/segmentsx.c, src/segmentsx.h: Renamed a
+	  couple of functions for clarity.
+
+2011-02-26 [r643]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.h, src/superx.c, src/relationsx.c,
+	  src/planetsplitter.c, src/osmparser.c, src/segmentsx.c,
+	  src/nodesx.c, src/segmentsx.h: Go back to the internal structure
+	  used (but reverted) during version 1.2 development where each
+	  segment is stored only once. This halves the memory usage (mmap
+	  files or just files) for planetsplitter. This is allowed because
+	  a new algorithm to create the node to segment indexes makes it
+	  simpler now that it was. This change is required so that
+	  super-node/segment optimisation doesn't remove mutual loops. This
+	  change doesn't handle turn restrictions yet.
+
+2011-02-24 [r642]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c: Change a variable name to match the one used in
+	  optimiser.c.
+
+2011-02-24 [r641]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/segmentsx.c: Create super-segments that go in
+	  loops and preserve all such loops.
+
+2011-02-23 [r640]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: Fix latent bug that can occur when
+	  de-duplicating segments.
+
+2011-02-23 [r639]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/Makefile: Fix error in creating web files containing
+	  profiles.
+
+2011-02-20 [r638]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Allow U-turns at via points for transport types
+	  that ignore turn restrictions.
+
+2011-02-20 [r637]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/functions.h, src/fakes.h: Don't allow
+	  U-turns at via points (but doesn't necessarily include turning
+	  round in the score when searching for optimum).
+
+2011-02-18 [r636]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Fix the code that stops routes doubling-back on
+	  themselves.
+
+2011-02-11 [r635]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/update-profiles.pl (added), xml/Makefile,
+	  web/www/routino, web/www/routino/router.html.en,
+	  web/www/routino/router.pl, web/www/routino/router.html.nl,
+	  web/www/routino/router.js: Move the Javascript and Perl profiles
+	  into separate files.
+
+2011-02-11 [r634]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/installation.html, web/www/openlayers/install.sh,
+	  doc/INSTALL.txt: Change to OpenLayers 2.10.
+
+2011-02-11 [r632-633]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/output.c: Don't confuse fake segments with junctions.
+
+	* src/optimiser.c: Fix problem with only one super-node in the
+	  route.
+
+2011-02-11 [r631]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/fakes.c: Fix bug with generating fake segments.
+
+2011-02-11 [r629-630]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* xml/routino-profiles.xml: Wheelchairs do not obey turn
+	  restrictions.
+
+	* web/www/routino/results.cgi, web/www/routino/router.html.en,
+	  src/router.c, web/www/routino/router.pl,
+	  web/www/routino/router.html.nl, web/www/routino/router.js: Print
+	  a message if routed OK, allow web users to see router output (now
+	  logged to file).
+
+2011-02-05 [r628]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Fix statistics for ways (broken by change for
+	  relations).
+
+2011-02-05 [r626-627]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/optimiser.c, src/segmentsx.c, src/nodesx.c:
+	  Change some output printed while running.
+
+	* src/filedumper.c: Fix problem with dumping turn relations.
+
+2011-02-05 [r625]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Print out the size of the relations.mem file.
+
+2011-02-05 [r623-624]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* web/www/routino/visualiser.cgi: Updated the visualiser to include
+	  turn restrictions.
+
+	* src/visualiser.c, doc/html/usage.html,
+	  web/www/routino/visualiser.js, src/visualiser.h,
+	  src/filedumper.c, doc/USAGE.txt, web/www/routino/visualiser.html:
+	  Updated the visualiser to include turn restrictions.
+
+2011-02-05 [r622]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/profiles.h, web/www/routino/noscript.cgi,
+	  web/www/routino/customrouter.cgi, xml/routino-profiles.xsd,
+	  web/www/routino/noscript.template.html, xml/routino-profiles.xml,
+	  doc/html/usage.html, web/www/routino/router.cgi, src/optimiser.c,
+	  web/www/routino/router.html.en, doc/USAGE.txt, src/router.c,
+	  web/www/routino/router.pl, src/profiles.c,
+	  web/www/routino/router.html.nl, web/www/routino/router.js:
+	  Include the option to obey turn restrictions in the profile for
+	  each transport type.
+
+2011-01-30 [r620-621]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/algorithm.html, doc/ALGORITHM.txt: Update algorithm
+	  description to include turn restrictions and a note about how the
+	  algorithm terminates the search.
+
+	* doc/TAGGING.txt, doc/USAGE.txt: Update text versions of documents
+	  to match HTML.
+
+2011-01-30 [r618-619]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/output.c: Correct comments.
+
+	* src/filedumper.c: Put a "restriction" tag into the turn
+	  restrictions when dumped.
+
+2011-01-30 [r617]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/functions.h, src/router.c: Ensure that the
+	  first/last node and first/last segment of the Results structure
+	  are filled in properly.
+
+2011-01-30 [r616]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Fix routing where the final node is a
+	  super-node.
+
+2011-01-29 [r615]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c: All nodes adjacent to a turn restriction must
+	  also be turn restrictions.
+
+2011-01-29 [r613-614]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/files.h: Fix assert problem.
+
+	* src/Makefile, src/xml/Makefile: Make dependency filename based on
+	  object file name (fixes overwriting problem with slim versions).
+
+2011-01-29 [r612]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/files.h, src/relationsx.c, src/waysx.c,
+	  src/segmentsx.c, src/nodesx.c, src/files.c: Ensure that record of
+	  closed file descriptors are erased.
+
+2011-01-29 [r610-611]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Don't check for turn relations in
+	  FindStartRoutes().
+
+	* src/optimiser.c: Add some comments, shuffle a few lines of code.
+
+2011-01-29 [r609]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Fix the code that allows overshooting by one
+	  node when finding finish nodes.
+
+2011-01-29 [r608]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/fakes.c, src/optimiser.c, src/Makefile, src/fakes.h,
+	  src/relations.c, src/router.c: When finding a normal route check
+	  for turn relations (considering previous segment). When finding
+	  turn relations convert fake segments into real ones.
+
+2011-01-29 [r607]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/nodes.c: Fix pathological case of rounding error for points
+	  almost exactly on a segment.
+
+2011-01-29 [r606]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c: Fix for route finding in planetsplitter.
+
+2011-01-24 [r605]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.h, src/superx.c, src/optimiser.c, src/functions.h,
+	  src/output.c, src/router.c, src/results.c: Finds routes and obeys
+	  turn restrictions (only tested with very simple route and
+	  restrictions, more turn restriction testing and regression
+	  testing required).
+
+2011-01-16 [r604]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relations.h, src/relations.c: Fix logic error with searching
+	  for via nodes.
+
+2011-01-15 [r603]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/results.h, src/superx.c, src/optimiser.c, src/output.c,
+	  src/results.c: Change the results structure to contain next
+	  segment and rename elements to clarify prev/next node and
+	  prev/next segment.
+
+2011-01-15 [r602]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c: Change to comment for clarification.
+
+2011-01-15 [r599-601]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/usage.html: Correction and clarification to filedumper
+	  usage.
+
+	* src/nodesx.h: Change to comment for clarification.
+
+	* src/relations.h, src/relationsx.c, src/planetsplitter.c,
+	  src/filedumper.c, src/relationsx.h, src/relations.c: Store the
+	  'from' and 'to' segments and not nodes (to handle fake nodes
+	  inserted in segments).
+
+2011-01-15 [r598]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/visualiser.c, src/output.c: Change the IsSuperNode() macro to
+	  take a single pointer argument.
+
+2011-01-09 [r597]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/superx.c, src/relationsx.c, src/types.h: Make the 'from' and
+	  'to' nodes of turn restrictions super-nodes.
+
+2011-01-09 [r596]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relations.h, src/optimiser.c, src/nodes.h, src/relations.c:
+	  Check turn relations when finding a route.
+
+2011-01-08 [r595]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c, src/filedumper.c, src/nodes.h: Change the
+	  IsSuperNode() macro to take a single pointer argument.
+
+2011-01-08 [r594]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/optimiser.c: Move the local variables closer to where they
+	  are used.
+
+2011-01-08 [r593]  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/html/tagging.html: Add information about the tags used for
+	  turn relations.
+
+2010-12-29  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	Changed version control environment from RCS to CVS to SVN.
+
+2010-12-29  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* doc/NEWS.txt: Temporary checkin to allow transition from RCS to
+	  CVS to SVN.
+
+	* xml/routino-tagging.xml: Pass through turn relation information.
+
+2010-12-21  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c: Add turn relations to the statistics and dump
+	  outputs.
+
+2010-12-21  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/ways.h, src/segments.h, src/nodes.h, src/ways.c: Optimise the
+	  node, segment, way lookup in slim mode by checking if the
+	  previous index is being requested again.
+
+	* src/relations.h, src/relations.c: Optimise the turn relation
+	  lookup.
+
+2010-12-21  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relations.h, src/relations.c: Add functions to search for
+	  turn relations that match a particular node.
+
+2010-12-21  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relations.h (added), src/relations.c (added): Initial
+	  revision
+
+	* src/superx.c, src/relationsx.c, src/types.h: Update the nodes to
+	  force a super-node where there is a turn restriction.
+
+2010-12-21  Andrew M. Bishop <amb at gedanken.demon.co.uk>
+
+	* src/relationsx.c, src/planetsplitter.c, src/relationsx.h: Finish
+	  the processing of the turn relations now that the extra node
+	  lookup table is in place.
+
+2010-12-20  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/nodesx.c, src/segmentsx.c, src/segmentsx.h:
+	Handle the SegmentX Segment in the same way as the other data structures (map
+	into memory when used, open/close the file if slim).  Create the real nodes
+	without mapping the segments into memory.
+
+	* src/nodesx.c, src/nodesx.h, src/relationsx.c, src/segmentsx.c, src/segmentsx.h,
+	src/waysx.h:
+	Make the PutBack*() functions be no-ops in slim mode and remove the
+	pre-processor guards from around the function calls.
+
+	* src/nodesx.c:
+	Don't map the file into memory for writing out the Nodes file.
+
+	* src/superx.c, src/waysx.c, src/waysx.h, src/nodesx.c, src/relationsx.c, src/segmentsx.c:
+	Close and open the files for the slim case to match the map/unmap of files for
+	the non-slim case.
+
+	* src/nodesx.c, src/segmentsx.c:
+	Make the last two changes work for slim mode.
+
+	* src/nodesx.c, src/nodesx.h:
+	Create the Nodes offset table at the end rather than during the sort process.
+
+	* src/nodesx.c, src/nodesx.h, src/segmentsx.c, src/segmentsx.h:
+	Don't maintain a copy of the whole set of Nodes along with the NodeXs but
+	generate the Node from the NodeX when written to disk.  Create a lookup table
+	between the original index and the geographically sorted index.
+
+2010-12-19  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/planetsplitter.c, src/relationsx.c:
+	Process the turn relations (apart from updating the indexes to the
+	geographically sorted nodes).
+
+	* src/superx.c: Handle the case of no super segments better.
+
+	* src/planetsplitter.c: Change around the order of the functions.
+
+	* src/relationsx.c:
+	A temporary check-in that handles turn restrictions more complicated than
+	actually allowed (ways must start/end at the via node).
+
+	* xml/routino-tagging.xml: Add mini-roundabouts.
+
+	* src/sorting.c: Bug fix for last change.
+
+2010-12-18  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/segmentsx.c:
+	Remove the test for sorting zero segments (now that the sort function doesn't
+	crash).
+
+	* src/nodesx.c, src/segmentsx.c, src/segmentsx.h, src/superx.c:
+	Duplicate the IndexFirstSegmentX() and IndexNextSegmentX() functions to create
+	two distinct one for use at different times.
+
+	* src/sorting.c: Handle the case where there is no data in the file.
+
+	* src/Makefile, src/filedumper.c, src/functions.h, src/optimiser.c, src/planetsplitter.c,
+	src/relationsx.c, src/relationsx.h, src/router.c, src/types.h:
+	Add a Relations data type and write out the turn relations that have been read
+	in.  Still doesn't perform the required processing after reading the data or use
+	the information for routing.
+
+2010-12-12  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c, src/typesx.h:
+	Change the names of the enumerated types for turn restrictions.
+
+2010-12-05  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/osmparser.c, src/relationsx.c, src/relationsx.h, src/typesx.h:
+	Parse turn restriction relations and store ones with a single via node.
+	(Doesn't do anything with them yet).
+
+	* src/nodesx.h, src/segmentsx.h, src/waysx.h:
+	Updated the comments for clarity.
+
+2010-12-04  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c, src/nodesx.c, src/nodesx.h, src/osmparser.c, src/types.h:
+	Improved version of previous change.
+
+2010-11-28  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/filedumper.c, src/nodesx.c, src/nodesx.h, src/osmparser.c, src/types.h:
+	Add parsing of mini-roundabouts.
+
+	* xml/routino-tagging.xml, src/filedumper.c, src/osmparser.c, src/types.c, src/types.h:
+	Remove the roundabout type from the parsing.
+
+	* src/fakes.c:
+	Fix some problems with fake nodes, in particular a route between two fake nodes
+	on the same segment can now be calculated.
+
+	* src/nodes.c:
+	Return the two nodes of a segment in the same order each time.
+
+2010-11-27  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* src/fakes.h, src/sorting.h: New file.
+
+	* src/fakes.c, src/functions.h, src/nodesx.c, src/optimiser.c, src/output.c,
+	src/relationsx.c, src/router.c, src/segmentsx.c, src/sorting.c, src/types.h, src/waysx.c:
+	Split functions.h into fakes.h, sorting.h and the remainder in functions.h.
+
+	* src/optimiser.c, src/router.c:
+	Move some of the complexity from router.c to optimiser.c.
+
+	* src/types.c, src/types.h, src/ways.h, src/waysx.c:
+	Change the wayprop_t type into properties_t.
+
+	* src/nodesx.h, src/osmparser.c, src/profiles.c, src/profiles.h, src/relationsx.c,
+	src/relationsx.h, src/types.c, src/types.h, src/ways.h, src/waysx.c, src/filedumper.c,
+	src/nodes.h, src/nodesx.c:
+	Change the allow_t type into transports_t (and associated enums and macros).
+
+	* src/types.h, src/ways.h: Change the waytype_t type into highway_t.
+
+2010-11-14  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
+
+	* xml/routino-tagging.xml:
+	Fix mis-spelling with surface=asphalt tag (patch from Michael Günnewig).
+
+	* src/filedumper.c, src/types.c, src/types.h, src/ways.h, src/waysx.c:
+	Print out statistics about what highways are included in the database.
+
 2010-11-13  Andrew M. Bishop  <amb at gedanken.demon.co.uk>
 
 	Version 1.5.1 released
@@ -2061,4 +3610,3 @@
 	* src/types.h, src/segments.c, src/router.c, src/planetsplitter.c, src/osmparser.c,
 	src/optimiser.c, src/nodes.c, src/functions.h, src/files.c, src/filedumper.c, src/Makefile:
 	New file.
-
diff --git a/Makefile b/Makefile
index d1d4250..34ee972 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,8 @@
-# $Header: /home/amb/routino/RCS/Makefile,v 1.5 2010/09/05 18:26:57 amb Exp $
-#
-# Makefile
+# Top level Makefile
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2009-2010 Andrew M. Bishop
+# This file Copyright 2009-2011 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,6 +39,13 @@ all$(top):
 
 ########
 
+test$(top): .FORCE
+	for dir in $(TOPDIRS); do \
+	   ( cd $$dir && $(MAKE) $@ ); \
+	done
+
+########
+
 install$(top): all$(top)
 	for dir in $(TOPDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
@@ -60,3 +65,7 @@ distclean$(top): clean$(top)
 	for dir in $(TOPDIRS); do \
 	   ( cd $$dir && $(MAKE) $@ ); \
 	done
+
+########
+
+.FORCE:
diff --git a/doc/ALGORITHM.txt b/doc/ALGORITHM.txt
index d61f09e..9da797f 100644
--- a/doc/ALGORITHM.txt
+++ b/doc/ALGORITHM.txt
@@ -46,9 +46,9 @@ Improved Algorithm
    At all times when looking at a node only those segments that are
    possible by the chosen means of transport are followed. This allows the
    type of transport to be handled easily. When finding the quickest route
-   the same rules apply except that criterion for sorting is the shortest
-   potential route (assuming that from each node to the end is the fastest
-   possible type of highway).
+   the same rules apply except that the criterion for sorting is the
+   shortest potential route (assuming that from each node to the end is
+   the fastest possible type of highway).
 
    This method also works, but again it isn't very fast. The problem is
    that the complexity is proportional to the number of nodes or segments
@@ -65,13 +65,15 @@ Final Algorithm
    UK at beginning of 2010) it finds one long route in a data set of
    1,000,000 nodes and a few hundred very short routes in the full data
    set. Since the time taken to find a route is proportional to the number
-   of nodes the main route takes 1/10th of the time and the very short
-   routes take almost no time at all.
+   of nodes that need to be considered the main route takes 1/10th of the
+   time and the very short routes take almost no time at all.
 
    The solution to making the algorithm fast is therefore to discard most
    of the nodes and only keep the interesting ones. In this case a node is
-   deemed to be interesting if it is the junction of two segments with
-   different properties. In the algorithm these are classed as
+   deemed to be interesting if it is the junction of three or more
+   segments or the junction of two segments with different properties or
+   has a routing restriction different from the connecting segments. In
+   the algorithm and following description these are classed as
    super-nodes. Starting at each super-node a super-segment is generated
    that finishes on another super-node and contains the shortest path
    along segments with identical properties (and these properties are
@@ -102,7 +104,37 @@ Final Algorithm
    or finish on a super-node. In these cases one or more of the steps
    listed can be removed or simplified.
 
+   When the first route reaches the final node the length of that route is
+   retained as a benchmark. Any shorter complete route that is calculated
+   later would replace this benchmark. As routes are tested any partial
+   routes that are longer than the benchmark can be immediately discarded.
+   Other partial routes have the length of a perfect straight highway to
+   the final node added to them and if the total exceeds the benchmark
+   they can also be discarded. Very quickly the number of possible routes
+   is reduced until the absolute shortest is found.
+
+   For routes that do not start or finish on a node in the original data
+   set a fake 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.
+
+
+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
+   included. Routino versions 1.4.1 to 1.5.1 used a slightly different
+   algorithm which only chose nodes that were junctions between segments
+   with different properties (or has a routing restriction that is
+   different from connecting segments in versions 1.5 and 1.5.1). The
+   addition of turn restrictions (described in more detail below) requires
+   the original algorithm since the super-segments more accurately reflect
+   the underlying topology.
+
+
 Routing Preferences
+-------------------
 
    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
@@ -123,10 +155,10 @@ Routing Preferences
           own speed limit and the user's speed preference for the type of
           highway is the starting point for the score.
 
-   Oneway restriction
-          If a highway has the oneway property in the opposite direction
+   One-way restriction
+          If a highway has the one-way property in the opposite direction
           to the desired travel and the user's preference is to obey
-          oneway restrictions then the segment is ignored.
+          one-way restrictions then the segment is ignored.
 
    Weight, height, width & length limits
           If a highway has one of these limits and its value is less than
@@ -144,18 +176,83 @@ Routing Preferences
           percentage and each highway either has that property or not. The
           user's property preference is scaled into the range 0.0 (for 0%)
           to 1.0 (for 100%) to give a weighted preference, a second
-          "non-property" weighted preference is calcuated in the same way
+          "non-property" weighted preference is calculated in the same way
           after subtracting the user's preference from 100%. If a segment
           has a particular property then the calculated score is divided
-          by the weighted preference for that property, if the segment
-          does not have this property then it is divided by the
-          non-property weighted preference. To ensure that setting
-          property preferences near 50% do not cause large variations in
-          routes the highway's preference is found by taking the square
-          root of the property preference.
-
-Implementation
---------------
+          by the weighted preference for that property, if not then it is
+          divided by the non-property weighted preference. A non-linear
+          transformation is applied so that changing property preferences
+          close to 50% do not cause large variations in routes.
+
+Turn Restrictions
+-----------------
+
+   The addition of turn restrictions adds a set of further complications
+   because it introduces a set of constraints that are far more complex
+   than one-way streets.
+
+   A turn restriction in the simplest case is a combination of a segment,
+   node and segment such that routes are not allowed to go from the first
+   segment to the second one through the specified node. Exceptions for
+   certain types of traffic can also be specified. Currently only this
+   simplest type of turn restriction is handled by the algorithm.
+
+   The first complication of turn restrictions is that the algorithm above
+   requires that super-segments are composed of segments with identical
+   properties. A turn restriction is not the same in both directions so a
+   super-segment cannot include any route through that turn restriction.
+   The node at the centre of the turn restriction must therefore be a
+   super-node to avoid this. In addition to this all nodes connected to
+   the turn restriction node by a single segment must also be super-nodes
+   to avoid any long-distance super-segments starting at the restricted
+   node.
+
+   The second complication of a turn restriction is that the optimum route
+   may require passing through the same node more than once. This can
+   happen where the route needs to work around a turn restriction by
+   driving past it, turning round (on a roundabout perhaps) and coming
+   back along the same highway. Without turn 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.
+
+   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 puts a limit on the amount of database
+   optimisation that can be performed because if too many super-segments
+   are removed the optimum work-around may also be removed. The solution
+   to this is to ensure that the database preserves all loops that can be
+   used to turn around and reverse direction, previously super-segments
+   that started and finished on the same super-node were disallowed.
+
+   Another side-effect of having the route composed of a set of locations
+   (nodes) as well as the direction of travel (segments used to reach
+   them) is that via points in the route can be forced to continue in the
+   original direction. If the chosen method of transport obeys turn
+   restrictions then it will not reverse direction at a via point but will
+   find an optimum route continuing in the same direction. The only
+   exception to this is when the route ahead at a waypoint is into a
+   dead-end and an immediate U-turn is allowed.
+
+   A side-effect of having the starting direction at a via point defined
+   by the previous part of the route is that overall non-optimal routes
+   may be found even though each section between via points is optimal.
+   For a route with a start, middle and end point defined it can be the
+   case that the shortest route from the start to the middle arrives in
+   the opposite direction to that required for the optimal route from the
+   middle to the end. The calculation of the route in separate sections
+   therefore may give a non-optimum result even though each 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.
+
+
+Data Implementation
+-------------------
 
    The hardest part of implementing this router is the data organisation.
    The arrangement of the data to minimise the number of operations
@@ -190,6 +287,12 @@ Implementation
    increase the database size (by about 10%) and are also marked with a
    flag.
 
+   The relations are stored separately from the nodes, segments and ways.
+   For the 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.
+
 
 Practicalities
 --------------
@@ -213,4 +316,4 @@ Practicalities
 
 --------
 
-Copyright 2008-2010 Andrew M. Bishop.
+Copyright 2008-2011 Andrew M. Bishop.
diff --git a/doc/DATA.txt b/doc/DATA.txt
index 8cd61ed..aeb84a6 100644
--- a/doc/DATA.txt
+++ b/doc/DATA.txt
@@ -18,9 +18,9 @@ OpenStreetMap Data
 
    Way
           A way is a collection of nodes that when joined together define
-          something (for example a road, a ralway, a boundary, a building,
-          a lake etc). The ways also have attributes that define them
-          (speed limits, type of road and restrictions for example).
+          something (for example a road, a railway, a boundary, a
+          building, a lake etc). The ways also have attributes that define
+          them (speed limits, type of road and restrictions for example).
 
    Relation
           A relation is a collection of items (usually ways) that are
@@ -61,7 +61,8 @@ Router Data
           the previous step and for each way the useful information for
           routing is stored. For the router the useful information is the
           type of highway, the speed limit, the allowed types of transport
-          and other restrictions (one-way, min height, max weight etc).
+          and other restrictions (one-way, minimum height, maximum weight
+          etc).
 
    Connections between highways
           The connections between highways are defined in the
@@ -107,7 +108,7 @@ Problems With OpenStreetMap Data
    don't because they do not share nodes.
 
    A lot of these problems can be found using the interactive data
-   visualiser that uses the same Routino rouing database.
+   visualiser that uses the same Routino routing database.
 
 
 --------
diff --git a/doc/INSTALL.txt b/doc/INSTALL.txt
index 170fc3a..2f6ec6d 100644
--- a/doc/INSTALL.txt
+++ b/doc/INSTALL.txt
@@ -116,7 +116,7 @@ Configuration of web files
    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.9.1). The files must be installed so
+   with OpenLayers library version 2.10). 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 and the files,
diff --git a/doc/Makefile b/doc/Makefile
index 08801d4..3d1dc88 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,10 +1,8 @@
-# $Header: /home/amb/routino/doc/RCS/Makefile,v 1.4 2010/09/05 18:27:07 amb Exp $
-#
 # Documentation directory Makefile
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2010 Andrew M. Bishop
+# This file Copyright 2010-2011 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,10 +26,11 @@ WEBDIR=../web/www/routino/documentation
 
 HTML_FILES=html/*
 TXT_FILES=*.txt
+TOP_FILES=../agpl-3.0.txt
 
 ########
 
-all :
+all:
 	-@[ -d $(WEBDIR) ] && \
 	  for file in $(HTML_FILES); do \
 	     if [ ! -f $(WEBDIR)/`basename $$file` ] || [ $$file -nt $(WEBDIR)/`basename $$file` ]; then \
@@ -42,9 +41,18 @@ all :
 
 ########
 
+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) ;\
diff --git a/doc/NEWS.txt b/doc/NEWS.txt
index 42bfc92..fe369ce 100644
--- a/doc/NEWS.txt
+++ b/doc/NEWS.txt
@@ -1,3 +1,128 @@
+Version 2.1 of Routino released : Sun Oct 23 2011
+-------------------------------------------------
+
+Bug fixes:
+  Speed up the routing by a factor of 5 by improving data handling functions.
+  Speed up database generation by reducing the default number of iterations.
+  Fix the handling of the 'except' tag on turn restrictions.
+  Fix the 'make install' option for the XML files.
+  Add some more typecasts when printing data from filedumper program.
+  Make the CGI script more robust if shortest/fastest is not passed in.
+
+Note: this version is compatible with databases from version 2.1.
+
+
+Version 2.1 of Routino released : Mon Oct 3 2011
+------------------------------------------------
+
+Bug fixes:
+  Fix bug in pathological cases with binary search (don't crash).
+  Make stricter checks for closest nodes just like in v2.0.3 for segments.
+  Fix routing bug where start node is a super-node and finish is close by.
+
+OSM tagging:
+  More testing of turn relations; invalid or useless ones are discarded.
+  An error log file can be generated to record parsing and processing errors.
+
+Configuration Files:
+  Add new options in the tagging rules XML file.
+  Add extra tagging rules to handle many problems found in the error log for UK.
+  Create special-use tagging rule files for walking, riding and driving.
+
+Test cases:
+  Create new test case for bug fixed in v2.0.3.
+  Save expected results to allow future regressions to be found.
+
+Note: this version is not compatible with databases from earlier versions.
+
+
+Version 2.0.3 of Routino released : Thu Aug 4 2011
+--------------------------------------------------
+
+Bug fixes:
+  Handle start node being a super-node with no previous segment (don't crash).
+  Make stricter checks against the profile when finding the closest segment.
+  Find a valid route if the start and end point are the same location.
+  Choose the better route if one with and one without super-nodes are available.
+
+Note: this version is compatible with databases from versions 2.0, 2.0.x.
+
+
+Version 2.0.2 of Routino released : Sun June 26 2011
+----------------------------------------------------
+
+Bug fixes:
+  Fix error with handling ferry routes (were ignored).
+  Force roundabouts to be one-way (was present in v1.5.1).
+  Handle super-nodes with no segments when processing (don't crash).
+
+Code improvements:
+  Use C99 standard by default and fix related warnings.
+  More code tidy-up for 32/64 bit node and index types.
+  Free some memory in various places (not serious leaks).
+
+Note: this version is compatible with databases from versions 2.0, 2.0.1.
+
+
+Version 2.0.1 of Routino released : Tue June 7 2011
+---------------------------------------------------
+
+Bug fixes:
+  Turn relations that specify missing nodes/ways are deleted (don't crash).
+  Shorten the messages printed by planetsplitter to keep below 80 characters.
+
+Code improvements:
+  Various code tidy-ups and 32/64 bit node and index improvements.
+
+OSM Tagging:
+  Check whether node/way/relation IDs fit in 32-bits (code ready for 64-bits).
+
+Note: this version is compatible with databases from version 2.0.
+
+
+Version 2.0 of Routino released : Mon May 30 2011
+-------------------------------------------------
+
+Bug fixes:
+  Fix mis-spelling with surface=asphalt tag
+  Routes between two waypoints on the same segment now work.
+  Fix reading of numeric entities from XML files (store as UTF-8 internally).
+  Fix turn description in HTML file (angles were biased to the right).
+  Fix possibility of occasionally missing turn information from output files.
+
+Test cases:
+  Added test cases for routing in slim and non-slim modes.
+
+Documentation:
+  Update documentation to reflect changes in program usage and function.
+  Install the license file in the documentation directory.
+
+OSM tagging:
+  Process the tags associated with turn restriction relations.
+  Remove the roundabout type from the parsing.
+  Add parsing of mini-roundabouts.
+
+Configuration Files:
+  Update profiles with new options related to turn restrictions.
+
+Web pages:
+  Change to OpenLayers v2.10.
+  Visualiser can display turn restrictions.
+  Put the profile information into separate files and auto-generate them.
+
+planetsplitter:
+  Store information about turn restriction relations.
+  Quite a large code re-organisation - now faster and uses less memory.
+
+router:
+  Take turn restriction relations into account when routing.
+  Continue same direction of travel at each waypoint (unless dead-end).
+  Add a new option to specify an initial direction to start travel.
+
+filedumper:
+  Print out statistics about what highways are included in the database.
+
+
 Version 1.5.1 of Routino released : Sat Nov 13 2010
 ---------------------------------------------------
 
@@ -32,7 +157,7 @@ Bug fixes:
 Documentation:
   Update documentation to reflect changes in program usage and function.
 
-OSM tagging
+OSM tagging:
   Traffic restrictions on nodes are now included in default tagging file.
   Added processing for ferry routes (as pseudo-highway type 'ferry').
   Process foot and bicycle route relations to create new properties.
@@ -42,7 +167,7 @@ Configuration Files:
   Added ferry information to profiles.
   Added foot and bicycle route relation processing.
 
-planetsplitter
+planetsplitter:
   The slim mode now includes the output data as well as the temporary data.
   The slim mode is now a separate executable and not a command line option.
   Traffic restrictions on nodes are now understood when parsing OSM files.
@@ -99,14 +224,14 @@ Bug fixes:
   Fix bug with profile preferences when optimising a route.
   Stricter check on profile validity before starting routing.
 
-planetsplitter
+planetsplitter:
   Add --parse-only and --process-only options (for incremental parsing).
   Allow filenames to be specified on command line (default is still stdin).
   Improved the '--help' information to describe all options.
   Remove --transport, --not-highway, --not-property options (use config file).
   Use tag transformation rules in configuration file not hard-coded.
 
-router
+router:
   Removed compiled-in profiles and use profiles loaded from XML file.
   Improved the '--help' information to describe all options.
   Change the name of the --profile-json and --profile-perl options.
@@ -117,11 +242,11 @@ router
   Added options to specify file of translations and language to use.
   Remove copyright.txt file and put information into translations file.
 
-filedumper
+filedumper:
   Improved the '--help' information to describe all options.
   Added the option to dump an OSM file containing database contents.
 
-Web Pages
+Web Pages:
   Combined generic map CSS into one file (not copied in two).
   Much better support for IE6/7/8 with browser detection but not perfect.
   Re-organised and tidied up the Javascript.
@@ -144,7 +269,7 @@ Bug fixes:
   Check the command line values for filedumper --dump options.
   Made the verbose output consistent between different places.
 
-OSM tagging
+OSM tagging:
   Recognise "designation" tag to determine designated paths.
   Recognise "steps" tag to determine the highway type.
   Recognise "wheelchair" tag to determine if wheelchairs are allowed on highway.
@@ -154,20 +279,20 @@ OSM tagging
   Recognise "bridge" tag to determine if a highway is a bridge.
   Recognise "tunnel" tag to determine if a highway is a tunnel.
 
-New Features
+New Features:
   Remove "bridleway" and "footway" highway types and use "path" highway instead.
   Added "steps" as a new highway type separate from the "path" type.
   Added "wheelchair" and "moped" to the list of possible transports.
   Added "paved", "multilane", "bridge", "tunnel" to list of highway properties.
 
-Web Pages
+Web Pages:
   Updated for new features listed above.
   Added popup to display instructions for each step in route on mouse-over.
   Added buttons next to waypoints for: add / remove / move up / move down.
   Highlight user selectable parts of form in yellow on mouse-over.
   A few small changes, improved CSS, improved Javascript.
 
-router
+router:
   For each waypoint choose closest point on a segment and not just closest node.
   Added the ability to set preferences based on highway properties.
   Changed the text output formats to include bearing and turn information.
@@ -176,59 +301,59 @@ router
 Version 1.2 of Routino released : Wed Oct 21 2009
 -------------------------------------------------
 
-OSM tagging
+OSM tagging:
   Recognise tags "vehicle" and "motor_vehicle".
   Handle duplicate ways in the input OSM file (e.g. concatenation of 2 files).
 
-Database
+Database:
   Identical ways are combined to reduce database size (~80% fewer ways stored).
 
-Routing
+Routing:
   Fix weight, height, width, length restriction routing.
   Allow up to 99 waypoints to be specified instead of 9.
 
-Visualiser
+Visualiser:
   Don't display speed limits for tracks and paths unless a value is set.
   Draw all super-segments that cross the selected boundary.
 
-Web Pages
+Web Pages:
   A few small changes, improved CSS, improved Javascript.
   Changed marker colour when waypoint not selected.
 
-planetsplitter
+planetsplitter:
   Optional slim mode uses minimal memory at the expense of temporary files.
 
-router
+router:
   Less CPU time for routing (~30% less).
 
-filedumper
+filedumper:
   Allow dumping individual nodes, segments and ways (for debug).
 
 
 Version 1.1 of Routino released : Sat Jun 13 2009
 -------------------------------------------------
 
-Inputs
+Inputs:
   Improve parsing of OSM file (imperial units).
   Ignore nodes that are missing from the input OSM file.
 
-Outputs
+Outputs:
   Create GPX route files as well as GPX track files.
   Read in an optional copyright.txt file and include contents in output.
   Make better choices about what to output in the abbreviated text file.
 
-Routing
+Routing:
   Allow generating a route with intermediate waypoints.
   Use preferences for highway types instead of yes/no choice.
   Choice of closest node to start/finish points ensures transport allowed.
 
-Visualiser
+Visualiser:
   Added data extraction function for viewing routing database data.
 
-Web Pages
+Web Pages:
   Include full set of web pages for creating customised online router.
 
-Documentation
+Documentation:
   Included NEWS.txt file.
   Included documentation for installation of web pages.
 
diff --git a/doc/README.txt b/doc/README.txt
index 875c02e..215f37a 100644
--- a/doc/README.txt
+++ b/doc/README.txt
@@ -19,7 +19,7 @@
    recorded and these set default limits on the types of traffic allowed.
    More specific information about permissions for different types of
    transport are also recorded as are maximum speed limits. Further
-   restrictions like oneway streets, weight, height, width and length
+   restrictions like one-way streets, weight, height, width and length
    limits are also included where specified. Additionally a set of
    properties of each highway are also recorded. The processing of the
    input file is controlled by a configuration file which determines the
@@ -33,7 +33,9 @@
    default and any specified in the original data). To make use of the
    information about restrictions the weight, height, width and length of
    the transport can also be specified. Further preferences about road
-   properties (e.g. paved or not) can also be selected.
+   properties (e.g. paved or not) can also be selected. The simplest type
+   of turn restrictions (those formed from an initial way, a node and a
+   second way) are also obeyed.
 
    The result of calculating the route can be presented in several
    different ways. An HTML file can be produced that contains a
@@ -105,6 +107,10 @@ Status
    Version 1.4.1 of Routino was released on 10th July 2010.
    Version 1.5 of Routino was released on 30th October 2010.
    Version 1.5.1 of Routino was released on 13th November 2010.
+   Version 2.0 of Routino was released on 30th May 2011.
+   Version 2.0.1 of Routino was released on 7th June 2011.
+   Version 2.0.2 of Routino was released on 26th June 2011.
+   Version 2.0.3 of Routino was released on 4th August 2011.
 
    The full version history is available in the NEWS.txt file.
 
@@ -126,7 +132,7 @@ License
 Copyright
 ---------
 
-   Routino is copyright Andrew M. Bishop 2008-2010.
+   Routino is copyright Andrew M. Bishop 2008-2011.
 
    Contact amb at gedanken.demon.co.uk for any questions or queries.
 
@@ -149,4 +155,4 @@ Download
 
 --------
 
-Copyright 2008-2010 Andrew M. Bishop.
+Copyright 2008-2011 Andrew M. Bishop.
diff --git a/doc/TAGGING.txt b/doc/TAGGING.txt
index f5ee6d2..7036802 100644
--- a/doc/TAGGING.txt
+++ b/doc/TAGGING.txt
@@ -29,7 +29,7 @@ Node Tags And Attributes
 ------------------------
 
    The node attributes id, latitude and longitude are used. The id
-   atribute is required to associate the node with the ways and the
+   attribute is required to associate the node with the ways and the
    position attributes are required to locate the node.
 
 Transport Specific Tags
@@ -103,13 +103,6 @@ The ref Tag
    The ref tag is used to provide the label for the highway when printing
    the results.
 
-The junction Tag
-- - - - - - - -
-
-   The junction tag is used to check if a highway is (part of) a
-   roundabout. This tag is used for information to label the highway if no
-   other name is provided.
-
 The multilane Tag
 - - - - - - - - -
 
@@ -192,9 +185,10 @@ The maxwidth Tag
 - - - - - - - -
 
    The maxwidth tag is used to specify the maximum width of any traffic on
-   the highway. This must be set to the minimum width of the contraints at
-   the 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.
+   the highway. This must be set to the minimum width of the constraints
+   at the 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.
 
 The maxlength Tag
 - - - - - - - - -
@@ -226,6 +220,15 @@ The bicycleroute Tag
    bicycle route and therefore should be applied to the individual member
    highways.
 
+The type, restriction & except Tags
+- - - - - - - - - - - - - - - - - -
+
+   For turn relations the information about the allowed or disallowed
+   turns are stored in the type, restriction and except tags. For a turn
+   restriction the type must be equal to "restriction", the restriction
+   must define the type of turn relation and except defines transport
+   types which are exempt from the restriction.
+
 
 Tag Transformations
 -------------------
@@ -325,7 +328,7 @@ Highway Defaults
    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     yes        no      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
    actually labelled as a highway of type "bridleway" or certain values of
@@ -446,7 +449,14 @@ Routes
    bicycle                      no                 yes
    bicycle;foot or foot;bicycle yes                yes
 
+Turn Restrictions
+- - - - - - - - -
+
+   No tag transformations are defined for turn restriction relations but
+   the type, restriction and except tags are passed through without
+   change.
+
 
 --------
 
-Copyright 2008-2010 Andrew M. Bishop.
+Copyright 2008-2011 Andrew M. Bishop.
diff --git a/doc/USAGE.txt b/doc/USAGE.txt
index fd8be31..cdabfe6 100644
--- a/doc/USAGE.txt
+++ b/doc/USAGE.txt
@@ -22,7 +22,7 @@ planetsplitter
                         [--sort-ram-size=<size>]
                         [--tmpdir=<dirname>]
                         [--parse-only | --process-only]
-                        [--loggable]
+                        [--loggable] [--errorlog[=<name>]]
                         [--max-iterations=<number>]
                         [--tagging=<filename>]
                         [<filename.osm> ...]
@@ -59,11 +59,16 @@ planetsplitter
    --loggable
           Print progress messages that are suitable for logging to a file;
           normally an incrementing counter is printed which is more
-          suitable for realtime display than logging.
+          suitable for real-time display than logging.
+
+   --errorlog[=<name>]
+          Log OSM parsing and processing errors to 'error.log' or the
+          specified file name (the '--dir' and '--prefix' options are
+          applied).
 
    --max-iterations=<number>
           The maximum number of iterations to use when generating
-          super-nodes and super-segments. Defaults to 10 which is normally
+          super-nodes and super-segments. Defaults to 5 which is normally
           enough.
 
    --tagging=<filename>
@@ -80,8 +85,7 @@ planetsplitter
 
    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 unwwanted
-   data.
+   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
@@ -122,10 +126,11 @@ router
                 --lon1=<longitude> --lat1=<latitude>
                 --lon2=<longitude> --lon2=<latitude>
                 [ ... --lon99=<longitude> --lon99=<latitude>]
+                [--heading=<bearing>]
                 [--highway-<highway>=<preference> ...]
                 [--speed-<highway>=<speed> ...]
                 [--property-<property>=<preference> ...]
-                [--oneway=(0|1)]
+                [--oneway=(0|1)] [--turns=(0|1)]
                 [--weight=<weight>]
                 [--height=<height>] [--width=<width>] [--length=<length>]
 
@@ -180,7 +185,7 @@ router
    --loggable
           Print progress messages that are suitable for logging to a file;
           normally an incrementing counter is printed which is more
-          suitable for realtime display than logging.
+          suitable for real-time display than logging.
 
    --quiet
           Don't generate any screen output while running (useful for
@@ -242,6 +247,11 @@ router
           sequence. The algorithm will use the closest node or point
           within a segment that allows the specified traffic type.
 
+   --heading=<bearing>
+          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.
+
    --highway-<highway>=<preference>
           Selects the percentage preference for using each particular type
           of highway. The value of <highway> can be selected from:
@@ -287,6 +297,11 @@ router
           (useful to not obey them when walking). Default value depends on
           the profile selected by the --transport option.
 
+   --turns=[0|1]
+          Selects if turn restrictions are to be obeyed (useful to not
+          obey them when walking). Default value depends on the profile
+          selected by the --transport option.
+
    --weight=<weight>
           Specifies the weight of the mode of transport in tonnes; ensures
           that the weight limit on the highway is not exceeded. Default
@@ -353,7 +368,8 @@ filedumper
                                   --data=<data-type>]
                     [--dump [--node=<node> ...]
                             [--segment=<segment> ...]
-                            [--way=<way> ...]]
+                            [--way=<way> ...]
+                            [--turn-relation=<relation> ...]]
                     [--dump-osm [--no-super]
                                 [--latmin=<latmin> --latmax=<latmax>
                                  --lonmin=<lonmin> --lonmax=<lonmax>]]
@@ -388,6 +404,7 @@ filedumper
                o junctions = segment count at each junction.
                o super = super-node and super-segments.
                o oneway = oneway segments.
+               o turns = turn restrictions.
                o speed = speed limits.
                o weight = weight limits.
                o height = height limits.
@@ -397,7 +414,8 @@ filedumper
    --dump
           Selects a data dumping mode which allows looking at individual
           items in the databases (specifying 'all' instead of a number
-          dumps all of them).
+          dumps all of them). More than one of the following parameters
+          can be specified on the command line.
 
         --node=<node>
                 Prints the information about the selected node number
@@ -412,6 +430,11 @@ filedumper
                 (internal number, not the way id number in the original
                 source file).
 
+        --turn-relation=<relation>
+                Prints the information about the selected turn relation
+                number (internal number, not the relation id number in the
+                original source file).
+
    --osm-dump
           Dumps the contents of the database as an OSM format XML file,
           the whole database will be dumped unless the latitude and
@@ -431,6 +454,7 @@ filedumper
    that operates in slim mode. In slim mode the database files are read as
    needed rather than being mapped into memory.
 
+
 tagmodifier
 -----------
 
@@ -448,7 +472,7 @@ tagmodifier
    --loggable
           Print progress messages that are suitable for logging to a file;
           normally an incrementing counter is printed which is more
-          suitable for realtime display than logging.
+          suitable for real-time display than logging.
 
    --tagging=<filename>
           The name of the XML file containing the tagging rules (defaults
@@ -461,4 +485,4 @@ tagmodifier
 
 --------
 
-Copyright 2008-2010 Andrew M. Bishop.
+Copyright 2008-2011 Andrew M. Bishop.
diff --git a/doc/html/algorithm.html b/doc/html/algorithm.html
index 3890b77..2552693 100644
--- a/doc/html/algorithm.html
+++ b/doc/html/algorithm.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -87,8 +87,8 @@ found.
 At all times when looking at a node only those segments that are possible by the
 chosen means of transport are followed.  This allows the type of transport to be
 handled easily.  When finding the quickest route the same rules apply except
-that criterion for sorting is the shortest potential route (assuming that from
-each node to the end is the fastest possible type of highway).
+that the criterion for sorting is the shortest potential route (assuming that
+from each node to the end is the fastest possible type of highway).
 <p>
 This method also works, but again it isn't very fast.  The problem is that the
 complexity is proportional to the number of nodes or segments in all routes
@@ -102,24 +102,42 @@ but with an important difference.  Instead of finding a long route among a data
 set of 8,000,000 nodes (number of highway nodes in UK at beginning of 2010) it
 finds one long route in a data set of 1,000,000 nodes and a few hundred very
 short routes in the full data set.  Since the time taken to find a route is
-proportional to the number of nodes the main route takes 1/10th of the time and
-the very short routes take almost no time at all.
+proportional to the number of nodes that need to be considered the main route
+takes 1/10th of the time and the very short routes take almost no time at all.
 <p>
 The solution to making the algorithm fast is therefore to discard most of the
 nodes and only keep the interesting ones.  In this case a node is deemed to be
-interesting if it is the junction of two segments with different properties.  In
-the algorithm these are classed as <em>super-nodes</em>.  Starting at each
-super-node a <em>super-segment</em> is generated that finishes on another
-super-node and contains the <em>shortest</em> path along segments with identical
-properties (and these properties are inherited by the super-segment).  The point
-of 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>
-<img alt="Original data" src="example0.png"><br>
-<img alt="Iteration 1" src="example1.png"><br>
-<img alt="Iteration 2" src="example2.png"><br>
+interesting if it is the junction of three or more segments or the junction of
+two segments with different properties or has a routing restriction different
+from the connecting segments.  In the algorithm and following description these
+are classed as <em>super-nodes</em>.  Starting at each super-node a
+<em>super-segment</em> is generated that finishes on another super-node and
+contains the <em>shortest</em> path along segments with identical properties
+(and these properties are inherited by the super-segment).  The point of
+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">
+<img alt="Original data" src="example0.png">
+<br>
+Original Highways
+<p align="center">
+<img alt="Iteration 1" src="example1.png">
+<br>
+First Iteration
+<p align="center">
+<img alt="Iteration 2" src="example2.png">
+<br>
+Second Iteration
+<p align="center">
+<img alt="Iteration 3" src="example3.png">
+<br>
+Third Iteration
+<p align="center">
+<img alt="Iteration 4" src="example4.png">
+<br>
+Fourth Iteration
 <p>
 To find a route between a start and finish point now comprises the following
 steps (assuming a shortest route is required):
@@ -140,6 +158,30 @@ result that still contains the full list of nodes that are visited.  There are
 some special cases though, for example very short routes that do not pass
 through any super-nodes, or routes that start or finish on a super-node.  In
 these cases one or more of the steps listed can be removed or simplified.
+<p>
+When the first route reaches the final node the length of that route is retained
+as a benchmark.  Any shorter complete route that is calculated later would
+replace this benchmark.  As routes are tested any partial routes that are longer
+than the benchmark can be immediately discarded.  Other partial routes have the
+length of a perfect straight highway to the final node added to them and if the
+total exceeds the benchmark they can also be discarded.  Very quickly the number
+of possible routes is reduced until the absolute shortest is found.
+<p>
+For routes that do not start or finish on a node in the original data set a fake
+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>
+
+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
+1.4.1 to 1.5.1 used a slightly different algorithm which only chose nodes that
+were junctions between segments with different properties (or has a routing
+restriction that is different from connecting segments in versions 1.5 and
+1.5.1).  The addition of turn restrictions (described in more detail below)
+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>
 
@@ -159,9 +201,9 @@ minimising the length.
   length of the segment and the lower of the highway's own speed limit and the
   user's speed preference for the type of highway is the starting point for the
   score.
-  <dt>Oneway restriction
-  <dd>If a highway has the oneway property in the opposite direction to the
-  desired travel and the user's preference is to obey oneway restrictions then
+  <dt>One-way restriction
+  <dd>If a highway has the one-way property in the opposite direction to the
+  desired travel and the user's preference is to obey one-way restrictions then
   the segment is ignored.
   <dt>Weight, height, width & length limits
   <dd>If a highway has one of these limits and its value is less than the user's
@@ -175,17 +217,73 @@ minimising the length.
   <dd>The other highway properties are specified by the user as a percentage and
   each highway either has that property or not.  The user's property preference
   is scaled into the range 0.0 (for 0%) to 1.0 (for 100%) to give a weighted
-  preference, a second "non-property" weighted preference is calcuated in the
+  preference, a second "non-property" weighted preference is calculated in the
   same way after subtracting the user's preference from 100%.  If a segment has
   a particular property then the calculated score is divided by the weighted
-  preference for that property, if the segment does not have this property then
-  it is divided by the non-property weighted preference.  To ensure that setting
-  property preferences near 50% do not cause large variations in routes the
-  highway's preference is found by taking the square root of the property
-  preference.
+  preference for that property, if not then it is divided by the non-property
+  weighted preference.  A non-linear transformation is applied so that changing
+  property preferences close to 50% do not cause large variations in routes.
 </dl>
 
-<h3><a name="H_1_1_5"></a>Implementation</h3>
+<h3><a name="H_1_1_5"></a>Turn Restrictions</h3>
+
+The addition of turn restrictions adds a set of further complications because it
+introduces a set of constraints that are far more complex than one-way streets.
+<p>
+A turn restriction in the simplest case is a combination of a segment, node and
+segment such that routes are not allowed to go from the first segment to the
+second one through the specified node.  Exceptions for certain types of traffic
+can also be specified.  Currently only this simplest type of turn restriction is
+handled by the algorithm.
+<p>
+The first complication of turn restrictions is that the algorithm above requires
+that super-segments are composed of segments with identical properties.  A turn
+restriction is not the same in both directions so a super-segment cannot include
+any route through that turn restriction.  The node at the centre of the turn
+restriction must therefore be a super-node to avoid this.  In addition to this
+all nodes connected to the turn restriction node by a single segment must also
+be super-nodes to avoid any long-distance super-segments starting at the
+restricted node.
+<p>
+The second complication of a turn restriction is that the optimum route may
+require passing through the same node more than once.  This can happen where the
+route needs to work around a turn restriction by driving past it, turning round
+(on a roundabout perhaps) and coming back along the same highway.  Without turn
+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.
+<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
+puts a limit on the amount of database optimisation that can be performed
+because if too many super-segments are removed the optimum work-around may also
+be removed.  The solution to this is to ensure that the database preserves all
+loops that can be used to turn around and reverse direction, previously
+super-segments that started and finished on the same super-node were disallowed.
+<p>
+Another side-effect of having the route composed of a set of locations (nodes)
+as well as the direction of travel (segments used to reach them) is that via
+points in the route can be forced to continue in the original direction.  If the
+chosen method of transport obeys turn restrictions then it will not reverse
+direction at a via point but will find an optimum route continuing in the same
+direction.  The only exception to this is when the route ahead at a waypoint is
+into a dead-end and an immediate U-turn is allowed.
+<p>
+A side-effect of having the starting direction at a via point defined by the
+previous part of the route is that overall non-optimal routes may be found even
+though each section between via points is optimal.  For a route with a start,
+middle and end point defined it can be the case that the shortest route from the
+start to the middle arrives in the opposite direction to that required for the
+optimal route from the middle to the end.  The calculation of the route in
+separate sections therefore may give a non-optimum result even though each
+section is itself optimum based on the start conditions.
+<p>
+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_4"></a>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
@@ -217,6 +315,12 @@ with the segments.  For the nodes they are the same as the normal nodes, so just
 a flag is needed to indicate that they are super.  The super-segments are in
 addition to the normal segments so they increase the database size (by about
 10%) and are also marked with a flag.
+<p>
+The relations are stored separately from the nodes, segments and ways.  For the
+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_6"></a>Practicalities</h3>
 
diff --git a/doc/html/configuration.html b/doc/html/configuration.html
index 2f3ac77..93a60cf 100644
--- a/doc/html/configuration.html
+++ b/doc/html/configuration.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
diff --git a/doc/html/data.html b/doc/html/data.html
index 52f74aa..4ddad96 100644
--- a/doc/html/data.html
+++ b/doc/html/data.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -60,7 +60,7 @@ The data that is collected by the OpenStreetMap project consists of
       example).
   <dt>Way
   <dd>A way is a collection of nodes that when joined together define something
-      (for example a road, a ralway, a boundary, a building, a lake etc).  The
+      (for example a road, a railway, a boundary, a building, a lake etc).  The
       ways also have attributes that define them (speed limits, type of road and
       restrictions for example).
   <dt>Relation
@@ -97,7 +97,7 @@ connections between the highways and the properties of those highways.
       previous step and for each way the useful information for routing is
       stored.  For the router the useful information is the type of highway, the
       speed limit, the allowed types of transport and other restrictions
-      (one-way, min height, max weight etc).
+      (one-way, minimum height, maximum weight etc).
   <dt>Connections between highways
   <dd>The connections between highways are defined in the OpenStreetMap data by
       ways that share nodes.  Since the ways may join in the middle and not just
@@ -140,7 +140,7 @@ important tags for routing (e.g.  speed limits).  There can also be problems
 with highways that should join but don't because they do not share nodes.
 <p>
 A lot of these problems can be found using the interactive data visualiser that
-uses the same Routino rouing database.
+uses the same Routino routing database.
 
 
 </div>
diff --git a/doc/html/example0.png b/doc/html/example0.png
index 4401486..1766a44 100644
Binary files a/doc/html/example0.png and b/doc/html/example0.png differ
diff --git a/doc/html/example1.png b/doc/html/example1.png
index be0048c..8c5b085 100644
Binary files a/doc/html/example1.png and b/doc/html/example1.png differ
diff --git a/doc/html/example2.png b/doc/html/example2.png
index c4746d8..bd6608a 100644
Binary files a/doc/html/example2.png and b/doc/html/example2.png differ
diff --git a/doc/html/example3.png b/doc/html/example3.png
new file mode 100644
index 0000000..6828842
Binary files /dev/null and b/doc/html/example3.png differ
diff --git a/doc/html/example4.png b/doc/html/example4.png
new file mode 100644
index 0000000..b0c196e
Binary files /dev/null and b/doc/html/example4.png differ
diff --git a/doc/html/index.html b/doc/html/index.html
index 2450127..b66ff11 100644
--- a/doc/html/index.html
+++ b/doc/html/index.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,7 +77,7 @@ for using the four programs are provided.
 When the programs are run they read in one or more
 <a href="configuration.html" title="Configuration Files">configuration files</a>.
 These files contain information about the routing preferences (types of highways,
-prefered speeds etc), tagging rules and translation information for the outputs.
+preferred speeds etc), tagging rules and translation information for the outputs.
 
 
 <h2><a name="H_1_5"></a>Output Files</h2>
diff --git a/doc/html/installation.html b/doc/html/installation.html
index faa752f..0096f22 100644
--- a/doc/html/installation.html
+++ b/doc/html/installation.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -177,7 +177,7 @@ to be within the web server accessible directory.
 
 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.9.1).
+(This version of Routino has been tested with OpenLayers library version 2.10).
 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
diff --git a/doc/html/readme.html b/doc/html/readme.html
index 8dd0712..4f4d5ac 100644
--- a/doc/html/readme.html
+++ b/doc/html/readme.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -66,7 +66,7 @@ bicycle, horse, motorcar, motorbike etc).
 When processing the OpenStreetMap data the types of highways are recorded and
 these set default limits on the types of traffic allowed.  More specific
 information about permissions for different types of transport are also recorded
-as are maximum speed limits.  Further restrictions like oneway streets, weight,
+as are maximum speed limits.  Further restrictions like one-way streets, weight,
 height, width and length limits are also included where specified. Additionally
 a set of properties of each highway are also recorded.  The processing of the
 input file is controlled by a configuration file which determines the
@@ -81,7 +81,9 @@ For each type of highway a default speed limit is defined (although the actual
 speed used will be the lowest of the default and any specified in the original
 data).  To make use of the information about restrictions the weight, height,
 width and length of the transport can also be specified.  Further preferences
-about road properties (e.g. paved or not) can also be selected.
+about road properties (e.g. paved or not) can also be selected.  The simplest
+type of turn restrictions (those formed from an initial way, a node and a second
+way) are also obeyed.
 
 <p>
 
@@ -158,76 +160,38 @@ Version 1.4.1 of Routino was released on 10th July 2010.
 Version 1.5 of Routino was released on 30th October 2010.
 <br>
 Version 1.5.1 of Routino was released on 13th November 2010.
+<br>
+Version 2.0 of Routino was released on 30th May 2011.
+<br>
+Version 2.0.1 of Routino was released on 7th June 2011.
+<br>
+Version 2.0.2 of Routino was released on 26th June 2011.
+<br>
+Version 2.0.3 of Routino was released on 4th August 2011.
+<br>
+Version 2.1 of Routino was released on 3rd October 2011.
+<br>
+Version 2.1.1 of Routino was released on 23rd October 2011.
 
 <p>
 
 The full version history is available in the NEWS file.
 
 
-<h3><a name="H_1_5_1" title="Changes"></a>Changes in Version 1.5</h3>
-
-<dl>
-  <dt>Bug fixes:
-  <dd>Check that number of nodes/segments/ways doesn't exceed numerical limits.
-  <br>Allow 32-bit systems to seek within files larger than 4GB.
-  <br>Allow nearly 4G nodes to be stored instead of 2G before.
-  <br>Added rules to makefile for installation (paths specified in top-level).
-  <br>Stricter checking of UTF-8 in XML files and better UTF-8 output.
-  <br>Improve error message if parsing of command line options fail.
-  <br>Fix bugs in router's --help-profile-json and --help-profile-perl options.
-  <br>Rename heapsort function to allow compilation on Mac OS with no change.
-  <br>Reduce impact of property preferences close to 50% by using sqrt().
-
-  <dt>Documentation:
-  <dd>Update documentation to reflect changes in program usage and function.
-
-  <dt>OSM tagging
-  <dd>Traffic restrictions on nodes are now included in default tagging file.
-  <br>Added processing for ferry routes (as pseudo-highway type 'ferry').
-  <br>Process foot and bicycle route relations to create new properties.
-
-  <dt>Configuration Files:
-  <dd>Added Dutch output translations.
-  <br>Added ferry information to profiles.
-  <br>Added foot and bicycle route relation processing.
-
-  <dt>planetsplitter
-  <dd>The slim mode now includes the output data as well as the temporary data.
-  <br>The slim mode is now a separate executable and not a command line option.
-  <br>Traffic restrictions on nodes are now understood when parsing OSM files.
-  <br>Falls back to installed tagging.xml configuration file as last resort.
-
-  <dt>router:
-  <dd>Added a slim mode (as a separate executable and not a command line option).
-  <br>Traffic will not be routed through a node that does not allow it.
-  <br>Falls back to installed profiles.xml & translations.xml files as last resort.
-
-  <dt>filedumper:
-  <dd>Added a slim mode (as a separate executable and not a command line option).
-
-  <dt>Web pages:
-  <dd>Added Dutch translation of router.html.
-</dl>
-
-<h3><a name="H_1_5_2"></a>Additional Changes in Version 1.5.1</h3>
+<h3><a name="H_1_5_1" title="Changes"></a>Changes in Version 2.1.1</h3>
 
 <dl>
   <dt>Bug fixes:
-  <dd>Ensure that enough memory is allocated for filenames.
-  <br>Fix bug that sometimes causes crash when processing route relations.
-
-  <dt>Documentation:
-  <dd>Update documentation to reflect changes in program usage and function.
-
-  <dt>Programs:
-  <dd>Add an option to make the output more suitable for a log file.
-
-  <dt>Documentation:
-  <dd>Update documentation to reflect changes in program usage.
+  <dd>Speed up the routing by a factor of 5 by improving data handling functions.
+  <br>Speed up database generation by reducing the default number of iterations.
+  <br>Fix the handling of the 'except' tag on turn restrictions.
+  <br>Fix the 'make install' option for the XML files.
+  <br>Add some more typecasts when printing data from filedumper program.
+  <br>Make the CGI script more robust if shortest/fastest is not passed in.
 </dl>
 
 
-<h3><a name="H_1_5_3"></a>License</h3>
+<h3><a name="H_1_5_2"></a>License</h3>
 
 This program is free software: you can redistribute it and/or modify it under
 the terms of the
@@ -243,9 +207,9 @@ additional requirements to anybody who provides a networked service using this
 software.
 
 
-<h3><a name="H_1_5_4"></a>Copyright</h3>
+<h3><a name="H_1_5_3"></a>Copyright</h3>
 
-Routino is copyright Andrew M. Bishop 2008-2010.
+Routino is copyright Andrew M. Bishop 2008-2011.
 
 
 <h2><a name="H_1_6"></a>Download</h2>
diff --git a/doc/html/tagging.html b/doc/html/tagging.html
index 8565511..3227659 100644
--- a/doc/html/tagging.html
+++ b/doc/html/tagging.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -76,7 +76,7 @@ are recognised as being affirmative and any other value is ignored.
 <h3><a name="H_1_2_1" title="Nodes"></a>Node Tags And Attributes</h3>
 
 The node attributes <em>id</em>, <em>latitude</em> and <em>longitude</em> are
-used.  The id atribute is required to associate the node with the ways and the
+used.  The id attribute is required to associate the node with the ways and the
 position attributes are required to locate the node.
 
 
@@ -94,6 +94,12 @@ 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>
+
+The <em>highway</em> tag for <em>mini_roundabout</em> is recognised since this
+provides information about places to turn around to bypass turn restrictions.
+
+
 <h3><a name="H_1_2_2" title="Ways"></a>Way Tags And Attributes</h3>
 
 The tags from the ways in the data are the ones that provide most of the
@@ -102,7 +108,7 @@ many segments associated with a way can be share a set of tags taken from the
 way.
 
 
-<h4><a name="H_1_2_2_1" title="highway"></a>The highway Tag</h4>
+<h4><a name="H_1_2_2_1" title="highway (way)"></a>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
@@ -165,13 +171,6 @@ 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="junction"></a>The junction Tag</h4>
-
-The <em>junction</em> tag is used to check if a highway is (part of) a
-roundabout.  This tag is used for information to label the highway if no other
-name is provided.
-
-
 <h4><a name="H_1_2_2_6" title="multilane"></a>The multilane Tag</h4>
 
 The <em>multilane</em> tag is used to identify whether a highway has multiple
@@ -251,7 +250,7 @@ to metres.
 <h4><a name="H_1_2_2_16" title="maxwidth"></a>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 contraints at the
+the highway.  This must be set to the minimum width of the constraints at the
 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.
 
@@ -283,6 +282,16 @@ 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>
+
+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
+a turn restriction the <em>type</em> must be equal to "restriction",
+the <em>restriction</em> must define the type of turn relation
+and <em>except</em> defines transport types which are exempt from the
+restriction.
+
+
 <h2><a name="H_1_3" title="Tag Transformations"></a>Tag Transformations</h2>
 
 This section describes the set of tag transformations that are contained in the
@@ -823,6 +832,14 @@ table below.
     <td class="center">yes
 </table>
 
+
+<h4><a name="H_1_3_3_2" title="Turn Restrictions"></a>Turn Restrictions</h4>
+
+No tag transformations are defined for turn restriction relations but
+the <em>type</em>, <em>restriction</em> and <em>except</em> tags are passed
+through without change.
+
+
 </div>
 
 <!-- Content End -->
diff --git a/doc/html/usage.html b/doc/html/usage.html
index 873a5c3..dab7bf9 100644
--- a/doc/html/usage.html
+++ b/doc/html/usage.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -65,7 +65,7 @@ Usage: planetsplitter [--help]
                       [--sort-ram-size=<size>]
                       [--tmpdir=<dirname>]
                       [--parse-only | --process-only]
-                      [--loggable]
+                      [--loggable] [--errorlog[=<name>]]
                       [--max-iterations=<number>]
                       [--tagging=<filename>]
                       [<filename.osm> ...]
@@ -95,11 +95,14 @@ Usage: planetsplitter [--help]
     routing database.
   <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 realtime
+    an incrementing counter is printed which is more suitable for real-time
     display than logging.
+  <dt>--errorlog[=<name>]
+  <dd>Log OSM parsing and processing errors to 'error.log' or the specified file
+    name (the '--dir' and '--prefix' options are applied).
   <dt>--max-iterations=<number>
   <dd>The maximum number of iterations to use when generating super-nodes and
-    super-segments.  Defaults to 10 which is normally enough.
+    super-segments.  Defaults to 5 which is normally enough.
   <dt>--tagging=<filename>
   <dd>Sets the filename containing the list of tagging rules in XML format for
     the parsing the input files.  If the file doesn't exist then dirname, prefix
@@ -114,7 +117,7 @@ Usage: planetsplitter [--help]
 <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 unwwanted data.</i>
+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
@@ -158,10 +161,11 @@ Usage: router [--help | --help-profile | --help-profile-xml |
               --lon1=<longitude> --lat1=<latitude>
               --lon2=<longitude> --lon2=<latitude>
               [ ... --lon99=<longitude> --lon99=<latitude>]
+              [--heading=<bearing>]
               [--highway-<highway>=<preference> ...]
               [--speed-<highway>=<speed> ...]
               [--property-<property>=<preference> ...]
-              [--oneway=(0|1)]
+              [--oneway=(0|1)] [--turns=(0|1)]
               [--weight=<weight>]
               [--height=<height>] [--width=<width>] [--length=<length>]
 </pre>
@@ -205,7 +209,7 @@ Usage: router [--help | --help-profile | --help-profile-xml |
     (quicker but less accurate unless the points are already near nodes).
   <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 realtime
+    an incrementing counter is printed which is more suitable for real-time
     display than logging.
   <dt>--quiet
   <dd>Don't generate any screen output while running (useful for running in a script).
@@ -256,6 +260,9 @@ 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>--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.
   <dt>--highway-<highway>=<preference>
   <dd>Selects the percentage preference for using each particular type of
       highway.  The value of <highway> can be selected from:
@@ -295,6 +302,10 @@ Usage: router [--help | --help-profile | --help-profile-xml |
   <dd>Selects if the direction of oneway streets are to be obeyed (useful to not
       obey them when walking).  Default value depends on the profile selected by
       the --transport option.
+  <dt>--turns=[0|1]
+  <dd>Selects if turn restrictions are to be obeyed (useful to not obey them
+      when walking).  Default value depends on the profile selected by the
+      --transport option.
   <dt>--weight=<weight>
   <dd>Specifies the weight of the mode of transport in tonnes; ensures that the
       weight limit on the highway is not exceeded.  Default value depends on the
@@ -361,7 +372,8 @@ Usage: filedumper [--help]
                                 --data=<data-type>]
                   [--dump [--node=<node> ...]
                           [--segment=<segment> ...]
-                          [--way=<way> ...]]
+                          [--way=<way> ...]
+                          [--turn-relation=<relation> ...]]
                   [--dump-osm [--no-super]
                               [--latmin=<latmin> --latmax=<latmax>
                                --lonmin=<lonmin> --lonmax=<lonmax>]]
@@ -391,6 +403,7 @@ Usage: filedumper [--help]
           <li>junctions = segment count at each junction.
           <li>super     = super-node and super-segments.
           <li>oneway    = oneway segments.
+          <li>turns     = turn restrictions.
           <li>speed     = speed limits.
           <li>weight    = weight limits.
           <li>height    = height limits.
@@ -401,6 +414,8 @@ Usage: filedumper [--help]
   <dt>--dump
   <dd>Selects a data dumping mode which allows looking at individual items in
     the databases (specifying 'all' instead of a number dumps all of them).
+    More than one of the following parameters can be specified on the command
+    line.
     <dl>
       <dt>--node=<node>
       <dd>Prints the information about the selected node number (internal
@@ -410,6 +425,10 @@ Usage: filedumper [--help]
       <dt>--way=<way>
       <dd>Prints the information about the selected way number (internal
         number, not the way id number in the original source file).
+      <dt>--turn-relation=<relation>
+      <dd>Prints the information about the selected turn relation number
+        (internal number, not the relation id number in the original source
+        file).
     </dl>
   <dt>--osm-dump
   <dd>Dumps the contents of the database as an OSM format XML file, the whole
@@ -449,7 +468,7 @@ Usage: tagmodifier [--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 realtime
+    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
diff --git a/src/Makefile b/src/Makefile
index 76a4ea8..a9397c0 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,10 +1,8 @@
-# $Header: /home/amb/routino/src/RCS/Makefile,v 1.43 2010/11/13 14:22:28 amb Exp $
-#
 # Source code Makefile
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2010 Andrew M. Bishop
+# This file Copyright 2008-2011 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,18 +31,18 @@ LEX=flex
 
 # Compilation program options
 
-CFLAGS=-Wall -Wmissing-prototypes
-#CFLAGS+= -Wextra -pedantic -std=c99
+CFLAGS=-Wall -Wmissing-prototypes -std=c99
+#CFLAGS+= -Wextra -pedantic
 LDFLAGS=-lm -lc
 
 CFLAGS+= -O3
 #CFLAGS+= -O0 -g
 #CFLAGS+= -pg
-#CFLAGS+= --coverage
+#CFLAGS+= -fprofile-arcs -ftest-coverage
 
 LDFLAGS+=
 #LDFLAGS+= -pg -static
-#LDFLAGS+= --coverage
+#LDFLAGS+= -fprofile-arcs -ftest-coverage
 
 LEXFLAGS=
 
@@ -61,7 +59,7 @@ EXE=planetsplitter planetsplitter-slim router router-slim filedumper filedumper-
 
 ########
 
-all : $(EXE)
+all: $(EXE)
 	-@[ -d $(WEBDIR) ] && \
 	  for file in $(EXE); do \
 	     if [ ! -f $(WEBDIR)/$$file ] || [ $$file -nt $(WEBDIR)/$$file ]; then \
@@ -69,7 +67,8 @@ all : $(EXE)
 	        cp -f $$file $(WEBDIR) ;\
 	     fi ;\
 	  done
-	@cd xml && $(MAKE) CC="$(CC)" LD="$(LD)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
+	@cd xml  && $(MAKE) CC="$(CC)" LD="$(LD)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
+	@cd test && $(MAKE) CC="$(CC)" LD="$(LD)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
 
 ########
 
@@ -98,7 +97,7 @@ planetsplitter-slim : $(PLANETSPLITTER_SLIM_OBJ)
 ########
 
 ROUTER_OBJ=router.o \
-	   nodes.o segments.o ways.o types.o fakes.o \
+	   nodes.o segments.o ways.o relations.o types.o fakes.o \
 	   optimiser.o output.o \
 	   files.o logging.o profiles.o xmlparse.o \
 	   results.o queue.o translations.o
@@ -109,7 +108,7 @@ router : $(ROUTER_OBJ)
 ########
 
 ROUTER_SLIM_OBJ=router-slim.o \
-	        nodes-slim.o segments-slim.o ways-slim.o types.o fakes.o \
+	        nodes-slim.o segments-slim.o ways-slim.o relations-slim.o types.o fakes-slim.o \
 	        optimiser-slim.o output-slim.o \
 	        files.o logging.o profiles.o xmlparse.o \
 	        results.o queue.o translations.o
@@ -120,7 +119,7 @@ router-slim : $(ROUTER_SLIM_OBJ)
 ########
 
 FILEDUMPER_OBJ=filedumper.o \
-	       nodes.o segments.o ways.o types.o \
+	       nodes.o segments.o ways.o relations.o types.o fakes.o \
                visualiser.o \
 	       files.o logging.o xmlparse.o
 
@@ -130,7 +129,7 @@ filedumper : $(FILEDUMPER_OBJ)
 ########
 
 FILEDUMPER_SLIM_OBJ=filedumper-slim.o \
-	       nodes-slim.o segments-slim.o ways-slim.o types.o \
+	       nodes-slim.o segments-slim.o ways-slim.o relations-slim.o types.o fakes-slim.o \
                visualiser-slim.o \
 	       files.o logging.o xmlparse.o
 
@@ -156,10 +155,16 @@ xmlparse.c : xmlparse.l
 ########
 
 %.o : %.c
-	$(CC) -c $(CFLAGS) $(FLAGS64) -DSLIM=0 -DDATADIR=\"$(datadir)\" $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $<)))
+	$(CC) -c $(CFLAGS) $(FLAGS64) -DSLIM=0 -DDATADIR=\"$(datadir)\" $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $@)))
 
 %-slim.o : %.c
-	$(CC) -c $(CFLAGS) $(FLAGS64) -DSLIM=1 -DDATADIR=\"$(datadir)\" $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $<)))
+	$(CC) -c $(CFLAGS) $(FLAGS64) -DSLIM=1 -DDATADIR=\"$(datadir)\" $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $@)))
+
+########
+
+test: .FORCE
+	cd xml  && $(MAKE) test
+	cd test && $(MAKE) test
 
 ########
 
@@ -176,8 +181,10 @@ install: all
 clean:
 	rm -f *.o
 	rm -f *~
+	rm -f *.gcda *.gcno *.gcov gmon.out
 	rm -f xmlparse.c
-	cd xml && $(MAKE) clean
+	cd xml  && $(MAKE) clean
+	cd test && $(MAKE) clean
 
 ########
 
@@ -186,11 +193,12 @@ distclean: clean
 	-rm -f $(EXE)
 	-rm -f $(D)
 	-rm -fr .deps
-	cd xml && $(MAKE) distclean
+	cd xml  && $(MAKE) distclean
+	cd test && $(MAKE) distclean
 
 ########
 
-.deps : .FORCE
+.deps: .FORCE
 	@[ -d .deps ] || mkdir $@
 
 $(D) : .deps
@@ -200,7 +208,7 @@ include $(D)
 
 ########
 
-.FORCE :
+.FORCE:
 
 ########
 
diff --git a/src/fakes.c b/src/fakes.c
index 4d4df3c..fd6df79 100644
--- a/src/fakes.c
+++ b/src/fakes.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/fakes.c,v 1.2 2010/08/04 16:44:51 amb Exp $
-
  Fake node and segment generation.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,7 +24,7 @@
 #include "nodes.h"
 #include "segments.h"
 
-#include "functions.h"
+#include "fakes.h"
 
 
 /*+ The minimum distance along a segment from a node to insert a fake node. (in km). +*/
@@ -34,20 +32,30 @@
 
 
 /*+ A set of fake segments to allow start/finish in the middle of a segment. +*/
-static Segment fake_segments[2*NWAYPOINTS];
+static Segment fake_segments[4*NWAYPOINTS+1];
+
+/*+ A set of pointers to the real segments underlying the fake segments. +*/
+static index_t real_segments[4*NWAYPOINTS+1];
 
 /*+ A set of fake node latitudes and longitudes. +*/
 static double fake_lon[NWAYPOINTS+1],fake_lat[NWAYPOINTS+1];
 
+/*+ The previous waypoint. +*/
+static int prevpoint=0;
+
 
 /*++++++++++++++++++++++++++++++++++++++
-  Create a pair of fake segments corresponding to the given segment split in two.
+  Create a pair of fake segments corresponding to the given segment split in two
+  (and will create an extra two fake segments if adjacent waypoints are on the
+  same segment).
 
   index_t CreateFakes Returns the fake node index (or a real one in special cases).
 
   Nodes *nodes The set of nodes to use.
 
-  int point Which of the waypoints is this.
+  Segments *segments The set of segments to use.
+
+  int point Which of the waypoints this is.
 
   Segment *segment The segment to split.
 
@@ -60,21 +68,43 @@ static double fake_lon[NWAYPOINTS+1],fake_lat[NWAYPOINTS+1];
   distance_t dist2 The distance to the second node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t CreateFakes(Nodes *nodes,int point,Segment *segment,index_t node1,index_t node2,distance_t dist1,distance_t dist2)
+index_t CreateFakes(Nodes *nodes,Segments *segments,int point,Segment *segment,index_t node1,index_t node2,distance_t dist1,distance_t dist2)
 {
  index_t fakenode;
  double lat1,lon1,lat2,lon2;
 
+ /* Initialise the segments to fake values */
+
+ fake_segments[4*point-4].node1=NO_NODE;
+ fake_segments[4*point-4].node2=NO_NODE;
+
+ fake_segments[4*point-3].node1=NO_NODE;
+ fake_segments[4*point-3].node2=NO_NODE;
+
+ fake_segments[4*point-2].node1=NO_NODE;
+ fake_segments[4*point-2].node2=NO_NODE;
+
+ fake_segments[4*point-1].node1=NO_NODE;
+ fake_segments[4*point-1].node2=NO_NODE;
+
  /* Check if we are actually close enough to an existing node */
 
  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))
+   {
+    prevpoint=point;
     return(node2);
+   }
 
  if(dist1<km_to_distance(MINSEGMENT) && dist2<km_to_distance(MINSEGMENT))
    {
+    prevpoint=point;
+
     if(dist1<dist2)
        return(node1);
     else
@@ -98,27 +128,76 @@ index_t CreateFakes(Nodes *nodes,int point,Segment *segment,index_t node1,index_
 
  if(fake_lat[point]>M_PI) fake_lat[point]-=2*M_PI;
 
+ /*
+  *    node1  fakenode                         node2
+  *      #----------*----------------------------#     real_segments[4*point-{4,3}]
+  *   
+  *      #----------*                                  fake_segments[4*point-4]
+  *                 *----------------------------#     fake_segments[4*point-3]
+  *   
+  *   
+  *    node1  fakenode[prevpoint]              node2
+  *      #----------*------------------%---------#     real_segments[4*prevpoint-{4,3,1}], real_segments[4*point-{4,3,2}]
+  *                              fakenode[point]
+  *      #----------*                                  fake_segments[4*prevpoint-4]
+  *                 *----------------------------#     fake_segments[4*prevpoint-3]
+  *                 *------------------%               fake_segments[4*prevpoint-1]
+  *      #-----------------------------%               fake_segments[4*point-4]
+  *                                    %---------#     fake_segments[4*point-3]
+  *                 *------------------%               fake_segments[4*point-2]
+  */
+
  /* Create the first fake segment */
 
- fake_segments[2*point-2]=*segment;
+ fake_segments[4*point-4]=*segment;
 
- if(segment->node1==node1)
-    fake_segments[2*point-2].node1=fakenode;
- else
-    fake_segments[2*point-2].node2=fakenode;
+ fake_segments[4*point-4].node2=fakenode;
 
- fake_segments[2*point-2].distance=DISTANCE(dist1)|DISTFLAG(segment->distance);
+ fake_segments[4*point-4].distance=DISTANCE(dist1)|DISTFLAG(segment->distance);
+
+ real_segments[4*point-4]=IndexSegment(segments,segment);
 
  /* Create the second fake segment */
 
- fake_segments[2*point-1]=*segment;
+ fake_segments[4*point-3]=*segment;
+
+ fake_segments[4*point-3].node1=fakenode;
 
- if(segment->node1==node2)
-    fake_segments[2*point-1].node1=fakenode;
- else
-    fake_segments[2*point-1].node2=fakenode;
+ fake_segments[4*point-3].distance=DISTANCE(dist2)|DISTFLAG(segment->distance);
 
- fake_segments[2*point-1].distance=DISTANCE(dist2)|DISTFLAG(segment->distance);
+ real_segments[4*point-3]=IndexSegment(segments,segment);
+
+ /* Create a third fake segment to join adjacent points if both are fake and on the same real segment */
+
+ if(prevpoint>0 && fake_segments[4*prevpoint-4].node1==node1 && fake_segments[4*prevpoint-3].node2==node2)
+   {
+    if(DISTANCE(dist1)>DISTANCE(fake_segments[4*prevpoint-4].distance)) /* point is further from node1 than prevpoint */
+      {
+       fake_segments[4*point-2]=fake_segments[4*prevpoint-3];
+
+       fake_segments[4*point-2].node2=fakenode;
+
+       fake_segments[4*point-2].distance=(DISTANCE(dist1)-DISTANCE(fake_segments[4*prevpoint-4].distance))|DISTFLAG(segment->distance);
+      }
+    else
+      {
+       fake_segments[4*point-2]=fake_segments[4*prevpoint-4];
+
+       fake_segments[4*point-2].node1=fakenode;
+
+       fake_segments[4*point-2].distance=(DISTANCE(fake_segments[4*prevpoint-4].distance)-DISTANCE(dist1))|DISTFLAG(segment->distance);
+      }
+
+    real_segments[4*point-2]=IndexSegment(segments,segment);
+
+    fake_segments[4*prevpoint-1]=fake_segments[4*point-2];
+
+    real_segments[4*prevpoint-1]=real_segments[4*point-2];
+   }
+
+ /* Return the fake node */
+
+ prevpoint=point;
 
  return(fakenode);
 }
@@ -127,7 +206,7 @@ index_t CreateFakes(Nodes *nodes,int point,Segment *segment,index_t node1,index_
 /*++++++++++++++++++++++++++++++++++++++
   Lookup the latitude and longitude of a fake node.
 
-  index_t fakenode The node to lookup.
+  index_t fakenode The fake node to lookup.
 
   double *latitude Returns the latitude
 
@@ -136,69 +215,78 @@ index_t CreateFakes(Nodes *nodes,int point,Segment *segment,index_t node1,index_
 
 void GetFakeLatLong(index_t fakenode, double *latitude,double *longitude)
 {
- index_t realnode=fakenode-NODE_FAKE;
+ index_t whichnode=fakenode-NODE_FAKE;
 
- *latitude =fake_lat[realnode];
- *longitude=fake_lon[realnode];
+ *latitude =fake_lat[whichnode];
+ *longitude=fake_lon[whichnode];
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Finds the first fake segment associated to a fake node.
 
-  Segment *FirstFakeSegment Returns the first fake segment.
+  Segment *FirstFakeSegment Returns a pointer to the first fake segment.
 
-  index_t fakenode The node to lookup.
+  index_t fakenode The fake node to lookup.
   ++++++++++++++++++++++++++++++++++++++*/
 
 Segment *FirstFakeSegment(index_t fakenode)
 {
- index_t realnode=fakenode-NODE_FAKE;
+ index_t whichnode=fakenode-NODE_FAKE;
 
- return(&fake_segments[2*realnode-2]);
+ return(&fake_segments[4*whichnode-4]);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Finds the next (there can only be two) fake segment associated to a fake node.
+  Finds the next fake segment associated to a fake node.
 
-  Segment *NextFakeSegment Returns the second fake segment.
+  Segment *NextFakeSegment Returns a pointer to the next fake segment.
 
-  Segment *segment The first fake segment.
+  Segment *fakesegment The first fake segment.
 
   index_t fakenode The node to lookup.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Segment *NextFakeSegment(Segment *segment,index_t fakenode)
+Segment *NextFakeSegment(Segment *fakesegment,index_t fakenode)
 {
- index_t realnode=fakenode-NODE_FAKE;
+ index_t whichnode=fakenode-NODE_FAKE;
+
+ if(fakesegment==&fake_segments[4*whichnode-4])
+    return(&fake_segments[4*whichnode-3]);
+
+ if(fakesegment==&fake_segments[4*whichnode-3] && fake_segments[4*whichnode-2].node1!=NO_NODE)
+    return(&fake_segments[4*whichnode-2]);
 
- if(segment==&fake_segments[2*realnode-2])
-    return(&fake_segments[2*realnode-1]);
- else
-    return(NULL);
+ if(fakesegment==&fake_segments[4*whichnode-3] && fake_segments[4*whichnode-1].node1!=NO_NODE)
+    return(&fake_segments[4*whichnode-1]);
+
+ if(fakesegment==&fake_segments[4*whichnode-2] && fake_segments[4*whichnode-1].node1!=NO_NODE)
+    return(&fake_segments[4*whichnode-1]);
+
+ return(NULL);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Finds the fake segment between a node and a fake node.
+  Finds the fake segment between a real node and a fake node.
 
   Segment *ExtraFakeSegment Returns a segment between the two specified nodes if it exists.
 
-  index_t node The real node.
+  index_t realnode The real node.
 
-  index_t fakenode The fake node to lookup.
+  index_t fakenode The fake node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Segment *ExtraFakeSegment(index_t node,index_t fakenode)
+Segment *ExtraFakeSegment(index_t realnode,index_t fakenode)
 {
- index_t realnode=fakenode-NODE_FAKE;
+ index_t whichnode=fakenode-NODE_FAKE;
 
- if(fake_segments[2*realnode-2].node1==node || fake_segments[2*realnode-2].node2==node)
-    return(&fake_segments[2*realnode-2]);
+ if(fake_segments[4*whichnode-4].node1==realnode || fake_segments[4*whichnode-4].node2==realnode)
+    return(&fake_segments[4*whichnode-4]);
 
- if(fake_segments[2*realnode-1].node1==node || fake_segments[2*realnode-1].node2==node)
-    return(&fake_segments[2*realnode-1]);
+ if(fake_segments[4*whichnode-3].node1==realnode || fake_segments[4*whichnode-3].node2==realnode)
+    return(&fake_segments[4*whichnode-3]);
 
  return(NULL);
 }
@@ -207,16 +295,16 @@ Segment *ExtraFakeSegment(index_t node,index_t fakenode)
 /*++++++++++++++++++++++++++++++++++++++
   Lookup a fake segment given its index.
 
-  Segment *LookupFakeSegment Returns a pointer to the segment.
+  Segment *LookupFakeSegment Returns a pointer to the fake segment.
 
   index_t fakesegment The index of the fake segment.
   ++++++++++++++++++++++++++++++++++++++*/
 
 Segment *LookupFakeSegment(index_t fakesegment)
 {
- index_t realsegment=fakesegment-SEGMENT_FAKE;
+ index_t whichsegment=fakesegment-SEGMENT_FAKE;
 
- return(&fake_segments[realsegment]);
+ return(&fake_segments[whichsegment]);
 }
 
 
@@ -225,12 +313,53 @@ Segment *LookupFakeSegment(index_t fakesegment)
 
   index_t IndexFakeSegment Returns the fake segment.
 
-  Segment *segment The segment to look for.
+  Segment *fakesegment The fake segment to look for.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t IndexFakeSegment(Segment *fakesegment)
+{
+ index_t whichsegment=fakesegment-&fake_segments[0];
+
+ return(whichsegment+SEGMENT_FAKE);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the real segment underlying a fake segment.
+
+  index_t IndexRealSegment Returns the index of the real segment.
+
+  index_t fakesegment The index of the fake segment.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t IndexRealSegment(index_t fakesegment)
+{
+ index_t whichsegment=fakesegment-SEGMENT_FAKE;
+
+ return(real_segments[whichsegment]);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Determine if a route between two fake segments is valid or a U-turn.
+
+  int IsFakeUTurn Returns true for a U-turn.
+
+  index_t fakesegment1 The first fake segment.
+
+  index_t fakesegment2 The second fake segment.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t IndexFakeSegment(Segment *segment)
+int IsFakeUTurn(index_t fakesegment1,index_t fakesegment2)
 {
- index_t realsegment=segment-&fake_segments[0];
+ index_t whichsegment1=fakesegment1-SEGMENT_FAKE;
+ index_t whichsegment2=fakesegment2-SEGMENT_FAKE;
+
+ if(fake_segments[whichsegment1].node1==fake_segments[whichsegment2].node1)
+    return(1);
+
+ if(fake_segments[whichsegment1].node2==fake_segments[whichsegment2].node2)
+    return(1);
 
- return(realsegment+SEGMENT_FAKE);
+ return(0);
 }
diff --git a/src/fakes.h b/src/fakes.h
new file mode 100644
index 0000000..c4bb8fe
--- /dev/null
+++ b/src/fakes.h
@@ -0,0 +1,54 @@
+/***************************************
+ Header file for fake node and segment function prototypes
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2011 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 FAKES_H
+#define FAKES_H    /*+ To stop multiple inclusions. +*/
+
+#include "types.h"
+
+
+/* Macros */
+
+/*+ Return true if this is a fake node. +*/
+#define IsFakeNode(xxx)    ((xxx)>=NODE_FAKE && (xxx)!=NO_NODE)
+
+/*+ Return true if this is a fake segment. +*/
+#define IsFakeSegment(xxx) ((xxx)>=SEGMENT_FAKE && (xxx)!=NO_SEGMENT)
+
+
+/* Functions in files.c */
+
+index_t CreateFakes(Nodes *nodes,Segments *segments,int point,Segment *segment,index_t node1,index_t node2,distance_t dist1,distance_t dist2);
+
+void GetFakeLatLong(index_t fakenode, double *latitude,double *longitude);
+
+Segment *FirstFakeSegment(index_t fakenode);
+Segment *NextFakeSegment(Segment *fakesegment,index_t fakenode);
+Segment *ExtraFakeSegment(index_t realnode,index_t fakenode);
+
+Segment *LookupFakeSegment(index_t index);
+index_t IndexFakeSegment(Segment *fakesegment);
+index_t IndexRealSegment(index_t fakesegment);
+
+int IsFakeUTurn(index_t fakesegment1,index_t fakesegment2);
+
+#endif /* FAKES_H */
diff --git a/src/filedumper.c b/src/filedumper.c
index c1c67ec..6ce5e40 100644
--- a/src/filedumper.c
+++ b/src/filedumper.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/filedumper.c,v 1.54 2010/09/15 18:19:36 amb Exp $
-
  Memory file dumper.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,11 +26,13 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <time.h>
+#include <math.h>
 
 #include "types.h"
 #include "nodes.h"
 #include "segments.h"
 #include "ways.h"
+#include "relations.h"
 
 #include "files.h"
 #include "visualiser.h"
@@ -41,13 +41,15 @@
 
 /* Local functions */
 
-static void print_node(Nodes* nodes,index_t item);
+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_turnrelation(Relations *relations,index_t item,Segments *segments,Nodes *nodes);
 
 static void print_head_osm(void);
-static void print_node_osm(Nodes* nodes,index_t item);
+static void print_node_osm(Nodes *nodes,index_t item);
 static void print_segment_osm(Segments *segments,index_t item,Ways *ways);
+static void print_turnrelation_osm(Relations *relations,index_t item,Segments *segments,Nodes *nodes);
 static void print_tail_osm(void);
 
 static char *RFC822Date(time_t t);
@@ -64,9 +66,10 @@ int main(int argc,char** argv)
  Nodes    *OSMNodes;
  Segments *OSMSegments;
  Ways     *OSMWays;
+ Relations*OSMRelations;
  int       arg;
  char     *dirname=NULL,*prefix=NULL;
- char     *nodes_filename,*segments_filename,*ways_filename;
+ char     *nodes_filename,*segments_filename,*ways_filename,*relations_filename;
  int       option_statistics=0;
  int       option_visualiser=0,coordcount=0;
  double    latmin=0,latmax=0,lonmin=0,lonmax=0;
@@ -110,6 +113,8 @@ int main(int argc,char** argv)
        ;
     else if(!strncmp(argv[arg],"--way=",6))
        ;
+    else if(!strncmp(argv[arg],"--turn-relation=",16))
+       ;
     else
        print_usage(0,argv[arg],NULL);
    }
@@ -125,6 +130,8 @@ int main(int argc,char** argv)
 
  OSMWays=LoadWayList(ways_filename=FileName(dirname,prefix,"ways.mem"));
 
+ OSMRelations=LoadRelationList(relations_filename=FileName(dirname,prefix,"relations.mem"));
+
  /* Write out the visualiser data */
 
  if(option_visualiser)
@@ -136,21 +143,23 @@ int main(int argc,char** argv)
        print_usage(0,NULL,"The --visualiser option must have --data.\n");
 
     if(!strcmp(option_data,"junctions"))
-       OutputJunctions(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputJunctions(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"super"))
-       OutputSuper(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputSuper(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"oneway"))
-       OutputOneway(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputOneway(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
+    else if(!strcmp(option_data,"turns"))
+       OutputTurnRestrictions(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"speed"))
-       OutputSpeedLimits(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputSpeedLimits(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"weight"))
-       OutputWeightLimits(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputWeightLimits(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"height"))
-       OutputHeightLimits(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputHeightLimits(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"width"))
-       OutputWidthLimits(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputWidthLimits(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else if(!strcmp(option_data,"length"))
-       OutputLengthLimits(OSMNodes,OSMSegments,OSMWays,latmin,latmax,lonmin,lonmax);
+       OutputLengthLimits(OSMNodes,OSMSegments,OSMWays,OSMRelations,latmin,latmax,lonmin,lonmax);
     else
        print_usage(0,option_data,NULL);
    }
@@ -169,19 +178,25 @@ 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'     - %9lld Bytes\n",prefix?prefix:"",prefix?"-":"",(long long)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'  - %9lld Bytes\n",prefix?prefix:"",prefix?"-":"",(long long)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'      - %9lld Bytes\n",prefix?prefix:"",prefix?"-":"",(long long)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\n",RFC822Date(buf.st_mtime));
     printf("\n");
 
@@ -191,17 +206,17 @@ int main(int argc,char** argv)
     printf("-----\n");
     printf("\n");
 
-    printf("sizeof(Node) =%9d Bytes\n",sizeof(Node));
-    printf("Number       =%9d\n",OSMNodes->file.number);
-    printf("Number(super)=%9d\n",OSMNodes->file.snumber);
+    printf("sizeof(Node) =%9lu Bytes\n",(unsigned long)sizeof(Node));
+    printf("Number       =%9"Pindex_t"\n",OSMNodes->file.number);
+    printf("Number(super)=%9"Pindex_t"\n",OSMNodes->file.snumber);
     printf("\n");
 
-    printf("Lat bins= %4d\n",OSMNodes->file.latbins);
-    printf("Lon bins= %4d\n",OSMNodes->file.lonbins);
+    printf("Lat bins= %4d\n",(int)OSMNodes->file.latbins);
+    printf("Lon bins= %4d\n",(int)OSMNodes->file.lonbins);
     printf("\n");
 
-    printf("Lat zero=%5d (%8.4f deg)\n",OSMNodes->file.latzero,radians_to_degrees(latlong_to_radians(bin_to_latlong(OSMNodes->file.latzero))));
-    printf("Lon zero=%5d (%8.4f deg)\n",OSMNodes->file.lonzero,radians_to_degrees(latlong_to_radians(bin_to_latlong(OSMNodes->file.lonzero))));
+    printf("Lat zero=%5d (%8.4f deg)\n",(int)OSMNodes->file.latzero,radians_to_degrees(latlong_to_radians(bin_to_latlong(OSMNodes->file.latzero))));
+    printf("Lon zero=%5d (%8.4f deg)\n",(int)OSMNodes->file.lonzero,radians_to_degrees(latlong_to_radians(bin_to_latlong(OSMNodes->file.lonzero))));
 
     /* Examine the segments */
 
@@ -210,10 +225,10 @@ int main(int argc,char** argv)
     printf("--------\n");
     printf("\n");
 
-    printf("sizeof(Segment)=%9d Bytes\n",sizeof(Segment));
-    printf("Number(total)  =%9d\n",OSMSegments->file.number);
-    printf("Number(super)  =%9d\n",OSMSegments->file.snumber);
-    printf("Number(normal) =%9d\n",OSMSegments->file.nnumber);
+    printf("sizeof(Segment)=%9lu Bytes\n",(unsigned long)sizeof(Segment));
+    printf("Number(total)  =%9"Pindex_t"\n",OSMSegments->file.number);
+    printf("Number(super)  =%9"Pindex_t"\n",OSMSegments->file.snumber);
+    printf("Number(normal) =%9"Pindex_t"\n",OSMSegments->file.nnumber);
 
     /* Examine the ways */
 
@@ -222,19 +237,31 @@ int main(int argc,char** argv)
     printf("----\n");
     printf("\n");
 
-    printf("sizeof(Way)      =%9d Bytes\n",sizeof(Way));
-    printf("Number(compacted)=%9d\n",OSMWays->file.number);
-    printf("Number(original) =%9d\n",OSMWays->file.onumber);
+    printf("sizeof(Way)      =%9lu Bytes\n",(unsigned long)sizeof(Way));
+    printf("Number(compacted)=%9"Pindex_t"\n",OSMWays->file.number);
+    printf("Number(original) =%9"Pindex_t"\n",OSMWays->file.onumber);
     printf("\n");
 
-    printf("Total names =%9ld Bytes\n",(long)buf.st_size-sizeof(Ways)-OSMWays->file.number*sizeof(Way));
+    stat(ways_filename,&buf);
+    printf("Total names =%9lu Bytes\n",(unsigned long)buf.st_size-(unsigned long)sizeof(Ways)-(unsigned long)OSMWays->file.number*(unsigned long)sizeof(Way));
     printf("\n");
 
+    printf("Included highways  : %s\n",HighwaysNameList(OSMWays->file.highways));
     printf("Included transports: %s\n",AllowedNameList(OSMWays->file.allow));
     printf("Included properties: %s\n",PropertiesNameList(OSMWays->file.props));
+
+    /* Examine the relations */
+
+    printf("\n");
+    printf("Relations\n");
+    printf("---------\n");
+    printf("\n");
+
+    printf("sizeof(TurnRelation)=%9lu Bytes\n",(unsigned long)sizeof(TurnRelation));
+    printf("Number              =%9"Pindex_t"\n",OSMRelations->file.trnumber);
    }
 
- /* Print out internal data */
+ /* Print out internal data (in plain text format) */
 
  if(option_dump)
    {
@@ -253,7 +280,7 @@ int main(int argc,char** argv)
           if(item>=0 && item<OSMNodes->file.number)
              print_node(OSMNodes,item);
           else
-             printf("Invalid node number; minimum=0, maximum=%d.\n",OSMNodes->file.number-1);
+             printf("Invalid node number; minimum=0, maximum=%"Pindex_t".\n",OSMNodes->file.number-1);
          }
        else if(!strcmp(argv[arg],"--segment=all"))
          {
@@ -267,7 +294,7 @@ int main(int argc,char** argv)
           if(item>=0 && item<OSMSegments->file.number)
              print_segment(OSMSegments,item);
           else
-             printf("Invalid segment number; minimum=0, maximum=%d.\n",OSMSegments->file.number-1);
+             printf("Invalid segment number; minimum=0, maximum=%"Pindex_t".\n",OSMSegments->file.number-1);
          }
        else if(!strcmp(argv[arg],"--way=all"))
          {
@@ -281,11 +308,25 @@ int main(int argc,char** argv)
           if(item>=0 && item<OSMWays->file.number)
              print_way(OSMWays,item);
           else
-             printf("Invalid way number; minimum=0, maximum=%d.\n",OSMWays->file.number-1);
+             printf("Invalid way number; minimum=0, maximum=%"Pindex_t".\n",OSMWays->file.number-1);
+         }
+       else if(!strcmp(argv[arg],"--turn-relation=all"))
+         {
+          for(item=0;item<OSMRelations->file.trnumber;item++)
+             print_turnrelation(OSMRelations,item,OSMSegments,OSMNodes);
+         }
+       else if(!strncmp(argv[arg],"--turn-relation=",16))
+         {
+          item=atoi(&argv[arg][16]);
+
+          if(item>=0 && item<OSMRelations->file.trnumber)
+             print_turnrelation(OSMRelations,item,OSMSegments,OSMNodes);
+          else
+             printf("Invalid turn relation number; minimum=0, maximum=%"Pindex_t".\n",OSMRelations->file.trnumber-1);
          }
    }
 
- /* Print out internal data in XML format */
+ /* Print out internal data (in OSM XML format) */
 
  if(option_dump_osm)
    {
@@ -296,19 +337,24 @@ int main(int argc,char** argv)
 
     if(coordcount)
       {
-       int32_t latminbin=latlong_to_bin(radians_to_latlong(latmin))-OSMNodes->file.latzero;
-       int32_t latmaxbin=latlong_to_bin(radians_to_latlong(latmax))-OSMNodes->file.latzero;
-       int32_t lonminbin=latlong_to_bin(radians_to_latlong(lonmin))-OSMNodes->file.lonzero;
-       int32_t lonmaxbin=latlong_to_bin(radians_to_latlong(lonmax))-OSMNodes->file.lonzero;
-       int latb,lonb,llbin;
+       ll_bin_t latminbin=latlong_to_bin(radians_to_latlong(latmin))-OSMNodes->file.latzero;
+       ll_bin_t latmaxbin=latlong_to_bin(radians_to_latlong(latmax))-OSMNodes->file.latzero;
+       ll_bin_t lonminbin=latlong_to_bin(radians_to_latlong(lonmin))-OSMNodes->file.lonzero;
+       ll_bin_t lonmaxbin=latlong_to_bin(radians_to_latlong(lonmax))-OSMNodes->file.lonzero;
+       ll_bin_t latb,lonb;
        index_t item,index1,index2;
 
+       if(latminbin<0)                      latminbin=0;
+       if(latmaxbin>OSMNodes->file.latbins) latmaxbin=OSMNodes->file.latbins-1;
+       if(lonminbin<0)                      lonminbin=0;
+       if(lonmaxbin>OSMNodes->file.lonbins) lonmaxbin=OSMNodes->file.lonbins-1;
+
        /* Loop through all of the nodes. */
 
        for(latb=latminbin;latb<=latmaxbin;latb++)
           for(lonb=lonminbin;lonb<=lonmaxbin;lonb++)
             {
-             llbin=lonb*OSMNodes->file.latbins+latb;
+             ll_bin2_t llbin=lonb*OSMNodes->file.latbins+latb;
 
              if(llbin<0 || llbin>(OSMNodes->file.latbins*OSMNodes->file.lonbins))
                 continue;
@@ -328,7 +374,7 @@ int main(int argc,char** argv)
 
                    print_node_osm(OSMNodes,item);
 
-                   segment=FirstSegment(OSMSegments,OSMNodes,item);
+                   segment=FirstSegment(OSMSegments,OSMNodes,item,1);
 
                    while(segment)
                      {
@@ -338,6 +384,18 @@ int main(int argc,char** argv)
 
                       segment=NextSegment(OSMSegments,segment,item);
                      }
+
+                   if(IsTurnRestrictedNode(node))
+                     {
+                      index_t relindex=FindFirstTurnRelation1(OSMRelations,item);
+
+                      while(relindex!=NO_RELATION)
+                        {
+                         print_turnrelation_osm(OSMRelations,relindex,OSMSegments,OSMNodes);
+
+                         relindex=FindNextTurnRelation1(OSMRelations,relindex);
+                        }
+                     }
                   }
                }
             }
@@ -352,6 +410,9 @@ int main(int argc,char** argv)
        for(item=0;item<OSMSegments->file.number;item++)
           if(!option_no_super || IsNormalSegment(LookupSegment(OSMSegments,item,1)))
              print_segment_osm(OSMSegments,item,OSMWays);
+
+       for(item=0;item<OSMRelations->file.trnumber;item++)
+          print_turnrelation_osm(OSMRelations,item,OSMSegments,OSMNodes);
       }
 
     print_tail_osm();
@@ -362,31 +423,31 @@ int main(int argc,char** argv)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the contents of a node from the routing database.
+  Print out the contents of a node from the routing database (as plain text).
 
   Nodes *nodes The set of nodes to use.
 
   index_t item The node index to print.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void print_node(Nodes* nodes,index_t item)
+static void print_node(Nodes *nodes,index_t item)
 {
  Node *node=LookupNode(nodes,item,1);
  double latitude,longitude;
 
  GetLatLong(nodes,item,&latitude,&longitude);
 
- printf("Node %d\n",item);
- printf("  firstseg=%d\n",node->firstseg);
+ printf("Node %"Pindex_t"\n",item);
+ printf("  firstseg=%"Pindex_t"\n",node->firstseg);
  printf("  latoffset=%d lonoffset=%d (latitude=%.6f longitude=%.6f)\n",node->latoffset,node->lonoffset,radians_to_degrees(latitude),radians_to_degrees(longitude));
  printf("  allow=%02x (%s)\n",node->allow,AllowedNameList(node->allow));
- if(IsSuperNode(nodes,item))
+ if(IsSuperNode(node))
     printf("  Super-Node\n");
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the contents of a segment from the routing database.
+  Print out the contents of a segment from the routing database (as plain text).
 
   Segments *segments The set of segments to use.
 
@@ -397,10 +458,10 @@ static void print_segment(Segments *segments,index_t item)
 {
  Segment *segment=LookupSegment(segments,item,1);
 
- printf("Segment %d\n",item);
- printf("  node1=%d node2=%d\n",segment->node1,segment->node2);
- printf("  next2=%d\n",segment->next2);
- printf("  way=%d\n",segment->way);
+ printf("Segment %"Pindex_t"\n",item);
+ printf("  node1=%"Pindex_t" node2=%"Pindex_t"\n",segment->node1,segment->node2);
+ printf("  next2=%"Pindex_t"\n",segment->next2);
+ printf("  way=%"Pindex_t"\n",segment->way);
  printf("  distance=%d (%.3f km)\n",DISTANCE(segment->distance),distance_to_km(DISTANCE(segment->distance)));
  if(IsSuperSegment(segment) && IsNormalSegment(segment))
     printf("  Super-Segment AND normal Segment\n");
@@ -414,7 +475,7 @@ static void print_segment(Segments *segments,index_t item)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the contents of a way from the routing database.
+  Print out the contents of a way from the routing database (as plain text).
 
   Ways *ways The set of ways to use.
 
@@ -424,11 +485,12 @@ static void print_segment(Segments *segments,index_t item)
 static void print_way(Ways *ways,index_t item)
 {
  Way *way=LookupWay(ways,item,1);
+ char *name=WayName(ways,way);
 
- printf("Way %d\n",item);
- if(*WayName(ways,way))
-    printf("  name=%s\n",WayName(ways,way));
- printf("  type=%02x (%s%s%s)\n",way->type,HighwayName(HIGHWAY(way->type)),way->type&Way_OneWay?",One-Way":"",way->type&Way_Roundabout?",Roundabout":"");
+ printf("Way %"Pindex_t"\n",item);
+ if(*name)
+    printf("  name=%s\n",name);
+ printf("  type=%02x (%s%s)\n",way->type,HighwayName(HIGHWAY(way->type)),way->type&Way_OneWay?",One-Way":"");
  printf("  allow=%02x (%s)\n",way->allow,AllowedNameList(way->allow));
  if(way->props)
     printf("  props=%02x (%s)\n",way->props,PropertiesNameList(way->props));
@@ -446,51 +508,111 @@ static void print_way(Ways *ways,index_t item)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Print out the contents of a turn relation from the routing database (as plain text).
+
+  Relations *relations The set of relations to use.
+
+  index_t item The turn relation index to print.
+
+  Segments *segments The set of segments to use.
+
+  Nodes *nodes The set of nodes to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static void print_turnrelation(Relations *relations,index_t item,Segments *segments,Nodes *nodes)
+{
+ TurnRelation *relation=LookupTurnRelation(relations,item,1);
+ Segment *segment;
+ index_t from_way=NO_WAY,to_way=NO_WAY;
+ index_t from_node=NO_NODE,to_node=NO_NODE;
+
+ segment=FirstSegment(segments,nodes,relation->via,1);
+
+ do
+   {
+    index_t seg=IndexSegment(segments,segment);
+
+    if(seg==relation->from)
+      {
+       from_node=OtherNode(segment,relation->from);
+       from_way=segment->way;
+      }
+
+    if(seg==relation->to)
+      {
+       to_node=OtherNode(segment,relation->to);
+       to_way=segment->way;
+      }
+
+    segment=NextSegment(segments,segment,relation->via);
+   }
+ while(segment);
+
+ printf("Relation %"Pindex_t"\n",item);
+ printf("  from=%"Pindex_t" (segment) = %"Pindex_t" (way) = %"Pindex_t" (node)\n",relation->from,from_way,from_node);
+ printf("  via=%"Pindex_t" (node)\n",relation->via);
+ printf("  to=%"Pindex_t" (segment) = %"Pindex_t" (way) = %"Pindex_t" (node)\n",relation->to,to_way,to_node);
+ if(relation->except)
+    printf("  except=%02x (%s)\n",relation->except,AllowedNameList(relation->except));
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Print out a header in OSM XML format.
   ++++++++++++++++++++++++++++++++++++++*/
 
 static void print_head_osm(void)
 {
  printf("<?xml version='1.0' encoding='UTF-8'?>\n");
- printf("<osm version='0.6' generator='JOSM'>\n");
+ printf("<osm version='0.6' generator='Routino'>\n");
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the contents of a node from the routing database in OSM XML format.
+  Print out the contents of a node from the routing database (in OSM XML format).
 
   Nodes *nodes The set of nodes to use.
 
   index_t item The node index to print.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void print_node_osm(Nodes* nodes,index_t item)
+static void print_node_osm(Nodes *nodes,index_t item)
 {
  Node *node=LookupNode(nodes,item,1);
  double latitude,longitude;
+ int i;
 
  GetLatLong(nodes,item,&latitude,&longitude);
 
- if(IsSuperNode(nodes,item))
+ if(node->allow==Transports_ALL && node->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));
+ else
    {
-    int i;
-
     printf("  <node id='%lu' lat='%.7f' lon='%.7f' version='1'>\n",(unsigned long)item+1,radians_to_degrees(latitude),radians_to_degrees(longitude));
-    printf("    <tag k='routino:super' v='yes' />\n");
+
+    if(node->flags & NODE_SUPER)
+       printf("    <tag k='routino:super' v='yes' />\n");
+
+    if(node->flags & NODE_UTURN)
+       printf("    <tag k='routino:uturn' v='yes' />\n");
+
+    if(node->flags & NODE_MINIRNDBT)
+       printf("    <tag k='highway' v='mini_roundabout' />\n");
+
+    if(node->flags & NODE_TURNRSTRCT)
+       printf("    <tag k='routino:turnrestriction' v='yes' />\n");
 
     for(i=1;i<Transport_Count;i++)
-       if(!(node->allow & ALLOWED(i)))
+       if(!(node->allow & TRANSPORTS(i)))
           printf("    <tag k='%s' v='no' />\n",TransportName(i));
 
     printf("  </node>\n");
    }
- else
-    printf("  <node id='%lu' lat='%.7f' lon='%.7f' version='1' />\n",(unsigned long)item+1,radians_to_degrees(latitude),radians_to_degrees(longitude));
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the contents of a segment from the routing database as a way in OSM XML format.
+  Print out the contents of a segment from the routing database (as a way in OSM XML format).
 
   Segments *segments The set of segments to use.
 
@@ -503,6 +625,7 @@ static void print_segment_osm(Segments *segments,index_t item,Ways *ways)
 {
  Segment *segment=LookupSegment(segments,item,1);
  Way *way=LookupWay(ways,segment->way,1);
+ char *name=WayName(ways,way);
  int i;
 
  printf("  <way id='%lu' version='1'>\n",(unsigned long)item+1);
@@ -523,18 +646,18 @@ static void print_segment_osm(Segments *segments,index_t item,Ways *ways)
  if(IsNormalSegment(segment))
     printf("    <tag k='routino:normal' v='yes' />\n");
 
+ printf("    <tag k='routino:distance' v='%.3f' />\n",distance_to_km(DISTANCE(segment->distance)));
+
  if(way->type & Way_OneWay)
     printf("    <tag k='oneway' v='yes' />\n");
- if(way->type & Way_Roundabout)
-    printf("    <tag k='junction' v='roundabout' />\n");
 
  printf("    <tag k='highway' v='%s' />\n",HighwayName(HIGHWAY(way->type)));
 
- if(IsNormalSegment(segment) && *WayName(ways,way))
-    printf("    <tag k='name' v='%s' />\n",ParseXML_Encode_Safe_XML(WayName(ways,way)));
+ if(IsNormalSegment(segment) && *name)
+    printf("    <tag k='name' v='%s' />\n",ParseXML_Encode_Safe_XML(name));
 
  for(i=1;i<Transport_Count;i++)
-    if(way->allow & ALLOWED(i))
+    if(way->allow & TRANSPORTS(i))
        printf("    <tag k='%s' v='yes' />\n",TransportName(i));
 
  for(i=1;i<Property_Count;i++)
@@ -558,6 +681,53 @@ static void print_segment_osm(Segments *segments,index_t item,Ways *ways)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Print out the contents of a turn relation from the routing database (in OSM XML 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_turnrelation_osm(Relations *relations,index_t item,Segments *segments,Nodes *nodes)
+{
+ TurnRelation *relation=LookupTurnRelation(relations,item,1);
+
+ Segment *segment_from=LookupSegment(segments,relation->from,1);
+ Segment *segment_to  =LookupSegment(segments,relation->to  ,2);
+
+ double angle=TurnAngle(nodes,segment_from,segment_to,relation->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("  <relation id='%lu' version='1'>\n",(unsigned long)item+1);
+ printf("    <tag k='type' v='restriction' />\n");
+ printf("    <tag k='restriction' v='%s'/>\n",restriction);
+
+ if(relation->except)
+    printf("    <tag k='except' v='%s' />\n",AllowedNameList(relation->except));
+
+ printf("    <member type='way' ref='%lu' role='from' />\n",(unsigned long)relation->from+1);
+ printf("    <member type='node' ref='%lu' role='via' />\n",(unsigned long)relation->via+1);
+ printf("    <member type='way' ref='%lu' role='to' />\n",(unsigned long)relation->to+1);
+
+ printf("  </relation>\n");
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Print out a tail in OSM XML format.
   ++++++++++++++++++++++++++++++++++++++*/
 
@@ -633,6 +803,7 @@ 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"
          "                  [--dump-osm [--no-super]\n"
          "                              [--latmin=<latmin> --latmax=<latmax>\n"
          "                               --lonmin=<lonmin> --lonmax=<lonmax>]]\n");
@@ -668,6 +839,7 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "      junctions = segment count at each junction.\n"
             "      super     = super-node and super-segments.\n"
             "      oneway    = oneway segments.\n"
+            "      turns     = turn restrictions.\n"
             "      speed     = speed limits.\n"
             "      weight    = weight limits.\n"
             "      height    = height limits.\n"
@@ -675,9 +847,10 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "      length    = length limits.\n"
             "\n"
             "--dump                    Dump selected contents of the database.\n"
-            "  --node=<node>           * the node with the selected number.\n"
-            "  --segment=<segment>     * the segment with the selected number.\n"
-            "  --way=<way>             * the way with the selected number.\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"
             "                          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"
diff --git a/src/files.c b/src/files.c
index fc42a27..2508954 100644
--- a/src/files.c
+++ b/src/files.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/files.c,v 1.25 2010/10/31 17:52:40 amb Exp $
-
  Functions to handle files.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,7 +19,6 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  ***************************************/
 
-#include <assert.h>
 
 #include <unistd.h>
 #include <stdlib.h>
@@ -53,15 +50,15 @@ static int nmappedfiles=0;
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Return a filename composed of the dirname, prefix and filename.
+  Return a filename composed of the dirname, prefix and name.
 
-  char *FileName Returns an allocated filename.
+  char *FileName Returns a pointer to memory allocated to the filename.
 
   const char *dirname The directory name.
 
   const char *prefix The file prefix.
 
-  const char *name The filename.
+  const char *name The main part of the name.
   ++++++++++++++++++++++++++++++++++++++*/
 
 char *FileName(const char *dirname,const char *prefix, const char *name)
@@ -75,7 +72,7 @@ char *FileName(const char *dirname,const char *prefix, const char *name)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open a file and map it into memory.
+  Open a file read-only and map it into memory.
 
   void *MapFile Returns the address of the file or exits in case of an error.
 
@@ -102,12 +99,12 @@ void *MapFile(const char *filename)
    {
     close(fd);
 
-    assert(0);
-
     fprintf(stderr,"Cannot mmap file '%s' for reading [%s].\n",filename,strerror(errno));
     exit(EXIT_FAILURE);
    }
 
+ /* Store the information about the mapped file */
+
  mappedfiles=(struct mmapinfo*)realloc((void*)mappedfiles,(nmappedfiles+1)*sizeof(struct mmapinfo));
 
  mappedfiles[nmappedfiles].filename=filename;
@@ -122,7 +119,7 @@ void *MapFile(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open a file and map it into memory.
+  Open a file read-write and map it into memory.
 
   void *MapFileWriteable Returns the address of the file or exits in case of an error.
 
@@ -153,6 +150,8 @@ void *MapFileWriteable(const char *filename)
     exit(EXIT_FAILURE);
    }
 
+ /* Store the information about the mapped file */
+
  mappedfiles=(struct mmapinfo*)realloc((void*)mappedfiles,(nmappedfiles+1)*sizeof(struct mmapinfo));
 
  mappedfiles[nmappedfiles].filename=filename;
@@ -167,7 +166,7 @@ void *MapFileWriteable(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Unmap a file.
+  Unmap a file and close it.
 
   void *UnmapFile Returns NULL (for similarity to the MapFile function).
 
@@ -208,7 +207,7 @@ void *UnmapFile(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open a new file on disk for writing to.
+  Open a new file on disk for writing.
 
   int OpenFileNew Returns the file descriptor if OK or exits in case of an error.
 
@@ -234,11 +233,11 @@ int OpenFileNew(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open a new file on disk for reading from and appending.
+  Open a new or existing file on disk for reading and appending.
 
   int OpenFileAppend Returns the file descriptor if OK or exits in case of an error.
 
-  const char *filename The name of the file to create.
+  const char *filename The name of the file to create or open.
   ++++++++++++++++++++++++++++++++++++++*/
 
 int OpenFileAppend(const char *filename)
@@ -286,7 +285,7 @@ int ReOpenFile(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Open an existing file on disk for reading from or writing to.
+  Open an existing file on disk for reading or writing.
 
   int ReOpenFileWriteable Returns the file descriptor if OK or exits in case of an error.
 
@@ -314,7 +313,7 @@ int ReOpenFileWriteable(const char *filename)
 /*++++++++++++++++++++++++++++++++++++++
   Get the size of a file.
 
-  off_t SizeFile Returns the file size.
+  off_t SizeFile Returns the file size if OK or exits in case of an error.
 
   const char *filename The name of the file to check.
   ++++++++++++++++++++++++++++++++++++++*/
@@ -355,19 +354,23 @@ int ExistsFile(const char *filename)
 /*++++++++++++++++++++++++++++++++++++++
   Close a file on disk.
 
+  int CloseFile returns -1 (for similarity to the *OpenFile* functions).
+
   int fd The file descriptor to close.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void CloseFile(int fd)
+int CloseFile(int fd)
 {
  close(fd);
+
+ return(-1);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Delete a file from disk.
 
-  int DeleteFile Returns 0 if OK or something else in case of an error.
+  int DeleteFile Returns 0 if OK.
 
   char *filename The name of the file to delete.
   ++++++++++++++++++++++++++++++++++++++*/
diff --git a/src/files.h b/src/files.h
index 6edf55c..0adbf2b 100644
--- a/src/files.h
+++ b/src/files.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/files.h,v 1.4 2010/10/09 18:20:18 amb Exp $
-
  Header file for file function prototypes
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -25,11 +23,12 @@
 #ifndef FILES_H
 #define FILES_H    /*+ To stop multiple inclusions. +*/
 
+#include <assert.h>
 #include <unistd.h>
 #include <sys/types.h>
 
 
-/* In files.c */
+/* Functions in files.c */
 
 char *FileName(const char *dirname,const char *prefix, const char *name);
 
@@ -50,7 +49,7 @@ int ExistsFile(const char *filename);
 
 static int SeekFile(int fd,off_t position);
 
-void CloseFile(int fd);
+int CloseFile(int fd);
 
 int DeleteFile(char *filename);
 
@@ -58,19 +57,21 @@ int DeleteFile(char *filename);
 /* Inline the frequently called functions */
 
 /*++++++++++++++++++++++++++++++++++++++
-  Write data to a file on disk.
+  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 from.
+  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)
 {
+ assert(fd!=-1);
+
  /* Write the data */
 
  if(write(fd,address,length)!=length)
@@ -81,19 +82,21 @@ static inline int WriteFile(int fd,const void *address,size_t length)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Read data from a file on disk.
+  Read data from a file descriptor.
 
   int ReadFile Returns 0 if OK or something else in case of an error.
 
   int fd The file descriptor to read from.
 
-  void *address The address of the data to be read into.
+  void *address The address the data is to be read into.
 
   size_t length The length of data to read.
   ++++++++++++++++++++++++++++++++++++++*/
 
 static inline int ReadFile(int fd,void *address,size_t length)
 {
+ assert(fd!=-1);
+
  /* Read the data */
 
  if(read(fd,address,length)!=length)
@@ -104,7 +107,7 @@ static inline int ReadFile(int fd,void *address,size_t length)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Seek to a position in a file on disk.
+  Seek to a position in a file descriptor.
 
   int SeekFile Returns 0 if OK or something else in case of an error.
 
@@ -115,6 +118,8 @@ static inline int ReadFile(int fd,void *address,size_t length)
 
 static inline int SeekFile(int fd,off_t position)
 {
+ assert(fd!=-1);
+
  /* Seek the data */
 
  if(lseek(fd,position,SEEK_SET)!=position)
diff --git a/src/functions.h b/src/functions.h
index ffa499e..c584b81 100644
--- a/src/functions.h
+++ b/src/functions.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/functions.h,v 1.58 2010/09/25 13:54:18 amb Exp $
-
- Header file for function prototypes
+ Header file for miscellaneous function prototypes
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,60 +29,24 @@
 #include "results.h"
 
 
-/*+ The number of waypoints allowed to be specified. +*/
-#define NWAYPOINTS 99
-
-
-/* In fakes.c */
-
-/*+ Return true if this is a fake node. +*/
-#define IsFakeNode(xxx)    ((xxx)>=NODE_FAKE)
-
-/*+ Return true if this is a fake segment. +*/
-#define IsFakeSegment(xxx) ((xxx)>=SEGMENT_FAKE)
-
-index_t CreateFakes(Nodes *nodes,int point,Segment *segment,index_t node1,index_t node2,distance_t dist1,distance_t dist2);
-
-void GetFakeLatLong(index_t node, double *latitude,double *longitude);
-
-Segment *FirstFakeSegment(index_t node);
-Segment *NextFakeSegment(Segment *segment,index_t node);
-Segment *ExtraFakeSegment(index_t node,index_t fakenode);
+/* Functions in optimiser.c */
 
-Segment *LookupFakeSegment(index_t index);
-index_t IndexFakeSegment(Segment *segment);
+Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t start_node,index_t prev_segment,index_t finish_node);
 
+Results *FindMiddleRoute(Nodes *supernodes,Segments *supersegments,Ways *superways,Relations *relations,Profile *profile,Results *begin,Results *end);
 
-/* In optimiser.c */
+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 *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t start,index_t finish,Profile *profile);
-Results *FindMiddleRoute(Nodes *supernodes,Segments *supersegments,Ways *superways,Results *begin,Results *end,Profile *profile);
+Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t finish_node);
 
-Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t start,Profile *profile);
-Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t finish,Profile *profile);
+Results *CombineRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,Results *begin,Results *middle);
 
-Results *CombineRoutes(Results *results,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile);
+void FixForwardRoute(Results *results,Result *finish_result);
 
-void FixForwardRoute(Results *results,index_t finish);
 
-
-/* In output.c */
+/* Functions in output.c */
 
 void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile);
 
 
-/* In sorting.c */
-
-/*+ The type, size and alignment of variable to store the variable length +*/
-#define FILESORT_VARINT   unsigned short
-#define FILESORT_VARSIZE  sizeof(FILESORT_VARINT)
-#define FILESORT_VARALIGN sizeof(void*)
-
-void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t));
-
-void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t));
-
-void filesort_heapsort(void **datap,size_t nitems,int(*compare)(const void*, const void*));
-
-
 #endif /* FUNCTIONS_H */
diff --git a/src/functionsx.h b/src/functionsx.h
index 4cf7594..bc7bb32 100644
--- a/src/functionsx.h
+++ b/src/functionsx.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/functionsx.h,v 1.6 2010/09/17 17:44:15 amb Exp $
-
- Header file for function prototypes for extended data types.
+ Header file for miscellaneous function prototypes for extended data types.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,10 +26,9 @@
 #include <stdio.h>
 
 #include "typesx.h"
-#include "profiles.h"
 
 
-/* In osmparser.c */
+/* Functions in osmparser.c */
 
 int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,RelationsX *OSMRelations);
 
diff --git a/src/logging.c b/src/logging.c
index bb43c07..857305e 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/logging.c,v 1.1 2010/11/13 14:22:40 amb Exp $
-
  Functions to handle logging functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,14 +22,18 @@
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
 
 #include "logging.h"
 
 
+/* Global variables */
+
 /*+ The option to print the output in a way that allows logging to a file. +*/
 int option_loggable=0;
 
-
 /* Local functions */
 
 static void vfprintf_first(FILE *file,const char *format,va_list ap);
@@ -40,11 +42,15 @@ static void vfprintf_last(FILE *file,const char *format,va_list ap);
 
 /* Local variables */
 
+/*+ 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;
+
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the first message in an overwriting sequence.
+  Print the first message in an overwriting sequence (to stdout).
 
   const char *format The format string.
 
@@ -67,7 +73,7 @@ void printf_first(const char *format, ...)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the middle message in an overwriting sequence.
+  Print the middle message in an overwriting sequence (to stdout).
 
   const char *format The format string.
 
@@ -90,7 +96,7 @@ void printf_middle(const char *format, ...)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the last message in an overwriting sequence.
+  Print the last message in an overwriting sequence (to stdout).
 
   const char *format The format string.
 
@@ -110,7 +116,7 @@ void printf_last(const char *format, ...)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the first message in an overwriting sequence.
+  Print the first message in an overwriting sequence to a specified file.
 
   FILE *file The file to write to.
 
@@ -135,7 +141,7 @@ void fprintf_first(FILE *file,const char *format, ...)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the middle message in an overwriting sequence.
+  Print the middle message in an overwriting sequence to a specified file.
 
   FILE *file The file to write to.
 
@@ -160,7 +166,7 @@ void fprintf_middle(FILE *file,const char *format, ...)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the last message in an overwriting sequence.
+  Print the last message in an overwriting sequence to a specified file.
 
   FILE *file The file to write to.
 
@@ -182,7 +188,7 @@ void fprintf_last(FILE *file,const char *format, ...)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the first message in an overwriting sequence.
+  Do the work to print the first message in an overwriting sequence.
 
   FILE *file The file to write to.
 
@@ -204,7 +210,7 @@ static void vfprintf_first(FILE *file,const char *format,va_list ap)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the middle message in an overwriting sequence.
+  Do the work to print the middle message in an overwriting sequence.
 
   FILE *file The file to write to.
 
@@ -222,12 +228,19 @@ static void vfprintf_middle(FILE *file,const char *format,va_list ap)
  fflush(file);
 
  if(retval>0)
-    printed_length=retval;
+   {
+    int new_printed_length=retval;
+
+    while(retval++<printed_length)
+       putchar(' ');
+
+    printed_length=new_printed_length;
+   }
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print the last message in an overwriting sequence.
+  Do the work to print the last message in an overwriting sequence.
 
   FILE *file The file to write to.
 
@@ -251,3 +264,57 @@ static void vfprintf_last(FILE *file,const char *format,va_list ap)
  putchar('\n');
  fflush(file);
 }
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  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);
+}
diff --git a/src/logging.h b/src/logging.h
index e3fe945..3a9c92b 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/logging.h,v 1.2 2010/11/13 14:50:33 amb Exp $
-
  Header file for logging function prototypes
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,7 +31,7 @@
 extern int option_loggable;
 
 
-/* In logging.c */
+/* Runtime progress logging functions in logging.c */
 
 #ifdef __GNUC__
 
@@ -58,4 +56,20 @@ void fprintf_last(FILE *file,const char *format, ...);
 #endif
 
 
+/* Parsing/processing error logging functions in logging.c */
+
+void open_errorlog(const char *filename,int append);
+void close_errorlog(void);
+
+#ifdef __GNUC__
+
+void logerror(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+
+#else
+
+void logerror(const char *format, ...);
+
+#endif
+
+
 #endif /* LOGGING_H */
diff --git a/src/nodes.c b/src/nodes.c
index f04678b..c8fde0d 100644
--- a/src/nodes.c
+++ b/src/nodes.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/nodes.c,v 1.44 2010/07/26 18:17:20 amb Exp $
-
  Node data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,10 +32,15 @@
 #include "profiles.h"
 
 
+/* Local functions */
+
+static int valid_segment_for_profile(Ways *ways,Segment *segment,Profile *profile);
+
+
 /*++++++++++++++++++++++++++++++++++++++
   Load in a node list from a file.
 
-  Nodes* LoadNodeList Returns the node list.
+  Nodes *LoadNodeList Returns the node list.
 
   const char *filename The name of the file to load.
   ++++++++++++++++++++++++++++++++++++++*/
@@ -45,6 +48,9 @@
 Nodes *LoadNodeList(const char *filename)
 {
  Nodes *nodes;
+#if SLIM
+ int i;
+#endif
 
  nodes=(Nodes*)malloc(sizeof(Nodes));
 
@@ -71,9 +77,8 @@ Nodes *LoadNodeList(const char *filename)
 
  nodes->nodesoffset=sizeof(NodesFile)+(nodes->file.latbins*nodes->file.lonbins+1)*sizeof(index_t);
 
- nodes->incache[0]=NO_NODE;
- nodes->incache[1]=NO_NODE;
- nodes->incache[2]=NO_NODE;
+ for(i=0;i<sizeof(nodes->cached)/sizeof(nodes->cached[0]);i++)
+    nodes->incache[i]=NO_NODE;
 
 #endif
 
@@ -82,11 +87,12 @@ Nodes *LoadNodeList(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find the closest node given its latitude, longitude and optionally profile.
+  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.
 
   index_t FindClosestNode Returns the closest node.
 
-  Nodes* nodes The set of nodes to search.
+  Nodes *nodes The set of nodes to search.
 
   Segments *segments The set of segments to use.
 
@@ -96,14 +102,14 @@ Nodes *LoadNodeList(const char *filename)
 
   double longitude The longitude to look for.
 
-  distance_t distance The maximum distance to look.
+  distance_t distance The maximum distance to look from the specified coordinates.
 
-  Profile *profile The profile of the mode of transport (or NULL).
+  Profile *profile The profile of the mode of transport.
 
   distance_t *bestdist Returns the distance to the best node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude,
+index_t FindClosestNode(Nodes *nodes,Segments *segments,Ways *ways,double latitude,double longitude,
                         distance_t distance,Profile *profile,distance_t *bestdist)
 {
  ll_bin_t   latbin=latlong_to_bin(radians_to_latlong(latitude ))-nodes->file.latzero;
@@ -117,7 +123,8 @@ index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitu
 
  do
    {
-    int latb,lonb,llbin;
+    ll_bin_t latb,lonb;
+    ll_bin2_t llbin;
 
     count=0;
 
@@ -188,30 +195,25 @@ index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitu
 
              if(dist<distance)
                {
-                if(profile)
-                  {
-                   Segment *segment;
+                Segment *segment;
 
-                   /* Decide if this is node is valid for the profile */
+                /* Check that at least one segment is valid for the profile */
 
-                   segment=FirstSegment(segments,nodes,i);
+                segment=FirstSegment(segments,nodes,i,1);
 
-                   do
+                do
+                  {
+                   if(IsNormalSegment(segment) && valid_segment_for_profile(ways,segment,profile))
                      {
-                      Way *way=LookupWay(ways,segment->way,1);
-
-                      if(way->allow&profile->allow)
-                         break;
+                      bestn=i;
+                      bestd=distance=dist;
 
-                      segment=NextSegment(segments,segment,i);
+                      break;
                      }
-                   while(segment);
 
-                   if(!segment)
-                      continue;
+                   segment=NextSegment(segments,segment,i);
                   }
-
-                bestn=i; bestd=distance=dist;
+                while(segment);
                }
             }
 
@@ -230,13 +232,15 @@ index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitu
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find the closest segment to a latitude, longitude and optionally profile.
+  Find the closest point on the closest segment given its latitude, longitude
+  and the profile of the mode of transport that must be able to move along this
+  segment.
 
   index_t FindClosestSegment Returns the closest segment index.
 
-  Nodes* nodes The set of nodes to search.
+  Nodes *nodes The set of nodes to use.
 
-  Segments *segments The set of segments to use.
+  Segments *segments The set of segments to search.
 
   Ways *ways The set of ways to use.
 
@@ -244,22 +248,22 @@ index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitu
 
   double longitude The longitude to look for.
 
-  distance_t distance The maximum distance to look.
+  distance_t distance The maximum distance to look from the specified coordinates.
 
-  Profile *profile The profile of the mode of transport (or NULL).
+  Profile *profile The profile of the mode of transport.
 
-  distance_t *bestdist Returns the distance to the best segment.
+  distance_t *bestdist Returns the distance to the closest point on the best segment.
 
-  index_t *bestnode1 Returns the best node at one end.
+  index_t *bestnode1 Returns the index of the node at one end of the closest segment.
 
-  index_t *bestnode2 Returns the best node at the other end.
+  index_t *bestnode2 Returns the index of the node at the other end of the closest segment.
 
-  distance_t *bestdist1 Returns the distance to the best node at one end.
+  distance_t *bestdist1 Returns the distance along the segment to the node at one end.
 
-  distance_t *bestdist2 Returns the distance to the best node at the other end.
+  distance_t *bestdist2 Returns the distance along the segment to the node at the other end.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude,
+index_t FindClosestSegment(Nodes *nodes,Segments *segments,Ways *ways,double latitude,double longitude,
                            distance_t distance,Profile *profile, distance_t *bestdist,
                            index_t *bestnode1,index_t *bestnode2,distance_t *bestdist1,distance_t *bestdist2)
 {
@@ -275,7 +279,8 @@ index_t FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double lat
 
  do
    {
-    int latb,lonb,llbin;
+    ll_bin_t latb,lonb;
+    ll_bin2_t llbin;
 
     count=0;
 
@@ -351,57 +356,65 @@ index_t FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double lat
 
                 /* Check each segment for closeness and if valid for the profile */
 
-                segment=FirstSegment(segments,nodes,i);
+                segment=FirstSegment(segments,nodes,i,1);
 
                 do
                   {
-                   if(IsNormalSegment(segment))
+                   if(IsNormalSegment(segment) && valid_segment_for_profile(ways,segment,profile))
                      {
-                      Way *way=NULL;
-
-                      if(profile)
-                         way=LookupWay(ways,segment->way,1);
+                      distance_t dist2,dist3;
+                      double lat2,lon2,dist3a,dist3b,distp;
 
-                      if(!profile || way->allow&profile->allow)
-                        {
-                         distance_t dist2,dist3;
-                         double lat2,lon2,dist3a,dist3b,distp;
+                      GetLatLong(nodes,OtherNode(segment,i),&lat2,&lon2);
 
-                         GetLatLong(nodes,OtherNode(segment,i),&lat2,&lon2);
+                      dist2=Distance(lat2,lon2,latitude,longitude);
 
-                         dist2=Distance(lat2,lon2,latitude,longitude);
+                      dist3=Distance(lat1,lon1,lat2,lon2);
 
-                         dist3=Distance(lat1,lon1,lat2,lon2);
+                      /* Use law of cosines (assume flat Earth) */
 
-                         /* 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;
 
-                         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)
+                        {
+                         distp=0;
+                        }
+                      else 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(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)
+                        {
+                         bests=IndexSegment(segments,segment);
 
-                         if(distp<(double)bestd)
+                         if(segment->node1==i)
                            {
-                            bests=IndexSegment(segments,segment);
                             bestn1=i;
                             bestn2=OtherNode(segment,i);
                             bestd1=(distance_t)dist3a;
                             bestd2=(distance_t)dist3b;
-                            bestd=(distance_t)distp;
                            }
+                         else
+                           {
+                            bestn1=OtherNode(segment,i);
+                            bestn2=i;
+                            bestd1=(distance_t)dist3b;
+                            bestd2=(distance_t)dist3a;
+                           }
+
+                         bestd=(distance_t)distp;
                         }
                      }
 
@@ -432,9 +445,61 @@ index_t FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double lat
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Check if the transport defined by the profile is allowed on the segment.
+
+  int valid_segment_for_profile Return 1 if it is or 0 if not.
+
+  Ways *ways The set of ways to use.
+
+  Segment *segment The segment to check.
+
+  Profile *profile The profile to check.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int valid_segment_for_profile(Ways *ways,Segment *segment,Profile *profile)
+{
+ Way *way=LookupWay(ways,segment->way,1);
+ score_t segment_pref;
+ int i;
+
+ /* mode of transport must be allowed on the highway */
+ if(!(way->allow&profile->allow))
+    return(0);
+
+ /* must obey weight restriction (if exists) */
+ if(way->weight && way->weight<profile->weight)
+    return(0);
+
+ /* must obey height/width/length restriction (if exists) */
+ if((way->height && way->height<profile->height) ||
+    (way->width  && way->width <profile->width ) ||
+    (way->length && way->length<profile->length))
+    return(0);
+
+ segment_pref=profile->highway[HIGHWAY(way->type)];
+
+ for(i=1;i<Property_Count;i++)
+    if(ways->file.props & PROPERTIES(i))
+      {
+       if(way->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)
+    return(0);
+
+ /* Must be OK */
+ return(1);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Get the latitude and longitude associated with a node.
 
-  Nodes *nodes The set of nodes.
+  Nodes *nodes The set of nodes to use.
 
   index_t index The node index.
 
@@ -446,16 +511,16 @@ index_t FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double lat
 void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude)
 {
  Node *node=LookupNode(nodes,index,2);
- int latbin=-1,lonbin=-1;
- int start,end,mid;
+ ll_bin_t latbin=-1,lonbin=-1;
+ ll_bin_t start,end,mid;
  index_t offset;
 
- /* Binary search - search key closest below is required.
+ /* Binary search - search key nearest match below is required.
   *
   *  # <- start  |  Check mid and move start or end if it doesn't match
   *  #           |
-  *  #           |  Since an inexact match is wanted we must set end=mid-1
-  *  # <- mid    |  or start=mid because we know that mid 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.
@@ -472,10 +537,10 @@ void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude)
 
     offset=LookupNodeOffset(nodes,nodes->file.latbins*mid);
 
-    if(offset<index)                    /* Mid point is too low */
+    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-1;
+       end=mid?(mid-1):mid;
     else                                /* Mid point is correct */
       {lonbin=mid;break;}
    }
@@ -506,10 +571,10 @@ void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude)
 
     offset=LookupNodeOffset(nodes,lonbin*nodes->file.latbins+mid);
 
-    if(offset<index)                    /* Mid point is too low */
+    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-1;
+       end=mid?(mid-1):mid;
     else                                /* Mid point is correct */
       {latbin=mid;break;}
    }
diff --git a/src/nodes.h b/src/nodes.h
index a1722cd..36f5010 100644
--- a/src/nodes.h
+++ b/src/nodes.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/nodes.h,v 1.37 2010/08/03 18:28:30 amb Exp $
-
  A header file for the nodes.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,24 +37,24 @@
 /*+ A structure containing a single node. +*/
 struct _Node
 {
- index_t    firstseg;           /*+ The index of the first segment. +*/
+ index_t      firstseg;         /*+ The index of the first segment. +*/
 
- ll_off_t   latoffset;          /*+ The node latitude offset within its bin. +*/
- ll_off_t   lonoffset;          /*+ The node longitude offset within its bin. +*/
+ ll_off_t     latoffset;        /*+ The node latitude offset within its bin. +*/
+ ll_off_t     lonoffset;        /*+ The node longitude offset within its bin. +*/
 
- allow_t    allow;              /*+ The types of transport that are allowed through the node. +*/
- uint16_t   flags;              /*+ Flags containing extra information (super-node, turn restriction). +*/
+ transports_t allow;            /*+ The types of transport that are allowed through the node. +*/
+ uint16_t     flags;            /*+ Flags containing extra information (e.g. super-node, turn restriction). +*/
 };
 
 
 /*+ A structure containing the header from the file. +*/
 typedef struct _NodesFile
 {
- index_t  number;               /*+ How many nodes in total? +*/
- index_t  snumber;              /*+ How many super-nodes? +*/
+ index_t  number;               /*+ The number of nodes in total. +*/
+ index_t  snumber;              /*+ The number of super-nodes. +*/
 
- index_t  latbins;              /*+ The number of bins containing latitude. +*/
- index_t  lonbins;              /*+ The number of bins containing longitude. +*/
+ 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. +*/
@@ -64,39 +62,39 @@ typedef struct _NodesFile
  NodesFile;
 
 
-/*+ A structure containing a set of nodes (and pointers to mmap file). +*/
+/*+ A structure containing a set of nodes. +*/
 struct _Nodes
 {
  NodesFile file;                /*+ The header data from the file. +*/
 
 #if !SLIM
 
- void     *data;                /*+ The memory mapped data. +*/
+ void     *data;                /*+ The memory mapped data in the file. +*/
 
- index_t  *offsets;             /*+ An array of offsets to the first node in each bin. +*/
+ index_t  *offsets;             /*+ A pointer to the array of offsets in the file. +*/
 
- Node     *nodes;               /*+ An array of nodes. +*/
+ Node     *nodes;               /*+ A pointer to the array of nodes in the file. +*/
 
 #else
 
  int       fd;                  /*+ The file descriptor for the file. +*/
  off_t     nodesoffset;         /*+ The offset of the nodes within the file. +*/
 
- Node      cached[3];           /*+ The cached nodes. +*/
- index_t   incache[3];          /*+ The indexes of the cached nodes. +*/
+ Node      cached[4];           /*+ Four cached nodes read from the file in slim mode. +*/
+ index_t   incache[4];          /*+ The indexes of the cached nodes. +*/
 
 #endif
 };
 
 
-/* Functions */
+/* Functions in nodes.c */
 
 Nodes *LoadNodeList(const char *filename);
 
-index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude,
+index_t FindClosestNode(Nodes *nodes,Segments *segments,Ways *ways,double latitude,double longitude,
                         distance_t distance,Profile *profile,distance_t *bestdist);
 
-index_t FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude,
+index_t FindClosestSegment(Nodes *nodes,Segments *segments,Ways *ways,double latitude,double longitude,
                            distance_t distance,Profile *profile, distance_t *bestdist,
                            index_t *bestnode1,index_t *bestnode2,distance_t *bestdist1,distance_t *bestdist2);
 
@@ -105,30 +103,33 @@ void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude);
 
 /* Macros and inline functions */
 
+/*+ Return true if this is a super-node. +*/
+#define IsSuperNode(xxx)            (((xxx)->flags)&NODE_SUPER)
+
+/*+ Return true if this is a turn restricted node. +*/
+#define IsTurnRestrictedNode(xxx)   (((xxx)->flags)&NODE_TURNRSTRCT)
+
+
 #if !SLIM
 
 /*+ Return a Node pointer given a set of nodes and an index. +*/
-#define LookupNode(xxx,yyy,zzz)     (&(xxx)->nodes[yyy])
-
-/*+ Return a Segment points given a Node pointer and a set of segments. +*/
-#define FirstSegment(xxx,yyy,zzz)   LookupSegment((xxx),(yyy)->nodes[zzz].firstseg,1)
+#define LookupNode(xxx,yyy,ppp)       (&(xxx)->nodes[yyy])
 
-/*+ Return true if this is a super-node. +*/
-#define IsSuperNode(xxx,yyy)        (((xxx)->nodes[yyy].flags)&NODE_SUPER)
+/*+ Return a Segment index given a Node pointer and a set of segments. +*/
+#define FirstSegment(xxx,yyy,zzz,ppp) LookupSegment((xxx),(yyy)->nodes[zzz].firstseg,1)
 
-/*+ Return the offset of a geographical region given a set of nodes and an index. +*/
-#define LookupNodeOffset(xxx,yyy)   ((xxx)->offsets[yyy])
+/*+ Return the offset of a geographical region given a set of nodes. +*/
+#define LookupNodeOffset(xxx,yyy)     ((xxx)->offsets[yyy])
 
 #else
 
 static Node *LookupNode(Nodes *nodes,index_t index,int position);
 
-#define FirstSegment(xxx,yyy,zzz)   LookupSegment((xxx),FirstSegment_internal(yyy,zzz),1)
+/*+ Return a Segment index given a Node pointer and a set of segments. +*/
+#define FirstSegment(xxx,yyy,zzz,ppp) LookupSegment((xxx),FirstSegment_internal(yyy,zzz),ppp)
 
 static index_t FirstSegment_internal(Nodes *nodes,index_t index);
 
-static int IsSuperNode(Nodes *nodes,index_t index);
-
 static index_t LookupNodeOffset(Nodes *nodes,index_t index);
 
 
@@ -137,7 +138,7 @@ static index_t LookupNodeOffset(Nodes *nodes,index_t index);
 
   Node *LookupNode Returns a pointer to the cached node information.
 
-  Nodes *nodes The nodes structure to use.
+  Nodes *nodes The set of nodes to use.
 
   index_t index The index of the node.
 
@@ -146,11 +147,14 @@ static index_t LookupNodeOffset(Nodes *nodes,index_t index);
 
 static inline Node *LookupNode(Nodes *nodes,index_t index,int position)
 {
- SeekFile(nodes->fd,nodes->nodesoffset+(off_t)index*sizeof(Node));
+ if(nodes->incache[position-1]!=index)
+   {
+    SeekFile(nodes->fd,nodes->nodesoffset+(off_t)index*sizeof(Node));
 
- ReadFile(nodes->fd,&nodes->cached[position-1],sizeof(Node));
+    ReadFile(nodes->fd,&nodes->cached[position-1],sizeof(Node));
 
- nodes->incache[position-1]=index;
+    nodes->incache[position-1]=index;
+   }
 
  return(&nodes->cached[position-1]);
 }
@@ -161,61 +165,27 @@ static inline Node *LookupNode(Nodes *nodes,index_t index,int position)
 
   index_t FirstSegment_internal Returns the index of the first segment.
 
-  Nodes *nodes The nodes structure to use.
+  Nodes *nodes The set of nodes to use.
 
   index_t index The index of the node.
   ++++++++++++++++++++++++++++++++++++++*/
 
 static inline index_t FirstSegment_internal(Nodes *nodes,index_t index)
 {
- if(nodes->incache[0]==index)
-    return(nodes->cached[0].firstseg);
- else if(nodes->incache[1]==index)
-    return(nodes->cached[1].firstseg);
- else if(nodes->incache[2]==index)
-    return(nodes->cached[2].firstseg);
- else
-   {
-    Node *node=LookupNode(nodes,index,3);
+ Node *node;
 
-    return(node->firstseg);
-   }
-}
+ node=LookupNode(nodes,index,4);
 
-
-/*++++++++++++++++++++++++++++++++++++++
-  Decide if a node is a super-node.
-
-  int IsSuperNode Return true if it is a supernode.
-
-  Nodes *nodes The nodes structure to use.
-
-  index_t index The index of the node.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static inline int IsSuperNode(Nodes *nodes,index_t index)
-{
- if(nodes->incache[0]==index)
-    return(nodes->cached[0].flags&NODE_SUPER);
- else if(nodes->incache[1]==index)
-    return(nodes->cached[1].flags&NODE_SUPER);
- else if(nodes->incache[2]==index)
-    return(nodes->cached[2].flags&NODE_SUPER);
- else
-   {
-    Node *node=LookupNode(nodes,index,3);
-
-    return(node->flags&NODE_SUPER);
-   }
+ return(node->firstseg);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Find the offset of nodes in a geographical region.
 
-  index_t LookupNodeOffset Returns the value of the index offset.
+  index_t LookupNodeOffset Returns the index offset.
 
-  Nodes *nodes The nodes structure to use.
+  Nodes *nodes The set of nodes to use.
 
   index_t index The index of the offset.
   ++++++++++++++++++++++++++++++++++++++*/
diff --git a/src/nodesx.c b/src/nodesx.c
index 257a589..474198c 100644
--- a/src/nodesx.c
+++ b/src/nodesx.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/nodesx.c,v 1.76 2010/11/13 14:22:28 amb Exp $
-
  Extented Node data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -40,7 +38,7 @@
 
 #include "files.h"
 #include "logging.h"
-#include "functions.h"
+#include "sorting.h"
 
 
 /* Variables */
@@ -54,7 +52,7 @@ static NodesX *sortnodesx;
 /* Functions */
 
 static int sort_by_id(NodeX *a,NodeX *b);
-static int index_by_id(NodeX *nodex,index_t index);
+static int deduplicate_and_index_by_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);
@@ -63,7 +61,7 @@ static int index_by_lat_long(NodeX *nodex,index_t index);
 /*++++++++++++++++++++++++++++++++++++++
   Allocate a new node list (create a new file or open an existing one).
 
-  NodesX *NewNodeList Returns the node list.
+  NodesX *NewNodeList Returns a pointer to the node list.
 
   int append Set to 1 if the file is to be opened for appending (now or later).
   ++++++++++++++++++++++++++++++++++++++*/
@@ -81,13 +79,7 @@ NodesX *NewNodeList(int append)
  if(append)
     sprintf(nodesx->filename,"%s/nodesx.input.tmp",option_tmpdirname);
  else
-    sprintf(nodesx->filename,"%s/nodesx.%p.tmp",option_tmpdirname,nodesx);
-
-#if SLIM
- nodesx->nfilename=(char*)malloc(strlen(option_tmpdirname)+32);
-
- sprintf(nodesx->nfilename,"%s/nodes.%p.tmp",option_tmpdirname,nodesx);
-#endif
+    sprintf(nodesx->filename,"%s/nodesx.%p.tmp",option_tmpdirname,(void*)nodesx);
 
  if(append)
    {
@@ -97,7 +89,7 @@ NodesX *NewNodeList(int append)
 
     size=SizeFile(nodesx->filename);
 
-    nodesx->xnumber=size/sizeof(NodeX);
+    nodesx->number=size/sizeof(NodeX);
    }
  else
     nodesx->fd=OpenFileNew(nodesx->filename);
@@ -109,9 +101,9 @@ NodesX *NewNodeList(int append)
 /*++++++++++++++++++++++++++++++++++++++
   Free a node list.
 
-  NodesX *nodesx The list to be freed.
+  NodesX *nodesx The set of nodes to be freed.
 
-  int keep Set to 1 if the file is to be kept.
+  int keep Set to 1 if the file is to be kept (for appending later).
   ++++++++++++++++++++++++++++++++++++++*/
 
 void FreeNodeList(NodesX *nodesx,int keep)
@@ -124,23 +116,12 @@ void FreeNodeList(NodesX *nodesx,int keep)
  if(nodesx->idata)
     free(nodesx->idata);
 
-#if !SLIM
- if(nodesx->ndata)
-    free(nodesx->ndata);
-#endif
-
-#if SLIM
- DeleteFile(nodesx->nfilename);
-
- free(nodesx->nfilename);
-#endif
+ if(nodesx->gdata)
+    free(nodesx->gdata);
 
  if(nodesx->super)
     free(nodesx->super);
 
- if(nodesx->offsets)
-    free(nodesx->offsets);
-
  free(nodesx);
 }
 
@@ -148,18 +129,20 @@ void FreeNodeList(NodesX *nodesx,int keep)
 /*++++++++++++++++++++++++++++++++++++++
   Append a single node to an unsorted node list.
 
-  NodesX* nodesx The set of nodes to process.
+  NodesX *nodesx The set of nodes to modify.
 
-  node_t id The node identification.
+  node_t id The node identifier from the original OSM data.
 
   double latitude The latitude of the node.
 
   double longitude The longitude of the node.
 
-  allow_t allow The allowed traffic types through the node.
+  transports_t allow The allowed traffic types through the node.
+
+  uint16_t flags The flags to set for this node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendNode(NodesX* nodesx,node_t id,double latitude,double longitude,allow_t allow)
+void AppendNode(NodesX *nodesx,node_t id,double latitude,double longitude,transports_t allow,uint16_t flags)
 {
  NodeX nodex;
 
@@ -167,32 +150,37 @@ void AppendNode(NodesX* nodesx,node_t id,double latitude,double longitude,allow_
  nodex.latitude =radians_to_latlong(latitude);
  nodex.longitude=radians_to_latlong(longitude);
  nodex.allow=allow;
+ nodex.flags=flags;
 
  WriteFile(nodesx->fd,&nodex,sizeof(NodeX));
 
- nodesx->xnumber++;
+ nodesx->number++;
 
- assert(nodesx->xnumber<NODE_FAKE); /* NODE_FAKE marks the high-water mark for real nodes. */
+ assert(nodesx->number<NODE_FAKE); /* NODE_FAKE marks the high-water mark for real nodes. */
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Sort the node list (i.e. create the sortable indexes).
+  Sort the node list.
 
-  NodesX* nodesx The set of nodes to process.
+  NodesX *nodesx The set of nodes to modify.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void SortNodeList(NodesX* nodesx)
+void SortNodeList(NodesX *nodesx)
 {
  int fd;
+ index_t xnumber;
 
  /* Print the start message */
 
  printf_first("Sorting Nodes");
 
- /* Close the files and re-open them (finished appending) */
+ /* Close the file (finished appending) */
+
+ nodesx->fd=CloseFile(nodesx->fd);
+
+ /* Re-open the file read-only and a new file writeable */
 
- CloseFile(nodesx->fd);
  nodesx->fd=ReOpenFile(nodesx->filename);
 
  DeleteFile(nodesx->filename);
@@ -201,26 +189,27 @@ void SortNodeList(NodesX* nodesx)
 
  /* Allocate the array of indexes */
 
- nodesx->idata=(node_t*)malloc(nodesx->xnumber*sizeof(node_t));
+ nodesx->idata=(node_t*)malloc(nodesx->number*sizeof(node_t));
 
  assert(nodesx->idata); /* Check malloc() worked */
 
  /* Sort by node indexes */
 
+ xnumber=nodesx->number;
+ nodesx->number=0;
+
  sortnodesx=nodesx;
 
- filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(const void*,const void*))sort_by_id,(int (*)(void*,index_t))index_by_id);
+ filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(const void*,const void*))sort_by_id,(int (*)(void*,index_t))deduplicate_and_index_by_id);
 
- /* Close the files and re-open them */
+ /* Close the files */
 
- CloseFile(nodesx->fd);
+ nodesx->fd=CloseFile(nodesx->fd);
  CloseFile(fd);
 
- nodesx->fd=ReOpenFile(nodesx->filename);
-
  /* Print the final message */
 
- printf_last("Sorted Nodes: Nodes=%d Duplicates=%d",nodesx->xnumber,nodesx->xnumber-nodesx->number);
+ printf_last("Sorted Nodes: Nodes=%"Pindex_t" Duplicates=%"Pindex_t,xnumber,xnumber-nodesx->number);
 }
 
 
@@ -249,16 +238,16 @@ static int sort_by_id(NodeX *a,NodeX *b)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Index the nodes after sorting.
+  Create the index of identifiers and discard duplicate nodes.
 
-  int index_by_id Return 1 if the value is to be kept, otherwise zero.
+  int deduplicate_and_index_by_id Return 1 if the value is to be kept, otherwise 0.
 
   NodeX *nodex The extended node.
 
   index_t index The index of this node in the total.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static int index_by_id(NodeX *nodex,index_t index)
+static int deduplicate_and_index_by_id(NodeX *nodex,index_t index)
 {
  if(index==0 || sortnodesx->idata[index-1]!=nodex->id)
    {
@@ -268,18 +257,22 @@ static int index_by_id(NodeX *nodex,index_t index)
 
     return(1);
    }
+ else
+   {
+    logerror("Node %"Pnode_t" is duplicated\n",nodex->id);
 
- return(0);
+    return(0);
+   }
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Sort the node list geographically.
 
-  NodesX* nodesx The set of nodes to process.
+  NodesX *nodesx The set of nodes to modify.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void SortNodeListGeographically(NodesX* nodesx)
+void SortNodeListGeographically(NodesX *nodesx)
 {
  int fd;
 
@@ -287,15 +280,14 @@ void SortNodeListGeographically(NodesX* nodesx)
 
  printf_first("Sorting Nodes Geographically");
 
- /* Allocate the memory for the geographical offsets array */
+ /* Allocate the memory for the geographical index array */
 
- nodesx->offsets=(index_t*)malloc((nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
+ nodesx->gdata=(index_t*)malloc(nodesx->number*sizeof(index_t));
 
- nodesx->latlonbin=0;
+ assert(nodesx->gdata); /* Check malloc() worked */
 
- /* Close the files and re-open them */
+ /* Re-open the file read-only and a new file writeable */
 
- CloseFile(nodesx->fd);
  nodesx->fd=ReOpenFile(nodesx->filename);
 
  DeleteFile(nodesx->filename);
@@ -308,18 +300,11 @@ void SortNodeListGeographically(NodesX* nodesx)
 
  filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(const void*,const void*))sort_by_lat_long,(int (*)(void*,index_t))index_by_lat_long);
 
- /* Close the files and re-open them */
+ /* Close the files */
 
- CloseFile(nodesx->fd);
+ nodesx->fd=CloseFile(nodesx->fd);
  CloseFile(fd);
 
- nodesx->fd=ReOpenFile(nodesx->filename);
-
- /* Finish off the indexing */
-
- for(;nodesx->latlonbin<=(nodesx->latbins*nodesx->lonbins);nodesx->latlonbin++)
-    nodesx->offsets[nodesx->latlonbin]=nodesx->number;
-
  /* Print the final message */
 
  printf_last("Sorted Nodes Geographically");
@@ -327,7 +312,9 @@ void SortNodeListGeographically(NodesX* nodesx)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Sort the nodes into latitude and longitude order.
+  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).
 
   int sort_by_lat_long Returns the comparison of the latitude and longitude fields.
 
@@ -356,29 +343,28 @@ static int sort_by_lat_long(NodeX *a,NodeX *b)
        return(1);
     else
       {
-#ifdef REGRESSION_TESTING
-       // Need this for regression testing because filesort_heapsort() is not order
-       // preserving like qsort() is (or was when tested).
-
-       index_t a_id=a->id;
-       index_t b_id=b->id;
-
-       if(a_id<b_id)
+       if(a->longitude<b->longitude)
           return(-1);
-       else if(a_id>b_id)
+       else if(a->longitude>b->longitude)
           return(1);
        else
-#endif
-          return(0);
+         {
+          if(a->latitude<b->latitude)
+             return(-1);
+          else if(a->latitude>b->latitude)
+             return(1);
+         }
+
+       return(0);
       }
    }
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Index the nodes after sorting.
+  Create the index between the sorted and unsorted nodes.
 
-  int index_by_lat_long Return 1 if the value is to be kept, otherwise zero.
+  int index_by_lat_long Return 1 if the value is to be kept, otherwise 0.
 
   NodeX *nodex The extended node.
 
@@ -387,14 +373,9 @@ static int sort_by_lat_long(NodeX *a,NodeX *b)
 
 static int index_by_lat_long(NodeX *nodex,index_t index)
 {
- /* Work out the offsets */
-
- ll_bin_t latbin=latlong_to_bin(nodex->latitude )-sortnodesx->latzero;
- ll_bin_t lonbin=latlong_to_bin(nodex->longitude)-sortnodesx->lonzero;
- int llbin=lonbin*sortnodesx->latbins+latbin;
+ /* Create the index from the previous sort to the current one */
 
- for(;sortnodesx->latlonbin<=llbin;sortnodesx->latlonbin++)
-    sortnodesx->offsets[sortnodesx->latlonbin]=index;
+ sortnodesx->gdata[nodex->id]=index;
 
  return(1);
 }
@@ -405,16 +386,16 @@ static int index_by_lat_long(NodeX *nodex,index_t index)
 
   index_t IndexNodeX Returns the index of the extended node with the specified id.
 
-  NodesX* nodesx The set of nodes to process.
+  NodesX *nodesx The set of nodes to use.
 
   node_t id The node id to look for.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t IndexNodeX(NodesX* nodesx,node_t id)
+index_t IndexNodeX(NodesX *nodesx,node_t id)
 {
- int start=0;
- int end=nodesx->number-1;
- int mid;
+ index_t start=0;
+ index_t end=nodesx->number-1;
+ index_t mid;
 
  /* Binary search - search key exact match only is required.
   *
@@ -442,7 +423,7 @@ index_t IndexNodeX(NodesX* nodesx,node_t id)
        if(nodesx->idata[mid]<id)      /* Mid point is too low */
           start=mid+1;
        else if(nodesx->idata[mid]>id) /* Mid point is too high */
-          end=mid-1;
+          end=mid?(mid-1):mid;
        else                           /* Mid point is correct */
           return(mid);
       }
@@ -462,22 +443,22 @@ index_t IndexNodeX(NodesX* nodesx,node_t id)
 /*++++++++++++++++++++++++++++++++++++++
   Remove any nodes that are not part of a highway.
 
-  NodesX *nodesx The complete node list.
+  NodesX *nodesx The set of nodes to modify.
 
-  SegmentsX *segmentsx The list of segments.
+  SegmentsX *segmentsx The set of segments to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx)
 {
  NodeX nodex;
- int total=0,highway=0,nothighway=0;
+ index_t total=0,highway=0,nothighway=0;
  ll_bin_t lat_min_bin,lat_max_bin,lon_min_bin,lon_max_bin;
  latlong_t lat_min,lat_max,lon_min,lon_max;
  int fd;
 
  /* Print the start message */
 
- printf_first("Checking: Nodes=0");
+ printf_first("Checking Nodes: Nodes=0");
 
  /* While we are here we can work out the range of data */
 
@@ -486,16 +467,19 @@ void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx)
  lon_min=radians_to_latlong( 4);
  lon_max=radians_to_latlong(-4);
 
- /* Modify the on-disk image */
+ /* Re-open the file read-only and a new file writeable */
+
+ nodesx->fd=ReOpenFile(nodesx->filename);
 
  DeleteFile(nodesx->filename);
 
  fd=OpenFileNew(nodesx->filename);
- SeekFile(nodesx->fd,0);
+
+ /* Modify the on-disk image */
 
  while(!ReadFile(nodesx->fd,&nodex,sizeof(NodeX)))
    {
-    if(IndexFirstSegmentX(segmentsx,nodex.id)==NO_SEGMENT)
+    if(!IsBitSet(segmentsx->usednode,total))
        nothighway++;
     else
       {
@@ -519,17 +503,15 @@ void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx)
     total++;
 
     if(!(total%10000))
-       printf_middle("Checking: Nodes=%d Highway=%d not-Highway=%d",total,highway,nothighway);
+       printf_middle("Checking Nodes: Nodes=%"Pindex_t" Highway=%"Pindex_t" not-Highway=%"Pindex_t,total,highway,nothighway);
    }
 
- /* Close the files and re-open them */
-
- CloseFile(nodesx->fd);
- CloseFile(fd);
+ nodesx->number=highway;
 
- nodesx->fd=ReOpenFile(nodesx->filename);
+ /* Close the files */
 
- nodesx->number=highway;
+ nodesx->fd=CloseFile(nodesx->fd);
+ CloseFile(fd);
 
  /* Work out the number of bins */
 
@@ -544,291 +526,171 @@ void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx)
  nodesx->latbins=(lat_max_bin-lat_min_bin)+1;
  nodesx->lonbins=(lon_max_bin-lon_min_bin)+1;
 
- /* Allocate and clear the super-node markers */
+ /* Free the now-unneeded index */
+
+ free(segmentsx->usednode);
+ segmentsx->usednode=NULL;
 
- nodesx->super=(uint8_t*)calloc(nodesx->number,sizeof(uint8_t));
+ /* Allocate and set the super-node markers */
+
+ nodesx->super=(uint8_t*)malloc((1+nodesx->number/8)*sizeof(uint8_t));
 
  assert(nodesx->super); /* Check calloc() worked */
 
+ memset(nodesx->super,~0,(1+nodesx->number/8));
+
  /* Print the final message */
 
- printf_last("Checked: Nodes=%d Highway=%d not-Highway=%d",total,highway,nothighway);
+ printf_last("Checked Nodes: Nodes=%"Pindex_t" Highway=%"Pindex_t" not-Highway=%"Pindex_t,total,highway,nothighway);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Create the real node data.
+  Insert the super-node flag and the first segment indexes after geographical sorting.
 
-  NodesX *nodesx The set of nodes to use.
+  NodesX *nodesx The set of nodes to modify.
 
-  int iteration The final super-node iteration.
+  SegmentsX *segmentsx The set of segments to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void CreateRealNodes(NodesX *nodesx,int iteration)
+void UpdateNodes(NodesX *nodesx,SegmentsX *segmentsx)
 {
  index_t i;
+ int fd;
 
  /* Print the start message */
 
- printf_first("Creating Real Nodes: Nodes=0");
-
- /* Map into memory */
+ printf_first("Updating Super Nodes: Nodes=0");
 
-#if !SLIM
- nodesx->xdata=MapFile(nodesx->filename);
-#endif
+ /* Re-open the file read-only and a new file writeable */
 
- /* Allocate the memory (or open the file) */
+ nodesx->fd=ReOpenFile(nodesx->filename);
 
-#if !SLIM
- nodesx->ndata=(Node*)malloc(nodesx->number*sizeof(Node));
+ DeleteFile(nodesx->filename);
 
- assert(nodesx->ndata); /* Check malloc() worked */
-#else
- nodesx->nfd=OpenFileNew(nodesx->nfilename);
-#endif
+ fd=OpenFileNew(nodesx->filename);
 
- /* Loop through and allocate. */
+ /* Modify the on-disk image */
 
  for(i=0;i<nodesx->number;i++)
    {
-    NodeX *nodex=LookupNodeX(nodesx,i,1);
-    Node  *node =LookupNodeXNode(nodesx,nodex->id,1);
+    NodeX nodex;
 
-    node->latoffset=latlong_to_off(nodex->latitude);
-    node->lonoffset=latlong_to_off(nodex->longitude);
-    node->firstseg=NO_SEGMENT;
-    node->allow=nodex->allow;
-    node->flags=0;
+    ReadFile(nodesx->fd,&nodex,sizeof(NodeX));
 
-    if(nodesx->super[nodex->id]==iteration)
-       node->flags|=NODE_SUPER;
+    if(IsBitSet(nodesx->super,nodex.id))
+       nodex.flags|=NODE_SUPER;
 
-#if SLIM
-    PutBackNodeXNode(nodesx,nodex->id,1);
-#endif
+    nodex.id=segmentsx->firstnode[nodesx->gdata[nodex.id]];
+
+    WriteFile(fd,&nodex,sizeof(NodeX));
 
     if(!((i+1)%10000))
-       printf_middle("Creating Real Nodes: Nodes=%d",i+1);
+       printf_middle("Updating Super Nodes: Nodes=%"Pindex_t,i+1);
    }
 
- /* Free the unneeded memory */
-
- free(nodesx->super);
- nodesx->super=NULL;
+ /* Close the files */
 
- /* Unmap from memory */
-
-#if !SLIM
- nodesx->xdata=UnmapFile(nodesx->filename);
-#endif
+ nodesx->fd=CloseFile(nodesx->fd);
+ CloseFile(fd);
 
  /* Print the final message */
 
- printf_last("Creating Real Nodes: Nodes=%d",nodesx->number);
+ printf_last("Updated Super Nodes: Nodes=%"Pindex_t,nodesx->number);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Assign the segment indexes to the nodes.
+  Save the final node list database to a file.
 
-  NodesX *nodesx The list of nodes to process.
+  NodesX *nodesx The set of nodes to save.
 
-  SegmentsX* segmentsx The set of segments to use.
+  const char *filename The name of the file to save.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void IndexNodes(NodesX *nodesx,SegmentsX *segmentsx)
+void SaveNodeList(NodesX *nodesx,const char *filename)
 {
  index_t i;
-
- if(nodesx->number==0 || segmentsx->number==0)
-    return;
+ int fd;
+ NodesFile nodesfile={0};
+ index_t super_number=0;
+ ll_bin2_t latlonbin=0,maxlatlonbins;
+ index_t *offsets;
 
  /* Print the start message */
 
- printf_first("Indexing Segments: Segments=0");
-
- /* Map into memory */
-
-#if !SLIM
- nodesx->xdata=MapFile(nodesx->filename);
- segmentsx->xdata=MapFile(segmentsx->filename);
-#endif
-
- /* Index the nodes */
-
- for(i=0;i<segmentsx->number;i++)
-   {
-    SegmentX *segmentx=LookupSegmentX(segmentsx,i,1);
-    node_t id1=segmentx->node1;
-    node_t id2=segmentx->node2;
-    Node *node1=LookupNodeXNode(nodesx,id1,1);
-    Node *node2=LookupNodeXNode(nodesx,id2,2);
-
-    /* Check node1 */
-
-    if(node1->firstseg==NO_SEGMENT)
-      {
-       node1->firstseg=i;
-
-#if SLIM
-       PutBackNodeXNode(nodesx,id1,1);
-#endif
-      }
-    else
-      {
-       index_t index=node1->firstseg;
+ printf_first("Writing Nodes: Nodes=0");
 
-       do
-         {
-          segmentx=LookupSegmentX(segmentsx,index,1);
+ /* Allocate the memory for the geographical offsets array */
 
-          if(segmentx->node1==id1)
-            {
-             index++;
+ offsets=(index_t*)malloc((nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
 
-             if(index>=segmentsx->number)
-                break;
+ assert(offsets); /* Check malloc() worked */
 
-             segmentx=LookupSegmentX(segmentsx,index,1);
+ latlonbin=0;
 
-             if(segmentx->node1!=id1)
-                break;
-            }
-          else
-            {
-             Segment *segment=LookupSegmentXSegment(segmentsx,index,1);
+ /* Re-open the file */
 
-             if(segment->next2==NO_NODE)
-               {
-                segment->next2=i;
+ nodesx->fd=ReOpenFile(nodesx->filename);
 
-#if SLIM
-                PutBackSegmentXSegment(segmentsx,index,1);
-#endif
+ /* Write out the nodes data */
 
-                break;
-               }
-             else
-                index=segment->next2;
-            }
-         }
-       while(1);
-      }
+ fd=OpenFileNew(filename);
 
-    /* Check node2 */
+ SeekFile(fd,sizeof(NodesFile)+(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
 
-    if(node2->firstseg==NO_SEGMENT)
-      {
-       node2->firstseg=i;
+ for(i=0;i<nodesx->number;i++)
+   {
+    NodeX nodex;
+    Node node;
+    ll_bin_t latbin,lonbin;
+    ll_bin2_t llbin;
 
-#if SLIM
-       PutBackNodeXNode(nodesx,id2,2);
-#endif
-      }
-    else
-      {
-       index_t index=node2->firstseg;
+    ReadFile(nodesx->fd,&nodex,sizeof(NodeX));
 
-       do
-         {
-          segmentx=LookupSegmentX(segmentsx,index,1);
+    /* Create the Node */
 
-          if(segmentx->node1==id2)
-            {
-             index++;
+    node.latoffset=latlong_to_off(nodex.latitude);
+    node.lonoffset=latlong_to_off(nodex.longitude);
+    node.firstseg=nodex.id;
+    node.allow=nodex.allow;
+    node.flags=nodex.flags;
 
-             if(index>=segmentsx->number)
-                break;
+    if(node.flags&NODE_SUPER)
+       super_number++;
 
-             segmentx=LookupSegmentX(segmentsx,index,1);
+    /* Work out the offsets */
 
-             if(segmentx->node1!=id2)
-                break;
-            }
-          else
-            {
-             Segment *segment=LookupSegmentXSegment(segmentsx,index,1);
+    latbin=latlong_to_bin(nodex.latitude )-nodesx->latzero;
+    lonbin=latlong_to_bin(nodex.longitude)-nodesx->lonzero;
+    llbin=lonbin*nodesx->latbins+latbin;
 
-             if(segment->next2==NO_NODE)
-               {
-                segment->next2=i;
+    for(;latlonbin<=llbin;latlonbin++)
+       offsets[latlonbin]=i;
 
-#if SLIM
-                PutBackSegmentXSegment(segmentsx,index,1);
-#endif
+    /* Write the data */
 
-                break;
-               }
-             else
-                index=segment->next2;
-            }
-         }
-       while(1);
-      }
+    WriteFile(fd,&node,sizeof(Node));
 
     if(!((i+1)%10000))
-       printf_middle("Indexing Segments: Segments=%d",i+1);
+       printf_middle("Writing Nodes: Nodes=%"Pindex_t,i+1);
    }
 
- /* Unmap from memory */
-
-#if !SLIM
- nodesx->xdata=UnmapFile(nodesx->filename);
- segmentsx->xdata=UnmapFile(segmentsx->filename);
-#endif
-
- /* Print the final message */
-
- printf_last("Indexed Segments: Segments=%d ",segmentsx->number);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Save the node list to a file.
-
-  NodesX* nodesx The set of nodes to save.
-
-  const char *filename The name of the file to save.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void SaveNodeList(NodesX* nodesx,const char *filename)
-{
- index_t i;
- int fd;
- NodesFile nodesfile={0};
- int super_number=0;
-
- /* Print the start message */
-
- printf_first("Writing Nodes: Nodes=0");
+ /* Close the file */
 
- /* Map into memory */
+ nodesx->fd=CloseFile(nodesx->fd);
 
-#if !SLIM
- nodesx->xdata=MapFile(nodesx->filename);
-#endif
+ /* Finish off the offset indexing and write them out */
 
- /* Write out the nodes data */
+ maxlatlonbins=nodesx->latbins*nodesx->lonbins;
 
- fd=OpenFileNew(filename);
+ for(;latlonbin<=maxlatlonbins;latlonbin++)
+    offsets[latlonbin]=nodesx->number;
 
  SeekFile(fd,sizeof(NodesFile));
- WriteFile(fd,nodesx->offsets,(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
+ WriteFile(fd,offsets,(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
 
- for(i=0;i<nodesx->number;i++)
-   {
-    NodeX *nodex=LookupNodeX(nodesx,i,1);
-    Node *node=LookupNodeXNode(nodesx,nodex->id,1);
-
-    if(node->flags&NODE_SUPER)
-       super_number++;
-
-    WriteFile(fd,node,sizeof(Node));
-
-    if(!((i+1)%10000))
-       printf_middle("Writing Nodes: Nodes=%d",i+1);
-   }
+ free(offsets);
 
  /* Write out the header structure */
 
@@ -846,13 +708,7 @@ void SaveNodeList(NodesX* nodesx,const char *filename)
 
  CloseFile(fd);
 
- /* Unmap from memory */
-
-#if !SLIM
- nodesx->xdata=UnmapFile(nodesx->filename);
-#endif
-
  /* Print the final message */
 
- printf_last("Wrote Nodes: Nodes=%d",nodesx->number);
+ printf_last("Wrote Nodes: Nodes=%"Pindex_t,nodesx->number);
 }
diff --git a/src/nodesx.h b/src/nodesx.h
index 35908af..25fea9c 100644
--- a/src/nodesx.h
+++ b/src/nodesx.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/nodesx.h,v 1.30 2010/08/02 18:44:54 amb Exp $
-
  A header file for the extended nodes.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,12 +39,14 @@
 /*+ An extended structure used for processing. +*/
 struct _NodeX
 {
- node_t    id;                  /*+ The node identifier. +*/
+ node_t       id;               /*+ The node identifier; initially the OSM value, later the Node index, finally the first segment. +*/
+
+ latlong_t    latitude;         /*+ The node latitude. +*/
+ latlong_t    longitude;        /*+ The node longitude. +*/
 
- latlong_t latitude;            /*+ The node latitude. +*/
- latlong_t longitude;           /*+ The node longitude. +*/
+ transports_t allow;            /*+ The node allowed traffic. +*/
 
- allow_t   allow;               /*+ The node allowed traffic. +*/
+ uint16_t     flags;            /*+ The node flags. +*/
 };
 
 /*+ A structure containing a set of nodes (memory format). +*/
@@ -55,147 +55,104 @@ struct _NodesX
  char     *filename;            /*+ The name of the temporary file. +*/
  int       fd;                  /*+ The file descriptor of the temporary file. +*/
 
- index_t   xnumber;             /*+ The number of unsorted extended nodes. +*/
+ index_t   number;              /*+ The number of extended nodes still being considered. +*/
 
 #if !SLIM
 
- NodeX    *xdata;               /*+ The extended node data (sorted). +*/
+ NodeX    *data;                /*+ The extended node data (when mapped into memory). +*/
 
 #else
 
- NodeX     xcached[2];          /*+ Two cached nodes read from the file in slim mode. +*/
+ NodeX     cached[2];           /*+ Two cached nodes read from the file in slim mode. +*/
 
 #endif
 
- index_t   number;              /*+ How many entries are still useful? +*/
-
  node_t   *idata;               /*+ The extended node IDs (sorted by ID). +*/
 
- uint8_t  *super;               /*+ A marker for super nodes (same order sorted nodes). +*/
-
-#if !SLIM
-
- Node     *ndata;               /*+ The actual nodes (same order as geographically sorted nodes). +*/
-
-#else
-
- char     *nfilename;           /*+ The name of the temporary file for nodes in slim mode. +*/
- int       nfd;                 /*+ The file descriptor of the temporary file. +*/
-
- Node      ncached[2];          /*+ Two cached nodes read from the file in slim mode. +*/
+ index_t  *gdata;               /*+ The final node indexes (sorted geographically). +*/
 
-#endif
+ uint8_t  *super;               /*+ A bit-mask marker for super nodes (same order as sorted nodes). +*/
 
  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. +*/
-
- index_t   latlonbin;           /*+ A temporary index into the offsets array. +*/
-
- index_t  *offsets;             /*+ An array of offset to the first node in each bin. +*/
 };
 
 
-/* Functions */
+/* Functions in nodesx.c */
 
 NodesX *NewNodeList(int append);
 void FreeNodeList(NodesX *nodesx,int keep);
 
 void SaveNodeList(NodesX *nodesx,const char *filename);
 
-index_t IndexNodeX(NodesX* nodesx,node_t id);
+index_t IndexNodeX(NodesX *nodesx,node_t id);
 
-void AppendNode(NodesX* nodesx,node_t id,double latitude,double longitude,allow_t allow);
+void AppendNode(NodesX *nodesx,node_t id,double latitude,double longitude,transports_t allow,uint16_t flags);
 
 void SortNodeList(NodesX *nodesx);
 
-void SortNodeListGeographically(NodesX* nodesx);
+void SortNodeListGeographically(NodesX *nodesx);
 
 void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx);
 
-void CreateRealNodes(NodesX *nodesx,int iteration);
-
-void IndexNodes(NodesX *nodesx,SegmentsX *segmentsx);
+void UpdateNodes(NodesX *nodesx,SegmentsX *segmentsx);
 
 
-/* Macros / inline functions */
+/* Macros and inline functions */
 
 #if !SLIM
 
-#define LookupNodeX(nodesx,index,position)      &(nodesx)->xdata[index]
+#define LookupNodeX(nodesx,index,position)      &(nodesx)->data[index]
   
-#define LookupNodeXNode(nodesx,index,position)  &(nodesx)->ndata[index]
+#define PutBackNodeX(nodesx,index,position)     /* nop */
 
 #else
 
-static NodeX *LookupNodeX(NodesX* nodesx,index_t index,int position);
+static NodeX *LookupNodeX(NodesX *nodesx,index_t index,int position);
 
-static Node *LookupNodeXNode(NodesX* nodesx,index_t index,int position);
-
-static void PutBackNodeXNode(NodesX* nodesx,index_t index,int position);
+static void PutBackNodeX(NodesX *nodesx,index_t index,int position);
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Lookup a particular extended node.
+  Lookup a particular extended node with the specified id from the file on disk.
 
-  NodeX *LookupNodeX Returns a pointer to the extended node with the specified id.
+  NodeX *LookupNodeX Returns a pointer to a cached copy of the extended node.
 
-  NodesX* nodesx The set of nodes to process.
+  NodesX *nodesx The set of nodes to use.
 
   index_t index The node index to look for.
 
   int position The position in the cache to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline NodeX *LookupNodeX(NodesX* nodesx,index_t index,int position)
+static inline NodeX *LookupNodeX(NodesX *nodesx,index_t index,int position)
 {
  SeekFile(nodesx->fd,(off_t)index*sizeof(NodeX));
 
- ReadFile(nodesx->fd,&nodesx->xcached[position-1],sizeof(NodeX));
-
- return(&nodesx->xcached[position-1]);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Lookup a particular extended node's normal node.
-
-  Node *LookupNodeXNode Returns a pointer to the node with the specified id.
-
-  NodesX* nodesx The set of nodes to process.
-
-  index_t index The node index to look for.
+ ReadFile(nodesx->fd,&nodesx->cached[position-1],sizeof(NodeX));
 
-  int position The position in the cache to use.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static inline Node *LookupNodeXNode(NodesX* nodesx,index_t index,int position)
-{
- SeekFile(nodesx->nfd,(off_t)index*sizeof(Node));
-
- ReadFile(nodesx->nfd,&nodesx->ncached[position-1],sizeof(Node));
-
- return(&nodesx->ncached[position-1]);
+ return(&nodesx->cached[position-1]);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Put back an extended node's normal node.
+  Put back an extended node's data into the file on disk.
 
-  NodesX* nodesx The set of nodes to process.
+  NodesX *nodesx The set of nodes to modify.
 
-  index_t index The node index to look for.
+  index_t index The node index to put back.
 
   int position The position in the cache to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline void PutBackNodeXNode(NodesX* nodesx,index_t index,int position)
+static inline void PutBackNodeX(NodesX *nodesx,index_t index,int position)
 {
- SeekFile(nodesx->nfd,(off_t)index*sizeof(Node));
+ SeekFile(nodesx->fd,(off_t)index*sizeof(NodeX));
 
- WriteFile(nodesx->nfd,&nodesx->ncached[position-1],sizeof(Node));
+ WriteFile(nodesx->fd,&nodesx->cached[position-1],sizeof(NodeX));
 }
 
 #endif /* SLIM */
diff --git a/src/optimiser.c b/src/optimiser.c
index d66ed44..298ab2f 100644
--- a/src/optimiser.c
+++ b/src/optimiser.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/optimiser.c,v 1.94 2010/11/13 14:22:28 amb Exp $
-
  Routing optimiser.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,17 +21,22 @@
 
 
 #include <stdio.h>
+#include <assert.h>
 
 #include "types.h"
 #include "nodes.h"
 #include "segments.h"
 #include "ways.h"
+#include "relations.h"
 
 #include "logging.h"
 #include "functions.h"
+#include "fakes.h"
 #include "results.h"
 
 
+/* Global variables */
+
 /*+ The option not to print any progress information. +*/
 extern int option_quiet;
 
@@ -41,6 +44,11 @@ extern int option_quiet;
 extern int option_quickest;
 
 
+/* Local functions */
+
+static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t endnode,index_t endsegment);
+
+
 /*++++++++++++++++++++++++++++++++++++++
   Find the optimum route between two nodes not passing through a super-node.
 
@@ -52,44 +60,44 @@ extern int option_quickest;
 
   Ways *ways The set of ways to use.
 
-  index_t start The start node.
-
-  index_t finish The finish node.
+  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 prev_segment The previous segment before the start node.
+
+  index_t finish_node The finish node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t start,index_t finish,Profile *profile)
+Results *FindNormalRoute(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;
- index_t node1,node2;
  score_t finish_score;
  double  finish_lat,finish_lon;
+ Result  *finish_result;
  Result  *result1,*result2;
- Node    *node;
- Segment *segment;
- Way     *way;
 
  /* Set up the finish conditions */
 
  finish_score=INF_SCORE;
+ finish_result=NULL;
 
- if(IsFakeNode(finish))
-    GetFakeLatLong(finish,&finish_lat,&finish_lon);
+ if(IsFakeNode(finish_node))
+    GetFakeLatLong(finish_node,&finish_lat,&finish_lon);
  else
-    GetLatLong(nodes,finish,&finish_lat,&finish_lon);
+    GetLatLong(nodes,finish_node,&finish_lat,&finish_lon);
 
  /* Create the list of results and insert the first node into the queue */
 
- results=NewResultsList(8);
+ results=NewResultsList(64);
 
- results->start=start;
- results->finish=finish;
+ results->start_node=start_node;
+ results->prev_segment=prev_segment;
 
- result1=InsertResult(results,start);
-
- ZeroResult(result1);
+ result1=InsertResult(results,results->start_node,results->prev_segment);
 
  queue=NewQueueList();
 
@@ -99,46 +107,84 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
  while((result1=PopFromQueue(queue)))
    {
+    Segment *segment;
+    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;
+
+    /* lookup if a turn restriction applies */
+    if(profile->turns && !IsFakeNode(node1) && IsTurnRestrictedNode(LookupNode(nodes,node1,1)))
+       turnrelation=FindFirstTurnRelation2(relations,node1,seg1r);
+
+    /* Loop across all segments */
 
     if(IsFakeNode(node1))
        segment=FirstFakeSegment(node1);
     else
-       segment=FirstSegment(segments,nodes,node1);
+       segment=FirstSegment(segments,nodes,node1,1);
 
     while(segment)
       {
+       Way *way;
+       index_t node2,seg2,seg2r;
        score_t segment_pref,segment_score,cumulative_score;
        int i;
 
-       node2=OtherNode(segment,node1);  /* need this here because we use node2 later */
+       node2=OtherNode(segment,node1); /* need this here because we use node2 at the end of the loop */
 
+       /* must be a normal segment */
        if(!IsNormalSegment(segment))
           goto endloop;
 
+       /* must obey one-way restrictions (unless profile allows) */
        if(profile->oneway && IsOnewayTo(segment,node1))
           goto endloop;
 
-       if(result1->prev==node2)
+       if(IsFakeNode(node1) || IsFakeNode(node2))
+         {
+          seg2 =IndexFakeSegment(segment);
+          seg2r=IndexRealSegment(seg2);
+         }
+       else
+         {
+          seg2 =IndexSegment(segments,segment);
+          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;
 
-       if(node2!=finish && !IsFakeNode(node2) && IsSuperNode(nodes,node2))
+       /* must not pass over super-node */
+       if(node2!=finish_node && !IsFakeNode(node2) && IsSuperNode(LookupNode(nodes,node2,2)))
           goto endloop;
 
        way=LookupWay(ways,segment->way,1);
 
+       /* mode of transport must be allowed on the highway */
        if(!(way->allow&profile->allow))
           goto endloop;
 
-       if(!profile->highway[HIGHWAY(way->type)])
-          goto endloop;
-
+       /* must obey weight restriction (if exists) */
        if(way->weight && way->weight<profile->weight)
           goto endloop;
 
+       /* must obey height/width/length restriction (if exists) */
        if((way->height && way->height<profile->height) ||
           (way->width  && way->width <profile->width ) ||
           (way->length && way->length<profile->length))
@@ -155,12 +201,14 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t star
                 segment_pref*=profile->props_no[i];
             }
 
+       /* profile preferences must allow this highway */
        if(segment_pref==0)
           goto endloop;
 
+       /* mode of transport must be allowed through node2 */
        if(!IsFakeNode(node2))
          {
-          node=LookupNode(nodes,node2,1);
+          Node *node=LookupNode(nodes,node2,2);
 
           if(!(node->allow&profile->allow))
              goto endloop;
@@ -173,25 +221,25 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
        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);
+       result2=FindResult(results,node2,seg2);
 
-       if(!result2)                         /* New end node */
+       if(!result2) /* New end node/segment combination */
          {
-          result2=InsertResult(results,node2);
-          result2->prev=node1;
-          result2->next=NO_NODE;
+          result2=InsertResult(results,node2,seg2);
+          result2->prev=result1;
           result2->score=cumulative_score;
-          if(IsFakeNode(node1) || IsFakeNode(node2))
-             result2->segment=IndexFakeSegment(segment);
-          else
-             result2->segment=IndexSegment(segments,segment);
 
-          if(node2==finish)
+          if(node2==finish_node)
             {
-             finish_score=cumulative_score;
+             if(cumulative_score<finish_score)
+               {
+                finish_score=cumulative_score;
+                finish_result=result2;
+               }
             }
           else
             {
@@ -200,18 +248,19 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t star
              InsertInQueue(queue,result2);
             }
          }
-       else if(cumulative_score<result2->score) /* New end node is better */
+       else if(cumulative_score<result2->score) /* New score for end node/segment combination is better */
          {
-          result2->prev=node1;
+          result2->prev=result1;
           result2->score=cumulative_score;
-          if(IsFakeNode(node1) || IsFakeNode(node2))
-             result2->segment=IndexFakeSegment(segment);
-          else
-             result2->segment=IndexSegment(segments,segment);
+          result2->segment=seg2;
 
-          if(node2==finish)
+          if(node2==finish_node)
             {
-             finish_score=cumulative_score;
+             if(cumulative_score<finish_score)
+               {
+                finish_score=cumulative_score;
+                finish_result=result2;
+               }
             }
           else
             {
@@ -232,8 +281,8 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t star
          {
           segment=NextSegment(segments,segment,node1);
 
-          if(!segment && IsFakeNode(finish))
-             segment=ExtraFakeSegment(node1,finish);
+          if(!segment && IsFakeNode(finish_node))
+             segment=ExtraFakeSegment(node1,finish_node);
          }
       }
    }
@@ -242,20 +291,20 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
  /* Check it worked */
 
- if(!FindResult(results,finish))
+ if(!finish_result)
    {
     FreeResultsList(results);
     return(NULL);
    }
 
- FixForwardRoute(results,finish);
+ FixForwardRoute(results,finish_result);
 
  return(results);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find the optimum route between two nodes where the start and end are a set of pre-routed super-nodes.
+  Find the optimum route between two nodes where the start and end are a set of pre/post-routed super-nodes.
 
   Results *FindMiddleRoute Returns a set of results.
 
@@ -265,25 +314,23 @@ Results *FindNormalRoute(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
   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 initial portion of the route.
 
   Results *end The final portion of the route.
-
-  Profile *profile The profile containing the transport type, speeds and allowed highways.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *begin,Results *end,Profile *profile)
+Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,Results *begin,Results *end)
 {
  Results *results;
  Queue   *queue;
- index_t node1,node2;
- index_t end_prev;
+ Result  *finish_result;
  score_t finish_score;
  double  finish_lat,finish_lon;
- Result  *result1,*result2,*result3;
- Node    *node;
- Segment *segment;
- Way     *way;
+ Result  *result1,*result2,*result3,*result4;
 
  if(!option_quiet)
     printf_first("Routing: Super-Nodes checked = 0");
@@ -291,44 +338,75 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
  /* Set up the finish conditions */
 
  finish_score=INF_DISTANCE;
- end_prev=NO_NODE;
+ finish_result=NULL;
 
- if(IsFakeNode(end->finish))
-    GetFakeLatLong(end->finish,&finish_lat,&finish_lon);
+ if(IsFakeNode(end->finish_node))
+    GetFakeLatLong(end->finish_node,&finish_lat,&finish_lon);
  else
-    GetLatLong(nodes,end->finish,&finish_lat,&finish_lon);
+    GetLatLong(nodes,end->finish_node,&finish_lat,&finish_lon);
 
  /* Create the list of results and insert the first node into the queue */
 
- results=NewResultsList(2048);
+ results=NewResultsList(65536);
 
- results->start=begin->start;
- results->finish=end->finish;
+ results->start_node=begin->start_node;
+ results->prev_segment=begin->prev_segment;
 
- result1=InsertResult(results,begin->start);
- result3=FindResult(begin,begin->start);
+ if(begin->number==1)
+   {
+    if(begin->prev_segment==NO_SEGMENT)
+       results->prev_segment=NO_SEGMENT;
+    else
+      {
+       index_t superseg=FindSuperSegment(nodes,segments,ways,relations,profile,begin->start_node,begin->prev_segment);
 
- *result1=*result3;
+       results->prev_segment=superseg;
+      }
+   }
+
+ result1=InsertResult(results,results->start_node,results->prev_segment);
 
  queue=NewQueueList();
 
- /* Insert the finish points of the beginning part of the path into the queue */
+ /* Insert the finish points of the beginning part of the path into the queue,
+    translating the segments into super-segments. */
 
  result3=FirstResult(begin);
 
  while(result3)
    {
-    if(result3->node!=begin->start && !IsFakeNode(result3->node) && IsSuperNode(nodes,result3->node))
+    if((results->start_node!=result3->node || results->prev_segment!=result3->segment) &&
+       !IsFakeNode(result3->node) && IsSuperNode(LookupNode(nodes,result3->node,1)))
       {
-       result2=InsertResult(results,result3->node);
+       Result *result5=result1;
+       index_t superseg=FindSuperSegment(nodes,segments,ways,relations,profile,result3->node,result3->segment);
 
-       *result2=*result3;
+       if(superseg!=result3->segment)
+         {
+          result5=InsertResult(results,result3->node,result3->segment);
 
-       result2->prev=begin->start;
+          result5->prev=result1;
+         }
+
+       if(!FindResult(results,result3->node,superseg))
+         {
+          result2=InsertResult(results,result3->node,superseg);
+          result2->prev=result5;
+
+          result2->score=result3->score;
+          result2->sortby=result3->score;
 
-       result2->sortby=result2->score;
+          InsertInQueue(queue,result2);
 
-       InsertInQueue(queue,result2);
+          if((result4=FindResult(end,result2->node,result2->segment)))
+            {
+             if((result2->score+result4->score)<finish_score)
+               {
+                finish_score=result2->score+result4->score;
+                finish_result=result2;
+               }
+            }
+         }
       }
 
     result3=NextResult(begin,result3);
@@ -341,40 +419,64 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
 
  while((result1=PopFromQueue(queue)))
    {
+    index_t node1,seg1;
+    Segment *segment;
+    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;
+
+    /* lookup if a turn restriction applies */
+    if(profile->turns && IsTurnRestrictedNode(LookupNode(nodes,node1,1))) /* node1 cannot be a fake node (must be a super-node) */
+       turnrelation=FindFirstTurnRelation2(relations,node1,seg1);
+
+    /* Loop across all segments */
 
-    segment=FirstSegment(segments,nodes,node1);
+    segment=FirstSegment(segments,nodes,node1,1); /* node1 cannot be a fake node (must be a super-node) */
 
     while(segment)
       {
+       Way *way;
+       Node *node;
+       index_t node2,seg2;
        score_t segment_pref,segment_score,cumulative_score;
        int i;
 
+       /* must be a super segment */
        if(!IsSuperSegment(segment))
           goto endloop;
 
+       /* must obey one-way restrictions (unless profile allows) */
        if(profile->oneway && IsOnewayTo(segment,node1))
           goto endloop;
 
        node2=OtherNode(segment,node1);
 
-       if(result1->prev==node2)
+       seg2=IndexSegment(segments,segment); /* node2 cannot be a fake node (must be a super-node) */
+
+       /* must not perform U-turn */
+       if(seg1==seg2) /* No fake segments, applies to all profiles */
+          goto endloop;
+
+       /* must obey turn relations */
+       if(turnrelation!=NO_RELATION && !IsTurnAllowed(relations,turnrelation,node1,seg1,seg2,profile->allow))
           goto endloop;
 
        way=LookupWay(ways,segment->way,1);
 
+       /* transport must be allowed on the highway */
        if(!(way->allow&profile->allow))
           goto endloop;
 
-       if(!profile->highway[HIGHWAY(way->type)])
-          goto endloop;
-
+       /* must obey weight restriction (if exists) */
        if(way->weight && way->weight<profile->weight)
           goto endloop;
 
+       /* must obey height/width/length restriction (if exists) */
        if((way->height && way->height<profile->height) ||
           (way->width  && way->width <profile->width ) ||
           (way->length && way->length<profile->length))
@@ -391,11 +493,13 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
                 segment_pref*=profile->props_no[i];
             }
 
+       /* profile preferences must allow this highway */
        if(segment_pref==0)
           goto endloop;
 
-       node=LookupNode(nodes,node2,1);
+       node=LookupNode(nodes,node2,2); /* node2 cannot be a fake node (must be a super-node) */
 
+       /* mode of transport must be allowed through node2 */
        if(!(node->allow&profile->allow))
           goto endloop;
 
@@ -406,25 +510,24 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
 
        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);
+       result2=FindResult(results,node2,seg2);
 
-       if(!result2)                         /* New end node */
+       if(!result2) /* New end node/segment pair */
          {
-          result2=InsertResult(results,node2);
-          result2->prev=node1;
-          result2->next=NO_NODE;
+          result2=InsertResult(results,node2,seg2);
+          result2->prev=result1;
           result2->score=cumulative_score;
-          result2->segment=IndexSegment(segments,segment);
 
-          if((result3=FindResult(end,node2)))
+          if((result3=FindResult(end,node2,seg2)))
             {
              if((result2->score+result3->score)<finish_score)
                {
                 finish_score=result2->score+result3->score;
-                end_prev=node2;
+                finish_result=result2;
                }
             }
           else
@@ -432,7 +535,8 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
              double lat,lon;
              distance_t direct;
 
-             GetLatLong(nodes,node2,&lat,&lon);
+             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)
@@ -443,18 +547,17 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
              InsertInQueue(queue,result2);
             }
          }
-       else if(cumulative_score<result2->score) /* New end node is better */
+       else if(cumulative_score<result2->score) /* New end node/segment pair is better */
          {
-          result2->prev=node1;
+          result2->prev=result1;
           result2->score=cumulative_score;
-          result2->segment=IndexSegment(segments,segment);
 
-          if((result3=FindResult(end,node2)))
+          if((result3=FindResult(end,node2,seg2)))
             {
              if((result2->score+result3->score)<finish_score)
                {
                 finish_score=result2->score+result3->score;
-                end_prev=node2;
+                finish_result=result2;
                }
             }
           else if(result2->score<finish_score)
@@ -462,7 +565,8 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
              double lat,lon;
              distance_t direct;
 
-             GetLatLong(nodes,node2,&lat,&lon);
+             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)
@@ -474,48 +578,111 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
             }
          }
 
-      endloop:
-
-       if(!option_quiet && !(results->number%10000))
+       if(!option_quiet && !(results->number%1000))
           printf_middle("Routing: Super-Nodes checked = %d",results->number);
 
-       segment=NextSegment(segments,segment,node1);
+      endloop:
+
+       segment=NextSegment(segments,segment,node1); /* node1 cannot be a fake node (must be a super-node) */
       }
    }
 
  if(!option_quiet)
     printf_last("Routing: Super-Nodes checked = %d",results->number);
 
- /* Finish off the end part of the route. */
-
- if(!FindResult(results,end->finish) && end_prev!=NO_NODE)
-   {
-    result2=InsertResult(results,end->finish);
-    result3=FindResult(end,end->finish);
-
-    *result2=*result3;
-
-    result2->prev=end_prev;
-    result2->score=finish_score;
-   }
-
  FreeQueueList(queue);
 
  /* Check it worked */
 
- if(end_prev==NO_NODE)
+ if(!finish_result)
    {
     FreeResultsList(results);
     return(NULL);
    }
 
- FixForwardRoute(results,end->finish);
+ /* Finish off the end part of the route */
+
+ if(finish_result->node!=end->finish_node)
+   {
+    result3=InsertResult(results,end->finish_node,NO_SEGMENT);
+
+    result3->prev=finish_result;
+    result3->score=finish_score;
+
+    finish_result=result3;
+   }
+
+ FixForwardRoute(results,finish_result);
 
  return(results);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Find the super-segment that represents the route that contains a particular segment.
+
+  index_t FindSuperSegment Returns the index of the super-segment.
+
+  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 endnode The super-node that the route ends at.
+
+  index_t endsegment The segment that the route ends with.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static index_t FindSuperSegment(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t endnode,index_t endsegment)
+{
+ Segment *segment;
+
+ if(IsFakeSegment(endsegment))
+    endsegment=IndexRealSegment(endsegment);
+
+ segment=LookupSegment(segments,endsegment,2);
+
+ if(IsSuperSegment(segment))
+    return(endsegment);
+
+ /* Loop across all segments */
+
+ segment=FirstSegment(segments,nodes,endnode,3); /* endnode cannot be a fake node (must be a super-node) */
+
+ while(segment)
+   {
+    if(IsSuperSegment(segment))
+      {
+       Results *results;
+       index_t startnode;
+
+       startnode=OtherNode(segment,endnode);
+
+       results=FindNormalRoute(nodes,segments,ways,relations,profile,startnode,NO_SEGMENT,endnode);
+
+       if(results && results->last_segment==endsegment)
+         {
+          FreeResultsList(results);
+          return(IndexSegment(segments,segment));
+         }
+
+       if(results)
+          FreeResultsList(results);
+      }
+
+    segment=NextSegment(segments,segment,endnode); /* endnode cannot be a fake node (must be a super-node) */
+   }
+
+ return(endsegment);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Find all routes from a specified node to any super-node.
 
   Results *FindStartRoutes Returns a set of results.
@@ -526,30 +693,36 @@ Results *FindMiddleRoute(Nodes *nodes,Segments *segments,Ways *ways,Results *beg
 
   Ways *ways The set of ways to use.
 
-  index_t start The start node.
+  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 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,index_t start,Profile *profile)
+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 *results;
  Queue   *queue;
- index_t node1,node2;
  Result  *result1,*result2;
- Node    *node;
- Segment *segment;
- Way     *way;
+ int     found_finish=0;
 
- /* Insert the first node into the queue */
+ /* Create the results and insert the start node */
 
- results=NewResultsList(8);
+ results=NewResultsList(64);
 
- results->start=start;
+ results->start_node=start_node;
+ results->prev_segment=prev_segment;
 
- result1=InsertResult(results,start);
+ result1=InsertResult(results,results->start_node,results->prev_segment);
 
- ZeroResult(result1);
+ /* Insert the first node into the queue */
 
  queue=NewQueueList();
 
@@ -559,40 +732,76 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
  while((result1=PopFromQueue(queue)))
    {
+    index_t node1,seg1,seg1r;
+    Segment *segment;
+    index_t turnrelation=NO_RELATION;
+
     node1=result1->node;
+    seg1=result1->segment;
+
+    if(IsFakeSegment(seg1))
+       seg1r=IndexRealSegment(seg1);
+    else
+       seg1r=seg1;
+
+    /* lookup if a turn restriction applies */
+    if(profile->turns && !IsFakeNode(node1) && IsTurnRestrictedNode(LookupNode(nodes,node1,1)))
+       turnrelation=FindFirstTurnRelation2(relations,node1,seg1r);
+
+    /* Loop across all segments */
 
     if(IsFakeNode(node1))
        segment=FirstFakeSegment(node1);
     else
-       segment=FirstSegment(segments,nodes,node1);
+       segment=FirstSegment(segments,nodes,node1,1);
 
     while(segment)
       {
+       Way *way;
+       index_t node2,seg2,seg2r;
        score_t segment_pref,segment_score,cumulative_score;
        int i;
 
+       node2=OtherNode(segment,node1); /* need this here because we use node2 at the end of the loop */
+
+       /* must be a normal segment */
        if(!IsNormalSegment(segment))
           goto endloop;
 
+       /* must obey one-way restrictions (unless profile allows) */
        if(profile->oneway && IsOnewayTo(segment,node1))
           goto endloop;
 
-       node2=OtherNode(segment,node1);
+       if(IsFakeNode(node1) || IsFakeNode(node2))
+         {
+          seg2 =IndexFakeSegment(segment);
+          seg2r=IndexRealSegment(seg2);
+         }
+       else
+         {
+          seg2 =IndexSegment(segments,segment);
+          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;
 
-       if(result1->prev==node2)
+       /* must obey turn relations */
+       if(turnrelation!=NO_RELATION && !IsTurnAllowed(relations,turnrelation,node1,seg1r,seg2r,profile->allow))
           goto endloop;
 
        way=LookupWay(ways,segment->way,1);
 
+       /* mode of transport must be allowed on the highway */
        if(!(way->allow&profile->allow))
           goto endloop;
 
-       if(!profile->highway[HIGHWAY(way->type)])
-          goto endloop;
-
+       /* must obey weight restriction (if exists) */
        if(way->weight && way->weight<profile->weight)
           goto endloop;
 
+       /* must obey height/width/length restriction (if exists) */
        if((way->height && way->height<profile->height) ||
           (way->width  && way->width <profile->width ) ||
           (way->length && way->length<profile->length))
@@ -609,12 +818,14 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t star
                 segment_pref*=profile->props_no[i];
             }
 
+       /* profile preferences must allow this highway */
        if(segment_pref==0)
           goto endloop;
 
+       /* mode of transport must be allowed through node2 */
        if(!IsFakeNode(node2))
          {
-          node=LookupNode(nodes,node2,1);
+          Node *node=LookupNode(nodes,node2,2);
 
           if(!(node->allow&profile->allow))
              goto endloop;
@@ -627,35 +838,32 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
        cumulative_score=result1->score+segment_score;
 
-       result2=FindResult(results,node2);
+       result2=FindResult(results,node2,seg2);
 
-       if(!result2)                         /* New end node */
+       if(!result2) /* New end node/segment combination */
          {
-          result2=InsertResult(results,node2);
-          result2->prev=node1;
-          result2->next=NO_NODE;
+          result2=InsertResult(results,node2,seg2);
+          result2->prev=result1;
           result2->score=cumulative_score;
-          if(IsFakeNode(node1) || IsFakeNode(node2))
-             result2->segment=IndexFakeSegment(segment);
-          else
-             result2->segment=IndexSegment(segments,segment);
 
-          if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2))
+          if(!IsFakeNode(node2) && IsSuperNode(LookupNode(nodes,node2,2)))
+             (*nsuper)++;
+
+          if(!IsFakeNode(node2) && !IsSuperNode(LookupNode(nodes,node2,2)))
             {
              result2->sortby=result2->score;
              InsertInQueue(queue,result2);
             }
+
+          if(node2==finish_node)
+             found_finish=1;
          }
-       else if(cumulative_score<result2->score) /* New end node is better */
+       else if(cumulative_score<result2->score) /* New end node/segment combination is better */
          {
-          result2->prev=node1;
+          result2->prev=result1;
           result2->score=cumulative_score;
-          if(IsFakeNode(node1) || IsFakeNode(node2))
-             result2->segment=IndexFakeSegment(segment);
-          else
-             result2->segment=IndexSegment(segments,segment);
 
-          if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2))
+          if(!IsFakeNode(node2) && !IsSuperNode(LookupNode(nodes,node2,2)))
             {
              result2->sortby=result2->score;
              InsertInQueue(queue,result2);
@@ -666,8 +874,15 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
        if(IsFakeNode(node1))
           segment=NextFakeSegment(segment,node1);
+       else if(IsFakeNode(node2))
+          segment=NULL; /* cannot call NextSegment() with a fake segment */
        else
+         {
           segment=NextSegment(segments,segment,node1);
+
+          if(!segment && IsFakeNode(finish_node))
+             segment=ExtraFakeSegment(node1,finish_node);
+         }
       }
    }
 
@@ -675,7 +890,7 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
  /* Check it worked */
 
- if(results->number==1)
+ if(results->number==1 || (*nsuper==0 && found_finish==0))
    {
     FreeResultsList(results);
     return(NULL);
@@ -686,7 +901,7 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find all routes from any super-node to a specific node.
+  Find all routes from any super-node to a specific node (by working backwards from the specific node to all super-nodes).
 
   Results *FindFinishRoutes Returns a set of results.
 
@@ -696,30 +911,28 @@ Results *FindStartRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t star
 
   Ways *ways The set of ways to use.
 
-  index_t finish The finishing node.
+  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 finishing node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t finish,Profile *profile)
+Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,index_t finish_node)
 {
- Results *results;
+ Results *results,*results2;
  Queue   *queue;
- index_t node1,node2;
- Result  *result1,*result2;
- Node    *node;
- Segment *segment;
- Way     *way;
+ Result  *result1,*result2,*result3;
 
- /* Insert the first node into the queue */
+ /* Create the results and insert the finish node */
 
- results=NewResultsList(8);
+ results=NewResultsList(64);
 
- results->finish=finish;
+ results->finish_node=finish_node;
 
- result1=InsertResult(results,finish);
+ result1=InsertResult(results,finish_node,NO_SEGMENT);
 
- ZeroResult(result1);
+ /* Insert the first node into the queue */
 
  queue=NewQueueList();
 
@@ -729,40 +942,81 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t fin
 
  while((result1=PopFromQueue(queue)))
    {
+    index_t node1,seg1,seg1r;
+    Segment *segment;
+    index_t turnrelation=NO_RELATION;
+
     node1=result1->node;
+    seg1=result1->segment;
+
+    if(IsFakeSegment(seg1))
+       seg1r=IndexRealSegment(seg1);
+    else
+       seg1r=seg1;
+
+    /* lookup if a turn restriction applies */
+    if(profile->turns && !IsFakeNode(node1) && IsTurnRestrictedNode(LookupNode(nodes,node1,1)))
+       turnrelation=FindFirstTurnRelation1(relations,node1); /* working backwards => turn relation sort order doesn't help */
+
+    /* Loop across all segments */
 
     if(IsFakeNode(node1))
        segment=FirstFakeSegment(node1);
     else
-       segment=FirstSegment(segments,nodes,node1);
+       segment=FirstSegment(segments,nodes,node1,1);
 
     while(segment)
       {
+       Way *way;
+       index_t node2,seg2,seg2r;
        score_t segment_pref,segment_score,cumulative_score;
        int i;
 
-       if(!IsNormalSegment(segment))
+       /* must be a normal segment */
+       if((IsFakeNode(node1) || !IsSuperNode(LookupNode(nodes,node1,1))) && !IsNormalSegment(segment))
           goto endloop;
 
-       if(profile->oneway && IsOnewayFrom(segment,node1))
+       /* must obey one-way restrictions (unless profile allows) */
+       if(profile->oneway && IsOnewayFrom(segment,node1)) /* Disallow oneway from node2 *to* node1 */
           goto endloop;
 
        node2=OtherNode(segment,node1);
 
-       if(result1->next==node2)
+       if(IsFakeNode(node1) || IsFakeNode(node2))
+         {
+          seg2 =IndexFakeSegment(segment);
+          seg2r=IndexRealSegment(seg2);
+         }
+       else
+         {
+          seg2 =IndexSegment(segments,segment);
+          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)
+         {
+          index_t turnrelation2=FindFirstTurnRelation2(relations,node1,seg2r); /* node2 -> node1 -> result1->next->node */
+
+          if(turnrelation2!=NO_RELATION && !IsTurnAllowed(relations,turnrelation2,node1,seg2r,seg1r,profile->allow))
+             goto endloop;
+         }
+
        way=LookupWay(ways,segment->way,1);
 
+       /* mode of transport must be allowed on the highway */
        if(!(way->allow&profile->allow))
           goto endloop;
 
-       if(!profile->highway[HIGHWAY(way->type)])
-          goto endloop;
-
+       /* must obey weight restriction (if exists) */
        if(way->weight && way->weight<profile->weight)
           goto endloop;
 
+       /* must obey height/width/length restriction (if exists) */
        if((way->height && way->height<profile->height) ||
           (way->width  && way->width <profile->width ) ||
           (way->length && way->length<profile->length))
@@ -779,12 +1033,14 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t fin
                 segment_pref*=profile->props_no[i];
             }
 
+       /* profile preferences must allow this highway */
        if(segment_pref==0)
           goto endloop;
 
+       /* mode of transport must be allowed through node2 */
        if(!IsFakeNode(node2))
          {
-          node=LookupNode(nodes,node2,1);
+          Node *node=LookupNode(nodes,node2,2);
 
           if(!(node->allow&profile->allow))
              goto endloop;
@@ -797,20 +1053,15 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t fin
 
        cumulative_score=result1->score+segment_score;
 
-       result2=FindResult(results,node2);
+       result2=FindResult(results,node2,seg2);
 
-       if(!result2)                         /* New end node */
+       if(!result2) /* New end node */
          {
-          result2=InsertResult(results,node2);
-          result2->prev=NO_NODE;
-          result2->next=node1;
+          result2=InsertResult(results,node2,seg2);
+          result2->next=result1;   /* working backwards */
           result2->score=cumulative_score;
-          if(IsFakeNode(node1) || IsFakeNode(node2))
-             result2->segment=IndexFakeSegment(segment);
-          else
-             result2->segment=IndexSegment(segments,segment);
 
-          if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2))
+          if(IsFakeNode(node1) || (!IsFakeNode(node1) && !IsSuperNode(LookupNode(nodes,node1,1)))) /* Overshoot by one segment */
             {
              result2->sortby=result2->score;
              InsertInQueue(queue,result2);
@@ -818,14 +1069,10 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t fin
          }
        else if(cumulative_score<result2->score) /* New end node is better */
          {
-          result2->next=node1;
+          result2->next=result1; /* working backwards */
           result2->score=cumulative_score;
-          if(IsFakeNode(node1) || IsFakeNode(node2))
-             result2->segment=IndexFakeSegment(segment);
-          else
-             result2->segment=IndexSegment(segments,segment);
 
-          if(!IsFakeNode(node2) && !IsSuperNode(nodes,node2))
+          if(IsFakeNode(node1) || (!IsFakeNode(node1) && !IsSuperNode(LookupNode(nodes,node1,1)))) /* Overshoot by one segment */
             {
              result2->sortby=result2->score;
              InsertInQueue(queue,result2);
@@ -851,7 +1098,46 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t fin
     return(NULL);
    }
 
- return(results);
+ /* Create a results structure with the node at the end of the segment opposite the start */
+
+ results2=NewResultsList(64);
+
+ results2->finish_node=results->finish_node;
+
+ result3=FirstResult(results);
+
+ while(result3)
+   {
+    if(result3->next)
+      {
+       result2=InsertResult(results2,result3->next->node,result3->segment);
+
+       result2->score=result3->next->score;
+      }
+
+    result3=NextResult(results,result3);
+   }
+
+ /* Fix up the result->next pointers */
+
+ result3=FirstResult(results);
+
+ while(result3)
+   {
+    if(result3->next && result3->next->next)
+      {
+       result1=FindResult(results2,result3->next->node,result3->segment);
+       result2=FindResult(results2,result3->next->next->node,result3->next->segment);
+
+       result1->next=result2;
+      }
+
+    result3=NextResult(results,result3);
+   }
+
+ FreeResultsList(results);
+
+ return(results2);
 }
 
 
@@ -860,100 +1146,173 @@ Results *FindFinishRoutes(Nodes *nodes,Segments *segments,Ways *ways,index_t fin
 
   Results *CombineRoutes Returns the results from joining the super-nodes.
 
-  Results *results The set of results from the super-nodes.
-
-  Nodes *nodes The list of nodes.
+  Nodes *nodes The set of nodes to use.
 
   Segments *segments The set of segments to use.
 
-  Ways *ways The list of ways.
+  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 set of results for the start of the route.
+
+  Results *middle The set of results from the super-node route.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Results *CombineRoutes(Results *results,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile)
+Results *CombineRoutes(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,Profile *profile,Results *begin,Results *middle)
 {
- Result *result1,*result2,*result3,*result4;
+ Result *midres,*comres1;
  Results *combined;
 
- combined=NewResultsList(64);
+ combined=NewResultsList(256);
 
- combined->start=results->start;
- combined->finish=results->finish;
+ combined->start_node=begin->start_node;
+ combined->prev_segment=begin->prev_segment;
 
- /* Sort out the combined route */
+ /* Insert the start point */
+
+ midres=FindResult(middle,middle->start_node,middle->prev_segment);
+
+ comres1=InsertResult(combined,combined->start_node,combined->prev_segment);
+
+ /* Insert the start of the route */
+
+ if(begin->number>1 && midres->next)
+   {
+    Result *begres;
 
- result1=FindResult(results,results->start);
+    midres=FindResult(middle,midres->next->node,midres->next->segment);
 
- result3=InsertResult(combined,results->start);
+    begres=FindResult(begin,midres->node,midres->segment);
 
- ZeroResult(result3);
+    FixForwardRoute(begin,begres);
+
+    if(midres->next && midres->next->node==midres->node)
+       midres=midres->next;
+
+    begres=FindResult(begin,begin->start_node,begin->prev_segment);
+
+    begres=begres->next;
+
+    do
+      {
+       Result *comres2;
+
+       comres2=InsertResult(combined,begres->node,begres->segment);
+
+       comres2->score=begres->score+comres1->score;
+       comres2->prev=comres1;
+
+       begres=begres->next;
+
+       comres1=comres2;
+      }
+    while(begres);
+   }
+
+ /* Sort out the combined route */
 
  do
    {
-    if(result1->next!=NO_NODE)
+    Result *result;
+
+    if(midres->next)
       {
-       Results *results2=FindNormalRoute(nodes,segments,ways,result1->node,result1->next,profile);
+       Results *results=FindNormalRoute(nodes,segments,ways,relations,profile,comres1->node,comres1->segment,midres->next->node);
 
-       result2=FindResult(results2,result1->node);
+       if(!results)
+          return(NULL);
 
-       result3->next=result2->next;
+       result=FindResult(results,midres->node,comres1->segment);
 
-       result2=FindResult(results2,result2->next);
+       result=result->next;
+
+       /*
+        *      midres                          midres->next
+        *         =                                  =
+        *      ---*----------------------------------*  = middle
+        *
+        *      ---*----.----.----.----.----.----.----*  = results
+        *              =
+        *             result
+        *
+        *      ---*----.----.----.----.----.----.----*  = combined
+        *         =    =
+        *   comres1  comres2
+        */
 
        do
          {
-          result4=InsertResult(combined,result2->node);
+          Result *comres2;
 
-          *result4=*result2;
-          result4->score+=result3->score;
+          comres2=InsertResult(combined,result->node,result->segment);
 
-          if(result2->next!=NO_NODE)
-             result2=FindResult(results2,result2->next);
-          else
-             result2=NULL;
-         }
-       while(result2);
+          comres2->score=result->score+comres1->score;
+          comres2->prev=comres1;
 
-       FreeResultsList(results2);
+          result=result->next;
 
-       result1=FindResult(results,result1->next);
+          comres1=comres2;
+         }
+       while(result);
 
-       result3=result4;
+       FreeResultsList(results);
       }
-    else
-       result1=NULL;
+
+    midres=midres->next;
    }
- while(result1);
+ while(midres);
+
+ FixForwardRoute(combined,comres1);
 
  return(combined);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Fx the forward route (i.e. setup next nodes for forward path from prev nodes on reverse path).
+  Fix the forward route (i.e. setup next pointers for forward path from prev nodes on reverse path).
 
   Results *results The set of results to update.
 
-  index_t finish The finish point.
+  Result *finish_result The result for the finish point.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void FixForwardRoute(Results *results,index_t finish)
+void FixForwardRoute(Results *results,Result *finish_result)
 {
- Result *result2=FindResult(results,finish);
- Result *result1;
+ Result *result2=finish_result;
+
+ /* Erase the old route if there is one */
+
+ if(results->finish_node!=NO_NODE)
+   {
+    Result *result1=FirstResult(results);
+
+    while(result1)
+      {
+       result1->next=NULL;
+
+       result1=NextResult(results,result1);
+      }
+   }
 
  /* Create the forward links for the optimum path */
 
  do
    {
-    if(result2->prev!=NO_NODE)
+    Result *result1;
+
+    if(result2->prev)
       {
-       index_t node1=result2->prev;
+       index_t node1=result2->prev->node;
+       index_t seg1=result2->prev->segment;
+
+       result1=FindResult(results,node1,seg1);
 
-       result1=FindResult(results,node1);
+       assert(!result1->next);   /* Bugs elsewhere can lead to infinite loop here. */
 
-       result1->next=result2->node;
+       result1->next=result2;
 
        result2=result1;
       }
@@ -962,5 +1321,6 @@ void FixForwardRoute(Results *results,index_t finish)
    }
  while(result2);
 
- results->finish=finish;
+ results->finish_node=finish_result->node;
+ results->last_segment=finish_result->segment;
 }
diff --git a/src/osmparser.c b/src/osmparser.c
index 03e17b7..e1fb0c4 100644
--- a/src/osmparser.c
+++ b/src/osmparser.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/osmparser.c,v 1.73 2010/11/13 14:22:28 amb Exp $
-
  OSM XML file parser (either JOSM or planet)
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,6 +20,7 @@
  ***************************************/
 
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -43,12 +42,19 @@
 
 /* Macros */
 
-#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 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 long nnodes=0,nways=0,nrelations=0;
+static index_t nnodes=0;
+static index_t nways=0;
+static index_t nrelations=0;
+
 static TagList *current_tags=NULL;
 
 static node_t *way_nodes=NULL;
@@ -60,6 +66,9 @@ static way_t      *relation_ways=NULL;
 static int         relation_nways=0;
 static relation_t *relation_relations=NULL;
 static int         relation_nrelations=0;
+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;
@@ -73,6 +82,10 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
 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 */
 
@@ -251,23 +264,28 @@ static int nodeType_function(const char *_tag_,int _type_,const char *id,const c
 
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
+
     nnodes++;
 
-    if(!(nnodes%1000))
-       printf_middle("Reading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+    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_STRING(_tag_,id); node_id=atoll(id); /* need long long conversion */
-    XMLPARSE_ASSERT_FLOATING(_tag_,lat,latitude);
-    XMLPARSE_ASSERT_FLOATING(_tag_,lon,longitude);
+    XMLPARSE_ASSERT_INTEGER(_tag_,id);   llid=atoll(id); /* need long long conversion */
+    node_id=(node_t)llid;
+    assert((long long)node_id==llid);      /* check node id can be stored in node_t data type. */
+
+    XMLPARSE_ASSERT_FLOATING(_tag_,lat); latitude =atof(lat);
+    XMLPARSE_ASSERT_FLOATING(_tag_,lon); longitude=atof(lon);
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&NodeRules,current_tags);
+    TagList *result=ApplyTaggingRules(&NodeRules,current_tags,node_id);
 
     process_node_tags(result,node_id,latitude,longitude);
 
@@ -295,9 +313,12 @@ static int ndType_function(const char *_tag_,int _type_,const char *ref)
 {
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
     node_t node_id;
 
-    XMLPARSE_ASSERT_STRING(_tag_,ref); node_id=atoll(ref); /* need long long conversion */
+    XMLPARSE_ASSERT_INTEGER(_tag_,ref); llid=atoll(ref); /* need long long conversion */
+    node_id=(node_t)llid;
+    assert((long long)node_id==llid);      /* check node id can be stored in node_t data type. */
 
     if(way_nnodes && (way_nnodes%256)==0)
        way_nodes=(node_t*)realloc((void*)way_nodes,(way_nnodes+256)*sizeof(node_t));
@@ -329,30 +350,55 @@ static int memberType_function(const char *_tag_,int _type_,const char *type,con
 {
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
+
     XMLPARSE_ASSERT_STRING(_tag_,type);
-    XMLPARSE_ASSERT_STRING(_tag_,ref);
+    XMLPARSE_ASSERT_INTEGER(_tag_,ref); llid=atoll(ref); /* need long long conversion */
 
     if(!strcmp(type,"node"))
       {
-       node_t node_id=atoll(ref); /* need long long conversion */
+       node_t node_id;
+
+       node_id=(node_t)llid;
+       assert((long long)node_id==llid);   /* 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=atoll(ref); /* need long long conversion */
+       way_t way_id;
+
+       way_id=(way_t)llid;
+       assert((long long)way_id==llid);   /* 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++]=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=atoll(ref); /* need long long conversion */
+       relation_t relation_id;
+
+       relation_id=(relation_t)llid;
+       assert((long long)relation_id==llid);   /* 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));
@@ -383,10 +429,12 @@ static int wayType_function(const char *_tag_,int _type_,const char *id)
 
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
+
     nways++;
 
     if(!(nways%1000))
-       printf_middle("Reading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+       printf_middle("Reading: Lines=%llu Nodes=%"Pindex_t" Ways=%"Pindex_t" Relations=%"Pindex_t,ParseXML_LineNumber(),nnodes,nways,nrelations);
 
     current_tags=NewTagList();
 
@@ -394,12 +442,15 @@ static int wayType_function(const char *_tag_,int _type_,const char *id)
 
     /* Handle the way information */
 
-    XMLPARSE_ASSERT_STRING(_tag_,id); way_id=atoll(id); /* need long long conversion */
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need long long conversion */
+
+    way_id=(way_t)llid;
+    assert((long long)way_id==llid);   /* check way id can be stored in way_t data type. */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&WayRules,current_tags);
+    TagList *result=ApplyTaggingRules(&WayRules,current_tags,way_id);
 
     process_way_tags(result,way_id);
 
@@ -429,23 +480,32 @@ static int relationType_function(const char *_tag_,int _type_,const char *id)
 
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
+
     nrelations++;
 
     if(!(nrelations%1000))
-       printf_middle("Reading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+       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_from=NO_WAY_ID;
+    relation_to=NO_WAY_ID;
+    relation_via=NO_NODE_ID;
+
     /* Handle the relation information */
 
-    XMLPARSE_ASSERT_STRING(_tag_,id); relation_id=atoll(id); /* need long long conversion */
+    XMLPARSE_ASSERT_INTEGER(_tag_,id); llid=atoll(id); /* need long long conversion */
+
+    relation_id=(relation_t)llid;
+    assert((long long)relation_id==llid);   /* check relation id can be stored in relation_t data type. */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&RelationRules,current_tags);
+    TagList *result=ApplyTaggingRules(&RelationRules,current_tags,relation_id);
 
     process_relation_tags(result,relation_id);
 
@@ -534,7 +594,13 @@ int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,R
 
  retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
 
- printf_last("Read: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+ 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);
 
  return(retval);
 }
@@ -554,8 +620,8 @@ int ParseOSM(FILE *file,NodesX *OSMNodes,SegmentsX *OSMSegments,WaysX *OSMWays,R
 
 static void process_node_tags(TagList *tags,node_t id,double latitude,double longitude)
 {
- allow_t allow=Allow_ALL;
-
+ transports_t allow=Transports_ALL;
+ uint16_t flags=0;
  int i;
 
  /* Parse the tags */
@@ -569,73 +635,117 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
       {
       case 'b':
        if(!strcmp(k,"bicycle"))
-          if(!ISTRUE(v))
-             allow&=~Allow_Bicycle;
+         {
+          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);
+         }
 
        break;
 
       case 'f':
        if(!strcmp(k,"foot"))
-          if(!ISTRUE(v))
-             allow&=~Allow_Foot;
+         {
+          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);
+         }
 
        break;
 
       case 'g':
        if(!strcmp(k,"goods"))
-          if(!ISTRUE(v))
-             allow&=~Allow_Goods;
+         {
+          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);
+         }
 
        break;
 
       case 'h':
+       if(!strcmp(k,"highway"))
+          if(!strcmp(v,"mini_roundabout"))
+             flags|=NODE_MINIRNDBT;
+
        if(!strcmp(k,"horse"))
-          if(!ISTRUE(v))
-             allow&=~Allow_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);
+         }
 
        if(!strcmp(k,"hgv"))
-          if(!ISTRUE(v))
-             allow&=~Allow_HGV;
+         {
+          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);
+         }
 
        break;
 
       case 'm':
        if(!strcmp(k,"moped"))
-          if(!ISTRUE(v))
-             allow&=~Allow_Moped;
+         {
+          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);
+         }
 
        if(!strcmp(k,"motorbike"))
-          if(!ISTRUE(v))
-             allow&=~Allow_Motorbike;
+         {
+          if(ISFALSE(v))
+             allow&=~Transports_Motorbike;
+          else if(!ISTRUE(v))
+             logerror("Node %"Pnode_t" has an unrecognised tag value 'motorbike' = '%s' (after tagging rules); using 'yes'.\n",id,v);
+         }
 
        if(!strcmp(k,"motorcar"))
-          if(!ISTRUE(v))
-             allow&=~Allow_Motorcar;
+         {
+          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);
+         }
 
        break;
 
       case 'p':
        if(!strcmp(k,"psv"))
-          if(!ISTRUE(v))
-             allow&=~Allow_PSV;
+         {
+          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);
+         }
 
        break;
 
       case 'w':
        if(!strcmp(k,"wheelchair"))
-          if(!ISTRUE(v))
-             allow&=~Allow_Wheelchair;
+         {
+          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);
+         }
 
        break;
 
       default:
-       ;
+       logerror("Node %"Pnode_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
       }
    }
 
  /* Create the node */
 
- AppendNode(nodes,id,degrees_to_radians(latitude),degrees_to_radians(longitude),allow);
+ AppendNode(nodes,id,degrees_to_radians(latitude),degrees_to_radians(longitude),allow,flags);
 }
 
 
@@ -650,12 +760,32 @@ static void process_node_tags(TagList *tags,node_t id,double latitude,double lon
 static void process_way_tags(TagList *tags,way_t id)
 {
  Way   way={0};
- int   oneway=0,roundabout=0;
- char *name=NULL,*ref=NULL;
-
+ int   oneway=0;
+ char *name=NULL,*ref=NULL,*refname=NULL;
  int i;
 
- /* Parse the tags */
+ /* Parse the tags - just look for highway */
+
+ for(i=0;i<tags->ntags;i++)
+   {
+    char *k=tags->k[i];
+    char *v=tags->v[i];
+
+    if(!strcmp(k,"highway"))
+      {
+       way.type=HighwayType(v);
+
+       if(way.type==Way_Count)
+          logerror("Way %"Pway_t" has an unrecognised highway type '%s' (after tagging rules); ignoring it.\n",id,v);
+      }
+   }
+
+ /* Don't continue if this is not a highway (bypass error logging) */
+
+ if(way.type==0 || way.type==Way_Count)
+    return;
+
+ /* Parse the tags - look for the others */
 
  for(i=0;i<tags->ntags;i++)
    {
@@ -666,140 +796,130 @@ static void process_way_tags(TagList *tags,way_t id)
       {
       case 'b':
        if(!strcmp(k,"bicycle"))
+         {
           if(ISTRUE(v))
-             way.allow|= Allow_Bicycle;
+             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);
+         }
 
        if(!strcmp(k,"bicycleroute"))
+         {
           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);
+         }
 
        if(!strcmp(k,"bridge"))
+         {
           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);
+         }
 
        break;
 
       case 'f':
        if(!strcmp(k,"foot"))
+         {
           if(ISTRUE(v))
-             way.allow|= Allow_Foot;
+             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);
+         }
 
        if(!strcmp(k,"footroute"))
+         {
           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);
+         }
 
        break;
 
       case 'g':
        if(!strcmp(k,"goods"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_Goods;
+             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);
+         }
 
        break;
 
       case 'h':
        if(!strcmp(k,"highway"))
-          way.type=HighwayType(v);
+          ;
 
        if(!strcmp(k,"horse"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_Horse;
+             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);
+         }
 
        if(!strcmp(k,"hgv"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_HGV;
-
-       break;
-
-      case 'j':
-       if(!strcmp(k,"junction") && !strcmp(v,"roundabout"))
-          roundabout=1;
+             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);
+         }
 
        break;
 
       case 'm':
        if(!strcmp(k,"maxspeed"))
-         {
-          if(strstr(v,"mph"))
-             way.speed=kph_to_speed(1.609*atof(v));
-          else
-             way.speed=kph_to_speed(atof(v));
-         }
+          way.speed=kph_to_speed(parse_speed(id,k,v));
 
        if(!strcmp(k,"maxweight"))
-         {
-          if(strstr(v,"kg"))
-             way.weight=tonnes_to_weight(atof(v)/1000);
-          else
-             way.weight=tonnes_to_weight(atof(v));
-         }
+          way.weight=tonnes_to_weight(parse_weight(id,k,v));
 
        if(!strcmp(k,"maxheight"))
-         {
-          if(strchr(v,'\''))
-            {
-             int feet,inches;
-
-             if(sscanf(v,"%d'%d\"",&feet,&inches)==2)
-                way.height=metres_to_height((feet+(double)inches/12.0)*0.254);
-             else if(sscanf(v,"%d'",&feet)==1)
-                way.height=metres_to_height((feet+(double)inches/12.0)*0.254);
-            }
-          else if(strstr(v,"ft") || strstr(v,"feet"))
-             way.height=metres_to_height(atof(v)*0.254);
-          else
-             way.height=metres_to_height(atof(v));
-         }
+          way.height=metres_to_height(parse_length(id,k,v));
 
        if(!strcmp(k,"maxwidth"))
-         {
-          if(strchr(v,'\''))
-            {
-             int feet,inches;
-
-             if(sscanf(v,"%d'%d\"",&feet,&inches)==2)
-                way.width=metres_to_height((feet+(double)inches/12.0)*0.254);
-             else if(sscanf(v,"%d'",&feet)==1)
-                way.width=metres_to_height((feet+(double)inches/12.0)*0.254);
-            }
-          else if(strstr(v,"ft") || strstr(v,"feet"))
-             way.width=metres_to_width(atof(v)*0.254);
-          else
-             way.width=metres_to_width(atof(v));
-         }
+          way.width=metres_to_height(parse_length(id,k,v));
 
        if(!strcmp(k,"maxlength"))
-         {
-          if(strchr(v,'\''))
-            {
-             int feet,inches;
-
-             if(sscanf(v,"%d'%d\"",&feet,&inches)==2)
-                way.length=metres_to_height((feet+(double)inches/12.0)*0.254);
-             else if(sscanf(v,"%d'",&feet)==1)
-                way.length=metres_to_height((feet+(double)inches/12.0)*0.254);
-            }
-          else if(strstr(v,"ft") || strstr(v,"feet"))
-             way.length=metres_to_length(atof(v)*0.254);
-          else
-             way.length=metres_to_length(atof(v));
-         }
+          way.length=metres_to_height(parse_length(id,k,v));
 
        if(!strcmp(k,"moped"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_Moped;
+             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);
+         }
 
        if(!strcmp(k,"motorbike"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_Motorbike;
+             way.allow|=Transports_Motorbike;
+          else if(!ISFALSE(v))
+             logerror("Way %"Pway_t" has an unrecognised tag value 'motorbike' = '%s' (after tagging rules); using 'no'.\n",id,v);
+         }
 
        if(!strcmp(k,"motorcar"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_Motorcar;
+             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);
+         }
 
        if(!strcmp(k,"multilane"))
+         {
           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);
+         }
 
        break;
 
@@ -816,18 +936,28 @@ static void process_way_tags(TagList *tags,way_t id)
              oneway=1;
           else if(!strcmp(v,"-1"))
              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);
          }
 
        break;
 
       case 'p':
        if(!strcmp(k,"paved"))
+         {
           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);
+         }
 
        if(!strcmp(k,"psv"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_PSV;
+             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);
+         }
 
        break;
 
@@ -839,76 +969,74 @@ static void process_way_tags(TagList *tags,way_t id)
 
       case 't':
        if(!strcmp(k,"tunnel"))
+         {
           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);
+         }
 
        break;
 
       case 'w':
        if(!strcmp(k,"wheelchair"))
+         {
           if(ISTRUE(v))
-             way.allow|=Allow_Wheelchair;
+             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);
+         }
 
        break;
 
       default:
-       ;
+       logerror("Way %"Pway_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
       }
    }
 
  /* Create the way */
 
- if(way.type>0 && way.type<Way_Count)
-   {
-    if(way.allow)
-      {
-       char *refname;
+ if(!way.allow)
+    return;
 
-       if(oneway)
-          way.type|=Way_OneWay;
+ if(way_nnodes==0)
+   {
+    logerror("Way %"Pway_t" has no nodes.\n",id);
 
-       if(roundabout)
-          way.type|=Way_Roundabout;
+    return;
+   }
 
-       if(ref && name)
-         {
-          refname=(char*)malloc(strlen(ref)+strlen(name)+4);
-          sprintf(refname,"%s (%s)",name,ref);
-         }
-       else if(ref && !name)
-          refname=ref;
-       else if(!ref && name)
-          refname=name;
-       else /* if(!ref && !name) */
-          refname="";
+ if(oneway)
+    way.type|=Way_OneWay;
 
-       AppendWay(ways,id,&way,refname);
+ if(ref && name)
+   {
+    refname=(char*)malloc(strlen(ref)+strlen(name)+4);
+    sprintf(refname,"%s (%s)",name,ref);
+   }
+ else if(ref && !name)
+    refname=ref;
+ else if(!ref && name)
+    refname=name;
+ else /* if(!ref && !name) */
+    refname="";
 
-       if(ref && name)
-          free(refname);
+ AppendWay(ways,id,&way,refname);
 
-       for(i=1;i<way_nnodes;i++)
-         {
-          node_t from=way_nodes[i-1];
-          node_t to  =way_nodes[i];
+ if(ref && name)
+    free(refname);
 
-          if(oneway>0)
-            {
-             AppendSegment(segments,id,from,to,ONEWAY_1TO2);
-             AppendSegment(segments,id,to,from,ONEWAY_2TO1);
-            }
-          else if(oneway<0)
-            {
-             AppendSegment(segments,id,from,to,ONEWAY_2TO1);
-             AppendSegment(segments,id,to,from,ONEWAY_1TO2);
-            }
-          else
-            {
-             AppendSegment(segments,id,from,to,0);
-             AppendSegment(segments,id,to,from,0);
-            }
-         }
-      }
+ for(i=1;i<way_nnodes;i++)
+   {
+    node_t from=way_nodes[i-1];
+    node_t to  =way_nodes[i];
+
+    if(oneway>0)
+       AppendSegment(segments,id,from,to,ONEWAY_1TO2);
+    else if(oneway<0)
+       AppendSegment(segments,id,from,to,ONEWAY_2TO1);
+    else
+       AppendSegment(segments,id,from,to,0);
    }
 }
 
@@ -923,7 +1051,10 @@ static void process_way_tags(TagList *tags,way_t id)
 
 static void process_relation_tags(TagList *tags,relation_t id)
 {
- allow_t routes=Allow_None;
+ transports_t routes=Transports_None;
+ transports_t except=Transports_None;
+ int relation_turn_restriction=0;
+ TurnRestriction restriction=TurnRestrict_None;
  int i;
 
  /* Parse the tags */
@@ -937,20 +1068,64 @@ static void process_relation_tags(TagList *tags,relation_t id)
       {
       case 'b':
        if(!strcmp(k,"bicycleroute"))
+         {
           if(ISTRUE(v))
-             routes|=Allow_Bicycle;
+             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);
+         }
+
+       break;
+
+      case 'e':
+       if(!strcmp(k,"except"))
+         {
+          for(i=1;i<Transport_Count;i++)
+             if(strstr(v,TransportName(i)))
+                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);
+         }
 
        break;
 
       case 'f':
        if(!strcmp(k,"footroute"))
+         {
           if(ISTRUE(v))
-             routes|=Allow_Foot;
+             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);
+         }
+
+       break;
+
+      case 'r':
+       if(!strcmp(k,"restriction"))
+         {
+          if(!strcmp(v,"no_right_turn"   )) restriction=TurnRestrict_no_right_turn;
+          if(!strcmp(v,"no_left_turn"    )) restriction=TurnRestrict_no_left_turn;
+          if(!strcmp(v,"no_u_turn"       )) restriction=TurnRestrict_no_u_turn;
+          if(!strcmp(v,"no_straight_on"  )) restriction=TurnRestrict_no_straight_on;
+          if(!strcmp(v,"only_right_turn" )) restriction=TurnRestrict_only_right_turn;
+          if(!strcmp(v,"only_left_turn"  )) restriction=TurnRestrict_only_left_turn;
+          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);
+         }
 
        break;
 
+      case 't':
+       if(!strcmp(k,"type"))
+          if(!strcmp(v,"restriction"))
+             relation_turn_restriction=1;
+       break;
+
       default:
-       ;
+       logerror("Relation %"Prelation_t" has an unrecognised tag '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
       }
    }
 
@@ -958,8 +1133,152 @@ static void process_relation_tags(TagList *tags,relation_t id)
     relations even if they are not routes because they might be referenced by
     other relations that are routes) */
 
- if(relation_nways || relation_nrelations)
+ if((relation_nways || relation_nrelations) && !relation_turn_restriction)
     AppendRouteRelation(relations,id,routes,
                         relation_ways,relation_nways,
                         relation_relations,relation_nrelations);
+
+ /* Create the turn restriction relation. */
+
+ 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
+       AppendTurnRestrictRelation(relations,id,
+                                  relation_from,relation_to,relation_via,
+                                  restriction,except);
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Convert a string containing a speed into a double precision.
+
+  double parse_speed Returns the speed in km/h if it can be parsed.
+
+  way_t id The way being processed.
+
+  const char *k The tag key.
+
+  const char *v The tag value.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static double parse_speed(way_t id,const char *k,const char *v)
+{
+ char *ev;
+ 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);
+ else
+   {
+    while(isspace(*ev)) ev++;
+
+    if(!strcmp(ev,"mph"))
+       return(1.609*value);
+
+    if(*ev==0 || !strcmp(ev,"kph"))
+       return(value);
+
+    logerror("Way %"Pway_t" has an un-parseable tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Convert a string containing a weight into a double precision.
+
+  double parse_weight Returns the weight in tonnes if it can be parsed.
+
+  way_t id The way being processed.
+
+  const char *k The tag key.
+
+  const char *v The tag value.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static double parse_weight(way_t id,const char *k,const char *v)
+{
+ char *ev;
+ 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);
+ else
+   {
+    while(isspace(*ev)) ev++;
+
+    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"))
+       return(value);
+
+    logerror("Way %"Pway_t" has an un-parseable tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Convert a string containing a length into a double precision.
+
+  double parse_length Returns the length in metres if it can be parsed.
+
+  way_t id The way being processed.
+
+  const char *k The tag key.
+
+  const char *v The tag value.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static double parse_length(way_t id,const char *k,const char *v)
+{
+ char *ev;
+ 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);
+ else
+   {
+    int en=0;
+    int feet=0,inches=0;
+
+    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])
+       return((feet+(double)inches/12.0)*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(sscanf(v,"%d feet %d inches%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(!strcmp(ev,"ft") || !strcmp(ev,"feet"))
+       return(value*0.254);
+
+    if(*ev==0 || !strcmp(ev,"m") || !strcmp(ev,"metre") || !strcmp(ev,"metres"))
+       return(value);
+
+    logerror("Way %"Pway_t" has an un-parseable tag value '%s' = '%s' (after tagging rules); ignoring it.\n",id,k,v);
+   }
+
+ return(0);
 }
diff --git a/src/output.c b/src/output.c
index cbada4f..802066e 100644
--- a/src/output.c
+++ b/src/output.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/output.c,v 1.40 2010/09/15 18:30:08 amb Exp $
-
  Routing output generator.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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 +37,7 @@
 
 #include "files.h"
 #include "functions.h"
+#include "fakes.h"
 #include "translations.h"
 #include "results.h"
 #include "xmlparse.h"
@@ -73,12 +72,6 @@ static char junction_other_way[Way_Count][Way_Count]=
  };
 
 
-/* Local functions */
-
-static int turn_angle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node);
-static int bearing_angle(Nodes *nodes,Segment *segment,index_t node);
-
-
 /*++++++++++++++++++++++++++++++++++++++
   Print the optimum route between two nodes.
 
@@ -86,11 +79,11 @@ static int bearing_angle(Nodes *nodes,Segment *segment,index_t node);
 
   int nresults The number of results in the list.
 
-  Nodes *nodes The list of nodes.
+  Nodes *nodes The set of nodes to use.
 
   Segments *segments The set of segments to use.
 
-  Ways *ways The list of ways.
+  Ways *ways The set of ways to use.
 
   Profile *profile The profile containing the transport type, speeds and allowed highways.
   ++++++++++++++++++++++++++++++++++++++*/
@@ -103,8 +96,8 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
  distance_t cum_distance=0;
  duration_t cum_duration=0;
  double finish_lat,finish_lon;
- int segment_count=0;
- int route_count=0;
+ int segment_count=0,route_count=0;
+ int point_count=0;
 
  /* Open the files */
 
@@ -311,27 +304,28 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
     if(gpxtrackfile)
        fprintf(gpxtrackfile,"<trkseg>\n");
 
-    if(IsFakeNode(results[point]->start))
-       GetFakeLatLong(results[point]->start,&start_lat,&start_lon);
+    if(IsFakeNode(results[point]->start_node))
+       GetFakeLatLong(results[point]->start_node,&start_lat,&start_lon);
     else
-       GetLatLong(nodes,results[point]->start,&start_lat,&start_lon);
+       GetLatLong(nodes,results[point]->start_node,&start_lat,&start_lon);
 
-    if(IsFakeNode(results[point]->finish))
-       GetFakeLatLong(results[point]->finish,&finish_lat,&finish_lon);
+    if(IsFakeNode(results[point]->finish_node))
+       GetFakeLatLong(results[point]->finish_node,&finish_lat,&finish_lon);
     else
-       GetLatLong(nodes,results[point]->finish,&finish_lat,&finish_lon);
+       GetLatLong(nodes,results[point]->finish_node,&finish_lat,&finish_lon);
 
-    result=FindResult(results[point],results[point]->start);
+    result=FindResult(results[point],results[point]->start_node,results[point]->prev_segment);
 
     do
       {
        double latitude,longitude;
        Result *nextresult;
+       index_t nextrealsegment;
        Segment *nextresultsegment;
 
-       if(result->node==results[point]->start)
+       if(result->node==results[point]->start_node)
          {latitude=start_lat; longitude=start_lon;}
-       else if(result->node==results[point]->finish)
+       else if(result->node==results[point]->finish_node)
          {latitude=finish_lat; longitude=finish_lon;}
        else
           GetLatLong(nodes,result->node,&latitude,&longitude);
@@ -340,31 +334,41 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
           fprintf(gpxtrackfile,"<trkpt lat=\"%.6f\" lon=\"%.6f\"/>\n",
                   radians_to_degrees(latitude),radians_to_degrees(longitude));
 
-       nextresult=FindResult(results[point],result->next);
+       nextresult=result->next;
 
        if(!nextresult)
           for(nextpoint=point+1;nextpoint<=nresults;nextpoint++)
              if(results[nextpoint])
                {
-                nextresult=FindResult(results[nextpoint],results[nextpoint]->start);
-                nextresult=FindResult(results[nextpoint],nextresult->next);
+                nextresult=FindResult(results[nextpoint],results[nextpoint]->start_node,results[nextpoint]->prev_segment);
+                nextresult=nextresult->next;
                 break;
                }
 
        if(nextresult)
          {
           if(IsFakeSegment(nextresult->segment))
+            {
              nextresultsegment=LookupFakeSegment(nextresult->segment);
+             nextrealsegment=IndexRealSegment(nextresult->segment);
+            }
           else
-             nextresultsegment=LookupSegment(segments,nextresult->segment,2);
+            {
+             nextresultsegment=LookupSegment(segments,nextresult->segment,1);
+             nextrealsegment=nextresult->segment;
+            }
          }
        else
+         {
           nextresultsegment=NULL;
+          nextrealsegment=NO_SEGMENT;
+         }
 
-       if(result->node!=results[point]->start)
+       if(result->node!=results[point]->start_node)
          {
           distance_t seg_distance=0;
           duration_t seg_duration=0;
+          index_t realsegment;
           Segment *resultsegment;
           Way *resultway;
           int important=0;
@@ -379,9 +383,15 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
           /* Get the properties of this segment */
 
           if(IsFakeSegment(result->segment))
+            {
              resultsegment=LookupFakeSegment(result->segment);
+             realsegment=IndexRealSegment(result->segment);
+            }
           else
-             resultsegment=LookupSegment(segments,result->segment,3);
+            {
+             resultsegment=LookupSegment(segments,result->segment,2);
+             realsegment=result->segment;
+            }
           resultway=LookupWay(ways,resultsegment->way,1);
 
           seg_distance+=DISTANCE(resultsegment->distance);
@@ -393,27 +403,31 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
 
           /* Decide if this is an important junction */
 
-          if(result->node==results[point]->finish)
+          if(result->node==results[point]->finish_node) /* Waypoint */
              important=10;
+          else if(realsegment==nextrealsegment) /* U-turn */
+             important=5;
           else
             {
-             Segment *segment=FirstSegment(segments,nodes,result->node);
+             Segment *segment=FirstSegment(segments,nodes,result->node,3);
 
              do
                {
                 index_t othernode=OtherNode(segment,result->node);
 
-                if(othernode!=result->prev && segment!=resultsegment)
+                if(othernode!=result->prev->node && IndexSegment(segments,segment)!=realsegment)
                    if(IsNormalSegment(segment) && (!profile->oneway || !IsOnewayTo(segment,result->node)))
                      {
                       Way *way=LookupWay(ways,segment->way,2);
 
-                      if(othernode==result->next) /* the next segment that we follow */
+                      if(othernode==nextresult->node) /* the next segment that we follow */
                         {
                          if(HIGHWAY(way->type)!=HIGHWAY(resultway->type))
                             if(important<2)
                                important=2;
                         }
+                      else if(IsFakeNode(nextresult->node))
+                         ;
                       else /* a segment that we don't follow */
                         {
                          if(junction_other_way[HIGHWAY(resultway->type)-1][HIGHWAY(way->type)-1])
@@ -464,20 +478,21 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
                                   distance_to_km(cum_distance),duration_to_minutes(cum_duration));
                 fprintf(htmlfile,"</span>]\n");
 
-                fprintf(htmlfile,"<tr class='c'><td class='l'><td class='r'>%.6f %.6f\n",
+                fprintf(htmlfile,"<tr class='c'><td class='l'>%d:<td class='r'>%.6f %.6f\n",
+                                 ++point_count,
                                  radians_to_degrees(latitude),radians_to_degrees(longitude));
 
                 if(nextresult)
                   {
                    if(!turn_str)
                      {
-                      turn_int=turn_angle(nodes,resultsegment,nextresultsegment,result->node);
-                      turn_str=translate_turn[(4+(22+turn_int)/45)%8];
+                      turn_int=(int)TurnAngle(nodes,resultsegment,nextresultsegment,result->node);
+                      turn_str=translate_turn[((202+turn_int)/45)%8];
                      }
 
                    if(!bearing_next_str)
                      {
-                      bearing_next_int=bearing_angle(nodes,nextresultsegment,nextresult->node);
+                      bearing_next_int=(int)BearingAngle(nodes,nextresultsegment,nextresult->node);
                       bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
                      }
 
@@ -515,7 +530,7 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
 
                 if(!bearing_str)
                   {
-                   bearing_int=bearing_angle(nodes,resultsegment,result->node);
+                   bearing_int=(int)BearingAngle(nodes,resultsegment,result->node);
                    bearing_str=translate_heading[(4+(22+bearing_int)/45)%8];
                   }
 
@@ -566,13 +581,13 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
                   {
                    if(!turn_str)
                      {
-                      turn_int=turn_angle(nodes,resultsegment,nextresultsegment,result->node);
-                      turn_str=translate_turn[(4+(22+turn_int)/45)%8];
+                      turn_int=(int)TurnAngle(nodes,resultsegment,nextresultsegment,result->node);
+                      turn_str=translate_turn[((202+turn_int)/45)%8];
                      }
 
                    if(!bearing_next_str)
                      {
-                      bearing_next_int=bearing_angle(nodes,nextresultsegment,nextresult->node);
+                      bearing_next_int=(int)BearingAngle(nodes,nextresultsegment,nextresult->node);
                       bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
                      }
 
@@ -622,14 +637,14 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
 
              if(!bearing_str)
                {
-                bearing_int=bearing_angle(nodes,resultsegment,result->node);
+                bearing_int=(int)BearingAngle(nodes,resultsegment,result->node);
                 bearing_str=translate_heading[(4+(22+bearing_int)/45)%8];
                }
 
              fprintf(textallfile,"%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t%3d\t%4d\t%s\n",
                                  radians_to_degrees(latitude),radians_to_degrees(longitude),
                                  IsFakeNode(result->node)?(NODE_FAKE-result->node):result->node,
-                                 (!IsFakeNode(result->node) && IsSuperNode(nodes,result->node))?'*':' ',type,
+                                 (!IsFakeNode(result->node) && IsSuperNode(LookupNode(nodes,result->node,1)))?'*':' ',type,
                                  distance_to_km(seg_distance),duration_to_minutes(seg_duration),
                                  distance_to_km(cum_distance),duration_to_minutes(cum_duration),
                                  profile->speed[HIGHWAY(resultway->type)],
@@ -642,14 +657,15 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
          }
        else if(!cum_distance)
          {
-          int   bearing_next_int=bearing_angle(nodes,nextresultsegment,nextresult->node);
+          int   bearing_next_int=(int)BearingAngle(nodes,nextresultsegment,nextresult->node);
           char *bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
 
           /* Print out the very first start point */
 
           if(htmlfile)
             {
-             fprintf(htmlfile,"<tr class='c'><td class='l'><td class='r'>%.6f %.6f\n",
+             fprintf(htmlfile,"<tr class='c'><td class='l'>%d:<td class='r'>%.6f %.6f\n",
+                              ++point_count,
                               radians_to_degrees(latitude),radians_to_degrees(longitude));
              fprintf(htmlfile,"<tr class='n'><td class='l'>%s:<td class='r'>",translate_html_start[0]);
              fprintf(htmlfile,translate_html_start[1],
@@ -674,7 +690,7 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
              fprintf(textallfile,"%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t\t\t\n",
                                  radians_to_degrees(latitude),radians_to_degrees(longitude),
                                  IsFakeNode(result->node)?(NODE_FAKE-result->node):result->node,
-                                 (!IsFakeNode(result->node) && IsSuperNode(nodes,result->node))?'*':' ',"Waypt",
+                                 (!IsFakeNode(result->node) && IsSuperNode(LookupNode(nodes,result->node,1)))?'*':' ',"Waypt",
                                  0.0,0.0,0.0,0.0);
          }
 
@@ -738,108 +754,3 @@ void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,W
  if(textallfile)
     fclose(textallfile);
 }
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Calculate the angle to turn at a junction from segment1 to segment2 at node.
-
-  int turn_angle Returns a value in the range -4 to +4 indicating the angle to turn.
-
-  Nodes *nodes The set of nodes.
-
-  Segment *segment1 The current segment.
-
-  Segment *segment2 The next segment.
-
-  index_t node The node at which they join.
-
-  Straight ahead is zero, turning to the right is positive (90 degrees) and turning to the left is negative.
-  Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int turn_angle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node)
-{
- double lat1,latm,lat2;
- double lon1,lonm,lon2;
- double angle1,angle2,angle;
- index_t node1,node2;
-
- node1=OtherNode(segment1,node);
- node2=OtherNode(segment2,node);
-
- if(IsFakeNode(node1))
-    GetFakeLatLong(node1,&lat1,&lon1);
- else
-    GetLatLong(nodes,node1,&lat1,&lon1);
-
- if(IsFakeNode(node))
-    GetFakeLatLong(node,&latm,&lonm);
- else
-    GetLatLong(nodes,node,&latm,&lonm);
-
- if(IsFakeNode(node2))
-    GetFakeLatLong(node2,&lat2,&lon2);
- else
-    GetLatLong(nodes,node2,&lat2,&lon2);
-
- angle1=atan2((lonm-lon1)*cos(latm),(latm-lat1));
- angle2=atan2((lon2-lonm)*cos(latm),(lat2-latm));
-
- angle=angle2-angle1;
-
- angle=radians_to_degrees(angle);
-
- angle=round(angle);
-
- if(angle<-180) angle+=360;
- if(angle> 180) angle-=360;
-
- return((int)angle);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Calculate the bearing of a segment from the given node.
-
-  int bearing_angle Returns a value in the range 0 to 359 indicating the bearing.
-
-  Nodes *nodes The set of nodes.
-
-  Segment *segment The segment.
-
-  index_t node The node to start.
-
-  Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude).
-  ++++++++++++++++++++++++++++++++++++++*/
-
-static int bearing_angle(Nodes *nodes,Segment *segment,index_t node)
-{
- double lat1,lat2;
- double lon1,lon2;
- double angle;
- index_t node1,node2;
-
- node1=node;
- node2=OtherNode(segment,node);
-
- if(IsFakeNode(node1))
-    GetFakeLatLong(node1,&lat1,&lon1);
- else
-    GetLatLong(nodes,node1,&lat1,&lon1);
-
- if(IsFakeNode(node2))
-    GetFakeLatLong(node2,&lat2,&lon2);
- else
-    GetLatLong(nodes,node2,&lat2,&lon2);
-
- angle=atan2((lat2-lat1),(lon2-lon1)*cos(lat1));
-
- angle=radians_to_degrees(angle);
-
- angle=round(270-angle);
-
- if(angle<  0) angle+=360;
- if(angle>360) angle-=360;
-
- return((int)angle);
-}
diff --git a/src/planetsplitter.c b/src/planetsplitter.c
index cba9d4d..c9175b8 100644
--- a/src/planetsplitter.c
+++ b/src/planetsplitter.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/planetsplitter.c,v 1.82 2010/11/13 14:22:28 amb Exp $
-
  OSM planet file splitter.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -69,8 +67,8 @@ int main(int argc,char** argv)
  WaysX      *Ways;
  RelationsX *Relations;
  int         iteration=0,quit=0;
- int         max_iterations=10;
- char       *dirname=NULL,*prefix=NULL,*tagging=NULL;
+ int         max_iterations=5;
+ char       *dirname=NULL,*prefix=NULL,*tagging=NULL,*errorlog=NULL;
  int         option_parse_only=0,option_process_only=0;
  int         option_filenames=0;
  int         arg;
@@ -95,6 +93,10 @@ int main(int argc,char** argv)
        option_process_only=1;
     else if(!strcmp(argv[arg],"--loggable"))
        option_loggable=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))
@@ -169,6 +171,11 @@ int main(int argc,char** argv)
 
  Relations=NewRelationList(option_parse_only||option_process_only);
 
+ /* Create the error log file */
+
+ if(errorlog)
+    open_errorlog(FileName(dirname,prefix,errorlog),option_parse_only||option_process_only);
+
  /* Parse the file */
 
  if(option_filenames)
@@ -216,6 +223,8 @@ int main(int argc,char** argv)
     return(0);
    }
 
+ DeleteXMLTaggingRules();
+
  /* Process the data */
 
  printf("\nProcess OSM Data\n================\n\n");
@@ -231,33 +240,43 @@ int main(int argc,char** argv)
 
  SortRelationList(Relations);
 
- /* Process the route relations (must be before compacting the ways) */
+ /* Remove bad segments (must be after sorting the nodes and segments) */
+
+ RemoveBadSegments(Nodes,Segments);
+
+ /* Remove non-highway nodes (must be after removing the bad segments) */
+
+ RemoveNonHighwayNodes(Nodes,Segments);
+
+ /* Process the route relations and first part of turn relations (must be before compacting the ways) */
 
  ProcessRouteRelations(Relations,Ways);
 
- FreeRelationList(Relations,0);
+ ProcessTurnRelations1(Relations,Nodes,Ways);
 
  /* Compact the ways (must be before measuring the segments) */
 
  CompactWayList(Ways);
 
- /* Remove bad segments (must be after sorting the nodes and segments) */
+ /* Measure the segments and replace node/way id with index (must be after removing non-highway nodes) */
 
- RemoveBadSegments(Nodes,Segments);
+ MeasureSegments(Segments,Nodes,Ways);
 
- /* Remove non-highway nodes (must be after removing the bad segments) */
+ /* Index the segments */
 
- RemoveNonHighwayNodes(Nodes,Segments);
+ IndexSegments(Segments,Nodes);
 
- /* Measure the segments and replace node/way id with index (must be after removing non-highway nodes) */
+ /* Convert the turn relations from ways into nodes */
 
- UpdateSegments(Segments,Nodes,Ways);
+ ProcessTurnRelations2(Relations,Nodes,Segments,Ways);
 
 
  /* Repeated iteration on Super-Nodes and Super-Segments */
 
  do
    {
+    int nsuper;
+
     printf("\nProcess Super-Data (iteration %d)\n================================%s\n\n",iteration,iteration>9?"=":"");
     fflush(stdout);
 
@@ -269,7 +288,9 @@ int main(int argc,char** argv)
 
        /* Select the super-segments */
 
-       SuperSegments=CreateSuperSegments(Nodes,Segments,Ways,iteration);
+       SuperSegments=CreateSuperSegments(Nodes,Segments,Ways);
+
+       nsuper=Segments->number;
       }
     else
       {
@@ -281,10 +302,9 @@ int main(int argc,char** argv)
 
        /* Select the super-segments */
 
-       SuperSegments2=CreateSuperSegments(Nodes,SuperSegments,Ways,iteration);
+       SuperSegments2=CreateSuperSegments(Nodes,SuperSegments,Ways);
 
-       if(SuperSegments->xnumber==SuperSegments2->xnumber)
-          quit=1;
+       nsuper=SuperSegments->number;
 
        FreeSegmentList(SuperSegments,0);
 
@@ -299,6 +319,15 @@ int main(int argc,char** argv)
 
     DeduplicateSegments(SuperSegments,Nodes,Ways);
 
+    /* Index the segments */
+
+    IndexSegments(SuperSegments,Nodes);
+
+    /* Check for end condition */
+
+    if(SuperSegments->number==nsuper)
+       quit=1;
+
     iteration++;
 
     if(iteration>max_iterations)
@@ -321,38 +350,38 @@ int main(int argc,char** argv)
 
  Segments=MergedSegments;
 
- /* Rotate segments so that node1<node2 */
-
- RotateSegments(Segments);
-
- /* Sort the segments */
+ /* Sort and re-index the segments */
 
  SortSegmentList(Segments);
 
- /* Remove duplicated segments */
-
- DeduplicateSegments(Segments,Nodes,Ways);
+ IndexSegments(Segments,Nodes);
 
  /* Cross reference the nodes and segments */
 
  printf("\nCross-Reference Nodes and Segments\n==================================\n\n");
  fflush(stdout);
 
- /* Sort the node list geographically */
+ /* Sort the nodes geographically and update the segment indexes accordingly */
 
  SortNodeListGeographically(Nodes);
 
- /* Create the real segments and nodes */
+ UpdateSegments(Segments,Nodes,Ways);
+
+ /* Sort the segments geographically and re-index them */
 
- CreateRealNodes(Nodes,iteration);
+ SortSegmentList(Segments);
 
- CreateRealSegments(Segments,Ways);
+ IndexSegments(Segments,Nodes);
 
- /* Fix the segment and node indexes */
+ /* Update the nodes */
 
- IndexNodes(Nodes,Segments);
+ UpdateNodes(Nodes,Segments);
 
- IndexSegments(Segments,Nodes);
+ /* Fix the turn relations after sorting nodes geographically */
+
+ UpdateTurnRelations(Relations,Nodes,Segments);
+
+ SortTurnRelationList(Relations);
 
  /* Output the results */
 
@@ -377,6 +406,17 @@ int main(int argc,char** argv)
 
  FreeWayList(Ways,0);
 
+ /* Write out the relations */
+
+ SaveRelationList(Relations,FileName(dirname,prefix,"relations.mem"));
+
+ FreeRelationList(Relations,0);
+
+ /* Close the error log file */
+
+ if(errorlog)
+    close_errorlog();
+
  return(0);
 }
 
@@ -399,7 +439,7 @@ static void print_usage(int detail,const char *argerr,const char *err)
          "                      [--sort-ram-size=<size>]\n"
          "                      [--tmpdir=<dirname>]\n"
          "                      [--parse-only | --process-only]\n"
-         "                      [--loggable]\n"
+         "                      [--loggable] [--errorlog[=<name>]]\n"
          "                      [--max-iterations=<number>]\n"
          "                      [--tagging=<filename>]\n"
          "                      [<filename.osm> ...]\n");
@@ -435,8 +475,11 @@ static void print_usage(int detail,const char *argerr,const char *err)
             "--process-only            Process the stored results from previous option.\n"
             "\n"
             "--loggable                Print progress messages suitable for logging to file.\n"
+            "--errorlog[=<name>]       Log parsing errors to 'error.log' or the given name\n"
+            "                          (the '--dir' and '--prefix' options are applied).\n"
             "\n"
-            "--max-iterations=<number> The number of iterations for finding super-nodes.\n"
+            "--max-iterations=<number> The number of iterations for finding super-nodes\n"
+            "                          (defaults to 5).\n"
             "\n"
             "--tagging=<filename>      The name of the XML file containing the tagging rules\n"
             "                          (defaults to 'tagging.xml' with '--dir' and\n"
diff --git a/src/profiles.c b/src/profiles.c
index 8c89d90..9b2f35e 100644
--- a/src/profiles.c
+++ b/src/profiles.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/profiles.c,v 1.47 2010/10/18 17:40:34 amb Exp $
-
  Load the profiles from a file and the functions for handling them.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -25,6 +23,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <math.h>
 
 #include "types.h"
 #include "ways.h"
@@ -35,6 +34,8 @@
 #include "xmlparse.h"
 
 
+/* Local variables */
+
 /*+ The profiles that have been loaded from file. +*/
 static Profile **loaded_profiles=NULL;
 
@@ -52,6 +53,7 @@ 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);
@@ -112,6 +114,13 @@ static xmltag propertiesType_tag=
                NULL,
                {&propertyType_tag,NULL}};
 
+/*+ The turnsType type tag. +*/
+static xmltag turnsType_tag=
+              {"turns",
+               1, {"obey"},
+               turnsType_function,
+               {NULL}};
+
 /*+ The weightType type tag. +*/
 static xmltag weightType_tag=
               {"weight",
@@ -145,7 +154,7 @@ static xmltag restrictionsType_tag=
               {"restrictions",
                0, {NULL},
                NULL,
-               {&onewayType_tag,&weightType_tag,&heightType_tag,&widthType_tag,&lengthType_tag,NULL}};
+               {&onewayType_tag,&turnsType_tag,&weightType_tag,&heightType_tag,&widthType_tag,&lengthType_tag,NULL}};
 
 /*+ The profileType type tag. +*/
 static xmltag profileType_tag=
@@ -204,7 +213,7 @@ static int speedType_function(const char *_tag_,int _type_,const char *highway,c
     if(highwaytype==Way_Count)
        XMLPARSE_INVALID(_tag_,highway);
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,kph,speed);
+    XMLPARSE_ASSERT_FLOATING(_tag_,kph); speed=atof(kph);
 
     loaded_profiles[nloaded_profiles-1]->speed[highwaytype]=kph_to_speed(speed);
    }
@@ -257,7 +266,7 @@ static int preferenceType_function(const char *_tag_,int _type_,const char *high
     if(highwaytype==Way_Count)
        XMLPARSE_INVALID(_tag_,highway);
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,percent,p);
+    XMLPARSE_ASSERT_FLOATING(_tag_,percent); p=atof(percent);
 
     loaded_profiles[nloaded_profiles-1]->highway[highwaytype]=p;
    }
@@ -310,7 +319,7 @@ static int propertyType_function(const char *_tag_,int _type_,const char *type,c
     if(property==Property_Count)
        XMLPARSE_INVALID(_tag_,type);
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,percent,p);
+    XMLPARSE_ASSERT_FLOATING(_tag_,percent); p=atof(percent);
 
     loaded_profiles[nloaded_profiles-1]->props_yes[property]=p;
    }
@@ -337,7 +346,7 @@ static int onewayType_function(const char *_tag_,int _type_,const char *obey)
    {
     int o;
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,obey,o);
+    XMLPARSE_ASSERT_INTEGER(_tag_,obey); o=atoi(obey);
 
     loaded_profiles[nloaded_profiles-1]->oneway=!!o;
    }
@@ -363,6 +372,33 @@ static int onewayType_function(const char *_tag_,int _type_,const char *obey)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  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.
+
+  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 *obey The contents of the 'obey' attribute (or NULL if not defined).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int turnsType_function(const char *_tag_,int _type_,const char *obey)
+{
+ if(_type_&XMLPARSE_TAG_START)
+   {
+    int o;
+
+    XMLPARSE_ASSERT_INTEGER(_tag_,obey); o=atoi(obey);
+
+    loaded_profiles[nloaded_profiles-1]->turns=!!o;
+   }
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   The function that is called when the weightType XSD type is seen
 
   int weightType_function Returns 0 if no error occured or something else otherwise.
@@ -380,7 +416,7 @@ static int weightType_function(const char *_tag_,int _type_,const char *limit)
    {
     double l;
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,limit,l);
+    XMLPARSE_ASSERT_FLOATING(_tag_,limit); l=atof(limit);
 
     loaded_profiles[nloaded_profiles-1]->weight=tonnes_to_weight(l);
    }
@@ -407,7 +443,7 @@ static int heightType_function(const char *_tag_,int _type_,const char *limit)
    {
     double l;
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,limit,l);
+    XMLPARSE_ASSERT_FLOATING(_tag_,limit); l=atof(limit);
 
     loaded_profiles[nloaded_profiles-1]->height=metres_to_height(l);
    }
@@ -434,7 +470,7 @@ static int widthType_function(const char *_tag_,int _type_,const char *limit)
    {
     double l;
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,limit,l);
+    XMLPARSE_ASSERT_FLOATING(_tag_,limit); l=atof(limit);
 
     loaded_profiles[nloaded_profiles-1]->width=metres_to_width(l);
    }
@@ -461,7 +497,7 @@ static int lengthType_function(const char *_tag_,int _type_,const char *limit)
    {
     double l;
 
-    XMLPARSE_ASSERT_FLOATING(_tag_,limit,l);
+    XMLPARSE_ASSERT_FLOATING(_tag_,limit); l=atof(limit);
 
     loaded_profiles[nloaded_profiles-1]->length=metres_to_length(l);
    }
@@ -580,6 +616,7 @@ static int profileType_function(const char *_tag_,int _type_,const char *name,co
 
 int ParseXMLProfiles(const char *filename)
 {
+ FILE *file;
  int retval;
 
  if(!ExistsFile(filename))
@@ -588,7 +625,7 @@ int ParseXMLProfiles(const char *filename)
     return(1);
    }
 
- FILE *file=fopen(filename,"r");
+ file=fopen(filename,"r");
 
  if(!file)
    {
@@ -618,7 +655,7 @@ int ParseXMLProfiles(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Get the profile for a type of transport.
+  Get a named profile.
 
   Profile *GetProfile Returns a pointer to the profile.
 
@@ -638,7 +675,7 @@ Profile *GetProfile(const char *name)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Update a profile with highway preference scaling factor.
+  Update a profile with the highway preference scaling factors.
 
   int UpdateProfile Returns 1 in case of a problem.
 
@@ -654,7 +691,7 @@ int UpdateProfile(Profile *profile,Ways *ways)
 
  /* Fix up the allowed transport types. */
 
- profile->allow=ALLOWED(profile->transport);
+ profile->allow=TRANSPORTS(profile->transport);
 
  if(!(profile->allow & ways->file.allow))
     return(1);
@@ -744,7 +781,7 @@ int UpdateProfile(Profile *profile,Ways *ways)
 
 void PrintProfile(const Profile *profile)
 {
- unsigned int i;
+ int i;
 
  printf("Profile\n=======\n");
 
@@ -771,6 +808,7 @@ void PrintProfile(const Profile *profile)
  printf("\n");
 
  printf("Obey one-way  : %s\n",profile->oneway?"yes":"no");
+ printf("Obey turns    : %s\n",profile->turns?"yes":"no");
  printf("Minimum weight: %.1f tonnes\n",weight_to_tonnes(profile->weight));
  printf("Minimum height: %.1f metres\n",height_to_metres(profile->height));
  printf("Minimum width : %.1f metres\n",width_to_metres(profile->width));
@@ -779,12 +817,12 @@ void PrintProfile(const Profile *profile)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the profiles as XML for use as program input.
+  Print out all of the loaded profiles as XML for use as program input.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void PrintProfilesXML(void)
 {
- unsigned int i,j;
+ int i,j;
  char *padding="                ";
 
  printf("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
@@ -814,6 +852,7 @@ void PrintProfilesXML(void)
 
     printf("    <restrictions>\n");
     printf("      <oneway obey=\"%d\" /> \n",loaded_profiles[j]->oneway);
+    printf("      <turns  obey=\"%d\" /> \n",loaded_profiles[j]->turns);
     printf("      <weight limit=\"%.1f\" />\n",weight_to_tonnes(loaded_profiles[j]->weight));
     printf("      <height limit=\"%.1f\" />\n",height_to_metres(loaded_profiles[j]->height));
     printf("      <width  limit=\"%.1f\" />\n",width_to_metres(loaded_profiles[j]->width));
@@ -829,12 +868,12 @@ void PrintProfilesXML(void)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the profiles as JavaScript Object Notation for use in a web form.
+  Print out all of the loaded profiles as JavaScript Object Notation for use in a web page.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void PrintProfilesJSON(void)
 {
- unsigned int i,j;
+ int i,j;
 
  printf("var routino={ // contains all default Routino options (generated using \"--help-profile-json\").\n");
  printf("\n");
@@ -865,7 +904,7 @@ void PrintProfilesJSON(void)
  printf("\n");
 
  printf("  // Restriction types\n");
- printf("  restrictions: { oneway: 1, weight: 2, height: 3, width: 4, length: 5 },\n");
+ printf("  restrictions: { oneway: 1, turns: 2, weight: 3, height: 4, width: 5, length: 6 },\n");
  printf("\n");
 
  printf("  // Allowed highways\n");
@@ -910,6 +949,10 @@ void PrintProfilesJSON(void)
  for(j=0;j<nloaded_profiles;j++)
     printf("%s%s: %4d",j==0?"":", ",TransportName(loaded_profiles[j]->transport),loaded_profiles[j]->oneway);
  printf(" },\n");
+ printf("    %12s: { ","turns");
+ for(j=0;j<nloaded_profiles;j++)
+    printf("%s%s: %4d",j==0?"":", ",TransportName(loaded_profiles[j]->transport),loaded_profiles[j]->turns);
+ printf(" },\n");
  printf("    %12s: { ","weight");
  for(j=0;j<nloaded_profiles;j++)
     printf("%s%s: %4.1f",j==0?"":", ",TransportName(loaded_profiles[j]->transport),weight_to_tonnes(loaded_profiles[j]->weight));
@@ -934,12 +977,12 @@ void PrintProfilesJSON(void)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Print out the profiles as Perl for use in a web CGI.
+  Print out all of the loaded profiles as Perl for use in a web CGI.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void PrintProfilesPerl(void)
 {
- unsigned int i,j;
+ int i,j;
 
  printf("$routino={ # contains all default Routino options (generated using \"--help-profile-perl\").\n");
  printf("\n");
@@ -970,7 +1013,7 @@ void PrintProfilesPerl(void)
  printf("\n");
 
  printf("  # Restriction types\n");
- printf("  restrictions => { oneway => 1, weight => 2, height => 3, width => 4, length => 5 },\n");
+ printf("  restrictions => { oneway => 1, turns => 2, weight => 3, height => 4, width => 5, length => 6 },\n");
  printf("\n");
 
  printf("  # Allowed highways\n");
@@ -1015,6 +1058,10 @@ void PrintProfilesPerl(void)
  for(j=0;j<nloaded_profiles;j++)
     printf("%s %s => %4d",j==0?"":", ",TransportName(loaded_profiles[j]->transport),loaded_profiles[j]->oneway);
  printf(" },\n");
+ printf("    %12s => {","turns");
+ for(j=0;j<nloaded_profiles;j++)
+    printf("%s %s => %4d",j==0?"":", ",TransportName(loaded_profiles[j]->transport),loaded_profiles[j]->turns);
+ printf(" },\n");
  printf("    %12s => {","weight");
  for(j=0;j<nloaded_profiles;j++)
     printf("%s %s => %4.1f",j==0?"":", ",TransportName(loaded_profiles[j]->transport),weight_to_tonnes(loaded_profiles[j]->weight));
diff --git a/src/profiles.h b/src/profiles.h
index 2082b5c..e3d4281 100644
--- a/src/profiles.h
+++ b/src/profiles.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/profiles.h,v 1.17 2010/07/31 18:21:18 amb Exp $
-
  A header file for the profiles.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,33 +32,34 @@
 /*+ A data structure to hold a transport type profile. +*/
 typedef struct _Profile
 {
- char      *name;                      /*+ The name of the profile. +*/
+ char        *name;                      /*+ The name of the profile. +*/
 
- Transport  transport;                 /*+ The type of transport. +*/
+ Transport    transport;                 /*+ The type of transport. +*/
 
- allow_t    allow;                     /*+ The type of transport expressed as what must be allowed. +*/
+ transports_t allow;                     /*+ The type of transport expressed as a bitmask. +*/
 
- score_t    highway[Way_Count];        /*+ A floating point preference for travel on the highway. +*/
- score_t    max_pref;                  /*+ The maximum preference for any highway type. +*/
+ score_t      highway[Way_Count];        /*+ A floating point preference for travel on the highway. +*/
+ score_t      max_pref;                  /*+ The maximum preference for any highway type. +*/
 
- speed_t    speed[Way_Count];          /*+ The maximum speed on each type of highway. +*/
- speed_t    max_speed;                 /*+ The maximum speed for any highway type. +*/
+ speed_t      speed[Way_Count];          /*+ The maximum speed on each type of highway. +*/
+ speed_t      max_speed;                 /*+ The maximum speed for any highway type. +*/
 
- score_t    props_yes[Property_Count]; /*+ A floating point preference for ways with this attribute. +*/
- score_t    props_no [Property_Count]; /*+ A floating point preference for ways without this attribute. +*/
+ score_t      props_yes[Property_Count]; /*+ A floating point preference for ways with this attribute. +*/
+ score_t      props_no [Property_Count]; /*+ A floating point preference for ways without this attribute. +*/
 
- int        oneway;                    /*+ A flag to indicate if one-way restrictions apply. +*/
+ int          oneway;                    /*+ A flag to indicate if one-way restrictions apply. +*/
+ int          turns;                     /*+ A flag to indicate if turn restrictions apply. +*/
 
- weight_t   weight;                    /*+ The minimum weight of the route. +*/
+ weight_t     weight;                    /*+ The minimum weight of the route. +*/
 
- height_t   height;                    /*+ The minimum height of vehicles on the route. +*/
- width_t    width;                     /*+ The minimum width of vehicles on the route. +*/
- length_t   length;                    /*+ The minimum length of vehicles on the route. +*/
+ height_t     height;                    /*+ The minimum height of vehicles on the route. +*/
+ width_t      width;                     /*+ The minimum width of vehicles on the route. +*/
+ length_t     length;                    /*+ The minimum length of vehicles on the route. +*/
 }
  Profile;
 
 
-/* Functions */
+/* Functions in profiles.c */
 
 int ParseXMLProfiles(const char *filename);
 
@@ -76,4 +75,5 @@ void PrintProfilesJSON(void);
 
 void PrintProfilesPerl(void);
 
+
 #endif /* PROFILES_H */
diff --git a/src/queue.c b/src/queue.c
index 68c4ccb..3c2d65e 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/queue.c,v 1.7 2009/11/13 19:24:11 amb Exp $
-
  Queue data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008,2009 Andrew M. Bishop
+ This file Copyright 2008-2011 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 +27,10 @@
 #include "results.h"
 
 
+/*+ The size of the increment to the allocated memory. +*/
+#define QUEUE_INCREMENT 1024
+
+
 /*+ A queue of results. +*/
 struct _Queue
 {
@@ -51,7 +53,7 @@ Queue *NewQueueList(void)
 
  queue=(Queue*)malloc(sizeof(Queue));
 
- queue->nallocated=1023;
+ queue->nallocated=QUEUE_INCREMENT;
  queue->noccupied=0;
 
  queue->data=(Result**)malloc(queue->nallocated*sizeof(Result*));
@@ -91,15 +93,15 @@ void InsertInQueue(Queue *queue,Result *result)
 
  if(result->queued==NOT_QUEUED)
    {
+    queue->noccupied++;
+    index=queue->noccupied;
+
     if(queue->noccupied==queue->nallocated)
       {
-       queue->nallocated=2*queue->nallocated+1;
+       queue->nallocated=queue->nallocated+QUEUE_INCREMENT;
        queue->data=(Result**)realloc((void*)queue->data,queue->nallocated*sizeof(Result*));
       }
 
-    index=queue->noccupied;
-    queue->noccupied++;
-
     queue->data[index]=result;
     queue->data[index]->queued=index;
    }
@@ -110,13 +112,13 @@ void InsertInQueue(Queue *queue,Result *result)
 
  /* Bubble up the new value */
 
- while(index>0 &&
-       queue->data[index]->sortby<queue->data[(index-1)/2]->sortby)
+ while(index>1 &&
+       queue->data[index]->sortby<queue->data[index/2]->sortby)
    {
     uint32_t newindex;
     Result *temp;
 
-    newindex=(index-1)/2;
+    newindex=index/2;
 
     temp=queue->data[index];
     queue->data[index]=queue->data[newindex];
@@ -149,27 +151,27 @@ Result *PopFromQueue(Queue *queue)
  if(queue->noccupied==0)
     return(NULL);
 
- retval=queue->data[0];
+ retval=queue->data[1];
  retval->queued=NOT_QUEUED;
 
- index=0;
- queue->noccupied--;
+ index=1;
 
  queue->data[index]=queue->data[queue->noccupied];
+ queue->noccupied--;
 
  /* Bubble down the newly promoted value */
 
- while((2*index+2)<queue->noccupied &&
-       (queue->data[index]->sortby>queue->data[2*index+1]->sortby ||
-        queue->data[index]->sortby>queue->data[2*index+2]->sortby))
+ while((2*index)<queue->noccupied &&
+       (queue->data[index]->sortby>queue->data[2*index  ]->sortby ||
+        queue->data[index]->sortby>queue->data[2*index+1]->sortby))
    {
     uint32_t newindex;
     Result *temp;
 
-    if(queue->data[2*index+1]->sortby<queue->data[2*index+2]->sortby)
-       newindex=2*index+1;
+    if(queue->data[2*index]->sortby<queue->data[2*index+1]->sortby)
+       newindex=2*index;
     else
-       newindex=2*index+2;
+       newindex=2*index+1;
 
     temp=queue->data[newindex];
     queue->data[newindex]=queue->data[index];
@@ -181,13 +183,13 @@ Result *PopFromQueue(Queue *queue)
     index=newindex;
    }
 
- if((2*index+2)==queue->noccupied &&
-    queue->data[index]->sortby>queue->data[2*index+1]->sortby)
+ if((2*index)==queue->noccupied &&
+    queue->data[index]->sortby>queue->data[2*index]->sortby)
    {
     uint32_t newindex;
     Result *temp;
 
-    newindex=2*index+1;
+    newindex=2*index;
 
     temp=queue->data[newindex];
     queue->data[newindex]=queue->data[index];
diff --git a/src/relations.c b/src/relations.c
new file mode 100644
index 0000000..f131675
--- /dev/null
+++ b/src/relations.c
@@ -0,0 +1,379 @@
+/***************************************
+ Relation data type functions.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2011 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 <sys/types.h>
+#include <stdlib.h>
+
+#include "relations.h"
+#include "fakes.h"
+
+#include "files.h"
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Load in a relation list from a file.
+
+  Relations *LoadRelationList Returns the relation list.
+
+  const char *filename The name of the file to load.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+Relations *LoadRelationList(const char *filename)
+{
+ Relations *relations;
+#if SLIM
+ int i;
+#endif
+
+ relations=(Relations*)malloc(sizeof(Relations));
+
+#if !SLIM
+
+ relations->data=MapFile(filename);
+
+ /* Copy the RelationsFile header structure from the loaded data */
+
+ relations->file=*((RelationsFile*)relations->data);
+
+ /* Set the pointers in the Relations structure. */
+
+ relations->turnrelations=(TurnRelation*)(relations->data+sizeof(RelationsFile));
+
+#else
+
+ relations->fd=ReOpenFile(filename);
+
+ /* Copy the RelationsFile header structure from the loaded data */
+
+ ReadFile(relations->fd,&relations->file,sizeof(RelationsFile));
+
+ relations->troffset=sizeof(RelationsFile);
+
+ for(i=0;i<sizeof(relations->cached)/sizeof(relations->cached[0]);i++)
+    relations->incache[i]=NO_RELATION;
+
+#endif
+
+ if(relations->file.trnumber>0)
+   {
+    TurnRelation *relation;
+
+    relation=LookupTurnRelation(relations,0,1);
+
+    relations->via_start =relation->via;
+
+    relation=LookupTurnRelation(relations,relations->file.trnumber-1,1);
+
+    relations->via_end =relation->via;
+   }
+
+ return(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.
+
+  Relations *relations The set of relations to use.
+
+  index_t via The node that the route is going via.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t FindFirstTurnRelation1(Relations *relations,index_t via)
+{
+ TurnRelation *relation;
+ index_t start=0;
+ index_t end=relations->file.trnumber-1;
+ index_t mid;
+ index_t match=-1;
+
+ /* Binary search - search key any exact match 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 matches (but may not be the first).
+  */
+
+ do
+   {
+    mid=(start+end)/2;              /* Choose mid point */
+
+    relation=LookupTurnRelation(relations,mid,1);
+
+    if(relation->via<via)           /* Mid point is too low for 'via' */
+       start=mid+1;
+    else if(relation->via>via)      /* Mid point is too high for 'via' */
+       end=mid?(mid-1):mid;
+    else                            /* Mid point is correct for 'from' */
+      {
+       match=mid;
+       break;
+      }
+   }
+ while((end-start)>1);
+
+ if(match==-1)                      /* Check if start matches */
+   {
+    relation=LookupTurnRelation(relations,start,1);
+
+    if(relation->via==via)
+       match=start;
+   }
+
+ if(match==-1)                      /* Check if end matches */
+   {
+    relation=LookupTurnRelation(relations,end,1);
+
+    if(relation->via==via)
+       match=end;
+   }
+
+ if(match==-1)
+    return(NO_RELATION);
+
+ while(match>0)                     /* Search backwards for the first match */
+   {
+    relation=LookupTurnRelation(relations,match-1,1);
+
+    if(relation->via==via)
+       match--;
+    else
+       break;
+   }
+
+ return(match);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the next turn relation in the file whose 'via' matches a specific node.
+
+  index_t FindNextTurnRelation1 Returns the index of the next turn relation matching.
+
+  Relations *relations The set of relations to use.
+
+  index_t current The current index of a relation that matches.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t FindNextTurnRelation1(Relations *relations,index_t current)
+{
+ TurnRelation *relation;
+ index_t via;
+
+ relation=LookupTurnRelation(relations,current,1);
+
+ via=relation->via;
+
+ current++;
+
+ if(current==relations->file.trnumber)
+    return(NO_RELATION);
+
+ relation=LookupTurnRelation(relations,current,1);
+
+ if(relation->via==via)
+    return(current);
+ else
+    return(NO_RELATION);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the first turn relation in the file whose 'via' and 'from' match a specific node and segment.
+
+  index_t FindFirstTurnRelation2 Returns the index of the first turn relation matching.
+
+  Relations *relations The set of relations to use.
+
+  index_t via The node that the route is going via.
+
+  index_t from The segment that the route is coming from.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t FindFirstTurnRelation2(Relations *relations,index_t via,index_t from)
+{
+ TurnRelation *relation;
+ index_t start=0;
+ index_t end=relations->file.trnumber-1;
+ index_t mid;
+ index_t match=-1;
+
+ if(IsFakeSegment(from))
+    from=IndexRealSegment(from);
+
+ /* Binary search - search key first match 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 matches (but may not be the first).
+  */
+
+ do
+   {
+    mid=(start+end)/2;              /* Choose mid point */
+
+    relation=LookupTurnRelation(relations,mid,1);
+
+    if(relation->via<via)           /* Mid point is too low for 'via' */
+       start=mid+1;
+    else if(relation->via>via)      /* Mid point is too high for 'via' */
+       end=mid?(mid-1):mid;
+    else                            /* Mid point is correct for 'via' */
+      {
+       if(relation->from<from)      /* Mid point is too low for 'from' */
+          start=mid+1;
+       else if(relation->from>from) /* Mid point is too high for 'from' */
+          end=mid?(mid-1):mid;
+       else                         /* Mid point is correct for 'from' */
+         {
+          match=mid;
+          break;
+         }
+      }
+   }
+ while((end-start)>1);
+
+ if(match==-1)                      /* Check if start matches */
+   {
+    relation=LookupTurnRelation(relations,start,1);
+
+    if(relation->via==via && relation->from==from)
+       match=start;
+   }
+
+ if(match==-1)                      /* Check if end matches */
+   {
+    relation=LookupTurnRelation(relations,end,1);
+
+    if(relation->via==via && relation->from==from)
+       match=end;
+   }
+
+ if(match==-1)
+    return(NO_RELATION);
+
+ while(match>0)                     /* Search backwards for the first match */
+   {
+    relation=LookupTurnRelation(relations,match-1,1);
+
+    if(relation->via==via && relation->from==from)
+       match--;
+    else
+       break;
+   }
+
+ return(match);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the next turn relation in the file whose 'via' and 'from' match a specific node and segment.
+
+  index_t FindNextTurnRelation2 Returns the index of the next turn relation matching.
+
+  Relations *relations The set of relations to use.
+
+  index_t current The current index of a relation that matches.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+index_t FindNextTurnRelation2(Relations *relations,index_t current)
+{
+ TurnRelation *relation;
+ index_t via,from;
+
+ relation=LookupTurnRelation(relations,current,1);
+
+ via=relation->via;
+ from=relation->from;
+
+ current++;
+
+ if(current==relations->file.trnumber)
+    return(NO_RELATION);
+
+ relation=LookupTurnRelation(relations,current,1);
+
+ if(relation->via==via && relation->from==from)
+    return(current);
+ else
+    return(NO_RELATION);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Determine if a turn is allowed between the nodes 'from', 'via' and 'to' for a particular transport type.
+
+  int IsTurnAllowed Return 1 if the turn is allowed or 0 if not.
+
+  Relations *relations The set of relations to use.
+
+  index_t index The index of the first turn relation containing 'via' and 'from'.
+
+  index_t via The via node.
+
+  index_t from The from segment.
+
+  index_t to The to segment.
+
+  transports_t transport The type of transport that is being routed.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+int IsTurnAllowed(Relations *relations,index_t index,index_t via,index_t from,index_t to,transports_t transport)
+{
+ if(IsFakeSegment(from))
+    from=IndexRealSegment(from);
+
+ if(IsFakeSegment(to))
+    to=IndexRealSegment(to);
+
+ while(index<relations->file.trnumber)
+   {
+    TurnRelation *relation=LookupTurnRelation(relations,index,1);
+
+    if(relation->via!=via)
+       return(1);
+
+    if(relation->from!=from)
+       return(1);
+
+    if(relation->to>to)
+       return(1);
+
+    if(relation->to==to)
+       if(!(relation->except & transport))
+          return(0);
+
+    index++;
+   }
+
+ return(1);
+}
diff --git a/src/relations.h b/src/relations.h
new file mode 100644
index 0000000..5f3e270
--- /dev/null
+++ b/src/relations.h
@@ -0,0 +1,137 @@
+/***************************************
+ A header file for the relations.
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2011 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 RELATIONS_H
+#define RELATIONS_H    /*+ To stop multiple inclusions. +*/
+
+#include <stdint.h>
+
+#include "types.h"
+
+#include "files.h"
+#include "profiles.h"
+
+
+/* Data structures */
+
+
+/*+ A structure containing a single relation. +*/
+struct _TurnRelation
+{
+ index_t      from;             /*+ The segment that the path comes from. +*/
+ index_t      via;              /*+ The node that the path goes via. +*/
+ index_t      to;               /*+ The segment that the path goes to. +*/
+
+ transports_t except;           /*+ The types of transports that that this relation does not apply to. +*/
+};
+
+
+/*+ A structure containing the header from the file. +*/
+typedef struct _RelationsFile
+{
+ index_t       trnumber;        /*+ The number of turn relations in total. +*/
+}
+ RelationsFile;
+
+
+/*+ A structure containing a set of relations (and pointers to mmap file). +*/
+struct _Relations
+{
+ RelationsFile file;            /*+ The header data from the file. +*/
+
+#if !SLIM
+
+ void         *data;            /*+ The memory mapped data. +*/
+
+ TurnRelation *turnrelations;   /*+ An array of nodes. +*/
+
+#else
+
+ int           fd;              /*+ The file descriptor for the file. +*/
+
+ 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. +*/
+
+#endif
+
+ index_t       via_start;       /*+ The first via node in the file. +*/
+ index_t       via_end;         /*+ The last via node in the file. +*/
+};
+
+
+/* Functions in relations.c */
+
+Relations *LoadRelationList(const char *filename);
+
+index_t FindFirstTurnRelation1(Relations *relations,index_t via);
+index_t FindNextTurnRelation1(Relations *relations,index_t current);
+
+index_t FindFirstTurnRelation2(Relations *relations,index_t via,index_t from);
+index_t FindNextTurnRelation2(Relations *relations,index_t current);
+
+int IsTurnAllowed(Relations *relations,index_t index,index_t via,index_t from,index_t to,transports_t transport);
+
+
+/* Macros and inline functions */
+
+#if !SLIM
+
+/*+ Return a Relation pointer given a set of relations and an index. +*/
+#define LookupTurnRelation(xxx,yyy,ppp)   (&(xxx)->turnrelations[yyy])
+
+#else
+
+static TurnRelation *LookupTurnRelation(Relations *relations,index_t index,int position);
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the Relation information for a particular relation.
+
+  TurnRelation *LookupTurnRelation Returns a pointer to the cached relation information.
+
+  Relations *relations The set of relations to use.
+
+  index_t index The index of the relation.
+
+  int position The position in the cache to store this result.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline TurnRelation *LookupTurnRelation(Relations *relations,index_t index,int position)
+{
+ if(relations->incache[position-1]!=index)
+   {
+    SeekFile(relations->fd,relations->troffset+(off_t)index*sizeof(TurnRelation));
+
+    ReadFile(relations->fd,&relations->cached[position-1],sizeof(TurnRelation));
+
+    relations->incache[position-1]=index;
+   }
+
+ return(&relations->cached[position-1]);
+}
+
+#endif
+
+
+#endif /* RELATIONS_H */
diff --git a/src/relationsx.c b/src/relationsx.c
index 1822f9d..b667d0c 100644
--- a/src/relationsx.c
+++ b/src/relationsx.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/relationsx.c,v 1.10 2010/11/13 14:57:30 amb Exp $
-
  Extended Relation data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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,12 +26,25 @@
 #include <string.h>
 #include <sys/stat.h>
 
+#include "types.h"
+#include "relations.h"
+
+#include "nodesx.h"
+#include "segmentsx.h"
 #include "waysx.h"
 #include "relationsx.h"
 
 #include "files.h"
 #include "logging.h"
-#include "functions.h"
+#include "sorting.h"
+
+
+/* Local functions */
+
+static int sort_by_id(TurnRestrictRelX *a,TurnRestrictRelX *b);
+static int deduplicate_by_id(TurnRestrictRelX *relationx,index_t index);
+
+static int sort_by_via(TurnRestrictRelX *a,TurnRestrictRelX *b);
 
 
 /* Variables */
@@ -41,6 +52,8 @@
 /*+ The command line '--tmpdir' option or its default value. +*/
 extern char *option_tmpdirname;
 
+static RelationsX* sortrelationsx;
+
 
 /*++++++++++++++++++++++++++++++++++++++
   Allocate a new relation list (create a new file or open an existing one).
@@ -58,12 +71,15 @@ RelationsX *NewRelationList(int append)
 
  assert(relationsx); /* Check calloc() worked */
 
+
+ /* Route Relations */
+
  relationsx->rfilename=(char*)malloc(strlen(option_tmpdirname)+32);
 
  if(append)
     sprintf(relationsx->rfilename,"%s/relationsx.route.input.tmp",option_tmpdirname);
  else
-    sprintf(relationsx->rfilename,"%s/relationsx.route.%p.tmp",option_tmpdirname,relationsx);
+    sprintf(relationsx->rfilename,"%s/relationsx.route.%p.tmp",option_tmpdirname,(void*)relationsx);
 
  if(append)
    {
@@ -80,7 +96,7 @@ RelationsX *NewRelationList(int append)
        SeekFile(relationsx->rfd,position);
        ReadFile(relationsx->rfd,&relationsize,FILESORT_VARSIZE);
 
-       relationsx->rxnumber++;
+       relationsx->rnumber++;
        position+=relationsize+FILESORT_VARSIZE;
       }
 
@@ -89,6 +105,29 @@ RelationsX *NewRelationList(int append)
  else
     relationsx->rfd=OpenFileNew(relationsx->rfilename);
 
+
+ /* Turn Restriction Relations */
+
+ relationsx->trfilename=(char*)malloc(strlen(option_tmpdirname)+32);
+
+ if(append)
+    sprintf(relationsx->trfilename,"%s/relationsx.turn.input.tmp",option_tmpdirname);
+ else
+    sprintf(relationsx->trfilename,"%s/relationsx.turn.%p.tmp",option_tmpdirname,(void*)relationsx);
+
+ if(append)
+   {
+    off_t size;
+
+    relationsx->trfd=OpenFileAppend(relationsx->trfilename);
+
+    size=SizeFile(relationsx->trfilename);
+
+    relationsx->trnumber=size/sizeof(TurnRestrictRelX);
+   }
+ else
+    relationsx->trfd=OpenFileNew(relationsx->trfilename);
+
  return(relationsx);
 }
 
@@ -96,18 +135,28 @@ RelationsX *NewRelationList(int append)
 /*++++++++++++++++++++++++++++++++++++++
   Free a relation list.
 
-  RelationsX *relationsx The list to be freed.
+  RelationsX *relationsx The set of relations to be freed.
 
-  int keep Set to 1 if the file is to be kept.
+  int keep Set to 1 if the file is to be kept (for appending later).
   ++++++++++++++++++++++++++++++++++++++*/
 
 void FreeRelationList(RelationsX *relationsx,int keep)
 {
+ /* Route relations */
+
  if(!keep)
     DeleteFile(relationsx->rfilename);
 
  free(relationsx->rfilename);
 
+
+ /* Turn Restriction relations */
+
+ if(!keep)
+    DeleteFile(relationsx->trfilename);
+
+ free(relationsx->trfilename);
+
  free(relationsx);
 }
 
@@ -119,7 +168,7 @@ void FreeRelationList(RelationsX *relationsx,int keep)
 
   relation_t id The ID of the relation.
 
-  allow_t routes The types of routes that this relation is for.
+  transports_t routes The types of routes that this relation is for.
 
   way_t *ways The array of ways that are members of the relation.
 
@@ -130,14 +179,15 @@ void FreeRelationList(RelationsX *relationsx,int keep)
   int nrelations The number of relations that are members of the relation.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendRouteRelation(RelationsX* relationsx,relation_t id,allow_t routes,
+void AppendRouteRelation(RelationsX* relationsx,relation_t id,
+                         transports_t routes,
                          way_t *ways,int nways,
                          relation_t *relations,int nrelations)
 {
  RouteRelX relationx;
  FILESORT_VARINT size;
- way_t zeroway=0;
- relation_t zerorelation=0;
+ way_t noway=NO_WAY;
+ relation_t norelation=NO_RELATION;
 
  relationx.id=id;
  relationx.routes=routes;
@@ -147,15 +197,54 @@ void AppendRouteRelation(RelationsX* relationsx,relation_t id,allow_t routes,
  WriteFile(relationsx->rfd,&size,FILESORT_VARSIZE);
  WriteFile(relationsx->rfd,&relationx,sizeof(RouteRelX));
 
- WriteFile(relationsx->rfd,ways    ,nways*sizeof(way_t));
- WriteFile(relationsx->rfd,&zeroway,      sizeof(way_t));
+ WriteFile(relationsx->rfd,ways  ,nways*sizeof(way_t));
+ WriteFile(relationsx->rfd,&noway,      sizeof(way_t));
+
+ WriteFile(relationsx->rfd,relations  ,nrelations*sizeof(relation_t));
+ WriteFile(relationsx->rfd,&norelation,           sizeof(relation_t));
+
+ relationsx->rnumber++;
+
+ assert(!(relationsx->rnumber==0)); /* Zero marks the high-water mark for relations. */
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Append a single relation to an unsorted turn restriction relation list.
+
+  RelationsX* relationsx The set of relations to process.
+
+  relation_t id The ID of the relation.
+
+  way_t from The way that the turn restriction starts from.
+
+  way_t to The way that the restriction finished on.
+
+  node_t via The node that the turn restriction passes through.
+
+  TurnRestriction restriction The type of restriction.
+
+  transports_t except The set of transports allowed to bypass the restriction.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void AppendTurnRestrictRelation(RelationsX* relationsx,relation_t id,
+                                way_t from,way_t to,node_t via,
+                                TurnRestriction restriction,transports_t except)
+{
+ TurnRestrictRelX relationx;
+
+ relationx.id=id;
+ relationx.from=from;
+ relationx.to=to;
+ relationx.via=via;
+ relationx.restriction=restriction;
+ relationx.except=except;
 
- WriteFile(relationsx->rfd,relations    ,nrelations*sizeof(relation_t));
- WriteFile(relationsx->rfd,&zerorelation,           sizeof(relation_t));
+ WriteFile(relationsx->trfd,&relationx,sizeof(TurnRestrictRelX));
 
- relationsx->rxnumber++;
+ relationsx->trnumber++;
 
- assert(!(relationsx->rxnumber==0)); /* Zero marks the high-water mark for relations. */
+ assert(!(relationsx->trnumber==0)); /* Zero marks the high-water mark for relations. */
 }
 
 
@@ -167,61 +256,243 @@ void AppendRouteRelation(RelationsX* relationsx,relation_t id,allow_t routes,
 
 void SortRelationList(RelationsX* relationsx)
 {
- /* Don't need to sort route relations */
+ /* Close the files (finished appending) */
+
+ relationsx->rfd=CloseFile(relationsx->rfd);
+
+ relationsx->trfd=CloseFile(relationsx->trfd);
+
+
+ /* Route Relations */
+
+
+ /* Turn Restriction Relations. */
+
+ if(relationsx->trnumber)
+   {
+    index_t trxnumber;
+    int trfd;
+
+    /* Print the start message */
+
+    printf_first("Sorting Turn Restriction Relations");
+
+    /* Re-open the file read-only and a new file writeable */
+
+    relationsx->trfd=ReOpenFile(relationsx->trfilename);
+
+    DeleteFile(relationsx->trfilename);
+
+    trfd=OpenFileNew(relationsx->trfilename);
+
+    /* Sort the relations */
+
+    trxnumber=relationsx->trnumber;
+    relationsx->trnumber=0;
+
+    sortrelationsx=relationsx;
+
+    filesort_fixed(relationsx->trfd,trfd,sizeof(TurnRestrictRelX),(int (*)(const void*,const void*))sort_by_id,(int (*)(void*,index_t))deduplicate_by_id);
+
+    /* Close the files */
+
+    relationsx->trfd=CloseFile(relationsx->trfd);
+    CloseFile(trfd);
+
+    /* Print the final message */
+
+    printf_last("Sorted Relations: Relations=%"Pindex_t" Duplicates=%"Pindex_t,trxnumber,trxnumber-relationsx->trnumber);
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the turn restriction relations into id order.
+
+  int sort_by_id Returns the comparison of the id fields.
+
+  TurnRestrictRelX *a The first extended relation.
+
+  TurnRestrictRelX *b The second extended relation.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int sort_by_id(TurnRestrictRelX *a,TurnRestrictRelX *b)
+{
+ relation_t a_id=a->id;
+ relation_t b_id=b->id;
+
+ if(a_id<b_id)
+    return(-1);
+ else if(a_id>b_id)
+    return(1);
+ else
+    return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Deduplicate the extended relations using the id after sorting.
+
+  int deduplicate_by_id Return 1 if the value is to be kept, otherwise 0.
+
+  TurnRestrictRelX *relationx The extended relation.
+
+  index_t index The index of this relation in the total.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int deduplicate_by_id(TurnRestrictRelX *relationx,index_t index)
+{
+ static relation_t previd;
+
+ if(index==0 || relationx->id!=previd)
+   {
+    previd=relationx->id;
+
+    sortrelationsx->trnumber++;
+
+    return(1);
+   }
+ else
+   {
+    logerror("Relation %"Prelation_t" is duplicated.\n",relationx->id);
+
+    return(0);
+   }
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the list of turn relations.
+
+  RelationsX* relationsx The set of relations to process.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SortTurnRelationList(RelationsX* relationsx)
+{
+ int trfd;
+
+ if(relationsx->trnumber==0)
+    return;
+
+ /* Print the start message */
+
+ printf_first("Sorting Turn Restriction Relations");
+
+ /* Re-open the file read-only and a new file writeable */
+
+ relationsx->trfd=ReOpenFile(relationsx->trfilename);
+
+ DeleteFile(relationsx->trfilename);
+
+ trfd=OpenFileNew(relationsx->trfilename);
+
+ /* Sort the relations */
+
+ filesort_fixed(relationsx->trfd,trfd,sizeof(TurnRestrictRelX),(int (*)(const void*,const void*))sort_by_via,NULL);
+
+ /* Close the files */
+
+ relationsx->trfd=CloseFile(relationsx->trfd);
+ CloseFile(trfd);
+
+ /* Print the final message */
+
+ printf_last("Sorted Relations: Relations=%"Pindex_t,relationsx->trnumber);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Sort the turn restriction relations into via index order (then by from and to segments).
+
+  int sort_by_via Returns the comparison of the via, from and to fields.
+
+  TurnRestrictRelX *a The first extended relation.
+
+  TurnRestrictRelX *b The second extended relation.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int sort_by_via(TurnRestrictRelX *a,TurnRestrictRelX *b)
+{
+ index_t a_id=a->via;
+ index_t b_id=b->via;
+
+ if(a_id<b_id)
+    return(-1);
+ else if(a_id>b_id)
+    return(1);
+ else
+   {
+    index_t a_id=a->from;
+    index_t b_id=b->from;
+
+    if(a_id<b_id)
+       return(-1);
+    else if(a_id>b_id)
+       return(1);
+    else
+      {
+       index_t a_id=a->to;
+       index_t b_id=b->to;
+
+       if(a_id<b_id)
+          return(-1);
+       else if(a_id>b_id)
+          return(1);
+       else
+          return(0);
+      }
+   }
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Process the route relations and apply the information to the ways.
 
-  RelationsX *relationsx The set of relations to process.
+  RelationsX *relationsx The set of relations to use.
 
-  WaysX *waysx The set of ways to update.
+  WaysX *waysx The set of ways to modify.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
 {
  RouteRelX *unmatched=NULL,*lastunmatched=NULL;
- int nunmatched=0,lastnunmatched=0,iteration=0;
+ int nunmatched=0,lastnunmatched=0,iteration=1;
 
  if(waysx->number==0)
     return;
 
- /* Map into memory */
+ /* Map into memory / open the files */
 
 #if !SLIM
- waysx->xdata=MapFileWriteable(waysx->filename);
-#endif
-
- /* Re-open the ways file read/write */
-
-#if SLIM
- CloseFile(waysx->fd);
+ waysx->data=MapFileWriteable(waysx->filename);
+#else
  waysx->fd=ReOpenFileWriteable(waysx->filename);
 #endif
 
- /* Open the file and read through it */
+ /* Re-open the file read-only */
 
  relationsx->rfd=ReOpenFile(relationsx->rfilename);
 
+ /* Read through the file. */
+
  do
    {
     int ways=0,relations=0;
-    int i;
+    index_t i;
 
     SeekFile(relationsx->rfd,0);
 
     /* Print the start message */
 
-    printf_first("Processing Route Relations: Iteration=%d Relations=0 Modified Ways=0",iteration);
+    printf_first("Processing Route Relations (%d): Relations=0 Modified Ways=0",iteration);
 
-    for(i=0;i<relationsx->rxnumber;i++)
+    for(i=0;i<relationsx->rnumber;i++)
       {
        FILESORT_VARINT size;
        RouteRelX relationx;
        way_t wayid;
        relation_t relationid;
-       allow_t routes=Allow_None;
+       transports_t routes=Transports_None;
 
        /* Read each route relation */
 
@@ -230,7 +501,7 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
 
        /* Decide what type of route it is */
 
-       if(iteration==0)
+       if(iteration==1)
          {
           relations++;
           routes=relationx.routes;
@@ -248,6 +519,7 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
                    routes=0; /* Nothing new to add */
                 else
                    routes=lastunmatched[j].routes;
+
                 break;
                }
          }
@@ -260,7 +532,10 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
 
           /* Update the ways that are listed for the relation */
 
-          if(wayid && routes)
+          if(wayid==NO_WAY)
+             continue;
+
+          if(routes)
             {
              index_t way=IndexWayX(waysx,wayid);
 
@@ -268,21 +543,21 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
                {
                 WayX *wayx=LookupWayX(waysx,way,1);
 
-                if(routes&Allow_Foot)
+                if(routes&Transports_Foot)
                    wayx->way.props|=Properties_FootRoute;
 
-                if(routes&Allow_Bicycle)
+                if(routes&Transports_Bicycle)
                    wayx->way.props|=Properties_BicycleRoute;
 
-#if SLIM
                 PutBackWayX(waysx,way,1);
-#endif
 
                 ways++;
                }
+             else
+                logerror("Route Relation %"Prelation_t" contains Way %"Pway_t" but it does not exist in the Routino database.\n",relationx.id,wayid);
             }
          }
-       while(wayid);
+       while(wayid!=NO_WAY);
 
        /* Loop through the relations */
 
@@ -292,7 +567,12 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
 
           /* Add the relations that are listed for this relation to the list for next time */
 
-          if(relationid && routes && relationid!=relationx.id)
+          if(relationid==NO_RELATION)
+             continue;
+
+          if(relationid==relationx.id)
+             logerror("Relation %"Prelation_t" contains itself.\n",relationx.id);
+          else if(routes)
             {
              if(nunmatched%256==0)
                 unmatched=(RouteRelX*)realloc((void*)unmatched,(nunmatched+256)*sizeof(RouteRelX));
@@ -303,10 +583,10 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
              nunmatched++;
             }
          }
-       while(relationid);
+       while(relationid!=NO_RELATION);
 
-       if(!((i+1)%10000))
-          printf_middle("Processing Route Relations: Iteration=%d Relations=%d Modified Ways=%d",iteration,relations,ways);
+       if(!((i+1)%1000))
+          printf_middle("Processing Route Relations (%d): Relations=%"Pindex_t" Modified Ways=%"Pindex_t,iteration,relations,ways);
       }
 
     if(lastunmatched)
@@ -320,25 +600,545 @@ void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx)
 
     /* Print the final message */
 
-    printf_last("Processed Route Relations: Iteration=%d Relations=%d Modified Ways=%d",iteration,relations,ways);
+    printf_last("Processed Route Relations (%d): Relations=%"Pindex_t" Modified Ways=%"Pindex_t,iteration,relations,ways);
    }
- while(lastnunmatched && ++iteration<5);
+ while(lastnunmatched && iteration++<8);
 
  if(lastunmatched)
     free(lastunmatched);
 
- CloseFile(relationsx->rfd);
+ /* Close the file */
+
+ relationsx->rfd=CloseFile(relationsx->rfd);
 
- /* Unmap from memory */
+ /* Unmap from memory / close the files */
 
 #if !SLIM
- waysx->xdata=UnmapFile(waysx->filename);
+ waysx->data=UnmapFile(waysx->filename);
+#else
+ waysx->fd=CloseFile(waysx->fd);
 #endif
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process the turn relations (first part) to update them with the node/way information.
+
+  RelationsX *relationsx The set of relations to modify.
+
+  NodesX *nodesx The set of nodes to use.
+
+  WaysX *waysx The set of ways to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ProcessTurnRelations1(RelationsX *relationsx,NodesX *nodesx,WaysX *waysx)
+{
+ int trfd;
+ index_t i,deleted=0;
+
+ /* Print the start message */
+
+ printf_first("Processing Turn Relations (1): Relations=0");
+
+ /* Re-open the file read-only and a new file writeable */
+
+ relationsx->trfd=ReOpenFile(relationsx->trfilename);
+
+ DeleteFile(relationsx->trfilename);
+
+ trfd=OpenFileNew(relationsx->trfilename);
+
+ /* Process all of the relations */
+
+ for(i=0;i<relationsx->trnumber;i++)
+   {
+    TurnRestrictRelX relationx;
+    node_t via;
+    way_t from,to;
+
+    ReadFile(relationsx->trfd,&relationx,sizeof(TurnRestrictRelX));
+
+    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);
+
+    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);
+
+    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);
+
+    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(TurnRestrictRelX));
+
+    if(!((i+1)%1000))
+       printf_middle("Processing Turn Relations (1): Relations=%"Pindex_t" Deleted=%"Pindex_t,i+1-deleted,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,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.
 
- /* Re-open the ways file read only */
+  SegmentsX *segmentsx The set of segments to use.
 
-#if SLIM
- CloseFile(waysx->fd);
+  WaysX *waysx The set of ways to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
+{
+ TurnRestrictRelX 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);
+ segmentsx->data=MapFile(segmentsx->filename);
+ waysx->data=MapFile(waysx->filename);
+#else
+ nodesx->fd=ReOpenFileWriteable(nodesx->filename);
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
  waysx->fd=ReOpenFile(waysx->filename);
 #endif
+
+ /* Re-open the file read-only and a new file writeable */
+
+ relationsx->trfd=ReOpenFile(relationsx->trfilename);
+
+ DeleteFile(relationsx->trfilename);
+
+ trfd=OpenFileNew(relationsx->trfilename);
+
+ /* Process all of the relations */
+
+ while(!ReadFile(relationsx->trfd,&relationx,sizeof(TurnRestrictRelX)))
+   {
+    NodeX *nodex;
+    SegmentX *segmentx;
+
+    if(relationx.restriction==TurnRestrict_no_right_turn ||
+       relationx.restriction==TurnRestrict_no_left_turn ||
+       relationx.restriction==TurnRestrict_no_u_turn ||
+       relationx.restriction==TurnRestrict_no_straight_on)
+      {
+       index_t node_from=NO_NODE,node_to=NO_NODE;
+       int oneway_from=0,oneway_to=0,vehicles_from=1,vehicles_to=1;
+
+       /* Find the segments that join the node 'via' */
+
+       segmentx=FirstSegmentX(segmentsx,relationx.via,1);
+
+       while(segmentx)
+         {
+          if(segmentx->way==relationx.from)
+            {
+             WayX *wayx=LookupWayX(waysx,segmentx->way,1);
+
+             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);
+                deleted++;
+                goto endloop;
+               }
+
+             node_from=OtherNode(segmentx,relationx.via);
+
+             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)))
+                vehicles_from=0;  /* not allowed */
+            }
+
+          if(segmentx->way==relationx.to)
+            {
+             WayX *wayx=LookupWayX(waysx,segmentx->way,1);
+
+             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);
+                deleted++;
+                goto endloop;
+               }
+
+             node_to=OtherNode(segmentx,relationx.via);
+
+             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)))
+                vehicles_to=0;  /* not allowed */
+            }
+
+          segmentx=NextSegmentX(segmentsx,segmentx,relationx.via,1);
+         }
+
+       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);
+
+       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);
+
+       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);
+
+       if(oneway_to)
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way is oneway towards the 'via' node.\n",relationx.id);
+
+       if(!vehicles_from)
+          logerror("Turn Relation %"Prelation_t" is not stored because the 'from' way does not allow vehicles.\n",relationx.id);
+
+       if(!vehicles_to)
+          logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way does not allow vehicles.\n",relationx.id);
+
+       if(oneway_from || oneway_to || !vehicles_from || !vehicles_to || node_from==NO_NODE || node_to==NO_NODE)
+         {
+          deleted++;
+          goto endloop;
+         }
+
+       /* Write the results */
+
+       relationx.from=node_from;
+       relationx.to  =node_to;
+
+       WriteFile(trfd,&relationx,sizeof(TurnRestrictRelX));
+
+       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
+      {
+       index_t node_from=NO_NODE,node_to=NO_NODE,node_other[MAX_SEG_PER_NODE];
+       int nnodes_other=0,i;
+       int oneway_from=0,vehicles_from=1;
+
+       /* Find the segments that join the node 'via' */
+
+       segmentx=FirstSegmentX(segmentsx,relationx.via,1);
+
+       while(segmentx)
+         {
+          if(segmentx->way==relationx.from)
+            {
+             WayX *wayx=LookupWayX(waysx,segmentx->way,1);
+
+             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);
+                deleted++;
+                goto endloop;
+               }
+
+             node_from=OtherNode(segmentx,relationx.via);
+
+             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)))
+                vehicles_from=0;  /* not allowed */
+            }
+
+          if(segmentx->way==relationx.to)
+            {
+             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);
+                deleted++;
+                goto endloop;
+               }
+
+             node_to=OtherNode(segmentx,relationx.via);
+            }
+
+          if(segmentx->way!=relationx.from && segmentx->way!=relationx.to)
+            {
+             WayX *wayx=LookupWayX(waysx,segmentx->way,1);
+
+             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)))
+                ;  /* not allowed */
+             else
+               {
+                assert(nnodes_other<MAX_SEG_PER_NODE); /* Only a limited amount of information stored. */
+
+                node_other[nnodes_other++]=OtherNode(segmentx,relationx.via);
+               }
+            }
+
+          segmentx=NextSegmentX(segmentsx,segmentx,relationx.via,1);
+         }
+
+       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);
+
+       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);
+
+       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);
+
+       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);
+
+       if(!vehicles_from)
+          logerror("Turn Relation %"Prelation_t" is not stored because the 'from' way does not allow vehicles.\n",relationx.id);
+
+       if(oneway_from || !vehicles_from || node_from==NO_NODE || node_to==NO_NODE || nnodes_other==0)
+         {
+          deleted++;
+          goto endloop;
+         }
+
+       /* Write the results */
+
+       for(i=0;i<nnodes_other;i++)
+         {
+          relationx.from=node_from;
+          relationx.to  =node_other[i];
+
+          WriteFile(trfd,&relationx,sizeof(TurnRestrictRelX));
+
+          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);
+         }
+      }
+
+    /* Force super nodes on via node and adjacent nodes */
+
+    nodex=LookupNodeX(nodesx,relationx.via,1);
+    nodex->flags|=NODE_TURNRSTRCT;
+    PutBackNodeX(nodesx,relationx.via,1);
+
+    segmentx=FirstSegmentX(segmentsx,relationx.via,1);
+
+    while(segmentx)
+      {
+       index_t othernode=OtherNode(segmentx,relationx.via);
+
+       nodex=LookupNodeX(nodesx,othernode,1);
+       nodex->flags|=NODE_TURNRSTRCT2;
+       PutBackNodeX(nodesx,othernode,1);
+
+       segmentx=NextSegmentX(segmentsx,segmentx,relationx.via,1);
+      }
+
+   endloop: ;
+   }
+
+ /* Close the files */
+
+ relationsx->trfd=CloseFile(relationsx->trfd);
+ CloseFile(trfd);
+
+ /* Unmap from memory / close the files */
+
+#if !SLIM
+ nodesx->data=UnmapFile(nodesx->filename);
+ segmentsx->data=UnmapFile(segmentsx->filename);
+ waysx->data=UnmapFile(waysx->filename);
+#else
+ nodesx->fd=CloseFile(nodesx->fd);
+ segmentsx->fd=CloseFile(segmentsx->fd);
+ waysx->fd=CloseFile(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);
+
+ relationsx->trnumber=total;
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Update the node indexes after geographical sorting.
+
+  RelationsX *relationsx The set of relations to modify.
+
+  NodesX *nodesx The set of nodes to use.
+
+  SegmentsX *segmentsx The set of segments to use.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void UpdateTurnRelations(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx)
+{
+ int trfd;
+ index_t i;
+
+ /* Print the start message */
+
+ printf_first("Updating Turn Relations: Relations=0");
+
+ /* Map into memory / open the files */
+
+#if !SLIM
+ segmentsx->data=MapFile(segmentsx->filename);
+#else
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
+#endif
+
+ /* Re-open the file read-only and a new file writeable */
+
+ relationsx->trfd=ReOpenFile(relationsx->trfilename);
+
+ DeleteFile(relationsx->trfilename);
+
+ trfd=OpenFileNew(relationsx->trfilename);
+
+ /* Process all of the relations */
+
+ for(i=0;i<relationsx->trnumber;i++)
+   {
+    TurnRestrictRelX relationx;
+    SegmentX *segmentx;
+    index_t from_node,via_node,to_node;
+
+    ReadFile(relationsx->trfd,&relationx,sizeof(TurnRestrictRelX));
+
+    from_node=nodesx->gdata[relationx.from];
+    via_node =nodesx->gdata[relationx.via];
+    to_node  =nodesx->gdata[relationx.to];
+
+    segmentx=FirstSegmentX(segmentsx,via_node,1);
+
+    do
+      {
+       if(OtherNode(segmentx,via_node)==from_node)
+          relationx.from=IndexSegmentX(segmentsx,segmentx);
+
+       if(OtherNode(segmentx,via_node)==to_node)
+          relationx.to=IndexSegmentX(segmentsx,segmentx);
+
+       segmentx=NextSegmentX(segmentsx,segmentx,via_node,1);
+      }
+    while(segmentx);
+
+    relationx.via=via_node;
+
+    WriteFile(trfd,&relationx,sizeof(TurnRestrictRelX));
+
+    if(!(relationsx->trnumber%1000))
+       printf_middle("Updating Turn Relations: Relations=%"Pindex_t,relationsx->trnumber);
+   }
+
+ /* Close the files */
+
+ relationsx->trfd=CloseFile(relationsx->trfd);
+ CloseFile(trfd);
+
+ /* Unmap from memory / close the files */
+
+#if !SLIM
+ segmentsx->data=UnmapFile(segmentsx->filename);
+#else
+ segmentsx->fd=CloseFile(segmentsx->fd);
+#endif
+
+ /* Print the final message */
+
+ printf_last("Updated Turn Relations: Relations=%"Pindex_t,relationsx->trnumber);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Save the relation list to a file.
+
+  RelationsX* relationsx The set of relations to save.
+
+  const char *filename The name of the file to save.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void SaveRelationList(RelationsX* relationsx,const char *filename)
+{
+ index_t i;
+ int fd;
+ RelationsFile relationsfile={0};
+
+ /* Print the start message */
+
+ printf_first("Writing Relations: Turn Relations=0");
+
+ /* Re-open the file read-only */
+
+ relationsx->trfd=ReOpenFile(relationsx->trfilename);
+
+ /* Write out the relations data */
+
+ fd=OpenFileNew(filename);
+
+ SeekFile(fd,sizeof(RelationsFile));
+
+ for(i=0;i<relationsx->trnumber;i++)
+   {
+    TurnRestrictRelX relationx;
+    TurnRelation relation;
+
+    ReadFile(relationsx->trfd,&relationx,sizeof(TurnRestrictRelX));
+
+    relation.from=relationx.from;
+    relation.via=relationx.via;
+    relation.to=relationx.to;
+    relation.except=relationx.except;
+
+    WriteFile(fd,&relation,sizeof(TurnRelation));
+
+    if(!((i+1)%1000))
+       printf_middle("Writing Relations: Turn Relations=%"Pindex_t,i+1);
+   }
+
+ /* Write out the header structure */
+
+ relationsfile.trnumber=relationsx->trnumber;
+
+ SeekFile(fd,0);
+ WriteFile(fd,&relationsfile,sizeof(RelationsFile));
+
+ CloseFile(fd);
+
+ /* Close the file */
+
+ relationsx->trfd=CloseFile(relationsx->trfd);
+
+ /* Print the final message */
+
+ printf_last("Wrote Relations: Turn Relations=%"Pindex_t,relationsx->trnumber);
 }
diff --git a/src/relationsx.h b/src/relationsx.h
index f215c65..2b1a5fc 100644
--- a/src/relationsx.h
+++ b/src/relationsx.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/relationsx.h,v 1.2 2010/09/25 18:47:32 amb Exp $
-
  A header file for the extended Relations structure.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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
@@ -38,9 +36,23 @@
 /*+ An extended structure containing a single route relation. +*/
 struct _RouteRelX
 {
- relation_t id;                 /*+ The relation identifier. +*/
+ relation_t   id;              /*+ The relation identifier. +*/
+
+ transports_t routes;          /*+ The types of transports that that this relation is for. +*/
+};
+
+
+/*+ An extended structure containing a single turn restriction relation. +*/
+struct _TurnRestrictRelX
+{
+ relation_t      id;           /*+ The relation identifier. +*/
 
- allow_t    routes;             /*+ The types of route that this relation belongs to. +*/
+ way_t           from;         /*+ The id of the starting way; initially the OSM value, later the NodeX index then 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. +*/
+
+ TurnRestriction restriction;  /*+ The type of restriction. +*/
+ transports_t    except;       /*+ The types of transports that that this relation does not apply to. +*/
 };
 
 
@@ -52,23 +64,43 @@ struct _RelationsX
  char      *rfilename;         /*+ The name of the temporary file (for the RouteRelX). +*/
  int        rfd;               /*+ The file descriptor of the temporary file (for the RouteRelX). +*/
 
- index_t    rxnumber;          /*+ The number of unsorted extended route relations. +*/
-};
+ index_t    rnumber;           /*+ The number of extended route relations. +*/
 
+ /* Turn restriction relations */
 
-/* Functions */
+ char      *trfilename;        /*+ The name of the temporary file (for the TurnRestrictRelX). +*/
+ int        trfd;              /*+ The file descriptor of the temporary file (for the TurnRestrictRelX). +*/
+
+ index_t    trnumber;          /*+ The number of extended turn restriction relations. +*/
+};
 
 
+/* Functions in relationsx.c */
+
 RelationsX *NewRelationList(int append);
 void FreeRelationList(RelationsX *relationsx,int keep);
 
-void AppendRouteRelation(RelationsX* relationsx,relation_t id,allow_t routes,
+void AppendRouteRelation(RelationsX* relationsx,relation_t id,
+                         transports_t routes,
                          way_t *ways,int nways,
                          relation_t *relations,int nrelations);
 
+void AppendTurnRestrictRelation(RelationsX* relationsx,relation_t id,
+                                way_t from,way_t to,node_t via,
+                                TurnRestriction restriction,transports_t except);
+
 void SortRelationList(RelationsX *relationsx);
 
+void SortTurnRelationList(RelationsX* relationsx);
+
 void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx);
 
+void ProcessTurnRelations1(RelationsX *relationsx,NodesX *nodesx,WaysX *waysx);
+void ProcessTurnRelations2(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx);
+
+void UpdateTurnRelations(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx);
+
+void SaveRelationList(RelationsX* relationsx,const char *filename);
+
 
 #endif /* RELATIONSX_H */
diff --git a/src/results.c b/src/results.c
index aa092de..5fcc7d6 100644
--- a/src/results.c
+++ b/src/results.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/results.c,v 1.22 2010/07/23 14:32:16 amb Exp $
-
  Result data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -25,25 +23,26 @@
 #include <sys/types.h>
 #include <string.h>
 #include <stdlib.h>
+#include <assert.h>
 
 #include "results.h"
 
-/*+ The size of the increment for the Results data structure. +*/
-#define RESULTS_INCREMENT 64
 
+/*+ The maximum number of collisions in a bin for the Results 'point' arrays before worrying. +*/
+#define MAX_COLLISIONS 32
+ 
 
 /*++++++++++++++++++++++++++++++++++++++
   Allocate a new results list.
 
   Results *NewResultsList Returns the results list.
 
-  int nbins The number of bins in the results array.
+  int nbins The initial number of bins in the results array.
   ++++++++++++++++++++++++++++++++++++++*/
 
 Results *NewResultsList(int nbins)
 {
  Results *results;
- uint32_t i;
 
  results=(Results*)malloc(sizeof(Results));
 
@@ -58,45 +57,44 @@ Results *NewResultsList(int nbins)
 
  results->mask=~results->mask;
 
- results->alloced=RESULTS_INCREMENT;
  results->number=0;
 
- results->count=(uint32_t*)malloc(results->nbins*sizeof(uint32_t));
- results->point=(Result***)malloc(results->nbins*sizeof(Result**));
+ results->npoint1=0;
 
- for(i=0;i<results->nbins;i++)
-   {
-    results->count[i]=0;
+ results->count=(uint8_t*)calloc(results->nbins,sizeof(uint8_t));
+ results->point=(Result***)malloc(MAX_COLLISIONS*sizeof(Result**));
 
-    results->point[i]=(Result**)malloc(results->alloced*sizeof(Result*));
-   }
+ results->ndata1=0;
+ results->ndata2=results->nbins;
 
- results->data=(Result**)malloc(1*sizeof(Result*));
- results->data[0]=(Result*)malloc(results->nbins*RESULTS_INCREMENT*sizeof(Result));
+ results->data=NULL;
 
- results->start=NO_NODE;
- results->finish=NO_NODE;
+ results->start_node=NO_NODE;
+ results->prev_segment=NO_SEGMENT;
+
+ results->finish_node=NO_NODE;
+ results->last_segment=NO_SEGMENT;
 
  return(results);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Allocate a new results list.
+  Free a results list.
 
   Results *results The results list to be destroyed.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void FreeResultsList(Results *results)
 {
- int i,c=(results->number-1)/(results->nbins*RESULTS_INCREMENT);
+ int i;
 
- for(i=c;i>=0;i--)
+ for(i=0;i<results->ndata1;i++)
     free(results->data[i]);
 
  free(results->data);
 
- for(i=0;i<results->nbins;i++)
+ for(i=0;i<results->npoint1;i++)
     free(results->point[i]);
 
  free(results->point);
@@ -115,91 +113,164 @@ void FreeResultsList(Results *results)
   Results *results The results structure to insert into.
 
   index_t node The node that is to be inserted into the results.
+
+  index_t segment The segment that is to be inserted into the results.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Result *InsertResult(Results *results,index_t node)
+Result *InsertResult(Results *results,index_t node,index_t segment)
 {
+ Result *result;
  int bin=node&results->mask;
- uint32_t i;
 
- /* Check that the arrays have enough space. */
+ /* 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)
+   {
+    int i,j,k;
+
+    results->nbins<<=1;
+    results->mask=(results->mask<<1)|1;
+
+    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*));
+
+    for(i=0;i<results->nbins/2;i++)
+      {
+       int c=results->count[i];
+
+       results->count[i+results->nbins/2]=0;
+
+       for(j=0,k=0;j<c;j++)
+         {
+          int newbin=results->point[j][i]->node&results->mask;
+
+          if(newbin==i)
+            {
+             if(k!=j)
+                results->point[k][i]=results->point[j][i];
+             k++;
+            }
+          else
+            {
+             results->point[results->count[newbin]][newbin]=results->point[j][i];
+
+             results->count[newbin]++;
+             results->count[i]--;
+            }
+         }
+      }
+
+    bin=node&results->mask;
+   }
+
+ /* Check that the arrays have enough space or allocate more. */
 
- if(results->count[bin]==results->alloced)
+ if(results->count[bin]==results->npoint1)
    {
-    results->alloced+=RESULTS_INCREMENT;
+    assert(results->npoint1<255);
+
+    results->npoint1++;
+
+    if(results->npoint1>MAX_COLLISIONS)
+       results->point=(Result***)realloc((void*)results->point,results->npoint1*sizeof(Result**));
 
-    for(i=0;i<results->nbins;i++)
-       results->point[i]=(Result**)realloc((void*)results->point[i],results->alloced*sizeof(Result*));
+    results->point[results->npoint1-1]=(Result**)malloc(results->nbins*sizeof(Result*));
    }
 
- if(results->number && (results->number%RESULTS_INCREMENT)==0 && (results->number%(RESULTS_INCREMENT*results->nbins))==0)
+ if((results->number%results->ndata2)==0)
    {
-    int c=results->number/(results->nbins*RESULTS_INCREMENT);
+    results->ndata1++;
 
-    results->data=(Result**)realloc((void*)results->data,(c+1)*sizeof(Result*));
-    results->data[c]=(Result*)malloc(results->nbins*RESULTS_INCREMENT*sizeof(Result));
+    results->data=(Result**)realloc((void*)results->data,results->ndata1*sizeof(Result*));
+    results->data[results->ndata1-1]=(Result*)malloc(results->ndata2*sizeof(Result));
    }
 
  /* Insert the new entry */
 
- results->point[bin][results->count[bin]]=&results->data[results->number/(results->nbins*RESULTS_INCREMENT)][results->number%(results->nbins*RESULTS_INCREMENT)];
+ results->point[results->count[bin]][bin]=&results->data[results->ndata1-1][results->number%results->ndata2];
 
  results->number++;
 
  results->count[bin]++;
 
- results->point[bin][results->count[bin]-1]->node=node;
- results->point[bin][results->count[bin]-1]->queued=NOT_QUEUED;
+ /* Initialise the result */
+
+ result=results->point[results->count[bin]-1][bin];
+
+ result->node=node;
+ result->segment=segment;
+
+ result->prev=NULL;
+ result->next=NULL;
+
+ result->score=0;
+ result->sortby=0;
+
+ result->queued=NOT_QUEUED;
 
- return(results->point[bin][results->count[bin]-1]);
+ return(result);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Zero the values in a result structure.
+  Find a result; search by node only (don't care about the segment but find the shortest).
 
-  Result *result The result to modify.
+  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.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void ZeroResult(Result *result)
+Result *FindResult1(Results *results,index_t node)
 {
- result->segment=NO_SEGMENT;
+ int bin=node&results->mask;
+ score_t best_score=INF_SCORE;
+ Result *best_result=NULL;
+ int i;
 
- result->prev=NO_NODE;
- result->next=NO_NODE;
+ 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];
+      }
 
- result->score=0;
- result->sortby=0;
+ return(best_result);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find a result; search by node.
+  Find a result; search by node and segment.
 
   Result *FindResult Returns the result that has been found.
 
   Results *results The results structure to search.
 
   index_t node The node that is to be found.
+
+  index_t segment The segment that was used to reach this node.
   ++++++++++++++++++++++++++++++++++++++*/
 
-Result *FindResult(Results *results,index_t node)
+Result *FindResult(Results *results,index_t node,index_t segment)
 {
  int bin=node&results->mask;
  int i;
 
  for(i=results->count[bin]-1;i>=0;i--)
-    if(results->point[bin][i]->node==node)
-       return(results->point[bin][i]);
+    if(results->point[i][bin]->segment==segment && results->point[i][bin]->node==node)
+       return(results->point[i][bin]);
 
  return(NULL);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find a result from a set of results.
+  Find the first result from a set of results.
 
-  Result *FirstResult Returns the first results from a set of results.
+  Result *FirstResult Returns the first result.
 
   Results *results The set of results.
   ++++++++++++++++++++++++++++++++++++++*/
@@ -211,9 +282,9 @@ Result *FirstResult(Results *results)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find a result from a set of results.
+  Find the next result from a set of results.
 
-  Result *NextResult Returns the next result from a set of results.
+  Result *NextResult Returns the next result.
 
   Results *results The set of results.
 
@@ -222,20 +293,20 @@ Result *FirstResult(Results *results)
 
 Result *NextResult(Results *results,Result *result)
 {
- int i,j=0,c=(results->number-1)/(results->nbins*RESULTS_INCREMENT);
+ int i,j=0;
 
- for(i=0;i<=c;i++)
+ for(i=0;i<results->ndata1;i++)
    {
     j=result-results->data[i];
 
-    if(j>=0 && j<(results->nbins*RESULTS_INCREMENT))
+    if(j>=0 && j<results->ndata2)
        break;
    }
 
- if(++j>=(results->nbins*RESULTS_INCREMENT))
+ if(++j>=results->ndata2)
    {i++;j=0;}
 
- if((i*(results->nbins*RESULTS_INCREMENT)+j)>=results->number)
+ if((i*results->ndata2+j)>=results->number)
     return(NULL);
 
  return(&results->data[i][j]);
diff --git a/src/results.h b/src/results.h
index 871e4f2..3878221 100644
--- a/src/results.h
+++ b/src/results.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/results.h,v 1.18 2010/07/23 14:32:16 amb Exp $
-
  A header file for the results.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,26 +31,27 @@
 /* Constants */
 
 /*+ A result is not currently queued. +*/
-#define NOT_QUEUED (uint32_t)(~0)
+#define NOT_QUEUED (uint32_t)(0)
 
 
 /* Data structures */
 
+typedef struct _Result Result;
+
 /*+ The result for a node. +*/
-typedef struct _Result
+struct _Result
 {
  index_t   node;                /*+ The node for which this result applies. +*/
- index_t   segment;             /*+ The segment for the path to here (from prev). +*/
+ index_t   segment;             /*+ The segmemt used to get to the node for which this result applies. +*/
 
- index_t   prev;                /*+ The previous node following the best path. +*/
- index_t   next;                /*+ The next node following the best path. +*/
+ Result   *prev;                /*+ The previous result following the best path to get to this node via the segment. +*/
+ Result   *next;                /*+ The next result following the best path from this node that was reached via the segment. +*/
 
  score_t   score;               /*+ The best actual weighted distance or duration score from the start to the node. +*/
-
  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;
+};
 
 /*+ A list of results. +*/
 typedef struct _Results
@@ -60,46 +59,51 @@ typedef struct _Results
  uint32_t  nbins;               /*+ The number of bins. +*/
  uint32_t  mask;                /*+ A bit mask to select the bottom 'nbins' bits. +*/
 
- uint32_t  alloced;             /*+ The amount of space allocated for results
-                                    (the length of the number and pointers arrays and
-                                     1/nbins times the amount in the real results). +*/
  uint32_t  number;              /*+ The total number of occupied results. +*/
 
- uint32_t *count;               /*+ An array of nbins counters of results in each array. +*/
+ uint8_t   npoint1;             /*+ The amount of space allocated for results
+                                    (the first dimension of the 'point' array). +*/
+
+ 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  **data;                /*+ An array of arrays containing the actual results
-                                    (don't need to realloc the array of data when adding more,
-                                    only realloc the array that points to the array of data).
+ 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. +*/
+
+ 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
                                     as more space is allocated (since realloc is not being used). +*/
 
- index_t start;                 /*+ The start node. +*/
- index_t finish;                /*+ The finish node. +*/
+ index_t start_node;            /*+ The start node. +*/
+ index_t prev_segment;          /*+ The previous segment to get to the start node (if any). +*/
+
+ index_t finish_node;           /*+ The finish node. +*/
+ index_t last_segment;          /*+ The last segment (to arrive at the finish node). +*/
 }
  Results;
 
 
-/* Forward definitions for opaque type */
+/* Forward definition for opaque type */
 
 typedef struct _Queue Queue;
 
 
-/* Results Functions */
+/* Results functions in results.c */
 
 Results *NewResultsList(int nbins);
 void FreeResultsList(Results *results);
 
-Result *InsertResult(Results *results,index_t node);
-void ZeroResult(Result *result);
+Result *InsertResult(Results *results,index_t node,index_t segment);
 
-Result *FindResult(Results *results,index_t node);
+Result *FindResult1(Results *results,index_t node);
+Result *FindResult(Results *results,index_t node,index_t segment);
 
 Result *FirstResult(Results *results);
 Result *NextResult(Results *results,Result *result);
 
 
-/* Queue Functions */
+/* Queue functions in queue.c */
 
 Queue *NewQueueList(void);
 void FreeQueueList(Queue *queue);
diff --git a/src/router.c b/src/router.c
index 2b58f3b..9d8d0e7 100644
--- a/src/router.c
+++ b/src/router.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/router.c,v 1.90 2010/11/13 14:22:28 amb Exp $
-
  OSM router.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,10 +29,12 @@
 #include "nodes.h"
 #include "segments.h"
 #include "ways.h"
+#include "relations.h"
 
 #include "files.h"
 #include "logging.h"
 #include "functions.h"
+#include "fakes.h"
 #include "translations.h"
 #include "profiles.h"
 
@@ -43,8 +43,7 @@
 #define MAXSEARCH  1
 
 
-/*+ A set of waypoint latitudes and longitudes. +*/
-static double point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
+/* Global variables */
 
 /*+ The option not to print any progress information. +*/
 int option_quiet=0;
@@ -70,8 +69,11 @@ 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;
@@ -79,7 +81,8 @@ int main(int argc,char** argv)
  int       exactnodes=0;
  Transport transport=Transport_None;
  Profile  *profile=NULL;
- index_t   start=NO_NODE,finish=NO_NODE;
+ index_t   start_node=NO_NODE,finish_node=NO_NODE;
+ index_t   join_segment=NO_SEGMENT;
  int       arg,point;
 
  /* Parse the command line arguments */
@@ -176,6 +179,8 @@ int main(int argc,char** argv)
     return(1);
    }
 
+ /* Choose the selected profile. */
+
  if(profilename)
    {
     profile=GetProfile(profilename);
@@ -252,6 +257,17 @@ int main(int argc,char** argv)
        point_lat[point]=degrees_to_radians(atof(p));
        point_used[point]+=2;
       }
+    else if(!strncmp(argv[arg],"--heading=",10))
+      {
+       double h=atof(&argv[arg][10]);
+
+       if(h>=-360 && h<=360)
+         {
+          heading=h;
+
+          if(heading<0) heading+=360;
+         }
+      }
     else if(!strncmp(argv[arg],"--transport=",12))
        ; /* Done this already */
     else if(!strncmp(argv[arg],"--highway-",10))
@@ -310,7 +326,7 @@ int main(int argc,char** argv)
 
        property=PropertyType(string);
 
-       if(property==Way_Count)
+       if(property==Property_Count)
           print_usage(0,argv[arg],NULL);
 
        profile->props_yes[property]=atof(equal+1);
@@ -319,6 +335,8 @@ int main(int argc,char** argv)
       }
     else if(!strncmp(argv[arg],"--oneway=",9))
        profile->oneway=!!atoi(&argv[arg][9]);
+    else if(!strncmp(argv[arg],"--turns=",8))
+       profile->turns=!!atoi(&argv[arg][8]);
     else if(!strncmp(argv[arg],"--weight=",9))
        profile->weight=tonnes_to_weight(atof(&argv[arg][9]));
     else if(!strncmp(argv[arg],"--height=",9))
@@ -335,6 +353,8 @@ int main(int argc,char** argv)
     if(point_used[point]==1 || point_used[point]==2)
        print_usage(0,NULL,"All waypoints must have latitude and longitude.");
 
+ /* Print one of the profiles if requested */
+
  if(help_profile)
    {
     PrintProfile(profile);
@@ -403,6 +423,8 @@ int main(int argc,char** argv)
 
  OSMWays=LoadWayList(FileName(dirname,prefix,"ways.mem"));
 
+ OSMRelations=LoadRelationList(FileName(dirname,prefix,"relations.mem"));
+
  if(UpdateProfile(profile,OSMWays))
    {
     fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n");
@@ -414,21 +436,23 @@ int main(int argc,char** argv)
  for(point=1;point<=NWAYPOINTS;point++)
    {
     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)
        continue;
 
     /* Find the closest point */
 
-    start=finish;
+    start_node=finish_node;
 
     if(exactnodes)
       {
-       finish=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin);
+       finish_node=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin);
       }
     else
       {
@@ -437,12 +461,12 @@ int main(int argc,char** argv)
        segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2);
 
        if(segment!=NO_SEGMENT)
-          finish=CreateFakes(OSMNodes,point,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
+          finish_node=CreateFakes(OSMNodes,OSMSegments,point,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
        else
-          finish=NO_NODE;
+          finish_node=NO_NODE;
       }
 
-    if(finish==NO_NODE)
+    if(finish_node==NO_NODE)
       {
        fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",point);
        return(1);
@@ -452,108 +476,143 @@ int main(int argc,char** argv)
       {
        double lat,lon;
 
-       if(IsFakeNode(finish))
-          GetFakeLatLong(finish,&lat,&lon);
+       if(IsFakeNode(finish_node))
+          GetFakeLatLong(finish_node,&lat,&lon);
        else
-          GetLatLong(OSMNodes,finish,&lat,&lon);
+          GetLatLong(OSMNodes,finish_node,&lat,&lon);
 
-       if(IsFakeNode(finish))
-          printf("Point %d is segment %d (node %d -> %d): %3.6f %4.6f = %2.3f km\n",point,segment,node1,node2,
+       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,
                  radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
        else
-          printf("Point %d is node %d: %3.6f %4.6f = %2.3f km\n",point,finish,
+          printf("Point %d is node %"Pindex_t": %3.6f %4.6f = %2.3f km\n",point,finish_node,
                  radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
       }
 
-    if(start==NO_NODE)
+    if(start_node==NO_NODE)
        continue;
 
-    if(start==finish)
+    if(start_node==finish_node)
        continue;
 
-    /* Calculate the beginning of the route */
+    if(heading!=-999 && join_segment==NO_SEGMENT)
+       join_segment=FindClosestSegmentHeading(OSMNodes,OSMSegments,OSMWays,start_node,heading,profile);
 
-    if(!IsFakeNode(start) && IsSuperNode(OSMNodes,start))
-      {
-       Result *result;
+    /* Calculate the beginning of the route */
 
-       begin=NewResultsList(1);
+    begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,&nsuper);
 
-       begin->start=start;
+    if(!begin && join_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. */
 
-       result=InsertResult(begin,start);
+       join_segment=NO_SEGMENT;
 
-       ZeroResult(result);
+       begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,&nsuper);
       }
-    else
-      {
-       begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,start,profile);
 
-       if(!begin)
-         {
-          fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n");
-          return(1);
-         }
+    if(!begin)
+      {
+       fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n");
+       return(1);
       }
 
-    if(FindResult(begin,finish))
+    finish_result=FindResult1(begin,finish_node);
+
+    if(nsuper || !finish_result)
       {
-       FixForwardRoute(begin,finish);
+       /* The route may include super-nodes but there may also be a route
+          without passing any super-nodes to fall back on */
 
-       results[point]=begin;
+       Results *middle;
+
+       /* Calculate the end of the route */
+
+       end=FindFinishRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,finish_node);
 
-       if(!option_quiet)
+       if(!end)
          {
-          printf("Routed: Super-Nodes Checked = %d\n",begin->number);
-          fflush(stdout);
+          fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n");
+          return(1);
          }
-      }
-    else
-      {
-       Results *superresults;
 
-       /* Calculate the end of the route */
+       /* Calculate the middle of the route */
 
-       if(!IsFakeNode(finish) && IsSuperNode(OSMNodes,finish))
+       middle=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,begin,end);
+
+       if(!middle && join_segment!=NO_SEGMENT && !finish_result)
          {
-          Result *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. */
+
+          FreeResultsList(begin);
 
-          end=NewResultsList(1);
+          begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,NO_SEGMENT,finish_node,&nsuper);
 
-          end->finish=finish;
+          middle=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,begin,end);
+         }
 
-          result=InsertResult(end,finish);
+       FreeResultsList(end);
 
-          ZeroResult(result);
+       if(!middle)
+         {
+          if(!finish_result)
+            {
+             fprintf(stderr,"Error: Cannot find super-route compatible with profile.\n");
+             return(1);
+            }
          }
        else
          {
-          end=FindFinishRoutes(OSMNodes,OSMSegments,OSMWays,finish,profile);
+          results[point]=CombineRoutes(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,begin,middle);
 
-          if(!end)
+          if(!results[point])
             {
-             fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n");
-             return(1);
+             if(!finish_result)
+               {
+                fprintf(stderr,"Error: Cannot find route compatible with profile.\n");
+                return(1);
+               }
             }
-         }
 
-       /* Calculate the middle of the route */
+          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 */
 
-       superresults=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,begin,end,profile);
+             Result *last_result=FindResult(results[point],results[point]->finish_node,results[point]->last_segment);
 
-       FreeResultsList(begin);
-       FreeResultsList(end);
+             if(last_result->score>finish_result->score)
+               {
+                FreeResultsList(results[point]);
+                results[point]=NULL;
+               }
+            }
 
-       if(!superresults)
-         {
-          fprintf(stderr,"Error: Cannot find route compatible with profile.\n");
-          return(1);
+          FreeResultsList(middle);
          }
+      }
+
+    if(finish_result && !results[point])
+      {
+       /* Use the direct route without passing any super-nodes if there was no
+          other route. */
 
-       results[point]=CombineRoutes(superresults,OSMNodes,OSMSegments,OSMWays,profile);
+       FixForwardRoute(begin,finish_result);
 
-       FreeResultsList(superresults);
+       results[point]=begin;
       }
+    else
+       FreeResultsList(begin);
+
+    join_segment=results[point]->last_segment;
+   }
+
+ if(!option_quiet)
+   {
+    printf("Routed OK\n");
+    fflush(stdout);
    }
 
  /* Print out the combined route */
@@ -598,7 +657,7 @@ static void print_usage(int detail,const char *argerr,const char *err)
          "              [--highway-<highway>=<preference> ...]\n"
          "              [--speed-<highway>=<speed> ...]\n"
          "              [--property-<property>=<preference> ...]\n"
-         "              [--oneway=(0|1)]\n"
+         "              [--oneway=(0|1)] [--turns=(0|1)]\n"
          "              [--weight=<weight>]\n"
          "              [--height=<height>] [--width=<width>] [--length=<length>]\n");
 
@@ -656,11 +715,14 @@ 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"
+            "--heading=<bearing>     Initial compass bearing at lowest numbered waypoint.\n"
+            "\n"
             "                                   Routing preference options\n"
             "--highway-<highway>=<preference>   * preference for highway type (%%).\n"
             "--speed-<highway>=<speed>          * speed for highway type (km/h).\n"
             "--property-<property>=<preference> * preference for proprty type (%%).\n"
-            "--oneway=(0|1)                     * oneway streets are to be obeyed.\n"
+            "--oneway=(0|1)                     * oneway restrictions are to be obeyed.\n"
+            "--turns=(0|1)                      * turn restrictions are to be obeyed.\n"
             "--weight=<weight>                  * maximum weight limit (tonnes).\n"
             "--height=<height>                  * maximum height limit (metres).\n"
             "--width=<width>                    * maximum width limit (metres).\n"
diff --git a/src/segments.c b/src/segments.c
index 4b9f274..18a7970 100644
--- a/src/segments.c
+++ b/src/segments.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/segments.c,v 1.47 2010/07/23 14:35:27 amb Exp $
-
  Segment data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,11 +22,13 @@
 
 #include <sys/types.h>
 #include <stdlib.h>
+#include <math.h>
 
 #include "types.h"
 #include "nodes.h"
 #include "segments.h"
 #include "ways.h"
+#include "fakes.h"
 
 #include "files.h"
 #include "profiles.h"
@@ -37,7 +37,7 @@
 /*++++++++++++++++++++++++++++++++++++++
   Load in a segment list from a file.
 
-  Segments* LoadSegmentList Returns the segment list that has just been loaded.
+  Segments *LoadSegmentList Returns the segment list that has just been loaded.
 
   const char *filename The name of the file to load.
   ++++++++++++++++++++++++++++++++++++++*/
@@ -45,6 +45,9 @@
 Segments *LoadSegmentList(const char *filename)
 {
  Segments *segments;
+#if SLIM
+ int i;
+#endif
 
  segments=(Segments*)malloc(sizeof(Segments));
 
@@ -68,9 +71,8 @@ Segments *LoadSegmentList(const char *filename)
 
  ReadFile(segments->fd,&segments->file,sizeof(SegmentsFile));
 
- segments->incache[0]=NO_SEGMENT;
- segments->incache[1]=NO_SEGMENT;
- segments->incache[2]=NO_SEGMENT;
+ for(i=0;i<sizeof(segments->cached)/sizeof(segments->cached[0]);i++)
+    segments->incache[i]=NO_SEGMENT;
 
 #endif
 
@@ -79,50 +81,87 @@ Segments *LoadSegmentList(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find the next segment with a particular starting node.
+  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.
+
+  Nodes *nodes The set of nodes to use.
 
-  Segment *NextSegment Returns a pointer to the next segment with the same id.
+  Segments *segments The set of segments to use.
 
-  Segments* segments The set of segments to process.
+  Ways *ways The set of ways to use.
 
-  Segment *segment The current segment.
+  index_t node1 The node to start from.
 
-  index_t node The current node.
+  double heading The desired heading from the node.
+
+  Profile *profile The profile of the mode of transport (or NULL).
   ++++++++++++++++++++++++++++++++++++++*/
 
-Segment *NextSegment(Segments* segments,Segment *segment,index_t node)
+index_t FindClosestSegmentHeading(Nodes *nodes,Segments *segments,Ways *ways,index_t node1,double heading,Profile *profile)
 {
- if(segment->node1==node)
-   {
-#if SLIM
-    index_t index=IndexSegment(segments,segment);
-    index++;
-
-    if(index>=segments->file.number)
-       return(NULL);
-    segment=LookupSegment(segments,index,1);
-    if(segment->node1!=node)
-       return(NULL);
-    else
-       return(segment);
-#else
-    segment++;
-    if(IndexSegment(segments,segment)>=segments->file.number || segment->node1!=node)
-       return(NULL);
-    else
-       return(segment);
-#endif
-   }
+ Segment *segment;
+ index_t best_seg=NO_SEGMENT;
+ double best_difference=360;
+
+ if(IsFakeNode(node1))
+    segment=FirstFakeSegment(node1);
  else
+    segment=FirstSegment(segments,nodes,node1,1);
+
+ while(segment)
    {
-    if(segment->next2==NO_NODE)
-       return(NULL);
+    Way *way;
+    index_t node2,seg2;
+    double bearing,difference;
+
+    node2=OtherNode(segment,node1);  /* need this here because we use node2 at the end of the loop */
+
+    if(!IsNormalSegment(segment))
+       goto endloop;
+
+    if(profile->oneway && IsOnewayFrom(segment,node1))
+       goto endloop;
+
+    if(IsFakeNode(node1) || IsFakeNode(node2))
+       seg2=IndexFakeSegment(segment);
+    else
+       seg2=IndexSegment(segments,segment);
+
+    way=LookupWay(ways,segment->way,1);
+
+    if(!(way->allow&profile->allow))
+       goto endloop;
+
+    bearing=BearingAngle(nodes,segment,node1);
+
+    difference=(heading-bearing);
+
+    if(difference<-180) difference+=360;
+    if(difference> 180) difference-=360;
+
+    if(difference<0) difference=-difference;
+
+    if(difference<best_difference)
+      {
+       best_difference=difference;
+       best_seg=seg2;
+      }
+
+   endloop:
+
+    if(IsFakeNode(node1))
+       segment=NextFakeSegment(segment,node1);
+    else if(IsFakeNode(node2))
+       segment=NULL; /* cannot call NextSegment() with a fake segment */
     else
-       return(LookupSegment(segments,segment->next2,1));
+       segment=NextSegment(segments,segment,node1);
    }
+
+ return(best_seg);
 }
- 
- 
+
+
 /*++++++++++++++++++++++++++++++++++++++
   Calculate the distance between two locations.
 
@@ -162,9 +201,9 @@ distance_t Distance(double lat1,double lon1,double lat2,double lon2)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Calculate the duration of segment.
+  Calculate the duration of travel on a segment.
 
-  duration_t Duration Returns the duration of travel between the nodes.
+  duration_t Duration Returns the duration of travel.
 
   Segment *segment The segment to traverse.
 
@@ -196,3 +235,106 @@ duration_t Duration(Segment *segment,Way *way,Profile *profile)
        return distance_speed_to_duration(distance,speed2);
    }
 }
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Calculate the angle to turn at a junction from segment1 to segment2 at node.
+
+  double TurnAngle Returns a value in the range -180 to +180 indicating the angle to turn.
+
+  Nodes *nodes The set of nodes to use.
+
+  Segment *segment1 The current segment.
+
+  Segment *segment2 The next segment.
+
+  index_t node The node at which they join.
+
+  Straight ahead is zero, turning to the right is positive (e.g. +90 degrees) and turning to the left is negative (e.g. -90 degrees).
+  Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+double TurnAngle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node)
+{
+ double lat1,latm,lat2;
+ double lon1,lonm,lon2;
+ double angle1,angle2,angle;
+ index_t node1,node2;
+
+ node1=OtherNode(segment1,node);
+ node2=OtherNode(segment2,node);
+
+ if(IsFakeNode(node1))
+    GetFakeLatLong(node1,&lat1,&lon1);
+ else
+    GetLatLong(nodes,node1,&lat1,&lon1);
+
+ if(IsFakeNode(node))
+    GetFakeLatLong(node,&latm,&lonm);
+ else
+    GetLatLong(nodes,node,&latm,&lonm);
+
+ if(IsFakeNode(node2))
+    GetFakeLatLong(node2,&lat2,&lon2);
+ else
+    GetLatLong(nodes,node2,&lat2,&lon2);
+
+ angle1=atan2((lonm-lon1)*cos(latm),(latm-lat1));
+ angle2=atan2((lon2-lonm)*cos(latm),(lat2-latm));
+
+ angle=angle2-angle1;
+
+ angle=radians_to_degrees(angle);
+
+ if(angle<-180) angle+=360;
+ if(angle> 180) angle-=360;
+
+ return(angle);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Calculate the bearing of a segment when heading to the given node.
+
+  double BearingAngle Returns a value in the range 0 to 359 indicating the bearing.
+
+  Nodes *nodes The set of nodes to use.
+
+  Segment *segment The segment.
+
+  index_t node The node to finish.
+
+  Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude).
+  ++++++++++++++++++++++++++++++++++++++*/
+
+double BearingAngle(Nodes *nodes,Segment *segment,index_t node)
+{
+ double lat1,lat2;
+ double lon1,lon2;
+ double angle;
+ index_t node1,node2;
+
+ node1=node;
+ node2=OtherNode(segment,node);
+
+ if(IsFakeNode(node1))
+    GetFakeLatLong(node1,&lat1,&lon1);
+ else
+    GetLatLong(nodes,node1,&lat1,&lon1);
+
+ if(IsFakeNode(node2))
+    GetFakeLatLong(node2,&lat2,&lon2);
+ else
+    GetLatLong(nodes,node2,&lat2,&lon2);
+
+ angle=atan2((lat2-lat1),(lon2-lon1)*cos(lat1));
+
+ angle=radians_to_degrees(angle);
+
+ angle=270-angle;
+
+ if(angle<  0) angle+=360;
+ if(angle>360) angle-=360;
+
+ return(angle);
+}
diff --git a/src/segments.h b/src/segments.h
index 464d6a3..5c22bb4 100644
--- a/src/segments.h
+++ b/src/segments.h
@@ -1,11 +1,11 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/segments.h,v 1.37 2010/07/31 14:36:15 amb Exp $
+ $Header: /home/amb/CVS/routino/src/segments.h,v 1.38 2010-12-21 17:18:41 amb Exp $
 
  A header file for the segments.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -53,9 +53,9 @@ struct _Segment
 /*+ A structure containing the header from the file. +*/
 typedef struct _SegmentsFile
 {
- index_t   number;              /*+ How many segments in total? +*/
- index_t   snumber;             /*+ How many super-segments? +*/
- index_t   nnumber;             /*+ How many normal segments? +*/
+ index_t   number;              /*+ The number of segments in total. +*/
+ index_t   snumber;             /*+ The number of super-segments. +*/
+ index_t   nnumber;             /*+ The number of normal segments. +*/
 }
  SegmentsFile;
 
@@ -75,23 +75,29 @@ struct _Segments
 
  int          fd;               /*+ The file descriptor for the file. +*/
 
- Segment      cached[3];        /*+ The cached segments. +*/
+ Segment      cached[3];        /*+ Three cached segments read from the file in slim mode. +*/
  index_t      incache[3];       /*+ The indexes of the cached segments. +*/
 
 #endif
 };
 
 
-/* Functions */
+/* Functions in segments.c */
 
 Segments *LoadSegmentList(const char *filename);
 
-Segment *NextSegment(Segments* segments,Segment *segment,index_t node);
+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);
 
 duration_t Duration(Segment *segment,Way *way,Profile *profile);
 
+double TurnAngle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node);
+double BearingAngle(Nodes *nodes,Segment *segment,index_t node);
+
+
+static inline Segment *NextSegment(Segments *segments,Segment *segment,index_t node);
+
 
 /* Macros and inline functions */
 
@@ -110,14 +116,46 @@ duration_t Duration(Segment *segment,Way *way,Profile *profile);
 /*+ 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. +*/
-#define LookupSegment(xxx,yyy,zzz) (&(xxx)->segments[yyy])
+#define LookupSegment(xxx,yyy,ppp) (&(xxx)->segments[yyy])
 
 /*+ Return a segment index given a set of segments and a pointer. +*/
-#define IndexSegment(xxx,yyy)      ((yyy)-&(xxx)->segments[0])
+#define IndexSegment(xxx,yyy)      (index_t)((yyy)-&(xxx)->segments[0])
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the next segment with a particular starting node.
+
+  Segment *NextSegment Returns a pointer to the next segment.
+
+  Segments *segments The set of segments to use.
+
+  Segment *segment The current segment.
+
+  index_t node The wanted node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline Segment *NextSegment(Segments *segments,Segment *segment,index_t node)
+{
+ if(segment->node1==node)
+   {
+    segment++;
+
+    if(IndexSegment(segments,segment)>=segments->file.number || segment->node1!=node)
+       return(NULL);
+    else
+       return(segment);
+   }
+ else
+   {
+    if(segment->next2==NO_SEGMENT)
+       return(NULL);
+    else
+       return(LookupSegment(segments,segment->next2,1));
+   }
+}
 
 #else
 
@@ -131,7 +169,7 @@ static index_t IndexSegment(Segments *segments,Segment *segment);
 
   Segment *LookupSegment Returns a pointer to the cached segment information.
 
-  Segments *segments The segments structure to use.
+  Segments *segments The set of segments to use.
 
   index_t index The index of the segment.
 
@@ -140,11 +178,14 @@ static index_t IndexSegment(Segments *segments,Segment *segment);
 
 static inline Segment *LookupSegment(Segments *segments,index_t index,int position)
 {
- SeekFile(segments->fd,sizeof(SegmentsFile)+(off_t)index*sizeof(Segment));
+ if(segments->incache[position-1]!=index)
+   {
+    SeekFile(segments->fd,sizeof(SegmentsFile)+(off_t)index*sizeof(Segment));
 
- ReadFile(segments->fd,&segments->cached[position-1],sizeof(Segment));
+    ReadFile(segments->fd,&segments->cached[position-1],sizeof(Segment));
 
- segments->incache[position-1]=index;
+    segments->incache[position-1]=index;
+   }
 
  return(&segments->cached[position-1]);
 }
@@ -155,20 +196,58 @@ static inline Segment *LookupSegment(Segments *segments,index_t index,int positi
 
   index_t IndexSegment Returns the index of the segment in the list.
 
-  Segments *segments The segments structure to use.
+  Segments *segments The set of segments to use.
 
   Segment *segment The segment whose index is to be found.
   ++++++++++++++++++++++++++++++++++++++*/
 
 static inline index_t IndexSegment(Segments *segments,Segment *segment)
 {
- int i;
+ int position1=segment-&segments->cached[0];
+
+ return(segments->incache[position1]);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Find the next segment with a particular starting node.
+
+  Segment *NextSegment Returns a pointer to the next segment.
+
+  Segments *segments The set of segments to use.
 
- for(i=0;i<sizeof(segments->cached)/sizeof(segments->cached[0]);i++)
-    if(&segments->cached[i]==segment)
-       return(segments->incache[i]);
+  Segment *segment The current segment.
 
- return(NO_SEGMENT);
+  index_t node The wanted node.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static inline Segment *NextSegment(Segments *segments,Segment *segment,index_t node)
+{
+ int position=segment-&segments->cached[-1];
+
+ if(segment->node1==node)
+   {
+    index_t index=IndexSegment(segments,segment);
+
+    index++;
+
+    if(index>=segments->file.number)
+       return(NULL);
+
+    segment=LookupSegment(segments,index,position);
+
+    if(segment->node1!=node)
+       return(NULL);
+    else
+       return(segment);
+   }
+ else
+   {
+    if(segment->next2==NO_SEGMENT)
+       return(NULL);
+    else
+       return(LookupSegment(segments,segment->next2,position));
+   }
 }
 
 #endif
diff --git a/src/segmentsx.c b/src/segmentsx.c
index c831531..b17b62b 100644
--- a/src/segmentsx.c
+++ b/src/segmentsx.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/segmentsx.c,v 1.69 2010/11/13 14:22:28 amb Exp $
-
  Extended Segment data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,15 +40,15 @@
 
 #include "files.h"
 #include "logging.h"
-#include "functions.h"
+#include "sorting.h"
 
 
-/* Variables */
+/* Global variables */
 
 /*+ The command line '--tmpdir' option or its default value. +*/
 extern char *option_tmpdirname;
 
-/* Local Functions */
+/* Local functions */
 
 static int sort_by_id(SegmentX *a,SegmentX *b);
 
@@ -78,13 +76,7 @@ SegmentsX *NewSegmentList(int append)
  if(append)
     sprintf(segmentsx->filename,"%s/segmentsx.input.tmp",option_tmpdirname);
  else
-    sprintf(segmentsx->filename,"%s/segmentsx.%p.tmp",option_tmpdirname,segmentsx);
-
-#if SLIM
- segmentsx->sfilename=(char*)malloc(strlen(option_tmpdirname)+32);
-
- sprintf(segmentsx->sfilename,"%s/segments.%p.tmp",option_tmpdirname,segmentsx);
-#endif
+    sprintf(segmentsx->filename,"%s/segmentsx.%p.tmp",option_tmpdirname,(void*)segmentsx);
 
  if(append)
    {
@@ -94,7 +86,7 @@ SegmentsX *NewSegmentList(int append)
 
     size=SizeFile(segmentsx->filename);
 
-    segmentsx->xnumber=size/sizeof(SegmentX);
+    segmentsx->number=size/sizeof(SegmentX);
    }
  else
     segmentsx->fd=OpenFileNew(segmentsx->filename);
@@ -106,9 +98,9 @@ SegmentsX *NewSegmentList(int append)
 /*++++++++++++++++++++++++++++++++++++++
   Free a segment list.
 
-  SegmentsX *segmentsx The list to be freed.
+  SegmentsX *segmentsx The set of segments to be freed.
 
-  int keep Set to 1 if the file is to be kept.
+  int keep Set to 1 if the file is to be kept (for appending later).
   ++++++++++++++++++++++++++++++++++++++*/
 
 void FreeSegmentList(SegmentsX *segmentsx,int keep)
@@ -118,23 +110,12 @@ void FreeSegmentList(SegmentsX *segmentsx,int keep)
 
  free(segmentsx->filename);
 
- if(segmentsx->idata)
-    free(segmentsx->idata);
+ if(segmentsx->usednode)
+    free(segmentsx->usednode);
 
  if(segmentsx->firstnode)
     free(segmentsx->firstnode);
 
-#if !SLIM
- if(segmentsx->sdata)
-    free(segmentsx->sdata);
-#endif
-
-#if SLIM
- DeleteFile(segmentsx->sfilename);
-
- free(segmentsx->sfilename);
-#endif
-
  free(segmentsx);
 }
 
@@ -142,7 +123,7 @@ void FreeSegmentList(SegmentsX *segmentsx,int keep)
 /*++++++++++++++++++++++++++++++++++++++
   Append a single segment to an unsorted segment list.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to modify.
 
   way_t way The way that the segment belongs to.
 
@@ -153,43 +134,57 @@ void FreeSegmentList(SegmentsX *segmentsx,int keep)
   distance_t distance The distance between the nodes (or just the flags).
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendSegment(SegmentsX* segmentsx,way_t way,node_t node1,node_t node2,distance_t distance)
+void AppendSegment(SegmentsX *segmentsx,way_t way,node_t node1,node_t node2,distance_t distance)
 {
  SegmentX segmentx;
 
+ if(node1>node2)
+   {
+    node_t temp;
+
+    temp=node1;
+    node1=node2;
+    node2=temp;
+
+    if(distance&(ONEWAY_2TO1|ONEWAY_1TO2))
+       distance^=ONEWAY_2TO1|ONEWAY_1TO2;
+   }
+
  segmentx.node1=node1;
  segmentx.node2=node2;
+ segmentx.next2=NO_SEGMENT;
  segmentx.way=way;
  segmentx.distance=distance;
 
  WriteFile(segmentsx->fd,&segmentx,sizeof(SegmentX));
 
- segmentsx->xnumber++;
+ segmentsx->number++;
 
- assert(segmentsx->xnumber<SEGMENT_FAKE); /* SEGMENT_FAKE marks the high-water mark for real segments. */
+ assert(segmentsx->number<SEGMENT_FAKE); /* SEGMENT_FAKE marks the high-water mark for real segments. */
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Sort the segment list.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to modify.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void SortSegmentList(SegmentsX* segmentsx)
+void SortSegmentList(SegmentsX *segmentsx)
 {
  int fd;
 
- if(segmentsx->xnumber==0)
-    return;
-
  /* Print the start message */
 
  printf_first("Sorting Segments");
 
- /* Close the files and re-open them (finished appending) */
+ /* Close the file (finished appending) */
+
+ if(segmentsx->fd!=-1)
+    segmentsx->fd=CloseFile(segmentsx->fd);
+
+ /* Re-open the file read-only and a new file writeable */
 
- CloseFile(segmentsx->fd);
  segmentsx->fd=ReOpenFile(segmentsx->filename);
 
  DeleteFile(segmentsx->filename);
@@ -200,23 +195,19 @@ void SortSegmentList(SegmentsX* segmentsx)
 
  filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),(int (*)(const void*,const void*))sort_by_id,NULL);
 
- segmentsx->number=segmentsx->xnumber;
-
- /* Close the files and re-open them */
+ /* Close the files */
 
- CloseFile(segmentsx->fd);
+ segmentsx->fd=CloseFile(segmentsx->fd);
  CloseFile(fd);
 
- segmentsx->fd=ReOpenFile(segmentsx->filename);
-
  /* Print the final message */
 
- printf_last("Sorted Segments: Segments=%d",segmentsx->xnumber);
+ printf_last("Sorted Segments: Segments=%"Pindex_t,segmentsx->number);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Sort the segments into id order (node1 then node2).
+  Sort the segments into id order, first by node1 then by node2, finally by distance.
 
   int sort_by_id Returns the comparison of the node fields.
 
@@ -260,151 +251,153 @@ static int sort_by_id(SegmentX *a,SegmentX *b)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find the first segment index with a particular starting node.
+  Find the first extended segment with a particular starting node index.
  
-  index_t IndexFirstSegmentX Returns a pointer to the index of the first extended segment with the specified id.
+  SegmentX *FirstSegmentX Returns a pointer to the first extended segment with the specified id.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to use.
 
-  node_t node The node to look for.
+  index_t nodeindex The node index to look for.
+
+  int position A flag to pass through.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t IndexFirstSegmentX(SegmentsX* segmentsx,node_t node)
+SegmentX *FirstSegmentX(SegmentsX *segmentsx,index_t nodeindex,int position)
 {
- int start=0;
- int end=segmentsx->number-1;
- int mid;
- int found;
-
- /* Check if the first node index exists */
-
- if(segmentsx->firstnode)
-   {
-    index_t index=segmentsx->firstnode[node];
-
-    if(segmentsx->firstnode[node+1]==index)
-       return(NO_SEGMENT);
-
-    return(index);
-   }
-
- /* 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.
-  */
-
- if(end<start)                         /* There are no nodes */
-    return(NO_SEGMENT);
- else if(node<segmentsx->idata[start]) /* Check key is not before start */
-    return(NO_SEGMENT);
- else if(node>segmentsx->idata[end])   /* Check key is not after end */
-    return(NO_SEGMENT);
- else
-   {
-    do
-      {
-       mid=(start+end)/2;                  /* Choose mid point */
-
-       if(segmentsx->idata[mid]<node)      /* Mid point is too low */
-          start=mid;
-       else if(segmentsx->idata[mid]>node) /* Mid point is too high */
-          end=mid;
-       else                                /* Mid point is correct */
-         {found=mid; goto found;}
-      }
-    while((end-start)>1);
-
-    if(segmentsx->idata[start]==node)      /* Start is correct */
-      {found=start; goto found;}
-
-    if(segmentsx->idata[end]==node)        /* End is correct */
-      {found=end; goto found;}
-   }
+ index_t index=segmentsx->firstnode[nodeindex];
+ SegmentX *segmentx;
 
- return(NO_SEGMENT);
+ if(index==NO_SEGMENT)
+    return(NULL);
 
- found:
+ segmentx=LookupSegmentX(segmentsx,index,position);
 
- while(found>0 && segmentsx->idata[found-1]==node)
-    found--;
+ if(segmentx->node1!=nodeindex && segmentx->node2!=nodeindex)
+    return(NULL);
 
- return(found);
+ return(segmentx);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find the next segment index with a particular starting node.
+  Find the next segment with a particular starting node index.
 
-  index_t IndexNextSegmentX Returns the index of the next segment with the same id.
+  SegmentX *NextSegmentX Returns a pointer to the next segment with the same id.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to use.
 
-  index_t segindex The current segment index.
+  SegmentX *segmentx The current segment.
 
   index_t nodeindex The node index.
+
+  int position A flag to pass through.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t IndexNextSegmentX(SegmentsX* segmentsx,index_t segindex,index_t nodeindex)
+SegmentX *NextSegmentX(SegmentsX *segmentsx,SegmentX *segmentx,index_t nodeindex,int position)
 {
- if(++segindex==segmentsx->firstnode[nodeindex+1])
-    return(NO_SEGMENT);
+ if(segmentx->node1==nodeindex)
+   {
+#if SLIM
+    index_t index=IndexSegmentX(segmentsx,segmentx);
+    index++;
+
+    if(index>=segmentsx->number)
+       return(NULL);
+    segmentx=LookupSegmentX(segmentsx,index,position);
+    if(segmentx->node1!=nodeindex)
+       return(NULL);
+    else
+       return(segmentx);
+#else
+    segmentx++;
+    if(IndexSegmentX(segmentsx,segmentx)>=segmentsx->number || segmentx->node1!=nodeindex)
+       return(NULL);
+    else
+       return(segmentx);
+#endif
+   }
  else
-    return(segindex);
+   {
+    if(segmentx->next2==NO_SEGMENT)
+       return(NULL);
+    else
+       return(LookupSegmentX(segmentsx,segmentx->next2,position));
+   }
 }
  
  
 /*++++++++++++++++++++++++++++++++++++++
-  Remove bad segments (duplicated, zero length or missing nodes).
+  Remove bad segments (duplicated, zero length or with missing nodes).
 
-  NodesX *nodesx The nodes to check.
+  NodesX *nodesx The set of nodes to use.
 
-  SegmentsX *segmentsx The segments to modify.
+  SegmentsX *segmentsx The set of segments to modify.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void RemoveBadSegments(NodesX *nodesx,SegmentsX *segmentsx)
 {
- int duplicate=0,loop=0,missing=0,good=0,total=0;
+ index_t duplicate=0,loop=0,nonode=0,good=0,total=0;
  SegmentX segmentx;
  int fd;
- node_t prevnode1=NO_NODE,prevnode2=NO_NODE;
+ node_t prevnode1=NO_NODE_ID,prevnode2=NO_NODE_ID;
 
  /* Print the start message */
 
- printf_first("Checking: Segments=0 Duplicate=0 Loop=0 Missing-Node=0");
+ printf_first("Checking Segments: Segments=0 Duplicate=0 Loop=0 No-Node=0");
 
- /* Allocate the array of indexes */
+ /* Allocate the array of node flags */
 
- segmentsx->idata=(node_t*)malloc(segmentsx->xnumber*sizeof(node_t));
+ segmentsx->usednode=(char*)calloc((1+nodesx->number/8),sizeof(char));
 
- assert(segmentsx->idata); /* Check malloc() worked */
+ assert(segmentsx->usednode); /* Check malloc() worked */
 
- /* Modify the on-disk image */
+ /* Re-open the file read-only and a new file writeable */
+
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
 
  DeleteFile(segmentsx->filename);
 
  fd=OpenFileNew(segmentsx->filename);
- SeekFile(segmentsx->fd,0);
+
+ /* Modify the on-disk image */
 
  while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
    {
+    index_t index1=IndexNodeX(nodesx,segmentx.node1);
+    index_t index2=IndexNodeX(nodesx,segmentx.node2);
+
     if(prevnode1==segmentx.node1 && prevnode2==segmentx.node2)
+      {
+       logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated.\n",segmentx.node1,segmentx.node2);
+
        duplicate++;
+      }
     else if(segmentx.node1==segmentx.node2)
+      {
+       logerror("Segment connects node %"Pnode_t" to itself.\n",segmentx.node1);
+
        loop++;
-    else if(IndexNodeX(nodesx,segmentx.node1)==NO_NODE ||
-            IndexNodeX(nodesx,segmentx.node2)==NO_NODE)
-       missing++;
+      }
+    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);
+
+       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);
+
+       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);
+
+       nonode++;
+      }
     else
       {
        WriteFile(fd,&segmentx,sizeof(SegmentX));
 
-       segmentsx->idata[good]=segmentx.node1;
+       SetBit(segmentsx->usednode,index1);
+       SetBit(segmentsx->usednode,index2);
+
        good++;
 
        prevnode1=segmentx.node1;
@@ -414,72 +407,59 @@ void RemoveBadSegments(NodesX *nodesx,SegmentsX *segmentsx)
     total++;
 
     if(!(total%10000))
-       printf_middle("Checking: Segments=%d Duplicate=%d Loop=%d Missing-Node=%d",total,duplicate,loop,missing);
+       printf_middle("Checking Segments: Segments=%"Pindex_t" Duplicate=%"Pindex_t" Loop=%"Pindex_t" No-Node=%"Pindex_t,total,duplicate,loop,nonode);
    }
 
- /* Close the files and re-open them */
-
- CloseFile(segmentsx->fd);
- CloseFile(fd);
+ segmentsx->number=good;
 
- segmentsx->fd=ReOpenFile(segmentsx->filename);
+ /* Close the files */
 
- segmentsx->number=good;
+ segmentsx->fd=CloseFile(segmentsx->fd);
+ CloseFile(fd);
 
  /* Print the final message */
 
- printf_last("Checked: Segments=%d Duplicate=%d Loop=%d Missing-Node=%d",total,duplicate,loop,missing);
+ printf_last("Checked Segments: Segments=%"Pindex_t" Duplicate=%"Pindex_t" Loop=%"Pindex_t" No-Node=%"Pindex_t,total,duplicate,loop,nonode);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Measure the segments and replace node/way ids with indexes.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to process.
 
-  NodesX *nodesx The list of nodes to use.
+  NodesX *nodesx The set of nodes to use.
 
-  WaysX *waysx The list of ways to use.
+  WaysX *waysx The set of ways to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void UpdateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx)
+void MeasureSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 {
  index_t index=0;
- int i,fd;
+ int fd;
  SegmentX segmentx;
 
  /* Print the start message */
 
  printf_first("Measuring Segments: Segments=0");
 
- /* Map into memory */
+ /* Map into memory /  open the file */
 
 #if !SLIM
- nodesx->xdata=MapFile(nodesx->filename);
+ nodesx->data=MapFile(nodesx->filename);
+#else
+ nodesx->fd=ReOpenFile(nodesx->filename);
 #endif
 
- /* Free the now-unneeded index */
-
- free(segmentsx->idata);
- segmentsx->idata=NULL;
-
- /* Allocate the array of indexes */
-
- segmentsx->firstnode=(index_t*)malloc((nodesx->number+1)*sizeof(index_t));
+ /* Re-open the file read-only and a new file writeable */
 
- assert(segmentsx->firstnode); /* Check malloc() worked */
-
- for(i=0;i<nodesx->number;i++)
-    segmentsx->firstnode[i]=NO_SEGMENT;
-
- segmentsx->firstnode[nodesx->number]=segmentsx->number;
-
- /* Modify the on-disk image */
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
 
  DeleteFile(segmentsx->filename);
 
  fd=OpenFileNew(segmentsx->filename);
- SeekFile(segmentsx->fd,0);
+
+ /* Modify the on-disk image */
 
  while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
    {
@@ -500,11 +480,6 @@ void UpdateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx)
 
     segmentx.distance|=DISTANCE(DistanceX(nodex1,nodex2));
 
-    /* Set the first segment index in the nodes */
-
-    if(index<segmentsx->firstnode[node1])
-       segmentsx->firstnode[node1]=index;
-
     /* Write the modified segment */
 
     WriteFile(fd,&segmentx,sizeof(SegmentX));
@@ -512,16 +487,14 @@ void UpdateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx)
     index++;
 
     if(!(index%10000))
-       printf_middle("Measuring Segments: Segments=%d",index);
+       printf_middle("Measuring Segments: Segments=%"Pindex_t,index);
    }
 
- /* Close the files and re-open them */
+ /* Close the files */
 
- CloseFile(segmentsx->fd);
+ segmentsx->fd=CloseFile(segmentsx->fd);
  CloseFile(fd);
 
- segmentsx->fd=ReOpenFile(segmentsx->filename);
-
  /* Free the other now-unneeded indexes */
 
  free(nodesx->idata);
@@ -530,394 +503,326 @@ void UpdateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx)
  free(waysx->idata);
  waysx->idata=NULL;
 
- /* Unmap from memory */
+ /* Unmap from memory / close the file */
 
 #if !SLIM
- nodesx->xdata=UnmapFile(nodesx->filename);
+ nodesx->data=UnmapFile(nodesx->filename);
+#else
+ nodesx->fd=CloseFile(nodesx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Measured Segments: Segments=%d",segmentsx->number);
-}
-
-
-/*++++++++++++++++++++++++++++++++++++++
-  Make the segments all point the same way (node1<node2).
-
-  SegmentsX* segmentsx The set of segments to process.
-  ++++++++++++++++++++++++++++++++++++++*/
-
-void RotateSegments(SegmentsX* segmentsx)
-{
- int index=0,rotated=0;
- int fd;
- SegmentX segmentx;
-
- /* Print the start message */
-
- printf_first("Rotating Segments: Segments=0 Rotated=0");
-
- /* Close the files and re-open them (finished appending) */
-
- CloseFile(segmentsx->fd);
- segmentsx->fd=ReOpenFile(segmentsx->filename);
-
- DeleteFile(segmentsx->filename);
-
- fd=OpenFileNew(segmentsx->filename);
-
- /* Modify the file contents */
-
- while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
-   {
-    if(segmentx.node1>segmentx.node2)
-      {
-       node_t temp;
-
-       temp=segmentx.node1;
-       segmentx.node1=segmentx.node2;
-       segmentx.node2=temp;
-
-       if(segmentx.distance&(ONEWAY_2TO1|ONEWAY_1TO2))
-          segmentx.distance^=ONEWAY_2TO1|ONEWAY_1TO2;
-
-       rotated++;
-      }
-
-    WriteFile(fd,&segmentx,sizeof(SegmentX));
-
-    index++;
-
-    if(!(index%10000))
-       printf_middle("Rotating Segments: Segments=%d Rotated=%d",index,rotated);
-   }
-
- /* Close the files and re-open them */
-
- CloseFile(segmentsx->fd);
- CloseFile(fd);
-
- segmentsx->fd=ReOpenFile(segmentsx->filename);
-
- /* Print the final message */
-
- printf_last("Rotated Segments: Segments=%d Rotated=%d",index,rotated);
+ printf_last("Measured Segments: Segments=%"Pindex_t,segmentsx->number);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Remove the duplicate segments.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to modify.
 
-  NodesX *nodesx The list of nodes to use.
+  NodesX *nodesx The set of nodes to use.
 
-  WaysX *waysx The list of ways to use.
+  WaysX *waysx The set of ways to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void DeduplicateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx)
+void DeduplicateSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 {
- int duplicate=0,good=0;
- index_t firstindex=0,index=0;
- int i,fd;
- SegmentX prevsegmentx[16],segmentx;
+ index_t duplicate=0,good=0;
+ index_t index=0;
+ int fd,nprev=0;
+ index_t prevnode1=NO_NODE,prevnode2=NO_NODE;
+ SegmentX prevsegx[MAX_SEG_PER_NODE],segmentx;
+ Way prevway[MAX_SEG_PER_NODE];
 
  /* Print the start message */
 
  printf_first("Deduplicating Segments: Segments=0 Duplicate=0");
 
- /* Map into memory */
+ /* Map into memory / open the file */
 
 #if !SLIM
- waysx->xdata=MapFile(waysx->filename);
+ waysx->data=MapFile(waysx->filename);
+#else
+ waysx->fd=ReOpenFile(waysx->filename);
 #endif
 
- /* Allocate the array of indexes */
-
- segmentsx->firstnode=(index_t*)malloc((nodesx->number+1)*sizeof(index_t));
-
- assert(segmentsx->firstnode); /* Check malloc() worked */
-
- for(i=0;i<nodesx->number;i++)
-    segmentsx->firstnode[i]=NO_SEGMENT;
+ /* Re-open the file read-only and a new file writeable */
 
- segmentsx->firstnode[nodesx->number]=segmentsx->number;
-
- /* Modify the on-disk image */
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
 
  DeleteFile(segmentsx->filename);
 
  fd=OpenFileNew(segmentsx->filename);
- SeekFile(segmentsx->fd,0);
+
+ /* Modify the on-disk image */
 
  while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
    {
+    WayX *wayx=LookupWayX(waysx,segmentx.way,1);
     int isduplicate=0;
 
-    if(index && segmentx.node1==prevsegmentx[0].node1 &&
-                segmentx.node2==prevsegmentx[0].node2)
+    if(segmentx.node1==prevnode1 && segmentx.node2==prevnode2)
       {
-       index_t previndex=firstindex;
+       int offset;
 
-       while(previndex<index)
+       for(offset=0;offset<nprev;offset++)
          {
-          int offset=previndex-firstindex;
-
-          if(DISTFLAG(segmentx.distance)==DISTFLAG(prevsegmentx[offset].distance))
-            {
-             WayX *wayx1=LookupWayX(waysx,prevsegmentx[offset].way,1);
-             WayX *wayx2=LookupWayX(waysx,    segmentx        .way,2);
-
-             if(!WaysCompare(&wayx1->way,&wayx2->way))
+          if(DISTFLAG(segmentx.distance)==DISTFLAG(prevsegx[offset].distance))
+             if(!WaysCompare(&prevway[offset],&wayx->way))
                {
                 isduplicate=1;
-                duplicate++;
                 break;
                }
-            }
+         }
 
-          previndex++;
+       if(isduplicate)
+         {
+          nprev--;
+
+          for(;offset<nprev;offset++)
+            {
+             prevsegx[offset]=prevsegx[offset+1];
+             prevway[offset] =prevway[offset+1];
+            }
          }
+       else
+         {
+          assert(nprev<MAX_SEG_PER_NODE); /* Only a limited amount of information stored. */
 
-       assert((index-firstindex)<(sizeof(prevsegmentx)/sizeof(prevsegmentx[0])));
+          prevsegx[nprev]=segmentx;
+          prevway[nprev] =wayx->way;
 
-       prevsegmentx[index-firstindex]=segmentx;
+          nprev++;
+         }
       }
     else
       {
-       firstindex=index;
-       prevsegmentx[0]=segmentx;
+       nprev=1;
+       prevnode1=segmentx.node1;
+       prevnode2=segmentx.node2;
+       prevsegx[0]=segmentx;
+       prevway[0] =wayx->way;
       }
 
-    if(!isduplicate)
+    if(isduplicate)
+       duplicate++;
+    else
       {
        WriteFile(fd,&segmentx,sizeof(SegmentX));
 
-       if(good<segmentsx->firstnode[segmentx.node1])
-          segmentsx->firstnode[segmentx.node1]=good;
-
        good++;
       }
 
     index++;
 
     if(!(index%10000))
-       printf_middle("Deduplicating Segments: Segments=%d Duplicate=%d",index,duplicate);
+       printf_middle("Deduplicating Segments: Segments=%"Pindex_t" Duplicate=%"Pindex_t,index,duplicate);
    }
 
- /* Close the files and re-open them */
-
- CloseFile(segmentsx->fd);
- CloseFile(fd);
-
- segmentsx->fd=ReOpenFile(segmentsx->filename);
-
  segmentsx->number=good;
 
- /* Fix-up the firstnode index for the missing nodes */
+ /* Close the files */
 
- for(i=nodesx->number-1;i>=0;i--)
-    if(segmentsx->firstnode[i]==NO_SEGMENT)
-       segmentsx->firstnode[i]=segmentsx->firstnode[i+1];
+ segmentsx->fd=CloseFile(segmentsx->fd);
+ CloseFile(fd);
 
- /* Unmap from memory */
+ /* Unmap from memory / close the file */
 
 #if !SLIM
- waysx->xdata=UnmapFile(waysx->filename);
+ waysx->data=UnmapFile(waysx->filename);
+#else
+ waysx->fd=CloseFile(waysx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Deduplicated Segments: Segments=%d Duplicate=%d Unique=%d",index,duplicate,index-duplicate);
+ printf_last("Deduplicated Segments: Segments=%"Pindex_t" Duplicate=%"Pindex_t" Unique=%"Pindex_t,index,duplicate,good);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Create the real segments data.
+  Index the segments by creating the firstnode index and filling in the segment next2 parameter.
 
-  SegmentsX* segmentsx The set of segments to use.
+  SegmentsX *segmentsx The set of segments to modify.
 
-  WaysX* waysx The set of ways to use.
+  NodesX *nodesx The sset of nodes to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void CreateRealSegments(SegmentsX *segmentsx,WaysX *waysx)
+void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx)
 {
- index_t i;
+ index_t index,i;
 
- if(segmentsx->number==0 || waysx->number==0)
+ if(segmentsx->number==0)
     return;
 
  /* Print the start message */
 
- printf_first("Creating Real Segments: Segments=0");
+ printf_first("Indexing Segments: Segments=0");
 
- /* Map into memory */
+ /* Allocate the array of indexes */
 
-#if !SLIM
- segmentsx->xdata=MapFile(segmentsx->filename);
- waysx->xdata=MapFile(waysx->filename);
-#endif
+ if(!segmentsx->firstnode)
+   {
+    segmentsx->firstnode=(index_t*)malloc(nodesx->number*sizeof(index_t));
 
- /* Free the unneeded memory */
+    assert(segmentsx->firstnode); /* Check malloc() worked */
+   }
 
- free(segmentsx->firstnode);
- segmentsx->firstnode=NULL;
+ for(i=0;i<nodesx->number;i++)
+    segmentsx->firstnode[i]=NO_SEGMENT;
 
- /* Allocate the memory (or open the file) */
+ /* Map into memory / open the files */
 
 #if !SLIM
- segmentsx->sdata=(Segment*)malloc(segmentsx->number*sizeof(Segment));
-
- assert(segmentsx->sdata); /* Check malloc() worked */
+ segmentsx->data=MapFileWriteable(segmentsx->filename);
 #else
- segmentsx->sfd=OpenFileNew(segmentsx->sfilename);
+ segmentsx->fd=ReOpenFileWriteable(segmentsx->filename);
 #endif
 
- /* Loop through and fill */
+ /* Read through the segments in reverse order */
 
- for(i=0;i<segmentsx->number;i++)
+ for(index=segmentsx->number-1;index!=NO_SEGMENT;index--)
    {
-    SegmentX *segmentx=LookupSegmentX(segmentsx,i,1);
-    Segment  *segment =LookupSegmentXSegment(segmentsx,i,1);
-    WayX     *wayx=LookupWayX(waysx,segmentx->way,1);
+    SegmentX *segmentx=LookupSegmentX(segmentsx,index,1);
 
-    segment->node1=0;
-    segment->node2=0;
-    segment->next2=NO_NODE;
-    segment->way=wayx->prop;
-    segment->distance=segmentx->distance;
+    segmentx->next2=segmentsx->firstnode[segmentx->node2];
 
-#if SLIM
-    PutBackSegmentXSegment(segmentsx,i,1);
-#endif
+    PutBackSegmentX(segmentsx,index,1);
 
-    if(!((i+1)%10000))
-       printf_middle("Creating Real Segments: Segments=%d",i+1);
+    segmentsx->firstnode[segmentx->node1]=index;
+    segmentsx->firstnode[segmentx->node2]=index;
+
+    if(!(index%10000))
+       printf_middle("Indexing Segments: Segments=%"Pindex_t,segmentsx->number-index);
    }
 
- /* Unmap from memory */
+ /* Unmap from memory / close the files */
 
 #if !SLIM
- segmentsx->xdata=UnmapFile(segmentsx->filename);
- waysx->xdata=UnmapFile(waysx->filename);
+ segmentsx->data=UnmapFile(segmentsx->filename);
+#else
+ segmentsx->fd=CloseFile(segmentsx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Creating Real Segments: Segments=%d",segmentsx->number);
+ printf_last("Indexed Segments: Segments=%"Pindex_t,segmentsx->number);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Assign the nodes indexes to the segments.
+  Update the segment indexes after geographical sorting.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to modify.
 
-  NodesX *nodesx The list of nodes to use.
+  NodesX *nodesx The set of nodes to use.
+
+  WaysX *waysx The set of ways to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void IndexSegments(SegmentsX* segmentsx,NodesX *nodesx)
+void UpdateSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
 {
  index_t i;
-
- if(nodesx->number==0 || segmentsx->number==0)
-    return;
+ int fd;
 
  /* Print the start message */
 
- printf_first("Indexing Nodes: Nodes=0");
+ printf_first("Updating Segments: Segments=0");
 
- /* Map into memory */
+ /* Map into memory / open the files */
 
 #if !SLIM
- nodesx->xdata=MapFile(nodesx->filename);
- segmentsx->xdata=MapFile(segmentsx->filename);
+ waysx->data=MapFile(waysx->filename);
+#else
+ waysx->fd=ReOpenFile(waysx->filename);
 #endif
 
- /* Index the segments */
+ /* Re-open the file read-only and a new file writeable */
 
- for(i=0;i<nodesx->number;i++)
-   {
-    NodeX  *nodex=LookupNodeX(nodesx,i,1);
-    Node   *node =LookupNodeXNode(nodesx,nodex->id,1);
-    index_t index=node->firstseg;
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
 
-    do
-      {
-       SegmentX *segmentx=LookupSegmentX(segmentsx,index,1);
-       Segment  *segment =LookupSegmentXSegment(segmentsx,index,1);
+ DeleteFile(segmentsx->filename);
 
-       if(segmentx->node1==nodex->id)
-         {
-          segment->node1=i;
+ fd=OpenFileNew(segmentsx->filename);
 
-#if SLIM
-          PutBackSegmentXSegment(segmentsx,index,1);
-#endif
+ /* Modify the on-disk image */
 
-          index++;
+ for(i=0;i<segmentsx->number;i++)
+   {
+    SegmentX segmentx;
+    WayX *wayx;
 
-          if(index>=segmentsx->number)
-             break;
+    ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX));
 
-          segmentx=LookupSegmentX(segmentsx,index,1);
+    segmentx.node1=nodesx->gdata[segmentx.node1];
+    segmentx.node2=nodesx->gdata[segmentx.node2];
 
-          if(segmentx->node1!=nodex->id)
-             break;
-         }
-       else
-         {
-          segment->node2=i;
+    if(segmentx.node1>segmentx.node2)
+      {
+       index_t temp;
 
-#if SLIM
-          PutBackSegmentXSegment(segmentsx,index,1);
-#endif
+       temp=segmentx.node1;
+       segmentx.node1=segmentx.node2;
+       segmentx.node2=temp;
 
-          if(segment->next2==NO_NODE)
-             break;
-          else
-             index=segment->next2;
-         }
+       if(segmentx.distance&(ONEWAY_2TO1|ONEWAY_1TO2))
+          segmentx.distance^=ONEWAY_2TO1|ONEWAY_1TO2;
       }
-    while(1);
+
+    wayx=LookupWayX(waysx,segmentx.way,1);
+
+    segmentx.way=wayx->prop;
+
+    WriteFile(fd,&segmentx,sizeof(SegmentX));
 
     if(!((i+1)%10000))
-       printf_middle("Indexing Nodes: Nodes=%d",i+1);
+       printf_middle("Updating Segments: Segments=%"Pindex_t,i+1);
    }
 
- /* Unmap from memory */
+ /* Close the files */
+
+ segmentsx->fd=CloseFile(segmentsx->fd);
+ CloseFile(fd);
+
+ /* Unmap from memory / close the files */
 
 #if !SLIM
- nodesx->xdata=UnmapFile(nodesx->filename);
- segmentsx->xdata=UnmapFile(segmentsx->filename);
+ waysx->data=UnmapFile(waysx->filename);
+#else
+ waysx->fd=CloseFile(waysx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Indexed Nodes: Nodes=%d",nodesx->number);
+ printf_last("Updated Segments: Segments=%"Pindex_t,segmentsx->number);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Save the segment list to a file.
 
-  SegmentsX* segmentsx The set of segments to save.
+  SegmentsX *segmentsx The set of segments to save.
 
   const char *filename The name of the file to save.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void SaveSegmentList(SegmentsX* segmentsx,const char *filename)
+void SaveSegmentList(SegmentsX *segmentsx,const char *filename)
 {
  index_t i;
  int fd;
  SegmentsFile segmentsfile={0};
- int super_number=0,normal_number=0;
+ index_t super_number=0,normal_number=0;
 
  /* Print the start message */
 
  printf_first("Writing Segments: Segments=0");
 
+ /* Re-open the file */
+
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
+
  /* Write out the segments data */
 
  fd=OpenFileNew(filename);
@@ -926,17 +831,26 @@ void SaveSegmentList(SegmentsX* segmentsx,const char *filename)
 
  for(i=0;i<segmentsx->number;i++)
    {
-    Segment *segment=LookupSegmentXSegment(segmentsx,i,1);
+    SegmentX segmentx;
+    Segment  segment;
+
+    ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX));
 
-    if(IsSuperSegment(segment))
+    segment.node1   =segmentx.node1;
+    segment.node2   =segmentx.node2;
+    segment.next2   =segmentx.next2;
+    segment.way     =segmentx.way;
+    segment.distance=segmentx.distance;
+
+    if(IsSuperSegment(&segment))
        super_number++;
-    if(IsNormalSegment(segment))
+    if(IsNormalSegment(&segment))
        normal_number++;
 
-    WriteFile(fd,segment,sizeof(Segment));
+    WriteFile(fd,&segment,sizeof(Segment));
 
     if(!((i+1)%10000))
-       printf_middle("Writing Segments: Segments=%d",i+1);
+       printf_middle("Writing Segments: Segments=%"Pindex_t,i+1);
    }
 
  /* Write out the header structure */
@@ -950,9 +864,13 @@ void SaveSegmentList(SegmentsX* segmentsx,const char *filename)
 
  CloseFile(fd);
 
+ /* Close the file */
+
+ segmentsx->fd=CloseFile(segmentsx->fd);
+
  /* Print the final message */
 
- printf_last("Wrote Segments: Segments=%d",segmentsx->number);
+ printf_last("Wrote Segments: Segments=%"Pindex_t,segmentsx->number);
 }
 
 
diff --git a/src/segmentsx.h b/src/segmentsx.h
index a843900..570b558 100644
--- a/src/segmentsx.h
+++ b/src/segmentsx.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/segmentsx.h,v 1.26 2010/07/31 14:36:15 amb Exp $
-
  A header file for the extended segments.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,10 +39,12 @@
 /*+ An extended structure used for processing. +*/
 struct _SegmentX
 {
- node_t     node1;              /*+ The id of the starting node. +*/
- node_t     node2;              /*+ The id of the finishing node. +*/
+ 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    next2;              /*+ The index of the next segment with the same node2. +*/
 
- way_t      way;                /*+ The id of the way. +*/
+ way_t      way;                /*+ The id of the way; initially the OSM value, later the WayX index. +*/
 
  distance_t distance;           /*+ The distance between the nodes. +*/
 };
@@ -56,39 +56,26 @@ struct _SegmentsX
  char      *filename;           /*+ The name of the temporary file. +*/
  int        fd;                 /*+ The file descriptor of the temporary file. +*/
 
- index_t    xnumber;            /*+ The number of unsorted extended nodes. +*/
+ index_t    number;             /*+ The number of extended segments still being considered. +*/
 
 #if !SLIM
 
- SegmentX  *xdata;              /*+ The extended segment data (unsorted). +*/
+ SegmentX  *data;               /*+ The extended segment data (when mapped into memory). +*/
 
 #else
 
- SegmentX   xcached[2];         /*+ Two cached segments read from the file in slim mode. +*/
+ SegmentX   cached[2];          /*+ Two cached extended segments read from the file in slim mode. +*/
+ index_t    incache[2];         /*+ The indexes of the cached extended segments. +*/
 
 #endif
 
- index_t    number;             /*+ How many entries are still useful? +*/
+ index_t   *firstnode;          /*+ The first segment index for each node. +*/
 
- node_t   *idata;               /*+ The extended segment data (sorted by node1 then node2). +*/
- index_t  *firstnode;           /*+ The first segment index for each node. +*/
-
-#if !SLIM
-
- Segment   *sdata;              /*+ The segment data (same order as n1data). +*/
-
-#else
-
- char     *sfilename;           /*+ The name of the temporary file for segments in slim mode. +*/
- int       sfd;                 /*+ The file descriptor of the temporary file. +*/
-
- Segment   scached[2];          /*+ Two cached segments read from the file in slim mode. +*/
-
-#endif
+ char      *usednode;           /*+ A flag to indicte if a node is used. +*/
 };
 
 
-/* Functions */
+/* Functions in segmentsx.c */
 
 
 SegmentsX *NewSegmentList(int append);
@@ -96,25 +83,24 @@ void FreeSegmentList(SegmentsX *segmentsx,int keep);
 
 void SaveSegmentList(SegmentsX *segmentsx,const char *filename);
 
-index_t IndexFirstSegmentX(SegmentsX* segmentsx,node_t node);
-
-index_t IndexNextSegmentX(SegmentsX* segmentsx,index_t segindex,index_t nodeindex);
+SegmentX *FirstSegmentX(SegmentsX *segmentsx,index_t nodeindex,int position);
+SegmentX *NextSegmentX(SegmentsX *segmentsx,SegmentX *segmentx,index_t nodeindex,int position);
 
-void AppendSegment(SegmentsX* segmentsx,way_t way,node_t node1,node_t node2,distance_t distance);
+void AppendSegment(SegmentsX *segmentsx,way_t way,node_t node1,node_t node2,distance_t distance);
 
-void SortSegmentList(SegmentsX* segmentsx);
+void SortSegmentList(SegmentsX *segmentsx);
 
 void RemoveBadSegments(NodesX *nodesx,SegmentsX *segmentsx);
 
-void UpdateSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx);
-
-void RotateSegments(SegmentsX* segmentsx);
+void MeasureSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx);
 
-void DeduplicateSegments(SegmentsX* segmentsx,NodesX *nodesx,WaysX *waysx);
+void DeduplicateSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx);
 
 void CreateRealSegments(SegmentsX *segmentsx,WaysX *waysx);
 
-void IndexSegments(SegmentsX* segmentsx,NodesX *nodesx);
+void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx);
+
+void UpdateSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx);
 
 
 /* Macros / inline functions */
@@ -122,78 +108,78 @@ void IndexSegments(SegmentsX* segmentsx,NodesX *nodesx);
 
 #if !SLIM
 
-#define LookupSegmentX(segmentsx,index,position)         &(segmentsx)->xdata[index]
-  
-#define LookupSegmentXSegment(segmentsx,index,position)  &(segmentsx)->sdata[index]
+#define LookupSegmentX(segmentsx,index,position)         &(segmentsx)->data[index]
 
+#define IndexSegmentX(segmentsx,segmentx)                (index_t)((segmentx)-&(segmentsx)->data[0])
+
+#define PutBackSegmentX(segmentsx,index,position)        /* nop */
+  
 #else
 
-static SegmentX *LookupSegmentX(SegmentsX* segmentsx,index_t index,int position);
+static SegmentX *LookupSegmentX(SegmentsX *segmentsx,index_t index,int position);
 
-static Segment *LookupSegmentXSegment(SegmentsX* segmentsx,index_t index,int position);
+static index_t IndexSegmentX(SegmentsX *segmentsx,SegmentX *segmentx);
 
-static void PutBackSegmentXSegment(SegmentsX* segmentsx,index_t index,int position);
+static void PutBackSegmentX(SegmentsX *segmentsx,index_t index,int position);
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Lookup a particular extended segment.
+  Lookup a particular extended segment with the specified id from the file on disk.
 
-  SegmentX *LookupSegmentX Returns a pointer to the extended segment with the specified id.
+  SegmentX *LookupSegmentX Returns a pointer to a cached copy of the extended segment.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to use.
 
   index_t index The segment index to look for.
 
   int position The position in the cache to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline SegmentX *LookupSegmentX(SegmentsX* segmentsx,index_t index,int position)
+static inline SegmentX *LookupSegmentX(SegmentsX *segmentsx,index_t index,int position)
 {
  SeekFile(segmentsx->fd,(off_t)index*sizeof(SegmentX));
 
- ReadFile(segmentsx->fd,&segmentsx->xcached[position-1],sizeof(SegmentX));
+ ReadFile(segmentsx->fd,&segmentsx->cached[position-1],sizeof(SegmentX));
+
+ segmentsx->incache[position-1]=index;
 
- return(&segmentsx->xcached[position-1]);
+ return(&segmentsx->cached[position-1]);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Lookup a particular extended segment's normal segment.
+  Find the extended segment index for a particular extended segment pointer.
 
-  Segment *LookupSegmentXSegment Returns a pointer to the segment with the specified id.
+  index_t IndexSegmentX Returns the index of the extended segment.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to use.
 
-  index_t index The segment index to look for.
-
-  int position The position in the cache to use.
+  SegmentX *segmentx The extended segment whose index is to be found.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline Segment *LookupSegmentXSegment(SegmentsX* segmentsx,index_t index,int position)
+static inline index_t IndexSegmentX(SegmentsX *segmentsx,SegmentX *segmentx)
 {
- SeekFile(segmentsx->sfd,(off_t)index*sizeof(Segment));
+ int position1=segmentx-&segmentsx->cached[0];
 
- ReadFile(segmentsx->sfd,&segmentsx->scached[position-1],sizeof(Segment));
-
- return(&segmentsx->scached[position-1]);
+ return(segmentsx->incache[position1]);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Put back an extended segment's normal segment.
+  Put back an extended segment's data into the file on disk.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to use.
 
-  index_t index The segment index to look for.
+  index_t index The segment index to put back.
 
   int position The position in the cache to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline void PutBackSegmentXSegment(SegmentsX* segmentsx,index_t index,int position)
+static inline void PutBackSegmentX(SegmentsX *segmentsx,index_t index,int position)
 {
- SeekFile(segmentsx->sfd,(off_t)index*sizeof(Segment));
+ SeekFile(segmentsx->fd,(off_t)index*sizeof(SegmentX));
 
- WriteFile(segmentsx->sfd,&segmentsx->scached[position-1],sizeof(Segment));
+ WriteFile(segmentsx->fd,&segmentsx->cached[position-1],sizeof(SegmentX));
 }
 
 #endif /* SLIM */
diff --git a/src/sorting.c b/src/sorting.c
index 396d363..7a0ba1e 100644
--- a/src/sorting.c
+++ b/src/sorting.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/sorting.c,v 1.11 2010/09/25 13:54:18 amb Exp $
-
  Merge sort functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2009-2010 Andrew M. Bishop
+ This file Copyright 2009-2011 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,11 +25,13 @@
 #include <string.h>
 #include <assert.h>
 
+#include "types.h"
+
 #include "files.h"
-#include "functions.h"
+#include "sorting.h"
 
 
-/* Variables */
+/* Global variables */
 
 /*+ The command line '--tmpdir' option or its default value. +*/
 extern char *option_tmpdirname;
@@ -103,6 +103,13 @@ void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const vo
 
     n=i;
 
+    /* Shortcut if there is no data and no previous files (i.e. no data at all) */
+
+    if(nfiles==0 && n==0)
+       goto tidy_and_exit;
+
+    /* No new data read in this time round */
+
     if(n==0)
        break;
 
@@ -179,7 +186,7 @@ void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const vo
 
  /* Perform an n-way merge using a binary heap */
 
- heap=(int*)malloc(nfiles*sizeof(int));
+ heap=(int*)malloc((1+nfiles)*sizeof(int));
 
  /* Fill the heap to start with */
 
@@ -191,19 +198,21 @@ void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const vo
 
     ReadFile(fds[i],datap[i],itemsize);
 
-    heap[i]=i;
+    index=i+1;
 
-    index=i;
+    heap[index]=i;
 
     /* Bubble up the new value */
 
-    while(index>0 &&
-          compare(datap[heap[index]],datap[heap[(index-1)/2]])<0)
+    while(index>1)
       {
        int newindex;
        int temp;
 
-       newindex=(index-1)/2;
+       newindex=index/2;
+
+       if(compare(datap[heap[index]],datap[heap[newindex]])>=0)
+          break;
 
        temp=heap[index];
        heap[index]=heap[newindex];
@@ -219,33 +228,34 @@ void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const vo
 
  do
    {
-    int index=0;
+    int index=1;
 
-    if(!buildindex || buildindex(datap[heap[0]],count))
+    if(!buildindex || buildindex(datap[heap[index]],count))
       {
-       WriteFile(fd_out,datap[heap[0]],itemsize);
+       WriteFile(fd_out,datap[heap[index]],itemsize);
        count++;
       }
 
-    if(ReadFile(fds[heap[0]],datap[heap[0]],itemsize))
+    if(ReadFile(fds[heap[index]],datap[heap[index]],itemsize))
       {
+       heap[index]=heap[ndata];
        ndata--;
-       heap[0]=heap[ndata];
       }
 
     /* Bubble down the new value */
 
-    while((2*index+2)<ndata &&
-          (compare(datap[heap[index]],datap[heap[2*index+1]])>0 ||
-           compare(datap[heap[index]],datap[heap[2*index+2]])>0))
+    while((2*index)<ndata)
       {
        int newindex;
        int temp;
 
-       if(compare(datap[heap[2*index+1]],datap[heap[2*index+2]])<0)
-          newindex=2*index+1;
-       else
-          newindex=2*index+2;
+       newindex=2*index;
+
+       if(compare(datap[heap[newindex]],datap[heap[newindex+1]])>=0)
+          newindex=newindex+1;
+
+       if(compare(datap[heap[index]],datap[heap[newindex]])<=0)
+          break;
 
        temp=heap[newindex];
        heap[newindex]=heap[index];
@@ -254,17 +264,21 @@ void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const vo
        index=newindex;
       }
 
-    if((2*index+2)==ndata &&
-       compare(datap[heap[index]],datap[heap[2*index+1]])>0)
+    if((2*index)==ndata)
       {
        int newindex;
        int temp;
 
-       newindex=2*index+1;
+       newindex=2*index;
 
-       temp=heap[newindex];
-       heap[newindex]=heap[index];
-       heap[index]=temp;
+       if(compare(datap[heap[index]],datap[heap[newindex]])<=0)
+          ; /* break */
+       else
+         {
+          temp=heap[newindex];
+          heap[newindex]=heap[index];
+          heap[index]=temp;
+         }
       }
    }
  while(ndata>0);
@@ -291,7 +305,7 @@ void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const vo
 
 /*++++++++++++++++++++++++++++++++++++++
   A function to sort the contents of a file of variable length objects (each
-  preceded by its length in 2 bytes) using a limited amount of RAM.
+  preceded by its length in FILESORT_VARSIZE bytes) using a limited amount of RAM.
 
   The data is sorted using a "Merge sort" http://en.wikipedia.org/wiki/Merge_sort
   and in particular an "external sort" http://en.wikipedia.org/wiki/External_sorting.
@@ -370,6 +384,8 @@ void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),
          }
       }
 
+    /* No new data read in this time round */
+
     if(n==0)
        break;
 
@@ -435,7 +451,7 @@ void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),
 
  /* Perform an n-way merge using a binary heap */
 
- heap=(int*)malloc(nfiles*sizeof(int));
+ heap=(int*)malloc((1+nfiles)*sizeof(int));
 
  datap=data+option_filesort_ramsize-nfiles*sizeof(void*);
 
@@ -454,19 +470,21 @@ void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),
 
     ReadFile(fds[i],datap[i],itemsize);
 
-    heap[i]=i;
+    index=i+1;
 
-    index=i;
+    heap[index]=i;
 
     /* Bubble up the new value */
 
-    while(index>0 &&
-          compare(datap[heap[index]],datap[heap[(index-1)/2]])<0)
+    while(index>1)
       {
        int newindex;
        int temp;
 
-       newindex=(index-1)/2;
+       newindex=index/2;
+
+       if(compare(datap[heap[index]],datap[heap[newindex]])>=0)
+          break;
 
        temp=heap[index];
        heap[index]=heap[newindex];
@@ -482,42 +500,43 @@ void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),
 
  do
    {
-    int index=0;
+    int index=1;
     FILESORT_VARINT itemsize;
 
-    if(!buildindex || buildindex(datap[heap[0]],count))
+    if(!buildindex || buildindex(datap[heap[index]],count))
       {
-       itemsize=*(FILESORT_VARINT*)(datap[heap[0]]-FILESORT_VARSIZE);
+       itemsize=*(FILESORT_VARINT*)(datap[heap[index]]-FILESORT_VARSIZE);
 
-       WriteFile(fd_out,datap[heap[0]]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
+       WriteFile(fd_out,datap[heap[index]]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
        count++;
       }
 
-    if(ReadFile(fds[heap[0]],&itemsize,FILESORT_VARSIZE))
+    if(ReadFile(fds[heap[index]],&itemsize,FILESORT_VARSIZE))
       {
+       heap[index]=heap[ndata];
        ndata--;
-       heap[0]=heap[ndata];
       }
     else
       {
-       *(FILESORT_VARINT*)(datap[heap[0]]-FILESORT_VARSIZE)=itemsize;
+       *(FILESORT_VARINT*)(datap[heap[index]]-FILESORT_VARSIZE)=itemsize;
 
-       ReadFile(fds[heap[0]],datap[heap[0]],itemsize);
+       ReadFile(fds[heap[index]],datap[heap[index]],itemsize);
       }
 
     /* Bubble down the new value */
 
-    while((2*index+2)<ndata &&
-          (compare(datap[heap[index]],datap[heap[2*index+1]])>0 ||
-           compare(datap[heap[index]],datap[heap[2*index+2]])>0))
+    while((2*index)<ndata)
       {
        int newindex;
        int temp;
 
-       if(compare(datap[heap[2*index+1]],datap[heap[2*index+2]])<0)
-          newindex=2*index+1;
-       else
-          newindex=2*index+2;
+       newindex=2*index;
+
+       if(compare(datap[heap[newindex]],datap[heap[newindex+1]])>=0)
+          newindex=newindex+1;
+
+       if(compare(datap[heap[index]],datap[heap[newindex]])<=0)
+          break;
 
        temp=heap[newindex];
        heap[newindex]=heap[index];
@@ -526,17 +545,21 @@ void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),
        index=newindex;
       }
 
-    if((2*index+2)==ndata &&
-       compare(datap[heap[index]],datap[heap[2*index+1]])>0)
+    if((2*index)==ndata)
       {
        int newindex;
        int temp;
 
-       newindex=2*index+1;
+       newindex=2*index;
 
-       temp=heap[newindex];
-       heap[newindex]=heap[index];
-       heap[index]=temp;
+       if(compare(datap[heap[index]],datap[heap[newindex]])<=0)
+          ; /* break */
+       else
+         {
+          temp=heap[newindex];
+          heap[newindex]=heap[index];
+          heap[index]=temp;
+         }
       }
    }
  while(ndata>0);
@@ -564,7 +587,7 @@ void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),
   A function to sort an array of pointers efficiently.
 
   The data is sorted using a "Heap sort" http://en.wikipedia.org/wiki/Heapsort,
-  in particular an this good because it can operate in-place and doesn't
+  in particular, this is good because it can operate in-place and doesn't
   allocate more memory like using qsort() does.
 
   void **datap A pointer to the array of pointers to sort.
@@ -577,27 +600,30 @@ void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),
 
 void filesort_heapsort(void **datap,size_t nitems,int(*compare)(const void*, const void*))
 {
+ void **datap1=&datap[-1];
  int i;
 
  /* Fill the heap by pretending to insert the data that is already there */
 
- for(i=1;i<nitems;i++)
+ for(i=2;i<=nitems;i++)
    {
     int index=i;
 
     /* Bubble up the new value (upside-down, put largest at top) */
 
-    while(index>0 &&
-          compare(datap[index],datap[(index-1)/2])>0) /* reversed compared to filesort() above */
+    while(index>1)
       {
        int newindex;
        void *temp;
 
-       newindex=(index-1)/2;
+       newindex=index/2;
+
+       if(compare(datap1[index],datap1[newindex])<=0) /* reversed compared to filesort_fixed() above */
+          break;
 
-       temp=datap[index];
-       datap[index]=datap[newindex];
-       datap[newindex]=temp;
+       temp=datap1[index];
+       datap1[index]=datap1[newindex];
+       datap1[newindex]=temp;
 
        index=newindex;
       }
@@ -605,47 +631,52 @@ void filesort_heapsort(void **datap,size_t nitems,int(*compare)(const void*, con
 
  /* Repeatedly pull out the root of the heap and swap with the bottom item */
 
- for(i=nitems-1;i>0;i--)
+ for(i=nitems;i>1;i--)
    {
-    int index=0;
+    int index=1;
     void *temp;
 
-    temp=datap[index];
-    datap[index]=datap[i];
-    datap[i]=temp;
+    temp=datap1[index];
+    datap1[index]=datap1[i];
+    datap1[i]=temp;
 
     /* Bubble down the new value (upside-down, put largest at top) */
 
-    while((2*index+2)<i &&
-          (compare(datap[index],datap[2*index+1])<0 || /* reversed compared to filesort() above */
-           compare(datap[index],datap[2*index+2])<0))  /* reversed compared to filesort() above */
+    while((2*index)<(i-1))
       {
        int newindex;
        void *temp;
 
-       if(compare(datap[2*index+1],datap[2*index+2])>0) /* reversed compared to filesort() above */
-          newindex=2*index+1;
-       else
-          newindex=2*index+2;
+       newindex=2*index;
+
+       if(compare(datap1[newindex],datap1[newindex+1])<=0) /* reversed compared to filesort_fixed() above */
+          newindex=newindex+1;
 
-       temp=datap[newindex];
-       datap[newindex]=datap[index];
-       datap[index]=temp;
+       if(compare(datap1[index],datap1[newindex])>=0) /* reversed compared to filesort_fixed() above */
+          break;
+
+       temp=datap1[newindex];
+       datap1[newindex]=datap1[index];
+       datap1[index]=temp;
 
        index=newindex;
       }
 
-    if((2*index+2)==i &&
-       compare(datap[index],datap[2*index+1])<0) /* reversed compared to filesort() above */
+    if((2*index)==(i-1))
       {
        int newindex;
        void *temp;
 
-       newindex=2*index+1;
+       newindex=2*index;
 
-       temp=datap[newindex];
-       datap[newindex]=datap[index];
-       datap[index]=temp;
+       if(compare(datap1[index],datap1[newindex])>=0) /* reversed compared to filesort_fixed() above */
+          ; /* break */
+       else
+         {
+          temp=datap1[newindex];
+          datap1[newindex]=datap1[index];
+          datap1[index]=temp;
+         }
       }
    }
 }
diff --git a/src/sorting.h b/src/sorting.h
new file mode 100644
index 0000000..141d33d
--- /dev/null
+++ b/src/sorting.h
@@ -0,0 +1,45 @@
+/***************************************
+ Header file for sorting function prototypes
+
+ Part of the Routino routing software.
+ ******************/ /******************
+ This file Copyright 2008-2011 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 SORTING_H
+#define SORTING_H    /*+ To stop multiple inclusions. +*/
+
+#include <stdlib.h>
+
+#include "types.h"
+
+
+/* Functions in sorting.c */
+
+/*+ The type, size and alignment of variable to store the variable length +*/
+#define FILESORT_VARINT   unsigned short
+#define FILESORT_VARSIZE  sizeof(FILESORT_VARINT)
+#define FILESORT_VARALIGN sizeof(void*)
+
+void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t));
+
+void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t));
+
+void filesort_heapsort(void **datap,size_t nitems,int(*compare)(const void*, const void*));
+
+
+#endif /* SORTING_H */
diff --git a/src/superx.c b/src/superx.c
index 182daef..b72f1da 100644
--- a/src/superx.c
+++ b/src/superx.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/superx.c,v 1.45 2010/11/13 14:22:28 amb Exp $
-
  Super-Segment data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,6 +20,7 @@
  ***************************************/
 
 
+#include <assert.h>
 #include <stdlib.h>
 #include <stdio.h>
 
@@ -37,25 +36,25 @@
 #include "results.h"
 
 
-/* Local Functions */
+/* Local functions */
 
-static Results *FindRoutesWay(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,node_t start,Way *match,int iteration);
+static Results *FindRoutesWay(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,node_t start,Way *match);
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Select the super-segments from the list of segments.
+  Select the super-nodes from the list of nodes.
 
-  NodesX *nodesx The nodes.
+  NodesX *nodesx The set of nodes to use.
 
-  SegmentsX *segmentsx The segments.
+  SegmentsX *segmentsx The set of segments to use.
 
-  WaysX *waysx The ways.
+  WaysX *waysx The set of ways to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
 void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 {
  index_t i;
- int nnodes=0;
+ index_t nnodes=0;
 
  if(nodesx->number==0 || segmentsx->number==0 || waysx->number==0)
     return;
@@ -64,107 +63,139 @@ void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 
  printf_first("Finding Super-Nodes: Nodes=0 Super-Nodes=0");
 
- /* Map into memory */
+ /* Map into memory / open the files */
 
 #if !SLIM
- nodesx->xdata=MapFile(nodesx->filename);
- segmentsx->xdata=MapFile(segmentsx->filename);
- waysx->xdata=MapFile(waysx->filename);
+ nodesx->data=MapFile(nodesx->filename);
+ segmentsx->data=MapFile(segmentsx->filename);
+ waysx->data=MapFile(waysx->filename);
+#else
+ nodesx->fd=ReOpenFile(nodesx->filename);
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
+ waysx->fd=ReOpenFile(waysx->filename);
 #endif
 
  /* Find super-nodes */
 
  for(i=0;i<nodesx->number;i++)
    {
-    NodeX *nodex=LookupNodeX(nodesx,i,1);
-    int difference=0;
-    index_t index1,index2;
+    if(IsBitSet(nodesx->super,i))
+      {
+       int issuper=0;
+       NodeX *nodex=LookupNodeX(nodesx,i,1);
 
-    index1=IndexFirstSegmentX(segmentsx,i);
+       if(nodex->flags&(NODE_TURNRSTRCT|NODE_TURNRSTRCT2))
+          issuper=1;
+       else
+         {
+          int count=0,j;
+          Way segmentway[MAX_SEG_PER_NODE];
+          int segmentweight[MAX_SEG_PER_NODE];
+          SegmentX *segmentx=FirstSegmentX(segmentsx,i,1);
 
-    while(index1!=NO_SEGMENT)
-      {
-       SegmentX *segmentx1=LookupSegmentX(segmentsx,index1,1);
-       WayX *wayx1=LookupWayX(waysx,segmentx1->way,1);
+          while(segmentx)
+            {
+             WayX *wayx=LookupWayX(waysx,segmentx->way,1);
+             int nsegments;
 
-       index1=IndexNextSegmentX(segmentsx,index1,i);
-       index2=index1;
+             /* Segments that are loops count twice */
 
-       /* If the node allows less traffic types than any connecting way ... */
+             assert(count<MAX_SEG_PER_NODE); /* Only a limited amount of information stored. */
 
-       if((wayx1->way.allow&nodex->allow)!=wayx1->way.allow)
-         {
-          difference=1;
-          break;
-         }
+             if(segmentx->node1==segmentx->node2)
+                segmentweight[count]=2;
+             else
+                segmentweight[count]=1;
 
-       while(index2!=NO_SEGMENT)
-         {
-          SegmentX *segmentx2=LookupSegmentX(segmentsx,index2,2);
-          WayX *wayx2=LookupWayX(waysx,segmentx2->way,2);
+             segmentway[count]=wayx->way;
 
-          /* If the ways are different in any attribute and there is a type of traffic that can use both ... */
+             /* If the node allows less traffic types than any connecting way then it is super */
 
-          if(WaysCompare(&wayx1->way,&wayx2->way))
-             if(wayx1->way.allow & wayx2->way.allow)
+             if((wayx->way.allow&nodex->allow)!=wayx->way.allow)
                {
-                difference=1;
+                issuper=1;
                 break;
                }
 
-          index2=IndexNextSegmentX(segmentsx,index2,i);
-         }
+             nsegments=segmentweight[count];
 
-       if(difference)
-          break;
-      }
+             for(j=0;j<count;j++)
+                if(wayx->way.allow & segmentway[j].allow)
+                  {
+                   /* If two ways are different in any attribute and there is a type of traffic that can use both then it is super */
+
+                   if(WaysCompare(&segmentway[j],&wayx->way))
+                     {
+                      issuper=1;
+                      break;
+                     }
 
-    /* Store the node if there is a difference in the connected ways that could affect routing. */
+                   /* If there are two other segments that can be used by the same types of traffic as this one then it is super */
 
-    if(difference)
-      {
-       nodesx->super[i]++;
+                   nsegments+=segmentweight[j];
+                   if(nsegments>2)
+                     {
+                      issuper=1;
+                      break;
+                     }
+                  }
+
+             if(issuper)
+                break;
+
+             segmentx=NextSegmentX(segmentsx,segmentx,i,1);
 
-       nnodes++;
+             count++;
+            }
+         }
+
+       /* Mark the node as super if it is. */
+
+       if(issuper)
+          nnodes++;
+       else
+          ClearBit(nodesx->super,i);
       }
 
     if(!((i+1)%10000))
-       printf_middle("Finding Super-Nodes: Nodes=%d Super-Nodes=%d",i+1,nnodes);
+       printf_middle("Finding Super-Nodes: Nodes=%"Pindex_t" Super-Nodes=%"Pindex_t,i+1,nnodes);
    }
 
- /* Unmap from memory */
+ /* Unmap from memory / close the files */
 
 #if !SLIM
- nodesx->xdata=UnmapFile(nodesx->filename);
- segmentsx->xdata=UnmapFile(segmentsx->filename);
- waysx->xdata=UnmapFile(waysx->filename);
+ nodesx->data=UnmapFile(nodesx->filename);
+ segmentsx->data=UnmapFile(segmentsx->filename);
+ waysx->data=UnmapFile(waysx->filename);
+#else
+ nodesx->fd=CloseFile(nodesx->fd);
+ segmentsx->fd=CloseFile(segmentsx->fd);
+ waysx->fd=CloseFile(waysx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Found Super-Nodes: Nodes=%d Super-Nodes=%d",nodesx->number,nnodes);
+ printf_last("Found Super-Nodes: Nodes=%"Pindex_t" Super-Nodes=%"Pindex_t,nodesx->number,nnodes);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Create the super-segments.
+  Create the super-segments from the existing segments.
 
-  SegmentsX *CreateSuperSegments Creates the super segments.
+  SegmentsX *CreateSuperSegments Returns the new super segments.
 
-  NodesX *nodesx The nodes.
-
-  SegmentsX *segmentsx The segments.
+  NodesX *nodesx The set of nodes to use.
 
-  WaysX *waysx The ways.
+  SegmentsX *segmentsx The set of segments to use.
 
-  int iteration The current super-node / super-segment iteration number.
+  WaysX *waysx The set of ways to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int iteration)
+SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx)
 {
  index_t i;
  SegmentsX *supersegmentsx;
- int sn=0,ss=0;
+ index_t sn=0,ss=0;
 
  supersegmentsx=NewSegmentList(0);
 
@@ -175,65 +206,65 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 
  printf_first("Creating Super-Segments: Super-Nodes=0 Super-Segments=0");
 
- /* Map into memory */
+ /* Map into memory / open the files */
 
 #if !SLIM
- segmentsx->xdata=MapFile(segmentsx->filename);
- waysx->xdata=MapFile(waysx->filename);
+ segmentsx->data=MapFile(segmentsx->filename);
+ waysx->data=MapFile(waysx->filename);
+#else
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
+ waysx->fd=ReOpenFile(waysx->filename);
 #endif
 
  /* Create super-segments for each super-node. */
 
  for(i=0;i<nodesx->number;i++)
    {
-    if(nodesx->super[i]>iteration)
+    if(IsBitSet(nodesx->super,i))
       {
-       index_t index,first;
+       SegmentX *segmentx;
+       int count=0,match;
+       Way prevway[MAX_SEG_PER_NODE];
 
-       index=first=IndexFirstSegmentX(segmentsx,i);
+       segmentx=FirstSegmentX(segmentsx,i,1);
 
-       while(index!=NO_SEGMENT)
+       while(segmentx)
          {
-          SegmentX *segmentx=LookupSegmentX(segmentsx,index,1);
           WayX *wayx=LookupWayX(waysx,segmentx->way,1);
 
           /* Check that this type of way hasn't already been routed */
 
-          if(index!=first)
-            {
-             index_t otherindex=first;
+          match=0;
 
-             while(otherindex!=NO_SEGMENT && otherindex!=index)
-               {
-                SegmentX *othersegmentx=LookupSegmentX(segmentsx,otherindex,2);
-                WayX *otherwayx=LookupWayX(waysx,othersegmentx->way,2);
+          if(count>0)
+            {
+             int j;
 
-                if(!WaysCompare(&otherwayx->way,&wayx->way))
+             for(j=0;j<count;j++)
+                if(!WaysCompare(&prevway[j],&wayx->way))
                   {
-                   wayx=NULL;
+                   match=1;
                    break;
                   }
-
-                otherindex=IndexNextSegmentX(segmentsx,otherindex,i);
-               }
             }
 
+          assert(count<MAX_SEG_PER_NODE); /* Only a limited amount of history stored. */
+
+          prevway[count++]=wayx->way;
+
           /* Route the way and store the super-segments. */
 
-          if(wayx)
+          if(!match)
             {
-             Results *results=FindRoutesWay(nodesx,segmentsx,waysx,i,&wayx->way,iteration);
+             Results *results=FindRoutesWay(nodesx,segmentsx,waysx,i,&wayx->way);
              Result *result=FirstResult(results);
 
              while(result)
                {
-                if(result->node!=i && nodesx->super[result->node]>iteration)
+                if(IsBitSet(nodesx->super,result->node) && result->segment!=NO_SEGMENT)
                   {
-                   if(wayx->way.type&Way_OneWay)
-                     {
+                   if(wayx->way.type&Way_OneWay && result->node!=i)
                       AppendSegment(supersegmentsx,segmentx->way,i,result->node,DISTANCE((distance_t)result->score)|ONEWAY_1TO2);
-                      AppendSegment(supersegmentsx,segmentx->way,result->node,i,DISTANCE((distance_t)result->score)|ONEWAY_2TO1);
-                     }
                    else
                       AppendSegment(supersegmentsx,segmentx->way,i,result->node,DISTANCE((distance_t)result->score));
 
@@ -246,26 +277,29 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
              FreeResultsList(results);
             }
 
-          index=IndexNextSegmentX(segmentsx,index,i);
+          segmentx=NextSegmentX(segmentsx,segmentx,i,1);
          }
 
        sn++;
 
        if(!(sn%10000))
-          printf_middle("Creating Super-Segments: Super-Nodes=%d Super-Segments=%d",sn,ss);
+          printf_middle("Creating Super-Segments: Super-Nodes=%"Pindex_t" Super-Segments=%"Pindex_t,sn,ss);
       }
    }
 
- /* Unmap from memory */
+ /* Unmap from memory / close the files */
 
 #if !SLIM
- segmentsx->xdata=UnmapFile(segmentsx->filename);
- waysx->xdata=UnmapFile(waysx->filename);
+ segmentsx->data=UnmapFile(segmentsx->filename);
+ waysx->data=UnmapFile(waysx->filename);
+#else
+ segmentsx->fd=CloseFile(segmentsx->fd);
+ waysx->fd=CloseFile(waysx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Created Super-Segments: Super-Nodes=%d Super-Segments=%d",sn,ss);
+ printf_last("Created Super-Segments: Super-Nodes=%"Pindex_t" Super-Segments=%"Pindex_t,sn,ss);
 
  return(supersegmentsx);
 }
@@ -274,33 +308,38 @@ SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,
 /*++++++++++++++++++++++++++++++++++++++
   Merge the segments and super-segments into a new segment list.
 
-  SegmentsX* MergeSuperSegments Returns a new set of merged segments.
+  SegmentsX *MergeSuperSegments Returns a new set of merged segments.
 
-  SegmentsX* segmentsx The set of segments to process.
+  SegmentsX *segmentsx The set of segments to use.
 
-  SegmentsX* supersegmentsx The set of super-segments to merge.
+  SegmentsX *supersegmentsx The set of super-segments to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-SegmentsX *MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx)
+SegmentsX *MergeSuperSegments(SegmentsX *segmentsx,SegmentsX *supersegmentsx)
 {
  index_t i,j;
- int m=0,a=0;
- SegmentsX* mergedsegmentsx;
+ index_t merged=0,added=0;
+ SegmentsX *mergedsegmentsx;
 
  mergedsegmentsx=NewSegmentList(0);
 
- if(segmentsx->number==0 || supersegmentsx->number==0)
+ if(segmentsx->number==0)
     return(mergedsegmentsx);
 
  /* Print the start message */
 
- printf_first("Merging: Segments=0 Super-Segments=0 Merged=0 Added=0");
+ printf_first("Merging Segments: Segments=0 Super=0 Merged=0 Added=0");
 
- /* Map into memory */
+ /* Map into memory / open the files */
 
 #if !SLIM
- segmentsx->xdata=MapFile(segmentsx->filename);
- supersegmentsx->xdata=MapFile(supersegmentsx->filename);
+ segmentsx->data=MapFile(segmentsx->filename);
+ if(supersegmentsx->number>0)
+    supersegmentsx->data=MapFile(supersegmentsx->filename);
+#else
+ segmentsx->fd=ReOpenFile(segmentsx->filename);
+ if(supersegmentsx->number>0)
+    supersegmentsx->fd=ReOpenFile(supersegmentsx->filename);
 #endif
 
  /* Loop through and create a new list of combined segments */
@@ -318,7 +357,7 @@ SegmentsX *MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx)
           segmentx->node2   ==supersegmentx->node2 &&
           segmentx->distance==supersegmentx->distance)
          {
-          m++;
+          merged++;
           j++;
           /* mark as super-segment and normal segment */
           super=1;
@@ -332,7 +371,7 @@ SegmentsX *MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx)
          {
           /* mark as super-segment */
           AppendSegment(mergedsegmentsx,supersegmentx->way,supersegmentx->node1,supersegmentx->node2,supersegmentx->distance|SEGMENT_SUPER);
-          a++;
+          added++;
           j++;
          }
        else
@@ -348,26 +387,31 @@ SegmentsX *MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx)
        AppendSegment(mergedsegmentsx,segmentx->way,segmentx->node1,segmentx->node2,segmentx->distance|SEGMENT_NORMAL);
 
     if(!((i+1)%10000))
-       printf_middle("Merging: Segments=%d Super-Segments=%d Merged=%d Added=%d",i+1,j,m,a);
+       printf_middle("Merging Segments: Segments=%"Pindex_t" Super=%"Pindex_t" Merged=%"Pindex_t" Added=%"Pindex_t,i+1,j,merged,added);
    }
 
- /* Unmap from memory */
+ /* Unmap from memory / close the files */
 
 #if !SLIM
- segmentsx->xdata=UnmapFile(segmentsx->filename);
- supersegmentsx->xdata=UnmapFile(supersegmentsx->filename);
+ segmentsx->data=UnmapFile(segmentsx->filename);
+ if(supersegmentsx->number>0)
+    supersegmentsx->data=UnmapFile(supersegmentsx->filename);
+#else
+ segmentsx->fd=CloseFile(segmentsx->fd);
+ if(supersegmentsx->number>0)
+    supersegmentsx->fd=CloseFile(supersegmentsx->fd);
 #endif
 
  /* Print the final message */
 
- printf_last("Merged: Segments=%d Super-Segments=%d Merged=%d Added=%d",segmentsx->number,supersegmentsx->number,m,a);
+ printf_last("Merged Segments: Segments=%"Pindex_t" Super=%"Pindex_t" Merged=%"Pindex_t" Added=%"Pindex_t,segmentsx->number,supersegmentsx->number,merged,added);
 
  return(mergedsegmentsx);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Find all routes from a specified node to any node in the specified list that follows a certain type of way.
+  Find all routes from a specified super-node to any other super-node that follows a certain type of way.
 
   Results *FindRoutesWay Returns a set of results.
 
@@ -379,29 +423,23 @@ SegmentsX *MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx)
 
   node_t start The start node.
 
-  Way *match The way that the route must match.
-
-  int iteration The current super-node / super-segment iteration number.
+  Way *match A template for the type of way that the route must follow.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static Results *FindRoutesWay(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,node_t start,Way *match,int iteration)
+static Results *FindRoutesWay(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,node_t start,Way *match)
 {
  Results *results;
  Queue *queue;
- index_t node1,node2;
  Result *result1,*result2;
- index_t index;
  WayX *wayx;
 
  /* Insert the first node into the queue */
 
- results=NewResultsList(8);
+ results=NewResultsList(4);
 
  queue=NewQueueList();
 
- result1=InsertResult(results,start);
-
- ZeroResult(result1);
+ result1=InsertResult(results,start,NO_SEGMENT);
 
  InsertInQueue(queue,result1);
 
@@ -409,56 +447,65 @@ static Results *FindRoutesWay(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,n
 
  while((result1=PopFromQueue(queue)))
    {
+    index_t node1;
+    SegmentX *segmentx;
+
     node1=result1->node;
 
-    index=IndexFirstSegmentX(segmentsx,node1);
+    segmentx=FirstSegmentX(segmentsx,node1,2); /* position 1 is already used */
 
-    while(index!=NO_SEGMENT)
+    while(segmentx)
       {
-       SegmentX *segmentx=LookupSegmentX(segmentsx,index,2); /* must use 2 here */
+       index_t node2,seg2;
        distance_t cumulative_distance;
 
-       if(segmentx->distance&ONEWAY_2TO1)
+       /* must not be one-way against the direction of travel */
+       if(IsOnewayTo(segmentx,node1))
           goto endloop;
 
-       node2=segmentx->node2;
+       node2=OtherNode(segmentx,node1);
+
+       seg2=IndexSegmentX(segmentsx,segmentx);
 
-       if(result1->prev==node2)
+       /* must not be a u-turn */
+       if(result1->segment==seg2)
           goto endloop;
 
        wayx=LookupWayX(waysx,segmentx->way,2);
 
+       /* must be the right type of way */
        if(WaysCompare(&wayx->way,match))
           goto endloop;
 
        cumulative_distance=(distance_t)result1->score+DISTANCE(segmentx->distance);
 
-       result2=FindResult(results,node2);
+       result2=FindResult(results,node2,seg2);
 
        if(!result2)                         /* New end node */
          {
-          result2=InsertResult(results,node2);
-          result2->prev=node1;
-          result2->next=NO_NODE;
+          result2=InsertResult(results,node2,seg2);
+          result2->prev=result1;
           result2->score=cumulative_distance;
           result2->sortby=cumulative_distance;
 
-          if(nodesx->super[node2]<=iteration)
+          /* don't route beyond a super-node. */
+          if(!IsBitSet(nodesx->super,node2))
              InsertInQueue(queue,result2);
          }
        else if(cumulative_distance<result2->score)
          {
-          result2->prev=node1;
+          result2->prev=result1;
           result2->score=cumulative_distance;
           result2->sortby=cumulative_distance;
 
-          if(nodesx->super[node2]<=iteration)
+          /* don't route beyond a super-node. */
+          if(!IsBitSet(nodesx->super,node2))
              InsertInQueue(queue,result2);
          }
 
       endloop:
 
-       index=IndexNextSegmentX(segmentsx,index,node1);
+       segmentx=NextSegmentX(segmentsx,segmentx,node1,2);
       }
    }
 
diff --git a/src/superx.h b/src/superx.h
index cb330df..dc95b81 100644
--- a/src/superx.h
+++ b/src/superx.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/superx.h,v 1.7 2009/09/06 15:51:09 amb Exp $
-
  Header for super-node and super-segment functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008,2009 Andrew M. Bishop
+ This file Copyright 2008-2011 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,11 +26,13 @@
 #include "typesx.h"
 
 
+/* Functions in superx.c */
+
 void ChooseSuperNodes(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx);
 
-SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int iteration);
+SegmentsX *CreateSuperSegments(NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx);
 
-SegmentsX* MergeSuperSegments(SegmentsX* segmentsx,SegmentsX* supersegmentsx);
+SegmentsX *MergeSuperSegments(SegmentsX *segmentsx,SegmentsX *supersegmentsx);
 
 
 #endif /* SUPERX_H */
diff --git a/src/tagging.c b/src/tagging.c
index 57e3cea..d3a85a5 100644
--- a/src/tagging.c
+++ b/src/tagging.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/tagging.c,v 1.5 2010/09/17 17:40:41 amb Exp $
-
  Load the tagging rules from a file and the functions for handling them.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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 +27,8 @@
 #include "files.h"
 #include "tagging.h"
 #include "xmlparse.h"
+#include "logging.h"
+#include "typesx.h"
 
 
 /* Global variables */
@@ -46,18 +46,20 @@ TaggingRule     *current_rule=NULL;
 
 /* Local functions */
 
-static void apply_actions(TaggingRule *rule,int match,TagList *input,TagList *output);
+static void apply_actions(TaggingRuleList *rules,TaggingRule *rule,int match,TagList *input,TagList *output,node_t id);
 
 
 /* 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 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 SetType_function(const char *_tag_,int _type_,const char *k,const char *v);
 
 
@@ -70,6 +72,13 @@ static xmltag SetType_tag=
                SetType_function,
                {NULL}};
 
+/*+ The UnsetType type tag. +*/
+static xmltag UnsetType_tag=
+              {"unset",
+               1, {"k"},
+               UnsetType_function,
+               {NULL}};
+
 /*+ The OutputType type tag. +*/
 static xmltag OutputType_tag=
               {"output",
@@ -77,19 +86,19 @@ static xmltag OutputType_tag=
                OutputType_function,
                {NULL}};
 
+/*+ The LogErrorType type tag. +*/
+static xmltag LogErrorType_tag=
+              {"logerror",
+               2, {"k","v"},
+               LogErrorType_function,
+               {NULL}};
+
 /*+ The IfType type tag. +*/
 static xmltag IfType_tag=
               {"if",
                2, {"k","v"},
                IfType_function,
-               {&SetType_tag,&OutputType_tag,NULL}};
-
-/*+ The RelationType type tag. +*/
-static xmltag RelationType_tag=
-              {"relation",
-               0, {NULL},
-               RelationType_function,
-               {&IfType_tag,NULL}};
+               {&SetType_tag,&UnsetType_tag,&OutputType_tag,&LogErrorType_tag,NULL}};
 
 /*+ The NodeType type tag. +*/
 static xmltag NodeType_tag=
@@ -105,6 +114,13 @@ static xmltag WayType_tag=
                WayType_function,
                {&IfType_tag,NULL}};
 
+/*+ The RelationType type tag. +*/
+static xmltag RelationType_tag=
+              {"relation",
+               0, {NULL},
+               RelationType_function,
+               {&IfType_tag,NULL}};
+
 /*+ The RoutinoTaggingType type tag. +*/
 static xmltag RoutinoTaggingType_tag=
               {"routino-tagging",
@@ -144,7 +160,28 @@ static xmltag *xml_toplevel_tags[]={&xmlDeclaration_tag,&RoutinoTaggingType_tag,
 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,0);
+    AppendTaggingAction(current_rule,k,v,TAGACTION_SET);
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  The function that is called when the UnsetType XSD type is seen
+
+  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 UnsetType_function(const char *_tag_,int _type_,const char *k)
+{
+ if(_type_&XMLPARSE_TAG_START)
+    AppendTaggingAction(current_rule,k,NULL,TAGACTION_UNSET);
 
  return(0);
 }
@@ -167,16 +204,16 @@ static int SetType_function(const char *_tag_,int _type_,const char *k,const cha
 static int OutputType_function(const char *_tag_,int _type_,const char *k,const char *v)
 {
  if(_type_&XMLPARSE_TAG_START)
-    AppendTaggingAction(current_rule,k,v,1);
+    AppendTaggingAction(current_rule,k,v,TAGACTION_OUTPUT);
 
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the IfType XSD type is seen
+  The function that is called when the LogErrorType XSD type is seen
 
-  int IfType_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.
 
@@ -187,31 +224,35 @@ static int OutputType_function(const char *_tag_,int _type_,const char *k,const
   const char *v The contents of the 'v' attribute (or NULL if not defined).
   ++++++++++++++++++++++++++++++++++++++*/
 
-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)
 {
  if(_type_&XMLPARSE_TAG_START)
-   {
-    current_rule=AppendTaggingRule(current_list,k,v);
-   }
+    AppendTaggingAction(current_rule,k,v,TAGACTION_LOGERROR);
 
  return(0);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The function that is called when the RelationType XSD type is seen
+  The function that is called when the IfType XSD type is seen
 
-  int RelationType_function Returns 0 if no error occured or something else otherwise.
+  int IfType_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 RelationType_function(const char *_tag_,int _type_)
+static int IfType_function(const char *_tag_,int _type_,const char *k,const char *v)
 {
  if(_type_&XMLPARSE_TAG_START)
-    current_list=&RelationRules;
+   {
+    current_rule=AppendTaggingRule(current_list,k,v);
+   }
 
  return(0);
 }
@@ -256,6 +297,25 @@ static int WayType_function(const char *_tag_,int _type_)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  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.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+static int RelationType_function(const char *_tag_,int _type_)
+{
+ if(_type_&XMLPARSE_TAG_START)
+    current_list=&RelationRules;
+
+ return(0);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   The function that is called when the RoutinoTaggingType XSD type is seen
 
   int RoutinoTaggingType_function Returns 0 if no error occured or something else otherwise.
@@ -301,6 +361,7 @@ static int WayType_function(const char *_tag_,int _type_)
 
 int ParseXMLTaggingRules(const char *filename)
 {
+ FILE *file;
  int retval;
 
  if(!ExistsFile(filename))
@@ -309,7 +370,7 @@ int ParseXMLTaggingRules(const char *filename)
     return(1);
    }
 
- FILE *file=fopen(filename,"r");
+ file=fopen(filename,"r");
 
  if(!file)
    {
@@ -329,6 +390,18 @@ int ParseXMLTaggingRules(const char *filename)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Delete the tagging rules loaded from the XML file.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DeleteXMLTaggingRules(void)
+{
+ DeleteTaggingRuleList(&NodeRules);
+ DeleteTaggingRuleList(&WayRules);
+ DeleteTaggingRuleList(&RelationRules);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Append a tagging rule to the list of rules.
 
   TaggingRule *AppendTaggingRule Returns the latest rule (the just added one).
@@ -373,17 +446,17 @@ TaggingRule *AppendTaggingRule(TaggingRuleList *rules,const char *k,const char *
 
   const char *v The tag value.
 
-  int output Set to 1 if this is an output rule.
+  int action Set to the type of action.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int output)
+void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int action)
 {
  if((rule->nactions%16)==0)
     rule->actions=(TaggingAction*)realloc((void*)rule->actions,(rule->nactions+16)*sizeof(TaggingAction));
 
  rule->nactions++;
 
- rule->actions[rule->nactions-1].output=output;
+ rule->actions[rule->nactions-1].action=action;
 
  if(k)
     rule->actions[rule->nactions-1].k=strcpy(malloc(strlen(k)+1),k);
@@ -398,6 +471,40 @@ void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int outpu
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Delete a tagging rule.
+
+  TaggingRuleList *rules The list of rules to be deleted.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DeleteTaggingRuleList(TaggingRuleList *rules)
+{
+ int i,j;
+
+ for(i=0;i<rules->nrules;i++)
+   {
+    if(rules->rules[i].k)
+       free(rules->rules[i].k);
+    if(rules->rules[i].v)
+       free(rules->rules[i].v);
+
+    for(j=0;j<rules->rules[i].nactions;j++)
+      {
+       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);
+      }
+
+    if(rules->rules[i].actions)
+       free(rules->rules[i].actions);
+   }
+
+ if(rules->rules)
+    free(rules->rules);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Create a new TagList structure.
 
   TagList *NewTagList Returns the new allocated TagList.
@@ -410,6 +517,32 @@ TagList *NewTagList(void)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Delete a tag list and the contents.
+
+  TagList *tags The list of tags to delete.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void DeleteTagList(TagList *tags)
+{
+ int i;
+
+ for(i=0;i<tags->ntags;i++)
+   {
+    if(tags->k[i]) free(tags->k[i]);
+    if(tags->v[i]) free(tags->v[i]);
+   }
+
+ if(tags->ntags)
+   {
+    free(tags->k);
+    free(tags->v);
+   }
+
+ free(tags);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Append a tag to the list of tags.
 
   TagList *tags The list of tags to add to.
@@ -465,28 +598,36 @@ void ModifyTag(TagList *tags,const char *k,const char *v)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Delete a tag list and the contents.
+  Delete an existing tag from the list of tags.
 
-  TagList *tags The list of tags to delete.
+  TagList *tags The list of tags to modify.
+
+  const char *k The tag key.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void DeleteTagList(TagList *tags)
+void DeleteTag(TagList *tags,const char *k)
 {
- int i;
+ int i,j;
 
  for(i=0;i<tags->ntags;i++)
-   {
-    if(tags->k[i]) free(tags->k[i]);
-    if(tags->v[i]) free(tags->v[i]);
-   }
+    if(!strcmp(tags->k[i],k))
+      {
+       if(tags->k[i]) free(tags->k[i]);
+       if(tags->v[i]) free(tags->v[i]);
 
- if(tags->ntags)
-   {
-    free(tags->k);
-    free(tags->v);
-   }
+       for(j=i+1;j<tags->ntags;j++)
+         {
+          tags->k[j-1]=tags->k[j];
+          tags->v[j-1]=tags->v[j];
+         }
 
- free(tags);
+       tags->ntags--;
+
+       tags->k[tags->ntags]=NULL;
+       tags->v[tags->ntags]=NULL;
+
+       return;
+      }
 }
 
 
@@ -498,9 +639,11 @@ void DeleteTagList(TagList *tags)
   TaggingRuleList *rules The tagging rules to apply.
 
   TagList *tags The tags to be modified.
+
+  node_t id The ID of the node, way or relation.
   ++++++++++++++++++++++++++++++++++++++*/
 
-TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags)
+TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags,node_t id)
 {
  TagList *result=NewTagList();
  int i,j;
@@ -511,24 +654,24 @@ TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags)
       {
        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[i],j,tags,result);
+             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[i],j,tags,result);
+             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[i],j,tags,result);
+             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[i],j,tags,result);
+          apply_actions(rules,&rules->rules[i],j,tags,result,id);
       }
    }
 
@@ -539,6 +682,8 @@ TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags)
 /*++++++++++++++++++++++++++++++++++++++
   Apply a set of actions to a matching tag.
 
+  TaggingRuleList *rules The tagging rules to apply.
+
   TaggingRule *rule The rule that matched (containing the actions).
 
   int match The matching tag number.
@@ -546,9 +691,11 @@ TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags)
   TagList *input The input tags.
 
   TagList *output The output tags.
+
+  node_t id The ID of the node, way or relation.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static void apply_actions(TaggingRule *rule,int match,TagList *input,TagList *output)
+static void apply_actions(TaggingRuleList *rules,TaggingRule *rule,int match,TagList *input,TagList *output,node_t id)
 {
  int i;
  
@@ -566,9 +713,20 @@ static void apply_actions(TaggingRule *rule,int match,TagList *input,TagList *ou
     else
        v=input->v[match];
 
-    if(rule->actions[i].output)
-       ModifyTag(output,k,v);
-    else
+    if(rule->actions[i].action==TAGACTION_SET)
        ModifyTag(input,k,v);
+    if(rule->actions[i].action==TAGACTION_UNSET)
+       DeleteTag(input,k);
+    if(rule->actions[i].action==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).\n",id,k,v);
+       if(rules==&WayRules)
+          logerror("Way %"Pway_t" has an unrecognised tag value '%s' = '%s' (in tagging rules).\n",id,k,v);
+       if(rules==&RelationRules)
+          logerror("Relation %"Prelation_t" has an unrecognised tag value '%s' = '%s' (in tagging rules).\n",id,k,v);
+      }
    }
 }
diff --git a/src/tagging.h b/src/tagging.h
index 4dfdbb8..189e9d7 100644
--- a/src/tagging.h
+++ b/src/tagging.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/tagging.h,v 1.2 2010/05/23 10:18:59 amb Exp $
-
  The data types for the tagging rules.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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,13 +22,23 @@
 #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
+
 
 /* Data types */
 
 /*+ A structure to contain the tagging action. +*/
 typedef struct _TaggingAction
 {
- int output;                    /*+ A flag to indicate if the output tags or input tags are to be changed. +*/
+ int action;                    /*+ A flag to indicate the type of action. +*/
 
  char *k;                       /*+ The tag key (or NULL). +*/
  char *v;                       /*+ The tag value (or NULL). +*/
@@ -70,26 +78,30 @@ typedef struct _TagList
  TagList;
 
 
-/* Variables */
+/* Global variables */
 
 extern TaggingRuleList NodeRules;
 extern TaggingRuleList WayRules;
 extern TaggingRuleList RelationRules;
 
 
-/* Functions */
+/* 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 output);
+void AppendTaggingAction(TaggingRule *rule,const char *k,const char *v,int action);
+void DeleteTaggingRuleList(TaggingRuleList *rules);
 
 TagList *NewTagList(void);
+void DeleteTagList(TagList *tags);
+
 void AppendTag(TagList *tags,const char *k,const char *v);
 void ModifyTag(TagList *tags,const char *k,const char *v);
-void DeleteTagList(TagList *tags);
+void DeleteTag(TagList *tags,const char *k);
 
-TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags);
+TagList *ApplyTaggingRules(TaggingRuleList *rules,TagList *tags,node_t id);
 
 
 #endif /* TAGGING_H */
diff --git a/src/tagmodifier.c b/src/tagmodifier.c
index e01b490..8e37186 100644
--- a/src/tagmodifier.c
+++ b/src/tagmodifier.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/tagmodifier.c,v 1.8 2010/11/13 14:22:28 amb Exp $
-
  Test application for OSM XML file parser / tagging rule testing.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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
@@ -36,7 +34,10 @@
 
 /* Local variables */
 
-static long nnodes=0,nways=0,nrelations=0;
+static unsigned long nnodes=0;
+static unsigned long nways=0;
+static unsigned long nrelations=0;
+
 TagList *current_tags=NULL;
 
 
@@ -254,19 +255,29 @@ 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;
+
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
+
     nnodes++;
 
-    if(!(nnodes%1000))
-       fprintf_middle(stderr,"Reading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+    if(!(nnodes%10000))
+       fprintf_middle(stderr,"Reading: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",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;
+    assert((long long)node_id==llid);      /* check node id can be stored in node_t data type. */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&NodeRules,current_tags);
+    TagList *result=ApplyTaggingRules(&NodeRules,current_tags,node_id);
     int i;
 
     for(i=0;i<result->ntags;i++)
@@ -370,19 +381,30 @@ 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;
+
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
+
     nways++;
 
     if(!(nways%1000))
-       fprintf_middle(stderr,"Reading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+       fprintf_middle(stderr,"Reading: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",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;
+    assert((long long)way_id==llid);   /* check way id can be stored in way_t data type. */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&WayRules,current_tags);
+    TagList *result=ApplyTaggingRules(&WayRules,current_tags,way_id);
     int i;
 
     for(i=0;i<result->ntags;i++)
@@ -436,19 +458,30 @@ 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;
+
  if(_type_&XMLPARSE_TAG_START)
    {
+    long long llid;
+
     nrelations++;
 
     if(!(nrelations%1000))
-       fprintf_middle(stderr,"Reading: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+       fprintf_middle(stderr,"Reading: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",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;
+    assert((long long)relation_id==llid);   /* check relation id can be stored in relation_t data type. */
    }
 
  if(_type_&XMLPARSE_TAG_END)
    {
-    TagList *result=ApplyTaggingRules(&RelationRules,current_tags);
+    TagList *result=ApplyTaggingRules(&RelationRules,current_tags,relation_id);
     int i;
 
     for(i=0;i<result->ntags;i++)
@@ -525,7 +558,7 @@ static int xmlDeclaration_function(const char *_tag_,int _type_,const char *vers
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  The main program for the planetsplitter.
+  The main program for the tagmodifier.
   ++++++++++++++++++++++++++++++++++++++*/
 
 int main(int argc,char **argv)
@@ -600,7 +633,7 @@ int main(int argc,char **argv)
 
  retval=ParseXML(file,xml_toplevel_tags,XMLPARSE_UNKNOWN_ATTR_IGNORE);
 
- fprintf_last(stderr,"Read: Lines=%ld Nodes=%ld Ways=%ld Relations=%ld",ParseXML_LineNumber(),nnodes,nways,nrelations);
+ fprintf_last(stderr,"Read: Lines=%llu Nodes=%lu Ways=%lu Relations=%lu",ParseXML_LineNumber(),nnodes,nways,nrelations);
 
  /* Tidy up */
 
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644
index 0000000..e708bc7
--- /dev/null
+++ b/src/test/Makefile
@@ -0,0 +1,84 @@
+# Test cases Makefile
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2011 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/>.
+#
+
+# Routino executables
+
+EXE=../planetsplitter ../planetsplitter-slim \
+    ../router ../router-slim \
+    ../filedumper ../filedumper-slim
+
+# Compilation targets
+
+O=$(notdir $(wildcard *.osm))
+S=$(foreach f,$(O),$(addsuffix .sh,$(basename $f)))
+
+########
+
+all :
+	@true
+
+########
+
+exe :
+	cd .. && $(MAKE)
+
+########
+
+test : exe
+	@status=true ;\
+	[ -d fat ] || mkdir fat ;\
+	for script in $(S); do \
+	   echo "" ;\
+	   echo "Testing: $$script (non-slim) ... " ;\
+	   if ./$$script fat; then echo "... passed"; else echo "... FAILED"; status=false; fi ;\
+	done ;\
+	[ -d slim ] || mkdir slim ;\
+	for script in $(S); do \
+	   echo "" ;\
+	   echo "Testing: $$script (slim) ... " ;\
+	   if ./$$script slim; then echo "... passed"; else echo "... FAILED"; status=false; fi ;\
+	done ;\
+	echo "" ;\
+	if $$status; then echo "Success: all tests passed"; else echo "Warning: Some tests FAILED"; fi ;\
+	$$status || exit 1 ;\
+	echo "" ;\
+	echo "Comparing: slim and non-slim results ... " ;\
+	if diff -q -r slim fat; then echo "... matched"; else echo "... match FAILED"; status=false; fi ;\
+	echo "" ;\
+	if $$status; then echo "Success: slim and non-slim results match"; else echo "Warning: slim and non-slim results are different - FAILED"; fi ;\
+	$$status
+
+########
+
+clean:
+	rm -rf slim
+	rm -rf fat
+	rm -f *.log
+	rm -f core
+	rm -f *~
+
+########
+
+distclean: clean
+	@true
+
+########
+
+.FORCE :
diff --git a/src/test/a-b-c.sh b/src/test/a-b-c.sh
new file mode 100755
index 0000000..c8e0c8a
--- /dev/null
+++ b/src/test/a-b-c.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+# Exit on error
+
+set -e
+
+# Test name
+
+name=`basename $0 .sh`
+
+# Slim or non-slim
+
+if [ "$1" = "slim" ]; then
+    slim="-slim"
+    dir="slim"
+else
+    slim=""
+    dir="fat"
+fi
+
+[ -d $dir ] || mkdir $dir
+
+# Run the programs under a run-time debugger
+
+debugger=valgrind
+debugger=
+
+# Name related options
+
+osm=$name.osm
+log=$name$slim.log
+
+option_prefix="--prefix=$name"
+option_dir="--dir=$dir"
+
+# Generic program options
+
+option_planetsplitter="--loggable --tagging=../../xml/routino-tagging.xml --errorlog"
+option_filedumper="--dump-osm"
+option_router="--loggable --transport=motorcar --profiles=../../xml/routino-profiles.xml --translations=copyright.xml"
+
+# Run planetsplitter
+
+echo "Running planetsplitter"
+
+echo ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm > $log
+$debugger ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm >> $log
+
+# Run filedumper
+
+echo "Running filedumper"
+
+echo ../filedumper$slim $option_dir $option_prefix $option_filedumper >> $log
+$debugger ../filedumper$slim $option_dir $option_prefix $option_filedumper > $dir/$osm
+
+# Waypoints
+
+waypoints=`perl waypoints.pl $osm list`
+
+# Run the router for each waypoint
+
+for waypoint in $waypoints; do
+
+    case $waypoint in
+        *a) waypoint=`echo $waypoint | sed -e 's%a$%%'` ;;
+        *) continue ;;
+    esac
+
+    echo "Running router : $waypoint"
+
+    waypoint_a=`perl waypoints.pl $osm ${waypoint}a 1`
+    waypoint_b=`perl waypoints.pl $osm ${waypoint}b 2`
+    waypoint_c=`perl waypoints.pl $osm ${waypoint}c 3`
+
+    [ -d $dir/$name-$waypoint ] || mkdir $dir/$name-$waypoint
+
+    echo ../router$slim $option_dir $option_prefix $option_osm $option_router $waypoint_a $waypoint_b $waypoint_c >> $log
+    $debugger ../router$slim $option_dir $option_prefix $option_osm $option_router $waypoint_a $waypoint_b $waypoint_c >> $log
+
+    mv shortest* $dir/$name-$waypoint
+
+    echo cmp $dir/$name-$waypoint/shortest-all.txt expected/$name-$waypoint.txt >> $log
+    cmp $dir/$name-$waypoint/shortest-all.txt expected/$name-$waypoint.txt >> $log
+
+done
diff --git a/src/test/a-b.sh b/src/test/a-b.sh
new file mode 100755
index 0000000..0361ab6
--- /dev/null
+++ b/src/test/a-b.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# Exit on error
+
+set -e
+
+# Test name
+
+name=`basename $0 .sh`
+
+# Slim or non-slim
+
+if [ "$1" = "slim" ]; then
+    slim="-slim"
+    dir="slim"
+else
+    slim=""
+    dir="fat"
+fi
+
+[ -d $dir ] || mkdir $dir
+
+# Run the programs under a run-time debugger
+
+debugger=valgrind
+debugger=
+
+# Name related options
+
+osm=$name.osm
+log=$name$slim.log
+
+option_prefix="--prefix=$name"
+option_dir="--dir=$dir"
+
+# Generic program options
+
+option_planetsplitter="--loggable --tagging=../../xml/routino-tagging.xml --errorlog"
+option_filedumper="--dump-osm"
+option_router="--loggable --transport=motorcar --profiles=../../xml/routino-profiles.xml --translations=copyright.xml"
+
+# Run planetsplitter
+
+echo "Running planetsplitter"
+
+echo ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm > $log
+$debugger ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm >> $log
+
+# Run filedumper
+
+echo "Running filedumper"
+
+echo ../filedumper$slim $option_dir $option_prefix $option_filedumper >> $log
+$debugger ../filedumper$slim $option_dir $option_prefix $option_filedumper > $dir/$osm
+
+# Waypoints
+
+waypoints=`perl waypoints.pl $osm list`
+
+# Run the router for each waypoint
+
+for waypoint in $waypoints; do
+
+    case $waypoint in
+        *a) waypoint=`echo $waypoint | sed -e 's%a$%%'` ;;
+        *) continue ;;
+    esac
+
+    echo "Running router : $waypoint"
+
+    waypoint_a=`perl waypoints.pl $osm ${waypoint}a 1`
+    waypoint_b=`perl waypoints.pl $osm ${waypoint}b 2`
+
+    [ -d $dir/$name-$waypoint ] || mkdir $dir/$name-$waypoint
+
+    echo ../router$slim $option_dir $option_prefix $option_osm $option_router $waypoint_a $waypoint_b >> $log
+    $debugger ../router$slim $option_dir $option_prefix $option_osm $option_router $waypoint_a $waypoint_b >> $log
+
+    mv shortest* $dir/$name-$waypoint
+
+    echo cmp $dir/$name-$waypoint/shortest-all.txt expected/$name-$waypoint.txt >> $log
+    cmp $dir/$name-$waypoint/shortest-all.txt expected/$name-$waypoint.txt >> $log
+
+done
diff --git a/src/test/copyright.xml b/src/test/copyright.xml
new file mode 100644
index 0000000..8cbc553
--- /dev/null
+++ b/src/test/copyright.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- ============================================================
+     An XML format file containing Routino output translations.
+
+     Part of the Routino routing software.
+     ============================================================
+     This file Copyright 2010-2011 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-translations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                      xsi:noNamespaceSchemaLocation="http://www.routino.org/xml/routino-translations.xsd">
+
+  <language lang="en">
+
+    <!-- Copyright of the data being routed, not of this file  -->
+    <copyright>
+      <creator string="Creator" text="Routino - http://www.routino.org/" />
+      <source  string="Source"  text="Routino test cases - (c) Andrew M. Bishop" />
+      <license string="License" text="GNU Affero General Public License v3 or later" />
+    </copyright>
+
+  </language>
+
+</routino-translations>
diff --git a/src/test/dead-ends.osm b/src/test/dead-ends.osm
new file mode 100644
index 0000000..f3b1684
--- /dev/null
+++ b/src/test/dead-ends.osm
@@ -0,0 +1,178 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='257' visible='true' version='1' lat='-0.21809858996693385' lon='-0.5191028643883174' />
+  <node id='259' visible='true' version='1' lat='-0.22070178828290346' lon='-0.5194778136559395' />
+  <node id='261' visible='true' version='1' lat='-0.22026348707492585' lon='-0.5193082654147766'>
+    <tag k='name' v='WP01' />
+  </node>
+  <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' />
+  </node>
+  <node id='271' visible='true' version='1' lat='-0.21838648826301083' lon='-0.5191374278276153'>
+    <tag k='name' v='WP04' />
+  </node>
+  <node id='273' visible='true' version='1' lat='-0.21967045850547667' lon='-0.5160319121186099'>
+    <tag k='name' v='WP10' />
+  </node>
+  <node id='275' visible='true' version='1' lat='-0.22001919809793816' lon='-0.5158468652583799'>
+    <tag k='name' v='WP09' />
+  </node>
+  <node id='277' visible='true' version='1' lat='-0.2203608205479482' lon='-0.5159607402492906'>
+    <tag k='name' v='WP08' />
+  </node>
+  <node id='279' visible='true' version='1' lat='-0.21999072956008625' lon='-0.5175122870004505'>
+    <tag k='name' v='WP06' />
+  </node>
+  <node id='281' visible='true' version='1' lat='-0.21962194811581368' lon='-0.5176842951742215'>
+    <tag k='name' v='WP07' />
+  </node>
+  <node id='283' visible='true' version='1' lat='-0.21954697775967735' lon='-0.5139082939501117'>
+    <tag k='name' v='WPfinish' />
+  </node>
+  <node id='285' visible='true' version='1' lat='-0.21952490240955844' lon='-0.5211328425348463'>
+    <tag k='name' v='WPstart' />
+  </node>
+  <node id='311' visible='true' version='1' lat='-0.21873259914979318' lon='-0.5139721830599432' />
+  <node id='315' visible='true' version='1' lat='-0.21894473644285053' lon='-0.5177120426858725' />
+  <node id='317' visible='true' version='1' lat='-0.21881956658439772' lon='-0.5209796816567882' />
+  <node id='319' visible='true' version='1' lat='-0.22034447159225826' lon='-0.5140417639627524' />
+  <node id='323' visible='true' version='1' lat='-0.22029573308467093' lon='-0.5176333429804723'>
+    <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' >
+    <tag k='name' v='WP11' />
+  </node>
+  <node id='331' visible='true' version='1' lat='-0.21934091992851262' lon='-0.5162056682348201' />
+  <node id='333' visible='true' version='1' lat='-0.2207820589348417' lon='-0.5161366988387935' />
+  <node id='335' visible='true' version='1' lat='-0.21845388180276712' lon='-0.5174706370059244' />
+  <node id='337' visible='true' version='1' lat='-0.21930182171484336' lon='-0.5178876596777171' />
+  <node id='339' visible='true' version='1' lat='-0.22073883963391433' lon='-0.5178047502907365' />
+  <node id='341' visible='true' version='1' lat='-0.22081738237868836' lon='-0.5140621784576588' />
+  <node id='343' visible='true' version='1' lat='-0.2206667536990629' lon='-0.5210598093726763' />
+  <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' />
+  <way id='377' visible='true' version='1'>
+    <nd ref='259' />
+    <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' />
+  </way>
+  <way id='383' visible='true' version='1'>
+    <nd ref='317' />
+    <nd ref='257' />
+    <nd ref='315' />
+    <nd ref='329' />
+    <nd ref='311' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='385' visible='true' version='1'>
+    <nd ref='325' />
+    <nd ref='261' />
+    <nd ref='323' />
+    <nd ref='277' />
+    <nd ref='319' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='387' visible='true' version='1'>
+    <nd ref='333' />
+    <nd ref='277' />
+    <nd ref='275' />
+    <nd ref='331' />
+    <nd ref='329' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='dead-end 3' />
+  </way>
+  <way id='389' visible='true' version='1'>
+    <nd ref='339' />
+    <nd ref='323' />
+    <nd ref='279' />
+    <nd ref='337' />
+    <nd ref='315' />
+    <nd ref='335' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='dead-end 2' />
+  </way>
+  <way id='391' visible='true' version='1'>
+    <nd ref='343' />
+    <nd ref='259' />
+    <nd ref='339' />
+    <nd ref='333' />
+    <nd ref='341' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </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='341' />
+    <nd ref='319' />
+    <nd ref='311' />
+    <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='343' />
+    <nd ref='325' />
+    <nd ref='317' />
+    <nd ref='373' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 1' />
+  </way>
+</osm>
diff --git a/src/test/dead-ends.sh b/src/test/dead-ends.sh
new file mode 120000
index 0000000..b627b17
--- /dev/null
+++ b/src/test/dead-ends.sh
@@ -0,0 +1 @@
+start-1-finish.sh
\ No newline at end of file
diff --git a/src/test/expected/dead-ends-WP01.txt b/src/test/expected/dead-ends-WP01.txt
new file mode 100644
index 0000000..f5d1551
--- /dev/null
+++ b/src/test/expected/dead-ends-WP01.txt
@@ -0,0 +1,17 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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.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*	Inter	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
new file mode 100644
index 0000000..7fc032d
--- /dev/null
+++ b/src/test/expected/dead-ends-WP02.txt
@@ -0,0 +1,19 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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.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*	Inter	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
new file mode 100644
index 0000000..4b62948
--- /dev/null
+++ b/src/test/expected/dead-ends-WP03.txt
@@ -0,0 +1,21 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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.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.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*	Inter	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
new file mode 100644
index 0000000..9c7d98b
--- /dev/null
+++ b/src/test/expected/dead-ends-WP04.txt
@@ -0,0 +1,25 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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.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.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*	Inter	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
new file mode 100644
index 0000000..b358b53
--- /dev/null
+++ b/src/test/expected/dead-ends-WP05.txt
@@ -0,0 +1,17 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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*	Inter	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
new file mode 100644
index 0000000..0c363bd
--- /dev/null
+++ b/src/test/expected/dead-ends-WP06.txt
@@ -0,0 +1,19 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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*	Inter	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
new file mode 100644
index 0000000..15a272f
--- /dev/null
+++ b/src/test/expected/dead-ends-WP07.txt
@@ -0,0 +1,21 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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.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*	Inter	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
new file mode 100644
index 0000000..68d5467
--- /dev/null
+++ b/src/test/expected/dead-ends-WP08.txt
@@ -0,0 +1,17 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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*	Inter	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
new file mode 100644
index 0000000..935b602
--- /dev/null
+++ b/src/test/expected/dead-ends-WP09.txt
@@ -0,0 +1,19 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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*	Inter	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
new file mode 100644
index 0000000..c409a01
--- /dev/null
+++ b/src/test/expected/dead-ends-WP10.txt
@@ -0,0 +1,21 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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.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*	Inter	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
new file mode 100644
index 0000000..4b7ee2f
--- /dev/null
+++ b/src/test/expected/dead-ends-WP11.txt
@@ -0,0 +1,23 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219535	  -0.521016	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -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*	Inter	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/loops-WP01.txt b/src/test/expected/loops-WP01.txt
new file mode 100644
index 0000000..e6e565f
--- /dev/null
+++ b/src/test/expected/loops-WP01.txt
@@ -0,0 +1,23 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220280	  -0.519256	      15*	Waypt	0.052	 0.07	 0.33	  0.3	 48	  29	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.044	 0.06	 0.37	  0.4	 48	  21	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.077	 0.10	 0.45	  0.5	 48	 345	loop 1
+ -0.218764	  -0.519471	      12 	Inter	0.056	 0.07	 0.51	  0.6	 48	 338	loop 1
+ -0.218380	  -0.519134	      16 	Inter	0.056	 0.07	 0.56	  0.6	 48	  41	loop 1
+ -0.218776	  -0.518838	      18 	Inter	0.055	 0.07	 0.62	  0.7	 48	 143	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.071	 0.09	 0.69	  0.8	 48	 224	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.077	 0.10	 0.77	  0.9	 48	 165	loop 1
+ -0.220695	  -0.519489	      11*	Junct	0.052	 0.07	 0.82	  0.9	 48	 209	loop 1
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 1.00	  1.2	 48	  91	high street
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.20	  1.4	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.25	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.33	  1.5	 96	   2	main 2
diff --git a/src/test/expected/loops-WP02.txt b/src/test/expected/loops-WP02.txt
new file mode 100644
index 0000000..16cf424
--- /dev/null
+++ b/src/test/expected/loops-WP02.txt
@@ -0,0 +1,23 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220280	  -0.519256	      15*	Junct	0.052	 0.07	 0.33	  0.3	 48	  29	loop 1
+ -0.219910	  -0.519112	      17 	Waypt	0.044	 0.06	 0.37	  0.4	 48	  21	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.077	 0.10	 0.45	  0.5	 48	 345	loop 1
+ -0.218764	  -0.519471	      12 	Inter	0.056	 0.07	 0.51	  0.6	 48	 338	loop 1
+ -0.218380	  -0.519134	      16 	Inter	0.056	 0.07	 0.56	  0.6	 48	  41	loop 1
+ -0.218776	  -0.518838	      18 	Inter	0.055	 0.07	 0.62	  0.7	 48	 143	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.071	 0.09	 0.69	  0.8	 48	 224	loop 1
+ -0.220280	  -0.519256	      15*	Junct	0.044	 0.06	 0.73	  0.8	 48	 201	loop 1
+ -0.220695	  -0.519489	      11*	Junct	0.052	 0.07	 0.79	  0.9	 48	 209	loop 1
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 0.97	  1.1	 48	  91	high street
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.17	  1.4	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.22	  1.4	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.30	  1.5	 96	   2	main 2
diff --git a/src/test/expected/loops-WP03.txt b/src/test/expected/loops-WP03.txt
new file mode 100644
index 0000000..453668d
--- /dev/null
+++ b/src/test/expected/loops-WP03.txt
@@ -0,0 +1,25 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220280	  -0.519256	      15*	Junct	0.052	 0.07	 0.33	  0.3	 48	  29	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.044	 0.06	 0.37	  0.4	 48	  21	loop 1
+ -0.219578	  -0.519198	      -2 	Waypt	0.038	 0.05	 0.41	  0.4	 48	 345	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.039	 0.05	 0.45	  0.5	 48	 345	loop 1
+ -0.218764	  -0.519471	      12 	Inter	0.056	 0.07	 0.51	  0.6	 48	 338	loop 1
+ -0.218380	  -0.519134	      16 	Inter	0.056	 0.07	 0.56	  0.6	 48	  41	loop 1
+ -0.218776	  -0.518838	      18 	Inter	0.055	 0.07	 0.62	  0.7	 48	 143	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.071	 0.09	 0.69	  0.8	 48	 224	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.077	 0.10	 0.77	  0.9	 48	 165	loop 1
+ -0.220280	  -0.519256	      15*	Junct	0.044	 0.06	 0.81	  0.9	 48	 201	loop 1
+ -0.220695	  -0.519489	      11*	Junct	0.052	 0.07	 0.86	  1.0	 48	 209	loop 1
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 1.05	  1.2	 48	  91	high street
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.25	  1.5	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.30	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.38	  1.6	 96	   2	main 2
diff --git a/src/test/expected/loops-WP04.txt b/src/test/expected/loops-WP04.txt
new file mode 100644
index 0000000..5460c2f
--- /dev/null
+++ b/src/test/expected/loops-WP04.txt
@@ -0,0 +1,25 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220280	  -0.519256	      15*	Junct	0.052	 0.07	 0.33	  0.3	 48	  29	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.044	 0.06	 0.37	  0.4	 48	  21	loop 1
+ -0.219237	  -0.519286	      13*	Inter	0.077	 0.10	 0.45	  0.5	 48	 345	loop 1
+ -0.218997	  -0.519053	      -2 	Waypt	0.037	 0.04	 0.49	  0.5	 48	  44	loop 1
+ -0.218776	  -0.518838	      18 	Inter	0.034	 0.04	 0.52	  0.6	 48	  44	loop 1
+ -0.218380	  -0.519134	      16 	Inter	0.055	 0.07	 0.58	  0.6	 48	 323	loop 1
+ -0.218764	  -0.519471	      12 	Inter	0.056	 0.07	 0.63	  0.7	 48	 221	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.056	 0.07	 0.69	  0.8	 48	 158	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.077	 0.10	 0.77	  0.9	 48	 165	loop 1
+ -0.220280	  -0.519256	      15*	Junct	0.044	 0.06	 0.81	  0.9	 48	 201	loop 1
+ -0.220695	  -0.519489	      11*	Junct	0.052	 0.07	 0.86	  1.0	 48	 209	loop 1
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 1.05	  1.2	 48	  91	high street
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.25	  1.5	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.30	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.38	  1.6	 96	   2	main 2
diff --git a/src/test/expected/loops-WP05.txt b/src/test/expected/loops-WP05.txt
new file mode 100644
index 0000000..c207bb3
--- /dev/null
+++ b/src/test/expected/loops-WP05.txt
@@ -0,0 +1,24 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220280	  -0.519256	      15*	Junct	0.052	 0.07	 0.33	  0.3	 48	  29	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.044	 0.06	 0.37	  0.4	 48	  21	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.077	 0.10	 0.45	  0.5	 48	 345	loop 1
+ -0.218764	  -0.519471	      12 	Inter	0.056	 0.07	 0.51	  0.6	 48	 338	loop 1
+ -0.218380	  -0.519134	      16 	Waypt	0.056	 0.07	 0.56	  0.6	 48	  41	loop 1
+ -0.218776	  -0.518838	      18 	Inter	0.055	 0.07	 0.62	  0.7	 48	 143	loop 1
+ -0.219237	  -0.519286	      13*	Junct	0.071	 0.09	 0.69	  0.8	 48	 224	loop 1
+ -0.219910	  -0.519112	      17 	Inter	0.077	 0.10	 0.77	  0.9	 48	 165	loop 1
+ -0.220280	  -0.519256	      15*	Junct	0.044	 0.06	 0.81	  0.9	 48	 201	loop 1
+ -0.220695	  -0.519489	      11*	Junct	0.052	 0.07	 0.86	  1.0	 48	 209	loop 1
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 1.05	  1.2	 48	  91	high street
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.25	  1.5	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.30	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.38	  1.6	 96	   2	main 2
diff --git a/src/test/expected/loops-WP06.txt b/src/test/expected/loops-WP06.txt
new file mode 100644
index 0000000..07593ec
--- /dev/null
+++ b/src/test/expected/loops-WP06.txt
@@ -0,0 +1,23 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 0.47	  0.5	 48	  91	high street
+ -0.220301	  -0.517576	      22*	Waypt	0.054	 0.07	 0.52	  0.6	 48	  27	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.044	 0.06	 0.56	  0.6	 48	  26	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.078	 0.10	 0.64	  0.7	 48	 345	loop 2
+ -0.218794	  -0.517763	      20 	Inter	0.056	 0.07	 0.70	  0.8	 48	 338	loop 2
+ -0.218417	  -0.517462	      23*	Junct	0.053	 0.07	 0.75	  0.9	 48	  38	loop 2
+ -0.218805	  -0.517131	      25 	Inter	0.056	 0.07	 0.81	  0.9	 48	 139	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.071	 0.09	 0.88	  1.0	 48	 224	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.078	 0.10	 0.95	  1.1	 48	 165	loop 2
+ -0.220739	  -0.517801	      19*	Junct	0.054	 0.07	 1.01	  1.2	 48	 207	loop 2
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.21	  1.4	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.26	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.34	  1.5	 96	   2	main 2
diff --git a/src/test/expected/loops-WP07.txt b/src/test/expected/loops-WP07.txt
new file mode 100644
index 0000000..30841ed
--- /dev/null
+++ b/src/test/expected/loops-WP07.txt
@@ -0,0 +1,23 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 0.47	  0.5	 48	  91	high street
+ -0.220301	  -0.517576	      22*	Junct	0.054	 0.07	 0.52	  0.6	 48	  27	loop 2
+ -0.219946	  -0.517397	      24 	Waypt	0.044	 0.06	 0.56	  0.6	 48	  26	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.078	 0.10	 0.64	  0.7	 48	 345	loop 2
+ -0.218794	  -0.517763	      20 	Inter	0.056	 0.07	 0.70	  0.8	 48	 338	loop 2
+ -0.218417	  -0.517462	      23*	Junct	0.053	 0.07	 0.75	  0.9	 48	  38	loop 2
+ -0.218805	  -0.517131	      25 	Inter	0.056	 0.07	 0.81	  0.9	 48	 139	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.071	 0.09	 0.88	  1.0	 48	 224	loop 2
+ -0.220301	  -0.517576	      22*	Junct	0.044	 0.06	 0.92	  1.1	 48	 206	loop 2
+ -0.220739	  -0.517801	      19*	Junct	0.054	 0.07	 0.97	  1.1	 48	 207	loop 2
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.17	  1.4	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.22	  1.4	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.30	  1.5	 96	   2	main 2
diff --git a/src/test/expected/loops-WP08.txt b/src/test/expected/loops-WP08.txt
new file mode 100644
index 0000000..34660a0
--- /dev/null
+++ b/src/test/expected/loops-WP08.txt
@@ -0,0 +1,25 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 0.47	  0.5	 48	  91	high street
+ -0.220301	  -0.517576	      22*	Junct	0.054	 0.07	 0.52	  0.6	 48	  27	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.044	 0.06	 0.56	  0.6	 48	  26	loop 2
+ -0.219597	  -0.517490	      -2 	Waypt	0.040	 0.05	 0.60	  0.7	 48	 345	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.038	 0.05	 0.64	  0.7	 48	 345	loop 2
+ -0.218794	  -0.517763	      20 	Inter	0.056	 0.07	 0.70	  0.8	 48	 338	loop 2
+ -0.218417	  -0.517462	      23*	Junct	0.053	 0.07	 0.75	  0.9	 48	  38	loop 2
+ -0.218805	  -0.517131	      25 	Inter	0.056	 0.07	 0.81	  0.9	 48	 139	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.071	 0.09	 0.88	  1.0	 48	 224	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.078	 0.10	 0.95	  1.1	 48	 165	loop 2
+ -0.220301	  -0.517576	      22*	Junct	0.044	 0.06	 1.00	  1.2	 48	 206	loop 2
+ -0.220739	  -0.517801	      19*	Junct	0.054	 0.07	 1.05	  1.2	 48	 207	loop 2
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.25	  1.5	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.30	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.38	  1.6	 96	   2	main 2
diff --git a/src/test/expected/loops-WP09.txt b/src/test/expected/loops-WP09.txt
new file mode 100644
index 0000000..b1a1cf1
--- /dev/null
+++ b/src/test/expected/loops-WP09.txt
@@ -0,0 +1,25 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 0.47	  0.5	 48	  91	high street
+ -0.220301	  -0.517576	      22*	Junct	0.054	 0.07	 0.52	  0.6	 48	  27	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.044	 0.06	 0.56	  0.6	 48	  26	loop 2
+ -0.219266	  -0.517579	      21*	Inter	0.078	 0.10	 0.64	  0.7	 48	 345	loop 2
+ -0.219013	  -0.517333	      -2 	Waypt	0.039	 0.05	 0.68	  0.8	 48	  44	loop 2
+ -0.218805	  -0.517131	      25 	Inter	0.032	 0.04	 0.71	  0.8	 48	  44	loop 2
+ -0.218417	  -0.517462	      23*	Junct	0.056	 0.07	 0.77	  0.9	 48	 319	loop 2
+ -0.218794	  -0.517763	      20 	Inter	0.053	 0.07	 0.82	  0.9	 48	 218	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.056	 0.07	 0.88	  1.0	 48	 158	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.078	 0.10	 0.95	  1.1	 48	 165	loop 2
+ -0.220301	  -0.517576	      22*	Junct	0.044	 0.06	 1.00	  1.2	 48	 206	loop 2
+ -0.220739	  -0.517801	      19*	Junct	0.054	 0.07	 1.05	  1.2	 48	 207	loop 2
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.25	  1.5	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.30	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.38	  1.6	 96	   2	main 2
diff --git a/src/test/expected/loops-WP10.txt b/src/test/expected/loops-WP10.txt
new file mode 100644
index 0000000..c3383f6
--- /dev/null
+++ b/src/test/expected/loops-WP10.txt
@@ -0,0 +1,24 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 0.47	  0.5	 48	  91	high street
+ -0.220301	  -0.517576	      22*	Junct	0.054	 0.07	 0.52	  0.6	 48	  27	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.044	 0.06	 0.56	  0.6	 48	  26	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.078	 0.10	 0.64	  0.7	 48	 345	loop 2
+ -0.218805	  -0.517131	      25 	Waypt	0.071	 0.09	 0.71	  0.8	 48	  44	loop 2
+ -0.218417	  -0.517462	      23*	Junct	0.056	 0.07	 0.77	  0.9	 48	 319	loop 2
+ -0.218794	  -0.517763	      20 	Inter	0.053	 0.07	 0.82	  0.9	 48	 218	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.056	 0.07	 0.88	  1.0	 48	 158	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.078	 0.10	 0.95	  1.1	 48	 165	loop 2
+ -0.220301	  -0.517576	      22*	Junct	0.044	 0.06	 1.00	  1.2	 48	 206	loop 2
+ -0.220739	  -0.517801	      19*	Junct	0.054	 0.07	 1.05	  1.2	 48	 207	loop 2
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.25	  1.5	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.30	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.38	  1.6	 96	   2	main 2
diff --git a/src/test/expected/loops-WP11.txt b/src/test/expected/loops-WP11.txt
new file mode 100644
index 0000000..12830fe
--- /dev/null
+++ b/src/test/expected/loops-WP11.txt
@@ -0,0 +1,24 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.219562	  -0.520851	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.220223	  -0.520885	       5*	Junct	0.073	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       4*	Junct	0.049	 0.03	 0.12	  0.1	 96	 181	main 1
+ -0.220695	  -0.519489	      11*	Junct	0.156	 0.20	 0.28	  0.3	 48	  91	high street
+ -0.220739	  -0.517801	      19*	Junct	0.187	 0.23	 0.47	  0.5	 48	  91	high street
+ -0.220301	  -0.517576	      22*	Junct	0.054	 0.07	 0.52	  0.6	 48	  27	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.044	 0.06	 0.56	  0.6	 48	  26	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.078	 0.10	 0.64	  0.7	 48	 345	loop 2
+ -0.218794	  -0.517763	      20 	Inter	0.056	 0.07	 0.70	  0.8	 48	 338	loop 2
+ -0.218417	  -0.517462	      23*	Waypt	0.053	 0.07	 0.75	  0.9	 48	  38	loop 2
+ -0.218805	  -0.517131	      25 	Inter	0.056	 0.07	 0.81	  0.9	 48	 139	loop 2
+ -0.219266	  -0.517579	      21*	Junct	0.071	 0.09	 0.88	  1.0	 48	 224	loop 2
+ -0.219946	  -0.517397	      24 	Inter	0.078	 0.10	 0.95	  1.1	 48	 165	loop 2
+ -0.220301	  -0.517576	      22*	Junct	0.044	 0.06	 1.00	  1.2	 48	 206	loop 2
+ -0.220739	  -0.517801	      19*	Junct	0.054	 0.07	 1.05	  1.2	 48	 207	loop 2
+ -0.220784	  -0.516035	      30*	Junct	0.196	 0.24	 1.25	  1.5	 48	  91	high street
+ -0.220311	  -0.516015	      31*	Inter	0.052	 0.03	 1.30	  1.5	 96	   2	main 2
+ -0.219596	  -0.515984	      -3 	Waypt	0.079	 0.05	 1.38	  1.6	 96	   2	main 2
diff --git a/src/test/expected/no-super-WP01.txt b/src/test/expected/no-super-WP01.txt
new file mode 100644
index 0000000..e4ca14f
--- /dev/null
+++ b/src/test/expected/no-super-WP01.txt
@@ -0,0 +1,16 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.215814	  -0.519419	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.215737	  -0.519035	       7 	Inter	0.043	 0.05	 0.04	  0.1	 48	  78	road1
+ -0.215935	  -0.518253	      10 	Inter	0.089	 0.11	 0.13	  0.2	 48	 104	road1
+ -0.215772	  -0.517713	      13 	Inter	0.062	 0.08	 0.19	  0.2	 48	  73	road1
+ -0.215952	  -0.516838	      15 	Inter	0.099	 0.12	 0.29	  0.4	 48	 101	road1
+ -0.215755	  -0.515945	      19 	Inter	0.101	 0.12	 0.39	  0.5	 48	  77	road1
+ -0.215897	  -0.515462	      -2 	Waypt	0.055	 0.07	 0.45	  0.6	 48	 106	road1
+ -0.215755	  -0.515945	      19 	Inter	0.055	 0.07	 0.50	  0.6	 48	 286	road1
+ -0.215952	  -0.516838	      15 	Inter	0.101	 0.12	 0.60	  0.8	 48	 257	road1
+ -0.215868	  -0.517244	      -3 	Waypt	0.046	 0.06	 0.65	  0.8	 48	 281	road1
diff --git a/src/test/expected/no-super-WP02.txt b/src/test/expected/no-super-WP02.txt
new file mode 100644
index 0000000..fa321e3
--- /dev/null
+++ b/src/test/expected/no-super-WP02.txt
@@ -0,0 +1,9 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.216472	  -0.519026	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.216671	  -0.515660	      -2 	Waypt	0.374	 0.47	 0.37	  0.5	 48	  93	road2
+ -0.216579	  -0.517212	      -3 	Waypt	0.172	 0.21	 0.55	  0.7	 48	 273	road2
diff --git a/src/test/expected/no-super-WP03.txt b/src/test/expected/no-super-WP03.txt
new file mode 100644
index 0000000..e0630c4
--- /dev/null
+++ b/src/test/expected/no-super-WP03.txt
@@ -0,0 +1,13 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.217185	  -0.519054	       5 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.217382	  -0.518273	       8 	Inter	0.089	 0.11	 0.09	  0.1	 48	 104	road3
+ -0.217219	  -0.517733	      11 	Inter	0.062	 0.08	 0.15	  0.2	 48	  73	road3
+ -0.217399	  -0.516857	      14 	Inter	0.099	 0.12	 0.25	  0.3	 48	 101	road3
+ -0.217202	  -0.515964	      17 	Waypt	0.101	 0.12	 0.35	  0.4	 48	  77	road3
+ -0.217399	  -0.516857	      14 	Inter	0.101	 0.12	 0.45	  0.6	 48	 257	road3
+ -0.217219	  -0.517733	      11 	Waypt	0.099	 0.12	 0.55	  0.7	 48	 281	road3
diff --git a/src/test/expected/no-super-WP04.txt b/src/test/expected/no-super-WP04.txt
new file mode 100644
index 0000000..901aced
--- /dev/null
+++ b/src/test/expected/no-super-WP04.txt
@@ -0,0 +1,17 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.217903	  -0.519035	       6 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.218100	  -0.518253	       9 	Inter	0.089	 0.11	 0.09	  0.1	 48	 104	road4
+ -0.217937	  -0.517713	      12 	Inter	0.062	 0.08	 0.15	  0.2	 48	  73	road4
+ -0.218117	  -0.516837	      16 	Inter	0.099	 0.12	 0.25	  0.3	 48	 101	road4
+ -0.217920	  -0.515945	      18 	Inter	0.101	 0.12	 0.35	  0.4	 48	  77	road4
+ -0.218143	  -0.515189	      21 	Inter	0.087	 0.11	 0.44	  0.5	 48	 106	road4
+ -0.217911	  -0.514640	      26 	Waypt	0.066	 0.08	 0.50	  0.6	 48	  67	road4
+ -0.218143	  -0.515189	      21 	Inter	0.066	 0.08	 0.57	  0.7	 48	 247	road4
+ -0.217920	  -0.515945	      18 	Inter	0.087	 0.11	 0.66	  0.8	 48	 286	road4
+ -0.218117	  -0.516837	      16 	Inter	0.101	 0.12	 0.76	  0.9	 48	 257	road4
+ -0.217937	  -0.517713	      12 	Waypt	0.099	 0.12	 0.86	  1.1	 48	 281	road4
diff --git a/src/test/expected/super-or-not-WP01.txt b/src/test/expected/super-or-not-WP01.txt
new file mode 100644
index 0000000..d76ce16
--- /dev/null
+++ b/src/test/expected/super-or-not-WP01.txt
@@ -0,0 +1,13 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.216158	  -0.518809	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.216435	  -0.518515	       4 	Inter	0.044	 0.06	 0.04	  0.1	 48	 133	Local road
+ -0.215866	  -0.517931	       5 	Inter	0.090	 0.11	 0.13	  0.2	 48	  45	Local road
+ -0.216470	  -0.517412	       6 	Inter	0.088	 0.11	 0.22	  0.3	 48	 139	Local road
+ -0.215930	  -0.516823	       7 	Inter	0.088	 0.11	 0.31	  0.4	 48	  47	Local road
+ -0.216451	  -0.516221	       8 	Inter	0.088	 0.11	 0.40	  0.5	 48	 130	Local road
+ -0.216158	  -0.515870	      -2 	Waypt	0.050	 0.06	 0.45	  0.6	 48	  50	Local road
diff --git a/src/test/expected/super-or-not-WP02.txt b/src/test/expected/super-or-not-WP02.txt
new file mode 100644
index 0000000..0bf9d23
--- /dev/null
+++ b/src/test/expected/super-or-not-WP02.txt
@@ -0,0 +1,10 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.216828	  -0.519457	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.217245	  -0.519637	       2*	Junct	0.050	 0.06	 0.05	  0.1	 48	 203	Local road
+ -0.217248	  -0.515206	      10*	Inter	0.493	 0.31	 0.54	  0.4	 96	  90	Main road
+ -0.216919	  -0.515308	      -2 	Waypt	0.038	 0.05	 0.58	  0.4	 48	 342	Local road
diff --git a/src/test/expected/super-or-not-WP03.txt b/src/test/expected/super-or-not-WP03.txt
new file mode 100644
index 0000000..4e51634
--- /dev/null
+++ b/src/test/expected/super-or-not-WP03.txt
@@ -0,0 +1,10 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.217245	  -0.519637	       2*	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.215919	  -0.519064	       3 	Inter	0.160	 0.20	 0.16	  0.2	 48	  23	Local road
+ -0.216435	  -0.518515	       4 	Inter	0.083	 0.10	 0.24	  0.3	 48	 133	Local road
+ -0.215866	  -0.517931	       5 	Waypt	0.090	 0.11	 0.33	  0.4	 48	  45	Local road
diff --git a/src/test/expected/turns-WP01.txt b/src/test/expected/turns-WP01.txt
new file mode 100644
index 0000000..84b4bb6
--- /dev/null
+++ b/src/test/expected/turns-WP01.txt
@@ -0,0 +1,19 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 0.24	  0.2	 96	 182	main 1
+ -0.220671	  -0.520223	      11*	Junct	0.074	 0.09	 0.32	  0.2	 48	  90	high street
+ -0.220691	  -0.519461	      19*	Junct	0.084	 0.10	 0.40	  0.3	 48	  91	high street
+ -0.220111	  -0.519553	      17 	Inter	0.065	 0.08	 0.47	  0.4	 48	 351	loop 1
+ -0.219817	  -0.519807	      15 	Inter	0.043	 0.05	 0.51	  0.5	 48	 319	loop 1
+ -0.220067	  -0.520109	      13*	Inter	0.043	 0.05	 0.55	  0.5	 48	 230	loop 1
+ -0.220191	  -0.520132	      -2 	Waypt	0.014	 0.02	 0.57	  0.6	 48	 190	loop 1
+ -0.220671	  -0.520223	      11*	Junct	0.054	 0.07	 0.62	  0.6	 48	 190	loop 1
+ -0.220666	  -0.520893	       5*	Junct	0.074	 0.09	 0.70	  0.7	 48	 270	high street
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 0.77	  0.8	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 0.79	  0.8	 96	 181	main 1
diff --git a/src/test/expected/turns-WP02.txt b/src/test/expected/turns-WP02.txt
new file mode 100644
index 0000000..6718fc4
--- /dev/null
+++ b/src/test/expected/turns-WP02.txt
@@ -0,0 +1,18 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 0.24	  0.2	 96	 182	main 1
+ -0.220671	  -0.520223	      11*	Junct	0.074	 0.09	 0.32	  0.2	 48	  90	high street
+ -0.220691	  -0.519461	      19*	Junct	0.084	 0.10	 0.40	  0.3	 48	  91	high street
+ -0.220111	  -0.519553	      17 	Inter	0.065	 0.08	 0.47	  0.4	 48	 351	loop 1
+ -0.219817	  -0.519807	      15 	Inter	0.043	 0.05	 0.51	  0.5	 48	 319	loop 1
+ -0.220067	  -0.520109	      13*	Waypt	0.043	 0.05	 0.55	  0.5	 48	 230	loop 1
+ -0.220671	  -0.520223	      11*	Junct	0.068	 0.09	 0.62	  0.6	 48	 190	loop 1
+ -0.220666	  -0.520893	       5*	Junct	0.074	 0.09	 0.70	  0.7	 48	 270	high street
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 0.77	  0.8	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 0.79	  0.8	 96	 181	main 1
diff --git a/src/test/expected/turns-WP03.txt b/src/test/expected/turns-WP03.txt
new file mode 100644
index 0000000..17ddcbb
--- /dev/null
+++ b/src/test/expected/turns-WP03.txt
@@ -0,0 +1,18 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 0.24	  0.2	 96	 182	main 1
+ -0.220671	  -0.520223	      11*	Junct	0.074	 0.09	 0.32	  0.2	 48	  90	high street
+ -0.220691	  -0.519461	      19*	Junct	0.084	 0.10	 0.40	  0.3	 48	  91	high street
+ -0.220111	  -0.519553	      17 	Inter	0.065	 0.08	 0.47	  0.4	 48	 351	loop 1
+ -0.219817	  -0.519807	      15 	Waypt	0.043	 0.05	 0.51	  0.5	 48	 319	loop 1
+ -0.220067	  -0.520109	      13*	Inter	0.043	 0.05	 0.55	  0.5	 48	 230	loop 1
+ -0.220671	  -0.520223	      11*	Junct	0.068	 0.09	 0.62	  0.6	 48	 190	loop 1
+ -0.220666	  -0.520893	       5*	Junct	0.074	 0.09	 0.70	  0.7	 48	 270	high street
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 0.77	  0.8	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 0.79	  0.8	 96	 181	main 1
diff --git a/src/test/expected/turns-WP04.txt b/src/test/expected/turns-WP04.txt
new file mode 100644
index 0000000..593d77f
--- /dev/null
+++ b/src/test/expected/turns-WP04.txt
@@ -0,0 +1,27 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 0.24	  0.2	 96	 182	main 1
+ -0.220671	  -0.520223	      11*	Junct	0.074	 0.09	 0.32	  0.2	 48	  90	high street
+ -0.220691	  -0.519461	      19*	Junct	0.084	 0.10	 0.40	  0.3	 48	  91	high street
+ -0.220708	  -0.518842	      22*	Junct	0.069	 0.09	 0.47	  0.4	 48	  91	high street
+ -0.220724	  -0.518071	      31*	Junct	0.085	 0.10	 0.56	  0.5	 48	  91	high street
+ -0.220143	  -0.518162	      30 	Inter	0.065	 0.08	 0.62	  0.6	 48	 351	loop 2
+ -0.219850	  -0.518416	      27 	Inter	0.043	 0.05	 0.67	  0.7	 48	 319	loop 2
+ -0.220100	  -0.518718	      24*	Inter	0.043	 0.05	 0.71	  0.7	 48	 230	loop 2
+ -0.220206	  -0.518739	      -2 	Waypt	0.012	 0.01	 0.72	  0.7	 48	 191	loop 2
+ -0.220708	  -0.518842	      22*	Junct	0.057	 0.07	 0.78	  0.8	 48	 191	loop 2
+ -0.220724	  -0.518071	      31*	Junct	0.085	 0.10	 0.86	  0.9	 48	  91	high street
+ -0.220739	  -0.517425	      34*	Junct	0.071	 0.09	 0.93	  1.0	 48	  91	high street
+ -0.220760	  -0.516647	      48*	Junct	0.086	 0.11	 1.02	  1.1	 48	  91	high street
+ -0.220784	  -0.516035	      54*	Junct	0.068	 0.09	 1.09	  1.2	 48	  92	high street
+ -0.221431	  -0.516056	      53*	Junct	0.072	 0.04	 1.16	  1.2	 96	 181	main 2
+ -0.221376	  -0.518235	      29*	Junct	0.242	 0.30	 1.40	  1.5	 48	 271	bottom road
+ -0.221360	  -0.518860	      21*	Junct	0.069	 0.09	 1.47	  1.6	 48	 271	bottom road
+ -0.221308	  -0.520914	       4*	Inter	0.228	 0.28	 1.70	  1.9	 48	 271	bottom road
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.73	  1.9	 96	 181	main 1
diff --git a/src/test/expected/turns-WP05.txt b/src/test/expected/turns-WP05.txt
new file mode 100644
index 0000000..113d90e
--- /dev/null
+++ b/src/test/expected/turns-WP05.txt
@@ -0,0 +1,26 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 0.24	  0.2	 96	 182	main 1
+ -0.220671	  -0.520223	      11*	Junct	0.074	 0.09	 0.32	  0.2	 48	  90	high street
+ -0.220691	  -0.519461	      19*	Junct	0.084	 0.10	 0.40	  0.3	 48	  91	high street
+ -0.220708	  -0.518842	      22*	Junct	0.069	 0.09	 0.47	  0.4	 48	  91	high street
+ -0.220724	  -0.518071	      31*	Junct	0.085	 0.10	 0.56	  0.5	 48	  91	high street
+ -0.220143	  -0.518162	      30 	Inter	0.065	 0.08	 0.62	  0.6	 48	 351	loop 2
+ -0.219850	  -0.518416	      27 	Inter	0.043	 0.05	 0.67	  0.7	 48	 319	loop 2
+ -0.220100	  -0.518718	      24*	Waypt	0.043	 0.05	 0.71	  0.7	 48	 230	loop 2
+ -0.220708	  -0.518842	      22*	Junct	0.069	 0.09	 0.78	  0.8	 48	 191	loop 2
+ -0.220724	  -0.518071	      31*	Junct	0.085	 0.10	 0.86	  0.9	 48	  91	high street
+ -0.220739	  -0.517425	      34*	Junct	0.071	 0.09	 0.93	  1.0	 48	  91	high street
+ -0.220760	  -0.516647	      48*	Junct	0.086	 0.11	 1.02	  1.1	 48	  91	high street
+ -0.220784	  -0.516035	      54*	Junct	0.068	 0.09	 1.09	  1.2	 48	  92	high street
+ -0.221431	  -0.516056	      53*	Junct	0.072	 0.04	 1.16	  1.2	 96	 181	main 2
+ -0.221376	  -0.518235	      29*	Junct	0.242	 0.30	 1.40	  1.5	 48	 271	bottom road
+ -0.221360	  -0.518860	      21*	Junct	0.069	 0.09	 1.47	  1.6	 48	 271	bottom road
+ -0.221308	  -0.520914	       4*	Inter	0.228	 0.28	 1.70	  1.9	 48	 271	bottom road
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.73	  1.9	 96	 181	main 1
diff --git a/src/test/expected/turns-WP06.txt b/src/test/expected/turns-WP06.txt
new file mode 100644
index 0000000..f6a6da3
--- /dev/null
+++ b/src/test/expected/turns-WP06.txt
@@ -0,0 +1,26 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 0.24	  0.2	 96	 182	main 1
+ -0.220671	  -0.520223	      11*	Junct	0.074	 0.09	 0.32	  0.2	 48	  90	high street
+ -0.220691	  -0.519461	      19*	Junct	0.084	 0.10	 0.40	  0.3	 48	  91	high street
+ -0.220708	  -0.518842	      22*	Junct	0.069	 0.09	 0.47	  0.4	 48	  91	high street
+ -0.220724	  -0.518071	      31*	Junct	0.085	 0.10	 0.56	  0.5	 48	  91	high street
+ -0.220143	  -0.518162	      30 	Inter	0.065	 0.08	 0.62	  0.6	 48	 351	loop 2
+ -0.219850	  -0.518416	      27 	Waypt	0.043	 0.05	 0.67	  0.7	 48	 319	loop 2
+ -0.220100	  -0.518718	      24*	Inter	0.043	 0.05	 0.71	  0.7	 48	 230	loop 2
+ -0.220708	  -0.518842	      22*	Junct	0.069	 0.09	 0.78	  0.8	 48	 191	loop 2
+ -0.220724	  -0.518071	      31*	Junct	0.085	 0.10	 0.86	  0.9	 48	  91	high street
+ -0.220739	  -0.517425	      34*	Junct	0.071	 0.09	 0.93	  1.0	 48	  91	high street
+ -0.220760	  -0.516647	      48*	Junct	0.086	 0.11	 1.02	  1.1	 48	  91	high street
+ -0.220784	  -0.516035	      54*	Junct	0.068	 0.09	 1.09	  1.2	 48	  92	high street
+ -0.221431	  -0.516056	      53*	Junct	0.072	 0.04	 1.16	  1.2	 96	 181	main 2
+ -0.221376	  -0.518235	      29*	Junct	0.242	 0.30	 1.40	  1.5	 48	 271	bottom road
+ -0.221360	  -0.518860	      21*	Junct	0.069	 0.09	 1.47	  1.6	 48	 271	bottom road
+ -0.221308	  -0.520914	       4*	Inter	0.228	 0.28	 1.70	  1.9	 48	 271	bottom road
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.73	  1.9	 96	 181	main 1
diff --git a/src/test/expected/turns-WP07.txt b/src/test/expected/turns-WP07.txt
new file mode 100644
index 0000000..b6732aa
--- /dev/null
+++ b/src/test/expected/turns-WP07.txt
@@ -0,0 +1,34 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.219184	  -0.515968	      55*	Junct	0.095	 0.12	 0.63	  0.7	 48	  92	top road
+ -0.220784	  -0.516035	      54*	Junct	0.178	 0.11	 0.81	  0.8	 96	 182	main 2
+ -0.220760	  -0.516647	      48*	Junct	0.068	 0.09	 0.88	  0.9	 48	 272	high street
+ -0.220739	  -0.517425	      34*	Inter	0.086	 0.11	 0.96	  1.0	 48	 271	high street
+ -0.220238	  -0.517313	      -2 	Waypt	0.057	 0.07	 1.02	  1.1	 48	  12	loop 3
+ -0.220089	  -0.517279	      35*	Inter	0.017	 0.02	 1.04	  1.1	 48	  12	loop 3
+ -0.219872	  -0.517009	      43 	Inter	0.038	 0.05	 1.08	  1.2	 48	  51	loop 3
+ -0.220097	  -0.516762	      47*	Inter	0.037	 0.04	 1.11	  1.2	 48	 132	loop 3
+ -0.220760	  -0.516647	      48*	Junct	0.074	 0.09	 1.19	  1.3	 48	 170	loop 3
+ -0.220739	  -0.517425	      34*	Junct	0.086	 0.11	 1.27	  1.4	 48	 271	high street
+ -0.220724	  -0.518071	      31*	Junct	0.071	 0.09	 1.34	  1.5	 48	 271	high street
+ -0.220708	  -0.518842	      22*	Junct	0.085	 0.10	 1.43	  1.6	 48	 271	high street
+ -0.220691	  -0.519461	      19*	Junct	0.069	 0.09	 1.50	  1.7	 48	 271	high street
+ -0.220671	  -0.520223	      11*	Junct	0.084	 0.10	 1.58	  1.8	 48	 271	high street
+ -0.220666	  -0.520893	       5*	Junct	0.074	 0.09	 1.66	  1.9	 48	 270	high street
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.73	  1.9	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.75	  2.0	 96	 181	main 1
diff --git a/src/test/expected/turns-WP08.txt b/src/test/expected/turns-WP08.txt
new file mode 100644
index 0000000..b8679cb
--- /dev/null
+++ b/src/test/expected/turns-WP08.txt
@@ -0,0 +1,31 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.219184	  -0.515968	      55*	Junct	0.095	 0.12	 0.63	  0.7	 48	  92	top road
+ -0.220784	  -0.516035	      54*	Junct	0.178	 0.11	 0.81	  0.8	 96	 182	main 2
+ -0.220760	  -0.516647	      48*	Junct	0.068	 0.09	 0.88	  0.9	 48	 272	high street
+ -0.220097	  -0.516762	      47*	Inter	0.074	 0.09	 0.95	  1.0	 48	 350	loop 3
+ -0.219872	  -0.517009	      43 	Inter	0.037	 0.04	 0.99	  1.1	 48	 312	loop 3
+ -0.220089	  -0.517279	      35*	Waypt	0.038	 0.05	 1.03	  1.1	 48	 231	loop 3
+ -0.220739	  -0.517425	      34*	Junct	0.074	 0.09	 1.10	  1.2	 48	 192	loop 3
+ -0.220724	  -0.518071	      31*	Junct	0.071	 0.09	 1.17	  1.3	 48	 271	high street
+ -0.220708	  -0.518842	      22*	Junct	0.085	 0.10	 1.26	  1.4	 48	 271	high street
+ -0.220691	  -0.519461	      19*	Junct	0.069	 0.09	 1.33	  1.5	 48	 271	high street
+ -0.220671	  -0.520223	      11*	Junct	0.084	 0.10	 1.41	  1.6	 48	 271	high street
+ -0.220666	  -0.520893	       5*	Junct	0.074	 0.09	 1.48	  1.7	 48	 270	high street
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.55	  1.7	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.58	  1.7	 96	 181	main 1
diff --git a/src/test/expected/turns-WP09.txt b/src/test/expected/turns-WP09.txt
new file mode 100644
index 0000000..3047b1c
--- /dev/null
+++ b/src/test/expected/turns-WP09.txt
@@ -0,0 +1,31 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.219184	  -0.515968	      55*	Junct	0.095	 0.12	 0.63	  0.7	 48	  92	top road
+ -0.220784	  -0.516035	      54*	Junct	0.178	 0.11	 0.81	  0.8	 96	 182	main 2
+ -0.220760	  -0.516647	      48*	Junct	0.068	 0.09	 0.88	  0.9	 48	 272	high street
+ -0.220097	  -0.516762	      47*	Inter	0.074	 0.09	 0.95	  1.0	 48	 350	loop 3
+ -0.219872	  -0.517009	      43 	Waypt	0.037	 0.04	 0.99	  1.1	 48	 312	loop 3
+ -0.220089	  -0.517279	      35*	Inter	0.038	 0.05	 1.03	  1.1	 48	 231	loop 3
+ -0.220739	  -0.517425	      34*	Junct	0.074	 0.09	 1.10	  1.2	 48	 192	loop 3
+ -0.220724	  -0.518071	      31*	Junct	0.071	 0.09	 1.17	  1.3	 48	 271	high street
+ -0.220708	  -0.518842	      22*	Junct	0.085	 0.10	 1.26	  1.4	 48	 271	high street
+ -0.220691	  -0.519461	      19*	Junct	0.069	 0.09	 1.33	  1.5	 48	 271	high street
+ -0.220671	  -0.520223	      11*	Junct	0.084	 0.10	 1.41	  1.6	 48	 271	high street
+ -0.220666	  -0.520893	       5*	Junct	0.074	 0.09	 1.48	  1.7	 48	 270	high street
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.55	  1.7	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.58	  1.7	 96	 181	main 1
diff --git a/src/test/expected/turns-WP10.txt b/src/test/expected/turns-WP10.txt
new file mode 100644
index 0000000..0f72880
--- /dev/null
+++ b/src/test/expected/turns-WP10.txt
@@ -0,0 +1,35 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 0.56	  0.6	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 0.58	  0.7	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 0.59	  0.7	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 0.61	  0.7	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 0.65	  0.8	 48	 269	top road
+ -0.219135	  -0.518823	      23*	Junct	0.133	 0.17	 0.79	  0.9	 48	 270	top road
+ -0.219131	  -0.519426	      20*	Junct	0.067	 0.08	 0.85	  1.0	 48	 270	top road
+ -0.219123	  -0.520207	      12*	Inter	0.086	 0.11	 0.94	  1.1	 48	 270	top road
+ -0.218605	  -0.520090	      -2 	Waypt	0.059	 0.07	 1.00	  1.2	 48	  12	loop 4
+ -0.218474	  -0.520060	      14*	Inter	0.015	 0.02	 1.01	  1.2	 48	  12	loop 4
+ -0.218258	  -0.519789	      16 	Inter	0.038	 0.05	 1.05	  1.2	 48	  51	loop 4
+ -0.218482	  -0.519542	      18*	Inter	0.037	 0.04	 1.09	  1.3	 48	 132	loop 4
+ -0.219131	  -0.519426	      20*	Junct	0.073	 0.09	 1.16	  1.4	 48	 169	loop 4
+ -0.219123	  -0.520207	      12*	Junct	0.086	 0.11	 1.25	  1.5	 48	 270	top road
+ -0.219107	  -0.520828	       6*	Junct	0.069	 0.09	 1.31	  1.6	 48	 271	top road
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 1.49	  1.7	 96	 182	main 1
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.56	  1.7	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.59	  1.7	 96	 181	main 1
diff --git a/src/test/expected/turns-WP11.txt b/src/test/expected/turns-WP11.txt
new file mode 100644
index 0000000..1707e33
--- /dev/null
+++ b/src/test/expected/turns-WP11.txt
@@ -0,0 +1,32 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 0.56	  0.6	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 0.58	  0.7	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 0.59	  0.7	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 0.61	  0.7	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 0.65	  0.8	 48	 269	top road
+ -0.219135	  -0.518823	      23*	Junct	0.133	 0.17	 0.79	  0.9	 48	 270	top road
+ -0.219131	  -0.519426	      20*	Junct	0.067	 0.08	 0.85	  1.0	 48	 270	top road
+ -0.218482	  -0.519542	      18*	Inter	0.073	 0.09	 0.93	  1.1	 48	 349	loop 4
+ -0.218258	  -0.519789	      16 	Inter	0.037	 0.04	 0.96	  1.1	 48	 312	loop 4
+ -0.218474	  -0.520060	      14*	Waypt	0.038	 0.05	 1.00	  1.2	 48	 231	loop 4
+ -0.219123	  -0.520207	      12*	Junct	0.074	 0.09	 1.07	  1.3	 48	 192	loop 4
+ -0.219107	  -0.520828	       6*	Junct	0.069	 0.09	 1.14	  1.4	 48	 271	top road
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 1.32	  1.5	 96	 182	main 1
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.39	  1.5	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.41	  1.5	 96	 181	main 1
diff --git a/src/test/expected/turns-WP12.txt b/src/test/expected/turns-WP12.txt
new file mode 100644
index 0000000..3b8babc
--- /dev/null
+++ b/src/test/expected/turns-WP12.txt
@@ -0,0 +1,32 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 0.56	  0.6	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 0.58	  0.7	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 0.59	  0.7	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 0.61	  0.7	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 0.65	  0.8	 48	 269	top road
+ -0.219135	  -0.518823	      23*	Junct	0.133	 0.17	 0.79	  0.9	 48	 270	top road
+ -0.219131	  -0.519426	      20*	Junct	0.067	 0.08	 0.85	  1.0	 48	 270	top road
+ -0.218482	  -0.519542	      18*	Inter	0.073	 0.09	 0.93	  1.1	 48	 349	loop 4
+ -0.218258	  -0.519789	      16 	Waypt	0.037	 0.04	 0.96	  1.1	 48	 312	loop 4
+ -0.218474	  -0.520060	      14*	Inter	0.038	 0.05	 1.00	  1.2	 48	 231	loop 4
+ -0.219123	  -0.520207	      12*	Junct	0.074	 0.09	 1.07	  1.3	 48	 192	loop 4
+ -0.219107	  -0.520828	       6*	Junct	0.069	 0.09	 1.14	  1.4	 48	 271	top road
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 1.32	  1.5	 96	 182	main 1
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.39	  1.5	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.41	  1.5	 96	 181	main 1
diff --git a/src/test/expected/turns-WP13.txt b/src/test/expected/turns-WP13.txt
new file mode 100644
index 0000000..5682d9c
--- /dev/null
+++ b/src/test/expected/turns-WP13.txt
@@ -0,0 +1,45 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 0.56	  0.6	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 0.58	  0.7	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 0.59	  0.7	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 0.61	  0.7	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 0.65	  0.8	 48	 269	top road
+ -0.218619	  -0.517921	      32*	Inter	0.067	 0.08	 0.72	  0.8	 48	 330	loop 5
+ -0.218431	  -0.518243	      28 	Inter	0.041	 0.05	 0.76	  0.9	 48	 300	loop 5
+ -0.218600	  -0.518557	      25*	Inter	0.039	 0.05	 0.80	  0.9	 48	 241	loop 5
+ -0.218691	  -0.518602	      -2 	Waypt	0.011	 0.01	 0.81	  1.0	 48	 206	loop 5
+ -0.219135	  -0.518823	      23*	Junct	0.054	 0.07	 0.86	  1.0	 48	 206	loop 5
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 1.00	  1.2	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 1.04	  1.2	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 1.05	  1.3	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 1.07	  1.3	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 1.09	  1.3	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 1.11	  1.3	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 1.13	  1.3	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 1.15	  1.4	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 1.16	  1.4	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 1.18	  1.4	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 1.22	  1.5	 48	 269	top road
+ -0.219135	  -0.518823	      23*	Junct	0.133	 0.17	 1.35	  1.6	 48	 270	top road
+ -0.219131	  -0.519426	      20*	Junct	0.067	 0.08	 1.42	  1.7	 48	 270	top road
+ -0.219123	  -0.520207	      12*	Junct	0.086	 0.11	 1.51	  1.8	 48	 270	top road
+ -0.219107	  -0.520828	       6*	Junct	0.069	 0.09	 1.58	  1.9	 48	 271	top road
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 1.75	  2.0	 96	 182	main 1
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.82	  2.0	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.85	  2.1	 96	 181	main 1
diff --git a/src/test/expected/turns-WP14.txt b/src/test/expected/turns-WP14.txt
new file mode 100644
index 0000000..dc4882d
--- /dev/null
+++ b/src/test/expected/turns-WP14.txt
@@ -0,0 +1,44 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 0.56	  0.6	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 0.58	  0.7	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 0.59	  0.7	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 0.61	  0.7	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 0.65	  0.8	 48	 269	top road
+ -0.218619	  -0.517921	      32*	Inter	0.067	 0.08	 0.72	  0.8	 48	 330	loop 5
+ -0.218431	  -0.518243	      28 	Inter	0.041	 0.05	 0.76	  0.9	 48	 300	loop 5
+ -0.218600	  -0.518557	      25*	Waypt	0.039	 0.05	 0.80	  0.9	 48	 241	loop 5
+ -0.219135	  -0.518823	      23*	Junct	0.066	 0.08	 0.86	  1.0	 48	 206	loop 5
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 1.00	  1.2	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 1.04	  1.2	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 1.06	  1.3	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 1.07	  1.3	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 1.09	  1.3	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 1.11	  1.3	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 1.13	  1.3	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 1.15	  1.4	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 1.17	  1.4	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 1.18	  1.4	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 1.22	  1.5	 48	 269	top road
+ -0.219135	  -0.518823	      23*	Junct	0.133	 0.17	 1.36	  1.6	 48	 270	top road
+ -0.219131	  -0.519426	      20*	Junct	0.067	 0.08	 1.42	  1.7	 48	 270	top road
+ -0.219123	  -0.520207	      12*	Junct	0.086	 0.11	 1.51	  1.8	 48	 270	top road
+ -0.219107	  -0.520828	       6*	Junct	0.069	 0.09	 1.58	  1.9	 48	 271	top road
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 1.75	  2.0	 96	 182	main 1
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.82	  2.0	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.85	  2.1	 96	 181	main 1
diff --git a/src/test/expected/turns-WP15.txt b/src/test/expected/turns-WP15.txt
new file mode 100644
index 0000000..bc50d39
--- /dev/null
+++ b/src/test/expected/turns-WP15.txt
@@ -0,0 +1,44 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.219123	  -0.520207	      12*	Junct	0.069	 0.09	 0.14	  0.1	 48	  91	top road
+ -0.219131	  -0.519426	      20*	Junct	0.086	 0.11	 0.23	  0.2	 48	  90	top road
+ -0.219135	  -0.518823	      23*	Junct	0.067	 0.08	 0.29	  0.3	 48	  90	top road
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 0.43	  0.5	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 0.47	  0.5	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 0.48	  0.6	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 0.50	  0.6	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 0.52	  0.6	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 0.54	  0.6	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 0.56	  0.6	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 0.58	  0.7	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 0.59	  0.7	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 0.61	  0.7	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 0.65	  0.8	 48	 269	top road
+ -0.218619	  -0.517921	      32*	Inter	0.067	 0.08	 0.72	  0.8	 48	 330	loop 5
+ -0.218431	  -0.518243	      28 	Waypt	0.041	 0.05	 0.76	  0.9	 48	 300	loop 5
+ -0.218600	  -0.518557	      25*	Inter	0.039	 0.05	 0.80	  0.9	 48	 241	loop 5
+ -0.219135	  -0.518823	      23*	Junct	0.066	 0.08	 0.86	  1.0	 48	 206	loop 5
+ -0.219145	  -0.517626	      33*	Junct	0.133	 0.17	 1.00	  1.2	 48	  90	top road
+ -0.219144	  -0.517257	      36*	Inter	0.041	 0.05	 1.04	  1.2	 48	  89	top road
+ -0.219291	  -0.517193	      38 	Inter	0.017	 0.02	 1.06	  1.3	 48	 156	roundabout
+ -0.219352	  -0.517060	      42 	Junct	0.016	 0.02	 1.07	  1.3	 48	 114	roundabout
+ -0.219304	  -0.516904	      45 	Inter	0.018	 0.02	 1.09	  1.3	 48	  72	roundabout
+ -0.219153	  -0.516826	      46*	Junct	0.018	 0.02	 1.11	  1.3	 48	  27	roundabout
+ -0.218966	  -0.516910	      44 	Inter	0.022	 0.03	 1.13	  1.3	 48	 335	roundabout
+ -0.218923	  -0.517072	      39 	Junct	0.018	 0.02	 1.15	  1.4	 48	 284	roundabout
+ -0.218998	  -0.517207	      37 	Inter	0.017	 0.02	 1.17	  1.4	 48	 240	roundabout
+ -0.219144	  -0.517257	      36*	Junct	0.017	 0.02	 1.18	  1.4	 48	 199	roundabout
+ -0.219145	  -0.517626	      33*	Junct	0.041	 0.05	 1.22	  1.5	 48	 269	top road
+ -0.219135	  -0.518823	      23*	Junct	0.133	 0.17	 1.36	  1.6	 48	 270	top road
+ -0.219131	  -0.519426	      20*	Junct	0.067	 0.08	 1.42	  1.7	 48	 270	top road
+ -0.219123	  -0.520207	      12*	Junct	0.086	 0.11	 1.51	  1.8	 48	 270	top road
+ -0.219107	  -0.520828	       6*	Junct	0.069	 0.09	 1.58	  1.9	 48	 271	top road
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 1.75	  2.0	 96	 182	main 1
+ -0.221308	  -0.520914	       4*	Inter	0.071	 0.04	 1.82	  2.0	 96	 181	main 1
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 1.85	  2.1	 96	 181	main 1
diff --git a/src/test/expected/turns-WP16.txt b/src/test/expected/turns-WP16.txt
new file mode 100644
index 0000000..287e3d9
--- /dev/null
+++ b/src/test/expected/turns-WP16.txt
@@ -0,0 +1,16 @@
+# Creator : Routino - http://www.routino.org/
+# Source : Routino test cases - (c) Andrew M. Bishop
+# License : GNU Affero General Public License v3 or later
+#
+#Latitude	Longitude	    Node	Type	Segment	Segment	Total	Total  	Speed	Bearing	Highway
+#        	         	        	    	Dist   	Durat'n	Dist 	Durat'n	     	       	       
+ -0.218453	  -0.520799	      -1 	Waypt	0.000	 0.00	 0.00	  0.0			
+ -0.219107	  -0.520828	       6*	Junct	0.072	 0.04	 0.07	  0.0	 96	 182	main 1
+ -0.220666	  -0.520893	       5*	Junct	0.173	 0.11	 0.24	  0.2	 96	 182	main 1
+ -0.221308	  -0.520914	       4*	Junct	0.071	 0.04	 0.32	  0.2	 96	 181	main 1
+ -0.221360	  -0.518860	      21*	Junct	0.228	 0.28	 0.54	  0.5	 48	  91	bottom road
+ -0.221711	  -0.518511	      26*	Waypt	0.055	 0.07	 0.60	  0.5	 48	 135	loop 6
+ -0.221376	  -0.518235	      29*	Junct	0.048	 0.06	 0.65	  0.6	 48	  39	loop 6
+ -0.221360	  -0.518860	      21*	Junct	0.069	 0.09	 0.72	  0.7	 48	 271	bottom road
+ -0.221308	  -0.520914	       4*	Inter	0.228	 0.28	 0.94	  1.0	 48	 271	bottom road
+ -0.221561	  -0.520922	      -3 	Waypt	0.027	 0.02	 0.97	  1.0	 96	 181	main 1
diff --git a/src/test/invalid-turn-relations.osm b/src/test/invalid-turn-relations.osm
new file mode 100644
index 0000000..8ba8b16
--- /dev/null
+++ b/src/test/invalid-turn-relations.osm
@@ -0,0 +1,230 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='57' visible='true' version='1' lat='-0.2206639471091671' lon='-0.5206861212034376' />
+  <node id='58' visible='true' version='1' lat='-0.22077223999470932' lon='-0.5163392044926929' />
+  <node id='61' visible='true' version='1' lat='-0.22068542234146674' lon='-0.5199851724982207' />
+  <node id='63' visible='true' version='1' lat='-0.22121142780027955' lon='-0.5200031409736897' />
+  <node id='68' visible='true' version='1' lat='-0.22070209612901087' lon='-0.51927521490923' />
+  <node id='72' visible='true' version='1' lat='-0.22071777806715892' lon='-0.5186074895360361' />
+  <node id='74' visible='true' version='1' lat='-0.22125354169876188' lon='-0.5186133719636564' />
+  <node id='86' visible='true' version='1' lat='-0.22073699379599293' lon='-0.5177892979578822' />
+  <node id='96' visible='true' version='1' lat='-0.221280772661584' lon='-0.5177920778771439' />
+  <node id='106' visible='true' version='1' lat='-0.22129681282128807' lon='-0.5170144218511278' />
+  <node id='107' visible='true' version='1' lat='-0.22075303395629983' lon='-0.5170116419318662' />
+  <node id='118' visible='true' version='1' lat='-0.2195990670258593' lon='-0.5163091229123892' />
+  <node id='119' visible='true' version='1' lat='-0.21949625394184963' lon='-0.5205982296714888' />
+  <node id='122' visible='true' version='1' lat='-0.2195106565302976' lon='-0.5199911498477413' />
+  <node id='124' visible='true' version='1' lat='-0.21952740822631348' lon='-0.5192511136098236' />
+  <node id='126' visible='true' version='1' lat='-0.21954320401859898' lon='-0.5185533061341185' />
+  <node id='142' visible='true' version='1' lat='-0.22008353464611746' lon='-0.5185561500168592' />
+  <node id='162' visible='true' version='1' lat='-0.2195613199707841' lon='-0.517753001429538' />
+  <node id='173' visible='true' version='1' lat='-0.22012793202460473' lon='-0.5169903885686242' />
+  <node id='174' visible='true' version='1' lat='-0.21958760139871253' lon='-0.5169875446858836' />
+  <node id='277' visible='true' version='1' lat='-0.21896493881565954' lon='-0.5192522336393591' />
+  <node id='279' visible='true' version='1' lat='-0.22009496450308905' lon='-0.519247211266105' />
+  <node id='465' visible='true' version='1' lat='-0.21947721713787466' lon='-0.5212084480218759' />
+  <node id='485' visible='true' version='1' lat='-0.21961229394941645' lon='-0.5156863486288665' />
+  <way id='593' visible='true' version='1'>
+    <nd ref='279' />
+    <nd ref='124' />
+    <tag k='highway' v='unclassified' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='59' visible='true' version='1'>
+    <nd ref='57' />
+    <nd ref='61' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='64' visible='true' version='1'>
+    <nd ref='61' />
+    <nd ref='63' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='66' visible='true' version='1'>
+    <nd ref='61' />
+    <nd ref='68' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='70' visible='true' version='1'>
+    <nd ref='68' />
+    <nd ref='72' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='75' visible='true' version='1'>
+    <nd ref='74' />
+    <nd ref='72' />
+    <tag k='highway' v='unclassified' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='80' visible='true' version='1'>
+    <nd ref='72' />
+    <nd ref='86' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='88' visible='true' version='1'>
+    <nd ref='86' />
+    <nd ref='107' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='95' visible='true' version='1'>
+    <nd ref='96' />
+    <nd ref='86' />
+    <tag k='highway' v='unclassified' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='105' visible='true' version='1'>
+    <nd ref='106' />
+    <nd ref='107' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='114' visible='true' version='1'>
+    <nd ref='107' />
+    <nd ref='58' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='128' visible='true' version='1'>
+    <nd ref='126' />
+    <nd ref='124' />
+    <tag k='highway' v='unclassified' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='129' visible='true' version='1'>
+    <nd ref='122' />
+    <nd ref='124' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='130' visible='true' version='1'>
+    <nd ref='119' />
+    <nd ref='122' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='141' visible='true' version='1'>
+    <nd ref='142' />
+    <nd ref='126' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='167' visible='true' version='1'>
+    <nd ref='118' />
+    <nd ref='174' />
+    <nd ref='162' />
+    <nd ref='126' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='172' visible='true' version='1'>
+    <nd ref='173' />
+    <nd ref='174' />
+    <tag k='highway' v='unclassified' />
+  </way>
+  <way id='278' action='modify' visible='true' version='1'>
+    <nd ref='277' />
+    <nd ref='124' />
+    <tag k='highway' v='unclassified' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='466' visible='true' version='1'>
+    <nd ref='465' />
+    <nd ref='119' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='483' visible='true' version='1'>
+    <nd ref='118' />
+    <nd ref='485' />
+    <tag k='highway' v='footway' />
+  </way>
+  <relation id='79' visible='true' version='1'>
+    <member type='way' ref='59' role='from' />
+    <member type='node' ref='61' role='via' />
+    <member type='way' ref='66' role='to' />
+    <tag k='name' v='straight on but only turn is not for motor vehicles' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='83' visible='true' version='1'>
+    <member type='node' ref='68' role='via' />
+    <member type='way' ref='70' role='to' />
+    <member type='way' ref='66' role='from' />
+    <tag k='name' v='straight on but no turn possible' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='91' visible='true' version='1'>
+    <member type='way' ref='70' role='from' />
+    <member type='node' ref='72' role='via' />
+    <member type='way' ref='80' role='to' />
+    <tag k='name' v='straight on but only turn is oneway' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='101' visible='true' version='1'>
+    <member type='way' ref='80' role='from' />
+    <member type='node' ref='86' role='via' />
+    <member type='way' ref='95' role='to' />
+    <tag k='name' v='no right turn into oneway' />
+    <tag k='restriction' v='no_right_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='117' visible='true' version='1'>
+    <member type='way' ref='88' role='from' />
+    <member type='node' ref='107' role='via' />
+    <member type='way' ref='105' role='to' />
+    <tag k='name' v='no right turn into non-vehicle way' />
+    <tag k='restriction' v='no_right_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='133' visible='true' version='1'>
+    <member type='way' ref='130' role='from' />
+    <member type='node' ref='122' role='via' />
+    <member type='way' ref='129' role='to' />
+    <tag k='name' v='straight on from incorrect oneway' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='137' visible='true' version='1'>
+    <member type='way' ref='128' role='to' />
+    <member type='way' ref='129' role='from' />
+    <member type='node' ref='124' role='via' />
+    <tag k='name' v='straight on but all directions are incorrect oneway' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='148' visible='true' version='1'>
+    <member type='way' ref='128' role='from' />
+    <member type='way' ref='141' role='to' />
+    <member type='node' ref='126' role='via' />
+    <tag k='name' v='no right turn from incorrect oneway' />
+    <tag k='restriction' v='no_right_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='182' visible='true' version='1'>
+    <member type='way' ref='167' role='from' />
+    <member type='node' ref='162' role='via' />
+    <member type='way' ref='167' role='to' />
+    <tag k='name' v='straight on not at end of way' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='184' visible='true' version='1'>
+    <member type='way' ref='167' role='from' />
+    <member type='node' ref='174' role='via' />
+    <member type='way' ref='172' role='to' />
+    <tag k='name' v='no right turn not at end of way' />
+    <tag k='restriction' v='no_right_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='477' visible='true' version='1'>
+    <member type='way' ref='466' role='from' />
+    <member type='node' ref='119' role='via' />
+    <member type='way' ref='130' role='to' />
+    <tag k='name' v='straight on from non-vehicle way' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='489' visible='true' version='1'>
+    <member type='way' ref='483' role='from' />
+    <member type='node' ref='118' role='via' />
+    <member type='way' ref='167' role='to' />
+    <tag k='name' v='no straight on from non-vehicle way' />
+    <tag k='restriction' v='no_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+</osm>
diff --git a/src/test/invalid-turn-relations.sh b/src/test/invalid-turn-relations.sh
new file mode 120000
index 0000000..bc8f799
--- /dev/null
+++ b/src/test/invalid-turn-relations.sh
@@ -0,0 +1 @@
+only-split.sh
\ No newline at end of file
diff --git a/src/test/loops.osm b/src/test/loops.osm
new file mode 100644
index 0000000..b83186e
--- /dev/null
+++ b/src/test/loops.osm
@@ -0,0 +1,168 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='484' version='1' visible='true' lat='-0.21901312467042416' lon='-0.517332609859675'>
+    <tag k='name' v='WP09' />
+  </node>
+  <node id='482' version='1' visible='true' lat='-0.2195967297237718' lon='-0.5174891879721774'>
+    <tag k='name' v='WP08' />
+  </node>
+  <node id='478' version='1' visible='true' lat='-0.21994546931795161' lon='-0.5173966645420623'>
+    <tag k='name' v='WP07' />
+  </node>
+  <node id='468' version='1' visible='true' lat='-0.2183796996477898' lon='-0.5191332581534525'>
+    <tag k='name' v='WP05' />
+  </node>
+  <node id='466' version='1' visible='true' lat='-0.21899177326561078' lon='-0.5190549690972014'>
+    <tag k='name' v='WP04' />
+  </node>
+  <node id='464' version='1' visible='true' lat='-0.21957537831978627' lon='-0.5191901956489079'>
+    <tag k='name' v='WP03' />
+  </node>
+  <node id='460' version='1' visible='true' lat='-0.2199098836454558' lon='-0.5191119065926569'>
+    <tag k='name' v='WP02' />
+  </node>
+  <node id='208' version='1' visible='true' lat='-0.21960114860694938' lon='-0.515860763795797'>
+    <tag k='name' v='WPfinish' />
+  </node>
+  <node id='204' version='1' visible='true' lat='-0.21955223220675274' lon='-0.5210161384621299'>
+    <tag k='name' v='WPstart' />
+  </node>
+  <node id='193' version='1' visible='true' lat='-0.21880536397011213' lon='-0.5171306758615497'>
+    <tag k='name' v='WP10' />
+  </node>
+  <node id='191' version='1' visible='true' lat='-0.21879379605820587' lon='-0.5177631602471016' />
+  <node id='190' version='1' visible='true' lat='-0.21926641829948795' lon='-0.5175789752337265' />
+  <node id='157' version='1' visible='true' lat='-0.21841686561366' lon='-0.5174617719465804'>
+    <tag k='name' v='WP11' />
+  </node>
+  <node id='149' version='1' visible='true' lat='-0.218775973869117' lon='-0.5188385645044677' />
+  <node id='147' version='1' visible='true' lat='-0.21876440595719907' lon='-0.5194710488900195' />
+  <node id='146' version='1' visible='true' lat='-0.21923702819939836' lon='-0.5192868638766445' />
+  <node id='145' version='1' visible='true' lat='-0.22027992726562468' lon='-0.5192553487141034'>
+    <tag k='name' v='WP01' />
+  </node>
+  <node id='144' version='1' visible='true' lat='-0.22069487557324122' lon='-0.5194891951207509' />
+  <node id='131' version='1' visible='true' lat='-0.2186995850954771' lon='-0.515945221575862' />
+  <node id='129' version='1' visible='true' lat='-0.21807335137583664' lon='-0.5192768664531259' />
+  <node id='123' version='1' visible='true' lat='-0.21881956658439772' lon='-0.5208128725880712' />
+  <node id='120' version='1' visible='true' lat='-0.22031145754149978' lon='-0.5160148024786712' />
+  <node id='117' version='1' visible='true' lat='-0.2203009132482348' lon='-0.5175762181579412'>
+    <tag k='name' v='WP06' />
+  </node>
+  <node id='109' version='1' visible='true' lat='-0.22022296242215664' lon='-0.5208850955075449' />
+  <node id='92' version='1' visible='true' lat='-0.2207386134847756' lon='-0.5178011278670027' />
+  <node id='54' version='1' visible='true' lat='-0.22078436832897996' lon='-0.5160352169735775' />
+  <node id='52' version='1' visible='true' lat='-0.2206667536990629' lon='-0.5208930003039592' />
+  <node id='51' version='1' visible='true' lat='-0.22189614840042465' lon='-0.5163053654637676' />
+  <node id='50' version='1' visible='true' lat='-0.22191520010406854' lon='-0.515835072317115' />
+  <node id='49' version='1' visible='true' lat='-0.22214182669974075' lon='-0.5160772027310793' />
+  <node id='48' version='1' visible='true' lat='-0.2216713552163718' lon='-0.5160632301909843' />
+  <node id='47' version='1' visible='true' lat='-0.21798215306964172' lon='-0.5161639796064453' />
+  <node id='46' version='1' visible='true' lat='-0.21774729558699316' lon='-0.515907564575113' />
+  <node id='45' version='1' visible='true' lat='-0.2180027381687473' lon='-0.5156713934099496' />
+  <node id='44' version='1' visible='true' lat='-0.21823992390738106' lon='-0.5159271465477753' />
+  <node id='24' version='1' visible='true' lat='-0.22193543526189635' lon='-0.5211703062894754' />
+  <node id='22' version='1' visible='true' lat='-0.22195451857008858' lon='-0.5207002437384278' />
+  <node id='21' version='1' visible='true' lat='-0.2221908395649969' lon='-0.5209415430673436' />
+  <node id='10' version='1' visible='true' lat='-0.21802147020530807' lon='-0.5210285258597515' />
+  <node id='8' version='1' visible='true' lat='-0.21778806952505525' lon='-0.52077169436654' />
+  <node id='6' version='1' visible='true' lat='-0.21804206074333024' lon='-0.5205359496121407' />
+  <node id='4' version='1' visible='true' lat='-0.21828070777942268' lon='-0.5207912709437164' />
+  <node id='3' version='1' visible='true' lat='-0.2217201380129468' lon='-0.5209275966384278' />
+  <way id='186' version='1' visible='true'>
+    <nd ref='92' />
+    <nd ref='117' />
+    <nd ref='478' />
+    <nd ref='190' />
+    <nd ref='191' />
+    <nd ref='157' />
+    <nd ref='193' />
+    <nd ref='190' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 2' />
+  </way>
+  <way id='143' version='1' visible='true'>
+    <nd ref='144' />
+    <nd ref='145' />
+    <nd ref='460' />
+    <nd ref='146' />
+    <nd ref='147' />
+    <nd ref='468' />
+    <nd ref='149' />
+    <nd ref='146' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 1' />
+  </way>
+  <way id='126' version='1' visible='true'>
+    <nd ref='123' />
+    <nd ref='129' />
+    <nd ref='157' />
+    <nd ref='131' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='112' version='1' visible='true'>
+    <nd ref='109' />
+    <nd ref='145' />
+    <nd ref='117' />
+    <nd ref='120' />
+    <tag k='highway' v='footway' />
+  </way>
+  <way id='55' version='1' visible='true'>
+    <nd ref='52' />
+    <nd ref='144' />
+    <nd ref='92' />
+    <nd ref='54' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='43' version='1' visible='true'>
+    <nd ref='49' />
+    <nd ref='50' />
+    <nd ref='48' />
+    <nd ref='51' />
+    <nd ref='49' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='42' version='1' visible='true'>
+    <nd ref='48' />
+    <nd ref='54' />
+    <nd ref='120' />
+    <nd ref='131' />
+    <nd ref='44' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 2' />
+  </way>
+  <way id='41' version='1' visible='true'>
+    <nd ref='44' />
+    <nd ref='45' />
+    <nd ref='46' />
+    <nd ref='47' />
+    <nd ref='44' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='20' version='1' visible='true'>
+    <nd ref='21' />
+    <nd ref='22' />
+    <nd ref='3' />
+    <nd ref='24' />
+    <nd ref='21' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='13' version='1' visible='true'>
+    <nd ref='4' />
+    <nd ref='6' />
+    <nd ref='8' />
+    <nd ref='10' />
+    <nd ref='4' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='5' version='1' visible='true'>
+    <nd ref='3' />
+    <nd ref='52' />
+    <nd ref='109' />
+    <nd ref='123' />
+    <nd ref='4' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 1' />
+  </way>
+</osm>
diff --git a/src/test/loops.sh b/src/test/loops.sh
new file mode 120000
index 0000000..b627b17
--- /dev/null
+++ b/src/test/loops.sh
@@ -0,0 +1 @@
+start-1-finish.sh
\ No newline at end of file
diff --git a/src/test/no-super.osm b/src/test/no-super.osm
new file mode 100644
index 0000000..c939cb8
--- /dev/null
+++ b/src/test/no-super.osm
@@ -0,0 +1,105 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='64' visible='true' version='1' lat='-0.21580935521018227' lon='-0.5154355738232163'>
+    <tag k='name' v='WP01b' />
+  </node>
+  <node id='62' visible='true' version='1' lat='-0.21666471332302356' lon='-0.5172180810465782'>
+    <tag k='name' v='WP02c' />
+  </node>
+  <node id='60' visible='true' version='1' lat='-0.2157435584302805' lon='-0.5194312611561215'>
+    <tag k='name' v='WP01a' />
+  </node>
+  <node id='45' visible='true' version='1' lat='-0.21591728779127017' lon='-0.5199274336780653' />
+  <node id='44' visible='true' version='1' lat='-0.21597736816873858' lon='-0.5151896331179581' />
+  <node id='43' visible='true' version='1' lat='-0.21573704665745827' lon='-0.5190348045870303' />
+  <node id='42' visible='true' version='1' lat='-0.21574562956861365' lon='-0.5146403229080908' />
+  <node id='41' visible='true' version='1' lat='-0.2159516194355604' lon='-0.5168375637475604' />
+  <node id='40' visible='true' version='1' lat='-0.21575421247979204' lon='-0.5159449346565257' />
+  <node id='39' visible='true' version='1' lat='-0.21593445361342675' lon='-0.5182537541323748' />
+  <node id='38' visible='true' version='1' lat='-0.21577137830214121' lon='-0.5177130268945367' />
+  <node id='289' visible='true' version='1' lat='-0.21719295194932486' lon='-0.5146598430558845' />
+  <node id='291' visible='true' version='1' lat='-0.21742469052732205' lon='-0.5152091532657517' />
+  <node id='293' visible='true' version='1' lat='-0.21720153485967852' lon='-0.5159644548043194'>
+    <tag k='name' v='WP03b' />
+  </node>
+  <node id='295' visible='true' version='1' lat='-0.21739894179660665' lon='-0.5168570838953541' />
+  <node id='297' visible='true' version='1' lat='-0.21721870068038443' lon='-0.5177325470423303'>
+    <tag k='name' v='WP03c' />
+  </node>
+  <node id='299' visible='true' version='1' lat='-0.2173817759761041' lon='-0.5182732742801686' />
+  <node id='301' visible='true' version='1' lat='-0.21718436903897512' lon='-0.5190543247348238'>
+    <tag k='name' v='WP03a' />
+  </node>
+  <node id='303' visible='true' version='1' lat='-0.21736461015558492' lon='-0.5199469538258588' />
+  <node id='317' visible='true' version='1' lat='-0.21645258968230982' lon='-0.5193407934495843' />
+  <node id='319' visible='true' version='1' lat='-0.21665139496835623' lon='-0.5199456759923936' />
+  <node id='320' visible='true' version='1' lat='-0.21647973675399712' lon='-0.5146585652224195' />
+  <node id='322' visible='true' version='1' lat='-0.2167114753429348' lon='-0.514975821764363' />
+  <node id='337' visible='true' version='1' lat='-0.21653560167566016' lon='-0.519026065647934'>
+    <tag k='name' v='WP02a' />
+  </node>
+  <node id='340' visible='true' version='1' lat='-0.21579199919973227' lon='-0.5172285073918816'>
+    <tag k='name' v='WP01c' />
+  </node>
+  <node id='341' visible='true' version='1' lat='-0.21676781704922854' lon='-0.5156691067434341'>
+    <tag k='name' v='WP02b' />
+  </node>
+  <node id='356' visible='true' version='1' lat='-0.21808280653903642' lon='-0.5199271360673275' />
+  <node id='357' visible='true' version='1' lat='-0.21790256543101175' lon='-0.5190345069762927'>
+    <tag k='name' v='WP04a' />
+  </node>
+  <node id='358' visible='true' version='1' lat='-0.2180999723587434' lon='-0.5182534565216375' />
+  <node id='359' visible='true' version='1' lat='-0.21793689707078506' lon='-0.5177127292837991'>
+    <tag k='name' v='WP04c' />
+  </node>
+  <node id='360' visible='true' version='1' lat='-0.2181171381784273' lon='-0.516837266136823' />
+  <node id='361' visible='true' version='1' lat='-0.21791973125089717' lon='-0.5159446370457883' />
+  <node id='362' visible='true' version='1' lat='-0.21814288690791456' lon='-0.5151893355072206' />
+  <node id='363' visible='true' version='1' lat='-0.21791114834095254' lon='-0.5146400252973532'>
+    <tag k='name' v='WP04b' />
+  </node>
+  <way id='46' visible='true' version='1'>
+    <nd ref='45' />
+    <nd ref='43' />
+    <nd ref='39' />
+    <nd ref='38' />
+    <nd ref='41' />
+    <nd ref='40' />
+    <nd ref='44' />
+    <nd ref='42' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='road1' />
+  </way>
+  <way id='305' visible='true' version='1'>
+    <nd ref='303' />
+    <nd ref='301' />
+    <nd ref='299' />
+    <nd ref='297' />
+    <nd ref='295' />
+    <nd ref='293' />
+    <nd ref='291' />
+    <nd ref='289' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='road3' />
+  </way>
+  <way id='324' visible='true' version='1'>
+    <nd ref='319' />
+    <nd ref='317' />
+    <nd ref='322' />
+    <nd ref='320' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='road2' />
+  </way>
+  <way id='355' visible='true' version='1'>
+    <nd ref='356' />
+    <nd ref='357' />
+    <nd ref='358' />
+    <nd ref='359' />
+    <nd ref='360' />
+    <nd ref='361' />
+    <nd ref='362' />
+    <nd ref='363' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='road4' />
+  </way>
+</osm>
diff --git a/src/test/no-super.sh b/src/test/no-super.sh
new file mode 120000
index 0000000..a093b70
--- /dev/null
+++ b/src/test/no-super.sh
@@ -0,0 +1 @@
+a-b-c.sh
\ No newline at end of file
diff --git a/src/test/only-split.sh b/src/test/only-split.sh
new file mode 100755
index 0000000..c28e4f4
--- /dev/null
+++ b/src/test/only-split.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# Exit on error
+
+set -e
+
+# Test name
+
+name=`basename $0 .sh`
+
+# Slim or non-slim
+
+if [ "$1" = "slim" ]; then
+    slim="-slim"
+    dir="slim"
+else
+    slim=""
+    dir="fat"
+fi
+
+[ -d $dir ] || mkdir $dir
+
+# Run the programs under a run-time debugger
+
+debugger=valgrind
+debugger=
+
+# Name related options
+
+osm=$name.osm
+log=$name$slim.log
+
+option_prefix="--prefix=$name"
+option_dir="--dir=$dir"
+
+# Generic program options
+
+option_planetsplitter="--loggable --tagging=../../xml/routino-tagging.xml --errorlog"
+option_filedumper="--dump-osm"
+
+# Run planetsplitter
+
+echo "Running planetsplitter"
+
+echo ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm > $log
+$debugger ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm >> $log
+
+# Run filedumper
+
+echo "Running filedumper"
+
+echo ../filedumper$slim $option_dir $option_prefix $option_filedumper >> $log
+$debugger ../filedumper$slim $option_dir $option_prefix $option_filedumper > $dir/$osm
+
diff --git a/src/test/start-1-finish.sh b/src/test/start-1-finish.sh
new file mode 100755
index 0000000..58e8f1a
--- /dev/null
+++ b/src/test/start-1-finish.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# Exit on error
+
+set -e
+
+# Test name
+
+name=`basename $0 .sh`
+
+# Slim or non-slim
+
+if [ "$1" = "slim" ]; then
+    slim="-slim"
+    dir="slim"
+else
+    slim=""
+    dir="fat"
+fi
+
+[ -d $dir ] || mkdir $dir
+
+# Run the programs under a run-time debugger
+
+debugger=valgrind
+debugger=
+
+# Name related options
+
+osm=$name.osm
+log=$name$slim.log
+
+option_prefix="--prefix=$name"
+option_dir="--dir=$dir"
+
+# Generic program options
+
+option_planetsplitter="--loggable --tagging=../../xml/routino-tagging.xml --errorlog"
+option_filedumper="--dump-osm"
+option_router="--loggable --transport=motorcar --profiles=../../xml/routino-profiles.xml --translations=copyright.xml"
+
+# Run planetsplitter
+
+echo "Running planetsplitter"
+
+echo ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm > $log
+$debugger ../planetsplitter$slim $option_dir $option_prefix $option_planetsplitter $osm >> $log
+
+# Run filedumper
+
+echo "Running filedumper"
+
+echo ../filedumper$slim $option_dir $option_prefix $option_filedumper >> $log
+$debugger ../filedumper$slim $option_dir $option_prefix $option_filedumper > $dir/$osm
+
+# Waypoints
+
+waypoints=`perl waypoints.pl $osm list`
+
+waypoint_start=`perl waypoints.pl $osm WPstart 1`
+waypoint_finish=`perl waypoints.pl $osm WPfinish 3`
+
+# Run the router for each waypoint
+
+for waypoint in $waypoints; do
+
+    [ ! $waypoint = "WPstart"  ] || continue
+    [ ! $waypoint = "WPfinish" ] || continue
+
+    echo "Running router : $waypoint"
+
+    waypoint_test=`perl waypoints.pl $osm $waypoint 2`
+
+    [ -d $dir/$name-$waypoint ] || mkdir $dir/$name-$waypoint
+
+    echo ../router$slim $option_dir $option_prefix $option_osm $option_router $waypoint_start $waypoint_test $waypoint_finish >> $log
+    $debugger ../router$slim $option_dir $option_prefix $option_osm $option_router $waypoint_start $waypoint_test $waypoint_finish >> $log
+
+    mv shortest* $dir/$name-$waypoint
+
+    echo cmp $dir/$name-$waypoint/shortest-all.txt expected/$name-$waypoint.txt >> $log
+    cmp $dir/$name-$waypoint/shortest-all.txt expected/$name-$waypoint.txt >> $log
+
+done
diff --git a/src/test/super-or-not.osm b/src/test/super-or-not.osm
new file mode 100644
index 0000000..90ecfb6
--- /dev/null
+++ b/src/test/super-or-not.osm
@@ -0,0 +1,55 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='86' visible='true' version='1' lat='-0.21688045301473516' lon='-0.5151829113120308'>
+    <tag k='name' v='WP02b' />
+  </node>
+  <node id='82' visible='true' version='1' lat='-0.21646963010999992' lon='-0.5174119101486654' />
+  <node id='77' visible='true' version='1' lat='-0.21721431100258048' lon='-0.5141522988242739' />
+  <node id='74' visible='true' version='1' lat='-0.21725785769610798' lon='-0.5202996845787107' />
+  <node id='72' visible='true' version='1' lat='-0.21797637812110027' lon='-0.5199295350232488' />
+  <node id='70' visible='true' version='1' lat='-0.2180562137217683' lon='-0.5149942076170893' />
+  <node id='38' visible='true' version='1' lat='-0.215865729480266' lon='-0.5179307619271613'>
+    <tag k='name' v='WP03b' />
+  </node>
+  <node id='39' visible='true' version='1' lat='-0.2164352406236952' lon='-0.5185150361715245' />
+  <node id='40' visible='true' version='1' lat='-0.21645095962829408' lon='-0.5162207323645169' />
+  <node id='41' visible='true' version='1' lat='-0.2159298460869398' lon='-0.5168230480787187' />
+  <node id='42' visible='true' version='1' lat='-0.21724799056850824' lon='-0.5152064339929149' />
+  <node id='43' visible='true' version='1' lat='-0.215918491230658' lon='-0.5190638359247135' />
+  <node id='44' visible='true' version='1' lat='-0.21594107925441666' lon='-0.5156105875143656' />
+  <node id='45' visible='true' version='1' lat='-0.2172454620011612' lon='-0.5196371203012323'>
+    <tag k='name' v='WP03a' />
+  </node>
+  <node id='60' visible='true' version='1' lat='-0.21608467422596514' lon='-0.5187345090517225'>
+    <tag k='name' v='WP01a' />
+  </node>
+  <node id='64' visible='true' version='1' lat='-0.21623756439742947' lon='-0.515812981213099'>
+    <tag k='name' v='WP01b' />
+  </node>
+  <node id='340' visible='true' version='1' lat='-0.21686615103763912' lon='-0.5193623107116035'>
+    <tag k='name' v='WP02a' />
+  </node>
+  <way id='75' visible='true' version='1'>
+    <nd ref='74' />
+    <nd ref='45' />
+    <nd ref='42' />
+    <nd ref='77' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='Main road' />
+  </way>
+  <way id='46' visible='true' version='1'>
+    <nd ref='72' />
+    <nd ref='45' />
+    <nd ref='43' />
+    <nd ref='39' />
+    <nd ref='38' />
+    <nd ref='82' />
+    <nd ref='41' />
+    <nd ref='40' />
+    <nd ref='44' />
+    <nd ref='42' />
+    <nd ref='70' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='Local road' />
+  </way>
+</osm>
diff --git a/src/test/super-or-not.sh b/src/test/super-or-not.sh
new file mode 120000
index 0000000..b1debba
--- /dev/null
+++ b/src/test/super-or-not.sh
@@ -0,0 +1 @@
+a-b.sh
\ No newline at end of file
diff --git a/src/test/turns.osm b/src/test/turns.osm
new file mode 100644
index 0000000..089582a
--- /dev/null
+++ b/src/test/turns.osm
@@ -0,0 +1,456 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='3' version='1' visible='true' lat='-0.2217201380129468' lon='-0.5209275966384278' />
+  <node id='4' version='1' visible='true' lat='-0.21828070777942268' lon='-0.5207912709437164' />
+  <node id='6' version='1' visible='true' lat='-0.21804206074333024' lon='-0.5205359496121407' />
+  <node id='8' version='1' visible='true' lat='-0.21778806952505525' lon='-0.52077169436654' />
+  <node id='10' version='1' visible='true' lat='-0.21802147020530807' lon='-0.5210285258597515' />
+  <node id='21' version='1' visible='true' lat='-0.2221908395649969' lon='-0.5209415430673436' />
+  <node id='22' version='1' visible='true' lat='-0.22195451857008858' lon='-0.5207002437384278' />
+  <node id='24' version='1' visible='true' lat='-0.22193543526189635' lon='-0.5211703062894754' />
+  <node id='44' version='1' visible='true' lat='-0.21823992390738106' lon='-0.5159271465477753' />
+  <node id='45' version='1' visible='true' lat='-0.2180027381687473' lon='-0.5156713934099496' />
+  <node id='46' version='1' visible='true' lat='-0.21774729558699316' lon='-0.515907564575113' />
+  <node id='47' version='1' visible='true' lat='-0.21798215306964172' lon='-0.5161639796064453' />
+  <node id='48' version='1' visible='true' lat='-0.2216713552163718' lon='-0.5160632301909843' />
+  <node id='49' version='1' visible='true' lat='-0.22214182669974075' lon='-0.5160772027310793' />
+  <node id='50' version='1' visible='true' lat='-0.22191520010406854' lon='-0.515835072317115' />
+  <node id='51' version='1' visible='true' lat='-0.22189614840042465' lon='-0.5163053654637676' />
+  <node id='52' version='1' visible='true' lat='-0.2206667536990629' lon='-0.5208930003039592' />
+  <node id='54' version='1' visible='true' lat='-0.22078436832897996' lon='-0.5160352169735775' />
+  <node id='113' version='1' visible='true' lat='-0.22135974620623747' lon='-0.518860446158009' />
+  <node id='115' version='1' visible='true' lat='-0.22171077830316824' lon='-0.5185110945906238'>
+    <tag k='name' v='WP16' />
+  </node>
+  <node id='117' version='1' visible='true' lat='-0.22137564544507876' lon='-0.5182342364502396' />
+  <node id='144' version='1' visible='true' lat='-0.22067170616836507' lon='-0.5202228983841708' />
+  <node id='146' version='1' visible='true' lat='-0.2206914242782775' lon='-0.5194618767809532' />
+  <node id='204' version='1' visible='true' lat='-0.21845168535119228' lon='-0.5209389065396647'>
+    <tag k='name' v='WPstart' />
+  </node>
+  <node id='208' version='1' visible='true' lat='-0.22155124019804942' lon='-0.5210546105815851'>
+    <tag k='name' v='WPfinish' />
+  </node>
+  <node id='345' version='1' visible='true' lat='-0.22130760599946336' lon='-0.5209140478361174' />
+  <node id='347' version='1' visible='true' lat='-0.22143095927716896' lon='-0.5160556378985264' />
+  <node id='366' version='1' visible='true' lat='-0.22006717109132917' lon='-0.5201090583078041'>
+    <tag k='name' v='WP02' />
+  </node>
+  <node id='368' version='1' visible='true' lat='-0.22011041864306613' lon='-0.5195530416788935' />
+  <node id='381' version='1' visible='true' lat='-0.22018081579726256' lon='-0.5201767078687767'>
+    <tag k='name' v='WP01' />
+  </node>
+  <node id='407' version='1' visible='true' lat='-0.2201433541990108' lon='-0.5181618144888986' />
+  <node id='408' version='1' visible='true' lat='-0.2201952178944254' lon='-0.518785480678782'>
+    <tag k='name' v='WP04' />
+  </node>
+  <node id='409' version='1' visible='true' lat='-0.22072435983293912' lon='-0.5180706495909584' />
+  <node id='410' version='1' visible='true' lat='-0.22010010664738394' lon='-0.5187178311178092'>
+    <tag k='name' v='WP05' />
+  </node>
+  <node id='411' version='1' visible='true' lat='-0.21985014157743876' lon='-0.5184157667048493'>
+    <tag k='name' v='WP06' />
+  </node>
+  <node id='414' version='1' visible='true' lat='-0.22070825072718983' lon='-0.5188415195272463' />
+  <node id='438' version='1' visible='true' lat='-0.21910696568370078' lon='-0.5208274567527769' />
+  <node id='440' version='1' visible='true' lat='-0.21914411941337283' lon='-0.517257414288283' />
+  <node id='442' version='1' visible='true' lat='-0.21915357836853688' lon='-0.5168261331058486' />
+  <node id='444' version='1' visible='true' lat='-0.21918392662304603' lon='-0.5159682545255313' />
+  <node id='450' version='1' visible='true' lat='-0.21935243417579808' lon='-0.5170599796507417' />
+  <node id='452' version='1' visible='true' lat='-0.2189233360894271' lon='-0.5170717085106757' />
+  <node id='460' version='1' visible='true' lat='-0.21981720602084437' lon='-0.5198069938948442'>
+    <tag k='name' v='WP03' />
+  </node>
+  <node id='462' version='1' visible='true' lat='-0.21956544771737352' lon='-0.5170629802253586' />
+  <node id='464' version='1' visible='true' lat='-0.21868575166121326' lon='-0.5170645248638079' />
+  <node id='466' version='1' visible='true' lat='-0.21899764600703492' lon='-0.5172063739650437' />
+  <node id='468' version='1' visible='true' lat='-0.21896530807951195' lon='-0.5169107352309166' />
+  <node id='470' version='1' visible='true' lat='-0.21930401615320513' lon='-0.5169042698221518' />
+  <node id='472' version='1' visible='true' lat='-0.2192913238300286' lon='-0.5171928800099428' />
+  <node id='476' version='1' visible='true' lat='-0.21912267853817124' lon='-0.520206478114032' />
+  <node id='478' version='1' visible='true' lat='-0.21847413773357155' lon='-0.5200595788170104'>
+    <tag k='name' v='WP11' />
+  </node>
+  <node id='480' version='1' visible='true' lat='-0.21825788992119952' lon='-0.5197892670883819'>
+    <tag k='name' v='WP12' />
+  </node>
+  <node id='482' version='1' visible='true' lat='-0.218481860869659' lon='-0.5195421249364931' />
+  <node id='484' version='1' visible='true' lat='-0.21913151893031774' lon='-0.5194265277011769' />
+  <node id='501' version='1' visible='true' lat='-0.218591557635407' lon='-0.5201383629528404'>
+    <tag k='name' v='WP10' />
+  </node>
+  <node id='523' version='1' visible='true' lat='-0.21865858904503085' lon='-0.5186744365873915'>
+    <tag k='name' v='WP13' />
+  </node>
+  <node id='524' version='1' visible='true' lat='-0.21913455501068563' lon='-0.5188231434133839' />
+  <node id='525' version='1' visible='true' lat='-0.21860054470950374' lon='-0.5185573641050701'>
+    <tag k='name' v='WP14' />
+  </node>
+  <node id='526' version='1' visible='true' lat='-0.21843089127829385' lon='-0.5182429439617734'>
+    <tag k='name' v='WP15' />
+  </node>
+  <node id='527' version='1' visible='true' lat='-0.21861908098471325' lon='-0.5179210352289688' />
+  <node id='528' version='1' visible='true' lat='-0.2191444862981745' lon='-0.5176260007804667' />
+  <node id='561' version='1' visible='true' lat='-0.22008888465226106' lon='-0.517279352425253'>
+    <tag k='name' v='WP08' />
+  </node>
+  <node id='564' version='1' visible='true' lat='-0.22022483800004092' lon='-0.5173643144262515'>
+    <tag k='name' v='WP07' />
+  </node>
+  <node id='565' version='1' visible='true' lat='-0.220096607787534' lon='-0.5167618985447355' />
+  <node id='566' version='1' visible='true' lat='-0.219872636863204' lon='-0.5170090406966246'>
+    <tag k='name' v='WP09' />
+  </node>
+  <node id='571' version='1' visible='true' lat='-0.22073868008618305' lon='-0.5174253809858272' />
+  <node id='575' version='1' visible='true' lat='-0.22076041278528005' lon='-0.5166462582606399' />
+  <way id='5' version='1' visible='true'>
+    <nd ref='3' />
+    <nd ref='345' />
+    <nd ref='52' />
+    <nd ref='438' />
+    <nd ref='4' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 1' />
+  </way>
+  <way id='13' version='1' visible='true'>
+    <nd ref='4' />
+    <nd ref='6' />
+    <nd ref='8' />
+    <nd ref='10' />
+    <nd ref='4' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='20' version='1' visible='true'>
+    <nd ref='21' />
+    <nd ref='22' />
+    <nd ref='3' />
+    <nd ref='24' />
+    <nd ref='21' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='41' version='1' visible='true'>
+    <nd ref='44' />
+    <nd ref='45' />
+    <nd ref='46' />
+    <nd ref='47' />
+    <nd ref='44' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='42' version='1' visible='true'>
+    <nd ref='48' />
+    <nd ref='347' />
+    <nd ref='54' />
+    <nd ref='444' />
+    <nd ref='44' />
+    <tag k='highway' v='primary' />
+    <tag k='name' v='main 2' />
+  </way>
+  <way id='43' version='1' visible='true'>
+    <nd ref='49' />
+    <nd ref='50' />
+    <nd ref='48' />
+    <nd ref='51' />
+    <nd ref='49' />
+    <tag k='highway' v='primary' />
+  </way>
+  <way id='55' version='1' visible='true'>
+    <nd ref='52' />
+    <nd ref='144' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='116' action='modify' visible='true'>
+    <nd ref='113' />
+    <nd ref='115' />
+    <nd ref='117' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 6' />
+  </way>
+  <way id='128' visible='true'>
+    <nd ref='113' />
+    <nd ref='117' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='bottom road' />
+  </way>
+  <way id='129' visible='true'>
+    <nd ref='117' />
+    <nd ref='347' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='bottom road' />
+  </way>
+  <way id='143' version='1' visible='true'>
+    <nd ref='144' />
+    <nd ref='366' />
+    <nd ref='460' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 1' />
+  </way>
+  <way id='348' version='1' visible='true'>
+    <nd ref='345' />
+    <nd ref='113' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='bottom road' />
+  </way>
+  <way id='370' version='1' visible='true'>
+    <nd ref='144' />
+    <nd ref='146' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='372' version='1' visible='true'>
+    <nd ref='460' />
+    <nd ref='368' />
+    <nd ref='146' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 1' />
+  </way>
+  <way id='412' version='1' visible='true'>
+    <nd ref='411' />
+    <nd ref='407' />
+    <nd ref='409' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 2' />
+  </way>
+  <way id='413' version='1' visible='true'>
+    <nd ref='414' />
+    <nd ref='410' />
+    <nd ref='411' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 2' />
+  </way>
+  <way id='425' version='1' visible='true'>
+    <nd ref='146' />
+    <nd ref='414' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='426' version='1' visible='true'>
+    <nd ref='414' />
+    <nd ref='409' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='427' version='1' visible='true'>
+    <nd ref='409' />
+    <nd ref='571' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='441' version='1' visible='true'>
+    <nd ref='438' />
+    <nd ref='476' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='top road' />
+  </way>
+  <way id='447' version='1' visible='true'>
+    <nd ref='440' />
+    <nd ref='472' />
+    <nd ref='450' />
+    <nd ref='470' />
+    <nd ref='442' />
+    <nd ref='468' />
+    <nd ref='452' />
+    <nd ref='466' />
+    <nd ref='440' />
+    <tag k='highway' v='residential' />
+    <tag k='junction' v='roundabout' />
+    <tag k='name' v='roundabout' />
+    <tag k='oneway' v='yes' />
+  </way>
+  <way id='448' version='1' visible='true'>
+    <nd ref='442' />
+    <nd ref='444' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='top road' />
+  </way>
+  <way id='463' version='1' visible='true'>
+    <nd ref='450' />
+    <nd ref='462' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='465' version='1' visible='true'>
+    <nd ref='452' />
+    <nd ref='464' />
+    <tag k='highway' v='residential' />
+  </way>
+  <way id='479' version='1' visible='true'>
+    <nd ref='476' />
+    <nd ref='478' />
+    <nd ref='480' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 4' />
+  </way>
+  <way id='493' version='1' visible='true'>
+    <nd ref='476' />
+    <nd ref='484' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='top road' />
+  </way>
+  <way id='494' version='1' visible='true'>
+    <nd ref='484' />
+    <nd ref='524' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='top road' />
+  </way>
+  <way id='521' version='1' visible='true'>
+    <nd ref='524' />
+    <nd ref='525' />
+    <nd ref='526' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 5' />
+  </way>
+  <way id='522' version='1' visible='true'>
+    <nd ref='526' />
+    <nd ref='527' />
+    <nd ref='528' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 5' />
+  </way>
+  <way id='533' version='1' visible='true'>
+    <nd ref='524' />
+    <nd ref='528' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='top road' />
+  </way>
+  <way id='534' version='1' visible='true'>
+    <nd ref='528' />
+    <nd ref='440' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='top road' />
+  </way>
+  <way id='546' version='1' visible='true'>
+    <nd ref='480' />
+    <nd ref='482' />
+    <nd ref='484' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 4' />
+  </way>
+  <way id='567' version='1' visible='true'>
+    <nd ref='566' />
+    <nd ref='565' />
+    <nd ref='575' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 3' />
+  </way>
+  <way id='568' version='1' visible='true'>
+    <nd ref='571' />
+    <nd ref='561' />
+    <nd ref='566' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='loop 3' />
+  </way>
+  <way id='579' version='1' visible='true'>
+    <nd ref='571' />
+    <nd ref='575' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <way id='580' version='1' visible='true'>
+    <nd ref='575' />
+    <nd ref='54' />
+    <tag k='highway' v='residential' />
+    <tag k='name' v='high street' />
+  </way>
+  <relation id='132' version='1' visible='true'>
+    <member type='node' ref='113' role='via' />
+    <member type='way' ref='348' role='from' />
+    <member type='way' ref='128' role='to' />
+    <tag k='except' v='motorcar' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='135' version='1' visible='true'>
+    <member type='node' ref='113' role='via' />
+    <member type='way' ref='348' role='to' />
+    <member type='way' ref='128' role='from' />
+    <tag k='except' v='motorcar' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='140' version='1' visible='true'>
+    <member type='node' ref='117' role='via' />
+    <member type='way' ref='129' role='to' />
+    <member type='way' ref='128' role='from' />
+    <tag k='except' v='motorcar' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='143' visible='true'>
+    <member type='node' ref='117' role='via' />
+    <member type='way' ref='129' role='from' />
+    <member type='way' ref='128' role='to' />
+    <tag k='except' v='motorcar' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='377' version='1' visible='true'>
+    <member type='way' ref='55' role='from' />
+    <member type='node' ref='144' role='via' />
+    <member type='way' ref='143' role='to' />
+    <tag k='restriction' v='no_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='430' version='1' visible='true'>
+    <member type='way' ref='425' role='from' />
+    <member type='node' ref='414' role='via' />
+    <member type='way' ref='413' role='to' />
+    <tag k='restriction' v='no_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='436' version='1' visible='true'>
+    <member type='way' ref='413' role='from' />
+    <member type='node' ref='414' role='via' />
+    <member type='way' ref='426' role='to' />
+    <tag k='restriction' v='only_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='497' version='1' visible='true'>
+    <member type='way' ref='441' role='from' />
+    <member type='node' ref='476' role='via' />
+    <member type='way' ref='479' role='to' />
+    <tag k='restriction' v='no_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='499' version='1' visible='true'>
+    <member type='way' ref='493' role='from' />
+    <member type='node' ref='484' role='via' />
+    <member type='way' ref='546' role='to' />
+    <tag k='restriction' v='no_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='540' version='1' visible='true'>
+    <member type='way' ref='521' role='from' />
+    <member type='node' ref='524' role='via' />
+    <member type='way' ref='533' role='to' />
+    <tag k='restriction' v='only_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='542' version='1' visible='true'>
+    <member type='way' ref='533' role='from' />
+    <member type='node' ref='528' role='via' />
+    <member type='way' ref='534' role='to' />
+    <tag k='restriction' v='only_straight_on' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='551' version='1' visible='true'>
+    <member type='way' ref='494' role='from' />
+    <member type='node' ref='524' role='via' />
+    <member type='way' ref='521' role='to' />
+    <tag k='restriction' v='no_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='583' version='1' visible='true'>
+    <member type='way' ref='427' role='from' />
+    <member type='node' ref='571' role='via' />
+    <member type='way' ref='568' role='to' />
+    <tag k='restriction' v='no_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+  <relation id='585' version='1' visible='true'>
+    <member type='way' ref='579' role='from' />
+    <member type='node' ref='575' role='via' />
+    <member type='way' ref='567' role='to' />
+    <tag k='restriction' v='no_left_turn' />
+    <tag k='type' v='restriction' />
+  </relation>
+</osm>
diff --git a/src/test/turns.sh b/src/test/turns.sh
new file mode 120000
index 0000000..b627b17
--- /dev/null
+++ b/src/test/turns.sh
@@ -0,0 +1 @@
+start-1-finish.sh
\ No newline at end of file
diff --git a/src/test/waypoints.pl b/src/test/waypoints.pl
new file mode 100755
index 0000000..681a689
--- /dev/null
+++ b/src/test/waypoints.pl
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+# Command line
+
+if($#ARGV<1 || $ARGV>2 || ! -f $ARGV[0])
+  {
+   die  "Usage: waypoints.pl <filename.osm> list\n".
+        "       waypoints.pl <filename.osm> <name> <number>\n";
+  }
+
+# Parse the file
+
+open(FILE,"<$ARGV[0]") || die "Cannot open '$ARGV[0]'\n";
+
+%waypoints=();
+ at waypoints=();
+ at waypoint_lat=();
+ at waypoint_lon=();
+$innode=0;
+
+while(<FILE>)
+  {
+   if($innode)
+     {
+      if(m%<tag k='name' v='([^']+)'%)
+        {
+         push(@waypoints,$1);
+         $waypoints{$1}=$#waypoints;
+        }
+      $innode=0 if(m%</node>%);
+     }
+   elsif(m%<node .* lat='([-.0-9]+)' *lon='([-.0-9]+)' *>%)
+     {
+      $innode=1;
+      push(@waypoint_lat,$1);
+      push(@waypoint_lon,$2);
+     }
+  }
+
+close(FILE);
+
+# Perform the action
+
+if($ARGV[1] eq "list")
+  {
+   print join(" ",sort @waypoints)."\n";
+   exit 0;
+  }
+
+if($waypoints{$ARGV[1]} ne "")
+  {
+   print "--lat$ARGV[2]=$waypoint_lat[$waypoints{$ARGV[1]}] --lon$ARGV[2]=$waypoint_lon[$waypoints{$ARGV[1]}]\n";
+   exit 0;
+  }
+
+exit 1;
diff --git a/src/translations.c b/src/translations.c
index 5812924..d852a0f 100644
--- a/src/translations.c
+++ b/src/translations.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/translations.c,v 1.13 2010/09/15 18:30:08 amb Exp $
-
  Load the translations from a file and the functions for handling them.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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
@@ -413,10 +411,9 @@ static int TurnType_function(const char *_tag_,int _type_,const char *direction,
  if(_type_&XMLPARSE_TAG_START && store)
    {
     char *xmlstring;
-
     int d;
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,direction,d);
+    XMLPARSE_ASSERT_INTEGER(_tag_,direction); d=atoi(direction);
     XMLPARSE_ASSERT_STRING(_tag_,string);
 
     d+=4;
@@ -454,7 +451,7 @@ static int HeadingType_function(const char *_tag_,int _type_,const char *directi
     char *xmlstring;
     int d;
 
-    XMLPARSE_ASSERT_INTEGER(_tag_,direction,d);
+    XMLPARSE_ASSERT_INTEGER(_tag_,direction); d=atoi(direction);
     XMLPARSE_ASSERT_STRING(_tag_,string);
 
     d+=4;
@@ -1099,6 +1096,7 @@ static int languageType_function(const char *_tag_,int _type_,const char *lang)
 
 int ParseXMLTranslations(const char *filename,const char *language)
 {
+ FILE *file;
  int retval;
 
  store_lang=language;
@@ -1109,7 +1107,7 @@ int ParseXMLTranslations(const char *filename,const char *language)
     return(1);
    }
 
- FILE *file=fopen(filename,"r");
+ file=fopen(filename,"r");
 
  if(!file)
    {
diff --git a/src/translations.h b/src/translations.h
index 13902ed..e387d9f 100644
--- a/src/translations.h
+++ b/src/translations.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/translations.h,v 1.4 2010/05/29 13:54:23 amb Exp $
-
  Load the translations from a file and the functions for handling them.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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,7 +26,7 @@
 #include "types.h"
 
 
-/* Variable declations */
+/* Global variable declarations */
 
 extern char *translate_copyright_creator[2];
 extern char *translate_copyright_source[2];
@@ -62,8 +60,10 @@ extern char *translate_gpx_inter;
 extern char *translate_gpx_trip;
 extern char *translate_gpx_finish;
 
-/* Functions */
+
+/* Functions in translations.c */
 
 int ParseXMLTranslations(const char *filename,const char *language);
 
+
 #endif /* TRANSLATIONS_H */
diff --git a/src/types.c b/src/types.c
index 22183db..f71ff19 100644
--- a/src/types.c
+++ b/src/types.c
@@ -1,5 +1,5 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/types.c,v 1.6 2010/09/17 17:43:41 amb Exp $
+ $Header: /home/amb/CVS/routino/src/types.c,v 1.10 2010-11-28 15:12:41 amb Exp $
 
  Functions for handling the data types.
 
@@ -236,7 +236,6 @@ const char *HighwayName(Highway highway)
     ;
 
    case Way_OneWay:
-   case Way_Roundabout:
     ;
    }
 
@@ -330,71 +329,158 @@ const char *PropertyName(Property property)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  A string containing the names of highways.
+
+  const char *HighwaysNameList Returns the list of names.
+
+  highways_t highways The highways type.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+const char *HighwaysNameList(highways_t highways)
+{
+ static char string[256];
+
+ string[0]=0;
+
+ if(highways & Highways_Motorway)
+    strcat(string,"motorway");
+
+ if(highways & Highways_Trunk)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"trunk");
+   }
+
+ if(highways & Highways_Primary)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"primary");
+   }
+
+ if(highways & Highways_Tertiary)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"tertiary");
+   }
+
+ if(highways & Highways_Unclassified)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"unclassified");
+   }
+
+ if(highways & Highways_Residential)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"residential");
+   }
+
+ if(highways & Highways_Service)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"service");
+   }
+
+ if(highways & Highways_Track)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"track");
+   }
+
+ if(highways & Highways_Cycleway)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"cycleway");
+   }
+
+ if(highways & Highways_Path)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"path");
+   }
+
+ if(highways & Highways_Steps)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"steps");
+   }
+
+ if(highways & Highways_Ferry)
+   {
+    if(*string) strcat(string,", ");
+    strcat(string,"ferry");
+   }
+
+ return(string);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   A string containing the names of allowed transports on a way.
 
   const char *AllowedNameList Returns the list of names.
 
-  allow_t allowed The allowed type.
+  transports_t allowed The allowed type.
   ++++++++++++++++++++++++++++++++++++++*/
 
-const char *AllowedNameList(allow_t allowed)
+const char *AllowedNameList(transports_t allowed)
 {
  static char string[256];
 
  string[0]=0;
 
- if(allowed & Allow_Foot)
+ if(allowed & Transports_Foot)
     strcat(string,"foot");
 
- if(allowed & Allow_Horse)
+ if(allowed & Transports_Horse)
    {
     if(*string) strcat(string,", ");
     strcat(string,"horse");
    }
 
- if(allowed & Allow_Wheelchair)
+ if(allowed & Transports_Wheelchair)
    {
     if(*string) strcat(string,", ");
     strcat(string,"wheelchair");
    }
 
- if(allowed & Allow_Bicycle)
+ if(allowed & Transports_Bicycle)
    {
     if(*string) strcat(string,", ");
     strcat(string,"bicycle");
    }
 
- if(allowed & Allow_Moped)
+ if(allowed & Transports_Moped)
    {
     if(*string) strcat(string,", ");
     strcat(string,"moped");
    }
 
- if(allowed & Allow_Motorbike)
+ if(allowed & Transports_Motorbike)
    {
     if(*string) strcat(string,", ");
     strcat(string,"motorbike");
    }
 
- if(allowed & Allow_Motorcar)
+ if(allowed & Transports_Motorcar)
    {
     if(*string) strcat(string,", ");
     strcat(string,"motorcar");
    }
 
- if(allowed & Allow_Goods)
+ if(allowed & Transports_Goods)
    {
     if(*string) strcat(string,", ");
     strcat(string,"goods");
    }
 
- if(allowed & Allow_HGV)
+ if(allowed & Transports_HGV)
    {
     if(*string) strcat(string,", ");
     strcat(string,"hgv");
    }
 
- if(allowed & Allow_PSV)
+ if(allowed & Transports_PSV)
    {
     if(*string) strcat(string,", ");
     strcat(string,"psv");
@@ -409,10 +495,10 @@ const char *AllowedNameList(allow_t allowed)
 
   const char *PropertiesNameList Returns the list of names.
 
-  wayprop_t properties The properties of the way.
+  properties_t properties The properties of the way.
   ++++++++++++++++++++++++++++++++++++++*/
 
-const char *PropertiesNameList(wayprop_t properties)
+const char *PropertiesNameList(properties_t properties)
 {
  static char string[256];
 
diff --git a/src/types.h b/src/types.h
index c8af825..2abbd66 100644
--- a/src/types.h
+++ b/src/types.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/types.h,v 1.48 2010/09/17 17:43:41 amb Exp $
-
  Type definitions
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -25,6 +23,7 @@
 #ifndef TYPES_H
 #define TYPES_H    /*+ To stop multiple inclusions. +*/
 
+#include <inttypes.h>
 #include <stdint.h>
 #include <math.h>
 
@@ -36,6 +35,10 @@
 
 /* Constants and macros for handling them */
 
+/*+ The number of waypoints allowed to be specified. +*/
+#define NWAYPOINTS 99
+
+
 /*+ An undefined node index. +*/
 #define NO_NODE        (~(index_t)0)
 
@@ -45,6 +48,9 @@
 /*+ An undefined way index. +*/
 #define NO_WAY         (~(index_t)0)
 
+/*+ An undefined relation index. +*/
+#define NO_RELATION    (~(index_t)0)
+
 
 /*+ The lowest number allowed for a fake node. +*/
 #define NODE_FAKE      ((index_t)0xffff0000)
@@ -62,11 +68,23 @@
 /*+ A flag to mark a node as a super-node. +*/
 #define NODE_SUPER     ((uint16_t)0x8000)
 
+/*+ A flag to mark a node as suitable for a U-turn. +*/
+#define NODE_UTURN     ((uint16_t)0x4000)
+
+/*+ A flag to mark a node as a mini-roundabout. +*/
+#define NODE_MINIRNDBT ((uint16_t)0x2000)
+
+/*+ A flag to mark a node as a turn relation via node. +*/
+#define NODE_TURNRSTRCT ((uint16_t)0x1000)
+
+/*+ A flag to mark a node as a turn relation via node. +*/
+#define NODE_TURNRSTRCT2 ((uint16_t)0x0800)
+
 
 /*+ A flag to mark a segment as one-way from node1 to node2. +*/
 #define ONEWAY_1TO2    ((distance_t)0x80000000)
 
-/*+ A flag to mark a segment as one-way node2 to node1. +*/
+/*+ A flag to mark a segment as one-way from node2 to node1. +*/
 #define ONEWAY_2TO1    ((distance_t)0x40000000)
 
 /*+ A flag to mark a segment as a super-segment. +*/
@@ -92,17 +110,23 @@
 /* Simple Types */
 
 
-/*+ A node, segment or way index. +*/
+/*+ A node, segment, way or relation index. +*/
 typedef uint32_t index_t;
 
+/*+ A printf formatting string for an index_t type (this should match the index_t definition above). +*/
+#define Pindex_t PRIu32         /* PRIu32 and PRIu64 are defined in intypes.h */
 
-/*+ A node latitude or longitude. +*/
+
+/*+ A node latitude or longitude (range: +/-pi*LAT_LONG_SCALE = +/-3.14*1024*65536 = ~29 bits). +*/
 typedef int32_t  latlong_t;
 
-/*+ A node latitude or longitude bin number. +*/
+/*+ A node latitude or longitude bin number (range: +/-pi*LAT_LONG_SCALE/LAT_LONG_BIN = +/-3.14*1024 = ~13 bits). +*/
 typedef int16_t  ll_bin_t;
 
-/*+ A node latitude or longitude offset. +*/
+/*+ A node latitude and longitude bin number (range: +/-(pi*LAT_LONG_SCALE/LAT_LONG_BIN)^2 = +/-(3.14*1024)^2 = ~26 bits). +*/
+typedef int32_t  ll_bin2_t;
+
+/*+ A node latitude or longitude offset (range: 0 -> LAT_LONG_BIN-1 = 0 -> 65535 = 16 bits). +*/
 typedef uint16_t ll_off_t;
 
 
@@ -112,6 +136,7 @@ typedef uint16_t ll_off_t;
 /*+ Conversion from a bin number to a latlong (integer latitude or longitude). +*/
 #define bin_to_latlong(xxx) ((latlong_t)(xxx)*LAT_LONG_BIN)
 
+
 /*+ Conversion from a latlong (integer latitude or longitude) to a bin offset. +*/
 #define latlong_to_off(xxx) (ll_off_t)((latlong_t)(xxx)&(LAT_LONG_BIN-1))
 
@@ -120,7 +145,7 @@ typedef uint16_t ll_off_t;
 
 
 /*+ Conversion from a latitude or longitude in radians to a latlong (integer latitude or longitude). +*/
-#define radians_to_latlong(xxx) ((latlong_t)floor((xxx)*LAT_LONG_SCALE))
+#define radians_to_latlong(xxx) ((latlong_t)floor((xxx)*LAT_LONG_SCALE+0.5))
 
 /*+ Conversion from a latlong (integer latitude or longitude) to a latitude or longitude in radians. +*/
 #define latlong_to_radians(xxx) ((double)(xxx)/LAT_LONG_SCALE)
@@ -146,7 +171,7 @@ typedef float score_t;
 /*+ Conversion from distance_t to kilometres. +*/
 #define distance_to_km(xx) ((double)(xx)/1000.0)
 
-/*+ Conversion from metres to distance_t. +*/
+/*+ Conversion from kilometres to distance_t. +*/
 #define km_to_distance(xx) ((distance_t)((double)(xx)*1000.0))
 
 /*+ Conversion from duration_t to minutes. +*/
@@ -162,10 +187,10 @@ typedef float score_t;
 #define distance_speed_to_duration(xx,yy) ((duration_t)(((double)(xx)/(double)(yy))*(36000.0/1000.0)))
 
 
-/*+ The type of a way. +*/
-typedef uint8_t waytype_t;
+/*+ The type of a highway. +*/
+typedef uint8_t highway_t;
 
-/*+ The different types of a way. +*/
+/*+ The different types of a highway. +*/
 typedef enum _Highway
  {
   Way_Motorway    = 1,
@@ -184,15 +209,43 @@ typedef enum _Highway
 
   Way_Count       =14,       /* One more than the number of highway types. */
 
-  Way_OneWay      =32,
-  Way_Roundabout  =64
+  Way_OneWay      =32
  }
  Highway;
 
 #define HIGHWAY(xx) ((xx)&0x1f)
 
+/*+ A bitmask of multiple highway types. +*/
+typedef uint16_t highways_t;
+
+#define HIGHWAYS(xx)  (1<<(HIGHWAY(xx)-1))
+
+/*+ The different types of a highway as a bitmask. +*/
+typedef enum _Highways
+ {
+  Highways_None         = 0,
+
+  Highways_Motorway     = HIGHWAYS(Way_Motorway    ),
+  Highways_Trunk        = HIGHWAYS(Way_Trunk       ),
+  Highways_Primary      = HIGHWAYS(Way_Primary     ),
+  Highways_Secondary    = HIGHWAYS(Way_Secondary   ),
+  Highways_Tertiary     = HIGHWAYS(Way_Tertiary    ),
+  Highways_Unclassified = HIGHWAYS(Way_Unclassified),
+  Highways_Residential  = HIGHWAYS(Way_Residential ),
+  Highways_Service      = HIGHWAYS(Way_Service     ),
+  Highways_Track        = HIGHWAYS(Way_Track       ),
+  Highways_Cycleway     = HIGHWAYS(Way_Cycleway    ),
+  Highways_Path         = HIGHWAYS(Way_Path        ),
+  Highways_Steps        = HIGHWAYS(Way_Steps       ),
+  Highways_Ferry        = HIGHWAYS(Way_Ferry       )
+ }
+ Highways;
+
+
+/*+ The type of a transport. +*/
+typedef uint8_t transport_t;
 
-/*+ The different methods of transport. +*/
+/*+ The different types of transport. +*/
 typedef enum _Transport
  {
   Transport_None       =  0,
@@ -213,33 +266,36 @@ typedef enum _Transport
  Transport;
 
 
-/*+ The allowed traffic on a way. +*/
-typedef uint16_t allow_t;
+/*+ A bitmask of multiple transport types. +*/
+typedef uint16_t transports_t;
 
-#define ALLOWED(xx)  (1<<((xx)-1))
+#define TRANSPORTS(xx)  (1<<((xx)-1))
 
-/*+ The different allowed traffic on a way. +*/
-typedef enum _Allowed
+/*+ The different types of transport as a bitmask. +*/
+typedef enum _Transports
  {
-  Allow_None       = 0,
-
-  Allow_Foot       = ALLOWED(Transport_Foot      ),
-  Allow_Horse      = ALLOWED(Transport_Horse     ),
-  Allow_Wheelchair = ALLOWED(Transport_Wheelchair),
-  Allow_Bicycle    = ALLOWED(Transport_Bicycle   ),
-  Allow_Moped      = ALLOWED(Transport_Moped     ),
-  Allow_Motorbike  = ALLOWED(Transport_Motorbike ),
-  Allow_Motorcar   = ALLOWED(Transport_Motorcar  ),
-  Allow_Goods      = ALLOWED(Transport_Goods     ),
-  Allow_HGV        = ALLOWED(Transport_HGV       ),
-  Allow_PSV        = ALLOWED(Transport_PSV       ),
-
-  Allow_ALL        = 65535
+  Transports_None       = 0,
+
+  Transports_Foot       = TRANSPORTS(Transport_Foot      ),
+  Transports_Horse      = TRANSPORTS(Transport_Horse     ),
+  Transports_Wheelchair = TRANSPORTS(Transport_Wheelchair),
+  Transports_Bicycle    = TRANSPORTS(Transport_Bicycle   ),
+  Transports_Moped      = TRANSPORTS(Transport_Moped     ),
+  Transports_Motorbike  = TRANSPORTS(Transport_Motorbike ),
+  Transports_Motorcar   = TRANSPORTS(Transport_Motorcar  ),
+  Transports_Goods      = TRANSPORTS(Transport_Goods     ),
+  Transports_HGV        = TRANSPORTS(Transport_HGV       ),
+  Transports_PSV        = TRANSPORTS(Transport_PSV       ),
+
+  Transports_ALL        = 65535
  }
- Allowed;
+ Transports;
+
 
+/*+ The type of a property. +*/
+typedef uint8_t property_t;
 
-/*+ The individual properties of a highway. +*/
+/*+ The different types of property. +*/
 typedef enum _Property
  {
   Property_None         = 0,
@@ -256,12 +312,12 @@ typedef enum _Property
  Property;
 
 
-/*+ The combined set of properties of a way. +*/
-typedef uint8_t wayprop_t;
+/*+ A bitmask of multiple properties. +*/
+typedef uint8_t properties_t;
 
 #define PROPERTIES(xx)  (1<<((xx)-1))
 
-/*+ The different properties of a way. +*/
+/*+ The different properties as a bitmask. +*/
 typedef enum _Properties
  {
   Properties_None         = 0,
@@ -281,16 +337,16 @@ typedef enum _Properties
 /*+ The speed limit of a way, measured in km/hour. +*/
 typedef uint8_t speed_t;
 
-/*+ The maximum weight of a way, measured in 0.2 tonnes. +*/
+/*+ The maximum weight of a way, measured in multiples of 0.2 tonnes. +*/
 typedef uint8_t weight_t;
 
-/*+ The maximum height of a way, measured in 0.1 metres. +*/
+/*+ The maximum height of a way, measured in multiples of 0.1 metres. +*/
 typedef uint8_t height_t;
 
-/*+ The maximum width of a way, measured in 0.1 metres. +*/
+/*+ The maximum width of a way, measured in multiples of 0.1 metres. +*/
 typedef uint8_t width_t;
 
-/*+ The maximum length of a way, measured in 0.1 metres. +*/
+/*+ The maximum length of a way, measured in multiples of 0.1 metres. +*/
 typedef uint8_t length_t;
 
 
@@ -339,8 +395,12 @@ typedef struct _Way Way;
 
 typedef struct _Ways Ways;
 
+typedef struct _TurnRelation TurnRelation;
+
+typedef struct _Relations Relations;
+
 
-/* Functions */
+/* Functions in types.c */
 
 Highway HighwayType(const char *highway);
 Transport TransportType(const char *transport);
@@ -350,8 +410,9 @@ const char *HighwayName(Highway highway);
 const char *TransportName(Transport transport);
 const char *PropertyName(Property property);
 
-const char *AllowedNameList(allow_t allowed);
-const char *PropertiesNameList(wayprop_t properties);
+const char *HighwaysNameList(highways_t highways);
+const char *AllowedNameList(transports_t allowed);
+const char *PropertiesNameList(properties_t properties);
 
 const char *HighwayList(void);
 const char *TransportList(void);
diff --git a/src/typesx.h b/src/typesx.h
index 4720c2d..417ba59 100644
--- a/src/typesx.h
+++ b/src/typesx.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/typesx.h,v 1.4 2010/09/17 17:42:21 amb Exp $
-
  Type definitions for eXtended types.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,9 +24,32 @@
 #define TYPESX_H    /*+ To stop multiple inclusions. +*/
 
 
+#include <inttypes.h>
 #include <stdint.h>
 
 
+/* Constants and macros for handling them */
+
+/*+ An undefined node ID. +*/
+#define NO_NODE_ID     (~(node_t)0)
+
+/*+ An undefined way ID. +*/
+#define NO_WAY_ID      (~(way_t)0)
+
+/*+ An undefined relation ID. +*/
+#define NO_RELATION_ID (~(relation_t)0)
+
+/*+ The maximum number of segments per node (used to size temporary storage). +*/
+#define MAX_SEG_PER_NODE 32
+
+
+/* Macro functions */
+
+#define ClearBit(xx,yy)    (xx)[(yy)/8]&=~(1<<((yy)%8))
+#define SetBit(xx,yy)      (xx)[(yy)/8]|= (1<<((yy)%8))
+#define IsBitSet(xx,yy)   ((xx)[(yy)/8]&  (1<<((yy)%8)))
+
+
 /* Simple Types */
 
 /*+ A node identifier - must be at least as large as index_t. +*/
@@ -41,6 +62,33 @@ typedef uint32_t way_t;
 typedef uint32_t relation_t;
 
 
+/*+ A printf formatting string for a node_t type (this should match the node_t definition above). +*/
+#define Pnode_t PRIu32          /* PRIu32 and PRIu64 are defined in intypes.h */
+
+/*+ A printf formatting string for a way_t type (this should match the way_t definition above). +*/
+#define Pway_t PRIu32           /* PRIu32 and PRIu64 are defined in intypes.h */
+
+/*+ A printf formatting string for a relation_t type (this should match the relation_t definition above). +*/
+#define Prelation_t PRIu32      /* PRIu32 and PRIu64 are defined in intypes.h */
+
+
+/* Enumerated types */
+
+/*+ Turn restrictions. +*/
+typedef enum _TurnRestriction
+ {
+  TurnRestrict_None              =0,
+  TurnRestrict_no_right_turn,
+  TurnRestrict_no_left_turn,
+  TurnRestrict_no_u_turn,
+  TurnRestrict_no_straight_on,
+  TurnRestrict_only_right_turn,
+  TurnRestrict_only_left_turn,
+  TurnRestrict_only_straight_on
+ }
+ TurnRestriction;
+
+
 /* Data structures */
 
 typedef struct _NodeX NodeX;
@@ -57,6 +105,8 @@ typedef struct _WaysX WaysX;
 
 typedef struct _RouteRelX RouteRelX;
 
+typedef struct _TurnRestrictRelX TurnRestrictRelX;
+
 typedef struct _RelationsX RelationsX;
 
 
diff --git a/src/visualiser.c b/src/visualiser.c
index aadcdb7..356e582 100644
--- a/src/visualiser.c
+++ b/src/visualiser.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/visualiser.c,v 1.10 2010/07/26 18:17:20 amb Exp $
-
  Extract data from Routino.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,7 +29,10 @@
 #include "nodes.h"
 #include "segments.h"
 #include "ways.h"
+#include "relations.h"
+
 
+/* Limit types */
 
 #define SPEED_LIMIT  1
 #define WEIGHT_LIMIT 2
@@ -45,9 +46,10 @@ typedef void (*callback_t)(index_t node,double latitude,double longitude);
 
 /* Local variables */
 
-static Nodes    *OSMNodes;
-static Segments *OSMSegments;
-static Ways     *OSMWays;
+static Nodes     *OSMNodes;
+static Segments  *OSMSegments;
+static Ways      *OSMWays;
+static Relations *OSMRelations;
 
 static double LatMin;
 static double LatMax;
@@ -62,6 +64,7 @@ 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);
+static void output_turnrestriction(index_t node,double latitude,double longitude);
 static void output_limits(index_t node,double latitude,double longitude);
 
 
@@ -74,6 +77,8 @@ static void output_limits(index_t node,double latitude,double longitude);
 
   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.
@@ -83,13 +88,14 @@ static void output_limits(index_t node,double latitude,double longitude);
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputJunctions(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax)
+void OutputJunctions(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
 {
  /* 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;
@@ -103,7 +109,7 @@ void OutputJunctions(Nodes *nodes,Segments *segments,Ways *ways,double latmin,do
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Process a single node (called as a callback).
+  Process a single node as a junction (called as a callback).
 
   index_t node The node to output.
 
@@ -118,7 +124,7 @@ static void output_junctions(index_t node,double latitude,double longitude)
  Way *firstway;
  int count=0,difference=0;
 
- segment=FirstSegment(OSMSegments,OSMNodes,node);
+ segment=FirstSegment(OSMSegments,OSMNodes,node,1);
  firstway=LookupWay(OSMWays,segment->way,1);
 
  do
@@ -149,6 +155,8 @@ static void output_junctions(index_t node,double latitude,double longitude)
 
   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.
@@ -158,13 +166,14 @@ static void output_junctions(index_t node,double latitude,double longitude)
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputSuper(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax)
+void OutputSuper(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
 {
  /* 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;
@@ -178,7 +187,7 @@ void OutputSuper(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Process a single node (called as a callback).
+  Process a single node as a super-node (called as a callback).
 
   index_t node The node to output.
 
@@ -191,12 +200,12 @@ static void output_super(index_t node,double latitude,double longitude)
 {
  Segment *segment;
 
- if(!IsSuperNode(OSMNodes,node))
+ if(!IsSuperNode(LookupNode(OSMNodes,node,1)))
     return;
 
  printf("%.6f %.6f n\n",radians_to_degrees(latitude),radians_to_degrees(longitude));
 
- segment=FirstSegment(OSMSegments,OSMNodes,node);
+ segment=FirstSegment(OSMSegments,OSMNodes,node,1);
 
  do
    {
@@ -226,6 +235,8 @@ static void output_super(index_t node,double latitude,double longitude)
 
   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.
@@ -235,13 +246,14 @@ static void output_super(index_t node,double latitude,double longitude)
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputOneway(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax)
+void OutputOneway(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
 {
  /* 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;
@@ -255,7 +267,7 @@ void OutputOneway(Nodes *nodes,Segments *segments,Ways *ways,double latmin,doubl
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Process a single node (called as a callback).
+  Process a single node and all connected one-way segments (called as a callback).
 
   index_t node The node to output.
 
@@ -268,7 +280,7 @@ static void output_oneway(index_t node,double latitude,double longitude)
 {
  Segment *segment;
 
- segment=FirstSegment(OSMSegments,OSMNodes,node);
+ segment=FirstSegment(OSMSegments,OSMNodes,node,1);
 
  do
    {
@@ -296,6 +308,93 @@ static void output_oneway(index_t node,double latitude,double longitude)
 
 
 /*++++++++++++++++++++++++++++++++++++++
+  Output the data for turn restrictions.
+
+  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.
+  ++++++++++++++++++++++++++++++++++++++*/
+
+void OutputTurnRestrictions(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
+{
+ /* 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 */
+
+ find_all_nodes(nodes,(callback_t)output_turnrestriction);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
+  Process a single node as the 'via' node for a turn restriction (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_turnrestriction(index_t node,double latitude,double longitude)
+{
+ index_t turnrelation=NO_RELATION;
+
+ if(!IsTurnRestrictedNode(LookupNode(OSMNodes,node,1)))
+    return;
+
+ turnrelation=FindFirstTurnRelation1(OSMRelations,node);
+
+ do
+   {
+    TurnRelation *relation;
+    Segment *from_segment,*to_segment;
+    index_t from_node,to_node;
+    double from_lat,from_lon,to_lat,to_lon;
+
+    relation=LookupTurnRelation(OSMRelations,turnrelation,1);
+
+    from_segment=LookupSegment(OSMSegments,relation->from,1);
+    to_segment  =LookupSegment(OSMSegments,relation->to  ,2);
+
+    from_node=OtherNode(from_segment,node);
+    to_node=OtherNode(to_segment,node);
+
+    GetLatLong(OSMNodes,from_node,&from_lat,&from_lon);
+    GetLatLong(OSMNodes,to_node,&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));
+
+    turnrelation=FindNextTurnRelation1(OSMRelations,turnrelation);
+   }
+ while(turnrelation!=NO_RELATION);
+}
+
+
+/*++++++++++++++++++++++++++++++++++++++
   Output the data for speed limits.
 
   Nodes *nodes The set of nodes to use.
@@ -304,6 +403,8 @@ static void output_oneway(index_t node,double latitude,double longitude)
 
   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.
@@ -313,13 +414,14 @@ static void output_oneway(index_t node,double latitude,double longitude)
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax)
+void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
 {
  /* 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;
@@ -343,6 +445,8 @@ void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,
 
   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.
@@ -352,13 +456,14 @@ void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax)
+void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
 {
  /* 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;
@@ -382,6 +487,8 @@ void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin
 
   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.
@@ -391,13 +498,14 @@ void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax)
+void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
 {
  /* 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;
@@ -421,6 +529,8 @@ void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin
 
   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.
@@ -430,13 +540,14 @@ void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax)
+void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax)
 {
  /* 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;
@@ -460,6 +571,8 @@ void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,
 
   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.
@@ -469,13 +582,14 @@ void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,
   double lonmax The maximum longitude.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void OutputLengthLimits(Nodes *nodes,Segments *segments,Ways *ways,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)
 {
  /* 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;
@@ -491,7 +605,7 @@ void OutputLengthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Process a single node (called as a callback).
+  Process a single node as a speed, weight, height, length, width limit (called as a callback).
 
   index_t node The node to output.
 
@@ -502,17 +616,18 @@ void OutputLengthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin
 
 static void output_limits(index_t node,double latitude,double longitude)
 {
- Segment *segment,*segments[16];
- Way *ways[16];
- int limits[16];
+#define MAX_STORED 32
+ Segment *segment,*segments[MAX_STORED];
+ Way *ways[MAX_STORED];
+ int limits[MAX_STORED];
  int count=0;
  int i,j,same=0;
 
- segment=FirstSegment(OSMSegments,OSMNodes,node);
+ segment=FirstSegment(OSMSegments,OSMNodes,node,1);
 
  do
    {
-    if(IsNormalSegment(segment) && count<16)
+    if(IsNormalSegment(segment) && count<MAX_STORED)
       {
        ways    [count]=LookupWay(OSMWays,segment->way,1);
        segments[count]=segment;
@@ -592,18 +707,18 @@ static void output_limits(index_t node,double latitude,double longitude)
 /*++++++++++++++++++++++++++++++++++++++
   A function to iterate through all nodes and call a callback function for each one.
 
-  Nodes *nodes The list of nodes to process.
+  Nodes *nodes The set of nodes to use.
 
   callback_t callback The callback function for each node.
   ++++++++++++++++++++++++++++++++++++++*/
 
 static void find_all_nodes(Nodes *nodes,callback_t callback)
 {
- int32_t latminbin=latlong_to_bin(radians_to_latlong(LatMin))-nodes->file.latzero;
- int32_t latmaxbin=latlong_to_bin(radians_to_latlong(LatMax))-nodes->file.latzero;
- int32_t lonminbin=latlong_to_bin(radians_to_latlong(LonMin))-nodes->file.lonzero;
- int32_t lonmaxbin=latlong_to_bin(radians_to_latlong(LonMax))-nodes->file.lonzero;
- int latb,lonb,llbin;
+ ll_bin_t latminbin=latlong_to_bin(radians_to_latlong(LatMin))-nodes->file.latzero;
+ ll_bin_t latmaxbin=latlong_to_bin(radians_to_latlong(LatMax))-nodes->file.latzero;
+ ll_bin_t lonminbin=latlong_to_bin(radians_to_latlong(LonMin))-nodes->file.lonzero;
+ ll_bin_t lonmaxbin=latlong_to_bin(radians_to_latlong(LonMax))-nodes->file.lonzero;
+ ll_bin_t latb,lonb;
  index_t i,index1,index2;
 
  /* Loop through all of the nodes. */
@@ -611,7 +726,7 @@ static void find_all_nodes(Nodes *nodes,callback_t callback)
  for(latb=latminbin;latb<=latmaxbin;latb++)
     for(lonb=lonminbin;lonb<=lonmaxbin;lonb++)
       {
-       llbin=lonb*nodes->file.latbins+latb;
+       ll_bin2_t llbin=lonb*nodes->file.latbins+latb;
 
        if(llbin<0 || llbin>(nodes->file.latbins*nodes->file.lonbins))
           continue;
diff --git a/src/visualiser.h b/src/visualiser.h
index 4ea38c7..2811114 100644
--- a/src/visualiser.h
+++ b/src/visualiser.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/visualiser.h,v 1.2 2009/07/09 17:31:56 amb Exp $
-
  Header file for visualiser functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008,2009 Andrew M. Bishop
+ This file Copyright 2008-2011 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,21 +26,22 @@
 #include "types.h"
 
 
-/* In visualiser.c */
+/* Functions in visualiser.c */
 
-void OutputJunctions(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
+void OutputJunctions(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
 
-void OutputSuper(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
+void OutputSuper(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
 
-void OutputOneway(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
+void OutputOneway(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
+void OutputTurnRestrictions(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
 
-void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
+void OutputSpeedLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
 
-void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
+void OutputWeightLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
 
-void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
-void OutputWidthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
-void OutputLengthLimits(Nodes *nodes,Segments *segments,Ways *ways,double latmin,double latmax,double lonmin,double lonmax);
+void OutputHeightLimits(Nodes *nodes,Segments *segments,Ways *ways,Relations *relations,double latmin,double latmax,double lonmin,double lonmax);
+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);
 
 
 #endif /* VISUALISER_H */
diff --git a/src/ways.c b/src/ways.c
index f3c0d3c..dabb377 100644
--- a/src/ways.c
+++ b/src/ways.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/ways.c,v 1.46 2010/07/24 10:09:07 amb Exp $
-
  Way data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,7 +30,7 @@
 /*++++++++++++++++++++++++++++++++++++++
   Load in a way list from a file.
 
-  Ways* LoadWayList Returns the way list.
+  Ways *LoadWayList Returns the way list.
 
   const char *filename The name of the file to load.
   ++++++++++++++++++++++++++++++++++++++*/
@@ -40,6 +38,9 @@
 Ways *LoadWayList(const char *filename)
 {
  Ways *ways;
+#if SLIM
+ int i;
+#endif
 
  ways=(Ways*)malloc(sizeof(Ways));
 
@@ -64,9 +65,11 @@ Ways *LoadWayList(const char *filename)
 
  ReadFile(ways->fd,&ways->file,sizeof(WaysFile));
 
+ for(i=0;i<sizeof(ways->cached)/sizeof(ways->cached[0]);i++)
+    ways->incache[i]=NO_WAY;
+
  ways->namesoffset=sizeof(WaysFile)+ways->file.number*sizeof(Way);
 
- ways->nincache=~0;
  ways->ncached=NULL;
 
 #endif
diff --git a/src/ways.h b/src/ways.h
index 13ae3ce..d92431e 100644
--- a/src/ways.h
+++ b/src/ways.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/ways.h,v 1.42 2010/08/30 12:32:07 amb Exp $
-
  A header file for the ways.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,31 +37,32 @@
 /*+ A structure containing a single way (members ordered to minimise overall size). +*/
 struct _Way
 {
- index_t    name;               /*+ The offset of the name of the way in the names array. +*/
+ index_t      name;             /*+ The offset of the name of the way in the names array. +*/
 
- allow_t    allow;              /*+ The type of traffic allowed on the way. +*/
+ transports_t allow;            /*+ The type of traffic allowed on the way. +*/
 
- waytype_t  type;               /*+ The highway type of the way. +*/
+ highway_t    type;             /*+ The highway type of the way. +*/
 
- wayprop_t  props;              /*+ The properties of the way. +*/
+ properties_t props;            /*+ The properties of the way. +*/
 
- speed_t    speed;              /*+ The defined maximum speed limit of the way. +*/
+ speed_t      speed;            /*+ The defined maximum speed limit of the way. +*/
 
- weight_t   weight;             /*+ The defined maximum weight of traffic on the way. +*/
- height_t   height;             /*+ The defined maximum height of traffic on the way. +*/
- width_t    width;              /*+ The defined maximum width of traffic on the way. +*/
- length_t   length;             /*+ The defined maximum length of traffic on the way. +*/
+ weight_t     weight;           /*+ The defined maximum weight of traffic on the way. +*/
+ height_t     height;           /*+ The defined maximum height of traffic on the way. +*/
+ width_t      width;            /*+ The defined maximum width of traffic on the way. +*/
+ length_t     length;           /*+ The defined maximum length of traffic on the way. +*/
 };
 
 
 /*+ A structure containing the header from the file. +*/
 typedef struct _WaysFile
 {
- index_t    number;             /*+ How many ways are stored? +*/
- index_t    onumber;            /*+ How many ways were there originally? +*/
+ index_t      number;           /*+ The number of ways stored. +*/
+ index_t      onumber;          /*+ The number of ways originally. +*/
 
- allow_t    allow;              /*+ The types of traffic that were seen when parsing. +*/
- wayprop_t  props;              /*+ The properties that were seen when parsing. +*/
+ highways_t   highways;         /*+ The types of highways that were seen when parsing. +*/
+ transports_t allow;            /*+ The types of traffic that were seen when parsing. +*/
+ properties_t props;            /*+ The properties that were seen when parsing. +*/
 }
  WaysFile;
 
@@ -85,17 +84,17 @@ struct _Ways
  int        fd;                 /*+ The file descriptor for the file. +*/
  off_t      namesoffset;        /*+ The offset of the names within the file. +*/
 
- Way        wcached[2];         /*+ The cached ways. +*/
+ Way        cached[2];          /*+ Two cached nodes read from the file in slim mode. +*/
+ index_t    incache[2];         /*+ The indexes of the cached ways. +*/
 
  char      *ncached;            /*+ The cached way name. +*/
- index_t    nincache;           /*+ The index of the cached way name. +*/
  int        nalloc;             /*+ The amount of memory allocated for the way name. +*/
 
 #endif
 };
 
 
-/* Functions */
+/* Functions in ways.c */
 
 Ways *LoadWayList(const char *filename);
 
@@ -124,7 +123,7 @@ static char *WayName(Ways *ways,Way *way);
 
   Way *LookupWay Returns a pointer to the cached way information.
 
-  Ways *ways The ways structure to use.
+  Ways *ways The set of ways to use.
 
   index_t index The index of the way.
 
@@ -133,11 +132,16 @@ static char *WayName(Ways *ways,Way *way);
 
 static inline Way *LookupWay(Ways *ways,index_t index,int position)
 {
- SeekFile(ways->fd,sizeof(WaysFile)+(off_t)index*sizeof(Way));
+ if(ways->incache[position-1]!=index)
+   {
+    SeekFile(ways->fd,sizeof(WaysFile)+(off_t)index*sizeof(Way));
 
- ReadFile(ways->fd,&ways->wcached[position-1],sizeof(Way));
+    ReadFile(ways->fd,&ways->cached[position-1],sizeof(Way));
 
- return(&ways->wcached[position-1]);
+    ways->incache[position-1]=index;
+   }
+
+ return(&ways->cached[position-1]);
 }
 
 
@@ -146,7 +150,7 @@ static inline Way *LookupWay(Ways *ways,index_t index,int position)
 
   char *WayName Returns a pointer to the name of the way.
 
-  Ways *ways The ways structure to use.
+  Ways *ways The set of ways to use.
 
   Way *way The Way pointer.
   ++++++++++++++++++++++++++++++++++++++*/
@@ -155,9 +159,6 @@ static inline char *WayName(Ways *ways,Way *way)
 {
  int n=0;
 
- if(way->name==ways->nincache)
-    return(ways->ncached);
-
  SeekFile(ways->fd,ways->namesoffset+way->name);
 
  if(!ways->ncached)
diff --git a/src/waysx.c b/src/waysx.c
index 619fad6..30ab48d 100644
--- a/src/waysx.c
+++ b/src/waysx.c
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/waysx.c,v 1.52 2010/11/13 14:22:28 amb Exp $
-
  Extended Way data type functions.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,17 +32,21 @@
 
 #include "files.h"
 #include "logging.h"
-#include "functions.h"
+#include "sorting.h"
 
 
-/* Variables */
+/* Global variables */
 
 /*+ The command line '--tmpdir' option or its default value. +*/
 extern char *option_tmpdirname;
 
+
+/* Local variables */
+
 /*+ A temporary file-local variable for use by the sort functions. +*/
 static WaysX *sortwaysx;
 
+
 /* Functions */
 
 static int sort_by_id(WayX *a,WayX *b);
@@ -75,7 +77,7 @@ WaysX *NewWayList(int append)
  if(append)
     sprintf(waysx->filename,"%s/waysx.input.tmp",option_tmpdirname);
  else
-    sprintf(waysx->filename,"%s/waysx.%p.tmp",option_tmpdirname,waysx);
+    sprintf(waysx->filename,"%s/waysx.%p.tmp",option_tmpdirname,(void*)waysx);
 
  if(append)
    {
@@ -92,7 +94,7 @@ WaysX *NewWayList(int append)
        SeekFile(waysx->fd,position);
        ReadFile(waysx->fd,&waysize,FILESORT_VARSIZE);
 
-       waysx->xnumber++;
+       waysx->number++;
        position+=waysize+FILESORT_VARSIZE;
       }
 
@@ -102,7 +104,7 @@ WaysX *NewWayList(int append)
     waysx->fd=OpenFileNew(waysx->filename);
 
  waysx->nfilename=(char*)malloc(strlen(option_tmpdirname)+32);
- sprintf(waysx->nfilename,"%s/waynames.%p.tmp",option_tmpdirname,waysx);
+ sprintf(waysx->nfilename,"%s/waynames.%p.tmp",option_tmpdirname,(void*)waysx);
 
  return(waysx);
 }
@@ -111,9 +113,9 @@ WaysX *NewWayList(int append)
 /*++++++++++++++++++++++++++++++++++++++
   Free a way list.
 
-  WaysX *waysx The list to be freed.
+  WaysX *waysx The set of ways to be freed.
 
-  int keep Set to 1 if the file is to be kept.
+  int keep Set to 1 if the file is to be kept (for appending later).
   ++++++++++++++++++++++++++++++++++++++*/
 
 void FreeWayList(WaysX *waysx,int keep)
@@ -137,7 +139,7 @@ void FreeWayList(WaysX *waysx,int keep)
 /*++++++++++++++++++++++++++++++++++++++
   Append a single way to an unsorted way list.
 
-  WaysX* waysx The set of ways to process.
+  WaysX *waysx The set of ways to process.
 
   way_t id The ID of the way.
 
@@ -146,7 +148,7 @@ void FreeWayList(WaysX *waysx,int keep)
   const char *name The name or reference of the way.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void AppendWay(WaysX* waysx,way_t id,Way *way,const char *name)
+void AppendWay(WaysX *waysx,way_t id,Way *way,const char *name)
 {
  WayX wayx;
  FILESORT_VARINT size;
@@ -161,22 +163,22 @@ void AppendWay(WaysX* waysx,way_t id,Way *way,const char *name)
  WriteFile(waysx->fd,&wayx,sizeof(WayX));
  WriteFile(waysx->fd,name,strlen(name)+1);
 
- waysx->xnumber++;
+ waysx->number++;
 
- assert(!(waysx->xnumber==0)); /* Zero marks the high-water mark for ways. */
+ assert(!(waysx->number==0)); /* Zero marks the high-water mark for ways. */
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Sort the list of ways.
 
-  WaysX* waysx The set of ways to process.
+  WaysX *waysx The set of ways to process.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void SortWayList(WaysX* waysx)
+void SortWayList(WaysX *waysx)
 {
- index_t i;
- int fd,nfd;
+ index_t i,xnumber;
+ int fd;
  char *names[2]={NULL,NULL};
  int namelen[2]={0,0};
  int nnames=0;
@@ -186,9 +188,12 @@ void SortWayList(WaysX* waysx)
 
  printf_first("Sorting Ways by Name");
 
- /* Close the file and re-open it (finished appending) */
+ /* Close the file (finished appending) */
+
+ waysx->fd=CloseFile(waysx->fd);
+
+ /* Re-open the file read-only and a new file writeable */
 
- CloseFile(waysx->fd);
  waysx->fd=ReOpenFile(waysx->filename);
 
  DeleteFile(waysx->filename);
@@ -201,30 +206,31 @@ void SortWayList(WaysX* waysx)
 
  /* Close the files */
 
- CloseFile(waysx->fd);
+ waysx->fd=CloseFile(waysx->fd);
  CloseFile(fd);
 
  /* Print the final message */
 
- printf_last("Sorted Ways by Name: Ways=%d",waysx->xnumber);
+ printf_last("Sorted Ways by Name: Ways=%"Pindex_t,waysx->number);
 
 
  /* Print the start message */
 
  printf_first("Separating Way Names: Ways=0 Names=0");
 
- /* Open the files */
+ /* Re-open the file read-only and new files writeable */
 
  waysx->fd=ReOpenFile(waysx->filename);
 
  DeleteFile(waysx->filename);
 
  fd=OpenFileNew(waysx->filename);
- nfd=OpenFileNew(waysx->nfilename);
+
+ waysx->nfd=OpenFileNew(waysx->nfilename);
 
  /* Copy from the single file into two files */
 
- for(i=0;i<waysx->xnumber;i++)
+ for(i=0;i<waysx->number;i++)
    {
     WayX wayx;
     FILESORT_VARINT size;
@@ -239,7 +245,7 @@ void SortWayList(WaysX* waysx)
 
     if(nnames==0 || strcmp(names[0],names[1]))
       {
-       WriteFile(nfd,names[nnames%2],size-sizeof(WayX));
+       WriteFile(waysx->nfd,names[nnames%2],size-sizeof(WayX));
 
        lastlength=waysx->nlength;
        waysx->nlength+=size-sizeof(WayX);
@@ -251,8 +257,8 @@ void SortWayList(WaysX* waysx)
 
     WriteFile(fd,&wayx,sizeof(WayX));
 
-    if(!((i+1)%10000))
-       printf_middle("Separating Way Names: Ways=%d Names=%d",i+1,nnames);
+    if(!((i+1)%1000))
+       printf_middle("Separating Way Names: Ways=%"Pindex_t" Names=%"Pindex_t,i+1,nnames);
    }
 
  if(names[0]) free(names[0]);
@@ -260,23 +266,21 @@ void SortWayList(WaysX* waysx)
 
  /* Close the files */
 
- CloseFile(waysx->fd);
+ waysx->fd=CloseFile(waysx->fd);
  CloseFile(fd);
 
- waysx->fd=ReOpenFile(waysx->filename);
-
- CloseFile(nfd);
+ waysx->nfd=CloseFile(waysx->nfd);
 
  /* Print the final message */
 
- printf_last("Separated Way Names: Ways=%d Names=%d ",waysx->xnumber,nnames);
+ printf_last("Separated Way Names: Ways=%"Pindex_t" Names=%"Pindex_t" ",waysx->number,nnames);
 
 
  /* Print the start message */
 
  printf_first("Sorting Ways");
 
- /* Open the files */
+ /* Re-open the file read-only and a new file writeable */
 
  waysx->fd=ReOpenFile(waysx->filename);
 
@@ -286,38 +290,37 @@ void SortWayList(WaysX* waysx)
 
  /* Allocate the array of indexes */
 
- waysx->idata=(way_t*)malloc(waysx->xnumber*sizeof(way_t));
+ waysx->idata=(way_t*)malloc(waysx->number*sizeof(way_t));
 
  assert(waysx->idata); /* Check malloc() worked */
 
  /* Sort the ways by index and index them */
 
+ xnumber=waysx->number;
  waysx->number=0;
 
  sortwaysx=waysx;
 
  filesort_fixed(waysx->fd,fd,sizeof(WayX),(int (*)(const void*,const void*))sort_by_id,(int (*)(void*,index_t))deduplicate_and_index_by_id);
 
- /* Close the files and re-open them */
+ /* Close the files */
 
- CloseFile(waysx->fd);
+ waysx->fd=CloseFile(waysx->fd);
  CloseFile(fd);
 
- waysx->fd=ReOpenFile(waysx->filename);
-
  /* Print the final message */
 
- printf_last("Sorted Ways: Ways=%d Duplicates=%d",waysx->number,waysx->xnumber-waysx->number);
+ printf_last("Sorted Ways: Ways=%"Pindex_t" Duplicates=%"Pindex_t,xnumber,xnumber-waysx->number);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
   Compact the list of ways.
 
-  WaysX* waysx The set of ways to process.
+  WaysX *waysx The set of ways to process.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void CompactWayList(WaysX* waysx)
+void CompactWayList(WaysX *waysx)
 {
  index_t i;
  int fd;
@@ -327,9 +330,8 @@ void CompactWayList(WaysX* waysx)
 
  printf_first("Sorting Ways by Properties");
 
- /* Close the file and re-open it */
+ /* Re-open the file read-only and a new file writeable */
 
- CloseFile(waysx->fd);
  waysx->fd=ReOpenFile(waysx->filename);
 
  DeleteFile(waysx->filename);
@@ -342,19 +344,19 @@ void CompactWayList(WaysX* waysx)
 
  /* Close the files */
 
- CloseFile(waysx->fd);
+ waysx->fd=CloseFile(waysx->fd);
  CloseFile(fd);
 
  /* Print the final message */
 
- printf_last("Sorted Ways by Properties: Ways=%d",waysx->number);
+ printf_last("Sorted Ways by Properties: Ways=%"Pindex_t,waysx->number);
 
 
  /* Print the start message */
 
  printf_first("Compacting Ways: Ways=0 Properties=0");
 
- /* Open the files */
+ /* Re-open the file read-only and a new file writeable */
 
  waysx->fd=ReOpenFile(waysx->filename);
 
@@ -383,25 +385,25 @@ void CompactWayList(WaysX* waysx)
 
     WriteFile(fd,&wayx,sizeof(WayX));
 
-    if(!((i+1)%10000))
-       printf_middle("Compacting Ways: Ways=%d Properties=%d",i+1,waysx->cnumber);
+    if(!((i+1)%1000))
+       printf_middle("Compacting Ways: Ways=%"Pindex_t" Properties=%"Pindex_t,i+1,waysx->cnumber);
    }
 
  /* Close the files */
 
- CloseFile(waysx->fd);
+ waysx->fd=CloseFile(waysx->fd);
  CloseFile(fd);
 
  /* Print the final message */
 
- printf_last("Compacted Ways: Ways=%d Properties=%d ",waysx->number,waysx->cnumber);
+ printf_last("Compacted Ways: Ways=%"Pindex_t" Properties=%"Pindex_t" ",waysx->number,waysx->cnumber);
 
 
  /* Print the start message */
 
  printf_first("Sorting Ways");
 
- /* Open the files */
+ /* Re-open the file read-only and a new file writeable */
 
  waysx->fd=ReOpenFile(waysx->filename);
 
@@ -413,16 +415,14 @@ void CompactWayList(WaysX* waysx)
 
  filesort_fixed(waysx->fd,fd,sizeof(WayX),(int (*)(const void*,const void*))sort_by_id,NULL);
 
- /* Close the files and re-open them */
+ /* Close the files */
 
- CloseFile(waysx->fd);
+ waysx->fd=CloseFile(waysx->fd);
  CloseFile(fd);
 
- waysx->fd=ReOpenFile(waysx->filename);
-
  /* Print the final message */
 
- printf_last("Sorted Ways: Ways=%d",waysx->number);
+ printf_last("Sorted Ways: Ways=%"Pindex_t,waysx->number);
 }
 
 
@@ -451,7 +451,7 @@ static int sort_by_id(WayX *a,WayX *b)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Sort the ways into name and id order.
+  Sort the ways into name order and then id order.
 
   int sort_by_name_and_id Returns the comparison of the name and id fields.
 
@@ -506,9 +506,9 @@ static int sort_by_name_and_prop_and_id(WayX *a,WayX *b)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Deduplicate the extended ways using the id after sorting and create the index.
+  Create the index of identifiers and discard duplicate ways.
 
-  int deduplicate_and_index_by_id Return 1 if the value is to be kept, otherwise zero.
+  int deduplicate_and_index_by_id Return 1 if the value is to be kept, otherwise 0.
 
   WayX *wayx The extended way.
 
@@ -529,8 +529,12 @@ static int deduplicate_and_index_by_id(WayX *wayx,index_t index)
 
     return(1);
    }
+ else
+   {
+    logerror("Way %"Pway_t" is duplicated.\n",wayx->id);
 
- return(0);
+    return(0);
+   }
 }
 
 
@@ -539,16 +543,16 @@ static int deduplicate_and_index_by_id(WayX *wayx,index_t index)
 
   index_t IndexWayX Returns the index of the extended way with the specified id.
 
-  WaysX* waysx The set of ways to process.
+  WaysX *waysx The set of ways to process.
 
   way_t id The way id to look for.
   ++++++++++++++++++++++++++++++++++++++*/
 
-index_t IndexWayX(WaysX* waysx,way_t id)
+index_t IndexWayX(WaysX *waysx,way_t id)
 {
- int start=0;
- int end=waysx->number-1;
- int mid;
+ index_t start=0;
+ index_t end=waysx->number-1;
+ index_t mid;
 
  /* Binary search - search key exact match only is required.
   *
@@ -576,7 +580,7 @@ index_t IndexWayX(WaysX* waysx,way_t id)
        if(waysx->idata[mid]<id)      /* Mid point is too low */
           start=mid+1;
        else if(waysx->idata[mid]>id) /* Mid point is too high */
-          end=mid-1;
+          end=mid?(mid-1):mid;
        else                          /* Mid point is correct */
           return(mid);
       }
@@ -596,28 +600,31 @@ index_t IndexWayX(WaysX* waysx,way_t id)
 /*++++++++++++++++++++++++++++++++++++++
   Save the way list to a file.
 
-  WaysX* waysx The set of ways to save.
+  WaysX *waysx The set of ways to save.
 
   const char *filename The name of the file to save.
   ++++++++++++++++++++++++++++++++++++++*/
 
-void SaveWayList(WaysX* waysx,const char *filename)
+void SaveWayList(WaysX *waysx,const char *filename)
 {
  index_t i;
- int fd,nfd;
+ int fd;
  int position=0;
  WaysFile waysfile={0};
- allow_t allow=0;
- wayprop_t  props=0;
+ highways_t   highways=0;
+ transports_t allow=0;
+ properties_t props=0;
 
  /* Print the start message */
 
  printf_first("Writing Ways: Ways=0");
 
- /* Map into memory */
+ /* Map into memory /  open the file */
 
 #if !SLIM
- waysx->xdata=MapFile(waysx->filename);
+ waysx->data=MapFile(waysx->filename);
+#else
+ waysx->fd=ReOpenFile(waysx->filename);
 #endif
 
  /* Write out the ways data */
@@ -630,27 +637,30 @@ void SaveWayList(WaysX* waysx,const char *filename)
    {
     WayX *wayx=LookupWayX(waysx,i,1);
 
-    allow|=wayx->way.allow;
-    props|=wayx->way.props;
+    highways|=HIGHWAYS(wayx->way.type);
+    allow   |=wayx->way.allow;
+    props   |=wayx->way.props;
 
     SeekFile(fd,sizeof(WaysFile)+(off_t)wayx->prop*sizeof(Way));
     WriteFile(fd,&wayx->way,sizeof(Way));
 
-    if(!((i+1)%10000))
-       printf_middle("Writing Ways: Ways=%d",i+1);
+    if(!((i+1)%1000))
+       printf_middle("Writing Ways: Ways=%"Pindex_t,i+1);
    }
 
- /* Unmap from memory */
+ /* Unmap from memory / close the file */
 
 #if !SLIM
- waysx->xdata=UnmapFile(waysx->filename);
+ waysx->data=UnmapFile(waysx->filename);
+#else
+ waysx->fd=CloseFile(waysx->fd);
 #endif
 
  /* Write out the ways names */
 
  SeekFile(fd,sizeof(WaysFile)+(off_t)waysx->cnumber*sizeof(Way));
 
- nfd=ReOpenFile(waysx->nfilename);
+ waysx->nfd=ReOpenFile(waysx->nfilename);
 
  while(position<waysx->nlength)
    {
@@ -660,21 +670,24 @@ void SaveWayList(WaysX* waysx,const char *filename)
     if((waysx->nlength-position)<1024)
        len=waysx->nlength-position;
 
-    ReadFile(nfd,temp,len);
+    ReadFile(waysx->nfd,temp,len);
     WriteFile(fd,temp,len);
 
     position+=len;
    }
 
- CloseFile(nfd);
+ /* Close the file */
+
+ waysx->nfd=CloseFile(waysx->nfd);
 
  /* Write out the header structure */
 
- waysfile.number=waysx->cnumber;
+ waysfile.number =waysx->cnumber;
  waysfile.onumber=waysx->number;
 
- waysfile.allow=allow;
- waysfile.props=props;
+ waysfile.highways=highways;
+ waysfile.allow   =allow;
+ waysfile.props   =props;
 
  SeekFile(fd,0);
  WriteFile(fd,&waysfile,sizeof(WaysFile));
@@ -683,5 +696,5 @@ void SaveWayList(WaysX* waysx,const char *filename)
 
  /* Print the final message */
 
- printf_last("Wrote Ways: Ways=%d",waysx->number);
+ printf_last("Wrote Ways: Ways=%"Pindex_t,waysx->number);
 }
diff --git a/src/waysx.h b/src/waysx.h
index a336561..d43c1ed 100644
--- a/src/waysx.h
+++ b/src/waysx.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/waysx.h,v 1.28 2010/09/25 18:47:32 amb Exp $
-
  A header file for the extended Ways structure.
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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,7 +39,7 @@
 /*+ An extended structure containing a single way. +*/
 struct _WayX
 {
- way_t    id;                   /*+ The way identifier. +*/
+ way_t    id;                   /*+ The way identifier; the OSM value. +*/
 
  index_t  prop;                 /*+ The index of the properties of the way in the compacted list. +*/
 
@@ -55,31 +53,30 @@ struct _WaysX
  char    *filename;             /*+ The name of the temporary file (for the WaysX). +*/
  int      fd;                   /*+ The file descriptor of the temporary file (for the WaysX). +*/
 
- index_t  xnumber;              /*+ The number of unsorted extended ways. +*/
+ index_t  number;               /*+ The number of extended ways still being considered. +*/
 
 #if !SLIM
 
- WayX    *xdata;                /*+ The extended data for the Ways (sorted). +*/
+ WayX    *data;                 /*+ The extended ways data (when mapped into memory). +*/
 
 #else
 
- WayX     xcached[2];           /*+ Two cached ways read from the file in slim mode. +*/
+ WayX     cached[2];            /*+ Two cached ways read from the file in slim mode. +*/
 
 #endif
 
- index_t  number;               /*+ How many entries are still useful? +*/
-
- index_t  cnumber;              /*+ How many entries are there after compacting? +*/
+ index_t  cnumber;              /*+ The number of entries after compacting. +*/
 
- index_t *idata;                /*+ The index of the extended data for the Ways (sorted by ID). +*/
+ way_t   *idata;                /*+ The extended way IDs (sorted by ID). +*/
 
  char    *nfilename;            /*+ The name of the temporary file (for the names). +*/
+ int      nfd;                  /*+ The file descriptor of the temporary file (for the names). +*/
 
- uint32_t nlength;              /*+ How long is the string of name entries? +*/
+ uint32_t nlength;              /*+ The length of the string of name entries. +*/
 };
 
 
-/* Functions */
+/* Functions in waysx.c */
 
 
 WaysX *NewWayList(int append);
@@ -87,9 +84,9 @@ void FreeWayList(WaysX *waysx,int keep);
 
 void SaveWayList(WaysX *waysx,const char *filename);
 
-index_t IndexWayX(WaysX* waysx,way_t id);
+index_t IndexWayX(WaysX *waysx,way_t id);
 
-void AppendWay(WaysX* waysx,way_t id,Way *way,const char *name);
+void AppendWay(WaysX *waysx,way_t id,Way *way,const char *name);
 
 void SortWayList(WaysX *waysx);
 
@@ -100,50 +97,54 @@ void CompactWayList(WaysX *waysx);
 
 #if !SLIM
 
-#define LookupWayX(waysx,index,position)  &(waysx)->xdata[index]
+#define LookupWayX(waysx,index,position)  &(waysx)->data[index]
   
+#define PutBackWayX(waysx,index,position) /* nop */
+
 #else
 
-static WayX *LookupWayX(WaysX* waysx,index_t index,int position);
+static WayX *LookupWayX(WaysX *waysx,index_t index,int position);
+
+static void PutBackWayX(WaysX *waysx,index_t index,int position);
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Lookup a particular extended way.
+  Lookup a particular extended way with the specified id from the file on disk.
 
-  WayX *LookupWayX Returns a pointer to the extended way with the specified id.
+  WayX *LookupWayX Returns a pointer to a cached copy of the extended way.
 
-  WaysX* waysx The set of ways to process.
+  WaysX *waysx The set of ways to use.
 
   index_t index The way index to look for.
 
   int position The position in the cache to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline WayX *LookupWayX(WaysX* waysx,index_t index,int position)
+static inline WayX *LookupWayX(WaysX *waysx,index_t index,int position)
 {
  SeekFile(waysx->fd,(off_t)index*sizeof(WayX));
 
- ReadFile(waysx->fd,&waysx->xcached[position-1],sizeof(WayX));
+ ReadFile(waysx->fd,&waysx->cached[position-1],sizeof(WayX));
 
- return(&waysx->xcached[position-1]);
+ return(&waysx->cached[position-1]);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Put back an extended way.
+  Put back an extended way's data into the file on disk.
 
-  WaysX* waysx The set of ways to process.
+  WaysX *waysx The set of ways to use.
 
   index_t index The way index to put back.
 
   int position The position in the cache to use.
   ++++++++++++++++++++++++++++++++++++++*/
 
-static inline void PutBackWayX(WaysX* waysx,index_t index,int position)
+static inline void PutBackWayX(WaysX *waysx,index_t index,int position)
 {
  SeekFile(waysx->fd,(off_t)index*sizeof(WayX));
 
- WriteFile(waysx->fd,&waysx->xcached[position-1],sizeof(WayX));
+ WriteFile(waysx->fd,&waysx->cached[position-1],sizeof(WayX));
 }
 
 #endif /* SLIM */
diff --git a/src/xml/Makefile b/src/xml/Makefile
index 81bfd04..470fffe 100644
--- a/src/xml/Makefile
+++ b/src/xml/Makefile
@@ -1,10 +1,8 @@
-# $Header: /home/amb/routino/src/xml/RCS/Makefile,v 1.10 2010/07/07 17:26:24 amb Exp $
-#
 # XML test programs Makefile
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2010 Andrew M. Bishop
+# This file Copyright 2010-2011 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 +46,7 @@ EXE=xsd-to-xmlparser
 
 ########
 
-all : $(EXE) $(C) $(E)
+all: $(EXE) $(C) $(E)
 	@true
 
 ########
@@ -78,7 +76,7 @@ xsd-to-xmlparser : xsd-to-xmlparser.o ../xmlparse.o
 ########
 
 %.o : %.c
-	$(CC) -c $(CFLAGS) $(FLAGS64) -I.. $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $<)))
+	$(CC) -c $(CFLAGS) $(FLAGS64) -I.. $< -o $@ -MMD -MP -MF $(addprefix .deps/,$(addsuffix .d,$(basename $@)))
 
 ########
 
@@ -106,9 +104,14 @@ test-skeleton.c : test/test.xsd xsd-to-xmlparser
 
 ########
 
+install:
+
+########
+
 clean:
 	rm -f *.o
 	rm -f *~
+	rm -f *.gcda *.gcno *.gcov gmon.out
 
 ########
 
@@ -121,7 +124,7 @@ distclean: clean
 
 ########
 
-.deps : .FORCE
+.deps: .FORCE
 	@[ -d .deps ] || mkdir $@
 
 $(D) : .deps
@@ -131,4 +134,4 @@ include $(D)
 
 ########
 
-.FORCE :
+.FORCE:
diff --git a/src/xml/test/bad-attr-character-ref.xml b/src/xml/test/bad-attr-character-ref.xml
deleted file mode 100644
index 6ab951c..0000000
--- a/src/xml/test/bad-attr-character-ref.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-
-<!-- good comment -->
-
-<test>
-
-  <level1 attr1="value1 & value2 < ŀ">
-    <level2>
-    </level2>
-  </level1>
-
-</test>
diff --git a/src/xml/xsd-to-xmlparser.c b/src/xml/xsd-to-xmlparser.c
index 6e15d9d..df2b6a4 100644
--- a/src/xml/xsd-to-xmlparser.c
+++ b/src/xml/xsd-to-xmlparser.c
@@ -1,5 +1,5 @@
 /***************************************
- $Header: /home/amb/routino/src/xml/RCS/xsd-to-xmlparser.c,v 1.10 2010/04/23 18:41:20 amb Exp $
+ $Header: /home/amb/CVS/routino/src/xml/xsd-to-xmlparser.c,v 1.10 2010-04-23 18:41:20 amb Exp $
 
  An XML parser for simplified XML Schema Definitions to create XML parser skeletons.
 
diff --git a/src/xmlparse.h b/src/xmlparse.h
index 93ff0c9..8c43cad 100644
--- a/src/xmlparse.h
+++ b/src/xmlparse.h
@@ -1,11 +1,9 @@
 /***************************************
- $Header: /home/amb/routino/src/RCS/xmlparse.h,v 1.12 2010/05/14 17:55:56 amb Exp $
-
  A simple XML parser
 
  Part of the Routino routing software.
  ******************/ /******************
- This file Copyright 2010 Andrew M. Bishop
+ This file Copyright 2010-2011 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
@@ -72,21 +70,21 @@ struct _xmltag
 
 int ParseXML(FILE *file,xmltag **tags,int options);
 
-unsigned long ParseXML_LineNumber(void);
+unsigned long long ParseXML_LineNumber(void);
 
 char *ParseXML_Decode_Entity_Ref(const char *string);
 char *ParseXML_Decode_Char_Ref(const char *string);
 char *ParseXML_Encode_Safe_XML(const char *string);
 
-int ParseXML_GetInteger(const char *string,int *number);
-int ParseXML_GetFloating(const char *string,double *number);
+int ParseXML_IsInteger(const char *string);
+int ParseXML_IsFloating(const char *string);
 
 /* Macros to simplify the callback functions */
 
 #define XMLPARSE_MESSAGE(tag,message) \
  do \
    { \
-    fprintf(stderr,"XML Parser: Error on line %ld: " message " in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+    fprintf(stderr,"XML Parser: Error on line %llu: " message " in <%s> tag.\n",ParseXML_LineNumber(),tag); \
     return(1); \
    } \
     while(0)
@@ -94,7 +92,7 @@ int ParseXML_GetFloating(const char *string,double *number);
 #define XMLPARSE_INVALID(tag,attribute) \
  do \
    { \
-    fprintf(stderr,"XML Parser: Error on line %ld: Invalid value for '" #attribute "' attribute in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+    fprintf(stderr,"XML Parser: Error on line %llu: Invalid value for '" #attribute "' attribute in <%s> tag.\n",ParseXML_LineNumber(),tag); \
     return(1); \
    } \
     while(0)
@@ -104,29 +102,29 @@ int ParseXML_GetFloating(const char *string,double *number);
    { \
     if(!attribute) \
       { \
-       fprintf(stderr,"XML Parser: Error on line %ld: '" #attribute "' attribute must be specified in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+       fprintf(stderr,"XML Parser: Error on line %llu: '" #attribute "' attribute must be specified in <%s> tag.\n",ParseXML_LineNumber(),tag); \
        return(1); \
       } \
    } \
     while(0)
 
-#define XMLPARSE_ASSERT_INTEGER(tag,attribute,result)  \
+#define XMLPARSE_ASSERT_INTEGER(tag,attribute)  \
  do \
    { \
-    if(!attribute || !*attribute || !ParseXML_GetInteger(attribute,&result)) \
+    if(!attribute || !*attribute || !ParseXML_IsInteger(attribute)) \
       { \
-       fprintf(stderr,"XML Parser: Error on line %ld: '" #attribute "' attribute must be a integer in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+       fprintf(stderr,"XML Parser: Error on line %llu: '" #attribute "' attribute must be a integer in <%s> tag.\n",ParseXML_LineNumber(),tag); \
        return(1); \
       } \
    } \
     while(0)
 
-#define XMLPARSE_ASSERT_FLOATING(tag,attribute,result)  \
+#define XMLPARSE_ASSERT_FLOATING(tag,attribute)  \
  do \
    { \
-    if(!attribute || !*attribute || !ParseXML_GetFloating(attribute,&result)) \
+    if(!attribute || !*attribute || !ParseXML_IsFloating(attribute)) \
       { \
-       fprintf(stderr,"XML Parser: Error on line %ld: '" #attribute "' attribute must be a number in <%s> tag.\n",ParseXML_LineNumber(),tag); \
+       fprintf(stderr,"XML Parser: Error on line %llu: '" #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
index 20ca05d..33b71f2 100644
--- a/src/xmlparse.l
+++ b/src/xmlparse.l
@@ -1,14 +1,12 @@
 %{
 /***************************************
- $Header: /home/amb/routino/src/RCS/xmlparse.l,v 1.20 2010/10/09 11:05:28 amb Exp $
-
  A simple generic XML parser where the structure comes from the function parameters.
  Not intended to be fully conforming to XML staandard 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 Andrew M. Bishop
+ This file Copyright 2010-2011 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 +27,7 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <string.h>
+#include <strings.h>
 
 #include "xmlparse.h"
 
@@ -72,12 +71,6 @@
 
 /* Lexer definitions */
 
-#define YY_SKIP_YYWRAP 1 /* Remove error with prototype of ..._yywrap */
-#ifndef yywrap
-/*+ Needed in lex but does nothing. +*/
-#define yywrap() 1
-#endif
-
 /*+ Reset the current string. +*/
 #define reset_string \
  if(!string) string=(char*)malloc(16); \
@@ -92,8 +85,6 @@
  strcpy(string+stringused,xx); \
  stringused+=newlen;
 
-#define YY_NO_INPUT
-
 
 /* Lexer functions and variables */
 
@@ -103,22 +94,28 @@ static char *yylval=NULL;
 
 static int xmlparse_options;
 
+static unsigned long long lineno;
+
 %}
 
 %option 8bit
 %option pointer
 %option batch
-%option yylineno
+%option never-interactive
 
 %option nodefault
 %option perf-report
 %option fast
+%option noread
+
 %option nounput
+%option noinput
+%option noyywrap
 
 
  /* Grammar based on http://www.w3.org/TR/2004/REC-xml-20040204/ but for ASCII tags not Unicode. */
 
-S               [ \t\r\n]
+S               [ \t]
 
 U1              [\x09\x0A\x0D\x20-\x7F]
 U2              [\xC2-\xDF][\x80-\xBF]
@@ -175,69 +172,81 @@ charref         &#({digit}+|x{xdigit}+);
 "<?"                        { BEGIN(XML_DECL_START); }
 "<"                         { BEGIN(TAG_START); }
 ">"                         { return(LEX_ERROR_CLOSE); }
-[^<>]+                      { }
+{N}                         { lineno++; }
+[^<>]                       { }
 
  /* Comments */
 
 <COMMENT>"--->"             { return(LEX_ERROR_COMMENT); }
 <COMMENT>"-->"              { BEGIN(INITIAL); }
-<COMMENT>"--"[^->]+         { }
-<COMMENT>[^-]+              { }
+<COMMENT>"--"[^->]          { }
+<COMMENT>{N}                { lineno++; }
+<COMMENT>[^-]               { }
 <COMMENT>"-"                { }
 
  /* CDATA */
 
 <CDATA>"]]>"                { BEGIN(INITIAL); }
 <CDATA>"]"                  { }
-<CDATA>[^]]+                { }
+<CDATA>{N}                  { lineno++; }
+<CDATA>[^]]                 { }
 
  /* CDATA */
 
 <DOCTYPE>"<"                { doctype_depth++; }
 <DOCTYPE>">"                { if(doctype_depth==0) BEGIN(INITIAL); else doctype_depth--; }
-<DOCTYPE>[^<>]+             { }
+<DOCTYPE>{N}                { lineno++; }
+<DOCTYPE>[^<>]              { }
 
  /* XML Declaration start */
 
 <XML_DECL_START>xml         { BEGIN(XML_DECL); yylval=yytext; return(LEX_XML_DECL_BEGIN); }
-<XML_DECL_START>.|{N}       { return(LEX_ERROR_XML_DECL_START); }
+<XML_DECL_START>{N}         { /* lineno++; */ return(LEX_ERROR_XML_DECL_START); }
+<XML_DECL_START>.           { return(LEX_ERROR_XML_DECL_START); }
 
  /* Tag 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>.|{N}             { return(LEX_ERROR_XML_DECL); }
+<XML_DECL>.                 { return(LEX_ERROR_XML_DECL); }
 
  /* Any tag start */
 
 <TAG_START>{name}           { BEGIN(TAG); yylval=yytext; return(LEX_TAG_BEGIN); }
-<TAG_START>.|{N}            { return(LEX_ERROR_TAG_START); }
+<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}             { return(LEX_ERROR_END_TAG); }
+<END_TAG1>{N}               { /* lineno++; */ return(LEX_ERROR_END_TAG); }
+<END_TAG1>.                 { return(LEX_ERROR_END_TAG); }
 
 <END_TAG2>">"               { BEGIN(INITIAL); }
-<END_TAG2>.|{N}             { return(LEX_ERROR_END_TAG); }
+<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>.|{N}                  { return(LEX_ERROR_TAG); }
+<TAG>.                      { return(LEX_ERROR_TAG); }
 
  /* Attributes */
 
 <ATTR_KEY>=                 { BEGIN(ATTR_VAL); }
-<ATTR_KEY>.|{N}             { return(LEX_ERROR_ATTR); }
+<ATTR_KEY>{N}               { /* lineno++; */ return(LEX_ERROR_ATTR); }
+<ATTR_KEY>.                 { return(LEX_ERROR_ATTR); }
 
 <ATTR_VAL>\"                { BEGIN(DQUOTED); reset_string; }
 <ATTR_VAL>\'                { BEGIN(SQUOTED); reset_string; }
-<ATTR_VAL>.|{N}             { return(LEX_ERROR_ATTR); }
+<ATTR_VAL>{N}               { /* lineno++; */ return(LEX_ERROR_ATTR); }
+<ATTR_VAL>.                 { return(LEX_ERROR_ATTR); }
 
  /* Quoted strings */
 
@@ -305,7 +314,7 @@ static inline int call_callback(const char *name,int (*callback)(),int type,int
    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 %d: too many attributes for tag '%s' source code needs changing.\n",yylineno,name);
+    fprintf(stderr,"XML Parser: Error on line %llu: too many attributes for tag '%s' source code needs changing.\n",lineno,name);
     exit(1);
    }
 }
@@ -343,7 +352,7 @@ int ParseXML(FILE *file,xmltag **tags,int options)
 
  yyrestart(yyin);
 
- yylineno=1;
+ lineno=1;
 
  BEGIN(INITIAL);
 
@@ -359,7 +368,7 @@ int ParseXML(FILE *file,xmltag **tags,int options)
 
        if(tag_stack)
          {
-          fprintf(stderr,"XML Parser: Error on line %d: XML declaration not before all other tags.\n",yylineno);
+          fprintf(stderr,"XML Parser: Error on line %llu: XML declaration not before all other tags.\n",lineno);
           yychar=LEX_ERROR_XML_NOT_FIRST;
           break;
          }
@@ -387,7 +396,7 @@ int ParseXML(FILE *file,xmltag **tags,int options)
 
        if(tag==NULL)
          {
-          fprintf(stderr,"XML Parser: Error on line %d: unexpected tag '%s'.\n",yylineno,yylval);
+          fprintf(stderr,"XML Parser: Error on line %llu: unexpected tag '%s'.\n",lineno,yylval);
           yychar=LEX_ERROR_UNEXP_TAG;
          }
 
@@ -444,13 +453,13 @@ int ParseXML(FILE *file,xmltag **tags,int options)
 
        if(strcmp(tag->name,yylval))
          {
-          fprintf(stderr,"XML Parser: Error on line %d: end tag '</%s>' doesn't match start tag '<%s ...>'.\n",yylineno,yylval,tag->name);
+          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 %d: end tag '</%s>' seen but there was no start tag '<%s ...>'.\n",yylineno,yylval,yylval);
+          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;
          }
 
@@ -491,11 +500,11 @@ int ParseXML(FILE *file,xmltag **tags,int options)
           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 %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
+             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 %d: unexpected attribute '%s' for tag '%s'.\n",yylineno,yylval,tag->name);
+             fprintf(stderr,"XML Parser: Warning on line %llu: unexpected attribute '%s' for tag '%s'.\n",lineno,yylval,tag->name);
          }
 
        break;
@@ -515,54 +524,54 @@ int ParseXML(FILE *file,xmltag **tags,int options)
 
        if(tag)
          {
-          fprintf(stderr,"XML Parser: Error on line %d: end of file seen without end tag '</%s>'.\n",yylineno,tag->name);
+          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 %d: character '<' seen not at start of tag.\n",yylineno);
+       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 %d: characters '<?' seen not at start of XML declaration.\n",yylineno);
+       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 %d: invalid character seen inside tag '<%s...>'.\n",yylineno,tag->name);
+       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 %d: invalid character seen inside XML declaration '<?%s...>'.\n",yylineno,tag->name);
+       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 %d: invalid attribute definition seen in tag.\n",yylineno);
+       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 %d: invalid character seen in end-tag.\n",yylineno);
+       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 %d: invalid comment seen.\n",yylineno);
+       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 %d: character '>' seen not at end of tag.\n",yylineno);
+       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 %d: invalid character '%s' seen in attribute value.\n",yylineno,yylval);
+       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 %d: invalid entity reference '%s' seen in attribute value.\n",yylineno,yylval);
+       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 %d: invalid character reference '%s' seen in attribute value.\n",yylineno,yylval);
+       fprintf(stderr,"XML Parser: Error on line %llu: invalid character reference '%s' seen in attribute value.\n",lineno,yylval);
        break;
       }
    }
@@ -587,12 +596,12 @@ int ParseXML(FILE *file,xmltag **tags,int options)
 /*++++++++++++++++++++++++++++++++++++++
   Return the current parser line number.
 
-  unsigned long ParseXML_LineNumber Returns the line number.
+  unsigned long long ParseXML_LineNumber Returns the line number.
   ++++++++++++++++++++++++++++++++++++++*/
 
-unsigned long ParseXML_LineNumber(void)
+unsigned long long ParseXML_LineNumber(void)
 {
- return(yylineno);
+ return(lineno);
 }
 
 
@@ -625,16 +634,48 @@ char *ParseXML_Decode_Entity_Ref(const char *string)
 
 char *ParseXML_Decode_Char_Ref(const char *string)
 {
- static char result[2]=" ";
- long int val;
-
- if(string[2]=='x') val=strtol(string+3,NULL,16);
- else               val=strtol(string+2,NULL,10);
+ static char result[5]="";
+ long int unicode;
 
- if(val<0 || val>255)
-    return(NULL);
+ if(string[2]=='x') unicode=strtol(string+3,NULL,16);
+ else               unicode=strtol(string+2,NULL,10);
 
- result[0]=val&0xff;
+ 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);
 }
@@ -717,22 +758,27 @@ char *ParseXML_Encode_Safe_XML(const char *string)
 
           /* Decode the UTF-8 */
 
-          if((string[i]&0xE0)==0xC0 && (string[i]&0x1F)>=2 && (string[i+1]&0xC0)==0x80)
+          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 */
+             /* 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 */
+             /* 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 */
+             /* 0001 0000-001F FFFF  =>  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
              unicode =(string[i++]&0x07)<<18;
              unicode|=(string[i++]&0x3F)<<12;
              unicode|=(string[i++]&0x3F)<<6;
@@ -778,16 +824,14 @@ char *ParseXML_Encode_Safe_XML(const char *string)
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Convert a string to a integer (checking that it really is a integer).
+  Check that a string really is an integer.
 
-  int ParseXML_GetInteger Returns 1 if a integer could be found or 0 otherwise.
+  int ParseXML_IsInteger Returns 1 if an integer could be found or 0 otherwise.
 
   const char *string The string to be parsed.
-
-  int *number Returns the number.
   ++++++++++++++++++++++++++++++++++++++*/
 
-int ParseXML_GetInteger(const char *string,int *number)
+int ParseXML_IsInteger(const char *string)
 {
  const char *p=string;
 
@@ -799,24 +843,20 @@ int ParseXML_GetInteger(const char *string,int *number)
 
  if(*p)
     return(0);
-
- *number=atoi(string);
-
- return(1);
+ else
+    return(1);
 }
 
 
 /*++++++++++++++++++++++++++++++++++++++
-  Convert a string to a floating point number (checking that it really is a number).
+  Check that a string really is a floating point number.
 
-  int ParseXML_GetFloating Returns 1 if a number could be found or 0 otherwise.
+  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 *number Returns the number.
   ++++++++++++++++++++++++++++++++++++++*/
 
-int ParseXML_GetFloating(const char *string,double *number)
+int ParseXML_IsFloating(const char *string)
 {
  const char *p=string;
 
@@ -839,8 +879,6 @@ int ParseXML_GetFloating(const char *string,double *number)
 
  if(*p)
     return(0);
-
- *number=atof(string);
-
- return(1);
+ else
+    return(1);
 }
diff --git a/web/bin/summarise-log.pl b/web/bin/summarise-log.pl
new file mode 100755
index 0000000..3f5dbe0
--- /dev/null
+++ b/web/bin/summarise-log.pl
@@ -0,0 +1,67 @@
+#!/usr/bin/perl
+
+die "Usage: $0 [-v] < <error-log-file>\n" if($#ARGV>0);
+
+$verbose=0;
+$verbose=1 if($#ARGV==0 && $ARGV[0] eq "-v");
+
+# Read in each line from the error log and store them
+
+%errors=();
+%errorids=();
+
+while(<STDIN>)
+  {
+   s%\r*\n%%;
+
+   undef $errorid;
+
+   if(m%Node ([0-9]+)%)           # Generic node
+     {
+      $errorid=$1;
+      s%Node [0-9]+%Node <node-id>%g;
+     }
+
+   if(m%Way ([0-9]+)%)            # Generic way
+     {
+      $errorid=$1;
+      s%Way [0-9]+%Way <way-id>%g;
+     }
+
+   if(m%Relation ([0-9]+)%)       # Generic relation
+     {
+      $errorid=$1;
+      s%Relation [0-9]+%Relation <relation-id>%g;
+     }
+
+   if(m%Nodes [0-9]+ and [0-9]+%i) # Special case nodes
+     {
+      s%Nodes [0-9]+ and [0-9]+%Nodes <node-id1> and <node-id2>%gi;
+     }
+
+   $errors{$_}++;
+
+   if($verbose && defined $errorid)
+     {
+      if(defined $errorids{$_})
+        {
+         $errorids{$_}.=",$errorid";
+        }
+      else
+        {
+         $errorids{$_}="$errorid";
+        }
+     }
+  }
+
+# Print out the results
+
+foreach $error (sort { $errors{$b} <=> $errors{$a} } (keys %errors))
+  {
+   printf "%9d : $error\n",$errors{$error};
+
+   if($verbose && defined $errorids{$error})
+     {
+      print "            $errorids{$error}\n";
+     }
+  }
diff --git a/web/www/openlayers/install.sh b/web/www/openlayers/install.sh
index 60a6eee..c42e2aa 100755
--- a/web/www/openlayers/install.sh
+++ b/web/www/openlayers/install.sh
@@ -1,6 +1,6 @@
 #!/bin/sh -x
 
-version=2.9.1
+version=2.10
 
 # Download the file.
 
diff --git a/web/www/routino/customrouter.cgi b/web/www/routino/customrouter.cgi
index 454818e..051e35c 100755
--- a/web/www/routino/customrouter.cgi
+++ b/web/www/routino/customrouter.cgi
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2010 Andrew M. Bishop
+# This file Copyright 2008-2011 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
@@ -46,6 +46,7 @@ $query=new CGI;
               "speed-[a-z]+"    => "[0-9.]+",
               "property-[a-z]+" => "[0-9.]+",
               "oneway"          => "(1|0|true|false|on|off)",
+              "turns"           => "(1|0|true|false|on|off)",
               "weight"          => "[0-9.]+",
               "height"          => "[0-9.]+",
               "width"           => "[0-9.]+",
diff --git a/web/www/routino/noscript.cgi b/web/www/routino/noscript.cgi
index 3421515..d838bad 100755
--- a/web/www/routino/noscript.cgi
+++ b/web/www/routino/noscript.cgi
@@ -4,7 +4,7 @@
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2008-2010 Andrew M. Bishop
+# This file Copyright 2008-2011 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,6 +42,7 @@ $query=new CGI;
               "speed-[a-z]+"    => "[0-9.]+",
               "property-[a-z]+" => "[0-9.]+",
               "oneway"          => "(1|0|true|false|on|off)",
+              "turns"           => "(1|0|true|false|on|off)",
               "weight"          => "[0-9.]+",
               "height"          => "[0-9.]+",
               "width"           => "[0-9.]+",
diff --git a/web/www/routino/noscript.template.html b/web/www/routino/noscript.template.html
index ded75d7..f5230bf 100644
--- a/web/www/routino/noscript.template.html
+++ b/web/www/routino/noscript.template.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -301,6 +301,10 @@
       <td class="right" ><input name="oneway" type="checkbox"><!-- oneway -->
       <td class="left"  >
     <tr>
+      <td class="left"  >Obey turn restrictions:
+      <td class="right" ><input name="turns" type="checkbox"><!-- turns -->
+      <td class="left"  >
+    <tr>
       <td class="left"  >Weight:
       <td class="right" ><input name="weight" type="text" size=3 class="right"><!-- weight -->
       <td class="left"  >tonnes
diff --git a/web/www/routino/profiles.js b/web/www/routino/profiles.js
new file mode 100644
index 0000000..69ec242
--- /dev/null
+++ b/web/www/routino/profiles.js
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////// Routino default profile ////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+var routino={ // contains all default Routino options (generated using "--help-profile-json").
+
+  // Default transport type
+  transport: 'motorcar',
+
+  // Transport types
+  transports: { foot: 1, horse: 2, wheelchair: 3, bicycle: 4, moped: 5, motorbike: 6, motorcar: 7, goods: 8, hgv: 9, psv: 10 },
+
+  // Highway types
+  highways: { motorway: 1, trunk: 2, primary: 3, secondary: 4, tertiary: 5, unclassified: 6, residential: 7, service: 8, track: 9, cycleway: 10, path: 11, steps: 12, ferry: 13 },
+
+  // Property types
+  properties: { paved: 1, multilane: 2, bridge: 3, tunnel: 4, footroute: 5, bicycleroute: 6 },
+
+  // Restriction types
+  restrictions: { oneway: 1, turns: 2, weight: 3, height: 4, width: 5, length: 6 },
+
+  // Allowed highways
+  profile_highway: {
+        motorway: { foot:   0, horse:   0, wheelchair:   0, bicycle:   0, moped:   0, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100 },
+           trunk: { foot:  40, horse:  25, wheelchair:  40, bicycle:  30, moped:  90, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100 },
+         primary: { foot:  50, horse:  50, wheelchair:  50, bicycle:  70, moped: 100, motorbike:  90, motorcar:  90, goods:  90, hgv:  90, psv:  90 },
+       secondary: { foot:  60, horse:  50, wheelchair:  60, bicycle:  80, moped:  90, motorbike:  80, motorcar:  80, goods:  80, hgv:  80, psv:  80 },
+        tertiary: { foot:  70, horse:  75, wheelchair:  70, bicycle:  90, moped:  80, motorbike:  70, motorcar:  70, goods:  70, hgv:  70, psv:  70 },
+    unclassified: { foot:  80, horse:  75, wheelchair:  80, bicycle:  90, moped:  70, motorbike:  60, motorcar:  60, goods:  60, hgv:  60, psv:  60 },
+     residential: { foot:  90, horse:  75, wheelchair:  90, bicycle:  90, moped:  60, motorbike:  50, motorcar:  50, goods:  50, hgv:  50, psv:  50 },
+         service: { foot:  90, horse:  75, wheelchair:  90, bicycle:  90, moped:  80, motorbike:  80, motorcar:  80, goods:  80, hgv:  80, psv:  80 },
+           track: { foot:  95, horse: 100, wheelchair:  95, bicycle:  90, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
+        cycleway: { foot:  95, horse:  90, wheelchair:  95, bicycle: 100, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
+            path: { foot: 100, horse: 100, wheelchair: 100, bicycle:  90, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
+           steps: { foot:  80, horse:   0, wheelchair:   0, bicycle:   0, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
+           ferry: { foot:  20, horse:  20, wheelchair:  20, bicycle:  20, moped:  20, motorbike:  20, motorcar:  20, goods:  20, hgv:  20, psv:  20 }
+     },
+
+  // Speed limits
+  profile_speed: {
+        motorway: { foot:   0, horse:   0, wheelchair:   0, bicycle:   0, moped:  48, motorbike: 112, motorcar: 112, goods:  96, hgv:  89, psv:  89 },
+           trunk: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  96, motorcar:  96, goods:  96, hgv:  80, psv:  80 },
+         primary: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  96, motorcar:  96, goods:  96, hgv:  80, psv:  80 },
+       secondary: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  88, motorcar:  88, goods:  88, hgv:  80, psv:  80 },
+        tertiary: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  80, motorcar:  80, goods:  80, hgv:  80, psv:  80 },
+    unclassified: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  64, motorcar:  64, goods:  64, hgv:  64, psv:  64 },
+     residential: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  48, motorcar:  48, goods:  48, hgv:  48, psv:  48 },
+         service: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  32, motorbike:  32, motorcar:  32, goods:  32, hgv:  32, psv:  32 },
+           track: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  16, motorbike:  16, motorcar:  16, goods:  16, hgv:  16, psv:  16 },
+        cycleway: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
+            path: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
+           steps: { foot:   4, horse:   0, wheelchair:   4, bicycle:   0, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
+           ferry: { foot:  10, horse:  10, wheelchair:  10, bicycle:  10, moped:  10, motorbike:  10, motorcar:  10, goods:  10, hgv:  10, psv:  10 }
+     },
+
+  // Highway properties
+  profile_property: {
+           paved: { foot:  50, horse:  20, wheelchair:  90, bicycle:  50, moped: 100, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100 },
+       multilane: { foot:  25, horse:  25, wheelchair:  25, bicycle:  25, moped:  35, motorbike:  60, motorcar:  60, goods:  60, hgv:  60, psv:  60 },
+          bridge: { foot:  50, horse:  50, wheelchair:  50, bicycle:  50, moped:  50, motorbike:  50, motorcar:  50, goods:  50, hgv:  50, psv:  50 },
+          tunnel: { foot:  50, horse:  50, wheelchair:  50, bicycle:  50, moped:  50, motorbike:  50, motorcar:  50, goods:  50, hgv:  50, psv:  50 },
+       footroute: { foot:  55, horse:  50, wheelchair:  55, bicycle:  50, moped:  50, motorbike:  50, motorcar:  45, goods:  45, hgv:  45, psv:  45 },
+    bicycleroute: { foot:  55, horse:  50, wheelchair:  55, bicycle:  60, moped:  50, motorbike:  50, motorcar:  45, goods:  45, hgv:  45, psv:  45 }
+     },
+
+  // Restrictions
+  profile_restrictions: {
+          oneway: { foot:    0, horse:    1, wheelchair:    0, bicycle:    1, moped:    1, motorbike:    1, motorcar:    1, goods:    1, hgv:    1, psv:    1 },
+           turns: { foot:    0, horse:    1, wheelchair:    0, bicycle:    1, moped:    1, motorbike:    1, motorcar:    1, goods:    1, hgv:    1, psv:    1 },
+          weight: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  5.0, hgv: 10.0, psv: 15.0 },
+          height: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  2.5, hgv:  3.0, psv:  3.0 },
+           width: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  2.0, hgv:  2.5, psv:  2.5 },
+          length: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  5.0, hgv:  6.0, psv:  6.0 }
+     }
+
+}; // end of routino variable
diff --git a/web/www/routino/profiles.pl b/web/www/routino/profiles.pl
new file mode 100644
index 0000000..362f8e6
--- /dev/null
+++ b/web/www/routino/profiles.pl
@@ -0,0 +1,78 @@
+################################################################################
+########################### Routino default profile ############################
+################################################################################
+
+$routino={ # contains all default Routino options (generated using "--help-profile-perl").
+
+  # Default transport type
+  transport => 'motorcar',
+
+  # Transport types
+  transports => { foot => 1, horse => 2, wheelchair => 3, bicycle => 4, moped => 5, motorbike => 6, motorcar => 7, goods => 8, hgv => 9, psv => 10 },
+
+  # Highway types
+  highways => { motorway => 1, trunk => 2, primary => 3, secondary => 4, tertiary => 5, unclassified => 6, residential => 7, service => 8, track => 9, cycleway => 10, path => 11, steps => 12, ferry => 13 },
+
+  # Property types
+  properties => { paved => 1, multilane => 2, bridge => 3, tunnel => 4, footroute => 5, bicycleroute => 6 },
+
+  # Restriction types
+  restrictions => { oneway => 1, turns => 2, weight => 3, height => 4, width => 5, length => 6 },
+
+  # Allowed highways
+  profile_highway => {
+      motorway => { foot =>   0,  horse =>   0,  wheelchair =>   0,  bicycle =>   0,  moped =>   0,  motorbike => 100,  motorcar => 100,  goods => 100,  hgv => 100,  psv => 100 },
+         trunk => { foot =>  40,  horse =>  25,  wheelchair =>  40,  bicycle =>  30,  moped =>  90,  motorbike => 100,  motorcar => 100,  goods => 100,  hgv => 100,  psv => 100 },
+       primary => { foot =>  50,  horse =>  50,  wheelchair =>  50,  bicycle =>  70,  moped => 100,  motorbike =>  90,  motorcar =>  90,  goods =>  90,  hgv =>  90,  psv =>  90 },
+     secondary => { foot =>  60,  horse =>  50,  wheelchair =>  60,  bicycle =>  80,  moped =>  90,  motorbike =>  80,  motorcar =>  80,  goods =>  80,  hgv =>  80,  psv =>  80 },
+      tertiary => { foot =>  70,  horse =>  75,  wheelchair =>  70,  bicycle =>  90,  moped =>  80,  motorbike =>  70,  motorcar =>  70,  goods =>  70,  hgv =>  70,  psv =>  70 },
+  unclassified => { foot =>  80,  horse =>  75,  wheelchair =>  80,  bicycle =>  90,  moped =>  70,  motorbike =>  60,  motorcar =>  60,  goods =>  60,  hgv =>  60,  psv =>  60 },
+   residential => { foot =>  90,  horse =>  75,  wheelchair =>  90,  bicycle =>  90,  moped =>  60,  motorbike =>  50,  motorcar =>  50,  goods =>  50,  hgv =>  50,  psv =>  50 },
+       service => { foot =>  90,  horse =>  75,  wheelchair =>  90,  bicycle =>  90,  moped =>  80,  motorbike =>  80,  motorcar =>  80,  goods =>  80,  hgv =>  80,  psv =>  80 },
+         track => { foot =>  95,  horse => 100,  wheelchair =>  95,  bicycle =>  90,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
+      cycleway => { foot =>  95,  horse =>  90,  wheelchair =>  95,  bicycle => 100,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
+          path => { foot => 100,  horse => 100,  wheelchair => 100,  bicycle =>  90,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
+         steps => { foot =>  80,  horse =>   0,  wheelchair =>   0,  bicycle =>   0,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
+         ferry => { foot =>  20,  horse =>  20,  wheelchair =>  20,  bicycle =>  20,  moped =>  20,  motorbike =>  20,  motorcar =>  20,  goods =>  20,  hgv =>  20,  psv =>  20 }
+     },
+
+  # Speed limits
+  profile_speed => {
+      motorway => { foot =>   0,  horse =>   0,  wheelchair =>   0,  bicycle =>   0,  moped =>  48,  motorbike => 112,  motorcar => 112,  goods =>  96,  hgv =>  89,  psv =>  89 },
+         trunk => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  96,  motorcar =>  96,  goods =>  96,  hgv =>  80,  psv =>  80 },
+       primary => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  96,  motorcar =>  96,  goods =>  96,  hgv =>  80,  psv =>  80 },
+     secondary => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  88,  motorcar =>  88,  goods =>  88,  hgv =>  80,  psv =>  80 },
+      tertiary => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  80,  motorcar =>  80,  goods =>  80,  hgv =>  80,  psv =>  80 },
+  unclassified => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  64,  motorcar =>  64,  goods =>  64,  hgv =>  64,  psv =>  64 },
+   residential => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  48,  motorcar =>  48,  goods =>  48,  hgv =>  48,  psv =>  48 },
+       service => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  32,  motorbike =>  32,  motorcar =>  32,  goods =>  32,  hgv =>  32,  psv =>  32 },
+         track => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  16,  motorbike =>  16,  motorcar =>  16,  goods =>  16,  hgv =>  16,  psv =>  16 },
+      cycleway => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
+          path => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
+         steps => { foot =>   4,  horse =>   0,  wheelchair =>   4,  bicycle =>   0,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
+         ferry => { foot =>  10,  horse =>  10,  wheelchair =>  10,  bicycle =>  10,  moped =>  10,  motorbike =>  10,  motorcar =>  10,  goods =>  10,  hgv =>  10,  psv =>  10 }
+     },
+
+  # Highway properties
+  profile_property => {
+         paved => { foot =>  50,  horse =>  20,  wheelchair =>  90,  bicycle =>  50,  moped => 100,  motorbike => 100,  motorcar => 100,  goods => 100,  hgv => 100,  psv => 100 },
+     multilane => { foot =>  25,  horse =>  25,  wheelchair =>  25,  bicycle =>  25,  moped =>  35,  motorbike =>  60,  motorcar =>  60,  goods =>  60,  hgv =>  60,  psv =>  60 },
+        bridge => { foot =>  50,  horse =>  50,  wheelchair =>  50,  bicycle =>  50,  moped =>  50,  motorbike =>  50,  motorcar =>  50,  goods =>  50,  hgv =>  50,  psv =>  50 },
+        tunnel => { foot =>  50,  horse =>  50,  wheelchair =>  50,  bicycle =>  50,  moped =>  50,  motorbike =>  50,  motorcar =>  50,  goods =>  50,  hgv =>  50,  psv =>  50 },
+     footroute => { foot =>  55,  horse =>  50,  wheelchair =>  55,  bicycle =>  50,  moped =>  50,  motorbike =>  50,  motorcar =>  45,  goods =>  45,  hgv =>  45,  psv =>  45 },
+  bicycleroute => { foot =>  55,  horse =>  50,  wheelchair =>  55,  bicycle =>  60,  moped =>  50,  motorbike =>  50,  motorcar =>  45,  goods =>  45,  hgv =>  45,  psv =>  45 }
+     },
+
+  # Restrictions
+  profile_restrictions => {
+          oneway => { foot =>    0,  horse =>    1,  wheelchair =>    0,  bicycle =>    1,  moped =>    1,  motorbike =>    1,  motorcar =>    1,  goods =>    1,  hgv =>    1,  psv =>    1 },
+           turns => { foot =>    0,  horse =>    1,  wheelchair =>    0,  bicycle =>    1,  moped =>    1,  motorbike =>    1,  motorcar =>    1,  goods =>    1,  hgv =>    1,  psv =>    1 },
+          weight => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  5.0,  hgv => 10.0,  psv => 15.0 },
+          height => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  2.5,  hgv =>  3.0,  psv =>  3.0 },
+           width => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  2.0,  hgv =>  2.5,  psv =>  2.5 },
+          length => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  5.0,  hgv =>  6.0,  psv =>  6.0 }
+     }
+
+}; # end of routino variable
+
+1;
diff --git a/web/www/routino/results.cgi b/web/www/routino/results.cgi
index 5236363..0fe93bf 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-2010 Andrew M. Bishop
+# This file Copyright 2008-2011 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
@@ -38,8 +38,8 @@ $query=new CGI;
 # Legal CGI parameters with regexp validity check
 
 %legalparams=(
-              "type"   => "(shortest|quickest)",
-              "format" => "(html|gpx-route|gpx-track|text|text-all)",
+              "type"   => "(shortest|quickest|router)",
+              "format" => "(html|gpx-route|gpx-track|text|text-all|log)",
 
               "uuid"   => "[0-9a-f]{32}"
              );
diff --git a/web/www/routino/router.cgi b/web/www/routino/router.cgi
index 41a6212..01facf2 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-2010 Andrew M. Bishop
+# This file Copyright 2008-2011 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
@@ -37,11 +37,13 @@ $query=new CGI;
 %legalparams=(
               "lon[1-9]"        => "[-0-9.]+",
               "lat[1-9]"        => "[-0-9.]+",
+              "heading"         => "[-0-9.]+",
               "transport"       => "[a-z]+",
               "highway-[a-z]+"  => "[0-9.]+",
               "speed-[a-z]+"    => "[0-9.]+",
               "property-[a-z]+" => "[0-9.]+",
               "oneway"          => "(1|0|true|false|on|off)",
+              "turns"           => "(1|0|true|false|on|off)",
               "weight"          => "[0-9.]+",
               "height"          => "[0-9.]+",
               "width"           => "[0-9.]+",
@@ -77,6 +79,8 @@ foreach $key (@rawparams)
 $type=$cgiparams{type};
 delete $cgiparams{type};
 
+$type="shortest" if(!$type);
+
 $format=$cgiparams{format};
 delete $cgiparams{format};
 
diff --git a/web/www/routino/router.html.en b/web/www/routino/router.html.en
index 2bfc0ba..d17bf7e 100644
--- a/web/www/routino/router.html.en
+++ b/web/www/routino/router.html.en
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -44,6 +44,7 @@
 <![endif]-->
 
 <!-- Router specific features -->
+<script src="profiles.js" type="text/javascript"></script>
 <script src="router.js" type="text/javascript"></script>
 <link href="router.css" type="text/css" rel="stylesheet">
 
@@ -332,6 +333,7 @@
         <div id="hideshow_restriction_div" style="display: none;">
           <table>
             <tr><td>Obey oneway:<td><input name="restrict-oneway" type="checkbox"    onchange="formSetRestriction('oneway')"><!-- oneway --><td>
+            <tr><td>Obey turns: <td><input name="restrict-turns"  type="checkbox"    onchange="formSetRestriction('turns' )"><!-- turns  --><td>
             <tr><td>Weight:     <td><input name="restrict-weight" type="text" size=3 onchange="formSetRestriction('weight')"><!-- weight --><td>tonnes
             <tr><td>Height:     <td><input name="restrict-height" type="text" size=3 onchange="formSetRestriction('height')"><!-- height --><td>metres
             <tr><td>Width:      <td><input name="restrict-width"  type="text" size=3 onchange="formSetRestriction('width' )"><!-- width  --><td>metres
@@ -424,11 +426,25 @@
     <div class="hideshow_box">
       <span class="hideshow_title">Status</span>
       <div id="result_status">
-        <span id="result_status_not_run"                        ><b><i>Router not run</i></b></span>
-        <span id="result_status_running"  style="display: none;"><b>Router running...</b></span>
-        <span id="result_status_complete" style="display: none;"><b>Routing completed</b></span>
-        <span id="result_status_error"    style="display: none;"><b>Router error</b></span>
-        <span id="result_status_failed"   style="display: none;"><b>Router failed to run</b></span>
+        <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>
 
diff --git a/web/www/routino/router.html.nl b/web/www/routino/router.html.nl
index d31dc01..0426b22 100644
--- a/web/www/routino/router.html.nl
+++ b/web/www/routino/router.html.nl
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 Andrew M. Bishop
 
  Dutch translation by Jan Jansen (August 2010).
 
@@ -46,6 +46,7 @@
 <![endif]-->
 
 <!-- Router specific features -->
+<script src="profiles.js" type="text/javascript"></script>
 <script src="router.js" type="text/javascript"></script>
 <link href="router.css" type="text/css" rel="stylesheet">
 
@@ -335,6 +336,7 @@
         <div id="hideshow_restriction_div" style="display: none;">
           <table>
             <tr><td>Volg Eenrichtingsverkeer:<td><input name="restrict-oneway" type="checkbox"    onchange="formSetRestriction('oneway')"><!-- oneway --><td>
+            <tr><td>Obey turn restrictions:  <td><input name="restrict-turns"  type="checkbox"    onchange="formSetRestriction('turns' )"><!-- turns  --><td>
             <tr><td>Gewicht:                 <td><input name="restrict-weight" type="text" size=3 onchange="formSetRestriction('weight')"><!-- weight --><td> ton
             <tr><td>Hoogte:                  <td><input name="restrict-height" type="text" size=3 onchange="formSetRestriction('height')"><!-- height --><td> meter
             <tr><td>Breedte:                 <td><input name="restrict-width"  type="text" size=3 onchange="formSetRestriction('width' )"><!-- width  --><td> meter
@@ -429,11 +431,25 @@
     <div class="hideshow_box">
       <span class="hideshow_title">Status</span>
       <div id="result_status">
-        <span id="result_status_not_run"                        ><b><i>Router niet in gebruik</i></b></span>
-        <span id="result_status_running"  style="display: none;"><b>Router werkt...</b></span>
-        <span id="result_status_complete" style="display: none;"><b>Routing voltooid</b></span>
-        <span id="result_status_error"    style="display: none;"><b>Router error</b></span>
-        <span id="result_status_failed"   style="display: none;"><b>Router werkt niet</b></span>
+        <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>
 
diff --git a/web/www/routino/router.js b/web/www/routino/router.js
index 5fea347..300830f 100644
--- a/web/www/routino/router.js
+++ b/web/www/routino/router.js
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2010 Andrew M. Bishop
+// This file Copyright 2008-2011 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,81 +19,6 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
-////////////////////////////////////////////////////////////////////////////////
-/////////////////////////// Routino default profile ////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-var routino={ // contains all default Routino options (generated using "--help-profile-json").
-
-  // Default transport type
-  transport: 'motorcar',
-
-  // Transport types
-  transports: { foot: 1, horse: 2, wheelchair: 3, bicycle: 4, moped: 5, motorbike: 6, motorcar: 7, goods: 8, hgv: 9, psv: 10 },
-
-  // Highway types
-  highways: { motorway: 1, trunk: 2, primary: 3, secondary: 4, tertiary: 5, unclassified: 6, residential: 7, service: 8, track: 9, cycleway: 10, path: 11, steps: 12, ferry: 13 },
-
-  // Property types
-  properties: { paved: 1, multilane: 2, bridge: 3, tunnel: 4, footroute: 5, bicycleroute: 6 },
-
-  // Restriction types
-  restrictions: { oneway: 1, weight: 2, height: 3, width: 4, length: 5 },
-
-  // Allowed highways
-  profile_highway: {
-        motorway: { foot:   0, horse:   0, wheelchair:   0, bicycle:   0, moped:   0, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100 },
-           trunk: { foot:  40, horse:  25, wheelchair:  40, bicycle:  30, moped:  90, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100 },
-         primary: { foot:  50, horse:  50, wheelchair:  50, bicycle:  70, moped: 100, motorbike:  90, motorcar:  90, goods:  90, hgv:  90, psv:  90 },
-       secondary: { foot:  60, horse:  50, wheelchair:  60, bicycle:  80, moped:  90, motorbike:  80, motorcar:  80, goods:  80, hgv:  80, psv:  80 },
-        tertiary: { foot:  70, horse:  75, wheelchair:  70, bicycle:  90, moped:  80, motorbike:  70, motorcar:  70, goods:  70, hgv:  70, psv:  70 },
-    unclassified: { foot:  80, horse:  75, wheelchair:  80, bicycle:  90, moped:  70, motorbike:  60, motorcar:  60, goods:  60, hgv:  60, psv:  60 },
-     residential: { foot:  90, horse:  75, wheelchair:  90, bicycle:  90, moped:  60, motorbike:  50, motorcar:  50, goods:  50, hgv:  50, psv:  50 },
-         service: { foot:  90, horse:  75, wheelchair:  90, bicycle:  90, moped:  80, motorbike:  80, motorcar:  80, goods:  80, hgv:  80, psv:  80 },
-           track: { foot:  95, horse: 100, wheelchair:  95, bicycle:  90, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
-        cycleway: { foot:  95, horse:  90, wheelchair:  95, bicycle: 100, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
-            path: { foot: 100, horse: 100, wheelchair: 100, bicycle:  90, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
-           steps: { foot:  80, horse:   0, wheelchair:   0, bicycle:   0, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
-           ferry: { foot:  20, horse:  20, wheelchair:  20, bicycle:  20, moped:  20, motorbike:  20, motorcar:  20, goods:  20, hgv:  20, psv:  20 }
-     },
-
-  // Speed limits
-  profile_speed: {
-        motorway: { foot:   0, horse:   0, wheelchair:   0, bicycle:   0, moped:  48, motorbike: 112, motorcar: 112, goods:  96, hgv:  89, psv:  89 },
-           trunk: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  96, motorcar:  96, goods:  96, hgv:  80, psv:  80 },
-         primary: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  96, motorcar:  96, goods:  96, hgv:  80, psv:  80 },
-       secondary: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  88, motorcar:  88, goods:  88, hgv:  80, psv:  80 },
-        tertiary: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  80, motorcar:  80, goods:  80, hgv:  80, psv:  80 },
-    unclassified: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  64, motorcar:  64, goods:  64, hgv:  64, psv:  64 },
-     residential: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  48, motorbike:  48, motorcar:  48, goods:  48, hgv:  48, psv:  48 },
-         service: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  32, motorbike:  32, motorcar:  32, goods:  32, hgv:  32, psv:  32 },
-           track: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:  16, motorbike:  16, motorcar:  16, goods:  16, hgv:  16, psv:  16 },
-        cycleway: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
-            path: { foot:   4, horse:   8, wheelchair:   4, bicycle:  20, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
-           steps: { foot:   4, horse:   0, wheelchair:   4, bicycle:   0, moped:   0, motorbike:   0, motorcar:   0, goods:   0, hgv:   0, psv:   0 },
-           ferry: { foot:  10, horse:  10, wheelchair:  10, bicycle:  10, moped:  10, motorbike:  10, motorcar:  10, goods:  10, hgv:  10, psv:  10 }
-     },
-
-  // Highway properties
-  profile_property: {
-           paved: { foot:  50, horse:  20, wheelchair:  90, bicycle:  50, moped: 100, motorbike: 100, motorcar: 100, goods: 100, hgv: 100, psv: 100 },
-       multilane: { foot:  25, horse:  25, wheelchair:  25, bicycle:  25, moped:  35, motorbike:  60, motorcar:  60, goods:  60, hgv:  60, psv:  60 },
-          bridge: { foot:  50, horse:  50, wheelchair:  50, bicycle:  50, moped:  50, motorbike:  50, motorcar:  50, goods:  50, hgv:  50, psv:  50 },
-          tunnel: { foot:  50, horse:  50, wheelchair:  50, bicycle:  50, moped:  50, motorbike:  50, motorcar:  50, goods:  50, hgv:  50, psv:  50 },
-       footroute: { foot:  55, horse:  50, wheelchair:  55, bicycle:  50, moped:  50, motorbike:  50, motorcar:  45, goods:  45, hgv:  45, psv:  45 },
-    bicycleroute: { foot:  55, horse:  50, wheelchair:  55, bicycle:  60, moped:  50, motorbike:  50, motorcar:  45, goods:  45, hgv:  45, psv:  45 }
-     },
-
-  // Restrictions
-  profile_restrictions: {
-          oneway: { foot:    0, horse:    1, wheelchair:    0, bicycle:    1, moped:    1, motorbike:    1, motorcar:    1, goods:    1, hgv:    1, psv:    1 },
-          weight: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  5.0, hgv: 10.0, psv: 15.0 },
-          height: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  2.5, hgv:  3.0, psv:  3.0 },
-           width: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  2.0, hgv:  2.5, psv:  2.5 },
-          length: { foot:  0.0, horse:  0.0, wheelchair:  0.0, bicycle:  0.0, moped:  0.0, motorbike:  0.0, motorcar:  0.0, goods:  5.0, hgv:  6.0, psv:  6.0 }
-     }
-
-}; // end of routino variable
 
 // Make a deep copy of the routino profile.
 
@@ -170,7 +95,7 @@ function form_init()
 
     for(var key in routino.restrictions)
       {
-       if(key=="oneway")
+       if(key=="oneway" || key=="turns")
           formSetRestriction(key);
        else
          {
@@ -273,7 +198,7 @@ function formSetTransport(type)
 
  for(var key in routino.restrictions)
    {
-    if(key=="oneway")
+    if(key=="oneway" || key=="turns")
        document.forms["form"].elements["restrict-" + key].checked=routino.profile_restrictions[key][routino.transport];
     else
        document.forms["form"].elements["restrict-" + key].value=routino.profile_restrictions[key][routino.transport];
@@ -328,12 +253,12 @@ function formSetProperty(type)
 
 
 //
-// Change of oneway rule in the form
+// Change of Restriction rule in the form
 //
 
 function formSetRestriction(type)
 {
- if(type=="oneway")
+ if(type=="oneway" || type=="turns")
     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;
@@ -1287,26 +1212,33 @@ function runRouterSuccess(response)
  var distinfo=lines[2];
  var message=lines[3];
 
+ var link;
+
  // Update the status message
 
  if(message!="")
    {
     displayStatus("result","error");
     hideshow_show('help_route');
+
+    link=document.getElementById("router_log_error");
+    link.href="results.cgi?uuid=" + uuid + ";type=router;format=log";
+
     return;
    }
  else
    {
     displayStatus("result","complete");
     hideshow_hide('help_route');
+
+    link=document.getElementById("router_log_complete");
+    link.href="results.cgi?uuid=" + uuid + ";type=router;format=log";
    }
 
  // Update the routing result message
 
  displayStatus(routing_type,"info",distinfo.bold());
 
- var link;
-
  link=document.getElementById(routing_type + "_html");
  link.href="results.cgi?uuid=" + uuid + ";type=" + routing_type + ";format=html";
  link=document.getElementById(routing_type + "_gpx_track");
diff --git a/web/www/routino/router.pl b/web/www/routino/router.pl
index 49f5077..7f43158 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-2010 Andrew M. Bishop
+# This file Copyright 2008-2011 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,6 +22,9 @@
 # Use the directory paths script
 require "paths.pl";
 
+# Load the profiles variables
+require "profiles.pl";
+
 # Use the perl Time::HiRes module
 use Time::HiRes qw(gettimeofday tv_interval);
 
@@ -31,83 +34,6 @@ $t0 = [gettimeofday];
 
 $data_prefix="";
 
-################################################################################
-########################### Routino default profile ############################
-################################################################################
-
-$routino={ # contains all default Routino options (generated using "--help-profile-perl").
-
-  # Default transport type
-  transport => 'motorcar',
-
-  # Transport types
-  transports => { foot => 1, horse => 2, wheelchair => 3, bicycle => 4, moped => 5, motorbike => 6, motorcar => 7, goods => 8, hgv => 9, psv => 10 },
-
-  # Highway types
-  highways => { motorway => 1, trunk => 2, primary => 3, secondary => 4, tertiary => 5, unclassified => 6, residential => 7, service => 8, track => 9, cycleway => 10, path => 11, steps => 12, ferry => 13 },
-
-  # Property types
-  properties => { paved => 1, multilane => 2, bridge => 3, tunnel => 4, footroute => 5, bicycleroute => 6 },
-
-  # Restriction types
-  restrictions => { oneway => 1, weight => 2, height => 3, width => 4, length => 5 },
-
-  # Allowed highways
-  profile_highway => {
-      motorway => { foot =>   0,  horse =>   0,  wheelchair =>   0,  bicycle =>   0,  moped =>   0,  motorbike => 100,  motorcar => 100,  goods => 100,  hgv => 100,  psv => 100 },
-         trunk => { foot =>  40,  horse =>  25,  wheelchair =>  40,  bicycle =>  30,  moped =>  90,  motorbike => 100,  motorcar => 100,  goods => 100,  hgv => 100,  psv => 100 },
-       primary => { foot =>  50,  horse =>  50,  wheelchair =>  50,  bicycle =>  70,  moped => 100,  motorbike =>  90,  motorcar =>  90,  goods =>  90,  hgv =>  90,  psv =>  90 },
-     secondary => { foot =>  60,  horse =>  50,  wheelchair =>  60,  bicycle =>  80,  moped =>  90,  motorbike =>  80,  motorcar =>  80,  goods =>  80,  hgv =>  80,  psv =>  80 },
-      tertiary => { foot =>  70,  horse =>  75,  wheelchair =>  70,  bicycle =>  90,  moped =>  80,  motorbike =>  70,  motorcar =>  70,  goods =>  70,  hgv =>  70,  psv =>  70 },
-  unclassified => { foot =>  80,  horse =>  75,  wheelchair =>  80,  bicycle =>  90,  moped =>  70,  motorbike =>  60,  motorcar =>  60,  goods =>  60,  hgv =>  60,  psv =>  60 },
-   residential => { foot =>  90,  horse =>  75,  wheelchair =>  90,  bicycle =>  90,  moped =>  60,  motorbike =>  50,  motorcar =>  50,  goods =>  50,  hgv =>  50,  psv =>  50 },
-       service => { foot =>  90,  horse =>  75,  wheelchair =>  90,  bicycle =>  90,  moped =>  80,  motorbike =>  80,  motorcar =>  80,  goods =>  80,  hgv =>  80,  psv =>  80 },
-         track => { foot =>  95,  horse => 100,  wheelchair =>  95,  bicycle =>  90,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
-      cycleway => { foot =>  95,  horse =>  90,  wheelchair =>  95,  bicycle => 100,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
-          path => { foot => 100,  horse => 100,  wheelchair => 100,  bicycle =>  90,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
-         steps => { foot =>  80,  horse =>   0,  wheelchair =>   0,  bicycle =>   0,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
-         ferry => { foot =>  20,  horse =>  20,  wheelchair =>  20,  bicycle =>  20,  moped =>  20,  motorbike =>  20,  motorcar =>  20,  goods =>  20,  hgv =>  20,  psv =>  20 }
-     },
-
-  # Speed limits
-  profile_speed => {
-      motorway => { foot =>   0,  horse =>   0,  wheelchair =>   0,  bicycle =>   0,  moped =>  48,  motorbike => 112,  motorcar => 112,  goods =>  96,  hgv =>  89,  psv =>  89 },
-         trunk => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  96,  motorcar =>  96,  goods =>  96,  hgv =>  80,  psv =>  80 },
-       primary => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  96,  motorcar =>  96,  goods =>  96,  hgv =>  80,  psv =>  80 },
-     secondary => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  88,  motorcar =>  88,  goods =>  88,  hgv =>  80,  psv =>  80 },
-      tertiary => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  80,  motorcar =>  80,  goods =>  80,  hgv =>  80,  psv =>  80 },
-  unclassified => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  64,  motorcar =>  64,  goods =>  64,  hgv =>  64,  psv =>  64 },
-   residential => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  48,  motorbike =>  48,  motorcar =>  48,  goods =>  48,  hgv =>  48,  psv =>  48 },
-       service => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  32,  motorbike =>  32,  motorcar =>  32,  goods =>  32,  hgv =>  32,  psv =>  32 },
-         track => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>  16,  motorbike =>  16,  motorcar =>  16,  goods =>  16,  hgv =>  16,  psv =>  16 },
-      cycleway => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
-          path => { foot =>   4,  horse =>   8,  wheelchair =>   4,  bicycle =>  20,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
-         steps => { foot =>   4,  horse =>   0,  wheelchair =>   4,  bicycle =>   0,  moped =>   0,  motorbike =>   0,  motorcar =>   0,  goods =>   0,  hgv =>   0,  psv =>   0 },
-         ferry => { foot =>  10,  horse =>  10,  wheelchair =>  10,  bicycle =>  10,  moped =>  10,  motorbike =>  10,  motorcar =>  10,  goods =>  10,  hgv =>  10,  psv =>  10 }
-     },
-
-  # Highway properties
-  profile_property => {
-         paved => { foot =>  50,  horse =>  20,  wheelchair =>  90,  bicycle =>  50,  moped => 100,  motorbike => 100,  motorcar => 100,  goods => 100,  hgv => 100,  psv => 100 },
-     multilane => { foot =>  25,  horse =>  25,  wheelchair =>  25,  bicycle =>  25,  moped =>  35,  motorbike =>  60,  motorcar =>  60,  goods =>  60,  hgv =>  60,  psv =>  60 },
-        bridge => { foot =>  50,  horse =>  50,  wheelchair =>  50,  bicycle =>  50,  moped =>  50,  motorbike =>  50,  motorcar =>  50,  goods =>  50,  hgv =>  50,  psv =>  50 },
-        tunnel => { foot =>  50,  horse =>  50,  wheelchair =>  50,  bicycle =>  50,  moped =>  50,  motorbike =>  50,  motorcar =>  50,  goods =>  50,  hgv =>  50,  psv =>  50 },
-     footroute => { foot =>  55,  horse =>  50,  wheelchair =>  55,  bicycle =>  50,  moped =>  50,  motorbike =>  50,  motorcar =>  45,  goods =>  45,  hgv =>  45,  psv =>  45 },
-  bicycleroute => { foot =>  55,  horse =>  50,  wheelchair =>  55,  bicycle =>  60,  moped =>  50,  motorbike =>  50,  motorcar =>  45,  goods =>  45,  hgv =>  45,  psv =>  45 }
-     },
-
-  # Restrictions
-  profile_restrictions => {
-          oneway => { foot =>    0,  horse =>    1,  wheelchair =>    0,  bicycle =>    1,  moped =>    1,  motorbike =>    1,  motorcar =>    1,  goods =>    1,  hgv =>    1,  psv =>    1 },
-          weight => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  5.0,  hgv => 10.0,  psv => 15.0 },
-          height => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  2.5,  hgv =>  3.0,  psv =>  3.0 },
-           width => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  2.0,  hgv =>  2.5,  psv =>  2.5 },
-          length => { foot =>  0.0,  horse =>  0.0,  wheelchair =>  0.0,  bicycle =>  0.0,  moped =>  0.0,  motorbike =>  0.0,  motorcar =>  0.0,  goods =>  5.0,  hgv =>  6.0,  psv =>  6.0 }
-     }
-
-}; # end of routino variable
-
-
 #
 # Fill in the default parameters using the ones above (don't use executable compiled in defaults)
 #
@@ -141,6 +67,9 @@ sub FillInDefaults
    $params{oneway} =~ s/(true|on)/1/;
    $params{oneway} =~ s/(false|off)/0/;
 
+   $params{turns} =~ s/(true|on)/1/;
+   $params{turns} =~ s/(false|off)/0/;
+
    foreach $restriction (keys %{$routino->{restrictions}})
      {
       $key="$restriction";
@@ -186,13 +115,22 @@ sub RunRouter
 
    $params.=" --dir=$data_dir" if($data_dir);
    $params.=" --prefix=$data_prefix" if($data_prefix);
-   $params.=" --quiet";
+   $params.=" --loggable";
 
-   $message=`$bin_dir/$router_exe $params 2>&1`;
+   system "$bin_dir/$router_exe $params > router.log 2>&1";
 
    (undef,undef,$cuser,$csystem) = times;
    $time=sprintf "time: %.3f CPU / %.3f elapsed",$cuser+$csystem,tv_interval($t0);
 
+   $message="";
+
+   if($? != 0)
+     {
+      $message=`tail -1 router.log`;
+     }
+
+   $result="";
+
    if(-f "$optimise.txt")
      {
       $result=`tail -1 $optimise.txt`;
@@ -217,7 +155,8 @@ sub RunRouter
            "gpx-route" => "-route.gpx",
            "gpx-track" => "-track.gpx",
            "text"      => ".txt",
-           "text-all"  => "-all.txt"
+           "text-all"  => "-all.txt",
+           "log"       => ".log"
           );
 
 # Possible MIME types
@@ -227,13 +166,16 @@ sub RunRouter
             "gpx-route" => "text/xml",
             "gpx-track" => "text/xml",
             "text"      => "text/plain",
-            "text-all"  => "text/plain"
+            "text-all"  => "text/plain",
+            "log"       => "text/plain"
            );
 
 sub ReturnOutput
   {
    my($uuid,$type,$format)=@_;
 
+   if($type eq "router") { $format="log" }
+
    $suffix=$suffixes{$format};
    $mime  =$mimetypes{$format};
 
diff --git a/web/www/routino/update-profiles.pl b/web/www/routino/update-profiles.pl
new file mode 100755
index 0000000..9a64306
--- /dev/null
+++ b/web/www/routino/update-profiles.pl
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+#
+# Update the Routino profile files
+#
+# Part of the Routino routing software.
+#
+# This file Copyright 2011 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 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);
+
+
+# Generate the Perl profiles.
+
+open(PROFILE,">profiles.pl") || die "Cannot open 'profiles.pl' to write.\n";
+
+print PROFILE "################################################################################\n";
+print PROFILE "########################### Routino default profile ############################\n";
+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";
+
+while(<EXECUTE>)
+  {
+   print PROFILE;
+  }
+
+close(EXECUTE);
+
+print PROFILE "\n";
+print PROFILE "1;\n";
+
+close(PROFILE);
+
+
+# Generate the Javascript profiles.
+
+open(PROFILE,">profiles.js") || die "Cannot open 'profiles.js' to write.\n";
+
+print PROFILE "////////////////////////////////////////////////////////////////////////////////\n";
+print PROFILE "/////////////////////////// Routino default profile ////////////////////////////\n";
+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";
+
+while(<EXECUTE>)
+  {
+   print PROFILE;
+  }
+
+close(EXECUTE);
+
+close(PROFILE);
diff --git a/web/www/routino/visualiser.cgi b/web/www/routino/visualiser.cgi
index 340d686..625113d 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-2010 Andrew M. Bishop
+# This file Copyright 2008-2011 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,7 +39,7 @@ $query=new CGI;
               "latmax" => "[-0-9.]+",
               "lonmin" => "[-0-9.]+",
               "lonmax" => "[-0-9.]+",
-              "data"   => "(junctions|super|oneway|speed|weight|height|width|length)"
+              "data"   => "(junctions|super|oneway|turns|speed|weight|height|width|length)"
              );
 
 # Validate the CGI parameters, ignore invalid ones
@@ -68,6 +68,7 @@ foreach $key (@rawparams)
          "speed"     => 0.2,
          "super"     => 0.2,
          "oneway"    => 0.2,
+         "turns"     => 0.3,
          "weight"    => 0.3,
          "height"    => 0.3,
          "width"     => 0.3,
diff --git a/web/www/routino/visualiser.html b/web/www/routino/visualiser.html
index c79bea9..29e8a14 100644
--- a/web/www/routino/visualiser.html
+++ b/web/www/routino/visualiser.html
@@ -6,7 +6,7 @@
 
  Part of the Routino routing software.
 
- This file Copyright 2008-2010 Andrew M. Bishop
+ This file Copyright 2008-2011 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
@@ -65,8 +65,7 @@
     <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
-      (e.g. private roads).
+      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>
         |
@@ -113,7 +112,8 @@
       <span id="hideshow_super_hide" onclick="hideshow_hide('super');" class="hideshow_hide">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).
+        Each super-node and the associated super-segments are shown (see
+        algorithm page for description).
       </div>
     </div>
 
@@ -122,8 +122,19 @@
       <span id="hideshow_oneway_hide" onclick="hideshow_hide('oneway');" class="hideshow_hide">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.
+        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_turns_show" onclick="hideshow_show('turns');" class="hideshow_show">Show</span>
+      <span id="hideshow_turns_hide" onclick="hideshow_hide('turns');" class="hideshow_hide">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>
 
diff --git a/web/www/routino/visualiser.js b/web/www/routino/visualiser.js
index 1444c89..375fd53 100644
--- a/web/www/routino/visualiser.js
+++ b/web/www/routino/visualiser.js
@@ -3,7 +3,7 @@
 //
 // Part of the Routino routing software.
 //
-// This file Copyright 2008-2010 Andrew M. Bishop
+// This file Copyright 2008-2011 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 @@ var data_types=[
                 "junctions",
                 "super",
                 "oneway",
+                "turns",
                 "speed",
                 "weight",
                 "height",
@@ -64,12 +65,14 @@ var super_node_style,super_segment_style;
 
 
 //
-// Oneway styles
+// 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;
+
 
 ////////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////// Map handling /////////////////////////////////
@@ -184,6 +187,8 @@ function map_init(lat,lon,zoom)
  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");
@@ -332,6 +337,9 @@ function displayData(datatype)
    case 'oneway':
     OpenLayers.loadURL(url,null,null,runOnewaySuccess,runFailure);
     break;
+   case 'turns':
+    OpenLayers.loadURL(url,null,null,runTurnsSuccess,runFailure);
+    break;
    case 'speed':
    case 'weight':
    case 'height':
@@ -533,6 +541,67 @@ function runOnewaySuccess(response)
 
 
 //
+// Success in getting the turn restrictions data
+//
+
+function runTurnsSuccess(response)
+{
+ var lines=response.responseText.split('\n');
+
+// This won't update the browser window
+// var div_status=document.getElementById("result_status");
+// div_status.innerHTML = "Processing " + (lines.length-2) + " turn restrictions ...";
+
+ 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,map.getProjectionObject());
+
+       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);
+
+ var div_status=document.getElementById("result_status");
+ div_status.innerHTML = "Processed " + (lines.length-2) + " turn restrictions";
+}
+
+
+//
 // Success in getting the speed/weight/height/width/length limits
 //
 
diff --git a/xml/Makefile b/xml/Makefile
index d2364cd..84805c2 100644
--- a/xml/Makefile
+++ b/xml/Makefile
@@ -1,10 +1,8 @@
-# $Header: /home/amb/routino/xml/RCS/Makefile,v 1.8 2010/09/05 18:27:17 amb Exp $
-#
 # XML directory Makefile
 #
 # Part of the Routino routing software.
 #
-# This file Copyright 2010 Andrew M. Bishop
+# This file Copyright 2010-2011 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,34 +20,68 @@
 
 # Web file paths
 
-WEBDIR=../web/data
+WEBDATADIR=../web/data
+WEBWWWDIR=../web/www/routino
 
 # Files to install
 
-FILES=profiles.xml \
-      translations.xml \
-      tagging.xml
+STANDARD_FILES=profiles.xml \
+               translations.xml \
+               tagging.xml
+
+SPECIAL_FILES=tagging-drive.xml \
+	      tagging-ride.xml \
+              tagging-walk.xml
 
 ########
 
-all :
-	-@[ -d $(WEBDIR) ] && \
-	  for file in $(FILES); do \
-	     if [ ! -f $(WEBDIR)/$$file ] || [ routino-$$file -nt $(WEBDIR)/$$file ]; then \
-	        echo cp routino-$$file $(WEBDIR)/$$file ;\
-	        cp -f routino-$$file $(WEBDIR)/$$file ;\
+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
+
+####
+
+tagging-drive.xml : routino-tagging.xml scripts/drive.pl
+	perl scripts/drive.pl < routino-tagging.xml > tagging-drive.xml
+
+tagging-ride.xml : routino-tagging.xml scripts/ride.pl
+	perl scripts/ride.pl < routino-tagging.xml > tagging-ride.xml
+
+tagging-walk.xml : routino-tagging.xml scripts/walk.pl
+	perl scripts/walk.pl < routino-tagging.xml > tagging-walk.xml
+
+########
+
+test:
 
 ########
 
 install: all
 	-[ -d $(DESTDIR)$(datadir) ] || mkdir -p $(DESTDIR)$(datadir)
-	@[ -d $(DESTDIR)$(datadir) ] && \
-	  for file in $(FILES); do \
+	- 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
 
 ########
 
@@ -59,7 +91,8 @@ clean:
 ########
 
 distclean: clean
-	rm -f $(WEBDIR)/*.xml
+	rm -f $(WEBDATADIR)/*.xml
+	rm -f $(SPECIAL_FILES)
 
 ########
 
diff --git a/xml/osm.xsd b/xml/osm.xsd
index 1b657a9..3b5d5cf 100644
--- a/xml/osm.xsd
+++ b/xml/osm.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" ?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/osm.xsd,v 1.3 2010/05/23 08:24:26 amb Exp $
+     $Header: /home/amb/CVS/routino/xml/osm.xsd,v 1.3 2010-05-23 08:24:26 amb Exp $
 
      An XML Schema Definition for the OSM (JOSM?) XML format
 
diff --git a/xml/routino-osm.xsd b/xml/routino-osm.xsd
index 092990a..607a47c 100644
--- a/xml/routino-osm.xsd
+++ b/xml/routino-osm.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" ?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/routino-osm.xsd,v 1.2 2010/05/23 08:24:26 amb Exp $
+     $Header: /home/amb/CVS/routino/xml/routino-osm.xsd,v 1.2 2010-05-23 08:24:26 amb Exp $
 
      An XML Schema Definition for the part of the OSM XML format read by Routino.
 
diff --git a/xml/routino-profiles.xml b/xml/routino-profiles.xml
index 669d823..8415d66 100644
--- a/xml/routino-profiles.xml
+++ b/xml/routino-profiles.xml
@@ -1,13 +1,11 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/routino-profiles.xml,v 1.5 2010/10/09 18:20:45 amb Exp $
-
      An XML format file containing Routino routing profiles
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010 Andrew M. Bishop
+     This file Copyright 2010-2011 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
@@ -59,6 +57,7 @@
     </properties>
     <restrictions>
       <oneway obey="0" /> 
+      <turns  obey="0" /> 
       <weight limit="0.0" />
       <height limit="0.0" />
       <width  limit="0.0" />
@@ -107,6 +106,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="0.0" />
       <height limit="0.0" />
       <width  limit="0.0" />
@@ -155,6 +155,7 @@
     </properties>
     <restrictions>
       <oneway obey="0" /> 
+      <turns  obey="0" /> 
       <weight limit="0.0" />
       <height limit="0.0" />
       <width  limit="0.0" />
@@ -203,6 +204,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="0.0" />
       <height limit="0.0" />
       <width  limit="0.0" />
@@ -251,6 +253,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="0.0" />
       <height limit="0.0" />
       <width  limit="0.0" />
@@ -299,6 +302,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="0.0" />
       <height limit="0.0" />
       <width  limit="0.0" />
@@ -347,6 +351,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="0.0" />
       <height limit="0.0" />
       <width  limit="0.0" />
@@ -395,6 +400,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="5.0" />
       <height limit="2.5" />
       <width  limit="2.0" />
@@ -443,6 +449,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="10.0" />
       <height limit="3.0" />
       <width  limit="2.5" />
@@ -491,6 +498,7 @@
     </properties>
     <restrictions>
       <oneway obey="1" /> 
+      <turns  obey="1" /> 
       <weight limit="15.0" />
       <height limit="3.0" />
       <width  limit="2.5" />
diff --git a/xml/routino-profiles.xsd b/xml/routino-profiles.xsd
index 7c13e94..6f8de26 100644
--- a/xml/routino-profiles.xsd
+++ b/xml/routino-profiles.xsd
@@ -1,13 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!-- ============================================================
-     $Header$
-
      An XML Schema Definition for the Routino profile XML format
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010 Andrew M. Bishop
+     This file Copyright 2010-2011 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,6 +59,7 @@
   <xsd:complexType name="restrictionsType">
     <xsd:sequence>
       <xsd:element name="oneway" type="onewayType"/>
+      <xsd:element name="turns"  type="turnsType"/>
       <xsd:element name="weight" type="weightType"/>
       <xsd:element name="height" type="heightType"/>
       <xsd:element name="width"  type="widthType"/>
@@ -89,6 +88,10 @@
     <xsd:attribute name="obey"    type="xsd:string"/>
   </xsd:complexType>
 
+  <xsd:complexType name="turnsType">
+    <xsd:attribute name="obey"    type="xsd:string"/>
+  </xsd:complexType>
+
   <xsd:complexType name="weightType">
     <xsd:attribute name="limit"   type="xsd:string"/>
   </xsd:complexType>
diff --git a/xml/routino-tagging-nomodify.xml b/xml/routino-tagging-nomodify.xml
index 28f127d..b834238 100644
--- a/xml/routino-tagging-nomodify.xml
+++ b/xml/routino-tagging-nomodify.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/routino-tagging-nomodify.xml,v 1.5 2010/09/17 17:40:55 amb Exp $
+     $Header: /home/amb/CVS/routino/xml/routino-tagging-nomodify.xml,v 1.5 2010-09-17 17:40:55 amb Exp $
 
      An XML format file containing Routino tagging rules - copy the input file
      directly to the output with no modifications (e.g. importing a file dumped
diff --git a/xml/routino-tagging.xml b/xml/routino-tagging.xml
index 4da652a..0324fc3 100644
--- a/xml/routino-tagging.xml
+++ b/xml/routino-tagging.xml
@@ -1,13 +1,11 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/routino-tagging.xml,v 1.5 2010/09/17 17:44:25 amb Exp $
-
      An XML format file containing Routino tagging rules
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010 Andrew M. Bishop
+     This file Copyright 2010-2011 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,9 +30,27 @@
       <output k="goods"      v="no"/>
       <output k="hgv"        v="no"/>
       <output k="psv"        v="no"/>
+
+      <unset k="barrier"/>
     </if>
 
     <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="turnstile">
+      <set v="foot_only"/>
+    </if>
+
+    <if k="barrier" v="footgate">
+      <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"/>
@@ -44,9 +60,11 @@
       <output k="goods"      v="no"/>
       <output k="hgv"        v="no"/>
       <output k="psv"        v="no"/>
+
+      <unset k="barrier"/>
     </if>
 
-    <if k="barrier" v="stile">
+    <if k="barrier" v="cycle_barrier">
       <output k="horse"      v="no"/>
       <output k="wheelchair" v="no"/>
       <output k="bicycle"    v="no"/>
@@ -56,18 +74,42 @@
       <output k="goods"      v="no"/>
       <output k="hgv"        v="no"/>
       <output k="psv"        v="no"/>
+
+      <unset k="barrier"/>
     </if>
 
-    <if k="barrier" v="turnstile">
+    <if k="barrier" v="horse_barrier">
       <output k="horse"      v="no"/>
-      <output k="wheelchair" v="no"/>
-      <output k="bicycle"    v="no"/>
+
+      <unset k="barrier"/>
+    </if>
+
+    <if k="barrier" v="cattle_grid">
+      <output k="horse"      v="no"/>
+
+      <unset k="barrier"/>
+    </if>
+
+    <if k="barrier" v="car_trap">
       <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>
+
+    <!-- Barriers that are too generic but listed to stop errors -->
+
+    <if k="barrier" v="gate">     <unset k="barrier"/></if>
+    <if k="barrier" v="entrance"> <unset k="barrier"/></if>
+    <if k="barrier" v="lift_gate"><unset k="barrier"/></if>
+    <if k="barrier" v="block">    <unset k="barrier"/></if>
+
+    <if k="barrier">
+      <logerror/>
     </if>
 
     <!-- Normalisation of access tags -->
@@ -136,6 +178,13 @@
     <if k="hgv"       ><output/></if>
     <if k="psv"       ><output/></if>
 
+
+    <!-- Mini-roundabouts  -->
+
+    <if k="highway" v="mini_roundabout">
+      <output/>
+    </if>
+
   </node>
 
   <!-- -------------------- Way rules -------------------- -->
@@ -145,6 +194,18 @@
     <!-- Note: The default is that no transport type is allowed on any highway;
                access must be specified to allow each transport type. -->
 
+    <if k="highway">
+      <output/>
+    </if>
+
+    <if>
+      <set k="not_highway" v="yes"/>
+    </if>
+
+    <if k="highway">
+      <unset k="not_highway"/>
+    </if>
+
     <!-- Highway types (includes default access and default properties) -->
 
     <if k="highway" v="motorway_link">
@@ -225,6 +286,10 @@
       <output k="paved"      v="yes"/>
     </if>
 
+    <if k="highway" v="tertiary_link">
+      <set v="tertiary"/>
+    </if>
+
     <if k="highway" v="tertiary">
       <output k="highway"/>
 
@@ -380,12 +445,11 @@
     </if>
 
     <if k="junction" v="roundabout">
-      <output k="junction" v="roundabout"/>
-      <output k="oneway"   v="yes"/>
+      <output k="oneway" v="yes"/>
     </if>
 
     <if k="route" v="ferry">
-      <set k="highway" v="ferry"/>
+      <output k="highway" v="ferry"/>
     </if>
 
     <!-- Normalisation of access tags -->
@@ -394,6 +458,8 @@
     <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="private"    ><set v="no"/></if>
 
@@ -441,16 +507,26 @@
       <output k="psv"/>
     </if>
 
-    <!-- Other access permissions (UK) -->
+    <!-- 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"/>
@@ -458,6 +534,12 @@
       <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">
@@ -469,6 +551,20 @@
       <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">
@@ -478,10 +574,28 @@
     <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>
@@ -495,23 +609,42 @@
 
     <!-- Normalisation of property tags -->
 
-    <if k="surface">
-      <set k="paved"/>
+    <if k="surface" v="paved">         <set k="paved" v="yes"/> <unset k="surface"/> </if>
+    <if k="surface" v="sealed">        <set k="paved" v="yes"/> <unset k="surface"/> </if>
+    <if k="surface" v="concrete">      <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="asphalt">       <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="paving_stones"> <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="setts">         <set k="paved" v="yes"/> <unset k="surface"/> </if>
+
+    <if k="surface" v="unpaved">       <set k="paved" v="no"/> <unset k="surface"/> </if>
+    <if k="surface" v="unsealed">      <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="ground">        <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="dirt">          <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="mud">           <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="gravel">        <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="cobblestone">   <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="compacted">     <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="not_highway">
+      <unset k="surface"/>
     </if>
 
-    <if k="surface" v="paved">
-      <set k="paved" v="yes"/>
-    </if>
-
-    <if k="surface" v="concrete">
-      <set k="paved" v="yes"/>
-    </if>
-
-    <if k="surface" v="ashphalt">
-      <set k="paved" v="yes"/>
+    <if k="surface">
+      <logerror/>
     </if>
 
-
     <if k="lanes">
       <set k="multilane" v="yes"/>
     </if>
@@ -525,8 +658,23 @@
     <if k="paved"    ><output/></if>
     <if k="multilane"><output/></if>
 
-    <if k="bridge"   ><output/></if>
-    <if k="tunnel"   ><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. -->
@@ -549,10 +697,14 @@
 
   </way>
 
-  <!-- Relation rules are not currently used -->
+  <!-- -------------------- Relation rules -------------------- -->
 
   <relation>
 
+    <if k="type">
+      <output/>
+    </if>
+
     <!-- Copy route relations -->
 
     <if k="route" v="foot">
@@ -581,6 +733,16 @@
       <output k="bicycleroute" v="yes"/>
     </if>
 
+    <!-- Pass through turn relations -->
+
+    <if k="restriction">
+      <output/>
+    </if>
+
+    <if k="except">
+      <output/>
+    </if>
+
   </relation>
 
 </routino-tagging>
diff --git a/xml/routino-tagging.xsd b/xml/routino-tagging.xsd
index 2193de0..d13da19 100644
--- a/xml/routino-tagging.xsd
+++ b/xml/routino-tagging.xsd
@@ -1,13 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/routino-tagging.xsd,v 1.3 2010/09/17 17:40:55 amb Exp $
-
      An XML Schema Definition for the Routino tagging rules XML format
 
      Part of the Routino routing software.
      ============================================================
-     This file Copyright 2010 Andrew M. Bishop
+     This file Copyright 2010-2011 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
@@ -53,8 +51,10 @@
 
   <xsd:complexType name="IfType">
     <xsd:sequence>
-      <xsd:element name="set"      type="SetType"     minOccurs="0" maxOccurs="unbounded"/>
-      <xsd:element name="output"   type="OutputType"  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"/>
@@ -65,9 +65,18 @@
     <xsd:attribute name="v" type="xsd:string"/>
   </xsd:complexType>
 
+  <xsd:complexType name="UnsetType">
+    <xsd:attribute name="k" type="xsd:string"/>
+  </xsd:complexType>
+
   <xsd:complexType name="OutputType">
     <xsd:attribute name="k" type="xsd:string"/>
     <xsd:attribute name="v" type="xsd:string"/>
   </xsd:complexType>
 
+  <xsd:complexType name="LogErrorType">
+    <xsd:attribute name="k" type="xsd:string"/>
+    <xsd:attribute name="v" type="xsd:string"/>
+  </xsd:complexType>
+
 </xsd:schema>
diff --git a/xml/routino-translations.xml b/xml/routino-translations.xml
index e68ba1a..9c433c7 100644
--- a/xml/routino-translations.xml
+++ b/xml/routino-translations.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/routino-translations.xml,v 1.11 2010/11/13 14:59:55 amb Exp $
+     $Header: /home/amb/CVS/routino/xml/routino-translations.xml,v 1.11 2010-11-13 14:59:55 amb Exp $
 
      An XML format file containing Routino output translations.
 
diff --git a/xml/routino-translations.xsd b/xml/routino-translations.xsd
index 75fbfab..878c253 100644
--- a/xml/routino-translations.xsd
+++ b/xml/routino-translations.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/routino-translations.xsd,v 1.3 2010/05/29 13:54:43 amb Exp $
+     $Header: /home/amb/CVS/routino/xml/routino-translations.xsd,v 1.3 2010-05-29 13:54:43 amb Exp $
 
      An XML Schema Definition for the Routino translations XML format
 
diff --git a/xml/scripts/drive.pl b/xml/scripts/drive.pl
new file mode 100755
index 0000000..08ee2b2
--- /dev/null
+++ b/xml/scripts/drive.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+
+while(<STDIN>)
+  {
+   if(m%</way>%)
+     {
+      print "    <!-- Special case for motor vehicles -->\n";
+      print "\n";
+      print "    <if>\n";
+      print "      <output k=\"foot\"       v=\"no\"/>\n";
+      print "      <output k=\"horse\"      v=\"no\"/>\n";
+      print "      <output k=\"wheelchair\" v=\"no\"/>\n";
+      print "      <output k=\"bicycle\"    v=\"no\"/>\n";
+      print "\n";
+      print "      <output k=\"bridge\" v=\"no\"/>\n";
+      print "      <output k=\"tunnel\" v=\"no\"/>\n";
+      print "    </if>\n";
+      print "\n";
+     }
+
+   if(m%</relation>%)
+     {
+      print "    <!-- Special case for motor vehicles -->\n";
+      print "\n";
+      print "    <if>\n";
+      print "      <output k=\"footroute\"    v=\"no\"/>\n";
+      print "      <output k=\"bicycleroute\" v=\"no\"/>\n";
+      print "    </if>\n";
+      print "\n";
+     }
+
+   print;
+  }
diff --git a/xml/scripts/ride.pl b/xml/scripts/ride.pl
new file mode 100755
index 0000000..1f01909
--- /dev/null
+++ b/xml/scripts/ride.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+while(<STDIN>)
+  {
+   if(m%</way>%)
+     {
+      print "    <!-- Special case for riding -->\n";
+      print "\n";
+      print "    <if>\n";
+      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=\"motorcar\"   v=\"no\"/>\n";
+      print "      <output k=\"goods\"      v=\"no\"/>\n";
+      print "      <output k=\"hgv\"        v=\"no\"/>\n";
+      print "      <output k=\"psv\"        v=\"no\"/>\n";
+      print "\n";
+      print "      <output k=\"bridge\" v=\"no\"/>\n";
+      print "      <output k=\"tunnel\" v=\"no\"/>\n";
+      print "\n";
+      print "      <output k=\"footroute\" v=\"no\"/>\n";
+      print "    </if>\n";
+      print "\n";
+     }
+
+   if(m%</relation>%)
+     {
+      print "    <!-- Special case for riding -->\n";
+      print "\n";
+      print "    <if>\n";
+      print "      <output k=\"footroute\" v=\"no\"/>\n";
+      print "    </if>\n";
+      print "\n";
+     }
+
+   print;
+  }
diff --git a/xml/scripts/walk.pl b/xml/scripts/walk.pl
new file mode 100755
index 0000000..969fb7a
--- /dev/null
+++ b/xml/scripts/walk.pl
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+
+while(<STDIN>)
+  {
+   if(m%</way>%)
+     {
+      print "    <!-- Special case for walking -->\n";
+      print "\n";
+      print "    <if>\n";
+      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=\"motorcar\"   v=\"no\"/>\n";
+      print "      <output k=\"goods\"      v=\"no\"/>\n";
+      print "      <output k=\"hgv\"        v=\"no\"/>\n";
+      print "      <output k=\"psv\"        v=\"no\"/>\n";
+      print "\n";
+      print "      <output k=\"oneway\" v=\"no\"/>\n";
+      print "\n";
+      print "      <output k=\"bridge\" v=\"no\"/>\n";
+      print "      <output k=\"tunnel\" v=\"no\"/>\n";
+      print "\n";
+      print "      <output k=\"bicycleroute\" v=\"no\"/>\n";
+      print "    </if>\n";
+      print "\n";
+     }
+
+   if(m%</relation>%)
+     {
+      print "    <!-- Special case for walking -->\n";
+      print "\n";
+      print "    <if>\n";
+      print "      <output k=\"restriction\" v=\"no\"/>\n";
+      print "\n";
+      print "      <output k=\"bicycleroute\" v=\"no\"/>\n";
+      print "    </if>\n";
+      print "\n";
+     }
+
+   print;
+  }
diff --git a/xml/xsd.xsd b/xml/xsd.xsd
index 3208cd3..5409524 100644
--- a/xml/xsd.xsd
+++ b/xml/xsd.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" ?>
 
 <!-- ============================================================
-     $Header: /home/amb/routino/xml/RCS/xsd.xsd,v 1.1 2010/03/28 15:27:05 amb Exp $
+     $Header: /home/amb/CVS/routino/xml/xsd.xsd,v 1.1 2010-03-28 15:27:05 amb Exp $
 
      An XML Schema Definition for the XML Schema Definition XML format
 

-- 
Packaging for routino



More information about the Pkg-grass-devel mailing list