[josm-plugins] 96/369: Imported Upstream version 0.0.svn18885

Bas Couwenberg sebastic at xs4all.nl
Sat Oct 18 12:03:30 UTC 2014


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

sebastic-guest pushed a commit to branch master
in repository josm-plugins.

commit 51e1ab63d6a41addff13e7b960ed7714c326991d
Author: Giovanni Mascellani <mascellani at poisson.phc.unipi.it>
Date:   Sat Dec 5 01:01:20 2009 +0100

    Imported Upstream version 0.0.svn18885
---
 {agpifoj => DirectUpload}/.classpath               |   0
 DirectUpload/.project                              |  17 +
 DirectUpload/LICENSE                               | 341 +++++++
 DirectUpload/README                                |   4 +
 {slippymap => DirectUpload}/build.xml              |  12 +-
 DirectUpload/images/UploadAction.png               | Bin 0 -> 105039 bytes
 DirectUpload/nbproject/build-impl.xml              | 629 +++++++++++++
 DirectUpload/nbproject/genfiles.properties         |   8 +
 DirectUpload/nbproject/private/private.properties  |   2 +
 DirectUpload/nbproject/private/private.xml         |   4 +
 DirectUpload/nbproject/project.properties          |  59 ++
 DirectUpload/nbproject/project.xml                 |  16 +
 .../josm/plugins/DirectUpload/UploadDataGui.form   | 164 ++++
 .../josm/plugins/DirectUpload/UploadDataGui.java   | 517 +++++++++++
 .../plugins/DirectUpload/UploadDataGuiPlugin.java  |  43 +
 agpifoj/.classpath                                 |   3 +-
 agpifoj/build.xml                                  |   2 +-
 .../josm/plugins/agpifoj/AgpifojLayer.java         |  54 +-
 .../plugins/agpifoj/CorrelateGpxWithImages.java    |  20 +-
 {measurement => cadastre-fr}/.classpath            |   2 +-
 cadastre-fr/.project                               |  17 +
 cadastre-fr/CONTRIBUTION                           |   4 +
 cadastre-fr/LICENSE                                | 341 +++++++
 cadastre-fr/README                                 |  26 +
 {livegps => cadastre-fr}/build.xml                 |  14 +-
 cadastre-fr/dist-lastest/readme.txt                |   4 +
 cadastre-fr/images/cadastre_small.png              | Bin 0 -> 222 bytes
 cadastre-fr/images/load.png                        | Bin 0 -> 883 bytes
 cadastre-fr/images/mapmode/adjustxywms.png         | Bin 0 -> 1300 bytes
 cadastre-fr/images/mapmode/adjustzwms.png          | Bin 0 -> 1468 bytes
 cadastre-fr/images/preferences/cadastrewms.gif     | Bin 0 -> 1068 bytes
 cadastre-fr/images/preferences/sel_box_1.png       | Bin 0 -> 435 bytes
 cadastre-fr/images/preferences/sel_box_2.png       | Bin 0 -> 426 bytes
 cadastre-fr/images/preferences/sel_box_3.png       | Bin 0 -> 452 bytes
 cadastre-fr/images/preferences/sel_box_4.png       | Bin 0 -> 507 bytes
 cadastre-fr/images/preferences/unsel_box_1.png     | Bin 0 -> 381 bytes
 cadastre-fr/images/preferences/unsel_box_2.png     | Bin 0 -> 371 bytes
 cadastre-fr/images/preferences/unsel_box_3.png     | Bin 0 -> 401 bytes
 cadastre-fr/images/preferences/unsel_box_4.png     | Bin 0 -> 451 bytes
 cadastre-fr/images/save.png                        | Bin 0 -> 845 bytes
 cadastre-fr/src/META-INF/MANIFEST.MF               |   6 +
 cadastre-fr/src/cadastre_fr/CacheControl.java      | 210 +++++
 .../cadastre_fr/CacheFileLambert4ZoneFilter.java   |  52 ++
 .../cadastre_fr/CacheFileLambert9ZoneFilter.java   |  59 ++
 .../src/cadastre_fr/CacheFileUTM20NFilter.java     |  53 ++
 cadastre-fr/src/cadastre_fr/CadastreGrabber.java   | 100 +++
 cadastre-fr/src/cadastre_fr/CadastreInterface.java | 508 +++++++++++
 cadastre-fr/src/cadastre_fr/CadastrePlugin.java    | 302 +++++++
 .../src/cadastre_fr/CadastrePreferenceSetting.java | 280 ++++++
 .../src/cadastre_fr/CheckSourceUploadHook.java     |  92 ++
 .../src/cadastre_fr/DownloadSVGBuilding.java       | 276 ++++++
 cadastre-fr/src/cadastre_fr/DownloadSVGTask.java   | 222 +++++
 .../src/cadastre_fr/DownloadWMSPlanImage.java      | 122 +++
 .../src/cadastre_fr/DownloadWMSVectorImage.java    |  82 ++
 .../src/cadastre_fr/DuplicateLayerException.java   |   5 +
 cadastre-fr/src/cadastre_fr/EastNorthBound.java    |  42 +
 cadastre-fr/src/cadastre_fr/GeorefImage.java       | 233 +++++
 cadastre-fr/src/cadastre_fr/ImageModifier.java     |  16 +
 .../src/cadastre_fr/MenuActionBoundaries.java      |  37 +
 .../src/cadastre_fr/MenuActionBuildings.java       |  37 +
 cadastre-fr/src/cadastre_fr/MenuActionGrab.java    |  46 +
 .../src/cadastre_fr/MenuActionGrabPlanImage.java   | 348 ++++++++
 .../src/cadastre_fr/MenuActionLoadFromCache.java   | 117 +++
 .../src/cadastre_fr/MenuActionNewLocation.java     |  99 ++
 .../src/cadastre_fr/MenuActionResetCookie.java     |  24 +
 .../src/cadastre_fr/MenuActionSaveRasterAs.java    |  74 ++
 .../src/cadastre_fr/RasterImageModifier.java       | 104 +++
 cadastre-fr/src/cadastre_fr/SVGParser.java         |  65 ++
 cadastre-fr/src/cadastre_fr/Scale.java             |  29 +
 cadastre-fr/src/cadastre_fr/SimplifyWay.java       | 128 +++
 .../src/cadastre_fr/VectorImageModifier.java       | 112 +++
 cadastre-fr/src/cadastre_fr/WMSAdjustAction.java   | 159 ++++
 cadastre-fr/src/cadastre_fr/WMSDownloadAction.java |  51 ++
 cadastre-fr/src/cadastre_fr/WMSLayer.java          | 613 +++++++++++++
 {measurement => colorscheme}/.classpath            |   2 +-
 colorscheme/.project                               |  17 +
 {livegps => editgpx}/.classpath                    |   3 +-
 editgpx/.project                                   |  24 +
 {slippymap => editgpx}/build.xml                   |  12 +-
 editgpx/images/editgpx_layer.png                   | Bin 0 -> 377 bytes
 editgpx/images/mapmode/editgpx_mode.png            | Bin 0 -> 1139 bytes
 .../josm/plugins/editgpx/EditGpxLayer.java         | 223 +++++
 .../josm/plugins/editgpx/EditGpxMode.java          | 134 +++
 .../josm/plugins/editgpx/EditGpxPlugin.java        | 115 +++
 .../josm/plugins/editgpx/GPXLayerImportAction.java | 143 +++
 {agpifoj => lakewalker}/.classpath                 |  14 +-
 lakewalker/.project                                |  17 +
 lakewalker/README                                  |  23 +
 {measurement => lakewalker}/build.xml              |   9 +-
 .../images/cursor/modifier/lakewalker-sml.png      | Bin 0 -> 1047 bytes
 lakewalker/images/lakewalker-sml.png               | Bin 0 -> 1047 bytes
 lakewalker/images/preferences/lakewalker.png       | Bin 0 -> 1097 bytes
 .../josm/plugins/lakewalker/BooleanConfigurer.java |  86 ++
 .../josm/plugins/lakewalker/Configurer.java        | 145 +++
 .../josm/plugins/lakewalker/DoubleConfigurer.java  |  63 ++
 .../josm/plugins/lakewalker/IntConfigurer.java     |  73 ++
 .../josm/plugins/lakewalker/Lakewalker.java        | 470 ++++++++++
 .../josm/plugins/lakewalker/LakewalkerAction.java  | 324 +++++++
 .../josm/plugins/lakewalker/LakewalkerApp.java     |  59 ++
 .../plugins/lakewalker/LakewalkerException.java    |  21 +
 .../josm/plugins/lakewalker/LakewalkerPlugin.java  |  25 +
 .../plugins/lakewalker/LakewalkerPreferences.java  | 160 ++++
 .../josm/plugins/lakewalker/LakewalkerReader.java  | 128 +++
 .../josm/plugins/lakewalker/LakewalkerWMS.java     | 234 +++++
 .../josm/plugins/lakewalker/StringConfigurer.java  |  81 ++
 .../plugins/lakewalker/StringEnumConfigurer.java   | 107 +++
 livegps/.classpath                                 |   2 +-
 livegps/build.xml                                  |   2 +-
 livegps/src/livegps/LiveGpsLayer.java              |   8 +-
 livegps/src/livegps/LiveGpsPlugin.java             |   3 +-
 measurement/.classpath                             |   2 +-
 measurement/build.xml                              |   2 +-
 .../josm/plugins/measurement/MeasurementLayer.java |   5 +-
 {measurement => routing}/.classpath                |   4 +-
 routing/.project                                   |  17 +
 {surveyor => routing}/build.xml                    |  32 +-
 routing/images/dialogs/routing.png                 | Bin 0 -> 1109 bytes
 routing/images/layer/routing_small.png             | Bin 0 -> 818 bytes
 routing/images/mapmode/add.png                     | Bin 0 -> 1030 bytes
 routing/images/mapmode/move.png                    | Bin 0 -> 964 bytes
 routing/images/mapmode/remove.png                  | Bin 0 -> 1043 bytes
 routing/images/mapmode/routing.png                 | Bin 0 -> 1109 bytes
 routing/images/preferences/routing.png             | Bin 0 -> 1109 bytes
 routing/images/routing/endflag.png                 | Bin 0 -> 568 bytes
 routing/images/routing/middleflag.png              | Bin 0 -> 514 bytes
 routing/images/routing/startflag.png               | Bin 0 -> 460 bytes
 routing/resources/log4j.xml                        |  22 +
 .../com/innovant/josm/jrt/core/EdgeIterator.java   |   9 +
 .../innovant/josm/jrt/core/PreferencesKeys.java    |  41 +
 .../com/innovant/josm/jrt/core/RoutingEdge.java    |  27 +
 .../com/innovant/josm/jrt/core/RoutingGraph.java   | 378 ++++++++
 .../josm/jrt/core/RoutingGraphDelegator.java       |  60 ++
 .../com/innovant/josm/jrt/core/RoutingProfile.java | 161 ++++
 .../innovant/josm/jrt/gtfs/GTFSTransportModes.java |  57 ++
 routing/src/com/innovant/josm/jrt/osm/OsmEdge.java | 108 +++
 .../src/com/innovant/josm/jrt/osm/OsmWayTypes.java |  79 ++
 .../innovant/josm/plugin/routing/RoutingLayer.java | 341 +++++++
 .../innovant/josm/plugin/routing/RoutingModel.java | 159 ++++
 .../josm/plugin/routing/RoutingPlugin.java         | 263 ++++++
 .../plugin/routing/actions/AddRouteNodeAction.java | 108 +++
 .../routing/actions/MoveRouteNodeAction.java       | 156 ++++
 .../routing/actions/RemoveRouteNodeAction.java     | 124 +++
 .../josm/plugin/routing/gui/RoutingDialog.java     | 156 ++++
 .../josm/plugin/routing/gui/RoutingMenu.java       | 195 ++++
 .../routing/gui/RoutingPreferenceDialog.java       | 235 +++++
 .../com/innovant/josm/plugin/routing/package.html  |   9 +
 slippymap/.classpath                               |   4 +-
 slippymap/build.xml                                |   2 +-
 .../josm/plugins/slippymap/SlippyMapLayer.java     | 992 ++++++++++++---------
 .../slippymap/SlippyMapPreferenceSetting.java      |  60 +-
 .../plugins/slippymap/SlippyMapPreferences.java    | 325 +++++--
 .../josm/plugins/slippymap/SlippyMapTile.java      | 188 ----
 surveyor/build.xml                                 |   2 +-
 .../surveyor/AutoSaveEditLayerTimerTask.java       |   3 +-
 .../josm/plugin/surveyor/action/SetNodeAction.java |   2 +-
 svn-info.xml                                       |   8 +-
 utilsplugin/build.xml                              |   2 +-
 utilsplugin/josm-utilsplugin.launch                |  12 +
 utilsplugin/src/UtilsPlugin/JoinAreasAction.java   |   5 +-
 utilsplugin/src/UtilsPlugin/JumpToAction.java      |   4 +-
 validator/build.xml                                | 109 ++-
 validator/ignoretags.cfg                           |  16 +-
 .../josm/plugins/validator/ErrorLayer.java         |   9 +-
 .../josm/plugins/validator/GridLayer.java          |   4 +-
 .../josm/plugins/validator/OSMValidatorPlugin.java |   2 +
 .../openstreetmap/josm/plugins/validator/Test.java |  39 +-
 .../josm/plugins/validator/TestError.java          |   2 +-
 .../josm/plugins/validator/ValidateAction.java     |   2 +-
 .../josm/plugins/validator/ValidateUploadHook.java |   2 +-
 .../josm/plugins/validator/ValidatorDialog.java    | 136 +--
 .../josm/plugins/validator/tests/Coastlines.java   | 186 +++-
 .../josm/plugins/validator/tests/CrossingWays.java |  14 +-
 .../plugins/validator/tests/DuplicateNode.java     |  19 +-
 .../josm/plugins/validator/tests/DuplicateWay.java |   6 +-
 .../josm/plugins/validator/tests/NameMismatch.java | 109 +++
 .../plugins/validator/tests/NodesWithSameName.java |  23 +-
 .../josm/plugins/validator/tests/TagChecker.java   |  52 +-
 .../josm/plugins/validator/tests/UnclosedWays.java |  70 +-
 .../plugins/validator/tests/UnconnectedWays.java   | 318 +++++--
 .../josm/plugins/validator/tests/UntaggedNode.java |   2 +-
 .../josm/plugins/validator/tests/UntaggedWay.java  |   6 +-
 .../validator/tests/WronglyOrderedWays.java        |   2 +
 .../josm/plugins/validator/util/NameVisitor.java   |   7 +-
 validator/tagchecker.cfg                           |   2 +-
 wmsplugin/.classpath                               |  14 +-
 wmsplugin/build.xml                                | 133 +--
 wmsplugin/sources.cfg                              |  28 +-
 wmsplugin/src/wmsplugin/GeorefImage.java           |  10 +-
 wmsplugin/src/wmsplugin/Help_WMSmenuAction.java    |  60 --
 .../src/wmsplugin/Map_Rectifier_WMSmenuAction.java |   2 +-
 wmsplugin/src/wmsplugin/WMSAdjustAction.java       |  45 +-
 wmsplugin/src/wmsplugin/WMSGrabber.java            |  24 +-
 wmsplugin/src/wmsplugin/WMSLayer.java              | 808 +++++++++--------
 wmsplugin/src/wmsplugin/WMSPlugin.java             |  39 +-
 wmsplugin/src/wmsplugin/WMSPreferenceEditor.java   |  62 +-
 195 files changed, 14736 insertions(+), 1718 deletions(-)

diff --git a/agpifoj/.classpath b/DirectUpload/.classpath
similarity index 100%
copy from agpifoj/.classpath
copy to DirectUpload/.classpath
diff --git a/DirectUpload/.project b/DirectUpload/.project
new file mode 100644
index 0000000..5a417af
--- /dev/null
+++ b/DirectUpload/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JOSM-DirectUpload</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>
diff --git a/DirectUpload/LICENSE b/DirectUpload/LICENSE
new file mode 100644
index 0000000..0d84b29
--- /dev/null
+++ b/DirectUpload/LICENSE
@@ -0,0 +1,341 @@
+
+            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.
diff --git a/DirectUpload/README b/DirectUpload/README
new file mode 100644
index 0000000..e718dcd
--- /dev/null
+++ b/DirectUpload/README
@@ -0,0 +1,4 @@
+Directly uploads GPX from active layer in JOSM to OpenStreetMap Server.
+This currently uses OSM Api 0.5.
+
+More Details and FAQ's at : http://wiki.openstreetmap.org/index.php/User:Subhodip/GSoC_Doc#DirectUpload_Plugin_in_JOSM_:
diff --git a/slippymap/build.xml b/DirectUpload/build.xml
similarity index 82%
copy from slippymap/build.xml
copy to DirectUpload/build.xml
index a0eed67..cbfd294 100644
--- a/slippymap/build.xml
+++ b/DirectUpload/build.xml
@@ -1,4 +1,4 @@
-<project name="slippymap" default="dist" basedir=".">
+<project name="DirectUpload" default="dist" basedir=".">
     <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
     <property name="plugin.dist.dir"        value="../../dist"/>
     <property name="plugin.build.dir"       value="build"/>
@@ -20,12 +20,12 @@
         </copy>
         <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
             <manifest>
-                <attribute name="Author" value="Frederik Ramm"/>
-                <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.slippymap.SlippyMapPlugin"/>
+                <attribute name="Author" value="Subhodip Biswas"/>
+                <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.DirectUpload.UploadDataGuiPlugin"/>
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
-                <attribute name="Plugin-Description" value="Displays a slippy map grid in JOSM. Can load tiles from slippy map as background and request updates."/>
-                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/SlippyMap"/>
-                <attribute name="Plugin-Mainversion" value="2196"/>
+                <attribute name="Plugin-Description" value="This plugin directly upload GPS Traces from current active layer in JOSM to openstreetmap.org."/>
+                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/User:Subhodip/GSoC_Doc#DirectUpload_Plugin_in_JOSM_:"/>
+                <attribute name="Plugin-Mainversion" value="2082"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/DirectUpload/images/UploadAction.png b/DirectUpload/images/UploadAction.png
new file mode 100644
index 0000000..6900786
Binary files /dev/null and b/DirectUpload/images/UploadAction.png differ
diff --git a/DirectUpload/nbproject/build-impl.xml b/DirectUpload/nbproject/build-impl.xml
new file mode 100644
index 0000000..7084414
--- /dev/null
+++ b/DirectUpload/nbproject/build-impl.xml
@@ -0,0 +1,629 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+  - initialization
+  - compilation
+  - jar
+  - execution
+  - debugging
+  - javadoc
+  - junit compilation
+  - junit execution
+  - junit debugging
+  - applet
+  - cleanup
+
+        -->
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="DirectUpload-impl">
+    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+    <!--
+                ======================
+                INITIALIZATION SECTION
+                ======================
+            -->
+    <target name="-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init" name="-init-private">
+        <property file="nbproject/private/config.properties"/>
+        <property file="nbproject/private/configs/${config}.properties"/>
+        <property file="nbproject/private/private.properties"/>
+    </target>
+    <target depends="-pre-init,-init-private" name="-init-user">
+        <property file="${user.properties.file}"/>
+        <!-- The two properties below are usually overridden -->
+        <!-- by the active platform. Just a fallback. -->
+        <property name="default.javac.source" value="1.4"/>
+        <property name="default.javac.target" value="1.4"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
+        <property file="nbproject/configs/${config}.properties"/>
+        <property file="nbproject/project.properties"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+        <available file="${manifest.file}" property="manifest.available"/>
+        <condition property="manifest.available+main.class">
+            <and>
+                <isset property="manifest.available"/>
+                <isset property="main.class"/>
+                <not>
+                    <equals arg1="${main.class}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="manifest.available+main.class+mkdist.available">
+            <and>
+                <istrue value="${manifest.available+main.class}"/>
+                <isset property="libs.CopyLibs.classpath"/>
+            </and>
+        </condition>
+        <condition property="have.tests">
+            <or>
+                <available file="${test.src.dir}"/>
+            </or>
+        </condition>
+        <condition property="have.sources">
+            <or>
+                <available file="${src.dir}"/>
+            </or>
+        </condition>
+        <condition property="netbeans.home+have.tests">
+            <and>
+                <isset property="netbeans.home"/>
+                <isset property="have.tests"/>
+            </and>
+        </condition>
+        <condition property="no.javadoc.preview">
+            <and>
+                <isset property="javadoc.preview"/>
+                <isfalse value="${javadoc.preview}"/>
+            </and>
+        </condition>
+        <property name="run.jvmargs" value=""/>
+        <property name="javac.compilerargs" value=""/>
+        <property name="work.dir" value="${basedir}"/>
+        <condition property="no.deps">
+            <and>
+                <istrue value="${no.dependencies}"/>
+            </and>
+        </condition>
+        <property name="javac.debug" value="true"/>
+        <property name="javadoc.preview" value="true"/>
+        <property name="application.args" value=""/>
+        <property name="source.encoding" value="${file.encoding}"/>
+        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+            <and>
+                <isset property="javadoc.encoding"/>
+                <not>
+                    <equals arg1="${javadoc.encoding}" arg2=""/>
+                </not>
+            </and>
+        </condition>
+        <property name="javadoc.encoding.used" value="${source.encoding}"/>
+        <property name="includes" value="**"/>
+        <property name="excludes" value=""/>
+        <property name="do.depend" value="false"/>
+        <condition property="do.depend.true">
+            <istrue value="${do.depend}"/>
+        </condition>
+        <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
+            <and>
+                <isset property="jaxws.endorsed.dir"/>
+                <available file="nbproject/jaxws-build.xml"/>
+            </and>
+        </condition>
+    </target>
+    <target name="-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
+        <fail unless="src.dir">Must set src.dir</fail>
+        <fail unless="test.src.dir">Must set test.src.dir</fail>
+        <fail unless="build.dir">Must set build.dir</fail>
+        <fail unless="dist.dir">Must set dist.dir</fail>
+        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+        <fail unless="dist.jar">Must set dist.jar</fail>
+    </target>
+    <target name="-init-macrodef-property">
+        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${@{value}}"/>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-javac">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="${javac.debug}" name="debug"/>
+            <attribute default="" name="sourcepath"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <sequential>
+                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </depend>
+            </sequential>
+        </macrodef>
+        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <sequential>
+                <fail unless="javac.includes">Must set javac.includes</fail>
+                <pathconvert pathsep="," property="javac.includes.binary">
+                    <path>
+                        <filelist dir="@{destdir}" files="${javac.includes}"/>
+                    </path>
+                    <globmapper from="*.java" to="*.class"/>
+                </pathconvert>
+                <delete>
+                    <files includes="${javac.includes.binary}"/>
+                </delete>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-junit">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <sequential>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
+                    <batchtest todir="${build.test.results.dir}">
+                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                    </batchtest>
+                    <classpath>
+                        <path path="${run.test.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-nbjpda">
+        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${main.class}" name="name"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <attribute default="" name="stopclassname"/>
+            <sequential>
+                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </nbjpdastart>
+            </sequential>
+        </macrodef>
+        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${build.classes.dir}" name="dir"/>
+            <sequential>
+                <nbjpdareload>
+                    <fileset dir="@{dir}" includes="${fix.classes}">
+                        <include name="${fix.includes}*.class"/>
+                    </fileset>
+                </nbjpdareload>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-debug-args">
+        <property name="version-output" value="java version "${ant.java.version}"/>
+        <condition property="have-jdk-older-than-1.4">
+            <or>
+                <contains string="${version-output}" substring="java version "1.0"/>
+                <contains string="${version-output}" substring="java version "1.1"/>
+                <contains string="${version-output}" substring="java version "1.2"/>
+                <contains string="${version-output}" substring="java version "1.3"/>
+            </or>
+        </condition>
+        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
+            <istrue value="${have-jdk-older-than-1.4}"/>
+        </condition>
+    </target>
+    <target depends="-init-debug-args" name="-init-macrodef-debug">
+        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${main.class}" name="classname"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java classname="@{classname}" dir="${work.dir}" fork="true">
+                    <jvmarg line="${debug-args-line}"/>
+                    <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-java">
+        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${main.class}" name="classname"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java classname="@{classname}" dir="${work.dir}" fork="true">
+                    <jvmarg line="${run.jvmargs}"/>
+                    <classpath>
+                        <path path="${run.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-presetdef-jar">
+        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <jar compress="${jar.compress}" jarfile="${dist.jar}">
+                <j2seproject1:fileset dir="${build.classes.dir}"/>
+            </jar>
+        </presetdef>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
+    <!--
+                ===================
+                COMPILATION SECTION
+                ===================
+            -->
+    <target depends="init" name="deps-jar" unless="no.deps"/>
+    <target depends="init,deps-jar" name="-pre-pre-compile">
+        <mkdir dir="${build.classes.dir}"/>
+    </target>
+    <target name="-pre-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="do.depend.true" name="-compile-depend">
+        <j2seproject3:depend/>
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
+        <j2seproject3:javac/>
+        <copy todir="${build.classes.dir}">
+            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+        </copy>
+    </target>
+    <target name="-post-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+    <target name="-pre-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:force-recompile/>
+        <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
+    </target>
+    <target name="-post-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+    <!--
+                ====================
+                JAR BUILDING SECTION
+                ====================
+            -->
+    <target depends="init" name="-pre-pre-jar">
+        <dirname file="${dist.jar}" property="dist.jar.dir"/>
+        <mkdir dir="${dist.jar.dir}"/>
+    </target>
+    <target name="-pre-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
+        <j2seproject1:jar/>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
+        <j2seproject1:jar manifest="${manifest.file}"/>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
+        <j2seproject1:jar manifest="${manifest.file}">
+            <j2seproject1:manifest>
+                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
+            </j2seproject1:manifest>
+        </j2seproject1:jar>
+        <echo>To run this application from the command line without Ant, try:</echo>
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
+        <pathconvert property="run.classpath.with.dist.jar">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+        </pathconvert>
+        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+        <pathconvert property="run.classpath.without.build.classes.dir">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to=""/>
+        </pathconvert>
+        <pathconvert pathsep=" " property="jar.classpath">
+            <path path="${run.classpath.without.build.classes.dir}"/>
+            <chainedmapper>
+                <flattenmapper/>
+                <globmapper from="*" to="lib/*"/>
+            </chainedmapper>
+        </pathconvert>
+        <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
+        <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
+            <fileset dir="${build.classes.dir}"/>
+            <manifest>
+                <attribute name="Main-Class" value="${main.class}"/>
+                <attribute name="Class-Path" value="${jar.classpath}"/>
+            </manifest>
+        </copylibs>
+        <echo>To run this application from the command line without Ant, try:</echo>
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
+        <echo>java -jar "${dist.jar.resolved}"</echo>
+    </target>
+    <target name="-post-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
+    <!--
+                =================
+                EXECUTION SECTION
+                =================
+            -->
+    <target depends="init,compile" description="Run a main class." name="run">
+        <j2seproject1:java>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <target name="-do-not-recompile">
+        <property name="javac.includes.binary" value=""/>
+    </target>
+    <target depends="init,-do-not-recompile,compile-single" name="run-single">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2seproject1:java classname="${run.class}"/>
+    </target>
+    <!--
+                =================
+                DEBUGGING SECTION
+                =================
+            -->
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
+        <j2seproject1:nbjpdastart name="${debug.class}"/>
+    </target>
+    <target depends="init,compile" name="-debug-start-debuggee">
+        <j2seproject3:debug>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
+        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+    </target>
+    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2seproject3:debug classname="${debug.class}"/>
+    </target>
+    <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+    <target depends="init" name="-pre-debug-fix">
+        <fail unless="fix.includes">Must set fix.includes</fail>
+        <property name="javac.includes" value="${fix.includes}.java"/>
+    </target>
+    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+        <j2seproject1:nbjpdareload/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+    <!--
+                ===============
+                JAVADOC SECTION
+                ===============
+            -->
+    <target depends="init" name="-javadoc-build">
+        <mkdir dir="${dist.javadoc.dir}"/>
+        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+            <classpath>
+                <path path="${javac.classpath}"/>
+            </classpath>
+            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
+                <filename name="**/*.java"/>
+            </fileset>
+        </javadoc>
+    </target>
+    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+    </target>
+    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+    <!--
+                =========================
+                JUNIT COMPILATION SECTION
+                =========================
+            -->
+    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+        <mkdir dir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-pre-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="do.depend.true" name="-compile-test-depend">
+        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
+        <copy todir="${build.test.classes.dir}">
+            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+    <target name="-pre-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
+        <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
+        <copy todir="${build.test.classes.dir}">
+            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+        </copy>
+    </target>
+    <target name="-post-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+    <!--
+                =======================
+                JUNIT EXECUTION SECTION
+                =======================
+            -->
+    <target depends="init" if="have.tests" name="-pre-test-run">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
+        <j2seproject3:junit testincludes="**/*Test.java"/>
+    </target>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+        <fail if="tests.failed">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init" if="have.tests" name="test-report"/>
+    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+    <target depends="init" if="have.tests" name="-pre-test-run-single">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <j2seproject3:junit excludes="" includes="${test.includes}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+        <fail if="tests.failed">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+    <!--
+                =======================
+                JUNIT DEBUGGING SECTION
+                =======================
+            -->
+    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
+        <delete file="${test.report.file}"/>
+        <mkdir dir="${build.test.results.dir}"/>
+        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
+            <customize>
+                <syspropertyset>
+                    <propertyref prefix="test-sys-prop."/>
+                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                </syspropertyset>
+                <arg value="${test.class}"/>
+                <arg value="showoutput=true"/>
+                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
+                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
+    </target>
+    <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+    <!--
+                =========================
+                APPLET EXECUTION SECTION
+                =========================
+            -->
+    <target depends="init,compile-single" name="run-applet">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject1:java classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <!--
+                =========================
+                APPLET DEBUGGING  SECTION
+                =========================
+            -->
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject3:debug classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
+    <!--
+                ===============
+                CLEANUP SECTION
+                ===============
+            -->
+    <target depends="init" name="deps-clean" unless="no.deps"/>
+    <target depends="init" name="-do-clean">
+        <delete dir="${build.dir}"/>
+        <delete dir="${dist.dir}"/>
+    </target>
+    <target name="-post-clean">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
+</project>
diff --git a/DirectUpload/nbproject/genfiles.properties b/DirectUpload/nbproject/genfiles.properties
new file mode 100644
index 0000000..249aeea
--- /dev/null
+++ b/DirectUpload/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=700b934e
+build.xml.script.CRC32=eedbed8a
+build.xml.stylesheet.CRC32=be360661
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=700b934e
+nbproject/build-impl.xml.script.CRC32=d2fe92a8
+nbproject/build-impl.xml.stylesheet.CRC32=487672f9
diff --git a/DirectUpload/nbproject/private/private.properties b/DirectUpload/nbproject/private/private.properties
new file mode 100644
index 0000000..46be75b
--- /dev/null
+++ b/DirectUpload/nbproject/private/private.properties
@@ -0,0 +1,2 @@
+jaxws.endorsed.dir=/usr/local/netbeans-6.1/java2/modules/ext/jaxws21/api
+user.properties.file=/home/subhodip/.netbeans/6.1/build.properties
diff --git a/DirectUpload/nbproject/private/private.xml b/DirectUpload/nbproject/private/private.xml
new file mode 100644
index 0000000..c1f155a
--- /dev/null
+++ b/DirectUpload/nbproject/private/private.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
+    <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
+</project-private>
diff --git a/DirectUpload/nbproject/project.properties b/DirectUpload/nbproject/project.properties
new file mode 100644
index 0000000..fcc99bc
--- /dev/null
+++ b/DirectUpload/nbproject/project.properties
@@ -0,0 +1,59 @@
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+debug.classpath=\
+    ${run.classpath}
+debug.test.classpath=\
+    ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/DirectUpload.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+excludes=
+includes=**
+jar.compress=false
+javac.classpath=
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.source=1.5
+javac.target=1.5
+javac.test.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}:\
+    ${libs.junit.classpath}:\
+    ${libs.junit_4.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+main.class=org.openstreetmap.josm.plugins.DirectUpload.UploadDataGui
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+platform.active=default_platform
+run.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+    ${javac.test.classpath}:\
+    ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/DirectUpload/nbproject/project.xml b/DirectUpload/nbproject/project.xml
new file mode 100644
index 0000000..0e89957
--- /dev/null
+++ b/DirectUpload/nbproject/project.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.java.j2seproject</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+            <name>DirectUpload</name>
+            <minimum-ant-version>1.6.5</minimum-ant-version>
+            <source-roots>
+                <root id="src.dir"/>
+            </source-roots>
+            <test-roots>
+                <root id="test.src.dir"/>
+            </test-roots>
+        </data>
+    </configuration>
+</project>
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.form b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.form
new file mode 100644
index 0000000..d00446e
--- /dev/null
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.form
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="2"/>
+    <Property name="alwaysOnTop" type="boolean" value="true"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <Group type="103" groupAlignment="1" attributes="0">
+                  <Group type="102" attributes="0">
+                      <EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="0" max="-2" attributes="0">
+                          <Component id="jLabel1" alignment="0" max="32767" attributes="1"/>
+                          <Component id="PublicTrace" alignment="0" min="-2" max="-2" attributes="1"/>
+                          <Component id="jLabel3" alignment="0" max="32767" attributes="1"/>
+                      </Group>
+                      <EmptySpace min="-2" pref="68" max="-2" attributes="0"/>
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="tagfield" pref="379" max="32767" attributes="0"/>
+                          <Group type="102" attributes="0">
+                              <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
+                              <EmptySpace pref="124" max="32767" attributes="0"/>
+                          </Group>
+                          <Component id="descriptionfield" pref="379" max="32767" attributes="0"/>
+                      </Group>
+                  </Group>
+                  <Group type="102" alignment="1" attributes="0">
+                      <EmptySpace pref="384" max="32767" attributes="0"/>
+                      <Component id="OkButton" min="-2" pref="79" max="-2" attributes="0"/>
+                      <EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
+                      <Component id="CancelButton" min="-2" pref="84" max="-2" attributes="0"/>
+                  </Group>
+                  <Group type="102" alignment="1" attributes="0">
+                      <EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
+                      <Component id="jScrollPane1" pref="527" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+              <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jLabel2" min="-2" pref="26" max="-2" attributes="0"/>
+              <EmptySpace min="-2" pref="34" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="descriptionfield" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="tagfield" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" pref="56" max="-2" attributes="0"/>
+              <Component id="PublicTrace" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jScrollPane1" min="-2" pref="103" max="-2" attributes="0"/>
+              <EmptySpace type="separate" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="OkButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="CancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="OutputDisplay">
+          <Properties>
+            <Property name="columns" type="int" value="20"/>
+            <Property name="rows" type="int" value="5"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JButton" name="OkButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="Ok"/>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="OkButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JButton" name="CancelButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="Cancel"/>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="CancelButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel2">
+      <Properties>
+        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+          <Font name="DejaVu Sans" size="14" style="1"/>
+        </Property>
+        <Property name="text" type="java.lang.String" value="Direct Upload to OpenStreetMap"/>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JCheckBox" name="PublicTrace">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="Public"/>
+        <Property name="toolTipText" type="java.lang.String" value="Selected makes your trace public in openstreetmap.org"/>
+      </Properties>
+      <Events>
+        <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="PublicTraceItemStateChanged"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel1">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="Description"/>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="descriptionfield">
+      <Properties>
+        <Property name="toolTipText" type="java.lang.String" value="Please enter Description about your trace."/>
+      </Properties>
+      <Events>
+        <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="descriptionfieldFocusLost"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="jLabel3">
+      <Properties>
+        <Property name="text" type="java.lang.String" value="Tags"/>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="tagfield">
+      <Properties>
+        <Property name="toolTipText" type="java.lang.String" value="Please enter tags about your trace."/>
+      </Properties>
+      <Events>
+        <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="tagfieldFocusLost"/>
+      </Events>
+    </Component>
+  </SubComponents>
+</Form>
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java
new file mode 100644
index 0000000..25f9416
--- /dev/null
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java
@@ -0,0 +1,517 @@
+/*
+ * UploadDataGui.java
+ *
+ * Created on August 17, 2008, 6:56 PM
+ * Copyright by Subhodip Biswas
+ * This program is free software and licensed under GPL.
+ */
+
+package org.openstreetmap.josm.plugins.DirectUpload;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.GpxWriter;
+import org.openstreetmap.josm.tools.Base64;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.UrlLabel;
+
+/**
+ *
+ * @author  subhodip, xeen
+ */
+public class UploadDataGui extends ExtendedDialog {
+    /**
+     * This enum contains the possible values for the visibility field and their
+     * explanation. Provides some methods for easier handling.
+     */
+    private enum visibility {
+        PRIVATE      (tr("Private (only shared as anonymous, unordered points)")),
+        PUBLIC       (tr("Public (shown in trace list and as anonymous, unordered points)")),
+        TRACKABLE    (tr("Trackable (only shared as anonymous, ordered points with timestamps)")),
+        IDENTIFIABLE (tr("Identifiable (shown in trace list and as identifiable, ordered points with timestamps)"));
+        
+        public final String description;
+        visibility(String description) {
+            this.description = description;
+        }
+        
+        /**
+         * "Converts" a given description into the actual enum. Returns null if no matching description
+         * is found.
+         * @param desc The description to look for
+         * @return visibility or null
+         */
+        public static visibility desc2visi(Object desc) {
+            for (visibility v : visibility.values()) {
+                if(desc.equals((String)v.description))
+                    return v;
+            }
+            return null;
+        }
+        
+        public String toString() {
+            return this.name().toLowerCase();
+        }
+    }
+
+	
+    // User for log in when uploading trace
+    private String username = Main.pref.get("osm-server.username");
+    private String password = Main.pref.get("osm-server.password");
+
+    // Fields are declared here for easy access
+    // Do not remove the space in JMultilineLabel. Otherwise the label will be empty
+    // as we don't know its contents yet and therefore have a height of 0. This will
+    // lead to unnecessary scrollbars.
+    private JMultilineLabel OutputDisplay = new JMultilineLabel(" ");
+    private JTextField descriptionField = new JTextField(50);
+    private JTextField tagsField = new JTextField(50);
+    private JComboBox visibilityCombo = new JComboBox();
+
+    // Constants used when generating upload request
+    private static final String API_VERSION = "0.6";
+    private static final String BOUNDARY = "----------------------------d10f7aa230e8";
+    private static final String LINE_END = "\r\n";
+    private static final String uploadTraceText = tr("Upload Trace");
+
+    // Filename and current date. Date will be used as fallback if filename not available
+    private String datename = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
+    private String filename = "";
+
+    private boolean cancelled = false;
+
+    public UploadDataGui() {
+        // Initalizes ExtendedDialog
+        super(Main.parent,
+                tr("Upload Traces"),
+                new String[] {uploadTraceText, tr("Cancel")},
+                true
+        );
+        JPanel content = initComponents();
+        autoSelectTrace();
+        setContent(content);
+        setButtonIcons(new String[] { "uploadtrace.png", "cancel.png" });
+        setupDialog();
+
+        buttons.get(0).setEnabled(!checkForGPXLayer());
+    }
+
+    /**
+     * Sets up the dialog window elements
+     * @return JPanel with components
+     */
+    private JPanel initComponents() {
+    	JLabel visibilityLabel = new JLabel(tr("Visibility"));
+        visibilityLabel.setToolTipText(tr("Defines the visibility of your trace for other OSM users."));
+        for(visibility v : visibility.values()) {
+        	visibilityCombo.addItem((String)v.description);
+        }
+        UrlLabel visiUrl = new UrlLabel(tr("http://wiki.openstreetmap.org/wiki/Visibility_of_GPS_traces"), tr("(What does that mean?)"));
+
+        JLabel descriptionLabel = new JLabel(tr("Description"));
+        descriptionField.setToolTipText(tr("Please enter Description about your trace."));
+
+        JLabel tagsLabel = new JLabel(tr("Tags (comma delimited)"));
+        tagsField.setToolTipText(tr("Please enter tags about your trace."));
+
+        JPanel p = new JPanel(new GridBagLayout());
+
+        OutputDisplay.setMaxWidth(findMaxDialogSize().width-10);
+        p.add(OutputDisplay, GBC.eol());
+
+        p.add(tagsLabel, GBC.eol().insets(0,10,0,0));
+        p.add(tagsField, GBC.eol().fill(GBC.HORIZONTAL));
+
+        p.add(descriptionLabel, GBC.eol().insets(0,10,0,0));
+        p.add(descriptionField, GBC.eol().fill(GBC.HORIZONTAL));
+
+        p.add(visibilityLabel, GBC.std().insets(0,10,0,0));
+        p.add(visiUrl, GBC.eol().insets(0,10,0,0));
+        p.add(visibilityCombo, GBC.eol());
+
+        return p;
+    }
+
+    /**
+     * This function will automatically select a GPX layer if it's the only one.
+     * If a GPX layer is found, its filename will be parsed and displayed
+     */
+    private void autoSelectTrace() {
+        // If no GPX layer is selected, select one for the user if there is only one GPX layer
+        if(Main.map != null && Main.map.mapView != null) {
+            MapView mv=Main.map.mapView;
+            if(!(mv.getActiveLayer() instanceof GpxLayer)) {
+                Layer lastLayer=null;
+                int layerCount=0;
+                for (Layer l : mv.getAllLayers()) {
+                    if(l instanceof GpxLayer) {
+                        lastLayer = l;
+                        layerCount++;
+                    }
+                }
+                if(layerCount == 1) mv.setActiveLayer(lastLayer);
+            }
+
+            if(mv.getActiveLayer() instanceof GpxLayer) {
+                GpxData data=((GpxLayer)Main.map.mapView.getActiveLayer()).data;
+                try {
+                    filename = data.storageFile.getName()
+                                    .replaceAll("[&?/\\\\]"," ").replaceAll("(\\.[^.]*)$","");
+                } catch(Exception e) { }
+                descriptionField.setText(getFilename());
+                OutputDisplay.setText(tr("Selected track: {0}", getFilename()));
+            }
+        }
+    }
+
+    /**
+     * This is the actual workhorse that manages the upload.
+     * @param String Description of the GPX track being uploaded
+     * @param String Tags assosciated with the GPX track being uploaded
+     * @param boolean Shall the GPX track be public
+     * @param GpxData The GPX Data to upload
+     */
+    private void upload(String description, String tags, String visi, GpxData gpxData, ProgressMonitor progressMonitor) throws IOException {
+    	progressMonitor.beginTask(null);
+    	try {
+    		if(checkForErrors(username, password, description, gpxData))
+    			return;
+
+    		// Clean description/tags from disallowed chars
+    		description = description.replaceAll("[&?/\\\\]"," ");
+    		tags = tags.replaceAll("[&?/\\\\.;]"," ");
+
+    		// Set progress dialog to indeterminate while connecting
+    		progressMonitor.indeterminateSubTask(tr("Connecting..."));
+
+    		try {
+    			// Generate data for upload
+    			ByteArrayOutputStream baos  = new ByteArrayOutputStream();
+    			writeGpxFile(baos, "file", gpxData);
+    			writeField(baos, "description", description);
+    			writeField(baos, "tags", (tags != null && tags.length() > 0) ? tags : "");
+    			writeField(baos, "visibility", visi);
+    			writeString(baos, "--" + BOUNDARY + "--" + LINE_END);
+
+    			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+    			HttpURLConnection conn = setupConnection(baos.size());
+
+    			progressMonitor.setTicksCount(baos.size());
+    			progressMonitor.subTask(null);
+
+    			try {
+    				flushToServer(bais, conn.getOutputStream(), progressMonitor);
+    			} catch(Exception e) {}
+
+    			if(cancelled) {
+    				conn.disconnect();
+    				OutputDisplay.setText(tr("Upload cancelled"));
+    				buttons.get(0).setEnabled(true);
+    				cancelled = false;
+    			} else {
+    				boolean success = finishUpConnection(conn);
+    				buttons.get(0).setEnabled(!success);
+    				if(success)
+    					buttons.get(1).setText(tr("Close"));
+    			}
+    		} catch(Exception e) {
+    			OutputDisplay.setText(tr("Error while uploading"));
+    			e.printStackTrace();
+    		}
+    	} finally {
+    		progressMonitor.finishTask();
+    	}
+    }
+
+    /**
+     * This function sets up the upload URL and logs in using the username and password given
+     * in the preferences.
+     * @param int The length of the content to be sent to the server
+     * @return HttpURLConnection The set up conenction
+     */
+    private HttpURLConnection setupConnection(int contentLength) throws Exception {
+        // Encode username and password
+        CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
+        String auth = username + ":" + password;
+        ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth));
+
+        // Upload URL
+        URL url = new URL("http://www.openstreetmap.org/api/" + API_VERSION + "/gpx/create");
+
+        // Set up connection and log in
+        HttpURLConnection c = (HttpURLConnection) url.openConnection();
+        c.setFixedLengthStreamingMode(contentLength);
+        c.setConnectTimeout(15000);
+        c.setRequestMethod("POST");
+        c.setDoOutput(true);
+        c.addRequestProperty("Authorization", "Basic " + Base64.encode(bytes));
+        c.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
+        c.addRequestProperty("Connection", "close"); // counterpart of keep-alive
+        c.addRequestProperty("Expect", "");
+        c.connect();
+
+        return c;
+    }
+
+    /**
+     * This function checks if the given connection finished up fine, closes it and returns the result.
+     * It also posts the result (or errors) to OutputDisplay.
+
+     * @param HttpURLConnection The connection to check/finish up
+     */
+    private boolean finishUpConnection(HttpURLConnection c) throws Exception {
+        String returnMsg = c.getResponseMessage();
+        boolean success = returnMsg.equals("OK");
+
+        if (c.getResponseCode() != 200) {
+            if (c.getHeaderField("Error") != null)
+                returnMsg += "\n" + c.getHeaderField("Error");
+        }
+
+        OutputDisplay.setText(success
+            ? tr("GPX upload was successful")
+            : tr("Upload failed. Server returned the following message: ") + returnMsg);
+
+        c.disconnect();
+        return success;
+    }
+
+    /**
+     * Uploads a given InputStream to a given OutputStream and sets progress
+     * @param InputSteam
+     * @param OutputStream
+     */
+    private void flushToServer(InputStream in, OutputStream out, ProgressMonitor progressMonitor) throws Exception {
+        // Upload in 10 kB chunks
+        byte[] buffer = new byte[10000];
+        int nread;
+        int cur = 0;
+        synchronized (in) {
+            while ((nread = in.read(buffer, 0, buffer.length)) >= 0) {
+                out.write(buffer, 0, nread);
+                cur += nread;
+                out.flush();
+                progressMonitor.worked(nread);
+                progressMonitor.subTask(getProgressText(cur, progressMonitor));
+
+                if(cancelled)
+                    break;
+            }
+        }
+        if(!cancelled)
+            out.flush();
+        progressMonitor.subTask("Waiting for server reply...");
+        buffer = null;
+    }
+
+    /**
+     * Generates the output string displayed in the PleaseWaitDialog.
+     * @param int Bytes already uploaded
+     * @return String Message
+     */
+    private String getProgressText(int cur, ProgressMonitor progressMonitor) {
+        int max = progressMonitor.getTicksCount();
+        int percent = Math.round(cur * 100 / max);
+        return tr("Uploading GPX track: {0}% ({1} of {2})",
+                        percent, formatBytes(cur), formatBytes(max));
+    }
+
+    /**
+     * Nicely calculates given bytes into MB, kB and B (with units)
+     * @param int Bytes
+     * @return String
+     */
+    private String formatBytes(int bytes) {
+        return (bytes > 1000 * 1000
+                    // Rounds to 2 decimal places
+                    ? new DecimalFormat("0.00")
+                        .format((double)Math.round(bytes/(1000*10))/100) + " MB"
+                    : (bytes > 1000
+                        ? Math.round(bytes/1000) + " kB"
+                        : bytes + " B"));
+    }
+
+    /**
+     * Checks for common errors and displays them in OutputDisplay if it finds any.
+     * Returns whether errors have been found or not.
+     * @param String OSM username
+     * @param String OSM password
+     * @param String GPX track description
+     * @param GpxData the GPX data to upload
+     * @return boolean true if errors have been found
+     */
+    private boolean checkForErrors(String username, String password,
+                                   String description, GpxData gpxData) {
+        String errors="";
+        if(description == null || description.length() == 0)
+            errors += tr("No description provided. Please provide some description.");
+
+        if(gpxData == null)
+            errors += tr("No GPX layer selected. Cannot upload a trace.");
+
+        if(username == null || username.length() == 0)
+            errors += tr("No username provided.");
+
+        if(password == null || password.length() == 0)
+            errors += tr("No password provided.");
+
+        OutputDisplay.setText(errors);
+        return errors.length() > 0;
+    }
+
+    /**
+     * Checks if a GPX layer is selected and returns the result. Also writes an error
+     * message to OutputDisplay if result is false.
+     * @return boolean True, if /no/ GPX layer is selected
+     */
+    private boolean checkForGPXLayer() {
+        if(Main.map == null
+                || Main.map.mapView == null
+                || Main.map.mapView.getActiveLayer() == null
+                || !(Main.map.mapView.getActiveLayer() instanceof GpxLayer)) {
+            OutputDisplay.setText(tr("No GPX layer selected. Cannot upload a trace."));
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * This creates the uploadTask that does the actual work and hands it to the main.worker to be executed.
+     */
+    private void setupUpload() {
+        if(checkForGPXLayer()) return;
+
+        // Disable Upload button so users can't just upload that track again
+        buttons.get(0).setEnabled(false);
+
+        PleaseWaitRunnable uploadTask = new PleaseWaitRunnable(tr("Uploading GPX Track")){
+            @Override protected void realRun() throws IOException {
+                  upload(descriptionField.getText(),
+                         tagsField.getText(),
+                         visibility.desc2visi(visibilityCombo.getSelectedItem()).toString(),
+                         ((GpxLayer)Main.map.mapView.getActiveLayer()).data,
+                         progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)
+                  );
+            }
+            @Override protected void finish() {}
+            @Override protected void cancel() {
+                cancelled = true;
+            }
+        };
+
+        Main.worker.execute(uploadTask);
+    }
+
+    /**
+     * Writes textfields (like in webbrowser) to the given ByteArrayOutputStream
+     * @param ByteArrayOutputStream
+     * @param String The name of the "textbox"
+     * @param String The value to write
+     */
+    private void writeField(ByteArrayOutputStream baos, String name, String value) throws IOException {
+        writeBoundary(baos);
+        writeString(baos, "Content-Disposition: form-data; name=\"" + name + "\"");
+        writeLineEnd(baos);
+        writeLineEnd(baos);
+        baos.write(value.getBytes("UTF-8"));
+        writeLineEnd(baos);
+    }
+
+    /**
+     * Writes gpxData (= file field in webbrowser) to the given ByteArrayOutputStream
+     * @param ByteArrayOutputStream
+     * @param String The name of the "upload field"
+     * @param GpxData The GPX data to upload
+     */
+    private void writeGpxFile(ByteArrayOutputStream baos, String name, GpxData gpxData) throws IOException {
+        writeBoundary(baos);
+        writeString(baos, "Content-Disposition: form-data; name=\"" + name + "\"; ");
+        writeString(baos, "filename=\"" + getFilename() + ".gpx" + "\"");
+        writeLineEnd(baos);
+        writeString(baos, "Content-Type: application/octet-stream");
+        writeLineEnd(baos);
+        writeLineEnd(baos);
+        new GpxWriter(baos).write(gpxData);
+        writeLineEnd(baos);
+    }
+
+    /**
+     * Writes a String to the given ByteArrayOutputStream
+     * @param ByteArrayOutputStream
+     * @param String
+     */
+    private void writeString(ByteArrayOutputStream baos, String s) {
+        try {
+            baos.write(s.getBytes());
+        } catch(Exception e) {}
+    }
+
+    /**
+     * Writes a newline to the given ByteArrayOutputStream
+     * @param ByteArrayOutputStream
+     */
+    private void writeLineEnd(ByteArrayOutputStream baos) {
+        writeString(baos, LINE_END);
+    }
+
+    /**
+     * Writes a boundary line to the given ByteArrayOutputStream
+     * @param ByteArrayOutputStream
+     */
+    private void writeBoundary(ByteArrayOutputStream baos) {
+        writeString(baos, "--" + BOUNDARY);
+        writeLineEnd(baos);
+    }
+
+    /**
+     * Returns the filename of the GPX file to be upload. If not available, returns current date
+     * as an alternative
+     * @param String
+     */
+    private String getFilename() {
+       return filename.equals("") ? datename : filename;
+    }
+
+    /**
+     * Overrides the default actions. Will not close the window when upload trace is clicked
+     */
+    @Override protected void buttonAction(ActionEvent evt) {
+        String a = evt.getActionCommand();
+        if(uploadTraceText.equals(a))
+            setupUpload();
+        else
+            setVisible(false);
+    }
+}
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java
new file mode 100644
index 0000000..aa5dea7
--- /dev/null
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java
@@ -0,0 +1,43 @@
+/*
+ *  Copyright by Subhodip Biswas
+ *  This program is free software and licensed under GPL.
+ *
+ */
+
+package org.openstreetmap.josm.plugins.DirectUpload;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.tools.Shortcut;
+/**
+ *
+ * @author subhodip
+ */
+public class UploadDataGuiPlugin extends Plugin{
+    UploadAction openaction;
+
+    public UploadDataGuiPlugin() {
+        openaction = new UploadAction();
+        Main.main.menu.toolsMenu.add(openaction);
+    }
+
+    class UploadAction extends JosmAction {
+
+        public UploadAction(){
+            super(tr("Upload Traces"), "UploadAction", tr("Uploads traces to openstreetmap.org"),
+            Shortcut.registerShortcut("tools:uploadtraces", tr("Tool: {0}", tr("Upload Traces")),
+            KeyEvent.VK_G, Shortcut.GROUP_MENU), false);
+        }
+        public void actionPerformed(ActionEvent e) {
+            UploadDataGui go = new UploadDataGui();
+            go.setVisible(true);
+        }
+    }
+}
\ No newline at end of file
diff --git a/agpifoj/.classpath b/agpifoj/.classpath
index b5edc13..07c06bb 100644
--- a/agpifoj/.classpath
+++ b/agpifoj/.classpath
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+	<classpathentry kind="lib" path="/JOSM/lib/metadata-extractor-2.3.1-nosun.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/agpifoj/build.xml b/agpifoj/build.xml
index 9008ceb..8a54e59 100644
--- a/agpifoj/build.xml
+++ b/agpifoj/build.xml
@@ -33,7 +33,7 @@
                 <attribute name="Plugin-Description" value="Another geotag plugin for JOSM. Correlates pictures with GPS tracks or import EXIF geotagged pictures."/>
                 <attribute name="Plugin-Early" value="false"/>
                 <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/AgPifoJ"/>
-                <attribute name="Plugin-Mainversion" value="2166"/>
+                <attribute name="Plugin-Mainversion" value="2450"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java
index b8010f8..789230c 100644
--- a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java
+++ b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java
@@ -8,7 +8,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 import static org.openstreetmap.josm.tools.I18n.trn;
 
 import java.awt.Component;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.event.MouseAdapter;
@@ -20,14 +20,17 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 
 import javax.swing.Icon;
 import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
 import javax.swing.JSeparator;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.CachedLatLon;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
@@ -101,11 +104,16 @@ public class AgpifojLayer extends Layer {
         private AgpifojLayer layer;
         private final File[] selection;
         private HashSet<String> loadedDirectories = new HashSet<String>();
-        private String errorMessage = "";
+        private LinkedHashSet<String> errorMessages;
+
+        protected void rememberError(String message) {
+        	this.errorMessages.add(message);
+        }
 
         public Loader(File[] selection) {
             super(tr("Extracting GPS locations from EXIF"));
             this.selection = selection;
+            errorMessages = new LinkedHashSet<String>();
         }
 
         @Override protected void realRun() throws IOException {
@@ -115,12 +123,12 @@ public class AgpifojLayer extends Layer {
             try {
                 addRecursiveFiles(files, selection);
             } catch(NullPointerException npe) {
-                errorMessage += tr("One of the selected files was null !!!");
+                rememberError(tr("One of the selected files was null"));
             }
 
             if (cancelled) {
                 return;
-            }            
+            }
             progressMonitor.subTask(tr("Read photos..."));
             progressMonitor.setTicksCount(files.size());
 
@@ -155,9 +163,6 @@ public class AgpifojLayer extends Layer {
             }
             layer = new AgpifojLayer(data);
             files.clear();
-            if (errorMessage != null && ! errorMessage.trim().equals("")) {
-            	progressMonitor.setErrorMessage(errorMessage);
-            }
         }
 
         private void addRecursiveFiles(List<File> files, File[] sel) {
@@ -178,8 +183,8 @@ public class AgpifojLayer extends Layer {
                         canonical = f.getCanonicalPath();
                     } catch (IOException e) {
                         e.printStackTrace();
-                        errorMessage += tr("Unable to get canonical path for directory {0}\n",
-                                           f.getAbsolutePath());
+                        rememberError(tr("Unable to get canonical path for directory {0}\n",
+                                           f.getAbsolutePath()));
                     }
 
                     if (canonical == null || loadedDirectories.contains(canonical)) {
@@ -195,10 +200,11 @@ public class AgpifojLayer extends Layer {
                             addRecursiveFiles(files, children);
                         } catch(NullPointerException npe) {
                             npe.printStackTrace();
-                            errorMessage += tr("Found null file in directory {0}\n", f.getPath());
+
+                            rememberError(tr("Found null file in directory {0}\n", f.getPath()));
                         }
                     } else {
-                    	errorMessage += tr("Error while getting files from directory {0}\n", f.getPath());
+                    	rememberError(tr("Error while getting files from directory {0}\n", f.getPath()));
                     }
 
                 } else {
@@ -211,7 +217,31 @@ public class AgpifojLayer extends Layer {
             }
         }
 
+        protected String formatErrorMessages() {
+        	StringBuffer sb = new StringBuffer();
+        	sb.append("<html>");
+    		if (errorMessages.size() == 1) {
+    			sb.append(errorMessages.iterator().next());
+    		} else {
+    			sb.append("<ul>");
+    			for (String msg: errorMessages) {
+    				sb.append("<li>").append(msg).append("</li>");
+    			}
+    			sb.append("/ul>");
+    		}
+    		sb.append("</html>");
+    		return sb.toString();
+        }
+
         @Override protected void finish() {
+        	if (!errorMessages.isEmpty()) {
+        		JOptionPane.showMessageDialog(
+        				Main.parent,
+        				formatErrorMessages(),
+        				tr("Error"),
+        				JOptionPane.ERROR_MESSAGE
+        		);
+        	}
             if (layer != null) {
                 Main.main.addLayer(layer);
                 layer.hook_up_mouse_events(); // Main.map.mapView should exist
@@ -331,7 +361,7 @@ public class AgpifojLayer extends Layer {
     }
 
     @Override
-    public void paint(Graphics g, MapView mv) {
+    public void paint(Graphics2D g, MapView mv, Bounds bounds) {
 
         int iconWidth = icon.getIconWidth() / 2;
         int iconHeight = icon.getIconHeight() / 2;
diff --git a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java
index 6d46151..602b989 100644
--- a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java
+++ b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java
@@ -95,7 +95,8 @@ public class CorrelateGpxWithImages implements ActionListener {
             this.file = file;
         }
 
-        public String toString() {
+        @Override
+		public String toString() {
             return name;
         }
     }
@@ -171,7 +172,7 @@ public class CorrelateGpxWithImages implements ActionListener {
                 } catch (SAXException x) {
                     x.printStackTrace();
                     JOptionPane.showMessageDialog(
-                    		Main.parent, 
+                    		Main.parent,
                     		tr("Error while parsing {0}",sel.getName())+": "+x.getMessage(),
                     		tr("Error"),
                     		JOptionPane.ERROR_MESSAGE
@@ -180,7 +181,7 @@ public class CorrelateGpxWithImages implements ActionListener {
                 } catch (IOException x) {
                     x.printStackTrace();
                     JOptionPane.showMessageDialog(
-                    		Main.parent, 
+                    		Main.parent,
                     		tr("Could not read \"{0}\"",sel.getName())+"\n"+x.getMessage(),
                     		tr("Error"),
                     		JOptionPane.ERROR_MESSAGE
@@ -385,8 +386,8 @@ public class CorrelateGpxWithImages implements ActionListener {
             boolean isOk = false;
             while (! isOk) {
                 int answer = JOptionPane.showConfirmDialog(
-                		Main.parent, panel, 
-                		tr("Synchronize time from a photo of the GPS receiver"), 
+                		Main.parent, panel,
+                		tr("Synchronize time from a photo of the GPS receiver"),
                 		JOptionPane.OK_CANCEL_OPTION,
                 		JOptionPane.QUESTION_MESSAGE
                 		);
@@ -636,11 +637,8 @@ public class CorrelateGpxWithImages implements ActionListener {
 
         // Search whether an other layer has yet defined some bounding box.
         // If none, we'll zoom to the bounding box of the layer with the photos.
-        Collection<Layer> layerCol = Main.map.mapView.getAllLayers();
-        Iterator<Layer> layerIter = layerCol.iterator();
         boolean boundingBoxedLayerFound = false;
-        while (layerIter.hasNext()) {
-            Layer l = layerIter.next();
+        for (Layer l: Main.map.mapView.getAllLayers()) {
             if (l != yLayer) {
                 BoundingXYVisitor bbox = new BoundingXYVisitor();
                 l.visitBoundingBox(bbox);
@@ -656,7 +654,7 @@ public class CorrelateGpxWithImages implements ActionListener {
             Main.map.mapView.recalculateCenterScale(bbox);
         }
 
-        Main.main.map.repaint();
+        Main.map.repaint();
 
         JOptionPane.showMessageDialog(Main.parent, tr("Found {0} matches of {1} in GPX track {2}", matched, dateImgLst.size(), selectedGpx.name),
                 tr("GPX Track loaded"),
@@ -885,7 +883,7 @@ public class CorrelateGpxWithImages implements ActionListener {
         // Settings are only saved temporarily to the layer.
         ExtendedDialog d = new ExtendedDialog(Main.parent,
             tr("Adjust timezone and offset"),
-            new String[] { tr("Close"),  tr("Default Values") }           
+            new String[] { tr("Close"),  tr("Default Values") }
         );
 
         d.setContent(p);
diff --git a/measurement/.classpath b/cadastre-fr/.classpath
similarity index 94%
copy from measurement/.classpath
copy to cadastre-fr/.classpath
index 05929d7..17b8e6a 100644
--- a/measurement/.classpath
+++ b/cadastre-fr/.classpath
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
 	<classpathentry kind="output" path="build"/>
 </classpath>
diff --git a/cadastre-fr/.project b/cadastre-fr/.project
new file mode 100644
index 0000000..5a26fbf
--- /dev/null
+++ b/cadastre-fr/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JOSM-Cadastre-fr</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>
diff --git a/cadastre-fr/CONTRIBUTION b/cadastre-fr/CONTRIBUTION
new file mode 100644
index 0000000..f1b8674
--- /dev/null
+++ b/cadastre-fr/CONTRIBUTION
@@ -0,0 +1,4 @@
+The French Cadastre plugin is designed and coded by Pieren <pieren3 at gmail.com>
+and is inspired by the wmsplugin for the special handling required by
+the french cadastre WMS www.cadatre.gouv.fr.
+This particular WMS provides vectorized or raster images for only one municipality at a time.
\ No newline at end of file
diff --git a/cadastre-fr/LICENSE b/cadastre-fr/LICENSE
new file mode 100644
index 0000000..fe31ef6
--- /dev/null
+++ b/cadastre-fr/LICENSE
@@ -0,0 +1,341 @@
+
+		    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.
diff --git a/cadastre-fr/README b/cadastre-fr/README
new file mode 100644
index 0000000..4561b8e
--- /dev/null
+++ b/cadastre-fr/README
@@ -0,0 +1,26 @@
+cadastre-fr is a plugin for JOSM that is able to handle the
+french land registry WMS. This WMS is not a complete standard WMS
+since it is not providing most of the standard services. It is also
+just delivering data for one municipality at a time. A special request
+is required at the beginning to specify which municipality is desired.
+
+The plugin is today only requesting PNG images on user request only. The
+images can be stored in a cache file and reloaded later. The grabed images
+can be done at any zoom level and the drawing method will always select the
+best images (highest scale) to be displayed in front of worst images
+(lowest scale).
+
+Also the plugin is able to transform original images to add the transparency
+or replace the default background by the JOSM background color. Enabling
+transparency gives the possibility to use several layers, each layer with a
+different municipality, making contrbutor's work much easier.
+
+Access to the french land registry WMS has been granted by the autorithies (DFFiP).
+It's opened not only for OSM but for all GIS applications. A new version of the
+WMS will take place mid 2009.
+
+Announce here (english):
+http://lists.openstreetmap.org/pipermail/talk/2009-January/033289.html
+First announcement here (french):
+http://lists.openstreetmap.org/pipermail/talk-fr/2009-January/005790.html
+
diff --git a/livegps/build.xml b/cadastre-fr/build.xml
similarity index 81%
copy from livegps/build.xml
copy to cadastre-fr/build.xml
index 1f7acd1..78ae672 100644
--- a/livegps/build.xml
+++ b/cadastre-fr/build.xml
@@ -1,4 +1,4 @@
-<project name="livegps" default="dist" basedir=".">
+<project name="cadastre-fr" default="dist" basedir=".">
     <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
     <property name="plugin.dist.dir"        value="../../dist"/>
     <property name="plugin.build.dir"       value="build"/>
@@ -20,13 +20,13 @@
         </copy>
         <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
             <manifest>
-                <attribute name="Author" value="Frederik Ramm"/>
-                <attribute name="Plugin-Class" value="livegps.LiveGpsPlugin"/>
+                <attribute name="Author" value="Pieren"/>
+                <attribute name="Plugin-Class" value="cadastre_fr.CadastrePlugin"/>
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
-                <attribute name="Plugin-Description" value="Support live GPS input (moving dot) through a connection to gpsd server."/>
-                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/LiveGPS"/>
-                <attribute name="Plugin-Mainversion" value="1890"/>
-                <attribute name="Plugin-Stage" value="50"/>
+                <attribute name="Plugin-Description" value="A special handler for the French land registry WMS server."/>
+                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/FR:JOSM/Fr:Plugin/Cadastre"/>
+                <attribute name="Plugin-Mainversion" value="2509"/>
+                <attribute name="Plugin-Stage" value="60"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/cadastre-fr/dist-lastest/readme.txt b/cadastre-fr/dist-lastest/readme.txt
new file mode 100644
index 0000000..4cfe411
--- /dev/null
+++ b/cadastre-fr/dist-lastest/readme.txt
@@ -0,0 +1,4 @@
+Here you find a temporary version of the plugin which works with josm-latest.jar
+where the official plugin location (in ../../dist/) works with josm-tested.jar.
+This build is committed manually and is not supported by the plugin manager of JOSM.
+Therefor, installation has to be done manually.
diff --git a/cadastre-fr/images/cadastre_small.png b/cadastre-fr/images/cadastre_small.png
new file mode 100644
index 0000000..ce02348
Binary files /dev/null and b/cadastre-fr/images/cadastre_small.png differ
diff --git a/cadastre-fr/images/load.png b/cadastre-fr/images/load.png
new file mode 100644
index 0000000..2019dc8
Binary files /dev/null and b/cadastre-fr/images/load.png differ
diff --git a/cadastre-fr/images/mapmode/adjustxywms.png b/cadastre-fr/images/mapmode/adjustxywms.png
new file mode 100644
index 0000000..2fa1964
Binary files /dev/null and b/cadastre-fr/images/mapmode/adjustxywms.png differ
diff --git a/cadastre-fr/images/mapmode/adjustzwms.png b/cadastre-fr/images/mapmode/adjustzwms.png
new file mode 100644
index 0000000..3a5fced
Binary files /dev/null and b/cadastre-fr/images/mapmode/adjustzwms.png differ
diff --git a/cadastre-fr/images/preferences/cadastrewms.gif b/cadastre-fr/images/preferences/cadastrewms.gif
new file mode 100644
index 0000000..1eccb90
Binary files /dev/null and b/cadastre-fr/images/preferences/cadastrewms.gif differ
diff --git a/cadastre-fr/images/preferences/sel_box_1.png b/cadastre-fr/images/preferences/sel_box_1.png
new file mode 100644
index 0000000..01d49f6
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_1.png differ
diff --git a/cadastre-fr/images/preferences/sel_box_2.png b/cadastre-fr/images/preferences/sel_box_2.png
new file mode 100644
index 0000000..d91f5af
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_2.png differ
diff --git a/cadastre-fr/images/preferences/sel_box_3.png b/cadastre-fr/images/preferences/sel_box_3.png
new file mode 100644
index 0000000..5b140c5
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_3.png differ
diff --git a/cadastre-fr/images/preferences/sel_box_4.png b/cadastre-fr/images/preferences/sel_box_4.png
new file mode 100644
index 0000000..d7dabc2
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_4.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_1.png b/cadastre-fr/images/preferences/unsel_box_1.png
new file mode 100644
index 0000000..8bb171a
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_1.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_2.png b/cadastre-fr/images/preferences/unsel_box_2.png
new file mode 100644
index 0000000..cb942a7
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_2.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_3.png b/cadastre-fr/images/preferences/unsel_box_3.png
new file mode 100644
index 0000000..a743628
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_3.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_4.png b/cadastre-fr/images/preferences/unsel_box_4.png
new file mode 100644
index 0000000..c99de83
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_4.png differ
diff --git a/cadastre-fr/images/save.png b/cadastre-fr/images/save.png
new file mode 100644
index 0000000..2949b3f
Binary files /dev/null and b/cadastre-fr/images/save.png differ
diff --git a/cadastre-fr/src/META-INF/MANIFEST.MF b/cadastre-fr/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..cfc5d09
--- /dev/null
+++ b/cadastre-fr/src/META-INF/MANIFEST.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Plugin-Class: cadastre_fr.CadastrePlugin
+Plugin-Description: A special handler for the French land registry WMS server
+Plugin-Version: 0.8
+Plugin-Stage: 60
+Plugin-Author: Pieren;<pieren3 at gmail.com>
diff --git a/cadastre-fr/src/cadastre_fr/CacheControl.java b/cadastre-fr/src/cadastre_fr/CacheControl.java
new file mode 100644
index 0000000..9ace78e
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheControl.java
@@ -0,0 +1,210 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+/**
+ * This class handles the WMS layer cache mechanism. The design is oriented for a good performance (no
+ * wait status on GUI, fast saving even in big file). A separate thread is created for each WMS
+ * layer to not suspend the GUI until disk I/O is terminated (a file for the cache can take
+ * several MB's). If the cache file already exists, new images are just appended to the file
+ * (performance). Since we use the ObjectStream methods, it is required to modify the standard
+ * ObjectOutputStream in order to have objects appended readable (otherwise a stream header
+ * is inserted before each append and an exception is raised at objects read).
+ */
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.projection.LambertCC9Zones;
+import org.openstreetmap.josm.data.projection.UTM_20N_France_DOM;
+
+public class CacheControl implements Runnable {
+    
+    public static final String cLambertCC9Z = "CC";
+
+    public static final String cUTM20N = "UTM";
+
+    public class ObjectOutputStreamAppend extends ObjectOutputStream {
+        public ObjectOutputStreamAppend(OutputStream out) throws IOException {
+            super(out);
+        }
+        protected void writeStreamHeader() throws IOException {
+            reset();
+        }
+    }
+
+    public static boolean cacheEnabled = true;
+
+    public static int cacheSize = 500;
+
+
+    public WMSLayer wmsLayer = null;
+
+    private ArrayList<GeorefImage> imagesToSave = new ArrayList<GeorefImage>();
+    private Lock imagesLock = new ReentrantLock();
+
+    public CacheControl(WMSLayer wmsLayer) {
+        cacheEnabled = Main.pref.getBoolean("cadastrewms.enableCaching", true);
+        this.wmsLayer = wmsLayer;
+        try {
+            cacheSize = Integer.parseInt(Main.pref.get("cadastrewms.cacheSize", String.valueOf(CadastrePreferenceSetting.DEFAULT_CACHE_SIZE)));
+        } catch (NumberFormatException e) {
+            cacheSize = CadastrePreferenceSetting.DEFAULT_CACHE_SIZE;
+        }
+        File path = new File(CadastrePlugin.cacheDir);
+        if (!path.exists())
+            path.mkdirs();
+        else // check directory capacity
+            checkDirSize(path);
+        new Thread(this).start();
+    }
+
+    private void checkDirSize(File path) {
+        long size = 0;
+        long oldestFileDate = Long.MAX_VALUE;
+        int oldestFile = 0;
+        File[] files = path.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            size += files[i].length();
+            if (files[i].lastModified() <  oldestFileDate) {
+                oldestFile = i;
+                oldestFileDate = files[i].lastModified();
+            }
+        }
+        if (size > cacheSize*1024*1024) {
+            System.out.println("Delete oldest file  \""+ files[oldestFile].getName()
+                    + "\" in cache dir to stay under the limit of " + cacheSize + " MB.");
+            files[oldestFile].delete();
+            checkDirSize(path);
+        }
+    }
+
+    public boolean loadCacheIfExist() {
+        try {
+            String extension = String.valueOf((wmsLayer.getLambertZone() + 1));
+            if (Main.proj instanceof LambertCC9Zones)
+                extension = cLambertCC9Z + extension;
+            else if (Main.proj instanceof UTM_20N_France_DOM)
+                extension = cUTM20N + extension;
+            File file = new File(CadastrePlugin.cacheDir + wmsLayer.getName() + "." + extension);
+            if (file.exists()) {
+                JOptionPane pane = new JOptionPane(
+                        tr("Location \"{0}\" found in cache.\n"+
+                        "Load cache first ?\n"+
+                        "(No = new cache)", wmsLayer.getName()),
+                        JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION, null);
+                // this below is a temporary workaround to fix the "always on top" issue
+                JDialog dialog = pane.createDialog(Main.parent, tr("Select Feuille"));
+                CadastrePlugin.prepareDialog(dialog);
+                dialog.setVisible(true);
+                int reply = (Integer)pane.getValue();
+                // till here
+
+                if (reply == JOptionPane.OK_OPTION && loadCache(file, wmsLayer.getLambertZone())) {
+                    return true;
+                } else {
+                    delete(file);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+        return false;
+    }
+
+    public void deleteCacheFile() {
+        try {
+            String extension = String.valueOf((wmsLayer.getLambertZone() + 1));
+            if (Main.proj instanceof LambertCC9Zones)
+                extension = cLambertCC9Z + extension;
+            delete(new File(CadastrePlugin.cacheDir + wmsLayer.getName() + "." + extension));
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+    }
+    
+    private void delete(File file) {
+        System.out.println("Delete file "+file);
+        if (file.exists())
+            file.delete();
+        while (file.exists()) // wait until file is really gone (otherwise appends to existing one)
+            CadastrePlugin.safeSleep(500);
+    }
+
+    public boolean loadCache(File file, int currentLambertZone) {
+        boolean successfulRead = false;
+        try {
+            FileInputStream fis = new FileInputStream(file);
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            successfulRead = wmsLayer.read(ois, currentLambertZone);
+            ois.close();
+            fis.close();
+        } catch (Exception ex) {
+            ex.printStackTrace(System.out);
+            JOptionPane.showMessageDialog(Main.parent, tr("Error loading file.\nProbably an old version of the cache file."), tr("Error"), JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+        if (successfulRead && wmsLayer.isRaster()) {
+            // serialized raster bufferedImage hangs-up on Java6. Recreate them here
+            wmsLayer.images.get(0).image = RasterImageModifier.fixRasterImage(wmsLayer.images.get(0).image);
+        }
+        return successfulRead;
+    }
+
+
+    public synchronized void saveCache(GeorefImage image) {
+        imagesLock.lock();
+        this.imagesToSave.add(image);
+        this.notify();
+        imagesLock.unlock();
+    }
+
+    /**
+     * Thread saving the grabbed images in background.
+     */
+    public synchronized void run() {
+        for (;;) {
+            imagesLock.lock();
+            // copy locally the images to save for better performance
+            ArrayList<GeorefImage> images = new ArrayList<GeorefImage>(imagesToSave);
+            imagesToSave.clear();
+            imagesLock.unlock();
+            if (images != null && !images.isEmpty()) {
+                String extension = String.valueOf((wmsLayer.getLambertZone() + 1));
+                if (Main.proj instanceof LambertCC9Zones)
+                    extension = cLambertCC9Z + extension;
+                else if (Main.proj instanceof UTM_20N_France_DOM)
+                    extension = cUTM20N + extension;
+                File file = new File(CadastrePlugin.cacheDir + wmsLayer.getName() + "." + extension);
+                try {
+                    if (file.exists()) {
+                        ObjectOutputStreamAppend oos = new ObjectOutputStreamAppend(
+                                new BufferedOutputStream(new FileOutputStream(file, true)));
+                        for (GeorefImage img : images) {
+                            oos.writeObject(img);
+                        }
+                        oos.close();
+                    } else {
+                        ObjectOutputStream oos = new ObjectOutputStream(
+                                new BufferedOutputStream(new FileOutputStream(file)));
+                        wmsLayer.write(oos);
+                        for (GeorefImage img : images) {
+                            oos.writeObject(img);
+                        }
+                        oos.close();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace(System.out);
+                }
+            }
+            try {wait();} catch (InterruptedException e) {
+                e.printStackTrace(System.out);
+            }
+        }
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/CacheFileLambert4ZoneFilter.java b/cadastre-fr/src/cadastre_fr/CacheFileLambert4ZoneFilter.java
new file mode 100644
index 0000000..3757b54
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheFileLambert4ZoneFilter.java
@@ -0,0 +1,52 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import java.io.File;
+import javax.swing.filechooser.FileFilter;
+
+public class CacheFileLambert4ZoneFilter extends FileFilter {
+
+    /**
+     * Derived from ExtensionFileFilter writen by imi
+     */
+    private final String extension;
+    private final String description;
+
+    public static CacheFileLambert4ZoneFilter[] filters = {
+        new CacheFileLambert4ZoneFilter("1", tr("Lambert Zone {0} cache file (.{0})", 1)),
+        new CacheFileLambert4ZoneFilter("2", tr("Lambert Zone {0} cache file (.{0})", 2)),
+        new CacheFileLambert4ZoneFilter("3", tr("Lambert Zone {0} cache file (.{0})", 3)),
+        new CacheFileLambert4ZoneFilter("4", tr("Lambert Zone {0} cache file (.{0})", 4))
+        };
+
+    /**
+     * Construct an extension file filter by giving the extension to check after.
+     *
+     */
+    private CacheFileLambert4ZoneFilter(String extension, String description) {
+        this.extension = extension;
+        this.description = description;
+    }
+
+    public boolean acceptName(String filename) {
+        String name = filename.toLowerCase();
+        for (String ext : extension.split(","))
+            if (name.endsWith("." + ext))
+                return true;
+        return false;
+    }
+
+    @Override
+    public boolean accept(File pathname) {
+        if (pathname.isDirectory())
+            return true;
+        return acceptName(pathname.getName());
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CacheFileLambert9ZoneFilter.java b/cadastre-fr/src/cadastre_fr/CacheFileLambert9ZoneFilter.java
new file mode 100644
index 0000000..b177eca
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheFileLambert9ZoneFilter.java
@@ -0,0 +1,59 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class CacheFileLambert9ZoneFilter extends FileFilter {
+
+    /**
+     * Derived from ExtensionFileFilter writen by imi
+     */
+    private final String extension;
+    private final String description;
+
+    public static CacheFileLambert9ZoneFilter[] filters = {
+        new CacheFileLambert9ZoneFilter("cc1", tr("Lambert CC9 Zone {0} cache file (.CC{0})",1)),
+        new CacheFileLambert9ZoneFilter("cc2", tr("Lambert CC9 Zone {0} cache file (.CC{0})",2)),
+        new CacheFileLambert9ZoneFilter("cc3", tr("Lambert CC9 Zone {0} cache file (.CC{0})",3)),
+        new CacheFileLambert9ZoneFilter("cc4", tr("Lambert CC9 Zone {0} cache file (.CC{0})",4)),
+        new CacheFileLambert9ZoneFilter("cc5", tr("Lambert CC9 Zone {0} cache file (.CC{0})",5)),
+        new CacheFileLambert9ZoneFilter("cc6", tr("Lambert CC9 Zone {0} cache file (.CC{0})",6)),
+        new CacheFileLambert9ZoneFilter("cc7", tr("Lambert CC9 Zone {0} cache file (.CC{0})",7)),
+        new CacheFileLambert9ZoneFilter("cc8", tr("Lambert CC9 Zone {0} cache file (.CC{0})",8)),
+        new CacheFileLambert9ZoneFilter("cc9", tr("Lambert CC9 Zone {0} cache file (.CC{0})",9))
+        };
+
+    /**
+     * Construct an extension file filter by giving the extension to check after.
+     *
+     */
+    private CacheFileLambert9ZoneFilter(String extension, String description) {
+        this.extension = extension;
+        this.description = description;
+    }
+
+    public boolean acceptName(String filename) {
+        String name = filename.toLowerCase();
+        for (String ext : extension.split(","))
+            if (name.endsWith("." + ext))
+                return true;
+        return false;
+    }
+
+    @Override
+    public boolean accept(File pathname) {
+        if (pathname.isDirectory())
+            return true;
+        return acceptName(pathname.getName());
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CacheFileUTM20NFilter.java b/cadastre-fr/src/cadastre_fr/CacheFileUTM20NFilter.java
new file mode 100644
index 0000000..cc30d0f
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheFileUTM20NFilter.java
@@ -0,0 +1,53 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class CacheFileUTM20NFilter extends FileFilter {
+
+    /**
+     * Derived from ExtensionFileFilter writen by imi
+     */
+    private final String extension;
+    private final String description;
+
+    public static CacheFileUTM20NFilter[] filters = {
+        new CacheFileUTM20NFilter("utm1", tr("Guadeloupe Fort-Marigot cache file (.UTM1)")),
+        new CacheFileUTM20NFilter("utm2", tr("Guadeloupe Ste-Anne cache file (.UTM2)")),
+        new CacheFileUTM20NFilter("utm3", tr("Martinique Fort Desaix cache file (.UTM3)"))
+        };
+
+    /**
+     * Construct an extension file filter by giving the extension to check after.
+     *
+     */
+    private CacheFileUTM20NFilter(String extension, String description) {
+        this.extension = extension;
+        this.description = description;
+    }
+
+    public boolean acceptName(String filename) {
+        String name = filename.toLowerCase();
+        for (String ext : extension.split(","))
+            if (name.endsWith("." + ext))
+                return true;
+        return false;
+    }
+
+    @Override
+    public boolean accept(File pathname) {
+        if (pathname.isDirectory())
+            return true;
+        return acceptName(pathname.getName());
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastreGrabber.java b/cadastre-fr/src/cadastre_fr/CadastreGrabber.java
new file mode 100644
index 0000000..aad2577
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastreGrabber.java
@@ -0,0 +1,100 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.ProgressInputStream;
+
+public class CadastreGrabber {
+
+    public final static double epsilon = 1e-11;
+
+    private CadastreInterface wmsInterface = new CadastreInterface();
+
+    public GeorefImage grab(WMSLayer wmsLayer, EastNorth lambertMin, EastNorth lambertMax) throws IOException, OsmTransferException {
+
+        try {
+            URL url = null;
+            if (wmsLayer.isRaster())
+                url = getURLRaster(wmsLayer, lambertMin, lambertMax);
+            else
+                url = getURLVector(lambertMin, lambertMax);
+            BufferedImage img = grab(url);
+            ImageModifier imageModified;
+            if (wmsLayer.isRaster())
+                imageModified = new RasterImageModifier(img);
+            else
+                imageModified = new VectorImageModifier(img);
+            return new GeorefImage(imageModified.bufferedImage, lambertMin, lambertMax);
+        } catch (MalformedURLException e) {
+            throw (IOException) new IOException(tr("CadastreGrabber: Illegal url.")).initCause(e);
+        }
+    }
+
+    private URL getURLRaster(WMSLayer wmsLayer, EastNorth lambertMin, EastNorth lambertMax) throws MalformedURLException {
+        // GET /scpc/wms?version=1.1&request=GetMap&layers=CDIF:PMC at QH4480001701&format=image/png&bbox=-1186,0,13555,8830&width=576&height=345&exception=application/vnd.ogc.se_inimage&styles= HTTP/1.1
+        final int cRasterX = 1000; // keep width constant and adjust width to original image proportions
+        String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+        str += "&layers=CDIF:PMC@";
+        str += wmsLayer.getCodeCommune();
+        str += "&format=image/png";
+        str += "&bbox=";
+        str += wmsLayer.eastNorth2raster(lambertMin, lambertMax);
+        //str += "&width=800&height=800"; // maximum allowed by wms server
+        str += "&width="+cRasterX+"&height="; // maximum allowed by wms server (576/345, 800/378, 1000/634)
+        str += (int)(cRasterX*(wmsLayer.communeBBox.max.getY() - wmsLayer.communeBBox.min.getY())/(wmsLayer.communeBBox.max.getX() - wmsLayer.communeBBox.min.getX()));
+        str += "&exception=application/vnd.ogc.se_inimage&styles=";
+        return new URL(str.replace(" ", "%20"));
+    }
+
+    private URL getURLVector(EastNorth lambertMin, EastNorth lambertMax) throws MalformedURLException {
+        String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+        str += "&layers=CDIF:LS3,CDIF:LS2,CDIF:LS1,CDIF:PARCELLE,CDIF:NUMERO";
+        str += ",CDIF:PT3,CDIF:PT2,CDIF:PT1,CDIF:LIEUDIT";
+        str += ",CDIF:SUBSECTION";
+        str += ",CDIF:SECTION";
+        str += ",CDIF:COMMUNE";
+        str += "&format=image/png";
+        //str += "&format=image/jpeg";
+        str += "&bbox="+lambertMin.east()+",";
+        str += lambertMin.north() + ",";
+        str += lambertMax.east() + ",";
+        str += lambertMax.north();
+        //str += "&width=800&height=600"; // maximum allowed by wms server
+        str += "&width=1000&height=800"; // maximum allowed by wms server
+        //str += "&exception=application/vnd.ogc.se_inimage"; // used by normal client but not required
+        str += "&styles=LS3_90,LS2_90,LS1_90,PARCELLE_90,NUMERO_90,PT3_90,PT2_90,PT1_90,LIEUDIT_90";
+        str += ",SUBSECTION_90";
+        str += ",SECTION_90";
+        str += ",COMMUNE_90";
+        System.out.println("URL="+str);
+        return new URL(str.replace(" ", "%20"));
+    }
+
+    private BufferedImage grab(URL url) throws IOException, OsmTransferException {
+        wmsInterface.urlConn = (HttpURLConnection)url.openConnection();
+        wmsInterface.urlConn.setRequestMethod("GET");
+        wmsInterface.setCookie();
+        InputStream is = new ProgressInputStream(wmsInterface.urlConn, NullProgressMonitor.INSTANCE);
+        BufferedImage img = ImageIO.read(is);
+        is.close();
+        return img;
+    }
+
+    public CadastreInterface getWmsInterface() {
+        return wmsInterface;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastreInterface.java b/cadastre-fr/src/cadastre_fr/CadastreInterface.java
new file mode 100644
index 0000000..b030d7e
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastreInterface.java
@@ -0,0 +1,508 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Vector;
+
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+
+public class CadastreInterface {
+    public boolean downloadCancelled = false;
+    public HttpURLConnection urlConn = null;
+
+    private String cookie;
+    private String interfaceRef = null;
+    private String lastWMSLayerName = null;
+    private URL searchFormURL;
+    private Vector<String> listOfCommunes = new Vector<String>();
+    private Vector<String> listOfTA = new Vector<String>();
+    class PlanImage {
+        String name;
+        String ref;
+        PlanImage(String name, String ref) {
+            this.name = name;
+            this.ref = ref;
+        }
+    }
+    private Vector<PlanImage> listOfFeuilles = new Vector<PlanImage>();
+
+    final String baseURL = "http://www.cadastre.gouv.fr";
+    final String cImageFormat = "Cette commune est au format ";
+    final String cCommuneListStart = "<select name=\"codeCommune\"";
+    final String cCommuneListEnd = "</select>";
+    final String c0ptionListStart = "<option value=\"";
+    final String cOptionListEnd = "</option>";
+    final String cBBoxCommunStart = "new GeoBox(";
+    final String cBBoxCommunEnd = ")";
+
+    final String cInterfaceVector = "afficherCarteCommune.do";
+    final String cInterfaceRasterTA = "afficherCarteTa.do";
+    final String cInterfaceRasterFeuille = "afficherCarteFeuille.do";
+    final String cImageLinkStart = "title=\"image\"><a href=\"#\" onClick=\"popup('afficherCarteFeuille.do?f=";
+    final String cImageNameStart = ">Feuille ";
+
+    public boolean retrieveInterface(WMSLayer wmsLayer) throws DuplicateLayerException {
+        if (wmsLayer.getName().equals(""))
+            return false;
+        // open the session with the French Cadastre web front end
+        downloadCancelled = false;
+        try {
+            if (cookie == null || !wmsLayer.getName().equals(lastWMSLayerName)) {
+                getCookie();
+                getInterface(wmsLayer);
+                this.lastWMSLayerName = wmsLayer.getName();
+            }
+            openInterface();
+        } catch (IOException e) {
+            /*JOptionPane.showMessageDialog(Main.parent,
+                    tr("Town/city {0} not found or not available\n" +
+                            "or action canceled", wmsLayer.getLocation()));*/
+            JOptionPane pane = new JOptionPane(
+                    tr("Town/city {0} not found or not available\n" +
+                            "or action canceled", wmsLayer.getLocation()),
+                            JOptionPane.INFORMATION_MESSAGE);
+            // this below is a temporary workaround to fix the "always on top" issue
+            JDialog dialog = pane.createDialog(Main.parent, tr("Select commune"));
+            CadastrePlugin.prepareDialog(dialog);
+            dialog.setVisible(true);
+            // till here
+            return false;
+        }
+        return true;
+    }
+
+    private void getCookie() throws IOException {
+        try {
+            // first, get the cookie from Cadastre to allow next downloads
+            searchFormURL = new URL(baseURL + "/scpc/rechercherPlan.do");
+            urlConn = (HttpURLConnection)searchFormURL.openConnection();
+            urlConn.setRequestMethod("GET");
+            urlConn.connect();
+            if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                throw new IOException("Cannot get Cadastre cookie.");
+            }
+            System.out.println("GET "+searchFormURL);
+            BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+            while(in.readLine() != null) {}  // read the buffer otherwise we sent POST too early
+            String headerName=null;
+            for (int i=1; (headerName = urlConn.getHeaderFieldKey(i))!=null; i++) {
+                if (headerName.equals("Set-Cookie")) {
+                    cookie = urlConn.getHeaderField(i);
+                    cookie = cookie.substring(0, cookie.indexOf(";"));
+                    System.out.println("Cookie="+cookie);
+                }
+            }
+        } catch (MalformedURLException e) {
+            throw (IOException) new IOException(
+                "Illegal url.").initCause(e);
+        }
+    }
+
+    public void resetCookie() {
+        lastWMSLayerName = null;
+    }
+
+    public void resetCookieIfNewLayer(String newWMSLayerName) {
+        if (!newWMSLayerName.equals(lastWMSLayerName)) {
+            resetCookie();
+        }
+    }
+
+    public void setCookie() {
+        this.urlConn.setRequestProperty("Cookie", this.cookie);
+    }
+
+    public void setCookie(HttpURLConnection urlConn) {
+        urlConn.setRequestProperty("Cookie", this.cookie);
+    }
+    
+    private void getInterface(WMSLayer wmsLayer) throws IOException, DuplicateLayerException {
+        // first attempt : search for given name without codeCommune
+        interfaceRef = postForm(wmsLayer, "");
+        // second attempt either from known codeCommune (e.g. from cache) or from ComboBox
+        if (interfaceRef == null) {
+            if (!wmsLayer.getCodeCommune().equals("")) {
+                // codeCommune is already known (from previous request or from cache on disk)
+                interfaceRef = postForm(wmsLayer, wmsLayer.getCodeCommune());
+            } else {
+                if (listOfCommunes.size() > 1) {
+                    // commune unknown, prompt the list of communes from
+                    // server and try with codeCommune
+                    wmsLayer.setCodeCommune(selectCommuneDialog());
+                    checkLayerDuplicates(wmsLayer);
+                    interfaceRef = postForm(wmsLayer, wmsLayer.getCodeCommune());
+                }
+                if (listOfCommunes.size() == 1 && wmsLayer.isRaster()) {
+                    // commune known but raster format. Select "Feuille" (non-georeferenced image) from list.
+                    int res = selectFeuilleDialog();
+                    if (res != -1) {
+                        // TODO
+                        wmsLayer.setCodeCommune(listOfFeuilles.elementAt(res).name);
+                        checkLayerDuplicates(wmsLayer);
+                        interfaceRef = buildRasterFeuilleInterfaceRef(wmsLayer.getCodeCommune());
+                    }
+                }
+            }
+        }
+
+        if (interfaceRef == null)
+            throw new IOException("Town/city " + wmsLayer.getLocation() + " not found.");
+    }
+
+    private void openInterface() throws IOException  {
+        try {
+            // finally, open the interface on server side giving access to the wms server
+            String lines = null;
+            String ln = null;
+            URL interfaceURL = new URL(baseURL + "/scpc/"+interfaceRef);
+            urlConn = (HttpURLConnection)interfaceURL.openConnection();
+            urlConn.setRequestMethod("GET");
+            setCookie();
+            urlConn.connect();
+            if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                throw new IOException("Cannot open Cadastre interface. GET response:"+urlConn.getResponseCode());
+            }
+            System.out.println("GET "+interfaceURL);
+            BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+            // read the buffer otherwise we sent POST too early
+            while ((ln = in.readLine()) != null) {
+                lines += ln;
+            }
+        } catch (MalformedURLException e) {
+            throw (IOException) new IOException(
+                "CadastreGrabber: Illegal url.").initCause(e);
+        }
+    }
+
+    /**
+     * Post the form with the commune name and check the returned answer which is embedded
+     * in HTTP XML packets. This function doesn't use an XML parser yet but that would be a good idea
+     * for the next releases.
+     * Two possibilities :
+     * - either the commune name matches and we receive an URL starting with "afficherCarteCommune.do" or
+     * - we don't receive a single answer but a list of possible values. This answer looks like:
+     *   <select name="codeCommune" class="long erreur" id="codeCommune">
+     *   <option value="">Choisir</option>
+     *   <option value="50061" >COLMARS - 04370</option>
+     *   <option value="QK066" >COLMAR - 68000</option>
+     *   </select>
+     * The returned string is the interface name used in further requests, e.g. "afficherCarteCommune.do?c=QP224"
+     * where QP224 is the code commune known by the WMS (or "afficherCarteTa.do?c=..." for raster images).
+     *
+     * @param location
+     * @param codeCommune
+     * @return retURL url to available code commune in the cadastre; "" if not found
+     * @throws IOException
+     */
+    private String postForm(WMSLayer wmsLayer, String codeCommune) throws IOException {
+        try {
+            String ln = null;
+            String lines = null;
+            listOfCommunes.clear();
+            listOfTA.clear();
+            // send a POST request with a city/town/village name
+            String content = "numerovoie=";
+            content += "&indiceRepetition=";
+            content += "&nomvoie=";
+            content += "&lieuDit=";
+            if (codeCommune == "") {
+                content += "&ville=" + new String(java.net.URLEncoder.encode(wmsLayer.getLocation(), "UTF-8"));
+                content += "&codePostal=";
+            } else {
+                content += "&codeCommune=" + codeCommune;
+            }
+            content += "&codeDepartement=";
+            content += "&nbResultatParPage=10";
+            urlConn = (HttpURLConnection)searchFormURL.openConnection();
+            urlConn.setRequestMethod("POST");
+            urlConn.setDoOutput(true);
+            urlConn.setDoInput(true);
+            setCookie();
+            OutputStream wr = urlConn.getOutputStream();
+            wr.write(content.getBytes());
+            System.out.println("POST "+content);
+            wr.flush();
+            wr.close();
+            BufferedReader rd = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+            while ((ln = rd.readLine()) != null) {
+                lines += ln;
+            }
+            rd.close();
+            urlConn.disconnect();
+            if (lines != null) {
+                if (lines.indexOf(cImageFormat) != -1) {
+                    int i = lines.indexOf(cImageFormat);
+                    int j = lines.indexOf(".", i);
+                    wmsLayer.setRaster(lines.substring(i+cImageFormat.length(), j).equals("image"));
+                }
+                if (!wmsLayer.isRaster() && lines.indexOf(cInterfaceVector) != -1) {  // "afficherCarteCommune.do"
+                    // shall be something like: interfaceRef = "afficherCarteCommune.do?c=X2269";
+                    lines = lines.substring(lines.indexOf(cInterfaceVector),lines.length());
+                    lines = lines.substring(0, lines.indexOf("'"));
+                    System.out.println("interface ref.:"+lines);
+                    return lines;
+                } else if (wmsLayer.isRaster() && lines.indexOf(cInterfaceRasterTA) != -1) { // "afficherCarteTa.do"
+                    // list of values parsed in listOfFeuilles (list all non-georeferenced images)
+                    lines = getFeuillesList();
+                    if (!downloadCancelled) {
+                        parseFeuillesList(lines);
+                        if (listOfFeuilles.size() > 0) {
+                            int res = selectFeuilleDialog();
+                            if (res != -1) {
+                                wmsLayer.setCodeCommune(listOfFeuilles.elementAt(res).name);
+                                checkLayerDuplicates(wmsLayer);
+                                interfaceRef = buildRasterFeuilleInterfaceRef(wmsLayer.getCodeCommune());
+                                wmsLayer.setCodeCommune(listOfFeuilles.elementAt(res).ref);
+                                lines = buildRasterFeuilleInterfaceRef(listOfFeuilles.elementAt(res).ref);
+                                System.out.println("interface ref.:"+lines);
+                                return lines;
+                            }
+                        }
+                    }
+                    return null;
+                } else if (lines.indexOf(cCommuneListStart) != -1 && lines.indexOf(cCommuneListEnd) != -1) {
+                    // list of values parsed in listOfCommunes
+                    int i = lines.indexOf(cCommuneListStart);
+                    int j = lines.indexOf(cCommuneListEnd, i);
+                    parseCommuneList(lines.substring(i, j));
+                }
+            }
+        } catch (MalformedURLException e) {
+            throw (IOException) new IOException(
+                "Illegal url.").initCause(e);
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private void parseCommuneList(String input) {
+        if (input.indexOf(c0ptionListStart) != -1) {
+            while (input.indexOf("<option value=\"") != -1) {
+                int i = input.indexOf(c0ptionListStart);
+                int j = input.indexOf(cOptionListEnd, i+c0ptionListStart.length());
+                int k = input.indexOf("\"", i+c0ptionListStart.length());
+                if (j != -1 && k > (i + c0ptionListStart.length())) {
+                    String lov = new String(input.substring(i+c0ptionListStart.length()-1, j));
+                    if (lov.indexOf(">") != -1) {
+                        System.out.println("parse "+lov);
+                        listOfCommunes.add(lov);
+                    } else
+                        System.err.println("unable to parse commune string:"+lov);
+                }
+                input = input.substring(j+cOptionListEnd.length());
+            }
+        }
+    }
+
+    private String getFeuillesList() {
+        // get all images in one html page
+        String ln = null;
+        String lines = null;
+        HttpURLConnection urlConn2 = null;
+        try {
+            URL getAllImagesURL = new URL(baseURL + "/scpc/listerFeuillesParcommune.do?keepVolatileSession=&offset=2000");
+            urlConn2 = (HttpURLConnection)getAllImagesURL.openConnection();
+            setCookie(urlConn2);
+            urlConn2.connect();
+            System.out.println("GET "+getAllImagesURL);
+            BufferedReader rd = new BufferedReader(new InputStreamReader(urlConn2.getInputStream()));
+            while ((ln = rd.readLine()) != null) {
+                lines += ln;
+            }
+            rd.close();
+            urlConn2.disconnect();
+            //System.out.println("GET="+lines);
+        } catch (IOException e) {
+            listOfFeuilles.clear();
+            e.printStackTrace();
+        }
+        return lines;
+    }
+    
+    private void parseFeuillesList(String input) {
+        listOfFeuilles.clear();
+        while (input.indexOf(cImageLinkStart) != -1) {
+            input = input.substring(input.indexOf(cImageLinkStart)+cImageLinkStart.length());
+            String refFeuille = input.substring(0, input.indexOf("'"));
+            String nameFeuille = input.substring(
+                    input.indexOf(cImageNameStart)+cImageNameStart.length(),
+                    input.indexOf(" -"));
+            listOfFeuilles.add(new PlanImage(nameFeuille, refFeuille));
+        }
+    }
+    
+    private String selectCommuneDialog() {
+        JPanel p = new JPanel(new GridBagLayout());
+        String[] communeList = new String[listOfCommunes.size() + 1];
+        communeList[0] = tr("Choose from...");
+        for (int i = 0; i < listOfCommunes.size(); i++) {
+            communeList[i + 1] = listOfCommunes.elementAt(i).substring(listOfCommunes.elementAt(i).indexOf(">")+1);
+        }
+        JComboBox inputCommuneList = new JComboBox(communeList);
+        p.add(inputCommuneList, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 0, 0, 0));
+        JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null) {
+            private static final long serialVersionUID = 1L;
+        };
+        //pane.createDialog(Main.parent, tr("Select commune")).setVisible(true);
+        // this below is a temporary workaround to fix the "always on top" issue
+        JDialog dialog = pane.createDialog(Main.parent, tr("Select commune"));
+        CadastrePlugin.prepareDialog(dialog);
+        dialog.setVisible(true);
+        // till here
+        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+            return null;
+        String result = listOfCommunes.elementAt(inputCommuneList.getSelectedIndex()-1);
+        return result.substring(1, result.indexOf(">")-2);
+    }
+
+    private int selectFeuilleDialog() {
+        JPanel p = new JPanel(new GridBagLayout());
+        Vector<String> ImageNames = new Vector<String>();
+        for (PlanImage src : listOfFeuilles) {
+            ImageNames.add(src.name);
+        }
+        JComboBox inputFeuilleList = new JComboBox(ImageNames);
+        p.add(inputFeuilleList, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 0, 0, 0));
+        JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null);
+        //pane.createDialog(Main.parent, tr("Select Feuille")).setVisible(true);
+        // this below is a temporary workaround to fix the "always on top" issue
+        JDialog dialog = pane.createDialog(Main.parent, tr("Select Feuille"));
+        CadastrePlugin.prepareDialog(dialog);
+        dialog.setVisible(true);
+        // till here
+        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+            return -1;
+        int result = inputFeuilleList.getSelectedIndex();
+        return result;
+    }
+
+    private String buildRasterFeuilleInterfaceRef(String codeCommune) {
+        return cInterfaceRasterFeuille + "?f=" + codeCommune;
+    }
+
+    /**
+     * Retrieve the bounding box size in pixels of the whole commune (point 0,0 at top, left corner)
+     * and store it in given wmsLayer
+     * In case of raster image, we also check in the same http request if the image is already georeferenced
+     * and store the result in the wmsLayer as well. 
+     * @param wmsLayer the WMSLayer where the commune data and images are stored
+     * @throws IOException
+     */
+    public void retrieveCommuneBBox(WMSLayer wmsLayer) throws IOException {
+        if (interfaceRef == null)
+            return;
+        String ln = null;
+        String line = null;
+        // send GET opening normally the small window with the commune overview
+        String content = baseURL + "/scpc/" + interfaceRef;
+        content += "&dontSaveLastForward&keepVolatileSession=";
+        searchFormURL = new URL(content);
+        urlConn = (HttpURLConnection)searchFormURL.openConnection();
+        urlConn.setRequestMethod("GET");
+        setCookie();
+        urlConn.connect();
+        if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
+            throw new IOException("Cannot get Cadastre response.");
+        }
+        System.out.println("GET "+searchFormURL);
+        BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+        while ((ln = in.readLine()) != null) {
+            line += ln;
+        }
+        in.close();
+        urlConn.disconnect();
+        parseBBoxCommune(wmsLayer, line);
+        if (wmsLayer.isRaster() && !wmsLayer.isAlreadyGeoreferenced()) {
+            parseGeoreferences(wmsLayer, line);
+        }
+    }
+
+    private void parseBBoxCommune(WMSLayer wmsLayer, String input) {
+        if (input.indexOf(cBBoxCommunStart) != -1) {
+            input = input.substring(input.indexOf(cBBoxCommunStart));
+            int i = input.indexOf(",");
+            double minx = Double.parseDouble(input.substring(cBBoxCommunStart.length(), i));
+            int j = input.indexOf(",", i+1);
+            double miny = Double.parseDouble(input.substring(i+1, j));
+            int k = input.indexOf(",", j+1);
+            double maxx = Double.parseDouble(input.substring(j+1, k));
+            int l = input.indexOf(cBBoxCommunEnd, k+1);
+            double maxy = Double.parseDouble(input.substring(k+1, l));
+            wmsLayer.setCommuneBBox( new EastNorthBound(new EastNorth(minx,miny), new EastNorth(maxx,maxy)));
+        }
+    }
+    
+    private void parseGeoreferences(WMSLayer wmsLayer, String input) {
+        if (input.lastIndexOf(cBBoxCommunStart) != -1) {
+            input = input.substring(input.lastIndexOf(cBBoxCommunStart));
+            input = input.substring(input.indexOf(cBBoxCommunEnd)+cBBoxCommunEnd.length());
+            int i = input.indexOf(",");
+            int j = input.indexOf(",", i+1);
+            double angle = Double.parseDouble(input.substring(i+1, j));
+            int k = input.indexOf(",", j+1);
+            double scale_origin = Double.parseDouble(input.substring(j+1, k));
+            int l = input.indexOf(",", k+1);
+            double dpi = Double.parseDouble(input.substring(k+1, l));
+            int m = input.indexOf(",", l+1);
+            double fX = Double.parseDouble(input.substring(l+1, m));
+            int n = input.indexOf(",", m+1);
+            double fY = Double.parseDouble(input.substring(m+1, n));
+            int o = input.indexOf(",", n+1);
+            double X0 = Double.parseDouble(input.substring(n+1, o));
+            int p = input.indexOf(",", o+1);
+            double Y0 = Double.parseDouble(input.substring(o+1, p));
+            if (X0 != 0.0 && Y0 != 0) {
+                wmsLayer.setAlreadyGeoreferenced(true);
+                wmsLayer.fX = fX;
+                wmsLayer.fY = fY;
+                wmsLayer.angle = angle;
+                wmsLayer.X0 = X0;
+                wmsLayer.Y0 = Y0;
+            }
+            System.out.println("parse georef:"+angle+","+scale_origin+","+dpi+","+fX+","+
+                    fY+","+X0+","+Y0);
+        }
+    }
+
+    private void checkLayerDuplicates(WMSLayer wmsLayer) throws DuplicateLayerException {
+        if (Main.map != null) {
+            for (Layer l : Main.map.mapView.getAllLayers()) {
+                if (l instanceof WMSLayer && l.getName().equals(wmsLayer.getName()) && (l != wmsLayer)) {
+                    System.out.println("Try to grab into a new layer when "+wmsLayer.getName()+" is already opened.");
+                    // remove the duplicated layer
+                    Main.map.mapView.removeLayer(wmsLayer);
+                    throw new DuplicateLayerException();
+                }
+            }
+        }
+    }
+
+    public void cancel() {
+        if (urlConn != null) {
+            urlConn.setConnectTimeout(1);
+            urlConn.setReadTimeout(1);
+            //urlConn.disconnect();
+        }
+        downloadCancelled = true;
+        lastWMSLayerName = null;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastrePlugin.java b/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
new file mode 100644
index 0000000..635f3fc
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
@@ -0,0 +1,302 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JDialog;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.KeyStroke;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.actions.UploadAction;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.data.projection.*;
+
+/**
+ *
+ * Plugin to access the French Cadastre WMS server at www.cadastre.gouv.fr This
+ * WMS server requires some specific handling like retrieving a cookie for a
+ * limitation in case of no activity, or the request to the server shall provide
+ * a city/town/village code.
+ *
+ * @author Pieren <pieren3 at gmail.com>,
+ * @author <matthieu.lochegnies at gmail.com> for the extension to codeCommune
+ * @version 0.8
+ * History:
+ * 0.1 17-Jun-2008 first prototype using a first Lambert projection impl. in core
+ * 0.2 22-Jun-2008 first stable version
+ * 0.3 24-Jun-2008 add code departement
+ * 0.4 06-Jul-2008 - add images scales, icons, menu items disabling
+ *                 - remove dependencies of wmsplugin
+ *                 - add option to force a Lambert zone (for median locations)
+ *                 - add auto-sourcing
+ * 0.5 16-Aug-2008 - add transparency in layer (allowing multiple wms layers displayed together)
+ *                 - no overlapping of grabbed images if transparency is enabled
+ *                 - set one layer per location
+ *                 - use utf-8 charset in POST request to server
+ *                 - improve the preferences setting dialog
+ *                 - cancel the current download is now possible
+ *                 - add automatic images caching and load on request (+ manage cache directory size)
+ *                 - enable auto-sourcing only if a WMS layer is used
+ * 0.6 18-Aug-2008 - suppress the null-exception message after the dialog 'open a layer first'
+ *                 - process the overlapping images when cache is loaded from disk
+ *                 - save the last 'new location request' text again in preferences
+ *                 - avoid duplicate layers with same name
+ *                 - set text input for new locations in upper case
+ *                 - the cache directory is configurable in "cadastrewms.cacheDir"
+ *                 - improve configuration change updates
+ * 0.7 24-Aug-2008 - mask images only if transparency enabled
+ *                 - validate projection name by Lambert.toString() method
+ * 0.8 25-Jan-2009 - display returned list of communes if direct name is not recognized by server
+ *                 - new possible grab factor of 100 square meters fixed size
+ *                 - minor fixes due to changes in JOSM core classes
+ *                 - first draft of raster image support
+ * 0.9 05-Feb-2009 - grab vectorized full commune bbox, save in file, convert to OSM way
+ *                   and simplify
+ * 1.0 18-Feb-2009 - fix various bugs in color management and preference dialog
+ *                 - increase PNG picture size requested to WMS (800x1000)
+ *                 - set 4th grab scale fixed size configurable (from 25 to 1000 meters)
+ * 1.1 11-Jun-2009 - fixed a null exception error when trying to displace a vectorized layer
+ *                 - propose to use shortcut F11 for grabbing
+ * 1.2 16-Aug-2009 - implementation of raster image grabbing, cropping and georeferencing (not the
+ *                   overview rasters (Tableau d'assemblage) but directly small units (Feuille)
+ * 1.3 23-Aug-2009 - improve georeferencing action cancellation
+ *                 - fixed bug of raster image loaded from cache not working on Java1.6
+ *                 - improve mouse click bounce detection during georeferencing process
+ * 1.4 25-Oct-2009 - add support for new Lambert CC 9 Zones projection
+ *                 - add optional crosspieces display on raster image layers
+ *                 - add automatic raster images georeferencing when WMS provides data
+ *                 - re-implement manual adjustment mode in raster image layer
+ * 1.5 21-Nov-2009 - major changes in projection in core : no magical zone prediction anymore for
+ *                   Lambert 4 and 9 zones; grid translation implemented for Lambert 4 zones;
+ *                   support of subprojections in preferences for zones setting and UTM20N
+ *                 - removed autosourcing of empty new nodes
+ * 1.6 28-Nov-2009 - Fix minor issues if Grab is called without layer (possible since projection rework)
+ */
+public class CadastrePlugin extends Plugin {
+    static String VERSION = "1.6";
+
+    static JMenu cadastreJMenu;
+
+    public static CadastreGrabber cadastreGrabber = new CadastreGrabber();
+
+    public static String source = "";
+
+    // true if the checkbox "auto-sourcing" is set in the plugin menu
+    public static boolean autoSourcing = false;
+
+    // true when the plugin is first used, e.g. grab from WMS or download cache file
+    public static boolean pluginUsed = false;
+
+    public static String cacheDir = null;
+
+    public static boolean alterColors = false;
+
+    public static boolean backgroundTransparent = false;
+
+    public static float transparency = 1.0f;
+
+    public static boolean drawBoundaries = false;
+
+    static private boolean menuEnabled = false;
+
+    /**
+     * Creates the plugin and setup the default settings if necessary
+     *
+     * @throws Exception
+     */
+    public CadastrePlugin() throws Exception {
+        System.out.println("Pluging \"cadastre-fr\" started...");
+        if (Main.pref.get("cadastrewms.cacheDir").equals(""))
+            cacheDir = Main.pref.getPreferencesDir()+"plugins/cadastrewms/";
+        else {
+            cacheDir = Main.pref.get("cadastrewms.cacheDir");
+            if (cacheDir.charAt(cacheDir.length()-1) != '\\' )
+                cacheDir += '\\';
+        }
+        System.out.println("current cache directory: "+cacheDir);
+
+        refreshConfiguration();
+
+        UploadAction.registerUploadHook(new CheckSourceUploadHook());
+
+    }
+
+    public static void refreshMenu() {
+        MainMenu menu = Main.main.menu;
+
+        if (cadastreJMenu == null) {
+            cadastreJMenu = menu.addMenu(marktr("Cadastre"), KeyEvent.VK_C, menu.defaultMenuPos, ht("/Plugin/CadastreFr"));
+            JosmAction grab = new MenuActionGrab();
+            JMenuItem menuGrab = new JMenuItem(grab);
+            KeyStroke ks = grab.getShortcut().getKeyStroke();
+            if (ks != null) {
+                menuGrab.setAccelerator(ks);
+            }
+            JMenuItem menuActionGrabPlanImage = new JMenuItem(new MenuActionGrabPlanImage());
+            JMenuItem menuSettings = new JMenuItem(new MenuActionNewLocation());
+            final JCheckBoxMenuItem menuSource = new JCheckBoxMenuItem(tr("Auto sourcing"));
+            menuSource.setSelected(autoSourcing);
+            menuSource.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent ev) {
+                    Main.pref.put("cadastrewms.autosourcing", menuSource.isSelected());
+                    autoSourcing = menuSource.isSelected();
+                }
+            });
+
+            JMenuItem menuResetCookie = new JMenuItem(new MenuActionResetCookie());
+            //JMenuItem menuLambertZone = new JMenuItem(new MenuActionLambertZone());
+            JMenuItem menuLoadFromCache = new JMenuItem(new MenuActionLoadFromCache());
+            // temporary disabled:
+            //JMenuItem menuActionBoundaries = new JMenuItem(new MenuActionBoundaries());
+            //JMenuItem menuActionBuildings = new JMenuItem(new MenuActionBuildings());
+
+            cadastreJMenu.add(menuGrab);
+            cadastreJMenu.add(menuActionGrabPlanImage);
+            cadastreJMenu.add(menuSettings);
+            cadastreJMenu.add(menuSource);
+            cadastreJMenu.add(menuResetCookie);
+            //cadastreJMenu.add(menuLambertZone);
+            cadastreJMenu.add(menuLoadFromCache);
+            // all SVG features disabled until official WMS is released
+            //cadastreJMenu.add(menuActionBoundaries);
+            //cadastreJMenu.add(menuActionBuildings);
+        }
+        setEnabledAll(menuEnabled);
+    }
+
+    public static void refreshConfiguration() {
+        source = checkSourceMillesime(); 
+        autoSourcing = Main.pref.getBoolean("cadastrewms.autosourcing", true);
+        alterColors = Main.pref.getBoolean("cadastrewms.alterColors");
+        drawBoundaries = Main.pref.getBoolean("cadastrewms.drawBoundaries", false);
+        if (alterColors) {
+            backgroundTransparent = Main.pref.getBoolean("cadastrewms.backgroundTransparent");
+            transparency = Float.parseFloat(Main.pref.get("cadastrewms.brightness", "1.0f"));
+        } else {
+            backgroundTransparent = false;
+            transparency = 1.0f;
+        }
+        // overwrite F11 shortcut used from the beginning by this plugin and recently used
+        // for full-screen switch in JOSM core
+        int i = 0;
+        String p = Main.pref.get("shortcut.shortcut."+i, null);
+        boolean alreadyRedefined = false;
+        while (p != null) {
+            String[] s = p.split(";");
+            alreadyRedefined = alreadyRedefined || s[0].equals("menu:view:fullscreen");
+            i++;
+            p = Main.pref.get("shortcut.shortcut."+i, null);
+        }
+        if (!alreadyRedefined) {
+            int reply = JOptionPane.showConfirmDialog(null,
+                    tr("Plugin cadastre-fr used traditionaly for grabbing the key shortcut F11\n"+
+                    "which is currently allocated for full-screen switch by default\n"+
+                    "Would you like to restore F11 for grab action ?"),
+                    tr("Restore grab shortcut F11"),
+                    JOptionPane.YES_NO_OPTION);
+            if (reply == JOptionPane.OK_OPTION) {
+                System.out.println("redefine fullscreen shortcut F11 to shift+F11");
+                Main.pref.put("shortcut.shortcut."+i, "menu:view:fullscreen;Toggle Full Screen view;122;5;122;64;false;true");
+                JOptionPane.showMessageDialog(Main.parent,tr("JOSM is stopped for the change to take effect."));
+                System.exit(0);
+            }
+        } else
+            System.out.println("shortcut F11 already redefined; do not change");
+
+        refreshMenu();
+    }
+
+    @Override
+    public PreferenceSetting getPreferenceSetting() {
+        return new CadastrePreferenceSetting();
+    }
+
+    private static void setEnabledAll(boolean isEnabled) {
+        for (int i = 0; i < cadastreJMenu.getItemCount(); i++) {
+            JMenuItem item = cadastreJMenu.getItem(i);
+            if (item != null)
+                if (item.getText().equals(MenuActionGrabPlanImage.name) /*||
+                    item.getText().equals(MenuActionGrab.name) ||
+                    item.getText().equals(MenuActionBoundaries.name) ||
+                    item.getText().equals(MenuActionBuildings.name)*/) {
+                    item.setEnabled(isEnabled);
+                }
+        }
+        menuEnabled = isEnabled;
+    }
+
+    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+        if (cadastreJMenu != null) {
+            if (oldFrame == null && newFrame != null) {
+                setEnabledAll(true);
+                Main.map.addMapMode(new IconToggleButton
+                        (new WMSAdjustAction(Main.map)));
+            } else if (oldFrame != null && newFrame == null) {
+                setEnabledAll(false);
+                //Lambert.layoutZone = -1;
+                //LambertCC9Zones.layoutZone = -1;
+            }
+        }
+    }
+    
+    public static boolean isCadastreProjection() {
+        return Main.proj.toString().equals(new Lambert().toString())
+            || Main.proj.toString().equals(new UTM_20N_France_DOM().toString())
+            || Main.proj.toString().equals(new GaussLaborde_Reunion().toString())
+            || Main.proj.toString().equals(new LambertCC9Zones().toString());
+    }
+
+    public static void safeSleep(long milliseconds) {
+        try {
+            Thread.sleep(milliseconds);
+        } catch (InterruptedException e) {}
+    }
+
+    // See OptionPaneUtil
+    // FIXME: this is a temporary solution. 
+    public static void prepareDialog(JDialog dialog) {
+        if (Main.pref.getBoolean("window-handling.option-pane-always-on-top", true)) {
+            try {
+                dialog.setAlwaysOnTop(true);
+            } catch(SecurityException e) {
+                System.out.println(tr("Warning: failed to put option pane dialog always on top. Exception was: {0}", e.toString()));
+            }
+        }
+        dialog.setModal(true);
+        dialog.toFront();
+        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+    }
+    
+    private static String checkSourceMillesime() {
+        java.util.Calendar calendar = java.util.Calendar.getInstance();
+        int currentYear = calendar.get(java.util.Calendar.YEAR);
+        String src = Main.pref.get("cadastrewms.source",
+            "cadastre-dgi-fr source : Direction G\u00e9n\u00e9rale des Imp\u00f4ts - Cadastre. Mise \u00e0 jour : AAAA");
+        String srcYear = src.substring(src.lastIndexOf(" ")+1);
+        Integer year = null;
+        try {
+            year = Integer.decode(srcYear);
+        } catch (NumberFormatException e) {}
+        if (srcYear.equals("AAAA") || (year != null && year < currentYear)) {
+            System.out.println("Replace source year "+srcYear+" by current year "+currentYear);
+            src = src.substring(0, src.lastIndexOf(" ")+1)+currentYear;
+            Main.pref.put("cadastrewms.source", src);
+        }
+        return src;
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java b/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
new file mode 100644
index 0000000..972e7d0
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
@@ -0,0 +1,280 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.*;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.I18n;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Preference settings for the French Cadastre plugin
+ *
+ * @author Pieren <pieren3 at gmail.com>
+ */
+public class CadastrePreferenceSetting implements PreferenceSetting {
+
+    static final int TRANS_MIN = 1;
+    static final int TRANS_MAX = 10;
+    private JSlider sliderTrans = new JSlider(JSlider.HORIZONTAL, TRANS_MIN, TRANS_MAX, TRANS_MAX);
+
+    private JTextField sourcing = new JTextField(20);
+
+    private JCheckBox alterColors = new JCheckBox(tr("Replace original background by JOSM background color."));
+
+    private JCheckBox reversGrey = new JCheckBox(tr("Reverse grey colors (for black backgrounds)."));
+
+    private JCheckBox transparency = new JCheckBox(tr("Set background transparent."));
+
+    private JCheckBox drawBoundaries = new JCheckBox(tr("Draw boundaries of downloaded data."));
+
+    private JCheckBox disableImageCropping = new JCheckBox(tr("Disable image cropping during georeferencing."));
+    
+    private JRadioButton grabMultiplier1 = new JRadioButton("", true);
+
+    private JRadioButton grabMultiplier2 = new JRadioButton("", true);
+
+    private JRadioButton grabMultiplier3 = new JRadioButton("", true);
+
+    private JRadioButton grabMultiplier4 = new JRadioButton("", true);
+    
+    private JRadioButton crosspiece1 = new JRadioButton("off");
+    
+    private JRadioButton crosspiece2 = new JRadioButton("50m");
+
+    private JRadioButton crosspiece3 = new JRadioButton("100m");
+
+    static final int DEFAULT_SQUARE_SIZE = 100;
+    private JTextField grabMultiplier4Size = new JTextField(5);
+
+    private JCheckBox enableCache = new JCheckBox(tr("Enable automatic caching."));
+
+    static final int DEFAULT_CACHE_SIZE = 500;
+    JLabel jLabelCacheSize = new JLabel(tr("Max. cache size (in MB)"));
+    private JTextField cacheSize = new JTextField(20);
+    
+    static final String DEFAULT_RASTER_DIVIDER = "5";
+    private JTextField rasterDivider = new JTextField(10);
+
+    static final int DEFAULT_CROSSPIECES = 0;
+    
+    public void addGui(final PreferenceDialog gui) {
+
+        String description = tr("A special handler of the French cadastre wms at www.cadastre.gouv.fr" + "<BR><BR>"
+                + "Please read the Terms and Conditions of Use here (in French): <br>"
+                + "<a href=\"http://www.cadastre.gouv.fr/scpc/html/CU_01_ConditionsGenerales_fr.html\"> "
+                + "http://www.cadastre.gouv.fr/scpc/html/CU_01_ConditionsGenerales_fr.html</a> <BR>"
+                + "before any upload of data created by this plugin.");
+        JPanel cadastrewms = gui.createPreferenceTab("cadastrewms.gif", I18n.tr("French cadastre WMS"), description);
+
+        // option to automatically set the source tag when uploading
+        sourcing.setText(CadastrePlugin.source);
+        sourcing.setToolTipText(tr("<html>Value of key \"source\" when autosourcing is enabled</html>"));
+        JLabel jLabelSource = new JLabel(tr("Source"));
+        cadastrewms.add(jLabelSource, GBC.eop().insets(0, 0, 0, 0));
+        cadastrewms.add(sourcing, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+
+        // option to alter the original colors of the wms images
+        alterColors.setSelected(Main.pref.getBoolean("cadastrewms.alterColors", false));
+        alterColors.setToolTipText(tr("Replace the original white background by the backgound color defined in JOSM preferences."));
+        cadastrewms.add(alterColors, GBC.eop().insets(0, 0, 0, 0));
+
+        // option to reverse the grey colors (to see texts background)
+        reversGrey.setSelected(Main.pref.getBoolean("cadastrewms.invertGrey", false));
+        reversGrey.setToolTipText(tr("Invert the original black and white colors (and all intermediate greys). Useful for texts on dark backgrounds."));
+        cadastrewms.add(reversGrey, GBC.eop().insets(00, 0, 0, 0));
+
+        // option to enable transparency
+        transparency.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                sliderTrans.setEnabled(transparency.isSelected());
+            }
+        });
+        transparency.setSelected(Main.pref.getBoolean("cadastrewms.backgroundTransparent", false));
+        transparency.setToolTipText(tr("Allows multiple layers stacking"));
+        cadastrewms.add(transparency, GBC.eop().insets(0, 0, 0, 0));
+
+        // slider for transparency level
+        sliderTrans.setSnapToTicks(true);
+        sliderTrans.setToolTipText(tr("Set WMS layers transparency. Right is opaque, left is transparent."));
+        sliderTrans.setMajorTickSpacing(10);
+        sliderTrans.setMinorTickSpacing(1);
+        sliderTrans.setValue((int)(Float.parseFloat(Main.pref.get("cadastrewms.brightness", "1.0f"))*10));
+        sliderTrans.setPaintTicks(true);
+        sliderTrans.setPaintLabels(false);
+        sliderTrans.setEnabled(transparency.isSelected());
+        cadastrewms.add(sliderTrans, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 250, 0));
+
+        // option to draw boundaries of downloaded data
+        drawBoundaries.setSelected(Main.pref.getBoolean("cadastrewms.drawBoundaries", false));
+        drawBoundaries.setToolTipText(tr("Draw a rectangle around downloaded data from WMS server."));
+        cadastrewms.add(drawBoundaries, GBC.eop().insets(0, 0, 0, 5));
+
+        // separator
+        cadastrewms.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
+        
+        // the vectorized images multiplier
+        JLabel jLabelScale = new JLabel(tr("Vector images grab multiplier:"));
+        cadastrewms.add(jLabelScale, GBC.std().insets(0, 5, 10, 0));
+        ButtonGroup bgGrabMultiplier = new ButtonGroup();
+        ActionListener multiplierActionListener = new ActionListener() {
+            public void actionPerformed(ActionEvent actionEvent) {
+              AbstractButton button = (AbstractButton) actionEvent.getSource();
+              grabMultiplier4Size.setEnabled(button == grabMultiplier4);
+            }
+          };
+        grabMultiplier1.setIcon(ImageProvider.get("preferences", "unsel_box_1"));
+        grabMultiplier1.setSelectedIcon(ImageProvider.get("preferences", "sel_box_1"));
+        grabMultiplier1.addActionListener( multiplierActionListener);
+        grabMultiplier2.setIcon(ImageProvider.get("preferences", "unsel_box_2"));
+        grabMultiplier2.setSelectedIcon(ImageProvider.get("preferences", "sel_box_2"));
+        grabMultiplier2.addActionListener( multiplierActionListener);
+        grabMultiplier2.setToolTipText(tr("Grab smaller images (higher quality but use more memory)"));
+        grabMultiplier3.setIcon(ImageProvider.get("preferences", "unsel_box_3"));
+        grabMultiplier3.setSelectedIcon(ImageProvider.get("preferences", "sel_box_3"));
+        grabMultiplier3.addActionListener( multiplierActionListener);
+        grabMultiplier3.setToolTipText(tr("Grab smaller images (higher quality but use more memory)"));
+        grabMultiplier4.setIcon(ImageProvider.get("preferences", "unsel_box_4"));
+        grabMultiplier4.setSelectedIcon(ImageProvider.get("preferences", "sel_box_4"));
+        grabMultiplier4.addActionListener( multiplierActionListener);
+        grabMultiplier4.setToolTipText(tr("Fixed size square (default is 100m)"));
+        bgGrabMultiplier.add(grabMultiplier1);
+        bgGrabMultiplier.add(grabMultiplier2);
+        bgGrabMultiplier.add(grabMultiplier3);
+        bgGrabMultiplier.add(grabMultiplier4);
+        String currentScale = Main.pref.get("cadastrewms.scale", "1");
+        if (currentScale.equals(Scale.X1.value))
+            grabMultiplier1.setSelected(true);
+        if (currentScale.equals(Scale.X2.value))
+            grabMultiplier2.setSelected(true);
+        if (currentScale.equals(Scale.X3.value))
+            grabMultiplier3.setSelected(true);
+        if (currentScale.equals(Scale.SQUARE_100M.value))
+            grabMultiplier4.setSelected(true);
+        cadastrewms.add(grabMultiplier1, GBC.std().insets(5, 0, 5, 0));
+        cadastrewms.add(grabMultiplier2, GBC.std().insets(5, 0, 5, 0));
+        cadastrewms.add(grabMultiplier3, GBC.std().insets(5, 0, 5, 0));
+        cadastrewms.add(grabMultiplier4, GBC.std().insets(5, 0, 5, 0));
+        int squareSize = getNumber("cadastrewms.squareSize", DEFAULT_SQUARE_SIZE);
+        grabMultiplier4Size.setText(String.valueOf(squareSize));
+        grabMultiplier4Size.setToolTipText(tr("Fixed size (from 25 to 1000 meters)"));
+        grabMultiplier4Size.setEnabled(currentScale.equals(Scale.SQUARE_100M.value));
+        cadastrewms.add(grabMultiplier4Size, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 0, 5));
+
+        // separator
+        cadastrewms.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
+
+        // for raster images (not vectorized), image grab divider (from 1 to 10)
+        String savedRasterDivider = Main.pref.get("cadastrewms.rasterDivider", DEFAULT_RASTER_DIVIDER);
+        JLabel jLabelRasterDivider = new JLabel(tr("Raster images grab multiplier:"));
+        rasterDivider.setText(savedRasterDivider);
+        rasterDivider.setToolTipText("Raster image grab division, from 1 to 10; 10 is very high definition");
+        cadastrewms.add(jLabelRasterDivider, GBC.std().insets(0, 5, 10, 0));
+        cadastrewms.add(rasterDivider, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 200, 5));
+        // option to disable image cropping during raster image georeferencing
+        disableImageCropping.setSelected(Main.pref.getBoolean("cadastrewms.noImageCropping", false));
+        disableImageCropping.setToolTipText(tr("Disable image cropping during georeferencing."));
+        cadastrewms.add(disableImageCropping, GBC.eop().insets(0, 0, 0, 0));
+        // the crosspiece display
+        JLabel jLabelCrosspieces = new JLabel(tr("Display crosspieces:"));
+        cadastrewms.add(jLabelCrosspieces, GBC.std().insets(0, 0, 10, 0));
+        ButtonGroup bgCrosspieces = new ButtonGroup();
+        int crosspieces = getNumber("cadastrewms.crosspieces", DEFAULT_CROSSPIECES);
+        if (crosspieces == 0) crosspiece1.setSelected(true);
+        if (crosspieces == 1) crosspiece2.setSelected(true);
+        if (crosspieces == 2) crosspiece3.setSelected(true);
+        bgCrosspieces.add(crosspiece1);
+        bgCrosspieces.add(crosspiece2);
+        bgCrosspieces.add(crosspiece3);
+        cadastrewms.add(crosspiece1, GBC.std().insets(5, 0, 5, 0));
+        cadastrewms.add(crosspiece2, GBC.std().insets(5, 0, 5, 0));
+        cadastrewms.add(crosspiece3, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 0, 5));
+
+        // separator
+        cadastrewms.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
+
+        // option to enable automatic caching
+        enableCache.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                jLabelCacheSize.setEnabled(enableCache.isSelected());
+                cacheSize.setEnabled(enableCache.isSelected());
+            }
+        });
+        enableCache.setSelected(Main.pref.getBoolean("cadastrewms.enableCaching", true));
+        enableCache.setToolTipText(tr("Replace the original white background by the backgound color defined in JOSM preferences."));
+        cadastrewms.add(enableCache, GBC.eop().insets(0, 0, 0, 0));
+
+        // option to fix the cache size(in MB)
+        int size = getNumber("cadastrewms.cacheSize", DEFAULT_CACHE_SIZE);
+        cacheSize.setText(String.valueOf(size));
+        cacheSize.setToolTipText(tr("Oldest files are automatically deleted when this size is exceeded"));
+        cadastrewms.add(jLabelCacheSize, GBC.std().insets(20, 0, 0, 0));
+        cadastrewms.add(cacheSize, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 200, 5));
+        
+        cadastrewms.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+
+    }
+
+    public boolean ok() {
+        Main.pref.put("cadastrewms.source", sourcing.getText());
+        CadastrePlugin.source = sourcing.getText();
+        Main.pref.put("cadastrewms.alterColors", alterColors.isSelected());
+        Main.pref.put("cadastrewms.invertGrey", reversGrey.isSelected());
+        Main.pref.put("cadastrewms.backgroundTransparent", transparency.isSelected());
+        Main.pref.put("cadastrewms.brightness", Float.toString((float)sliderTrans.getValue()/10));
+        Main.pref.put("cadastrewms.drawBoundaries", drawBoundaries.isSelected());
+        if (grabMultiplier1.isSelected())
+            Main.pref.put("cadastrewms.scale", Scale.X1.toString());
+        else if (grabMultiplier2.isSelected())
+            Main.pref.put("cadastrewms.scale", Scale.X2.toString());
+        else if (grabMultiplier3.isSelected())
+            Main.pref.put("cadastrewms.scale", Scale.X3.toString());
+        else {
+            Main.pref.put("cadastrewms.scale", Scale.SQUARE_100M.toString());
+            try {
+                int squareSize = Integer.parseInt(grabMultiplier4Size.getText());
+                if (squareSize >= 25 && squareSize <= 1000)
+                    Main.pref.put("cadastrewms.squareSize", grabMultiplier4Size.getText());
+            } catch (NumberFormatException e) { // ignore the last input
+            }
+        }
+        try {
+            int i = Integer.parseInt(rasterDivider.getText());
+            if (i > 0 && i < 11)
+                Main.pref.put("cadastrewms.rasterDivider", String.valueOf(i));
+        } catch (NumberFormatException e) { // ignore the last input
+        }
+        Main.pref.put("cadastrewms.noImageCropping", disableImageCropping.isSelected());
+        if (crosspiece1.isSelected()) Main.pref.put("cadastrewms.crosspieces", "0");
+        else if (crosspiece2.isSelected()) Main.pref.put("cadastrewms.crosspieces", "1");
+        else if (crosspiece3.isSelected()) Main.pref.put("cadastrewms.crosspieces", "2");
+        Main.pref.put("cadastrewms.enableCaching", enableCache.isSelected());
+
+        // spread data into objects instead of restarting the application
+        try {
+            CacheControl.cacheSize = Integer.parseInt(cacheSize.getText());
+            Main.pref.put("cadastrewms.cacheSize", String.valueOf(CacheControl.cacheSize));
+        } catch (NumberFormatException e) { // ignore the last input
+        }
+        CacheControl.cacheEnabled = enableCache.isSelected();
+        CadastrePlugin.refreshConfiguration();
+        CadastrePlugin.refreshMenu();
+
+        return false;
+    }
+
+    private int getNumber(String pref_parameter, int def_value) {
+        try {
+            return Integer.parseInt(Main.pref.get(pref_parameter, String.valueOf(def_value)));
+        } catch (NumberFormatException e) {
+            return def_value;
+        }
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java b/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java
new file mode 100644
index 0000000..712a5b5
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java
@@ -0,0 +1,92 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.upload.UploadHook;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.data.APIDataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
+import org.openstreetmap.josm.tools.GBC;
+
+/**
+ * This hook is called at JOSM upload and will check if new nodes and ways provide
+ * a tag "source=". If not and if auto-sourcing is enabled, it will add
+ * automatically a tag "source"="Cadastre..." as defined in the plugin preferences.
+ */
+public class CheckSourceUploadHook implements UploadHook
+{
+    /** Serializable ID */
+    private static final long serialVersionUID = -1;
+
+    /**
+     * Add the tag "source" if it doesn't exist for all new Nodes and Ways before uploading
+     */
+    public boolean checkUpload(APIDataSet apiDataSet) 
+    {
+        if (CadastrePlugin.autoSourcing && CadastrePlugin.pluginUsed && !apiDataSet.getPrimitivesToAdd().isEmpty()) {
+            Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
+            for (OsmPrimitive osm : apiDataSet.getPrimitivesToAdd()) {
+                if ((osm instanceof Way && (osm.getKeys().size() == 0 || !tagSourceExist(osm)))
+                 || (osm instanceof Node && osm.getKeys().size() > 0 && !tagSourceExist(osm))) {
+                    sel.add(osm);
+                }
+            }
+            if (!sel.isEmpty()) {
+                displaySource(sel);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check whenever one of the keys of the object is "source"
+     * @param OsmPrimitive
+     * @return true if one of keys is "source"
+     */
+    private boolean tagSourceExist(OsmPrimitive osm) {
+        for (String key : osm.keySet()) {
+            if (key.equals("source") ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Displays a screen with the list of objects which will be tagged with
+     * source="cadastre.." if it is approved.
+     * @param sel the list of elements added without a key "source"
+     */
+    private void displaySource(Collection<OsmPrimitive> sel)
+    {
+        if (!sel.isEmpty()) {
+            JPanel p = new JPanel(new GridBagLayout());
+            OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
+            p.add(new JLabel(tr("Auto-tag source added:")), GBC.eol());
+            JList l = new JList(sel.toArray());
+            l.setCellRenderer(renderer);
+            l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
+            p.add(new JScrollPane(l), GBC.eol().fill());
+            boolean bContinue = JOptionPane.showConfirmDialog(Main.parent, p, tr("Add \"source=...\" to elements?"),
+                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
+            if (bContinue)
+                Main.main.undoRedo.add(new ChangePropertyCommand(sel, "source", CadastrePlugin.source));
+        }
+
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadSVGBuilding.java b/cadastre-fr/src/cadastre_fr/DownloadSVGBuilding.java
new file mode 100644
index 0000000..ab36189
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadSVGBuilding.java
@@ -0,0 +1,276 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.ProgressInputStream;
+
+public class DownloadSVGBuilding extends PleaseWaitRunnable {
+
+    private WMSLayer wmsLayer;
+    private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+    private CadastreInterface wmsInterface;
+    private String svg = null;
+    private static EastNorthBound currentView = null;
+    private EastNorthBound viewBox = null;
+
+    public DownloadSVGBuilding(WMSLayer wmsLayer) {
+        super(tr("Downloading {0}", wmsLayer.getName()));
+
+        this.wmsLayer = wmsLayer;
+        this.wmsInterface = grabber.getWmsInterface();
+    }
+
+    @Override
+    public void realRun() throws IOException, OsmTransferException {
+    	progressMonitor.indeterminateSubTask(tr("Contacting WMS Server..."));
+        try {
+            if (wmsInterface.retrieveInterface(wmsLayer)) {
+                svg = grabBoundary(currentView);
+                if (svg == null)
+                    return;
+                getViewBox(svg);
+                if (viewBox == null)
+                    return;
+                createBuildings(svg);
+            }
+        } catch (DuplicateLayerException e) {
+            System.err.println("removed a duplicated layer");
+        }
+    }
+
+    @Override
+    protected void cancel() {
+        grabber.getWmsInterface().cancel();
+    }
+
+    @Override
+    protected void finish() {
+    }
+
+    private boolean getViewBox(String svg) {
+        double[] box = new SVGParser().getViewBox(svg);
+        if (box != null) {
+            viewBox = new EastNorthBound(new EastNorth(box[0], box[1]),
+                    new EastNorth(box[0]+box[2], box[1]+box[3]));
+            return true;
+        }
+        System.out.println("Unable to parse SVG data (viewBox)");
+        return false;
+    }
+
+    /**
+     *  The svg contains more than one commune boundary defined by path elements. So detect
+     *  which path element is the best fitting to the viewBox and convert it to OSM objects
+     */
+    private void createBuildings(String svg) {
+        String[] SVGpaths = new SVGParser().getClosedPaths(svg);
+        ArrayList<ArrayList<EastNorth>> eastNorths = new ArrayList<ArrayList<EastNorth>>();
+
+        // convert SVG nodes to eastNorth coordinates
+        for (int i=0; i< SVGpaths.length; i++) {
+            ArrayList<EastNorth> eastNorth = new ArrayList<EastNorth>();
+            createNodes(SVGpaths[i], eastNorth);
+            if (eastNorth.size() > 2)
+                eastNorths.add(eastNorth);
+        }
+
+        // create nodes and closed ways
+        DataSet svgDataSet = new DataSet();
+        for (ArrayList<EastNorth> path : eastNorths) {
+            Way wayToAdd = new Way();
+            for (EastNorth eastNorth : path) {
+                Node nodeToAdd = new Node(Main.proj.eastNorth2latlon(eastNorth));
+                // check if new node is not already created by another new path
+                Node nearestNewNode = checkNearestNode(nodeToAdd, svgDataSet.getNodes());
+                if (nearestNewNode == nodeToAdd)
+                    svgDataSet.addPrimitive(nearestNewNode);
+                wayToAdd.addNode(nearestNewNode); // either a new node or an existing one
+            }
+            wayToAdd.addNode(wayToAdd.getNode(0)); // close the way
+            svgDataSet.addPrimitive(wayToAdd);
+        }
+
+        // TODO remove small boxes (4 nodes with less than 1 meter distance)
+        /*
+        for (Way w : svgDataSet.ways)
+            if (w.nodes.size() == 5)
+                for (int i = 0; i < w.nodes.size()-2; i++) {
+                    if (w.nodes.get(i).eastNorth.distance(w.nodes.get(i+1).eastNorth))
+                }*/
+
+        // simplify ways and check if we can reuse existing OSM nodes
+        for (Way wayToAdd : svgDataSet.getWays())
+            new SimplifyWay().simplifyWay(wayToAdd, svgDataSet, 0.5);
+        // check if the new way or its nodes is already in OSM layer
+        for (Node n : svgDataSet.getNodes()) {
+            Node nearestNewNode = checkNearestNode(n, Main.main.getCurrentDataSet().getNodes());
+            if (nearestNewNode != n) {
+                // replace the SVG node by the OSM node
+                for (Way w : svgDataSet.getWays()) {
+                    int replaced = 0;
+                    for (Node node : w.getNodes())
+                        if (node == n) {
+                            node = nearestNewNode;
+                            replaced++;
+                        }
+                    if (w.getNodesCount() == replaced)
+                        w.setDeleted(true);
+                }
+                n.setDeleted(true);
+            }
+
+        }
+
+        Collection<Command> cmds = new LinkedList<Command>();
+        for (Node node : svgDataSet.getNodes())
+            if (!node.isDeleted())
+                cmds.add(new AddCommand(node));
+        for (Way way : svgDataSet.getWays())
+            if (!way.isDeleted())
+                cmds.add(new AddCommand(way));
+        Main.main.undoRedo.add(new SequenceCommand(tr("Create buildings"), cmds));
+        Main.map.repaint();
+    }
+
+    private void createNodes(String SVGpath, ArrayList<EastNorth> eastNorth) {
+        // looks like "M981283.38 368690.15l143.81 72.46 155.86 ..."
+        String[] coor = SVGpath.split("[MlZ ]"); //coor[1] is x, coor[2] is y
+        double dx = Double.parseDouble(coor[1]);
+        double dy = Double.parseDouble(coor[2]);
+        for (int i=3; i<coor.length; i+=2){
+            if (coor[i].equals("")) {
+                eastNorth.clear(); // some paths are just artifacts
+                return;
+            }
+            double east = dx+=Double.parseDouble(coor[i]);
+            double north = dy+=Double.parseDouble(coor[i+1]);
+            eastNorth.add(new EastNorth(east,north));
+        }
+        // flip the image (svg using a reversed Y coordinate system)
+        double pivot = viewBox.min.getY() + (viewBox.max.getY() - viewBox.min.getY()) / 2;
+        for (EastNorth en : eastNorth) {
+            en.setLocation(en.east(), 2 * pivot - en.north());
+        }
+        return;
+    }
+
+    /**
+     * Check if node can be reused.
+     * @param nodeToAdd the candidate as new node
+     * @return the already existing node (if any), otherwise the new node candidate.
+     */
+    private Node checkNearestNode(Node nodeToAdd, Collection<Node> nodes) {
+        double epsilon = 0.05; // smallest distance considering duplicate node
+        for (Node n : nodes) {
+            if (!n.isDeleted() && !n.incomplete) {
+                double dist = n.getEastNorth().distance(nodeToAdd.getEastNorth());
+                if (dist < epsilon) {
+                    return n;
+                }
+            }
+        }
+        return nodeToAdd;
+    }
+
+    private String grabBoundary(EastNorthBound bbox) throws IOException, OsmTransferException {
+        try {
+            URL url = null;
+            url = getURLsvg(bbox);
+            return grabSVG(url);
+        } catch (MalformedURLException e) {
+            throw (IOException) new IOException(tr("CadastreGrabber: Illegal url.")).initCause(e);
+        }
+    }
+
+    private URL getURLsvg(EastNorthBound bbox) throws MalformedURLException {
+        String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+        str += "&layers=";
+        str += "CDIF:LS2";
+        str += "&format=image/svg";
+        str += "&bbox="+bbox.min.east()+",";
+        str += bbox.min.north() + ",";
+        str += bbox.max.east() + ",";
+        str += bbox.max.north();
+        str += "&width=800&height=600"; // maximum allowed by wms server
+        str += "&exception=application/vnd.ogc.se_inimage";
+        str += "&styles=";
+        str += "LS2_90";
+        System.out.println("URL="+str);
+        return new URL(str.replace(" ", "%20"));
+    }
+
+    private String grabSVG(URL url) throws IOException, OsmTransferException {
+        wmsInterface.urlConn = (HttpURLConnection)url.openConnection();
+        wmsInterface.urlConn.setRequestMethod("GET");
+        wmsInterface.setCookie();
+        InputStream is = new ProgressInputStream(wmsInterface.urlConn, NullProgressMonitor.INSTANCE);
+        File file = new File(CadastrePlugin.cacheDir + "building.svg");
+        String svg = new String();
+        try {
+            if (file.exists())
+                file.delete();
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, true));
+            InputStreamReader isr =new InputStreamReader(is);
+            BufferedReader br = new BufferedReader(isr);
+            String line="";
+            while ( null!=(line=br.readLine())){
+                line += "\n";
+                bos.write(line.getBytes());
+                svg += line;
+            }
+            bos.close();
+        } catch (IOException e) {
+            e.printStackTrace(System.out);
+        }
+        is.close();
+        return svg;
+    }
+
+    public static void download(WMSLayer wmsLayer) {
+        MapView mv = Main.map.mapView;
+        currentView = new EastNorthBound(mv.getEastNorth(0, mv.getHeight()),
+                mv.getEastNorth(mv.getWidth(), 0));
+        if ((currentView.max.east() - currentView.min.east()) > 1000 ||
+                (currentView.max.north() - currentView.min.north() > 1000)) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("To avoid cadastre WMS overload,\nbuilding import size is limited to 1 km2 max."));
+            return;
+        }
+        if (CadastrePlugin.autoSourcing == false) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("Please, enable auto-sourcing and check cadastre millesime."));
+            return;
+        }
+        Main.worker.execute(new DownloadSVGBuilding(wmsLayer));
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadSVGTask.java b/cadastre-fr/src/cadastre_fr/DownloadSVGTask.java
new file mode 100644
index 0000000..bba3fcb
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadSVGTask.java
@@ -0,0 +1,222 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.ProgressInputStream;
+/**
+ * Grab the SVG administrative boundaries of the active commune layer (cadastre),
+ * isolate the SVG path of the concerned commune (other municipalities are also
+ * downloaded in the SVG data), convert to OSM nodes and way plus simplify.
+ * Thanks to Frederic Rodrigo for his help.
+ */
+public class DownloadSVGTask extends PleaseWaitRunnable {
+
+    private WMSLayer wmsLayer;
+    private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+    private CadastreInterface wmsInterface;
+    private String svg = null;
+    private EastNorthBound viewBox = null;
+
+    public DownloadSVGTask(WMSLayer wmsLayer) {
+        super(tr("Downloading {0}", wmsLayer.getName()));
+
+        this.wmsLayer = wmsLayer;
+        this.wmsInterface = grabber.getWmsInterface();
+    }
+
+    @Override
+    public void realRun() throws IOException, OsmTransferException {
+    	progressMonitor.indeterminateSubTask(tr("Contacting WMS Server..."));
+        try {
+            if (wmsInterface.retrieveInterface(wmsLayer)) {
+                svg = grabBoundary(wmsLayer.getCommuneBBox());
+                if (svg == null)
+                    return;
+                progressMonitor.indeterminateSubTask(tr("Extract SVG ViewBox..."));
+                getViewBox(svg);
+                if (viewBox == null)
+                    return;
+                progressMonitor.indeterminateSubTask(tr("Extract best fitting boundary..."));
+                createWay(svg);
+            }
+        } catch (DuplicateLayerException e) {
+            System.err.println("removed a duplicated layer");
+        }
+    }
+
+    @Override
+    protected void cancel() {
+        grabber.getWmsInterface().cancel();
+    }
+
+    @Override
+    protected void finish() {
+    }
+
+    private boolean getViewBox(String svg) {
+        double[] box = new SVGParser().getViewBox(svg);
+        if (box != null) {
+            viewBox = new EastNorthBound(new EastNorth(box[0], box[1]),
+                    new EastNorth(box[0]+box[2], box[1]+box[3]));
+            return true;
+        }
+        System.out.println("Unable to parse SVG data (viewBox)");
+        return false;
+    }
+
+    /**
+     *  The svg contains more than one commune boundary defined by path elements. So detect
+     *  which path element is the best fitting to the viewBox and convert it to OSM objects
+     */
+    private void createWay(String svg) {
+        String[] SVGpaths = new SVGParser().getClosedPaths(svg);
+        ArrayList<Double> fitViewBox = new ArrayList<Double>();
+        ArrayList<ArrayList<EastNorth>> eastNorths = new ArrayList<ArrayList<EastNorth>>();
+        for (int i=0; i< SVGpaths.length; i++) {
+            ArrayList<EastNorth> eastNorth = new ArrayList<EastNorth>();
+            fitViewBox.add( createNodes(SVGpaths[i], eastNorth) );
+            eastNorths.add(eastNorth);
+        }
+        // the smallest fitViewBox indicates the best fitting path in viewBox
+        Double min = Collections.min(fitViewBox);
+        int bestPath = fitViewBox.indexOf(min);
+        List<Node> nodeList = new ArrayList<Node>();
+        for (EastNorth eastNorth : eastNorths.get(bestPath)) {
+            nodeList.add(new Node(Main.proj.eastNorth2latlon(eastNorth)));
+        }
+        Way wayToAdd = new Way();
+        Collection<Command> cmds = new LinkedList<Command>();
+        for (Node node : nodeList) {
+            cmds.add(new AddCommand(node));
+            wayToAdd.addNode(node);
+        }
+        wayToAdd.addNode(wayToAdd.getNode(0)); // close the circle
+
+        // simplify the way
+        double threshold = Double.parseDouble(Main.pref.get("cadastrewms.simplify-way-boundary", "1.0"));
+        new SimplifyWay().simplifyWay(wayToAdd, Main.main.getCurrentDataSet(), threshold);
+
+        cmds.add(new AddCommand(wayToAdd));
+        Main.main.undoRedo.add(new SequenceCommand(tr("Create boundary"), cmds));
+        Main.map.repaint();
+    }
+
+    private double createNodes(String SVGpath, ArrayList<EastNorth> eastNorth) {
+        // looks like "M981283.38 368690.15l143.81 72.46 155.86 ..."
+        String[] coor = SVGpath.split("[MlZ ]"); //coor[1] is x, coor[2] is y
+        double dx = Double.parseDouble(coor[1]);
+        double dy = Double.parseDouble(coor[2]);
+        double minY = Double.MAX_VALUE;
+        double minX = Double.MAX_VALUE;
+        double maxY = Double.MIN_VALUE;
+        double maxX = Double.MIN_VALUE;
+        for (int i=3; i<coor.length; i+=2){
+            double east = dx+=Double.parseDouble(coor[i]);
+            double north = dy+=Double.parseDouble(coor[i+1]);
+            eastNorth.add(new EastNorth(east,north));
+            minX = minX > east ? east : minX;
+            minY = minY > north ? north : minY;
+            maxX = maxX < east ? east : maxX;
+            maxY = maxY < north ? north : maxY;
+        }
+        // flip the image (svg using a reversed Y coordinate system)
+        double pivot = viewBox.min.getY() + (viewBox.max.getY() - viewBox.min.getY()) / 2;
+        for (EastNorth en : eastNorth) {
+            en.setLocation(en.east(), 2 * pivot - en.north());
+        }
+        return Math.abs(minX - viewBox.min.getX())+Math.abs(maxX - viewBox.max.getX())
+        +Math.abs(minY - viewBox.min.getY())+Math.abs(maxY - viewBox.max.getY());
+    }
+
+    private String grabBoundary(EastNorthBound bbox) throws IOException, OsmTransferException {
+        try {
+            URL url = null;
+            url = getURLsvg(bbox);
+            return grabSVG(url);
+        } catch (MalformedURLException e) {
+            throw (IOException) new IOException(tr("CadastreGrabber: Illegal url.")).initCause(e);
+        }
+    }
+
+    private URL getURLsvg(EastNorthBound bbox) throws MalformedURLException {
+        String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+        str += "&layers=";
+        str += "CDIF:COMMUNE";
+        str += "&format=image/svg";
+        str += "&bbox="+bbox.min.east()+",";
+        str += bbox.min.north() + ",";
+        str += bbox.max.east() + ",";
+        str += bbox.max.north();
+        str += "&width=800&height=600"; // maximum allowed by wms server
+        str += "&styles=";
+        str += "COMMUNE_90";
+        System.out.println("URL="+str);
+        return new URL(str.replace(" ", "%20"));
+    }
+
+    private String grabSVG(URL url) throws IOException, OsmTransferException {
+        wmsInterface.urlConn = (HttpURLConnection)url.openConnection();
+        wmsInterface.urlConn.setRequestMethod("GET");
+        wmsInterface.setCookie();
+        InputStream is = new ProgressInputStream(wmsInterface.urlConn, NullProgressMonitor.INSTANCE);
+        File file = new File(CadastrePlugin.cacheDir + "boundary.svg");
+        String svg = new String();
+        try {
+            if (file.exists())
+                file.delete();
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, true));
+            InputStreamReader isr =new InputStreamReader(is);
+            BufferedReader br = new BufferedReader(isr);
+            String line="";
+            while ( null!=(line=br.readLine())){
+                line += "\n";
+                bos.write(line.getBytes());
+                svg += line;
+            }
+            bos.close();
+        } catch (IOException e) {
+            e.printStackTrace(System.out);
+        }
+        is.close();
+        return svg;
+    }
+
+    public static void download(WMSLayer wmsLayer) {
+        if (CadastrePlugin.autoSourcing == false) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("Please, enable auto-sourcing and check cadastre millesime."));
+            return;
+        }
+        Main.worker.execute(new DownloadSVGTask(wmsLayer));
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java b/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java
new file mode 100644
index 0000000..adb0d6d
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java
@@ -0,0 +1,122 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+
+public class DownloadWMSPlanImage {
+    
+    private Future<Task> task = null;
+    private WMSLayer wmsLayer;
+    private Bounds bounds;
+    private boolean dontGeoreference = false;
+    
+    private class Task extends PleaseWaitRunnable {
+        private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+        public Task(WMSLayer wmsLayer, Bounds bounds) {
+            super(tr("Downloading {0}", wmsLayer.getName()));
+        }
+
+        @Override
+        public void realRun() throws IOException {
+            progressMonitor.indeterminateSubTask(tr("Contacting cadastre WMS ..."));
+            try {
+                if (grabber.getWmsInterface().retrieveInterface(wmsLayer)) {
+                    if (!wmsLayer.images.isEmpty()) {
+                        //JOptionPane.showMessageDialog(Main.parent,tr("Image already loaded"));
+                        JOptionPane pane = new JOptionPane(
+                                tr("Image already loaded")
+                                , JOptionPane.INFORMATION_MESSAGE);
+                        // this below is a temporary workaround to fix the "always on top" issue
+                        JDialog dialog = pane.createDialog(Main.parent, "");
+                        CadastrePlugin.prepareDialog(dialog);
+                        dialog.setVisible(true);
+                        // till here
+                        dontGeoreference = true;
+                    } else if (grabber.getWmsInterface().downloadCancelled){
+                        // do nothing
+                    } else {
+                        // first time we grab an image for this layer
+                        if (CacheControl.cacheEnabled) {
+                            if (wmsLayer.getCacheControl().loadCacheIfExist()) {
+                                dontGeoreference = true;
+                                Main.map.mapView.repaint();
+                                return;
+                            }
+                        }
+                        if (wmsLayer.isRaster()) {
+                            // set raster image commune bounding box based on current view (before adjustment)
+                            grabber.getWmsInterface().retrieveCommuneBBox(wmsLayer);
+                            wmsLayer.setRasterBounds(bounds);
+                            // grab new images from wms server into active layer
+                            wmsLayer.grab(grabber, bounds);
+                            if (grabber.getWmsInterface().downloadCancelled) {
+                                wmsLayer.images.clear();
+                                Main.map.mapView.repaint();
+                            } else {
+                                // next steps follow in method finish() when download is terminated
+                                wmsLayer.joinRasterImages();
+                            }
+                        } else {
+                            /*JOptionPane.showMessageDialog(Main.parent,tr("Municipality vectorized !\n"+
+                                    "Use the normal Cadastre Grab menu."));*/
+                            JOptionPane pane = new JOptionPane(
+                                    tr("Municipality vectorized !\nUse the normal Cadastre Grab menu.")
+                                    , JOptionPane.INFORMATION_MESSAGE);
+                            // this below is a temporary workaround to fix the "always on top" issue
+                            JDialog dialog = pane.createDialog(Main.parent, "");
+                            CadastrePlugin.prepareDialog(dialog);
+                            dialog.setVisible(true);
+                            // till here
+                        }
+                    }
+                }
+            } catch (DuplicateLayerException e) {
+                // we tried to grab onto a duplicated layer (removed)
+                System.err.println("removed a duplicated layer");
+            }
+        }
+        
+        @Override
+        protected void cancel() {
+            grabber.getWmsInterface().cancel();
+            dontGeoreference = true;
+        }
+
+        @Override
+        protected void finish() {
+        }
+    }
+    
+    public void download(WMSLayer wmsLayer) {
+        MapView mv = Main.map.mapView;
+        Bounds bounds = new Bounds(mv.getLatLon(0, mv.getHeight()), mv.getLatLon(mv.getWidth(), 0));
+
+        //Main.worker.execute(new DownloadWMSPlanImage(wmsLayer, bounds));
+        Task t = new Task(wmsLayer, bounds);
+        this.wmsLayer = wmsLayer;
+        this.bounds = bounds;
+        task = Main.worker.submit(t, t);
+    }
+
+    public boolean waitFinished() {
+        if (task != null) {
+            try {
+                task.get();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return dontGeoreference;
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadWMSVectorImage.java b/cadastre-fr/src/cadastre_fr/DownloadWMSVectorImage.java
new file mode 100644
index 0000000..9bed6c8
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadWMSVectorImage.java
@@ -0,0 +1,82 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+
+public class DownloadWMSVectorImage extends PleaseWaitRunnable {
+
+    private WMSLayer wmsLayer;
+
+    private Bounds bounds;
+
+    private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+
+    public DownloadWMSVectorImage(WMSLayer wmsLayer, Bounds bounds) {
+        super(tr("Downloading {0}", wmsLayer.getName()));
+
+        this.wmsLayer = wmsLayer;
+        this.bounds = bounds;
+    }
+
+    @Override
+    public void realRun() throws IOException {
+        progressMonitor.indeterminateSubTask(tr("Contacting WMS Server..."));
+        try {
+            if (grabber.getWmsInterface().retrieveInterface(wmsLayer)) {
+                boolean useFactor = true;
+                if (wmsLayer.images.isEmpty()) {
+                    // first time we grab an image for this layer
+                    if (CacheControl.cacheEnabled) {
+                        if (wmsLayer.getCacheControl().loadCacheIfExist()) {
+                            Main.map.mapView.zoomTo(wmsLayer.getCommuneBBox().toBounds());
+                            //Main.map.mapView.repaint();
+                            return;
+                        }
+                    }
+                    if (wmsLayer.isRaster()) {
+                        // set raster image commune bounding box based on current view (before adjustment)
+                        wmsLayer.setRasterBounds(bounds);
+                    } else {
+                        // set vectorized commune bounding box by opening the standard web window
+                        grabber.getWmsInterface().retrieveCommuneBBox(wmsLayer);
+                        // if it is the first layer, use the communeBBox as grab bbox (and not divided)
+                        if (Main.map.mapView.getAllLayers().size() == 1 ) {
+                            bounds = wmsLayer.getCommuneBBox().toBounds();
+                            Main.map.mapView.zoomTo(bounds);
+                            useFactor = false;
+                        }
+                    }
+                }
+                // grab new images from wms server into active layer
+                wmsLayer.grab(grabber, bounds, useFactor);
+            }
+        } catch (DuplicateLayerException e) {
+            // we tried to grab onto a duplicated layer (removed)
+            System.err.println("removed a duplicated layer");
+        }
+    }
+
+    @Override
+    protected void cancel() {
+        grabber.getWmsInterface().cancel();
+    }
+
+    @Override
+    protected void finish() {
+    }
+
+    public static void download(WMSLayer wmsLayer) {
+        MapView mv = Main.map.mapView;
+        Bounds bounds = new Bounds(mv.getLatLon(0, mv.getHeight()), mv.getLatLon(mv.getWidth(), 0));
+
+        Main.worker.execute(new DownloadWMSVectorImage(wmsLayer, bounds));
+
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java b/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java
new file mode 100644
index 0000000..22e491f
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java
@@ -0,0 +1,5 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+class DuplicateLayerException extends Exception {
+    private static final long serialVersionUID = 1L;}
diff --git a/cadastre-fr/src/cadastre_fr/EastNorthBound.java b/cadastre-fr/src/cadastre_fr/EastNorthBound.java
new file mode 100644
index 0000000..d0e051c
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/EastNorthBound.java
@@ -0,0 +1,42 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.io.Serializable;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.EastNorth;
+
+public class EastNorthBound implements Serializable {
+
+    private static final long serialVersionUID = 8451650309216472069L;
+
+    public EastNorth min, max;
+    public EastNorthBound(EastNorth min, EastNorth max) {
+        this.min = min;
+        this.max = max;
+    }
+
+    public boolean contains(EastNorth eastNorth) {
+        if (eastNorth.east() < min.east() || eastNorth.north() < min.north())
+            return false;
+        if (eastNorth.east() > max.east() || eastNorth.north() > max.north())
+            return false;
+        return true;
+    }
+
+    public EastNorthBound interpolate(EastNorthBound en2, double proportion) {
+        EastNorthBound enb = new EastNorthBound(this.min.interpolate(en2.min, proportion),
+                this.max.interpolate(en2.max, proportion));
+        return enb;
+    }
+    
+    public Bounds toBounds() {
+        return new Bounds(Main.proj.eastNorth2latlon(min), Main.proj.eastNorth2latlon(max));
+    }
+
+    @Override public String toString() {
+        return "EastNorthBound[" + min.east() + "," + min.north() + "," + max.east() + "," + max.north() + "]";
+    }
+}
+
diff --git a/cadastre-fr/src/cadastre_fr/GeorefImage.java b/cadastre-fr/src/cadastre_fr/GeorefImage.java
new file mode 100644
index 0000000..a24918f
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/GeorefImage.java
@@ -0,0 +1,233 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Point;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+
+public class GeorefImage implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public EastNorth min;
+    public EastNorth max;
+    public BufferedImage image;
+
+    private double pixelPerEast;
+    private double pixelPerNorth;
+
+    public GeorefImage(BufferedImage img, EastNorth min, EastNorth max) {
+        image = img;
+        this.min = min;
+        this.max = max;
+        updatePixelPer();
+    }
+
+    public static GraphicsConfiguration getDefaultConfiguration() {
+        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsDevice gd = ge.getDefaultScreenDevice();
+        return gd.getDefaultConfiguration();
+    }
+
+    private void getNewBounding(EastNorth min, EastNorth max, EastNorth c, EastNorth d) {
+        EastNorth pt[] = new EastNorth[4];
+        pt[0] = min;
+        pt[1] = max;
+        pt[2] = c;
+        pt[3] = d;
+        double smallestEast = Double.MAX_VALUE;
+        double smallestNorth = Double.MAX_VALUE;
+        double highestEast = Double.MIN_VALUE;
+        double highestNorth = Double.MIN_VALUE;
+        for(int i=0; i<=3; i++) {
+            smallestEast = Math.min(pt[i].east(), smallestEast);
+            smallestNorth = Math.min(pt[i].north(), smallestNorth);
+            highestEast = Math.max(pt[i].east(), highestEast);
+            highestNorth = Math.max(pt[i].north(), highestNorth);
+        }
+        min.setLocation(smallestEast, smallestNorth);
+        max.setLocation(highestEast, highestNorth);
+    }
+
+    public boolean contains(EastNorth en) {
+        return min.east() <= en.east() && en.east() <= max.east() && min.north() <= en.north()
+                && en.north() <= max.north();
+    }
+
+    public void paint(Graphics2D g, NavigatableComponent nc, boolean backgroundTransparent, float transparency,
+            boolean drawBoundaries) {
+        if (image == null || min == null || max == null)
+            return;
+
+        Point minPt = nc.getPoint(min), maxPt = nc.getPoint(max);
+
+        if (!g.hitClip(minPt.x, maxPt.y, maxPt.x - minPt.x, minPt.y - maxPt.y))
+            return;
+
+        if (backgroundTransparent && transparency < 1.0f)
+            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparency));
+        if (drawBoundaries) {
+            g.setColor(Color.green);
+            g.drawRect(minPt.x, maxPt.y, maxPt.x - minPt.x, minPt.y - maxPt.y);
+        }
+        g.drawImage(image, minPt.x, maxPt.y, maxPt.x, minPt.y, // dest
+                0, 0, image.getWidth(), image.getHeight(), // src
+                null);
+        if (backgroundTransparent && transparency < 1.0f)
+            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
+    }
+
+    /**
+     * Is the given bbox overlapping this image ?
+     */
+    public boolean overlap(GeorefImage georefImage) {
+        if (this.contains(georefImage.min) || this.contains(georefImage.max))
+            return true;
+        if (this.contains(new EastNorth(georefImage.min.east(), georefImage.max.north()))
+                || this.contains(new EastNorth(georefImage.max.east(), georefImage.min.north())))
+            return true;
+        return false;
+    }
+
+    /**
+     * Make all pixels masked by the given georefImage transparent in this image
+     *
+     * @param georefImage
+     */
+    public void withdraw(GeorefImage georefImage) {
+        double minMaskEast = (georefImage.min.east() > this.min.east()) ? georefImage.min.east() : this.min.east();
+        double maxMaskEast = (georefImage.max.east() < this.max.east()) ? georefImage.max.east() : this.max.east();
+        double minMaskNorth = (georefImage.min.north() > this.min.north()) ? georefImage.min.north() : this.min.north();
+        double maxMaskNorth = (georefImage.max.north() < this.max.north()) ? georefImage.max.north() : this.max.north();
+        if ((maxMaskNorth - minMaskNorth) > 0 && (maxMaskEast - minMaskEast) > 0) {
+            double pixelPerEast = (max.east() - min.east()) / image.getWidth();
+            double pixelPerNorth = (max.north() - min.north()) / image.getHeight();
+            int minXMaskPixel = (int) ((minMaskEast - min.east()) / pixelPerEast);
+            int minYMaskPixel = (int) ((max.north() - maxMaskNorth) / pixelPerNorth);
+            int widthXMaskPixel = Math.abs((int) ((maxMaskEast - minMaskEast) / pixelPerEast));
+            int heightYMaskPixel = Math.abs((int) ((maxMaskNorth - minMaskNorth) / pixelPerNorth));
+            Graphics g = image.getGraphics();
+            for (int x = minXMaskPixel; x < minXMaskPixel + widthXMaskPixel; x++)
+                for (int y = minYMaskPixel; y < minYMaskPixel + heightYMaskPixel; y++)
+                    image.setRGB(x, y, VectorImageModifier.cadastreBackgroundTransp);
+            g.dispose();
+        }
+    }
+
+    /**
+     * Method required by BufferedImage serialization.
+     * Save only primitives to keep cache independent of software changes.
+     */
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        max = new EastNorth(in.readDouble(), in.readDouble());
+        min = new EastNorth(in.readDouble(), in.readDouble());
+        image = (BufferedImage) ImageIO.read(ImageIO.createImageInputStream(in));
+        updatePixelPer();
+    }
+
+    /**
+     * Method required by BufferedImage serialization.
+     * Cache uses only primitives to stay independent of software changes.
+     */
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.writeDouble(max.getX());
+        out.writeDouble(max.getY());
+        out.writeDouble(min.getX());
+        out.writeDouble(min.getY());
+        ImageIO.write(image, "png", ImageIO.createImageOutputStream(out));
+    }
+
+    private void updatePixelPer() {
+        pixelPerEast = image.getWidth()/(max.east()-min.east());
+        pixelPerNorth = image.getHeight()/(max.north()-min.north());
+    }
+
+    public double getPixelPerEast() {
+        return pixelPerEast;
+    }
+
+    public double getPixelPerNorth() {
+        return pixelPerNorth;
+    }
+
+    @Override
+    public String toString() {
+        return "GeorefImage[min=" + min + ", max=" + max + ", image" + image + "]";
+    }
+
+    /*
+     * Following methods are used for affine transformation of two points p1 and p2
+     */
+    /**
+     * Add a translation (dx, dy) to this image min,max coordinates
+     * @param dx delta added to X image coordinate
+     * @param dy delta added to Y image coordinate
+     */
+    public void shear(double dx, double dy) {
+        min = new EastNorth(min.east() + dx, min.north() + dy);
+        max = new EastNorth(max.east() + dx, max.north() + dy);
+    }
+    
+    /**
+     * Change this image scale by moving the min,max coordinates around an anchor
+     * @param anchor 
+     * @param proportion
+     */
+    public void scale(EastNorth anchor, double proportion) {
+        min = anchor.interpolate(min, proportion);
+        max = anchor.interpolate(max, proportion);
+        updatePixelPer();
+    }
+
+    /**
+     * Rotate this image and its min/max coordinates around anchor point
+     * @param anchor anchor of rotation
+     * @param angle angle of rotation (in radians)
+     */
+    public void rotate(EastNorth anchor, double angle) {
+        EastNorth min2 = new EastNorth(min.east(), max.north());
+        EastNorth max2 = new EastNorth(max.east(), min.north());
+        min = min.rotate(anchor, angle);
+        max = max.rotate(anchor, angle);
+        min2 = min2.rotate(anchor, angle);
+        max2 = max2.rotate(anchor, angle);
+        getNewBounding(min, max, min2, max2);
+        image = tilt(image, angle);
+    }
+
+    /**
+     * Rotate by copying original buffered image into a new one with new dimensions 
+     * @param image
+     * @param angle
+     * @return
+     */
+    public static BufferedImage tilt(BufferedImage image, double angle) {
+        double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle));
+        int w = image.getWidth(), h = image.getHeight();
+        int neww = (int)Math.floor(w*cos+h*sin), newh = (int)Math.floor(h*cos+w*sin);
+        GraphicsConfiguration gc = getDefaultConfiguration();
+        BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT);
+        Graphics2D g = result.createGraphics();
+        g.translate((neww-w)/2, (newh-h)/2);
+        g.rotate(angle, w/2, h/2);
+        g.drawRenderedImage(image, null);
+        g.dispose();
+        return result;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/ImageModifier.java b/cadastre-fr/src/cadastre_fr/ImageModifier.java
new file mode 100644
index 0000000..a0c86c7
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/ImageModifier.java
@@ -0,0 +1,16 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.image.BufferedImage;
+
+public abstract class ImageModifier {
+    /**
+     * Current background color used by cadastre.gouv.fr
+     */
+    //public static int cadastreBackgroundTransp = 1; // original white but transparent
+
+    private static final long serialVersionUID = 1L;
+
+    public BufferedImage bufferedImage;
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionBoundaries.java b/cadastre-fr/src/cadastre_fr/MenuActionBoundaries.java
new file mode 100644
index 0000000..74dd071
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionBoundaries.java
@@ -0,0 +1,37 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionBoundaries extends JosmAction {
+    
+    public static String name = "Administrative boundary";
+
+    private static final long serialVersionUID = 1L;
+    private WMSLayer wmsLayer = null;
+   
+    public MenuActionBoundaries() {
+        super(tr(name), "cadastre_small", tr("Extract commune boundary"), null, false);
+    }
+
+    public void actionPerformed(ActionEvent arg0) {
+        wmsLayer = WMSDownloadAction.getLayer();
+        if (wmsLayer != null) {
+            if (wmsLayer.isRaster()) {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("Only on vectorized layers"), tr("Error"),
+                        JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+            DownloadSVGTask.download(wmsLayer);
+        }
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionBuildings.java b/cadastre-fr/src/cadastre_fr/MenuActionBuildings.java
new file mode 100644
index 0000000..857885b
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionBuildings.java
@@ -0,0 +1,37 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionBuildings extends JosmAction {
+    
+    public static String name = "Building footprints";
+
+    private static final long serialVersionUID = 1L;
+    private WMSLayer wmsLayer = null;
+   
+    public MenuActionBuildings() {
+        super(tr(name), "cadastre_small", tr("Extract building footprints"), null, false);
+    }
+
+    public void actionPerformed(ActionEvent arg0) {
+        wmsLayer = WMSDownloadAction.getLayer();
+        if (wmsLayer != null) {
+            if (wmsLayer.isRaster()) {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("Only on vectorized layers"), tr("Error"),
+                        JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+            DownloadSVGBuilding.download(wmsLayer);
+        }
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionGrab.java b/cadastre-fr/src/cadastre_fr/MenuActionGrab.java
new file mode 100644
index 0000000..24028d4
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionGrab.java
@@ -0,0 +1,46 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class MenuActionGrab extends JosmAction {
+
+    /**
+     * Action calling the wms grabber for cadastre.gouv.fr
+     */
+    private static final long serialVersionUID = 1L;
+
+    public static String name = "Cadastre grab";
+
+    public MenuActionGrab() {
+        super(tr(name), "cadastre_small", tr("Download Image from French Cadastre WMS"),
+                Shortcut.registerShortcut("cadastre:grab", tr("Cadastre: {0}", tr("Download Image from French Cadastre WMS")),
+                KeyEvent.VK_F11, Shortcut.GROUP_DIRECT), false);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        if (Main.map != null) {
+            if (CadastrePlugin.isCadastreProjection()) {
+                WMSLayer wmsLayer = WMSDownloadAction.getLayer();
+                if (wmsLayer != null)
+                    DownloadWMSVectorImage.download(wmsLayer);
+            } else {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("To enable the cadastre WMS plugin, change\n"
+                         + "the current projection to one of the cadastre\n"
+                         + "projections and retry"));
+            }
+        } else
+            new MenuActionNewLocation().actionPerformed(e);
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java b/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java
new file mode 100644
index 0000000..506d555
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java
@@ -0,0 +1,348 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.ArrayList;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+
+public class MenuActionGrabPlanImage extends JosmAction implements Runnable, MouseListener {
+
+    /**
+     * Action calling the wms grabber for non georeferenced images called "plan image"
+     */
+    private static final long serialVersionUID = 1L;
+
+    public static String name = "Georeference an image";
+
+    private DownloadWMSPlanImage downloadWMSPlanImage;
+    private WMSLayer wmsLayer;
+    private int countMouseClicked = 0;
+    private int mode = 0;
+    private int cGetCorners = 1;
+    private int cGetLambertCrosspieces = 2;
+    private EastNorth ea1;
+    private long mouseClickedTime = 0;
+    private EastNorth georefpoint1;
+    private EastNorth georefpoint2;
+    /**
+     * The time which needs to pass between two clicks during georeferencing, in milliseconds
+     */
+    private int initialClickDelay;
+
+    public MenuActionGrabPlanImage() {
+        super(tr(name), "cadastre_small", tr("Grab non-georeferenced image"), null, false);
+    }
+
+    public void actionCompleted() {
+        countMouseClicked = 0;
+        mode = 0;
+        mouseClickedTime = System.currentTimeMillis();
+    }
+
+    public void actionInterrupted() {
+        actionCompleted();
+        wmsLayer = null;
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        if (wmsLayer == null || Main.map == null || Main.map.mapView == null) return;
+        if (countMouseClicked == 0 && mode == 0) return;
+        for (Layer l : Main.map.mapView.getAllLayersAsList())
+            if (l == wmsLayer)
+                return;
+        JOptionPane.showMessageDialog(Main.parent, tr("Georeferencing interrupted"));
+        actionInterrupted();
+    }
+
+    public void actionPerformed(ActionEvent ae) {
+        if (Main.map != null) {
+            if (CadastrePlugin.isCadastreProjection()) {
+                //wmsLayer = WMSDownloadAction.getLayer();
+                wmsLayer = new MenuActionNewLocation().addNewLayer(new ArrayList<WMSLayer>());
+                if (wmsLayer == null) return;
+                downloadWMSPlanImage = new DownloadWMSPlanImage();
+                downloadWMSPlanImage.download(wmsLayer);
+                initialClickDelay = Main.pref.getInteger("cadastrewms.georef-click-delay",200);
+                // download sub-images of the cadastre scan and join them into one single
+                Main.worker.execute(this);
+            } else {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("To enable the cadastre WMS plugin, change\n"
+                         + "the current projection to one of the cadastre\n"
+                         + "projections and retry"));
+            }
+        }
+    }
+
+    public void run() {
+        // wait until plan image is fully loaded and joined into one single image
+        boolean loadedFromCache = downloadWMSPlanImage.waitFinished();
+        if (wmsLayer.images.size() == 1 && !loadedFromCache) {
+            int reply = JOptionPane.CANCEL_OPTION;
+            if (wmsLayer.isAlreadyGeoreferenced()) {
+                reply = JOptionPane.showConfirmDialog(null,
+                        tr("This image contains georeference data.\n"+
+                                "Do you want to use them ?"),
+                        null,
+                        JOptionPane.YES_NO_OPTION);
+            }
+            if (reply == JOptionPane.OK_OPTION) {
+                transformGeoreferencedImg();
+            } else {
+                mouseClickedTime = System.currentTimeMillis();
+                Main.map.mapView.addMouseListener(this);
+                if (Main.pref.getBoolean("cadastrewms.noImageCropping", false) == false)
+                    startCropping();
+                else
+                    startGeoreferencing();
+            }
+        } else // action cancelled or image loaded from cache (and already georeferenced)
+            Main.map.repaint();
+    }
+
+    public void mouseClicked(MouseEvent e) {
+        if (System.currentTimeMillis() - mouseClickedTime < initialClickDelay) {
+            System.out.println("mouse click bounce detected");
+            return; // mouse click anti-bounce
+        }
+        else
+            mouseClickedTime = System.currentTimeMillis();
+        countMouseClicked++;
+        EastNorth ea = Main.proj.latlon2eastNorth(Main.map.mapView.getLatLon(e.getX(), e.getY()));
+        System.out.println("clic:"+countMouseClicked+" ,"+ea+", mode:"+mode);
+        // ignore clicks outside the image
+        if (ea.east() < wmsLayer.images.get(0).min.east() || ea.east() > wmsLayer.images.get(0).max.east()
+                || ea.north() < wmsLayer.images.get(0).min.north() || ea.north() > wmsLayer.images.get(0).max.north())
+            return;
+        if (mode == cGetCorners) {
+            if (countMouseClicked == 1) {
+                ea1 = ea;
+                continueCropping();
+            }
+            if (countMouseClicked == 2) {
+                wmsLayer.cropImage(ea1, ea);
+                Main.map.mapView.repaint();
+                startGeoreferencing();
+            }
+        } else if (mode == cGetLambertCrosspieces) {
+            if (countMouseClicked == 1) {
+                ea1 = ea;
+                if (inputLambertPosition())
+                    continueGeoreferencing();
+            }
+            if (countMouseClicked == 2) {
+                if (inputLambertPosition()) {
+                    Main.map.mapView.removeMouseListener(this);
+                    affineTransform(ea1, ea, georefpoint1, georefpoint2);
+                    wmsLayer.saveNewCache();
+                    Main.map.mapView.repaint();
+                    actionCompleted();
+                }
+            }
+        }
+    }
+
+    /**
+     *
+     * @return false if all operations are canceled
+     */
+    private boolean startCropping() {
+	    mode = cGetCorners;
+	    countMouseClicked = 0;
+		Object[] options = { "OK", "Cancel" };
+		int ret = JOptionPane.showOptionDialog( null,
+				tr("Click first corner for image cropping\n(two points required)"),
+				tr("Image cropping"),
+	    		JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+	    		null, options, options[0]);
+	    if (ret == JOptionPane.OK_OPTION) {
+	        mouseClickedTime = System.currentTimeMillis();
+	    } else
+	    	if (canceledOrRestartCurrAction("image cropping"))
+	    		return startCropping();
+	    return true;
+    }
+
+    /**
+     *
+     * @return false if all operations are canceled
+     */
+    private boolean continueCropping() {
+		Object[] options = { "OK", "Cancel" };
+		int ret = JOptionPane.showOptionDialog( null,
+				tr("Click second corner for image cropping"),
+				tr("Image cropping"),
+	    		JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+	    		null, options, options[0]);
+	    if (ret != JOptionPane.OK_OPTION) {
+	    	if (canceledOrRestartCurrAction("image cropping"))
+	    		return startCropping();
+	    }
+	    return true;
+    }
+
+    /**
+     *
+     * @return false if all operations are canceled
+     */
+    private boolean startGeoreferencing() {
+	    countMouseClicked = 0;
+	    mode = cGetLambertCrosspieces;
+		Object[] options = { "OK", "Cancel" };
+		int ret = JOptionPane.showOptionDialog( null,
+				tr("Click first Lambert crosspiece for georeferencing\n(two points required)"),
+				tr("Image georeferencing"),
+	    		JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+	    		null, options, options[0]);
+	    if (ret == JOptionPane.OK_OPTION) {
+	        mouseClickedTime = System.currentTimeMillis();
+	    } else
+	    	if (canceledOrRestartCurrAction("georeferencing"))
+	    		return startGeoreferencing();
+	    return true;
+    }
+
+    /**
+     *
+     * @return false if all operations are canceled
+     */
+    private boolean continueGeoreferencing() {
+		Object[] options = { "OK", "Cancel" };
+		int ret = JOptionPane.showOptionDialog( null,
+				tr("Click second Lambert crosspiece for georeferencing"),
+				tr("Image georeferencing"),
+	    		JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+	    		null, options, options[0]);
+	    if (ret != JOptionPane.OK_OPTION) {
+	    	if (canceledOrRestartCurrAction("georeferencing"))
+	    		return startGeoreferencing();
+	    }
+	    return true;
+    }
+
+    /**
+     *
+     * @return false if all operations are canceled
+     */
+    private boolean canceledOrRestartCurrAction(String action) {
+    	Object[] options = { "Cancel", "Retry" };
+    	int selectedValue = JOptionPane.showOptionDialog( null,
+        		tr("Do you want to cancel completely\n"+
+        				"or just retry "+action+" ?"), "",
+        		JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
+        		null, options, options[0]);
+        if (selectedValue == 0) { // "Cancel"
+        	// remove layer
+        	Main.map.mapView.removeLayer(wmsLayer);
+            wmsLayer = null;
+            Main.map.mapView.removeMouseListener(this);
+        	return false;
+        } else
+            countMouseClicked = 0;
+        return true;
+    }
+
+    private boolean inputLambertPosition() {
+        JLabel labelEnterPosition = new JLabel(tr("Enter cadastre east,north position"));
+        JLabel labelWarning = new JLabel(tr("(Warning: verify north with arrow !!)"));
+        JPanel p = new JPanel(new GridBagLayout());
+        JLabel labelEast = new JLabel(tr("East"));
+        JLabel labelNorth = new JLabel(tr("North"));
+        final JTextField inputEast = new JTextField();
+        final JTextField inputNorth = new JTextField();
+        p.add(labelEnterPosition, GBC.eol());
+        p.add(labelWarning, GBC.eol());
+        p.add(labelEast, GBC.std().insets(0, 0, 10, 0));
+        p.add(inputEast, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
+        p.add(labelNorth, GBC.std().insets(0, 0, 10, 0));
+        p.add(inputNorth, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
+        JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null);
+        String number;
+        if (countMouseClicked == 1) number = "first";
+        else number = "second";
+        pane.createDialog(Main.parent, tr("Set {0} Lambert coordinates",number)).setVisible(true);
+        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue())) {
+            if (canceledOrRestartCurrAction("georeferencing"))
+                startGeoreferencing();
+            return false;
+        }
+        if (inputEast.getText().length() != 0 && inputNorth.getText().length() != 0) {
+            try {
+                double e = Double.parseDouble(inputEast.getText());
+                double n = Double.parseDouble(inputNorth.getText());
+                if (countMouseClicked == 1)
+                    georefpoint1 = new EastNorth(e, n);
+                else
+                    georefpoint2 = new EastNorth(e, n);
+                return true;
+            } catch (NumberFormatException e) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Use point org1 as anchor for scale, then move org1 to dst1, then rotate org2 on dst2
+     * around org1/dst1 anchor
+     * @param org1 first point at original coordinate system (the grabbed image)
+     * @param org2 second point "
+     * @param dst1 first point at final destination coordinate system (the real east/north coordinate system)
+     * @param dst2 second point "
+     */
+    private void affineTransform(EastNorth org1, EastNorth org2, EastNorth dst1, EastNorth dst2) {
+        double angle = dst1.heading(dst2) - org1.heading(org2);
+        double proportion = dst1.distance(dst2)/org1.distance(org2);
+        // move
+        double dx = dst1.getX() - org1.getX();
+        double dy = dst1.getY() - org1.getY();
+        wmsLayer.images.get(0).shear(dx, dy);
+        org1 = org1.add(dx, dy); // org1=dst1 now
+        org2 = org2.add(dx, dy);
+        // rotate : org1(=dst1 now) is anchor for rotation and scale
+        wmsLayer.images.get(0).rotate(dst1, angle);
+        org2 = org2.rotate(dst1, angle);
+        // scale image from anchor org1(=dst1 now)
+        wmsLayer.images.get(0).scale(dst1, proportion);
+    }
+
+    private void transformGeoreferencedImg() {
+        georefpoint1 = new EastNorth(wmsLayer.X0, wmsLayer.Y0);
+        georefpoint2 = new EastNorth(wmsLayer.X0+wmsLayer.fX*wmsLayer.communeBBox.max.getX(),
+                wmsLayer.Y0+wmsLayer.fY*wmsLayer.communeBBox.max.getX());
+        ea1 = new EastNorth(wmsLayer.images.get(0).min.east(), wmsLayer.images.get(0).max.north());
+        EastNorth ea2 = wmsLayer.images.get(0).max;
+        affineTransform(ea1, ea2, georefpoint1, georefpoint2);
+        wmsLayer.saveNewCache();
+        Main.map.mapView.repaint();
+    }
+
+    public void mouseEntered(MouseEvent arg0) {
+    }
+
+    public void mouseExited(MouseEvent arg0) {
+    }
+
+    public void mousePressed(MouseEvent arg0) {
+    }
+
+    public void mouseReleased(MouseEvent arg0) {
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java b/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java
new file mode 100644
index 0000000..81ec64b
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java
@@ -0,0 +1,117 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.projection.Lambert;
+import org.openstreetmap.josm.data.projection.LambertCC9Zones;
+import org.openstreetmap.josm.data.projection.UTM_20N_France_DOM;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+public class MenuActionLoadFromCache extends JosmAction {
+    private static final long serialVersionUID = 1L;
+
+    public static String name = "Load layer from cache";
+
+    public MenuActionLoadFromCache() {
+        super(tr(name), "cadastre_small", tr("Load location from cache (only if cache is enabled)"), null, false);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        JFileChooser fc = createAndOpenFileChooser();
+        if (fc == null)
+            return;
+
+        File[] files = fc.getSelectedFiles();
+        int layoutZone = getCurrentProjZone();
+        nextFile:
+        for (File file : files) {
+            if (file.exists()) {
+                String filename = file.getName();
+                String ext = (filename.lastIndexOf(".")==-1)?"":filename.substring(filename.lastIndexOf(".")+1,filename.length());
+                if ((ext.length() == 3 && ext.substring(0, CacheControl.cLambertCC9Z.length()).equals(CacheControl.cLambertCC9Z) &&
+                    !(Main.proj instanceof LambertCC9Zones))
+                    || (ext.length() == 4 && ext.substring(0, CacheControl.cUTM20N.length()).equals(CacheControl.cUTM20N) &&
+                            !(Main.proj instanceof UTM_20N_France_DOM))
+                    || (ext.length() == 1) && !(Main.proj instanceof Lambert)) {
+                        JOptionPane.showMessageDialog(Main.parent, tr("{0} not allowed with the current projection", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+                        continue;
+                } else {
+                    String location = filename.substring(0, filename.lastIndexOf("."));
+                    if (ext.length() == 3 && ext.substring(0, CacheControl.cLambertCC9Z.length()).equals(CacheControl.cLambertCC9Z))
+                        ext = ext.substring(2);
+                    else if (ext.length() == 4 && ext.substring(0, CacheControl.cUTM20N.length()).equals(CacheControl.cUTM20N))
+                        ext = ext.substring(3);
+                    // check the extension and its compatibility with current projection
+                    try {
+                        int cacheZone = Integer.parseInt(ext) - 1;
+                        if (cacheZone >=0 && cacheZone <= 9) {
+                            if (cacheZone != layoutZone) {
+                                JOptionPane.showMessageDialog(Main.parent, tr("Cannot load cache {0} which is not compatible with current projection zone", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+                                continue nextFile;
+                            } else
+                                System.out.println("Load cache " + filename);
+                        }
+                    } catch (NumberFormatException ex) {
+                        JOptionPane.showMessageDialog(Main.parent, tr("Selected file {0} is not a cache file from this plugin (invalid extension)", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+                        continue nextFile;
+                    }
+                    // check if the selected cache is not already displayed
+                    if (Main.map != null) {
+                        for (Layer l : Main.map.mapView.getAllLayers()) {
+                            if (l instanceof WMSLayer && l.getName().equals(location)) {
+                                JOptionPane.showMessageDialog(Main.parent, tr("The location {0} is already on screen. Cache not loaded.", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+                                continue nextFile;
+                            }
+                        }
+                    }
+                    // create layer and load cache
+                    WMSLayer wmsLayer = new WMSLayer("", "", Integer.parseInt(ext)-1);
+                    if (wmsLayer.getCacheControl().loadCache(file, layoutZone))
+                        Main.main.addLayer(wmsLayer);
+                    
+                }
+            }
+        }
+
+    }
+
+    protected static JFileChooser createAndOpenFileChooser() {
+        JFileChooser fc = new JFileChooser(new File(CadastrePlugin.cacheDir));
+        fc.setMultiSelectionEnabled(true);
+        int layoutZone = new MenuActionLoadFromCache().getCurrentProjZone();
+        if (layoutZone != -1) {
+            if (Main.proj instanceof Lambert)
+                fc.addChoosableFileFilter(CacheFileLambert4ZoneFilter.filters[layoutZone]);
+            else if (Main.proj instanceof LambertCC9Zones)
+                fc.addChoosableFileFilter(CacheFileLambert9ZoneFilter.filters[layoutZone]);
+            else if (Main.proj instanceof UTM_20N_France_DOM)
+                fc.addChoosableFileFilter(CacheFileUTM20NFilter.filters[layoutZone]);
+        }
+        fc.setAcceptAllFileFilterUsed(false);
+
+        int answer = fc.showOpenDialog(Main.parent);
+        if (answer != JFileChooser.APPROVE_OPTION)
+            return null;
+
+        return fc;
+    }
+
+    private int getCurrentProjZone() {
+        int zone = -1;
+        if (Main.proj instanceof LambertCC9Zones)
+            zone = ((LambertCC9Zones)Main.proj).getLayoutZone();
+        else if (Main.proj instanceof Lambert)
+            zone = ((Lambert)Main.proj).getLayoutZone();
+        else if (Main.proj instanceof UTM_20N_France_DOM)
+            zone = ((UTM_20N_France_DOM)Main.proj).getCurrentGeodesic();
+        return zone;
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java b/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
new file mode 100644
index 0000000..1d40d5e
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
@@ -0,0 +1,99 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.projection.Lambert;
+import org.openstreetmap.josm.data.projection.LambertCC9Zones;
+import org.openstreetmap.josm.data.projection.UTM_20N_France_DOM;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+
+public class MenuActionNewLocation extends JosmAction {
+
+    private static final long serialVersionUID = 1L;
+
+    public MenuActionNewLocation() {
+        super(tr("Change location"), "cadastre_small", tr("Set a new location for the next request"), null, false);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        WMSLayer wmsLayer = addNewLayer(new ArrayList<WMSLayer>());
+        if (wmsLayer != null)
+            DownloadWMSVectorImage.download(wmsLayer);
+    }
+
+    public WMSLayer addNewLayer(ArrayList<WMSLayer> existingLayers) {
+        String location = "";
+        String codeDepartement = "";
+        String codeCommune = "";
+        boolean resetCookie = false;
+        JLabel labelSectionNewLocation = new JLabel(tr("Add a new layer"));
+        JPanel p = new JPanel(new GridBagLayout());
+        JLabel labelLocation = new JLabel(tr("Location"));
+        final JTextField inputTown = new JTextField( Main.pref.get("cadastrewms.location") );
+        inputTown.setToolTipText(tr("<html>Enter the town,village or city name.<br>"
+                + "Use the syntax and punctuation known by www.cadastre.gouv.fr .</html>"));
+
+        p.add(labelSectionNewLocation, GBC.eol());
+        p.add(labelLocation, GBC.std().insets(10, 0, 0, 0));
+        p.add(inputTown, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+        JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null) {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public void selectInitialValue() {
+                inputTown.requestFocusInWindow();
+                inputTown.selectAll();
+            }
+        };
+        pane.createDialog(Main.parent, tr("Add new layer")).setVisible(true);
+        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+            return null;
+
+        WMSLayer wmsLayer = null;
+        if (!inputTown.getText().equals("")) {
+            location = inputTown.getText().toUpperCase();
+            resetCookie = true;
+            Main.pref.put("cadastrewms.location", location);
+            Main.pref.put("cadastrewms.codeCommune", codeCommune);
+            if (Main.map != null) {
+                for (Layer l : Main.map.mapView.getAllLayers()) {
+                    if (l instanceof WMSLayer && l.getName().equalsIgnoreCase(location + codeDepartement)) {
+                        return null;
+                    }
+                }
+            }
+            // add the layer if it doesn't exist
+            int zone = -1;
+            if (Main.proj instanceof LambertCC9Zones)
+                zone = ((LambertCC9Zones)Main.proj).getLayoutZone();
+            else if (Main.proj instanceof Lambert)
+                zone = ((Lambert)Main.proj).getLayoutZone();
+            else if (Main.proj instanceof UTM_20N_France_DOM)
+                zone = ((UTM_20N_France_DOM)Main.proj).getCurrentGeodesic();
+            wmsLayer = new WMSLayer(location, codeCommune, zone);
+            Main.main.addLayer(wmsLayer);
+            System.out.println("Add new layer with Location:" + inputTown.getText());
+        } else if (existingLayers != null && existingLayers.size() > 0 && Main.map.mapView.getActiveLayer() instanceof WMSLayer) {
+            wmsLayer = (WMSLayer)Main.map.mapView.getActiveLayer();
+            resetCookie = true;
+        }
+
+        if (resetCookie)
+            CadastrePlugin.cadastreGrabber.getWmsInterface().resetCookieIfNewLayer(wmsLayer.getName());
+        return wmsLayer;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java b/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java
new file mode 100644
index 0000000..bf50dbf
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java
@@ -0,0 +1,24 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionResetCookie extends JosmAction {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    public MenuActionResetCookie() {
+        super(tr("Reset cookie"), "cadastre_small", tr("Get a new cookie (session timeout)"), null, false);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        CadastrePlugin.cadastreGrabber.getWmsInterface().resetCookie();
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionSaveRasterAs.java b/cadastre-fr/src/cadastre_fr/MenuActionSaveRasterAs.java
new file mode 100644
index 0000000..13d9cf4
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionSaveRasterAs.java
@@ -0,0 +1,74 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionSaveRasterAs extends JosmAction {
+
+    public static String name = "Save image as PNG";
+    
+    private static final long serialVersionUID = 1L;
+    
+    private WMSLayer wmsLayer;
+    
+    public class FiltrePng extends FileFilter {
+        @Override
+        public boolean accept(File file) {
+            if (file.isDirectory()) { 
+                return true;
+            } 
+            return file.getName().toLowerCase().endsWith(".png");
+        }
+
+        @Override
+        public String getDescription() {
+            return tr("PNG files (*.png)");
+        }
+        
+    }
+    
+    FiltrePng filtrePng = new FiltrePng();
+
+    public MenuActionSaveRasterAs(WMSLayer wmsLayer) {
+        super(tr(name), "save", tr("Export as PNG format (only raster images)"), null, false);
+        this.wmsLayer = wmsLayer;
+    }
+
+    public void actionPerformed(ActionEvent arg0) {
+        File file;
+        JFileChooser fc = new JFileChooser();
+        fc.setFileFilter(filtrePng);
+        int returnVal = fc.showSaveDialog(Main.parent);
+        if (returnVal == JFileChooser.APPROVE_OPTION) {
+            file = fc.getSelectedFile();
+            if (!file.getName().endsWith(".png"))
+                file = new File(file.getParent(), file.getName()+".png");
+            BufferedImage bi = wmsLayer.images.get(0).image; 
+            try {
+                ImageIO.write(bi, "png", file);
+/*
+                FileOutputStream flux = new FileOutputStream(file);
+                BufferedOutputStream fluxBuf = new BufferedOutputStream(flux);
+                JPEGImageEncoder codec = JPEGCodec.createJPEGEncoder(fluxBuf, JPEGCodec.getDefaultJPEGEncodeParam(bi));
+                codec.encode(bi);
+                fluxBuf.close();
+*/
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/RasterImageModifier.java b/cadastre-fr/src/cadastre_fr/RasterImageModifier.java
new file mode 100644
index 0000000..fd1470a
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/RasterImageModifier.java
@@ -0,0 +1,104 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.awt.image.ComponentColorModel;
+
+import org.openstreetmap.josm.Main;
+
+public class RasterImageModifier extends ImageModifier {
+
+    private int cadastreBackground = -1; // white
+    
+    public static int cadastreBackgroundTransp = 16777215; // original white but transparent
+    
+    private boolean transparencyEnabled = false;
+
+    public RasterImageModifier(BufferedImage bi) {
+        bufferedImage = bi;
+        transparencyEnabled = Main.pref.getBoolean("cadastrewms.backgroundTransparent"); 
+        if (transparencyEnabled)
+            makeTransparent();
+        if (Main.pref.getBoolean("cadastrewms.invertGrey"))
+            invertGrey();
+    }
+
+    /**
+     * Invert black/white/grey pixels (to change original black characters to white).
+     */
+    private void invertGrey() {
+        int w = bufferedImage.getWidth();
+        int h = bufferedImage.getHeight();
+        for (int x = 0; x < w; x++) {
+            for (int y = 0; y < h; y++) {
+                int pixel = bufferedImage.getRGB(x, y);
+                if ((!transparencyEnabled && pixel != cadastreBackground) 
+                        || (transparencyEnabled && pixel != cadastreBackgroundTransp)) {
+                    bufferedImage.setRGB(x, y, reverseIfGrey(pixel));
+                }
+            }
+        }
+    }
+
+    /**
+     * Reverse the grey value if the pixel is grey (light grey becomes dark grey)
+     * Used for texts.
+     * @param pixel
+     * @return
+     */
+    private int reverseIfGrey(int pixel) {
+        Color col = new Color(pixel);
+        int r = col.getRed();
+        int g = col.getGreen();
+        int b = col.getBlue();
+        if ((b == r) && (b == g)) {
+            pixel = (0x00 << 32) + ((byte) (255 - r) << 16) + ((byte) (255 - r) << 8) + ((byte) (255 - r));
+        }
+        return pixel;
+    }
+
+    private void makeTransparent() {
+        if (bufferedImage.getColorModel() instanceof ComponentColorModel) {
+            int width = bufferedImage.getWidth();
+            int height = bufferedImage.getHeight();
+            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+            for (int y = 0; y < height; y++) {
+                for (int x = 0; x < width; x++) {
+                    int rgb = bufferedImage.getRGB(x, y);
+                    Color c = new Color(rgb);
+                    int r = c.getRed();
+                    int g = c.getGreen();
+                    int b = c.getBlue();
+                    Color maskedColor;
+                    if (rgb == cadastreBackground) {
+                        maskedColor = new Color(r, g, b, 0x00); // transparent
+                    } else {
+                        maskedColor = new Color(r, g, b, 0xFF); // opaque
+                    }
+                    //maskedColor = new Color(r, g, b, alpha);
+                    bi.setRGB(x, y, maskedColor.getRGB());
+                }
+            }
+            bufferedImage = bi;
+        }
+        return;
+    }
+    
+    /**
+     * Temporary fix for Java6 which doesn't de-serialize correctly cached image on disk. 
+     * Recreate a new raster image based on what is loaded/serialized from disk cache. 
+     * @param img
+     * @return new image
+     */
+    public static BufferedImage fixRasterImage(BufferedImage img) {
+        int width = img.getWidth();
+        int height = img.getHeight();
+        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        int rgbArray[] = new int[width * height];
+        img.getRGB(0, 0, width, height, rgbArray, 0, width);
+        bi.setRGB(0, 0, width, height, rgbArray, 0, width);
+        return bi;
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/SVGParser.java b/cadastre-fr/src/cadastre_fr/SVGParser.java
new file mode 100644
index 0000000..d3ae0ad
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/SVGParser.java
@@ -0,0 +1,65 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.util.ArrayList;
+
+/**
+ * This class is not intended to be a real SVG parser. It's also not using existing
+ * xml parsers. It's just extracting the required strings from an SVG file coming
+ * from the French land registry cadastre.gouv.fr 
+ *
+ */
+public class SVGParser {
+
+    private String cViewBoxStart = "viewBox=\"";
+    private String cViewBoxEnd = "\"";
+    private String cPathStart = "<path d=\"";
+    private String cClosedPathEnd = "\"/>";
+
+    /**
+     * The SVG viewBox looks like this:
+     *   viewBox="969780.0 320377.11 5466.130000000005 2846.429999999993"
+     * @param svg the SVG XML data
+     * @return double [x,y,dx,dy] of viewBox; null if parsing failed
+     */
+    public double[] getViewBox(String svg) {
+        int s = svg.indexOf(cViewBoxStart)+cViewBoxStart.length();
+        int e = svg.indexOf(cViewBoxEnd, s);
+        if (s != -1 && e != -1) {
+            try {
+                String str = svg.substring(s, e);
+                String [] viewBox = str.split(" ");
+                double[] dbox = new double[4];
+                for (int i = 0; i<4; i++)
+                    dbox[i] = Double.parseDouble(viewBox[i]); 
+                return dbox;
+            } catch (Exception ex) {
+                return null;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Closed SVG paths are finishing with a "Z" at the end of the moves list.
+     * @param svg
+     * @return
+     */
+    public String [] getClosedPaths(String svg) {
+        ArrayList<String> path = new ArrayList<String>();
+        int i = 0;
+        while (svg.indexOf(cPathStart, i) != -1) {
+            int s = svg.indexOf(cPathStart, i) + cViewBoxStart.length();
+            int e = svg.indexOf(cClosedPathEnd, s);
+            if (s != -1 && e != -1) {
+                String onePath = svg.substring(s, e); 
+                if (onePath.indexOf("Z") != -1) // only closed SVG path
+                    path.add(onePath);
+            } else
+                break;
+            i = e;
+        }
+        return path.toArray(new String[ path.size() ]);
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/Scale.java b/cadastre-fr/src/cadastre_fr/Scale.java
new file mode 100644
index 0000000..49d2373
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/Scale.java
@@ -0,0 +1,29 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+/**
+ * List of possible grab factors each time we call the grab action.
+ * X1 means that only one bounding box is grabbed where X2 means that the current
+ * view is split in 2x2 bounding boxes and X3 is 3x3 boxes.
+ * SQUARE_100M is a special value where bounding boxes have a fixed size of 100x100 meters
+ * and east,north are rounded to the lowest 100 meter as well, thus none of the bounding boxes
+ * are overlapping each others.
+ */
+public enum Scale {
+    X1("1"),
+    X2("2"),
+    X3("3"),
+    SQUARE_100M("4");
+
+    /**
+     * value is the string equivalent stored in the preferences file
+     */
+    public final String value;
+
+    Scale(String value) {
+        this.value = value;
+    }
+    public String toString() {
+        return value;
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/SimplifyWay.java b/cadastre-fr/src/cadastre_fr/SimplifyWay.java
new file mode 100644
index 0000000..d6a8b35
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/SimplifyWay.java
@@ -0,0 +1,128 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+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 org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+ 
+
+/**
+ * Imported from plugin UtilsPlugin
+ * @author 
+ *
+ */
+public class SimplifyWay {
+    public void simplifyWay(Way w, DataSet dataSet, double threshold) {
+        Way wnew = new Way(w);
+
+        int toI = wnew.getNodesCount() - 1;
+        for (int i = wnew.getNodesCount() - 1; i >= 0; i--) {
+            CollectBackReferencesVisitor backRefsV = new CollectBackReferencesVisitor(dataSet, false);
+            backRefsV.visit(wnew.getNode(i));
+            boolean used = false;
+            if (backRefsV.getData().size() == 1) {
+                used = Collections.frequency(w.getNodes(), wnew.getNode(i)) > 1;
+            } else {
+                backRefsV.getData().remove(w);
+                used = !backRefsV.getData().isEmpty();
+            }
+            if (!used)
+                used = wnew.getNode(i).isTagged();
+
+            if (used) {
+                simplifyWayRange(wnew, i, toI, threshold);
+                toI = i;
+            }
+        }
+        simplifyWayRange(wnew, 0, toI, threshold);
+
+        HashSet<Node> delNodes = new HashSet<Node>();
+        delNodes.addAll(w.getNodes());
+        delNodes.removeAll(wnew.getNodes());
+
+        if (wnew.getNodesCount() != w.getNodesCount()) {
+            Collection<Command> cmds = new LinkedList<Command>();
+            cmds.add(new ChangeCommand(w, wnew));
+            cmds.add(new DeleteCommand(delNodes));
+            Main.main.undoRedo.add(new SequenceCommand(trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds));
+            Main.map.repaint();
+        }
+    }
+
+    public void simplifyWayRange(Way wnew, int from, int to, double thr) {
+        if (to - from >= 2) {
+            ArrayList<Node> ns = new ArrayList<Node>();
+            simplifyWayRange(wnew, from, to, ns, thr);
+            List<Node> nodes = wnew.getNodes();
+            for (int j = to - 1; j > from; j--)
+                nodes.remove(j);
+            nodes.addAll(from+1, ns);
+            wnew.setNodes(nodes);
+        }
+    }
+    
+    /*
+     * Takes an interval [from,to] and adds nodes from (from,to) to ns.
+     * (from and to are indices of wnew.nodes.)
+     */
+    public void simplifyWayRange(Way wnew, int from, int to, ArrayList<Node> ns, double thr) {
+        Node fromN = wnew.getNode(from), toN = wnew.getNode(to);
+
+        int imax = -1;
+        double xtemax = 0;
+        for (int i = from + 1; i < to; i++) {
+            Node n = wnew.getNode(i);
+            double xte = Math.abs(EARTH_RAD
+                    * xtd(fromN.getCoor().lat() * Math.PI / 180, fromN.getCoor().lon() * Math.PI / 180, toN.getCoor().lat() * Math.PI
+                            / 180, toN.getCoor().lon() * Math.PI / 180, n.getCoor().lat() * Math.PI / 180, n.getCoor().lon() * Math.PI
+                            / 180));
+            if (xte > xtemax) {
+                xtemax = xte;
+                imax = i;
+            }
+        }
+
+        if (imax != -1 && xtemax >= thr) {
+            simplifyWayRange(wnew, from, imax, ns, thr);
+            ns.add(wnew.getNode(imax));
+            simplifyWayRange(wnew, imax, to, ns, thr);
+        }
+    }
+    public static double EARTH_RAD = 6378137.0;
+    /* From Aviaton Formulary v1.3
+     * http://williams.best.vwh.net/avform.htm
+     */
+    public static double dist(double lat1, double lon1, double lat2, double lon2) {
+        return 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) + Math.cos(lat1) * Math.cos(lat2)
+                * Math.pow(Math.sin((lon1 - lon2) / 2), 2)));
+    }
+
+    public static double course(double lat1, double lon1, double lat2, double lon2) {
+        return Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
+                * Math.cos(lat2) * Math.cos(lon1 - lon2))
+                % (2 * Math.PI);
+    }
+    public static double xtd(double lat1, double lon1, double lat2, double lon2, double lat3, double lon3) {
+        double dist_AD = dist(lat1, lon1, lat3, lon3);
+        double crs_AD = course(lat1, lon1, lat3, lon3);
+        double crs_AB = course(lat1, lon1, lat2, lon2);
+        return Math.asin(Math.sin(dist_AD) * Math.sin(crs_AD - crs_AB));
+    }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/VectorImageModifier.java b/cadastre-fr/src/cadastre_fr/VectorImageModifier.java
new file mode 100644
index 0000000..befd3bc
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/VectorImageModifier.java
@@ -0,0 +1,112 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.WritableRaster;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.ColorHelper;
+
+public class VectorImageModifier extends ImageModifier {
+
+    private int cadastreBackground = -1; // white
+    
+    public static int cadastreBackgroundTransp = 1; // original white but transparent
+
+    private boolean withBackground = false;
+
+    private int backgroundPixel = 0;
+
+    private int backgroundSampleX, backgroundSampleY;
+
+    public VectorImageModifier(BufferedImage bi) {
+        bufferedImage = bi;
+        if (Main.pref.getBoolean("cadastrewms.backgroundTransparent"))
+            makeTransparent();
+        else if (Main.pref.getBoolean("cadastrewms.alterColors"))
+            replaceBackground();
+        if (Main.pref.getBoolean("cadastrewms.invertGrey"))
+            invertGrey();
+    }
+
+    /**
+     * Replace the background color by the josm color.background color.
+     */
+    private void replaceBackground() {
+        int w = bufferedImage.getWidth();
+        int h = bufferedImage.getHeight();
+        int josmBackgroundColor = ColorHelper.html2color(Main.pref.get("color.background", "#000000")).getRGB();
+        for (int x = 0; x < w; x++) {
+            for (int y = 0; y < h; y++) {
+                int pixel = bufferedImage.getRGB(x, y);
+                if (pixel == cadastreBackground) {
+                    bufferedImage.setRGB(x, y, josmBackgroundColor);
+                    if (!withBackground)
+                        withBackground = true;
+                    backgroundSampleX = x;
+                    backgroundSampleY = y;
+                }
+            }
+        }
+    }
+
+    /**
+     * Invert black/white/grey pixels (to change original black characters to white).
+     */
+    private void invertGrey() {
+        int w = bufferedImage.getWidth();
+        int h = bufferedImage.getHeight();
+        for (int x = 0; x < w; x++) {
+            for (int y = 0; y < h; y++) {
+                int pixel = bufferedImage.getRGB(x, y);
+                if (pixel != cadastreBackground) {
+                    bufferedImage.setRGB(x, y, reverseIfGrey(pixel));
+                }
+            }
+        }
+    }
+
+    /**
+     * Reverse the grey value if the pixel is grey (light grey becomes dark grey)
+     * Used for texts.
+     * @param pixel
+     * @return
+     */
+    private int reverseIfGrey(int pixel) {
+        Color col = new Color(pixel);
+        int r = col.getRed();
+        int g = col.getGreen();
+        int b = col.getBlue();
+        if ((b == r) && (b == g)) {
+            pixel = (0x00 << 32) + ((byte) (255 - r) << 16) + ((byte) (255 - r) << 8) + ((byte) (255 - r));
+        }
+        return pixel;
+    }
+
+    private void makeTransparent() {
+        ColorModel colorModel = bufferedImage.getColorModel();
+        if (bufferedImage.getColorModel() instanceof IndexColorModel) {
+            // vector image (IndexColorModel)
+            IndexColorModel icm = (IndexColorModel) colorModel;
+            WritableRaster raster = bufferedImage.getRaster();
+            // pixel is offset in ICM's palette
+            if (withBackground)
+                backgroundPixel = raster.getSample(backgroundSampleX, backgroundSampleY, 0);
+            else
+                backgroundPixel = 1; // default Cadastre background sample
+            int size = icm.getMapSize();
+            byte[] reds = new byte[size];
+            byte[] greens = new byte[size];
+            byte[] blues = new byte[size];
+            icm.getReds(reds);
+            icm.getGreens(greens);
+            icm.getBlues(blues);
+            IndexColorModel icm2 = new IndexColorModel(colorModel.getPixelSize(), size, reds, greens, blues,
+                    backgroundPixel);
+            bufferedImage = new BufferedImage(icm2, raster, bufferedImage.isAlphaPremultiplied(), null);
+        }
+        return;
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java b/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java
new file mode 100644
index 0000000..95cb1d8
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java
@@ -0,0 +1,159 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Cursor;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+public class WMSAdjustAction extends MapMode implements
+        MouseListener, MouseMotionListener{
+
+    private static final long serialVersionUID = 1L;
+    private ArrayList<WMSLayer> modifiedLayers = new ArrayList<WMSLayer>();
+    WMSLayer selectedLayer;
+    private boolean rasterMoved;
+    private EastNorth prevEastNorth;
+    enum Mode { moveXY, moveZ, rotate}
+    private Mode mode = null;
+
+    public WMSAdjustAction(MapFrame mapFrame) {
+        super(tr("Adjust WMS"), "adjustxywms",
+                        tr("Adjust the position of the WMS layer (raster images only)"), mapFrame,
+                        ImageProvider.getCursor("normal", "move"));
+    }
+
+    @Override public void enterMode() {
+        if (Main.map != null) {
+            selectedLayer = null;
+            WMSLayer possibleLayer = null;
+            int cRasterLayers = 0;
+            for (Layer l : Main.map.mapView.getAllLayers()) {
+                if (l instanceof WMSLayer && ((WMSLayer)l).isRaster()) {
+                    possibleLayer = (WMSLayer)l;
+                    cRasterLayers++;
+                }
+            }
+            Layer activeLayer = Main.map.mapView.getActiveLayer();
+            if (activeLayer instanceof WMSLayer && ((WMSLayer)activeLayer).isRaster()) {
+                selectedLayer = (WMSLayer)activeLayer;
+            } else if (cRasterLayers == 1) {
+                selectedLayer = possibleLayer;
+            }
+            if (selectedLayer != null) {
+                super.enterMode();
+                Main.map.mapView.addMouseListener(this);
+                Main.map.mapView.addMouseMotionListener(this);
+                rasterMoved = false;
+            } else {
+                JOptionPane.showMessageDialog(Main.parent,tr("This mode works only if active layer is\n"
+                        +"a cadastre \"plan image\" (raster image)"));
+            }
+        }
+    }
+
+    @Override public void exitMode() {
+        super.exitMode();
+        Main.map.mapView.removeMouseListener(this);
+        Main.map.mapView.removeMouseMotionListener(this);
+        if (rasterMoved && CacheControl.cacheEnabled) {
+            int reply = JOptionPane.showConfirmDialog(null,
+                    "Save the changes in cache ?",
+                    "Update cache",
+                    JOptionPane.YES_NO_OPTION);
+            if (reply == JOptionPane.OK_OPTION) {
+                saveModifiedLayers();
+            }
+        }
+        modifiedLayers.clear();
+        selectedLayer = null;
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        if (e.getButton() != MouseEvent.BUTTON1)
+            return;
+        boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+        // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
+        boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
+        if (shift)
+            mode = Mode.moveZ;
+        else if (ctrl)
+            mode = Mode.rotate;
+        else
+            mode = Mode.moveXY;
+        rasterMoved = true;
+        prevEastNorth = Main.map.mapView.getEastNorth(e.getX(), e.getY());
+        Main.map.mapView.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+    }
+
+    @Override public void mouseDragged(MouseEvent e) {
+        EastNorth newEastNorth = Main.map.mapView.getEastNorth(e.getX(),e.getY());
+        if (mode == Mode.moveXY) {
+            displace(prevEastNorth, newEastNorth);
+        } else if (mode == Mode.moveZ) {
+            resize(newEastNorth);
+        } else if (mode == Mode.rotate) {
+            rotate(prevEastNorth, newEastNorth);
+        }
+        if (!modifiedLayers.contains(selectedLayer))
+            modifiedLayers.add(selectedLayer);
+        Main.map.mapView.repaint();
+        prevEastNorth = newEastNorth;
+    }
+
+    private void displace(EastNorth start, EastNorth end) {
+        selectedLayer.displace(end.east()-start.east(), end.north()-start.north());
+    }
+
+    private void resize(EastNorth newEastNorth) {
+        EastNorth center = selectedLayer.getRasterCenter();
+        double dPrev = prevEastNorth.distance(center.east(), center.north());
+        double dNew = newEastNorth.distance(center.east(), center.north());
+        selectedLayer.resize(center, dNew/dPrev);
+    }
+
+    private void rotate(EastNorth start, EastNorth end) {
+        EastNorth pivot = selectedLayer.getRasterCenter();
+        double startAngle = Math.atan2(start.east()-pivot.east(), start.north()-pivot.north());
+        double endAngle = Math.atan2(end.east()-pivot.east(), end.north()-pivot.north());
+        double rotationAngle = endAngle - startAngle;
+        selectedLayer.rotate(pivot, rotationAngle);
+    }
+
+    @Override public void mouseReleased(MouseEvent e) {
+        //Main.map.mapView.repaint();
+        Main.map.mapView.setCursor(Cursor.getDefaultCursor());
+        prevEastNorth = null;
+        mode = null;
+    }
+
+    public void mouseEntered(MouseEvent e) {
+    }
+    public void mouseExited(MouseEvent e) {
+    }
+    public void mouseMoved(MouseEvent e) {
+    }
+
+    @Override public void mouseClicked(MouseEvent e) {
+    }
+
+    private void saveModifiedLayers() {
+        for (WMSLayer wmsLayer : modifiedLayers) {
+            wmsLayer.saveNewCache();
+        }
+    }
+}
diff --git a/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java b/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java
new file mode 100644
index 0000000..082f76d
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java
@@ -0,0 +1,51 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+public class WMSDownloadAction extends JosmAction {
+
+    private static final long serialVersionUID = 1L;
+
+    public WMSDownloadAction(String layerName) {
+        super(layerName, "wmsmenu", tr("Download WMS tile from {0}",layerName), null, false);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        DownloadWMSVectorImage.download(getLayer());
+    }
+
+    public static WMSLayer getLayer() {
+        // check if we already have a layer created. if not, create; if yes, reuse.
+        ArrayList<WMSLayer> existingWMSlayers = new ArrayList<WMSLayer>();
+        if (Main.map != null) {
+            Layer activeLayer = Main.map.mapView.getActiveLayer();
+            if (activeLayer instanceof WMSLayer)
+                return (WMSLayer) activeLayer;
+            for (Layer l : Main.map.mapView.getAllLayers()) {
+                if (l instanceof WMSLayer) {
+                    existingWMSlayers.add((WMSLayer)l);
+                }
+            }
+            if (existingWMSlayers.size() == 1)
+                return existingWMSlayers.get(0);
+            if (existingWMSlayers.size() == 0)
+                return new MenuActionNewLocation().addNewLayer(existingWMSlayers);
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("More than one WMS layer present\nSelect one of them first, then retry"));
+        } else {
+            return new MenuActionNewLocation().addNewLayer(existingWMSlayers);
+        }
+        return null;
+    }
+};
+
diff --git a/cadastre-fr/src/cadastre_fr/WMSLayer.java b/cadastre-fr/src/cadastre_fr/WMSLayer.java
new file mode 100644
index 0000000..5a9972c
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/WMSLayer.java
@@ -0,0 +1,613 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.EastNorth;
+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.io.OsmTransferException;
+
+/**
+ * This is a layer that grabs the current screen from the French cadastre WMS
+ * server. The data fetched this way is tiled and managed to the disc to reduce
+ * server load.
+ */
+public class WMSLayer extends Layer implements ImageObserver {
+
+    Component[] component = null;
+
+    private int lambertZone = -1;
+
+    protected static final Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(
+            CadastrePlugin.class.getResource("/images/cadastre_small.png")));
+
+    protected Vector<GeorefImage> images = new Vector<GeorefImage>();
+
+    protected final int serializeFormatVersion = 2;
+
+    private ArrayList<EastNorthBound> dividedBbox = new ArrayList<EastNorthBound>();
+
+    private CacheControl cacheControl = null;
+
+    private String location = "";
+
+    private String codeCommune = "";
+
+    public EastNorthBound communeBBox = new EastNorthBound(new EastNorth(0,0), new EastNorth(0,0));
+
+    private boolean isRaster = false;
+
+    private boolean isAlreadyGeoreferenced = false;
+
+    public double X0, Y0, angle, fX, fY;
+
+    private EastNorth rasterMin;
+    private EastNorth rasterMax;
+    private double rasterRatio;
+
+    private JMenuItem saveAsPng;
+
+    public WMSLayer() {
+        this(tr("Blank Layer"), "", -1);
+    }
+
+    public WMSLayer(String location, String codeCommune, int lambertZone) {
+        super(buildName(location, codeCommune));
+        this.location = location;
+        this.codeCommune = codeCommune;
+        this.lambertZone = lambertZone;
+        // enable auto-sourcing option
+        CadastrePlugin.pluginUsed = true;
+    }
+
+    private static String buildName(String location, String codeCommune) {
+        String ret = new String(location.toUpperCase());
+        if (codeCommune != null && !codeCommune.equals(""))
+            ret += "(" + codeCommune + ")";
+        return  ret;
+    }
+
+    private String rebuildName() {
+        return buildName(this.location.toUpperCase(), this.codeCommune);
+    }
+
+    public void grab(CadastreGrabber grabber, Bounds b) throws IOException {
+        grab(grabber, b, true);
+    }
+    
+    public void grab(CadastreGrabber grabber, Bounds b, boolean useFactor) throws IOException {
+        if (useFactor) {
+            if (isRaster) {
+                b = new Bounds(Main.proj.eastNorth2latlon(rasterMin), Main.proj.eastNorth2latlon(rasterMax));
+                divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.rasterDivider",
+                        CadastrePreferenceSetting.DEFAULT_RASTER_DIVIDER)));
+            } else
+                divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.scale", Scale.X1.toString())));
+        } else
+            divideBbox(b, 1);
+
+        for (EastNorthBound n : dividedBbox) {
+            GeorefImage newImage;
+            try {
+                newImage = grabber.grab(this, n.min, n.max);
+            } catch (IOException e) {
+                System.out.println("Download action cancelled by user or server did not respond");
+                break;
+            } catch (OsmTransferException e) {
+                System.out.println("OSM transfer failed");
+                break;
+            }
+            if (grabber.getWmsInterface().downloadCancelled) {
+                System.out.println("Download action cancelled by user");
+                break;
+            }
+            if (CadastrePlugin.backgroundTransparent) {
+                for (GeorefImage img : images) {
+                    if (img.overlap(newImage))
+                        // mask overlapping zone in already grabbed image
+                        img.withdraw(newImage);
+                    else
+                        // mask overlapping zone in new image only when new
+                        // image covers completely the existing image
+                        newImage.withdraw(img);
+                }
+            }
+            images.add(newImage);
+            saveToCache(newImage);
+            Main.map.mapView.repaint();
+        }
+    }
+
+    /**
+     *
+     * @param b      the original bbox, usually the current bbox on screen
+     * @param factor 1 = source bbox 1:1
+     *               2 = source bbox divided by 2x2 smaller boxes
+     *               3 = source bbox divided by 3x3 smaller boxes
+     *               4 = hard coded size of boxes (100 meters) rounded allowing
+     *                   grabbing of next contiguous zone
+     */
+    private void divideBbox(Bounds b, int factor) {
+        EastNorth lambertMin = Main.proj.latlon2eastNorth(b.getMin());
+        EastNorth lambertMax = Main.proj.latlon2eastNorth(b.getMax());
+        double minEast = lambertMin.east();
+        double minNorth = lambertMin.north();
+        double dEast = (lambertMax.east() - minEast) / factor;
+        double dNorth = (lambertMax.north() - minNorth) / factor;
+        dividedBbox.clear();
+        if (factor < 4 || isRaster) {
+            for (int xEast = 0; xEast < factor; xEast++)
+                for (int xNorth = 0; xNorth < factor; xNorth++) {
+                    dividedBbox.add(new EastNorthBound(new EastNorth(minEast + xEast * dEast, minNorth + xNorth * dNorth),
+                                new EastNorth(minEast + (xEast + 1) * dEast, minNorth + (xNorth + 1) * dNorth)));
+            }
+        } else {
+            // divide to fixed size squares
+            int cSquare = Integer.parseInt(Main.pref.get("cadastrewms.squareSize", "100"));
+            minEast = minEast - minEast % cSquare;
+            minNorth = minNorth - minNorth % cSquare;
+            for (int xEast = (int)minEast; xEast < lambertMax.east(); xEast+=cSquare)
+                for (int xNorth = (int)minNorth; xNorth < lambertMax.north(); xNorth+=cSquare) {
+                    dividedBbox.add(new EastNorthBound(new EastNorth(xEast, xNorth),
+                                new EastNorth(xEast + cSquare, xNorth + cSquare)));
+            }
+        }
+    }
+
+    @Override
+    public Icon getIcon() {
+        return icon;
+    }
+
+    @Override
+    public String getToolTipText() {
+        String str = tr("WMS layer ({0}), {1} tile(s) loaded", getName(), images.size());
+        if (isRaster) {
+            str += "\n"+tr("Is not vectorized.");
+            str += "\n"+tr("Raster size: {0}", communeBBox);
+        } else
+            str += "\n"+tr("Is vectorized.");
+            str += "\n"+tr("Commune bbox: {0}", communeBBox);
+        return str;
+    }
+
+    @Override
+    public boolean isMergable(Layer other) {
+        return false;
+    }
+
+    @Override
+    public void mergeFrom(Layer from) {
+    }
+
+    @Override
+    public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
+        synchronized(this){
+            for (GeorefImage img : images)
+                img.paint(g, mv, CadastrePlugin.backgroundTransparent,
+                        CadastrePlugin.transparency, CadastrePlugin.drawBoundaries);
+        }
+        if (this.isRaster) {
+            paintCrosspieces(g, mv);
+        }
+    }
+
+    @Override
+    public void visitBoundingBox(BoundingXYVisitor v) {
+        for (GeorefImage img : images) {
+            v.visit(img.min);
+            v.visit(img.max);
+        }
+    }
+
+    @Override
+    public Object getInfoComponent() {
+        return getToolTipText();
+    }
+
+    @Override
+    public Component[] getMenuEntries() {
+        saveAsPng = new JMenuItem(new MenuActionSaveRasterAs(this));
+        saveAsPng.setEnabled(isRaster);
+        component = new Component[] { new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+                new JMenuItem(new MenuActionLoadFromCache()),
+                saveAsPng,
+                new JMenuItem(new LayerListPopup.InfoAction(this)),
+
+        };
+        return component;
+    }
+
+    public GeorefImage findImage(EastNorth eastNorth) {
+        // Iterate in reverse, so we return the image which is painted last.
+        // (i.e. the topmost one)
+        for (int i = images.size() - 1; i >= 0; i--) {
+            if (images.get(i).contains(eastNorth)) {
+                return images.get(i);
+            }
+        }
+        return null;
+    }
+
+    public boolean isOverlapping(Bounds bounds) {
+        GeorefImage georefImage =
+            new GeorefImage(new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB ), // not really important
+            Main.proj.latlon2eastNorth(bounds.getMin()),
+            Main.proj.latlon2eastNorth(bounds.getMax()));
+        for (GeorefImage img : images) {
+            if (img.overlap(georefImage))
+                return true;
+        }
+        return false;
+    }
+
+    public void saveToCache(GeorefImage image) {
+        if (CacheControl.cacheEnabled && !isRaster()) {
+            getCacheControl().saveCache(image);
+        }
+    }
+
+    public void saveNewCache() {
+        if (CacheControl.cacheEnabled) {
+            getCacheControl().deleteCacheFile();
+            for (GeorefImage image : images)
+                getCacheControl().saveCache(image);
+        }
+    }
+
+    public CacheControl getCacheControl() {
+        if (cacheControl == null)
+            cacheControl = new CacheControl(this);
+        return cacheControl;
+    }
+
+    /**
+     * Convert the eastNorth input coordinates to raster coordinates.
+     * The original raster size is [0,0,12286,8730] where 0,0 is the upper left corner and
+     * 12286,8730 is the approx. raster max size.
+     * @return the raster coordinates for the wms server request URL (minX,minY,maxX,maxY)
+     */
+    public String eastNorth2raster(EastNorth min, EastNorth max) {
+        double minX = (min.east() - rasterMin.east()) / rasterRatio;
+        double minY = (min.north() - rasterMin.north()) / rasterRatio;
+        double maxX = (max.east() - rasterMin.east()) / rasterRatio;
+        double maxY = (max.north() - rasterMin.north()) / rasterRatio;
+        return minX+","+minY+","+maxX+","+maxY;
+    }
+
+
+    public String getLocation() {
+        return location;
+    }
+
+    public void setLocation(String location) {
+        this.location = location;
+        setName(rebuildName());
+    }
+
+    public String getCodeCommune() {
+        return codeCommune;
+    }
+
+    public void setCodeCommune(String codeCommune) {
+        this.codeCommune = codeCommune;
+        setName(rebuildName());
+    }
+
+    public boolean isRaster() {
+        return isRaster;
+    }
+
+    public void setRaster(boolean isRaster) {
+        this.isRaster = isRaster;
+        if (saveAsPng != null)
+            saveAsPng.setEnabled(isRaster);
+    }
+
+    public boolean isAlreadyGeoreferenced() {
+        return isAlreadyGeoreferenced;
+    }
+
+    public void setAlreadyGeoreferenced(boolean isAlreadyGeoreferenced) {
+        this.isAlreadyGeoreferenced = isAlreadyGeoreferenced;
+    }
+
+    /**
+     * Set raster positions used for grabbing and georeferencing.
+     * rasterMin is the Eaast North of bottom left corner raster image on the screen when image is grabbed.
+     * The bounds width and height are the raster width and height. The image width matches the current view
+     * and the image height is adapted.
+     * Required: the communeBBox must be set (normally it is catched by CadastreInterface and saved by DownloadWMSPlanImage)
+     * @param bounds the current main map view boundaries
+     */
+    public void setRasterBounds(Bounds bounds) {
+        EastNorth rasterCenter = Main.proj.latlon2eastNorth(bounds.getCenter());
+        EastNorth eaMin = Main.proj.latlon2eastNorth(bounds.getMin());
+        EastNorth eaMax = Main.proj.latlon2eastNorth(bounds.getMax());
+        double rasterSizeX = communeBBox.max.getX() - communeBBox.min.getX();
+        double rasterSizeY = communeBBox.max.getY() - communeBBox.min.getY();
+        double ratio = rasterSizeY/rasterSizeX;
+        // keep same ratio on screen as WMS bbox (stored in communeBBox)
+        rasterMin = new EastNorth(eaMin.getX(), rasterCenter.getY()-(eaMax.getX()-eaMin.getX())*ratio/2);
+        rasterMax = new EastNorth(eaMax.getX(), rasterCenter.getY()+(eaMax.getX()-eaMin.getX())*ratio/2);
+        rasterRatio = (rasterMax.getX()-rasterMin.getX())/rasterSizeX;
+    }
+
+    /**
+     * Called by CacheControl when a new cache file is created on disk.
+     * Save only primitives to keep cache independent of software changes.
+     * @param oos
+     * @throws IOException
+     */
+    public void write(ObjectOutputStream oos) throws IOException {
+        oos.writeInt(this.serializeFormatVersion);
+        oos.writeObject(this.location);    // String
+        oos.writeObject(this.codeCommune); // String
+        oos.writeInt(this.lambertZone);
+        oos.writeBoolean(this.isRaster);
+        if (this.isRaster) {
+            oos.writeDouble(this.rasterMin.getX());
+            oos.writeDouble(this.rasterMin.getY());
+            oos.writeDouble(this.rasterMax.getX());
+            oos.writeDouble(this.rasterMax.getY());
+            oos.writeDouble(this.rasterRatio);
+        }
+        oos.writeDouble(this.communeBBox.min.getX());
+        oos.writeDouble(this.communeBBox.min.getY());
+        oos.writeDouble(this.communeBBox.max.getX());
+        oos.writeDouble(this.communeBBox.max.getY());
+    }
+
+    /**
+     * Called by CacheControl when a cache file is read from disk.
+     * Cache uses only primitives to stay independent of software changes.
+     * @param ois
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public boolean read(ObjectInputStream ois, int currentLambertZone) throws IOException, ClassNotFoundException {
+        int sfv = ois.readInt();
+        if (sfv != this.serializeFormatVersion) {
+            JOptionPane.showMessageDialog(Main.parent, tr("Unsupported cache file version; found {0}, expected {1}\nCreate a new one.",
+                    sfv, this.serializeFormatVersion), tr("Cache Format Error"), JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+        this.setLocation((String) ois.readObject());
+        this.setCodeCommune((String) ois.readObject());
+        this.lambertZone = ois.readInt();
+        this.setRaster(ois.readBoolean());
+        if (this.isRaster) {
+            double X = ois.readDouble();
+            double Y = ois.readDouble();
+            this.rasterMin = new EastNorth(X, Y);
+            X = ois.readDouble();
+            Y = ois.readDouble();
+            this.rasterMax = new EastNorth(X, Y);
+            this.rasterRatio = ois.readDouble();
+        }
+        double minX = ois.readDouble();
+        double minY = ois.readDouble();
+        double maxX = ois.readDouble();
+        double maxY = ois.readDouble();
+        this.communeBBox =  new EastNorthBound(new EastNorth(minX, minY), new EastNorth(maxX, maxY));
+        if (this.lambertZone != currentLambertZone && currentLambertZone != -1) {
+            JOptionPane.showMessageDialog(Main.parent, tr("Lambert zone {0} in cache "+
+                    "incompatible with current Lambert zone {1}",
+                    this.lambertZone+1, currentLambertZone), tr("Cache Lambert Zone Error"), JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+        synchronized(this){
+            boolean EOF = false;
+            try {
+                while (!EOF) {
+                    GeorefImage newImage = (GeorefImage) ois.readObject();
+                    for (GeorefImage img : this.images) {
+                        if (CadastrePlugin.backgroundTransparent) {
+                            if (img.overlap(newImage))
+                                // mask overlapping zone in already grabbed image
+                                img.withdraw(newImage);
+                            else
+                                // mask overlapping zone in new image only when
+                                // new image covers completely the existing image
+                                newImage.withdraw(img);
+                        }
+                    }
+                    this.images.add(newImage);
+                }
+            } catch (EOFException ex) {
+                // expected exception when all images are read
+            }
+        }
+        System.out.println("Cache loaded for location "+location+" with "+images.size()+" images");
+        return true;
+    }
+
+    /**
+     * Join the grabbed images into one single.
+     * Works only for images grabbed from non-georeferenced images (Feuilles cadastrales)(same amount of
+     * images in x and y)
+     */
+    public void joinRasterImages() {
+        if (images.size() > 1) {
+            EastNorth min = images.get(0).min;
+            EastNorth max = images.get(images.size()-1).max;
+            int oldImgWidth = images.get(0).image.getWidth();
+            int oldImgHeight = images.get(0).image.getHeight();
+            int newWidth = oldImgWidth*(int)Math.sqrt(images.size());
+            int newHeight = oldImgHeight*(int)Math.sqrt(images.size());
+            BufferedImage new_img = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
+            Graphics g = new_img.getGraphics();
+            // Coordinate (0,0) is on top,left corner where images are grabbed from bottom left
+            int rasterDivider = (int)Math.sqrt(images.size());
+            for (int h = 0; h < rasterDivider; h++) {
+                for (int v = 0; v < rasterDivider; v++) {
+                    int newx = h*oldImgWidth;
+                    int newy = newHeight - oldImgHeight - (v*oldImgHeight);
+                    int j = h*rasterDivider + v;
+                    g.drawImage(images.get(j).image, newx, newy, this);
+                }
+            }
+            synchronized(this) {
+                images.clear();
+                images.add(new GeorefImage(new_img, min, max));
+            }
+        }
+    }
+
+    /**
+     * Image cropping based on two EN coordinates pointing to two corners in diagonal
+     * Because it's coming from user mouse clics, we have to sort de positions first.
+     * Works only for raster image layer (only one image in collection).
+     * Updates layer georeferences.
+     * @param en1
+     * @param en2
+     */
+    public void cropImage(EastNorth en1, EastNorth en2){
+        // adj1 is corner bottom, left
+        EastNorth adj1 = new EastNorth(en1.east() <= en2.east() ? en1.east() : en2.east(),
+                en1.north() <= en2.north() ? en1.north() : en2.north());
+        // adj2 is corner top, right
+        EastNorth adj2 = new EastNorth(en1.east() > en2.east() ? en1.east() : en2.east(),
+                en1.north() > en2.north() ? en1.north() : en2.north());
+        // s1 and s2 have 0,0 at top, left where all EastNorth coord. have 0,0 at bottom, left
+        int sx1 = (int)((adj1.getX() - images.get(0).min.getX())*images.get(0).getPixelPerEast());
+        int sy1 = (int)((images.get(0).max.getY() - adj2.getY())*images.get(0).getPixelPerNorth());
+        int sx2 = (int)((adj2.getX() - images.get(0).min.getX())*images.get(0).getPixelPerEast());
+        int sy2 = (int)((images.get(0).max.getY() - adj1.getY())*images.get(0).getPixelPerNorth());
+        int newWidth = Math.abs(sx2 - sx1);
+        int newHeight = Math.abs(sy2 - sy1);
+        BufferedImage new_img = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
+        Graphics g = new_img.getGraphics();
+        g.drawImage(images.get(0).image, 0, 0, newWidth-1, newHeight-1,
+                sx1, sy1, sx2, sy2,
+                this);
+        images.set(0, new GeorefImage(new_img, adj1, adj2));
+        // important: update the layer georefs !
+        rasterMin = adj1;
+        rasterMax = adj2;
+        rasterRatio = (rasterMax.getX()-rasterMin.getX())/(communeBBox.max.getX() - communeBBox.min.getX());
+        setCommuneBBox(new EastNorthBound(new EastNorth(0,0), new EastNorth(newWidth-1,newHeight-1)));
+    }
+
+    public EastNorthBound getCommuneBBox() {
+        return communeBBox;
+    }
+
+    public void setCommuneBBox(EastNorthBound entireCommune) {
+        this.communeBBox = entireCommune;
+//        if (Main.proj instanceof LambertCC9Zones)
+//            setLambertCC9Zone(communeBBox.min.north());
+    }
+
+    /**
+     * Method required by ImageObserver when drawing an image
+     */
+    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
+        return false;
+    }
+
+    public int getLambertZone() {
+        return lambertZone;
+    }
+
+//    public void setLambertCC9Zone(double north) {
+//        int lambertZone = LambertCC9Zones.north2ZoneNumber(north);
+//        this.lambertZone = lambertZone;
+//        if (((LambertCC9Zones)Main.proj).getLayoutZone() != lambertZone) {
+//            String currentZone = MenuActionLambertZone.lambert9zones[((LambertCC9Zones)Main.proj).getLayoutZone()+1];
+//            String destZone = MenuActionLambertZone.lambert9zones[lambertZone+1];
+//            if (Main.map.mapView.getAllLayers().size() == 1) {
+//                /* Enable this code below when JOSM will have a proper support of dynamic projection change
+//                 *
+//                System.out.println("close all layers and change current Lambert zone from "+LambertCC9Zones.layoutZone+" to "+lambertZone);
+//                Bounds b = null;
+//                if (Main.map != null && Main.map.mapView != null)
+//                    b = Main.map.mapView.getRealBounds();
+//                LambertCC9Zones.layoutZone = lambertZone;
+//                Main.map.mapView.zoomTo(b);
+//                */
+//            } else {
+//                JOptionPane.showMessageDialog(Main.parent, tr("Current layer is in Lambert CC9 Zone \"{0}\"\n"+
+//                        "where the commune is in Lambert CC9 Zone \"{1}\".\n"+
+//                        "Upload your changes, close all layers and change\n"+
+//                        "manually the Lambert zone from the Cadastre menu"
+//                        , currentZone, destZone));
+//            }
+//        }
+//    }
+
+    public EastNorth getRasterCenter() {
+        return new EastNorth((images.get(0).max.east()+images.get(0).min.east())/2,
+                (images.get(0).max.north()+images.get(0).min.north())/2);
+    }
+
+    public void displace(double dx, double dy) {
+        this.rasterMin = new EastNorth(rasterMin.east() + dx, rasterMin.north() + dy);
+        images.get(0).shear(dx, dy);
+    }
+
+    public void resize(EastNorth rasterCenter, double proportion) {
+        this.rasterMin = rasterMin.interpolate(rasterCenter, proportion);
+        images.get(0).scale(rasterCenter, proportion);
+    }
+
+    public void rotate(EastNorth rasterCenter, double angle) {
+        this.rasterMin = rasterMin.rotate(rasterCenter, angle);
+        images.get(0).rotate(rasterCenter, angle);
+    }
+
+    private void paintCrosspieces(Graphics g, MapView mv) {
+        String crosspieces = Main.pref.get("cadastrewms.crosspieces", "0");
+        if (!crosspieces.equals("0")) {
+            int modulo = 50;
+            if (crosspieces.equals("2")) modulo = 100;
+            EastNorthBound currentView = new EastNorthBound(mv.getEastNorth(0, mv.getHeight()),
+                    mv.getEastNorth(mv.getWidth(), 0));
+            int minX = ((int)currentView.min.east()/modulo+1)*modulo;
+            int minY = ((int)currentView.min.north()/modulo+1)*modulo;
+            int maxX = ((int)currentView.max.east()/modulo)*modulo;
+            int maxY = ((int)currentView.max.north()/modulo)*modulo;
+            int size=(maxX-minX)/modulo;
+            if (size<20) {
+                int px= size > 10 ? 2 : Math.abs(12-size);
+                g.setColor(Color.green);
+                for (int x=minX; x<=maxX; x+=modulo) {
+                    for (int y=minY; y<=maxY; y+=modulo) {
+                        Point p = mv.getPoint(new EastNorth(x,y));
+                        g.drawLine(p.x-px, p.y, p.x+px, p.y);
+                        g.drawLine(p.x, p.y-px, p.x, p.y+px);
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/measurement/.classpath b/colorscheme/.classpath
similarity index 94%
copy from measurement/.classpath
copy to colorscheme/.classpath
index 05929d7..17b8e6a 100644
--- a/measurement/.classpath
+++ b/colorscheme/.classpath
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
 	<classpathentry kind="output" path="build"/>
 </classpath>
diff --git a/colorscheme/.project b/colorscheme/.project
new file mode 100644
index 0000000..f9fda49
--- /dev/null
+++ b/colorscheme/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JOSM-colorscheme</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>
diff --git a/livegps/.classpath b/editgpx/.classpath
similarity index 85%
copy from livegps/.classpath
copy to editgpx/.classpath
index 81a65e8..7d87ead 100644
--- a/livegps/.classpath
+++ b/editgpx/.classpath
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0.15"/>
+	<classpathentry kind="src" path="src-common"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/editgpx/.project b/editgpx/.project
new file mode 100644
index 0000000..569f47e
--- /dev/null
+++ b/editgpx/.project
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>editgpx</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>
+	<linkedResources>
+		<link>
+			<name>src-common</name>
+			<type>2</type>
+			<location>/Users/leo/dev/josm/core/src</location>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/slippymap/build.xml b/editgpx/build.xml
similarity index 83%
copy from slippymap/build.xml
copy to editgpx/build.xml
index a0eed67..b991a6e 100644
--- a/slippymap/build.xml
+++ b/editgpx/build.xml
@@ -1,4 +1,4 @@
-<project name="slippymap" default="dist" basedir=".">
+<project name="editgpx" default="dist" basedir=".">
     <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
     <property name="plugin.dist.dir"        value="../../dist"/>
     <property name="plugin.build.dir"       value="build"/>
@@ -20,12 +20,12 @@
         </copy>
         <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
             <manifest>
-                <attribute name="Author" value="Frederik Ramm"/>
-                <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.slippymap.SlippyMapPlugin"/>
+                <attribute name="Author" value="Martin Garbe" />
+                <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.editgpx.EditGpxPlugin" />
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
-                <attribute name="Plugin-Description" value="Displays a slippy map grid in JOSM. Can load tiles from slippy map as background and request updates."/>
-                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/SlippyMap"/>
-                <attribute name="Plugin-Mainversion" value="2196"/>
+                <attribute name="Plugin-Description" value="Allows the user to anonymize timestamps and delete parts of huge GPX tracks very fast." />
+                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/EditGpx" />
+                <attribute name="Plugin-Mainversion" value="2450" />
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/editgpx/images/editgpx_layer.png b/editgpx/images/editgpx_layer.png
new file mode 100644
index 0000000..5744c27
Binary files /dev/null and b/editgpx/images/editgpx_layer.png differ
diff --git a/editgpx/images/mapmode/editgpx_mode.png b/editgpx/images/mapmode/editgpx_mode.png
new file mode 100644
index 0000000..b8b06ad
Binary files /dev/null and b/editgpx/images/mapmode/editgpx_mode.png differ
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
new file mode 100644
index 0000000..de997af
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
@@ -0,0 +1,223 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+
+import javax.swing.AbstractAction;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JMenuItem;
+import javax.swing.JSeparator;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+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.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+
+public class EditGpxLayer extends Layer {
+
+    private static Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(EditGpxPlugin.class.getResource("/images/editgpx_layer.png")));
+    private DataSet dataSet;
+    private GPXLayerImportAction layerImport;
+
+    public EditGpxLayer(String str, DataSet ds) {
+        super(str);
+        dataSet = ds;
+        layerImport = new GPXLayerImportAction(dataSet);
+    }
+
+    /**
+     * check if dataSet is empty
+     * if so show import dialog to user
+     */
+    public void initializeImport() {
+        try {
+            if(dataSet.getNodes().isEmpty() ) {
+                layerImport.activateImport();
+            }
+        } catch (Exception e) {
+            System.out.println(e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public Icon getIcon() {
+        return icon;
+    }
+
+    @Override
+    public Object getInfoComponent() {
+        return getToolTipText();
+    }
+
+    @Override
+    public Component[] getMenuEntries() {
+        return new Component[] {
+            new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+            new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+            new JSeparator(),
+            new JMenuItem(layerImport),
+            new JMenuItem(new ConvertToGpxLayerAction()),
+            new JMenuItem(new ConvertToAnonTimeGpxLayerAction()),
+            new JSeparator(),
+            new JMenuItem(new LayerListPopup.InfoAction(this))};
+    }
+
+    @Override
+    public String getToolTipText() {
+        return tr("Layer for editing GPX tracks");
+    }
+
+    @Override
+    public boolean isMergable(Layer other) {
+        // TODO
+        return false;
+    }
+
+    @Override
+    public void mergeFrom(Layer from) {
+        // TODO
+    }
+
+    @Override
+    public void paint(Graphics2D g, MapView mv, Bounds bounds) {
+        g.setColor(Color.yellow);
+
+        //don't iterate through dataSet whiling making changes
+        synchronized(layerImport.importing) {
+            for(Node n: dataSet.getNodes()) {
+                if (!n.isDeleted()) {
+                    Point pnt = Main.map.mapView.getPoint(n.getEastNorth());
+                    g.drawOval(pnt.x - 2, pnt.y - 2, 4, 4);
+                }
+            }
+        }
+    }
+
+
+    public void reset(){
+        //TODO implement a reset
+    }
+
+
+    @Override
+    public void visitBoundingBox(BoundingXYVisitor v) {
+        // TODO Auto-generated method stub
+    }
+
+
+    /**
+     * convert a DataSet to GPX
+     *
+     * @param boolean anonTime If true set all time and date in GPX to 01/01/1970 00:00 ?
+     * @return GPXData
+     */
+    private GpxData toGpxData(boolean anonTime) {
+        GpxData gpxData = new GpxData();
+        HashSet<Node> doneNodes = new HashSet<Node>();
+        //add all ways
+        for (Way w : dataSet.getWays()) {
+            if (w.incomplete || w.isDeleted()) continue;
+            GpxTrack trk = new GpxTrack();
+            gpxData.tracks.add(trk);
+
+            if (w.get("name") != null)
+                trk.attr.put("name", w.get("name"));
+
+            ArrayList<WayPoint> trkseg = null;
+            for (Node n : w.getNodes()) {
+                if (n.incomplete || n.isDeleted()) {
+                    trkseg = null;
+                    continue;
+                }
+
+                Date tstamp = n.getTimestamp();
+
+                if (trkseg == null) {
+                    trkseg = new ArrayList<WayPoint>();
+                    trk.trackSegs.add(trkseg);
+                }
+                doneNodes.add(n);
+
+                WayPoint wpt = new WayPoint(n.getCoor());
+                if (anonTime) {
+                    wpt.attr.put("time", "1970-01-01T00:00:00Z");
+                } else {
+                    wpt.attr.put("time", DateUtils.fromDate(tstamp));
+                }
+                wpt.setTime();
+
+                trkseg.add(wpt);
+            }
+        }
+
+        // add nodes as waypoints
+        for (Node n : dataSet.getNodes()) {
+            if (n.incomplete || n.isDeleted() || doneNodes.contains(n)) continue;
+
+            Date tstamp = n.getTimestamp();
+
+            WayPoint wpt = new WayPoint(n.getCoor());
+            if (anonTime) {
+                wpt.attr.put("time", "1970-01-01T00:00:00Z");
+            } else {
+                wpt.attr.put("time", DateUtils.fromDate(tstamp));
+            }
+            wpt.setTime();
+
+            if (n.getKeys() != null && n.keySet().contains("name")) {
+                wpt.attr.put("name", n.get("name"));
+            }
+            gpxData.waypoints.add(wpt);
+        }
+        return gpxData;
+    }
+
+    //context item "Convert to GPX layer"
+    public class ConvertToGpxLayerAction extends AbstractAction {
+        public ConvertToGpxLayerAction() {
+            super(tr("Convert to GPX layer"), ImageProvider.get("converttogpx"));
+        }
+        public void actionPerformed(ActionEvent e) {
+            Main.main.addLayer(new GpxLayer(toGpxData(false), tr("Converted from: {0}", getName())));
+            Main.main.removeLayer(EditGpxLayer.this);
+        }
+    }
+
+    //context item "Convert to GPX layer with anonymised time"
+    public class ConvertToAnonTimeGpxLayerAction extends AbstractAction {
+        public ConvertToAnonTimeGpxLayerAction() {
+            super(tr("Convert to GPX layer with anonymised time"), ImageProvider.get("converttogpx"));
+        }
+        public void actionPerformed(ActionEvent e) {
+            Main.main.addLayer(new GpxLayer(toGpxData(true), tr("Converted from: {0}", getName())));
+            Main.main.removeLayer(EditGpxLayer.this);
+        }
+    }
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java
new file mode 100644
index 0000000..d62935e
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java
@@ -0,0 +1,134 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+
+
+public class EditGpxMode extends MapMode {
+
+    private static final long serialVersionUID = 7940589057093872411L;
+    Point pointPressed;
+    DataSet dataSet;
+    MapFrame mapFrame;
+    Rectangle oldRect;
+    MapFrame frame;
+
+    public EditGpxMode(MapFrame mapFrame, String name, String desc, DataSet ds) {
+        super(name, "editgpx_mode.png", desc, mapFrame, Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+        dataSet = ds;
+    }
+
+    @Override public void enterMode() {
+        super.enterMode();
+        Main.map.mapView.addMouseListener(this);
+        Main.map.mapView.addMouseMotionListener(this);
+    }
+
+    @Override public void exitMode() {
+        super.exitMode();
+        Main.map.mapView.removeMouseListener(this);
+        Main.map.mapView.removeMouseMotionListener(this);
+    }
+
+
+    @Override public void mousePressed(MouseEvent e) {
+        pointPressed = new Point(e.getPoint());
+    }
+
+
+    @Override public void mouseDragged(MouseEvent e) {
+        if ( (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) ==  MouseEvent.BUTTON1_DOWN_MASK) {
+            //if button1 is hold, draw the rectangle.
+            paintRect(pointPressed, e.getPoint());
+        }
+    }
+
+    @Override public void mouseReleased(MouseEvent e) {
+        if (e.getButton() != MouseEvent.BUTTON1) {
+            return;
+        } else {
+            Point pointReleased = e.getPoint();
+
+            Rectangle r = createRect(pointReleased, pointPressed);
+
+            //go through nodes and mark the ones in the selection rect as deleted
+            for (Node n: dataSet.getNodes()) {
+                Point p = Main.map.mapView.getPoint(n);
+                if (r.contains(p)) {
+                	n.setDeleted(true);
+                }
+            }
+            oldRect = null;
+            Main.map.mapView.repaint();
+        }
+    }
+
+    /**
+     * create rectangle out of two given corners
+     */
+    public Rectangle createRect(Point p1, Point p2) {
+        int x,y,w,h;
+        if (p1.x == p2.x && p1.y == p2.y) {
+            //if p1 and p2 same points draw a small rectangle around them
+            x = p1.x -1;
+            y = p1.y -1;
+            w = 3;
+            h = 3;
+        } else {
+            if (p1.x < p2.x){
+                x = p1.x;
+                w = p2.x-p1.x;
+            } else {
+                x = p2.x;
+                w = p1.x-p2.x;
+            }
+            if (p1.y < p2.y) {
+                y = p1.y;
+                h = p2.y-p1.y;
+            } else {
+                y = p2.y;
+                h = p1.y-p2.y;
+            }
+        }
+        return new Rectangle(x,y,w,h);
+    }
+
+    /**
+     * Draw a selection rectangle on screen.
+     */
+    private void paintRect(Point p1, Point p2) {
+        Graphics g = frame.getGraphics();//Main.map.mapView.getGraphics();
+
+        Rectangle r = oldRect;
+        if (r != null) {
+            //overwrite old rct
+            g.setXORMode(Color.BLACK);
+            g.setColor(Color.WHITE);
+            g.drawRect(r.x,r.y,r.width,r.height);
+        }
+
+        g.setXORMode(Color.BLACK);
+        g.setColor(Color.WHITE);
+        r = createRect(p1,p2);
+        g.drawRect(r.x,r.y,r.width,r.height);
+        oldRect = r;
+    }
+
+
+    public void setFrame(MapFrame mapFrame) {
+        frame = mapFrame;
+    }
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java
new file mode 100644
index 0000000..400eb55
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java
@@ -0,0 +1,115 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.IconToggleButton;
+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.plugins.Plugin;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * Provides an editable GPX layer. Editable layer here means the deletion of points is supported.
+ * This plugin can be used to prepare tracks for upload to OSM eg. delete uninteresting parts
+ * of the track.
+ * Additionally while converting the track back to a normal GPX layer the time can be made
+ * anonymous. This feature sets all time stamps to 1970-01-01 00:00.
+ *
+ * TODO:
+ * - BUG: when importing eGpxLayer is shown as RawGpxLayer??
+ * - BUG: after deletion of layer not all data is deleted (eg dataset)
+ * - implement reset if user made mistake while marking
+ *
+ *
+ */
+public class EditGpxPlugin extends Plugin {
+
+    private IconToggleButton btn;
+    private EditGpxMode mode;
+    protected static EditGpxLayer eGpxLayer;
+    protected static DataSet dataSet;
+    public static boolean active = false;
+
+    public EditGpxPlugin() {
+        dataSet = new DataSet();
+        mode = new EditGpxMode(Main.map, "editgpx", tr("edit gpx tracks"), dataSet);
+
+        btn = new IconToggleButton(mode);
+        btn.setVisible(true);
+    }
+
+    /**
+     * initialize button. if button is pressed create new layer.
+     */
+    @Override
+    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+        if(oldFrame == null && newFrame != null) {
+            mode.setFrame(newFrame);
+
+            if(Main.map != null)
+                Main.map.addMapMode(btn);
+
+            active = btn.isSelected();
+
+            btn.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    active = btn.isSelected();
+                    if (active) {
+                        Main.worker.execute(new Runnable() {
+                            public void run() {
+                                updateLayer();
+                            }
+                        });
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * create new layer, add listeners and try importing gpx data.
+     */
+    private void updateLayer() {
+        if(eGpxLayer == null) {
+            eGpxLayer = new EditGpxLayer(tr("EditGpx"), dataSet);
+            Main.main.addLayer(eGpxLayer);
+            Layer.listeners.add(new LayerChangeListener(){
+
+                public void activeLayerChange(final Layer oldLayer, final Layer newLayer) {
+                    if(newLayer instanceof EditGpxLayer)
+                        EditGpxPlugin.eGpxLayer = (EditGpxLayer)newLayer;
+                }
+
+                public void layerAdded(final Layer newLayer) {
+                }
+
+                public void layerRemoved(final Layer oldLayer) {
+                    if(oldLayer == eGpxLayer) {
+                        eGpxLayer = null;
+                        //dataSet = new DataSet();
+
+                    }
+                }
+            });
+
+            eGpxLayer.initializeImport();
+        }
+        Main.map.mapView.repaint();
+    }
+
+    public static ImageIcon loadIcon(String name) {
+        URL url = EditGpxPlugin.class.getResource("/images/editgpx.png");
+        return new ImageIcon(url);
+    }
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java
new file mode 100644
index 0000000..be1d608
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java
@@ -0,0 +1,143 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ *
+ * other source from mesurement plugin written by Raphael Mack
+ *
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.Collection;
+
+import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Import GPX data from available layers
+ *
+ *
+ */
+class GPXLayerImportAction extends AbstractAction {
+
+
+    private static final long serialVersionUID = 5794897888911798168L;
+    private DataSet dataSet;
+    public Object importing = new Object(); //used for synchronization
+
+    public GPXLayerImportAction(DataSet ds) {
+        //TODO what is icon at the end?
+        super(tr("Import path from GPX layer"), ImageProvider.get("dialogs", "edit"));
+        this.dataSet = ds;
+    }
+
+    /**
+     * shows a list of GPX layers. if user selects one the data from this layer is
+     * imported.
+     */
+    public void activateImport() {
+        Box panel = Box.createVerticalBox();
+        DefaultListModel dModel= new DefaultListModel();
+
+        final JList layerList = new JList(dModel);
+        Collection<Layer> data = Main.map.mapView.getAllLayers();
+        Layer lastLayer = null;
+        int layerCnt = 0;
+
+        for (Layer l : data){
+                if(l instanceof GpxLayer){
+                    dModel.addElement(l);
+                    lastLayer = l;
+                    layerCnt++;
+                }
+        }
+        if(layerCnt == 1){
+                layerList.setSelectedValue(lastLayer, true);
+        }
+        if(layerCnt > 0){
+            layerList.setCellRenderer(new DefaultListCellRenderer(){
+                    @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+                        Layer layer = (Layer)value;
+                        JLabel label = (JLabel)super.getListCellRendererComponent(list,
+                                                                                  layer.getName(), index, isSelected, cellHasFocus);
+                        Icon icon = layer.getIcon();
+                        label.setIcon(icon);
+                        label.setToolTipText(layer.getToolTipText());
+                        return label;
+                    }
+                });
+
+            JCheckBox dropFirst = new JCheckBox(tr("Drop existing path"));
+
+            panel.add(layerList);
+            panel.add(dropFirst);
+
+            final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
+                    @Override public void selectInitialValue() {
+                        layerList.requestFocusInWindow();
+                    }
+                };
+            final JDialog dlg = optionPane.createDialog(Main.parent, tr("Import path from GPX layer"));
+            dlg.setVisible(true);
+
+            Object answer = optionPane.getValue();
+            if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
+                (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
+                return;
+            }
+
+            GpxLayer gpx = (GpxLayer)layerList.getSelectedValue();
+
+            synchronized(importing) {
+                for (GpxTrack trk : gpx.data.tracks) {
+                    for (Collection<WayPoint> segment : trk.trackSegs) {
+                        Way w = new Way();
+                        for (WayPoint p : segment) {
+                            Node n = new Node(p.getCoor());
+                            String timestr = p.getString("time");
+                            if(timestr != null)
+                                n.setTimestamp(DateUtils.fromString(timestr));
+                            dataSet.addPrimitive(n);
+                            w.addNode(n); //TODO what to do with these while deletion
+                        }
+                        dataSet.addPrimitive(w);
+                    }
+                }
+            }
+            Main.map.mapView.repaint();
+
+        } else {
+            // no gps layer
+            JOptionPane.showMessageDialog(Main.parent,tr("No GPX data layer found."));
+        }
+    }
+
+    /**
+     * called when pressing "Import.." from context menu of EditGpx layer
+     *
+     */
+    public void actionPerformed(ActionEvent arg0) {
+        activateImport();
+    }
+}
diff --git a/agpifoj/.classpath b/lakewalker/.classpath
similarity index 82%
copy from agpifoj/.classpath
copy to lakewalker/.classpath
index b5edc13..227cb45 100644
--- a/agpifoj/.classpath
+++ b/lakewalker/.classpath
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+	<classpathentry kind="output" path="build"/>
+</classpath>
diff --git a/lakewalker/.project b/lakewalker/.project
new file mode 100644
index 0000000..9dd9576
--- /dev/null
+++ b/lakewalker/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>josm-lakewalker</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>
diff --git a/lakewalker/README b/lakewalker/README
new file mode 100644
index 0000000..f09b1c6
--- /dev/null
+++ b/lakewalker/README
@@ -0,0 +1,23 @@
+README FOR Lakewalker
+=======================
+
+The Lakewalker plugin can be used to generate traces of lakes, rivers or shoreline from
+LANDSAT imagery (or theoretically any imagery that is provided on a WMS server).
+
+Currently the plugin features 2 modes of operation: Native and Classic. The classic mode 
+requires the Python lakewalker.py script to be downloaded into the lakewalker directory
+within the JOSM plugin directory.
+
+Common Features:
+ - On-screen feedback to Lakewalker progress with cancel
+ - Tag ways appropriately
+ - limit ways to length
+ - Manage Landsat image cache from Prefs screen
+
+Native Features:
+ - Doesn't require Python
+
+Classic Features:
+ - Access all lakewalker options via preferences
+ - Read data in thread
+ - ...
\ No newline at end of file
diff --git a/measurement/build.xml b/lakewalker/build.xml
similarity index 83%
copy from measurement/build.xml
copy to lakewalker/build.xml
index 8833eb3..e2662af 100644
--- a/measurement/build.xml
+++ b/lakewalker/build.xml
@@ -1,4 +1,4 @@
-<project name="measurement" default="dist" basedir=".">
+<project name="lakewalker" default="dist" basedir=".">
     <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
     <property name="plugin.dist.dir"        value="../../dist"/>
     <property name="plugin.build.dir"       value="build"/>
@@ -20,10 +20,11 @@
         </copy>
         <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
             <manifest>
-                <attribute name="Author" value="Raphael Mack, Reza Mohammadi"/>
-                <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.measurement.MeasurementPlugin"/>
+                <attribute name="Author" value="Brent Easton, Jason Reid"/>
+                <attribute name="Main-Class" value="org.openstreetmap.josm.plugins.lakewalker.LakewalkerApp"/>
+                <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.lakewalker.LakewalkerPlugin" />
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
-                <attribute name="Plugin-Description" value="Provide a measurement dialog and a layer to measure length and angle of segments, area surrounded by a (simple) closed way and create measurement paths (which also can be imported from a gps layer)."/>
+                <attribute name="Plugin-Description" value="Helps vectorizing WMS images." />
                 <attribute name="Plugin-Mainversion" value="2012"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
diff --git a/lakewalker/images/cursor/modifier/lakewalker-sml.png b/lakewalker/images/cursor/modifier/lakewalker-sml.png
new file mode 100644
index 0000000..2479605
Binary files /dev/null and b/lakewalker/images/cursor/modifier/lakewalker-sml.png differ
diff --git a/lakewalker/images/lakewalker-sml.png b/lakewalker/images/lakewalker-sml.png
new file mode 100644
index 0000000..2479605
Binary files /dev/null and b/lakewalker/images/lakewalker-sml.png differ
diff --git a/lakewalker/images/preferences/lakewalker.png b/lakewalker/images/preferences/lakewalker.png
new file mode 100644
index 0000000..d951ac5
Binary files /dev/null and b/lakewalker/images/preferences/lakewalker.png differ
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/BooleanConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/BooleanConfigurer.java
new file mode 100644
index 0000000..05d90b1
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/BooleanConfigurer.java
@@ -0,0 +1,86 @@
+/*
+ * $Id: BooleanConfigurer.java 705 2005-09-15 13:24:50 +0000 (Thu, 15 Sep 2005) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available 
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+/**
+ * Configurer for Boolean values
+ */
+public class BooleanConfigurer extends Configurer {
+  private javax.swing.JCheckBox box;
+
+  public BooleanConfigurer() {
+    this(false);
+  }
+  
+  public BooleanConfigurer(boolean val) {
+    this(null, "", val);
+  }
+  
+  public BooleanConfigurer(String key, String name, Boolean val) {
+    super(key, name, val);
+  }
+
+  public BooleanConfigurer(String key, String name, boolean val) {
+    super(key, name, val ? Boolean.TRUE : Boolean.FALSE);
+  }
+
+  public BooleanConfigurer(String key, String name) {
+    this(key, name, Boolean.FALSE);
+  }
+
+  public String getValueString() {
+    return booleanValue().toString();
+  }
+
+  public void setValue(Object o) {
+    super.setValue(o);
+    if (box != null
+      && !o.equals(new Boolean(box.isSelected()))) {
+      box.setSelected(booleanValue().booleanValue());
+    }
+  }
+
+  public void setValue(String s) {
+    setValue(Boolean.valueOf(s));
+  }
+
+  public void setName(String s) {
+    super.setName(s);
+    if (box != null) {
+      box.setText(s);
+    }
+  }
+
+  public java.awt.Component getControls() {
+    if (box == null) {
+      box = new javax.swing.JCheckBox(getName());
+      box.setSelected(booleanValue().booleanValue());
+      box.addItemListener(new java.awt.event.ItemListener() {
+        public void itemStateChanged(java.awt.event.ItemEvent e) {
+          setValue(new Boolean(box.isSelected()));
+        }
+      });
+    }
+    return box;
+  }
+
+  public Boolean booleanValue() {
+    return (Boolean) value;
+  }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Configurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Configurer.java
new file mode 100644
index 0000000..8cdd72e
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Configurer.java
@@ -0,0 +1,145 @@
+/*
+ * $Id: Configurer.java 2175 2007-06-04 04:19:59 +0000 (Mon, 04 Jun 2007) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available 
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import java.beans.PropertyChangeListener;
+
+/**
+ * A property editor class.  Wraps an Object value and provides
+ * methods for saving and restoring the Object from a String.  Also
+ * includes a {@link java.awt.Component} that can be placed into a
+ * property editing window, allowing the user to change the value
+ * interactively.
+ * */
+public abstract class Configurer {
+// FIXME: maybe parameterize this so that value can have the right type
+// in subclasses?
+  public static final String NAME_PROPERTY = "Configurer.name";
+  //    public static final String VALUE_PROPERTY = "value";
+
+  /** A String the uniquely identifies this property */
+  protected String key;
+  /** A String that provides a short description of the property to the user */
+  protected String name;
+  /** The value */
+  protected Object value;
+  protected java.beans.PropertyChangeSupport changeSupport;
+  /** When noUpdate is true, setting the value programmatically will not
+   * result in an update of the GUI Component */
+  protected boolean noUpdate = false;
+  /** When frozen is true, setting the value programmatically will not
+   * result in a PropertyChangeEvent being fired */
+  protected boolean frozen = false;
+
+  public Configurer(String key, String name) {
+    this(key, name, null);
+  }
+
+  public Configurer(String key, String name, Object val) {
+    this.key = key;
+    this.name = name;
+    changeSupport = new java.beans.PropertyChangeSupport(this);
+    setValue(val);
+  }
+
+  /**
+   * Unique identifier
+   */
+  public String getKey() {
+    return key;
+  }
+
+  /**
+   * Plain English description of the Object
+   */
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String s) {
+    String oldName = name;
+    name = s;
+    if (!frozen) {
+      changeSupport.firePropertyChange(NAME_PROPERTY, oldName, name);
+    }
+  }
+
+  /**
+   * The Object value
+   * May be null if the Object has not been initialized
+   */
+  public Object getValue() {
+    return value;
+  }
+
+  /**
+   * @return a String representation of the Object value
+   */
+  public abstract String getValueString();
+
+  /**
+   * Set the Object value
+   */
+  public void setValue(Object o) {
+    Object oldValue = getValue();
+    value = o;
+    if (!frozen) {
+      changeSupport.firePropertyChange(key, oldValue, value);
+    }
+  }
+
+  /**
+   * If true, then don't fire PropertyChangeEvents when the value is reset
+   */
+  public void setFrozen(boolean val) {
+    frozen = val;
+  }
+
+  public boolean isFrozen() {
+    return frozen;
+  }
+
+  /**
+   * Fire a PropertyChangeEvent as if the value had been set from null
+   */
+  public void fireUpdate() {
+    changeSupport.firePropertyChange(key, null, value);
+  }
+
+  /**
+   * Set the Object value from a String
+   */
+  public abstract void setValue(String s);
+
+  /**
+   * GUI interface for setting the option in an editing window
+   */
+  public abstract java.awt.Component getControls();
+
+  /**
+   * Add a listener to be notified when the Object state changes
+   */
+  public void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
+    changeSupport.addPropertyChangeListener(l);
+  }
+  
+  public void removePropertyChangeListener(PropertyChangeListener l) {
+    changeSupport.removePropertyChangeListener(l);
+  }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/DoubleConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/DoubleConfigurer.java
new file mode 100644
index 0000000..4ced3f7
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/DoubleConfigurer.java
@@ -0,0 +1,63 @@
+/*
+ * $Id: DoubleConfigurer.java 5 2003-10-02 15:11:38 +0000 (Thu, 02 Oct 2003) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+/**
+ * A Configurer for Double values
+ */
+public class DoubleConfigurer extends StringConfigurer {
+
+    public DoubleConfigurer() {
+        super();
+    }
+
+    public DoubleConfigurer(String key, String name) {
+        this(key, name, new Double(0));
+    }
+
+    public DoubleConfigurer(String key, String name, Double val) {
+        super(key, name, val == null ? null : val.toString());
+    }
+
+    public void setValue(String s) {
+        Double d = null;
+        try {
+            d = Double.valueOf(s);
+        }
+        catch (NumberFormatException e) {
+            d = null;
+        }
+        if (d != null)
+            setValue(d);
+    }
+
+    public void setValue(Object o) {
+        if (!noUpdate && nameField != null && o != null) {
+            nameField.setText(o.toString());
+        }
+        super.setValue(o);
+    }
+
+    public String getValueString() {
+        if (value == null || value.equals("")) {
+            return null;
+        }
+        return value.toString();
+    }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/IntConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/IntConfigurer.java
new file mode 100644
index 0000000..210b56b
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/IntConfigurer.java
@@ -0,0 +1,73 @@
+/*
+ * $Id: IntConfigurer.java 874 2006-03-15 14:20:56 +0000 (Wed, 15 Mar 2006) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+/**
+ * A Configurer for Integer values
+ */
+public class IntConfigurer extends StringConfigurer {
+  
+  public IntConfigurer() {
+    super();
+  }
+  
+  public IntConfigurer(String key, String name) {
+    this(key, name, new Integer(0));
+  }
+
+  public IntConfigurer(String key, String name, Integer val) {
+    super(key, name);
+    if (val != null) {
+      setValue(val);
+    }
+  }
+
+  public void setValue(String s) {
+    Integer i = null;
+    try {
+      i = Integer.valueOf(s);
+    }
+    catch (NumberFormatException e) {
+      i = null;
+    }
+    if (i != null) {
+      setValue(i);
+    }
+  }
+  
+  public int getIntValue(int defaultValue) {
+    if (getValue() instanceof Integer) {
+      return ((Integer)getValue()).intValue();
+    }
+    else {
+      return defaultValue;
+    }
+  }
+
+  public void setValue(Object o) {
+    if (!noUpdate && nameField != null && o != null) {
+      nameField.setText(o.toString());
+    }
+    super.setValue(o);
+  }
+
+  public String getValueString() {
+    return value == null ? null : value.toString();
+  }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Lakewalker.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Lakewalker.java
new file mode 100644
index 0000000..3903c23
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Lakewalker.java
@@ -0,0 +1,470 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public class Lakewalker {
+    protected Collection<Command> commands = new LinkedList<Command>();
+    protected Collection<Way> ways = new ArrayList<Way>();
+    protected boolean cancel;
+
+    private int waylen;
+    private int maxnode;
+    private int threshold;
+    private double epsilon;
+    private int resolution;
+    private int tilesize;
+    private String startdir;
+    private String wmslayer;
+
+    private File workingdir;
+
+    private int[] dirslat = new int[] {0,1,1,1,0,-1,-1,-1};
+    private int[] dirslon = new int[] {1,1,0,-1,-1,-1,0,1};
+
+    double start_radius_big = 0.001;
+    double start_radius_small = 0.0002;
+
+    public Lakewalker(int waylen, int maxnode, int threshold, double epsilon, int resolution, int tilesize, String startdir, String wmslayer, File workingdir){
+        this.waylen = waylen;
+        this.maxnode = maxnode;
+        this.threshold = threshold;
+        this.epsilon = epsilon;
+        this.resolution = resolution;
+        this.tilesize = tilesize;
+        this.startdir = startdir;
+        this.wmslayer = wmslayer;
+
+        this.workingdir = workingdir;
+    }
+
+    /**
+     *  east = 0
+     *  northeast = 1
+     *  north = 2
+     *  northwest = 3
+     *  west = 4
+     *  southwest = 5
+     *  south = 6
+     *  southeast = 7
+     */
+    private int getDirectionIndex(String direction) throws ArrayIndexOutOfBoundsException{
+        int i=0;
+        if(direction.equals("East") || direction.equals("east")){
+            i = 0;
+        } else if(direction.equals("Northeast") || direction.equals("northeast")){
+            i =  1;
+        } else if(direction.equals("North") || direction.equals("north")){
+            i =  2;
+        } else if(direction.equals("Northwest") || direction.equals("northwest")){
+            i =  3;
+        } else if(direction.equals("West") || direction.equals("west")){
+            i =  4;
+        } else if(direction.equals("Southwest") || direction.equals("southwest")){
+            i =  5;
+        } else if(direction.equals("South") || direction.equals("south")){
+            i =  6;
+        } else if(direction.equals("Southeast") || direction.equals("southeast")){
+            i =  7;
+        } else {
+            throw new ArrayIndexOutOfBoundsException(tr("Direction index '{0}' not found",direction));
+        }
+        return i;
+    }
+
+    /**
+     * Do a trace
+     *
+     * @param lat
+     * @param lon
+     * @param tl_lon
+     * @param br_lon
+     * @param tl_lat
+     * @param br_lat
+     */
+    public ArrayList<double[]> trace(double lat, double lon, double tl_lon, double br_lon, double tl_lat, double br_lat, ProgressMonitor progressMonitor) throws LakewalkerException {
+
+    	progressMonitor.beginTask(null);
+
+    	try {
+
+    		LakewalkerWMS wms = new LakewalkerWMS(this.resolution, this.tilesize, this.wmslayer, this.workingdir);
+    		LakewalkerBBox bbox = new LakewalkerBBox(tl_lat,tl_lon,br_lat,br_lon);
+
+    		Boolean detect_loop = false;
+
+    		ArrayList<double[]> nodelist = new ArrayList<double[]>();
+
+    		int[] xy = geo_to_xy(lat,lon,this.resolution);
+
+    		if(!bbox.contains(lat, lon)){
+    			throw new LakewalkerException(tr("The starting location was not within the bbox"));
+    		}
+
+    		int v;
+
+    		progressMonitor.indeterminateSubTask(tr("Looking for shoreline..."));
+
+    		while(true){
+    			double[] geo = xy_to_geo(xy[0],xy[1],this.resolution);
+    			if(bbox.contains(geo[0],geo[1])==false){
+    				break;
+    			}
+
+    			v = wms.getPixel(xy[0], xy[1], progressMonitor.createSubTaskMonitor(0, false));
+    			if(v > this.threshold){
+    				break;
+    			}
+
+    			int delta_lat = this.dirslat[getDirectionIndex(this.startdir)];
+    			int delta_lon = this.dirslon[getDirectionIndex(this.startdir)];
+
+    			xy[0] = xy[0]+delta_lon;
+    			xy[1] = xy[1]+delta_lat;
+
+    		}
+
+    		int[] startxy = new int[] {xy[0], xy[1]};
+    		double[] startgeo = xy_to_geo(xy[0],xy[1],this.resolution);
+
+    		//System.out.printf("Found shore at lat %.4f lon %.4f\n",lat,lon);
+
+    		int last_dir = this.getDirectionIndex(this.startdir);
+
+    		for(int i = 0; i < this.maxnode; i++){
+
+    			// Print a counter
+    			if(i % 250 == 0){
+    				progressMonitor.indeterminateSubTask(tr("{0} nodes so far...",i));
+    				//System.out.println(i+" nodes so far...");
+    			}
+
+    			// Some variables we need
+    			int d;
+    			int test_x=0;
+    			int test_y=0;
+    			int new_dir = 0;
+
+    			// Loop through all the directions we can go
+    			for(d = 1; d <= this.dirslat.length; d++){
+
+    				// Decide which direction we want to look at from this pixel
+    				new_dir = (last_dir + d + 4) % 8;
+
+    				test_x = xy[0] + this.dirslon[new_dir];
+    				test_y = xy[1] + this.dirslat[new_dir];
+
+    				double[] geo = xy_to_geo(test_x,test_y,this.resolution);
+
+    				if(!bbox.contains(geo[0], geo[1])){
+    					System.out.println("Outside bbox");
+    					break;
+    				}
+
+    				v = wms.getPixel(test_x, test_y, progressMonitor.createSubTaskMonitor(0, false));
+    				if(v > this.threshold){
+    					break;
+    				}
+
+    				if(d == this.dirslat.length-1){
+    					System.out.println("Got stuck");
+    					break;
+    				}
+    			}
+
+    			// Remember this direction
+    			last_dir = new_dir;
+
+    			// Set the pixel we found as current
+    			xy[0] = test_x;
+    			xy[1] = test_y;
+
+    			// Break the loop if we managed to get back to our starting point
+    			if(xy[0] == startxy[0] && xy[1] == startxy[1]){
+    				break;
+    			}
+
+    			// Store this node
+    			double[] geo = xy_to_geo(xy[0],xy[1],this.resolution);
+    			nodelist.add(geo);
+    			//System.out.println("Adding node at "+xy[0]+","+xy[1]+" ("+geo[1]+","+geo[0]+")");
+
+    			// Check if we got stuck in a loop
+    			double start_proximity = Math.pow((geo[0] - startgeo[0]),2) + Math.pow((geo[1] - startgeo[1]),2);
+
+    			if(detect_loop){
+    				if(start_proximity < Math.pow(start_radius_small,2)){
+    					System.out.println("Detected loop");
+    					break;
+    				}
+    			}else{
+    				if(start_proximity > Math.pow(start_radius_big,2)){
+    					detect_loop = true;
+    				}
+    			}
+    		}
+
+    		return nodelist;
+    	} finally {
+    		progressMonitor.finishTask();
+    	}
+    }
+
+    /**
+     * Remove duplicate nodes from the list
+     *
+     * @param nodes
+     * @return
+     */
+    public ArrayList<double[]> duplicateNodeRemove(ArrayList<double[]> nodes){
+
+        if(nodes.size() <= 1){
+            return nodes;
+        }
+
+        double lastnode[] = new double[] {nodes.get(0)[0], nodes.get(0)[1]};
+
+        for(int i = 1; i < nodes.size(); i++){
+            double[] thisnode = new double[] {nodes.get(i)[0], nodes.get(i)[1]};
+
+            if(thisnode[0] == lastnode[0] && thisnode[1] == lastnode[1]){
+                // Remove the node
+                nodes.remove(i);
+
+                // Shift back one index
+                i = i - 1;
+            }
+            lastnode = thisnode;
+        }
+
+        return nodes;
+    }
+
+    /**
+     * Reduce the number of vertices based on their proximity to each other
+     *
+     * @param nodes
+     * @param proximity
+     * @return
+     */
+    public ArrayList<double[]> vertexReduce(ArrayList<double[]> nodes, double proximity){
+
+        // Check if node list is empty
+        if(nodes.size()<=1){
+            return nodes;
+        }
+
+        double[] test_v = nodes.get(0);
+        ArrayList<double[]> reducednodes = new ArrayList<double[]>();
+
+        double prox_sq = Math.pow(proximity, 2);
+
+        for(int v = 0; v < nodes.size(); v++){
+            if(Math.pow(nodes.get(v)[0] - test_v[0],2) + Math.pow(nodes.get(v)[1] - test_v[1],2) > prox_sq){
+                reducednodes.add(nodes.get(v));
+                test_v = nodes.get(v);
+            }
+        }
+
+        return reducednodes;
+    }
+
+    public double pointLineDistance(double[] p1, double[] p2, double[] p3){
+
+        double x0 = p1[0];
+        double y0 = p1[1];
+        double x1 = p2[0];
+        double y1 = p2[1];
+        double x2 = p3[0];
+        double y2 = p3[1];
+
+        if(x2 == x1 && y2 == y1){
+            return Math.sqrt(Math.pow(x1-x0,2) + Math.pow(y1-y0,2));
+        } else {
+            return Math.abs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2));
+        }
+    }
+
+        /*
+    public ArrayList<double[]> douglasPeuckerNR(ArrayList<double[]> nodes, double epsilon){
+        command_stack = [(0, len(nodes) - 1)]
+
+        Vector result_stack = new Vector();
+
+        while(command_stack.size() > 0){
+            cmd = command_stack.pop();
+            if(type(cmd) == tuple){
+                (start, end) = cmd
+                (node, dist) = dp_findpoint(nodes, start, end)
+                if(dist > epsilon){
+                    command_stack.append("+")
+                    command_stack.append((start, node))
+                    command_stack.append((node, end))
+                } else {
+                    result_stack.append((start, end))
+                }
+            } elseif(cmd == "+"){
+                first = result_stack.pop()
+                second = result_stack.pop()
+                if(first[-1] == second[0]){
+                    result_stack.append(first + second[1:])
+                    //print "Added %s and %s; result is %s" % (first, second, result_stack[-1])
+                }else {
+                    error("ERROR: Cannot connect nodestrings!")
+                    #print first
+                    #print second
+                    return;
+                }
+            } else {
+                error("ERROR: Can't understand command \"%s\"" % (cmd,))
+                return
+
+        if(len(result_stack) == 1){
+            return [nodes[x] for x in result_stack[0]];
+        } else {
+            error("ERROR: Command stack is empty but result stack has %d nodes!" % len(result_stack));
+            return;
+        }
+
+        farthest_node = None
+        farthest_dist = 0
+        first = nodes[0]
+        last = nodes[-1]
+
+        for(i in xrange(1, len(nodes) - 1){
+            d = point_line_distance(nodes[i], first, last)
+            if(d > farthest_dist){
+                farthest_dist = d
+                farthest_node = i
+            }
+        }
+        if(farthest_dist > epsilon){
+            seg_a = douglas_peucker(nodes[0:farthest_node+1], epsilon)
+            seg_b = douglas_peucker(nodes[farthest_node:-1], epsilon)
+            //print "Minimized %d nodes to %d + %d nodes" % (len(nodes), len(seg_a), len(seg_b))
+            nodes = seg_a[:-1] + seg_b
+        } else {
+            return [nodes[0], nodes[-1]];
+        }
+        return nodes;
+    }
+        */
+
+    public ArrayList<double[]> douglasPeucker(ArrayList<double[]> nodes, double epsilon, int depth){
+
+        // Check if node list is empty
+        if(nodes.size()<=1 || depth > 500){
+            return nodes;
+        }
+
+        int farthest_node = -1;
+        double farthest_dist = 0;
+        double[] first = nodes.get(0);
+        double[] last = nodes.get(nodes.size()-1);
+
+        ArrayList<double[]> new_nodes = new ArrayList<double[]>();
+
+        double d = 0;
+
+        for(int i = 1; i < nodes.size(); i++){
+            d = pointLineDistance(nodes.get(i),first,last);
+            if(d>farthest_dist){
+                farthest_dist = d;
+                farthest_node = i;
+            }
+        }
+
+        ArrayList<double[]> seg_a = new ArrayList<double[]>();
+        ArrayList<double[]> seg_b = new ArrayList<double[]>();
+
+        if(farthest_dist > epsilon){
+            seg_a = douglasPeucker(sublist(nodes,0,farthest_node+1),epsilon, depth+1);
+            seg_b = douglasPeucker(sublist(nodes,farthest_node,nodes.size()-1),epsilon,depth+1);
+
+            new_nodes.addAll(seg_a);
+            new_nodes.addAll(seg_b);
+        } else {
+            new_nodes.add(nodes.get(0));
+            new_nodes.add(nodes.get(nodes.size()-1));
+        }
+        return new_nodes;
+    }
+
+    private ArrayList<double[]> sublist(ArrayList<double[]> l, int i, int f) throws ArrayIndexOutOfBoundsException {
+        ArrayList<double[]> sub = new ArrayList<double[]>();
+
+        if(f<i || i < 0 || f < 0 || f > l.size()){
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+        for(int j = i; j < f; j++){
+            sub.add(l.get(j));
+        }
+        return sub;
+    }
+
+    public double[] xy_to_geo(int x, int y, double resolution){
+        double[] geo = new double[2];
+        geo[0] = y / resolution;
+        geo[1] = x / resolution;
+        return geo;
+    }
+
+    public int[] geo_to_xy(double lat, double lon, double resolution){
+        int[] xy = new int[2];
+
+        xy[0] = (int)Math.floor(lon * resolution + 0.5);
+        xy[1] = (int)Math.floor(lat * resolution + 0.5);
+
+        return xy;
+    }
+
+    /*
+     * User has hit the cancel button
+     */
+    public void cancel() {
+      cancel = true;
+    }
+
+    /**
+     * Class to do checking of whether the point is within our bbox
+     *
+     * @author Jason Reid
+     */
+    private class LakewalkerBBox {
+
+        private double top = 90;
+        private double left = -180;
+        private double bottom = -90;
+        private double right = 180;
+
+        protected LakewalkerBBox(double top, double left, double bottom, double right){
+          this.left = left;
+          this.right = right;
+          this.top = top;
+          this.bottom = bottom;
+        }
+
+        protected Boolean contains(double lat, double lon){
+          if(lat >= this.top || lat <= this.bottom){
+            return false;
+          }
+          if(lon >= this.right || lon <= this.left){
+              return false;
+          }
+          if((this.right - this.left) % 360 == 0){
+            return true;
+          }
+          return (lon - this.left) % 360 <= (this.right - this.left) % 360;
+        }
+    }
+}
+
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerAction.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerAction.java
new file mode 100644
index 0000000..0524c97
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerAction.java
@@ -0,0 +1,324 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Cursor;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.xml.sax.SAXException;
+
+/**
+ * Interface to Darryl Shpak's Lakewalker module
+ *
+ * @author Brent Easton
+ */
+class LakewalkerAction extends JosmAction implements MouseListener {
+
+    private static final long serialVersionUID = 1L;
+    protected String name;
+    protected Cursor oldCursor;
+    protected Thread executeThread;
+    protected boolean cancel;
+    protected boolean active = false;
+
+    protected Collection<Command> commands = new LinkedList<Command>();
+    protected Collection<Way> ways = new ArrayList<Way>();
+
+    public LakewalkerAction(String name) {
+        super(name, "lakewalker-sml", tr("Lake Walker."),
+        Shortcut.registerShortcut("tools:lakewalker", tr("Tool: {0}", tr("Lake Walker")),
+        KeyEvent.VK_L, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
+        this.name = name;
+        setEnabled(true);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        if(Main.map == null || Main.map.mapView == null || active)
+            return;
+
+        active = true;
+        oldCursor = Main.map.mapView.getCursor();
+        Main.map.mapView.setCursor(ImageProvider.getCursor("crosshair", "lakewalker-sml"));
+        Main.map.mapView.addMouseListener(this);
+    }
+
+    /**
+    * check for presence of cache folder and trim cache to specified size.
+    * size/age limit is on a per folder basis.
+    */
+    private void cleanupCache() {
+        final long maxCacheAge = System.currentTimeMillis()-Main.pref.getInteger(LakewalkerPreferences.PREF_MAXCACHEAGE, 100)*24*60*60*1000L;
+        final long maxCacheSize = Main.pref.getInteger(LakewalkerPreferences.PREF_MAXCACHESIZE, 300)*1024*1024;
+
+        for (String wmsFolder : LakewalkerPreferences.WMSLAYERS) {
+            String wmsCacheDirName = Main.pref.getPreferencesDir()+"plugins/Lakewalker/"+wmsFolder;
+            File wmsCacheDir = new File(wmsCacheDirName);
+
+            if (wmsCacheDir.exists() && wmsCacheDir.isDirectory()) {
+                File wmsCache[] = wmsCacheDir.listFiles();
+
+                // sort files by date (most recent first)
+                Arrays.sort(wmsCache, new Comparator<File>() {
+                    public int compare(File f1, File f2) {
+                        return (int)(f2.lastModified()-f1.lastModified());
+                    }
+                });
+
+                // delete aged or oversized, keep newest. Once size/age limit was reached delete all older files
+                long folderSize = 0;
+                boolean quickdelete = false;
+                for (File cacheEntry : wmsCache) {
+                    if (!cacheEntry.isFile()) continue;
+                    if (!quickdelete) {
+                        folderSize += cacheEntry.length();
+                        if (folderSize > maxCacheSize) {
+                            quickdelete = true;
+                        } else if (cacheEntry.lastModified() < maxCacheAge) {
+                            quickdelete = true;
+                        }
+                    }
+
+                    if (quickdelete) {
+                        cacheEntry.delete();
+                    }
+                }
+
+            } else {
+                // create cache directory
+                if (!wmsCacheDir.mkdirs()) {
+                    JOptionPane.showMessageDialog(Main.parent, tr("Error creating cache directory: {0}", wmsCacheDirName));
+                }
+            }
+        }
+    }
+
+    protected void lakewalk(Point clickPoint){
+        /**
+        * Positional data
+        */
+        final LatLon pos = Main.map.mapView.getLatLon(clickPoint.x, clickPoint.y);
+        final LatLon topLeft = Main.map.mapView.getLatLon(0, 0);
+        final LatLon botRight = Main.map.mapView.getLatLon(Main.map.mapView.getWidth(),
+            Main.map.mapView.getHeight());
+
+        /**
+        * Cache/working directory location
+        */
+        final File working_dir = new File(Main.pref.getPreferencesDir(), "plugins/Lakewalker");
+
+        /*
+        * Collect options
+        */
+        final int waylen = Main.pref.getInteger(LakewalkerPreferences.PREF_MAX_SEG, 500);
+        final int maxnode = Main.pref.getInteger(LakewalkerPreferences.PREF_MAX_NODES, 50000);
+        final int threshold = Main.pref.getInteger(LakewalkerPreferences.PREF_THRESHOLD_VALUE, 90);
+        final double epsilon = Main.pref.getDouble(LakewalkerPreferences.PREF_EPSILON, 0.0003);
+        final int resolution = Main.pref.getInteger(LakewalkerPreferences.PREF_LANDSAT_RES, 4000);
+        final int tilesize = Main.pref.getInteger(LakewalkerPreferences.PREF_LANDSAT_SIZE, 2000);
+        final String startdir = Main.pref.get(LakewalkerPreferences.PREF_START_DIR, "east");
+        final String wmslayer = Main.pref.get(LakewalkerPreferences.PREF_WMS, "IR1");
+
+        try {
+            PleaseWaitRunnable lakewalkerTask = new PleaseWaitRunnable(tr("Tracing")){
+                @Override protected void realRun() throws SAXException {
+                    progressMonitor.subTask(tr("checking cache..."));
+                    cleanupCache();
+                    processnodelist(pos, topLeft, botRight, waylen, maxnode, threshold,
+                            epsilon,resolution,tilesize,startdir,wmslayer, working_dir,
+                            progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+                }
+                @Override protected void finish() {
+                }
+
+				@Override protected void cancel() {
+					LakewalkerAction.this.cancel();
+				}
+            };
+            Thread executeThread = new Thread(lakewalkerTask);
+            executeThread.start();
+        }
+        catch (Exception ex) {
+          System.out.println("Exception caught: " + ex.getMessage());
+        }
+    }
+
+    private void processnodelist(LatLon pos, LatLon topLeft, LatLon botRight, int waylen, int maxnode, int threshold, double epsilon, int resolution, int tilesize, String startdir, String wmslayer, File workingdir, ProgressMonitor progressMonitor){
+        progressMonitor.beginTask(null, 3);
+        try {
+            ArrayList<double[]> nodelist = new ArrayList<double[]>();
+
+            Lakewalker lw = new Lakewalker(waylen,maxnode,threshold,epsilon,resolution,tilesize,startdir,wmslayer,workingdir);
+            try {
+                nodelist = lw.trace(pos.lat(),pos.lon(),topLeft.lon(),botRight.lon(),topLeft.lat(),botRight.lat(),
+                        progressMonitor.createSubTaskMonitor(1, false));
+            } catch(LakewalkerException e){
+                System.out.println(e.getError());
+            }
+
+            System.out.println(nodelist.size()+" nodes generated");
+
+            /**
+            * Run the nodelist through a vertex reduction algorithm
+            */
+
+            progressMonitor.subTask(tr("Running vertex reduction..."));
+
+            nodelist = lw.vertexReduce(nodelist, epsilon);
+
+            //System.out.println("After vertex reduction "+nodelist.size()+" nodes remain.");
+
+            /**
+            * And then through douglas-peucker approximation
+            */
+
+            progressMonitor.worked(1);
+            progressMonitor.subTask(tr("Running Douglas-Peucker approximation..."));
+
+            nodelist = lw.douglasPeucker(nodelist, epsilon, 0);
+
+            //System.out.println("After Douglas-Peucker approximation "+nodelist.size()+" nodes remain.");
+
+            /**
+            * And then through a duplicate node remover
+            */
+
+            progressMonitor.worked(1);
+            progressMonitor.subTask(tr("Removing duplicate nodes..."));
+
+            nodelist = lw.duplicateNodeRemove(nodelist);
+
+            //System.out.println("After removing duplicate nodes, "+nodelist.size()+" nodes remain.");
+
+
+            // if for some reason (image loading failed, ...) nodelist is empty, no more processing required.
+            if (nodelist.size() == 0) {
+                return;
+            }
+
+            /**
+            * Turn the arraylist into osm nodes
+            */
+
+            Way way = new Way();
+            Node n = null;
+            Node fn = null;
+
+            double eastOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_EAST_OFFSET, 0.0);
+            double northOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_NORTH_OFFSET, 0.0);
+
+            int nodesinway = 0;
+
+            for(int i = 0; i< nodelist.size(); i++){
+                if (cancel) {
+                    return;
+                }
+
+                try {
+                    LatLon ll = new LatLon(nodelist.get(i)[0]+northOffset, nodelist.get(i)[1]+eastOffset);
+                    n = new Node(ll);
+                    if(fn==null){
+                        fn = n;
+                    }
+                    commands.add(new AddCommand(n));
+
+                } catch (Exception ex) {
+                }
+
+                way.addNode(n);
+
+                if(nodesinway > Main.pref.getInteger(LakewalkerPreferences.PREF_MAX_SEG, 500)){
+                    String waytype = Main.pref.get(LakewalkerPreferences.PREF_WAYTYPE, "water");
+
+                    if(!waytype.equals("none")){
+                        way.put("natural",waytype);
+                    }
+
+                    way.put("source", Main.pref.get(LakewalkerPreferences.PREF_SOURCE, "Landsat"));
+                    commands.add(new AddCommand(way));
+
+                    way = new Way();
+
+                    way.addNode(n);
+
+                    nodesinway = 0;
+                }
+                nodesinway++;
+            }
+
+
+            String waytype = Main.pref.get(LakewalkerPreferences.PREF_WAYTYPE, "water");
+
+            if(!waytype.equals("none")){
+                way.put("natural",waytype);
+            }
+
+            way.put("source", Main.pref.get(LakewalkerPreferences.PREF_SOURCE, "Landsat"));
+
+            way.addNode(fn);
+
+            commands.add(new AddCommand(way));
+
+            if (!commands.isEmpty()) {
+                Main.main.undoRedo.add(new SequenceCommand(tr("Lakewalker trace"), commands));
+                Main.main.getCurrentDataSet().setSelected(ways);
+            } else {
+                System.out.println("Failed");
+            }
+
+            commands = new LinkedList<Command>();
+            ways = new ArrayList<Way>();
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+
+    public void cancel() {
+        cancel = true;
+    }
+
+    public void mouseClicked(MouseEvent e) {
+        if(active)
+        {
+            active = false;
+            Main.map.mapView.removeMouseListener(this);
+            Main.map.mapView.setCursor(oldCursor);
+            lakewalk(e.getPoint());
+        }
+    }
+
+    public void mouseEntered(MouseEvent e) {
+    }
+
+    public void mouseExited(MouseEvent e) {
+    }
+
+    public void mousePressed(MouseEvent e) {
+    }
+
+    public void mouseReleased(MouseEvent e) {
+    }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerApp.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerApp.java
new file mode 100644
index 0000000..11560c7
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerApp.java
@@ -0,0 +1,59 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+
+public class LakewalkerApp {
+    public static void main(String[] args){
+        double lat = 52.31384;
+        double lon = -79.135;
+        double toplat = 52.3165;
+        double botlat = 52.3041;
+        double leftlon = -79.1442;
+        double rightlon = -79.1093;
+
+        // ?lat=39.15579999999999&lon=2.9411&zoom=12&layers=B000F000F
+        lat = 39.1422;
+        lon = 2.9102;
+
+        toplat = 39.2229;
+        botlat = 39.0977;
+        leftlon = 2.8560;
+        rightlon = 3.0462;
+
+        int waylen = 250;
+        int maxnode = 5000;
+        int threshold = 100;
+        double dp = 0.0003;
+        int tilesize = 2000;
+        int resolution = 4000;
+        String startdir = "East";
+        String wmslayer = "IR2";
+
+        File working_dir = new File("Lakewalker");
+
+        ArrayList<double[]> nodelist = null;
+
+        Lakewalker lw = new Lakewalker(waylen,maxnode,threshold,dp,resolution,tilesize,startdir,wmslayer,working_dir);
+        try {
+            nodelist = lw.trace(lat,lon,leftlon,rightlon,toplat,botlat, NullProgressMonitor.INSTANCE);
+        } catch(LakewalkerException e){
+            System.out.println(e.getError());
+        }
+
+        System.out.println(nodelist.size()+" nodes generated");
+
+        nodelist = lw.vertexReduce(nodelist, dp);
+
+        System.out.println("After vertex reduction, "+nodelist.size()+" nodes remain.");
+
+        nodelist = lw.douglasPeucker(nodelist, dp, 0);
+
+        System.out.println("After dp approximation, "+nodelist.size()+" nodes remain.");
+
+
+
+    }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerException.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerException.java
new file mode 100644
index 0000000..48409a7
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerException.java
@@ -0,0 +1,21 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+class LakewalkerException extends Exception {
+    String error;
+
+    public LakewalkerException(){
+        super();
+        this.error = tr("An unknown error has occurred");
+    }
+
+    public LakewalkerException(String err){
+        super();
+        this.error = err;
+    }
+
+    public String getError(){
+      return this.error;
+    }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPlugin.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPlugin.java
new file mode 100644
index 0000000..b4c8395
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPlugin.java
@@ -0,0 +1,25 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+
+/**
+ * Interface to Darryl Shpak's Lakewalker python module
+ *
+ * @author Brent Easton
+ */
+public class LakewalkerPlugin extends Plugin {
+  public LakewalkerPlugin() {
+    MainMenu.add(Main.main.menu.toolsMenu, new LakewalkerAction(tr("Lake Walker")));
+  }
+
+  public PreferenceSetting getPreferenceSetting()
+  {
+    return new LakewalkerPreferences();
+  }
+
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPreferences.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPreferences.java
new file mode 100644
index 0000000..a4f5f3d
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPreferences.java
@@ -0,0 +1,160 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import javax.swing.Box;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.I18n;
+
+public class LakewalkerPreferences implements PreferenceSetting {
+    public static final String[] DIRECTIONS = new String[]
+    {marktr("east"), marktr("northeast"), marktr("north"), marktr("northwest"),
+    marktr("west"), marktr("southwest"), marktr("south"), marktr("southeast")};
+    public static final String[] WAYTYPES = new String[]
+    {marktr("water"), marktr("coastline"), marktr("land"), marktr("none")};
+    public static final String[] WMSLAYERS = new String[] {"IR1", "IR2", "IR3"};
+
+    public static final String PREF_MAX_SEG = "lakewalker.max_segs_in_way";
+    public static final String PREF_MAX_NODES = "lakewalker.max_nodes";
+    public static final String PREF_THRESHOLD_VALUE = "lakewalker.thresholdvalue";
+    public static final String PREF_EPSILON = "lakewalker.epsilon";
+    public static final String PREF_LANDSAT_RES = "lakewalker.landsat_res";
+    public static final String PREF_LANDSAT_SIZE = "lakewalker.landsat_size";
+    public static final String PREF_EAST_OFFSET = "lakewalker.east_offset";
+    public static final String PREF_NORTH_OFFSET = "lakewalker.north_offset";
+    public static final String PREF_START_DIR = "lakewalker.startdir";
+    public static final String PREF_WAYTYPE = "lakewalker.waytype";
+    public static final String PREF_WMS = "lakewalker.wms";
+    public static final String PREF_SOURCE = "lakewalker.source";
+    public static final String PREF_MAXCACHESIZE = "lakewalker.maxcachesize";
+    public static final String PREF_MAXCACHEAGE = "lakewalker.maxcacheage";
+
+    protected IntConfigurer maxSegsConfig = new IntConfigurer();
+    protected JLabel maxSegsLabel = new JLabel(tr("Maximum number of segments per way"));
+    protected IntConfigurer maxNodesConfig = new IntConfigurer();
+    protected JLabel maxNodesLabel = new JLabel(tr("Maximum number of nodes in initial trace"));
+    protected IntConfigurer thresholdConfig = new IntConfigurer();
+    protected JLabel thresholdLabel = new JLabel(tr("Maximum gray value to count as water (0-255)"));
+    protected DoubleConfigurer epsilonConfig = new DoubleConfigurer();
+    protected JLabel epsilonLabel = new JLabel(tr("Line simplification accuracy (degrees)"));
+    protected IntConfigurer landsatResConfig = new IntConfigurer();
+    protected JLabel landsatResLabel = new JLabel(tr("Resolution of Landsat tiles (pixels per degree)"));
+    protected IntConfigurer landsatSizeConfig = new IntConfigurer();
+    protected JLabel landsatSizeLabel = new JLabel(tr("Size of Landsat tiles (pixels)"));
+    protected DoubleConfigurer eastOffsetConfig = new DoubleConfigurer();
+    protected JLabel eastOffsetLabel = new JLabel(tr("Shift all traces to east (degrees)"));
+    protected DoubleConfigurer northOffsetConfig = new DoubleConfigurer();
+    protected JLabel northOffsetLabel = new JLabel(tr("Shift all traces to north (degrees)"));
+    protected StringEnumConfigurer startDirConfig = new StringEnumConfigurer(DIRECTIONS);
+    protected JLabel startDirLabel = new JLabel(tr("Direction to search for land"));
+    protected StringEnumConfigurer lakeTypeConfig = new StringEnumConfigurer(WAYTYPES);
+    protected JLabel lakeTypeLabel = new JLabel(tr("Tag ways as"));
+    protected StringEnumConfigurer wmsConfig = new StringEnumConfigurer(WMSLAYERS);
+    protected JLabel wmsLabel = new JLabel(tr("WMS Layer"));
+    protected IntConfigurer maxCacheSizeConfig = new IntConfigurer();
+    protected JLabel maxCacheSizeLabel = new JLabel(tr("Maximum cache size (MB)"));
+    protected IntConfigurer maxCacheAgeConfig = new IntConfigurer();
+    protected JLabel maxCacheAgeLabel = new JLabel(tr("Maximum cache age (days)"));
+    protected StringConfigurer sourceConfig = new StringConfigurer();
+    protected JLabel sourceLabel = new JLabel(tr("Source text"));
+
+    public void addGui(PreferenceDialog gui) {
+        maxSegsConfig.setToolTipText(tr("Maximum number of segments allowed in each generated way. Default 250."));
+        maxNodesConfig.setToolTipText(tr("Maximum number of nodes to generate before bailing out (before simplifying lines). Default 50000."));
+        thresholdConfig.setToolTipText(tr("Maximum gray value to accept as water (based on Landsat IR-1 data). Can be in the range 0-255. Default 90."));
+        epsilonConfig.setToolTipText(tr("Accuracy of Douglas-Peucker line simplification, measured in degrees.<br>Lower values give more nodes, and more accurate lines. Default 0.0003."));
+        landsatResConfig.setToolTipText(tr("Resolution of Landsat tiles, measured in pixels per degree. Default 4000."));
+        landsatSizeConfig.setToolTipText(tr("Size of one landsat tile, measured in pixels. Default 2000."));
+        eastOffsetConfig.setToolTipText(tr("Offset all points in East direction (degrees). Default 0."));   
+        northOffsetConfig.setToolTipText(tr("Offset all points in North direction (degrees). Default 0."));   
+        startDirConfig.setToolTipText(tr("Direction to search for land. Default east."));
+        lakeTypeConfig.setToolTipText(tr("Tag ways as water, coastline, land or nothing. Default is water."));
+        wmsConfig.setToolTipText(tr("Which WMS layer to use for tracing against. Default is IR1."));
+        maxCacheSizeConfig.setToolTipText(tr("Maximum size of each cache directory in bytes. Default is 300MB"));
+        maxCacheAgeConfig.setToolTipText(tr("Maximum age of each cached file in days. Default is 100"));
+        sourceConfig.setToolTipText(tr("Data source text. Default is Landsat."));
+
+        String description = tr("A plugin to trace water bodies on Landsat imagery.");
+        JPanel prefPanel = gui.createPreferenceTab("lakewalker.png", I18n.tr("Lakewalker Plugin Preferences"), description);
+        buildPreferences(prefPanel);
+
+        maxSegsConfig.setValue(Main.pref.getInteger(PREF_MAX_SEG, 500));
+        maxNodesConfig.setValue(Main.pref.getInteger(PREF_MAX_NODES, 50000));
+        thresholdConfig.setValue(Main.pref.getInteger(PREF_THRESHOLD_VALUE, 90));
+        epsilonConfig.setValue(Main.pref.getDouble(PREF_EPSILON, 0.0003));
+        landsatResConfig.setValue(Main.pref.getInteger(PREF_LANDSAT_RES, 4000));
+        landsatSizeConfig.setValue(Main.pref.getInteger(PREF_LANDSAT_SIZE, 2000));
+        eastOffsetConfig.setValue(Main.pref.getDouble(PREF_EAST_OFFSET, 0.0));
+        northOffsetConfig.setValue(Main.pref.getDouble(PREF_NORTH_OFFSET, 0.0));
+        startDirConfig.setValue(Main.pref.get(PREF_START_DIR, "east"));
+        lakeTypeConfig.setValue(Main.pref.get(PREF_WAYTYPE, "water"));
+        wmsConfig.setValue(Main.pref.get(PREF_WMS, "IR1"));
+        sourceConfig.setValue(Main.pref.get(PREF_SOURCE, "Landsat"));
+        maxCacheSizeConfig.setValue(Main.pref.getInteger(PREF_MAXCACHESIZE, 300));
+        maxCacheAgeConfig.setValue(Main.pref.getInteger(PREF_MAXCACHEAGE, 100));
+    }
+
+    public void buildPreferences(JPanel prefPanel) {
+        GBC labelConstraints = GBC.std().insets(10,5,5,0);
+        GBC dataConstraints = GBC.eol().insets(0,5,0,0).fill(GBC.HORIZONTAL);
+
+        prefPanel.add(maxSegsLabel, labelConstraints);
+        prefPanel.add(maxSegsConfig.getControls(), dataConstraints);
+        prefPanel.add(maxNodesLabel, labelConstraints);
+        prefPanel.add(maxNodesConfig.getControls(), dataConstraints);
+        prefPanel.add(thresholdLabel, labelConstraints);
+        prefPanel.add(thresholdConfig.getControls(), dataConstraints);
+        prefPanel.add(epsilonLabel, labelConstraints);
+        prefPanel.add(epsilonConfig.getControls(), dataConstraints);
+        prefPanel.add(landsatResLabel, labelConstraints);
+        prefPanel.add(landsatResConfig.getControls(), dataConstraints);
+        prefPanel.add(landsatSizeLabel, labelConstraints);
+        prefPanel.add(landsatSizeConfig.getControls(), dataConstraints);    
+        prefPanel.add(eastOffsetLabel, labelConstraints);
+        prefPanel.add(eastOffsetConfig.getControls(), dataConstraints);
+        prefPanel.add(northOffsetLabel, labelConstraints);
+        prefPanel.add(northOffsetConfig.getControls(), dataConstraints);
+        prefPanel.add(startDirLabel, labelConstraints);
+        prefPanel.add(startDirConfig.getControls(), dataConstraints);    
+        prefPanel.add(lakeTypeLabel, labelConstraints);
+        prefPanel.add(lakeTypeConfig.getControls(), dataConstraints);
+        prefPanel.add(wmsLabel, labelConstraints);
+        prefPanel.add(wmsConfig.getControls(), dataConstraints);
+        prefPanel.add(maxCacheSizeLabel, labelConstraints);
+        prefPanel.add(maxCacheSizeConfig.getControls(), dataConstraints);
+        prefPanel.add(maxCacheAgeLabel, labelConstraints);
+        prefPanel.add(maxCacheAgeConfig.getControls(), dataConstraints);
+        prefPanel.add(sourceLabel, labelConstraints);
+        prefPanel.add(sourceConfig.getControls(), dataConstraints);
+
+        prefPanel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+    }
+
+    /*
+    * Save entered preference values on OK button
+    */
+    public boolean ok() {
+        Main.pref.put(PREF_MAX_SEG, maxSegsConfig.getValueString());
+        Main.pref.put(PREF_MAX_NODES, maxNodesConfig.getValueString());
+        Main.pref.put(PREF_THRESHOLD_VALUE, thresholdConfig.getValueString());
+        Main.pref.put(PREF_EPSILON, epsilonConfig.getValueString());
+        Main.pref.put(PREF_LANDSAT_RES, landsatResConfig.getValueString());
+        Main.pref.put(PREF_LANDSAT_SIZE, landsatSizeConfig.getValueString());
+        Main.pref.put(PREF_EAST_OFFSET, eastOffsetConfig.getValueString());
+        Main.pref.put(PREF_NORTH_OFFSET, northOffsetConfig.getValueString());
+        Main.pref.put(PREF_START_DIR, startDirConfig.getValueString());
+        Main.pref.put(PREF_WAYTYPE, lakeTypeConfig.getValueString());
+        Main.pref.put(PREF_WMS, wmsConfig.getValueString());
+        Main.pref.put(PREF_MAXCACHESIZE, maxCacheSizeConfig.getValueString());
+        Main.pref.put(PREF_MAXCACHEAGE, maxCacheAgeConfig.getValueString());
+        Main.pref.put(PREF_SOURCE, sourceConfig.getValueString());
+        return false;
+    }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerReader.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerReader.java
new file mode 100644
index 0000000..4add238
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerReader.java
@@ -0,0 +1,128 @@
+/* LakewalkerReader.java
+ *
+ * Read and process data from a Lakwalker python module
+ *
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public class LakewalkerReader {
+    protected Collection<Command> commands = new LinkedList<Command>();
+    protected Collection<Way> ways = new ArrayList<Way>();
+    protected boolean cancel;
+
+    /*
+    * Read the data
+    */
+    public void read(BufferedReader input, ProgressMonitor progressMonitor) {
+        /*
+        * Lakewalker will output data it stdout. Each line has a code in
+        * character 1 indicating the type of data on the line:
+        *
+        * m text - Status message l name [size] - Access landsat image name. size
+        * is returned if it needs to be downloaded. e text - Error message s nnn -
+        * Start node data stream, nnn seperate tracings to follow t nnn - Start
+        * tracing, nnn nodes to follow x [o] - End of Tracing. o indicates do not
+        * connect last node to first n lat lon [o] - Node. o indicates it is an
+        * open node (not connected to the previous node) z - End of data stream
+        */
+
+    	progressMonitor.beginTask(null);
+    	try {
+    		Way way = new Way();
+    		String line;
+    		progressMonitor.indeterminateSubTask(tr("Initializing"));
+    		double eastOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_EAST_OFFSET, 0.0);
+    		double northOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_NORTH_OFFSET, 0.0);
+    		char option = ' ';
+
+    		try {
+    			Node n = null;  // The current node being created
+    			Node tn = null; // The last node of the previous way
+    			Node fn = null; // Node to hold the first node in the trace
+
+    			while ((line = input.readLine()) != null) {
+    				if (cancel)
+    					return;
+    				System.out.println(line);
+    				option = line.charAt(0);
+    				switch (option) {
+    				case 'n':
+    					String[] tokens = line.split(" ");
+
+    					if(tn==null){
+    						try {
+    							LatLon ll = new LatLon(Double.parseDouble(tokens[1])+northOffset,
+    									Double.parseDouble(tokens[2])+eastOffset);
+    							n = new Node(ll);
+    							if(fn==null)
+    								fn = n;
+    							commands.add(new AddCommand(n));
+    						}
+    						catch (Exception ex) {}
+    					} else {
+    						// If there is a last node, and this node has the same coordinates
+    						// then we substitute for the previous node
+    						n = tn;
+    						tn = null;
+    					}
+    					way.addNode(n);
+    					break;
+    				case 's':
+    					progressMonitor.indeterminateSubTask(line.substring(2));
+    					break;
+    				case 'x':
+    					String waytype = Main.pref.get(LakewalkerPreferences.PREF_WAYTYPE, "water");
+
+    					if(!waytype.equals("none"))
+    						way.put("natural",waytype);
+    					way.put("source", Main.pref.get(LakewalkerPreferences.PREF_SOURCE, "Landsat"));
+    					commands.add(new AddCommand(way));
+    					break;
+    				case 't':
+    					way = new Way();
+    					tn = n;
+    					break;
+    				case 'e':
+    					cancel = true;
+    					break;
+    				}
+    			}
+    			input.close();
+
+    			// Add the start node to the end of the trace to form a closed shape
+    			way.addNode(fn);
+    		}
+    		catch (Exception ex) { }
+
+    		if (!commands.isEmpty()) {
+    			Main.main.undoRedo.add(new SequenceCommand(tr("Lakewalker trace"), commands));
+    			Main.main.getCurrentDataSet().setSelected(ways);
+    		}
+    	} finally {
+    		progressMonitor.finishTask();
+    	}
+    }
+
+    /*
+    * User has hit the cancel button
+    */
+    public void cancel() {
+        cancel = true;
+    }
+}
\ No newline at end of file
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerWMS.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerWMS.java
new file mode 100644
index 0000000..8871fff
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerWMS.java
@@ -0,0 +1,234 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public class LakewalkerWMS {
+
+    private BufferedImage image;
+    private int imagex;
+    private int imagey;
+
+    // Vector to cache images in memory
+    private Vector<BufferedImage> images = new Vector<BufferedImage>();
+    // Hashmap to hold the mapping of cached images
+    private HashMap<String,Integer> imageindex = new HashMap<String,Integer>();
+
+    private int resolution;
+    private int tilesize;
+
+    private String wmslayer;
+
+    private File working_dir;
+
+    public LakewalkerWMS(int resolution, int tilesize, String wmslayer, File workdir){
+        this.resolution = resolution;
+        this.tilesize = tilesize;
+        this.working_dir = workdir;
+        this.wmslayer = wmslayer;
+    }
+
+    public BufferedImage getTile(int x, int y, ProgressMonitor progressMonitor) throws LakewalkerException {
+    	progressMonitor.beginTask(tr("Downloading image tile..."));
+    	try {
+    		String layer = "global_mosaic_base";
+
+    		int[] bottom_left_xy = new int[2];
+    		bottom_left_xy[0] = floor(x,this.tilesize);
+    		bottom_left_xy[1] = floor(y,this.tilesize);
+
+    		int[] top_right_xy = new int[2];
+    		top_right_xy[0] = bottom_left_xy[0] + this.tilesize;
+    		top_right_xy[1] = bottom_left_xy[1] + this.tilesize;
+
+    		double[] topright_geo = xy_to_geo(top_right_xy[0],top_right_xy[1],this.resolution);
+    		double[] bottomleft_geo = xy_to_geo(bottom_left_xy[0],bottom_left_xy[1],this.resolution);
+
+    		String filename = this.wmslayer+"/landsat_"+this.resolution+"_"+this.tilesize+
+    		"_xy_"+bottom_left_xy[0]+"_"+bottom_left_xy[1]+".png";
+
+    		// The WMS server only understands decimal points using periods, so we need
+    		// to convert to a locale that uses that to build the proper URL
+    		NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
+    		DecimalFormat df = (DecimalFormat)nf;
+    		df.applyLocalizedPattern("0.000000");
+
+    		String urlloc = "http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&layers="+layer+
+    		"&styles="+wmslayer+"&srs=EPSG:4326&format=image/png"+
+    		"&bbox="+df.format(bottomleft_geo[1])+","+df.format(bottomleft_geo[0])+
+    		","+df.format(topright_geo[1])+","+df.format(topright_geo[0])+
+    		"&width="+this.tilesize+"&height="+this.tilesize;
+
+    		File file = new File(this.working_dir,filename);
+
+    		// Calculate the hashmap key
+    		String hashkey = Integer.toString(bottom_left_xy[0])+":"+Integer.toString(bottom_left_xy[1]);
+
+    		// See if this image is already loaded
+    		if(this.image != null){
+    			if(this.imagex != bottom_left_xy[0] || this.imagey != bottom_left_xy[1]){
+
+    				// Check if this image exists in the hashmap
+    				if(this.imageindex.containsKey(hashkey)){
+    					// Store which image we have
+    					this.imagex = bottom_left_xy[0];
+    					this.imagey = bottom_left_xy[1];
+
+    					// Retrieve from cache
+    					this.image = this.images.get(this.imageindex.get(hashkey));
+    					return this.image;
+    				} else {
+    					this.image = null;
+    				}
+    			} else {
+    				return this.image;
+    			}
+    		}
+
+    		try {
+    			System.out.println("Looking for image in disk cache: "+filename);
+
+    			// Read from a file
+    			this.image = ImageIO.read(file);
+
+    			this.images.add(this.image);
+    			this.imageindex.put(hashkey,this.images.size()-1);
+
+    		} catch(FileNotFoundException e){
+    			System.out.println("Could not find cached image, downloading.");
+    		} catch(IOException e){
+    			System.out.println(e.getMessage());
+    		} catch(Exception e){
+    			System.out.println(e.getMessage());
+    		}
+
+    		if(this.image == null){
+    			/**
+    			 * Try downloading the image
+    			 */
+    			try {
+    				System.out.println("Downloading from "+urlloc);
+
+    				// Read from a URL
+    				URL url = new URL(urlloc);
+    				this.image = ImageIO.read(url); // this can return null!
+    			} catch(MalformedURLException e){
+    				System.out.println(e.getMessage());
+    			} catch(IOException e){
+    				System.out.println(e.getMessage());
+    			} catch(Exception e){
+    				System.out.println(e.getMessage());
+    			}
+
+    			if (this.image != null) {
+    				this.images.add(this.image);
+    				this.imageindex.put(hashkey,this.images.size()-1);
+
+    				this.saveimage(file,this.image);
+    			}
+    		}
+
+    		this.imagex = bottom_left_xy[0];
+    		this.imagey = bottom_left_xy[1];
+
+    		if(this.image == null){
+    			throw new LakewalkerException(tr("Could not acquire image"));
+    		}
+
+    		return this.image;
+    	} finally {
+    		progressMonitor.finishTask();
+    	}
+    }
+
+    public void saveimage(File file, BufferedImage image){
+        /**
+         * Save the image to the cache
+         */
+        try {
+            ImageIO.write(image, "png", file);
+            System.out.println("Saved image to cache");
+        } catch(Exception e){
+            System.out.println(e.getMessage());
+        }
+    }
+
+    public int getPixel(int x, int y, ProgressMonitor progressMonitor) throws LakewalkerException{
+        // Get the previously shown text
+
+
+        BufferedImage image = null;
+
+        try {
+            image = this.getTile(x,y, progressMonitor);
+        } catch(LakewalkerException e){
+            System.out.println(e.getError());
+            throw new LakewalkerException(e.getMessage());
+        }
+
+        int tx = floor(x,this.tilesize);
+        int ty = floor(y,this.tilesize);
+
+        int pixel_x = (x-tx);
+        int pixel_y = (this.tilesize-1)-(y-ty);
+
+        //System.out.println("("+x+","+y+") maps to ("+pixel_x+","+pixel_y+") by ("+tx+", "+ty+")");
+
+        int rgb = image.getRGB(pixel_x,pixel_y);
+
+        int pixel;
+
+        int r = (rgb >> 16) & 0xff;
+        int g = (rgb >>  8) & 0xff;
+        int b = (rgb >>  0) & 0xff;
+
+        pixel = (int)((0.30 * r) + (0.59 * b) + (0.11 * g));
+
+        return pixel;
+    }
+
+    public int floor(int num, int precision){
+        double dnum = num/(double)precision;
+        BigDecimal val = new BigDecimal(dnum) ;
+        val = val.setScale(0, BigDecimal.ROUND_FLOOR);
+        return val.intValue()*precision;
+    }
+
+    public double floor(double num) {
+        BigDecimal val = new BigDecimal(num) ;
+        val = val.setScale(0, BigDecimal.ROUND_FLOOR);
+        return val.doubleValue() ;
+    }
+
+    public double[] xy_to_geo(int x, int y, double resolution){
+        double[] geo = new double[2];
+        geo[0] = y / resolution;
+        geo[1] = x / resolution;
+        return geo;
+    }
+
+    public int[] geo_to_xy(double lat, double lon, double resolution){
+        int[] xy = new int[2];
+
+        xy[0] = (int)Math.floor(lon * resolution + 0.5);
+        xy[1] = (int)Math.floor(lat * resolution + 0.5);
+
+        return xy;
+    }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringConfigurer.java
new file mode 100644
index 0000000..49e2bb6
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringConfigurer.java
@@ -0,0 +1,81 @@
+/*
+ * $Id: StringConfigurer.java 2073 2007-05-10 14:34:31 +0000 (Thu, 10 May 2007) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available 
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+/**
+ * A Configurer for String values
+ */
+public class StringConfigurer extends Configurer {
+  protected JPanel p;
+  protected JTextField nameField = new JTextField(12);
+
+  public StringConfigurer() {
+    this(null, "");
+  }
+  
+  public StringConfigurer(String key, String name) {
+    this(key, name, "");
+  }
+
+  public StringConfigurer(String key, String name, String val) {
+    super(key, name, val);
+  }
+
+  public String getValueString() {
+    return (String) value;
+  }
+
+  public void setValue(String s) {
+    if (!noUpdate && nameField != null) {
+      nameField.setText(s);
+    }
+    setValue((Object) s);
+  }
+
+  public void setToolTipText(String s) {
+    nameField.setToolTipText(s);
+  }
+  
+  public java.awt.Component getControls() {
+    if (p == null) {
+      p = new JPanel();
+      p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
+      p.add(new JLabel(getName()));
+      nameField.setMaximumSize
+        (new java.awt.Dimension(nameField.getMaximumSize().width,
+                                nameField.getPreferredSize().height));
+      nameField.setText(getValueString());
+      p.add(nameField);
+      nameField.addKeyListener(new java.awt.event.KeyAdapter() {
+        public void keyReleased(java.awt.event.KeyEvent evt) {
+          noUpdate = true;
+          setValue(nameField.getText());
+          noUpdate = false;
+        }
+      });
+    }
+    return p;
+  }
+
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringEnumConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringEnumConfigurer.java
new file mode 100644
index 0000000..b19acba
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringEnumConfigurer.java
@@ -0,0 +1,107 @@
+/*
+ * $Id: StringEnumConfigurer.java 2472 2007-10-01 04:10:19 +0000 (Mon, 01 Oct 2007) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+/*
+ * Created by IntelliJ IDEA.
+ * User: rkinney
+ * Date: Jul 20, 2002
+ * Time: 3:52:36 AM
+ * To change template for new class use
+ * Code Style | Class Templates options (Tools | IDE Options).
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.Box;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+
+/**
+ * A Configurer that returns a String from among a list of possible values
+ */
+public class StringEnumConfigurer extends Configurer {
+    protected String[] validValues;
+    protected String[] transValues;
+    protected JComboBox box;
+    protected Box panel;
+    protected String tooltipText = "";
+
+    public StringEnumConfigurer(String key, String name, String[] validValues) {
+        super(key, name);
+        this.validValues = validValues;
+        transValues = new String[validValues.length];
+        for(int i = 0; i < validValues.length; ++i)
+            transValues[i] = tr(validValues[i]);
+    }
+
+    public StringEnumConfigurer(String[] validValues) {
+        this(null, "", validValues);
+    }
+
+    public void setToolTipText(String s) {
+        tooltipText = s;
+    }
+    public Component getControls() {
+        if (panel == null) {
+            panel = Box.createHorizontalBox();
+            panel.add(new JLabel(name));
+            box = new JComboBox(transValues);
+            box.setToolTipText(tooltipText);
+            box.setMaximumSize(new Dimension(box.getMaximumSize().width,box.getPreferredSize().height));
+            setValue(value);
+            box.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    noUpdate = true;
+                    setValue(box.getSelectedIndex());
+                    noUpdate = false;
+                }
+            });
+            panel.add(box);
+        }
+        return panel;
+    }
+    public void setValue(Object o) {
+        if(o == null)
+            o = new Integer(0);
+        super.setValue(o);
+        if(!noUpdate && box != null)
+            box.setSelectedIndex((Integer)o);
+    }
+
+    public void setValue(String s) {
+        Integer n = 0;
+        for (int i = 0; i < transValues.length; ++i)
+        {
+            if (transValues[i].equals(s) || validValues[i].equals(s)){
+                n = i;
+                break;
+            }
+        }
+        setValue(n);
+    }
+
+    public String getValueString() {
+        return validValues[(Integer)value];
+    }
+}
diff --git a/livegps/.classpath b/livegps/.classpath
index 81a65e8..b5edc13 100644
--- a/livegps/.classpath
+++ b/livegps/.classpath
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0.15"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/livegps/build.xml b/livegps/build.xml
index 1f7acd1..3a36827 100644
--- a/livegps/build.xml
+++ b/livegps/build.xml
@@ -25,7 +25,7 @@
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
                 <attribute name="Plugin-Description" value="Support live GPS input (moving dot) through a connection to gpsd server."/>
                 <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/LiveGPS"/>
-                <attribute name="Plugin-Mainversion" value="1890"/>
+                <attribute name="Plugin-Mainversion" value="2450"/>
                 <attribute name="Plugin-Stage" value="50"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
diff --git a/livegps/src/livegps/LiveGpsLayer.java b/livegps/src/livegps/LiveGpsLayer.java
index a38c003..3d46b89 100644
--- a/livegps/src/livegps/LiveGpsLayer.java
+++ b/livegps/src/livegps/LiveGpsLayer.java
@@ -3,7 +3,7 @@ package livegps;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Color;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Point;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
@@ -13,13 +13,13 @@ import java.util.Collection;
 import java.util.Date;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.data.gpx.GpxTrack;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
-import org.openstreetmap.josm.tools.ColorHelper;
 
 public class LiveGpsLayer extends GpxLayer implements PropertyChangeListener {
     public static final String LAYER_NAME = tr("LiveGPS layer");
@@ -100,12 +100,12 @@ public class LiveGpsLayer extends GpxLayer implements PropertyChangeListener {
         autocenter = ac;
     }
 
-    @Override public void paint(Graphics g, MapView mv)
+    @Override public void paint(Graphics2D g, MapView mv, Bounds bounds)
     {
         //System.out.println("in paint");
         synchronized (LiveGpsLock.class) {
             //System.out.println("in synced paint");
-            super.paint(g, mv);
+            super.paint(g, mv, bounds);
 //          int statusHeight = 50;
 //          Rectangle mvs = mv.getBounds();
 //          mvs.y = mvs.y + mvs.height - statusHeight;
diff --git a/livegps/src/livegps/LiveGpsPlugin.java b/livegps/src/livegps/LiveGpsPlugin.java
index 5b29083..bad5b19 100644
--- a/livegps/src/livegps/LiveGpsPlugin.java
+++ b/livegps/src/livegps/LiveGpsPlugin.java
@@ -1,5 +1,6 @@
 package livegps;
 
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
@@ -97,7 +98,7 @@ public class LiveGpsPlugin extends Plugin implements LayerChangeListener
     public LiveGpsPlugin()
     {
         MainMenu menu = Main.main.menu;
-        lgpsmenu = menu.addMenu(marktr("LiveGPS"), KeyEvent.VK_G, menu.defaultMenuPos);
+        lgpsmenu = menu.addMenu(marktr("LiveGPS"), KeyEvent.VK_G, menu.defaultMenuPos, ht("/Plugin/LiveGPS"));
 
         JosmAction captureAction = new CaptureAction();
         lgpscapture = new JCheckBoxMenuItem(captureAction);
diff --git a/measurement/.classpath b/measurement/.classpath
index 05929d7..17b8e6a 100644
--- a/measurement/.classpath
+++ b/measurement/.classpath
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
 	<classpathentry kind="output" path="build"/>
 </classpath>
diff --git a/measurement/build.xml b/measurement/build.xml
index 8833eb3..527d1ec 100644
--- a/measurement/build.xml
+++ b/measurement/build.xml
@@ -24,7 +24,7 @@
                 <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.measurement.MeasurementPlugin"/>
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
                 <attribute name="Plugin-Description" value="Provide a measurement dialog and a layer to measure length and angle of segments, area surrounded by a (simple) closed way and create measurement paths (which also can be imported from a gps layer)."/>
-                <attribute name="Plugin-Mainversion" value="2012"/>
+                <attribute name="Plugin-Mainversion" value="2450"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java b/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java
index 34e8066..75602fd 100644
--- a/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java
+++ b/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java
@@ -4,7 +4,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Color;
 import java.awt.Component;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Point;
 import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
@@ -28,6 +28,7 @@ import javax.swing.JOptionPane;
 import javax.swing.JSeparator;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.gpx.GpxTrack;
 import org.openstreetmap.josm.data.gpx.WayPoint;
@@ -70,7 +71,7 @@ public class MeasurementLayer extends Layer {
 
     }
 
-    @Override public void paint(Graphics g, final MapView mv) {
+    @Override public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
         g.setColor(Color.green);
         Point l = null;
         for(WayPoint p:points){
diff --git a/measurement/.classpath b/routing/.classpath
similarity index 68%
copy from measurement/.classpath
copy to routing/.classpath
index 05929d7..beca5e1 100644
--- a/measurement/.classpath
+++ b/routing/.classpath
@@ -1,7 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+	<classpathentry kind="lib" path="lib/jgrapht-jdk1.5.jar"/>
+	<classpathentry kind="lib" path="lib/log4j-1.2.15.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
 	<classpathentry kind="output" path="build"/>
 </classpath>
diff --git a/routing/.project b/routing/.project
new file mode 100644
index 0000000..29a1851
--- /dev/null
+++ b/routing/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>JOSM-routing</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>
diff --git a/surveyor/build.xml b/routing/build.xml
similarity index 65%
copy from surveyor/build.xml
copy to routing/build.xml
index 18a9d5e..28e873a 100644
--- a/surveyor/build.xml
+++ b/routing/build.xml
@@ -1,29 +1,36 @@
-<project name="surveyor" default="dist" basedir=".">
+<project name="routing" default="dist" basedir=".">
+	<!-- Define some properties -->
     <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
     <property name="plugin.dist.dir"        value="../../dist"/>
     <property name="plugin.build.dir"       value="build"/>
     <property name="plugin.jar"             value="${plugin.dist.dir}/${ant.project.name}.jar"/>
-    <property name="livegpsplugin.jar"      value="${plugin.dist.dir}/livegps.jar"/>
+    <property name="jgrapht"                value="lib/jgrapht-jdk1.5.jar"/>
+    <property name="log4j"                  value="lib/log4j-1.2.15.jar"/>
     <property name="ant.build.javac.target" value="1.5"/>
+	<!-- Some initializations for several other targets -->
     <target name="init">
         <mkdir dir="${plugin.build.dir}"/>
     </target>
-    <target name="compile" depends="init">
+	<!-- Compile sources -->
+    <target name="compile" depends="init" description="Compile sources">
         <echo message="creating ${plugin.jar}"/>
         <javac srcdir="src" debug="true" destdir="${plugin.build.dir}">
             <compilerarg value="-Xlint:deprecation"/>
             <compilerarg value="-Xlint:unchecked"/>
             <classpath>
                 <pathelement location="${josm}"/>
-                <pathelement location="${livegpsplugin.jar}"/>
+                <pathelement location="${jgrapht}"/>
+                <pathelement location="${log4j}"/>
             </classpath>
         </javac>
     </target>
-    <target name="dist" depends="compile,revision">
+	<!-- Generate distribution -->
+    <target name="dist" depends="compile,revision" description="Generate distribution">
+        <unjar dest="${plugin.build.dir}" src="${jgrapht}"/>
+        <unjar dest="${plugin.build.dir}" src="${log4j}"/>
         <copy todir="${plugin.build.dir}/">
             <fileset dir="resources">
                 <include name="*.xml"/>
-                <include name="audio/*"/>
             </fileset>
         </copy>
         <copy todir="${plugin.build.dir}/images">
@@ -31,14 +38,13 @@
         </copy>
         <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
             <manifest>
-                <attribute name="Author" value="Christof Dallermassl"/>
-                <attribute name="Plugin-Class" value="at.dallermassl.josm.plugin.surveyor.SurveyorPlugin"/>
+                <attribute name="Author" value="Jose Vidal <vidalfree at gmail.com>, Juangui Jordán <juangui at gmail.com>"/>
+                <attribute name="Plugin-Class" value="com.innovant.josm.plugin.routing.RoutingPlugin"/>
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
-                <attribute name="Plugin-Description" value="Allow adding markers/nodes on current gps positions."/>
-                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Surveyor"/>
-                <attribute name="Plugin-Mainversion" value="2012"/>
-                <attribute name="Plugin-Requires" value="livegps"/>
-                <attribute name="Plugin-Stage" value="60"/>
+                <attribute name="Plugin-Description" value="Provides routing capabilities."/>
+                <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Routing"/>
+                <attribute name="Plugin-Mainversion" value="2450"/>
+                <attribute name="Plugin-Stage" value="50"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/routing/images/dialogs/routing.png b/routing/images/dialogs/routing.png
new file mode 100644
index 0000000..d817b85
Binary files /dev/null and b/routing/images/dialogs/routing.png differ
diff --git a/routing/images/layer/routing_small.png b/routing/images/layer/routing_small.png
new file mode 100644
index 0000000..f03698f
Binary files /dev/null and b/routing/images/layer/routing_small.png differ
diff --git a/routing/images/mapmode/add.png b/routing/images/mapmode/add.png
new file mode 100644
index 0000000..24d0c18
Binary files /dev/null and b/routing/images/mapmode/add.png differ
diff --git a/routing/images/mapmode/move.png b/routing/images/mapmode/move.png
new file mode 100644
index 0000000..21b873f
Binary files /dev/null and b/routing/images/mapmode/move.png differ
diff --git a/routing/images/mapmode/remove.png b/routing/images/mapmode/remove.png
new file mode 100644
index 0000000..1617590
Binary files /dev/null and b/routing/images/mapmode/remove.png differ
diff --git a/routing/images/mapmode/routing.png b/routing/images/mapmode/routing.png
new file mode 100644
index 0000000..d817b85
Binary files /dev/null and b/routing/images/mapmode/routing.png differ
diff --git a/routing/images/preferences/routing.png b/routing/images/preferences/routing.png
new file mode 100644
index 0000000..d817b85
Binary files /dev/null and b/routing/images/preferences/routing.png differ
diff --git a/routing/images/routing/endflag.png b/routing/images/routing/endflag.png
new file mode 100644
index 0000000..b3a715a
Binary files /dev/null and b/routing/images/routing/endflag.png differ
diff --git a/routing/images/routing/middleflag.png b/routing/images/routing/middleflag.png
new file mode 100644
index 0000000..9661b0e
Binary files /dev/null and b/routing/images/routing/middleflag.png differ
diff --git a/routing/images/routing/startflag.png b/routing/images/routing/startflag.png
new file mode 100644
index 0000000..fd743fc
Binary files /dev/null and b/routing/images/routing/startflag.png differ
diff --git a/routing/resources/log4j.xml b/routing/resources/log4j.xml
new file mode 100644
index 0000000..0921e1a
--- /dev/null
+++ b/routing/resources/log4j.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ 
+<!DOCTYPE log4j:configuration SYSTEM "dtds/org/apache/log4j/xml/log4j.dtd"> 
+    
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+   
+  <appender name="console" class="org.apache.log4j.ConsoleAppender">		
+	<layout class="org.apache.log4j.PatternLayout">
+	    <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%c] %-5p - %m%n" />
+	</layout>
+	</appender> 
+   
+   <logger name="com.innovant.josm.plugin.routing.RoutingModel">
+  		<level value="trace"/> 
+	</logger>
+   
+   
+   <root>   
+     <level value="debuger"></level>
+     <appender-ref ref="console"/> 
+   </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/routing/src/com/innovant/josm/jrt/core/EdgeIterator.java b/routing/src/com/innovant/josm/jrt/core/EdgeIterator.java
new file mode 100644
index 0000000..c46f127
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/EdgeIterator.java
@@ -0,0 +1,9 @@
+package com.innovant.josm.jrt.core;
+
+public interface EdgeIterator {
+
+    public boolean hasNext();
+    
+    public RoutingEdge next();
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java b/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java
new file mode 100644
index 0000000..dcdeeec
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+package com.innovant.josm.jrt.core;
+
+public enum PreferencesKeys {
+    KEY_ACTIVE_ROUTE_COLOR ("routing.active.route.color"),
+    KEY_INACTIVE_ROUTE_COLOR ("routing.inactive.route.color"),
+    KEY_ROUTE_WIDTH ("routing.route.width"),
+    KEY_ROUTE_SELECT ("routing.route.select");
+
+    public final String key;
+    PreferencesKeys (String key) {
+        this.key=key;
+    }
+
+    public String getKey() {return key;};
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingEdge.java b/routing/src/com/innovant/josm/jrt/core/RoutingEdge.java
new file mode 100644
index 0000000..af36d81
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingEdge.java
@@ -0,0 +1,27 @@
+package com.innovant.josm.jrt.core;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+public interface RoutingEdge {
+
+      public LatLon fromLatLon();
+
+      public LatLon toLatLon();
+      
+      public Object fromV();
+
+      public Object toV();
+
+      public double getLength();
+      
+      public void setLength(double length);
+      
+      public double getSpeed();
+
+      public void setSpeed(double speed);
+      
+      public boolean isOneway();
+      
+      public void setOneway(boolean isOneway);
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingGraph.java b/routing/src/com/innovant/josm/jrt/core/RoutingGraph.java
new file mode 100644
index 0000000..ef5b316
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingGraph.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.jrt.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.jgrapht.Graph;
+import org.jgrapht.alg.BellmanFordShortestPath;
+import org.jgrapht.alg.DijkstraShortestPath;
+import org.jgrapht.graph.AsUndirectedGraph;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+/**
+ * Class utility to work with graph routers.
+ *
+ * @author Juangui
+ * @author Jose Vidal
+ */
+public class RoutingGraph {
+
+    /**
+     * Routing Profile
+     */
+    private RoutingProfile routingProfile;
+
+    /**
+     * Diferent algorithms to apply to the graph.
+     */
+    public enum Algorithm {
+        ROUTING_ALG_DIJKSTRA, ROUTING_ALG_BELLMANFORD
+    };
+
+    /**
+     * Search criteria for the route.
+     */
+    public enum RouteType {FASTEST,SHORTEST};
+
+    /**
+     *
+     */
+    private RouteType routeType;
+
+    /**
+     * Associated Osm DataSet
+     */
+    private DataSet data;
+
+    /**
+     * Logger.
+     */
+    static Logger logger = Logger.getLogger(RoutingGraph.class);
+
+    /**
+     * Graph state
+     * <code>true</code> Graph in memory.
+     * <code>false</code> Graph not created.
+     */
+//  public boolean graphState;
+
+    /**
+     * OSM Graph.
+     */
+//  private DirectedWeightedMultigraph<Node, OsmEdge> graph;
+//  private WeightedMultigraph<Node, OsmEdge> graph;
+    private Graph<Node, OsmEdge> graph;
+    private RoutingGraphDelegator rgDelegator=null;
+
+    /**
+     * Speeds
+     */
+    private Map<String,Double> waySpeeds;
+
+    /**
+     * Default Constructor.
+     */
+    public RoutingGraph(DataSet data) {
+//      this.graphState = false;
+        this.graph = null;
+        this.data = data;
+        routeType=RouteType.SHORTEST;
+        routingProfile=new RoutingProfile("default");
+        routingProfile.setOnewayUse(true); // Don't ignore oneways by default
+        this.setWaySpeeds(routingProfile.getWaySpeeds());
+        logger.debug("Created RoutingGraph");
+    }
+
+    /**
+     * Create OSM graph for routing
+     *
+     * @return
+     */
+    public void createGraph() {
+
+        logger.debug("Creating Graph...");
+        graph = new DirectedWeightedMultigraph<Node, OsmEdge>(OsmEdge.class);
+        rgDelegator=new RoutingGraphDelegator(graph);
+        rgDelegator.setRouteType(this.routeType);
+        // iterate all ways and segments for all nodes:
+        for (Way way : data.getWays()) {
+            if (way != null && !way.isDeleted() && this.isvalidWay(way)) {
+                Node from = null;
+                for (Node to : way.getNodes()) {
+                    // Ignore the node if deleted
+                    if (!to.isDeleted()) {
+                        graph.addVertex(to);
+                        if (from != null) {
+                            addEdge(way, from, to);
+                            if (!isOneWay(way)){
+                                addEdge(way, to, from);}
+                        }
+                        from = to;
+                    }
+                }
+            }
+        }
+//      graph.vertexSet().size();
+        logger.debug("End Create Graph");
+        logger.debug("Vertex: "+graph.vertexSet().size());
+        logger.debug("Edges: "+graph.edgeSet().size());
+    }
+
+    /**
+     * Compute weight and add edge to the graph
+     * @param way
+     * @param from
+     * @param to
+     */
+    private void addEdge(Way way,Node from, Node to) {
+        double length = from.getCoor().greatCircleDistance(to.getCoor());
+
+        OsmEdge edge = new OsmEdge(way, from, to);
+        edge.setSpeed(12.1);
+        graph.addEdge(from, to, edge);
+        // weight = getWeight(way);
+        double weight = getWeight(way, length);
+        setWeight(edge, length);
+        logger.debug("edge for way " + way.getId()
+                     + "(from node " + from.getId() + " to node "
+                     + to.getId() + ") has weight: " + weight);
+        //((GraphDelegator<Node,OsmEdge>) graph).setEdgeWeight(edge, weight);
+        ((DirectedWeightedMultigraph<Node,OsmEdge>)graph).setEdgeWeight(edge, weight);
+    }
+
+    /**
+     * Set the weight for the given segment depending on the highway type
+     * and the length of the segment. The higher the value, the less it is used
+     * in routing.
+     *
+     * @param way
+     *            the way.
+     * @return
+     */
+    private void setWeight(OsmEdge osmedge, double length) {
+
+        osmedge.setLength(length);
+        if (this.waySpeeds.containsKey(osmedge.getWay().get("highway")))
+            osmedge.setSpeed(this.waySpeeds.get(osmedge.getWay().get("highway")));
+
+    }
+
+    /**
+     * Returns the weight for the given segment depending on the highway type
+     * and the length of the segment. The higher the value, the less it is used
+     * in routing.
+     *
+     * @param way
+     *            the way.
+     * @return
+     */
+    private double getWeight(Way way, double length) {
+        // Default speed if no setting is found
+        double speed = 1;
+
+        switch (routeType) {
+        case SHORTEST:
+            // Same speed for all types of ways
+            if (this.waySpeeds.containsKey("residential"))
+                speed=this.waySpeeds.get("residential");
+            break;
+        case FASTEST:
+            // Each type of way may have a different speed
+            if (this.waySpeeds.containsKey(way.get("highway")))
+                speed=this.waySpeeds.get(way.get("highway"));
+            logger.debug("Speed="+speed);
+            break;
+        default:
+            break;
+        }
+        // Return the time spent to traverse the way
+        return length / speed;
+    }
+
+    /**
+     * Check is One Way.
+     *
+     * @param way
+     *            the way.
+     * @return <code>true</code> is a one way. <code>false</code> is not a one
+     *         way.
+     */
+    private boolean isOneWay(Way way) {
+        // FIXME: oneway=-1 is ignored for the moment!
+        return way.get("oneway") != null
+                || "motorway".equals(way.get("highway"));
+    }
+
+    /**
+     * Check if a Way is correct.
+     *
+     * @param way
+     *            The way.
+     * @return <code>true</code> is valid. <code>false</code> is not valid.
+     */
+    public boolean isvalidWay(Way way) {
+        if (!way.isTagged())
+            return false;
+
+        return way.get("highway") != null || way.get("junction") != null
+                || way.get("service") != null;
+
+    }
+
+    public boolean isvalidNode(Node node) {
+        return true;
+    }
+
+    /**
+     * Apply selected routing algorithm to the graph.
+     *
+     * @param nodes
+     *            Nodes used to calculate path.
+     * @param algorithm
+     *            Algorithm used to compute the path,
+     *            RoutingGraph.Algorithm.ROUTING_ALG_DIJKSTRA or
+     *            RoutingGraph.Algorithm.ROUTING_ALG_BELLMANFORD
+     * @return new path.
+     */
+    public List<OsmEdge> applyAlgorithm(List<Node> nodes, Algorithm algorithm) {
+        List<OsmEdge> path = new ArrayList<OsmEdge>();
+        Graph<Node,OsmEdge> g;
+        double totalWeight = 0;
+
+        if (graph == null)
+            this.createGraph();
+        logger.debug("apply algorithm between nodes ");
+
+        for (Node node : nodes) {
+            logger.debug(node.getId());
+        }
+        logger.debug("-----------------------------------");
+
+        // Assign the graph or an undirected view of the graph to g,
+        // depending on whether oneway tags are used or not
+        if (routingProfile.isOnewayUsed())
+            g = graph;
+        else
+            g = new AsUndirectedGraph<Node, OsmEdge>((DirectedWeightedMultigraph<Node,OsmEdge>)graph);
+        //TODO: Problemas no tiene encuenta el tema de oneway.
+        switch (algorithm) {
+        case ROUTING_ALG_DIJKSTRA:
+            logger.debug("Using Dijkstra algorithm");
+            DijkstraShortestPath<Node, OsmEdge> routingk = null;
+            for (int index = 1; index < nodes.size(); ++index) {
+                routingk = new DijkstraShortestPath<Node, OsmEdge>(rgDelegator, nodes
+                        .get(index - 1), nodes.get(index));
+                if (routingk.getPathEdgeList() == null) {
+                    logger.debug("no path found!");
+                    break;
+                }
+                path.addAll(routingk.getPathEdgeList());
+                totalWeight += routingk.getPathLength();
+            }
+            break;
+        case ROUTING_ALG_BELLMANFORD:
+            logger.debug("Using Bellman Ford algorithm");
+            for (int index = 1; index < nodes.size(); ++index) {
+                path = BellmanFordShortestPath.findPathBetween(rgDelegator, nodes
+                        .get(index - 1), nodes.get(index));
+                if (path == null) {
+                    logger.debug("no path found!");
+                    return null;
+                }
+            }
+            break;
+        default:
+            logger.debug("Wrong algorithm");
+            break;
+        }
+
+        logger.debug("shortest path found: " + path + "\nweight: "
+                        + totalWeight);
+        return path;
+    }
+
+    /**
+     * Return the number of vertices.
+     * @return the number of vertices.
+     */
+    public int getVertexCount(){
+        int value=0;
+        if (graph!=null) value=graph.vertexSet().size();
+        return value;
+    }
+
+    /**
+     * Return the number of edges.
+     * @return the number of edges.
+     */
+    public int getEdgeCount(){
+        int value=0;
+        if (graph!=null) value=graph.edgeSet().size();
+        return value;
+    }
+
+    /**
+     * @param routeType the routeType to set
+     */
+    public void setTypeRoute(RouteType routetype) {
+        this.routeType = routetype;
+        this.rgDelegator.setRouteType(routetype);
+    }
+
+    /**
+     * @return the routeType
+     */
+    public RouteType getTypeRoute() {
+        return routeType;
+    }
+
+    public Map<String, Double> getWaySpeeds() {
+        return waySpeeds;
+    }
+
+    public void setWaySpeeds(Map<String, Double> waySpeeds) {
+        this.waySpeeds = waySpeeds;
+    }
+
+    public void resetGraph() {
+        graph=null;
+    }
+
+    public RoutingProfile getRoutingProfile() {
+        return routingProfile;
+    }
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingGraphDelegator.java b/routing/src/com/innovant/josm/jrt/core/RoutingGraphDelegator.java
new file mode 100644
index 0000000..bc3c40c
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingGraphDelegator.java
@@ -0,0 +1,60 @@
+/**
+ * 
+ */
+package com.innovant.josm.jrt.core;
+
+import org.apache.log4j.Logger;
+import org.jgrapht.Graph;
+import org.jgrapht.graph.GraphDelegator;
+import org.openstreetmap.josm.data.osm.Node;
+
+import com.innovant.josm.jrt.core.RoutingGraphDelegator;
+import com.innovant.josm.jrt.core.RoutingGraph.RouteType;
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+/**
+ * @author jose
+ *
+ */
+public class RoutingGraphDelegator extends GraphDelegator<Node, OsmEdge> {
+
+    /**
+     * Logger.
+     */
+    static Logger logger = Logger.getLogger(RoutingGraphDelegator.class);
+    
+    /**
+     *
+     */
+    private RouteType routeType;
+    
+    public RoutingGraphDelegator(Graph<Node, OsmEdge> arg0) {
+        super(arg0);
+    }
+    
+
+    public RouteType getRouteType() {
+        return routeType;
+    }
+
+    public void setRouteType(RouteType routeType) {
+        this.routeType = routeType;
+    }
+
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public double getEdgeWeight(OsmEdge edge) {
+        double weight=Double.MAX_VALUE;
+        
+        if (routeType==RouteType.SHORTEST) weight=edge.getLength();
+        if (routeType==RouteType.FASTEST) weight=edge.getLength() / edge.getSpeed();
+        // Return the time spent to traverse the way
+        return weight;
+    }
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingProfile.java b/routing/src/com/innovant/josm/jrt/core/RoutingProfile.java
new file mode 100644
index 0000000..da8be74
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingProfile.java
@@ -0,0 +1,161 @@
+package com.innovant.josm.jrt.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+
+
+/**
+ * This class holds information about a routing profile.
+ *
+ * A routing profile specifies the type of vehicle that will go through the route
+ * and the conditions with respect to the traversal of different types of edges
+ *
+ * For instance, a pedestrian can traverse streets in both directions, walk through
+ * pedestrian ways and almost all types of ways except motorways, climb steps, and
+ * can ignore turn restrictions, while a handicapped person would have the same profile
+ * except for climbing steps. A car can drive at the maximum allowed speed of the way,
+ * and can not use cycleways nor pedestrian ways, while a bicycle can, but its maximum
+ * speed for any type of way would be around 50km/h.
+ *
+ * When combined with public transit data, information of which types of transport modes
+ * are allowed for the vehicle can be stored in the profile. For instance, bicycles are
+ * usually allowed to travel on board of trains, trams and subways.
+ *
+ * @author juangui
+ *
+ */
+public class RoutingProfile {
+    /**
+     * logger
+     */
+    static Logger logger = Logger.getLogger(RoutingProfile.class);
+    /**
+     * True if oneway is used for routing (i.e. for cars).
+     */
+    private boolean useOneway;
+
+    /**
+     * True if turn restrictions are used for routing (i.e. for cars).
+     */
+    private boolean useRestrictions;
+
+    /**
+     * True if maximum allowed speed of ways is considered for routing (i.e. for cars).
+     */
+    private boolean useMaxAllowedSpeed;
+
+    /**
+     * Name of the routing profile, for identification issues (i.e. "pedestrian").
+     */
+    private String name;
+
+    /**
+     * Holds traverse speed for each type of way, using the type as key.
+     * A speed of zero means that this type of way cannot be traversed.
+     */
+    private Map<String,Double> waySpeeds;
+
+
+
+    /**
+     * Holds permission of use for each type of transport mode, using the mode as key.
+     */
+    private Map<String,Boolean> allowedModes;
+
+    /**
+     * Constructor
+     * @param name The name for the routing profile. Please use a name that is
+     * self descriptive, i.e., something that an application user would
+     * understand (like "pedestrian", "motorbike", "bicycle", etc.)
+     */
+    public RoutingProfile(String name) {
+        logger.debug("Init RoutingProfile with name: "+name);
+        this.name = name;
+        waySpeeds=new HashMap<String,Double>();
+        Map<String,String> prefs=Main.pref.getAllPrefix("routing.profile."+name+".speed");
+        for(String key:prefs.keySet()){
+            waySpeeds.put((key.split("\\.")[4]), Double.valueOf(prefs.get(key)));
+        }
+        for (String key:waySpeeds.keySet())
+            logger.debug(key+ "-- speed: "+waySpeeds.get(key));
+        logger.debug("End init RoutingProfile with name: "+name);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setOnewayUse(boolean useOneway) {
+        this.useOneway = useOneway;
+    }
+
+    public boolean isOnewayUsed() {
+        return useOneway;
+    }
+
+    public void setRestrictionsUse(boolean useRestrictions) {
+        this.useRestrictions = useRestrictions;
+    }
+
+    public boolean isRestrictionsUsed() {
+        return useRestrictions;
+    }
+
+    public void setMaxAllowedSpeedUse(boolean useMaxAllowedSpeed) {
+        this.useMaxAllowedSpeed = useMaxAllowedSpeed;
+    }
+
+    public boolean isMaxAllowedSpeedUsed() {
+        return useMaxAllowedSpeed;
+    }
+
+    public void setWayTypeSpeed(String type, double speed) {
+        waySpeeds.put(type, speed);
+    }
+
+    public void setTransportModePermission(String mode, boolean permission) {
+        allowedModes.put(mode, permission);
+    }
+
+    /**
+     * Return whether the driving profile specifies that a particular type of way
+     * can be traversed
+     * @param type Key for the way type
+     * @return True if the way type can be traversed
+     */
+    public boolean isWayTypeAllowed(String type) {
+        if (waySpeeds.get(type) != 0.0)
+            return true;
+        return false;
+    }
+
+    /**
+     * Return whether the driving profile specifies that a particular type of transport
+     * mode can be used
+     * @param mode Key for the way type
+     * @return True if the way type can be traversed
+     */
+    public boolean isTransportModeAllowed(String mode) {
+        return allowedModes.get(mode);
+    }
+
+    public double getSpeed(String key){
+        if(!waySpeeds.containsKey(key)) return 0.0;
+        return waySpeeds.get(key);
+    }
+
+    public Map<String, Double> getWaySpeeds() {
+        return waySpeeds;
+    }
+
+    public void setWaySpeeds(Map<String, Double> waySpeeds) {
+        this.waySpeeds = waySpeeds;
+    }
+}
diff --git a/routing/src/com/innovant/josm/jrt/gtfs/GTFSTransportModes.java b/routing/src/com/innovant/josm/jrt/gtfs/GTFSTransportModes.java
new file mode 100644
index 0000000..b9c5581
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/gtfs/GTFSTransportModes.java
@@ -0,0 +1,57 @@
+package com.innovant.josm.jrt.gtfs;
+
+/**
+ * Constants for parsing GTFS and to use in Routing Profiles
+ * @author juangui
+ * TODO Using integers is suitable to parse gtfs feeds but
+ * Routing Profile keys should be Strings
+ */
+public class GTFSTransportModes {
+
+    /**
+     * 0 - Tram, Streetcar, Light rail. Any light rail or street level system within
+     *     a metropolitan area.
+     */
+    public static final int TRAM = 0;
+    public static final int STREETCAR = 0;
+    public static final int LIGHT_RAIL = 0;
+
+    /**
+     * 1 - Subway, Metro. Any underground rail system within a metropolitan area.
+     */
+    public static final int SUBWAY = 1;
+    public static final int METRO = 1;
+
+    /**
+     * 2 - Rail. Used for intercity or long-distance travel.
+     */
+    public static final int RAIL = 2;
+
+    /**
+     * 3 - Bus. Used for short- and long-distance bus routes.
+     */
+    public static final int BUS = 3;
+
+    /**
+     * 4 - Ferry. Used for short- and long-distance boat service.
+     */
+    public static final int FERRY = 4;
+
+    /**
+     * 5 - Cable car. Used for street-level cable cars where the cable runs beneath the car.
+     */
+    public static final int CABLE_CAR = 5;
+
+    /**
+     * 6 - Gondola, Suspended cable car. Typically used for aerial cable cars where
+     *     the car is suspended from the cable.
+     */
+    public static final int GONDOLA = 6;
+    public static final int SUSPENDED_CABLE_CAR = 6;
+
+    /**
+     * 7 - Funicular. Any rail system designed for steep inclines.
+     */
+    public static final int FUNICULAR = 7;
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/osm/OsmEdge.java b/routing/src/com/innovant/josm/jrt/osm/OsmEdge.java
new file mode 100644
index 0000000..fbf531e
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/osm/OsmEdge.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+package com.innovant.josm.jrt.osm;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+
+/**
+ * Class that represents an edge of the graph.
+ * @author jose
+ */
+public class OsmEdge extends DefaultWeightedEdge {
+ /**
+  * Serial
+  */
+  private static final long serialVersionUID = 1L;
+  /**
+   * Way associated
+   */
+  private Way way;
+  /**
+   * Nodes in the edge
+   */
+  private Node from, to;
+  /**
+   * Length edge
+   */
+  private double length;
+  /**
+   * Speed edge.
+   */
+  private double speed;
+
+
+/**
+   * Constructor
+   * @param way
+   * @param length
+   */
+  public OsmEdge(Way way, Node from, Node to) {
+        super();
+        this.way = way;
+        this.from = from;
+        this.to = to;
+        this.length = from.getCoor().greatCircleDistance(to.getCoor());
+      }
+
+  /**
+   * @return the way
+   */
+  public Way getWay() {
+      return this.way;
+  }
+
+  public EastNorth fromEastNorth() {
+      return this.from.getEastNorth();
+  }
+
+  public EastNorth toEastNorth() {
+      return this.to.getEastNorth();
+  }
+
+  /**
+   * Returns length of segment in meters
+   * @return length of segment in meters.
+   */
+  public double getLength() {
+    return length;
+  }
+  
+  public void setLength(double length) {
+    this.length = length;
+}
+
+public double getSpeed() {
+        return speed;
+  }
+
+  public void setSpeed(double speed) {
+        this.speed = speed;
+  }
+}
diff --git a/routing/src/com/innovant/josm/jrt/osm/OsmWayTypes.java b/routing/src/com/innovant/josm/jrt/osm/OsmWayTypes.java
new file mode 100644
index 0000000..0a110ce
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/osm/OsmWayTypes.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.jrt.osm;
+
+/**
+ * @author jvidal
+ *
+ */
+public enum OsmWayTypes {
+    MOTORWAY ("motorway",120),
+    MOTORWAY_LINK ("motorway_link",120),
+    TRUNK ("trunk",120),
+    TRUNK_LINK ("trunk_link",120),
+    PRIMARY  ("primary",100),
+    PRIMARY_LINK ("primary_link",100),
+    SECONDARY ("secondary",90),
+    TERTIARY ("tertiary",90),
+    UNCLASSIFIED ("unclassified",50),
+    ROAD ("road",100),
+    RESIDENTIAL ("residential",50),
+    LIVING_STREET ("living_street",30),
+    SERVICE ("service",30),
+    TRACK ("track",50),
+    PEDESTRIAN ("pedestrian",30),
+    BUS_GUIDEWAY ("bus_guideway",50),
+    PATH ("path",40),
+    CYCLEWAY ("cycleway",40),
+    FOOTWAY ("footway",20),
+    BRIDLEWAY ("bridleway",40),
+    BYWAY ("byway",50),
+    STEPS ("steps",10);
+
+    /**
+     * Default Constructor
+     * @param tag
+     */
+    OsmWayTypes(String tag,int speed) {
+        this.tag = tag;
+        this.speed = speed;
+    }
+
+    /**
+     * Tag
+     */
+    private final String tag;
+    private final int speed;
+
+    /**
+     * @return
+     */
+    public String getTag() {return tag;};
+    public int getSpeed() {return speed;};
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java b/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
new file mode 100644
index 0000000..a53c3d7
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing;
+
+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.Stroke;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.Icon;
+import javax.swing.JMenuItem;
+import javax.swing.JSeparator;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+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.OsmDataLayer;
+import org.openstreetmap.josm.tools.ColorHelper;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.jrt.core.PreferencesKeys;
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+
+/**
+ * A JOSM layer that encapsulates the representation of the shortest path.
+ * @author juangui
+ * @author Jose Vidal
+ */
+public class RoutingLayer extends Layer {
+
+    /**
+     * Logger
+     */
+    static Logger logger = Logger.getLogger(RoutingLayer.class);
+
+    /**
+     * Constant
+     */
+    private static final double ARROW_PHI = Math.toRadians(20);
+
+    /**
+     * Routing Model
+     */
+    private RoutingModel routingModel;
+
+    /**
+     * Start, Middle and End icons
+     */
+    private Icon startIcon,middleIcon,endIcon;
+
+    /**
+     * Associated OSM layer
+     */
+    private OsmDataLayer dataLayer;
+
+    /**
+     * Default constructor
+     * @param name Layer name.
+     */
+    public RoutingLayer(String name, OsmDataLayer dataLayer) {
+        super(name);
+        logger.debug("Creating Routing Layer...");
+        if(startIcon == null) startIcon = ImageProvider.get("routing", "startflag");
+        if(middleIcon == null) middleIcon = ImageProvider.get("routing", "middleflag");
+        if(endIcon == null) endIcon = ImageProvider.get("routing", "endflag");
+        this.dataLayer = dataLayer;
+        this.routingModel = new RoutingModel(dataLayer.data);
+        logger.debug("Routing Layer created.");
+    }
+
+    /**
+     * Getter Routing Model.
+     * @return the routingModel
+     */
+    public RoutingModel getRoutingModel() {
+        return this.routingModel;
+    }
+
+    /**
+     * Gets associated data layer
+     * @return OsmDataLayer associated to the RoutingLayer
+     */
+    public OsmDataLayer getDataLayer() {
+        return dataLayer;
+    }
+
+    /**
+     * Gets nearest node belonging to a highway tagged way
+     * @param p Point on the screen
+     * @return The nearest highway node, in the range of the snap distance
+     */
+    public final Node getNearestHighwayNode(Point p) {
+        Node nearest = null;
+        double minDist = 0;
+        for (Way w : dataLayer.data.getWays()) {
+            if (w.isDeleted() || w.incomplete || w.get("highway")==null) continue;
+            for (Node n : w.getNodes()) {
+                if (n.isDeleted() || n.incomplete) continue;
+
+                Point P = Main.map.mapView.getPoint(n);
+                double dist = p.distanceSq(P);
+                if (dist < NavigatableComponent.snapDistance) {
+                    if ((nearest == null) || (dist < minDist)) {
+                        nearest = n;
+                        minDist = dist;
+                    }
+                }
+            }
+        }
+        return nearest;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#getIcon()
+     */
+    @Override
+    public Icon getIcon() {
+        Icon icon = ImageProvider.get("layer", "routing_small");
+        return icon;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#getInfoComponent()
+     */
+    @Override
+    public Object getInfoComponent() {
+        String info = "<html>"
+                        + "<body>"
+                            +"Graph Vertex: "+this.routingModel.routingGraph.getVertexCount()+"<br/>"
+                            +"Graph Edges: "+this.routingModel.routingGraph.getEdgeCount()+"<br/>"
+                        + "</body>"
+                    + "</html>";
+        return info;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#getMenuEntries()
+     */
+    @Override
+    public Component[] getMenuEntries() {
+        Collection<Component> components = new ArrayList<Component>();
+        components.add(new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)));
+//        components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)));
+        components.add(new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)));
+        components.add(new JSeparator());
+        components.add(new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)));
+        components.add(new JSeparator());
+        components.add(new JMenuItem(new LayerListPopup.InfoAction(this)));
+        return components.toArray(new Component[0]);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#getToolTipText()
+     */
+    @Override
+    public String getToolTipText() {
+        String tooltip = this.routingModel.routingGraph.getVertexCount() + " vertices, "
+                + this.routingModel.routingGraph.getEdgeCount() + " edges";
+        return tooltip;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#isMergable(org.openstreetmap.josm.gui.layer.Layer)
+     */
+    @Override
+    public boolean isMergable(Layer other) {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#mergeFrom(org.openstreetmap.josm.gui.layer.Layer)
+     */
+    @Override
+    public void mergeFrom(Layer from) {
+        // This layer is not mergable, so do nothing
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#paint(java.awt.Graphics, org.openstreetmap.josm.gui.MapView)
+     */
+    @Override
+    public void paint(Graphics2D g, MapView mv, Bounds bounds) {
+        boolean isActiveLayer = (mv.getActiveLayer().equals(this));
+        // Get routing nodes (start, middle, end)
+        List<Node> nodes = routingModel.getSelectedNodes();
+        if(nodes == null || nodes.size() == 0) {
+            logger.debug("no nodes selected");
+            return;
+        }
+
+        // Get path stroke color from preferences
+        // Color is different for active and inactive layers
+        String colorString;
+        if (isActiveLayer) {
+            if (Main.pref.hasKey(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key))
+                    colorString = Main.pref.get(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key);
+            else {
+                colorString = ColorHelper.color2html(Color.RED);
+                Main.pref.put(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key, colorString);
+            }
+        } else {
+            if (Main.pref.hasKey(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key))
+                colorString = Main.pref.get(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key);
+            else {
+                colorString = ColorHelper.color2html(Color.decode("#dd2222"));
+                Main.pref.put(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key, colorString);
+            }
+        }
+        Color color = ColorHelper.html2color(colorString);
+
+        // Get path stroke width from preferences
+        String widthString = Main.pref.get(PreferencesKeys.KEY_ROUTE_WIDTH.key);
+        if (widthString.length() == 0) {
+            widthString = "8";
+            // FIXME add after good width is found: Main.pref.put(KEY_ROUTE_WIDTH, widthString);
+        }
+        int width = Integer.parseInt(widthString);
+
+        // Paint routing path
+        List<OsmEdge> routeEdges = routingModel.getRouteEdges();
+        if(routeEdges != null) {
+            for(OsmEdge edge : routeEdges) {
+                drawEdge(g, mv, edge, color, width, true);
+            }
+        }
+
+        // paint start icon
+        Node node = nodes.get(0);
+        Point screen = mv.getPoint(node);
+        startIcon.paintIcon(mv, g, screen.x - startIcon.getIconWidth()/2,
+                screen.y - startIcon.getIconHeight());
+
+        // paint middle icons
+        for(int index = 1; index < nodes.size() - 1; ++index) {
+            node = nodes.get(index);
+            screen = mv.getPoint(node);
+            middleIcon.paintIcon(mv, g, screen.x - startIcon.getIconWidth()/2,
+                    screen.y - middleIcon.getIconHeight());
+        }
+        // paint end icon
+        if(nodes.size() > 1) {
+            node = nodes.get(nodes.size() - 1);
+            screen = mv.getPoint(node);
+            endIcon.paintIcon(mv, g, screen.x - startIcon.getIconWidth()/2,
+                    screen.y - endIcon.getIconHeight());
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#visitBoundingBox(org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor)
+     */
+    @Override
+    public void visitBoundingBox(BoundingXYVisitor v) {
+        for (Node node : routingModel.getSelectedNodes()) {
+            v.visit(node);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer#destroy()
+     */
+    @Override
+    public void destroy() {
+        routingModel.reset();
+//      layerAdded = false;
+    }
+
+    /**
+     * Draw a line with the given color.
+     */
+    private void drawEdge(Graphics g, MapView mv, OsmEdge edge, Color col, int width,
+            boolean showDirection) {
+        g.setColor(col);
+        Point from;
+        Point to;
+        from = mv.getPoint(edge.fromEastNorth());
+        to = mv.getPoint(edge.toEastNorth());
+
+            Graphics2D g2d = (Graphics2D)g;
+            Stroke oldStroke = g2d.getStroke();
+            g2d.setStroke(new BasicStroke(width)); // thickness
+            g.drawLine(from.x, from.y, to.x, to.y);
+            if (showDirection) {
+                double t = Math.atan2(to.y-from.y, to.x-from.x) + Math.PI;
+                g.drawLine(to.x,to.y, (int)(to.x + 10*Math.cos(t-ARROW_PHI)), (int)(to.y + 10*Math.sin(t-ARROW_PHI)));
+                g.drawLine(to.x,to.y, (int)(to.x + 10*Math.cos(t+ARROW_PHI)), (int)(to.y + 10*Math.sin(t+ARROW_PHI)));
+            }
+            g2d.setStroke(oldStroke);
+    }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/RoutingModel.java b/routing/src/com/innovant/josm/plugin/routing/RoutingModel.java
new file mode 100644
index 0000000..553721c
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/RoutingModel.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+
+import com.innovant.josm.jrt.core.RoutingGraph;
+import com.innovant.josm.jrt.core.RoutingGraph.Algorithm;
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+
+/**
+ * This class holds all the routing data and operations
+ * @author juangui
+ * @author Jose Vidal
+ *
+ */
+public class RoutingModel {
+
+    /**
+     * Logger
+     */
+    static Logger logger = Logger.getLogger(RoutingModel.class);
+
+    /**
+     * Graph to calculate route
+     */
+    public RoutingGraph routingGraph=null;
+
+    /**
+     * List of nodes that the route has to traverse
+     */
+    private List<Node> nodes=null;
+
+    private List<OsmEdge> path=null;
+    /**
+     * Flag to advise about changes in the selected nodes.
+     */
+    private boolean changeNodes=false;
+    /**
+     * Default Constructor.
+     */
+    public RoutingModel(DataSet data) {
+        nodes = new ArrayList<Node>();
+System.out.println("gr " + data);
+        routingGraph = new RoutingGraph(data);
+    }
+
+    /**
+     * Method that returns the selected nodes to calculate route.
+     * @return the selectedNodes
+     */
+    public List<Node> getSelectedNodes() {
+        return nodes;
+    }
+
+    /**
+     * Adds a node to the route node list.
+     * @param node the node to add.
+     */
+    public void addNode(Node node) {
+        nodes.add(node);
+        this.changeNodes=true;
+    }
+
+    /**
+     * Removes a node from the route node list.
+     * @param index the index of the node to remove.
+     */
+    public void removeNode(int index) {
+        if (nodes.size()>index) {
+            nodes.remove(index);
+            this.changeNodes=true;
+        }
+    }
+
+    /**
+     * Inserts a node in the route node list.
+     * @param index the index where the node will be inserted
+     * @param node the node to be inserted
+     */
+    public void insertNode(int index, Node node) {
+        if (nodes.size()>=index) {
+            nodes.add(index, node);
+            this.changeNodes=true;
+        }
+    }
+
+    /**
+     * Reverse list of nodes
+     */
+    public void reverseNodes() {
+        List<Node> aux = new ArrayList<Node>();
+        for (Node n : nodes) {
+            aux.add(0,n);
+        }
+        nodes = aux;
+        this.changeNodes=true;
+    }
+
+    /**
+     * Get the edges of the route.
+     * @return A list of edges forming the shortest path
+     */
+    public List<OsmEdge> getRouteEdges() {
+        if (this.changeNodes || path==null)
+        {
+            path=this.routingGraph.applyAlgorithm(nodes, Algorithm.ROUTING_ALG_DIJKSTRA);
+            this.changeNodes=false;
+        }
+        return path;
+    }
+
+    /**
+     * Marks that some node or the node order has changed so the path should be computed again
+     */
+    public void setNodesChanged() {
+        this.changeNodes = true;
+    }
+
+    /**
+     * Resets all data.
+     */
+    public void reset() {
+        nodes.clear();
+        this.changeNodes=true;
+    }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/RoutingPlugin.java b/routing/src/com/innovant/josm/plugin/routing/RoutingPlugin.java
new file mode 100644
index 0000000..d91443b
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/RoutingPlugin.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+
+import com.innovant.josm.plugin.routing.actions.AddRouteNodeAction;
+import com.innovant.josm.plugin.routing.actions.MoveRouteNodeAction;
+import com.innovant.josm.plugin.routing.actions.RemoveRouteNodeAction;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+import com.innovant.josm.plugin.routing.gui.RoutingMenu;
+import com.innovant.josm.plugin.routing.gui.RoutingPreferenceDialog;
+
+/**
+ * The main class of the routing plugin
+ * @author juangui
+ * @author Jose Vidal
+ * @author cdaller
+ *
+ * @version 0.3
+ */
+public class RoutingPlugin extends Plugin implements LayerChangeListener {
+    /**
+     * Logger
+     */
+    static Logger logger = Logger.getLogger(RoutingPlugin.class);
+
+    /**
+     * The list of routing layers
+     */
+    private ArrayList<RoutingLayer> layers;
+
+    /**
+     * The side dialog where nodes are listed
+     */
+    private RoutingDialog routingDialog;
+
+    /**
+     * Preferences Settings Dialog.
+     */
+    private PreferenceSetting preferenceSettings;
+
+    /**
+     * MapMode for adding route nodes.
+     * We use this field to enable or disable the mode automatically.
+     */
+    private AddRouteNodeAction addRouteNodeAction;
+
+    /**
+     * MapMode for removing route nodes.
+     * We use this field to enable or disable the mode automatically.
+     */
+    private RemoveRouteNodeAction removeRouteNodeAction;
+
+    /**
+     * MapMode for moving route nodes.
+     * We use this field to enable or disable the mode automatically.
+     */
+    private MoveRouteNodeAction moveRouteNodeAction;
+
+    /**
+     * IconToggleButton for adding route nodes, we use this field to show or hide the button.
+     */
+    private IconToggleButton addRouteNodeButton;
+
+    /**
+     * IconToggleButton for removing route nodes, we use this field to show or hide the button.
+     */
+    private IconToggleButton removeRouteNodeButton;
+
+    /**
+     * IconToggleButton for moving route nodes, we use this field to show or hide the button.
+     */
+    private IconToggleButton moveRouteNodeButton;
+
+    /**
+     * IconToggleButton for moving route nodes, we use this field to show or hide the button.
+     */
+    private RoutingMenu menu;
+
+    /**
+     * Reference for the plugin class (as if it were a singleton)
+     */
+    private static RoutingPlugin plugin;
+
+    /**
+     * Default Constructor
+     */
+    public RoutingPlugin() {
+        super();
+        plugin = this; // Assign reference to the plugin class
+        DOMConfigurator.configure("log4j.xml");
+        logger.debug("Loading routing plugin...");
+        preferenceSettings=new RoutingPreferenceDialog();
+        // Create side dialog
+        routingDialog = new RoutingDialog();
+        // Initialize layers list
+        layers = new ArrayList<RoutingLayer>();
+        // Add menu
+        menu = new RoutingMenu();
+        // Register this class as LayerChangeListener
+        Layer.listeners.add(this);
+        logger.debug("Finished loading plugin");
+    }
+
+    /**
+     * Provides static access to the plugin instance, to enable access to the plugin methods
+     * @return the instance of the plugin
+     */
+    public static RoutingPlugin getInstance() {
+        return plugin;
+    }
+
+    /**
+     * Get the routing side dialog
+     * @return The instance of the routing side dialog
+     */
+    public RoutingDialog getRoutingDialog() {
+        return routingDialog;
+    }
+
+    public void addLayer() {
+        OsmDataLayer osmLayer = Main.map.mapView.getEditLayer();
+        RoutingLayer layer = new RoutingLayer(tr("Routing") + " [" + osmLayer.getName() + "]", osmLayer);
+        layers.add(layer);
+        Main.main.addLayer(layer);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.plugins.Plugin#mapFrameInitialized(org.openstreetmap.josm.gui.MapFrame, org.openstreetmap.josm.gui.MapFrame)
+     */
+    @Override
+    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+        if(newFrame != null) {
+            // Create plugin map modes
+            addRouteNodeAction = new AddRouteNodeAction(newFrame);
+            removeRouteNodeAction = new RemoveRouteNodeAction(newFrame);
+            moveRouteNodeAction = new MoveRouteNodeAction(newFrame);
+            // Create plugin buttons and add them to the toolbar
+            addRouteNodeButton = new IconToggleButton(addRouteNodeAction);
+            removeRouteNodeButton = new IconToggleButton(removeRouteNodeAction);
+            moveRouteNodeButton = new IconToggleButton(moveRouteNodeAction);
+            newFrame.addMapMode(addRouteNodeButton);
+            newFrame.addMapMode(removeRouteNodeButton);
+            newFrame.addMapMode(moveRouteNodeButton);
+            newFrame.toolGroup.add(addRouteNodeButton);
+            newFrame.toolGroup.add(removeRouteNodeButton);
+            newFrame.toolGroup.add(moveRouteNodeButton);
+            // Hide them by default
+            addRouteNodeButton.setVisible(false);
+            removeRouteNodeButton.setVisible(false);
+            moveRouteNodeButton.setVisible(false);
+            // Enable menu
+            menu.enableStartItem();
+            newFrame.addToggleDialog(routingDialog);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener#activeLayerChange(org.openstreetmap.josm.gui.layer.Layer, org.openstreetmap.josm.gui.layer.Layer)
+     */
+    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+        routingDialog.refresh();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener#layerAdded(org.openstreetmap.josm.gui.layer.Layer)
+     */
+    public void layerAdded(Layer newLayer) {
+        // Add button(s) to the tool bar when the routing layer is added
+        if (newLayer instanceof RoutingLayer) {
+            addRouteNodeButton.setVisible(true);
+            removeRouteNodeButton.setVisible(true);
+            moveRouteNodeButton.setVisible(true);
+            menu.enableRestOfItems();
+            // Set layer on top and select layer, also refresh toggleDialog to reflect selection
+            Main.map.mapView.moveLayer(newLayer, 0);
+            logger.debug("Added routing layer.");
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener#layerRemoved(org.openstreetmap.josm.gui.layer.Layer)
+     */
+    public void layerRemoved(Layer oldLayer) {
+        if ((oldLayer instanceof RoutingLayer) & (layers.size()==1)) {
+            // Remove button(s) from the tool bar when the last routing layer is removed
+            addRouteNodeButton.setVisible(false);
+            removeRouteNodeButton.setVisible(false);
+            moveRouteNodeButton.setVisible(false);
+            menu.disableRestOfItems();
+            layers.remove(oldLayer);
+            logger.debug("Removed routing layer.");
+        } else if (oldLayer instanceof OsmDataLayer) {
+            // Remove all associated routing layers
+            // Convert to Array to prevent ConcurrentModificationException when removing layers from ArrayList
+            // FIXME: can't remove associated routing layers without triggering exceptions in some cases
+            RoutingLayer[] layersArray = layers.toArray(new RoutingLayer[0]);
+            for (int i=0;i<layersArray.length;i++) {
+                if (layersArray[i].getDataLayer().equals(oldLayer)) {
+                    try {
+                        // Remove layer
+                        Main.map.mapView.removeLayer(layersArray[i]);
+                    } catch (IllegalArgumentException e) {
+                    }
+                }
+            }
+        }
+        // Reload RoutingDialog table model
+        routingDialog.refresh();
+    }
+
+    /* (non-Javadoc)
+     * @see org.openstreetmap.josm.plugins.Plugin#getPreferenceSetting()
+     */
+    @Override
+    public PreferenceSetting getPreferenceSetting() {
+        return preferenceSettings;
+    }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/actions/AddRouteNodeAction.java b/routing/src/com/innovant/josm/plugin/routing/actions/AddRouteNodeAction.java
new file mode 100644
index 0000000..3c6b626
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/actions/AddRouteNodeAction.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.MouseEvent;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+
+/**
+ * Accounts for the selection or unselection of the routing tool in the tool bar,
+ * and the mouse events when this tool is selected
+ * @author Juangui
+ * @author Jose Vidal
+ *
+ */
+public class AddRouteNodeAction extends MapMode {
+    /**
+     * Serial.
+     */
+    private static final long serialVersionUID = 1L;
+    /**
+     * Logger.
+     */
+    static Logger logger = Logger.getLogger(AddRouteNodeAction.class);
+    /**
+     * Routing Dialog.
+     */
+    private RoutingDialog routingDialog;
+
+    /**
+     * Constructor
+     * @param mapFrame
+     */
+    public AddRouteNodeAction(MapFrame mapFrame) {
+        // TODO Use constructor with shortcut
+        super(tr("Routing"), "add",
+                tr("Click to add destination."),
+                mapFrame, ImageProvider.getCursor("crosshair", null));
+        this.routingDialog = RoutingPlugin.getInstance().getRoutingDialog();
+    }
+
+    @Override public void enterMode() {
+        super.enterMode();
+        Main.map.mapView.addMouseListener(this);
+    }
+
+    @Override public void exitMode() {
+        super.exitMode();
+        Main.map.mapView.removeMouseListener(this);
+    }
+
+    @Override public void mouseClicked(MouseEvent e) {
+        // If left button is clicked
+        if (e.getButton() == MouseEvent.BUTTON1) {
+            // Search for nearest highway node
+            Node node = null;
+            if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+                RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+                node = layer.getNearestHighwayNode(e.getPoint());
+                if(node == null) {
+                    logger.debug("no selected node");
+                    return;
+                }
+                logger.debug("selected node " + node);
+                layer.getRoutingModel().addNode(node);
+                routingDialog.addNode(node);
+            }
+        }
+        Main.map.repaint();
+    }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/actions/MoveRouteNodeAction.java b/routing/src/com/innovant/josm/plugin/routing/actions/MoveRouteNodeAction.java
new file mode 100644
index 0000000..98ca21e
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/actions/MoveRouteNodeAction.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Point;
+import java.awt.event.MouseEvent;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+
+/**
+ * Accounts for the selection or unselection of the routing tool in the tool bar,
+ * and the mouse events when this tool is selected
+ * @author Juangui
+ * @author Jose Vidal
+ *
+ */
+public class MoveRouteNodeAction extends MapMode {
+    /**
+     * Serial.
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Square of the distance radius where route nodes can be selected for dragging
+     */
+    private static final int DRAG_SQR_RADIUS = 100;
+
+    /**
+     * Logger.
+     */
+    static Logger logger = Logger.getLogger(RoutingLayer.class);
+
+    /**
+     * Routing Dialog.
+     */
+    private RoutingDialog routingDialog;
+
+    /**
+     * Index of dragged node
+     */
+    private int index;
+
+    /**
+     * Constructor
+     * @param mapFrame
+     */
+    public MoveRouteNodeAction(MapFrame mapFrame) {
+        // TODO Use constructor with shortcut
+        super(tr("Routing"), "move",
+                tr("Click and drag to move destination"),
+                mapFrame, ImageProvider.getCursor("normal", "move"));
+        this.routingDialog = RoutingPlugin.getInstance().getRoutingDialog();
+    }
+
+    @Override public void enterMode() {
+        super.enterMode();
+        Main.map.mapView.addMouseListener(this);
+    }
+
+    @Override public void exitMode() {
+        super.exitMode();
+        Main.map.mapView.removeMouseListener(this);
+    }
+
+    @Override public void mousePressed(MouseEvent e) {
+        // If left button is pressed
+        if (e.getButton() == MouseEvent.BUTTON1) {
+            if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+                RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+                RoutingModel routingModel = layer.getRoutingModel();
+                // Search for the nearest node in the list
+                List<Node> nl = routingModel.getSelectedNodes();
+                index = -1;
+                double dmax = DRAG_SQR_RADIUS; // maximum distance, in pixels
+                for (int i=0;i<nl.size();i++) {
+                    Node node = nl.get(i);
+                    double d = Main.map.mapView.getPoint(node).distanceSq(e.getPoint());
+                    if (d < dmax) {
+                        dmax = d;
+                        index = i;
+                    }
+                }
+                if (index>=0)
+                    logger.debug("Moved from node " + nl.get(index));
+            }
+        }
+    }
+
+    @Override public void mouseReleased(MouseEvent e) {
+        // If left button is released and a route node is being dragged
+        if ((e.getButton() == MouseEvent.BUTTON1) && (index>=0)) {
+            searchAndReplaceNode(e.getPoint());
+        }
+    }
+
+    @Override public void mouseDragged(MouseEvent e) {
+    }
+
+    private void searchAndReplaceNode(Point point) {
+        if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+            RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+            RoutingModel routingModel = layer.getRoutingModel();
+            // Search for nearest highway node
+            Node node = null;
+            node = layer.getNearestHighwayNode(point);
+            if (node == null) {
+                logger.debug("Didn't found a close node to move to.");
+                return;
+            }
+            logger.debug("Moved to node " + node);
+            routingModel.removeNode(index);
+            routingDialog.removeNode(index);
+            routingModel.insertNode(index, node);
+            routingDialog.insertNode(index, node);
+            Main.map.repaint();
+        }
+    }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/actions/RemoveRouteNodeAction.java b/routing/src/com/innovant/josm/plugin/routing/actions/RemoveRouteNodeAction.java
new file mode 100644
index 0000000..172031b
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/actions/RemoveRouteNodeAction.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.MouseEvent;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+
+/**
+ * Accounts for the selection or unselection of the remove route nodes tool in the tool bar,
+ * and the mouse events when this tool is selected
+ * @author Juangui
+ * @author Jose Vidal
+ *
+ */
+public class RemoveRouteNodeAction extends MapMode {
+    /**
+     * Serial.
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Square of the distance radius where route nodes can be removed
+     */
+    private static final int REMOVE_SQR_RADIUS = 100;
+
+    /**
+     * Logger.
+     */
+    static Logger logger = Logger.getLogger(RoutingLayer.class);
+    /**
+     * Routing Dialog.
+     */
+    private RoutingDialog routingDialog;
+
+    public RemoveRouteNodeAction(MapFrame mapFrame) {
+        // TODO Use constructor with shortcut
+        super(tr("Routing"), "remove",
+                tr("Click to remove destination"),
+                mapFrame, ImageProvider.getCursor("normal", "delete"));
+        this.routingDialog = RoutingPlugin.getInstance().getRoutingDialog();
+    }
+
+    @Override public void enterMode() {
+        super.enterMode();
+        Main.map.mapView.addMouseListener(this);
+    }
+
+    @Override public void exitMode() {
+        super.exitMode();
+        Main.map.mapView.removeMouseListener(this);
+    }
+
+    @Override public void mouseClicked(MouseEvent e) {
+        // If left button is clicked
+        if (e.getButton() == MouseEvent.BUTTON1) {
+            if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+                RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+                RoutingModel routingModel = layer.getRoutingModel();
+                // Search for the nearest node in the list
+                List<Node> nl = routingModel.getSelectedNodes();
+                int index = -1;
+                double dmax = REMOVE_SQR_RADIUS; // maximum distance, in pixels
+                for (int i=0;i<nl.size();i++) {
+                    Node node = nl.get(i);
+                    double d = Main.map.mapView.getPoint(node).distanceSq(e.getPoint());
+                    if (d < dmax) {
+                        dmax = d;
+                        index = i;
+                    }
+                }
+                // If found a close node, remove it and recalculate route
+                if (index >= 0) {
+                    // Remove node
+                    logger.debug("Removing node " + nl.get(index));
+                    routingModel.removeNode(index);
+                    routingDialog.removeNode(index);
+                    Main.map.repaint();
+                } else {
+                    logger.debug("Can't find a node to remove.");
+                }
+            }
+        }
+    }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingDialog.java b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingDialog.java
new file mode 100644
index 0000000..ed86883
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingDialog.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.ComponentOrientation;
+import java.awt.Font;
+import java.awt.event.KeyEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultListModel;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.border.EtchedBorder;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.tools.Shortcut;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+
+
+/**
+ * @author jose
+ *
+ */
+public class RoutingDialog extends ToggleDialog {
+
+    private DefaultListModel model;
+    private JList jList = null;
+    private JScrollPane jScrollPane = null;
+
+    /**
+     * Serial UID
+     */
+    private static final long serialVersionUID = 8625615652900341987L;
+
+    public RoutingDialog() {
+        super(tr("Routing"), "routing", tr("Open a list of routing nodes"),
+                Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Routing")), KeyEvent.VK_R, Shortcut.GROUP_LAYER), 150);
+        model= new DefaultListModel();
+        this.setSize(456, 292);
+        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+        this.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
+        this.setName("PrincipalDialog");
+        this.setFont(new Font("Dialog", Font.PLAIN, 12));
+        this.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+        this.add(getJScrollPane(), null);
+
+    }
+
+    /**
+     * This method initializes jScrollPane
+     *
+     * @return javax.swing.JScrollPane
+     */
+    private JScrollPane getJScrollPane() {
+        if (jScrollPane == null) {
+            jScrollPane = new JScrollPane();
+            jScrollPane.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
+            jScrollPane.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+            jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+            jScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+            jScrollPane.setName("nList");
+            jScrollPane.setViewportBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
+            jScrollPane.setViewportView(getJList());
+        }
+        return jScrollPane;
+    }
+
+    /**
+     * This method initializes jList
+     *
+     * @return javax.swing.JList
+     */
+    private JList getJList() {
+        if (jList == null) {
+            jList = new JList();
+            jList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+            jList.setModel(model);
+        }
+        return jList;
+    }
+
+    /**
+     * Remove item from the list of nodes
+     * @param index
+     */
+    public void removeNode(int index) {
+        model.remove(index);
+    }
+
+    /**
+     * Add item to the list of nodes
+     * @param obj
+     */
+    public void addNode(Node n) {
+        model.addElement(n.getId()+" ["+n.getCoor().toDisplayString()+"]");
+    }
+
+    /**
+     * Insert item to the list of nodes
+     * @param index
+     * @param obj
+     */
+    public void insertNode(int index, Node n) {
+        model.insertElementAt(n.getId()+" ["+n.getCoor().toDisplayString()+"]", index);
+    }
+
+    /**
+     * Clear list of nodes
+     */
+    public void clearNodes() {
+        model.clear();
+    }
+
+    public void refresh() {
+        clearNodes();
+        if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+            RoutingLayer routingLayer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+            RoutingModel routingModel = routingLayer.getRoutingModel();
+            for (Node n : routingModel.getSelectedNodes()) {
+                addNode(n);
+            }
+        }
+    }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingMenu.java b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingMenu.java
new file mode 100644
index 0000000..29dff73
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingMenu.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.gui;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainMenu;
+
+import com.innovant.josm.jrt.core.RoutingGraph.RouteType;
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+
+/**
+ * The menu bar from this plugin
+ * @author jvidal
+ *
+ */
+public class RoutingMenu extends JMenu {
+
+    /**
+     * Default serial version UID
+     */
+    private static final long serialVersionUID = 3559922048225708480L;
+
+    private JMenuItem startMI;
+    private JMenuItem reverseMI;
+    private JMenuItem clearMI;
+    private JMenu criteriaM;
+    private JMenu menu;
+
+    /**
+     * @param s
+     */
+    public RoutingMenu() {
+        MainMenu mm = Main.main.menu;
+        menu = mm.addMenu(marktr("Routing"), KeyEvent.VK_O, mm.defaultMenuPos, ht("/Plugin/Routing"));
+
+        startMI = new JMenuItem(tr("Add routing layer"));
+        startMI.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                RoutingPlugin.getInstance().addLayer();
+            }
+        });
+        menu.add(startMI);
+
+        menu.addSeparator();
+        ButtonGroup group = new ButtonGroup();
+
+        criteriaM = new JMenu(tr("Criteria"));
+
+        JRadioButtonMenuItem rshorter = new JRadioButtonMenuItem(tr("Shortest"));
+        rshorter.setSelected(true);
+        rshorter.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+                    RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+                    RoutingModel routingModel = layer.getRoutingModel();
+                    if (e.getStateChange()==ItemEvent.SELECTED) {
+                        routingModel.routingGraph.setTypeRoute(RouteType.SHORTEST);
+                    } else {
+                        routingModel.routingGraph.setTypeRoute(RouteType.FASTEST);
+                    }
+                //  routingModel.routingGraph.resetGraph();
+                //  routingModel.routingGraph.createGraph();
+                    //TODO: Change this way
+                    //FIXME: do not change node but recalculate routing.
+                    routingModel.setNodesChanged();
+                    Main.map.repaint();
+                }
+            }
+
+        });
+
+        JRadioButtonMenuItem rfaster = new JRadioButtonMenuItem(tr("Fastest"));
+        group.add(rshorter);
+        group.add(rfaster);
+        criteriaM.add(rshorter);
+        criteriaM.add(rfaster);
+
+        criteriaM.addSeparator();
+        JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem("Ignore oneways");
+        cbmi.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+                    RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+                    RoutingModel routingModel = layer.getRoutingModel();
+                    if (e.getStateChange()==ItemEvent.SELECTED)
+                        routingModel.routingGraph.getRoutingProfile().setOnewayUse(false);
+                    else
+                        routingModel.routingGraph.getRoutingProfile().setOnewayUse(true);
+                    routingModel.setNodesChanged();
+                    Main.map.repaint();
+                }
+            }
+        });
+        criteriaM.add(cbmi);
+        menu.add(criteriaM);
+
+        menu.addSeparator();
+        reverseMI = new JMenuItem(tr("Reverse route"));
+        reverseMI.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+                    RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+                    RoutingModel routingModel = layer.getRoutingModel();
+                    routingModel.reverseNodes();
+                    Main.map.repaint();
+                }
+            }
+        });
+        menu.add(reverseMI);
+
+        clearMI = new JMenuItem(tr("Clear route"));
+        clearMI.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+                    RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+                    RoutingModel routingModel = layer.getRoutingModel();
+                    // Reset routing nodes and paths
+                    routingModel.reset();
+                    RoutingPlugin.getInstance().getRoutingDialog().clearNodes();
+                    Main.map.repaint();
+                }
+            }
+        });
+        menu.add(clearMI);
+
+        // Initially disabled
+        disableAllItems();
+    }
+
+    public void disableAllItems() {
+        startMI.setEnabled(false);
+        reverseMI.setEnabled(false);
+        clearMI.setEnabled(false);
+        criteriaM.setEnabled(false);
+    }
+
+    public void enableStartItem() {
+        startMI.setEnabled(true);
+    }
+
+    public void enableRestOfItems() {
+        reverseMI.setEnabled(true);
+        clearMI.setEnabled(true);
+        criteriaM.setEnabled(true);
+    }
+
+    public void disableRestOfItems() {
+        reverseMI.setEnabled(false);
+        clearMI.setEnabled(false);
+        criteriaM.setEnabled(false);
+    }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java
new file mode 100644
index 0000000..488f700
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java
@@ -0,0 +1,235 @@
+/*
+ *
+ * Copyright (C) 2008 Innovant
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ *  Innovant
+ *   juangui at gmail.com
+ *   vidalfree at gmail.com
+ *
+ *  http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.gui;
+
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.DefaultTableModel;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+
+import com.innovant.josm.jrt.osm.OsmWayTypes;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+
+public class RoutingPreferenceDialog implements PreferenceSetting {
+
+    /**
+     * Logger
+     */
+    static Logger logger = Logger.getLogger(RoutingPreferenceDialog.class);
+
+    private Map<String, String> orig;
+    private DefaultTableModel model;
+
+    /**
+     * Constructor
+     */
+    public RoutingPreferenceDialog() {
+        super();
+        readPreferences();
+    }
+
+    public void addGui(final PreferenceDialog gui) {
+
+        JPanel principal = gui.createPreferenceTab("routing",
+                tr("Routing Plugin Preferences"), tr("Configure routing preferences."));
+
+        JPanel p = new JPanel();
+        p.setLayout(new GridBagLayout());
+
+        model = new DefaultTableModel(new String[] { tr("Highway type"),
+                tr("Speed (Km/h)") }, 0) {
+            private static final long serialVersionUID = 4253339034781567453L;
+
+            @Override
+            public boolean isCellEditable(int row, int column) {
+                return column != 0;
+            }
+        };
+        final JTable list = new JTable(model);
+        loadSpeeds(model);
+
+        JScrollPane scroll = new JScrollPane(list);
+
+        p.add(scroll, GBC.eol().fill(GBC.BOTH));
+        scroll.setPreferredSize(new Dimension(200, 200));
+
+        JButton add = new JButton(tr("Add"));
+        p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
+        p.add(add, GBC.std().insets(0, 5, 0, 0));
+        add.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                JPanel p = new JPanel(new GridBagLayout());
+                p.add(new JLabel(tr("Weight")), GBC.std().insets(0, 0, 5, 0));
+                JComboBox key = new JComboBox();
+                for (OsmWayTypes pk : OsmWayTypes.values())
+                    key.addItem(pk.getTag());
+                JTextField value = new JTextField(10);
+                p.add(key, GBC.eop().insets(5, 0, 0, 0).fill(GBC.HORIZONTAL));
+                p.add(new JLabel(tr("Value")), GBC.std().insets(0, 0, 5, 0));
+                p.add(value, GBC.eol().insets(5, 0, 0, 0).fill(GBC.HORIZONTAL));
+                int answer = JOptionPane.showConfirmDialog(gui, p,
+                        tr("Enter weight values"),
+                        JOptionPane.OK_CANCEL_OPTION);
+                if (answer == JOptionPane.OK_OPTION) {
+                    model
+                    .addRow(new String[] {
+                            key.getSelectedItem().toString(),
+                            value.getText() });
+                }
+            }
+        });
+
+        JButton delete = new JButton(tr("Delete"));
+        p.add(delete, GBC.std().insets(0, 5, 0, 0));
+        delete.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                if (list.getSelectedRow() == -1)
+                    JOptionPane.showMessageDialog(gui,
+                            tr("Please select the row to delete."));
+                else {
+                    Integer i;
+                    while ((i = list.getSelectedRow()) != -1)
+                        model.removeRow(i);
+                }
+            }
+        });
+
+        JButton edit = new JButton(tr("Edit"));
+        p.add(edit, GBC.std().insets(5, 5, 5, 0));
+        edit.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                edit(gui, list);
+            }
+        });
+
+        JTabbedPane Opciones = new JTabbedPane();
+        Opciones.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+
+        Opciones.addTab("Profile", null, p, null);
+//      Opciones.addTab("Preferences", new JPanel());
+
+        list.addMouseListener(new MouseAdapter(){
+            @Override public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2)
+                    edit(gui, list);
+            }
+        });
+
+        principal.add(Opciones, GBC.eol().fill(GBC.BOTH));
+
+    }
+
+    public boolean ok() {
+        for (int i = 0; i < model.getRowCount(); ++i) {
+            String value = model.getValueAt(i, 1).toString();
+            if (value.length() != 0) {
+                String key = model.getValueAt(i, 0).toString();
+                String origValue = orig.get(key);
+                if (origValue == null || !origValue.equals(value))
+                    Main.pref.put(key, value);
+                orig.remove(key); // processed.
+            }
+        }
+        for (Entry<String, String> e : orig.entrySet())
+            Main.pref.put(e.getKey(), null);
+        return false;
+    }
+
+    private void edit(final PreferenceDialog gui, final JTable list) {
+        if (list.getSelectedRowCount() != 1) {
+            JOptionPane.showMessageDialog(gui,
+                    tr("Please select the row to edit."));
+            return;
+        }
+        String v = JOptionPane.showInputDialog(tr("New value for {0}", model
+                .getValueAt(list.getSelectedRow(), 0)), model.getValueAt(list
+                        .getSelectedRow(), 1));
+        if (v != null)
+            model.setValueAt(v, list.getSelectedRow(), 1);
+    }
+
+    private void loadSpeeds(DefaultTableModel model) {
+        // Read dialog values from preferences
+        readPreferences();
+        // Put these values in the model
+        for (String tag : orig.keySet()) {
+            model.addRow(new String[] { tag, orig.get(tag) });
+        }
+    }
+
+    private void readPreferences() {
+        orig = Main.pref.getAllPrefix("routing.profile.default.speed");
+        if (orig.size() == 0) { // defaults
+            logger.debug("Loading Default Preferences.");
+            for (OsmWayTypes owt : OsmWayTypes.values()) {
+                Main.pref.putInteger("routing.profile.default.speed."
+                        + owt.getTag(), owt.getSpeed());
+            }
+            orig = Main.pref.getAllPrefix("routing.profile.default.speed");
+        }
+        else logger.debug("Default preferences already exist.");
+    }
+
+    private String getKeyTag(String tag) {
+        return tag.split(".", 5)[4];
+    }
+
+    private String getTypeTag(String tag) {
+        return tag.split(".", 5)[3];
+    }
+
+    private String getNameTag(String tag) {
+        return tag.split(".", 5)[2];
+    }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/package.html b/routing/src/com/innovant/josm/plugin/routing/package.html
new file mode 100644
index 0000000..d45fa25
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Package</title>
+</head>
+<body bgcolor="white">
+Main classes of Plugin.
+</body>
+</html>
diff --git a/slippymap/.classpath b/slippymap/.classpath
index 32eb8e6..17b8e6a 100644
--- a/slippymap/.classpath
+++ b/slippymap/.classpath
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<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 combineaccessrules="false" kind="src" path="/JOSM"/>
 	<classpathentry kind="output" path="build"/>
 </classpath>
diff --git a/slippymap/build.xml b/slippymap/build.xml
index a0eed67..b387b4e 100644
--- a/slippymap/build.xml
+++ b/slippymap/build.xml
@@ -25,7 +25,7 @@
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
                 <attribute name="Plugin-Description" value="Displays a slippy map grid in JOSM. Can load tiles from slippy map as background and request updates."/>
                 <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/SlippyMap"/>
-                <attribute name="Plugin-Mainversion" value="2196"/>
+                <attribute name="Plugin-Mainversion" value="2450"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
index e33a6d5..663a982 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
@@ -4,8 +4,8 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Color;
 import java.awt.Component;
-import java.awt.Dimension;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Image;
 import java.awt.Point;
 import java.awt.Rectangle;
@@ -15,22 +15,29 @@ import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.image.ImageObserver;
 import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
+import java.util.HashSet;
 import java.util.LinkedList;
-import java.util.TreeSet;
+import java.util.List;
 
 import javax.swing.AbstractAction;
 import javax.swing.Icon;
+import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JMenuItem;
 import javax.swing.JPopupMenu;
 import javax.swing.JSeparator;
 import javax.swing.SwingUtilities;
 
+import org.openstreetmap.gui.jmapviewer.JobDispatcher;
+import org.openstreetmap.gui.jmapviewer.MemoryTileCache;
+import org.openstreetmap.gui.jmapviewer.OsmFileCacheTileLoader;
+import org.openstreetmap.gui.jmapviewer.Tile;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
@@ -48,73 +55,141 @@ import org.openstreetmap.josm.tools.ImageProvider;
  * @author Dave Hansen <dave at sr71.net>
  *
  */
-public class SlippyMapLayer extends Layer implements ImageObserver,
-    PreferenceChangedListener {
+public class SlippyMapLayer extends Layer implements PreferenceChangedListener, ImageObserver,
+    TileLoaderListener {
+    boolean debug = false;
+    void out(String s)
+    {
+        Main.debug(s);
+    }
+
+    protected MemoryTileCache tileCache;
+    protected TileSource tileSource;
+    protected TileLoader tileLoader;
+    JobDispatcher jobDispatcher = JobDispatcher.getInstance();
+
+    HashSet<Tile> tileRequestsOutstanding = new HashSet<Tile>();
+    public synchronized void tileLoadingFinished(Tile tile, boolean success)
+    {
+        tile.setLoaded(true);
+        needRedraw = true;
+        Main.map.repaint(100);
+        tileRequestsOutstanding.remove(tile);
+        if (debug)
+            out("tileLoadingFinished() tile: " + tile + " success: " + success);
+    }
+    public TileCache getTileCache()
+    {
+        return tileCache;
+    }
+    void clearTileCache()
+    {
+        if (debug)
+            out("clearing tile storage");
+        tileCache = new MemoryTileCache();
+        tileCache.setCacheSize(2000);
+    }
+
     /**
      * Actual zoom lvl. Initial zoom lvl is set to
      * {@link SlippyMapPreferences#getMinZoomLvl()}.
      */
-    public int currentZoomLevel = SlippyMapPreferences.getMinZoomLvl();
-    private Hashtable<SlippyMapKey, SlippyMapTile> tileStorage = null;
+    public int currentZoomLevel;
 
-    Point[][] pixelpos = new Point[21][21];
     LatLon lastTopLeft;
     LatLon lastBotRight;
     private Image bufferImage;
-    private SlippyMapTile clickedTile;
+    private Tile clickedTile;
     private boolean needRedraw;
     private JPopupMenu tileOptionMenu;
+    JCheckBoxMenuItem autoZoomPopup;
+    Tile showMetadataTile;
 
-    static void debug(String msg)
+    void redraw()
     {
+        needRedraw = true;
+        Main.map.repaint();
+    }
 
+    void newTileStorage()
+    {
+        int origZoom = currentZoomLevel;
+        tileSource = SlippyMapPreferences.getMapSource();
+        // The minimum should also take care of integer parsing
+        // errors which would leave us with a zoom of -1 otherwise
+        if (tileSource.getMaxZoom() < currentZoomLevel)
+            currentZoomLevel = tileSource.getMaxZoom();
+        if (tileSource.getMinZoom() > currentZoomLevel)
+            currentZoomLevel = tileSource.getMinZoom();
+        if (currentZoomLevel != origZoom) {
+            out("changed currentZoomLevel loading new tile store from " + origZoom + " to " + currentZoomLevel);
+            out("tileSource.getMinZoom(): " + tileSource.getMinZoom());
+            out("tileSource.getMaxZoom(): " + tileSource.getMaxZoom());
+            SlippyMapPreferences.setLastZoom(currentZoomLevel);
+        }
+        clearTileCache();
+        //tileLoader = new OsmTileLoader(this);
+        tileLoader = new OsmFileCacheTileLoader(this);
     }
+
     @SuppressWarnings("serial")
     public SlippyMapLayer() {
         super(tr("Slippy Map"));
+
         setBackgroundLayer(true);
+        this.setVisible(true);
 
-        clearTileStorage();
+        currentZoomLevel = SlippyMapPreferences.getLastZoom();
+        newTileStorage();
 
         tileOptionMenu = new JPopupMenu();
+
+        autoZoomPopup = new JCheckBoxMenuItem();
+        autoZoomPopup.setAction(new AbstractAction(tr("Auto Zoom")) {
+            public void actionPerformed(ActionEvent ae) {
+                boolean new_state = !SlippyMapPreferences.getAutozoom();
+                SlippyMapPreferences.setAutozoom(new_state);
+            }
+        });
+        autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
+        tileOptionMenu.add(autoZoomPopup);
+
         tileOptionMenu.add(new JMenuItem(new AbstractAction(tr("Load Tile")) {
             public void actionPerformed(ActionEvent ae) {
                 if (clickedTile != null) {
-                    loadSingleTile(clickedTile);
-                    needRedraw = true;
-                    Main.map.repaint();
+                    loadTile(clickedTile);
+                    redraw();
                 }
             }
         }));
 
         tileOptionMenu.add(new JMenuItem(new AbstractAction(
-                tr("Show Tile Status")) {
+                tr("Show Tile Info")) {
             public void actionPerformed(ActionEvent ae) {
+                out("info tile: " + clickedTile);
                 if (clickedTile != null) {
-                    clickedTile.loadMetadata();
-                    needRedraw = true;
-                    Main.map.repaint();
+                    showMetadataTile = clickedTile;
+                    redraw();
                 }
             }
         }));
 
+        /* FIXME
         tileOptionMenu.add(new JMenuItem(new AbstractAction(
                 tr("Request Update")) {
             public void actionPerformed(ActionEvent ae) {
                 if (clickedTile != null) {
                     clickedTile.requestUpdate();
-                    needRedraw = true;
-                    Main.map.repaint();
+                    redraw();
                 }
             }
-        }));
+        }));*/
 
         tileOptionMenu.add(new JMenuItem(new AbstractAction(
                 tr("Load All Tiles")) {
             public void actionPerformed(ActionEvent ae) {
-                loadAllTiles();
-                needRedraw = true;
-                Main.map.repaint();
+                loadAllTiles(true);
+                redraw();
             }
         }));
 
@@ -123,8 +198,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
                 new AbstractAction(tr("Increase zoom")) {
                     public void actionPerformed(ActionEvent ae) {
                         increaseZoomLevel();
-                        needRedraw = true;
-                        Main.map.repaint();
+                        redraw();
                     }
                 }));
 
@@ -132,19 +206,34 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
                 new AbstractAction(tr("Decrease zoom")) {
                     public void actionPerformed(ActionEvent ae) {
                         decreaseZoomLevel();
-                        Main.map.repaint();
+                        redraw();
                     }
                 }));
 
+        // FIXME: currently ran in errors
+		
+		tileOptionMenu.add(new JMenuItem(
+                new AbstractAction(tr("Snap to tile size")) {
+                    public void actionPerformed(ActionEvent ae) {
+                        if (lastImageScale == null) {
+                            out("please wait for a tile to be loaded before snapping");
+                            return;
+                        }
+                        double new_factor = Math.sqrt(lastImageScale);
+                        if (debug)
+                            out("tile snap: scale was: " + lastImageScale + ", new factor: " + new_factor);
+                        Main.map.mapView.zoomToFactor(new_factor);
+                        redraw();
+                    }
+                }));
+        // end of adding menu commands
+
         tileOptionMenu.add(new JMenuItem(
                 new AbstractAction(tr("Flush Tile Cache")) {
                     public void actionPerformed(ActionEvent ae) {
                         System.out.print("flushing all tiles...");
-                        for (SlippyMapTile t : tileStorage.values()) {
-                            t.dropImage();
-                        }
+                        clearTileCache();
                         System.out.println("done");
-                        shrinkTileStorage(0);
                     }
                 }));
         // end of adding menu commands
@@ -157,8 +246,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
                         if (e.getButton() != MouseEvent.BUTTON3)
                             return;
                         clickedTile = getTileForPixelpos(e.getX(), e.getY());
-                        tileOptionMenu.show(e.getComponent(), e.getX(), e
-                                .getY());
+                        tileOptionMenu.show(e.getComponent(), e.getX(), e.getY());
                     }
                 });
 
@@ -179,6 +267,32 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
         Main.pref.listener.add(this);
     }
 
+    void zoomChanged()
+    {
+        if (debug)
+            out("zoomChanged(): " + currentZoomLevel);
+        needRedraw = true;
+        jobDispatcher.cancelOutstandingJobs();
+        tileRequestsOutstanding.clear();
+        SlippyMapPreferences.setLastZoom(currentZoomLevel);
+    }
+
+    int getMaxZoomLvl()
+    {
+        int ret = SlippyMapPreferences.getMaxZoomLvl();
+        if (tileSource.getMaxZoom() < ret)
+            ret = tileSource.getMaxZoom();
+        return ret;
+    }
+
+    int getMinZoomLvl()
+    {
+        int ret = SlippyMapPreferences.getMinZoomLvl();
+        if (tileSource.getMinZoom() > ret)
+            ret = tileSource.getMinZoom();
+        return ret;
+    }
+
     /**
      * Zoom in, go closer to map.
      *
@@ -186,8 +300,9 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
      */
     public boolean zoomIncreaseAllowed()
     {
-        boolean zia = currentZoomLevel < SlippyMapPreferences.getMaxZoomLvl();
-        this.debug("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + SlippyMapPreferences.getMaxZoomLvl() );
+        boolean zia = currentZoomLevel < this.getMaxZoomLvl();
+        if (debug)
+            out("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + this.getMaxZoomLvl() );
         return zia;
     }
     public boolean increaseZoomLevel()
@@ -195,11 +310,12 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
         lastImageScale = null;
         if (zoomIncreaseAllowed()) {
             currentZoomLevel++;
-            this.debug("increasing zoom level to: " + currentZoomLevel);
-            needRedraw = true;
+            if (debug)
+                out("increasing zoom level to: " + currentZoomLevel);
+            zoomChanged();
         } else {
             System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+
-                             "MaxZoomLvl ("+SlippyMapPreferences.getMaxZoomLvl()+") reached.");
+                             "MaxZoomLvl ("+this.getMaxZoomLvl()+") reached.");
             return false;
         }
         return true;
@@ -212,15 +328,16 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
      */
     public boolean zoomDecreaseAllowed()
     {
-        return currentZoomLevel > SlippyMapPreferences.getMinZoomLvl();
+        return currentZoomLevel > this.getMinZoomLvl();
     }
     public boolean decreaseZoomLevel() {
-        int minZoom = SlippyMapPreferences.getMinZoomLvl();
+        int minZoom = this.getMinZoomLvl();
         lastImageScale = null;
         if (zoomDecreaseAllowed()) {
-            this.debug("decreasing zoom level to: " + currentZoomLevel);
+            if (debug)
+                out("decreasing zoom level to: " + currentZoomLevel);
             currentZoomLevel--;
-            needRedraw = true;
+            zoomChanged();
         } else {
             System.err.println("current zoom lvl couldnt be decreased. MinZoomLvl("+minZoom+") reached.");
             return false;
@@ -228,137 +345,61 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
         return true;
     }
 
-    public void clearTileStorage() {
-        // when max zoom lvl is begin saved, this method is called and probably
-        // the setting isnt saved yet.
-        int maxZoom = SlippyMapPreferences.getMaxZoomLvl();
-        tileStorage = new Hashtable<SlippyMapKey, SlippyMapTile>();
-        checkTileStorage();
-    }
-
-    class TileTimeComp implements Comparator<SlippyMapTile> {
-            public int compare(SlippyMapTile s1, SlippyMapTile s2) {
-                    long t1 = s1.access_time();
-                    long t2 = s2.access_time();
-                    if (s1 == s2)
-                            return 0;
-                    if (t1 == t2) {
-                            t1 = s1.hashCode();
-                            t2 = s2.hashCode();
-                    }
-                    if (t1 < t2)
-                            return -1;
-                    return 1;
-            }
-    }
-
-    /**
-     * <p>
-     * Check if tiles.size() is not more than maxNrTiles.
-     * If yes, oldest tiles by timestamp are flushed from
-     * the cache.
-     * </p>
+    /*
+     * We use these for quick, hackish calculations.  They
+     * are temporary only and intentionally not inserted
+     * into the tileCache.
      */
-    public void shrinkTileStorage(int maxNrTiles)
-    {
-        TreeSet<SlippyMapTile> tiles = new TreeSet<SlippyMapTile>(new TileTimeComp());
-        tiles.addAll(tileStorage.values());
-        int nr_to_drop = tiles.size() - maxNrTiles;
-        if (nr_to_drop <= 0) {
-            this.debug("total of " + tiles.size() + " loaded tiles, size OK (< " + maxNrTiles + ")");
-            return;
-        }
-        this.debug("total of " + tiles.size() + " tiles, need to flush " + nr_to_drop + " tiles");
-        for (SlippyMapTile t : tiles) {
-            if (nr_to_drop <= 0)
-                break;
-            t.dropImage();
-            nr_to_drop--;
-            //tileStorage.remove(t.getKey());
-        }
-    }
-    long lastCheck = 0;
-    public void checkTileStorage() {
-        long now = System.currentTimeMillis();
-        if (now - lastCheck < 1000)
-            return;
-        lastCheck = now;
-        shrinkTileStorage(200);
-    }
-
-    LinkedList<SlippyMapTile> downloadQueue = new LinkedList<SlippyMapTile>();
-    LinkedList<Image> downloadList = new LinkedList<Image>();
-    int simultaneousTileDownloads = 5;
-    int maxQueueSize = 50;
-    synchronized void markDone(Image i)
-    {
-        boolean inList = downloadList.remove(i);
-        if (!inList) {
-            Main.debug("ERROR: downloaded image was not queued");
-            return;
-        }
-        //System.out.print("currently downloading: " + downloadList.size() +
-        //                   " queue size: " + downloadQueue.size() +"    \r");
-        if (downloadQueue.size() > 0)
-            loadSingleTile(downloadQueue.getLast());
-    }
-    synchronized void loadSingleTile(SlippyMapTile tile)
-    {
-        // this moves the tile to the front of the line
-        if (downloadQueue.contains(tile))
-            downloadQueue.remove(tile);
-        downloadQueue.addLast(tile);
-        // We assume that a queue larger than this is
-        // too big and the downloads at the end of it
-        // too old to be relevant.
-        while (downloadQueue.size() > maxQueueSize)
-            downloadQueue.removeFirst();
-        if (downloadList.size() > simultaneousTileDownloads)
-            return;
-        //System.out.print("currently downloading: " + downloadList.size() +
-        //                   " queue size: " + downloadQueue.size() + "    \r");
-        while (!downloadQueue.isEmpty()) {
-            // This is a FIFO queue.  The reasoning is
-            // that we want to draw the most recently
-            // requested images now.  We may have panned
-            // or zoomed away
-            tile = downloadQueue.removeLast();
-            Image img = tile.loadImage();
-            if (imageLoaded(img))
-                continue;
-            Toolkit.getDefaultToolkit().prepareImage(img, -1, -1, this);
-            downloadList.add(img);
-            break;
-        }
+    synchronized Tile tempCornerTile(Tile t) {
+        int x = t.getXtile() + 1;
+        int y = t.getYtile() + 1;
+        int zoom = t.getZoom();
+        Tile tile = getTile(x, y, zoom);
+        if (tile != null)
+            return tile;
+        return new Tile(tileSource, x, y, zoom);
     }
-
-    synchronized SlippyMapTile getTile(int x, int y, int zoom) {
-        SlippyMapKey key = new SlippyMapKey(x, y, zoom);
-        if (!key.valid) {
-            return null;
+    synchronized Tile getOrCreateTile(int x, int y, int zoom) {
+        Tile tile = getTile(x, y, zoom);
+        if (tile == null) {
+            tile = new Tile(tileSource, x, y, zoom);
+            tileCache.addTile(tile);
+            tile.loadPlaceholderFromCache(tileCache);
         }
-        return tileStorage.get(key);
+        return tile;
     }
 
-    synchronized SlippyMapTile putTile(SlippyMapTile tile, int x, int y, int zoom) {
-        SlippyMapKey key = new SlippyMapKey(x, y, zoom);
-        if (!key.valid) {
-            return null;
-        }
-        return tileStorage.put(key, tile);
+    /*
+     * This can and will return null for tiles that are not
+     * already in the cache.
+     */
+    synchronized Tile getTile(int x, int y, int zoom) {
+        int max = (1 << zoom);
+        if (x < 0 || x >= max || y < 0 || y >= max)
+                return null;
+        Tile tile = tileCache.getTile(tileSource, x, y, zoom);
+        return tile;
     }
 
-    synchronized SlippyMapTile getOrCreateTile(int x, int y, int zoom) {
-        SlippyMapTile tile = getTile(x, y, zoom);
-        if (tile != null) {
-            return tile;
-        }
-        tile = new SlippyMapTile(x, y, zoom);
-        putTile(tile, x, y, zoom);
-        return tile;
+    synchronized boolean loadTile(Tile tile)
+    {
+        if (tile == null)
+            return false;
+        if (tile.hasError())
+            return false;
+        if (tile.isLoaded())
+            return false;
+        if (tile.isLoading())
+            return false;
+        if (tileRequestsOutstanding.contains(tile))
+            return false;
+        tileRequestsOutstanding.add(tile);
+        jobDispatcher.addJob(tileLoader.createTileLoaderJob(tileSource,
+                             tile.getXtile(), tile.getYtile(), tile.getZoom()));
+        return true;
     }
 
-    void loadAllTiles() {
+    void loadAllTiles(boolean force) {
         MapView mv = Main.map.mapView;
         LatLon topLeft = mv.getLatLon(0, 0);
         LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
@@ -367,17 +408,11 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
 
         // if there is more than 18 tiles on screen in any direction, do not
         // load all tiles!
-        if (ts.tilesSpanned() > (18*18)) {
+        if (ts.tooLarge()) {
             System.out.println("Not downloading all tiles because there is more than 18 tiles on an axis!");
             return;
         }
-
-        for (Tile t : ts.allTiles()) {
-            SlippyMapTile tile = getOrCreateTile(t.x, t.y, currentZoomLevel);
-            if (tile.getImage() == null) {
-                this.loadSingleTile(tile);
-            }
-        }//end of for Tile t
+        ts.loadAllTiles(force);
     }
 
     /*
@@ -385,13 +420,38 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
      * a 100x100 image being scaled to 50x50 would return 0.25.
      */
     Image lastScaledImage = null;
+    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
+        boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
+        needRedraw = true;
+        if (debug)
+            out("imageUpdate() done: " + done + " calling repaint");
+        Main.map.repaint(done ? 0 : 100);
+        return !done;
+    }
+    boolean imageLoaded(Image i) {
+        if (i == null)
+            return false;
+        int status = Toolkit.getDefaultToolkit().checkImage(i, -1, -1, this);
+        if ((status & ALLBITS) != 0)
+            return true;
+        return false;
+    }
+    Image getLoadedTileImage(Tile tile)
+    {
+        if (!tile.isLoaded())
+            return null;
+        Image img = tile.getImage();
+        if (!imageLoaded(img))
+            return null;
+        return img;
+    }
 
-    double getImageScaling(Image img, Point p0, Point p1) {
+    double getImageScaling(Image img, Rectangle r) {
         int realWidth = -1;
         int realHeight = -1;
-           if (img != null) {
+        if (img != null) {
             realWidth = img.getHeight(this);
-               realWidth = img.getWidth(this);
+            realWidth = img.getWidth(this);
         }
         if (realWidth == -1 || realHeight == -1) {
             /*
@@ -401,7 +461,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
              * guess.
              */
             if (lastScaledImage != null) {
-                return getImageScaling(lastScaledImage, p0, p1);
+                return getImageScaling(lastScaledImage, r);
             }
             realWidth = 256;
             realHeight = 256;
@@ -413,8 +473,8 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
          * the millions, so make this a double to prevent integer
          * overflows.
          */
-        double drawWidth = p1.x - p0.x;
-        double drawHeight = p1.x - p0.x;
+        double drawWidth = r.width;
+        double drawHeight = r.height;
         // stem.out.println("drawWidth: " + drawWidth + " drawHeight: " +
         // drawHeight);
 
@@ -424,133 +484,226 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
         return drawArea / realArea;
     }
 
-    boolean imageLoaded(Image i) {
-        if (i == null)
-            return false;
+    LatLon tileLatLon(Tile t)
+    {
+        int zoom = t.getZoom();
+        return new LatLon(tileYToLat(t.getYtile(), zoom),
+                          tileXToLon(t.getXtile(), zoom));
+    }
 
-        int status = Toolkit.getDefaultToolkit().checkImage(i, -1, -1, this);
-        if ((status & ALLBITS) != 0)
-            return true;
-        return false;
+    int paintFromOtherZooms(Graphics g, Tile topLeftTile, Tile botRightTile)
+    {
+        LatLon topLeft  = tileLatLon(topLeftTile);
+        LatLon botRight = tileLatLon(botRightTile);
+
+
+        /*
+         * Go looking for tiles in zoom levels *other* than the current
+         * one. Even if they might look bad, they look better than a
+         * blank tile.
+         *
+         * Make darn sure that the tilesCache can either hold all of
+         * these "fake" tiles or that they don't get inserted in it to
+         * begin with.
+         */
+        //int otherZooms[] = {-5, -4, -3, 2, -2, 1, -1};
+        int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5};
+        int painted = 0;
+        debug = true;
+        for (int zoomOff : otherZooms) {
+            int zoom = currentZoomLevel + zoomOff;
+            if ((zoom < this.getMinZoomLvl()) ||
+                (zoom > this.getMaxZoomLvl())) {
+                continue;
+            }
+            TileSet ts = new TileSet(topLeft, botRight, zoom);
+            int zoom_painted = 0;
+            this.paintTileImages(g, ts, zoom, null);
+            if (debug && zoom_painted > 0)
+                out("painted " + zoom_painted + "/"+ ts.size() +
+                    " tiles from zoom("+zoomOff+"): " + zoom);
+            painted += zoom_painted;
+            if (zoom_painted >= ts.size()) {
+                if (debug)
+                    out("broke after drawing " + zoom_painted + "/"+ ts.size() + " at zoomOff: " + zoomOff);
+                break;
+            }
+        }
+        debug = false;
+        return painted;
+    }
+    Rectangle tileToRect(Tile t1)
+    {
+        /*
+         * We need to get a box in which to draw, so advance by one tile in
+         * each direction to find the other corner of the box.
+         * Note: this somewhat pollutes the tile cache
+         */
+        Tile t2 = tempCornerTile(t1);
+        Rectangle rect = new Rectangle(pixelPos(t1));
+        rect.add(pixelPos(t2));
+        return rect;
     }
 
+    // 'source' is the pixel coordinates for the area that
+    // the img is capable of filling in.  However, we probably
+    // only want a portion of it.
+    //
+    // 'border' is the screen cordinates that need to be drawn.
+    //  We must not draw outside of it.
+    void drawImageInside(Graphics g, Image sourceImg, Rectangle source, Rectangle border)
+    {
+        Rectangle target = source;
+
+        // If a border is specified, only draw the intersection
+        // if what we have combined with what we are supposed
+        // to draw.
+        if (border != null) {
+            target = source.intersection(border);
+            if (debug)
+                out("source: " + source + "\nborder: " + border + "\nintersection: " + target);
+        }
+
+        // All of the rectangles are in screen coordinates.  We need
+        // to how these correlate to the sourceImg pixels.  We could
+        // avoid doing this by scaling the image up to the 'source' size,
+        // but this should be cheaper.
+        //
+        // In some projections, x any y are scaled differently enough to
+        // cause a pixel or two of fudge.  Calculate them separately.
+        double imageYScaling = sourceImg.getHeight(this) / source.getHeight();
+        double imageXScaling = sourceImg.getWidth(this) / source.getWidth();
+
+        // How many pixels into the 'source' rectangle are we drawing?
+        int screen_x_offset = target.x - source.x;
+        int screen_y_offset = target.y - source.y;
+        // And how many pixels into the image itself does that
+        // correlate to?
+        int img_x_offset = (int)(screen_x_offset * imageXScaling);
+        int img_y_offset = (int)(screen_y_offset * imageYScaling);
+        // Now calculate the other corner of the image that we need
+        // by scaling the 'target' rectangle's dimensions.
+        int img_x_end   = img_x_offset + (int)(target.getWidth() * imageXScaling);
+        int img_y_end   = img_y_offset + (int)(target.getHeight() * imageYScaling);
+
+        if (debug) {
+            out("drawing image into target rect: " + target);
+        }
+        g.drawImage(sourceImg,
+                        target.x, target.y,
+                        target.x + target.width, target.y + target.height,
+                        img_x_offset, img_y_offset,
+                        img_x_end, img_y_end,
+                        this);
+        float fadeBackground = SlippyMapPreferences.getFadeBackground();
+        if (fadeBackground != 0f) {
+            // dimm by painting opaque rect...
+            g.setColor(new Color(1f, 1f, 1f, fadeBackground));
+            g.fillRect(target.x, target.y,
+                       target.width, target.height);
+        }
+    }
     Double lastImageScale = null;
-    int paintTileImages(Graphics g, TileSet ts, int zoom) {
-        int paintedTiles = 0;
+    // This function is called for several zoom levels, not just
+    // the current one.  It should not trigger any tiles to be
+    // downloaded.  It should also avoid polluting the tile cache
+    // with any tiles since these tiles are not mandatory.
+    //
+    // The "border" tile tells us the boundaries of where we may
+    // draw.  It will not be from the zoom level that is being
+    // drawn currently.  If drawing the currentZoomLevel,
+    // border is null and we draw the entire tile set.
+    List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
+        Rectangle borderRect = null;
+        if (border != null)
+            borderRect = tileToRect(border);
+        List<Tile> missedTiles = new LinkedList<Tile>();
         boolean imageScaleRecorded = false;
-        Image img = null;
-        for (Tile t : ts.allTiles()) {
-            SlippyMapTile tile = getTile(t.x, t.y, zoom);
-            if (tile == null) {
-                // Don't trigger tile loading if this isn't
-                // the exact zoom level we're looking for
-                if (zoom != currentZoomLevel)
-                    continue;
-                tile = getOrCreateTile(t.x, t.y, zoom);
-                if (SlippyMapPreferences.getAutoloadTiles())
-                    loadSingleTile(tile);
-            }
-            img = tile.getImage();
-            if (img == null)
+        for (Tile tile : ts.allTiles()) {
+            Image img = getLoadedTileImage(tile);
+            if (img == null) {
+                if (debug)
+                    out("missed tile: " + tile);
+                missedTiles.add(tile);
                 continue;
-            if ((zoom != currentZoomLevel) && !tile.isDownloaded())
-                continue;
-            Point p = t.pixelPos(zoom);
-            /*
-             * We need to get a box in which to draw, so advance by one tile in
-             * each direction to find the other corner of the box
-             */
-            Tile t2 = new Tile(t.x + 1, t.y + 1);
-            Point p2 = t2.pixelPos(zoom);
-            if (imageLoaded(img)) {
-                g.drawImage(img, p.x, p.y, p2.x - p.x, p2.y - p.y, this);
-                paintedTiles++;
             }
-            if (img == null)
+            Rectangle sourceRect = tileToRect(tile);
+            if (borderRect != null && !sourceRect.intersects(borderRect))
                 continue;
+            drawImageInside(g, img, sourceRect, borderRect);
             if (!imageScaleRecorded && zoom == currentZoomLevel) {
-                lastImageScale = new Double(getImageScaling(img, p, p2));
+                lastImageScale = new Double(getImageScaling(img, sourceRect));
                 imageScaleRecorded = true;
             }
-            float fadeBackground = SlippyMapPreferences.getFadeBackground();
-            if (fadeBackground != 0f) {
-                // dimm by painting opaque rect...
-                g.setColor(new Color(1f, 1f, 1f, fadeBackground));
-                g.fillRect(p.x, p.y, p2.x - p.x, p2.y - p.y);
-            }
         }// end of for
-        return paintedTiles;
+        return missedTiles;
     }
 
-    void paintTileText(TileSet ts, Graphics g, MapView mv, int zoom, Tile t) {
+    void paintTileText(TileSet ts, Tile tile, Graphics g, MapView mv, int zoom, Tile t) {
         int fontHeight = g.getFontMetrics().getHeight();
-
-        SlippyMapTile tile = getTile(t.x, t.y, zoom);
-        if (tile == null) {
+        if (tile == null)
             return;
-        }
-        if (tile.getImage() == null) {
-            loadSingleTile(tile);
-        }
-        Point p = t.pixelPos(zoom);
+        Point p = pixelPos(t);
         int texty = p.y + 2 + fontHeight;
 
         if (SlippyMapPreferences.getDrawDebug()) {
-            g.drawString("x=" + t.x + " y=" + t.y + " z=" + zoom + "", p.x + 2, texty);
+            g.drawString("x=" + t.getXtile() + " y=" + t.getYtile() + " z=" + zoom + "", p.x + 2, texty);
             texty += 1 + fontHeight;
-            if ((t.x % 32 == 0) && (t.y % 32 == 0)) {
-                g.drawString("x=" + t.x / 32 + " y=" + t.y / 32 + " z=7", p.x + 2, texty);
+            if ((t.getXtile() % 32 == 0) && (t.getYtile() % 32 == 0)) {
+                g.drawString("x=" + t.getXtile() / 32 + " y=" + t.getYtile() / 32 + " z=7", p.x + 2, texty);
                 texty += 1 + fontHeight;
             }
         }// end of if draw debug
 
-        String md = tile.getMetadata();
-        if (md != null) {
-            g.drawString(md, p.x + 2, texty);
-            texty += 1 + fontHeight;
+        if (tile == showMetadataTile) {
+            String md = tile.toString();
+            if (md != null) {
+                g.drawString(md, p.x + 2, texty);
+                texty += 1 + fontHeight;
+            }
         }
 
         String tileStatus = tile.getStatus();
-        Image tileImage = tile.getImage();
-        if (!imageLoaded(tileImage)) {
+        if (!tile.isLoaded()) {
             g.drawString(tr("image " + tileStatus), p.x + 2, texty);
             texty += 1 + fontHeight;
         }
-        /*
-        We already do repaint when the images load
-        we don't need to poll like this
-        */
-        if (!imageLoaded(tileImage)) {
-            needRedraw = true;
-            Main.map.repaint(100);
-        }
 
+        int xCursor = -1;
+        int yCursor = -1;
         if (SlippyMapPreferences.getDrawDebug()) {
-            if (ts.leftTile(t)) {
-                if (t.y % 32 == 31) {
+            if (yCursor < t.getYtile()) {
+                if (t.getYtile() % 32 == 31) {
                     g.fillRect(0, p.y - 1, mv.getWidth(), 3);
                 } else {
                     g.drawLine(0, p.y, mv.getWidth(), p.y);
                 }
+                yCursor = t.getYtile();
+            }
+            // This draws the vertical lines for the entire
+            // column. Only draw them for the top tile in
+            // the column.
+            if (xCursor < t.getXtile()) {
+                if (SlippyMapPreferences.getDrawDebug()) {
+                    if (t.getXtile() % 32 == 0) {
+                        // level 7 tile boundary
+                        g.fillRect(p.x - 1, 0, 3, mv.getHeight());
+                    } else {
+                        g.drawLine(p.x, 0, p.x, mv.getHeight());
+                    }
+                }
+                xCursor = t.getXtile();
             }
-        }// /end of if draw debug
-    }
-
-    private class Tile {
-        public int x;
-        public int y;
-
-        Tile(int x, int y) {
-            this.x = x;
-            this.y = y;
         }
+    }
 
-        public Point pixelPos(int zoom) {
-            double lon = tileXToLon(this.x, zoom);
-            LatLon tmpLL = new LatLon(tileYToLat(this.y, zoom), lon);
-            MapView mv = Main.map.mapView;
-            return mv.getPoint(tmpLL);
-        }
+    public Point pixelPos(LatLon ll) {
+        return Main.map.mapView.getPoint(ll);
+    }
+    public Point pixelPos(Tile t) {
+        double lon = tileXToLon(t.getXtile(), t.getZoom());
+        LatLon tmpLL = new LatLon(tileYToLat(t.getYtile(), t.getZoom()), lon);
+        return pixelPos(tmpLL);
     }
     private class TileSet {
         int z12x0, z12x1, z12y0, z12y1;
@@ -559,10 +712,10 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
 
         TileSet(LatLon topLeft, LatLon botRight, int zoom) {
             this.zoom = zoom;
-            z12x0 = lonToTileX(topLeft.lon(), zoom) - 1;
-            z12y0 = latToTileY(topLeft.lat(),  zoom) - 1;
-            z12x1 = lonToTileX(botRight.lon(), zoom) + 1;
-            z12y1 = latToTileY(botRight.lat(), zoom) + 1;
+            z12x0 = lonToTileX(topLeft.lon(),  zoom);
+            z12y0 = latToTileY(topLeft.lat(),  zoom);
+            z12x1 = lonToTileX(botRight.lon(), zoom);
+            z12y1 = latToTileY(botRight.lat(), zoom);
             if (z12x0 > z12x1) {
                 int tmp = z12x0;
                 z12x0 = z12x1;
@@ -579,49 +732,93 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
             if (z12x1 > tileMax) z12x1 = tileMax;
             if (z12y1 > tileMax) z12y1 = tileMax;
         }
+        boolean tooSmall() {
+            return this.tilesSpanned() < 2.1;
+        }
+        boolean tooLarge() {
+            return this.tilesSpanned() > 10;
+        }
+        boolean insane() {
+            return this.tilesSpanned() > 100;
+        }
         double tilesSpanned() {
-            int x_span = z12x1 - z12x0;
-            int y_span = z12y1 - z12y0;
-            return Math.sqrt(1.0 * x_span * y_span);
+            return Math.sqrt(1.0 * this.size());
+        }
+
+        double size() {
+            double x_span = z12x1 - z12x0 + 1.0;
+            double y_span = z12y1 - z12y0 + 1.0;
+            return x_span * y_span;
         }
 
         /*
-         * This is pretty silly. Should probably just be implemented as an
-         * iterator to keep from having to instantiate all these tiles.
+         * Get all tiles represented by this TileSet that are
+         * already in the tileCache.
          */
         List<Tile> allTiles()
         {
+            return this.allTiles(false);
+        }
+        private List<Tile> allTiles(boolean create)
+        {
             List<Tile> ret = new ArrayList<Tile>();
+            // Don't even try to iterate over the set.
+            // Someone created a crazy number of them
+            if (this.insane())
+                return ret;
             for (int x = z12x0; x <= z12x1; x++) {
                 for (int y = z12y0; y <= z12y1; y++) {
-                    Tile t = new Tile(x % tileMax, y % tileMax);
-                    ret.add(t);
+                    Tile t;
+                    if (create)
+                        t = getOrCreateTile(x % tileMax, y % tileMax, zoom);
+                    else
+                        t = getTile(x % tileMax, y % tileMax, zoom);
+                    if (t != null)
+                        ret.add(t);
                 }
             }
             return ret;
         }
-
+        void loadAllTiles(boolean force)
+        {
+            List<Tile> tiles = this.allTiles(true);
+            boolean autoload = SlippyMapPreferences.getAutoloadTiles();
+            if (!autoload && !force)
+               return;
+            int nr_queued = 0;
+            for (Tile t : tiles) {
+                if (loadTile(t))
+                    nr_queued++;
+            }
+            if (debug)
+                if (nr_queued > 0)
+                    out("queued to load: " + nr_queued + "/" + tiles.size() + " tiles at zoom: " + zoom);
+        }
         boolean topTile(Tile t) {
-            if (t.y == z12y0 )
+            if (t.getYtile() == z12y0 )
                 return true;
             return false;
         }
 
         boolean leftTile(Tile t) {
-            if (t.x == z12x0 )
+            if (t.getXtile() == z12x0 )
                 return true;
             return false;
         }
     }
 
+    boolean autoZoomEnabled()
+    {
+        return autoZoomPopup.isSelected();
+    }
     /**
      */
     @Override
-    public void paint(Graphics g, MapView mv) {
-        long start = System.currentTimeMillis();
+    public void paint(Graphics2D g, MapView mv, Bounds bounds) {
+        //long start = System.currentTimeMillis();
         LatLon topLeft = mv.getLatLon(0, 0);
         LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
-        Graphics oldg = g;
+        Graphics2D oldg = g;
 
         if (botRight.lon() == 0.0 || botRight.lat() == 0) {
             Main.debug("still initializing??");
@@ -633,7 +830,8 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
                 && mv.getWidth() == bufferImage.getWidth(null) && mv.getHeight() == bufferImage.getHeight(null)
                 && !needRedraw) {
 
-            this.debug("drawing buffered image");
+            if (debug)
+                out("drawing buffered image");
             g.drawImage(bufferImage, 0, 0, null);
             return;
         }
@@ -642,122 +840,125 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
         lastTopLeft = topLeft;
         lastBotRight = botRight;
         bufferImage = mv.createImage(mv.getWidth(), mv.getHeight());
-        g = bufferImage.getGraphics();
+        g = (Graphics2D) bufferImage.getGraphics();
 
-        TileSet ts = new TileSet(topLeft, botRight, currentZoomLevel);
         int zoom = currentZoomLevel;
-
-        if (zoomDecreaseAllowed() && (ts.tilesSpanned() > 18)) {
-            this.debug("too many tiles, decreasing zoom from " + currentZoomLevel);
-            if (decreaseZoomLevel())
-                this.paint(oldg, mv);
-            return;
-        }
-        if (zoomIncreaseAllowed() && (ts.tilesSpanned() < 1.0)) {
-            this.debug("doesn't even cover one tile (" + ts.tilesSpanned()
-                       + "), increasing zoom from " + currentZoomLevel);
-            if (increaseZoomLevel())
-                 this.paint(oldg, mv);
-            return;
+        TileSet ts = new TileSet(topLeft, botRight, zoom);
+
+        if (autoZoomEnabled()) {
+            if (zoomDecreaseAllowed() && ts.tooLarge()) {
+                if (debug)
+                    out("too many tiles, decreasing zoom from " + currentZoomLevel);
+                if (decreaseZoomLevel())
+                    this.paint(oldg, mv, bounds);
+                return;
+            }
+            if (zoomIncreaseAllowed() && ts.tooSmall()) {
+                if (debug)
+                    out("too zoomed in, (" + ts.tilesSpanned()
+                        + "), increasing zoom from " + currentZoomLevel);
+                if (increaseZoomLevel())
+                     this.paint(oldg, mv, bounds);
+                return;
+            }
         }
 
-        if (ts.tilesSpanned() <= 0) {
-            System.out.println("doesn't even cover one tile, increasing zoom from " + currentZoomLevel);
-            if (increaseZoomLevel()) {
-                this.paint(oldg, mv);
-            }
-            return;
+        // Too many tiles... refuse to draw
+        if (!ts.tooLarge()) {
+            //out("size: " + ts.size() + " spanned: " + ts.tilesSpanned());
+            ts.loadAllTiles(false);
         }
 
         int fontHeight = g.getFontMetrics().getHeight();
 
         g.setColor(Color.DARK_GRAY);
 
-        /*
-         * Go looking for tiles in zoom levels *other* than the current
-         * one. Even if they might look bad, they look better than a
-         * blank tile.
-         */
-        int otherZooms[] = {-5, -4, -3, 2, -2, 1, -1};
-        for (int zoomOff : otherZooms) {
-            int zoom2 = currentZoomLevel + zoomOff;
-            if ((zoom2 < SlippyMapPreferences.getMinZoomLvl()) ||
-                (zoom2 > SlippyMapPreferences.getMaxZoomLvl())) {
-                continue;
+        List<Tile> missedTiles = this.paintTileImages(g, ts, currentZoomLevel, null);
+        int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5};
+        for (int zoomOffset : otherZooms) {
+            if (!autoZoomEnabled())
+                break;
+            if (!SlippyMapPreferences.getAutoloadTiles())
+                break;
+            int newzoom = currentZoomLevel + zoomOffset;
+            if (missedTiles.size() <= 0)
+                break;
+            List<Tile> newlyMissedTiles = new LinkedList<Tile>();
+            for (Tile missed : missedTiles) {
+                Tile t2 = tempCornerTile(missed);
+                LatLon topLeft2  = tileLatLon(missed);
+                LatLon botRight2 = tileLatLon(t2);
+                TileSet ts2 = new TileSet(topLeft2, botRight2, newzoom);
+                if (ts2.tooLarge())
+                    continue;
+                newlyMissedTiles.addAll(this.paintTileImages(g, ts2, newzoom, missed));
             }
-            TileSet ts2 = new TileSet(topLeft, botRight, zoom2);
-            int painted = this.paintTileImages(g, ts2, zoom2);
-            //if (painted > 0)
-            //    System.out.println("painted " + painted + " tiles from zoom: " + zoom2);
+            missedTiles = newlyMissedTiles;
+        }
+        if (debug && missedTiles.size() > 0) {
+            out("still missed "+missedTiles.size()+" in the end");
         }
-        /*
-         * Save this for last since it will get painted over all the others
-         */
-        this.paintTileImages(g, ts, currentZoomLevel);
         g.setColor(Color.red);
 
+        // The current zoom tileset is guaranteed to have all of
+        // its tiles
         for (Tile t : ts.allTiles()) {
-            // This draws the vertical lines for the entire
-            // column. Only draw them for the top tile in
-            // the column.
-            if (ts.topTile(t)) {
-                Point p = t.pixelPos(currentZoomLevel);
-                if (SlippyMapPreferences.getDrawDebug()) {
-                    if (t.x % 32 == 0) {
-                        // level 7 tile boundary
-                        g.fillRect(p.x - 1, 0, 3, mv.getHeight());
-                    } else {
-                        g.drawLine(p.x, 0, p.x, mv.getHeight());
-                    }
-                }
-            }
-            this.paintTileText(ts, g, mv, currentZoomLevel, t);
+            this.paintTileText(ts, t, g, mv, currentZoomLevel, t);
         }
-        float fadeBackground = SlippyMapPreferences.getFadeBackground();
         oldg.drawImage(bufferImage, 0, 0, null);
 
-        if (lastImageScale != null) {
+        if (autoZoomEnabled() && lastImageScale != null) {
             // If each source image pixel is being stretched into > 3
             // drawn pixels, zoom in... getting too pixelated
             if (lastImageScale > 3 && zoomIncreaseAllowed()) {
-                if (SlippyMapPreferences.getAutozoom()) {
-                    this.debug("autozoom increase: scale: " + lastImageScale);
-                    increaseZoomLevel();
-                }
-                this.paint(oldg, mv);
+                if (debug)
+                    out("autozoom increase: scale: " + lastImageScale);
+                increaseZoomLevel();
+                this.paint(oldg, mv, bounds);
             // If each source image pixel is being squished into > 0.32
             // of a drawn pixels, zoom out.
             } else if ((lastImageScale < 0.45) && (lastImageScale > 0) && zoomDecreaseAllowed()) {
-                if (SlippyMapPreferences.getAutozoom()) {
-                    this.debug("autozoom decrease: scale: " + lastImageScale);
-                    decreaseZoomLevel();
-                }
-                this.paint(oldg, mv);
+                if (debug)
+                    out("autozoom decrease: scale: " + lastImageScale);
+                decreaseZoomLevel();
+                this.paint(oldg, mv, bounds);
             }
         }
-        g.setColor(Color.black);
-        g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
+        //g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
+        oldg.setColor(Color.black);
+        if (ts.insane()) {
+            oldg.drawString("zoom in to load any tiles", 120, 120);
+        } else if (ts.tooLarge()) {
+            oldg.drawString("zoom in to load more tiles", 120, 120);
+        } else if (ts.tooSmall()) {
+            oldg.drawString("increase zoom level to see more detail", 120, 120);
+        }
     }// end of paint method
 
     /**
      * This isn't very efficient, but it is only used when the
      * user right-clicks on the map.
      */
-    SlippyMapTile getTileForPixelpos(int px, int py) {
+    Tile getTileForPixelpos(int px, int py) {
+        if (debug)
+            out("getTileForPixelpos("+px+", "+py+")");
         MapView mv = Main.map.mapView;
         Point clicked = new Point(px, py);
         LatLon topLeft = mv.getLatLon(0, 0);
         LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
-        TileSet ts = new TileSet(topLeft, botRight, currentZoomLevel);
         int z = currentZoomLevel;
+        TileSet ts = new TileSet(topLeft, botRight, z);
 
+        if (!ts.tooLarge())
+            ts.loadAllTiles(false); // make sure there are tile objects for all tiles
         Tile clickedTile = null;
         Point p1 = null, p2 = null;
         for (Tile t1 : ts.allTiles()) {
-            Tile t2 = new Tile(t1.x+1, t1.y+1);
-            p1 = t1.pixelPos(z);
-            p2 = t2.pixelPos(z);
-            Rectangle r = new Rectangle(p1,new Dimension(p2.x, p2.y));
+            Tile t2 = tempCornerTile(t1);
+            Rectangle r = new Rectangle(pixelPos(t1));
+            r.add(pixelPos(t2));
+            if (debug)
+                out("r: " + r + " clicked: " + clicked);
             if (!r.contains(clicked))
                 continue;
             clickedTile  = t1;
@@ -765,11 +966,9 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
         }
         if (clickedTile == null)
              return null;
-        System.out.println("clicked on tile: " + clickedTile.x + " " + clickedTile.y +
+        System.out.println("clicked on tile: " + clickedTile.getXtile() + " " + clickedTile.getYtile() +
                            " scale: " + lastImageScale + " currentZoomLevel: " + currentZoomLevel);
-        SlippyMapTile tile = getOrCreateTile(clickedTile.x, clickedTile.y, currentZoomLevel);
-        checkTileStorage();
-        return tile;
+        return clickedTile;
     }
 
     @Override
@@ -832,64 +1031,9 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
         return x * 45.0 / Math.pow(2.0, zoom - 3) - 180.0;
     }
 
-    private SlippyMapTile imgToTile(Image img) {
-        // we use the enumeration to avoid ConcurrentUpdateExceptions
-        // with other users of the tileStorage
-        Enumeration<SlippyMapTile> e = tileStorage.elements();
-        while (e.hasMoreElements()) {
-            SlippyMapTile t = e.nextElement();
-            if (t.getImageNoTimestamp() != img) {
-                continue;
-            }
-            return t;
-        }
-        return null;
-    }
-
     private static int nr_loaded = 0;
     private static int at_zoom = -1;
 
-    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
-        boolean error = (infoflags & ERROR) != 0;
-        boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
-        boolean success = ((infoflags & (ALLBITS)) != 0);
-        SlippyMapTile imageTile = imgToTile(img);
-        if (success || error) {
-            this.debug("tile done loading: " + imageTile);
-            markDone(img);
-        }
-        if (imageTile == null) {
-            System.err.println("no tile for image");
-            return false;
-        }
-
-        if ((infoflags & ERROR) != 0) {
-            String url; // = "unknown";
-            url = imageTile.getImageURL().toString();
-            System.err.println("imageUpdate(" + img + ") error " + url + ")");
-        }
-        if (((infoflags & ALLBITS) != 0)) {
-            int z = imageTile.getZoom();
-            if (z == at_zoom) {
-                nr_loaded++;
-            } else {
-                //System.out.println("downloaded " + nr_loaded + " at: " + at_zoom + " now going to " + z +
-                //                " class: " + img.getClass());
-                nr_loaded = 0;
-                at_zoom = z;
-            }
-        }
-        if ((infoflags & SOMEBITS) != 0) {
-            // if (y%100 == 0)
-            //System.out.println("imageUpdate("+img+") SOMEBITS ("+x+","+y+")");
-        }
-        // Repaint immediately if we are done, otherwise batch up
-        // repaint requests every 100 milliseconds
-        needRedraw = true;
-        Main.map.repaint(done ? 0 : 100);
-        return !done;
-    }
-
     /*
      * (non-Javadoc)
      *
@@ -903,8 +1047,12 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
             // when fade background changed, no need to clear tile storage
             // TODO move this code to SlippyMapPreferences class.
             if (!key.equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) {
-                clearTileStorage();
+                autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
+            }
+            if (key.equals(SlippyMapPreferences.PREFERENCE_TILE_SOURCE)) {
+                newTileStorage();
             }
+            redraw();
         }
     }
 
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java
index 3ea6ea8..13b956e 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java
@@ -9,6 +9,13 @@ import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JSlider;
 import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.util.Collection;
+
+import org.openstreetmap.gui.jmapviewer.*;
+import org.openstreetmap.gui.jmapviewer.interfaces.*;
 
 import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
@@ -28,18 +35,21 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
     
     private JCheckBox autozoomActive = new JCheckBox(tr("autozoom"));
     private JCheckBox autoloadTiles = new JCheckBox(tr("autoload tiles"));
-    private JSpinner maxZoomLvl = new JSpinner();
+    private JSpinner maxZoomLvl;
+    private JSpinner minZoomLvl = new JSpinner();
     private JSlider fadeBackground = new JSlider(0, 100);
     
     public void addGui(PreferenceDialog gui)
     {
+        minZoomLvl = new JSpinner(new SpinnerNumberModel(SlippyMapPreferences.DEFAULT_MIN_ZOOM, SlippyMapPreferences.MIN_ZOOM, SlippyMapPreferences.MAX_ZOOM, 1));
+        maxZoomLvl = new JSpinner(new SpinnerNumberModel(SlippyMapPreferences.DEFAULT_MAX_ZOOM, SlippyMapPreferences.MIN_ZOOM, SlippyMapPreferences.MAX_ZOOM, 1));
         //String description = tr("A plugin that adds to JOSM new layer. This layer could render external tiles.");
         JPanel slippymapTab = gui.createPreferenceTab("slippymap.png", tr("SlippyMap"), tr("Settings for the SlippyMap plugin."));
-        String[] allMapUrls = SlippyMapPreferences.getAllMapUrls();
-        tileSourceCombo = new JComboBox(allMapUrls);
-        tileSourceCombo.setEditable(true);
-        String source = SlippyMapPreferences.getMapUrl();
-        tileSourceCombo.setSelectedItem(source);
+        Collection<TileSource> allSources = SlippyMapPreferences.getAllMapSources();
+        //Collection<String> allSources = SlippyMapPreferences.getAllMapNames();
+        tileSourceCombo = new JComboBox(allSources.toArray());
+        //tileSourceCombo.setEditable(true);
+        tileSourceCombo.setSelectedItem(SlippyMapPreferences.getMapSource());
         slippymapTab.add(new JLabel(tr("Tile Sources")), GBC.std());
         slippymapTab.add(GBC.glue(5, 0), GBC.std());
         slippymapTab.add(tileSourceCombo, GBC.eol().fill(GBC.HORIZONTAL));
@@ -52,20 +62,51 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
         slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
         slippymapTab.add(autoloadTiles, GBC.eol().fill(GBC.HORIZONTAL));
         
+        slippymapTab.add(new JLabel(tr("Min zoom lvl: ")), GBC.std());
+        slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+        slippymapTab.add(this.minZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
+        
         slippymapTab.add(new JLabel(tr("Max zoom lvl: ")), GBC.std());
         slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
         slippymapTab.add(this.maxZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
-        
+
         slippymapTab.add(new JLabel(tr("Fade background: ")), GBC.std());
         slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
         slippymapTab.add(this.fadeBackground, GBC.eol().fill(GBC.HORIZONTAL));
         
         slippymapTab.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
 
+        tileSourceCombo.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                //Main.debug("updating spinner models because of tileSourceCombo");
+                updateSpinnerModels();
+            }
+        });
+
         this.loadSettings();
+        updateSpinnerModels();
     }
 
 
+    void updateSpinnerModel(JSpinner s, int min, int max)
+    {
+        int val = (Integer)s.getValue();
+        //Main.debug("updating spinner model val: " + val + " " + min + "->" + max);
+        val = Math.min(max, val);
+        val = Math.max(min, val);
+        SpinnerNumberModel model = new SpinnerNumberModel(val, min, max, 1);
+        s.setModel(model);
+    }
+
+    void updateSpinnerModels()
+    {
+        TileSource ts = (TileSource)this.tileSourceCombo.getSelectedItem();
+        int min = ts.getMinZoom();
+        int max = ts.getMaxZoom();
+        updateSpinnerModel(minZoomLvl, min, max);
+        updateSpinnerModel(maxZoomLvl, min, max);
+    }
+
     /**
      * <p>
      * Load settings from {@link SlippyMapPreferences} class. Loaded preferences are stored to local GUI components.
@@ -74,6 +115,7 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
      * 	<li>autozoom - {@link #autozoomActive} - {@link SlippyMapPreferences#getAutozoom()}</li>
      * 	<li>autoload - {@link #autoloadTiles} - {@link SlippyMapPreferences#getAutoloadTiles()}</li>
      * 	<li>maxZoomLvl - {@link #maxZoomLvl} - {@link SlippyMapPreferences#getMaxZoomLvl()}</li>
+     * 	<li>minZoomLvl - {@link #minZoomLvl} - {@link SlippyMapPreferences#getMaxZoomLvl()}</li>
      * </ul>
      * </p>
      */
@@ -81,6 +123,7 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
         this.autozoomActive.setSelected(SlippyMapPreferences.getAutozoom());
         this.autoloadTiles.setSelected(SlippyMapPreferences.getAutoloadTiles());
         this.maxZoomLvl.setValue(SlippyMapPreferences.getMaxZoomLvl());
+        this.minZoomLvl.setValue(SlippyMapPreferences.getMinZoomLvl());
         this.fadeBackground.setValue(Math.round(SlippyMapPreferences.getFadeBackground()*100f));
     }
     
@@ -94,10 +137,11 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
      */
     public boolean ok()
     {
-        SlippyMapPreferences.setMapUrl(this.tileSourceCombo.getSelectedItem().toString());
+        SlippyMapPreferences.setMapSource((TileSource)this.tileSourceCombo.getSelectedItem());
         SlippyMapPreferences.setAutozoom(this.autozoomActive.isSelected());
         SlippyMapPreferences.setAutoloadTiles(this.autoloadTiles.isSelected());
         SlippyMapPreferences.setMaxZoomLvl((Integer)this.maxZoomLvl.getValue());
+        SlippyMapPreferences.setMinZoomLvl((Integer)this.minZoomLvl.getValue());
         SlippyMapPreferences.setFadeBackground(this.fadeBackground.getValue()/100f);
         //restart isn't required
         return false;
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
index ebae7ce..814a0ef 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
@@ -1,43 +1,65 @@
 package org.openstreetmap.josm.plugins.slippymap;
 
 import org.openstreetmap.josm.Main;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import org.openstreetmap.gui.jmapviewer.*;
+import org.openstreetmap.gui.jmapviewer.interfaces.*;
 
 /**
  * Preferences for Slippy Map Tiles
- * 
+ *
  * @author Hakan Tandogan <hakan at gurkensalat.com>
  * @author LuVar <lubomir.varga at freemap.sk>
- * 
+ *
  */
 public class SlippyMapPreferences
 {
     public static final String PREFERENCE_PREFIX   = "slippymap";
 
-    public static final String PREFERENCE_TILE_URL = PREFERENCE_PREFIX + ".tile_url";
+    public static final String PREFERENCE_TILE_CUSTOM_SOURCE = PREFERENCE_PREFIX + ".custom_tile_source_";
+    public static final String PREFERENCE_TILE_SOURCE = PREFERENCE_PREFIX + ".tile_source";
     public static final String PREFERENCE_AUTOZOOM = PREFERENCE_PREFIX + ".autozoom";
     public static final String PREFERENCE_AUTOLOADTILES = PREFERENCE_PREFIX + ".autoload_tiles";
     public static final String PREFERENCE_MIN_ZOOM_LVL = PREFERENCE_PREFIX + ".min_zoom_lvl";
     public static final String PREFERENCE_MAX_ZOOM_LVL = PREFERENCE_PREFIX + ".max_zoom_lvl";
+    public static final String PREFERENCE_LAST_ZOOM = PREFERENCE_PREFIX + ".last_zoom_lvl";
     public static final String PREFERENCE_FADE_BACKGROUND = PREFERENCE_PREFIX + ".fade_background";
     public static final String PREFERENCE_DRAW_DEBUG = PREFERENCE_PREFIX + ".draw_debug";
-    
-    public static String getMapUrl()
-    {
-        String url = Main.pref.get(PREFERENCE_TILE_URL);
 
-        if (url == null || "".equals(url))
-        {
-            url = "http://tah.openstreetmap.org/Tiles/tile"; // t at h
-            Main.pref.put(PREFERENCE_TILE_URL, url);
-        }
+    public static final int MAX_ZOOM = 30;
+    public static final int MIN_ZOOM = 2;
+    public static final int DEFAULT_MAX_ZOOM = 20;
+    public static final int DEFAULT_MIN_ZOOM = 2;
 
-        return url;
+    public static TileSource getMapSource()
+    {
+        String name = Main.pref.get(PREFERENCE_TILE_SOURCE);
+        return getMapSource(name);
+    }
+    public static TileSource getMapSource(String name)
+    {
+        List<TileSource> sources = SlippyMapPreferences.getAllMapSources();
+        TileSource source = sources.get(0);
+        if (name == null || "".equals(name)) {
+            name = source.getName();
+            Main.pref.put(PREFERENCE_TILE_SOURCE, name);
+        }
+        for (TileSource s : sources) {
+            if (!name.equals(s.getName()))
+                continue;
+            source = s;
+            break;
+        }
+        return source;
     }
-    
-    public static void setMapUrl(String mapUrl) {
-    	Main.pref.put(SlippyMapPreferences.PREFERENCE_TILE_URL, mapUrl);
+
+    public static void setMapSource(TileSource source) {
+    	Main.pref.put(SlippyMapPreferences.PREFERENCE_TILE_SOURCE, source.getName());
     }
-    
+
     public static boolean getAutozoom()
     {
         String autozoom = Main.pref.get(PREFERENCE_AUTOZOOM);
@@ -50,15 +72,28 @@ public class SlippyMapPreferences
 
         return Boolean.parseBoolean(autozoom);
     }
-    
+
     public static void setAutozoom(boolean autozoom) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_AUTOZOOM, autozoom);
     }
-    
+
     public static void setDrawDebug(boolean drawDebug) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_DRAW_DEBUG, drawDebug);
     }
-    
+
+    public static void setLastZoom(int zoom) {
+    	Main.pref.put(SlippyMapPreferences.PREFERENCE_LAST_ZOOM, ""+zoom);
+    }
+    public static int getLastZoom() {
+    	int ret = -1;
+        String pref = Main.pref.get(SlippyMapPreferences.PREFERENCE_LAST_ZOOM);
+        try {
+            ret = Integer.parseInt(pref);
+        } catch (NumberFormatException e) {
+        }
+        return ret;
+    }
+
     public static boolean getDrawDebug()
     {
         String drawDebug = Main.pref.get(PREFERENCE_DRAW_DEBUG);
@@ -71,7 +106,7 @@ public class SlippyMapPreferences
 
         return Boolean.parseBoolean(drawDebug);
     }
-    
+
     public static boolean getAutoloadTiles()
     {
         String autoloadTiles = Main.pref.get(PREFERENCE_AUTOLOADTILES);
@@ -84,13 +119,13 @@ public class SlippyMapPreferences
 
         return Boolean.parseBoolean(autoloadTiles);
     }
-    
+
     public static void setFadeBackground(float fadeBackground) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND, fadeBackground + "");
     }
 
     /**
-     * 
+     *
      * @return	number between 0 and 1, inclusive
      */
     public static float getFadeBackground() {
@@ -101,7 +136,7 @@ public class SlippyMapPreferences
         	fadeBackground = "0.0";
             Main.pref.put(PREFERENCE_FADE_BACKGROUND, fadeBackground);
         }
-        
+
         float parsed;
         try {
         	parsed = Float.parseFloat(fadeBackground);
@@ -120,100 +155,200 @@ public class SlippyMapPreferences
         }
         return parsed;
     }
-    
+
     public static void setAutoloadTiles(boolean autoloadTiles) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_AUTOLOADTILES, autoloadTiles);
     }
-    
-    public static int getMaxZoomLvl()
-    {
-        String maxZoomLvl = Main.pref.get(PREFERENCE_MAX_ZOOM_LVL);
 
-        if (maxZoomLvl == null || "".equals(maxZoomLvl))
-        {
-        	maxZoomLvl = "17";
-            Main.pref.put(PREFERENCE_MAX_ZOOM_LVL, maxZoomLvl);
-        }
-
-        int navrat;
+    private static int getIntPref(String prefName, int def)
+    {
+        int pref;
         try {
-        	navrat = Integer.parseInt(maxZoomLvl);
+        	//Should we use Main.pref.getInteger(str)?
+        	pref = Main.pref.getInteger(prefName, def);
         } catch (Exception ex) {
-        	throw new RuntimeException("Problem while converting string to int. Converting value of prefetrences " + PREFERENCE_MAX_ZOOM_LVL + ". Value=\"" + maxZoomLvl + "\". Should be an integer. Error: " + ex.getMessage(), ex);
+            String str = Main.pref.get(prefName);
+            Main.pref.put(prefName, null);
+        	throw new RuntimeException("Problem while converting string to int. "
+                                       + "Converting value of preferences "
+                                       + prefName + ". Value=\"" + str
+                                       + "\". Should be an integer. Error: "
+                                       + ex.getMessage(), ex);
         }
-        if(navrat > 30) {
-    		System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
-    		navrat = 30;
-    	}
-        //if(navrat < SlippyMapPreferences.getMinZoomLvl()) {
-    	//	System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
-    	//	navrat = SlippyMapPreferences.getMinZoomLvl();
-    	//}
-        return navrat;
+        return pref;
     }
-    
-    public static void setMaxZoomLvl(int maxZoomLvl) {
-    	if(maxZoomLvl > 30) {
+
+    static int checkMaxZoomLvl(int maxZoomLvl)
+    {
+    	if(maxZoomLvl > MAX_ZOOM) {
     		System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
-    		maxZoomLvl = 30;
+    		maxZoomLvl = MAX_ZOOM;
     	}
-    	if(maxZoomLvl < SlippyMapPreferences.getMinZoomLvl()) {
+    	if(maxZoomLvl < SlippyMapPreferences.__getMinZoomLvl()) {
     		System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
-    		maxZoomLvl = SlippyMapPreferences.getMinZoomLvl();
+    		maxZoomLvl = SlippyMapPreferences.__getMinZoomLvl();
     	}
-    	Main.pref.put(SlippyMapPreferences.PREFERENCE_MAX_ZOOM_LVL, "" + maxZoomLvl);
+        TileSource ts = getMapSource();
+        if (ts != null && ts.getMaxZoom() < SlippyMapPreferences.__getMinZoomLvl()) {
+    		System.err.println("decreasing maxZoomLvl to match new tile source");
+            maxZoomLvl = ts.getMaxZoom();
+        }
+        return maxZoomLvl;
     }
-    
-    public static int getMinZoomLvl()
-    {
-        String minZoomLvl = Main.pref.get(PREFERENCE_MIN_ZOOM_LVL);
 
-        if (minZoomLvl == null || "".equals(minZoomLvl))
-        {
-        	minZoomLvl = "" + (SlippyMapPreferences.getMaxZoomLvl() - 4);
-            Main.pref.put(PREFERENCE_MIN_ZOOM_LVL, minZoomLvl);
-        }
+    public static int getMaxZoomLvl()
+    {
+        int maxZoomLvl = getIntPref(PREFERENCE_MAX_ZOOM_LVL, DEFAULT_MAX_ZOOM);
+        return checkMaxZoomLvl(maxZoomLvl);
+    }
 
-        int navrat;
-        try {
-        	navrat = Integer.parseInt(minZoomLvl);
-        } catch (Exception ex) {
-        	throw new RuntimeException("Problem while converting string to int. Converting value of prefetrences " + PREFERENCE_MIN_ZOOM_LVL + ". Value=\"" + minZoomLvl + "\". Should be an integer. Error: " + ex.getMessage(), ex);
-        }
-        if(navrat < 2) {
-    		System.err.println("minZoomLvl shouldnt be lees than 2! Setting to 2.");
-    		navrat = 2;
-    	}
-        //if(navrat > SlippyMapPreferences.getMaxZoomLvl()) {
-    	//	System.err.println("minZoomLvl shouldnt be more than maxZoomLvl! Setting to maxZoomLvl.");
-    	//	navrat = SlippyMapPreferences.getMaxZoomLvl();
-    	//}
-        return navrat;
+    public static void setMaxZoomLvl(int maxZoomLvl) {
+        maxZoomLvl = checkMaxZoomLvl(maxZoomLvl);
+    	Main.pref.put(SlippyMapPreferences.PREFERENCE_MAX_ZOOM_LVL, "" + maxZoomLvl);
     }
-    
-    public static void setMinZoomLvl(int minZoomLvl) {
-    	if(minZoomLvl < 2) {
-    		System.err.println("minZoomLvl shouldnt be lees than 2! Setting to 2.");
-    		minZoomLvl = 2;
+
+    static int checkMinZoomLvl(int minZoomLvl)
+    {
+        if(minZoomLvl < MIN_ZOOM) {
+    		System.err.println("minZoomLvl shouldnt be lees than "+MIN_ZOOM+"! Setting to that.");
+    		minZoomLvl = MIN_ZOOM;
     	}
     	if(minZoomLvl > SlippyMapPreferences.getMaxZoomLvl()) {
     		System.err.println("minZoomLvl shouldnt be more than maxZoomLvl! Setting to maxZoomLvl.");
     		minZoomLvl = SlippyMapPreferences.getMaxZoomLvl();
     	}
+        return minZoomLvl;
+    }
+
+    private static int __getMinZoomLvl()
+    {
+        // We can use this internally
+        return getIntPref(PREFERENCE_MIN_ZOOM_LVL, DEFAULT_MIN_ZOOM);
+    }
+    public static int getMinZoomLvl()
+    {
+        return checkMinZoomLvl(__getMinZoomLvl());
+    }
+
+    public static void setMinZoomLvl(int minZoomLvl) {
+        minZoomLvl = checkMinZoomLvl(minZoomLvl);
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_MIN_ZOOM_LVL, "" + minZoomLvl);
     }
-    
-    public static String[] getAllMapUrls()
+
+    public static class Coastline extends OsmTileSource.AbstractOsmTileSource {
+        public Coastline() {
+            super("Coastline", "http://hypercube.telascience.org/tiles/1.0.0/coastline");
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+    public static class FreeMapySk extends OsmTileSource.AbstractOsmTileSource {
+        public FreeMapySk() {
+            super("freemapy.sk", "http://www.freemap.sk/layers/allinone/?");
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+    public static class FreeMapySkPokus extends OsmTileSource.AbstractOsmTileSource {
+        public FreeMapySkPokus() {
+            super("freemapy.sk pokus 2", "http://www.freemap.sk/layers/tiles/?");
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+
+    public static class NearMap extends OsmTileSource.AbstractOsmTileSource {
+        public NearMap() {
+            super("NearMap Australia", "http://www.nearmap.com/maps/hl=en&nml=Vert&");
+        }
+
+        public int getMaxZoom() {
+            return 21;
+        }
+
+        public String getTilePath(int zoom, int tilex, int tiley) {
+            return "z=" + zoom + "&x=" + tilex + "&y=" + tiley;
+        }
+
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+
+    public static class Custom extends OsmTileSource.AbstractOsmTileSource {
+        public Custom(String name, String url) {
+            super(name, url);
+        }
+        public Custom(String name, String url, String extension) {
+            super(name, url);
+            this.extension = extension;
+        }
+        String extension;
+        @Override
+        public String getExtension() {
+            if (extension == null)
+                return super.getExtension();
+            return extension;
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+
+    public static List<TileSource> getCustomSources()
     {
-        String[] defaultTileSources = new String[]
-        {
-                "http://tah.openstreetmap.org/Tiles/tile", // t at h
-                "http://tah.openstreetmap.org/Tiles/maplint", // maplint
-                "http://tile.openstreetmap.org", // mapnik
-                "http://hypercube.telascience.org/tiles/1.0.0/coastline", // coastline
-                "http://www.freemap.sk/layers/allinone/?", //freemapy.sk
-                "http://www.freemap.sk/layers/tiles/?", //freemapy.sk pokus 2
-        };
-        return defaultTileSources;
+        List<TileSource> ret = new ArrayList<TileSource>();
+        Map<String, String> customSources = Main.pref.getAllPrefix(PREFERENCE_TILE_CUSTOM_SOURCE);
+        for (String key : customSources.keySet()) {
+            String short_key = key.replaceFirst(PREFERENCE_TILE_CUSTOM_SOURCE, "");
+            // slippymap.custom_tile_source_1.name=OOC layer
+            // slippymap.custom_tile_source_1.url=http://a.ooc.openstreetmap.org/npe
+            // slippymap.custom_tile_source_1.ext=png
+
+            if (!(short_key.endsWith("name")))
+                continue;
+            String url_key = short_key.replaceFirst("name","url");
+            String ext_key = short_key.replaceFirst("name","ext");
+            String name = customSources.get(key);
+            String url = customSources.get(PREFERENCE_TILE_CUSTOM_SOURCE + url_key);
+            String ext = customSources.get(PREFERENCE_TILE_CUSTOM_SOURCE + ext_key);
+            // ext may be null, but that's OK
+            System.out.println("found new tile source: '" +name+"' url:'"+url+"'"+"' ext:'"+ext+"'");
+            ret.add(new Custom(name, url, ext));
+        }
+        return ret;
+    }
+
+    public static ArrayList<TileSource> sources = null;
+    public static List<TileSource> getAllMapSources()
+    {
+        if (sources != null)
+            return sources;
+        sources = new ArrayList<TileSource>();
+        // first here is the default if the user does not set one
+        sources.add(new OsmTileSource.Mapnik());
+        sources.add(new OsmTileSource.CycleMap());
+        sources.add(new OsmTileSource.TilesAtHome());
+        sources.add(new Coastline());
+        sources.add(new FreeMapySkPokus());
+        sources.add(new FreeMapySk());
+        sources.add(new NearMap());
+        sources.addAll(getCustomSources());
+        // Probably need to either add these or let users add them somehow
+        //      "http://hypercube.telascience.org/tiles/1.0.0/coastline", // coastline
+        //      "http://www.freemap.sk/layers/allinone/?", //freemapy.sk
+        //      "http://www.freemap.sk/layers/tiles/?", //freemapy.sk pokus 2
+        return sources;
+    }
+
+    public static TileSource getSourceNamed(String name)
+    {
+        for (TileSource s : getAllMapSources())
+            if (s.getName().equals(name))
+                return s;
+        return null;
     }
 }
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java
index 7e100ef..e69de29 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java
@@ -1,188 +0,0 @@
-package org.openstreetmap.josm.plugins.slippymap;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.Image;
-import java.awt.Toolkit;
-import java.awt.image.ImageObserver;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-
-/**
- * Class that contains information about one single slippy map tile.
- * 
- * @author Frederik Ramm <frederik at remote.org>
- * @author LuVar <lubomir.varga at freemap.sk>
- * @author Dave Hansen <dave at sr71.net>
- * 
- */
-public class SlippyMapTile implements ImageObserver
-{
-    private Image  tileImage;
-	private long timestamp;
-
-	private int x;
-	private int y;
-	private int z;
-	// Setting this to pending is a bit of a hack
-	// as it requires knowledge that SlippyMapLayer
-	// will put this tile in a queue before it calls
-	// loadImage().  But, this gives the best message
-	// to the user.
-	private String status = "pending download";
-    
-    private boolean imageDownloaded = false;
-
-    private String metadata;
-
-    public SlippyMapTile(int x, int y, int z)
-    {
-        this.x = x;
-        this.y = y;
-        this.z = z;
-		timestamp = System.currentTimeMillis();
-    }
-
-    public String getStatus()
-    {
-        return status;
-    }
-    public String getMetadata()
-    {
-        return metadata;
-    }
-
-    public URL getImageURL()
-    {
-        try
-        {
-            return new URL(SlippyMapPreferences.getMapUrl() + "/" + z + "/" + x + "/" + y + ".png");
-        }
-        catch (MalformedURLException mfu)
-        {
-            mfu.printStackTrace();
-        }
-            return null;
-        }
-
-    public Image loadImage()
-    {
-		// We do not update the timestamp in this function
-		// The download code prioritizes the most recent
-		// downloads and will download the oldest tiles last.
-        URL imageURL = this.getImageURL();
-        tileImage = Toolkit.getDefaultToolkit().createImage(imageURL);
-        Toolkit.getDefaultToolkit().prepareImage(tileImage, -1, -1, this);
-		Toolkit.getDefaultToolkit().sync();
-		status = "being downloaded";
-		return tileImage;
-    }
-	public String toString()
-	{
-			return "SlippyMapTile{zoom=" + z + " (" + x + "," + y + ") '" + status + "'}";
-	}
-	synchronized public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
-	{
-		if ((infoflags & ALLBITS) != 0) {
-    	    imageDownloaded = true;
-	        status = "downloaded";
-			if (tileImage == null) {
-                System.out.println("completed null'd image: " + this.toString());
-			}
-			tileImage = img;
-			return false;
-        }
-		return true;
-	}
-
-    public Image getImageNoTimestamp() {
-    	return tileImage;
-    }
-    
-    public Image getImage()
-    {
-        timestamp = System.currentTimeMillis();
-        return tileImage;
-    }
-
-    public int getZoom() {
-    	return z;
-    }
-    
-    synchronized public void dropImage()
-    {
-		if(tileImage != null) {
-			tileImage.flush();
-		    status = "dropped";
-		}
-		tileImage = null;
-		//  This should work in theory but doesn't seem to actually
-		//  reduce the X server memory usage
-		//tileImage.flush();
-	    imageDownloaded = false;
-    }
-    
-    public boolean isDownloaded() {
-    	return imageDownloaded;
-    }
-    
-    public void loadMetadata()
-    {
-        try
-        {
-            URL dev = new URL(
-                    "http://tah.openstreetmap.org/Tiles/info_short.php?x=" + x
-                            + "&y=" + y + "&z=" + z + "/layer=tile");
-            URLConnection devc = dev.openConnection();
-            BufferedReader in = new BufferedReader(new InputStreamReader(devc
-                    .getInputStream()));
-            metadata = tr(in.readLine());
-        }
-        catch (Exception ex)
-        {
-            metadata = tr("error loading metadata" + ex.toString());
-        }
-
-    }
-
-    public void requestUpdate()
-    {
-		if (z != 12) {
-            metadata = tr("error requesting update: not zoom-level 12");
-		}
-        try
-        {
-            URL dev = new URL("http://tah.openstreetmap.org/Request/create/?x=" + x
-                    + "&y=" + y + "&priority=1&src=slippymap_plugin");
-            URLConnection devc = dev.openConnection();
-            BufferedReader in = new BufferedReader(new InputStreamReader(devc
-                    .getInputStream()));
-			timestamp = System.currentTimeMillis();
-            metadata = tr("requested: {0}", tr(in.readLine()));
-        }
-        catch (Exception ex)
-        {
-            metadata = tr("error requesting update");
-        }
-    }
-
-    public long access_time()
-    {
-        return timestamp;
-    }
-
-    public boolean equals(Object o)
-    {
-        if (!(o instanceof SlippyMapTile))
-            return false;
-        SlippyMapTile other = (SlippyMapTile) o;
-        return (this.x == other.x && this.y == other.y && this.z == other.z);
-    }
-    SlippyMapKey getKey()
-    {
-	    return new SlippyMapKey(x, y, z);
-    }
-}
diff --git a/surveyor/build.xml b/surveyor/build.xml
index 18a9d5e..a435964 100644
--- a/surveyor/build.xml
+++ b/surveyor/build.xml
@@ -36,7 +36,7 @@
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
                 <attribute name="Plugin-Description" value="Allow adding markers/nodes on current gps positions."/>
                 <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Surveyor"/>
-                <attribute name="Plugin-Mainversion" value="2012"/>
+                <attribute name="Plugin-Mainversion" value="2401"/>
                 <attribute name="Plugin-Requires" value="livegps"/>
                 <attribute name="Plugin-Stage" value="60"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
diff --git a/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java b/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java
index b8b580a..5b772cf 100644
--- a/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java
+++ b/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java
@@ -20,7 +20,6 @@ import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.io.OsmWriter;
-import org.openstreetmap.josm.io.XmlWriter;
 
 /**
  * @author cdaller
@@ -54,7 +53,7 @@ public class AutoSaveEditLayerTimerTask extends TimerTask {
             File tmpFile = new File(file.getAbsoluteFile()+".tmp");
             System.out.println("AutoSaving osm data to file " + file.getAbsolutePath());
             synchronized(LiveGpsLock.class) {
-                OsmWriter w = new OsmWriter(new PrintWriter(new FileOutputStream(tmpFile)), false, dataset.version);
+                OsmWriter w = new OsmWriter(new PrintWriter(new FileOutputStream(tmpFile)), false, dataset.getVersion());
                 w.header();
                 w.writeDataSources(dataset);
                 w.writeContent(dataset);
diff --git a/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java b/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java
index fc5995a..6629050 100644
--- a/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java
+++ b/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java
@@ -69,7 +69,7 @@ public class SetNodeAction implements SurveyorAction {
         }
         node.put("created_by", "JOSM-surveyor-plugin");
         synchronized(LiveGpsLock.class) {
-            Main.map.mapView.getEditLayer().data.nodes.add(node);
+            Main.map.mapView.getEditLayer().data.addPrimitive(node);
             Main.main.getCurrentDataSet().setSelected(node);
         }
         Main.map.repaint();
diff --git a/svn-info.xml b/svn-info.xml
index 2d681c9..3d703d4 100644
--- a/svn-info.xml
+++ b/svn-info.xml
@@ -3,16 +3,16 @@
 <entry
    kind="dir"
    path="plugins"
-   revision="18465">
+   revision="18946">
 <url>http://svn.openstreetmap.org/applications/editors/josm/plugins</url>
 <repository>
 <root>http://svn.openstreetmap.org</root>
 <uuid>b9d5c4c9-76e1-0310-9c85-f3177eceb1e4</uuid>
 </repository>
 <commit
-   revision="18453">
-<author>daeron</author>
-<date>2009-11-04T14:17:43.184975Z</date>
+   revision="18946">
+<author>stoecker</author>
+<date>2009-12-04T21:55:59.367019Z</date>
 </commit>
 </entry>
 </info>
diff --git a/utilsplugin/build.xml b/utilsplugin/build.xml
index 3f9871e..f2664d2 100644
--- a/utilsplugin/build.xml
+++ b/utilsplugin/build.xml
@@ -24,7 +24,7 @@
                 <attribute name="Plugin-Class" value="UtilsPlugin.UtilsPlugin"/>
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
                 <attribute name="Plugin-Description" value="Several utilities that make your life easier: e.g. simplify way, join areas, jump to position."/>
-                <attribute name="Plugin-Mainversion" value="2166"/>
+                <attribute name="Plugin-Mainversion" value="2402"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
diff --git a/utilsplugin/josm-utilsplugin.launch b/utilsplugin/josm-utilsplugin.launch
new file mode 100644
index 0000000..a51a882
--- /dev/null
+++ b/utilsplugin/josm-utilsplugin.launch
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/JOSM/src/org/openstreetmap/josm/gui/MainApplication.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 6"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.openstreetmap.josm.gui.MainApplication"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="JOSM-utilsplugin"/>
+</launchConfiguration>
diff --git a/utilsplugin/src/UtilsPlugin/JoinAreasAction.java b/utilsplugin/src/UtilsPlugin/JoinAreasAction.java
index bcae225..397d74a 100644
--- a/utilsplugin/src/UtilsPlugin/JoinAreasAction.java
+++ b/utilsplugin/src/UtilsPlugin/JoinAreasAction.java
@@ -169,7 +169,8 @@ public class JoinAreasAction extends JosmAction {
 
         if(joinAreas(selWays[0], selWays[ways == 2 ? 1 : 0])) {
             Main.map.mapView.repaint();
-            DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
+            DataSet ds = Main.main.getCurrentDataSet();
+            ds.fireSelectionChanged();
         } else
             JOptionPane.showMessageDialog(Main.parent, tr("No intersection found. Nothing was changed."));
     }
@@ -441,7 +442,7 @@ public class JoinAreasAction extends JosmAction {
      */
     private ArrayList<RelationRole> removeFromRelations(OsmPrimitive osm) {
         ArrayList<RelationRole> result = new ArrayList<RelationRole>();
-        for (Relation r : Main.main.getCurrentDataSet().relations) {
+        for (Relation r : Main.main.getCurrentDataSet().getRelations()) {
             if (r.isDeleted() || r.incomplete) continue;
             for (RelationMember rm : r.getMembers()) {
                 if (rm.getMember() != osm) continue;
diff --git a/utilsplugin/src/UtilsPlugin/JumpToAction.java b/utilsplugin/src/UtilsPlugin/JumpToAction.java
index b85711a..b2dfb26 100644
--- a/utilsplugin/src/UtilsPlugin/JumpToAction.java
+++ b/utilsplugin/src/UtilsPlugin/JumpToAction.java
@@ -121,8 +121,8 @@ public class JumpToAction extends JosmAction implements MouseListener {
         if(!url.hasFocus()) return;
         Bounds b = OsmUrlToBounds.parse(url.getText());
         if (b != null) {
-            lat.setText(Double.toString((b.min.lat() + b.max.lat())/2));
-            lon.setText(Double.toString((b.min.lon() + b.max.lon())/2));
+            lat.setText(Double.toString((b.getMin().lat() + b.getMax().lat())/2));
+            lon.setText(Double.toString((b.getMin().lon() + b.getMax().lon())/2));
 
             int zoomLvl = 16;
             String[] args = url.getText().substring(url.getText().indexOf('?')+1).split("&");
diff --git a/validator/build.xml b/validator/build.xml
index aa74d50..ed54538 100644
--- a/validator/build.xml
+++ b/validator/build.xml
@@ -1,4 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+** This is the build.xml for the validator plugin
+**
+** Usage
+** =====
+** To build it run
+**
+**    > ant  dist
+**
+** To install the generated plugin locally (in your default plugin directory) run
+**
+**    > ant  install
+**
+** To build against the core in ../../core, create a correct manifest and deploy to
+** SVN, 
+**    set the properties commit.message and plugin.main.version
+** and run
+**    > ant  publish
+**
+**
+-->
 <project name="validator" default="dist" basedir=".">
+	
+	<!-- 
+	  ** update before publishing 
+	-->
+	<property name="commit.message" value="Updated build.xml" />		
+	<property name="plugin.main.version" value="2452" />
+		
+		
     <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
     <property name="plugin.dist.dir"        value="../../dist"/>
     <property name="plugin.build.dir"       value="build"/>
@@ -25,7 +55,7 @@
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
                 <attribute name="Plugin-Description" value="An OSM data validator. It checks for problems in data, and provides fixes for the common ones. Spellcheck integrated for tag names."/>
                 <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Validator"/>
-                <attribute name="Plugin-Mainversion" value="2168"/>
+                <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
             </manifest>
         </jar>
@@ -53,4 +83,81 @@
         </condition>
         <copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
     </target>
+	
+	<!--
+	 ************************** Publishing the plugin *********************************** 
+	-->
+		<!--
+		  ** extracts the JOSM release for the JOSM version in ../core and saves it in the 
+		  ** property ${coreversion.info.entry.revision}
+		  **
+		-->
+		<target name="core-info">
+	        <exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
+	                    <env key="LANG" value="C"/>
+	                    <arg value="info"/>
+	                    <arg value="--xml"/>
+	                    <arg value="../../core"/>
+	        </exec>
+	        <xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
+			<echo>Building against core revision ${coreversion.info.entry.revision}.</echo>			
+			<echo>Plugin-Mainversion is set to ${plugin.main.version}.</echo>
+			<delete file="core.info.xml" />
+		</target>
+
+		<!--
+		 ** commits the source tree for this plugin
+		-->
+		<target name="commit-current">
+			<echo>Commiting the plugin source with message '${commit.message}' ...</echo>
+		    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+		                    <env key="LANG" value="C"/>
+			                <arg value="-m '${commit.message}'"/>
+		    				<arg value="commit"/>
+		                    <arg value="."/>
+		    </exec>	    
+		</target>
+
+		<!--
+		** updates (svn up) the source tree for this plugin
+		-->
+		<target name="update-current">
+			<echo>Updating plugin source ...</echo>
+		    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+		                    <env key="LANG" value="C"/>
+		                    <arg value="up"/>
+		                    <arg value="."/>
+		    </exec>	    
+			<echo>Updating ${plugin.jar} ...</echo>
+		    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+		                    <env key="LANG" value="C"/>
+		                    <arg value="up"/>
+		                    <arg value="../dist/${plugin.jar}"/>
+		    </exec>	    
+		</target>
+		
+		<!--
+		 ** commits the plugin.jar 
+		 -->
+		<target name="commit-dist">
+				<echo>
+***** Properties of published ${plugin.jar} *****
+Commit message    : '${commit.message}'					
+Plugin-Mainversion: ${plugin.main.version}
+JOSM build version: ${coreversion.info.entry.revision}
+Plugin-Version    : ${version.entry.commit.revision}
+***** / Properties of published ${plugin.jar} *****					
+					
+Now commiting ${plugin.jar} ...
+</echo>					
+			    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+			                    <env key="LANG" value="C"/>
+				    			<arg value="-m '${commit.message}'"/>
+			    				<arg value="commit"/>	                			
+            			        <arg value="${plugin.jar}"/>
+			    </exec>	    
+	   	</target>
+				
+		<target name="publish" depends="core-info,commit-current,update-current,clean,dist,commit-dist">
+		</target>
 </project>
diff --git a/validator/ignoretags.cfg b/validator/ignoretags.cfg
index 028cec5..c28a228 100644
--- a/validator/ignoretags.cfg
+++ b/validator/ignoretags.cfg
@@ -248,13 +248,13 @@ T:religion=pastafarian|denomination=EVKdFSMiD
 T:religion=christian|denomination=foursquare
 T:religion=christian|denomination=greek_orthodox
 T:religion=jewish|denomination=hasidic
-T:religion=jewish|denomination=humanistic  
+T:religion=jewish|denomination=humanistic
 T:religion=muslim|denomination=ibadi
 T:religion=muslim|denomination=ismaili
 T:religion=christian|denomination=jehovahs_witness
 T:religion=christian|denomination=kabbalah
-T:religion=christian|denomination=karaite  
-T:religion=jewish|denomination=liberal  
+T:religion=christian|denomination=karaite
+T:religion=jewish|denomination=liberal
 T:religion=christian|denomination=living_waters_church
 T:religion=christian|denomination=lutheran
 T:religion=christian|denomination=maronite
@@ -269,23 +269,23 @@ T:religion=christian|denomination=nondenominational
 T:religion=jewish|denomination=nondenominational
 T:religion=muslim|denomination=nondenominational
 T:religion=christian|denomination=old_catholic
-T:religion=christian|denomination=orthodox 
-T:religion=jewish|denomination=orthodox 
+T:religion=christian|denomination=orthodox
+T:religion=jewish|denomination=orthodox
 T:religion=christian|denomination=pentecostal
 T:religion=christian|denomination=presbyterian
-T:religion=jewish|denomination=progressive 
+T:religion=jewish|denomination=progressive
 T:religion=christian|denomination=protestant
 T:religion=christian|denomination=quaker
 T:religion=jewish|denomination=reconstructionist
 T:religion=jewish|denomination=reform
-T:religion=jewish|denomination=renewal   
+T:religion=jewish|denomination=renewal
 T:religion=christian|denomination=roman_catholic
 T:religion=christian|denomination=russian_orthodox
 T:religion=christian|denomination=salvation_army
 T:religion=jewish|denomination=samaritan
 T:religion=christian|denomination=seventh_day_adventist
 T:religion=muslim|denomination=shia
-T:religion=muslim|denomination=sunni  
+T:religion=muslim|denomination=sunni
 T:religion=jewish|denomination=ultra_orthodox
 T:religion=christian|denomination=united
 T:religion=christian|denomination=united_reformed
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java b/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java
index 58ac364..6de2c33 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java
@@ -3,7 +3,7 @@ package org.openstreetmap.josm.plugins.validator;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Component;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.util.Enumeration;
 import java.util.List;
 
@@ -14,6 +14,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
@@ -26,7 +27,7 @@ import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
  * A layer showing error messages.
- * 
+ *
  * @author frsantos
  */
 public class ErrorLayer extends Layer implements LayerChangeListener {
@@ -53,7 +54,7 @@ public class ErrorLayer extends Layer implements LayerChangeListener {
      */
     @SuppressWarnings("unchecked")
     @Override
-    public void paint(final Graphics g, final MapView mv) {
+    public void paint(final Graphics2D g, final MapView mv, Bounds bounds) {
         DefaultMutableTreeNode root = plugin.validationDialog.tree.getRoot();
         if (root == null || root.getChildCount() == 0)
             return;
@@ -113,7 +114,7 @@ public class ErrorLayer extends Layer implements LayerChangeListener {
     @Override
     public Component[] getMenuEntries() {
         return new Component[] { new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
-                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)), 
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
                 new JSeparator(),
                 new JMenuItem(new RenameLayerAction(null, this)), new JSeparator(),
                 new JMenuItem(new LayerListPopup.InfoAction(this)) };
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java b/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java
index d4a0326..dc1a730 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java
@@ -3,6 +3,7 @@ package org.openstreetmap.josm.plugins.validator;
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Point;
 import java.awt.geom.Point2D;
 
@@ -12,6 +13,7 @@ import javax.swing.JSeparator;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -53,7 +55,7 @@ public class GridLayer extends Layer
      * Draw the grid and highlight all cells acuppied by any selected primitive.
      */
     @Override
-    public void paint(final Graphics g, final MapView mv)
+    public void paint(final Graphics2D g, final MapView mv, Bounds bounds)
     {
         if( !Main.pref.hasKey(PreferenceEditor.PREF_DEBUG + ".grid") )
             return;
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java b/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
index b09371b..a916459 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
@@ -39,6 +39,7 @@ import org.openstreetmap.josm.plugins.validator.tests.CrossingWays;
 import org.openstreetmap.josm.plugins.validator.tests.DuplicateNode;
 import org.openstreetmap.josm.plugins.validator.tests.DuplicateWay;
 import org.openstreetmap.josm.plugins.validator.tests.DuplicatedWayNodes;
+import org.openstreetmap.josm.plugins.validator.tests.NameMismatch;
 import org.openstreetmap.josm.plugins.validator.tests.NodesWithSameName;
 import org.openstreetmap.josm.plugins.validator.tests.OverlappingWays;
 import org.openstreetmap.josm.plugins.validator.tests.SelfIntersectingWay;
@@ -97,6 +98,7 @@ public class OSMValidatorPlugin extends Plugin implements LayerChangeListener {
             TagChecker.class, // ID 1201 .. 1299
             UnconnectedWays.class, // ID 1301 .. 1399
             DuplicateWay.class, // ID 1401 .. 1499
+            NameMismatch.class, // ID  1501 ..  1599
     };
 
     /**
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/Test.java b/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
index 4ef3790..26d738a 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
@@ -1,5 +1,7 @@
 package org.openstreetmap.josm.plugins.validator;
 
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -8,6 +10,7 @@ import javax.swing.JCheckBox;
 import javax.swing.JPanel;
 
 import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.BackreferencedDataSet;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
@@ -16,7 +19,6 @@ import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.tools.GBC;
-import static org.openstreetmap.josm.tools.I18n.tr;
 
 /**
  * Parent class for all validation tests.
@@ -55,11 +57,16 @@ public class Test extends AbstractVisitor
 
     /** Whether the test is run on a partial selection data */
     protected boolean partialSelection;
-    
+
     /** the progress monitor to use */
     protected ProgressMonitor progressMonitor;
 
     /**
+     * the data structure with child->parent references
+     */
+    protected BackreferencedDataSet backreferenceDataSet;
+
+    /**
      * Constructor
      * @param name Name of the test
      * @param description Description of the test
@@ -87,18 +94,19 @@ public class Test extends AbstractVisitor
     public static void initialize(OSMValidatorPlugin plugin) throws Exception {}
 
     /**
-     * Start the test using a given progress monitor 
-     * 
-     * @param progressMonitor  the progress monitor 
+     * Start the test using a given progress monitor
+     *
+     * @param progressMonitor  the progress monitor
      */
     public void startTest(ProgressMonitor progressMonitor) {
-    	if (progressMonitor == null) {
-    		this.progressMonitor = NullProgressMonitor.INSTANCE;
-    	} else {
-    		this.progressMonitor = progressMonitor;
-    	}
-    	this.progressMonitor.beginTask(tr("Running test {0}", name));
-    	errors = new ArrayList<TestError>(30);
+        backreferenceDataSet = new BackreferencedDataSet();
+        if (progressMonitor == null) {
+                this.progressMonitor = NullProgressMonitor.INSTANCE;
+        } else {
+                this.progressMonitor = progressMonitor;
+        }
+        this.progressMonitor.beginTask(tr("Running test {0}", name));
+        errors = new ArrayList<TestError>(30);
     }
 
     /**
@@ -124,8 +132,9 @@ public class Test extends AbstractVisitor
      * actions and destroy the used structures
      */
     public void endTest() {
-    	progressMonitor.finishTask();
-    	progressMonitor = null;
+        progressMonitor.finishTask();
+        progressMonitor = null;
+        backreferenceDataSet = null;
     }
 
     /**
@@ -136,7 +145,7 @@ public class Test extends AbstractVisitor
      */
     public void visit(Collection<OsmPrimitive> selection)
     {
-    	progressMonitor.setTicksCount(selection.size());
+        progressMonitor.setTicksCount(selection.size());
         for (OsmPrimitive p : selection) {
             if( p.isUsable() )
                 p.visit(this);
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java b/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
index cd85619..47aa3b6 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
@@ -152,7 +152,7 @@ public class TestError {
         String ignorestring = getIgnoreSubGroup();
         for (OsmPrimitive o : primitives) {
             // ignore data not yet uploaded
-            if (o.getId() == 0)
+            if (o.isNew())
                 return null;
             String type = "u";
             if (o instanceof Way)
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
index 341ce90..547928f 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
@@ -139,7 +139,7 @@ public class ValidateAction extends JosmAction {
 				public void run() {
 			        plugin.validationDialog.tree.setErrors(errors);
 			        plugin.validationDialog.setVisible(true);
-			        DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
+			        Main.main.getCurrentDataSet().fireSelectionChanged();
 				}				
 			};
 			if (SwingUtilities.isEventDispatchThread()) {
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java
index 5736c2f..6b24967 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java
@@ -120,7 +120,7 @@ public class ValidateUploadHook implements UploadHook
         {
             plugin.validationDialog.tree.setErrors(errors);
             plugin.validationDialog.setVisible(true);
-            DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
+            Main.main.getCurrentDataSet().fireSelectionChanged();
         }
         return res == JOptionPane.YES_OPTION;
     }
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java b/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
index e5efd64..f7f2edf 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
@@ -142,7 +142,7 @@ public class ValidatorDialog extends ToggleDialog implements ActionListener, Sel
             return;
 
         Set<DefaultMutableTreeNode> processedNodes = new HashSet<DefaultMutableTreeNode>();
-               
+
         DuplicateNode.clearBackreferences();
         LinkedList<TestError> errorsToFix = new LinkedList<TestError>();
         for (TreePath path : selectionPaths) {
@@ -159,11 +159,11 @@ public class ValidatorDialog extends ToggleDialog implements ActionListener, Sel
                 processedNodes.add(childNode);
                 Object nodeInfo = childNode.getUserObject();
                 if (nodeInfo instanceof TestError) {
-                	errorsToFix.add((TestError)nodeInfo);
+                    errorsToFix.add((TestError)nodeInfo);
                 }
             }
         }
-        
+
         // run fix task asynchronously
         //
         FixTask fixTask = new FixTask(errorsToFix);
@@ -444,78 +444,78 @@ public class ValidatorDialog extends ToggleDialog implements ActionListener, Sel
         HashSet<OsmPrimitive> filter = new HashSet<OsmPrimitive>(newSelection);
         tree.setFilter(filter);
     }
-    
+
     /**
-     * Task for fixing a collection of {@see TestError}s. Can be run asynchronously. 
-     * 
+     * Task for fixing a collection of {@see TestError}s. Can be run asynchronously.
+     *
      *
      */
     class FixTask extends PleaseWaitRunnable {
-    	private Collection<TestError> testErrors;
-    	private boolean canceled;
-    	private LinkedList<Command> fixCommands;
-    
-    	
-    	public FixTask(Collection<TestError> testErrors) {
-    		super(tr("Fixing errors ..."), false /* don't ignore exceptions */);
-    		this.testErrors = testErrors == null ? new ArrayList<TestError> (): testErrors;
-    		fixCommands = new LinkedList<Command>();
-    	}
-
-		@Override
-		protected void cancel() {
-			this.canceled = true; 			
-		}
-
-		@Override
-		protected void finish() {
-			// do nothing
-		}
- 		
-		@Override
-		protected void realRun() throws SAXException, IOException,
-				OsmTransferException {
-			ProgressMonitor monitor = getProgressMonitor();
-			try {				
-				monitor.setTicksCount(testErrors.size());
-				int i=0;
-				for (TestError error: testErrors) {
-					i++;
-					monitor.subTask(tr("Fixing ({0}/{1}): ''{2}''", i, testErrors.size(),error.getMessage()));
-					if (this.canceled) 
-						return;
-					final Command fixCommand = error.getFix();
+        private Collection<TestError> testErrors;
+        private boolean canceled;
+        private LinkedList<Command> fixCommands;
+
+
+        public FixTask(Collection<TestError> testErrors) {
+            super(tr("Fixing errors ..."), false /* don't ignore exceptions */);
+            this.testErrors = testErrors == null ? new ArrayList<TestError> (): testErrors;
+            fixCommands = new LinkedList<Command>();
+        }
+
+        @Override
+        protected void cancel() {
+            this.canceled = true;
+        }
+
+        @Override
+        protected void finish() {
+            // do nothing
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException,
+                OsmTransferException {
+            ProgressMonitor monitor = getProgressMonitor();
+            try {
+                monitor.setTicksCount(testErrors.size());
+                int i=0;
+                for (TestError error: testErrors) {
+                    i++;
+                    monitor.subTask(tr("Fixing ({0}/{1}): ''{2}''", i, testErrors.size(),error.getMessage()));
+                    if (this.canceled)
+                        return;
+                    final Command fixCommand = error.getFix();
                     if (fixCommand != null) {
-                    	fixCommands.add(fixCommand);
-        				SwingUtilities.invokeAndWait(
-        						new Runnable() {
-        							public void run() {
-        								Main.main.undoRedo.addNoRedraw(fixCommand);
-        							}
-        						}
-        				);
+                        fixCommands.add(fixCommand);
+                        SwingUtilities.invokeAndWait(
+                                new Runnable() {
+                                    public void run() {
+                                        Main.main.undoRedo.addNoRedraw(fixCommand);
+                                    }
+                                }
+                        );
                         error.setIgnored(true);
                     }
                     monitor.worked(1);
-				}		
-				monitor.subTask(tr("Updating map ..."));
-				SwingUtilities.invokeAndWait(new Runnable() {
-					public void run() {
-						Main.main.undoRedo.afterAdd();
-						Main.map.repaint();
-						tree.resetErrors();
-						DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
-					}
-				});
-			} catch(InterruptedException e) { 
-				// FIXME: signature of realRun should have a generic checked exception we
-				// could throw here
-				throw new RuntimeException(e);
-			} catch(InvocationTargetException e) {
-				throw new RuntimeException(e);
-			} finally {
-				monitor.finishTask();
-			}			
-		}
+                }
+                monitor.subTask(tr("Updating map ..."));
+                SwingUtilities.invokeAndWait(new Runnable() {
+                    public void run() {
+                        Main.main.undoRedo.afterAdd();
+                        Main.map.repaint();
+                        tree.resetErrors();
+                        Main.main.getCurrentDataSet().fireSelectionChanged();
+                    }
+                });
+            } catch(InterruptedException e) {
+                // FIXME: signature of realRun should have a generic checked exception we
+                // could throw here
+                throw new RuntimeException(e);
+            } catch(InvocationTargetException e) {
+                throw new RuntimeException(e);
+            } finally {
+                monitor.finishTask();
+            }
+        }
     }
 }
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java
index 3f248b3..0c808ad 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java
@@ -2,31 +2,36 @@ package org.openstreetmap.josm.plugins.validator.tests;
 
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.geom.Point2D;
+import java.awt.geom.Area;
 import java.util.*;
 
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.plugins.validator.Severity;
 import org.openstreetmap.josm.plugins.validator.Test;
 import org.openstreetmap.josm.plugins.validator.TestError;
-import org.openstreetmap.josm.plugins.validator.util.Bag;
-import org.openstreetmap.josm.plugins.validator.util.Util;
 
 /**
  * Check coastlines for errors
  *
  * @author frsantos
+ * @author Teemu Koskinen
  */
 public class Coastlines extends Test
 {
-    protected static int UNORDERED_COASTLINES = 901;
+    protected static int UNORDERED_COASTLINE = 901;
+    protected static int REVERSED_COASTLINE = 902;
+    protected static int UNCONNECTED_COASTLINE = 903;
 
-    /** All ways, grouped by cells */
-    Map<Point2D,List<Way>> _cellWays;
-    /** The already detected errors */
-    Bag<Way, Way> _errorWays;
+    private List<Way> coastlines;
+
+    private Area downloadedArea = null;
 
     /**
      * Constructor
@@ -41,50 +46,157 @@ public class Coastlines extends Test
     public void startTest(ProgressMonitor monitor)
     {
     	super.startTest(monitor);
-        _cellWays = new HashMap<Point2D,List<Way>>(1000);
-        _errorWays = new Bag<Way, Way>();
+
+        OsmDataLayer layer = Main.map.mapView.getEditLayer();
+
+        if (layer != null)
+            downloadedArea = layer.data.getDataSourceArea();
+
+        coastlines = new LinkedList<Way>();
     }
 
     @Override
     public void endTest()
     {
-    	super.endTest();
-        _cellWays = null;
-        _errorWays = null;
+        for (Way c1 : coastlines) {
+            Node head = c1.firstNode();
+            Node tail = c1.lastNode();
+
+            if (head.equals(tail))
+                continue;
+
+            int headWays = 0;
+            int tailWays = 0;
+            boolean headReversed = false;
+            boolean tailReversed = false;
+            boolean headUnordered = false;
+            boolean tailUnordered = false;
+            Way next = null;
+            Way prev = null;
+
+            for (Way c2 : coastlines) {
+                if (c1 == c2)
+                    continue;
+
+                if (c2.containsNode(head)) {
+                    headWays++;
+                    next = c2;
+
+                    if (head.equals(c2.firstNode()))
+                        headReversed = true;
+                    else if (!head.equals(c2.lastNode()))
+                        headUnordered = true;
+                }
+
+                if (c2.containsNode(tail)) {
+                    tailWays++;
+                    prev = c2;
+
+                    if (tail.equals(c2.lastNode()))
+                        tailReversed = true;
+                    else if (!tail.equals(c2.firstNode()))
+                        tailUnordered = true;
+                }
+            }
+
+
+            List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
+            primitives.add(c1);
+
+            if (headWays == 0 || tailWays == 0) {
+                List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
+
+                System.out.println("Unconnected coastline: " + c1.getId());
+                if (headWays == 0 && (downloadedArea == null || downloadedArea.contains(head.getCoor()))) {
+                    System.out.println("headways: " +headWays+ " node: " + head.toString());
+                    highlight.add(head);
+                }
+                if (tailWays == 0 && (downloadedArea == null || downloadedArea.contains(tail.getCoor()))) {
+                    System.out.println("tailways: " +tailWays+ " tail: " + tail.toString());
+                    highlight.add(tail);
+                }
+
+                if (highlight.size() > 0)
+                    errors.add(new TestError(this, Severity.ERROR, tr("Unconnected coastline"),
+                                             UNCONNECTED_COASTLINE, primitives, highlight));
+            }
+
+            boolean unordered = false;
+            boolean reversed = false;
+
+            if (headWays == 1 && headReversed && tailWays == 1 && tailReversed)
+                reversed = true;
+
+            if (headWays > 1 || tailWays > 1)
+                unordered = true;
+            else if (headUnordered || tailUnordered)
+                unordered = true;
+            else if (reversed && next == prev)
+                unordered = true;
+
+            if (unordered) {
+                List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
+
+                System.out.println("Unordered coastline: " + c1.toString());
+                if (headWays > 1 || headUnordered || reversed) {
+                    System.out.println("head: " + head.toString());
+                    highlight.add(head);
+                }
+                if (tailWays > 1 || tailUnordered || reversed) {
+                    System.out.println("tail: " + tail.toString());
+                    highlight.add(tail);
+                }
+
+                errors.add(new TestError(this, Severity.ERROR, tr("Unordered coastline"),
+                                         UNORDERED_COASTLINE, primitives, highlight));
+            }
+            else if (reversed) {
+                errors.add(new TestError(this, Severity.ERROR, tr("Reversed coastline"),
+                                         REVERSED_COASTLINE, primitives));
+            }
+        }
+
+        coastlines = null;
+        downloadedArea = null;
+
+        super.endTest();
     }
 
     @Override
-    public void visit(Way w)
+    public void visit(Way way)
     {
-        if( !w.isUsable() )
+        if (!way.isUsable())
             return;
 
-        String natural = w.get("natural");
-        if( natural == null || !natural.equals("coastline") )
+        String natural = way.get("natural");
+        if (natural == null || !natural.equals("coastline"))
             return;
 
-        List<List<Way>> cellWays = Util.getWaysInCell(w, _cellWays);
-        for( List<Way> ways : cellWays)
-        {
-            for( Way w2 : ways)
-            {
-                if( _errorWays.contains(w, w2) || _errorWays.contains(w2, w) )
-                    continue;
+        coastlines.add(way);
+    }
 
-                String natural2 = w.get("natural");
-                if( natural2 == null || !natural2.equals("coastline") )
-                    continue;
+    @Override
+    public Command fixError(TestError testError) {
+        if (isFixable(testError)) {
+            Way way = (Way) testError.getPrimitives().iterator().next();
+            Way newWay = new Way(way);
 
-                if( w.getNodes().get(0).equals(w2.getNodes().get(0)) || w.getNodes().get(w.getNodesCount() - 1).equals(w2.getNodes().get(w2.getNodesCount() - 1)))
-                {
-                    List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
-                    primitives.add(w);
-                    primitives.add(w2);
-                    errors.add( new TestError(this, Severity.ERROR, tr("Unordered coastline"), UNORDERED_COASTLINES, primitives) );
-                    _errorWays.add(w, w2);
-                }
-            }
-            ways.add(w);
+            List<Node> nodesCopy = newWay.getNodes();
+            Collections.reverse(nodesCopy);
+            newWay.setNodes(nodesCopy);
+
+            return new ChangeCommand(way, newWay);
         }
+
+        return null;
+    }
+
+    @Override
+    public boolean isFixable(TestError testError) {
+        if (testError.getTester() instanceof Coastlines) {
+            return (testError.getCode() == REVERSED_COASTLINE);
+        }
+
+        return false;
     }
 }
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java
index db5b090..3c5d1d1 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java
@@ -73,14 +73,13 @@ public class CrossingWays extends Test
             return;
 
         String coastline1 = w.get("natural");
-        boolean isCoastline1 = coastline1 != null && (coastline1.equals("water") || coastline1.equals("coastline"));
+        boolean isCoastline1 = "water".equals(coastline1) || "coastline".equals(coastline1);
         String railway1 = w.get("railway");
-        boolean isSubway1 = railway1 != null && railway1.equals("subway");
-        boolean isBuilding = false;
-        if(w.get("building") != null)
-            isBuilding = true;
-        
-        if( w.get("highway") == null && w.get("waterway") == null && (railway1 == null || isSubway1)  && !isCoastline1 && !isBuilding)
+        boolean isSubway1 = "subway".equals(railway1);
+        boolean isTram1 = "tram".equals(railway1);
+        boolean isBuilding = (w.get("building") != null);
+
+        if( w.get("highway") == null && w.get("waterway") == null && (railway1 == null || isSubway1 || isTram1)  && !isCoastline1 && !isBuilding)
             return;
 
         String layer1 = w.get("layer");
@@ -108,6 +107,7 @@ public class CrossingWays extends Test
 
                     if( !es1.intersects(es2) ) continue;
                     if( isSubway1 && "subway".equals(railway2)) continue;
+                    if( isTram1 && "tram".equals(railway2)) continue;
 
                     boolean isCoastline2 = coastline2 != null && (coastline2.equals("water") || coastline2.equals("coastline"));
                     if( isCoastline1 != isCoastline2 ) continue;
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
index 9a63fc6..539468f 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
@@ -3,8 +3,8 @@ package org.openstreetmap.josm.plugins.validator.tests;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.geom.Area;
-
 import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -30,21 +30,20 @@ import org.openstreetmap.josm.plugins.validator.util.Bag;
  * @author frsantos
  */
 public class DuplicateNode extends Test{
-	
+
 	private static BackreferencedDataSet backreferences;
-	
+
 	public static BackreferencedDataSet getBackreferenceDataSet() {
 		if (backreferences == null) {
-			backreferences = new BackreferencedDataSet(Main.main.getEditLayer().data);
-			backreferences.build();
+			backreferences = new BackreferencedDataSet();
 		}
 		return backreferences;
 	}
-	
+
 	public static void clearBackreferences() {
 		backreferences = null;
 	}
-	
+
     protected static int DUPLICATE_NODE = 1;
 
     /** Bag of all nodes */
@@ -109,7 +108,7 @@ public class DuplicateNode extends Test{
     public Command fixError(TestError testError)
     {
         Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>(testError.getPrimitives());
-        LinkedList<Node> nodes = new LinkedList<Node>(OsmPrimitive.getFilteredList(sel, Node.class));
+        LinkedHashSet<Node> nodes = new LinkedHashSet<Node>(OsmPrimitive.getFilteredList(sel, Node.class));
         Node target = MergeNodesAction.selectTargetNode(nodes);
         if(checkAndConfirmOutlyingDeletes(nodes))
             return MergeNodesAction.mergeNodes(Main.main.getEditLayer(),getBackreferenceDataSet(), nodes, target);
@@ -127,11 +126,11 @@ public class DuplicateNode extends Test{
      * Check whether user is about to delete data outside of the download area.
      * Request confirmation if he is.
      */
-    private static boolean checkAndConfirmOutlyingDeletes(LinkedList<Node> del) {
+    private static boolean checkAndConfirmOutlyingDeletes(LinkedHashSet<Node> del) {
         Area a = Main.main.getCurrentDataSet().getDataSourceArea();
         if (a != null) {
             for (OsmPrimitive osm : del) {
-                if (osm instanceof Node && osm.getId() != 0) {
+                if (osm instanceof Node && !osm.isNew()) {
                     Node n = (Node) osm;
                     if (!a.contains(n.getCoor())) {
                         return ConditionalOptionPaneUtil.showConfirmationDialog(
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java
index 98a0cc6..4711fa3 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java
@@ -5,8 +5,8 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Vector;
 import java.util.Map;
+import java.util.Vector;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.command.Command;
@@ -14,8 +14,8 @@ import org.openstreetmap.josm.command.DeleteCommand;
 import org.openstreetmap.josm.command.SequenceCommand;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.plugins.validator.Severity;
 import org.openstreetmap.josm.plugins.validator.Test;
@@ -119,7 +119,7 @@ public class DuplicateWay extends Test
         // Only one way will be kept - the one with lowest positive ID, if such exist
         // or one "at random" if no such exists. Rest of the ways will be deleted
         for (Way w: ways) {
-            if (w.getId() > 0) {
+            if (!w.isNew()) {
                 if (idToKeep == 0 || w.getId() < idToKeep) idToKeep = w.getId();
             }
         }
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/NameMismatch.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NameMismatch.java
new file mode 100644
index 0000000..999db32
--- /dev/null
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NameMismatch.java
@@ -0,0 +1,109 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+
+/**
+ * Check for missing name:* translations.
+ * <p>
+ * This test finds multilingual objects whose 'name' attribute is not
+ * equal to any 'name:*' attribute and not a composition of some
+ * 'name:*' attributes separated by ' - '.
+ * <p>
+ * For example, a node with name=Europe, name:de=Europa should have
+ * name:en=Europe to avoid triggering this test.  An object with
+ * name='Suomi - Finland' should have at least name:fi=Suomi and
+ * name:sv=Finland to avoid a warning (name:et=Soome would not
+ * matter).  Also, complain if an object has some name:* attribute but
+ * no name.
+ *
+ * @author Skela
+ */
+public class NameMismatch extends Test {
+    protected static final int NAME_MISSING = 1501;
+    protected static final int NAME_TRANSLATION_MISSING = 1502;
+
+    public NameMismatch() {
+        super(tr("Missing name:* translation."),
+            tr("This test finds multilingual objects whose 'name' attribute is not equal to some 'name:*' attribute and not a composition of 'name:*' attributes, e.g., Italia - Italien - Italy."));
+    }
+
+    /**
+     * Report a missing translation.
+     *
+     * @param p The primitive whose translation is missing
+     */
+    private void missingTranslation(OsmPrimitive p) {
+        errors.add(new TestError(this, Severity.OTHER,
+            tr("A name:* translation is missing."),
+            NAME_TRANSLATION_MISSING, p));
+    }
+
+    /**
+     * Check a primitive for a name mismatch.
+     *
+     * @param p The primitive to be tested
+     */
+    public void check(OsmPrimitive p) {
+        HashSet<String> names = new HashSet<String>();
+
+        for (Entry<String, String> entry : p.entrySet()) {
+            if (entry.getKey().startsWith("name:")) {
+                String name_s = entry.getValue();
+                if (name_s != null) {
+                    names.add(name_s);
+                }
+            }
+        }
+
+        if (names.isEmpty()) return;
+
+        String name = p.get("name");
+
+        if (name == null) {
+            errors.add(new TestError(this, Severity.OTHER,
+                tr("A name is missing, even though name:* exists."),
+                                     NAME_MISSING, p));
+	    return;
+	}
+
+        if (names.contains(name)) return;
+        /* If name is not equal to one of the name:*, it should be a
+        composition of some (not necessarily all) name:* labels.
+        Check if this is the case. */
+
+        String split_names[] = name.split(" - ");
+        if (split_names.length == 1) {
+            /* The name is not composed of multiple parts. Complain. */
+            missingTranslation(p);
+            return;
+        }
+
+        /* Check that each part corresponds to a translated name:*. */
+        for (String n : split_names) {
+            if (!names.contains(n)) {
+                missingTranslation(p);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Checks a name mismatch in all primitives.
+     *
+     * @param selection The primitives to be tested
+     */
+    @Override public void visit(Collection<OsmPrimitive> selection) {
+        for (OsmPrimitive p : selection)
+            if (!p.isDeleted() && !p.incomplete)
+                check(p);
+    }
+}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java
index 6e2c103..831b4a0 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java
@@ -5,6 +5,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 import java.util.Map;
 import java.util.List;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.ArrayList;
 
 
@@ -25,7 +26,7 @@ public class NodesWithSameName extends Test {
     }
 
     @Override public void startTest(ProgressMonitor monitor) {
-    	super.startTest(monitor);
+        super.startTest(monitor);
         namesToNodes = new HashMap<String, List<Node>>();
     }
 
@@ -34,7 +35,12 @@ public class NodesWithSameName extends Test {
 
         String name = n.get("name");
         String sign = n.get("traffic_sign");
-        if (name == null || (sign != null && sign.equals("city_limit"))) return;
+        String highway = n.get("highway");
+        if (name == null
+            || (sign != null && sign.equals("city_limit"))
+            || (highway != null && highway.equals("bus_stop"))) {
+            return;
+        }
 
         List<Node> nodes = namesToNodes.get(name);
         if (nodes == null)
@@ -46,8 +52,17 @@ public class NodesWithSameName extends Test {
     @Override public void endTest() {
         for (List<Node> nodes : namesToNodes.values()) {
             if (nodes.size() > 1) {
-                errors.add(new TestError(this, Severity.OTHER,
-                    tr("Nodes with same name"), SAME_NAME, nodes));
+                // Report the same-name nodes, unless each has a unique ref=*.
+                HashSet<String> refs = new HashSet<String>();
+
+                for (Node n : nodes) {
+                    String ref = n.get("ref");
+                    if (ref == null || !refs.add(ref)) {
+                        errors.add(new TestError(this, Severity.OTHER,
+                            tr("Nodes with same name"), SAME_NAME, nodes));
+                        break;
+                    }
+                }
             }
         }
         super.endTest();
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java
index c5ecc16..dc8f206 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java
@@ -439,15 +439,19 @@ public class TagChecker extends Test
                 }
             }
         }
-        if(checkPaint && p.errors != null)
+        if(checkPaint)
         {
-            for(String s: p.errors)
+            List<String> pe = p.getDataSet().getErrors(p);
+            if(pe != null)
             {
-                /* passing translated text also to original string, as we already
-                translated the stuff before. Makes the ignore file language dependend. */
-                errors.add( new TestError(this, Severity.WARNING, tr("Painting problem"),
-                s, s, PAINT, p) );
-                withErrors.add(p, "P");
+                for(String s: pe)
+                {
+                    /* passing translated text also to original string, as we already
+                    translated the stuff before. Makes the ignore file language dependend. */
+                    errors.add( new TestError(this, Severity.WARNING, tr("Painting problem"),
+                    s, s, PAINT, p) );
+                    withErrors.add(p, "P");
+                }
             }
         }
 
@@ -555,7 +559,7 @@ public class TagChecker extends Test
     @Override
     public void startTest(ProgressMonitor monitor)
     {
-    	super.startTest(monitor);
+        super.startTest(monitor);
         checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS, true);
         if( isBeforeUpload )
             checkKeys = checkKeys && Main.pref.getBoolean(PREF_CHECK_KEYS_BEFORE_UPLOAD, true);
@@ -580,7 +584,7 @@ public class TagChecker extends Test
     @Override
     public void visit(Collection<OsmPrimitive> selection)
     {
-        if( checkKeys || checkValues || checkComplex)
+        if( checkKeys || checkValues || checkComplex || checkPaint || checkFixmes)
             super.visit(selection);
     }
 
@@ -621,11 +625,11 @@ public class TagChecker extends Test
         addSrcButton.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent e) {
                 String source = JOptionPane.showInputDialog(
-                		Main.parent, 
-                		tr("TagChecker source"),
-                		tr("TagChecker source"),
-                		JOptionPane.QUESTION_MESSAGE
-                		);
+                        Main.parent,
+                        tr("TagChecker source"),
+                        tr("TagChecker source"),
+                        JOptionPane.QUESTION_MESSAGE
+                        );
                 if (source != null)
                     ((DefaultListModel)Sources.getModel()).addElement(source);
                 Sources.clearSelection();
@@ -652,19 +656,19 @@ public class TagChecker extends Test
                     else
                     {
                         JOptionPane.showMessageDialog(
-                        		Main.parent, 
-                        		tr("Please select the row to edit."),
-                        		tr("Information"),
-                        		JOptionPane.INFORMATION_MESSAGE
-                        		);
+                                Main.parent,
+                                tr("Please select the row to edit."),
+                                tr("Information"),
+                                JOptionPane.INFORMATION_MESSAGE
+                                );
                     }
                 }
                 else {
-                	String source = (String)JOptionPane.showInputDialog(Main.parent,
-                			tr("TagChecker source"),
-                			tr("TagChecker source"),
-                			JOptionPane.QUESTION_MESSAGE, null, null,
-                			Sources.getSelectedValue());
+                    String source = (String)JOptionPane.showInputDialog(Main.parent,
+                            tr("TagChecker source"),
+                            tr("TagChecker source"),
+                            JOptionPane.QUESTION_MESSAGE, null, null,
+                            Sources.getSelectedValue());
                     if (source != null)
                         ((DefaultListModel)Sources.getModel()).setElementAt(source, row);
                 }
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java
index 506e55c..59d8739 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java
@@ -10,6 +10,7 @@ import java.util.List;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.plugins.validator.Severity;
@@ -35,7 +36,7 @@ public class UnclosedWays extends Test {
 
     @Override
     public void startTest(ProgressMonitor monitor) {
-    	super.startTest(monitor);
+        super.startTest(monitor);
         _errorWays = new Bag<Way, Way>();
     }
 
@@ -48,26 +49,22 @@ public class UnclosedWays extends Test {
     private String type;
     private String etype;
     private int mode;
-    private boolean force;
 
-    public void set(boolean f, int m, String text, String desc) {
+    public void set(int m, String text, String desc) {
         etype = MessageFormat.format(text, desc);
         type = tr(text, tr(desc));
         mode = m;
-        force = f;
     }
 
-    public void set(boolean f, int m, String text) {
+    public void set(int m, String text) {
         etype = text;
         type = tr(text);
         mode = m;
-        force = f;
     }
 
     @Override
     public void visit(Way w) {
         String test;
-        force = false; /* force even if end-to-end distance is long */
         type = etype = null;
         mode = 0;
 
@@ -75,53 +72,56 @@ public class UnclosedWays extends Test {
             return;
 
         test = w.get("natural");
-        if (test != null)
-            set(!"coastline".equals(test), 1101, marktr("natural type {0}"), test);
+        if (test != null && !"coastline".equals(test) && !"cliff".equals(test))
+            set(1101, marktr("natural type {0}"), test);
         test = w.get("landuse");
         if (test != null)
-            set(true, 1102, marktr("landuse type {0}"), test);
+            set(1102, marktr("landuse type {0}"), test);
         test = w.get("amenities");
         if (test != null)
-            set(true, 1103, marktr("amenities type {0}"), test);
+            set(1103, marktr("amenities type {0}"), test);
         test = w.get("sport");
         if (test != null && !test.equals("water_slide"))
-            set(true, 1104, marktr("sport type {0}"), test);
+            set(1104, marktr("sport type {0}"), test);
         test = w.get("tourism");
         if (test != null)
-            set(true, 1105, marktr("tourism type {0}"), test);
+            set(1105, marktr("tourism type {0}"), test);
         test = w.get("shop");
         if (test != null)
-            set(true, 1106, marktr("shop type {0}"), test);
+            set(1106, marktr("shop type {0}"), test);
         test = w.get("leisure");
         if (test != null)
-            set(true, 1107, marktr("leisure type {0}"), test);
+            set(1107, marktr("leisure type {0}"), test);
         test = w.get("waterway");
         if (test != null && test.equals("riverbank"))
-            set(true, 1108, marktr("waterway type {0}"), test);
+            set(1108, marktr("waterway type {0}"), test);
         Boolean btest = OsmUtils.getOsmBoolean(w.get("building"));
         if (btest != null && btest)
-            set(true, 1120, marktr("building"));
+            set(1120, marktr("building"));
         btest = OsmUtils.getOsmBoolean(w.get("area"));
         if (btest != null && btest)
-            set(true, 1130, marktr("area"));
-
-        if (type != null && !w.isClosed())
-        {
-            Node f = w.getNode(0);
-            Node l = w.getNode(w.getNodesCount() - 1);
-            if(force || f.getCoor().greatCircleDistance(l.getCoor()) < 10000)
-            {
-                List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
-                List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
-                primitives.add(w);
-                // The important parts of an unclosed way are the first and
-                // the last node which should be connected, therefore we highlight them
-                highlight.add(f);
-                highlight.add(l);
-                errors.add(new TestError(this, Severity.WARNING, tr("Unclosed way"), type, etype, mode, primitives,
-                        highlight));
-                _errorWays.add(w, w);
+            set(1130, marktr("area"));
+
+        if (type != null && !w.isClosed()) {
+            for (OsmPrimitive parent: this.backreferenceDataSet.getParents(w)) {
+                if (parent instanceof Relation && "multipolygon".equals(parent.get("type")))
+                    return;
             }
+            Node f = w.firstNode();
+            Node l = w.lastNode();
+
+            List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
+            List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
+            primitives.add(w);
+
+            // The important parts of an unclosed way are the first and
+            // the last node which should be connected, therefore we highlight them
+            highlight.add(f);
+            highlight.add(l);
+
+            errors.add(new TestError(this, Severity.WARNING, tr("Unclosed way"),
+                            type, etype, mode, primitives, highlight));
+            _errorWays.add(w, w);
         }
     }
 }
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java
index 6666394..ce93a3d 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java
@@ -4,15 +4,25 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.geom.Area;
 import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+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.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.BBox;
+import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.data.osm.QuadBuckets;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.plugins.validator.PreferenceEditor;
@@ -35,6 +45,10 @@ public class UnconnectedWays extends Test
     Set<Node> endnodes_highway; // nodes at end of way
     Set<Node> middlenodes; // nodes in middle of way
     Set<Node> othernodes; // nodes appearing at least twice
+    //NodeSearchCache nodecache;
+    QuadBuckets<Node> nodecache;
+    Area ds_area;
+    DataSet ds;
 
     double mindist;
     double minmiddledist;
@@ -50,7 +64,7 @@ public class UnconnectedWays extends Test
     @Override
     public void startTest(ProgressMonitor monitor)
     {
-    	super.startTest(monitor);
+        super.startTest(monitor);
         ways = new HashSet<MyWaySegment>();
         endnodes = new HashSet<Node>();
         endnodes_highway = new HashSet<Node>();
@@ -58,128 +72,253 @@ public class UnconnectedWays extends Test
         othernodes = new HashSet<Node>();
         mindist = Main.pref.getDouble(PREFIX + ".node_way_distance", 10.0)/6378135.0;
         minmiddledist = Main.pref.getDouble(PREFIX + ".way_way_distance", 0.0)/6378135.0;
+        this.ds = Main.main.getCurrentDataSet();
+        this.ds_area = ds.getDataSourceArea();
     }
 
     @Override
     public void endTest()
     {
-        Area a = Main.main.getCurrentDataSet().getDataSourceArea();
+        //Area a = Main.ds.getDataSourceArea();
         Map<Node, Way> map = new HashMap<Node, Way>();
-        for(Node en : endnodes_highway)
-        {
-            Boolean isexit = OsmUtils.getOsmBoolean(en.get("noexit"));
-            if("turning_circle".equals(en.get("highway")) ||
-            (isexit != null && isexit) || en.get("barrier") != null)
-                continue;
-            for(MyWaySegment s : ways)
-            {
-                if(!s.isBoundary && !s.isAbandoned && s.highway && s.nearby(en, mindist) && (a == null || a.contains(en.getCoor())))
-                    map.put(en, s.w);
+        long last = -1;
+        for (int iter = 0; iter < 1; iter++) {
+        last = System.currentTimeMillis();
+        long last_print = -1;
+        int nr = 0;
+        Collection<MyWaySegment> tmp_ways = ways;
+        for(MyWaySegment s : tmp_ways) {
+            nr++;
+            long now = System.currentTimeMillis();
+            if (now - last_print > 200) {
+                //System.err.println("processing segment nr: " + nr + " of " + ways.size());
+                last_print = now;
+            }
+            for(Node en : s.nearbyNodes(mindist)) {
+                if (en == null)
+                    continue;
+                //if("turning_circle".equals(en.get("highway")) ||
+                //        (isexit != null && isexit) || en.get("barrier") != null)
+                //    c4++;
+                if(!s.highway)
+                    continue;
+                if (!endnodes_highway.contains(en))
+                    continue;
+                Boolean isexit = OsmUtils.getOsmBoolean(en.get("noexit"));
+                if("turning_circle".equals(en.get("highway")) ||
+                   "bus_stop".equals(en.get("highway")) ||
+                  (isexit != null && isexit) || en.get("barrier") != null)
+                    continue;
+                // There's a small false-positive here.  Imagine an intersection
+                // like a 't'.  If the top part of the 't' is short enough, it
+                // will trigger the node at the very top of the 't' to be unconnected
+                // to the way that "crosses" the 't'.  We should probably check that
+                // the ways to which 'en' belongs are not connected to 's.w'.
+                map.put(en, s.w);
             }
         }
-        if(map.size() > 0)
+        //System.out.println("p1 elapsed: " + (System.currentTimeMillis()-last));
+        last = System.currentTimeMillis();
+        }
+        for(Map.Entry<Node, Way> error : map.entrySet())
         {
-            for(Map.Entry<Node, Way> error : map.entrySet())
-            {
-                errors.add(new TestError(this, Severity.WARNING,
-                tr("Way end node near other highway"), UNCONNECTED_WAYS,
-                Arrays.asList(error.getKey(), error.getValue())));
-            }
+            errors.add(new TestError(this, Severity.WARNING,
+            tr("Way end node near other highway"), UNCONNECTED_WAYS,
+            Arrays.asList(error.getKey(), error.getValue())));
         }
         map.clear();
-        for(Node en : endnodes_highway)
+        for(MyWaySegment s : ways)
         {
-            for(MyWaySegment s : ways)
+            for(Node en : s.nearbyNodes(mindist))
             {
-                if(!s.isBoundary && !s.isAbandoned && !s.highway && s.nearby(en, mindist) && !s.isArea() && (a == null || a.contains(en.getCoor())))
+                if (endnodes_highway.contains(en) && !s.highway && !s.isArea()) {
                     map.put(en, s.w);
-            }
-        }
-        for(Node en : endnodes)
-        {
-            for(MyWaySegment s : ways)
-            {
-                if(!s.isBoundary && !s.isAbandoned && s.nearby(en, mindist) && !s.isArea() && (a == null || a.contains(en.getCoor())))
+                } else if (endnodes.contains(en) && !s.isArea()) {
                     map.put(en, s.w);
+                }
             }
         }
-        if(map.size() > 0)
+        //System.out.println("p2 elapsed: " + (System.currentTimeMillis()-last));
+        last = System.currentTimeMillis();
+        for(Map.Entry<Node, Way> error : map.entrySet())
         {
-            for(Map.Entry<Node, Way> error : map.entrySet())
-            {
-                errors.add(new TestError(this, Severity.WARNING,
-                tr("Way end node near other way"), UNCONNECTED_WAYS,
-                Arrays.asList(error.getKey(), error.getValue())));
-            }
+            errors.add(new TestError(this, Severity.WARNING,
+            tr("Way end node near other way"), UNCONNECTED_WAYS,
+            Arrays.asList(error.getKey(), error.getValue())));
         }
         /* the following two use a shorter distance */
         if(minmiddledist > 0.0)
         {
             map.clear();
-            for(Node en : middlenodes)
+            for(MyWaySegment s : ways)
             {
-                for(MyWaySegment s : ways)
+                for(Node en : s.nearbyNodes(minmiddledist))
                 {
-                    if(!s.isBoundary && !s.isAbandoned && s.nearby(en, minmiddledist) && (a == null || a.contains(en.getCoor())))
-                        map.put(en, s.w);
+                    if (!middlenodes.contains(en))
+                        continue;
+                    map.put(en, s.w);
                 }
             }
-            if(map.size() > 0)
+            //System.out.println("p3 elapsed: " + (System.currentTimeMillis()-last));
+            last = System.currentTimeMillis();
+            for(Map.Entry<Node, Way> error : map.entrySet())
             {
-                for(Map.Entry<Node, Way> error : map.entrySet())
-                {
-                    errors.add(new TestError(this, Severity.OTHER,
-                    tr("Way node near other way"), UNCONNECTED_WAYS,
-                    Arrays.asList(error.getKey(), error.getValue())));
-                }
+                errors.add(new TestError(this, Severity.OTHER,
+                tr("Way node near other way"), UNCONNECTED_WAYS,
+                Arrays.asList(error.getKey(), error.getValue())));
             }
             map.clear();
-            for(Node en : othernodes)
+            for(MyWaySegment s : ways)
             {
-                for(MyWaySegment s : ways)
+                for(Node en : s.nearbyNodes(minmiddledist))
                 {
-                    if(!s.isBoundary && !s.isAbandoned && s.nearby(en, minmiddledist) && (a == null || a.contains(en.getCoor())))
-                        map.put(en, s.w);
+                    if (!othernodes.contains(en))
+                        continue;
+                    map.put(en, s.w);
                 }
             }
-            if(map.size() > 0)
+            //System.out.println("p4 elapsed: " + (System.currentTimeMillis()-last));
+            last = System.currentTimeMillis();
+            for(Map.Entry<Node, Way> error : map.entrySet())
             {
-                for(Map.Entry<Node, Way> error : map.entrySet())
-                {
-                    errors.add(new TestError(this, Severity.OTHER,
-                    tr("Connected way end node near other way"), UNCONNECTED_WAYS,
-                    Arrays.asList(error.getKey(), error.getValue())));
-                }
+                errors.add(new TestError(this, Severity.OTHER,
+                tr("Connected way end node near other way"), UNCONNECTED_WAYS,
+                Arrays.asList(error.getKey(), error.getValue())));
             }
         }
         ways = null;
         endnodes = null;
         super.endTest();
+        //System.out.println("p99 elapsed: " + (System.currentTimeMillis()-last));
+        last = System.currentTimeMillis();
     }
 
     private class MyWaySegment
     {
-        private Line2D line;
-        public Way w;
-        public Boolean isAbandoned = false;
-        public Boolean isBoundary = false;
-        public Boolean highway;
+        private final Line2D line;
+        public final Way w;
+        public final boolean isAbandoned;
+        public final boolean isBoundary;
+        public final boolean highway;
+        private final double len;
+        private Set<Node> nearbyNodeCache;
+        double nearbyNodeCacheDist = -1.0;
+        final Node n1;
+        final Node n2;
 
         public MyWaySegment(Way w, Node n1, Node n2)
         {
             this.w = w;
             String railway = w.get("railway");
-            this.isAbandoned = railway != null && railway.equals("abandoned");
-            this.highway = w.get("highway") != null || (railway != null && !isAbandoned);
-            this.isBoundary = w.get("boundary") != null && w.get("boundary").equals("administrative") && !this.highway;
+            String highway = w.get("highway");
+            this.isAbandoned = "abandoned".equals(railway) || "yes".equals(w.get("disused"));
+            this.highway = (highway != null || railway != null) && !isAbandoned;
+            this.isBoundary = !this.highway && "administrative".equals(w.get("boundary"));
             line = new Line2D.Double(n1.getEastNorth().east(), n1.getEastNorth().north(),
-            n2.getEastNorth().east(), n2.getEastNorth().north());
+                                     n2.getEastNorth().east(), n2.getEastNorth().north());
+            len = line.getP1().distance(line.getP2());
+            this.n1 = n1;
+            this.n2 = n2;
         }
 
         public boolean nearby(Node n, double dist)
         {
-            return !w.containsNode(n)
-            && line.ptSegDist(n.getEastNorth().east(), n.getEastNorth().north()) < dist;
+//            return !w.containsNode(n)
+//            && line.ptSegDist(n.getEastNorth().east(), n.getEastNorth().north()) < dist;
+            if (w == null)
+                Main.debug("way null");
+            if (w.containsNode(n))
+                return false;
+            EastNorth coord = n.getEastNorth();
+            if (coord == null)
+                return false;
+            Point2D p = new Point2D.Double(coord.east(), coord.north());
+            if (line.getP1().distance(p) > len+dist)
+                return false;
+            if (line.getP2().distance(p) > len+dist)
+                return false;
+            return line.ptSegDist(p) < dist;
+        }
+        public List<LatLon> getBounds(double fudge)
+        {
+            double x1 = n1.getCoor().lon();
+            double x2 = n2.getCoor().lon();
+            if (x1 > x2) {
+                double tmpx = x1;
+                x1 = x2;
+                x2 = tmpx;
+            }
+            double y1 = n1.getCoor().lat();
+            double y2 = n2.getCoor().lat();
+            if (y1 > y2) {
+                double tmpy = y1;
+                y1 = y2;
+                y2 = tmpy;
+            }
+            LatLon topLeft  = new LatLon(y2+fudge, x1-fudge);
+            LatLon botRight = new LatLon(y1-fudge, x2+fudge);
+            List<LatLon> ret = new ArrayList<LatLon>();
+            ret.add(topLeft);
+            ret.add(botRight);
+            return ret;
+        }
+
+        public Collection<Node> nearbyNodes(double dist)
+        {
+            // If you're looking for nodes that are farther
+            // away that we looked for last time, the cached
+            // result is no good
+            if (dist > nearbyNodeCacheDist) {
+                //if (nearbyNodeCacheDist != -1)
+                //    System.out.println("destroyed MyWaySegment nearby node cache:" + dist + " > " +  nearbyNodeCacheDist);
+                nearbyNodeCache = null;
+            }
+            if (nearbyNodeCache != null) {
+                // If we've cached an aread greater than the
+                // one now being asked for...
+                if (nearbyNodeCacheDist > dist) {
+                    //System.out.println("had to trim MyWaySegment nearby node cache.");
+                    // Used the cached result and trim out
+                    // the nodes that are not in the smaller
+                    // area, but keep the old larger cache.
+                    Set<Node> trimmed = new HashSet<Node>(nearbyNodeCache);
+                    for (Node n : new HashSet<Node>(nearbyNodeCache)) {
+                        if (!nearby(n, dist))
+                            trimmed.remove(n);
+                    }
+                    return trimmed;
+                }
+                return nearbyNodeCache;
+            }
+            /*
+             * We know that any point near the line must be at
+             * least as close as the other end of the line, plus
+             * a little fudge for the distance away ('dist').
+             */
+
+            // This needs to be a hash set because the searches
+            // overlap a bit and can return duplicate nodes.
+            nearbyNodeCache = null;
+            List<LatLon> bounds = this.getBounds(dist);
+            List<Node> found_nodes = ds.searchNodes(new BBox(bounds.get(0), bounds.get(1)));
+            if (found_nodes == null)
+                return Collections.emptySet();
+
+            for (Node n : found_nodes) {
+                if (!nearby(n, dist) ||
+                     (ds_area != null && !ds_area.contains(n.getCoor())))
+                    continue;
+                // It is actually very rare for us to find a node
+                // so defer as much of the work as possible, like
+                // allocating the hash set
+                if (nearbyNodeCache == null)
+                    nearbyNodeCache = new HashSet<Node>();
+                nearbyNodeCache.add(n);
+            }
+            nearbyNodeCacheDist = dist;
+            if (nearbyNodeCache == null)
+                nearbyNodeCache = Collections.emptySet();
+            return nearbyNodeCache;
         }
 
         public boolean isArea() {
@@ -189,32 +328,49 @@ public class UnconnectedWays extends Test
         }
     }
 
-    @Override
-    public void visit(Way w)
+    List<MyWaySegment> getWaySegments(Way w)
     {
-        if( !w.isUsable() )
-            return;
+        List<MyWaySegment> ret = new ArrayList<MyWaySegment>();
+        if (!w.isUsable()
+            || w.get("barrier") != null
+            || "cliff".equals(w.get("natural")))
+            return ret;
+
         int size = w.getNodesCount();
         if(size < 2)
-            return;
+            return ret;
         for(int i = 1; i < size; ++i)
         {
             if(i < size-1)
                 addNode(w.getNode(i), middlenodes);
-            ways.add(new MyWaySegment(w, w.getNode(i-1), w.getNode(i)));
+            MyWaySegment ws = new MyWaySegment(w, w.getNode(i-1), w.getNode(i));
+            if (ws.isBoundary || ws.isAbandoned)
+                continue;
+            ret.add(ws);
         }
+        return ret;
+    }
+
+    @Override
+    public void visit(Way w)
+    {
+        ways.addAll(getWaySegments(w));
         Set<Node> set = endnodes;
         if(w.get("highway") != null || w.get("railway") != null)
             set = endnodes_highway;
-        addNode(w.getNode(0), set);
-        addNode(w.getNode(size-1), set);
+        addNode(w.firstNode(), set);
+        addNode(w.lastNode(), set);
+    }
+    @Override
+    public void visit(Node n)
+    {
     }
     private void addNode(Node n, Set<Node> s)
     {
-        Boolean m = middlenodes.contains(n);
-        Boolean e = endnodes.contains(n);
-        Boolean eh = endnodes_highway.contains(n);
-        Boolean o = othernodes.contains(n);
+        boolean m = middlenodes.contains(n);
+        boolean e = endnodes.contains(n);
+        boolean eh = endnodes_highway.contains(n);
+        boolean o = othernodes.contains(n);
         if(!m && !e && !o && !eh)
             s.add(n);
         else if(!o)
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java
index b9fb80e..6c6e09e 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java
@@ -57,7 +57,7 @@ public class UntaggedNode extends Test
                     p.visit(this);
                 }
             }
-            for (Way w : Main.main.getCurrentDataSet().ways) {
+            for (Way w : Main.main.getCurrentDataSet().getWays()) {
                 visit(w);
             }
         } else {
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java
index 29183eb..0f75208 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java
@@ -89,7 +89,7 @@ public class UntaggedWay extends Test
 
                     if( !hasName && !isRoundabout)
                         errors.add( new TestError(this, Severity.WARNING, tr("Unnamed ways"), UNNAMED_WAY, w) );
-		    else if(isRoundabout)
+                    else if(isRoundabout)
                         errors.add( new TestError(this, Severity.WARNING, tr("Unnamed junction"), UNNAMED_JUNCTION, w) );
                 }
             }
@@ -114,9 +114,9 @@ public class UntaggedWay extends Test
     @Override
     public void startTest(ProgressMonitor monitor)
     {
-    	super.startTest(monitor);
+        super.startTest(monitor);
         multipolygonways = new LinkedList<Way>();
-        for (final Relation r : Main.main.getCurrentDataSet().relations)
+        for (Relation r : Main.main.getCurrentDataSet().getRelations())
         {
             if(r.isUsable() && "multipolygon".equals(r.get("type")))
             {
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java
index 1ab0996..4614099 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java
@@ -57,6 +57,8 @@ public class WronglyOrderedWays extends Test  {
 
         if( !w.isUsable() )
             return;
+        if (w.getNodesCount() <= 0)
+            return;
 
         String natural = w.get("natural");
         if( natural == null)
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java b/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java
index 435644e..60733ea 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java
@@ -13,6 +13,7 @@ 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.data.osm.visitor.AbstractVisitor;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
 import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
@@ -42,7 +43,7 @@ public class NameVisitor extends AbstractVisitor {
      * is displayed.
      */
     public void visit(Node n) {
-        name = n.getName();
+        name = n.getDisplayName(DefaultNameFormatter.getInstance());
         addId(n);
         icon = ImageProvider.get("data", "node");
         className = "node";
@@ -54,7 +55,7 @@ public class NameVisitor extends AbstractVisitor {
      * is displayed with x being the number of nodes in the way.
      */
     public void visit(Way w) {
-        name = w.getName();
+        name = w.getDisplayName(DefaultNameFormatter.getInstance());
         addId(w);
         icon = ImageProvider.get("data", "way");
         className = "way";
@@ -64,7 +65,7 @@ public class NameVisitor extends AbstractVisitor {
     /**
      */
     public void visit(Relation e) {
-        name = e.getName();
+        name = e.getDisplayName(DefaultNameFormatter.getInstance());
         addId(e);
         icon = ImageProvider.get("data", "relation");
         className = "relation";
diff --git a/validator/tagchecker.cfg b/validator/tagchecker.cfg
index 1438909..e16209d 100644
--- a/validator/tagchecker.cfg
+++ b/validator/tagchecker.cfg
@@ -74,6 +74,6 @@ relation : E : type != *                                       # relation withou
 
 node : I : amenity == /restaurant|cafe|fast_food/ && name != * # restaurant without name
 #way  : I : highway != * && railway != * && waterway != * && name == * # unusual named way type
-*    : W : natural == water && waterway == *                   # unusual tag combination
+#*    : W : natural == water && waterway == *                   # unusual tag combination
 *    : W : highway == * && waterway == *                       # unusual tag combination
 *    : W : highway == * && natural == *                        # unusual tag combination
diff --git a/wmsplugin/.classpath b/wmsplugin/.classpath
index 2f5994b..663bc1b 100644
--- a/wmsplugin/.classpath
+++ b/wmsplugin/.classpath
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="lib" path="C:/prj.ht/josm-snapshot-467.jar"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="output" path="build"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+	<classpathentry kind="output" path="build"/>
+</classpath>
diff --git a/wmsplugin/build.xml b/wmsplugin/build.xml
index 099377d..67a3121 100644
--- a/wmsplugin/build.xml
+++ b/wmsplugin/build.xml
@@ -17,22 +17,26 @@
 **
 ** To build against the core in ../../core, create a correct manifest and deploy to
 ** SVN, run
-**    - set the property commit.message 
-**    - set the property josm.reference.release to lowest JOSM release number this
+**    - set the property commit.message
+**    - set the property plugin.main.version to lowest JOSM release number this
 **      plugin build is compatible with
 **    > ant  deploy
 **
 **
 -->
 <project name="wmsplugin" default="dist" basedir=".">
-    <property name="josm"                   location="../../core/dist/josm-custom.jar"/>
+	
+	
+	<property name="commit.message" value="fixed JOSM issue #3940" />		
+	<property name="plugin.main.version" value="2450" />
+	
+	
+	<property name="josm"                   location="../../core/dist/josm-custom.jar"/>
     <property name="plugin.dist.dir"        value="../../dist"/>
     <property name="plugin.build.dir"       value="build"/>
     <property name="plugin.jar"             value="${plugin.dist.dir}/${ant.project.name}.jar"/>
     <property name="ant.build.javac.target" value="1.5"/>
-	<property name="commit.message"         value="fixing JOSM issue #3186" />
-	<property name="josm.reference.release" value="2196" />
-	
+
     <target name="init">
         <mkdir dir="${plugin.build.dir}"/>
     </target>
@@ -55,7 +59,7 @@
                 <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
                 <attribute name="Plugin-Description" value="Display georeferenced images as background in JOSM (WMS servers, Yahoo, ...)."/>
                 <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/WMSPlugin"/>
-                <attribute name="Plugin-Mainversion" value="${josm.reference.release}"/>
+                <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
                 <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
                 <attribute name="de_Plugin-Link" value="http://wiki.openstreetmap.org/wiki/DE:JOSM/Plugins/WMSPlugin"/>
             </manifest>
@@ -84,56 +88,81 @@
         </condition>
         <copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
     </target>
-	
-	<target name="core-info">
-	        <exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
-	                    <env key="LANG" value="C"/>
-	                    <arg value="info"/>
-	                    <arg value="--xml"/>
-	                    <arg value="../../core"/>
-	        </exec>
-	        <xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
-			<echo>Building against core revision ${coreversion.info.entry.revision} ...</echo>
-			<delete file="core.info.xml" />
-		</target>
 
-		
-		<target name="commit-current">
-			<echo>Commiting the plugin source ...</echo>
-		    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+	<!--
+		 ************************** Publishing the plugin *********************************** 
+		-->
+			<!--
+			  ** extracts the JOSM release for the JOSM version in ../core and saves it in the 
+			  ** property ${coreversion.info.entry.revision}
+			  **
+			-->
+			<target name="core-info">
+		        <exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
 		                    <env key="LANG" value="C"/>
-		                    <arg value="commit"/>
-		                    <arg value="-m "${commit.message}""/>
-		                    <arg value="."/>
-		    </exec>	    
-		</target>
+		                    <arg value="info"/>
+		                    <arg value="--xml"/>
+		                    <arg value="../../core"/>
+		        </exec>
+		        <xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
+				<echo>Building against core revision ${coreversion.info.entry.revision}.</echo>			
+				<echo>Plugin-Mainversion is set to ${plugin.main.version}.</echo>
+				<delete file="core.info.xml" />
+			</target>
 
-		
-		<target name="update-current">
-			<echo>Updating basedir ...</echo>
-		    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
-		                    <env key="LANG" value="C"/>
-		                    <arg value="up"/>
-		                    <arg value="."/>
-		    </exec>	    
-			<echo>Updating ${plugin.jar} ...</echo>
-		    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
-		                    <env key="LANG" value="C"/>
-		                    <arg value="up"/>
-		                    <arg value="${plugin.jar}"/>
-		    </exec>	    
-		</target>
-		
-		<target name="commit-dist">
-				<echo>Commiting ${plugin.jar} ...</echo>
+			<!--
+			 ** commits the source tree for this plugin
+			-->
+			<target name="commit-current">
+				<echo>Commiting the plugin source with message '${commit.message}' ...</echo>
 			    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
 			                    <env key="LANG" value="C"/>
 			                    <arg value="commit"/>
-	                			<arg value="-m "${commit.message}""/>
-			                    <arg value="${plugin.jar}"/>
+			                    <arg value="-m '${commit.message}'"/>
+			                    <arg value="."/>
+			    </exec>	    
+			</target>
+
+			<!--
+			** updates (svn up) the source tree for this plugin
+			-->
+			<target name="update-current">
+				<echo>Updating plugin source ...</echo>
+			    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+			                    <env key="LANG" value="C"/>
+			                    <arg value="up"/>
+			                    <arg value="."/>
+			    </exec>	    
+				<echo>Updating ${plugin.jar} ...</echo>
+			    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+			                    <env key="LANG" value="C"/>
+			                    <arg value="up"/>
+			                    <arg value="../dist/${plugin.jar}"/>
 			    </exec>	    
-	   	</target>
-		
-		<target name="deploy" depends="core-info,commit-current,update-current,clean,dist,commit-dist">
-		</target>
+			</target>
+			
+			<!--
+			 ** commits the plugin.jar 
+			 -->
+			<target name="commit-dist">
+					<echo>
+	***** Properties of published ${plugin.jar} *****
+	Commit message    : '${commit.message}'					
+	Plugin-Mainversion: ${plugin.main.version}
+	JOSM build version: ${coreversion.info.entry.revision}
+	Plugin-Version    : ${version.entry.commit.revision}
+	***** / Properties of published ${plugin.jar} *****					
+						
+	Now commiting ${plugin.jar} ...
+	</echo>					
+				    <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+				                    <env key="LANG" value="C"/>
+					    			<arg value="-m '${commit.message}'"/>
+				    				<arg value="commit"/>	                			
+	            			        <arg value="${plugin.jar}"/>
+				    </exec>	    
+		   	</target>
+					
+			<target name="publish" depends="core-info,commit-current,update-current,clean,dist,commit-dist">
+			</target>
 </project>
diff --git a/wmsplugin/sources.cfg b/wmsplugin/sources.cfg
index 5dd428b..a0c11e3 100644
--- a/wmsplugin/sources.cfg
+++ b/wmsplugin/sources.cfg
@@ -4,9 +4,6 @@
 #
 true;Landsat;http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&layers=global_mosaic&styles=&format=image/jpeg&
 true;Open Aerial Map;http://openaerialmap.org/wms/?VERSION=1.0&request=GetMap&layers=world&styles=&format=image/jpeg&
-# fails with division by zero error
-false;NPE Maps;http://nick.dev.openstreetmap.org/openpaths/freemap.php?layers=npe&
-false;NPE Maps (Tim);http://dev.openstreetmap.org/~timsc/wms2/map.php?
 #
 # different forms for web access
 # must be html:<url>
@@ -18,14 +15,38 @@ false;TilesAtHome;html:http://josm.openstreetmap.de/wmsplugin/TilesAtHome.html?
 #
 # only for Germany
 false;Streets NRW Geofabrik.de;http://tools.geofabrik.de/osmi/view/strassennrw/josmwms?
+#
+#
+# only for North America
 # Terraserver USCG - High resolution maps
 false;Terraserver Topo;http://terraservice.net/ogcmap.ashx?version=1.1.1&request=GetMap&Layers=drg&styles=&format=image/jpeg&
 false;Terraserver Urban;http://terraservice.net/ogcmap.ashx?version=1.1.1&request=GetMap&Layers=urbanarea&styles=&format=image/jpeg&
+#
+#
 # only for Czech Republic
 false;Czech CUZK:KM;http://wms.cuzk.cz/wms.asp?service=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=kn,def_budovy,prehledky&FORMAT=image/png&TRANSPARENT=TRUE&
 false;Czech UHUL:ORTOFOTO;http://geoportal2.uhul.cz/cgi-bin/oprl.asp?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=Ortofoto_cb&STYLES=default&FORMAT=image/jpeg&TRANSPARENT=TRUE&
 #
 #
+# only for GB
+# fails with division by zero error
+false;NPE Maps;http://nick.dev.openstreetmap.org/openpaths/freemap.php?layers=npe&
+false;NPE Maps (Tim);http://dev.openstreetmap.org/~timsc/wms2/map.php?
+false;7th Series (OS7);http://ooc.openstreetmap.org/wms/map.php?source=os7&
+#
+#
+# only for Japan
+false;MLIT Japan (ORTHO);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+false;MLIT Japan (ORTHO01);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO01&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+false;MLIT Japan (ORTHO02);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO02&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+false;MLIT Japan (ORTHO03);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO03&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+#
+#
+# only for Italy
+false;Lodi - Italy;http://sit.provincia.lodi.it/mapserver/mapserv.exe?map=ortofoto_wgs84.map&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=Terraitaly%20Ortofoto%202007&STYLES=%2C%2C&FORMAT=image/png&TRANSPARENT=TRUE&
+false;Sicily - Italy;http://88.53.214.52/arcgis/services/OrtofotoATA_20072008_f33/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=CRS:84&LAYERS=0&STYLES=default&FORMAT=image/jpeg& 
+#
+#
 # URLS must be designed to append arguments directly behind. So the URLS should either end with '?' or '&'
 # Following arguments are added: width, height, bbox, srs (projection method)
 # srs is only added when no srs is given already (In this case the projection is checked
@@ -37,3 +58,4 @@ false;Czech UHUL:ORTOFOTO;http://geoportal2.uhul.cz/cgi-bin/oprl.asp?SERVICE=WMS
 # {bbox} is replaced by bounding box using projected coordinates
 # {width} is requested display width
 # {height} is requested display height
+# {w},{s},{n},{e} are replaced by corresponding coordinates
diff --git a/wmsplugin/src/wmsplugin/GeorefImage.java b/wmsplugin/src/wmsplugin/GeorefImage.java
index 2e6f87b..3f62193 100644
--- a/wmsplugin/src/wmsplugin/GeorefImage.java
+++ b/wmsplugin/src/wmsplugin/GeorefImage.java
@@ -136,10 +136,10 @@ public class GeorefImage implements Serializable {
         min = (EastNorth) in.readObject();
         boolean hasImage = in.readBoolean();
         if (hasImage)
-        	image = (BufferedImage) ImageIO.read(ImageIO.createImageInputStream(in));
+            image = (BufferedImage) ImageIO.read(ImageIO.createImageInputStream(in));
         else {
-        	in.readObject(); // read null from input stream
-        	image = null;
+            in.readObject(); // read null from input stream
+            image = null;
         }
     }
 
@@ -147,10 +147,10 @@ public class GeorefImage implements Serializable {
         out.writeObject(max);
         out.writeObject(min);
         if(image == null) {
-        	out.writeBoolean(false);
+            out.writeBoolean(false);
             out.writeObject(null);
         } else {
-        	out.writeBoolean(true);
+            out.writeBoolean(true);
             ImageIO.write(image, "png", ImageIO.createImageOutputStream(out));
         }
     }
diff --git a/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java b/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java
deleted file mode 100644
index f02f4fc..0000000
--- a/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package wmsplugin;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.event.ActionEvent;
-import javax.swing.*;
-import org.openstreetmap.josm.actions.JosmAction;
-
-
-
-public class Help_WMSmenuAction extends JosmAction {
-
-    /**
-     *
-     */
-
-
-    public Help_WMSmenuAction() {
-        //super("Help / About");
-        super(tr("help"), "help", tr("Help / About"), null, false);
-
-    }
-
-    public void actionPerformed(ActionEvent e) {
-        //todo - put this into a txt file?
-          String helptext =
-            tr("You can add, edit and delete WMS entries in the WMSplugin Preference Tab - "  +
-            "these will then show up in the WMS menu.\n\n"+
-
-            "You can also do this manually in the Advanced Preferences, using the following schema:\n"+
-            "wmsplugin.url.1.name=Landsat\n"+
-            "wmsplugin.url.1.url=http://onearth.jpl.nasa.gov....\n"+
-            "wmsplugin.url.2.name=NPE Maps... etc\n\n"+
-
-            "Full WMS URL input format example (landsat)\n"+
-            "http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&\n"+
-            "layers=global_mosaic&styles=&srs=EPSG:4326&format=image/jpeg\n\n"+
-
-            "For Metacarta's Map Rectifier http://labs.metacarta.com/rectifier/ , you only need to input the relevant 'id'.\n" +
-            "To add a Metacarta Map Rectifier menu item, manually create the URL like in this example, " +
-            "replacing 73 with your image id:\n" +
-            "http://labs.metacarta.com/rectifier/wms.cgi?id=73\n" +
-            "&srs=EPSG:4326&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png\n\n" +
-
-            "Note: Make sure the image is suitable, copyright-wise, if in doubt, don't use.");
-
-        JTextPane tp = new JTextPane();
-          JScrollPane js = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
-                  JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
-
-
-          js.getViewport().add(tp);
-          JFrame jf = new JFrame(tr("WMS Plugin Help"));
-          jf.getContentPane().add(js);
-          jf.pack();
-          jf.setSize(400,500);
-          jf.setVisible(true);
-          tp.setText(helptext);
-    }
-}
diff --git a/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java b/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
index b588ac9..26e99d2 100644
--- a/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
+++ b/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
@@ -154,7 +154,7 @@ public class Map_Rectifier_WMSmenuAction extends JosmAction {
         // This repeatedly shows the dialog in case there has been an error.
         // The loop is break;-ed if the users cancels
         outer: while(true) {
-        	diag.showDialog();
+            diag.showDialog();
             int answer = diag.getValue();
             // Break loop when the user cancels
             if(answer != 1)
diff --git a/wmsplugin/src/wmsplugin/WMSAdjustAction.java b/wmsplugin/src/wmsplugin/WMSAdjustAction.java
index 03b2a10..369041c 100644
--- a/wmsplugin/src/wmsplugin/WMSAdjustAction.java
+++ b/wmsplugin/src/wmsplugin/WMSAdjustAction.java
@@ -9,6 +9,7 @@ import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.util.List;
+import java.util.logging.Logger;
 
 import javax.swing.DefaultComboBoxModel;
 import javax.swing.DefaultListCellRenderer;
@@ -29,9 +30,9 @@ import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
 
 
-public class WMSAdjustAction extends MapMode implements
-    MouseListener, MouseMotionListener{
-
+public class WMSAdjustAction extends MapMode implements MouseListener, MouseMotionListener{
+    static private final Logger logger = Logger.getLogger(WMSAdjustAction.class.getName());
+    
     GeorefImage selectedImage;
     boolean mouseDown;
     EastNorth prevEastNorth;
@@ -48,19 +49,19 @@ public class WMSAdjustAction extends MapMode implements
     @Override public void enterMode() {
         super.enterMode();       
         if (!hasWMSLayersToAdjust()) {
-        	warnNoWMSLayers();
-        	return;
+            warnNoWMSLayers();
+            return;
         }
         List<WMSLayer> wmsLayers = Main.map.mapView.getLayersOfType(WMSLayer.class);
         if (wmsLayers.size() == 1) {
-        	adjustingLayer = wmsLayers.get(0);
+            adjustingLayer = wmsLayers.get(0);
         } else {
-        	adjustingLayer = (WMSLayer)askAdjustLayer(Main.map.mapView.getLayersOfType(WMSLayer.class));
+            adjustingLayer = (WMSLayer)askAdjustLayer(Main.map.mapView.getLayersOfType(WMSLayer.class));
         }
         if (adjustingLayer == null)
-        	return;
+            return;
         if (!adjustingLayer.isVisible()) {
-        	adjustingLayer.setVisible(true);
+            adjustingLayer.setVisible(true);
         }
         Main.map.mapView.addMouseListener(this);
         Main.map.mapView.addMouseMotionListener(this);
@@ -173,10 +174,10 @@ public class WMSAdjustAction extends MapMode implements
        pnl.add(layerList, GBC.eol());
    
        ExtendedDialog diag = new ExtendedDialog(
-    		   Main.parent, 
-    		   tr("Select WMS layer"), 
-    		   new String[] { tr("Start adjusting"),tr("Cancel") }
-    		   );
+               Main.parent, 
+               tr("Select WMS layer"), 
+               new String[] { tr("Start adjusting"),tr("Cancel") }
+               );
        diag.setContent(pnl);
        diag.setButtonIcons(new String[] { "mapmode/adjustwms", "cancel" });
        diag.showDialog();
@@ -193,7 +194,7 @@ public class WMSAdjustAction extends MapMode implements
     */
    protected void warnNoWMSLayers() {
        JOptionPane.showMessageDialog(
-    		   Main.parent,
+               Main.parent,
                tr("There are currently no WMS layer to adjust."),
                tr("No layers to adjust"), 
                JOptionPane.WARNING_MESSAGE
@@ -206,15 +207,13 @@ public class WMSAdjustAction extends MapMode implements
     * @return true if there is at least one WMS layer
     */
    protected boolean hasWMSLayersToAdjust() {
-	   if (Main.map == null) return false;
-	   if (Main.map.mapView == null) return false;
-	   return ! Main.map.mapView.getLayersOfType(WMSLayer.class).isEmpty();
+       if (Main.map == null) return false;
+       if (Main.map.mapView == null) return false;
+       return ! Main.map.mapView.getLayersOfType(WMSLayer.class).isEmpty();
    }
 
-
-
-	@Override
-	protected void updateEnabledState() {
-		setEnabled(hasWMSLayersToAdjust());
-	}   
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(hasWMSLayersToAdjust());
+    }   
 }
diff --git a/wmsplugin/src/wmsplugin/WMSGrabber.java b/wmsplugin/src/wmsplugin/WMSGrabber.java
index eecb3c9..e157667 100644
--- a/wmsplugin/src/wmsplugin/WMSGrabber.java
+++ b/wmsplugin/src/wmsplugin/WMSGrabber.java
@@ -33,10 +33,10 @@ import org.openstreetmap.josm.io.ProgressInputStream;
 
 
 public class WMSGrabber extends Grabber {
-	public static boolean isUrlWithPatterns(String url) {
-		return  url != null && url.contains("{") && url.contains("}");
-	}
-	
+    public static boolean isUrlWithPatterns(String url) {
+        return url != null && url.contains("{") && url.contains("}");
+    }
+    
     protected String baseURL;
     private final boolean urlWithPatterns;
 
@@ -70,7 +70,7 @@ public class WMSGrabber extends Grabber {
             }
             image.downloadingStarted = false;
         } catch(Exception e) {
-        	e.printStackTrace();
+            e.printStackTrace();
             throw new Exception(e.getMessage() + "\nImage couldn't be fetched: " + (url != null ? url.toString() : ""));
         }
     }
@@ -101,17 +101,21 @@ public class WMSGrabber extends Grabber {
         if (urlWithPatterns) {
             str = str.replaceAll("\\{proj\\}", myProj)
             .replaceAll("\\{bbox\\}", bbox)
+            .replaceAll("\\{w\\}", latLonFormat.format(w))
+            .replaceAll("\\{s\\}", latLonFormat.format(s))
+            .replaceAll("\\{e\\}", latLonFormat.format(e))
+            .replaceAll("\\{n\\}", latLonFormat.format(n))
             .replaceAll("\\{width\\}", String.valueOf(wi))
             .replaceAll("\\{height\\}", String.valueOf(ht));
         } else {
             str += "bbox=" + bbox
                 + getProjection(baseURL, false)
                 + "&width=" + wi + "&height=" + ht;
-        	if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
-        		System.out.println(tr("Warning: The base URL ''{0}'' for a WMS service doesn't have a trailing '&' or a trailing '?'.", baseURL));
-        		System.out.println(tr("Warning: Fetching WMS tiles is likely to fail. Please check you preference settings."));
-        		System.out.println(tr("Warning: The complete URL is ''{0}''.", str));
-        	}
+            if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
+                System.out.println(tr("Warning: The base URL ''{0}'' for a WMS service doesn't have a trailing '&' or a trailing '?'.", baseURL));
+                System.out.println(tr("Warning: Fetching WMS tiles is likely to fail. Please check you preference settings."));
+                System.out.println(tr("Warning: The complete URL is ''{0}''.", str));
+            }
         }
         return new URL(str.replace(" ", "%20"));
     }
diff --git a/wmsplugin/src/wmsplugin/WMSLayer.java b/wmsplugin/src/wmsplugin/WMSLayer.java
index f514def..e9b8aff 100644
--- a/wmsplugin/src/wmsplugin/WMSLayer.java
+++ b/wmsplugin/src/wmsplugin/WMSLayer.java
@@ -4,6 +4,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Component;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.io.File;
@@ -26,6 +27,7 @@ import javax.swing.JSeparator;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.DiskAccessAction;
 import org.openstreetmap.josm.actions.SaveActionBase;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.ProjectionBounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
@@ -38,391 +40,427 @@ import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
  * This is a layer that grabs the current screen from an WMS server. The data
- * fetched this way is tiled and managerd to the disc to reduce server load.
+ * fetched this way is tiled and managed to the disc to reduce server load.
  */
 public class WMSLayer extends Layer {
-	protected static final Icon icon =
-		new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png")));
-
-	public int messageNum = 5; //limit for messages per layer
-	protected MapView mv;
-	protected String resolution;
-	protected boolean stopAfterPaint = false;
-	protected int ImageSize = 500;
-	protected int dax = 10;
-	protected int day = 10;
-	protected int minZoom = 3;
-	protected double dx = 0.0;
-	protected double dy = 0.0;
-	protected double pixelPerDegree;
-	protected GeorefImage[][] images = new GeorefImage[dax][day];
-	JCheckBoxMenuItem startstop = new JCheckBoxMenuItem(tr("Automatic downloading"), true);
-	protected JCheckBoxMenuItem alphaChannel = new JCheckBoxMenuItem(new ToggleAlphaAction());
-	protected String baseURL;
-	protected String cookies;
-	protected final int serializeFormatVersion = 5;
-
-	private ExecutorService executor = null;
-
-	/** set to true if this layer uses an invalid base url */
-	private boolean usesInvalidUrl = false;
-	/** set to true if the user confirmed to use an potentially invalid WMS base url */
-	private boolean isInvalidUrlConfirmed = false;
-	
-	public WMSLayer() {
-		this(tr("Blank Layer"), null, null);
-		initializeImages();
-		mv = Main.map.mapView;
-	}
-
-	public WMSLayer(String name, String baseURL, String cookies) {
-		super(name);
-		alphaChannel.setSelected(Main.pref.getBoolean("wmsplugin.alpha_channel"));
-		setBackgroundLayer(true); /* set global background variable */ 
-		initializeImages();
-		this.baseURL = baseURL;
-		this.cookies = cookies;
-		WMSGrabber.getProjection(baseURL, true);
-		mv = Main.map.mapView;
-		resolution = mv.getDist100PixelText();
-		pixelPerDegree = getPPD();
-
-		executor = Executors.newFixedThreadPool(3);
-		if (!baseURL.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(baseURL)) {
-			if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
-				if (!confirmMalformedUrl(baseURL)) {
-					System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", baseURL));
-					usesInvalidUrl = true;
-					setName(getName() + tr("(deactivated)"));
-					return;
-				} else {
-					isInvalidUrlConfirmed = true;
-				}
-			}
-		}
-	}
-
-	public double getDx(){
-		return dx;
-	}
-
-	public double getDy(){
-		return dy;
-	}
-
-	@Override
-	public void destroy() {	
-		try {
-			executor.shutdownNow();
-			// Might not be initalized, so catch NullPointer as well
-		} catch(Exception x) {
-			x.printStackTrace();
-		}
-	}
-
-	public double getPPD(){
-		ProjectionBounds bounds = mv.getProjectionBounds();
-		return mv.getWidth() / (bounds.max.east() - bounds.min.east());
-	}
-
-	public void initializeImages() {
-		images = new GeorefImage[dax][day];
-		for(int x = 0; x<dax; ++x) {
-			for(int y = 0; y<day; ++y) {
-				images[x][y]= new GeorefImage(false);
-			}
-		}
-	}
-
-	@Override public Icon getIcon() {
-		return icon;
-	}
-
-	@Override public String getToolTipText() {
-		if(startstop.isSelected())
-			return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution);
-		else
-			return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution);
-	}
-
-	@Override public boolean isMergable(Layer other) {
-		return false;
-	}
-
-	@Override public void mergeFrom(Layer from) {
-	}
-
-	private ProjectionBounds XYtoBounds (int x, int y) {
-		return new ProjectionBounds(
-				new EastNorth(      x * ImageSize / pixelPerDegree,       y * ImageSize / pixelPerDegree),
-				new EastNorth((x + 1) * ImageSize / pixelPerDegree, (y + 1) * ImageSize / pixelPerDegree));
-	}
-
-	private int modulo (int a, int b) {
-		return a % b >= 0 ? a%b : a%b+b;
-	}
-
-	@Override public void paint(Graphics g, final MapView mv) {
-		if(baseURL == null) return;
-		if (usesInvalidUrl && !isInvalidUrlConfirmed) return;
-
-		if( !startstop.isSelected() || (pixelPerDegree / getPPD() > minZoom) ){ //don't download when it's too outzoomed
-			for(int x = 0; x<dax; ++x) {
-				for(int y = 0; y<day; ++y) {
-					images[modulo(x,dax)][modulo(y,day)].paint(g, mv, dx, dy);
-				}
-			}
-		} else {
-			downloadAndPaintVisible(g, mv);
-		}
-	}
-
-	public void displace(double dx, double dy) {
-		this.dx += dx;
-		this.dy += dy;
-	}
-
-	protected boolean confirmMalformedUrl(String url) {
-		if (isInvalidUrlConfirmed)
-			return true;
-		String msg  = tr("<html>The base URL<br>"
-				        + "''{0}''<br>"
-				        + "for this WMS layer does neither end with a ''&'' nor with a ''?''.<br>"
-				        + "This is likely to lead to invalid WMS request. You should check your<br>"
-				        + "preference settings.<br>"
-				        + "Do you want to fetch WMS tiles anyway?",				        
-				        url);
-		String [] options = new String[] {
-			tr("Yes, fetch images"),
-			tr("No, abort")
-		};
-		int ret = JOptionPane.showOptionDialog(
-				Main.parent, 
-				msg,
-				tr("Invalid URL?"),
-				JOptionPane.YES_NO_OPTION, 
-				JOptionPane.WARNING_MESSAGE, 
-				null, 
-				options, options[1]
-		);
-		switch(ret) {
-		case JOptionPane.YES_OPTION: return true;
-		default: return false;
-		}
-	}
-	protected void downloadAndPaintVisible(Graphics g, final MapView mv){
-		if (usesInvalidUrl)
-			return;
-		ProjectionBounds bounds = mv.getProjectionBounds();
-		int bminx= (int)Math.floor (((bounds.min.east() - dx) * pixelPerDegree) / ImageSize );
-		int bminy= (int)Math.floor (((bounds.min.north() - dy) * pixelPerDegree) / ImageSize );
-		int bmaxx= (int)Math.ceil  (((bounds.max.east() - dx) * pixelPerDegree) / ImageSize );
-		int bmaxy= (int)Math.ceil  (((bounds.max.north() - dy) * pixelPerDegree) / ImageSize );
-
-		if((bmaxx - bminx > dax) || (bmaxy - bminy > day)){
-			JOptionPane.showMessageDialog(
-					Main.parent,
-					tr("The requested area is too big. Please zoom in a little, or change resolution"),
-					tr("Error"),
-					JOptionPane.ERROR_MESSAGE
-			);
-			return;
-		}		
-		
-		for(int x = bminx; x<bmaxx; ++x) {
-			for(int y = bminy; y<bmaxy; ++y){
-				GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
-				g.drawRect(x, y, dax, bminy);
-				if(!img.paint(g, mv, dx, dy) && !img.downloadingStarted){
-					img.downloadingStarted = true;
-					img.image = null;
-					img.flushedResizedCachedInstance();
-					Grabber gr = WMSPlugin.getGrabber(XYtoBounds(x,y), img, mv, this);
-					gr.setPriority(1);
-					executor.submit(gr);
-				}
-			}
-		}
-	}
-
-	@Override public void visitBoundingBox(BoundingXYVisitor v) {
-		for(int x = 0; x<dax; ++x) {
-			for(int y = 0; y<day; ++y)
-				if(images[x][y].image!=null){
-					v.visit(images[x][y].min);
-					v.visit(images[x][y].max);
-				}
-		}
-	}
-
-	@Override public Object getInfoComponent() {
-		return getToolTipText();
-	}
-
-	@Override public Component[] getMenuEntries() {
-		return new Component[]{
-				new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
-				new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
-				new JSeparator(),
-				new JMenuItem(new LoadWmsAction()),
-				new JMenuItem(new SaveWmsAction()),
-				new JSeparator(),
-				startstop,
-				alphaChannel,
-				new JMenuItem(new ChangeResolutionAction()),
-				new JMenuItem(new ReloadErrorTilesAction()),
-				new JMenuItem(new DownloadAction()),
-				new JSeparator(),
-				new JMenuItem(new LayerListPopup.InfoAction(this))
-		};
-	}
-
-	public GeorefImage findImage(EastNorth eastNorth) {
-		for(int x = 0; x<dax; ++x) {
-			for(int y = 0; y<day; ++y)
-				if(images[x][y].image!=null && images[x][y].min!=null && images[x][y].max!=null)
-					if(images[x][y].contains(eastNorth, dx, dy))
-						return images[x][y];
-		}
-		return null;
-	}
-
-	public class DownloadAction extends AbstractAction {
-		public DownloadAction() {
-			super(tr("Download visible tiles"));
-		}
-		public void actionPerformed(ActionEvent ev) {
-			downloadAndPaintVisible(mv.getGraphics(), mv);
-		}
-	}
-
-	public class ChangeResolutionAction extends AbstractAction {
-		public ChangeResolutionAction() {
-			super(tr("Change resolution"));
-		}
-		public void actionPerformed(ActionEvent ev) {
-			initializeImages();
-			resolution = mv.getDist100PixelText();
-			pixelPerDegree = getPPD();
-			mv.repaint();
-		}
-	}
-
-	public class ReloadErrorTilesAction extends AbstractAction {
-		public ReloadErrorTilesAction() {
-			super(tr("Reload erroneous tiles"));
-		}
-		public void actionPerformed(ActionEvent ev) {
-			// Delete small files, because they're probably blank tiles.
-			// See https://josm.openstreetmap.de/ticket/2307
-			WMSPlugin.cache.customCleanUp(CacheFiles.CLEAN_SMALL_FILES, 2048);
-
-			for (int x = 0; x < dax; ++x) {
-				for (int y = 0; y < day; ++y) {
-					GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
-					if(img.failed){
-						img.image = null;
-						img.flushedResizedCachedInstance();
-						img.downloadingStarted = false;
-						img.failed = false;
-						mv.repaint();
-					}
-				}
-			}
-		}
-	}
-
-	public class ToggleAlphaAction extends AbstractAction {
-		public ToggleAlphaAction() {
-			super(tr("Alpha channel"));
-		}
-		public void actionPerformed(ActionEvent ev) {
-			JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource();
-			boolean alphaChannel = checkbox.isSelected();
-			Main.pref.put("wmsplugin.alpha_channel", alphaChannel);
-
-			// clear all resized cached instances and repaint the layer
-			for (int x = 0; x < dax; ++x) {
-				for (int y = 0; y < day; ++y) {
-					GeorefImage img = images[modulo(x, dax)][modulo(y, day)];
-					img.flushedResizedCachedInstance();
-				}
-			}
-			mv.repaint();
-		}
-	}
-	
-	public class SaveWmsAction extends AbstractAction {
-		public SaveWmsAction() {
-			super(tr("Save WMS layer to file"), ImageProvider.get("save"));
-		}
-		public void actionPerformed(ActionEvent ev) {
-			File f = SaveActionBase.createAndOpenSaveFileChooser(
-					tr("Save WMS layer"), ".wms");
-			try {
-				if (f != null) {
-					ObjectOutputStream oos = new ObjectOutputStream(
-							new FileOutputStream(f)
-					);
-					oos.writeInt(serializeFormatVersion);
-					oos.writeInt(dax);
-					oos.writeInt(day);
-					oos.writeInt(ImageSize);
-					oos.writeDouble(pixelPerDegree);
-					oos.writeObject(getName());
-					oos.writeObject(baseURL);
-					oos.writeObject(images);
-					oos.close();
-				}
-			} catch (Exception ex) {
-				ex.printStackTrace(System.out);
-			}
-		}
-	}
-
-	public class LoadWmsAction extends AbstractAction {
-		public LoadWmsAction() {
-			super(tr("Load WMS layer from file"), ImageProvider.get("load"));
-		}
-		public void actionPerformed(ActionEvent ev) {
-			JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true,
-					false, tr("Load WMS layer"), "wms");
-			if(fc == null) return;
-			File f = fc.getSelectedFile();
-			if (f == null) return;
-			try
-			{
-				FileInputStream fis = new FileInputStream(f);
-				ObjectInputStream ois = new ObjectInputStream(fis);
-				int sfv = ois.readInt();
-				if (sfv != serializeFormatVersion) {
-					JOptionPane.showMessageDialog(Main.parent,
-							tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion),
-							tr("File Format Error"),
-							JOptionPane.ERROR_MESSAGE);
-					return;
-				}
-				startstop.setSelected(false);
-				dax = ois.readInt();
-				day = ois.readInt();
-				ImageSize = ois.readInt();
-				pixelPerDegree = ois.readDouble();
-				setName((String)ois.readObject());
-				baseURL = (String) ois.readObject();
-				images = (GeorefImage[][])ois.readObject();
-				ois.close();
-				fis.close();
-				mv.repaint();
-			}
-			catch (Exception ex) {
-				// FIXME be more specific
-				ex.printStackTrace(System.out);
-				JOptionPane.showMessageDialog(Main.parent,
-						tr("Error loading file"),
-						tr("Error"),
-						JOptionPane.ERROR_MESSAGE);
-				return;
-			}
-		}
-	}
+    protected static final Icon icon =
+        new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png")));
+
+    public int messageNum = 5; //limit for messages per layer
+    protected MapView mv;
+    protected String resolution;
+    protected boolean stopAfterPaint = false;
+    protected int ImageSize = 500;
+    protected int dax = 10;
+    protected int day = 10;
+    protected int minZoom = 3;
+    protected double dx = 0.0;
+    protected double dy = 0.0;
+    protected double pixelPerDegree;
+    protected GeorefImage[][] images = new GeorefImage[dax][day];
+    JCheckBoxMenuItem startstop = new JCheckBoxMenuItem(tr("Automatic downloading"), true);
+    protected JCheckBoxMenuItem alphaChannel = new JCheckBoxMenuItem(new ToggleAlphaAction());
+    protected String baseURL;
+    protected String cookies;
+    protected final int serializeFormatVersion = 5;
+
+    private ExecutorService executor = null;
+
+    /** set to true if this layer uses an invalid base url */
+    private boolean usesInvalidUrl = false;
+    /** set to true if the user confirmed to use an potentially invalid WMS base url */
+    private boolean isInvalidUrlConfirmed = false;
+
+    public WMSLayer() {
+        this(tr("Blank Layer"), null, null);
+        initializeImages();
+        mv = Main.map.mapView;
+    }
+
+    public WMSLayer(String name, String baseURL, String cookies) {
+        super(name);
+        alphaChannel.setSelected(Main.pref.getBoolean("wmsplugin.alpha_channel"));
+        setBackgroundLayer(true); /* set global background variable */
+        initializeImages();
+        this.baseURL = baseURL;
+        this.cookies = cookies;
+        WMSGrabber.getProjection(baseURL, true);
+        mv = Main.map.mapView;
+
+        // quick hack to predefine the PixelDensity to reuse the cache
+        int codeIndex = getName().indexOf("#PPD=");
+        if (codeIndex != -1) {
+            pixelPerDegree = Double.valueOf(getName().substring(codeIndex+5));
+        } else {
+            pixelPerDegree = getPPD();
+        }
+        resolution = mv.getDist100PixelText();
+        pixelPerDegree = getPPD();
+
+        executor = Executors.newFixedThreadPool(3);
+        if (baseURL != null && !baseURL.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(baseURL)) {
+            if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
+                if (!confirmMalformedUrl(baseURL)) {
+                    System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", baseURL));
+                    usesInvalidUrl = true;
+                    setName(getName() + tr("(deactivated)"));
+                    return;
+                } else {
+                    isInvalidUrlConfirmed = true;
+                }
+            }
+        }
+    }
+
+    public double getDx(){
+        return dx;
+    }
+
+    public double getDy(){
+        return dy;
+    }
+
+    @Override
+    public void destroy() {
+        try {
+            executor.shutdownNow();
+            // Might not be initialized, so catch NullPointer as well
+        } catch(Exception x) {
+            x.printStackTrace();
+        }
+    }
+
+    public double getPPD(){
+        ProjectionBounds bounds = mv.getProjectionBounds();
+        return mv.getWidth() / (bounds.max.east() - bounds.min.east());
+    }
+
+    public void initializeImages() {
+        images = new GeorefImage[dax][day];
+        for(int x = 0; x<dax; ++x) {
+            for(int y = 0; y<day; ++y) {
+                images[x][y]= new GeorefImage(false);
+            }
+        }
+    }
+
+    @Override public Icon getIcon() {
+        return icon;
+    }
+
+    @Override public String getToolTipText() {
+        if(startstop.isSelected())
+            return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution);
+        else
+            return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution);
+    }
+
+    @Override public boolean isMergable(Layer other) {
+        return false;
+    }
+
+    @Override public void mergeFrom(Layer from) {
+    }
+
+    private ProjectionBounds XYtoBounds (int x, int y) {
+        return new ProjectionBounds(
+                new EastNorth(      x * ImageSize / pixelPerDegree,       y * ImageSize / pixelPerDegree),
+                new EastNorth((x + 1) * ImageSize / pixelPerDegree, (y + 1) * ImageSize / pixelPerDegree));
+    }
+
+    private int modulo (int a, int b) {
+        return a % b >= 0 ? a%b : a%b+b;
+    }
+
+    @Override public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
+        if(baseURL == null) return;
+        if (usesInvalidUrl && !isInvalidUrlConfirmed) return;
+
+        if( !startstop.isSelected() || (pixelPerDegree / getPPD() > minZoom) ){ //don't download when it's too outzoomed
+            for(int x = 0; x<dax; ++x) {
+                for(int y = 0; y<day; ++y) {
+                    images[modulo(x,dax)][modulo(y,day)].paint(g, mv, dx, dy);
+                }
+            }
+        } else {
+            downloadAndPaintVisible(g, mv);
+        }
+    }
+
+    public void displace(double dx, double dy) {
+        this.dx += dx;
+        this.dy += dy;
+    }
+
+    protected boolean confirmMalformedUrl(String url) {
+        if (isInvalidUrlConfirmed)
+            return true;
+        String msg  = tr("<html>The base URL<br>"
+                        + "''{0}''<br>"
+                        + "for this WMS layer does neither end with a ''&'' nor with a ''?''.<br>"
+                        + "This is likely to lead to invalid WMS request. You should check your<br>"
+                        + "preference settings.<br>"
+                        + "Do you want to fetch WMS tiles anyway?",
+                        url);
+        String [] options = new String[] {
+            tr("Yes, fetch images"),
+            tr("No, abort")
+        };
+        int ret = JOptionPane.showOptionDialog(
+                Main.parent,
+                msg,
+                tr("Invalid URL?"),
+                JOptionPane.YES_NO_OPTION,
+                JOptionPane.WARNING_MESSAGE,
+                null,
+                options, options[1]
+        );
+        switch(ret) {
+        case JOptionPane.YES_OPTION: return true;
+        default: return false;
+        }
+    }
+    protected void downloadAndPaintVisible(Graphics g, final MapView mv){
+        if (usesInvalidUrl)
+            return;
+        ProjectionBounds bounds = mv.getProjectionBounds();
+        int bminx= (int)Math.floor (((bounds.min.east() - dx) * pixelPerDegree) / ImageSize );
+        int bminy= (int)Math.floor (((bounds.min.north() - dy) * pixelPerDegree) / ImageSize );
+        int bmaxx= (int)Math.ceil  (((bounds.max.east() - dx) * pixelPerDegree) / ImageSize );
+        int bmaxy= (int)Math.ceil  (((bounds.max.north() - dy) * pixelPerDegree) / ImageSize );
+
+        if((bmaxx - bminx > dax) || (bmaxy - bminy > day)){
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("The requested area is too big. Please zoom in a little, or change resolution"),
+                    tr("Error"),
+                    JOptionPane.ERROR_MESSAGE
+            );
+            return;
+        }
+
+        for(int x = bminx; x<bmaxx; ++x) {
+            for(int y = bminy; y<bmaxy; ++y){
+                GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
+                g.drawRect(x, y, dax, bminy);
+                if(!img.paint(g, mv, dx, dy) && !img.downloadingStarted){
+                    img.downloadingStarted = true;
+                    img.image = null;
+                    img.flushedResizedCachedInstance();
+                    Grabber gr = WMSPlugin.getGrabber(XYtoBounds(x,y), img, mv, this);
+                    gr.setPriority(1);
+                    executor.submit(gr);
+                }
+            }
+        }
+    }
+
+    @Override public void visitBoundingBox(BoundingXYVisitor v) {
+        for(int x = 0; x<dax; ++x) {
+            for(int y = 0; y<day; ++y)
+                if(images[x][y].image!=null){
+                    v.visit(images[x][y].min);
+                    v.visit(images[x][y].max);
+                }
+        }
+    }
+
+    @Override public Object getInfoComponent() {
+        return getToolTipText();
+    }
+
+    @Override public Component[] getMenuEntries() {
+        return new Component[]{
+                new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+                new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+                new JSeparator(),
+                new JMenuItem(new LoadWmsAction()),
+                new JMenuItem(new SaveWmsAction()),
+                new JMenuItem(new BookmarkWmsAction()),
+                new JSeparator(),
+                startstop,
+                alphaChannel,
+                new JMenuItem(new ChangeResolutionAction()),
+                new JMenuItem(new ReloadErrorTilesAction()),
+                new JMenuItem(new DownloadAction()),
+                new JSeparator(),
+                new JMenuItem(new LayerListPopup.InfoAction(this))
+        };
+    }
+
+    public GeorefImage findImage(EastNorth eastNorth) {
+        for(int x = 0; x<dax; ++x) {
+            for(int y = 0; y<day; ++y)
+                if(images[x][y].image!=null && images[x][y].min!=null && images[x][y].max!=null)
+                    if(images[x][y].contains(eastNorth, dx, dy))
+                        return images[x][y];
+        }
+        return null;
+    }
+
+    public class DownloadAction extends AbstractAction {
+        public DownloadAction() {
+            super(tr("Download visible tiles"));
+        }
+        public void actionPerformed(ActionEvent ev) {
+            downloadAndPaintVisible(mv.getGraphics(), mv);
+        }
+    }
+
+    public class ChangeResolutionAction extends AbstractAction {
+        public ChangeResolutionAction() {
+            super(tr("Change resolution"));
+        }
+        public void actionPerformed(ActionEvent ev) {
+            initializeImages();
+            resolution = mv.getDist100PixelText();
+            pixelPerDegree = getPPD();
+            mv.repaint();
+        }
+    }
+
+    public class ReloadErrorTilesAction extends AbstractAction {
+        public ReloadErrorTilesAction() {
+            super(tr("Reload erroneous tiles"));
+        }
+        public void actionPerformed(ActionEvent ev) {
+            // Delete small files, because they're probably blank tiles.
+            // See https://josm.openstreetmap.de/ticket/2307
+            WMSPlugin.cache.customCleanUp(CacheFiles.CLEAN_SMALL_FILES, 4096);
+
+            for (int x = 0; x < dax; ++x) {
+                for (int y = 0; y < day; ++y) {
+                    GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
+                    if(img.failed){
+                        img.image = null;
+                        img.flushedResizedCachedInstance();
+                        img.downloadingStarted = false;
+                        img.failed = false;
+                        mv.repaint();
+                    }
+                }
+            }
+        }
+    }
+
+    public class ToggleAlphaAction extends AbstractAction {
+        public ToggleAlphaAction() {
+            super(tr("Alpha channel"));
+        }
+        public void actionPerformed(ActionEvent ev) {
+            JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource();
+            boolean alphaChannel = checkbox.isSelected();
+            Main.pref.put("wmsplugin.alpha_channel", alphaChannel);
+
+            // clear all resized cached instances and repaint the layer
+            for (int x = 0; x < dax; ++x) {
+                for (int y = 0; y < day; ++y) {
+                    GeorefImage img = images[modulo(x, dax)][modulo(y, day)];
+                    img.flushedResizedCachedInstance();
+                }
+            }
+            mv.repaint();
+        }
+    }
+
+    public class SaveWmsAction extends AbstractAction {
+        public SaveWmsAction() {
+            super(tr("Save WMS layer to file"), ImageProvider.get("save"));
+        }
+        public void actionPerformed(ActionEvent ev) {
+            File f = SaveActionBase.createAndOpenSaveFileChooser(
+                    tr("Save WMS layer"), ".wms");
+            try {
+                if (f != null) {
+                    ObjectOutputStream oos = new ObjectOutputStream(
+                            new FileOutputStream(f)
+                    );
+                    oos.writeInt(serializeFormatVersion);
+                    oos.writeInt(dax);
+                    oos.writeInt(day);
+                    oos.writeInt(ImageSize);
+                    oos.writeDouble(pixelPerDegree);
+                    oos.writeObject(getName());
+                    oos.writeObject(baseURL);
+                    oos.writeObject(images);
+                    oos.close();
+                }
+            } catch (Exception ex) {
+                ex.printStackTrace(System.out);
+            }
+        }
+    }
+
+    public class LoadWmsAction extends AbstractAction {
+        public LoadWmsAction() {
+            super(tr("Load WMS layer from file"), ImageProvider.get("load"));
+        }
+        public void actionPerformed(ActionEvent ev) {
+            JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true,
+                    false, tr("Load WMS layer"), "wms");
+            if(fc == null) return;
+            File f = fc.getSelectedFile();
+            if (f == null) return;
+            try
+            {
+                FileInputStream fis = new FileInputStream(f);
+                ObjectInputStream ois = new ObjectInputStream(fis);
+                int sfv = ois.readInt();
+                if (sfv != serializeFormatVersion) {
+                    JOptionPane.showMessageDialog(Main.parent,
+                            tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion),
+                            tr("File Format Error"),
+                            JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                startstop.setSelected(false);
+                dax = ois.readInt();
+                day = ois.readInt();
+                ImageSize = ois.readInt();
+                pixelPerDegree = ois.readDouble();
+                setName((String)ois.readObject());
+                baseURL = (String) ois.readObject();
+                images = (GeorefImage[][])ois.readObject();
+                ois.close();
+                fis.close();
+                mv.repaint();
+            }
+            catch (Exception ex) {
+                // FIXME be more specific
+                ex.printStackTrace(System.out);
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("Error loading file"),
+                        tr("Error"),
+                        JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+        }
+    }
+    /**
+     * This action will add a WMS layer menu entry with the current WMS layer URL and name extended by the current resolution.
+     * When using the menu entry again, the WMS cache will be used properly.
+     */
+    public class BookmarkWmsAction extends AbstractAction {
+        public BookmarkWmsAction() {
+            super(tr("Set WMS Bookmark"));
+        }
+        public void actionPerformed(ActionEvent ev) {
+            int i = 0;
+            while (Main.pref.hasKey("wmsplugin.url."+i+".url")) {
+                i++;
+            }
+            String baseName;
+            // cut old parameter
+            int parameterIndex = getName().indexOf("#PPD=");
+            if (parameterIndex != -1) {
+                baseName = getName().substring(0,parameterIndex);
+            }
+            else {
+                baseName = getName();
+            }
+            Main.pref.put("wmsplugin.url."+ i +".url",baseURL );
+            Main.pref.put("wmsplugin.url."+String.valueOf(i)+".name", baseName + "#" + getPPD() );
+            WMSPlugin.refreshMenu();
+        }
+    }
 }
diff --git a/wmsplugin/src/wmsplugin/WMSPlugin.java b/wmsplugin/src/wmsplugin/WMSPlugin.java
index 313676e..81732ed 100644
--- a/wmsplugin/src/wmsplugin/WMSPlugin.java
+++ b/wmsplugin/src/wmsplugin/WMSPlugin.java
@@ -1,5 +1,6 @@
 package wmsplugin;
 
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
@@ -50,13 +51,13 @@ public class WMSPlugin extends Plugin {
     static boolean doOverlap = false;
     static int overlapEast = 14;
     static int overlapNorth = 4;
-    
+
     // remember state of menu item to restore on changed preferences
     static private boolean menuEnabled = false;
-    
+
     protected void initExporterAndImporter() {
-    	ExtensionFileFilter.exporters.add(new WMSLayerExporter());
-    	ExtensionFileFilter.importers.add(new WMSLayerImporter());
+        ExtensionFileFilter.exporters.add(new WMSLayerExporter());
+        ExtensionFileFilter.importers.add(new WMSLayerImporter());
     }
 
     public WMSPlugin() {
@@ -93,19 +94,19 @@ public class WMSPlugin extends Plugin {
 
         TreeSet<String> keys = new TreeSet<String>(prefs.keySet());
 
-        // Here we load the settings for "overlap" checkbox and spinboxes. 
-         
-        try { 
-            doOverlap = Boolean.valueOf(prefs.get("wmsplugin.url.overlap"));             
-        } catch (Exception e) {} // If sth fails, we drop to default settings. 
- 
-        try { 
-            overlapEast = Integer.valueOf(prefs.get("wmsplugin.url.overlapEast"));             
-        } catch (Exception e) {} // If sth fails, we drop to default settings. 
- 
-        try { 
-            overlapNorth = Integer.valueOf(prefs.get("wmsplugin.url.overlapNorth"));             
-        } catch (Exception e) {} // If sth fails, we drop to default settings. 
+        // Here we load the settings for "overlap" checkbox and spinboxes.
+
+        try {
+            doOverlap = Boolean.valueOf(prefs.get("wmsplugin.url.overlap"));
+        } catch (Exception e) {} // If sth fails, we drop to default settings.
+
+        try {
+            overlapEast = Integer.valueOf(prefs.get("wmsplugin.url.overlapEast"));
+        } catch (Exception e) {} // If sth fails, we drop to default settings.
+
+        try {
+            overlapNorth = Integer.valueOf(prefs.get("wmsplugin.url.overlapNorth"));
+        } catch (Exception e) {} // If sth fails, we drop to default settings.
 
         // And then the names+urls of WMS servers
         int prefid = 0;
@@ -168,7 +169,7 @@ public class WMSPlugin extends Plugin {
         MainMenu menu = Main.main.menu;
 
         if (wmsJMenu == null)
-            wmsJMenu = menu.addMenu(marktr("WMS"), KeyEvent.VK_W, menu.defaultMenuPos);
+            wmsJMenu = menu.addMenu(marktr("WMS"), KeyEvent.VK_W, menu.defaultMenuPos, ht("/Plugin/WMS"));
         else
             wmsJMenu.removeAll();
 
@@ -186,8 +187,6 @@ public class WMSPlugin extends Plugin {
                 Main.main.addLayer(new WMSLayer());
             }
         }));
-        wmsJMenu.addSeparator();
-        wmsJMenu.add(new JMenuItem(new Help_WMSmenuAction()));
         setEnabledAll(menuEnabled);
     }
 
diff --git a/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java b/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
index bdd155b..9d21e3e 100644
--- a/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
+++ b/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
@@ -58,7 +58,7 @@ public class WMSPreferenceEditor implements PreferenceSetting {
         final DefaultTableModel modeldef = new DefaultTableModel(
         new String[]{tr("Menu Name (Default)"), tr("WMS URL (Default)")}, 0);
         final JTable listdef = new JTable(modeldef){
-        	@Override
+            @Override
             public boolean isCellEditable(int row,int column){return false;}
         };
         JScrollPane scrolldef = new JScrollPane(listdef);
@@ -84,10 +84,10 @@ public class WMSPreferenceEditor implements PreferenceSetting {
                 p.add(new JLabel(tr("WMS URL")), GBC.std().insets(0,0,5,0));
                 p.add(value, GBC.eol().insets(5,0,0,0).fill(GBC.HORIZONTAL));
                 int answer = JOptionPane.showConfirmDialog(
-                		gui, p, 
-                		tr("Enter a menu name and WMS URL"), 
-                		JOptionPane.OK_CANCEL_OPTION,
-                		JOptionPane.QUESTION_MESSAGE);
+                        gui, p, 
+                        tr("Enter a menu name and WMS URL"), 
+                        JOptionPane.OK_CANCEL_OPTION,
+                        JOptionPane.QUESTION_MESSAGE);
                 if (answer == JOptionPane.OK_OPTION) {
                     model.addRow(new String[]{key.getText(), value.getText()});
                 }
@@ -116,35 +116,35 @@ public class WMSPreferenceEditor implements PreferenceSetting {
                 int[] lines = listdef.getSelectedRows();
                 if (lines.length == 0) {
                     JOptionPane.showMessageDialog(
-                    		gui, 
-                    		tr("Please select at least one row to copy."),
-                    		tr("Information"),
-                    		JOptionPane.INFORMATION_MESSAGE
-                    		);
+                            gui, 
+                            tr("Please select at least one row to copy."),
+                            tr("Information"),
+                            JOptionPane.INFORMATION_MESSAGE
+                            );
                     return;
                 }
                 
                 outer: for(int i = 0; i < lines.length; i++) {
-                	String c1 = modeldef.getValueAt(lines[i], 0).toString();
-                	String c2 = modeldef.getValueAt(lines[i], 1).toString();
-                	
-                	// Check if an entry with exactly the same values already
-                	// exists
-                	for(int j = 0; j < model.getRowCount(); j++) {
-                		if(c1.equals(model.getValueAt(j, 0).toString()) 
-                				&& c2.equals(model.getValueAt(j, 1).toString())) {
-                			// Select the already existing row so the user has
-                			// some feedback in case an entry exists
-                			list.getSelectionModel().setSelectionInterval(j, j);
-                			list.scrollRectToVisible(list.getCellRect(j, 0, true));
-                			continue outer;
-                		}
-                	}
-                	
-	                model.addRow(new String[] {c1, c2});
-	                int lastLine = model.getRowCount() - 1;
-	                list.getSelectionModel().setSelectionInterval(lastLine, lastLine);
-	                list.scrollRectToVisible(list.getCellRect(lastLine, 0, true));
+                    String c1 = modeldef.getValueAt(lines[i], 0).toString();
+                    String c2 = modeldef.getValueAt(lines[i], 1).toString();
+                    
+                    // Check if an entry with exactly the same values already
+                    // exists
+                    for(int j = 0; j < model.getRowCount(); j++) {
+                        if(c1.equals(model.getValueAt(j, 0).toString()) 
+                                && c2.equals(model.getValueAt(j, 1).toString())) {
+                            // Select the already existing row so the user has
+                            // some feedback in case an entry exists
+                            list.getSelectionModel().setSelectionInterval(j, j);
+                            list.scrollRectToVisible(list.getCellRect(j, 0, true));
+                            continue outer;
+                        }
+                    }
+                    
+                    model.addRow(new String[] {c1, c2});
+                    int lastLine = model.getRowCount() - 1;
+                    list.getSelectionModel().setSelectionInterval(lastLine, lastLine);
+                    list.scrollRectToVisible(list.getCellRect(lastLine, 0, true));
                 }
             }
         });
@@ -181,7 +181,7 @@ public class WMSPreferenceEditor implements PreferenceSetting {
         overlapPanel.add(labelNorth); 
         overlapPanel.add(spinNorth); 
          
-        p.add(overlapPanel);	
+        p.add(overlapPanel);    
     }
 
     public boolean ok() {

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



More information about the Pkg-grass-devel mailing list