Index: applications/editors/josm/plugins/cadastre-fr/CONTRIBUTION
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/CONTRIBUTION	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/CONTRIBUTION	(revision 13382)
@@ -0,0 +1,4 @@
+The French Cadastre plugin is designed and coded by Pieren <pieren3@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. 
Index: applications/editors/josm/plugins/cadastre-fr/LICENSE
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/LICENSE	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/LICENSE	(revision 13382)
@@ -0,0 +1,346 @@
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: applications/editors/josm/plugins/cadastre-fr/README
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/README	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/README	(revision 13382)
@@ -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
+
Index: applications/editors/josm/plugins/cadastre-fr/build.xml
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/build.xml	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/build.xml	(revision 13382)
@@ -0,0 +1,62 @@
+<project name="cadastre-fr" default="dist" basedir=".">
+
+  <!-- compilation properties -->
+  <property name="josm.build.dir"	value="../../core"/>
+  <property name="josm.home.dir"	value="${user.home}/.josm"/>
+  <property name="josm"			location="../../core/dist/josm-custom.jar" />
+  <property name="plugin.build.dir"	value="build"/>
+  <property name="plugin.dist.dir"	value="../../dist"/>
+  <property name="plugin.name"		value="${ant.project.name}"/>
+  <property name="plugin.jar"		value="../../dist/${plugin.name}.jar"/>
+
+  <property name="ant.build.javac.target" value="1.5"/>
+  
+  <target name="init">
+    <mkdir dir="${plugin.build.dir}"/>
+  </target>
+
+  <target name="compile" depends="init">
+    <echo message="creating ${plugin.jar}"/>
+    <javac srcdir="src" classpath="${josm}" destdir="build" debug="true">
+      <include name="**/*.java" />
+    </javac>
+  </target>
+
+  <target name="dist" depends="compile">
+    <copy todir="${plugin.build.dir}/images">
+      <fileset dir="images"/>
+    </copy>
+    <exec append="false" output="REVISION" executable="svn" failifexecutionfails="false">
+      <env key="LANG" value="C"/>
+      <arg value="info"/>
+      <arg value="--xml"/>
+      <arg value="."/>
+    </exec>
+    <xmlproperty file="REVISION" prefix="version" keepRoot="false" collapseAttributes="true"/>
+    <delete file="REVISION"/>
+    <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
+      <manifest>
+		<attribute name="Plugin-Class" value="cadastre_fr.CadastrePlugin"/>
+		<attribute name="Plugin-Description" value="A special handler for the french land registry WMS server"/>
+		<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
+		<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
+		<attribute name="Plugin-Stage" value="60"/>
+		<attribute name="Author" value="Pieren &lt;pieren3@gmail.com>"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="clean">
+    <delete dir="${plugin.build.dir}" />
+    <delete file="${plugin.jar}" />
+  </target>
+  
+  <target name="install" depends="dist">
+    <copy file="${plugin.jar}" todir="${user.home}/.josm/plugins"/>
+  </target>
+
+  <target name="test" depends="install">
+    <java jar="${josm}" fork="true"/>
+  </target>
+
+</project>
Index: applications/editors/josm/plugins/cadastre-fr/src/META-INF/MANIFEST.MF
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/META-INF/MANIFEST.MF	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/META-INF/MANIFEST.MF	(revision 13382)
@@ -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@gmail.com>
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CacheControl.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CacheControl.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CacheControl.java	(revision 13382)
@@ -0,0 +1,230 @@
+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.JOptionPane;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+
+public class CacheControl implements Runnable {
+
+    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 {
+            File file = new File(CadastrePlugin.cacheDir + wmsLayer.name + "." + String.valueOf(wmsLayer.lambertZone+1));
+            if (file.exists()) {
+                int reply = JOptionPane.showConfirmDialog(null, 
+                        "Location \""+wmsLayer.name+"\" found in cache.\n"+
+                        "Load cache first ?\n"+
+                        "(No = new cache)",
+                        "Location in cache",
+                        JOptionPane.YES_NO_OPTION);
+                if (reply == JOptionPane.OK_OPTION) {
+                    return loadCache(file, wmsLayer.lambertZone);
+                } else
+                    file.delete();
+            }            
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+        return false;
+    }
+    
+    public void deleteCacheFile() {
+        try {
+            File file = new File(CadastrePlugin.cacheDir + wmsLayer.name + "." + String.valueOf(wmsLayer.lambertZone+1));
+            if (file.exists())
+                file.delete();
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+    }
+    
+    public boolean loadCache(File file, int currentLambertZone) {
+        try {
+            FileInputStream fis = new FileInputStream(file);
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            int sfv = ois.readInt();
+            if (sfv != wmsLayer.serializeFormatVersion) {
+                JOptionPane.showMessageDialog(Main.parent, tr("Unsupported WMS file version; found {0}, expected {1}",
+                        sfv, wmsLayer.serializeFormatVersion), tr("Cache Format Error"), JOptionPane.ERROR_MESSAGE);
+                return false;
+            }
+            wmsLayer.setLocation((String) ois.readObject());
+            wmsLayer.setCodeCommune((String) ois.readObject());
+            wmsLayer.lambertZone = ois.readInt();
+            wmsLayer.setRaster(ois.readBoolean());
+            wmsLayer.setRasterMin((EastNorth) ois.readObject());
+            wmsLayer.setRasterCenter((EastNorth) ois.readObject());
+            wmsLayer.setRasterRatio(ois.readDouble());
+            if (wmsLayer.lambertZone != currentLambertZone) {
+                JOptionPane.showMessageDialog(Main.parent, tr("Lambert zone {0} in cache "+
+                        " incompatible with current Lambert zone {1}",
+                        wmsLayer.lambertZone+1, currentLambertZone), tr("Cache Lambert Zone Error"), JOptionPane.ERROR_MESSAGE);
+                return false;
+            }
+            boolean EOF = false;
+            try {
+                while (!EOF) {
+                    GeorefImage newImage = (GeorefImage) ois.readObject();
+                    for (GeorefImage img : wmsLayer.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);
+                        }
+                    }
+                    wmsLayer.images.add(newImage);
+                }
+            } catch (EOFException e) {}
+            ois.close();
+            fis.close();
+        } catch (Exception ex) {
+            ex.printStackTrace(System.out);
+            JOptionPane
+                    .showMessageDialog(Main.parent, tr("Error loading file"), tr("Error"), JOptionPane.ERROR_MESSAGE);
+        }
+        return true;
+    }
+
+
+    public boolean existCache() {
+        try {
+            /*
+            File pathname = new File(CadastrePlugin.cacheDir);
+            String[] fileNames = pathname.list();
+            for (String fileName : fileNames) {
+                System.out.println("check file:"+fileName);
+                //WMSLayer cached = new WMSLayer(new File(cacheDir+fileName));
+            }*/
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+        return false;
+    }
+    
+    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()) {
+                File file = new File(CadastrePlugin.cacheDir + wmsLayer.name + "." + String.valueOf((wmsLayer.lambertZone + 1)));
+                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)));
+                        oos.writeInt(wmsLayer.serializeFormatVersion);
+                        oos.writeObject(wmsLayer.getLocation());
+                        oos.writeObject(wmsLayer.getCodeCommune());
+                        oos.writeInt(wmsLayer.lambertZone);
+                        oos.writeBoolean(wmsLayer.isRaster());
+                        oos.writeObject(wmsLayer.getRasterMin());
+                        oos.writeObject(wmsLayer.getRasterCenter());
+                        oos.writeDouble(wmsLayer.getRasterRatio());
+                        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);
+            }
+        }
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CacheFileFilter.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CacheFileFilter.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CacheFileFilter.java	(revision 13382)
@@ -0,0 +1,51 @@
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import java.io.File;
+import javax.swing.filechooser.FileFilter;
+
+public class CacheFileFilter extends FileFilter {
+
+    /**
+     * Derived from ExtensionFileFilter writen by imi
+     */
+    private final String extension;
+    private final String description;
+
+    public static CacheFileFilter[] filters = { 
+        new CacheFileFilter("1", tr("Lambert Zone 1 cache file (.1)")),
+        new CacheFileFilter("2", tr("Lambert Zone 2 cache file (.2)")),
+        new CacheFileFilter("3", tr("Lambert Zone 3 cache file (.3)")),
+        new CacheFileFilter("4", tr("Lambert Zone 4 cache file (.4)")) 
+        };
+
+    /**
+     * Construct an extension file filter by giving the extension to check after.
+     *
+     */
+    private CacheFileFilter(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;
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastreGrabber.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastreGrabber.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastreGrabber.java	(revision 13382)
@@ -0,0 +1,102 @@
+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.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.io.ProgressInputStream;
+
+public class CadastreGrabber {
+
+	public static final double epsilon = 1e-11;
+	
+	private CadastreInterface wmsInterface = new CadastreInterface(this);
+    private String lastWMSLayerName = null;
+	
+	CadastreGrabber() {
+        getWmsInterface().downloadCancelled = false;	    
+	}
+	
+    public GeorefImage grab(WMSLayer wmsLayer, EastNorth lambertMin, EastNorth lambertMax) throws IOException {
+
+        try {
+            URL url = null;
+            if (wmsLayer.isRaster())
+                url = getURLRaster(wmsLayer, lambertMin, lambertMax);
+            else
+                url = getURLVector(lambertMin, lambertMax);
+            System.out.println("grab:"+url);
+            BufferedImage img = grab(url);
+            ImageModifier imageModified = new ImageModifier(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 {
+        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=600&height=600"; // maximum allowed by wms server
+        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 += "&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 {
+	    wmsInterface.urlConn = (HttpURLConnection)url.openConnection();
+	    wmsInterface.urlConn.setRequestMethod("GET");
+	    wmsInterface.setCookie();
+		InputStream is = new ProgressInputStream(wmsInterface.urlConn, Main.pleaseWaitDlg);
+		BufferedImage img = ImageIO.read(is);
+        is.close();
+        return img;
+	}
+
+    public CadastreInterface getWmsInterface() {
+        return wmsInterface;
+    }
+
+    public String getLastWMSLayerName() {
+        return lastWMSLayerName;
+    }
+
+    public void setLastWMSLayerName(String lastWMSLayerName) {
+        this.lastWMSLayerName = lastWMSLayerName;
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastreInterface.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastreInterface.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastreInterface.java	(revision 13382)
@@ -0,0 +1,336 @@
+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.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+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 CadastreGrabber cadastreGrabber;
+    private String cookie;
+    private String interfaceRef = null;
+    private URL searchFormURL;
+    private Vector<String> listOfCommunes = new Vector<String>();
+    private Vector<String> listOfTA = new Vector<String>();
+
+    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 cInterfaceVector = "afficherCarteCommune.do";
+    final String cInterfaceRaster = "afficherCarteTa.do";
+        
+    CadastreInterface(CadastreGrabber cadastreGrabber) {
+        this.cadastreGrabber = cadastreGrabber;
+    }
+    
+    public boolean retrieveInterface(WMSLayer wmsLayer) throws DuplicateLayerException {
+        if (wmsLayer.name.equals(""))
+            return false;
+        // open the session with the french Cadastre web front end
+        downloadCancelled = false;
+        try {
+            if (cookie == null || !wmsLayer.name.equals(cadastreGrabber.getLastWMSLayerName())) {
+                getCookie();
+                getInterface(wmsLayer);
+                cadastreGrabber.setLastWMSLayerName(wmsLayer.name);
+            }
+            openInterface();
+        } catch (IOException e) {
+            e.printStackTrace();
+            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 (IOException) new IOException("Cannot get Cadastre cookie.");
+            }
+            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() {
+        cadastreGrabber.setLastWMSLayerName(null);
+    }
+    
+    public void resetCookieIfNewLayer(String newWMSLayerName) {
+        if (!newWMSLayerName.equals(cadastreGrabber.getLastWMSLayerName())) {
+            resetCookie();
+        }
+    }
+    
+    public void setCookie() {
+        urlConn.setRequestProperty("Cookie", 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 (wmsLayer.isRaster() && listOfTA.size() > 1) {
+                    // commune known but raster format. Select "tableau d'assemblage" from list.
+                    wmsLayer.setCodeCommune(selectTADialog());
+                    checkLayerDuplicates(wmsLayer);
+                    interfaceRef = buildRasterInterfaceRef(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
+            URL interfaceURL = new URL(baseURL + "/scpc/"+interfaceRef);
+            urlConn = (HttpURLConnection)interfaceURL.openConnection();
+            urlConn.setRequestMethod("GET");
+            setCookie();
+            urlConn.connect();
+            if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                throw (IOException) new IOException("Cannot open Cadastre interface. GET response:"+urlConn.getResponseCode());
+            }
+            BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+            while(in.readLine() != null) {}  // read the buffer otherwise we sent POST too early
+            System.out.println("GET to open interface sent");
+        } 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>
+     * 
+     * @param location
+     * @param codeCommune
+     * @return retURL url to available cadastre vectorised master piece; "" if not found
+     * @throws IOException
+     */
+    private String postForm(WMSLayer wmsLayer, String codeCommune) throws IOException {
+        try {
+            String ln = null;
+            String line = 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());
+            wr.flush();
+            wr.close();
+            BufferedReader rd = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+            while ((ln = rd.readLine()) != null) {
+                line += ln;
+            }
+            rd.close();
+            urlConn.disconnect();
+            System.out.println("POST="+line);
+            if (line.indexOf(cImageFormat) != -1) {
+                int i = line.indexOf(cImageFormat);
+                int j = line.indexOf(".", i);
+                wmsLayer.setRaster(line.substring(i+cImageFormat.length(), j).equals("image"));
+            }
+            if (!wmsLayer.isRaster() && line.indexOf(cInterfaceVector) != -1) {  // "afficherCarteCommune.do"
+                // shall be something like: interfaceRef = "afficherCarteCommune.do?c=X2269";
+                line = line.substring(line.indexOf(cInterfaceVector),line.length());
+                line = line.substring(0, line.indexOf("'"));
+                System.out.println("interface ref.:"+line);
+                return line;
+            } else if (wmsLayer.isRaster() && line.indexOf(cInterfaceRaster) != -1) { // "afficherCarteTa.do"
+                // list of values parsed in listOfTA (Tableau d'assemblage)
+                parseTAList(line.substring(line.indexOf(cInterfaceRaster)));
+                if (listOfTA.size() == 1) {
+                    wmsLayer.setCodeCommune(listOfTA.firstElement());
+                    return buildRasterInterfaceRef(listOfTA.firstElement());
+                }
+                return null;
+            } else if (line.indexOf(cCommuneListStart) != -1 && line.indexOf(cCommuneListEnd) != -1) {
+                // list of values parsed in listOfCommunes
+                int i = line.indexOf(cCommuneListStart);
+                int j = line.indexOf(cCommuneListEnd, i);
+                parseCommuneList(line.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 void parseTAList(String input) {
+        while (input.indexOf(cInterfaceRaster) != -1) {
+            input = input.substring(input.indexOf(cInterfaceRaster));
+            String codeTA = input.substring(0, input.indexOf("'"));
+            codeTA = codeTA.substring(codeTA.indexOf("=")+1);
+            if (!listOfTA.contains(codeTA)) {
+                System.out.println("parse "+codeTA);
+                listOfTA.add(codeTA);                
+            }
+            input = input.substring(cInterfaceRaster.length());
+        }
+    }
+
+    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);
+        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 String selectTADialog() {
+        JPanel p = new JPanel(new GridBagLayout());
+        JComboBox inputTAList = new JComboBox(listOfTA);
+        p.add(inputTAList, 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 Tableau d'Assemblage")).setVisible(true);
+        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+            return null;
+        String result = listOfTA.elementAt(inputTAList.getSelectedIndex());
+        return result;
+    }
+
+    private String buildRasterInterfaceRef(String codeCommune) {
+        return cInterfaceRaster + "?f=" + codeCommune;
+    }
+    
+    private void checkLayerDuplicates(WMSLayer wmsLayer) throws DuplicateLayerException {
+        if (Main.map != null) {
+            for (Layer l : Main.map.mapView.getAllLayers()) {
+                if (l instanceof WMSLayer && l.name.equals(wmsLayer.name) && (l != wmsLayer)) {
+                    System.out.println("Try to grab into a new layer when "+wmsLayer.name+" is already opened.");
+                    // remove the duplicated layer
+                    Main.map.mapView.removeLayer(wmsLayer);
+                    throw new DuplicateLayerException();
+                }
+            }
+        }
+    }
+    
+    public void cancel() {      
+        Main.pleaseWaitDlg.currentAction.setText(tr("Aborting..."));
+        if (urlConn != null) {
+            urlConn.setConnectTimeout(1);
+            urlConn.setReadTimeout(1);
+            //urlConn.disconnect();
+        }
+        downloadCancelled = true;
+        cadastreGrabber.setLastWMSLayerName(null);
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastrePlugin.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastrePlugin.java	(revision 13382)
@@ -0,0 +1,209 @@
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.LinkedList;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+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.actions.UploadAction.UploadHook;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.data.projection.Lambert;
+
+/**
+ * 
+ * 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@gmail.com>, 
+ * @author <matthieu.lochegnies@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 
+ */
+public class CadastrePlugin extends Plugin {
+    static String VERSION = "0.8";
+
+    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 String colorBackground;
+
+    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 \"french cadastre\" started...");
+        if (Main.proj.toString().equals(new Lambert().toString()) != true) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("To enable the plugin cadastrewms, change\nthe JOSM projection to Lambert and restart"));
+            return;
+        }
+        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();
+        refreshMenu();
+
+        // add a hook at uploading to insert/verify the source=cadastre tag
+        LinkedList<UploadHook> hooks = ((UploadAction) Main.main.menu.upload).uploadHooks;
+        hooks.add(0, new CheckSourceUploadHook());
+    }
+
+    public void refreshMenu() throws Exception {
+        JMenuBar menu = Main.main.menu;
+
+        if (cadastreJMenu == null) {
+            cadastreJMenu = new JMenu(tr("Cadastre"));
+            cadastreJMenu.setMnemonic(KeyEvent.VK_C);
+            menu.add(cadastreJMenu, menu.getMenuCount()-2);
+            
+            JosmAction grab = new MenuActionGrab();
+            JMenuItem menuGrab = new JMenuItem(grab);
+            KeyStroke ks = grab.getShortcut().getKeyStroke();
+            if (ks != null) {
+                menuGrab.setAccelerator(ks);
+            }
+            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());
+            
+            cadastreJMenu.add(menuGrab);
+            cadastreJMenu.add(menuSettings);
+            cadastreJMenu.add(menuSource);
+            cadastreJMenu.add(menuResetCookie);
+            cadastreJMenu.add(menuLambertZone);
+            cadastreJMenu.add(menuLoadFromCache);
+        }
+        setEnabledAll(menuEnabled);
+    }
+
+    public static void refreshConfiguration() {
+        source = Main.pref.get("cadastrewms.source",
+                "cadastre-dgi-fr source : Direction Générale des Impôts - Cadastre ; mise à jour : AAAA");
+        autoSourcing = Main.pref.getBoolean("cadastrewms.autosourcing", true);
+        alterColors = Main.pref.getBoolean("cadastrewms.alterColors");
+        drawBoundaries = Main.pref.getBoolean("cadastrewms.drawBoundaries", false);
+        colorBackground = Main.pref.get("color.background", "#FFFFFF");
+        if (alterColors) {
+            backgroundTransparent = Main.pref.getBoolean("cadastrewms.backgroundTransparent");
+            transparency = Float.parseFloat(Main.pref.get("cadastrewms.brightness", "1.0f"));
+        } else {
+            backgroundTransparent = false;
+            transparency = 1.0f;
+        }
+    }
+
+    @Override
+    public PreferenceSetting getPreferenceSetting() {
+        return new CadastrePreferenceSetting();
+    }
+
+    private void setEnabledAll(boolean isEnabled) {
+        for (int i = 0; i < cadastreJMenu.getItemCount(); i++) {
+            JMenuItem item = cadastreJMenu.getItem(i);
+            if (item != null)
+                if (item.getText().equals(MenuActionGrab.name))
+                    item.setEnabled(isEnabled);
+                else if (item.getText().equals(MenuActionLambertZone.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;
+            }
+        }
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java	(revision 13382)
@@ -0,0 +1,201 @@
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+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@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 JRadioButton grabMultiplier1 = new JRadioButton("", true);
+
+    private JRadioButton grabMultiplier2 = new JRadioButton("", true);
+
+    private JRadioButton grabMultiplier3 = new JRadioButton("", true);
+    
+    private JRadioButton grabMultiplier4 = new JRadioButton("", true);
+    
+    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);
+
+    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.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                reversGrey.setEnabled(alterColors.isSelected());
+                transparency.setEnabled(alterColors.isSelected());
+                sliderTrans.setEnabled(transparency.isSelected() && alterColors.isSelected());
+            }
+        });
+        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 texts from black to white (and all intermediate greys)."));
+        reversGrey.setEnabled(alterColors.isSelected());
+        cadastrewms.add(reversGrey, GBC.eop().insets(20, 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"));
+        transparency.setEnabled(alterColors.isSelected());
+        cadastrewms.add(transparency, GBC.eop().insets(20, 0, 0, 0));
+
+        // slider for transparency level
+        sliderTrans.setPreferredSize(new Dimension(20,200));
+        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);
+        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));
+
+        // the downloaded images multiplier
+        JLabel jLabelScale = new JLabel(tr("Image grab multiplier:"));
+        cadastrewms.add(jLabelScale, GBC.std().insets(0, 5, 10, 0));
+        ButtonGroup bg = new ButtonGroup();
+        grabMultiplier1.setIcon(ImageProvider.get("preferences", "unsel_box_1"));
+        grabMultiplier1.setSelectedIcon(ImageProvider.get("preferences", "sel_box_1"));
+        grabMultiplier2.setIcon(ImageProvider.get("preferences", "unsel_box_2"));
+        grabMultiplier2.setSelectedIcon(ImageProvider.get("preferences", "sel_box_2"));
+        grabMultiplier3.setIcon(ImageProvider.get("preferences", "unsel_box_3"));
+        grabMultiplier3.setSelectedIcon(ImageProvider.get("preferences", "sel_box_3"));
+        grabMultiplier4.setIcon(ImageProvider.get("preferences", "unsel_box_4"));
+        grabMultiplier4.setSelectedIcon(ImageProvider.get("preferences", "sel_box_4"));
+        String multiplierTooltip = "Grab smaller images (higher quality but use more memory)";
+        grabMultiplier3.setToolTipText(multiplierTooltip);
+        bg.add(grabMultiplier1);
+        bg.add(grabMultiplier2);
+        bg.add(grabMultiplier3);
+        bg.add(grabMultiplier4);
+        if (Main.pref.get("cadastrewms.scale", "1").equals(Scale.X1))
+            grabMultiplier1.setSelected(true);
+        if (Main.pref.get("cadastrewms.scale", "1").equals(Scale.X2))
+            grabMultiplier2.setSelected(true);
+        if (Main.pref.get("cadastrewms.scale", "1").equals(Scale.X3))
+            grabMultiplier3.setSelected(true);
+        if (Main.pref.get("cadastrewms.scale", "1").equals(Scale.SQUARE_100M))
+            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.eol().fill(GBC.HORIZONTAL).insets(5, 0, 5, 0));
+        
+        // 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;
+        try {
+            size = Integer.parseInt(Main.pref.get("cadastrewms.cacheSize", String.valueOf(DEFAULT_CACHE_SIZE)));
+        } catch (NumberFormatException e) {
+            size = 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, 0, 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());
+        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();
+
+        return false;
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java	(revision 13382)
@@ -0,0 +1,93 @@
+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.UploadAction.UploadHook;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+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(Collection<OsmPrimitive> add, Collection<OsmPrimitive> update, Collection<OsmPrimitive> delete)
+    {
+        if (CadastrePlugin.autoSourcing && CadastrePlugin.pluginUsed && !add.isEmpty()) {
+            Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
+            for (OsmPrimitive osm : add) {
+                if ((osm instanceof Node || osm instanceof Way) 
+                        && (osm.keys == null || !tagSourceExist(osm))) {
+                    sel.add(osm);
+                }
+            }
+            if (!sel.isEmpty()) {
+                return 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.keys.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"
+     * @return true if it is accepted by user 
+     */
+    private boolean displaySource(Collection<OsmPrimitive> sel) 
+    {
+        boolean bContinue = true;
+        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());
+            bContinue = JOptionPane.showConfirmDialog(Main.parent, p, tr("Add \"source=Cadastre...\" to ?"),
+                    JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
+            if (bContinue)
+                Main.main.undoRedo.add(new ChangePropertyCommand(sel, "source", CadastrePlugin.source));
+        }
+        return bContinue;
+                
+    }    
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/DownloadWMSTask.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/DownloadWMSTask.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/DownloadWMSTask.java	(revision 13382)
@@ -0,0 +1,66 @@
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.data.Bounds;
+
+public class DownloadWMSTask extends PleaseWaitRunnable {
+
+    private WMSLayer wmsLayer;
+
+    private Bounds bounds;
+
+    private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+
+    public DownloadWMSTask(WMSLayer wmsLayer, Bounds bounds) {
+        super(tr("Downloading {0}", wmsLayer.name));
+
+        this.wmsLayer = wmsLayer;
+        this.bounds = bounds;
+    }
+
+    @Override
+    public void realRun() throws IOException {
+        Main.pleaseWaitDlg.currentAction.setText(tr("Contacting WMS Server..."));
+        try {
+            if (grabber.getWmsInterface().retrieveInterface(wmsLayer)) {
+                if (wmsLayer.isRaster() && wmsLayer.images.isEmpty())
+                    wmsLayer.setRasterBounds(bounds);
+                if (CacheControl.cacheEnabled && wmsLayer.images.isEmpty()) {
+                    // images loaded from cache
+                    if (wmsLayer.getCacheControl().loadCacheIfExist()) {
+                        Main.map.mapView.repaint();
+                        return;
+                    }
+                }
+                // grab new images from wms server into active layer
+                wmsLayer.grab(grabber, bounds);
+            }
+        } 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() {
+        //wmsLayer.saveToCache();
+    }
+
+    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 DownloadWMSTask(wmsLayer, bounds));
+
+    }
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java	(revision 13382)
@@ -0,0 +1,4 @@
+package cadastre_fr;
+
+class DuplicateLayerException extends Exception {
+    private static final long serialVersionUID = 1L;}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/GeorefImage.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/GeorefImage.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/GeorefImage.java	(revision 13382)
@@ -0,0 +1,218 @@
+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;
+import org.openstreetmap.josm.tools.ColorHelper;
+
+public class GeorefImage implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public EastNorth min, max;
+
+    public EastNorth org_min, org_max;
+
+    public BufferedImage image;
+    
+    private double angle = 0; // in radian
+    
+    private BufferedImage rotated_image; // only if angle <> 0
+
+    double pixelPerEast;
+    double pixelPerNorth;
+
+    public GeorefImage(BufferedImage img, EastNorth min, EastNorth max) {
+        image = img;
+        this.min = min;
+        this.max = max;
+        updatePixelPer();
+    }
+
+    public void displace(double dx, double dy) {
+        min = new EastNorth(min.east() + dx, min.north() + dy);
+        max = new EastNorth(max.east() + dx, max.north() + dy);
+    }
+    
+    public void resize(EastNorth rasterCenter, double proportion) {
+        min = min.interpolate(rasterCenter, proportion);
+        max = max.interpolate(rasterCenter, proportion);
+        updatePixelPer();
+    }
+    
+    public void rotate(EastNorth pivot, double delta) {
+        if (angle == 0) {
+            org_min = min; 
+            org_max = max;
+        }
+        this.angle += delta;
+        
+        EastNorth imageCenter = org_min.interpolate(org_max, 0.5);
+        EastNorth newimageCenter = imageCenter.rotate(pivot, angle);
+        min.setLocation(org_min.east() + newimageCenter.east()-imageCenter.east(),
+                org_min.north() + newimageCenter.north()-imageCenter.north());
+        max.setLocation(org_max.east() + newimageCenter.east()-imageCenter.east(),
+                org_max.north() + newimageCenter.north()-imageCenter.north());
+        EastNorth min2 = new EastNorth(min.east(), max.north());
+        EastNorth max2 = new EastNorth(max.east(), min.north());
+        min = org_min.rotate(newimageCenter, angle);
+        max = org_max.rotate(newimageCenter, angle);
+        min2 = min2.rotate(newimageCenter, angle);
+        max2 = max2.rotate(newimageCenter, angle);
+        getNewBounding(min, max, min2, max2);
+        
+        rotated_image = tilt(image, angle);
+    }
+    
+    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;
+    }
+    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;
+
+        BufferedImage toDisplay;
+        if (angle != 0)
+            toDisplay = rotated_image;
+        else
+            toDisplay = image;
+
+        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(toDisplay, minPt.x, maxPt.y, maxPt.x, minPt.y, // dest
+                0, 0, toDisplay.getWidth(), toDisplay.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();
+            int josmBackgroundColor = ColorHelper.html2color(CadastrePlugin.colorBackground).getRGB();
+            for (int x = minXMaskPixel; x < minXMaskPixel + widthXMaskPixel; x++)
+                for (int y = minYMaskPixel; y < minYMaskPixel + heightYMaskPixel; y++)
+                    if (CadastrePlugin.alterColors)
+                        image.setRGB(x, y, josmBackgroundColor);
+                    else
+                        image.setRGB(x, y, ImageModifier.cadastreBackground);
+            g.dispose();
+        }
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        max = (EastNorth) in.readObject();
+        min = (EastNorth) in.readObject();
+        image = (BufferedImage) ImageIO.read(ImageIO.createImageInputStream(in));
+        updatePixelPer();
+    }
+
+
+    private void updatePixelPer() {
+        pixelPerEast = image.getWidth()/(max.east()-min.east());
+        pixelPerNorth = image.getHeight()/(max.north()-min.north());
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.writeObject(max);
+        out.writeObject(min);
+        ImageIO.write(image, "png", ImageIO.createImageOutputStream(out));
+    }
+
+    @Override
+    public String toString() {
+        return "GeorefImage[min=" + min + ", max=" + max + ", image" + image + "]";
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/GeorefImageRaster.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/GeorefImageRaster.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/GeorefImageRaster.java	(revision 13382)
@@ -0,0 +1,15 @@
+package cadastre_fr;
+
+import java.awt.image.BufferedImage;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+
+public class GeorefImageRaster extends GeorefImage {
+
+    private static final long serialVersionUID = 1L;
+
+    public GeorefImageRaster(BufferedImage img, EastNorth min, EastNorth max) {
+        super(img, min, max);
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/ImageModifier.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/ImageModifier.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/ImageModifier.java	(revision 13382)
@@ -0,0 +1,126 @@
+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 ImageModifier {
+
+    /**
+     * Current background color used by cadastre.gouv.fr
+     */
+    private static final long serialVersionUID = 1L;
+
+    public static final int cadastreBackground = -1; // white
+
+    public BufferedImage bufferedImage;
+
+    private boolean withBackground = false;
+
+    private int backgroundPixel = 0;
+    
+    private int backgroundSampleX, backgroundSampleY;
+
+    public ImageModifier(BufferedImage bi) {
+        bufferedImage = bi;
+        if (Main.pref.getBoolean("cadastrewms.alterColors")) {
+            changeColors();
+            if (Main.pref.getBoolean("cadastrewms.backgroundTransparent")) {
+                makeTransparent();
+            }
+        }
+    }
+
+    /**
+     * Replace the background color by the josm color.background color.
+     * @param bi
+     * @return
+     */
+    private void changeColors() {
+        int w = bufferedImage.getWidth();
+        int h = bufferedImage.getHeight();
+        int pixel;
+        int josmBackgroundColor = ColorHelper.html2color(Main.pref.get("color.background", "#FFFFFF")).getRGB();
+        boolean invertGrey = (Main.pref.getBoolean("cadastrewms.invertGrey"));
+        for (int x = 0; x < w; x++) {
+            for (int y = 0; y < h; y++) {
+                pixel = bufferedImage.getRGB(x, y);
+                if (pixel == cadastreBackground) {
+                    bufferedImage.setRGB(x, y, josmBackgroundColor);
+                    if (!withBackground)
+                        withBackground = true;
+                    backgroundSampleX = x;
+                    backgroundSampleY = y;
+                } else if (invertGrey) {
+                    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);
+        } else {
+            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++) {
+                    Color c = new Color(bufferedImage.getRGB(x, y));
+                    int r = c.getRed();
+                    int g = c.getGreen();
+                    int b = c.getBlue();
+                    Color maskedColor;
+                    if (r==0 && g==0 && b==0) {
+                        maskedColor = new Color(r, g, b, 0x00);
+                    } else {
+                        maskedColor = new Color(r, g, b, 0xFF);
+                    }
+                    bi.setRGB(x, y, maskedColor.getRGB());
+                }
+            }
+            bufferedImage = bi;
+        }
+        return;
+    }
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionGrab.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionGrab.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionGrab.java	(revision 13382)
@@ -0,0 +1,35 @@
+package cadastre_fr;
+
+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.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) {
+            WMSLayer wmsLayer = WMSDownloadAction.getLayer();
+            if (wmsLayer != null)
+                DownloadWMSTask.download(wmsLayer);
+        }
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionLambertZone.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionLambertZone.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionLambertZone.java	(revision 13382)
@@ -0,0 +1,51 @@
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.projection.Lambert;
+import org.openstreetmap.josm.tools.GBC;
+
+public class MenuActionLambertZone extends JosmAction {
+
+    private static final long serialVersionUID = 1L;
+
+    public static String name = "Change Lambert zone";
+
+    public MenuActionLambertZone() {
+        super(tr(name), "cadastre_small", tr("Set manually the Lambert zone (e.g. for locations between two zones)"),
+                null, false);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        JPanel p = new JPanel(new GridBagLayout());
+        String[] zones = { "", "1 (51.30° to 48.15°)", "2 (48.15° to 45.45°)", "3 (45.45° to 42.76°)", "4 (Corsica)" };
+        final JComboBox inputLambertZone = new JComboBox(zones);
+        JLabel newLambertZone = new JLabel(tr("Zone"));
+        p.add(newLambertZone, GBC.std());
+        p.add(inputLambertZone, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 0));
+        JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null) {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public void selectInitialValue() {
+                inputLambertZone.setSelectedIndex(0);
+            }
+        };
+        pane.createDialog(Main.parent, tr("Lambert zone")).setVisible(true);
+        if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+            return;
+        if (inputLambertZone.getSelectedIndex() > 0) {
+            Lambert.layoutZone = inputLambertZone.getSelectedIndex() - 1;
+        }
+    }
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java	(revision 13382)
@@ -0,0 +1,82 @@
+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 org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.projection.Lambert;
+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();
+        nextFile:
+        for (File file : files) {
+            String filename = file.getName();
+            String ext = (filename.lastIndexOf(".")==-1)?"":filename.substring(filename.lastIndexOf(".")+1,filename.length());
+            String location = filename.substring(0, filename.lastIndexOf("."));
+            // check the extension and its Lambert zone consistency 
+            try {
+                int cacheZone = Integer.parseInt(ext) - 1;
+                if (cacheZone >=0 && cacheZone <= 3) {
+                    if (Lambert.layoutZone == -1) {
+                        Lambert.layoutZone = cacheZone;
+                        System.out.println("Load cache \"" + filename + "\" in Lambert Zone " + (Lambert.layoutZone+1));
+                    } else if (cacheZone != Lambert.layoutZone) {
+                        System.out.println("Cannot load cache \"" + filename + "\" which is not in current Lambert Zone "
+                                + Lambert.layoutZone);
+                        continue nextFile;
+                    } else
+                        System.out.println("Load cache " + filename);
+                }
+            } catch (NumberFormatException ex) {
+                System.out.println("Selected file \"" + filename + "\" is not a WMS cache file (invalid extension)");
+                continue;
+            }
+            // 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.name.equals(location)) {
+                        System.out.println("The location " + filename + " is already on screen. Cache not loaded.");
+                        continue nextFile;
+                    }
+                }
+            }
+            // create layer and load cache
+            WMSLayer wmsLayer = new WMSLayer("", "", Integer.parseInt(ext)-1);
+            wmsLayer.getCacheControl().loadCache(file, Lambert.layoutZone);
+            Main.main.addLayer(wmsLayer);
+        }
+        
+    }
+
+    protected static JFileChooser createAndOpenFileChooser() {
+        JFileChooser fc = new JFileChooser(new File(CadastrePlugin.cacheDir));
+        fc.setMultiSelectionEnabled(true);
+        if (Lambert.layoutZone != -1)
+            fc.addChoosableFileFilter(CacheFileFilter.filters[Lambert.layoutZone]);
+        fc.setAcceptAllFileFilterUsed(false);
+
+        int answer = fc.showOpenDialog(Main.parent);
+        if (answer != JFileChooser.APPROVE_OPTION)
+            return null;
+
+        return fc;
+    }
+
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java	(revision 13382)
@@ -0,0 +1,100 @@
+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.JComboBox;
+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.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)
+            DownloadWMSTask.download(wmsLayer);
+    }
+	
+	public WMSLayer addNewLayer(ArrayList<WMSLayer> existingLayers) {
+        if (Main.map == null) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("Open a layer first (GPX, OSM, cache)"));
+            return null;
+            /*
+        } else if (existingLayers != null && existingLayers.size() > 0) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("Select one cadastre layer first"));
+            return null;*/
+        } else {
+            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>"));
+            JComboBox inputWMSList = null;
+
+            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);
+                for (Layer l : Main.map.mapView.getAllLayers()) {
+                    if (l instanceof WMSLayer && l.name.equalsIgnoreCase(location + codeDepartement)) {
+                        return null;
+                    }
+                }
+                // add the layer if it doesn't exist
+                wmsLayer = new WMSLayer(location, codeCommune, Lambert.layoutZone);
+                Main.main.addLayer(wmsLayer);
+                System.out.println("Add new layer with Location:" + inputTown.getText());
+            } else if (existingLayers != null && existingLayers.size() > 0 && inputWMSList.getSelectedIndex() > 0) {
+                wmsLayer = existingLayers.get(inputWMSList.getSelectedIndex()-1);            
+                resetCookie = true;
+            }
+
+            if (resetCookie)
+                CadastrePlugin.cadastreGrabber.getWmsInterface().resetCookieIfNewLayer(wmsLayer.name);
+            return wmsLayer;
+        }
+    }
+	
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java	(revision 13382)
@@ -0,0 +1,23 @@
+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();
+    }
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/Scale.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/Scale.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/Scale.java	(revision 13382)
@@ -0,0 +1,28 @@
+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;
+    }
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java	(revision 13382)
@@ -0,0 +1,155 @@
+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;
+    GeorefImage selectedImage;
+    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"), mapFrame, 
+						ImageProvider.getCursor("normal", "move"));
+	}
+
+	@Override public void enterMode() {
+		super.enterMode();
+		Main.map.mapView.addMouseListener(this);
+		Main.map.mapView.addMouseMotionListener(this);
+		rasterMoved = false;
+		/*/ FOR TEST
+        for (Layer layer : Main.map.mapView.getAllLayers()) {
+            if (layer.visible && layer instanceof WMSLayer) {
+                WMSLayer wmsLayer = (WMSLayer)layer;
+                wmsLayer.rotate(Math.PI/4);
+            }
+        }
+        Main.map.mapView.repaint();*/
+	}
+
+	@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();
+	}
+
+	@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;
+        for (Layer layer : Main.map.mapView.getAllLayers()) {
+            if (layer.visible && layer instanceof WMSLayer) {
+                prevEastNorth = Main.map.mapView.getEastNorth(e.getX(), e.getY());
+                selectedLayer = ((WMSLayer) layer);
+                selectedImage = selectedLayer.findImage(prevEastNorth);
+                if (selectedImage != null) {
+                    Main.map.mapView.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+                }
+            }
+        }
+    }
+
+	@Override public void mouseDragged(MouseEvent e) {
+		if(selectedImage != null && (mode == Mode.moveXY || mode == Mode.moveZ || mode == Mode.rotate)) {
+            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);
+	        }
+            rasterMoved = true;
+            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) {
+        double dPrev = prevEastNorth.distance(selectedLayer.getRasterCenter().east(), selectedLayer.getRasterCenter().north());
+        double dNew = newEastNorth.distance(selectedLayer.getRasterCenter().east(), selectedLayer.getRasterCenter().north());
+        selectedLayer.resize(1 - 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(rotationAngle);              	    
+	}
+
+	@Override public void mouseReleased(MouseEvent e) {
+		//Main.map.mapView.repaint();
+		Main.map.mapView.setCursor(Cursor.getDefaultCursor());
+		selectedImage = null;	
+		prevEastNorth = null;
+		selectedLayer = 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();
+        }
+	}
+}
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java	(revision 13382)
@@ -0,0 +1,45 @@
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+
+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;
+
+    //private String layerName;
+	
+	public WMSDownloadAction(String layerName) {
+		super(layerName, "wmsmenu", tr("Download WMS tile from {0}",layerName), null, false);
+	}
+	
+	public void actionPerformed(ActionEvent e) {		
+		DownloadWMSTask.download(getLayer());
+	}
+
+	public static WMSLayer getLayer() {
+		// check if we already have a layer created. if not, create; if yes, reuse.
+        if (Main.map != null) {
+            Layer activeLayer = Main.map.mapView.getActiveLayer();
+            if (activeLayer instanceof WMSLayer)
+                return (WMSLayer) activeLayer;
+            ArrayList<WMSLayer> existingWMSlayers = new ArrayList<WMSLayer>();
+            for (Layer l : Main.map.mapView.getAllLayers()) {
+                if (l instanceof WMSLayer) {
+                    existingWMSlayers.add((WMSLayer)l);
+                }
+            }
+            if (existingWMSlayers.size() == 1)
+                return existingWMSlayers.get(0);
+            return new MenuActionNewLocation().addNewLayer(existingWMSlayers);
+        }
+        return null;
+	}
+};
+
Index: applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSLayer.java
===================================================================
--- applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSLayer.java	(revision 13382)
+++ applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSLayer.java	(revision 13382)
@@ -0,0 +1,511 @@
+package cadastre_fr;
+
+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.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+
+import javax.swing.AbstractAction;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JFileChooser;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JSeparator;
+import javax.swing.filechooser.FileFilter;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.data.projection.Lambert;
+import org.openstreetmap.josm.data.Bounds;
+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.tools.ImageProvider;
+import org.openstreetmap.josm.data.coor.EastNorth;
+
+/**
+ * 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 {
+
+    public class EastNorthBound {
+        public EastNorth min, max;
+        public EastNorthBound(EastNorth min, EastNorth max) {
+            this.min = min;
+            this.max = max;
+        }
+        @Override public String toString() {
+            return "EastNorthBound[" + min.east() + "," + min.north() + "," + max.east() + "," + max.north() + "]";
+        }
+    }
+    
+    Component[] component = null;  
+
+    public int lambertZone = -1;
+
+    protected static final Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(
+            CadastrePlugin.class.getResource("/images/cadastre_small.png")));
+
+    protected ArrayList<GeorefImage> images = new ArrayList<GeorefImage>();
+    
+    protected final int serializeFormatVersion = 1;
+    
+    private ArrayList<EastNorthBound> dividedBbox = new ArrayList<EastNorthBound>();
+    
+    private CacheControl cacheControl = null;
+    
+    private String location = "";
+
+    private String codeCommune = "";
+    
+    private boolean isRaster = false;
+    
+    private EastNorth rasterMin;
+    
+    private EastNorth rasterCenter;
+    
+    private double rasterRatio;
+
+    double cRasterMaxSizeX = 12286;
+    double cRasterMaxSizeY = 8730;
+
+    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 = Lambert.layoutZone;
+        // 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 {
+        divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.scale", Scale.X1.toString())));
+
+        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;
+            }
+            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();
+            /*
+            try { if (dividedBbox.size() > 1) Thread.sleep(1000);
+            } catch (InterruptedException e) {};*/
+        }
+    }
+
+    /**
+     * 
+     * @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.min);
+        EastNorth lambertMax = Main.proj.latlon2eastNorth(b.max);
+        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) {
+            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 = 100; // expressed in meters in projection Lambert
+            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() {
+        return tr("WMS layer ({0}), {1} tile(s) loaded", name, images.size());
+    }
+
+    @Override
+    public boolean isMergable(Layer other) {
+        return false;
+    }
+
+    @Override
+    public void mergeFrom(Layer from) {
+    }
+
+    @Override
+    public void paint(Graphics g, final MapView mv) {
+        for (GeorefImage img : images)
+            img.paint((Graphics2D) g, mv, CadastrePlugin.backgroundTransparent, 
+                    CadastrePlugin.transparency, CadastrePlugin.drawBoundaries);
+    }
+
+    @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() {
+        /*
+        return new Component[] { new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), new JMenuItem(new LoadWmsAction()),
+                new JMenuItem(new SaveWmsAction()), new JSeparator(),
+                new JMenuItem(new LayerListPopup.InfoAction(this)) };
+                */
+        component = new Component[] { new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), new JMenuItem(new LoadWmsAction()),
+                new JMenuItem(new SaveWmsAction()), new JSeparator(),
+                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.min),
+            Main.proj.latlon2eastNorth(bounds.max));
+        for (GeorefImage img : images) {
+            if (img.overlap(georefImage))
+                return true;
+        }
+        return false;
+    }
+
+    public void saveToCache(GeorefImage image) {
+        if (CacheControl.cacheEnabled) {
+            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;
+    }
+    
+    public class SaveWmsAction extends AbstractAction {
+        private static final long serialVersionUID = 1L;
+
+        public SaveWmsAction() {
+            super(tr("Save WMS layer to file"), ImageProvider.get("save"));
+        }
+
+        public void actionPerformed(ActionEvent ev) {
+            File f = openFileDialog(false);
+            try {
+                FileOutputStream fos = new FileOutputStream(f);
+                ObjectOutputStream oos = new ObjectOutputStream(fos);
+                oos.writeInt(serializeFormatVersion);
+                oos.writeInt(lambertZone);
+                oos.writeInt(images.size());
+                for (GeorefImage img : images) {
+                    oos.writeObject(img);
+                }
+                oos.close();
+                fos.close();
+            } catch (Exception ex) {
+                ex.printStackTrace(System.out);
+            }
+        }
+    }
+
+    public class LoadWmsAction extends AbstractAction {
+        private static final long serialVersionUID = 1L;
+
+        public LoadWmsAction() {
+            super(tr("Load WMS layer from file"), ImageProvider.get("load"));
+        }
+
+        public void actionPerformed(ActionEvent ev) {
+            File f = openFileDialog(true);
+            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;
+                }
+                lambertZone = ois.readInt();
+                int numImg = ois.readInt();
+                for (int i = 0; i < numImg; i++) {
+                    GeorefImage img = (GeorefImage) ois.readObject();
+                    images.add(img);
+                }
+                ois.close();
+                fis.close();
+            } 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 JFileChooser createAndOpenFileChooser(boolean open, boolean multiple) {
+        String curDir = Main.pref.get("lastDirectory");
+        if (curDir.equals(""))
+            curDir = ".";
+        JFileChooser fc = new JFileChooser(new File(curDir));
+        fc.setMultiSelectionEnabled(multiple);
+        for (int i = 0; i < ExtensionFileFilter.filters.length; ++i)
+            fc.addChoosableFileFilter(ExtensionFileFilter.filters[i]);
+        fc.setAcceptAllFileFilterUsed(true);
+
+        int answer = open ? fc.showOpenDialog(Main.parent) : fc.showSaveDialog(Main.parent);
+        if (answer != JFileChooser.APPROVE_OPTION)
+            return null;
+
+        if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir))
+            Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
+
+        if (!open) {
+            File file = fc.getSelectedFile();
+            if (file == null
+                    || (file.exists() && JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(Main.parent,
+                            tr("File exists. Overwrite?"), tr("Overwrite"), JOptionPane.YES_NO_OPTION)))
+                return null;
+        }
+
+        return fc;
+    }
+
+    public static File openFileDialog(boolean open) {
+        JFileChooser fc = createAndOpenFileChooser(open, false);
+        if (fc == null)
+            return null;
+
+        File file = fc.getSelectedFile();
+
+        String fn = file.getPath();
+        if (fn.indexOf('.') == -1) {
+            FileFilter ff = fc.getFileFilter();
+            if (ff instanceof ExtensionFileFilter)
+                fn = "." + ((ExtensionFileFilter) ff).defaultExtension;
+            else
+                fn += ".osm";
+            file = new File(fn);
+        }
+        return file;
+    }
+
+    /**
+     * 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;
+        this.name = rebuildName();
+        repaintLayerListDialog();
+    }
+
+    public String getCodeCommune() {
+        return codeCommune;
+    }
+
+    public void setCodeCommune(String codeCommune) {
+        this.codeCommune = codeCommune;
+        this.name = rebuildName();
+        repaintLayerListDialog();
+    }
+
+    public boolean isRaster() {
+        return isRaster;
+    }
+
+    public void setRaster(boolean isRaster) {
+        this.isRaster = isRaster;
+    }
+
+    /**
+     * Set the eastNorth position in rasterMin which is the 0,0 coordinate (bottom left corner).
+     * The bounds width is the raster width and height is calculate on a fixed image ratio. 
+     * @param bounds
+     */
+    public void setRasterBounds(Bounds bounds) {
+        rasterMin = new EastNorth(Main.proj.latlon2eastNorth(bounds.min).east(), Main.proj.latlon2eastNorth(bounds.min).north());
+        EastNorth rasterMax = new EastNorth(Main.proj.latlon2eastNorth(bounds.max).east(), Main.proj.latlon2eastNorth(bounds.max).north());
+        // now, resize on same proportion as wms server raster images (bounds center)
+        double rasterHalfHeight = (rasterMax.east() - rasterMin.east())/cRasterMaxSizeX*cRasterMaxSizeY/2;
+        double rasterMid = rasterMin.north() + (rasterMax.north()-rasterMin.north())/2;
+        rasterMin.setLocation(rasterMin.east(), rasterMid - rasterHalfHeight);
+        rasterMax.setLocation(rasterMax.east(), rasterMid + rasterHalfHeight);
+        rasterCenter = new EastNorth(rasterMin.east()+(rasterMax.east()-rasterMin.east())/2,
+                rasterMin.north()+(rasterMax.north()-rasterMin.north())/2);
+        rasterRatio = (rasterMax.east() - rasterMin.east()) / cRasterMaxSizeX;
+    }
+
+    public EastNorth getRasterMin() {
+        return rasterMin;
+    }
+
+    public void setRasterMin(EastNorth rasterMin) {
+        this.rasterMin = rasterMin;
+    }
+
+    public void displace(double dx, double dy) {
+        this.rasterMin = new EastNorth(rasterMin.east() + dx, rasterMin.north() + dy);
+        this.rasterCenter = new EastNorth(rasterCenter.east() + dx, rasterCenter.north() + dy);
+        for (GeorefImage img : images)
+            img.displace(dx, dy);
+    }
+
+    public void resize(double proportion) {
+        this.rasterMin = rasterMin.interpolate(rasterCenter, proportion);
+        for (GeorefImage img : images)
+            img.resize(rasterCenter, proportion);
+    }
+    
+    public void rotate(double angle) {
+        this.rasterMin = rasterMin.rotate(rasterCenter, angle);
+        for (GeorefImage img : images)
+            img.rotate(rasterCenter, angle);
+    }
+    
+    /**
+     * Repaint the LayerList dialog.
+     * This is the only way I found to refresh the layer name in the layer list when it changes
+     * later (after the construction).
+     */
+    private void repaintLayerListDialog() {
+        if (Main.map != null) {
+            for (Component c : Main.map.toggleDialogs.getComponents()) {
+                if (c instanceof LayerListDialog) {
+                    c.repaint();
+                }
+            }
+        }
+    }
+
+    public double getRasterRatio() {
+        return rasterRatio;
+    }
+
+    public void setRasterRatio(double rasterRatio) {
+        this.rasterRatio = rasterRatio;
+    }
+
+    public EastNorth getRasterCenter() {
+        return rasterCenter;
+    }
+
+    public void setRasterCenter(EastNorth rasterCenter) {
+        this.rasterCenter = rasterCenter;
+    }
+
+}
