Index: /applications/editors/josm/plugins/graphview/.classpath
===================================================================
--- /applications/editors/josm/plugins/graphview/.classpath	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/.classpath	(revision 16520)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/josm"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
+	<classpathentry kind="output" path="build"/>
+</classpath>
Index: /applications/editors/josm/plugins/graphview/.project
===================================================================
--- /applications/editors/josm/plugins/graphview/.project	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/.project	(revision 16520)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>validator</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: /applications/editors/josm/plugins/graphview/LICENSE
===================================================================
--- /applications/editors/josm/plugins/graphview/LICENSE	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/LICENSE	(revision 16520)
@@ -0,0 +1,346 @@
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /applications/editors/josm/plugins/graphview/build.xml
===================================================================
--- /applications/editors/josm/plugins/graphview/build.xml	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/build.xml	(revision 16520)
@@ -0,0 +1,108 @@
+<project name="graphview" default="dist" basedir=".">
+
+    <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
+    <property name="plugin.build.dir"       value="build"/>
+    <property name="plugin.src.dir"         value="src"/>
+    <!-- this is the directory where the plugin jar is copied to -->
+    <property name="plugin.dist.dir"        value="../../dist"/>
+    <property name="ant.build.javac.target" value="1.5"/>
+    <property name="plugin.dist.dir"        value="../../dist"/>
+    <property name="plugin.jar"             value="${plugin.dist.dir}/${ant.project.name}.jar"/>
+
+    <!--
+    **********************************************************
+    ** init - initializes the build
+    **********************************************************
+    -->
+    <target name="init">
+        <mkdir dir="${plugin.build.dir}"/>
+    </target>
+
+    <!--
+    **********************************************************
+    ** compile - complies the source tree
+    **********************************************************
+    -->
+    <target name="compile" depends="init">
+        <echo message="compiling sources for  ${plugin.jar} ... "/>
+        <javac srcdir="src" classpath="${josm}" debug="true" destdir="${plugin.build.dir}">
+            <compilerarg value="-Xlint:deprecation"/>
+            <compilerarg value="-Xlint:unchecked"/>
+        </javac>
+    </target>
+
+    <!--
+    **********************************************************
+    ** dist - creates the plugin jar
+    **********************************************************
+    -->
+    <target name="dist" depends="compile,revision">
+        <echo message="creating ${plugin.jar} ... "/>
+        <copy todir="${plugin.build.dir}/images">
+            <fileset dir="images"/>
+        </copy>
+        <copy todir="${plugin.build.dir}/files">
+            <fileset dir="files"/>
+        </copy>
+        <copy todir="${plugin.build.dir}">
+            <fileset dir=".">
+                <include name="LICENSE" />
+            </fileset>
+        </copy>
+        <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
+            <manifest>
+                <attribute name="Author" value="Tobias Knerr"/>
+                <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.graphview.plugin.GraphViewPlugin"/>
+                <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
+                <attribute name="Plugin-Description" value="Visualizes routing information as a routing graph."/>
+                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Graphview"/>
+                <attribute name="Plugin-Mainversion" value="0.1"/>
+                <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
+            </manifest>
+        </jar>
+    </target>
+
+    <!--
+    **********************************************************
+    ** revision - extracts the current revision number for the
+    **    file build.number and stores it in the XML property
+    **    version.*
+    **********************************************************
+    -->
+    <target name="revision">
+
+        <exec append="false" output="REVISION" executable="svn" failifexecutionfails="false">
+            <env key="LANG" value="C"/>
+            <arg value="info"/>
+            <arg value="--xml"/>
+            <arg value="."/>
+        </exec>
+        <xmlproperty file="REVISION" prefix="version" keepRoot="false" collapseAttributes="true"/>
+        <delete file="REVISION"/>
+    </target>
+
+    <!--
+    **********************************************************
+    ** clean - clean up the build environment
+    **********************************************************
+    -->
+    <target name="clean">
+        <delete dir="${plugin.build.dir}"/>
+        <delete file="${plugin.jar}"/>
+    </target>
+
+    <!--
+    **********************************************************
+    ** install - install the plugin in your local JOSM installation
+    **********************************************************
+    -->
+    <target name="install" depends="dist">
+        <property environment="env"/>
+        <condition property="josm.plugins.dir" value="${env.APPDATA}/JOSM/plugins" else="${user.home}/.josm/plugins">
+            <and>
+                <os family="windows"/>
+            </and>
+        </condition>
+        <copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
+    </target>
+</project>
Index: /applications/editors/josm/plugins/graphview/files/accessRuleset.xml
===================================================================
--- /applications/editors/josm/plugins/graphview/files/accessRuleset.xml	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/files/accessRuleset.xml	(revision 16520)
@@ -0,0 +1,205 @@
+<accessRuleset>
+
+	<classes>
+		<class name="access">
+			<class name="foot"/>
+			<class name="horse"/>
+			<class name="vehicle">
+				<class name="bicycle" />
+				<class name="carriage" />
+				<class name="motor_vehicle">
+					<class name="motorcycle">
+						<class name="moped"/>
+					</class>
+					<class name="motorcar">
+						<class name="caravan"/>
+						<class name="goods"/>
+						<class name="hgv"/>
+						<class name="psv"/>
+						<class name="agricultural"/>
+						<class name="emergency"/>
+						<class name="hazmat"/>
+					</class>
+					<class name="motorboat"/>
+					<class name="boat"/>
+				</class>
+				<class name="bicycle" />
+			</class>
+		</class>
+	</classes>
+	
+	<basetags>
+		<tag k="highway" v="motorway"/>
+		<tag k="highway" v="motorway_link"/>
+		<tag k="highway" v="trunk"/>
+		<tag k="highway" v="trunk_link"/>
+		<tag k="highway" v="primary"/>
+		<tag k="highway" v="primary_link"/>
+		<tag k="highway" v="secondary"/>
+		<tag k="highway" v="secondary_link"/>
+		<tag k="highway" v="tertiary"/>
+		<tag k="highway" v="unclassified"/>
+		<tag k="highway" v="road"/>
+		<tag k="highway" v="residential"/>
+		<tag k="highway" v="living_street"/>
+		<tag k="highway" v="service"/>
+		<tag k="highway" v="track"/>
+		<tag k="highway" v="pedestrian"/>
+		<tag k="highway" v="path"/>
+		<tag k="highway" v="cycleway"/>
+		<tag k="highway" v="footway"/>
+		<tag k="highway" v="bridleway"/>
+		<tag k="highway" v="byway"/>
+		<tag k="highway" v="steps"/>
+		<tag k="highway" v="platform"/>
+		<tag k="railway" v="platform"/>
+	</basetags>
+		
+	<implications>
+			
+		<!-- implications based on 
+		     http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Access-Restrictions
+		     section Default  -->
+	
+		<implication>
+			<condition>
+				<tag k="highway" v="motorway" />
+			</condition>
+			<implies>
+				<tag k="access"  v="designated"/>
+				<tag k="moped"   v="no"/>
+				<tag k="horse"   v="no"/>
+				<tag k="bicycle" v="no"/>
+				<tag k="foot"    v="no"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<or>
+					<tag k="highway" v="trunk" />
+					<tag k="highway" v="primary" />
+					<tag k="highway" v="secondary" />
+					<tag k="highway" v="tertiary" />
+					<tag k="highway" v="unclassified" />
+					<tag k="highway" v="residential" />
+					<tag k="highway" v="living_street" />
+					<tag k="highway" v="road" />
+				</or>
+			</condition>
+			<implies>
+				<tag k="access"  v="yes"/>
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="pedestrian" />
+			</condition>
+			<implies>
+				<tag k="access" v="no"/>
+				<tag k="foot"   v="yes"/>
+			</implies>
+		</implication>		
+		<implication>
+			<condition>
+				<tag k="highway" v="path" />
+			</condition>
+			<implies>
+				<tag k="access"  v="no"/>
+				<tag k="horse"   v="yes"/>
+				<tag k="bicycle" v="yes"/>
+				<tag k="foot"    v="yes"/>
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="bridleway" />
+			</condition>
+			<implies>
+				<tag k="access" v="no"/>
+				<tag k="horse"  v="designated"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<tag k="highway" v="cycleway" />
+			</condition>
+			<implies>
+				<tag k="access"  v="no"/>
+				<tag k="bicycle" v="designated"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<tag k="highway" v="footway" />
+			</condition>
+			<implies>
+				<tag k="access" v="no"/>
+				<tag k="foot"   v="designated"/>
+			</implies>
+		</implication>
+		
+		<!--  barriers -->
+		
+		<implication>
+			<condition>
+				<key k="barrier" />				
+			</condition>
+			<implies>
+				<tag k="access" v="no" />
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="barrier" v="bollard" />				
+			</condition>
+			<implies>
+				<tag k="foot" v="yes" />
+				<tag k="bicycle" v="yes" />
+			</implies>
+		</implication>
+			
+		<!--  direct implications -->
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="motorway" />
+			</condition>
+			<implies>
+				<tag k="oneway" v="yes" />
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="motorway_link" />
+			</condition>
+			<implies>
+				<tag k="oneway" v="yes" />
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="junction" v="roundabout" />
+			</condition>
+			<implies>
+				<tag k="oneway"  v="yes"/>
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="steps" />
+			</condition>
+			<implies>
+				<tag k="access"  v="no"/>
+				<tag k="foot"  v="yes"/>
+			</implies>
+		</implication>
+						
+	</implications>
+	
+</accessRuleset>
Index: /applications/editors/josm/plugins/graphview/files/accessRuleset_de.xml
===================================================================
--- /applications/editors/josm/plugins/graphview/files/accessRuleset_de.xml	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/files/accessRuleset_de.xml	(revision 16520)
@@ -0,0 +1,215 @@
+<accessRuleset>
+
+	<classes>
+		<class name="access">
+			<class name="foot"/>
+			<class name="horse"/>
+			<class name="vehicle">
+				<class name="bicycle" />
+				<class name="carriage" />
+				<class name="motor_vehicle">
+					<class name="motorcycle">
+						<class name="moped"/>
+					</class>
+					<class name="motorcar">
+						<class name="caravan"/>
+						<class name="goods"/>
+						<class name="hgv"/>
+						<class name="psv"/>
+						<class name="agricultural"/>
+						<class name="emergency"/>
+						<class name="hazmat"/>
+					</class>
+					<class name="motorboat"/>
+					<class name="boat"/>
+				</class>
+				<class name="bicycle" />
+			</class>
+		</class>
+	</classes>
+	
+	<basetags>
+		<tag k="highway" v="motorway"/>
+		<tag k="highway" v="motorway_link"/>
+		<tag k="highway" v="trunk"/>
+		<tag k="highway" v="trunk_link"/>
+		<tag k="highway" v="primary"/>
+		<tag k="highway" v="primary_link"/>
+		<tag k="highway" v="secondary"/>
+		<tag k="highway" v="secondary_link"/>
+		<tag k="highway" v="tertiary"/>
+		<tag k="highway" v="unclassified"/>
+		<tag k="highway" v="road"/>
+		<tag k="highway" v="residential"/>
+		<tag k="highway" v="living_street"/>
+		<tag k="highway" v="service"/>
+		<tag k="highway" v="track"/>
+		<tag k="highway" v="pedestrian"/>
+		<tag k="highway" v="path"/>
+		<tag k="highway" v="cycleway"/>
+		<tag k="highway" v="footway"/>
+		<tag k="highway" v="bridleway"/>
+		<tag k="highway" v="byway"/>
+		<tag k="highway" v="steps"/>
+		<tag k="highway" v="platform"/>
+		<tag k="railway" v="platform"/>
+	</basetags>
+		
+	<implications>
+			
+		<!-- implications based on 
+		     http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Access-Restrictions
+		     section Germany  -->
+	
+		<implication>
+			<condition>
+				<tag k="highway" v="motorway" />
+			</condition>
+			<implies>
+				<tag k="access"  v="yes"/>
+				<tag k="moped"   v="no"/>
+				<tag k="horse"   v="no"/>
+				<tag k="bicycle" v="no"/>
+				<tag k="foot"    v="no"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<or>
+					<tag k="highway" v="trunk" />
+					<tag k="highway" v="primary" />
+					<tag k="highway" v="secondary" />
+					<tag k="highway" v="tertiary" />
+					<tag k="highway" v="unclassified" />
+					<tag k="highway" v="residential" />
+					<tag k="highway" v="living_street" />
+					<tag k="highway" v="road" />
+				</or>
+			</condition>
+			<implies>
+				<tag k="access"  v="yes"/>
+			</implies>
+		</implication>
+			
+		<implication>
+			<condition>
+				<tag k="highway" v="path" />
+			</condition>
+			<implies>
+				<tag k="access"  v="no"/>
+				<tag k="horse"   v="yes"/>
+				<tag k="bicycle" v="yes"/>
+				<tag k="foot"    v="yes"/>
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="bridleway" />
+			</condition>
+			<implies>
+				<tag k="access" v="no"/>
+				<tag k="horse"  v="designated"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<tag k="highway" v="cycleway" />
+			</condition>
+			<implies>
+				<tag k="access"  v="no"/>
+				<tag k="bicycle" v="designated"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<or>
+					<tag k="highway" v="footway" />
+					<tag k="highway" v="pedestrian" />
+				</or>
+			</condition>
+			<implies>
+				<tag k="access" v="no"/>
+				<tag k="foot"   v="designated"/>
+			</implies>
+		</implication>
+		
+		<!--  local non-base-tags -->
+		
+		<implication>
+			<condition>
+				<tag k="motorroad" v="yes"/>
+			</condition>
+			<implies>
+				<tag k="minspeed"     v="60"/>
+				<tag k="foot"         v="no"/>
+				<tag k="bicycle"      v="no"/>				
+				<tag k="moped"        v="no"/>								
+				<tag k="horse"        v="no"/>								
+				<tag k="agricultural" v="no"/>
+			</implies>		
+		</implication>
+				
+		<!--  barriers -->
+		
+		<implication>
+			<condition>
+				<key k="barrier" />				
+			</condition>
+			<implies>
+				<tag k="access" v="no" />
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="barrier" v="bollard" />				
+			</condition>
+			<implies>
+				<tag k="foot" v="yes" />
+				<tag k="bicycle" v="yes" />
+			</implies>
+		</implication>
+			
+		<!--  direct implications -->
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="motorway" />
+			</condition>
+			<implies>
+				<tag k="oneway" v="yes" />
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="motorway_link" />
+			</condition>
+			<implies>
+				<tag k="oneway" v="yes" />
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="junction" v="roundabout" />
+			</condition>
+			<implies>
+				<tag k="oneway"  v="yes"/>
+			</implies>
+		</implication>
+		
+		<implication>
+			<condition>
+				<tag k="highway" v="steps" />
+			</condition>
+			<implies>
+				<tag k="access"  v="no"/>
+				<tag k="foot"  v="yes"/>
+			</implies>
+		</implication>
+		
+	</implications>
+	
+</accessRuleset>
Index: /applications/editors/josm/plugins/graphview/images/graphview_no_shadow.svg
===================================================================
--- /applications/editors/josm/plugins/graphview/images/graphview_no_shadow.svg	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/images/graphview_no_shadow.svg	(revision 16520)
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   version="1.0"
+   width="24"
+   height="24"
+   id="svg2">
+  <defs
+     id="defs4" />
+  <g
+     id="layer1">
+    <path
+       d="M 1.1364216,0.89276048 L 11.402097,8.4689046 L 17.235728,20.085659 L 23.422912,23.431789"
+       id="path3154"
+       style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-625.37277,-203.94857)"
+       id="path3168"
+       style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-619.53913,-192.73587)"
+       id="path3170"
+       style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       d="M 11.338963,8.4689046 L 7.323606,18.696699"
+       id="path3174"
+       style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+    <path
+       d="M 3.9459087,0.92432786 L 19.57802,8.0459033 L 6.8879779,18.601998 L 1.7993341,21.506186"
+       id="path3172"
+       style="fill:none;fill-rule:evenodd;stroke:#008000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-617.00113,-204.40314)"
+       id="path3164"
+       style="opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-629.32499,-193.80916)"
+       id="path3166"
+       style="opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+  </g>
+</svg>
Index: /applications/editors/josm/plugins/graphview/images/graphview_shadow.svg
===================================================================
--- /applications/editors/josm/plugins/graphview/images/graphview_shadow.svg	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/images/graphview_shadow.svg	(revision 16520)
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="24"
+   height="24"
+   id="svg2">
+  <defs
+     id="defs4">
+    <filter
+       id="filter3278">
+      <feGaussianBlur
+         id="feGaussianBlur3280"
+         stdDeviation="0.28899698"
+         inkscape:collect="always" />
+    </filter>
+  </defs>
+  <g
+     id="layer1">
+    <g
+       transform="translate(-27.484835,-5.7569321)"
+       id="g2483"
+       style="filter:url(#filter3278)">
+      <path
+         d="M 28.919255,7.0429857 L 39.184931,14.61913 L 45.018562,26.235884 L 50.643246,29.269514"
+         id="path2469"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+         transform="matrix(34.999999,0,0,34.999999,-597.58994,-197.79834)"
+         id="path2471"
+         style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <path
+         d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+         transform="matrix(34.999999,0,0,34.999999,-591.7563,-186.58564)"
+         id="path2473"
+         style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <path
+         d="M 39.121797,14.61913 L 35.10644,24.846924"
+         id="path2475"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 31.728742,7.0745531 L 47.360854,14.196129 L 34.670811,24.752223 L 29.582168,27.656411"
+         id="path2477"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+         transform="matrix(34.999999,0,0,34.999999,-589.2183,-198.25291)"
+         id="path2479"
+         style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <path
+         d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+         transform="matrix(34.999999,0,0,34.999999,-601.54216,-187.65893)"
+         id="path2481"
+         style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+    <path
+       d="M 0.9489216,0.58026048 L 11.214597,8.1564046 L 17.048228,19.773159 L 22.735412,22.931789"
+       id="path3154"
+       style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-625.56027,-204.26107)"
+       id="path3168"
+       style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-619.72663,-193.04837)"
+       id="path3170"
+       style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       d="M 11.151463,8.1564046 L 7.136106,18.384199"
+       id="path3174"
+       style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-617.18863,-204.71564)"
+       id="path3164"
+       style="opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <path
+       d="M 18.24588,6.0697923 A 0.063134536,0.063134536 0 1 1 18.11961,6.0697923 A 0.063134536,0.063134536 0 1 1 18.24588,6.0697923 z"
+       transform="matrix(34.999999,0,0,34.999999,-629.51249,-194.12166)"
+       id="path3166"
+       style="opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    <g
+       transform="translate(0.258671,-7.0609486)"
+       id="g3244"
+       style="filter:url(#filter3278)">
+      <path
+         d="M 14.082641,19.80879 L 12.794695,20.863136"
+         id="path3254"
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+    </g>
+    <path
+       d="M 3.7584087,0.61182786 L 19.39052,7.7334033 L 6.7004779,18.289498 L 1.6118341,21.193686"
+       id="path3172"
+       style="fill:none;fill-rule:evenodd;stroke:#008000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
+  </g>
+</svg>
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessEvaluator.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessEvaluator.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessEvaluator.java	(revision 16520)
@@ -0,0 +1,42 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+
+/**
+ * determines the access rights for an object under certain conditions.
+ * This interface is generic, so it can be used for different representations of ways and nodes
+ * (e.g. internal JOSM representation).
+ *
+ * Generic implementations will usually use {@link DataSource} objects
+ * to implement tag evaluation in an abstract way.
+ *
+ * @param <N>  type of the nodes
+ * @param <W>  type of the ways
+ */
+public interface AccessEvaluator<N, W> {
+
+	/**
+	 * checks whether a way may be accessed in the given direction
+	 *
+	 * @param way                object to be checked; != null
+	 * @param segmentProperties  map from road property types to their values for this way's
+	 *                           segments; each value must be a valid value for its property type;
+	 *                           != null
+	 */
+	public boolean wayUsable(W way, boolean forward,
+			Map<RoadPropertyType<?>, Object> roadPropertyValues);
+
+	/**
+	 * checks whether a node may be accessed/passed
+	 *
+	 * @param node               object to be checked; != null
+	 * @param segmentProperties  map from road property types to their values for SegmentNodes
+	 *                           based on this node, each value must be a valid value for its
+	 *                           property type; != null
+	 */
+	public boolean nodeUsable(N node, Map<RoadPropertyType<?>, Object> roadPropertyValues);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessParameters.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessParameters.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessParameters.java	(revision 16520)
@@ -0,0 +1,39 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyType;
+
+/**
+ * information that describes the vehicle and situation to evaluate access for.
+ * AccessParameters should be immutable.
+ */
+public interface AccessParameters {
+
+	public String getAccessClass();
+
+	/**
+	 * returns true iff a road with a given access type can be used
+	 * @param accessType  access type to check usablitly for; != null
+	 */
+	public boolean getAccessTypeUsable(AccessType accessType);
+
+	/**
+	 * returns all {@link VehiclePropertyType}s a value is avaliable for.
+	 * The value can be accessed using {@link #getVehiclePropertyValue(VehiclePropertyType)}
+	 * @return  collection of property types; != null
+	 */
+	public Collection<VehiclePropertyType<?>> getAvailableVehicleProperties();
+
+	/**
+	 * returns the value for a vehicle property.
+	 *
+	 * @param <V>              type of property value
+	 * @param vehicleProperty  property to get value for; != null
+	 * @return                 value for vehicleProperty, null if no value is available.
+	 *                         Guaranteed to be valid according to vehicleProperty's
+	 *                         {@link VehiclePropertyType#isValidValue(Object)} method.
+	 */
+	public <V> V getVehiclePropertyValue(VehiclePropertyType<V> vehicleProperty);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessRuleset.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessRuleset.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessRuleset.java	(revision 16520)
@@ -0,0 +1,38 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+
+/**
+ * set of assumptions and implications for access;
+ * will usually depend on local traffic laws
+ */
+public interface AccessRuleset {
+
+	/**
+	 * for a mode of transport, returns all transport categories it is a subset of.
+	 * For example, the returned collection for "motorcycle" might include "motor_vehicle" and "vehicle".
+	 *
+	 * @param transportMode  mode of transport to get "supertypes" for; != null
+	 * @return parameters superset categories, including the parameter itself,
+	 *         in the order of decreasing specificness
+	 *         empty if the parameter was no known mode of transport; != null
+	 */
+	public List<String> getAccessHierarchyAncestors(String transportMode);
+
+	/**
+	 * returns all base tags.
+	 * Base tags are tags that make an object "eligible" for access evaluation
+	 * (commonly things like highway=* or barrier=*)
+	 */
+	public Collection<Tag> getBaseTags();
+
+	/**
+	 * returns ruleset-specific implications
+	 * @return  list of implications in the order they are expected to be applied; != null
+	 */
+	public List<Implication> getImplications();
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessRulesetReader.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessRulesetReader.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessRulesetReader.java	(revision 16520)
@@ -0,0 +1,235 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * utility class that can read access rulesets from xml files
+ */
+public class AccessRulesetReader {
+
+	public static class AccessRulesetSyntaxException extends IOException {
+		private static final long serialVersionUID = 1L;
+		public AccessRulesetSyntaxException(String message) {
+			super(message);
+		};
+		public AccessRulesetSyntaxException(Throwable t) {
+			super(t);
+		}
+	}
+
+	/** private constructor to prevent instantiation */
+	private AccessRulesetReader() { }
+
+	public static AccessRuleset readAccessRuleset(InputStream inputStream)
+	throws AccessRulesetSyntaxException, IOException {
+
+		RulesetHandler rulesetHandler = new RulesetHandler();
+
+		try {
+			XMLReader reader = XMLReaderFactory.createXMLReader();
+			InputSource input = new InputSource(inputStream);
+			reader.setContentHandler(rulesetHandler);
+			reader.setErrorHandler(null);
+			reader.parse(input);
+		} catch (SAXException e) {
+			throw new AccessRulesetSyntaxException(e);
+		}
+
+		return rulesetHandler.getAccessRuleset();
+	}
+
+	private static class RulesetHandler extends DefaultHandler {
+
+		private static class AccessClass {
+			final String name;
+			final AccessClass parent;
+			public AccessClass(String name, AccessClass parent) {
+				this.name = name;
+				this.parent = parent;
+			}
+			List<String> getAncestorHierarchy() {
+				List<String> names;
+				if (parent == null) {
+					names = new LinkedList<String>();
+				} else {
+					names = parent.getAncestorHierarchy();
+				}
+				names.add(0, name);
+				return names;
+			}
+		}
+
+		private final Collection<AccessClass> accessClasses = new LinkedList<AccessClass>();
+		private final Collection<Tag> baseTags = new LinkedList<Tag>();
+
+		private static enum Section {NONE, CLASSES, BASETAGS, IMPLICATIONS};
+		private Section currentSection = Section.NONE;
+
+		private AccessClass currentAccessClass = null;
+
+		private ImplicationXMLReader implicationReader = null;
+		private final List<Implication> implications = new LinkedList<Implication>();
+
+		/** returns the AccessRuleset that was read */
+		AccessRuleset getAccessRuleset() {
+
+			return new AccessRuleset() {
+
+				public List<String> getAccessHierarchyAncestors(String transportMode) {
+					for (AccessClass accessClass : accessClasses) {
+						if (accessClass.name.equals(transportMode)) {
+							return accessClass.getAncestorHierarchy();
+						}
+					}
+					return new LinkedList<String>();
+				}
+
+				public Collection<Tag> getBaseTags() {
+					return baseTags;
+				}
+
+				public List<Implication> getImplications() {
+					return implications;
+				}
+
+			};
+		}
+
+		@Override
+		public void startElement(String uri, String localName, String name,
+				Attributes attributes) throws SAXException {
+
+			if (implicationReader != null) {
+				implicationReader.startElement(uri, localName, name, attributes);
+				return;
+			}
+
+			if ("classes".equals(name)) {
+
+				if (currentSection != Section.NONE) {
+					throw new SAXException("classes element below root child level");
+				}
+
+				currentSection = Section.CLASSES;
+
+			} else if ("class".equals(name)) {
+
+				String className = attributes.getValue("name");
+
+				if (currentSection != Section.CLASSES) {
+					throw new SAXException("class element (" + className + ") outside classes element");
+				} else if (className == null) {
+					throw new SAXException("class element without name");
+				}
+
+				AccessClass newAccessClass = new AccessClass(className, currentAccessClass);
+
+				accessClasses.add(newAccessClass);
+
+				currentAccessClass = newAccessClass;
+
+			} else if ("basetags".equals(name)) {
+
+				if (currentSection != Section.NONE) {
+					throw new SAXException("classes element below root child level");
+				}
+
+				currentSection = Section.BASETAGS;
+
+			} else if ("tag".equals(name)) {
+
+				if (currentSection == Section.BASETAGS) {
+					baseTags.add(readTag(attributes));
+				} else {
+					throw new SAXException("tag element outside basetag and implication elements");
+				}
+
+			} else if ("implications".equals(name)) {
+
+				if (currentSection != Section.NONE) {
+					throw new SAXException("implications element below root child level");
+				}
+
+				implicationReader = new ImplicationXMLReader();
+				currentSection = Section.IMPLICATIONS;
+
+			}
+
+		}
+
+		private static Tag readTag(Attributes attributes) throws SAXException {
+
+			String key = attributes.getValue("k");
+			String value = attributes.getValue("v");
+
+			if (key == null) {
+				throw new SAXException("tag without key");
+			} else if (value == null) {
+				throw new SAXException("tag without value (key is " + key + ")");
+			}
+
+			return new Tag(key, value);
+		}
+
+		@Override
+		public void endElement(String uri, String localName, String name)
+		throws SAXException {
+
+			if (implicationReader != null && !"implications".equals(name)) {
+				implicationReader.endElement(uri, localName, name);
+			}
+
+			if ("classes".equals(name)) {
+
+				if (currentSection != Section.CLASSES) {
+					throw new SAXException("closed classes while it wasn't open");
+				} else if (currentAccessClass != null) {
+					throw new SAXException("closed classes element before all class elements were closed");
+				}
+
+				currentSection = Section.NONE;
+
+			} else if ("class".equals(name)) {
+
+				if (currentAccessClass == null) {
+					throw new SAXException("closed class element while none was open");
+				}
+
+				currentAccessClass = currentAccessClass.parent;
+
+			} else if ("basetags".equals(name)) {
+
+				if (currentSection != Section.BASETAGS) {
+					throw new SAXException("closed basetags while it wasn't open");
+				}
+
+				currentSection = Section.NONE;
+
+			} else if ("implications".equals(name)) {
+
+				if (currentSection != Section.IMPLICATIONS) {
+					throw new SAXException("closed implications while it wasn't open");
+				}
+
+				implications.addAll(implicationReader.getImplications());
+				implicationReader = null;
+				currentSection = Section.NONE;
+
+			}
+
+		}
+
+	};
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessType.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessType.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/AccessType.java	(revision 16520)
@@ -0,0 +1,38 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+public enum AccessType {
+
+	YES("yes"),
+	PERMISSIVE("permissive"),
+	DESIGNATED("designated"),
+	DESTINATION("destination"),
+	AGRICULTURAL("agricultural"),
+	FORESTRY("forestry"),
+	DELIVERY("delivery"),
+	PRIVATE("private"),
+	NO("no"),
+	UNDEFINED();
+
+	private String[] valueStrings;
+	private AccessType(String... valueStrings) {
+		this.valueStrings = valueStrings;
+	}
+
+	/**
+	 * returns the AccessType that fits for a tag's value
+	 *
+	 * @param valueString  a tag's value; != null
+	 * @return             AccessType for the value; != null, will be UNDEFINED for unknown values
+	 */
+	public static AccessType getAccessType(String valueString) {
+		for (AccessType accessType : AccessType.values()) {
+			for (String typeValueString : accessType.valueStrings) {
+				if (typeValueString.equals(valueString)) {
+					return accessType;
+				}
+			}
+		}
+		return UNDEFINED;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/Implication.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/Implication.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/Implication.java	(revision 16520)
@@ -0,0 +1,61 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.util.TagCondition;
+
+/**
+ * immutable representation of a tag implication rule.
+ */
+public final class Implication {
+
+	private final TagCondition condition;
+	private final Collection<Tag> impliedTags;
+
+	public Implication(TagCondition condition, Collection<Tag> impliedTags) {
+		this.condition = condition;
+		this.impliedTags = impliedTags;
+	}
+
+	/**
+	 * applies this implication to a tag group.
+	 * The resulting tag group will contain all tags from the original group
+	 * and all implied tags with a key that didn't occur in the original group.
+	 *
+	 * @param tags  tag group to apply implications to; != null
+	 */
+	public TagGroup apply(TagGroup tags) {
+
+		if (condition.matches(tags)) {
+
+			Map<String, String> newTagMap = new HashMap<String, String>();
+
+			for (Tag tag : tags) {
+				newTagMap.put(tag.key, tag.value);
+			}
+
+			for (Tag impliedTag : impliedTags) {
+				if (!newTagMap.containsKey(impliedTag.key)) {
+					newTagMap.put(impliedTag.key, impliedTag.value);
+				}
+			}
+
+			return new MapBasedTagGroup(newTagMap);
+
+		} else {
+			return tags;
+		}
+
+	}
+
+	@Override
+	public String toString() {
+		return condition.toString() + " => " + impliedTags.toString();
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/ImplicationXMLReader.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/ImplicationXMLReader.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/ImplicationXMLReader.java	(revision 16520)
@@ -0,0 +1,294 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.openstreetmap.josm.plugins.graphview.core.util.TagCondition;
+import org.openstreetmap.josm.plugins.graphview.core.util.TagConditionLogic;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * class to be used in SAXHandler implementations for reading implication sections of xml files
+ */
+public class ImplicationXMLReader {
+
+	private final List<Implication> implications = new LinkedList<Implication>();
+
+	private static enum State {BEFORE_IMPLICATION, BEFORE_CONDITION, CONDITION, BEFORE_IMPLIES, IMPLIES, AFTER_IMPLIES};
+	private State state = State.BEFORE_IMPLICATION;
+
+	private ConditionReader currentConditionReader;
+	private TagCondition currentCondition;
+	private Collection<Tag> currentImpliedTags;
+
+	boolean tagOpen = false;
+
+	public void startElement(String uri, String localName, String name, Attributes attributes)
+	throws SAXException {
+
+		switch (state) {
+
+			case BEFORE_IMPLICATION:
+
+				if ("implication".equals(name)) {
+					state = State.BEFORE_CONDITION;
+					return;
+				}
+				break;
+
+			case BEFORE_CONDITION:
+
+				if ("condition".equals(name)) {
+					currentConditionReader = new ConditionReader();
+					state = State.CONDITION;
+					return;
+				}
+				break;
+
+			case CONDITION:
+				currentConditionReader.startElement(uri, localName, name, attributes);
+				return;
+
+			case BEFORE_IMPLIES:
+
+				if ("implies".equals(name)) {
+					currentImpliedTags = new LinkedList<Tag>();
+					state = State.IMPLIES;
+					return;
+				}
+				break;
+
+			case IMPLIES:
+
+				if ("tag".equals(name)) {
+					if (tagOpen) {
+						throw new SAXException("tag element inside other tag element");
+					}
+					currentImpliedTags.add(readTag(attributes));
+					tagOpen = true;
+					return;
+				}
+				break;
+
+		}
+
+		//all vaild paths end with return; reaching this indicates an invalid tag
+		throw new SAXException("invalid opening xml tag <" + name + "> in state " + state);
+
+	}
+
+	public void endElement(String uri, String localName, String name)
+	throws SAXException {
+
+		switch (state) {
+
+			case CONDITION:
+
+				if (name.equals("condition")) {
+					if (!currentConditionReader.isFinished()) {
+						throw new SAXException("condition isn't finished at </condition> tag");
+					} else {
+						currentCondition = currentConditionReader.getCondition();
+						currentConditionReader = null;
+						state = State.BEFORE_IMPLIES;
+						return;
+					}
+				} else {
+					currentConditionReader.endElement(uri, localName, name);
+					return;
+				}
+
+			case IMPLIES:
+
+				if (name.equals("implies")) {
+					state = State.AFTER_IMPLIES;
+					return;
+				} else if (name.equals("tag")) {
+					if (!tagOpen) {
+						throw new SAXException("closing tag element that was not open");
+					}
+					tagOpen = false;
+					return;
+				}
+				break;
+
+			case AFTER_IMPLIES:
+
+				if (name.equals("implication")) {
+					implications.add(new Implication(currentCondition, currentImpliedTags));
+					currentCondition = null;
+					currentImpliedTags = null;
+					state = State.BEFORE_IMPLICATION;
+					return;
+				}
+				break;
+
+		}
+
+		//all vaild paths end with return; reaching this indicates an invalid tag
+		throw new SAXException("invalid closing xml tag </" + name + "> in state " + state);
+
+	}
+
+	public List<Implication> getImplications() throws SAXException {
+
+		if (state != State.BEFORE_IMPLICATION) {
+			throw new SAXException("some tags not been closed; now in state " + state);
+		} else {
+			return new ArrayList<Implication>(implications);
+		}
+	}
+
+	private static Tag readTag(Attributes attributes) throws SAXException {
+
+		String key = attributes.getValue("k");
+		String value = attributes.getValue("v");
+
+		if (key == null) {
+			throw new SAXException("tag without key");
+		} else if (value == null) {
+			throw new SAXException("tag without value (key is " + key + ")");
+		}
+
+		return new Tag(key, value);
+	}
+
+	private static String readKey(Attributes attributes) throws SAXException {
+
+		String key = attributes.getValue("k");
+
+		if (key == null) {
+			throw new SAXException("key element without attribute k");
+		}
+
+		return key;
+	}
+
+	/**
+	 * class to be used for reading tag condition sections of xml files
+	 */
+	private static class ConditionReader {
+
+		String openingName;
+		TagCondition condition;
+		boolean finished;
+
+		private final List<ConditionReader> childReaders = new LinkedList<ConditionReader>();
+		private ConditionReader currentChildReader = null;
+
+		public void startElement(String uri, String localName, String name, Attributes attributes)
+		throws SAXException {
+
+			if (finished) {
+				throw new SAXException("condition is already finished at <" + name + ">");
+			}
+
+			if (currentChildReader != null) {
+				currentChildReader.startElement(uri, localName, name, attributes);
+				return;
+			}
+
+			//first tag is start tag of this condition
+			if (openingName == null) {
+
+				openingName = name;
+
+				if ("tag".equals(name)) {
+					condition = TagConditionLogic.tag(readTag(attributes));
+				} else if ("key".equals(name)) {
+					condition = TagConditionLogic.key(readKey(attributes));
+				} else if (!("or".equals(name)) && !("and".equals(name)) && !("not".equals(name))) {
+					throw new SAXException("unknown tag for condition: " + name);
+				}
+
+				//all tags after the first are start tags of child conditions
+			} else {
+
+				if ("tag".equals(openingName) || "key".equals(openingName)) {
+					throw new SAXException("element must not have childs: " + openingName);
+				}
+
+				currentChildReader = new ConditionReader();
+				currentChildReader.startElement(uri, localName, name, attributes);
+
+			}
+
+		}
+
+		public void endElement(String uri, String localName, String name)
+		throws SAXException {
+
+			if (finished) {
+				throw new SAXException("condition is already finished at </" + name + ">");
+			}
+
+			/* if active child reader exists, pass parameter to it. */
+			if (currentChildReader != null) {
+
+				currentChildReader.endElement(uri, localName, name);
+
+				if (currentChildReader.isFinished()) {
+					childReaders.add(currentChildReader);
+					currentChildReader = null;
+				}
+
+			} else {
+
+				if (openingName.equals(name)) {
+
+					List<TagCondition> childConditions = new ArrayList<TagCondition>();
+					for (ConditionReader childReader : childReaders) {
+						childConditions.add(childReader.getCondition());
+					}
+
+					if ("and".equals(openingName)) {
+						if (childConditions.size() > 0) {
+							condition = TagConditionLogic.and(childConditions);
+						} else {
+							throw new SAXException("<and> needs at least one child");
+						}
+					} else if ("or".equals(openingName)) {
+						if (childConditions.size() > 0) {
+							condition = TagConditionLogic.or(childConditions);
+						} else {
+							throw new SAXException("<or> needs at least one child");
+						}
+					} else if ("not".equals(openingName)) {
+						if (childConditions.size() == 1) {
+							condition = TagConditionLogic.not(childConditions.get(0));
+						} else {
+							throw new SAXException("<not> needs at least one child");
+						}
+					}
+
+					finished = true;
+
+				} else {
+					throw new SAXException("wrong closing tag " + name +
+							" (</" + openingName + "> expected");
+				}
+
+			}
+
+		}
+
+		public boolean isFinished() {
+			return finished;
+		}
+
+		public TagCondition getCondition() {
+			if (!finished) {
+				throw new IllegalStateException("condition " + openingName + " not yet finished");
+			} else {
+				assert condition != null;
+				return condition;
+			}
+		}
+
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/RulesetAccessEvaluator.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/RulesetAccessEvaluator.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/access/RulesetAccessEvaluator.java	(revision 16520)
@@ -0,0 +1,198 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import static org.openstreetmap.josm.plugins.graphview.core.access.AccessType.UNDEFINED;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+
+/**
+ * AccessEvaluator based on a single AccessRuleset
+ */
+public class RulesetAccessEvaluator<N, W, R> implements AccessEvaluator<N, W> {
+
+	private final DataSource<N, W, R> dataSource;
+	private final AccessRuleset ruleset;
+	private final AccessParameters parameters;
+
+	/**
+	 * @param dataSource  object that allows access to data objects and tags/members; != null
+	 * @param ruleset     ruleset that is used for evaluation; != null
+	 * @param parameters  parameters object that describes the vehicle
+	 *                    and situation to evaluate access for; != null
+	 */
+	public RulesetAccessEvaluator(DataSource<N, W, R> dataSource, AccessRuleset ruleset, AccessParameters parameters) {
+		assert dataSource != null && ruleset != null && parameters != null;
+
+		this.dataSource = dataSource;
+		this.ruleset = ruleset;
+		this.parameters = parameters;
+
+	}
+
+	public boolean wayUsable(W way, boolean forward,
+			Map<RoadPropertyType<?>, Object> segmentPropertyValues) {
+
+		TagGroup wayTags = dataSource.getTagsW(way);
+
+		TagGroup wayTagsWithImplications = new MapBasedTagGroup(wayTags);
+		for (Implication implication : ruleset.getImplications()) {
+			wayTagsWithImplications = implication.apply(wayTagsWithImplications);
+		}
+
+		/* check base tagging */
+
+		boolean usableWay = false;
+		for (Tag tag : ruleset.getBaseTags()) {
+			if (wayTags.contains(tag)) {
+				usableWay = true;
+				break;
+			}
+		}
+
+		if (!usableWay) {
+			return false;
+		}
+
+		/* evaluate one-way tagging */
+
+		String onewayValue =  wayTagsWithImplications.getValue("oneway");
+
+		if (forward && "-1".equals(onewayValue)
+				&& !"foot".equals(parameters.getAccessClass())) {
+			return false;
+		}
+
+		if (!forward
+				&& ("1".equals(onewayValue) || "yes".equals(onewayValue) || "true".equals(onewayValue))
+				&& !"foot".equals(parameters.getAccessClass())) {
+			return false;
+		}
+
+		/* evaluate properties and access tagging */
+
+		return objectUsable(segmentPropertyValues, wayTags);
+	}
+
+	public boolean nodeUsable(N node, Map<RoadPropertyType<?>,Object> roadPropertyValues) {
+
+		TagGroup nodeTags = dataSource.getTagsN(node);
+
+		return objectUsable(roadPropertyValues, nodeTags);
+	};
+
+	private boolean objectUsable(Map<RoadPropertyType<?>, Object> roadPropertyValues,
+			TagGroup tags) {
+
+		/* evaluate road properties */
+
+		for (RoadPropertyType<?> property : roadPropertyValues.keySet()) {
+			if (!property.isUsable(roadPropertyValues.get(property), parameters)) {
+				return false;
+			}
+		}
+
+		/* evaluate access type */
+
+		AccessType accessType = UNDEFINED;
+
+		if (tags.size() > 0) {
+
+			Map<String, AccessType> accessTypePerClass =
+				createAccessTypePerClassMap(tags, ruleset.getAccessHierarchyAncestors(parameters.getAccessClass()));
+
+			for (String accessClass : ruleset.getAccessHierarchyAncestors(parameters.getAccessClass())) {
+				accessType = accessTypePerClass.get(accessClass);
+				if (accessType != UNDEFINED) { break; }
+			}
+
+		}
+
+		return parameters.getAccessTypeUsable(accessType);
+	}
+
+	private Map<String, AccessType> createAccessTypePerClassMap(
+			TagGroup wayTags, Collection<String> accessClasses) {
+
+		/*
+		 * create map and fill with UNDEFINED values
+		 * (this also allows to use keySet instead of accessClasses later)
+		 */
+
+		Map<String, AccessType> accessTypePerClass = new HashMap<String, AccessType>();
+
+		for (String accessClass : accessClasses) {
+			accessTypePerClass.put(accessClass, AccessType.UNDEFINED);
+		}
+
+		/* evaluate implied tagging of base tag */
+
+		Tag baseTag = null;
+		for (Tag tag : wayTags) {
+			if (ruleset.getBaseTags().contains(tag)) {
+				baseTag = tag;
+				break;
+			}
+		}
+
+		if (baseTag != null) {
+
+			TagGroup tagsWithBaseImplications = new MapBasedTagGroup(baseTag);
+			for (Implication implication : ruleset.getImplications()) {
+				tagsWithBaseImplications = implication.apply(tagsWithBaseImplications);
+			}
+
+			setAccessTypesFromTags(accessTypePerClass, tagsWithBaseImplications);
+
+		}
+
+		/* evaluate implied tagging of other tags */
+
+		Map<String, String> tagMap = new HashMap<String, String>();
+		for (Tag tag : wayTags) {
+			if (!tag.equals(baseTag)) {
+				tagMap.put(tag.key, tag.value);
+			}
+		}
+
+		TagGroup tagsWithOtherImplications = new MapBasedTagGroup(tagMap);
+		for (Implication implication : ruleset.getImplications()) {
+			tagsWithOtherImplications = implication.apply(tagsWithOtherImplications);
+		}
+
+		setAccessTypesFromTags(accessTypePerClass, tagsWithOtherImplications);
+
+		/* evaluate explicit access tagging */
+
+		for (String key : ruleset.getAccessHierarchyAncestors(parameters.getAccessClass())) {
+			String value = wayTags.getValue(key);
+			if (value != null) {
+				AccessType accessType = AccessType.getAccessType(value);
+				accessTypePerClass.put(key, accessType);
+			}
+		}
+
+		return accessTypePerClass;
+	}
+
+	/**
+	 * adds all access information from a collection of tags to a [access class -> access type] map.
+	 * Existing entries will be replaced.
+	 */
+	private void setAccessTypesFromTags(Map<String, AccessType> accessTypePerClass, TagGroup tags) {
+		for (String accessClass : accessTypePerClass.keySet()) {
+			String value = tags.getValue(accessClass);
+			if (value != null) {
+				AccessType accessType = AccessType.getAccessType(value);
+				accessTypePerClass.put(accessClass, accessType);
+			}
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSource.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSource.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSource.java	(revision 16520)
@@ -0,0 +1,65 @@
+package org.openstreetmap.josm.plugins.graphview.core.data;
+
+/**
+ * source of OSM data that can be used to build graphs from
+ *
+ * @param <N>  node type
+ * @param <W>  way type
+ * @param <R>  relation type
+ */
+public interface DataSource<N, W, R> {
+
+	public static interface RelationMember {
+		/** returns the member's role. != null */
+		public String getRole();
+		/** returns the member itself. Must be instance of N, W or R, != null */
+		public Object getMember();
+	}
+
+	/** returns all nodes */
+	public Iterable<N> getNodes();
+
+	/** returns all ways */
+	public Iterable<W> getWays();
+
+	/** returns all relations */
+	public Iterable<R> getRelations();
+
+	/** returns a node's latitude */
+	public double getLat(N node);
+
+	/** returns a node's longitude */
+	public double getLon(N node);
+
+	/** returns a way's nodes */
+	public Iterable<N> getNodes(W way);
+
+	/** returns a relation's members */
+	public Iterable<RelationMember> getMembers(R relation);
+
+	/** returns a node's tags */
+	public TagGroup getTagsN(N node);
+
+	/** returns a way's tags */
+	public TagGroup getTagsW(W way);
+
+	/** returns a relation's tags */
+	public TagGroup getTagsR(R relation);
+
+	/**
+	 * adds an observer.
+	 * Does nothing if the parameter is already an observer of this DataSource.
+	 *
+	 * @param observer  observer object, != null
+	 */
+	public void addObserver(DataSourceObserver observer);
+
+	/**
+	 * deletes an observer that has been added using {@link #addObserver(DataSourceObserver)}.
+	 * Does nothing if the parameter isn't currently an observer of this DataSource.
+	 *
+	 * @param observer  observer object, != null
+	 */
+	public void deleteObserver(DataSourceObserver observer);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSourceObserver.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSourceObserver.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/DataSourceObserver.java	(revision 16520)
@@ -0,0 +1,16 @@
+package org.openstreetmap.josm.plugins.graphview.core.data;
+
+/**
+ * observer that will be informed about changes in a DataSource
+ * if it has been registered using {@link DataSource#addObserver(DataSourceObserver)}.
+ * Not every DataSource will send updates, because some don't change.
+ */
+public interface DataSourceObserver {
+
+	/**
+	 * informs this observer about changes in an observed data source
+	 * @param dataSource  observed data source that has changed; != null
+	 */
+	public void update(DataSource<?, ?, ?> dataSource);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/MapBasedTagGroup.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/MapBasedTagGroup.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/MapBasedTagGroup.java	(revision 16520)
@@ -0,0 +1,105 @@
+package org.openstreetmap.josm.plugins.graphview.core.data;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * TagGroup that uses a key-value-Map to store tags
+ */
+public class MapBasedTagGroup implements TagGroup {
+
+	private final Map<String, String> tagMap;
+
+	/**
+	 * @param tagMap  map from keys to values; != null;
+	 *                must not be modified after being used as parameter
+	 */
+	public MapBasedTagGroup(Map<String, String> tagMap) {
+		if (tagMap == null) {
+			throw new IllegalArgumentException();
+		}
+
+		this.tagMap = tagMap;
+	}
+
+	/**
+	 * @param tags  tags to add to the group; != null, each != null
+	 */
+	public MapBasedTagGroup(Iterable<Tag> tags) {
+		if (tags == null) {
+			throw new IllegalArgumentException();
+		}
+		this.tagMap = new HashMap<String, String>();
+		for (Tag tag : tags) {
+			if (tag == null) {
+				throw new IllegalArgumentException();
+			} else {
+				this.tagMap.put(tag.key, tag.value);
+			}
+		}
+	}
+
+	/**
+	 * @param tags  tags to add to the group; each != null
+	 */
+	public MapBasedTagGroup(Tag... tags) {
+		this.tagMap = new HashMap<String, String>(tags.length);
+		for (Tag tag : tags) {
+			if (tag == null) {
+				throw new IllegalArgumentException();
+			} else {
+				this.tagMap.put(tag.key, tag.value);
+			}
+		}
+	}
+
+	public String getValue(String key) {
+		assert key != null;
+		return tagMap.get(key);
+	}
+
+	public boolean containsKey(String key) {
+		assert key != null;
+		return tagMap.containsKey(key);
+	}
+
+	public boolean containsValue(String value) {
+		assert value != null;
+		return tagMap.containsValue(value);
+	}
+
+	public boolean contains(Tag tag) {
+		assert tag != null;
+		return tag.value.equals(tagMap.get(tag.key));
+	}
+
+	public int size() {
+		return tagMap.size();
+	}
+
+	/**
+	 * returns an Iterator providing access to all Tags.
+	 * The Iterator does not support the {@link Iterator#remove()} method.
+	 */
+	public Iterator<Tag> iterator() {
+
+		Collection<Tag> tagCollection = new LinkedList<Tag>();
+
+		for (String key : tagMap.keySet()) {
+			tagCollection.add(new Tag(key, tagMap.get(key)));
+		}
+
+		return Collections.unmodifiableCollection(tagCollection).iterator();
+
+	}
+
+	@Override
+	public String toString() {
+		return tagMap.toString();
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/Tag.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/Tag.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/Tag.java	(revision 16520)
@@ -0,0 +1,40 @@
+package org.openstreetmap.josm.plugins.graphview.core.data;
+
+/**
+ * immutable representation of an OSM tag (key-value-pair)
+ */
+public class Tag {
+
+	/** key of the tag; != null */
+	public final String key;
+
+	/** value of the tag; != null */
+	public final String value;
+
+	public Tag(String key, String value) {
+		assert key != null && value != null;
+		this.key = key;
+		this.value = value;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof Tag)) {
+			return false;
+		} else {
+			Tag otherTag = (Tag)obj;
+			return key.equals(otherTag.key) && value.equals(otherTag.value);
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		return key.hashCode() + value.hashCode();
+	}
+
+	@Override
+	public String toString() {
+		return key + "=" + value;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/TagGroup.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/TagGroup.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/data/TagGroup.java	(revision 16520)
@@ -0,0 +1,39 @@
+package org.openstreetmap.josm.plugins.graphview.core.data;
+
+/**
+ * represents a group of OSM tags (e.g. all tags of a way).
+ * TagGroups are expected to be immutable, so modifying the tags means creation of a new group.
+ * This interface requires that keys are unique, which is guaranteed since OSM API 0.6.
+ */
+public interface TagGroup extends Iterable<Tag> {
+
+	/**
+	 * returns the value for the given key or null if no tag in this group uses that key
+	 * @param key  key whose value will be returned; != null
+	 */
+	public String getValue(String key);
+
+	/**
+	 * returns true if this tag group contains a tag with the given key
+	 * @param key  key to check for; != null
+	 */
+	public boolean containsKey(String key);
+
+	/**
+	 * returns true if this tag group contains at least one tag with the given value
+	 * @param value  value to check for; != null
+	 */
+	public boolean containsValue(String value);
+
+	/**
+	 * returns true if this tag group contains the given tag
+	 * @param tag  tag to check for; != null
+	 */
+	public boolean contains(Tag tag);
+
+	/**
+	 * returns the number of tags in this group
+	 */
+	public int size();
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/ConnectorEvaluationGroup.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/ConnectorEvaluationGroup.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/ConnectorEvaluationGroup.java	(revision 16520)
@@ -0,0 +1,109 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.openstreetmap.josm.plugins.graphview.core.transition.Restriction;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+
+/**
+ * evaluation group that is based on segments and connects the node-based
+ * {@link JunctionEvaluationGroup}s.
+ */
+class ConnectorEvaluationGroup extends EvaluationGroup {
+
+	private final Set<Segment> segments;
+	private final List<SegmentNode> borderNodes;
+
+	/**
+	 * @param segments     set of Segments, must not be modified
+	 *                     after being used as constructor parameter; != null
+	 * @param borderNodes  nodes that are used as starting/target nodes for sequences; != null
+	 */
+	public ConnectorEvaluationGroup(Set<Segment> segments, Set<SegmentNode> borderNodes) {
+		assert segments != null && borderNodes != null;
+
+		this.segments = segments;
+		this.borderNodes = new ArrayList<SegmentNode>(borderNodes);
+	}
+
+	/**
+	 * returns all nodes that can be used as start/target nodes
+	 * @return  border nodes; != null
+	 */
+	public Collection<SegmentNode> getBorderNodes() {
+		return borderNodes;
+	}
+
+	/**
+	 * returns all segments in the group
+	 * @return  segment set; != null
+	 */
+	public Set<Segment> getSegments() {
+		return segments;
+	}
+
+	/**
+	 * returns a segment sequence that runs from an inbound to an outbound
+	 * segment or null if no connection is possible.
+	 * {@link EvaluationGroup#evaluate(Collection)} needs be called before this method.
+	 *
+	 * @param  startNode   start of the potential sequence; must be border node; != null
+	 * @param  targetNode  target of the potential sequence; must be border node; != null
+	 * @return             sequence of segments or null
+	 */
+	public List<Segment> getSegmentSequence(SegmentNode startNode, SegmentNode targetNode) {
+		assert startNode != null && borderNodes.contains(startNode);
+		assert targetNode != null && borderNodes.contains(targetNode);
+
+		if (!evaluated) { throw new IllegalStateException("group not yet evaluated"); }
+
+		int inboundIndex = borderNodes.indexOf(startNode);
+		int outboundIndex = borderNodes.indexOf(targetNode);
+
+		return segmentSequences[inboundIndex][outboundIndex];
+	}
+
+	@Override
+	protected void evaluateImpl(Collection<Restriction> restrictions) {
+
+		/* find segment sequences from inbound to outbound segments */
+
+		@SuppressWarnings("unchecked") //cannot create generic array without cast
+		List<Segment>[][] sequenceArray = new List[borderNodes.size()][borderNodes.size()];
+
+		for (int startIndex = 0; startIndex < borderNodes.size(); startIndex ++) {
+			for (int targetIndex = 0; targetIndex < borderNodes.size(); targetIndex ++) {
+
+				List<Segment> sequence =
+					findSegmentSequence(borderNodes.get(startIndex),
+							borderNodes.get(targetIndex), restrictions);
+
+				sequenceArray[startIndex][targetIndex] = sequence;
+
+			}
+		}
+
+		segmentSequences = sequenceArray;
+	}
+
+	@Override
+	protected boolean isUsableNode(SegmentNode node) {
+		return shareElement(segments, node.getInboundSegments())
+		|| shareElement(segments, node.getOutboundSegments());
+	}
+
+	@Override
+	protected boolean isUsableSegment(Segment segment) {
+		return segments.contains(segment);
+	}
+
+	@Override
+	public String toString() {
+		return "ConnectorEG " + segments;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/EvaluationGroup.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/EvaluationGroup.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/EvaluationGroup.java	(revision 16520)
@@ -0,0 +1,294 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+
+import org.openstreetmap.josm.plugins.graphview.core.transition.Restriction;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+
+/**
+ * group of nodes and/or segments that will be evaluated independently from other groups
+ */
+abstract class EvaluationGroup {
+
+	protected boolean evaluated = false;
+
+	/**
+	 * array of sequences.
+	 * First index is inbound segment/start node index,
+	 * second index is outbound segment/target node index.
+	 * Will contain the segment sequence after evaluation or null if none exists.
+	 */
+	protected List<Segment>[][] segmentSequences;
+
+	private static final List<Segment> EMPTY_SEGMENT_LIST =
+		Collections.unmodifiableList(new ArrayList<Segment>(0));
+
+	private static final List<Restriction> EMPTY_RESTRICTION_LIST =
+		Collections.unmodifiableList(new ArrayList<Restriction>(0));
+
+	private static class State {
+		SegmentNode currentNode;
+		Set<SegmentNode> visitedNodes;
+		Collection<Restriction> activeRestrictions;
+		List<Segment> segmentHistory;
+	}
+
+
+	/**
+	 * tries to find a legal sequence of segments between two segment nodes.
+	 *
+	 * @return  list of segments if connection is possible, null otherwise.
+	 */
+	protected List<Segment> findSegmentSequence(
+			SegmentNode firstNode, SegmentNode lastNode,
+			Collection<Restriction> restrictions) {
+
+		return findSegmentSequence(firstNode, lastNode, restrictions,
+				EMPTY_RESTRICTION_LIST, EMPTY_RESTRICTION_LIST);
+
+	}
+
+	/**
+	 * tries to find a legal sequence of segments between two segments.
+	 *
+	 * @return  list of segments if connection is possible, null otherwise.
+	 *          The list does NOT include firstSegment and lastSegment,
+	 *          but they are considered for restrictions.
+	 */
+	protected List<Segment> findSegmentSequence(
+			Segment firstSegment, Segment lastSegment,
+			Collection<Restriction> restrictions) {
+
+		if (firstSegment == lastSegment) {
+
+			return EMPTY_SEGMENT_LIST;
+
+		} else {
+
+			Collection<Restriction> initiallyActiveRestrictions =
+				activeRestrictionsAfterSegment(firstSegment, EMPTY_RESTRICTION_LIST, restrictions);
+
+			Collection<Restriction> restrictionsForbiddenAtLastNode = new HashSet<Restriction>();
+			for (Restriction restriction : restrictions) {
+				if (restriction.getTos().contains(lastSegment)) {
+					restrictionsForbiddenAtLastNode.add(restriction);
+				}
+			}
+
+			return findSegmentSequence(
+					firstSegment.getNode2(), lastSegment.getNode1(), restrictions,
+					initiallyActiveRestrictions, restrictionsForbiddenAtLastNode);
+		}
+
+	}
+
+	/**
+	 * tries to find a legal sequence of segments between two segment nodes.
+	 *
+	 * @param restrictions  all restrictions that have to be taken into account
+	 * @param initiallyActiveRestrictions  restrictions that are already active at firstNode
+	 * @param restrictionsForbiddenAtLastNode  restrictions that must NOT be active at lastNode
+	 * @return  list of segments if connection is possible, null otherwise.
+	 */
+	private List<Segment> findSegmentSequence(
+			SegmentNode firstNode, SegmentNode lastNode,
+			Collection<Restriction> restrictions,
+			Collection<Restriction> initiallyActiveRestrictions,
+			Collection<Restriction> restrictionsForbiddenAtLastNode) {
+
+		if (firstNode == lastNode
+				&& !shareElement(initiallyActiveRestrictions, restrictionsForbiddenAtLastNode)) {
+			return EMPTY_SEGMENT_LIST;
+		}
+
+		Queue<State> stateQueue = new LinkedList<State>();
+		stateQueue.add(createStartingState(firstNode, initiallyActiveRestrictions));
+
+		/* search for a possible segment sequence */
+
+		while (stateQueue.size() > 0) {
+
+			State state = stateQueue.poll();
+
+			Collection<State> subsequentStates = createSubsequentStates(state, restrictions);
+
+			for (State subsequentState : subsequentStates) {
+				if (subsequentState.currentNode == lastNode
+						&& !shareElement(subsequentState.activeRestrictions,
+								restrictionsForbiddenAtLastNode)) {
+					return subsequentState.segmentHistory;
+				}
+			}
+
+			stateQueue.addAll(subsequentStates);
+
+		}
+
+		return null;
+	}
+
+	private static State createStartingState(SegmentNode firstNode,
+			Collection<Restriction> initiallyActiveRestrictions) {
+
+		State startingState = new State();
+		startingState.currentNode = firstNode;
+		startingState.activeRestrictions = initiallyActiveRestrictions;
+		startingState.segmentHistory = EMPTY_SEGMENT_LIST;
+		startingState.visitedNodes = new HashSet<SegmentNode>();
+		startingState.visitedNodes.add(firstNode);
+
+		return startingState;
+	}
+
+	private List<State> createSubsequentStates(State state, Collection<Restriction> allRestrictions) {
+
+		List<State> subsequentStates = new ArrayList<State>();
+
+		for (Segment segment : state.currentNode.getOutboundSegments()) {
+
+			if (isUsableSegment(segment) &&
+					isLegalSegment(segment, state.activeRestrictions)) {
+
+				State newState = new State();
+
+				newState.activeRestrictions = activeRestrictionsAfterSegment(
+						segment, state.activeRestrictions, allRestrictions);
+
+				newState.segmentHistory = new ArrayList<Segment>(state.segmentHistory.size() + 1);
+				newState.segmentHistory.addAll(state.segmentHistory);
+				newState.segmentHistory.add(segment);
+
+				newState.currentNode = segment.getNode2();
+
+				newState.visitedNodes = new HashSet<SegmentNode>(state.visitedNodes);
+				newState.visitedNodes.add(newState.currentNode);
+
+				/* add state to queue,
+				 * but avoid cycles as well as leaving the node set
+				 */
+
+				if (!state.visitedNodes.contains(newState.currentNode)
+						&& isUsableNode(newState.currentNode)) {
+
+					subsequentStates.add(newState);
+
+				}
+			}
+		}
+
+		return subsequentStates;
+	}
+
+	/**
+	 * returns all restrictions from a collection that have a segment as from member
+	 * @return  segment list; != null; must not be modified.
+	 *          May throw an exception when modifying is attempted.
+	 */
+	private static List<Restriction> getRestrictionsStartedBySegment(
+			Collection<Restriction> restrictions, Segment segment) {
+
+		List<Restriction> result = EMPTY_RESTRICTION_LIST;
+		for (Restriction restriction : restrictions) {
+			if (restriction.getFrom() == segment) {
+				if (result == EMPTY_RESTRICTION_LIST) {
+					result = new ArrayList<Restriction>(restrictions.size());
+				}
+				result.add(restriction);
+			}
+		}
+
+		return result;
+	}
+
+	private static Collection<Restriction> activeRestrictionsAfterSegment(Segment segment,
+			Collection<Restriction> activeRestrictionsBeforeSegment,
+			Collection<Restriction> allRestrictions) {
+
+		Collection<Restriction> result = EMPTY_RESTRICTION_LIST;
+
+		for (Restriction restriction : activeRestrictionsBeforeSegment) {
+			if (restriction.getVias().contains(segment)) {
+				if (result == EMPTY_RESTRICTION_LIST) {
+					result = new ArrayList<Restriction>(allRestrictions.size());
+				}
+				result.add(restriction);
+			}
+		}
+
+		Collection<Restriction> newRestrictions =
+			getRestrictionsStartedBySegment(allRestrictions, segment);
+
+		if (newRestrictions.size() > 0) {
+			if (result == EMPTY_RESTRICTION_LIST) {
+				result = newRestrictions;
+			} else {
+				result.addAll(newRestrictions);
+			}
+		}
+
+		return result;
+	}
+
+	private static boolean isLegalSegment(
+			Segment segment, Collection<Restriction> activeRestrictions) {
+
+		for (Restriction restriction : activeRestrictions) {
+			if (restriction.getTos().contains(segment)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/** returns true iff at least one element is contained in both collections */
+	protected static boolean shareElement(
+			Collection<?> collection1, Collection<?> collection2) {
+		for (Object element : collection1) {
+			if (collection2.contains(element)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public final void evaluate(Collection<Restriction> restrictions) {
+
+		if (evaluated) { return; }
+
+		evaluateImpl(restrictions);
+
+		evaluated = true;
+	}
+
+	/**
+	 * finds in- and outbound segments (if necessary) and segment sequences.
+	 * After calling this method, the group must be correctly evaluated
+	 * (see {@link #isCorrectlyEvaluated()}).
+	 *
+	 * @param restrictions  restrictions that are used when determining possible connections,
+	 *                      will not be modified; != null
+	 */
+	abstract protected void evaluateImpl(Collection<Restriction> restrictions);
+
+	/**
+	 * returns whether a node can be used while finding a segment sequence
+	 * @param node  node to check; != null
+	 */
+	abstract protected boolean isUsableNode(SegmentNode node);
+
+	/**
+	 * returns whether a segment can be used while finding a segment sequence
+	 * @param segment  segment to check; != null
+	 */
+	abstract protected boolean isUsableSegment(Segment segment);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/GraphEdge.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/GraphEdge.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/GraphEdge.java	(revision 16520)
@@ -0,0 +1,21 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+
+/**
+ * directed connection between two nodes in a {@link WayGraph}
+ */
+public interface GraphEdge {
+
+	/** returns the node this edge starts at; != null */
+	GraphNode getStartNode();
+
+	/** returns the node this edge leads to; != null */
+	GraphNode getTargetNode();
+
+	/** returns the series of segments that are represented by this edge; != null */
+	List<Segment> getSegments();
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/GraphNode.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/GraphNode.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/GraphNode.java	(revision 16520)
@@ -0,0 +1,40 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+
+/**
+ * node in a {@link WayGraph};
+ * is based on one Segment and one of its SegmentNodes and describes the state
+ * of being on the segment immediately next to the node.
+ */
+public interface GraphNode {
+
+	/**
+	 * returns all edges that lead to this GraphNode; != null
+	 */
+	public Collection<GraphEdge> getInboundEdges();
+
+	/**
+	 * returns all edges that start at this GraphNode; != null
+	 */
+	public Collection<GraphEdge> getOutboundEdges();
+
+	/**
+	 * returns the SegmentNode this GraphNode is based on
+	 *
+	 * @return  SegmentNode, must be one of the nodes of the Segment returned
+	 *          by {@link #getSegment()}; != null
+	 */
+	public SegmentNode getSegmentNode();
+
+	/**
+	 * returns the Segment this GraphNode is based on
+	 *
+	 * @return  Segment; != null
+	 */
+	public Segment getSegment();
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/JunctionEvaluationGroup.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/JunctionEvaluationGroup.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/JunctionEvaluationGroup.java	(revision 16520)
@@ -0,0 +1,136 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.openstreetmap.josm.plugins.graphview.core.transition.Restriction;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+
+/**
+ * group of nodes that will be evaluated independently from other groups
+ */
+class JunctionEvaluationGroup extends EvaluationGroup {
+
+	private final Set<SegmentNode> segmentNodes;
+
+	protected List<Segment> inboundSegments;
+	protected List<Segment> outboundSegments;
+
+	/**
+	 * @param segmentNodes  set of SegmentNodes, must not be modified
+	 *                      after being used as constructor parameter; != null
+	 */
+	public JunctionEvaluationGroup(Set<SegmentNode> segmentNodes) {
+		assert segmentNodes != null;
+		this.segmentNodes = segmentNodes;
+	}
+
+	/**
+	 * returns all segments that can be used to enter this group.
+	 * {@link #evaluate(Iterable)} needs be called before this method.
+	 *
+	 * @return  segment collection; != null
+	 */
+	public Collection<Segment> getInboundSegments() {
+		if (!evaluated) { throw new IllegalStateException("group not yet evaluated"); }
+		return inboundSegments;
+	}
+
+	/**
+	 * returns all segments that can be used to leave this group.
+	 * {@link #evaluate(Iterable)} needs be called before this method.
+	 *
+	 * @return  segment collection; != null
+	 */
+	public Collection<Segment> getOutboundSegments() {
+		if (!evaluated) { throw new IllegalStateException("group not yet evaluated"); }
+		return outboundSegments;
+	}
+
+	/**
+	 * returns a segment sequence that runs from an inbound to an outbound
+	 * segment or null if no connection is possible.
+	 * {@link EvaluationGroup#evaluate(Collection)} needs be called before this method.
+	 *
+	 * @param  inboundSegment  start of the potential sequence;
+	 *                         must be inbound segment; != null
+	 * @param  outboundSegment target of the potential sequence;
+	 *                         must be outbound segment; != null
+	 * @return  sequence of segments or null
+	 */
+	public List<Segment> getSegmentSequence(Segment inboundSegment, Segment outboundSegment) {
+		assert inboundSegment != null && inboundSegments.contains(inboundSegment);
+		assert outboundSegment != null && outboundSegments.contains(outboundSegment);
+
+		if (!evaluated) { throw new IllegalStateException("group not yet evaluated"); }
+
+		int inboundIndex = inboundSegments.indexOf(inboundSegment);
+		int outboundIndex = outboundSegments.indexOf(outboundSegment);
+
+		return segmentSequences[inboundIndex][outboundIndex];
+	}
+
+	@Override
+	protected void evaluateImpl(final Collection<Restriction> restrictions) {
+
+		assert restrictions != null;
+
+		/* find inbound and outbound segments. An inbound segment is a segment whose target
+		 * is in the set and whose start node isn't (analogous for outbound segments)       */
+
+		inboundSegments = new ArrayList<Segment>();
+		outboundSegments = new ArrayList<Segment>();
+
+		for (SegmentNode segmentNode : segmentNodes) {
+			for (Segment segment : segmentNode.getInboundSegments()) {
+				if (!segmentNodes.contains(segment.getNode1())) {
+					inboundSegments.add(segment);
+				}
+			}
+			for (Segment segment : segmentNode.getOutboundSegments()) {
+				if (!segmentNodes.contains(segment.getNode2())) {
+					outboundSegments.add(segment);
+				}
+			}
+		}
+
+		/* find segment sequences from inbound to outbound segments */
+
+		@SuppressWarnings("unchecked") //cannot create generic array without cast
+		List<Segment>[][] sequenceArray = new List[inboundSegments.size()][outboundSegments.size()];
+
+		for (int inboundIndex = 0; inboundIndex < inboundSegments.size(); inboundIndex ++) {
+			for (int outboundIndex = 0; outboundIndex < outboundSegments.size(); outboundIndex ++) {
+
+				List<Segment> sequence =
+					findSegmentSequence(inboundSegments.get(inboundIndex),
+							outboundSegments.get(outboundIndex), restrictions);
+
+				sequenceArray[inboundIndex][outboundIndex] = sequence;
+
+			}
+		}
+
+		segmentSequences = sequenceArray;
+
+	}
+
+	@Override
+	protected boolean isUsableNode(SegmentNode node) {
+		return segmentNodes.contains(node);
+	}
+
+	@Override
+	protected boolean isUsableSegment(Segment segment) {
+		return segmentNodes.contains(segment.getNode1())
+		&& segmentNodes.contains(segment.getNode2());
+	}
+
+	@Override
+	public String toString() {
+		return "JunctionEG " + segmentNodes;
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/TSBasedWayGraph.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/TSBasedWayGraph.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/TSBasedWayGraph.java	(revision 16520)
@@ -0,0 +1,449 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openstreetmap.josm.plugins.graphview.core.transition.Restriction;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+import org.openstreetmap.josm.plugins.graphview.core.transition.TransitionStructure;
+import org.openstreetmap.josm.plugins.graphview.core.transition.TransitionStructureObserver;
+
+/**
+ * WayGraph that is based on a {@link TransitionStructure} and updated when it changes.
+ */
+public class TSBasedWayGraph implements WayGraph, TransitionStructureObserver {
+
+	private static class GraphNodeImpl implements GraphNode {
+		private final SegmentNode node;
+		private final Segment segment;
+		private final List<GraphEdge> incomingEdges = new ArrayList<GraphEdge>();
+		private final List<GraphEdge> outgoingEdges = new ArrayList<GraphEdge>();
+		public GraphNodeImpl(SegmentNode node, Segment segment) {
+			assert node != null && segment != null;
+			assert segment.getNode1() == node || segment.getNode2() == node;
+			this.node = node;
+			this.segment = segment;
+		}
+		public SegmentNode getSegmentNode() {
+			return node;
+		}
+		public Segment getSegment() {
+			return segment;
+		}
+		public void addIncomingEdge(GraphEdge edge) {
+			assert edge != null;
+			incomingEdges.add(edge);
+		}
+		public Collection<GraphEdge> getInboundEdges() {
+			return incomingEdges;
+		}
+		public void addOutgoingEdge(GraphEdge edge) {
+			assert edge != null;
+			outgoingEdges.add(edge);
+		}
+		public Collection<GraphEdge> getOutboundEdges() {
+			return outgoingEdges;
+		}
+		@Override
+		public String toString() {
+			return "(" + node + "; " + segment + ")";
+		}
+	}
+
+	private static class GraphEdgeImpl implements GraphEdge {
+		private final GraphNode startNode;
+		private final GraphNode targetNode;
+		private final List<Segment> segments;
+		public GraphEdgeImpl(GraphNode startNode, GraphNode targetNode, List<Segment> segments) {
+			assert startNode != null && targetNode != null && segments != null;
+			this.startNode = startNode;
+			this.targetNode = targetNode;
+			this.segments = segments;
+		}
+		public GraphNode getStartNode() {
+			return startNode;
+		}
+		public GraphNode getTargetNode() {
+			return targetNode;
+		}
+		public List<Segment> getSegments() {
+			return segments;
+		}
+		@Override
+		public String toString() {
+			return "(" + startNode + "-->" + targetNode + ")";
+		}
+	};
+
+	private final Set<WayGraphObserver> observers = new HashSet<WayGraphObserver>();
+
+	private final TransitionStructure transitionStructure;
+
+	private Collection<GraphNode> nodes;
+	private List<GraphEdge> edges;
+
+	/**
+	 * create a WayGraph based on a {@link TransitionStructure}
+	 * @param transitionStructure  transition structure this graph is to be based on; != null
+	 */
+	public TSBasedWayGraph(TransitionStructure transitionStructure) {
+		assert transitionStructure != null;
+
+		this.transitionStructure = transitionStructure;
+		transitionStructure.addObserver(this);
+
+		createNodesAndEdges();
+	}
+
+	public Collection<GraphEdge> getEdges() {
+		return edges;
+	}
+
+	public Collection<GraphNode> getNodes() {
+		return nodes;
+	}
+
+	private void createNodesAndEdges() {
+
+		Collection<EvaluationGroup> evaluationGroups =
+			createEvaluationGroups(transitionStructure);
+
+		for (EvaluationGroup evaluationGroup : evaluationGroups) {
+			evaluationGroup.evaluate(transitionStructure.getRestrictions());
+		}
+
+		createNodesAndEdgesFromEvaluationGroups(evaluationGroups);
+
+		evaluationGroups = null;
+	}
+
+	private static Collection<EvaluationGroup> createEvaluationGroups(
+			TransitionStructure transitionStructure) {
+
+		Map<SegmentNode, Set<SegmentNode>> nodeSetMap =
+			new HashMap<SegmentNode, Set<SegmentNode>>();
+
+		/* first step: everything that is part of the same restriction goes into the same set */
+
+		for (Restriction restriction : transitionStructure.getRestrictions()) {
+
+			/* group every node in via segments (which includes the
+			 * last node of from and the first node of to) into a set */
+
+			SegmentNode firstNode = restriction.getFrom().getNode2();
+			createSetIfHasNone(firstNode, nodeSetMap);
+
+			for (Segment segment : restriction.getVias()) {
+				putInSameSet(segment.getNode1(), firstNode, nodeSetMap);
+				putInSameSet(segment.getNode2(), firstNode, nodeSetMap);
+			}
+
+			for (Segment segment : restriction.getTos()) {
+				putInSameSet(segment.getNode1(), firstNode, nodeSetMap);
+			}
+
+		}
+
+		/* second step: create own sets for each junction and end point
+		 * (node connected with more than / less than two nodes). */
+
+		for (SegmentNode node : transitionStructure.getNodes()) {
+
+			if (!nodeSetMap.containsKey(node)
+					&& !isConnectedWithExactly2Nodes(node)) {
+
+				createSetIfHasNone(node, nodeSetMap);
+
+			}
+
+		}
+
+		/* third step: create segment sets for all segments that are not in one of the node sets
+		 * (that is, at least one node is not part of a junction evaluation group
+		 *  or the nodes are part of different junction evaluation groups)  */
+
+		Map<Segment, Set<Segment>> segmentSetMap =
+			new HashMap<Segment, Set<Segment>>();
+
+		for (Segment segment : transitionStructure.getSegments()) {
+
+			SegmentNode node1 = segment.getNode1();
+			SegmentNode node2 = segment.getNode2();
+
+			if (!nodeSetMap.containsKey(node1) || !nodeSetMap.containsKey(node2)
+					|| nodeSetMap.get(node1) != nodeSetMap.get(node2)) {
+
+				createSetIfHasNone(segment, segmentSetMap);
+
+				for (Segment subsequentSegment : segment.getNode2().getOutboundSegments()) {
+					if (!nodeSetMap.containsKey(node2)
+							|| subsequentSegment.getNode2() == node1) {
+						putInSameSet(subsequentSegment, segment, segmentSetMap);
+					}
+				}
+				//note that segments leading to this segment will share sets anyway,
+				//because this segment is a subsequent segment of them
+
+
+			}
+
+		}
+
+		/* create EvaluationGroup objects */
+
+		Collection<EvaluationGroup> evaluationGroups =
+			new ArrayList<EvaluationGroup>(nodeSetMap.size() + segmentSetMap.size());
+
+		Set<Set<SegmentNode>> nodeSets = new HashSet<Set<SegmentNode>>(nodeSetMap.values());
+		for (Set<SegmentNode> nodeSet : nodeSets) {
+			evaluationGroups.add(new JunctionEvaluationGroup(nodeSet));
+		}
+
+		HashSet<Set<Segment>> hashSets = new HashSet<Set<Segment>>(segmentSetMap.values());
+		for (Set<Segment> segmentSet : hashSets) {
+			Set<SegmentNode> borderNodes = new HashSet<SegmentNode>();
+			for (Segment segment : segmentSet) {
+				if (nodeSetMap.containsKey(segment.getNode1())) {
+					borderNodes.add(segment.getNode1());
+				}
+				if (nodeSetMap.containsKey(segment.getNode2())) {
+					borderNodes.add(segment.getNode2());
+				}
+			}
+			evaluationGroups.add(new ConnectorEvaluationGroup(segmentSet, borderNodes));
+		}
+
+		return evaluationGroups;
+	}
+
+	private void createNodesAndEdgesFromEvaluationGroups(
+			Collection<EvaluationGroup> evaluationGroups) {
+
+		nodes = new LinkedList<GraphNode>();
+		edges = new LinkedList<GraphEdge>();
+
+		//map from Segments to GraphNodes;
+		//for those GraphNodes representing an "approaching node on segment" state
+		final Map<Segment, GraphNodeImpl> segment2GNMap_approaching =
+			new HashMap<Segment, GraphNodeImpl>();
+
+		//map from Segments to GraphNodes;
+		//for those GraphNodes representing a "leaving node on segment" state
+		final Map<Segment, GraphNodeImpl> segment2GNMap_leaving =
+			new HashMap<Segment, GraphNodeImpl>();
+
+		//map from SegmentNodes to GraphNode collections;
+		//for those GraphNodes representing an "approaching node on segment" state
+		final Map<SegmentNode, Collection<GraphNodeImpl>> segNode2GNMap_approaching =
+			new HashMap<SegmentNode, Collection<GraphNodeImpl>>();
+
+		//map from SegmentNodes to GraphNodes collections;
+		//for those GraphNodes representing a "leaving node on segment" state
+		final Map<SegmentNode, Collection<GraphNodeImpl>> segNode2GNMap_leaving =
+			new HashMap<SegmentNode, Collection<GraphNodeImpl>>();
+
+
+
+		/* create graph nodes and edges for junction evaluation groups */
+
+		for (EvaluationGroup evaluationGroup : evaluationGroups) {
+			if (evaluationGroup instanceof JunctionEvaluationGroup) {
+
+				JunctionEvaluationGroup junctionEG = (JunctionEvaluationGroup) evaluationGroup;
+
+				//create graph nodes
+				for (Segment segment : junctionEG.getInboundSegments()) {
+					GraphNodeImpl graphNode = new GraphNodeImpl(segment.getNode2(), segment);
+					nodes.add(graphNode);
+					segment2GNMap_approaching.put(segment, graphNode);
+					addToCollectionMap(segNode2GNMap_approaching, segment.getNode2(), graphNode);
+				}
+				for (Segment segment : junctionEG.getOutboundSegments()) {
+					GraphNodeImpl graphNode = new GraphNodeImpl(segment.getNode1(), segment);
+					nodes.add(graphNode);
+					segment2GNMap_leaving.put(segment, graphNode);
+					addToCollectionMap(segNode2GNMap_leaving, segment.getNode1(), graphNode);
+				}
+
+				//create graph edges for all segment sequences between in- and outbound edges
+				for (Segment inboundSegment : junctionEG.getInboundSegments()) {
+					for (Segment outboundSegment : junctionEG.getOutboundSegments()) {
+
+						List<Segment> segmentSequence =
+							junctionEG.getSegmentSequence(inboundSegment, outboundSegment);
+
+						if (segmentSequence != null) {
+
+							createGraphEdge(
+									segment2GNMap_approaching.get(inboundSegment),
+									segment2GNMap_leaving.get(outboundSegment),
+									segmentSequence);
+
+						}
+					}
+				}
+
+			}
+		}
+
+		/* create graph edges for connector evaluation groups.
+		 * Because GraphNodes are created for pairs of SegmentNodes (from connector groups)
+		 * and Segments (from junction groups), the GraphNodes already exist.
+		 */
+
+		for (EvaluationGroup evaluationGroup : evaluationGroups) {
+			if (evaluationGroup instanceof ConnectorEvaluationGroup) {
+
+				ConnectorEvaluationGroup connectorEG = (ConnectorEvaluationGroup) evaluationGroup;
+
+				for (SegmentNode startNode : connectorEG.getBorderNodes()) {
+					for (SegmentNode targetNode : connectorEG.getBorderNodes()) {
+
+						if (segNode2GNMap_leaving.containsKey(startNode)
+								&& segNode2GNMap_approaching.containsKey(targetNode)) {
+
+							for (GraphNodeImpl startGraphNode : segNode2GNMap_leaving.get(startNode)) {
+								for (GraphNodeImpl targetGraphNode : segNode2GNMap_approaching.get(targetNode)) {
+
+									if (connectorEG.getSegments().contains(startGraphNode.getSegment())
+											&& connectorEG.getSegments().contains(targetGraphNode.getSegment())) {
+
+										List<Segment> segmentSequence =
+											connectorEG.getSegmentSequence(startNode, targetNode);
+
+										if (segmentSequence != null) {
+											createGraphEdge(
+													startGraphNode,
+													targetGraphNode,
+													segmentSequence);
+										}
+
+									}
+
+								}
+							}
+
+						}
+
+					}
+				}
+
+			}
+		}
+
+	}
+
+	/**
+	 * creates a GraphEdge;
+	 * adds it to its nodes' collections and {@link #edges} collection.
+	 */
+	private void createGraphEdge(
+			GraphNodeImpl startNode, GraphNodeImpl targetNode, List<Segment> segments) {
+
+		GraphEdge newEdge = new GraphEdgeImpl(startNode, targetNode, segments);
+
+		startNode.addOutgoingEdge(newEdge);
+		targetNode.addIncomingEdge(newEdge);
+
+		edges.add(newEdge);
+
+	}
+
+	private static boolean isConnectedWithExactly2Nodes(SegmentNode node) {
+
+		Set<SegmentNode> connectedNodes = new HashSet<SegmentNode>(2);
+
+		for (Segment segment : node.getInboundSegments()) {
+			connectedNodes.add(segment.getNode1());
+		}
+		for (Segment segment : node.getOutboundSegments()) {
+			connectedNodes.add(segment.getNode2());
+		}
+
+		return connectedNodes.size() == 2;
+	}
+
+	/**
+	 * creates a set for an object if none exists in a map.
+	 * The set will contain the object and be added to the map with the object being its key.
+	 */
+	private static <T> void createSetIfHasNone(T object, Map<T, Set<T>> objectSetMap) {
+
+		if (!objectSetMap.containsKey(object)) {
+			@SuppressWarnings("unchecked") //no set with generic parameter can be created directly
+			Set<T> set = new HashSet();
+			set.add(object);
+			objectSetMap.put(object, set);
+		}
+
+	}
+
+	/**
+	 * puts an object in another object's set.
+	 * If both nodes have sets already, these sets are merged.
+	 * The objectSetMap is modified accordingly.
+	 *
+	 * @param object        object that might or might not be in a set; != null
+	 * @param objectInSet   object that is guaranteed to be in a set; != null
+	 * @param objectSetMap  map from objects to the one set they are part of; != null
+	 */
+	private static <T> void putInSameSet(T object, T objectInSet, Map<T, Set<T>> objectSetMap) {
+		assert object != null && objectInSet != null && objectSetMap != null;
+		assert objectSetMap.containsKey(objectInSet);
+
+		Set<T> set = objectSetMap.get(objectInSet);
+
+		if (objectSetMap.containsKey(object)) {
+
+			/* merge the two sets */
+			Set<T> oldSet = objectSetMap.get(object);
+			for (T objectFromOldSet : oldSet) {
+				set.add(objectFromOldSet);
+				objectSetMap.put(objectFromOldSet, set);
+			}
+
+		} else {
+
+			/* add object to objectInSet's set */
+			set.add(object);
+			objectSetMap.put(object, set);
+
+		}
+
+	}
+
+	private static <K, E> void addToCollectionMap(final Map<K, Collection<E>> map, K key, E entry) {
+		if (!map.containsKey(key)) {
+			Collection<E> newCollection = new ArrayList<E>();
+			map.put(key, newCollection);
+		}
+		map.get(key).add(entry);
+	}
+
+	public void update(TransitionStructure transitionStructure) {
+		createNodesAndEdges();
+		notifyObservers();
+	}
+
+	public void addObserver(WayGraphObserver observer) {
+		observers.add(observer);
+	}
+
+	public void deleteObserver(WayGraphObserver observer) {
+		observers.remove(observer);
+	}
+
+	private void notifyObservers() {
+		for (WayGraphObserver observer : observers) {
+			observer.update(this);
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/WayGraph.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/WayGraph.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/WayGraph.java	(revision 16520)
@@ -0,0 +1,29 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+import java.util.Collection;
+
+/**
+ * graph representing OSM ways purely as nodes and directed edges
+ */
+public interface WayGraph {
+
+	Collection<GraphNode> getNodes();
+	Collection<GraphEdge> getEdges();
+
+	/**
+	 * adds an observer.
+	 * Does nothing if the parameter is already an observer of this WayGraph.
+	 *
+	 * @param observer  observer object, != null
+	 */
+	public void addObserver(WayGraphObserver observer);
+
+	/**
+	 * deletes an observer that has been added using {@link #addObserver(WayGraphObserver)}.
+	 * Does nothing if the parameter isn't currently an observer of this WayGraph.
+	 *
+	 * @param observer  observer object, != null
+	 */
+	public void deleteObserver(WayGraphObserver observer);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/WayGraphObserver.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/WayGraphObserver.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/graph/WayGraphObserver.java	(revision 16520)
@@ -0,0 +1,15 @@
+package org.openstreetmap.josm.plugins.graphview.core.graph;
+
+/**
+ * observer that will be informed about changes in a WayGraph
+ * if it has been registered using {@link WayGraph#addObserver(WayGraphObserver)}
+ */
+public interface WayGraphObserver {
+
+	/**
+	 * informs this observer about changes in an observed graph
+	 * @param wayGraph  observed graph that has changed; != null
+	 */
+	public void update(WayGraph wayGraph);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadIncline.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadIncline.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadIncline.java	(revision 16520)
@@ -0,0 +1,54 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.util.ValueStringParser;
+
+public class RoadIncline implements RoadPropertyType<Float> {
+
+	public <N, W, R> Float evaluateN(N node, AccessParameters accessParameters,
+			DataSource<N,W,R> dataSource) {
+		return null;
+	};
+
+	public <N, W, R> Float evaluateW(W way, boolean forward, AccessParameters accessParameters,
+			DataSource<N,W,R> dataSource) {
+		assert way != null && accessParameters != null && dataSource != null;
+
+		TagGroup tags = dataSource.getTagsW(way);
+		String inclineString = tags.getValue("incline");
+
+		if (inclineString != null) {
+			Float incline = ValueStringParser.parseIncline(inclineString);
+			if (incline != null) {
+				if (!forward) {
+					incline = -incline;
+				}
+				return incline;
+			}
+		}
+
+		return null;
+	};
+
+	public boolean isUsable(Object propertyValue, AccessParameters accessParameters) {
+		assert propertyValue instanceof Float;
+
+		float incline = (Float)propertyValue;
+
+		Float maxInclineUp =
+			accessParameters.getVehiclePropertyValue(VehiclePropertyTypes.MAX_INCLINE_UP);
+		Float maxInclineDown =
+			accessParameters.getVehiclePropertyValue(VehiclePropertyTypes.MAX_INCLINE_DOWN);
+
+		if (maxInclineUp != null && incline > maxInclineUp) {
+			return false;
+		} else if (maxInclineDown != null && -incline > maxInclineDown) {
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxaxleload.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxaxleload.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxaxleload.java	(revision 16520)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.AXLELOAD;
+
+public class RoadMaxaxleload extends RoadValueLimit {
+	public RoadMaxaxleload() {
+		super("maxaxleload", AXLELOAD, LimitType.MAXIMUM);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxheight.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxheight.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxheight.java	(revision 16520)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.HEIGHT;
+
+public class RoadMaxheight extends RoadValueLimit {
+	public RoadMaxheight() {
+		super("maxheight", HEIGHT, LimitType.MAXIMUM);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxlength.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxlength.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxlength.java	(revision 16520)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.LENGTH;
+
+public class RoadMaxlength extends RoadValueLimit {
+	public RoadMaxlength() {
+		super("maxlength", LENGTH, LimitType.MAXIMUM);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxspeed.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxspeed.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxspeed.java	(revision 16520)
@@ -0,0 +1,70 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.util.ValueStringParser;
+
+public class RoadMaxspeed implements RoadPropertyType<Float> {
+
+	private DataSource<?, ?, ?> lastDataSource;
+
+	/**
+	 * (re)creates information like boundaries if data source has changed
+	 * since last call to {@link #evaluate(Object, boolean, AccessParameters, DataSource)}
+	 */
+	private <N, W, R> void initializeIfNecessary(DataSource<N, W, R> dataSource) {
+
+		if (dataSource != lastDataSource) {
+
+			/*
+			 *
+			 * currently no activities;
+			 * place boundaries or similar features can be handled here
+			 * once there is consensus on the topic of implicit maxspeeds, trafficzones etc.
+			 *
+			 */
+
+			lastDataSource = dataSource;
+		}
+	}
+
+	public <N, W, R> Float evaluateN(N node, AccessParameters accessParameters,
+			DataSource<N, W, R> dataSource) {
+		assert node != null && accessParameters != null && dataSource != null;
+
+		initializeIfNecessary(dataSource);
+
+		return evaluateTags(dataSource.getTagsN(node));
+	}
+
+	public <N, W, R> Float evaluateW(W way, boolean forward, AccessParameters accessParameters,
+			DataSource<N, W, R> dataSource) {
+		assert way != null && accessParameters != null && dataSource != null;
+
+		initializeIfNecessary(dataSource);
+
+		return evaluateTags(dataSource.getTagsW(way));
+	}
+
+	private Float evaluateTags(TagGroup tags) {
+		String maxspeedString = tags.getValue("maxspeed");
+
+		if (maxspeedString != null) {
+
+			Float maxspeed = ValueStringParser.parseSpeed(maxspeedString);
+			if (maxspeed != null) {
+				return maxspeed;
+			}
+
+		}
+
+		return null;
+	}
+
+	public boolean isUsable(Object propertyValue, AccessParameters accessParameters) {
+		assert propertyValue instanceof Float;
+		return true;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxweight.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxweight.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxweight.java	(revision 16520)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.WEIGHT;
+
+public class RoadMaxweight extends RoadValueLimit {
+	public RoadMaxweight() {
+		super("maxweight", WEIGHT, LimitType.MAXIMUM);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxwidth.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxwidth.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxwidth.java	(revision 16520)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.WIDTH;
+
+public class RoadMaxwidth extends RoadValueLimit {
+	public RoadMaxwidth() {
+		super("maxwidth", WIDTH, LimitType.MAXIMUM);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMinspeed.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMinspeed.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadMinspeed.java	(revision 16520)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.SPEED;
+
+public class RoadMinspeed extends RoadValueLimit {
+	public RoadMinspeed() {
+		super("minspeed", SPEED, LimitType.MINIMUM);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadPropertyType.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadPropertyType.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadPropertyType.java	(revision 16520)
@@ -0,0 +1,53 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+
+/**
+ * type of road property (as opposed to individual property values that can be identified using
+ * a RoadPropertyType's evaluate methods).
+ *
+ * RoadPropertyType objects should be stateless (except for performance speedups).
+ *
+ * @param <V>  property value type
+ */
+public interface RoadPropertyType<V> {
+
+	/**
+	 * determines the property value for way-based segments.
+	 * Uses the way the segment is created from.
+	 *
+	 * @param way               way that is to be evaluated; != null
+	 * @param forward           chooses whether the property is evaluated
+	 *                          in (true) or against (false) way direction
+	 * @param accessParameters  access parameters for properties that depend on vehicle/situation
+	 * @param dataSource        object providing access to all data; != null
+	 * @return                  value of this property for the way;
+	 *                          null if property cannot be determined / does not apply
+	 */
+	public <N, W, R> V evaluateW(W way, boolean forward,
+			AccessParameters accessParameters, DataSource<N, W, R> dataSource);
+
+	/**
+	 * determines the property value for node-based segments.
+	 * Uses the node the segment is created from.
+	 *
+	 * @param way               node that is to be evaluated; != null
+	 * @param accessParameters  access parameters for properties that depend on vehicle/situation
+	 * @param dataSource        object providing access to all data; != null
+	 * @return                  value of this property for the way;
+	 *                          null if property cannot be determined / does not apply
+	 */
+	public <N, W, R> V evaluateN(N node,
+			AccessParameters accessParameters, DataSource<N, W, R> dataSource);
+
+	/**
+	 * checks whether a segment with a given value for this property can be used by a vehicle
+	 * with a certain set of access parameters
+	 *
+	 * @param object  value of this property for the segment;
+	 *                MUST be of type V; != null
+	 */
+	public boolean isUsable(Object object, AccessParameters accessParameters);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadSurface.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadSurface.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadSurface.java	(revision 16520)
@@ -0,0 +1,40 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+public class RoadSurface implements RoadPropertyType<String> {
+
+	public <N, W, R> String evaluateN(N node, AccessParameters accessParameters,
+			DataSource<N,W,R> dataSource) {
+		return null;
+	};
+
+	public <N, W, R> String evaluateW(W way, boolean forward, AccessParameters accessParameters,
+			DataSource<N,W,R> dataSource) {
+		assert way != null && accessParameters != null && dataSource != null;
+
+		TagGroup tags = dataSource.getTagsW(way);
+		return tags.getValue("surface");
+
+	};
+
+	public boolean isUsable(Object propertyValue, AccessParameters accessParameters) {
+		assert propertyValue instanceof String;
+
+		String surface = (String)propertyValue;
+
+		Collection<String> surfaceBlacklist =
+			accessParameters.getVehiclePropertyValue(VehiclePropertyTypes.SURFACE_BLACKLIST);
+
+		if (surfaceBlacklist != null && surfaceBlacklist.contains(surface)) {
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadTracktype.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadTracktype.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadTracktype.java	(revision 16520)
@@ -0,0 +1,53 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+public class RoadTracktype implements RoadPropertyType<Integer> {
+
+	public <N, W, R> Integer evaluateN(N node, AccessParameters accessParameters,
+			DataSource<N,W,R> dataSource) {
+		return null;
+	};
+
+	public <N, W, R> Integer evaluateW(W way, boolean forward, AccessParameters accessParameters,
+			DataSource<N,W,R> dataSource) {
+		assert way != null && accessParameters != null && dataSource != null;
+
+		TagGroup tags = dataSource.getTagsW(way);
+		String tracktypeString = tags.getValue("tracktype");
+
+		if (tracktypeString != null) {
+			if        ("grade1".equals(tracktypeString)) {
+				return 1;
+			} else if ("grade2".equals(tracktypeString)) {
+				return 2;
+			} else if ("grade3".equals(tracktypeString)) {
+				return 3;
+			} else if ("grade4".equals(tracktypeString)) {
+				return 4;
+			} else if ("grade5".equals(tracktypeString)) {
+				return 5;
+			}
+		}
+
+		return null;
+	};
+
+	public boolean isUsable(Object propertyValue, AccessParameters accessParameters) {
+		assert propertyValue instanceof Integer;
+
+		int tracktype = (Integer)propertyValue;
+
+		Integer maxTracktype =
+			accessParameters.getVehiclePropertyValue(VehiclePropertyTypes.MAX_TRACKTYPE);
+
+		if (maxTracktype != null && tracktype > maxTracktype) {
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadValueLimit.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadValueLimit.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadValueLimit.java	(revision 16520)
@@ -0,0 +1,74 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.util.ValueStringParser;
+
+/**
+ * abstract superclass for road property types that define a limit for a vehicle property
+ */
+abstract public class RoadValueLimit implements RoadPropertyType<Float> {
+
+	protected static enum LimitType {MINIMUM, MAXIMUM};
+
+	private final String keyName;
+	private final VehiclePropertyType<Float> vehicleProperty;
+
+	private final LimitType upperLimit;
+
+	/**
+	 * @param keyName          key that is used to add this property to a way; value must be in
+	 *                         format readable by {@link ValueStringParser#parseOsmDecimal(String)};
+	 *                         != null
+	 * @param vehicleProperty  vehicle property that is limited by this road property; != null
+	 * @param upperLimit       type of limit; != null
+	 */
+	protected RoadValueLimit(String keyName, VehiclePropertyType<Float> vehicleProperty,
+			LimitType upperLimit) {
+		assert keyName != null && vehicleProperty != null && upperLimit != null;
+
+		this.keyName = keyName;
+		this.vehicleProperty = vehicleProperty;
+		this.upperLimit = upperLimit;
+	}
+
+	public <N, W, R> Float evaluateW(W way, boolean forward,
+			AccessParameters accessParameters, DataSource<N, W, R> dataSource) {
+		assert way != null && accessParameters != null && dataSource != null;
+		return evaluateTags(dataSource.getTagsW(way));
+	}
+
+	public <N, W, R> Float evaluateN(N node,
+			AccessParameters accessParameters, DataSource<N, W, R> dataSource) {
+		assert node != null && accessParameters != null && dataSource != null;
+		return evaluateTags(dataSource.getTagsN(node));
+	}
+
+	private final Float evaluateTags(TagGroup tags) {
+		String valueString = tags.getValue(keyName);
+		if (valueString != null) {
+			Float value = ValueStringParser.parseOsmDecimal(valueString, false);
+			return value;
+		} else {
+			return null;
+		}
+	}
+
+	public boolean isUsable(Object propertyValue, AccessParameters accessParameters) {
+		assert propertyValue instanceof Float;
+
+		Float vehicleValue = accessParameters.getVehiclePropertyValue(vehicleProperty);
+
+		if (vehicleValue != null) {
+			switch(upperLimit) {
+				case MINIMUM: return vehicleValue >= (Float) propertyValue;
+				case MAXIMUM: return vehicleValue <= (Float) propertyValue;
+				default:      throw new Error("unhandled LimitType");
+			}
+		} else {
+			return true;
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadWidth.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadWidth.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/RoadWidth.java	(revision 16520)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.WIDTH;
+
+public class RoadWidth extends RoadValueLimit {
+	public RoadWidth() {
+		super("width", WIDTH, LimitType.MAXIMUM);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/VehiclePropertyType.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/VehiclePropertyType.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/VehiclePropertyType.java	(revision 16520)
@@ -0,0 +1,19 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+/**
+ * represents an aspect of a vehicle (such as its weight or length)
+ * that can be used for comparisons with physical or legal limits of ways.
+ *
+ * VehiclePropertyType objects should be stateless (except for performance speedups).
+ *
+ * @param <V>  type of property values
+ */
+public interface VehiclePropertyType<V> {
+
+	/**
+	 * determines whether a value is valid.
+	 * null is never a valid value and must not be used as parameter.
+	 */
+	public boolean isValidValue(Object value);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/VehiclePropertyTypes.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/VehiclePropertyTypes.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/property/VehiclePropertyTypes.java	(revision 16520)
@@ -0,0 +1,73 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import java.util.Collection;
+
+/**
+ * utility class with publicly available instances of {@link VehiclePropertyType} implementations.
+ * The implementing classes themselves aren't available to ensure that only one instance exists.
+ */
+public final class VehiclePropertyTypes {
+
+	/** prevents instantiation */
+	private VehiclePropertyTypes() { }
+
+	private static final class NonnegativeFloatProperty implements VehiclePropertyType<Float> {
+		public boolean isValidValue(Object value) {
+			return value instanceof Float && (Float)value >= 0;
+		}
+	}
+
+	/** length of a vehicle in meters; negative values are invalid */
+	public static final VehiclePropertyType<Float> LENGTH = new NonnegativeFloatProperty();
+
+	/** width of a vehicle in meters; negative values are invalid */
+	public static final VehiclePropertyType<Float> WIDTH = new NonnegativeFloatProperty();
+
+	/** height of a vehicle in meters; negative values are invalid */
+	public static final VehiclePropertyType<Float> HEIGHT = new NonnegativeFloatProperty();
+
+	/** weight of a vehicle in tons; negative values are invalid */
+	public static final VehiclePropertyType<Float> WEIGHT = new NonnegativeFloatProperty();
+
+	/** axleload of a vehicle in tons; negative values are invalid */
+	public static final VehiclePropertyType<Float> AXLELOAD = new NonnegativeFloatProperty();
+
+	/** speed a vehicle can reach in km/h; negative values are invalid */
+	public static final VehiclePropertyType<Float> SPEED = new NonnegativeFloatProperty();
+
+	/** maximum incline a vehicle can go up; negative values are invalid */
+	public static final VehiclePropertyType<Float> MAX_INCLINE_UP = new NonnegativeFloatProperty();
+
+	/** maximum incline a vehicle can go down; negative values are invalid */
+	public static final VehiclePropertyType<Float> MAX_INCLINE_DOWN = new NonnegativeFloatProperty();
+
+	/** surface types ("surface" key values) the vehicle cannot use */
+	public static final VehiclePropertyType<Collection<String>> SURFACE_BLACKLIST = new VehiclePropertyType<Collection<String>>() {
+		public boolean isValidValue(Object value) {
+
+			if (!(value instanceof Collection)) {
+				return false;
+			}
+
+			for (Object contentObject : (Collection<?>)value) {
+				if (!(contentObject instanceof String)) {
+					return false;
+				}
+			}
+
+			return true;
+		}
+	};
+
+	/**
+	 * maximum tracktype grade the vehicle can use;
+	 * values are integers from = to 5
+	 * (values of key "tracktype" without "grade_" prefix, 0 is for "none")
+	 */
+	public static final VehiclePropertyType<Integer> MAX_TRACKTYPE = new VehiclePropertyType<Integer>() {
+		public boolean isValidValue(Object value) {
+			return value instanceof Integer && (Integer)value >= 0 && (Integer)value <= 5;
+		}
+	};
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/GenericTransitionStructure.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/GenericTransitionStructure.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/GenericTransitionStructure.java	(revision 16520)
@@ -0,0 +1,704 @@
+package org.openstreetmap.josm.plugins.graphview.core.transition;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessEvaluator;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessRuleset;
+import org.openstreetmap.josm.plugins.graphview.core.access.RulesetAccessEvaluator;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSourceObserver;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+
+/**
+ * generic TransitionStructure implementation using a {@link DataSource} to access OSM data
+ *
+ * @param <N>  node type
+ * @param <W>  way type
+ * @param <R>  relation type
+ */
+public class GenericTransitionStructure<N, W, R> implements TransitionStructure, DataSourceObserver {
+
+	private static final Collection<Segment> EMPTY_SEGMENT_LIST =
+		Collections.unmodifiableList(new ArrayList<Segment>(0));
+	private static final Collection<Restriction> EMPTY_RESTRICTION_COLLECTION =
+		new ArrayList<Restriction>(0);
+
+	private static class SegmentNodeImpl implements SegmentNode {
+		private final double lat;
+		private final double lon;
+		private final List<Segment> inboundSegments = new LinkedList<Segment>();
+		private final List<Segment> outboundSegments = new LinkedList<Segment>();
+		private final Map<RoadPropertyType<?>, Object> properties;
+		public SegmentNodeImpl(double lat, double lon, Map<RoadPropertyType<?>, Object> properties) {
+			assert properties != null;
+			this.lat = lat;
+			this.lon = lon;
+			this.properties = properties;
+		}
+		public double getLat() {
+			return lat;
+		}
+		public double getLon() {
+			return lon;
+		}
+		public void addInboundSegment(Segment segment) {
+			inboundSegments.add(segment);
+		}
+		public void addOutboundSegment(Segment segment) {
+			outboundSegments.add(segment);
+		}
+		public Collection<Segment> getOutboundSegments() {
+			return outboundSegments;
+		}
+		public Collection<Segment> getInboundSegments() {
+			return inboundSegments;
+		}
+
+		public <P> void setProperty(RoadPropertyType<P> property, P value) {
+			properties.put(property, value);
+		}
+		public Collection<RoadPropertyType<?>> getAvailableProperties() {
+			return properties.keySet();
+		}
+		public <P> P getPropertyValue(RoadPropertyType<P> property) {
+			@SuppressWarnings("unchecked") //cast is safe due to type parameter of setProperty
+			P result = (P) properties.get(property);
+			return result;
+		}
+		public Map<RoadPropertyType<?>, Object> getProperties() {
+			return properties;
+		}
+
+		@Override
+		public String toString() {
+			return "(" + lat + ", " + lon + ")";
+		}
+	}
+
+	private static class SegmentImpl implements Segment {
+		private final SegmentNode node1;
+		private final SegmentNode node2;
+		private final Map<RoadPropertyType<?>, Object> properties;
+		public SegmentImpl(SegmentNode node1, SegmentNode node2, Map<RoadPropertyType<?>, Object> properties) {
+			this.node1 = node1;
+			this.node2 = node2;
+			this.properties = properties;
+		}
+		public SegmentNode getNode1() {
+			return node1;
+		}
+		public SegmentNode getNode2() {
+			return node2;
+		}
+		public <P> void setProperty(RoadPropertyType<P> property, P value) {
+			properties.put(property, value);
+		}
+		public Collection<RoadPropertyType<?>> getAvailableProperties() {
+			return properties.keySet();
+		}
+		public <P> P getPropertyValue(RoadPropertyType<P> property) {
+			@SuppressWarnings("unchecked") //cast is safe due to type parameter of setProperty
+			P result = (P) properties.get(property);
+			return result;
+		}
+
+		@Override
+		public String toString() {
+			return "(" + node1 + "->" + node2 + ")";
+		}
+	}
+
+	private static class RestrictionImpl implements Restriction {
+		private final Segment from;
+		private final Collection<Segment> vias;
+		private final Collection<Segment> tos;
+
+		/** constructor, will directly use collection references, collections must not be changed after usage as constructor param */
+		public RestrictionImpl(Segment from, Collection<Segment> vias, Collection<Segment> tos) {
+			this.from = from;
+			this.vias = Collections.unmodifiableCollection(vias);
+			this.tos = Collections.unmodifiableCollection(tos);
+		}
+
+		public Segment getFrom() {
+			return from;
+		}
+		public Collection<Segment> getVias() {
+			return vias;
+		}
+		public Collection<Segment> getTos() {
+			return tos;
+		}
+
+		@Override
+		public String toString() {
+			return from + " -> " + vias + " -> " + tos;
+		}
+	}
+
+	private final Class<N> nodeClass;
+	private final Class<W> wayClass;
+	private final Class<R> relationClass;
+
+	private final Set<TransitionStructureObserver> observers = new HashSet<TransitionStructureObserver>();
+
+	private final Collection<RoadPropertyType<?>> properties;
+
+	private final DataSource<N, W, R> dataSource;
+
+	private AccessParameters accessParameters;
+	private AccessRuleset ruleset;
+
+	private AccessEvaluator<N, W> accessEvaluator;
+
+	private Collection<SegmentNode> nodes = null;
+	private Collection<Segment> segments = new LinkedList<Segment>();
+	private Collection<Restriction> restrictions = new LinkedList<Restriction>();
+
+	public GenericTransitionStructure(
+			Class<N> nodeClass, Class<W> wayClass, Class<R> relationClass,
+			AccessParameters accessParameters, AccessRuleset ruleset,
+			DataSource<N, W, R> dataSource,
+			Collection<RoadPropertyType<?>> properties) {
+
+		assert nodeClass != null && wayClass != null && relationClass != null;
+		assert accessParameters != null && ruleset != null;
+		assert dataSource != null;
+		assert properties != null;
+
+		this.nodeClass = nodeClass;
+		this.wayClass = wayClass;
+		this.relationClass = relationClass;
+
+		this.dataSource = dataSource;
+
+		this.properties = properties;
+
+		setAccessParametersAndRuleset(accessParameters, ruleset);
+
+		dataSource.addObserver(this);
+	}
+
+	/**
+	 * sets new access parameters and/or a new ruleset.
+	 * Causes a data update if at least one is actually changed.
+	 *
+	 * @param accessParameters  new access parameters, null indicates no change
+	 * @param ruleset           new ruleset, null indicates no change
+	 */
+	public void setAccessParametersAndRuleset(AccessParameters accessParameters, AccessRuleset ruleset) {
+
+		if (accessParameters != null) {
+			this.accessParameters = accessParameters;
+		}
+		if (ruleset != null) {
+			this.ruleset = ruleset;
+		}
+
+		if (accessParameters != null || ruleset != null) {
+
+			assert dataSource != null;
+
+			accessEvaluator = new RulesetAccessEvaluator<N, W, R>(
+					dataSource,
+					this.ruleset,
+					this.accessParameters);
+
+			updateData();
+			notifyObservers();
+
+		}
+
+	}
+
+	public Collection<SegmentNode> getNodes() {
+		return nodes;
+	}
+
+	public Collection<Segment> getSegments() {
+		return segments;
+	}
+
+	public Collection<Restriction> getRestrictions() {
+		return restrictions;
+	}
+
+	/**
+	 * creates nodes, segments and restrictions based on the data source
+	 */
+	protected void updateData() {
+
+		ArrayList<SegmentNode> nodes = new ArrayList<SegmentNode>();
+		ArrayList<Segment> segments = new ArrayList<Segment>();
+
+		Map<N, SegmentNodeImpl> nodeCreationMap = new HashMap<N, SegmentNodeImpl>();
+		Map<W, List<Segment>> waySegmentMap = new HashMap<W, List<Segment>>();
+
+		/* create segments (nodes are created only when included in a segment) */
+
+		for (W way : dataSource.getWays()) {
+			createSegmentsAndSegmentNodes(way, accessEvaluator, nodes, segments, nodeCreationMap, waySegmentMap);
+		}
+
+		nodes.trimToSize();
+		segments.trimToSize();
+
+		/* create restrictions */
+
+		Collection<Restriction> restrictions =
+			createRestrictionsFromTurnRestrictions(dataSource.getRelations(), nodeCreationMap, waySegmentMap);
+
+		restrictions.addAll(createRestrictionsFromBarrierNodes(nodeCreationMap, waySegmentMap));
+
+		/* keep data and inform observers */
+
+		this.nodes = nodes;
+		this.segments = segments;
+		this.restrictions = restrictions;
+
+		notifyObservers();
+
+	}
+
+	/**
+	 * creates all Segments and SegmentNodes for a way
+	 *
+	 * @param way                 way to create Segments and SegmentNodes from; != null
+	 * @param wayAccessEvaluator  evaluator object that decides whether way is usable; != null
+	 * @param nodes               collection of SegmentNodes, new SegmentNodes will be added here; != null
+	 * @param segments            collection of Segments, new Segments will be added here; != null
+	 * @param nodeCreationMap     map providing the SegmentNode that has been created from a Node,
+	 *                            if new SegmentNodes are created, they will be added appropriately; != null
+	 * @param waySegmentMap       map providing the Segments that have been created from a Way,
+	 *                            if new Segments are created, they will be added appropriately; != null
+	 */
+	private void createSegmentsAndSegmentNodes(W way, AccessEvaluator<N, W> wayAccessEvaluator,
+			Collection<SegmentNode> nodes, Collection<Segment> segments,
+			Map<N, SegmentNodeImpl> nodeCreationMap, Map<W, List<Segment>> waySegmentMap) {
+
+		assert way != null && wayAccessEvaluator != null && nodes != null && segments != null && nodeCreationMap != null && waySegmentMap != null;
+
+		/* calculate property values */
+
+		Map<RoadPropertyType<?>, Object> forwardPropertyValues = getWayPropertyMap(way, true);
+		Map<RoadPropertyType<?>, Object> backwardPropertyValues = getWayPropertyMap(way, false);
+
+		/* create segments from the way if it can be accessed and isn't incomplete or deleted */
+
+		boolean forwardAccess = wayAccessEvaluator.wayUsable(way, true, forwardPropertyValues);
+		boolean backwardAccess = wayAccessEvaluator.wayUsable(way, false, backwardPropertyValues);
+
+		if (forwardAccess || backwardAccess) {
+
+			if (!waySegmentMap.containsKey(way)) {
+				waySegmentMap.put(way, new LinkedList<Segment>());
+			}
+
+			/* create segments from all pairs of subsequent nodes */
+
+			N previousNode = null;
+			for (N node : dataSource.getNodes(way)) {
+				if (previousNode != null) {
+
+					SegmentNodeImpl node1 =
+						getOrCreateSegmentNodeForNode(previousNode, nodes, nodeCreationMap);
+					SegmentNodeImpl node2 =
+						getOrCreateSegmentNodeForNode(node, nodes, nodeCreationMap);
+
+					if (forwardAccess) {
+						SegmentImpl segment = new SegmentImpl(node1, node2, forwardPropertyValues);
+						segments.add(segment);
+						waySegmentMap.get(way).add(segment);
+						node1.addOutboundSegment(segment);
+						node2.addInboundSegment(segment);
+					}
+					if (backwardAccess) { //no "else if" because both can be valid
+						SegmentImpl segment = new SegmentImpl(node2, node1, backwardPropertyValues);
+						segments.add(segment);
+						waySegmentMap.get(way).add(segment);
+						node1.addInboundSegment(segment);
+						node2.addOutboundSegment(segment);
+					}
+
+				}
+				previousNode = node;
+			}
+
+		}
+	}
+
+	/**
+	 * if no segment node for a node exists in the nodeCreationMap,
+	 * creates a segment node for it and adds it to the nodeCreationMap and the nodes collection
+	 * and returns it; otherwise returns the existing segment node.
+	 */
+	private SegmentNodeImpl getOrCreateSegmentNodeForNode(N node,
+			Collection<SegmentNode> nodes, Map<N, SegmentNodeImpl> nodeCreationMap) {
+
+		SegmentNodeImpl segmentNode = nodeCreationMap.get(node);
+
+		if (segmentNode == null) {
+
+			Map<RoadPropertyType<?>, Object> nodePropertyValues = getNodePropertyMap(node);
+			segmentNode = new SegmentNodeImpl(dataSource.getLat(node), dataSource.getLon(node),
+					nodePropertyValues);
+
+			nodeCreationMap.put(node, segmentNode);
+			nodes.add(segmentNode);
+
+		}
+
+		return segmentNode;
+	}
+
+	/**
+	 * creates all Restrictions from a collection of Relations.
+	 * Only "type=restriction" relations are relevant for restrictions.
+	 *
+	 * @param relations        Relations to create Restrictions from.
+	 *                         They can have any type key, as filtering is done inside this method.
+	 * @param nodeCreationMap  map providing the SegmentNode that has been created from a Node,
+	 *                         will not be modified; != null
+	 * @param waySegmentMap    map providing the Segments that have been created from a Way,
+	 *                         will not be modified; != null
+	 * @return                 Restrictions created from the Relations; != null, but may be empty
+	 */
+	private Collection<Restriction> createRestrictionsFromTurnRestrictions(
+			Iterable<R> relations,
+			Map<N, SegmentNodeImpl> nodeCreationMap,
+			Map<W, List<Segment>> waySegmentMap) {
+
+		assert relations != null && nodeCreationMap != null && waySegmentMap != null;
+
+		Collection<Restriction> results = new LinkedList<Restriction>();
+
+		for (R relation : relations) {
+
+			TagGroup tags = dataSource.getTagsR(relation);
+
+			if ("restriction".equals(tags.getValue("type"))
+					&& tags.getValue("restriction") != null ) {
+
+				//evaluate relation
+				if (tags.getValue("restriction").startsWith("no_")) {
+					results.addAll(createRestrictionsFromRestrictionRelation(relation, true, nodeCreationMap, waySegmentMap));
+				} else if (tags.getValue("restriction").startsWith("only_")) {
+					results.addAll(createRestrictionsFromRestrictionRelation(relation, false, nodeCreationMap, waySegmentMap));
+				}
+
+			}
+		}
+
+		return results;
+	}
+
+	@SuppressWarnings("unchecked") //several generic casts that are checked with isInstance
+	private Collection<Restriction> createRestrictionsFromRestrictionRelation(
+			R relation,
+			boolean restrictive,
+			Map<N, SegmentNodeImpl> nodeCreationMap,
+			Map<W, List<Segment>> waySegmentMap) {
+
+		assert relationClass.isInstance(relation);
+
+		/* collect information about the relation */
+
+		W fromWay = null;
+		Collection<N> viaNodes = new LinkedList<N>();
+		Collection<W> viaWays = new LinkedList<W>();
+		Collection<W> toWays = new LinkedList<W>();
+
+		for (DataSource.RelationMember member : dataSource.getMembers(relation)) {
+
+			if ("from".equals(member.getRole())) {
+				if (fromWay != null || !wayClass.isInstance(member.getMember())) {
+					//broken restriction
+					return EMPTY_RESTRICTION_COLLECTION;
+				} else {
+					fromWay = (W)member.getMember();
+				}
+			} else if ("to".equals(member.getRole())) {
+				if (!wayClass.isInstance(member.getMember())) {
+					//broken restriction
+					return EMPTY_RESTRICTION_COLLECTION;
+				} else {
+					toWays.add((W)member.getMember());
+				}
+			} else if ("via".equals(member.getRole())) {
+				if (wayClass.isInstance(member.getMember())) {
+					viaWays.add((W)member.getMember());
+				} else if (nodeClass.isInstance(member.getMember())) {
+					viaNodes.add((N)member.getMember());
+				}
+			}
+
+		}
+
+		if (fromWay != null && toWays.size() > 0 &&
+				(viaNodes.size() > 0 || viaWays.size() > 0)) {
+
+			return createRestrictionsFromRestrictionRelationMembers(
+					restrictive, nodeCreationMap, waySegmentMap,
+					fromWay, viaNodes, viaWays, toWays);
+
+		} else {
+			return new ArrayList<Restriction>(0);
+		}
+	}
+
+	private Collection<Restriction> createRestrictionsFromRestrictionRelationMembers(
+			boolean restrictive,
+			Map<N, SegmentNodeImpl> nodeCreationMap, Map<W, List<Segment>> waySegmentMap,
+			W fromWay, Collection<N> viaNodes, Collection<W> viaWays, Collection<W> toWays) {
+
+		Collection<SegmentNode> nodesCreatedFromViaNodes = new ArrayList<SegmentNode>(viaNodes.size());
+		for (N viaNode : viaNodes) {
+			if (nodeCreationMap.containsKey(viaNode)) {
+				nodesCreatedFromViaNodes.add(nodeCreationMap.get(viaNode));
+			}
+		}
+
+		/* check completeness of restriction to avoid dealing with incomplete restriction info */
+
+		if (!waySegmentMap.containsKey(fromWay)) {
+			//broken restriction
+			return EMPTY_RESTRICTION_COLLECTION;
+		}
+
+		for (W viaWay : viaWays) {
+			if (!waySegmentMap.containsKey(viaWay)) {
+				//broken restriction
+				return EMPTY_RESTRICTION_COLLECTION;
+			}
+		}
+
+		for (W toWay : toWays) {
+			if (!waySegmentMap.containsKey(toWay)) {
+				//broken restriction
+				return EMPTY_RESTRICTION_COLLECTION;
+			}
+		}
+
+		/* find all via segments:
+		 * via segments are segments created from via ways
+		 * or segments starting and ending with nodes created from via nodes */
+
+		ArrayList<Segment> viaSegments = new ArrayList<Segment>();
+
+		for (W viaWay : viaWays) {
+			viaSegments.addAll(waySegmentMap.get(viaWay));
+		}
+
+		for (SegmentNode nodeCreatedFromViaNode : nodesCreatedFromViaNodes) {
+			for (Segment segment : nodeCreatedFromViaNode.getOutboundSegments()) {
+				if (nodesCreatedFromViaNodes.contains(segment.getNode2())) {
+					viaSegments.add(segment);
+				}
+			}
+		}
+
+		viaSegments.trimToSize();
+
+		/* create a set with all nodes that are based on via members */
+
+		Set<SegmentNode> nodesCreatedFromViaMembers
+		= new HashSet<SegmentNode>(nodesCreatedFromViaNodes);
+
+		for (W viaWay : viaWays) {
+			for (N viaWayNode : dataSource.getNodes(viaWay)) {
+				nodesCreatedFromViaMembers.add(nodeCreationMap.get(viaWayNode));
+			}
+		}
+
+		/*
+		 * find from segment and to segments:
+		 * Such a segment contains a node based on a via member.
+		 * Each way should contain only one possible segment
+		 * connecting to via members (due to splitting).
+		 */
+
+		Segment fromSegment = null;
+		Collection<Segment> toSegments = new ArrayList<Segment>();
+
+		for (Segment possibleFromSegment : waySegmentMap.get(fromWay)) {
+			if (nodesCreatedFromViaMembers.contains(possibleFromSegment.getNode2())) {
+
+				if (fromSegment == null) {
+					fromSegment = possibleFromSegment;
+				} else {
+					//broken restriction
+					return EMPTY_RESTRICTION_COLLECTION;
+				}
+
+			}
+		}
+		if (fromSegment == null) {
+			//broken restriction
+			return EMPTY_RESTRICTION_COLLECTION;
+		}
+
+		if (restrictive) {
+
+			for (W toWay : toWays) {
+				if (waySegmentMap.containsKey(toWay)) {
+					Segment toSegment = null;
+					for (Segment possibleToSegment : waySegmentMap.get(toWay)) {
+						if (nodesCreatedFromViaMembers.contains(possibleToSegment.getNode1())) {
+
+							if (toSegment == null) {
+								toSegment = possibleToSegment;
+							} else {
+								//broken restriction
+								return EMPTY_RESTRICTION_COLLECTION;
+							}
+
+						}
+					}
+					if (toSegment == null) {
+						//broken restriction
+						return EMPTY_RESTRICTION_COLLECTION;
+					} else {
+						toSegments.add(toSegment);
+					}
+				}
+			}
+
+		} else { //!restrictive
+
+			/* forbidden "to" segments are all segments that start at a "via" node
+			 * and are neither a via segment nor created from an allowed "to" way */
+
+			for (SegmentNode toStartingNode : nodesCreatedFromViaMembers) {
+				for (Segment outboundSegment : toStartingNode.getOutboundSegments()) {
+
+					if (!viaSegments.contains(outboundSegment)) {
+
+						boolean isAllowed = false;
+
+						for (W toWay : toWays) {
+							if (waySegmentMap.get(toWay).contains(outboundSegment)) {
+								isAllowed = true;
+								break;
+							}
+						}
+
+						if (!isAllowed) {
+							toSegments.add(outboundSegment);
+						}
+
+					}
+
+				}
+			}
+
+		}
+
+		/* create restriction */
+
+		Collection<Restriction> results = new ArrayList<Restriction>(1);
+		results.add(new RestrictionImpl(fromSegment, viaSegments, toSegments));
+		return results;
+	}
+
+	/**
+	 * creates Restrictions from barrier nodes (nodes that are considered impassable by the
+	 * {@link #accessEvaluator}). These restrictions prevent moving from a segment before the
+	 * barrier node to a segment after the barrier node.
+	 *
+	 * @param nodeCreationMap  map providing the SegmentNode that has been created from a node,
+	 *                         will not be modified; != null
+	 * @param waySegmentMap    map providing the Segments that have been created from a way,
+	 *                         will not be modified; != null
+	 * @return                 Restrictions created from barrier nodes; != null, but may be empty
+	 */
+	private Collection<Restriction> createRestrictionsFromBarrierNodes(
+			Map<N, SegmentNodeImpl> nodeCreationMap,
+			Map<W, List<Segment>> waySegmentMap) {
+
+		assert nodeCreationMap != null;
+		assert waySegmentMap != null;
+
+		Collection<Restriction> results = new LinkedList<Restriction>();
+
+		for (N node : nodeCreationMap.keySet()) {
+
+			if (!accessEvaluator.nodeUsable(node, nodeCreationMap.get(node).getProperties())) {
+
+				SegmentNode barrierNode = nodeCreationMap.get(node);
+
+				for (Segment inboundSegment : barrierNode.getInboundSegments()) {
+					for (Segment outboundSegment : barrierNode.getOutboundSegments()) {
+						results.add(new RestrictionImpl(inboundSegment, EMPTY_SEGMENT_LIST, Arrays.asList(outboundSegment)));
+					}
+				}
+
+			}
+
+		}
+
+		return results;
+	}
+
+	/**
+	 * determines the values of all RoadPropertyTypes from {@link #properties} for a way and
+	 * creates a map with the types that have non-null values as keys, property values as content
+	 */
+	private Map<RoadPropertyType<?>, Object> getWayPropertyMap(W way, boolean forward) {
+		Map<RoadPropertyType<?>, Object> propertyValues;
+		propertyValues = new HashMap<RoadPropertyType<?>, Object>();
+		for (RoadPropertyType<?> property : properties) {
+			Object value = property.evaluateW(way, forward, accessParameters, dataSource);
+			if (value != null) {
+				propertyValues.put(property, value);
+			}
+		}
+		return propertyValues;
+	}
+
+	/**
+	 * determines the values of all RoadPropertyTypes from {@link #properties} for a node and
+	 * creates a map with the types that have non-null values as keys, property values as content
+	 */
+	private Map<RoadPropertyType<?>, Object> getNodePropertyMap(N node) {
+		Map<RoadPropertyType<?>, Object> propertyValues;
+		propertyValues = new HashMap<RoadPropertyType<?>, Object>();
+		for (RoadPropertyType<?> property : properties) {
+			Object value = property.evaluateN(node, accessParameters, dataSource);
+			if (value != null) {
+				propertyValues.put(property, value);
+			}
+		}
+		return propertyValues;
+	}
+
+	public void update(DataSource<?, ?, ?> dataSource) {
+		assert this.dataSource == dataSource;
+		updateData();
+	}
+
+	public void addObserver(TransitionStructureObserver observer) {
+		observers.add(observer);
+	}
+
+	public void deleteObserver(TransitionStructureObserver observer) {
+		observers.remove(observer);
+	}
+
+	protected void notifyObservers() {
+		for (TransitionStructureObserver observer : observers) {
+			observer.update(this);
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/Restriction.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/Restriction.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/Restriction.java	(revision 16520)
@@ -0,0 +1,34 @@
+package org.openstreetmap.josm.plugins.graphview.core.transition;
+
+import java.util.Collection;
+
+/**
+ * physical or legal restriction that forbids entering and leaving a set
+ * of {@link Segment}s using certain in- and outbound segments
+ * despite those Segments being connected by sharing nodes
+ */
+public interface Restriction {
+
+	/**
+	 * returns the starting segment that will trigger the restriction when used;
+	 * != null
+	 */
+	public Segment getFrom();
+
+	/**
+	 * returns the "via" segments.
+	 * The restriction will remain active as long as only via segments are used.
+	 *
+	 * @return  unmodifiable collection of segments; != null
+	 */
+	public Collection<Segment> getVias();
+
+	/**
+	 * returns the forbidden "to" segments.
+	 * The restriction prevents leaving the via segment set by using one of the to segments.
+	 *
+	 * @return  unmodifiable collection of segments; != null
+	 */
+	public Collection<Segment> getTos();
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/Segment.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/Segment.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/Segment.java	(revision 16520)
@@ -0,0 +1,18 @@
+package org.openstreetmap.josm.plugins.graphview.core.transition;
+
+/**
+ * connection between two {@link SegmentNode}s in a {@link TransitionStructure}
+ */
+public interface Segment extends TransitionStructureElement {
+
+	/**
+	 * returns the node this segment starts at; != null
+	 */
+	public SegmentNode getNode1();
+
+	/**
+	 * returns the node this segment leads to; != null
+	 */
+	public SegmentNode getNode2();
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/SegmentNode.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/SegmentNode.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/SegmentNode.java	(revision 16520)
@@ -0,0 +1,22 @@
+package org.openstreetmap.josm.plugins.graphview.core.transition;
+
+import java.util.Collection;
+
+/**
+ * node in a {@link TransitionStructure}
+ */
+public interface SegmentNode extends TransitionStructureElement {
+
+	/** returns the node's latitude */
+	public double getLat();
+
+	/** returns the node's longitude */
+	public double getLon();
+
+	/** returns all segments that end at this node */
+	Collection<Segment> getInboundSegments();
+
+	/** returns all segments that start at this node */
+	Collection<Segment> getOutboundSegments();
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructure.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructure.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructure.java	(revision 16520)
@@ -0,0 +1,31 @@
+package org.openstreetmap.josm.plugins.graphview.core.transition;
+
+import java.util.Collection;
+
+/**
+ * graph-like structure for transition between OSM node/way/relation representation
+ * and the WayGraph. It consists of Nodes, Segments, and Restrictions.
+ */
+public interface TransitionStructure {
+
+	public Collection<SegmentNode> getNodes();
+	public Collection<Segment> getSegments();
+	public Collection<Restriction> getRestrictions();
+
+	/**
+	 * adds an observer.
+	 * Does nothing if the parameter is already an observer of this TransitionStructure.
+	 *
+	 * @param observer  observer object, != null
+	 */
+	public void addObserver(TransitionStructureObserver observer);
+
+	/**
+	 * deletes an observer that has been added using {@link #addObserver(TransitionStructureObserver)}.
+	 * Does nothing if the parameter isn't currently an observer of this TransitionStructure.
+	 *
+	 * @param observer  observer object, != null
+	 */
+	public void deleteObserver(TransitionStructureObserver observer);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructureElement.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructureElement.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructureElement.java	(revision 16520)
@@ -0,0 +1,27 @@
+package org.openstreetmap.josm.plugins.graphview.core.transition;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+
+/**
+ * superinterface for {@link SegmentNode} and {@link Segment}; contains property related methods
+ */
+public interface TransitionStructureElement {
+
+	/**
+	 * returns the types of this object's properties
+	 *
+	 * @return  property type collection; != null
+	 */
+	public Collection<RoadPropertyType<?>> getAvailableProperties();
+
+	/**
+	 * returns the value of a property for this object
+	 *
+	 * @param propertyType   property type to return value for; != null
+	 * @return property      value of the property for this segment; null if not available
+	 */
+	public <P> P getPropertyValue(RoadPropertyType<P> propertyType);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructureObserver.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructureObserver.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/transition/TransitionStructureObserver.java	(revision 16520)
@@ -0,0 +1,15 @@
+package org.openstreetmap.josm.plugins.graphview.core.transition;
+
+/**
+ * observer that will be informed about changes in a TransitionStructure
+ * if it has been registered using {@link TransitionStructure#addObserver(TransitionStructureObserver)}
+ */
+public interface TransitionStructureObserver {
+
+	/**
+	 * informs this observer about changes in an observed transition structure
+	 * @param transitionStructure  observed transition structure that has changed; != null
+	 */
+	public void update(TransitionStructure transitionStructure);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/GraphUtil.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/GraphUtil.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/GraphUtil.java	(revision 16520)
@@ -0,0 +1,52 @@
+package org.openstreetmap.josm.plugins.graphview.core.util;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.graph.WayGraph;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+
+/**
+ * utility class for calculating information about {@link WayGraph}s
+ */
+public final class GraphUtil {
+
+	/** prevents instantiation */
+	private GraphUtil() { }
+
+	/**
+	 * checks whether a node is an "end node"
+	 * (a node whose {@link SegmentNode} is connected to at most one other {@link SegmentNode})
+	 */
+	public static final boolean isEndNode(GraphNode node) {
+
+		SegmentNode ownSegmentNode = node.getSegmentNode();
+
+		SegmentNode connectedNode = null;
+
+		for (Segment inboundSegment : node.getSegmentNode().getInboundSegments()) {
+			SegmentNode otherSegmentNode = inboundSegment.getNode1();
+			if (otherSegmentNode != ownSegmentNode) {
+				if (connectedNode == null) {
+					connectedNode = otherSegmentNode;
+				} else if (connectedNode != otherSegmentNode) {
+					return false;
+				}
+			}
+		}
+
+		for (Segment outboundSegment : node.getSegmentNode().getOutboundSegments()) {
+			SegmentNode otherSegmentNode = outboundSegment.getNode2();
+			if (otherSegmentNode != ownSegmentNode) {
+				if (connectedNode == null) {
+					connectedNode = otherSegmentNode;
+				} else if (connectedNode != otherSegmentNode) {
+					return false;
+				}
+			}
+		}
+
+		return true;
+
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/TagCondition.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/TagCondition.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/TagCondition.java	(revision 16520)
@@ -0,0 +1,18 @@
+package org.openstreetmap.josm.plugins.graphview.core.util;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+/**
+ * condition for a collection of tags (such as "contains highway=*, but not access=no").
+ * Used for implications.
+ */
+public interface TagCondition {
+
+	/**
+	 * returns true if the tags match the condition
+	 *
+	 * @param tags  tags to check against the condition; != null
+	 */
+	boolean matches(TagGroup tags);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/TagConditionLogic.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/TagConditionLogic.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/TagConditionLogic.java	(revision 16520)
@@ -0,0 +1,203 @@
+package org.openstreetmap.josm.plugins.graphview.core.util;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+/**
+ * utility class for creating and combining TagCondition objects.
+ */
+public final class TagConditionLogic {
+
+	/** prevents instantiation */
+	private TagConditionLogic(){ }
+
+	/**
+	 * creates a condition that is fulfilled if the set of tags contains a given tag
+	 *
+	 * @param tag  tag that must be in the tag collection; != null
+	 */
+	public static TagCondition tag(final Tag tag) {
+		assert tag != null;
+		return new TagCondition() {
+			public boolean matches(TagGroup tags) {
+				return tags.contains(tag);
+			}
+			@Override
+			public String toString() {
+				return tag.toString();
+			}
+		};
+	}
+
+	/**
+	 * creates a condition that is fulfilled if the set of tags contains a tag with the given key
+	 *
+	 * @param key  the key to look for; != null
+	 */
+	public static TagCondition key(final String key) {
+		assert key != null;
+		return new TagCondition() {
+			public boolean matches(TagGroup tags) {
+				return tags.containsKey(key);
+			}
+			@Override
+			public String toString() {
+				return key;
+			}
+		};
+	}
+
+	/**
+	 * combines conditions using a boolean "and"
+	 *
+	 * @param condition   first condition; != null
+	 * @param conditions  more conditions; each != null
+	 */
+	public static TagCondition and(final TagCondition condition, final TagCondition... conditions) {
+		return new TagCondition() {
+			public boolean matches(TagGroup tags) {
+				for (TagCondition c : conditions) {
+					if (!c.matches(tags)) {
+						return false;
+					}
+				}
+				return condition.matches(tags);
+			}
+			@Override
+			public String toString() {
+				StringBuilder result = new StringBuilder();
+				result.append("(");
+				result.append(condition);
+				for (TagCondition c : conditions) {
+					result.append(" && ");
+					result.append(c);
+				}
+				result.append(")");
+				return result.toString();
+			}
+		};
+	}
+
+	/**
+	 * combines conditions using a boolean "and"
+	 *
+	 * @param conditions   collection of conditions, must contain at least one element; != null
+	 */
+	public static TagCondition and(final Collection<TagCondition> conditions) {
+		if (conditions.size() == 0) {
+			throw new IllegalArgumentException("collection must contain at least one condition");
+		}
+		return new TagCondition() {
+			public boolean matches(TagGroup tags) {
+				for (TagCondition c : conditions) {
+					if (!c.matches(tags)) {
+						return false;
+					}
+				}
+				return true;
+			}
+			@Override
+			public String toString() {
+				StringBuilder result = new StringBuilder();
+				result.append("(");
+				boolean firstCondition = true;
+				for (TagCondition c : conditions) {
+					if (!firstCondition) {
+						result.append(" && ");
+					}
+					firstCondition = false;
+					result.append(c);
+				}
+				result.append(")");
+				return result.toString();
+			}
+		};
+	}
+
+	/**
+	 * combines conditions using a boolean "or"
+	 *
+	 * @param condition   first condition; != null
+	 * @param conditions  more conditions; each != null
+	 */
+	public static TagCondition or(final TagCondition condition, final TagCondition... conditions) {
+		return new TagCondition() {
+			public boolean matches(TagGroup tags) {
+				for (TagCondition c : conditions) {
+					if (c.matches(tags)) {
+						return true;
+					}
+				}
+				return condition.matches(tags);
+			}
+			@Override
+			public String toString() {
+				StringBuilder result = new StringBuilder();
+				result.append("(");
+				result.append(condition);
+				for (TagCondition c : conditions) {
+					result.append(" || ");
+					result.append(c);
+				}
+				result.append(")");
+				return result.toString();
+			}
+		};
+	}
+
+	/**
+	 * combines conditions using a boolean "or"
+	 *
+	 * @param conditions   collection of conditions, must contain at least one element; != null
+	 */
+	public static TagCondition or(final Collection<TagCondition> conditions) {
+		if (conditions.size() == 0) {
+			throw new IllegalArgumentException("collection must contain at least one condition");
+		}
+		return new TagCondition() {
+			public boolean matches(TagGroup tags) {
+				for (TagCondition c : conditions) {
+					if (c.matches(tags)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			@Override
+			public String toString() {
+				StringBuilder result = new StringBuilder();
+				result.append("(");
+				boolean firstCondition = true;
+				for (TagCondition c : conditions) {
+					if (!firstCondition) {
+						result.append(" || ");
+					}
+					firstCondition = false;
+					result.append(c);
+				}
+				result.append(")");
+				return result.toString();
+			}
+		};
+	}
+
+	/**
+	 * inverts a condition
+	 *
+	 * @param condition  condition to invert, != null
+	 */
+	public static TagCondition not(final TagCondition condition) {
+		return new TagCondition() {
+			public boolean matches(TagGroup tags) {
+				return !condition.matches(tags);
+			}
+			@Override
+			public String toString() {
+				return "!" + condition;
+			}
+		};
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/ValueStringParser.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/ValueStringParser.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/util/ValueStringParser.java	(revision 16520)
@@ -0,0 +1,121 @@
+package org.openstreetmap.josm.plugins.graphview.core.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class ValueStringParser {
+
+	/** prevents instantiation */
+	private ValueStringParser() { }
+
+	/** pattern that splits into a part before and after a decimal point */
+	private static final Pattern DEC_POINT_PATTERN = Pattern.compile("^(\\-?\\d+)\\.(\\d+)$");
+
+	public static final Float parseOsmDecimal(String value, boolean allowNegative) {
+
+		/* positive integer */
+
+		try {
+
+			int weight = Integer.parseInt(value);
+			if (weight >= 0 || allowNegative) {
+				return (float)weight;
+			}
+
+		} catch (NumberFormatException nfe) {}
+
+		/* positive number with decimal point */
+
+		Matcher matcher = DEC_POINT_PATTERN.matcher(value);
+
+		if (matcher.matches()) {
+
+			String stringBeforePoint = matcher.group(1);
+			String stringAfterPoint = matcher.group(2);
+
+			if (stringBeforePoint.length() > 0 || stringAfterPoint.length() > 0) {
+
+				try {
+
+					float beforePoint = Integer.parseInt(stringBeforePoint);
+					float afterPoint = Integer.parseInt(stringAfterPoint);
+
+					double result = Math.signum(beforePoint) *
+					(Math.abs(beforePoint)
+							+ Math.pow(10, -stringAfterPoint.length()) * afterPoint);
+
+					if (result >= 0 || allowNegative) {
+						return (float)result;
+					}
+
+				} catch (NumberFormatException nfe) {}
+
+			}
+		}
+
+		return null;
+	}
+
+	private static final Pattern KMH_PATTERN = Pattern.compile("^(\\d+)\\s*km/h$");
+	private static final Pattern MPH_PATTERN = Pattern.compile("^(\\d+)\\s*mph$");
+
+	private static final float KM_PER_MILE = 1.609344f;
+
+	/**
+	 * parses a speed value given e.g. for the "maxspeed" key.
+	 *
+	 * @return  speed in km/h; null if value had syntax errors
+	 */
+	public static final Float parseSpeed(String value) {
+
+		/* try numeric speed (implied km/h) */
+
+		Float maxspeed = parseOsmDecimal(value, false);
+		if (maxspeed != null) {
+			return maxspeed;
+		}
+
+		/* try km/h speed */
+
+		Matcher kmhMatcher = KMH_PATTERN.matcher(value);
+		if (kmhMatcher.matches()) {
+			String kmhString = kmhMatcher.group(1);
+			try {
+				return (float)Integer.parseInt(kmhString);
+			} catch (NumberFormatException nfe) {}
+		}
+
+		/* try mph speed */
+
+		Matcher mphMatcher = MPH_PATTERN.matcher(value);
+		if (mphMatcher.matches()) {
+			String mphString = mphMatcher.group(1);
+			try {
+				int mph = Integer.parseInt(mphString);
+				return KM_PER_MILE * mph;
+			} catch (NumberFormatException nfe) {}
+		}
+
+		/* all possibilities failed */
+
+		return null;
+	}
+
+	private static final Pattern INCLINE_PATTERN = Pattern.compile("^(\\-?\\d+(?:\\.\\d+)?)\\s*%$");
+
+	/**
+	 * parses an incline value as given for the "incline" key.
+	 *
+	 * @return  incline in percents; null if value had syntax errors
+	 */
+	public static final Float parseIncline(String value) {
+
+		Matcher inclineMatcher = INCLINE_PATTERN.matcher(value);
+		if (inclineMatcher.matches()) {
+			String inclineString = inclineMatcher.group(1);
+			return parseOsmDecimal(inclineString, true);
+		}
+
+		return null;
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/ColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/ColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/ColorScheme.java	(revision 16520)
@@ -0,0 +1,25 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+
+/**
+ * scheme for coloring edges' segments according to some criteria
+ */
+public interface ColorScheme {
+
+	/**
+	 * returns the color to be used for a node in a WayGraph
+	 * @param edge  GraphNode to determine the color for; != null
+	 */
+	public Color getNodeColor(GraphNode node);
+
+	/**
+	 * returns the color to be used for an edge in a WayGraph
+	 * @param segment  segment to determine the color for; != null
+	 */
+	public Color getSegmentColor(Segment segment);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/DefaultNodePositioner.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/DefaultNodePositioner.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/DefaultNodePositioner.java	(revision 16520)
@@ -0,0 +1,47 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+
+/**
+ * default strategy to place nodes.
+ * Will move every node whose SegmentNode is connected to more than two segments
+ * at most 1/3 of segment length away from the original position.
+ */
+public class DefaultNodePositioner implements NodePositioner {
+
+	public LatLonCoords getPosition(GraphNode node) {
+
+		SegmentNode segmentNode = node.getSegmentNode();
+
+		if (2 >= segmentNode.getInboundSegments().size()
+				+ segmentNode.getOutboundSegments().size() ) {
+
+			return new LatLonCoords(
+					node.getSegmentNode().getLat(),
+					node.getSegmentNode().getLon());
+
+		} else {
+
+			SegmentNode node1 = node.getSegment().getNode1();
+			SegmentNode node2 = node.getSegment().getNode2();
+
+			assert segmentNode == node1 || segmentNode == node2;
+
+			LatLonCoords result;
+
+			if (segmentNode == node1) {
+				result = new LatLonCoords(
+						(2 * node1.getLat() + node2.getLat()) / 3,
+						(2 * node1.getLon() + node2.getLon()) / 3);
+			} else {
+				result = new LatLonCoords(
+						(node1.getLat() + 2 * node2.getLat()) / 3,
+						(node1.getLon() + 2 * node2.getLon()) / 3);
+			}
+
+			return result;
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/EndNodeColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/EndNodeColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/EndNodeColorScheme.java	(revision 16520)
@@ -0,0 +1,32 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.util.GraphUtil;
+
+/**
+ * color scheme that highlights end nodes
+ */
+public class EndNodeColorScheme implements ColorScheme {
+
+	private final Color nodeColor;
+	private final Color endNodeColor;
+	private final Color segmentColor;
+
+	public EndNodeColorScheme(Color nodeColor, Color endNodeColor, Color segmentColor) {
+		this.nodeColor = nodeColor;
+		this.endNodeColor = endNodeColor;
+		this.segmentColor = segmentColor;
+	}
+
+	public Color getNodeColor(GraphNode node) {
+		return GraphUtil.isEndNode(node) ? endNodeColor : nodeColor;
+	}
+
+	public Color getSegmentColor(Segment segment) {
+		return segmentColor;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/FloatPropertyColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/FloatPropertyColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/FloatPropertyColorScheme.java	(revision 16520)
@@ -0,0 +1,185 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphEdge;
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+
+/**
+ * scheme using values of a property to determine a color.
+ * Nodes are given an average color of all incoming and outgoing segments.
+ */
+public class FloatPropertyColorScheme implements ColorScheme {
+
+	private final Class<? extends RoadPropertyType<Float>> propertyClass;
+	private final Map<Float, Color> colorMap;
+	private final Color defaultColor;
+
+	/**
+	 * @param propertyClass  property type to get values for; != null
+	 * @param colorMap       map from some values to colors.
+	 *                       Colors for all other values are interpolated.
+	 *                       This map will be copied and not used directly later. != null
+	 * @param defaultColor   color that is used when the property is not available; != null
+	 */
+	public FloatPropertyColorScheme(Class<? extends RoadPropertyType<Float>> propertyClass,
+			Map<Float, Color> colorMap, Color defaultColor) {
+		assert propertyClass != null && colorMap != null && defaultColor != null;
+
+		this.propertyClass = propertyClass;
+		this.colorMap = new HashMap<Float, Color>(colorMap);
+		this.defaultColor = defaultColor;
+	}
+
+	public Color getSegmentColor(Segment segment) {
+		assert segment != null;
+
+		Float propertyValue = null;
+		Collection<RoadPropertyType<?>> availableProperties = segment.getAvailableProperties();
+		for (RoadPropertyType<?> property : availableProperties) {
+			if (propertyClass.isInstance(property)) {
+				@SuppressWarnings("unchecked") //has been checked using isInstance
+				RoadPropertyType<Float> floatProperty = (RoadPropertyType<Float>)property;
+				propertyValue = segment.getPropertyValue(floatProperty);
+				break;
+			}
+		}
+
+		if (propertyValue != null) {
+			return getColorForValue(propertyValue);
+		} else {
+			return defaultColor;
+		}
+	}
+
+	public Color getNodeColor(GraphNode node) {
+
+		List<Color> segmentColors = new ArrayList<Color>();
+
+		for (GraphEdge edge : node.getInboundEdges()) {
+			if (edge.getSegments().size() > 0) {
+				Segment firstSegment = edge.getSegments().get(0);
+				segmentColors.add(getSegmentColor(firstSegment));
+			}
+		}
+		for (GraphEdge edge : node.getOutboundEdges()) {
+			if (edge.getSegments().size() > 0) {
+				Segment lastSegment = edge.getSegments().get(edge.getSegments().size()-1);
+				segmentColors.add(getSegmentColor(lastSegment));
+			}
+		}
+
+		if (segmentColors.size() > 0) {
+			return averageColor(segmentColors);
+		} else {
+			return Color.WHITE;
+		}
+
+	}
+
+	/**
+	 * returns the color for a value
+	 * @param value  value to get color for; != null
+	 * @return       color; != null
+	 */
+	protected Color getColorForValue(Float value) {
+		assert value != null;
+
+		if (colorMap.containsKey(value)) {
+
+			return colorMap.get(value);
+
+		} else {
+
+			LinkedList<Float> valuesWithDefinedColor = new LinkedList<Float>(colorMap.keySet());
+			Collections.sort(valuesWithDefinedColor);
+
+			if (value <= valuesWithDefinedColor.getFirst()) {
+
+				return colorMap.get(valuesWithDefinedColor.getFirst());
+
+			} else if (value >= valuesWithDefinedColor.getLast()) {
+
+				return colorMap.get(valuesWithDefinedColor.getLast());
+
+			} else {
+
+				/* interpolate */
+
+				Float lowerValue = valuesWithDefinedColor.getFirst();
+				Float higherValue = null;
+
+				for (Float v : valuesWithDefinedColor) {
+					if (v >= value) {
+						higherValue = v;
+						break;
+					}
+					lowerValue = v;
+				}
+
+				assert lowerValue != null && higherValue != null;
+
+				Color lowerColor = colorMap.get(lowerValue);
+				Color higherColor = colorMap.get(higherValue);
+
+				float weightHigherColor = (value - lowerValue) / (higherValue - lowerValue);
+
+				return weightedAverageColor(lowerColor, higherColor, weightHigherColor);
+
+			}
+
+		}
+
+	}
+
+	/**
+	 * returns an average of all colors that have been passed as parameter
+	 *
+	 * @param colors  colors to calculate average from; not empty or null
+	 * @return        average color; != null
+	 */
+	private static Color averageColor(List<Color> colors) {
+		assert colors != null && colors.size() > 0;
+
+		float weightPerColor = 1.0f / colors.size();
+
+		Color average = new Color(0,0,0);
+
+		for (Color color : colors) {
+			average = new Color(
+					Math.min(Math.round(average.getRed() + weightPerColor*color.getRed()), 255),
+					Math.min(Math.round(average.getGreen() + weightPerColor*color.getGreen()), 255),
+					Math.min(Math.round(average.getBlue() + weightPerColor*color.getBlue()), 255));
+		}
+
+		return average;
+	}
+
+	/**
+	 * returns a weighted average of two colors
+	 *
+	 * @param color1        first color for the average; != null
+	 * @param color2        second color for the average; != null
+	 * @param weightColor2  weight of color2; must be in [0..1]
+	 * @return              average color; != null
+	 */
+	private static Color weightedAverageColor(Color color1, Color color2, float weightColor2) {
+		assert color1 != null && color2 != null;
+		assert 0 <= weightColor2 && weightColor2 <= 1;
+
+		return new Color(
+				Math.round((1 - weightColor2) * color1.getRed() + weightColor2 * color2.getRed()),
+				Math.round((1 - weightColor2) * color1.getGreen() + weightColor2 * color2.getGreen()),
+				Math.round((1 - weightColor2) * color1.getBlue() + weightColor2 * color2.getBlue()));
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/InclineColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/InclineColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/InclineColorScheme.java	(revision 16520)
@@ -0,0 +1,26 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadIncline;
+
+/**
+ * scheme using edge colors that depend on incline.
+ */
+public class InclineColorScheme extends FloatPropertyColorScheme {
+
+	private static final Map<Float, Color> COLOR_MAP;
+
+	static {
+		COLOR_MAP = new HashMap<Float, Color>();
+		COLOR_MAP.put(-30f, Color.BLUE);
+		COLOR_MAP.put(0f, Color.WHITE);
+		COLOR_MAP.put(30f, Color.RED);
+	}
+
+	public InclineColorScheme() {
+		super(RoadIncline.class, COLOR_MAP, Color.GRAY);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/LatLonCoords.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/LatLonCoords.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/LatLonCoords.java	(revision 16520)
@@ -0,0 +1,24 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+/**
+ * immutable representation of a latitude-longitude pair.
+ */
+public final class LatLonCoords {
+
+	private final double lat;
+	private final double lon;
+
+	public LatLonCoords(double lat, double lon) {
+		this.lat = lat;
+		this.lon = lon;
+	}
+
+	public double getLat() {
+		return lat;
+	}
+
+	public double getLon() {
+		return lon;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxheightColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxheightColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxheightColorScheme.java	(revision 16520)
@@ -0,0 +1,26 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxheight;
+
+/**
+ * scheme using edge colors that depend on maximum height.
+ */
+public class MaxheightColorScheme extends FloatPropertyColorScheme {
+
+	private static final Map<Float, Color> COLOR_MAP;
+
+	static {
+		COLOR_MAP = new HashMap<Float, Color>();
+		COLOR_MAP.put(0f, new Color(0, 0, 50));
+		COLOR_MAP.put(10f, new Color(100, 100, 255));
+		COLOR_MAP.put(30f, new Color(200, 200, 255));
+	}
+
+	public MaxheightColorScheme() {
+		super(RoadMaxheight.class, COLOR_MAP, Color.WHITE);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxspeedColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxspeedColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxspeedColorScheme.java	(revision 16520)
@@ -0,0 +1,28 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxspeed;
+
+/**
+ * scheme using edge colors that depend on maximum speed.
+ */
+public class MaxspeedColorScheme extends FloatPropertyColorScheme {
+
+	private static final Map<Float, Color> COLOR_MAP;
+
+	static {
+		COLOR_MAP = new HashMap<Float, Color>();
+		COLOR_MAP.put(0f, new Color(50, 0, 0));
+		COLOR_MAP.put(30f, Color.RED);
+		COLOR_MAP.put(60f, Color.YELLOW);
+		COLOR_MAP.put(90f, Color.GREEN);
+		COLOR_MAP.put(150f, Color.BLUE);
+	}
+
+	public MaxspeedColorScheme() {
+		super(RoadMaxspeed.class, COLOR_MAP, Color.GRAY);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxweightColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxweightColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/MaxweightColorScheme.java	(revision 16520)
@@ -0,0 +1,26 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxweight;
+
+/**
+ * scheme using edge colors that depend on maximum weight.
+ */
+public class MaxweightColorScheme extends FloatPropertyColorScheme {
+
+	private static final Map<Float, Color> COLOR_MAP;
+
+	static {
+		COLOR_MAP = new HashMap<Float, Color>();
+		COLOR_MAP.put(0f, new Color(0, 0, 50));
+		COLOR_MAP.put(20f, new Color(100, 100, 255));
+		COLOR_MAP.put(50f, new Color(200, 200, 255));
+	}
+
+	public MaxweightColorScheme() {
+		super(RoadMaxweight.class, COLOR_MAP, Color.WHITE);
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/NodePositioner.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/NodePositioner.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/NodePositioner.java	(revision 16520)
@@ -0,0 +1,13 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.graph.WayGraph;
+
+/**
+ * strategy for arranging a {@link WayGraph}'s nodes
+ */
+public interface NodePositioner {
+
+	LatLonCoords getPosition(GraphNode node);
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/NonMovingNodePositioner.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/NonMovingNodePositioner.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/NonMovingNodePositioner.java	(revision 16520)
@@ -0,0 +1,13 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+
+public class NonMovingNodePositioner implements NodePositioner {
+
+	public LatLonCoords getPosition(GraphNode node) {
+		return new LatLonCoords(
+				node.getSegmentNode().getLat(),
+				node.getSegmentNode().getLon());
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/SingleColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/SingleColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/core/visualisation/SingleColorScheme.java	(revision 16520)
@@ -0,0 +1,30 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import java.awt.Color;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+
+/**
+ * scheme giving the same color to all segments and the same color to all nodes
+ * (but possibly different colors for one of them)
+ */
+public class SingleColorScheme implements ColorScheme {
+
+	private final Color nodeColor;
+	private final Color segmentColor;
+
+	public SingleColorScheme(Color nodeColor, Color segmentColor) {
+		this.nodeColor = nodeColor;
+		this.segmentColor = segmentColor;
+	}
+
+	public Color getNodeColor(GraphNode node) {
+		return nodeColor;
+	}
+
+	public Color getSegmentColor(Segment segment) {
+		return segmentColor;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/GraphViewPlugin.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/GraphViewPlugin.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/GraphViewPlugin.java	(revision 16520)
@@ -0,0 +1,271 @@
+package org.openstreetmap.josm.plugins.graphview.plugin;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessRuleset;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessRulesetReader;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessRulesetReader.AccessRulesetSyntaxException;
+import org.openstreetmap.josm.plugins.graphview.core.graph.TSBasedWayGraph;
+import org.openstreetmap.josm.plugins.graphview.core.graph.WayGraph;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadIncline;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxaxleload;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxheight;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxlength;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxspeed;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxweight;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxwidth;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMinspeed;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadSurface;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadTracktype;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadWidth;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.DefaultNodePositioner;
+import org.openstreetmap.josm.plugins.graphview.plugin.data.JOSMTransitionStructure;
+import org.openstreetmap.josm.plugins.graphview.plugin.dialogs.GraphViewDialog;
+import org.openstreetmap.josm.plugins.graphview.plugin.dialogs.GraphViewPreferenceEditor;
+import org.openstreetmap.josm.plugins.graphview.plugin.layer.GraphViewLayer;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferences;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.InternalRuleset;
+
+/**
+ * A routing graph visualization tool for JOSM
+ */
+public class GraphViewPlugin extends Plugin implements LayerChangeListener, Observer {
+
+	private static final Collection<RoadPropertyType<?>> PROPERTIES;
+
+	static {
+		PROPERTIES = new LinkedList<RoadPropertyType<?>>();
+		PROPERTIES.add(new RoadIncline());
+		PROPERTIES.add(new RoadMaxaxleload());
+		PROPERTIES.add(new RoadMaxheight());
+		PROPERTIES.add(new RoadMaxlength());
+		PROPERTIES.add(new RoadMaxspeed());
+		PROPERTIES.add(new RoadMaxweight());
+		PROPERTIES.add(new RoadMaxwidth());
+		PROPERTIES.add(new RoadMinspeed());
+		PROPERTIES.add(new RoadSurface());
+		PROPERTIES.add(new RoadTracktype());
+		PROPERTIES.add(new RoadWidth());
+	}
+
+	private final GraphViewDialog laneDialog;
+
+	private final GraphViewPreferences preferences;
+
+	private JOSMTransitionStructure transitionStructure;
+	private GraphViewLayer graphViewLayer;
+
+	/** creates the plugin */
+	public GraphViewPlugin() {
+
+		preferences = GraphViewPreferences.getInstance();
+		this.preferences.addObserver(this);
+
+		laneDialog = new GraphViewDialog(this);
+
+	}
+
+	/** allows creation/update of GraphViewLayer */
+	public void createGraphViewLayer() {
+
+		try {
+
+			if (graphViewLayer != null) {
+
+				AccessRuleset accessRuleset = getAccessRuleset();
+
+				if (accessRuleset == null) {
+					JOptionPane.showMessageDialog(Main.parent, "No ruleset has been selected!", "No ruleset", JOptionPane.ERROR_MESSAGE);
+				} else {
+					transitionStructure.setAccessParametersAndRuleset(preferences.getCurrentParameterBookmark(), accessRuleset);
+					transitionStructure.forceUpdate();
+				}
+
+			} else {
+
+				AccessRuleset accessRuleset = getAccessRuleset();
+
+				if (accessRuleset == null) {
+					JOptionPane.showMessageDialog(Main.parent, "No ruleset has been selected!",
+							"No ruleset", JOptionPane.ERROR_MESSAGE);
+				} else {
+
+					transitionStructure = new JOSMTransitionStructure(
+							preferences.getCurrentParameterBookmark(),
+							accessRuleset,
+							PROPERTIES);
+
+					WayGraph graph = new TSBasedWayGraph(transitionStructure);
+
+					graphViewLayer = new GraphViewLayer();
+					graphViewLayer.setWayGraph(graph);
+					graphViewLayer.setColorScheme(preferences.getCurrentColorScheme());
+					graphViewLayer.setNodePositioner(new DefaultNodePositioner());
+
+					Main.main.addLayer(graphViewLayer);
+
+				}
+
+			}
+
+		} catch (AccessRulesetSyntaxException e) {
+			JOptionPane.showMessageDialog(Main.parent, "syntax exception in access ruleset:\n" + e);
+			e.printStackTrace();
+		} catch (FileNotFoundException e) {
+			JOptionPane.showMessageDialog(Main.parent, "file not found:\n" + e);
+			e.printStackTrace();
+		} catch (IOException e) {
+			JOptionPane.showMessageDialog(Main.parent, "problem when accessing a file:\n" + e);
+			e.printStackTrace();
+		}
+
+	}
+
+	/** allows update of GraphViewLayer */
+	public void updateGraphViewLayer() {
+
+		try {
+
+			if (graphViewLayer != null) {
+
+				AccessRuleset accessRuleset = getAccessRuleset();
+
+				if (accessRuleset == null) {
+					JOptionPane.showMessageDialog(Main.parent, "No ruleset has been selected!",
+							"No ruleset", JOptionPane.ERROR_MESSAGE);
+				} else {
+					transitionStructure.setAccessParametersAndRuleset(
+							preferences.getCurrentParameterBookmark(), accessRuleset);
+					transitionStructure.forceUpdate();
+				}
+
+			}
+
+		} catch (AccessRulesetSyntaxException e) {
+			JOptionPane.showMessageDialog(Main.parent, "syntax exception in access ruleset:\n" + e);
+			e.printStackTrace();
+		} catch (FileNotFoundException e) {
+			JOptionPane.showMessageDialog(Main.parent, "file not found:\n" + e);
+			e.printStackTrace();
+		} catch (IOException e) {
+			JOptionPane.showMessageDialog(Main.parent, "problem when accessing a file:\n" + e);
+			e.printStackTrace();
+		}
+
+	}
+
+	/** repaints the GraphViewLayer without recalculating the graph (visual update) */
+	public void repaintGraphViewLayer() {
+
+		if (graphViewLayer != null) {
+			Main.panel.repaint();
+		}
+
+	}
+
+	/**
+	 * @return ruleset read from a source as specified by preferences, null if the preferences
+	 *         don't specify a ruleset source
+	 * @throws AccessRulesetSyntaxException
+	 * @throws IOException
+	 * @throws FileNotFoundException
+	 */
+	private AccessRuleset getAccessRuleset()
+	throws AccessRulesetSyntaxException, IOException, FileNotFoundException {
+
+		InputStream rulesetInputStream;
+
+		if (preferences.getUseInternalRulesets()) {
+
+			InternalRuleset ruleset = preferences.getCurrentInternalRuleset();
+
+			if (ruleset == null) {
+				return null;
+			}
+
+			ClassLoader classLoader = this.getClass().getClassLoader();
+			URL rulesetURL = classLoader.getResource(ruleset.getResourceName());
+
+			if (rulesetURL != null) {
+				rulesetInputStream = rulesetURL.openStream();
+			} else {
+				throw new FileNotFoundException("couldn't find built-in ruleset " + ruleset);
+			}
+
+		} else {
+
+			File rulesetFile = preferences.getCurrentRulesetFile();
+
+			if (rulesetFile == null) {
+				return null;
+			}
+
+			rulesetInputStream = new FileInputStream(rulesetFile);
+
+		}
+
+		return AccessRulesetReader.readAccessRuleset(rulesetInputStream);
+
+	}
+
+	@Override
+	public PreferenceSetting getPreferenceSetting() {
+		return new GraphViewPreferenceEditor();
+	}
+
+	@Override
+	public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+		if (newFrame != null) {
+			newFrame.addToggleDialog(laneDialog);
+			Layer.listeners.add(this);
+		} else {
+			Layer.listeners.remove(this);
+		}
+	}
+
+	public void layerRemoved(Layer oldLayer) {
+		if (oldLayer == graphViewLayer) {
+			graphViewLayer = null;
+		} else if (oldLayer == Main.map.mapView.getEditLayer()) { //data layer removed
+			if (graphViewLayer != null) {
+				Main.map.mapView.removeLayer(graphViewLayer);
+				graphViewLayer = null;
+			}
+		}
+	}
+
+	public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+		//do nothing
+	}
+
+	public void layerAdded(Layer newLayer) {
+		//do nothing
+	}
+
+	public void update(Observable arg0, Object arg1) {
+		if (arg0 == preferences) {
+			if (graphViewLayer != null) {
+				graphViewLayer.setColorScheme(preferences.getCurrentColorScheme());
+			}
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/data/JOSMDataSource.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/data/JOSMDataSource.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/data/JOSMDataSource.java	(revision 16520)
@@ -0,0 +1,202 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.data;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSourceObserver;
+import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+/**
+ * DataSource that gets data from JOSM;
+ * this DataSource type does not send updates!
+ */
+
+public class JOSMDataSource implements DataSource<Node, Way, Relation> {
+
+	public double getLat(Node node) {
+		return node.getCoor().lat();
+	}
+
+	public double getLon(Node node) {
+		return node.getCoor().lon();
+	}
+
+	public Iterable<RelationMember> getMembers(Relation relation) {
+		List<RelationMember> members = new ArrayList<RelationMember>(relation.members.size());
+		for (org.openstreetmap.josm.data.osm.RelationMember member : relation.members) {
+			if (!member.member.deleted && !member.member.incomplete) {
+				members.add(new RelationMemberImpl(member));
+			}
+		}
+		return members;
+	}
+
+	public Iterable<Node> getNodes(Way way) {
+		return new FilteredOsmPrimitiveIterable<Node>(way.nodes);
+	}
+
+	public Iterable<Node> getNodes() {
+		return new FilteredOsmPrimitiveIterable<Node>(Main.ds.nodes);
+	}
+
+	public Iterable<Relation> getRelations() {
+		return new FilteredRelationIterable(Main.ds.relations);
+	}
+
+	public Iterable<Way> getWays() {
+		return new FilteredOsmPrimitiveIterable<Way>(Main.ds.ways);
+	}
+
+	public TagGroup getTagsN(Node node) {
+		return getTags(node);
+	}
+
+	public TagGroup getTagsW(Way way) {
+		return getTags(way);
+	}
+
+	public TagGroup getTagsR(Relation relation) {
+		return getTags(relation);
+	}
+
+	private TagGroup getTags(OsmPrimitive primitive) {
+		if (primitive.keys == null) {
+			return EMPTY_TAG_GROUP;
+		} else {
+			return new MapBasedTagGroup(primitive.keys);
+		}
+	}
+
+	private static final TagGroup EMPTY_TAG_GROUP;
+	static {
+		Map<String, String> emptyMap = new HashMap<String, String>(0);
+		EMPTY_TAG_GROUP = new MapBasedTagGroup(emptyMap);
+	}
+
+	/**
+	 * Iterable of OsmPrimitive objects based on an existing Iterable,
+	 * will filter incomplete and deleted objects from the iterator.
+	 *
+	 * @param <P>  OsmPrimitive subtype
+	 */
+	public static class FilteredOsmPrimitiveIterable<P extends OsmPrimitive> implements Iterable<P> {
+
+		private final Iterable<P> originalIterable;
+
+		public FilteredOsmPrimitiveIterable(Iterable<P> originalIterable) {
+			this.originalIterable = originalIterable;
+		}
+
+		/** returns an iterator. The iterator does not support {@link Iterator#remove()}. */
+		public Iterator<P> iterator() {
+			return new FilteredIterator(originalIterable.iterator());
+		}
+
+		private class FilteredIterator implements Iterator<P> {
+
+			private final Iterator<P> originalIterator;
+
+			private P next;
+
+			public FilteredIterator(Iterator<P> originalIterator) {
+				this.originalIterator = originalIterator;
+				updateNext();
+			}
+
+			public boolean hasNext() {
+				return next != null;
+			}
+
+			public P next() {
+				if (next != null) {
+					P result = next;
+					updateNext();
+					return result;
+				} else {
+					throw new NoSuchElementException();
+				}
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+
+			private void updateNext() {
+				if (originalIterator.hasNext()) {
+					next = originalIterator.next();
+					if (!accept(next)) {
+						updateNext();
+					}
+				} else {
+					next = null;
+				}
+			}
+
+		}
+
+		protected boolean accept(P primitive) {
+			return !primitive.deleted && !primitive.incomplete;
+		}
+	}
+
+	/**
+	 * Relation-specific variant of the FilteredOsmPrimitiveIterable,
+	 * also checks completeness of relation's members
+	 */
+	public static class FilteredRelationIterable extends FilteredOsmPrimitiveIterable<Relation> {
+
+		public FilteredRelationIterable(Iterable<Relation> originalIterable) {
+			super(originalIterable);
+		}
+
+		@Override
+		protected boolean accept(Relation relation) {
+			boolean complete = true;
+			for (org.openstreetmap.josm.data.osm.RelationMember member : relation.members) {
+				if (member.member == null || member.member.deleted || member.member.incomplete) {
+					complete = false;
+				}
+			}
+			return complete && super.accept(relation);
+		}
+	}
+
+	public static class RelationMemberImpl implements RelationMember {
+		private final String role;
+		private final Object member;
+		public RelationMemberImpl(org.openstreetmap.josm.data.osm.RelationMember originalMember) {
+			this.role = originalMember.role;
+			this.member = originalMember.member;
+		}
+		public String getRole() {
+			return role;
+		}
+		public Object getMember() {
+			return member;
+		}
+	}
+
+	private final Set<DataSourceObserver> observers = new HashSet<DataSourceObserver>();
+
+	public void addObserver(DataSourceObserver observer) {
+		observers.add(observer);
+	}
+
+	public void deleteObserver(DataSourceObserver observer) {
+		observers.remove(observer);
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/data/JOSMTransitionStructure.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/data/JOSMTransitionStructure.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/data/JOSMTransitionStructure.java	(revision 16520)
@@ -0,0 +1,35 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.data;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessRuleset;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.transition.GenericTransitionStructure;
+
+/**
+ * transition structure that retrieves data from a {@link JOSMDataSource}
+ */
+public class JOSMTransitionStructure extends GenericTransitionStructure<Node, Way, Relation> {
+
+	private static final JOSMDataSource DATA_SOURCE = new JOSMDataSource();
+
+	public JOSMTransitionStructure(AccessParameters accessParameters, AccessRuleset ruleset,
+			Collection<RoadPropertyType<?>> properties) {
+
+		super(Node.class, Way.class, Relation.class,
+				accessParameters, ruleset,
+				DATA_SOURCE,
+				properties);
+
+	}
+
+	/** causes an update (as if the DataSource had noticed a change) */
+	public void forceUpdate() {
+		super.update(DATA_SOURCE);
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/AccessParameterDialog.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/AccessParameterDialog.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/AccessParameterDialog.java	(revision 16520)
@@ -0,0 +1,461 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.dialogs;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.MAX_INCLINE_DOWN;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.MAX_INCLINE_UP;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.MAX_TRACKTYPE;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.SURFACE_BLACKLIST;
+
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.PreferenceAccessParameters;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.VehiclePropertyStringParser.PropertyValueSyntaxException;
+
+public class AccessParameterDialog extends JDialog {
+
+	public static interface BookmarkAction {
+		public void execute(String name, PreferenceAccessParameters parameters);
+	}
+
+	/**
+	 * map that contains all float value vehicle properties (as those can be treated uniformly)
+	 * and their labels
+	 */
+	private static final Map<VehiclePropertyType<Float>, String> FLOAT_PROPERTIES;
+
+	static {
+		FLOAT_PROPERTIES = new LinkedHashMap<VehiclePropertyType<Float>, String>();
+		FLOAT_PROPERTIES.put(VehiclePropertyTypes.HEIGHT, "height (m)");
+		FLOAT_PROPERTIES.put(VehiclePropertyTypes.WIDTH, "width (m)");
+		FLOAT_PROPERTIES.put(VehiclePropertyTypes.LENGTH, "length (m)");
+		FLOAT_PROPERTIES.put(VehiclePropertyTypes.SPEED, "speed (km/h)");
+		FLOAT_PROPERTIES.put(VehiclePropertyTypes.WEIGHT, "weight (t)");
+		FLOAT_PROPERTIES.put(VehiclePropertyTypes.AXLELOAD, "axleload (t)");
+	}
+
+	private static final Collection<Character> FORBIDDEN_CHARS =
+		Arrays.asList(',', ';', '{', '}', '=', '|');
+
+	private class BookmarkNamePanel extends JPanel {
+
+		private final JTextField bookmarkNameTextField;
+
+		public BookmarkNamePanel(String initialName) {
+			super();
+			this.setBorder(BorderFactory.createTitledBorder("bookmark name"));
+
+			this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+			bookmarkNameTextField = new JTextField(initialName);
+			this.add(bookmarkNameTextField);
+
+		}
+
+		public String getBookmarkName() {
+
+			String name = bookmarkNameTextField.getText();
+
+			if (existingBookmarkNames.contains(name)) {
+				JOptionPane.showMessageDialog(this, "Bookmark name already exists!");
+				return null;
+			}
+
+			for (char nameChar : name.toCharArray()) {
+				if (FORBIDDEN_CHARS.contains(nameChar)) {
+					JOptionPane.showMessageDialog(this, "Bookmark name must not contain '" +
+							nameChar + "'!");
+					return null;
+				}
+			}
+
+			return name;
+		}
+	}
+
+	private static class AccessClassPanel extends JPanel {
+
+		private final JTextField accessClassTextField;
+
+		public AccessClassPanel(PreferenceAccessParameters initialParameters) {
+			super();
+			this.setBorder(BorderFactory.createTitledBorder("access class"));
+
+			this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+			accessClassTextField = new JTextField(initialParameters.getAccessClass());
+			this.add(accessClassTextField);
+
+		}
+
+		public String getAccessClass() {
+
+			String name = accessClassTextField.getText();
+
+			for (char nameChar : name.toCharArray()) {
+				if (FORBIDDEN_CHARS.contains(nameChar)) {
+					JOptionPane.showMessageDialog(this, "Access class must not contain '" +
+							nameChar + "'!");
+					return null;
+				}
+			}
+
+			return name;
+		}
+	}
+
+	private static class AccessTypesPanel extends JPanel {
+
+		private static final int COLS = 4;
+
+		private final Map<AccessType, JCheckBox> accessTypeCheckBoxes =
+			new EnumMap<AccessType, JCheckBox>(AccessType.class);
+
+		public AccessTypesPanel(PreferenceAccessParameters initialParameters) {
+			super();
+			this.setBorder(BorderFactory.createTitledBorder("access types"));
+
+			this.setLayout(
+					new GridLayout(((COLS-1 + AccessType.values().length) / COLS), COLS));
+
+			for (AccessType accessType : AccessType.values()) {
+				JCheckBox checkBox = new JCheckBox(accessType.toString());
+				checkBox.setSelected(initialParameters.getAccessTypeUsable(accessType));
+				accessTypeCheckBoxes.put(accessType, checkBox);
+				this.add(checkBox);
+			}
+
+		}
+
+		public Collection<AccessType> getUsableAccessTypes() {
+
+			Collection<AccessType> usableAccessTypes = new LinkedList<AccessType>();
+
+			for (AccessType accessType : AccessType.values()) {
+				if (accessTypeCheckBoxes.get(accessType).isSelected()) {
+					usableAccessTypes.add(accessType);
+				}
+			}
+
+			return usableAccessTypes;
+		}
+	}
+
+	private static class VehiclePropertiesPanel extends JPanel {
+
+		private static final int COLS = 2;
+
+		private final Map<VehiclePropertyType<Float>, JTextField> floatPropertyTextFields =
+			new HashMap<VehiclePropertyType<Float>, JTextField>();
+
+		public VehiclePropertiesPanel(PreferenceAccessParameters initialParameters) {
+			super();
+			this.setBorder(BorderFactory.createTitledBorder("vehicle properties"));
+
+			this.setLayout(new GridLayout(((COLS-1 + FLOAT_PROPERTIES.size()) / COLS),
+					2*COLS));
+
+			for (VehiclePropertyType<Float> vehicleProperty : FLOAT_PROPERTIES.keySet()) {
+
+				JLabel label = new JLabel(FLOAT_PROPERTIES.get(vehicleProperty));
+				this.add(label);
+
+				JTextField textField = new JTextField();
+
+				String vehiclePropertyString =
+					initialParameters.getVehiclePropertyString(vehicleProperty);
+				if (vehiclePropertyString != null) {
+					textField.setText(vehiclePropertyString);
+				}
+
+				floatPropertyTextFields.put(vehicleProperty, textField);
+				this.add(textField);
+			}
+
+		}
+
+		public Map<VehiclePropertyType<?>, String> getVehiclePropertyStrings() {
+
+			Map<VehiclePropertyType<?>, String> vehiclePropertyStrings =
+				new HashMap<VehiclePropertyType<?>, String>();
+
+			for (VehiclePropertyType<Float> vehicleProperty : floatPropertyTextFields.keySet()) {
+				String textFieldContent = floatPropertyTextFields.get(vehicleProperty).getText();
+				if (textFieldContent.trim().length() > 0) {
+					vehiclePropertyStrings.put(vehicleProperty, textFieldContent);
+				}
+			}
+
+			return vehiclePropertyStrings;
+		}
+	}
+
+	private static class RoadQualityPanel extends JPanel {
+
+		private JTextField inclineUpTextField;
+		private JTextField inclineDownTextField;
+		private JTextField surfaceTextField;
+		private JTextField tracktypeTextField;
+
+		public RoadQualityPanel(PreferenceAccessParameters initialParameters) {
+			super();
+			this.setBorder(BorderFactory.createTitledBorder("road requirements"));
+
+
+			this.setLayout(new GridLayout(4, 2));
+
+			/* incline up */
+			{
+				JLabel inclineUpLabel = new JLabel("max. incline up (%, pos.)");
+				inclineUpLabel.setToolTipText("maximum incline the vehicle can go up");
+				this.add(inclineUpLabel);
+
+				inclineUpTextField = new JTextField();
+
+				String vehiclePropertyString =
+					initialParameters.getVehiclePropertyString(MAX_INCLINE_UP);
+				if (vehiclePropertyString != null) {
+					inclineUpTextField.setText(vehiclePropertyString);
+				}
+				inclineUpTextField.setToolTipText("maximum incline the vehicle can go up");
+
+				this.add(inclineUpTextField);
+			}
+
+			/* incline down */
+			{
+				JLabel inclineDownLabel = new JLabel("max. incline down (%, pos.)");
+				inclineDownLabel.setToolTipText("maximum incline the vehicle can go down");
+				this.add(inclineDownLabel);
+
+				inclineDownTextField = new JTextField();
+
+				String vehiclePropertyString =
+					initialParameters.getVehiclePropertyString(MAX_INCLINE_DOWN);
+				if (vehiclePropertyString != null) {
+					inclineDownTextField.setText(vehiclePropertyString);
+				}
+				inclineDownTextField.setToolTipText("maximum incline the vehicle can go down");
+
+				this.add(inclineDownTextField);
+			}
+
+			/* surface */
+			{
+				JLabel surfaceLabel = new JLabel("surface blacklist");
+				surfaceLabel.setToolTipText("list of surfaces the vehicle cannot use, "
+						+ "values are separated by semicolons (;)");
+				this.add(surfaceLabel);
+
+				surfaceTextField = new JTextField();
+
+				String vehiclePropertyString =
+					initialParameters.getVehiclePropertyString(SURFACE_BLACKLIST);
+
+				if (vehiclePropertyString != null) {
+					surfaceTextField.setText(vehiclePropertyString);
+				}
+
+				surfaceTextField.setToolTipText("list of surfaces the vehicle cannot use, "
+						+ "values are separated by semicolons (;)");
+
+				this.add(surfaceTextField);
+			}
+
+			/* tracktype */
+			{
+				JLabel tracktypeLabel = new JLabel("max. tracktype grade");
+				tracktypeLabel.setToolTipText("worst tracktype (1-5) the vehicle can still use,"
+						+ " 0 for none");
+				this.add(tracktypeLabel);
+
+				tracktypeTextField = new JTextField();
+
+				String vehiclePropertyString =
+					initialParameters.getVehiclePropertyString(MAX_TRACKTYPE);
+				if (vehiclePropertyString != null) {
+					tracktypeTextField.setText(vehiclePropertyString);
+				}
+				tracktypeTextField.setToolTipText("worst tracktype (1-5) the vehicle can still use,"
+						+ " 0 for none");
+
+				this.add(tracktypeTextField);
+			}
+
+		}
+
+		public Map<VehiclePropertyType<?>, String> getVehiclePropertyStrings() {
+
+			Map<VehiclePropertyType<?>, String> vehiclePropertyStrings =
+				new HashMap<VehiclePropertyType<?>, String>();
+
+			String incUpString = inclineUpTextField.getText();
+			if (incUpString.trim().length() > 0) {
+				vehiclePropertyStrings.put(MAX_INCLINE_UP, incUpString);
+			}
+
+			String incDownString = inclineDownTextField.getText();
+			if (incDownString.trim().length() > 0) {
+				vehiclePropertyStrings.put(MAX_INCLINE_DOWN, incDownString);
+			}
+
+			String surfaceString = surfaceTextField.getText();
+			if (surfaceString.trim().length() > 0) {
+				vehiclePropertyStrings.put(SURFACE_BLACKLIST, surfaceString);
+			}
+
+			String tracktypeString = tracktypeTextField.getText();
+			if (tracktypeString.trim().length() > 0) {
+				vehiclePropertyStrings.put(MAX_TRACKTYPE, tracktypeString);
+			}
+
+			return vehiclePropertyStrings;
+		}
+	}
+
+	private class OkCancelPanel extends JPanel {
+
+		public OkCancelPanel() {
+
+			new BoxLayout(this, BoxLayout.X_AXIS);
+
+			JButton okButton = new JButton(existingBookmark?"Change bookmark":"Create bookmark");
+			okButton.addActionListener(new ActionListener() {
+				public void actionPerformed(ActionEvent e) {
+					String bookmarkName = bookmarkNamePanel.getBookmarkName();
+					if (bookmarkName != null) {
+						PreferenceAccessParameters parameters = getAccessParameters();
+						if (parameters != null) {
+							okAction.execute(bookmarkName, parameters);
+							AccessParameterDialog.this.dispose();
+						}
+					}
+				}
+			});
+			this.add(okButton);
+
+			JButton cancelButton = new JButton("Cancel");
+			cancelButton.addActionListener(new ActionListener() {
+				public void actionPerformed(ActionEvent e) {
+					AccessParameterDialog.this.dispose();
+				}
+			});
+			this.add(cancelButton);
+
+		}
+
+	}
+
+	private boolean existingBookmark = false;
+	private final Collection<String> existingBookmarkNames;
+
+	private final BookmarkAction okAction;
+
+	private final BookmarkNamePanel bookmarkNamePanel;
+	private final AccessClassPanel accessClassPanel;
+	private final AccessTypesPanel accessTypesPanel;
+	private final VehiclePropertiesPanel vehiclePropertiesPanel;
+	private final RoadQualityPanel roadQualityPanel;
+
+	public AccessParameterDialog(final Frame owner, boolean existingBookmark, String initialName,
+			Collection<String> existingBookmarkNames,
+			PreferenceAccessParameters initialAccessParameters, BookmarkAction okAction) {
+		super(owner, "edit access parameters", true);
+
+		this.existingBookmark = existingBookmark;
+		this.existingBookmarkNames = existingBookmarkNames;
+		this.okAction = okAction;
+
+		GridBagLayout layout = new GridBagLayout();
+		this.setLayout(layout);
+
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.gridx = 0;
+		gbc.weighty = 1;
+		gbc.fill = GridBagConstraints.BOTH;
+
+		bookmarkNamePanel = new BookmarkNamePanel(initialName);
+		gbc.gridy = 0;
+		layout.setConstraints(bookmarkNamePanel, gbc);
+		this.add(bookmarkNamePanel);
+
+		accessClassPanel = new AccessClassPanel(initialAccessParameters);
+		gbc.gridy = 1;
+		layout.setConstraints(accessClassPanel, gbc);
+		this.add(accessClassPanel);
+
+		accessTypesPanel = new AccessTypesPanel(initialAccessParameters);
+		gbc.gridy = 2;
+		layout.setConstraints(accessTypesPanel, gbc);
+		this.add(accessTypesPanel);
+
+		vehiclePropertiesPanel = new VehiclePropertiesPanel(initialAccessParameters);
+		gbc.gridy = 3;
+		layout.setConstraints(vehiclePropertiesPanel, gbc);
+		this.add(vehiclePropertiesPanel);
+
+		roadQualityPanel = new RoadQualityPanel(initialAccessParameters);
+		gbc.gridy = 4;
+		layout.setConstraints(roadQualityPanel, gbc);
+		this.add(roadQualityPanel);
+
+		JPanel okCancelPanel = new OkCancelPanel();
+		gbc.gridy = 5;
+		gbc.weighty = 0;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		layout.setConstraints(okCancelPanel, gbc);
+		this.add(okCancelPanel);
+
+		this.pack();
+	}
+
+	private PreferenceAccessParameters getAccessParameters() {
+
+		String accessClass = accessClassPanel.getAccessClass();
+		Collection<AccessType> usableAccessTypes = accessTypesPanel.getUsableAccessTypes();
+		Map<VehiclePropertyType<?>, String> vehiclePropertyStrings =
+			vehiclePropertiesPanel.getVehiclePropertyStrings();
+		Map<VehiclePropertyType<?>, String> additionalVehiclePropertyStrings =
+			roadQualityPanel.getVehiclePropertyStrings();
+
+		if (accessClass != null && usableAccessTypes != null && vehiclePropertyStrings != null
+				&& additionalVehiclePropertyStrings != null) {
+
+			vehiclePropertyStrings.putAll(additionalVehiclePropertyStrings);
+
+			try {
+				return new PreferenceAccessParameters(accessClass, usableAccessTypes, vehiclePropertyStrings);
+			} catch (PropertyValueSyntaxException e) {
+				JOptionPane.showMessageDialog(this, e.getMessage());
+				return null;
+			}
+
+		} else {
+			return null;
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/GraphViewDialog.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/GraphViewDialog.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/GraphViewDialog.java	(revision 16520)
@@ -0,0 +1,294 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.dialogs;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Set;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessRulesetReader;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.ColorScheme;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.EndNodeColorScheme;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.InclineColorScheme;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.MaxheightColorScheme;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.MaxspeedColorScheme;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.MaxweightColorScheme;
+import org.openstreetmap.josm.plugins.graphview.plugin.GraphViewPlugin;
+import org.openstreetmap.josm.plugins.graphview.plugin.layer.PreferencesColorScheme;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferences;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.InternalRuleset;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Dialog for graph view configuration.
+ */
+public class GraphViewDialog extends ToggleDialog implements Observer {
+
+	private static final int HEIGHT = 150;
+
+	/** map from labels to available color schemes */
+	private final LinkedHashMap<String, ColorScheme> availableColorSchemes;
+
+
+	private final GraphViewPreferences preferences;
+	private final GraphViewPlugin plugin;
+
+	private final JComboBox rulesetComboBox;
+	private final JComboBox bookmarkComboBox;
+	private final JComboBox colorSchemeComboBox;
+
+	/**
+	 * list of ruleset files in the order currently used by rulesetComboBox;
+	 * null if internal rulesets are used
+	 */
+	private List<File> rulesetFiles;
+
+	public GraphViewDialog(final GraphViewPlugin plugin) {
+
+		super("Graph View Dialog", "graphview",
+				"Open the dialog for graph view configuration.", (Shortcut)null, HEIGHT);
+
+		this.preferences = GraphViewPreferences.getInstance();
+		this.plugin = plugin;
+
+		availableColorSchemes = new LinkedHashMap<String, ColorScheme>();
+
+		availableColorSchemes.put("default",
+				new PreferencesColorScheme(preferences));
+		availableColorSchemes.put("end nodes",
+				new EndNodeColorScheme(Color.GRAY, Color.RED, Color.GRAY));
+		availableColorSchemes.put("maxspeed",
+				new MaxspeedColorScheme());
+		availableColorSchemes.put("maxweight",
+				new MaxweightColorScheme());
+		availableColorSchemes.put("maxheight",
+				new MaxheightColorScheme());
+		availableColorSchemes.put("incline",
+				new InclineColorScheme());
+
+		JPanel selectionPanel = new JPanel();
+		GridBagLayout selectionLayout = new GridBagLayout();
+		selectionPanel.setLayout(selectionLayout);
+
+		GridBagConstraints gbcLabel = new GridBagConstraints();
+		gbcLabel.gridx = 0;
+		gbcLabel.anchor = GridBagConstraints.WEST;
+		gbcLabel.insets = new Insets(0, 5, 0, 5);
+
+		GridBagConstraints gbcComboBox = new GridBagConstraints();
+		gbcComboBox.gridx = 1;
+		gbcComboBox.fill = GridBagConstraints.HORIZONTAL;
+		gbcComboBox.weightx = 1;
+
+
+		/* create ruleset label and combo box */
+		{
+			JLabel rulesetLabel = new JLabel("ruleset:");
+			gbcLabel.gridy = 0;
+			selectionLayout.setConstraints(rulesetLabel, gbcLabel);
+			selectionPanel.add(rulesetLabel);
+
+			rulesetComboBox = new JComboBox();
+			rulesetComboBox.addActionListener(rulesetActionListener);
+			gbcComboBox.gridy = 0;
+			selectionLayout.setConstraints(rulesetComboBox, gbcComboBox);
+			selectionPanel.add(rulesetComboBox);
+		}
+
+		/* create bookmark label and combo box */
+		{
+			JLabel bookmarkLabel = new JLabel("parameters:");
+			gbcLabel.gridy = 1;
+			selectionLayout.setConstraints(bookmarkLabel, gbcLabel);
+			selectionPanel.add(bookmarkLabel);
+
+			bookmarkComboBox = new JComboBox();
+			bookmarkComboBox.addActionListener(bookmarkActionListener);
+			gbcComboBox.gridy = 1;
+			selectionLayout.setConstraints(bookmarkComboBox, gbcComboBox);
+			selectionPanel.add(bookmarkComboBox);
+		}
+
+		/* create color scheme label and combo box */
+		{
+			JLabel colorSchemeLabel = new JLabel("coloring:");
+			gbcLabel.gridy = 2;
+			selectionLayout.setConstraints(colorSchemeLabel, gbcLabel);
+			selectionPanel.add(colorSchemeLabel);
+
+			colorSchemeComboBox = new JComboBox();
+			for (String colorSchemeName : availableColorSchemes.keySet()) {
+				colorSchemeComboBox.addItem(colorSchemeName);
+				ColorScheme colorScheme = availableColorSchemes.get(colorSchemeName);
+				if (colorScheme.getClass().equals(preferences.getCurrentColorScheme().getClass())) {
+					colorSchemeComboBox.setSelectedItem(colorSchemeName);
+				}
+			}
+			colorSchemeComboBox.addActionListener(colorSchemeActionListener);
+			gbcComboBox.gridy = 2;
+			selectionLayout.setConstraints(colorSchemeComboBox, gbcComboBox);
+			selectionPanel.add(colorSchemeComboBox);
+		}
+
+		this.add(BorderLayout.CENTER, selectionPanel);
+
+
+		JPanel buttonPanel = new JPanel();
+		JButton showLayerButton = new JButton("create/update graph");
+		showLayerButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				plugin.createGraphViewLayer();
+			}
+		});
+		buttonPanel.add(showLayerButton);
+
+		this.add(BorderLayout.SOUTH, buttonPanel);
+
+		updateSelections();
+		this.preferences.addObserver(this);
+
+	}
+
+	private final ActionListener rulesetActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+			if (rulesetComboBox.getSelectedItem() != null) {
+				int selectedRulesetIndex = rulesetComboBox.getSelectedIndex();
+				if (rulesetFiles != null) {
+					File selectedRulesetFile = rulesetFiles.get(selectedRulesetIndex);
+					preferences.setCurrentRulesetFile(selectedRulesetFile);
+					preferences.distributeChanges();
+					plugin.updateGraphViewLayer();
+				} else {
+					if (selectedRulesetIndex < InternalRuleset.values().length) {
+						InternalRuleset selectedIRR = InternalRuleset.values()[selectedRulesetIndex];
+						preferences.setCurrentInternalRuleset(selectedIRR);
+						preferences.distributeChanges();
+						plugin.updateGraphViewLayer();
+					}
+				}
+			}
+		}
+	};
+
+	private final ActionListener bookmarkActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+			String selectedBookmarkName = (String)bookmarkComboBox.getSelectedItem();
+			if (selectedBookmarkName != null) {
+				preferences.setCurrentParameterBookmarkName(selectedBookmarkName);
+				preferences.distributeChanges();
+				plugin.updateGraphViewLayer();
+			}
+		}
+	};
+
+	private final ActionListener colorSchemeActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+			assert availableColorSchemes.containsKey(colorSchemeComboBox.getSelectedItem());
+			String colorSchemeLabel = (String)colorSchemeComboBox.getSelectedItem();
+			preferences.setCurrentColorScheme(availableColorSchemes.get(colorSchemeLabel));
+			preferences.distributeChanges();
+			plugin.repaintGraphViewLayer();
+		}
+	};
+
+	public void update(Observable observable, Object param) {
+		if (observable == preferences) {
+			updateSelections();
+		}
+	}
+
+	protected void updateSelections() {
+
+		/* update rulesets */
+
+		rulesetComboBox.removeActionListener(rulesetActionListener);
+
+		if (preferences.getUseInternalRulesets()) {
+
+			rulesetFiles = null;
+
+			rulesetComboBox.removeAllItems();
+			for (int i=0; i < InternalRuleset.values().length; i++) {
+				InternalRuleset ruleset = InternalRuleset.values()[i];
+				rulesetComboBox.addItem(ruleset.toString());
+				if (ruleset == preferences.getCurrentInternalRuleset()) {
+					rulesetComboBox.setSelectedIndex(i);
+				}
+			}
+
+			if (preferences.getCurrentInternalRuleset() == null) {
+				rulesetComboBox.addItem("");
+				rulesetComboBox.setSelectedIndex(InternalRuleset.values().length);
+			}
+
+		} else {
+
+			rulesetFiles = new LinkedList<File>();
+
+			File[] filesInRulesetFolder = preferences.getRulesetFolder().listFiles();
+
+			if (filesInRulesetFolder != null) {
+				for (File possibleRulesetFile : filesInRulesetFolder) {
+					try {
+						AccessRulesetReader.readAccessRuleset(new FileInputStream(possibleRulesetFile));
+						rulesetFiles.add(possibleRulesetFile);
+					} catch (IOException ioe) {
+						//don't add to rulesetFiles
+					}
+				}
+			}
+
+			Collections.sort(rulesetFiles);
+
+			rulesetComboBox.removeAllItems();
+			for (int i=0; i < rulesetFiles.size(); i++) {
+				File rulesetFile = rulesetFiles.get(i);
+				rulesetComboBox.addItem(rulesetFile.getName());
+				if (rulesetFile.equals(preferences.getCurrentRulesetFile())) {
+					rulesetComboBox.setSelectedIndex(i);
+				}
+			}
+
+		}
+
+		rulesetComboBox.addActionListener(rulesetActionListener);
+
+		/* update bookmarks */
+
+		bookmarkComboBox.removeActionListener(bookmarkActionListener);
+
+		String activeBookmarkName = preferences.getCurrentParameterBookmarkName();
+		Set<String> bookmarkNames = new HashSet<String>(preferences.getParameterBookmarks().keySet());
+
+		bookmarkComboBox.removeAllItems();
+		for (String bookmarkName : bookmarkNames) {
+			bookmarkComboBox.addItem(bookmarkName);
+			if (bookmarkName.equals(activeBookmarkName)) {
+				bookmarkComboBox.setSelectedItem(bookmarkName);
+			}
+		}
+
+		bookmarkComboBox.addActionListener(bookmarkActionListener);
+
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/GraphViewPreferenceEditor.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/GraphViewPreferenceEditor.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/dialogs/GraphViewPreferenceEditor.java	(revision 16520)
@@ -0,0 +1,363 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.dialogs;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.graphview.plugin.dialogs.AccessParameterDialog.BookmarkAction;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferenceDefaults;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferences;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.PreferenceAccessParameters;
+import org.openstreetmap.josm.tools.GBC;
+
+public class GraphViewPreferenceEditor implements PreferenceSetting {
+
+	private File rulesetFolder;
+	private Map<String, PreferenceAccessParameters> parameterBookmarks;
+
+	private JPanel preferencePanel;
+
+	private JCheckBox internalRulesetCheckBox;
+	private JLabel rulesetFolderLabel;
+	private JTextField rulesetFolderTextField;
+	private JButton selectRulesetFolderButton;
+
+	private JComboBox bookmarkComboBox;
+	private JButton editBookmarkButton;
+	private JButton deleteBookmarkButton;
+
+	private JCheckBox separateDirectionsCheckBox;
+
+	public void addGui(PreferenceDialog gui) {
+
+		readPreferences();
+
+		preferencePanel = gui.createPreferenceTab("graphview", "Graphview",
+		"Settings for the Graphview plugin that visualizes routing graphs.");
+
+		JPanel mainPanel = createMainPanel();
+
+		preferencePanel.add(mainPanel, GBC.eol().fill(GBC.BOTH));
+
+		updateVehiclePanel(GraphViewPreferences.getInstance().getCurrentParameterBookmarkName());
+
+	}
+
+	/**
+	 * creates local versions of preference information
+	 * that will only be written to real preferences if the OK button is pressed
+	 */
+	private void readPreferences() {
+
+		GraphViewPreferences preferences = GraphViewPreferences.getInstance();
+
+		rulesetFolder = preferences.getRulesetFolder();
+
+		parameterBookmarks =
+			new HashMap<String, PreferenceAccessParameters>(preferences.getParameterBookmarks());
+
+	}
+
+	private JPanel createMainPanel() {
+
+		JPanel mainPanel = new JPanel();
+
+		GridBagLayout mainLayout = new GridBagLayout();
+		mainPanel.setLayout(mainLayout);
+
+		GridBagConstraints constraints = new GridBagConstraints();
+		constraints.fill = GridBagConstraints.HORIZONTAL;
+		constraints.weightx = 1;
+		constraints.gridx = 0;
+
+		{
+			JPanel rulesetPanel = createRulesetPanel();
+			constraints.gridy = 0;
+			mainLayout.setConstraints(rulesetPanel, constraints);
+			mainPanel.add(rulesetPanel);
+		} {
+			JPanel vehiclePanel = createVehiclePanel();
+			constraints.gridy = 1;
+			mainLayout.setConstraints(vehiclePanel, constraints);
+			mainPanel.add(vehiclePanel);
+		} {
+			JPanel visualizationPanel = createVisualizationPanel();
+			constraints.gridy = 2;
+			mainLayout.setConstraints(visualizationPanel, constraints);
+			mainPanel.add(visualizationPanel);
+		}
+
+		mainPanel.add(GBC.glue(0, 0));
+
+		return mainPanel;
+
+	}
+
+	private JPanel createRulesetPanel() {
+
+		JPanel rulesetPanel = new JPanel();
+		rulesetPanel.setBorder(BorderFactory.createTitledBorder("ruleset"));
+		rulesetPanel.setLayout(new BoxLayout(rulesetPanel, BoxLayout.Y_AXIS));
+
+		internalRulesetCheckBox = new JCheckBox("use built-in rulesets");
+		internalRulesetCheckBox.setSelected(GraphViewPreferences.getInstance().getUseInternalRulesets());
+		internalRulesetCheckBox.addActionListener(internalRulesetActionListener);
+		rulesetPanel.add(internalRulesetCheckBox);
+
+		rulesetFolderLabel = new JLabel("external ruleset directory:");
+		rulesetPanel.add(rulesetFolderLabel);
+
+		rulesetFolderTextField = new JTextField();
+		rulesetFolderTextField.setText(rulesetFolder.getPath());
+		rulesetFolderTextField.setEditable(false);
+		rulesetPanel.add(rulesetFolderTextField);
+
+		selectRulesetFolderButton = new JButton("select directory");
+		selectRulesetFolderButton.addActionListener(selectRulesetFolderActionListener);
+		rulesetPanel.add(selectRulesetFolderButton);
+
+		updateRulesetPanel();
+
+		return rulesetPanel;
+	}
+
+	private JPanel createVehiclePanel() {
+
+		JPanel vehiclePanel = new JPanel();
+		vehiclePanel.setBorder(BorderFactory.createTitledBorder("vehicle"));
+		vehiclePanel.setLayout(new BoxLayout(vehiclePanel, BoxLayout.Y_AXIS));
+
+		bookmarkComboBox = new JComboBox();
+		vehiclePanel.add(bookmarkComboBox);
+
+		JPanel buttonPanel = new JPanel();
+		buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
+
+		JButton createButton = new JButton("create");
+		createButton.addActionListener(createVehicleActionListener);
+		buttonPanel.add(createButton);
+
+		editBookmarkButton = new JButton("edit");
+		editBookmarkButton.addActionListener(editVehicleActionListener);
+		buttonPanel.add(editBookmarkButton);
+
+		deleteBookmarkButton = new JButton("delete");
+		deleteBookmarkButton.addActionListener(deleteVehicleActionListener);
+		buttonPanel.add(deleteBookmarkButton);
+
+		JButton restoreDefaultsButton = new JButton("restore defaults");
+		restoreDefaultsButton.addActionListener(restoreVehicleDefaultsActionListener);
+		buttonPanel.add(restoreDefaultsButton);
+
+		vehiclePanel.add(buttonPanel);
+
+		return vehiclePanel;
+	}
+
+	private JPanel createVisualizationPanel() {
+
+		JPanel visualizationPanel = new JPanel();
+		visualizationPanel.setBorder(BorderFactory.createTitledBorder("visualization"));
+		visualizationPanel.setLayout(new BoxLayout(visualizationPanel, BoxLayout.Y_AXIS));
+
+		separateDirectionsCheckBox = new JCheckBox("draw directions separately");
+		separateDirectionsCheckBox.setSelected(GraphViewPreferences.getInstance().getSeparateDirections());
+		visualizationPanel.add(separateDirectionsCheckBox);
+
+		return visualizationPanel;
+	}
+
+	public boolean ok() {
+
+		GraphViewPreferences preferences = GraphViewPreferences.getInstance();
+
+		preferences.setUseInternalRulesets(internalRulesetCheckBox.isSelected());
+		preferences.setRulesetFolder(rulesetFolder);
+
+		preferences.setParameterBookmarks(parameterBookmarks);
+
+		String selectedBookmarkName = (String)bookmarkComboBox.getSelectedItem();
+		preferences.setCurrentParameterBookmarkName(selectedBookmarkName);
+
+		preferences.setSeparateDirections(separateDirectionsCheckBox.isSelected());
+
+		preferences.distributeChanges();
+
+		return false;
+	}
+
+	private final ActionListener internalRulesetActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+			updateRulesetPanel();
+		}
+	};
+
+	private final ActionListener selectRulesetFolderActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+
+			File initialFCDirectory = rulesetFolder;
+			if (rulesetFolder.getParentFile() != null) {
+				initialFCDirectory = rulesetFolder.getParentFile();
+			}
+
+			final JFileChooser fc = new JFileChooser();
+			fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+			fc.setCurrentDirectory(initialFCDirectory);
+
+			int returnVal = fc.showOpenDialog(preferencePanel);
+
+			if (returnVal == JFileChooser.APPROVE_OPTION) {
+				rulesetFolder = fc.getSelectedFile();
+				rulesetFolderTextField.setText(rulesetFolder.getPath());
+			}
+
+		}
+	};
+
+	private final ActionListener createVehicleActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+
+			PreferenceAccessParameters defaultBookmarkParameters =
+				GraphViewPreferenceDefaults.createDefaultBookmarkAccessParameters();
+
+			AccessParameterDialog apd = new AccessParameterDialog(
+					null,
+					false,
+					"new bookmark",
+					parameterBookmarks.keySet(),
+					defaultBookmarkParameters,
+					new BookmarkAction() {
+						public void execute(String name, PreferenceAccessParameters parameters) {
+							parameterBookmarks.put(name, parameters);
+							updateVehiclePanel(name);
+						}
+					});
+
+			apd.setVisible(true);
+		}
+	};
+
+	private final ActionListener editVehicleActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+			if (bookmarkComboBox.getSelectedItem() != null) {
+
+				final String selectedBookmarkName = (String)bookmarkComboBox.getSelectedItem();
+				PreferenceAccessParameters parameters =
+					parameterBookmarks.get(selectedBookmarkName);
+
+				Collection<String> otherBookmarkNames = new LinkedList<String>();
+				for (String bookmarkName : parameterBookmarks.keySet()) {
+					if (!bookmarkName.equals(selectedBookmarkName)) {
+						otherBookmarkNames.add(bookmarkName);
+					}
+				}
+
+				AccessParameterDialog apd = new AccessParameterDialog(
+						null,
+						true,
+						selectedBookmarkName,
+						otherBookmarkNames,
+						parameters,
+						new BookmarkAction() {
+							public void execute(String name, PreferenceAccessParameters parameters) {
+								parameterBookmarks.remove(selectedBookmarkName);
+								parameterBookmarks.put(name, parameters);
+								updateVehiclePanel(name);
+							}
+						});
+
+				apd.setVisible(true);
+			}
+
+		}
+	};
+
+	private final ActionListener deleteVehicleActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+			if (bookmarkComboBox.getSelectedItem() != null) {
+
+				String selectedBookmarkName = (String)bookmarkComboBox.getSelectedItem();
+
+				int userChoice = JOptionPane.showConfirmDialog(
+						preferencePanel,
+						"Really delete \"" + selectedBookmarkName + "\"?",
+						"Bookmark deletion",
+						JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
+
+				if (userChoice == JOptionPane.YES_OPTION) {
+					parameterBookmarks.remove(selectedBookmarkName);
+					updateVehiclePanel(null);
+				}
+
+			}
+		}
+	};
+
+	private final ActionListener restoreVehicleDefaultsActionListener = new ActionListener() {
+		public void actionPerformed(ActionEvent e) {
+
+			int userChoice = JOptionPane.showConfirmDialog(
+					preferencePanel,
+					"Really restore default bookmarks?\n"
+					+ "All manually added or edited bookmarks will be lost!",
+					"Bookmark reset",
+					JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
+
+			if (userChoice == JOptionPane.YES_OPTION) {
+				parameterBookmarks.clear();
+				parameterBookmarks.putAll(
+						GraphViewPreferenceDefaults.createDefaultAccessParameterBookmarks());
+				updateVehiclePanel(null);
+			}
+
+		}
+	};
+
+	private void updateRulesetPanel() {
+
+		rulesetFolderLabel.setEnabled(!internalRulesetCheckBox.isSelected());
+		rulesetFolderTextField.setEnabled(!internalRulesetCheckBox.isSelected());
+		selectRulesetFolderButton.setEnabled(!internalRulesetCheckBox.isSelected());
+
+	}
+
+	private void updateVehiclePanel(String selectedBookmarkName) {
+
+		bookmarkComboBox.removeAllItems();
+		for (String bookmarkName : parameterBookmarks.keySet()) {
+			bookmarkComboBox.addItem(bookmarkName);
+		}
+
+		if (selectedBookmarkName == null) {
+			if (bookmarkComboBox.getItemCount() > 0) {
+				bookmarkComboBox.setSelectedIndex(0);
+			}
+		} else {
+			bookmarkComboBox.setSelectedItem(selectedBookmarkName);
+		}
+
+		editBookmarkButton.setEnabled(parameterBookmarks.size() > 0);
+		deleteBookmarkButton.setEnabled(parameterBookmarks.size() > 0);
+
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/layer/GraphViewLayer.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/layer/GraphViewLayer.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/layer/GraphViewLayer.java	(revision 16520)
@@ -0,0 +1,346 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.layer;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+
+import javax.swing.Icon;
+import javax.swing.JMenuItem;
+import javax.swing.JSeparator;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphEdge;
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.graph.WayGraph;
+import org.openstreetmap.josm.plugins.graphview.core.graph.WayGraphObserver;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.transition.SegmentNode;
+import org.openstreetmap.josm.plugins.graphview.core.util.GraphUtil;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.ColorScheme;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.LatLonCoords;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.NodePositioner;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.NonMovingNodePositioner;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferences;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * layer for displaying the graph visualization
+ */
+public class GraphViewLayer extends Layer implements LayerChangeListener, WayGraphObserver {
+
+	private static final int NODE_RADIUS = 5;
+
+	/**
+	 * offset from real position if {@link GraphViewPreferences#getSeparateDirections()} is active,
+	 * causes the graph nodes for the two directions to be visually distinguishable.
+	 * Positive values move "forward" direction to the right.
+	 */
+	private static final double DIRECTIONAL_OFFSET = 20;
+
+	private static final double OFFSET_ANGLE = 0.5 * Math.PI;
+
+	private static final double MIN_QUAD_DISTANCE_FOR_OFFSET = 4;
+
+	private static final boolean CONNECT_ALL_NODE_PAIRS = false;
+
+	/** an arrow head that points along the x-axis to (0,0) */
+	private static final Shape ARROW_HEAD;
+
+	static {
+
+		Polygon head = new Polygon();
+
+		head.addPoint(  0,  0);
+		head.addPoint(-15, +4);
+		head.addPoint(-15, -4);
+
+		ARROW_HEAD = head;
+	}
+
+	private WayGraph wayGraph = null;
+
+	private ColorScheme colorScheme = null;
+	private NodePositioner nodePositioner = new NonMovingNodePositioner();
+
+	public GraphViewLayer() {
+		super("Graph view");
+		Layer.listeners.add(this);
+	}
+
+	/** sets the WayGraph that is to be displayed by this layer, may be null */
+	public void setWayGraph(WayGraph wayGraph) {
+		if (this.wayGraph != null) {
+			this.wayGraph.deleteObserver(this);
+		}
+		this.wayGraph = wayGraph;
+		if (wayGraph != null) {
+			wayGraph.addObserver(this);
+		}
+	}
+
+	/** sets the ColorScheme that is to be used for choosing colors, may be null */
+	public void setColorScheme(ColorScheme colorScheme) {
+		this.colorScheme = colorScheme;
+		Main.panel.repaint();
+	}
+
+	/**
+	 * sets the NodePositioner that is to be used for determining node placement,
+	 * null will cause a {@link NonMovingNodePositioner} to be used.
+	 */
+	public void setNodePositioner(NodePositioner nodePositioner) {
+		this.nodePositioner = nodePositioner;
+		if (nodePositioner == null) {
+			this.nodePositioner = new NonMovingNodePositioner();
+		}
+		Main.panel.repaint();
+	}
+
+	@Override
+	public Icon getIcon() {
+		return ImageProvider.get("layer", "graphview");
+	}
+
+	private void paintGraphNode(final GraphNode node, final Graphics g, final MapView mv) {
+
+		Color color = colorScheme != null ? colorScheme.getNodeColor(node) : Color.LIGHT_GRAY;
+
+		Point p = getNodePoint(node, mv);
+		g.setColor(color);
+		g.fillOval(p.x - NODE_RADIUS, p.y - NODE_RADIUS, 2 * NODE_RADIUS, 2 * NODE_RADIUS);
+
+	}
+
+	private void paintGraphEdge(final GraphEdge e, final Graphics2D g2D, final MapView mv) {
+
+		if (!CONNECT_ALL_NODE_PAIRS && GraphViewPreferences.getInstance().getSeparateDirections()) {
+
+			//don't paint edges between nodes from the same SegmentNode and simply inverted Segment
+			if (e.getStartNode().getSegmentNode() == e.getTargetNode().getSegmentNode()
+					&& e.getStartNode().getSegment().getNode2() == e.getTargetNode().getSegment().getNode1()
+					&& e.getStartNode().getSegment().getNode1() == e.getTargetNode().getSegment().getNode2()) {
+				return;
+			}
+
+		}
+
+		/* draw line(s) */
+
+		g2D.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+		if (e.getSegments().size() > 0) {
+
+			Segment firstSegment = e.getSegments().get(0);
+			Segment lastSegment = e.getSegments().get(e.getSegments().size() - 1);
+
+			//draw segments
+
+			for (Segment segment : e.getSegments()) {
+
+				Color color = Color.WHITE;
+				if (colorScheme != null) {
+					color = colorScheme.getSegmentColor(segment);
+				}
+				g2D.setColor(color);
+
+				Point p1 = getNodePoint(segment.getNode1(), mv);
+				Point p2 = getNodePoint(segment.getNode2(), mv);
+
+				if (segment == firstSegment) {
+					p1 = getNodePoint(e.getStartNode(), mv);
+				}
+				if (segment == lastSegment) {
+					p2 = getNodePoint(e.getTargetNode(), mv);
+				}
+
+				g2D.draw(new Line2D.Float(p1.x, p1.y, p2.x, p2.y));
+
+			}
+
+		} else {
+
+			g2D.setColor(Color.WHITE);
+
+			Point p1 = getNodePoint(e.getStartNode(), mv);
+			Point p2 = getNodePoint(e.getTargetNode(), mv);
+
+			g2D.draw(new Line2D.Float(p1.x, p1.y, p2.x, p2.y));
+
+		}
+
+		/* draw arrow head (note: color of last segment is still set) */
+
+		{
+			Point p1 = getNodePoint(e.getStartNode(), mv);
+			Point p2 = getNodePoint(e.getTargetNode(), mv);
+
+			if (e.getSegments().size() > 0) {
+				Segment lastSegment = e.getSegments().get(e.getSegments().size() - 1);
+				p1 = getNodePoint(lastSegment.getNode1(), mv);
+			}
+
+			double angle = angleFromXAxis(p1, p2); // angle between x-axis and [p1,p2]
+			Shape head = ARROW_HEAD;
+			head = AffineTransform.getRotateInstance(angle).createTransformedShape(head);
+			head = AffineTransform.getTranslateInstance(p2.x, p2.y).createTransformedShape(head);
+
+			g2D.fill(head);
+
+		}
+	}
+
+	private Point getNodePoint(GraphNode node, MapView mv) {
+
+		Point nodePoint = getNodePoint(nodePositioner.getPosition(node), mv);
+
+		if (GraphViewPreferences.getInstance().getSeparateDirections()
+				&& !GraphUtil.isEndNode(node)) {
+
+			SegmentNode node1 = node.getSegment().getNode1();
+			SegmentNode node2 = node.getSegment().getNode2();
+
+			Point node1Point = getNodePoint(node1, mv);
+			Point node2Point = getNodePoint(node2, mv);
+
+			double segmentX = node2Point.getX() - node1Point.getX();
+			double segmentY = node2Point.getY() - node1Point.getY();
+
+			if (segmentX*segmentX + segmentY*segmentY >= MIN_QUAD_DISTANCE_FOR_OFFSET) {
+
+				double rotatedX = Math.cos(OFFSET_ANGLE) * segmentX - Math.sin(OFFSET_ANGLE) * segmentY;
+				double rotatedY = Math.sin(OFFSET_ANGLE) * segmentX + Math.cos(OFFSET_ANGLE) * segmentY;
+
+				double segmentLength = Math.sqrt(rotatedX * rotatedX + rotatedY * rotatedY);
+
+				double normalizedX = rotatedX / segmentLength;
+				double normalizedY = rotatedY / segmentLength;
+
+				nodePoint.x += DIRECTIONAL_OFFSET * normalizedX;
+				nodePoint.y += DIRECTIONAL_OFFSET * normalizedY;
+
+			}
+
+		}
+
+		return nodePoint;
+	}
+	private Point getNodePoint(SegmentNode node, MapView mv) {
+		LatLonCoords coords = new LatLonCoords(node.getLat(), node.getLon());
+		return getNodePoint(coords, mv);
+	}
+	private Point getNodePoint(LatLonCoords coords, MapView mv) {
+		LatLon latLon = new LatLon(coords.getLat(), coords.getLon());
+		EastNorth eastNorth = Main.proj.latlon2eastNorth(latLon);
+		return mv.getPoint(eastNorth);
+	}
+
+	/**
+	 * calculates the angle between the x axis and a vector given by two points
+	 * @param p1  first point for vector; != null
+	 * @param p2  second point for vector; != null
+	 * @return  angle in radians, in range [-Pi .. +Pi]
+	 */
+	private double angleFromXAxis(Point p1, Point p2) {
+		assert p1 != null && p2 != null;
+
+		final float vecX = p2.x - p1.x;
+		final float vecY = p2.y - p1.y;
+
+		final float vecLength = (float)Math.sqrt(vecX*vecX + vecY*vecY);
+
+		final float dotProductVecAxis = vecX;
+
+		float angle = (float)Math.acos(dotProductVecAxis / vecLength);
+
+		if (p2.y < p1.y) {
+			angle = -angle;
+		}
+
+		assert -Math.PI*0.5 < angle && angle <= Math.PI*0.5;
+
+		return angle;
+	}
+
+	@Override
+	public void paint(final Graphics g, final MapView mv) {
+
+		assert g instanceof Graphics2D;
+		Graphics2D g2D = ((Graphics2D)g);
+
+		if (wayGraph != null) {
+
+			for (GraphNode n : wayGraph.getNodes()) {
+				paintGraphNode(n, g, mv);
+			}
+
+			for (GraphEdge e : wayGraph.getEdges()) {
+				paintGraphEdge(e, g2D, mv);
+			}
+
+		}
+
+	}
+
+	@Override
+	public String getToolTipText() {
+		return "routing graph calculated by the GraphView plugin";
+	}
+
+	@Override
+	public void mergeFrom(Layer from) {
+		throw new AssertionError("GraphView layer is not mergable");
+	}
+
+	@Override
+	public boolean isMergable(Layer other) {
+		return false;
+	}
+
+	@Override
+	public void visitBoundingBox(BoundingXYVisitor v) {
+	}
+
+	@Override
+	public Object getInfoComponent() {
+		return getToolTipText();
+	}
+
+	@Override
+	public Component[] getMenuEntries() {
+		return new Component[] { new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+				new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), new JSeparator(),
+				new JMenuItem(new RenameLayerAction(null, this)), new JSeparator(),
+				new JMenuItem(new LayerListPopup.InfoAction(this)) };
+	}
+
+	public void update(WayGraph wayGraph) {
+		assert wayGraph == this.wayGraph;
+		Main.panel.repaint();
+	}
+
+	public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+		//do nothing
+	}
+	public void layerAdded(Layer newLayer) {
+		//do nothing
+	}
+	public void layerRemoved(Layer oldLayer) {
+		//do nothing
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/layer/PreferencesColorScheme.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/layer/PreferencesColorScheme.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/layer/PreferencesColorScheme.java	(revision 16520)
@@ -0,0 +1,29 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.layer;
+
+import java.awt.Color;
+
+import org.openstreetmap.josm.plugins.graphview.core.graph.GraphNode;
+import org.openstreetmap.josm.plugins.graphview.core.transition.Segment;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.ColorScheme;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.GraphViewPreferences;
+
+/**
+ * color scheme using node and segment colors from preferences
+ */
+public class PreferencesColorScheme implements ColorScheme {
+
+	private final GraphViewPreferences preferences;
+
+	public PreferencesColorScheme(GraphViewPreferences preferences) {
+		this.preferences = preferences;
+	}
+
+	public Color getNodeColor(GraphNode node) {
+		return preferences.getNodeColor();
+	}
+
+	public Color getSegmentColor(Segment segment) {
+		return preferences.getSegmentColor();
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferenceDefaults.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferenceDefaults.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferenceDefaults.java	(revision 16520)
@@ -0,0 +1,111 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.preferences;
+
+import static org.openstreetmap.josm.plugins.graphview.core.access.AccessType.DESIGNATED;
+import static org.openstreetmap.josm.plugins.graphview.core.access.AccessType.PERMISSIVE;
+import static org.openstreetmap.josm.plugins.graphview.core.access.AccessType.UNDEFINED;
+import static org.openstreetmap.josm.plugins.graphview.core.access.AccessType.YES;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.VehiclePropertyStringParser.PropertyValueSyntaxException;
+
+/**
+ * utility class generating the default preferences
+ */
+public final class GraphViewPreferenceDefaults {
+
+	/** prevents instantiation */
+	private GraphViewPreferenceDefaults() { }
+
+	/** creates a default "empty" bookmark */
+	public static PreferenceAccessParameters createDefaultBookmarkAccessParameters() {
+
+		Collection<AccessType> accessTypes =
+			Arrays.asList(UNDEFINED, YES, PERMISSIVE, DESIGNATED);
+
+		Map<VehiclePropertyType<?>, String> propertyStringMap =
+			new HashMap<VehiclePropertyType<?>, String>();
+
+		try {
+			return new PreferenceAccessParameters("", accessTypes, propertyStringMap);
+		} catch (PropertyValueSyntaxException e) {
+			throw new AssertionError(e);
+		}
+
+	}
+
+	/** creates the default map of access parameter bookmarks */
+	public static Map<String, PreferenceAccessParameters> createDefaultAccessParameterBookmarks() {
+
+		try {
+
+			Map<String, PreferenceAccessParameters> result =
+				new HashMap<String, PreferenceAccessParameters>();
+
+			Collection<AccessType> accessTypes =
+				Arrays.asList(UNDEFINED, YES, PERMISSIVE, DESIGNATED);
+
+			/* create motorcar bookmark */
+			{
+				Map<VehiclePropertyType<?>, String> propertyMap =
+					new HashMap<VehiclePropertyType<?>, String>();
+
+				PreferenceAccessParameters accessParameters =
+					new PreferenceAccessParameters("motorcar", accessTypes, propertyMap);
+
+				result.put("motorcar", accessParameters);
+			}
+
+			/* create hgv bookmark */
+			{
+				Map<VehiclePropertyType<?>, String> propertyMap =
+					new HashMap<VehiclePropertyType<?>, String>();
+				propertyMap.put(VehiclePropertyTypes.WEIGHT, "3.5");
+
+				PreferenceAccessParameters accessParameters =
+					new PreferenceAccessParameters("hgv", accessTypes, propertyMap);
+
+				result.put("hgv (3.5 t)", accessParameters);
+			}
+
+			/* create bicycle bookmark */
+			{
+				Map<VehiclePropertyType<?>, String> propertyMap =
+					new HashMap<VehiclePropertyType<?>, String>();
+
+				PreferenceAccessParameters accessParameters =
+					new PreferenceAccessParameters("bicycle", accessTypes, propertyMap);
+
+				result.put("bicycle", accessParameters);
+			}
+
+			/* create pedestrian bookmark */
+			{
+				Map<VehiclePropertyType<?>, String> propertyMap =
+					new HashMap<VehiclePropertyType<?>, String>();
+
+				PreferenceAccessParameters accessParameters =
+					new PreferenceAccessParameters("foot", accessTypes, propertyMap);
+
+				result.put("pedestrian", accessParameters);
+			}
+
+			return result;
+
+		} catch (PropertyValueSyntaxException e) {
+			throw new AssertionError(e);
+		}
+	}
+
+	public static File getDefaultRulesetFolder() {
+		return new File(System.getProperty("user.home"));
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferences.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferences.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferences.java	(revision 16520)
@@ -0,0 +1,489 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.preferences;
+
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.AXLELOAD;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.HEIGHT;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.LENGTH;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.MAX_INCLINE_DOWN;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.MAX_INCLINE_UP;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.MAX_TRACKTYPE;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.SPEED;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.SURFACE_BLACKLIST;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.WEIGHT;
+import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.WIDTH;
+
+import java.awt.Color;
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Observable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.visualisation.ColorScheme;
+import org.openstreetmap.josm.plugins.graphview.plugin.layer.PreferencesColorScheme;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.VehiclePropertyStringParser.PropertyValueSyntaxException;
+
+/**
+ * preferences of the GraphView plugin.
+ * Observers will be notified when preferences change,
+ * changes will also be synchronized (two-way) with JOSM's preference storage.
+ * This is a singleton class.
+ */
+public class GraphViewPreferences extends Observable {
+
+	private static GraphViewPreferences instance;
+
+	/**
+	 * returns the single instance of GraphViewPreferences.
+	 * @param ignoreSyntaxErrors
+	 * @return
+	 */
+	public static GraphViewPreferences getInstance() {
+		if (instance == null) {
+			instance = new GraphViewPreferences();
+		}
+		return instance;
+	}
+
+	private boolean useInternalRulesets;
+	private File rulesetFolder;
+	private File currentRulesetFile;
+	private InternalRuleset currentInternalRuleset;
+
+	private String currentParameterBookmarkName;
+	private Map<String, PreferenceAccessParameters> parameterBookmarks;
+
+	private ColorScheme currentColorScheme;
+	private Color nodeColor;
+	private Color segmentColor;
+
+	private boolean separateDirections;
+
+
+	public synchronized boolean getUseInternalRulesets() {
+		return useInternalRulesets;
+	}
+	public synchronized void setUseInternalRulesets(boolean useInternalRulesets) {
+		this.useInternalRulesets = useInternalRulesets;
+	}
+
+	public synchronized File getRulesetFolder() {
+		return rulesetFolder;
+	}
+	public synchronized void setRulesetFolder(File rulesetFolder) {
+		this.rulesetFolder = rulesetFolder;
+	}
+
+	public synchronized File getCurrentRulesetFile() {
+		return currentRulesetFile;
+	}
+	public synchronized void setCurrentRulesetFile(File currentRulesetFile) {
+		this.currentRulesetFile = currentRulesetFile;
+	}
+
+	public synchronized InternalRuleset getCurrentInternalRuleset() {
+		return currentInternalRuleset;
+	}
+	public synchronized void setCurrentInternalRuleset(InternalRuleset internalRuleset) {
+		this.currentInternalRuleset = internalRuleset;
+	}
+
+	/**
+	 * returns the name (map key) of the currently selected parameter bookmark
+	 * or null if none is selected.
+	 * If a name is returned, is has to be a key of the map returned by
+	 * {@link #getParameterBookmarks()}.
+	 */
+	public synchronized String getCurrentParameterBookmarkName() {
+		assert parameterBookmarks.containsKey(currentParameterBookmarkName);
+		return currentParameterBookmarkName;
+	}
+
+	/**
+	 * returns the access parameters of the currently selected parameter bookmark
+	 * or null if none is selected.
+	 */
+	public synchronized AccessParameters getCurrentParameterBookmark() {
+		if (currentParameterBookmarkName == null) {
+			return null;
+		} else {
+			assert parameterBookmarks.containsKey(currentParameterBookmarkName);
+			return parameterBookmarks.get(currentParameterBookmarkName);
+		}
+	}
+
+	/**
+	 * sets the active parameter bookmark using its name as an identifier
+	 * @param currentParameters  name of bookmark to set or null (no active bookmark).
+	 *                           Non-null values must be keys of the map returned by
+	 *                           {@link #getParameterBookmarks()}.
+	 */
+	public synchronized void setCurrentParameterBookmarkName(String parameterBookmarkName) {
+		assert parameterBookmarks.containsKey(parameterBookmarkName);
+		this.currentParameterBookmarkName = parameterBookmarkName;
+	}
+
+	public synchronized Map<String, PreferenceAccessParameters> getParameterBookmarks() {
+		return Collections.unmodifiableMap(parameterBookmarks);
+	}
+	public synchronized void setParameterBookmarks(
+			Map<String, PreferenceAccessParameters> parameterBookmarks) {
+		assert parameterBookmarks != null;
+
+		this.parameterBookmarks =
+			new HashMap<String, PreferenceAccessParameters>(parameterBookmarks);
+	}
+
+	public synchronized ColorScheme getCurrentColorScheme() {
+		return currentColorScheme;
+	}
+	public synchronized void setCurrentColorScheme(ColorScheme currentColorScheme) {
+		this.currentColorScheme = currentColorScheme;
+	}
+
+	public synchronized Color getNodeColor() {
+		return nodeColor;
+	}
+	public synchronized void setNodeColor(Color nodeColor) {
+		this.nodeColor = nodeColor;
+	}
+
+	public synchronized Color getSegmentColor() {
+		return segmentColor;
+	}
+	public synchronized void setSegmentColor(Color segmentColor) {
+		this.segmentColor = segmentColor;
+	}
+
+	public synchronized boolean getSeparateDirections() {
+		return separateDirections;
+	}
+	public synchronized void setSeparateDirections(boolean separateDirections) {
+		this.separateDirections = separateDirections;
+	}
+
+	/**
+	 * writes changes to JOSM's preferences and notifies observers.
+	 * Must be called explicitly after setters (to prevent distributing incomplete changes).
+	 */
+	public void distributeChanges() {
+		writePreferences();
+		setChanged();
+		notifyObservers();
+	}
+
+	private GraphViewPreferences() {
+
+		/* set defaults first (in case preferences are incomplete) */
+
+		fillDefaults();
+
+		/* read preferences and overwrite defaults */
+
+		readPreferences();
+
+		/* write preferences
+		 * (this will restore missing/defect preferences,
+		 *  but will simply rewrite valid preferences) */
+
+		writePreferences();
+
+	}
+
+	private void fillDefaults() {
+
+		parameterBookmarks = GraphViewPreferenceDefaults.createDefaultAccessParameterBookmarks();
+
+		if (parameterBookmarks.size() > 0) {
+			currentParameterBookmarkName = parameterBookmarks.keySet().iterator().next();
+		} else {
+			currentParameterBookmarkName = null;
+		}
+
+		useInternalRulesets = true;
+		rulesetFolder = GraphViewPreferenceDefaults.getDefaultRulesetFolder();
+		currentRulesetFile = null;
+		currentInternalRuleset = null;
+
+		nodeColor = Color.WHITE;
+		segmentColor = Color.WHITE;
+		currentColorScheme = new PreferencesColorScheme(this);
+
+		separateDirections = false;
+
+	}
+
+	private void writePreferences() {
+
+		Main.pref.put("graphview.parameterBookmarks",
+				createAccessParameterBookmarksString(parameterBookmarks));
+
+		if (currentParameterBookmarkName != null) {
+			Main.pref.put("graphview.activeBookmark", currentParameterBookmarkName);
+		}
+
+		Main.pref.put("graphview.useInternalRulesets", useInternalRulesets);
+
+		Main.pref.put("graphview.rulesetFolder", rulesetFolder.getPath());
+
+		if (currentRulesetFile != null) {
+			Main.pref.put("graphview.rulesetFile", currentRulesetFile.getPath());
+		}
+		if (currentInternalRuleset != null) {
+			Main.pref.put("graphview.rulesetResource", currentInternalRuleset.toString());
+		}
+
+		Main.pref.put("graphview.defaultNodeColor", createColorString(nodeColor));
+		Main.pref.put("graphview.defaultSegmentColor", createColorString(segmentColor));
+
+		Main.pref.put("graphview.separateDirections", separateDirections);
+
+	}
+
+	private void readPreferences() {
+
+		if (Main.pref.hasKey("graphview.parameterBookmarks")) {
+			String bookmarksString = Main.pref.get("graphview.parameterBookmarks");
+			parameterBookmarks = parseAccessParameterBookmarksString(bookmarksString);
+		}
+
+		if (Main.pref.hasKey("graphview.activeBookmark")) {
+			currentParameterBookmarkName = Main.pref.get("graphview.activeBookmark");
+		}
+		if (!parameterBookmarks.containsKey(currentParameterBookmarkName)) {
+			currentParameterBookmarkName = null;
+		}
+
+
+		useInternalRulesets = Main.pref.getBoolean("graphview.useInternalRulesets", true);
+
+		if (Main.pref.hasKey("graphview.rulesetFolder")) {
+			String dirString = Main.pref.get("graphview.rulesetFolder");
+			rulesetFolder = new File(dirString);
+		}
+		if (Main.pref.hasKey("graphview.rulesetFile")) {
+			String fileString = Main.pref.get("graphview.rulesetFile");
+			currentRulesetFile = new File(fileString);
+		}
+
+		if (Main.pref.hasKey("graphview.rulesetResource")) {
+			String rulesetString = Main.pref.get("graphview.rulesetResource");
+			//get the enum value for the string
+			//(InternalRuleset.valueOf cannot be used because it cannot handle invalid strings well)
+			for (InternalRuleset ruleset : InternalRuleset.values()) {
+				if (ruleset.toString().equals(rulesetString)) {
+					currentInternalRuleset = ruleset;
+					break;
+				}
+			}
+		}
+
+		if (Main.pref.hasKey("graphview.defaultNodeColor")) {
+			Color color = parseColorString(Main.pref.get("graphview.defaultNodeColor"));
+			if (color != null) {
+				nodeColor = color;
+			}
+		}
+		if (Main.pref.hasKey("graphview.defaultSegmentColor")) {
+			Color color = parseColorString(Main.pref.get("graphview.defaultSegmentColor"));
+			if (color != null) {
+				segmentColor = color;
+			}
+		}
+
+		separateDirections = Main.pref.getBoolean("graphview.separateDirections", false);
+
+	}
+
+	private static final Pattern ACCESS_PARAM_PATTERN = Pattern.compile("^([^;]*);([^;]*);types=\\{([^\\}]*)\\};properties=\\{([^\\}]*)\\}$");
+
+	private static final Pattern PROPERTY_MAP_ENTRY_PATTERN = Pattern.compile("^([^=]*)=(.*)$");
+
+	private static final Map<VehiclePropertyType<?>, String> VEHICLE_PROPERTY_TYPE_NAME_MAP =
+		new HashMap<VehiclePropertyType<?>, String>();
+
+
+	static {
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(AXLELOAD, "AXLELOAD");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(HEIGHT, "HEIGHT");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(LENGTH, "LENGTH");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(MAX_INCLINE_DOWN, "MAX_INCLINE_DOWN");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(MAX_INCLINE_UP, "MAX_INCLINE_UP");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(MAX_TRACKTYPE, "MAX_TRACKTYPE");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(SPEED, "SPEED");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(SURFACE_BLACKLIST, "SURFACE_BLACKLIST");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(WEIGHT, "WEIGHT");
+		VEHICLE_PROPERTY_TYPE_NAME_MAP.put(WIDTH, "WIDTH");
+	}
+
+	private static String createAccessParameterBookmarksString(
+			Map<String, PreferenceAccessParameters> parameterBookmarks) {
+
+		StringBuilder stringBuilder = new StringBuilder();
+
+		boolean firstEntry = true;
+
+		for (String bookmarkName : parameterBookmarks.keySet()) {
+
+			if (!firstEntry) {
+				stringBuilder.append("|");
+			} else {
+				firstEntry = false;
+			}
+
+			stringBuilder.append(createAccessParameterBookmarkString(
+					bookmarkName,
+					parameterBookmarks.get(bookmarkName)));
+
+		}
+
+		return stringBuilder.toString();
+	}
+
+	private static String createAccessParameterBookmarkString(
+			String bookmarkName, PreferenceAccessParameters parameters) {
+
+		StringBuilder stringBuilder = new StringBuilder();
+
+		stringBuilder.append(bookmarkName).append(";");
+
+		stringBuilder.append(parameters.getAccessClass());
+
+		stringBuilder.append(";types={");
+		for (AccessType accessType : AccessType.values()) {
+			if (parameters.getAccessTypeUsable(accessType)) {
+				stringBuilder.append(accessType).append(",");
+			}
+		}
+
+		if(stringBuilder.charAt(stringBuilder.length()-1) == ',') {
+			stringBuilder.deleteCharAt(stringBuilder.length()-1);
+		}
+		stringBuilder.append("}");
+
+		stringBuilder.append(";properties={");
+
+		for (VehiclePropertyType<?> vehiclePropertyType : VEHICLE_PROPERTY_TYPE_NAME_MAP.keySet()) {
+			String propertyString = parameters.getVehiclePropertyString(vehiclePropertyType);
+			if (propertyString != null) {
+				stringBuilder.append(VEHICLE_PROPERTY_TYPE_NAME_MAP.get(vehiclePropertyType));
+				stringBuilder.append("=");
+				stringBuilder.append(propertyString);
+				stringBuilder.append(",");
+			}
+		}
+
+		if(stringBuilder.charAt(stringBuilder.length()-1) == ',') {
+			stringBuilder.deleteCharAt(stringBuilder.length()-1);
+		}
+		stringBuilder.append("}");
+
+		assert ACCESS_PARAM_PATTERN.matcher(stringBuilder.toString()).matches();
+
+		return stringBuilder.toString();
+	}
+
+	private static Map<String, PreferenceAccessParameters> parseAccessParameterBookmarksString(
+			String string) {
+
+		Map<String, PreferenceAccessParameters> resultMap =
+			new HashMap<String, PreferenceAccessParameters>();
+
+		String[] bookmarkStrings = string.split("\\|");
+
+		for (String bookmarkString : bookmarkStrings) {
+			parseAccessParameterBookmarkString(bookmarkString, resultMap);
+		}
+
+		return resultMap;
+	}
+
+	private static void parseAccessParameterBookmarkString(String bookmarkString,
+			Map<String, PreferenceAccessParameters> resultMap) {
+
+		Matcher matcher = ACCESS_PARAM_PATTERN.matcher(bookmarkString);
+
+		if (matcher.matches()) {
+
+			String bookmarkName = matcher.group(1);
+
+			String accessClass = matcher.group(2);
+
+			String[] accessTypeStrings = matcher.group(3).split(",");
+			Collection<AccessType> accessTypes = new LinkedList<AccessType>();
+			for (String accessTypeString : accessTypeStrings) {
+				AccessType accessType = AccessType.valueOf(accessTypeString);
+				if (accessType != null) {
+					accessTypes.add(accessType);
+				}
+			}
+
+
+			String[] vehiclePropertyStrings = matcher.group(4).split(",");
+			Map<VehiclePropertyType<?>, String> vehiclePropertyMap =
+				new HashMap<VehiclePropertyType<?>, String>();
+
+			for (String vehiclePropertyString : vehiclePropertyStrings) {
+
+				Matcher entryMatcher = PROPERTY_MAP_ENTRY_PATTERN.matcher(vehiclePropertyString);
+				if (entryMatcher.matches()) {
+
+					String propertyTypeString = entryMatcher.group(1);
+					String propertyValueString = entryMatcher.group(2);
+
+					for (VehiclePropertyType<?> propertyType :
+						VEHICLE_PROPERTY_TYPE_NAME_MAP.keySet()) {
+
+						if (propertyTypeString.equals(
+								VEHICLE_PROPERTY_TYPE_NAME_MAP.get(propertyType))) {
+
+							vehiclePropertyMap.put(propertyType, propertyValueString);
+
+						}
+
+					}
+
+				}
+
+			}
+
+			try {
+
+				PreferenceAccessParameters accessParameters =
+					new PreferenceAccessParameters(accessClass, accessTypes, vehiclePropertyMap);
+
+				resultMap.put(bookmarkName, accessParameters);
+
+			} catch (PropertyValueSyntaxException e) {
+				//don't add bookmark
+			}
+
+		}
+	}
+
+	private static final Pattern COLOR_PATTERN =
+		Pattern.compile("^(\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})$");
+	private String createColorString(Color color) {
+		return color.getRed() + ", " + color.getGreen() + ", " + color.getBlue();
+	}
+
+	private Color parseColorString(String string) {
+		Matcher matcher = COLOR_PATTERN.matcher(string);
+		if (!matcher.matches()) {
+			return null;
+		} else {
+			int r = Integer.parseInt(matcher.group(1));
+			int g = Integer.parseInt(matcher.group(2));
+			int b = Integer.parseInt(matcher.group(3));
+			return new Color(r, g, b);
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/InternalRuleset.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/InternalRuleset.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/InternalRuleset.java	(revision 16520)
@@ -0,0 +1,15 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.preferences;
+
+public enum InternalRuleset {
+
+	DEFAULT("files/accessRuleset.xml"),
+	GERMANY("files/accessRuleset_de.xml");
+
+	private String resourceName;
+	private InternalRuleset(String resourceName) {
+		this.resourceName = resourceName;
+	}
+	public String getResourceName() {
+		return resourceName;
+	}
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/PreferenceAccessParameters.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/PreferenceAccessParameters.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/PreferenceAccessParameters.java	(revision 16520)
@@ -0,0 +1,101 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.preferences;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyType;
+
+/**
+ * implementation of AccessParameters that stores not only property values,
+ * but also the original property Strings just like the user entered them.
+ */
+public class PreferenceAccessParameters implements AccessParameters {
+
+	private final String accessClass;
+	private final Map<AccessType, Boolean> accessTypeUsableMap;
+	private final Map<VehiclePropertyType<?>, String> vehiclePropertyStrings;
+	private final Map<VehiclePropertyType<?>, Object> vehiclePropertyValues;
+
+	public String getAccessClass() {
+		return accessClass;
+	}
+
+	public boolean getAccessTypeUsable(AccessType accessType) {
+		assert accessType != null;
+		return accessTypeUsableMap.get(accessType);
+	}
+
+	public Collection<VehiclePropertyType<?>> getAvailableVehicleProperties() {
+		return vehiclePropertyValues.keySet();
+	}
+
+	/**
+	 * returns the value for a vehicle property.
+	 *
+	 * @param <D>              type of property value
+	 * @param vehicleProperty  property to get value for; != null
+	 * @return                 value for vehicleProperty, null if no value is available.
+	 *                         Guaranteed to be valid according to vehicleProperty's
+	 *                         {@link VehiclePropertyType#isValidValue(Object)} method.
+	 */
+	public <D> D getVehiclePropertyValue(VehiclePropertyType<D> vehicleProperty) {
+		assert vehicleProperty != null;
+
+		@SuppressWarnings("unchecked")
+		D value = (D)vehiclePropertyValues.get(vehicleProperty);
+		return value;
+	}
+
+	/**
+	 * returns the unparsed String for a vehicle property.
+	 *
+	 * @param vehicleProperty  property to get String for; != null
+	 * @return                 unparsed String, null if no value is available.
+	 */
+	public String getVehiclePropertyString(VehiclePropertyType<?> vehicleProperty) {
+		assert vehicleProperty != null;
+
+		return vehiclePropertyStrings.get(vehicleProperty);
+	}
+
+	/**
+	 * @param vehiclePropertyStrings  map from vehicle properties to string representations
+	 *                                that will be parsed using {@link VehiclePropertyStringParser}
+	 *                                to get the property values; != null
+	 *
+	 * @throws VehiclePropertyStringParser.PropertyValueSyntaxException
+	 *         if a String from vehiclePropertyStrings contains a syntax error
+	 */
+	public PreferenceAccessParameters(String accessClass,
+			Collection<AccessType> usableAccessTypes,
+			Map<VehiclePropertyType<?>, String> vehiclePropertyStrings)
+	throws VehiclePropertyStringParser.PropertyValueSyntaxException {
+
+		this.accessClass = accessClass;
+
+		accessTypeUsableMap = new EnumMap<AccessType, Boolean>(AccessType.class);
+		for (AccessType accessType : AccessType.values()) {
+			accessTypeUsableMap.put(accessType, usableAccessTypes.contains(accessType));
+		}
+
+		/* check and use vehicle properties */
+
+		this.vehiclePropertyStrings = Collections.unmodifiableMap(
+				new HashMap<VehiclePropertyType<?>, String>(vehiclePropertyStrings));
+
+		this.vehiclePropertyValues = new HashMap<VehiclePropertyType<?>, Object>();
+		for (VehiclePropertyType<?> vehiclePropertyType : vehiclePropertyStrings.keySet()) {
+			String propertyValueString = vehiclePropertyStrings.get(vehiclePropertyType);
+			Object propertyValue = VehiclePropertyStringParser.parsePropertyValue(
+					vehiclePropertyType, propertyValueString);
+			this.vehiclePropertyValues.put(vehiclePropertyType, propertyValue);
+		}
+
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/VehiclePropertyStringParser.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/VehiclePropertyStringParser.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/VehiclePropertyStringParser.java	(revision 16520)
@@ -0,0 +1,151 @@
+package org.openstreetmap.josm.plugins.graphview.plugin.preferences;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes;
+import org.openstreetmap.josm.plugins.graphview.core.util.ValueStringParser;
+
+/**
+ * utility class for interpreting Strings as vehicle property values
+ */
+public final class VehiclePropertyStringParser {
+	
+	/** prevents instantiation */
+	private VehiclePropertyStringParser() { }
+	
+	/**
+	 * Exception class for syntax errors in property value Strings,
+	 * the message contains the reason using one of this utility class' public String constants.
+	 */
+	public static class PropertyValueSyntaxException extends Exception {
+		private static final long serialVersionUID = 1L;
+		public PropertyValueSyntaxException(String message) {
+			super(message);
+		}
+	}
+	
+	public static final String ERROR_WEIGHT =
+		"Weights must be given as positive decimal numbers without unit.";
+	public static final String ERROR_LENGTH =
+		"Lengths must be given as positive decimal numbers without unit.";
+	public static final String ERROR_SPEED =
+		"Speeds should be given as numbers without unit or "
+		+ "as numbers followed by \"mph\".";
+	public static final String ERROR_INCLINE =
+		"Inclines must be given as positive decimal numbers with followed by \"%\".";
+	public static final String ERROR_TRACKTYPE =
+		"Tracktype grades must be given as integers between 0 and 5.";
+	public static final String ERROR_SURFACE =
+		"Surface values must not contain any of the following characters: ',' '{' '}' '=' '|";
+	
+	private static final List<Character> FORBIDDEN_SURFACE_CHARS =
+		Arrays.asList(',', '{', '}', '=', '|');
+	
+	/**
+	 * returns the value represented by the propertyValueString
+	 *
+	 * @throws PropertyValueSyntaxException  if the string has syntax errors that prevent parsing
+	 * @throws InvalidParameterException     if an unknown property type was passed
+	 *
+	 * @param propertyType         type of the property; != null
+	 * @param propertyValueString  string to parse; != null
+	 * @return                     property value; != null.
+	 *                             Guaranteed to be valid according to propertyType's
+	 *                             {@link VehiclePropertyType#isValidValue(Object)} method.
+	 */
+	public static final <V> V parsePropertyValue(
+			VehiclePropertyType<V> propertyType, String propertyValueString)
+	throws PropertyValueSyntaxException {
+		
+		assert propertyType != null && propertyValueString != null;
+		
+		if (propertyType == VehiclePropertyTypes.AXLELOAD
+				|| propertyType == VehiclePropertyTypes.WEIGHT) {
+			
+			Float value = ValueStringParser.parseOsmDecimal(propertyValueString, false);
+			if (value != null && propertyType.isValidValue(value)) {
+				@SuppressWarnings("unchecked") //V must be float because of propertyType condition
+				V result = (V)value;
+				return result;
+			} else {
+				throw new PropertyValueSyntaxException(ERROR_WEIGHT);
+			}
+			
+		} else if (propertyType == VehiclePropertyTypes.HEIGHT
+				|| propertyType == VehiclePropertyTypes.LENGTH
+				|| propertyType == VehiclePropertyTypes.WIDTH) {
+			
+			Float value = ValueStringParser.parseOsmDecimal(propertyValueString, false);
+			if (value != null && propertyType.isValidValue(value)) {
+				@SuppressWarnings("unchecked") //V must be float because of propertyType condition
+				V result = (V)value;
+				return result;
+			} else {
+				throw new PropertyValueSyntaxException(ERROR_LENGTH);
+			}
+			
+		} else if (propertyType == VehiclePropertyTypes.SPEED) {
+			
+			Float value = ValueStringParser.parseSpeed(propertyValueString);
+			if (value != null && propertyType.isValidValue(value)) {
+				@SuppressWarnings("unchecked") //V must be float because of propertyType condition
+				V result = (V)value;
+				return result;
+			} else {
+				throw new PropertyValueSyntaxException(ERROR_SPEED);
+			}
+			
+		} else if (propertyType == VehiclePropertyTypes.MAX_INCLINE_DOWN
+				|| propertyType == VehiclePropertyTypes.MAX_INCLINE_UP) {
+			
+			Float value = ValueStringParser.parseIncline(propertyValueString);
+			if (value != null && propertyType.isValidValue(value)) {
+				@SuppressWarnings("unchecked") //V must be float because of propertyType condition
+				V result = (V)value;
+				return result;
+			} else {
+				throw new PropertyValueSyntaxException(ERROR_INCLINE);
+			}
+			
+		} else if (propertyType == VehiclePropertyTypes.MAX_TRACKTYPE) {
+			
+			try {
+				int value = Integer.parseInt(propertyValueString);
+				if (0 <= value && value <= 5) {
+					@SuppressWarnings("unchecked") //V must be int because of propertyType condition
+					V result = (V)(Integer)value;
+					return result;
+				}
+			} catch (NumberFormatException e) {}
+			
+			throw new PropertyValueSyntaxException(ERROR_TRACKTYPE);
+			
+		} else if (propertyType == VehiclePropertyTypes.SURFACE_BLACKLIST) {
+			
+			String[] surfaces = propertyValueString.split(";\\s*");
+			Collection<String> surfaceBlacklist = new ArrayList<String>(surfaces.length);
+			for (String surface : surfaces) {
+				for (char nameChar : surface.toCharArray()) {
+					if (FORBIDDEN_SURFACE_CHARS.contains(nameChar)) {
+						throw new PropertyValueSyntaxException(ERROR_SURFACE);
+					}
+				}
+				surfaceBlacklist.add(surface);
+			}
+			
+			@SuppressWarnings("unchecked") //V must be Collection because of propertyType condition
+			V result = (V)surfaceBlacklist;
+			return result;
+			
+		} else {
+			throw new InvalidParameterException("unknown property type: " + propertyType);
+		}
+		
+	}
+	
+}
Index: /applications/editors/josm/plugins/graphview/test/files/accessRuleset_valid.xml
===================================================================
--- /applications/editors/josm/plugins/graphview/test/files/accessRuleset_valid.xml	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/files/accessRuleset_valid.xml	(revision 16520)
@@ -0,0 +1,54 @@
+<accessRuleset>
+	<unknown></unknown>
+	<classes>
+		<class name="vehicle">
+			<class name="motor_vehicle">
+				<class name="bus"></class>
+			</class>
+			<class name="bicycle" />
+		</class>
+	</classes>
+	<unknown />
+	
+	<basetags>
+		<tag k="highway" v="residential"/>
+		<tag k="highway" v="cycleway"/>		
+	</basetags>
+	
+	<implications>
+		<implication>
+			<condition>
+				<tag k="highway" v="cycleway"/>
+			</condition>
+			<implies>
+				<tag k="bicycle" v="designated"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<and>
+					<tag k="highway" v="steps"/>
+					<not>
+						<key k="escalator"/>
+					</not>
+				</and>
+			</condition>
+			<implies>
+				<tag k="normal_steps" v="yes"/>
+			</implies>
+		</implication>
+		<implication>
+			<condition>
+				<or>
+					<tag k="construction" v="yes"/>
+					<tag k="disused" v="yes"/>
+					<tag k="abandoned" v="yes"/>
+				</or>
+			</condition>
+			<implies>
+				<tag k="usable" v="no"/>
+			</implies>
+		</implication>
+	</implications>
+	
+</accessRuleset>
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/FullGraphCreationTest.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/FullGraphCreationTest.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/FullGraphCreationTest.java	(revision 16520)
@@ -0,0 +1,182 @@
+package org.openstreetmap.josm.plugins.graphview.core;
+
+import static org.junit.Assert.assertSame;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.junit.Test;
+import org.openstreetmap.josm.plugins.graphview.core.TestDataSource.TestNode;
+import org.openstreetmap.josm.plugins.graphview.core.TestDataSource.TestRelation;
+import org.openstreetmap.josm.plugins.graphview.core.TestDataSource.TestRelationMember;
+import org.openstreetmap.josm.plugins.graphview.core.TestDataSource.TestWay;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessParameters;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessRuleset;
+import org.openstreetmap.josm.plugins.graphview.core.access.AccessType;
+import org.openstreetmap.josm.plugins.graphview.core.access.Implication;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.openstreetmap.josm.plugins.graphview.core.graph.TSBasedWayGraph;
+import org.openstreetmap.josm.plugins.graphview.core.graph.WayGraph;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadPropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadWidth;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyType;
+import org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes;
+import org.openstreetmap.josm.plugins.graphview.core.transition.GenericTransitionStructure;
+import org.openstreetmap.josm.plugins.graphview.core.transition.TransitionStructure;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.PreferenceAccessParameters;
+import org.openstreetmap.josm.plugins.graphview.plugin.preferences.VehiclePropertyStringParser.PropertyValueSyntaxException;
+
+public class FullGraphCreationTest {
+
+	private static final AccessParameters ACCESS_PARAMS;
+	static {
+		Map<VehiclePropertyType<?>, String> vehiclePropertyValues =
+			new HashMap<VehiclePropertyType<?>, String>();
+		vehiclePropertyValues.put(VehiclePropertyTypes.WIDTH, "3.0");
+
+		try {
+			ACCESS_PARAMS = new PreferenceAccessParameters(
+					"test_vehicle",
+					Arrays.asList(AccessType.UNDEFINED),
+					vehiclePropertyValues);
+		} catch (PropertyValueSyntaxException e) {
+			throw new Error(e);
+		}
+	}
+
+	private static final AccessRuleset TEST_RULESET = new AccessRuleset() {
+		public java.util.List<String> getAccessHierarchyAncestors(String transportMode) {
+			return Arrays.asList(transportMode);
+		}
+		public java.util.Collection<Tag> getBaseTags() {
+			return Arrays.asList(new Tag("highway", "test"));
+		}
+		public java.util.List<Implication> getImplications() {
+			return new LinkedList<Implication>();
+		}
+	};
+
+	@Test
+	public void testTJunction() {
+
+		TestDataSource ds = new TestDataSource();
+
+		TestNode nodeN = new TestNode(2, 1);
+		TestNode nodeW = new TestNode(1, 0);
+		TestNode nodeS = new TestNode(0, 1);
+		TestNode nodeC = new TestNode(1, 1);
+
+		ds.nodes.addAll(Arrays.asList(nodeN, nodeW, nodeS, nodeC));
+
+		TestWay wayNC = new TestWay();
+		wayNC.tags.put("highway", "test");
+		wayNC.nodes.addAll(Arrays.asList(nodeN, nodeC));
+		TestWay wayCS = new TestWay();
+		wayCS.tags.put("highway", "test");
+		wayCS.nodes.addAll(Arrays.asList(nodeC, nodeS));
+		TestWay wayCW = new TestWay();
+		wayCW.tags.put("highway", "test");
+		wayCW.nodes.addAll(Arrays.asList(nodeC, nodeW));
+
+		ds.ways.add(wayNC);
+		ds.ways.add(wayCS);
+		ds.ways.add(wayCW);
+
+		/* variant 1: no restrictions */
+		{
+			TransitionStructure ts1 = createTestTransitionStructure(ds);
+
+			assertSame(4, size(ts1.getNodes()));
+			assertSame(6, size(ts1.getSegments()));
+			assertSame(0, size(ts1.getRestrictions()));
+
+			WayGraph graph1 = new TSBasedWayGraph(ts1);
+
+			assertSame(12, graph1.getNodes().size());
+			assertSame(24, graph1.getEdges().size());
+		}
+		/* variant 2: no left turn from S to W */
+		{
+			ds.relations.add(createTurnRestrictionRelation(wayCS, nodeC, wayCW, "no_left_turn"));
+			TransitionStructure ts2 = createTestTransitionStructure(ds);
+
+			assertSame(4, size(ts2.getNodes()));
+			assertSame(6, size(ts2.getSegments()));
+			assertSame(1, size(ts2.getRestrictions()));
+
+			WayGraph graph2 = new TSBasedWayGraph(ts2);
+
+			assertSame(12, graph2.getNodes().size());
+			assertSame(23, graph2.getEdges().size());
+		}
+
+	}
+
+	@Test
+	public void testBarrier() {
+
+		TestDataSource ds = new TestDataSource();
+
+		TestNode node1 = new TestNode(0, 1);
+		TestNode nodeB = new TestNode(0, 2);
+		nodeB.tags.put("width", "1");
+		TestNode node2 = new TestNode(0, 3);
+
+		ds.nodes.addAll(Arrays.asList(node1, nodeB, node2));
+
+		TestWay way = new TestWay();
+		way.tags.put("highway", "test");
+		way.tags.put("oneway", "yes");
+		way.nodes.addAll(Arrays.asList(node1, nodeB, node2));
+		ds.ways.add(way);
+
+		/* variant 1: no restrictions */
+
+		TransitionStructure ts = createTestTransitionStructure(ds);
+
+		assertSame(3, size(ts.getNodes()));
+		assertSame(2, size(ts.getSegments()));
+		assertSame(1, size(ts.getRestrictions()));
+
+		WayGraph graph = new TSBasedWayGraph(ts);
+
+		assertSame(4, graph.getNodes().size());
+		assertSame(2, graph.getEdges().size());
+
+	}
+
+	private TestRelation createTurnRestrictionRelation(
+			TestWay from, TestNode via, TestWay to, String restriction) {
+		TestRelation resultRelation = new TestRelation();
+		resultRelation.tags.put("type", "restriction");
+		resultRelation.tags.put("restriction", restriction);
+		resultRelation.members.add(new TestRelationMember("from", from));
+		resultRelation.members.add(new TestRelationMember("via", via));
+		resultRelation.members.add(new TestRelationMember("to", to));
+		return resultRelation;
+	}
+
+	private TransitionStructure createTestTransitionStructure(TestDataSource dataSource) {
+
+		LinkedList<RoadPropertyType<?>> properties = new LinkedList<RoadPropertyType<?>>();
+		properties.add(new RoadWidth());
+
+		return new GenericTransitionStructure<TestNode, TestWay, TestRelation>(
+				TestNode.class, TestWay.class, TestRelation.class,
+				ACCESS_PARAMS, TEST_RULESET, dataSource, properties);
+	}
+
+	private static int size(Iterable<?> iterable) {
+		Iterator<?> iterator = iterable.iterator();
+		int size = 0;
+		while (iterator.hasNext()) {
+			iterator.next();
+			size ++;
+		}
+		return size;
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/TestDataSource.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/TestDataSource.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/TestDataSource.java	(revision 16520)
@@ -0,0 +1,124 @@
+package org.openstreetmap.josm.plugins.graphview.core;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.DataSourceObserver;
+import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+public class TestDataSource implements DataSource<TestDataSource.TestNode, TestDataSource.TestWay, TestDataSource.TestRelation> {
+
+	public static class TestPrimitive {
+		public final Map<String, String> tags = new HashMap<String, String>();
+	};
+
+	public static class TestNode extends TestPrimitive {
+		public final double lat;
+		public final double lon;
+		public TestNode() {
+			this(0, 0);
+		}
+		public TestNode(double lat, double lon) {
+			this.lat = lat;
+			this.lon = lon;
+		}
+		@Override
+		public String toString() {
+			return "(" + lat + ", " + lon + "); " + tags;
+		}
+	}
+
+	public static class TestWay extends TestPrimitive {
+		public final List<TestNode> nodes = new LinkedList<TestNode>();
+		@Override
+		public String toString() {
+			return nodes + "; " + tags;
+		}
+	}
+
+	public static class TestRelation extends TestPrimitive {
+		public final Collection<RelationMember> members = new LinkedList<RelationMember>();
+		@Override
+		public String toString() {
+			return members + "; " + tags;
+		}
+	}
+
+	public static class TestRelationMember implements RelationMember {
+		public final String role;
+		public final TestPrimitive member;
+		public TestRelationMember(String role, TestPrimitive member) {
+			this.role = role;
+			this.member = member;
+		}
+		public TestPrimitive getMember() {
+			return member;
+		}
+		public String getRole() {
+			return role;
+		}
+		@Override
+		public String toString() {
+			return role + "=" + member;
+		}
+	}
+
+
+	public final Collection<TestNode> nodes = new LinkedList<TestNode>();
+	public final Collection<TestWay> ways = new LinkedList<TestWay>();
+	public final Collection<TestRelation> relations = new LinkedList<TestRelation>();
+
+
+	public double getLat(TestNode node) {
+		return node.lat;
+	}
+	public double getLon(TestNode node) {
+		return node.lon;
+	}
+
+	public Iterable<RelationMember> getMembers(TestRelation relation) {
+		return relation.members;
+	}
+
+	public Iterable<TestNode> getNodes() {
+		return nodes;
+	}
+
+	public Iterable<TestNode> getNodes(TestWay way) {
+		return way.nodes;
+	}
+
+	public Iterable<TestWay> getWays() {
+		return ways;
+	}
+
+	public Iterable<TestRelation> getRelations() {
+		return relations;
+	}
+
+	public TagGroup getTagsN(TestNode node) {
+		return new MapBasedTagGroup(node.tags);
+	}
+
+	public TagGroup getTagsW(TestWay way) {
+		return new MapBasedTagGroup(way.tags);
+	}
+
+	public TagGroup getTagsR(TestRelation relation) {
+		return new MapBasedTagGroup(relation.tags);
+	}
+
+	public void addObserver(DataSourceObserver observer) {
+		// not needed for test
+	}
+
+	public void deleteObserver(DataSourceObserver observer) {
+		// not needed for test
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/access/AccessRulesetReaderTest.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/access/AccessRulesetReaderTest.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/access/AccessRulesetReaderTest.java	(revision 16520)
@@ -0,0 +1,110 @@
+package org.openstreetmap.josm.plugins.graphview.core.access;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+
+public class AccessRulesetReaderTest {
+
+	@Test
+	public void testReadAccessRuleset_valid_classes() throws IOException {
+
+		InputStream is = new FileInputStream("plugins/graphview/test/files/accessRuleset_valid.xml");
+		AccessRuleset ruleset = AccessRulesetReader.readAccessRuleset(is);
+		assertNotNull(ruleset);
+
+
+		assertEquals("vehicle", ruleset.getAccessHierarchyAncestors("vehicle").get(0));
+
+		assertEquals("motor_vehicle", ruleset.getAccessHierarchyAncestors("motor_vehicle").get(0));
+		assertEquals("vehicle", ruleset.getAccessHierarchyAncestors("motor_vehicle").get(1));
+
+		assertEquals("bus", ruleset.getAccessHierarchyAncestors("bus").get(0));
+		assertEquals("motor_vehicle", ruleset.getAccessHierarchyAncestors("bus").get(1));
+		assertEquals("vehicle", ruleset.getAccessHierarchyAncestors("bus").get(2));
+
+		assertEquals("bicycle", ruleset.getAccessHierarchyAncestors("bicycle").get(0));
+		assertEquals("vehicle", ruleset.getAccessHierarchyAncestors("bicycle").get(1));
+
+		assertFalse(ruleset.getAccessHierarchyAncestors("bus").contains("bicycle"));
+
+		assertSame(ruleset.getAccessHierarchyAncestors("boat").size(), 0);
+
+	}
+
+	@Test
+	public void testReadAccessRuleset_valid_basetags() throws IOException {
+
+		InputStream is = new FileInputStream("plugins/graphview/test/files/accessRuleset_valid.xml");
+		AccessRuleset ruleset = AccessRulesetReader.readAccessRuleset(is);
+		assertNotNull(ruleset);
+
+		assertSame(2, ruleset.getBaseTags().size());
+
+		assertTrue(ruleset.getBaseTags().contains(new Tag("highway", "residential")));
+		assertTrue(ruleset.getBaseTags().contains(new Tag("highway", "cycleway")));
+		assertFalse(ruleset.getBaseTags().contains(new Tag("building", "residential")));
+		assertFalse(ruleset.getBaseTags().contains(new Tag("highway", "stop")));
+
+	}
+
+	@Test
+	public void testReadAccessRuleset_valid_implications() throws IOException {
+
+		InputStream is = new FileInputStream("plugins/graphview/test/files/accessRuleset_valid.xml");
+		AccessRuleset ruleset = AccessRulesetReader.readAccessRuleset(is);
+		assertNotNull(ruleset);
+
+		List<Implication> implications = ruleset.getImplications();
+
+		assertSame(3, implications.size());
+
+		TagGroup[] tagGroups = new TagGroup[4];
+		tagGroups[0] = createTagGroup(new Tag("highway", "cycleway"));
+		tagGroups[1] = createTagGroup(new Tag("highway", "steps"));
+		tagGroups[2] = createTagGroup(new Tag("highway", "steps"), new Tag("escalator", "yes"));
+		tagGroups[3] = createTagGroup(new Tag("disused", "yes"), new Tag("construction", "no"));
+
+		for (Implication implication : implications) {
+			for (int i = 0; i < tagGroups.length; i++) {
+				tagGroups[i] = implication.apply(tagGroups[i]);
+			}
+		}
+
+		assertSame(2, tagGroups[0].size());
+		assertTrue(tagGroups[0].contains(new Tag("bicycle", "designated")));
+
+		assertSame(2, tagGroups[1].size());
+		assertTrue(tagGroups[1].contains(new Tag("normal_steps", "yes")));
+
+		assertSame(2, tagGroups[2].size());
+		assertFalse(tagGroups[2].contains(new Tag("normal_steps", "yes")));
+
+		assertSame(3, tagGroups[3].size());
+		assertTrue(tagGroups[3].contains(new Tag("usable", "no")));
+	}
+
+	private static TagGroup createTagGroup(Tag... tags) {
+		Map<String, String> tagMap = new HashMap<String, String>();
+		for (Tag tag : tags) {
+			tagMap.put(tag.key, tag.value);
+		}
+		return new MapBasedTagGroup(tagMap);
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadInclineTest.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadInclineTest.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadInclineTest.java	(revision 16520)
@@ -0,0 +1,24 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import org.junit.Test;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+
+public class RoadInclineTest extends RoadPropertyTest {
+
+	private static void testIncline(Float expectedInclineForward, Float expectedInclineBackward,
+			String inclineString) {
+
+		testEvaluateW(new RoadIncline(),
+				expectedInclineForward, expectedInclineBackward,
+				new Tag("incline", inclineString));
+	}
+
+	@Test
+	public void testEvaluate() {
+		testIncline(5f, -5f, "5 %");
+		testIncline(9.5f, -9.5f, "9.5 %");
+		testIncline(-2.5f, 2.5f, "-2.5%");
+		testIncline(null, null, "steep");
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxspeedTest.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxspeedTest.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadMaxspeedTest.java	(revision 16520)
@@ -0,0 +1,32 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import org.junit.Test;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+
+public class RoadMaxspeedTest extends RoadPropertyTest {
+
+	private static void testMaxspeed(float expectedMaxspeed, String maxspeedString) {
+		testEvaluateBoth(new RoadMaxspeed(),	expectedMaxspeed, new Tag("maxspeed", maxspeedString));
+	}
+
+	@Test
+	public void testEvaluate_numeric() {
+		testMaxspeed(30, "30");
+		testMaxspeed(48, "48.28");
+	}
+
+	@Test
+	public void testEvaluate_kmh() {
+		testMaxspeed(50, "50 km/h");
+		testMaxspeed(120, "120km/h");
+		testMaxspeed(30, "30	km/h");
+	}
+
+	@Test
+	public void testEvaluate_mph() {
+		testMaxspeed(72, "45 mph");
+		testMaxspeed(64, "40mph");
+		testMaxspeed(24, "15	mph");
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadPropertyTest.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadPropertyTest.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/property/RoadPropertyTest.java	(revision 16520)
@@ -0,0 +1,44 @@
+package org.openstreetmap.josm.plugins.graphview.core.property;
+
+import static org.junit.Assert.assertEquals;
+
+import org.openstreetmap.josm.plugins.graphview.core.TestDataSource;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+
+abstract public class RoadPropertyTest {
+
+	protected static <P> void testEvaluateW(RoadPropertyType<P> property, P expectedForward, P expectedBackward, Tag... wayTags) {
+
+		TestDataSource ds = new TestDataSource();
+		TestDataSource.TestWay testWay = new TestDataSource.TestWay();
+		for (Tag tag : wayTags) {
+			testWay.tags.put(tag.key, tag.value);
+		}
+		ds.ways.add(testWay);
+
+		assertEquals(expectedForward, property.evaluateW(testWay, true, null, ds));
+		assertEquals(expectedBackward, property.evaluateW(testWay, false, null, ds));
+
+	}
+
+	protected static <P> void testEvaluateN(RoadPropertyType<P> property, P expected, Tag... nodeTags) {
+
+		TestDataSource ds = new TestDataSource();
+		TestDataSource.TestNode testNode = new TestDataSource.TestNode();
+		for (Tag tag : nodeTags) {
+			testNode.tags.put(tag.key, tag.value);
+		}
+		ds.nodes.add(testNode);
+
+		RoadMaxspeed m = new RoadMaxspeed();
+
+		assertEquals(expected, m.evaluateN(testNode, null, ds));
+
+	}
+
+	protected static <P> void testEvaluateBoth(RoadPropertyType<P> property, P expected, Tag... nodeTags) {
+		testEvaluateW(property, expected, expected, nodeTags);
+		testEvaluateN(property, expected, nodeTags);
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/util/TagConditionLogicTest.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/util/TagConditionLogicTest.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/util/TagConditionLogicTest.java	(revision 16520)
@@ -0,0 +1,115 @@
+package org.openstreetmap.josm.plugins.graphview.core.util;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup;
+import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
+import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
+
+public class TagConditionLogicTest {
+
+	TagGroup groupA;
+	TagGroup groupB;
+
+	@Before
+	public void setUp() {
+		Map<String, String> mapA = new HashMap<String, String>();
+		mapA.put("key1", "value1");
+		mapA.put("key2", "value2");
+		mapA.put("key3", "value1");
+		groupA = new MapBasedTagGroup(mapA);
+
+		Map<String, String> mapB = new HashMap<String, String>();
+		mapB.put("key1", "value1");
+		mapB.put("key4", "value4");
+		groupB = new MapBasedTagGroup(mapB);
+	}
+
+	@Test
+	public void testTag() {
+		TagCondition condition = TagConditionLogic.tag(new Tag("key3", "value1"));
+		assertTrue(condition.matches(groupA));
+		assertFalse(condition.matches(groupB));
+	}
+
+	@Test
+	public void testKey() {
+		TagCondition condition = TagConditionLogic.key("key3");
+		assertTrue(condition.matches(groupA));
+		assertFalse(condition.matches(groupB));
+	}
+
+	@Test
+	public void testAnd() {
+		TagCondition condition1 = TagConditionLogic.tag(new Tag("key2", "value2"));
+		TagCondition conditionAnd1a = TagConditionLogic.and(condition1);
+		TagCondition conditionAnd1b = TagConditionLogic.and(Arrays.asList(condition1));
+
+		assertTrue(conditionAnd1a.matches(groupA));
+		assertTrue(conditionAnd1b.matches(groupA));
+		assertFalse(conditionAnd1a.matches(groupB));
+		assertFalse(conditionAnd1b.matches(groupB));
+
+		TagCondition condition2 = TagConditionLogic.tag(new Tag("key1", "value1"));
+		TagCondition conditionAnd2a = TagConditionLogic.and(condition1, condition2);
+		TagCondition conditionAnd2b = TagConditionLogic.and(Arrays.asList(condition1, condition2));
+
+		assertTrue(conditionAnd2a.matches(groupA));
+		assertTrue(conditionAnd2b.matches(groupA));
+		assertFalse(conditionAnd2a.matches(groupB));
+		assertFalse(conditionAnd2b.matches(groupB));
+
+		TagCondition condition3 = TagConditionLogic.tag(new Tag("key4", "value4"));
+		TagCondition conditionAnd3a = TagConditionLogic.and(condition1, condition2, condition3);
+		TagCondition conditionAnd3b = TagConditionLogic.and(Arrays.asList(condition1, condition2, condition3));
+
+		assertFalse(conditionAnd3a.matches(groupA));
+		assertFalse(conditionAnd3b.matches(groupA));
+		assertFalse(conditionAnd3a.matches(groupB));
+		assertFalse(conditionAnd3b.matches(groupB));
+	}
+
+	@Test
+	public void testOr() {
+		TagCondition condition1 = TagConditionLogic.tag(new Tag("key42", "value42"));
+		TagCondition conditionOr1a = TagConditionLogic.or(condition1);
+		TagCondition conditionOr1b = TagConditionLogic.or(Arrays.asList(condition1));
+
+		assertFalse(conditionOr1a.matches(groupA));
+		assertFalse(conditionOr1b.matches(groupA));
+		assertFalse(conditionOr1a.matches(groupB));
+		assertFalse(conditionOr1b.matches(groupB));
+
+		TagCondition condition2 = TagConditionLogic.tag(new Tag("key3", "value1"));
+		TagCondition conditionOr2a = TagConditionLogic.or(condition1, condition2);
+		TagCondition conditionOr2b = TagConditionLogic.or(Arrays.asList(condition1, condition2));
+
+		assertTrue(conditionOr2a.matches(groupA));
+		assertTrue(conditionOr2b.matches(groupA));
+		assertFalse(conditionOr2a.matches(groupB));
+		assertFalse(conditionOr2b.matches(groupB));
+
+		TagCondition condition3 = TagConditionLogic.tag(new Tag("key1", "value1"));
+		TagCondition conditionOr3a = TagConditionLogic.or(condition1, condition2, condition3);
+		TagCondition conditionOr3b = TagConditionLogic.or(Arrays.asList(condition1, condition2, condition3));
+
+		assertTrue(conditionOr3a.matches(groupA));
+		assertTrue(conditionOr3b.matches(groupA));
+		assertTrue(conditionOr3a.matches(groupB));
+		assertTrue(conditionOr3b.matches(groupB));
+	}
+
+	@Test
+	public void testNot() {
+		TagCondition condition = TagConditionLogic.not(TagConditionLogic.key("key3"));
+		assertFalse(condition.matches(groupA));
+		assertTrue(condition.matches(groupB));
+	}
+
+}
Index: /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/visualisation/FloatPropertyColorSchemeTest.java
===================================================================
--- /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/visualisation/FloatPropertyColorSchemeTest.java	(revision 16520)
+++ /applications/editors/josm/plugins/graphview/test/org/openstreetmap/josm/plugins/graphview/core/visualisation/FloatPropertyColorSchemeTest.java	(revision 16520)
@@ -0,0 +1,49 @@
+package org.openstreetmap.josm.plugins.graphview.core.visualisation;
+
+import static org.junit.Assert.assertEquals;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.josm.plugins.graphview.core.property.RoadMaxweight;
+
+public class FloatPropertyColorSchemeTest {
+
+	private FloatPropertyColorScheme subject;
+
+	@Before
+	public void setUp() {
+
+		Map<Float, Color> colorMap = new HashMap<Float, Color>();
+		colorMap.put( 5f, new Color( 42,  42,  42));
+		colorMap.put(10f, new Color(100, 100, 100));
+		colorMap.put(20f, new Color(200, 200, 200));
+
+		subject = new FloatPropertyColorScheme(RoadMaxweight.class, colorMap, Color.RED);
+	}
+
+	@Test
+	public void testGetColorForValue_below() {
+		assertEquals(new Color(42, 42, 42), subject.getColorForValue(1f));
+		assertEquals(new Color(42, 42, 42), subject.getColorForValue(5f));
+	}
+
+	@Test
+	public void testGetColorForValue_above() {
+		assertEquals(new Color(200, 200, 200), subject.getColorForValue(25f));
+	}
+
+	@Test
+	public void testGetColorForValue_value() {
+		assertEquals(new Color(100, 100, 100), subject.getColorForValue(10f));
+	}
+
+	@Test
+	public void testGetColorForValue_interpolate() {
+		assertEquals(new Color(150, 150, 150), subject.getColorForValue(15f));
+	}
+
+}
