[josm-plugins] 96/369: Imported Upstream version 0.0.svn18885
Bas Couwenberg
sebastic at xs4all.nl
Sat Oct 18 12:03:30 UTC 2014
This is an automated email from the git hooks/post-receive script.
sebastic-guest pushed a commit to branch master
in repository josm-plugins.
commit 51e1ab63d6a41addff13e7b960ed7714c326991d
Author: Giovanni Mascellani <mascellani at poisson.phc.unipi.it>
Date: Sat Dec 5 01:01:20 2009 +0100
Imported Upstream version 0.0.svn18885
---
{agpifoj => DirectUpload}/.classpath | 0
DirectUpload/.project | 17 +
DirectUpload/LICENSE | 341 +++++++
DirectUpload/README | 4 +
{slippymap => DirectUpload}/build.xml | 12 +-
DirectUpload/images/UploadAction.png | Bin 0 -> 105039 bytes
DirectUpload/nbproject/build-impl.xml | 629 +++++++++++++
DirectUpload/nbproject/genfiles.properties | 8 +
DirectUpload/nbproject/private/private.properties | 2 +
DirectUpload/nbproject/private/private.xml | 4 +
DirectUpload/nbproject/project.properties | 59 ++
DirectUpload/nbproject/project.xml | 16 +
.../josm/plugins/DirectUpload/UploadDataGui.form | 164 ++++
.../josm/plugins/DirectUpload/UploadDataGui.java | 517 +++++++++++
.../plugins/DirectUpload/UploadDataGuiPlugin.java | 43 +
agpifoj/.classpath | 3 +-
agpifoj/build.xml | 2 +-
.../josm/plugins/agpifoj/AgpifojLayer.java | 54 +-
.../plugins/agpifoj/CorrelateGpxWithImages.java | 20 +-
{measurement => cadastre-fr}/.classpath | 2 +-
cadastre-fr/.project | 17 +
cadastre-fr/CONTRIBUTION | 4 +
cadastre-fr/LICENSE | 341 +++++++
cadastre-fr/README | 26 +
{livegps => cadastre-fr}/build.xml | 14 +-
cadastre-fr/dist-lastest/readme.txt | 4 +
cadastre-fr/images/cadastre_small.png | Bin 0 -> 222 bytes
cadastre-fr/images/load.png | Bin 0 -> 883 bytes
cadastre-fr/images/mapmode/adjustxywms.png | Bin 0 -> 1300 bytes
cadastre-fr/images/mapmode/adjustzwms.png | Bin 0 -> 1468 bytes
cadastre-fr/images/preferences/cadastrewms.gif | Bin 0 -> 1068 bytes
cadastre-fr/images/preferences/sel_box_1.png | Bin 0 -> 435 bytes
cadastre-fr/images/preferences/sel_box_2.png | Bin 0 -> 426 bytes
cadastre-fr/images/preferences/sel_box_3.png | Bin 0 -> 452 bytes
cadastre-fr/images/preferences/sel_box_4.png | Bin 0 -> 507 bytes
cadastre-fr/images/preferences/unsel_box_1.png | Bin 0 -> 381 bytes
cadastre-fr/images/preferences/unsel_box_2.png | Bin 0 -> 371 bytes
cadastre-fr/images/preferences/unsel_box_3.png | Bin 0 -> 401 bytes
cadastre-fr/images/preferences/unsel_box_4.png | Bin 0 -> 451 bytes
cadastre-fr/images/save.png | Bin 0 -> 845 bytes
cadastre-fr/src/META-INF/MANIFEST.MF | 6 +
cadastre-fr/src/cadastre_fr/CacheControl.java | 210 +++++
.../cadastre_fr/CacheFileLambert4ZoneFilter.java | 52 ++
.../cadastre_fr/CacheFileLambert9ZoneFilter.java | 59 ++
.../src/cadastre_fr/CacheFileUTM20NFilter.java | 53 ++
cadastre-fr/src/cadastre_fr/CadastreGrabber.java | 100 +++
cadastre-fr/src/cadastre_fr/CadastreInterface.java | 508 +++++++++++
cadastre-fr/src/cadastre_fr/CadastrePlugin.java | 302 +++++++
.../src/cadastre_fr/CadastrePreferenceSetting.java | 280 ++++++
.../src/cadastre_fr/CheckSourceUploadHook.java | 92 ++
.../src/cadastre_fr/DownloadSVGBuilding.java | 276 ++++++
cadastre-fr/src/cadastre_fr/DownloadSVGTask.java | 222 +++++
.../src/cadastre_fr/DownloadWMSPlanImage.java | 122 +++
.../src/cadastre_fr/DownloadWMSVectorImage.java | 82 ++
.../src/cadastre_fr/DuplicateLayerException.java | 5 +
cadastre-fr/src/cadastre_fr/EastNorthBound.java | 42 +
cadastre-fr/src/cadastre_fr/GeorefImage.java | 233 +++++
cadastre-fr/src/cadastre_fr/ImageModifier.java | 16 +
.../src/cadastre_fr/MenuActionBoundaries.java | 37 +
.../src/cadastre_fr/MenuActionBuildings.java | 37 +
cadastre-fr/src/cadastre_fr/MenuActionGrab.java | 46 +
.../src/cadastre_fr/MenuActionGrabPlanImage.java | 348 ++++++++
.../src/cadastre_fr/MenuActionLoadFromCache.java | 117 +++
.../src/cadastre_fr/MenuActionNewLocation.java | 99 ++
.../src/cadastre_fr/MenuActionResetCookie.java | 24 +
.../src/cadastre_fr/MenuActionSaveRasterAs.java | 74 ++
.../src/cadastre_fr/RasterImageModifier.java | 104 +++
cadastre-fr/src/cadastre_fr/SVGParser.java | 65 ++
cadastre-fr/src/cadastre_fr/Scale.java | 29 +
cadastre-fr/src/cadastre_fr/SimplifyWay.java | 128 +++
.../src/cadastre_fr/VectorImageModifier.java | 112 +++
cadastre-fr/src/cadastre_fr/WMSAdjustAction.java | 159 ++++
cadastre-fr/src/cadastre_fr/WMSDownloadAction.java | 51 ++
cadastre-fr/src/cadastre_fr/WMSLayer.java | 613 +++++++++++++
{measurement => colorscheme}/.classpath | 2 +-
colorscheme/.project | 17 +
{livegps => editgpx}/.classpath | 3 +-
editgpx/.project | 24 +
{slippymap => editgpx}/build.xml | 12 +-
editgpx/images/editgpx_layer.png | Bin 0 -> 377 bytes
editgpx/images/mapmode/editgpx_mode.png | Bin 0 -> 1139 bytes
.../josm/plugins/editgpx/EditGpxLayer.java | 223 +++++
.../josm/plugins/editgpx/EditGpxMode.java | 134 +++
.../josm/plugins/editgpx/EditGpxPlugin.java | 115 +++
.../josm/plugins/editgpx/GPXLayerImportAction.java | 143 +++
{agpifoj => lakewalker}/.classpath | 14 +-
lakewalker/.project | 17 +
lakewalker/README | 23 +
{measurement => lakewalker}/build.xml | 9 +-
.../images/cursor/modifier/lakewalker-sml.png | Bin 0 -> 1047 bytes
lakewalker/images/lakewalker-sml.png | Bin 0 -> 1047 bytes
lakewalker/images/preferences/lakewalker.png | Bin 0 -> 1097 bytes
.../josm/plugins/lakewalker/BooleanConfigurer.java | 86 ++
.../josm/plugins/lakewalker/Configurer.java | 145 +++
.../josm/plugins/lakewalker/DoubleConfigurer.java | 63 ++
.../josm/plugins/lakewalker/IntConfigurer.java | 73 ++
.../josm/plugins/lakewalker/Lakewalker.java | 470 ++++++++++
.../josm/plugins/lakewalker/LakewalkerAction.java | 324 +++++++
.../josm/plugins/lakewalker/LakewalkerApp.java | 59 ++
.../plugins/lakewalker/LakewalkerException.java | 21 +
.../josm/plugins/lakewalker/LakewalkerPlugin.java | 25 +
.../plugins/lakewalker/LakewalkerPreferences.java | 160 ++++
.../josm/plugins/lakewalker/LakewalkerReader.java | 128 +++
.../josm/plugins/lakewalker/LakewalkerWMS.java | 234 +++++
.../josm/plugins/lakewalker/StringConfigurer.java | 81 ++
.../plugins/lakewalker/StringEnumConfigurer.java | 107 +++
livegps/.classpath | 2 +-
livegps/build.xml | 2 +-
livegps/src/livegps/LiveGpsLayer.java | 8 +-
livegps/src/livegps/LiveGpsPlugin.java | 3 +-
measurement/.classpath | 2 +-
measurement/build.xml | 2 +-
.../josm/plugins/measurement/MeasurementLayer.java | 5 +-
{measurement => routing}/.classpath | 4 +-
routing/.project | 17 +
{surveyor => routing}/build.xml | 32 +-
routing/images/dialogs/routing.png | Bin 0 -> 1109 bytes
routing/images/layer/routing_small.png | Bin 0 -> 818 bytes
routing/images/mapmode/add.png | Bin 0 -> 1030 bytes
routing/images/mapmode/move.png | Bin 0 -> 964 bytes
routing/images/mapmode/remove.png | Bin 0 -> 1043 bytes
routing/images/mapmode/routing.png | Bin 0 -> 1109 bytes
routing/images/preferences/routing.png | Bin 0 -> 1109 bytes
routing/images/routing/endflag.png | Bin 0 -> 568 bytes
routing/images/routing/middleflag.png | Bin 0 -> 514 bytes
routing/images/routing/startflag.png | Bin 0 -> 460 bytes
routing/resources/log4j.xml | 22 +
.../com/innovant/josm/jrt/core/EdgeIterator.java | 9 +
.../innovant/josm/jrt/core/PreferencesKeys.java | 41 +
.../com/innovant/josm/jrt/core/RoutingEdge.java | 27 +
.../com/innovant/josm/jrt/core/RoutingGraph.java | 378 ++++++++
.../josm/jrt/core/RoutingGraphDelegator.java | 60 ++
.../com/innovant/josm/jrt/core/RoutingProfile.java | 161 ++++
.../innovant/josm/jrt/gtfs/GTFSTransportModes.java | 57 ++
routing/src/com/innovant/josm/jrt/osm/OsmEdge.java | 108 +++
.../src/com/innovant/josm/jrt/osm/OsmWayTypes.java | 79 ++
.../innovant/josm/plugin/routing/RoutingLayer.java | 341 +++++++
.../innovant/josm/plugin/routing/RoutingModel.java | 159 ++++
.../josm/plugin/routing/RoutingPlugin.java | 263 ++++++
.../plugin/routing/actions/AddRouteNodeAction.java | 108 +++
.../routing/actions/MoveRouteNodeAction.java | 156 ++++
.../routing/actions/RemoveRouteNodeAction.java | 124 +++
.../josm/plugin/routing/gui/RoutingDialog.java | 156 ++++
.../josm/plugin/routing/gui/RoutingMenu.java | 195 ++++
.../routing/gui/RoutingPreferenceDialog.java | 235 +++++
.../com/innovant/josm/plugin/routing/package.html | 9 +
slippymap/.classpath | 4 +-
slippymap/build.xml | 2 +-
.../josm/plugins/slippymap/SlippyMapLayer.java | 992 ++++++++++++---------
.../slippymap/SlippyMapPreferenceSetting.java | 60 +-
.../plugins/slippymap/SlippyMapPreferences.java | 325 +++++--
.../josm/plugins/slippymap/SlippyMapTile.java | 188 ----
surveyor/build.xml | 2 +-
.../surveyor/AutoSaveEditLayerTimerTask.java | 3 +-
.../josm/plugin/surveyor/action/SetNodeAction.java | 2 +-
svn-info.xml | 8 +-
utilsplugin/build.xml | 2 +-
utilsplugin/josm-utilsplugin.launch | 12 +
utilsplugin/src/UtilsPlugin/JoinAreasAction.java | 5 +-
utilsplugin/src/UtilsPlugin/JumpToAction.java | 4 +-
validator/build.xml | 109 ++-
validator/ignoretags.cfg | 16 +-
.../josm/plugins/validator/ErrorLayer.java | 9 +-
.../josm/plugins/validator/GridLayer.java | 4 +-
.../josm/plugins/validator/OSMValidatorPlugin.java | 2 +
.../openstreetmap/josm/plugins/validator/Test.java | 39 +-
.../josm/plugins/validator/TestError.java | 2 +-
.../josm/plugins/validator/ValidateAction.java | 2 +-
.../josm/plugins/validator/ValidateUploadHook.java | 2 +-
.../josm/plugins/validator/ValidatorDialog.java | 136 +--
.../josm/plugins/validator/tests/Coastlines.java | 186 +++-
.../josm/plugins/validator/tests/CrossingWays.java | 14 +-
.../plugins/validator/tests/DuplicateNode.java | 19 +-
.../josm/plugins/validator/tests/DuplicateWay.java | 6 +-
.../josm/plugins/validator/tests/NameMismatch.java | 109 +++
.../plugins/validator/tests/NodesWithSameName.java | 23 +-
.../josm/plugins/validator/tests/TagChecker.java | 52 +-
.../josm/plugins/validator/tests/UnclosedWays.java | 70 +-
.../plugins/validator/tests/UnconnectedWays.java | 318 +++++--
.../josm/plugins/validator/tests/UntaggedNode.java | 2 +-
.../josm/plugins/validator/tests/UntaggedWay.java | 6 +-
.../validator/tests/WronglyOrderedWays.java | 2 +
.../josm/plugins/validator/util/NameVisitor.java | 7 +-
validator/tagchecker.cfg | 2 +-
wmsplugin/.classpath | 14 +-
wmsplugin/build.xml | 133 +--
wmsplugin/sources.cfg | 28 +-
wmsplugin/src/wmsplugin/GeorefImage.java | 10 +-
wmsplugin/src/wmsplugin/Help_WMSmenuAction.java | 60 --
.../src/wmsplugin/Map_Rectifier_WMSmenuAction.java | 2 +-
wmsplugin/src/wmsplugin/WMSAdjustAction.java | 45 +-
wmsplugin/src/wmsplugin/WMSGrabber.java | 24 +-
wmsplugin/src/wmsplugin/WMSLayer.java | 808 +++++++++--------
wmsplugin/src/wmsplugin/WMSPlugin.java | 39 +-
wmsplugin/src/wmsplugin/WMSPreferenceEditor.java | 62 +-
195 files changed, 14736 insertions(+), 1718 deletions(-)
diff --git a/agpifoj/.classpath b/DirectUpload/.classpath
similarity index 100%
copy from agpifoj/.classpath
copy to DirectUpload/.classpath
diff --git a/DirectUpload/.project b/DirectUpload/.project
new file mode 100644
index 0000000..5a417af
--- /dev/null
+++ b/DirectUpload/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>JOSM-DirectUpload</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/DirectUpload/LICENSE b/DirectUpload/LICENSE
new file mode 100644
index 0000000..0d84b29
--- /dev/null
+++ b/DirectUpload/LICENSE
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/DirectUpload/README b/DirectUpload/README
new file mode 100644
index 0000000..e718dcd
--- /dev/null
+++ b/DirectUpload/README
@@ -0,0 +1,4 @@
+Directly uploads GPX from active layer in JOSM to OpenStreetMap Server.
+This currently uses OSM Api 0.5.
+
+More Details and FAQ's at : http://wiki.openstreetmap.org/index.php/User:Subhodip/GSoC_Doc#DirectUpload_Plugin_in_JOSM_:
diff --git a/slippymap/build.xml b/DirectUpload/build.xml
similarity index 82%
copy from slippymap/build.xml
copy to DirectUpload/build.xml
index a0eed67..cbfd294 100644
--- a/slippymap/build.xml
+++ b/DirectUpload/build.xml
@@ -1,4 +1,4 @@
-<project name="slippymap" default="dist" basedir=".">
+<project name="DirectUpload" default="dist" basedir=".">
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="plugin.build.dir" value="build"/>
@@ -20,12 +20,12 @@
</copy>
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
- <attribute name="Author" value="Frederik Ramm"/>
- <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.slippymap.SlippyMapPlugin"/>
+ <attribute name="Author" value="Subhodip Biswas"/>
+ <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.DirectUpload.UploadDataGuiPlugin"/>
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
- <attribute name="Plugin-Description" value="Displays a slippy map grid in JOSM. Can load tiles from slippy map as background and request updates."/>
- <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/SlippyMap"/>
- <attribute name="Plugin-Mainversion" value="2196"/>
+ <attribute name="Plugin-Description" value="This plugin directly upload GPS Traces from current active layer in JOSM to openstreetmap.org."/>
+ <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/User:Subhodip/GSoC_Doc#DirectUpload_Plugin_in_JOSM_:"/>
+ <attribute name="Plugin-Mainversion" value="2082"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/DirectUpload/images/UploadAction.png b/DirectUpload/images/UploadAction.png
new file mode 100644
index 0000000..6900786
Binary files /dev/null and b/DirectUpload/images/UploadAction.png differ
diff --git a/DirectUpload/nbproject/build-impl.xml b/DirectUpload/nbproject/build-impl.xml
new file mode 100644
index 0000000..7084414
--- /dev/null
+++ b/DirectUpload/nbproject/build-impl.xml
@@ -0,0 +1,629 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT ***
+*** EDIT ../build.xml INSTEAD ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+ - initialization
+ - compilation
+ - jar
+ - execution
+ - debugging
+ - javadoc
+ - junit compilation
+ - junit execution
+ - junit debugging
+ - applet
+ - cleanup
+
+ -->
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="DirectUpload-impl">
+ <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+ <!--
+ ======================
+ INITIALIZATION SECTION
+ ======================
+ -->
+ <target name="-pre-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init" name="-init-private">
+ <property file="nbproject/private/config.properties"/>
+ <property file="nbproject/private/configs/${config}.properties"/>
+ <property file="nbproject/private/private.properties"/>
+ </target>
+ <target depends="-pre-init,-init-private" name="-init-user">
+ <property file="${user.properties.file}"/>
+ <!-- The two properties below are usually overridden -->
+ <!-- by the active platform. Just a fallback. -->
+ <property name="default.javac.source" value="1.4"/>
+ <property name="default.javac.target" value="1.4"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-user" name="-init-project">
+ <property file="nbproject/configs/${config}.properties"/>
+ <property file="nbproject/project.properties"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+ <available file="${manifest.file}" property="manifest.available"/>
+ <condition property="manifest.available+main.class">
+ <and>
+ <isset property="manifest.available"/>
+ <isset property="main.class"/>
+ <not>
+ <equals arg1="${main.class}" arg2="" trim="true"/>
+ </not>
+ </and>
+ </condition>
+ <condition property="manifest.available+main.class+mkdist.available">
+ <and>
+ <istrue value="${manifest.available+main.class}"/>
+ <isset property="libs.CopyLibs.classpath"/>
+ </and>
+ </condition>
+ <condition property="have.tests">
+ <or>
+ <available file="${test.src.dir}"/>
+ </or>
+ </condition>
+ <condition property="have.sources">
+ <or>
+ <available file="${src.dir}"/>
+ </or>
+ </condition>
+ <condition property="netbeans.home+have.tests">
+ <and>
+ <isset property="netbeans.home"/>
+ <isset property="have.tests"/>
+ </and>
+ </condition>
+ <condition property="no.javadoc.preview">
+ <and>
+ <isset property="javadoc.preview"/>
+ <isfalse value="${javadoc.preview}"/>
+ </and>
+ </condition>
+ <property name="run.jvmargs" value=""/>
+ <property name="javac.compilerargs" value=""/>
+ <property name="work.dir" value="${basedir}"/>
+ <condition property="no.deps">
+ <and>
+ <istrue value="${no.dependencies}"/>
+ </and>
+ </condition>
+ <property name="javac.debug" value="true"/>
+ <property name="javadoc.preview" value="true"/>
+ <property name="application.args" value=""/>
+ <property name="source.encoding" value="${file.encoding}"/>
+ <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+ <and>
+ <isset property="javadoc.encoding"/>
+ <not>
+ <equals arg1="${javadoc.encoding}" arg2=""/>
+ </not>
+ </and>
+ </condition>
+ <property name="javadoc.encoding.used" value="${source.encoding}"/>
+ <property name="includes" value="**"/>
+ <property name="excludes" value=""/>
+ <property name="do.depend" value="false"/>
+ <condition property="do.depend.true">
+ <istrue value="${do.depend}"/>
+ </condition>
+ <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
+ <and>
+ <isset property="jaxws.endorsed.dir"/>
+ <available file="nbproject/jaxws-build.xml"/>
+ </and>
+ </condition>
+ </target>
+ <target name="-post-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
+ <fail unless="src.dir">Must set src.dir</fail>
+ <fail unless="test.src.dir">Must set test.src.dir</fail>
+ <fail unless="build.dir">Must set build.dir</fail>
+ <fail unless="dist.dir">Must set dist.dir</fail>
+ <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+ <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+ <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+ <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+ <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+ <fail unless="dist.jar">Must set dist.jar</fail>
+ </target>
+ <target name="-init-macrodef-property">
+ <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ <sequential>
+ <property name="@{name}" value="${@{value}}"/>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-javac">
+ <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="${javac.debug}" name="debug"/>
+ <attribute default="" name="sourcepath"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
+ <customize/>
+ </javac>
+ </sequential>
+ </macrodef>
+ <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <sequential>
+ <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </depend>
+ </sequential>
+ </macrodef>
+ <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <sequential>
+ <fail unless="javac.includes">Must set javac.includes</fail>
+ <pathconvert pathsep="," property="javac.includes.binary">
+ <path>
+ <filelist dir="@{destdir}" files="${javac.includes}"/>
+ </path>
+ <globmapper from="*.java" to="*.class"/>
+ </pathconvert>
+ <delete>
+ <files includes="${javac.includes.binary}"/>
+ </delete>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-junit">
+ <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <sequential>
+ <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
+ <batchtest todir="${build.test.results.dir}">
+ <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+ <filename name="@{testincludes}"/>
+ </fileset>
+ </batchtest>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ <jvmarg line="${run.jvmargs}"/>
+ </junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-nbjpda">
+ <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="name"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <attribute default="" name="stopclassname"/>
+ <sequential>
+ <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </nbjpdastart>
+ </sequential>
+ </macrodef>
+ <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${build.classes.dir}" name="dir"/>
+ <sequential>
+ <nbjpdareload>
+ <fileset dir="@{dir}" includes="${fix.classes}">
+ <include name="${fix.includes}*.class"/>
+ </fileset>
+ </nbjpdareload>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-debug-args">
+ <property name="version-output" value="java version "${ant.java.version}"/>
+ <condition property="have-jdk-older-than-1.4">
+ <or>
+ <contains string="${version-output}" substring="java version "1.0"/>
+ <contains string="${version-output}" substring="java version "1.1"/>
+ <contains string="${version-output}" substring="java version "1.2"/>
+ <contains string="${version-output}" substring="java version "1.3"/>
+ </or>
+ </condition>
+ <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
+ <istrue value="${have-jdk-older-than-1.4}"/>
+ </condition>
+ </target>
+ <target depends="-init-debug-args" name="-init-macrodef-debug">
+ <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${main.class}" name="classname"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" fork="true">
+ <jvmarg line="${debug-args-line}"/>
+ <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-java">
+ <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="classname"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" fork="true">
+ <jvmarg line="${run.jvmargs}"/>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-presetdef-jar">
+ <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <jar compress="${jar.compress}" jarfile="${dist.jar}">
+ <j2seproject1:fileset dir="${build.classes.dir}"/>
+ </jar>
+ </presetdef>
+ </target>
+ <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
+ <!--
+ ===================
+ COMPILATION SECTION
+ ===================
+ -->
+ <target depends="init" name="deps-jar" unless="no.deps"/>
+ <target depends="init,deps-jar" name="-pre-pre-compile">
+ <mkdir dir="${build.classes.dir}"/>
+ </target>
+ <target name="-pre-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-depend">
+ <j2seproject3:depend/>
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
+ <j2seproject3:javac/>
+ <copy todir="${build.classes.dir}">
+ <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target name="-post-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+ <target name="-pre-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile/>
+ <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
+ </target>
+ <target name="-post-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+ <!--
+ ====================
+ JAR BUILDING SECTION
+ ====================
+ -->
+ <target depends="init" name="-pre-pre-jar">
+ <dirname file="${dist.jar}" property="dist.jar.dir"/>
+ <mkdir dir="${dist.jar.dir}"/>
+ </target>
+ <target name="-pre-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
+ <j2seproject1:jar/>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
+ <j2seproject1:jar manifest="${manifest.file}"/>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
+ <j2seproject1:jar manifest="${manifest.file}">
+ <j2seproject1:manifest>
+ <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
+ </j2seproject1:manifest>
+ </j2seproject1:jar>
+ <echo>To run this application from the command line without Ant, try:</echo>
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <pathconvert property="run.classpath.with.dist.jar">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+ </pathconvert>
+ <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <pathconvert property="run.classpath.without.build.classes.dir">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to=""/>
+ </pathconvert>
+ <pathconvert pathsep=" " property="jar.classpath">
+ <path path="${run.classpath.without.build.classes.dir}"/>
+ <chainedmapper>
+ <flattenmapper/>
+ <globmapper from="*" to="lib/*"/>
+ </chainedmapper>
+ </pathconvert>
+ <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
+ <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
+ <fileset dir="${build.classes.dir}"/>
+ <manifest>
+ <attribute name="Main-Class" value="${main.class}"/>
+ <attribute name="Class-Path" value="${jar.classpath}"/>
+ </manifest>
+ </copylibs>
+ <echo>To run this application from the command line without Ant, try:</echo>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <echo>java -jar "${dist.jar.resolved}"</echo>
+ </target>
+ <target name="-post-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
+ <!--
+ =================
+ EXECUTION SECTION
+ =================
+ -->
+ <target depends="init,compile" description="Run a main class." name="run">
+ <j2seproject1:java>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <target name="-do-not-recompile">
+ <property name="javac.includes.binary" value=""/>
+ </target>
+ <target depends="init,-do-not-recompile,compile-single" name="run-single">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <j2seproject1:java classname="${run.class}"/>
+ </target>
+ <!--
+ =================
+ DEBUGGING SECTION
+ =================
+ -->
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger">
+ <j2seproject1:nbjpdastart name="${debug.class}"/>
+ </target>
+ <target depends="init,compile" name="-debug-start-debuggee">
+ <j2seproject3:debug>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
+ <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+ </target>
+ <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
+ <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+ <j2seproject3:debug classname="${debug.class}"/>
+ </target>
+ <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+ <target depends="init" name="-pre-debug-fix">
+ <fail unless="fix.includes">Must set fix.includes</fail>
+ <property name="javac.includes" value="${fix.includes}.java"/>
+ </target>
+ <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+ <j2seproject1:nbjpdareload/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+ <!--
+ ===============
+ JAVADOC SECTION
+ ===============
+ -->
+ <target depends="init" name="-javadoc-build">
+ <mkdir dir="${dist.javadoc.dir}"/>
+ <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+ <classpath>
+ <path path="${javac.classpath}"/>
+ </classpath>
+ <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
+ <filename name="**/*.java"/>
+ </fileset>
+ </javadoc>
+ </target>
+ <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+ <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+ </target>
+ <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+ <!--
+ =========================
+ JUNIT COMPILATION SECTION
+ =========================
+ -->
+ <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+ <mkdir dir="${build.test.classes.dir}"/>
+ </target>
+ <target name="-pre-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-test-depend">
+ <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+ <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
+ <copy todir="${build.test.classes.dir}">
+ <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target name="-post-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+ <target name="-pre-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
+ <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
+ <copy todir="${build.test.classes.dir}">
+ <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target name="-post-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+ <!--
+ =======================
+ JUNIT EXECUTION SECTION
+ =======================
+ -->
+ <target depends="init" if="have.tests" name="-pre-test-run">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
+ <j2seproject3:junit testincludes="**/*Test.java"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+ <fail if="tests.failed">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init" if="have.tests" name="test-report"/>
+ <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+ <target depends="init" if="have.tests" name="-pre-test-run-single">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+ <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+ <j2seproject3:junit excludes="" includes="${test.includes}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+ <fail if="tests.failed">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+ <!--
+ =======================
+ JUNIT DEBUGGING SECTION
+ =======================
+ -->
+ <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
+ <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+ <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
+ <delete file="${test.report.file}"/>
+ <mkdir dir="${build.test.results.dir}"/>
+ <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
+ <customize>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <arg value="${test.class}"/>
+ <arg value="showoutput=true"/>
+ <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
+ <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+ <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
+ </target>
+ <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+ <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+ <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+ <!--
+ =========================
+ APPLET EXECUTION SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" name="run-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject1:java classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <!--
+ =========================
+ APPLET DEBUGGING SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject3:debug classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
+ <!--
+ ===============
+ CLEANUP SECTION
+ ===============
+ -->
+ <target depends="init" name="deps-clean" unless="no.deps"/>
+ <target depends="init" name="-do-clean">
+ <delete dir="${build.dir}"/>
+ <delete dir="${dist.dir}"/>
+ </target>
+ <target name="-post-clean">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
+</project>
diff --git a/DirectUpload/nbproject/genfiles.properties b/DirectUpload/nbproject/genfiles.properties
new file mode 100644
index 0000000..249aeea
--- /dev/null
+++ b/DirectUpload/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=700b934e
+build.xml.script.CRC32=eedbed8a
+build.xml.stylesheet.CRC32=be360661
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=700b934e
+nbproject/build-impl.xml.script.CRC32=d2fe92a8
+nbproject/build-impl.xml.stylesheet.CRC32=487672f9
diff --git a/DirectUpload/nbproject/private/private.properties b/DirectUpload/nbproject/private/private.properties
new file mode 100644
index 0000000..46be75b
--- /dev/null
+++ b/DirectUpload/nbproject/private/private.properties
@@ -0,0 +1,2 @@
+jaxws.endorsed.dir=/usr/local/netbeans-6.1/java2/modules/ext/jaxws21/api
+user.properties.file=/home/subhodip/.netbeans/6.1/build.properties
diff --git a/DirectUpload/nbproject/private/private.xml b/DirectUpload/nbproject/private/private.xml
new file mode 100644
index 0000000..c1f155a
--- /dev/null
+++ b/DirectUpload/nbproject/private/private.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
+ <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
+</project-private>
diff --git a/DirectUpload/nbproject/project.properties b/DirectUpload/nbproject/project.properties
new file mode 100644
index 0000000..fcc99bc
--- /dev/null
+++ b/DirectUpload/nbproject/project.properties
@@ -0,0 +1,59 @@
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/DirectUpload.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+excludes=
+includes=**
+jar.compress=false
+javac.classpath=
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.source=1.5
+javac.target=1.5
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${libs.junit.classpath}:\
+ ${libs.junit_4.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+main.class=org.openstreetmap.josm.plugins.DirectUpload.UploadDataGui
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/DirectUpload/nbproject/project.xml b/DirectUpload/nbproject/project.xml
new file mode 100644
index 0000000..0e89957
--- /dev/null
+++ b/DirectUpload/nbproject/project.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.java.j2seproject</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+ <name>DirectUpload</name>
+ <minimum-ant-version>1.6.5</minimum-ant-version>
+ <source-roots>
+ <root id="src.dir"/>
+ </source-roots>
+ <test-roots>
+ <root id="test.src.dir"/>
+ </test-roots>
+ </data>
+ </configuration>
+</project>
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.form b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.form
new file mode 100644
index 0000000..d00446e
--- /dev/null
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.form
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
+ <Properties>
+ <Property name="defaultCloseOperation" type="int" value="2"/>
+ <Property name="alwaysOnTop" type="boolean" value="true"/>
+ </Properties>
+ <SyntheticProperties>
+ <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+ </SyntheticProperties>
+ <AuxValues>
+ <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ </AuxValues>
+
+ <Layout>
+ <DimensionLayout dim="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" alignment="1" attributes="0">
+ <Group type="103" groupAlignment="1" attributes="0">
+ <Group type="102" attributes="0">
+ <EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" max="-2" attributes="0">
+ <Component id="jLabel1" alignment="0" max="32767" attributes="1"/>
+ <Component id="PublicTrace" alignment="0" min="-2" max="-2" attributes="1"/>
+ <Component id="jLabel3" alignment="0" max="32767" attributes="1"/>
+ </Group>
+ <EmptySpace min="-2" pref="68" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="tagfield" pref="379" max="32767" attributes="0"/>
+ <Group type="102" attributes="0">
+ <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
+ <EmptySpace pref="124" max="32767" attributes="0"/>
+ </Group>
+ <Component id="descriptionfield" pref="379" max="32767" attributes="0"/>
+ </Group>
+ </Group>
+ <Group type="102" alignment="1" attributes="0">
+ <EmptySpace pref="384" max="32767" attributes="0"/>
+ <Component id="OkButton" min="-2" pref="79" max="-2" attributes="0"/>
+ <EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
+ <Component id="CancelButton" min="-2" pref="84" max="-2" attributes="0"/>
+ </Group>
+ <Group type="102" alignment="1" attributes="0">
+ <EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
+ <Component id="jScrollPane1" pref="527" max="32767" attributes="0"/>
+ </Group>
+ </Group>
+ <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ <DimensionLayout dim="1">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" attributes="0">
+ <EmptySpace max="-2" attributes="0"/>
+ <Component id="jLabel2" min="-2" pref="26" max="-2" attributes="0"/>
+ <EmptySpace min="-2" pref="34" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="descriptionfield" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="tagfield" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace min="-2" pref="56" max="-2" attributes="0"/>
+ <Component id="PublicTrace" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="-2" attributes="0"/>
+ <Component id="jScrollPane1" min="-2" pref="103" max="-2" attributes="0"/>
+ <EmptySpace type="separate" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="OkButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="CancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace min="-2" max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ </Layout>
+ <SubComponents>
+ <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+ <AuxValues>
+ <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+ </AuxValues>
+
+ <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+ <SubComponents>
+ <Component class="javax.swing.JTextArea" name="OutputDisplay">
+ <Properties>
+ <Property name="columns" type="int" value="20"/>
+ <Property name="rows" type="int" value="5"/>
+ </Properties>
+ </Component>
+ </SubComponents>
+ </Container>
+ <Component class="javax.swing.JButton" name="OkButton">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="Ok"/>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="OkButtonActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JButton" name="CancelButton">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="Cancel"/>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="CancelButtonActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JLabel" name="jLabel2">
+ <Properties>
+ <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+ <Font name="DejaVu Sans" size="14" style="1"/>
+ </Property>
+ <Property name="text" type="java.lang.String" value="Direct Upload to OpenStreetMap"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JCheckBox" name="PublicTrace">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="Public"/>
+ <Property name="toolTipText" type="java.lang.String" value="Selected makes your trace public in openstreetmap.org"/>
+ </Properties>
+ <Events>
+ <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="PublicTraceItemStateChanged"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JLabel" name="jLabel1">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="Description"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JTextField" name="descriptionfield">
+ <Properties>
+ <Property name="toolTipText" type="java.lang.String" value="Please enter Description about your trace."/>
+ </Properties>
+ <Events>
+ <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="descriptionfieldFocusLost"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JLabel" name="jLabel3">
+ <Properties>
+ <Property name="text" type="java.lang.String" value="Tags"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JTextField" name="tagfield">
+ <Properties>
+ <Property name="toolTipText" type="java.lang.String" value="Please enter tags about your trace."/>
+ </Properties>
+ <Events>
+ <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="tagfieldFocusLost"/>
+ </Events>
+ </Component>
+ </SubComponents>
+</Form>
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java
new file mode 100644
index 0000000..25f9416
--- /dev/null
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java
@@ -0,0 +1,517 @@
+/*
+ * UploadDataGui.java
+ *
+ * Created on August 17, 2008, 6:56 PM
+ * Copyright by Subhodip Biswas
+ * This program is free software and licensed under GPL.
+ */
+
+package org.openstreetmap.josm.plugins.DirectUpload;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.GpxWriter;
+import org.openstreetmap.josm.tools.Base64;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.UrlLabel;
+
+/**
+ *
+ * @author subhodip, xeen
+ */
+public class UploadDataGui extends ExtendedDialog {
+ /**
+ * This enum contains the possible values for the visibility field and their
+ * explanation. Provides some methods for easier handling.
+ */
+ private enum visibility {
+ PRIVATE (tr("Private (only shared as anonymous, unordered points)")),
+ PUBLIC (tr("Public (shown in trace list and as anonymous, unordered points)")),
+ TRACKABLE (tr("Trackable (only shared as anonymous, ordered points with timestamps)")),
+ IDENTIFIABLE (tr("Identifiable (shown in trace list and as identifiable, ordered points with timestamps)"));
+
+ public final String description;
+ visibility(String description) {
+ this.description = description;
+ }
+
+ /**
+ * "Converts" a given description into the actual enum. Returns null if no matching description
+ * is found.
+ * @param desc The description to look for
+ * @return visibility or null
+ */
+ public static visibility desc2visi(Object desc) {
+ for (visibility v : visibility.values()) {
+ if(desc.equals((String)v.description))
+ return v;
+ }
+ return null;
+ }
+
+ public String toString() {
+ return this.name().toLowerCase();
+ }
+ }
+
+
+ // User for log in when uploading trace
+ private String username = Main.pref.get("osm-server.username");
+ private String password = Main.pref.get("osm-server.password");
+
+ // Fields are declared here for easy access
+ // Do not remove the space in JMultilineLabel. Otherwise the label will be empty
+ // as we don't know its contents yet and therefore have a height of 0. This will
+ // lead to unnecessary scrollbars.
+ private JMultilineLabel OutputDisplay = new JMultilineLabel(" ");
+ private JTextField descriptionField = new JTextField(50);
+ private JTextField tagsField = new JTextField(50);
+ private JComboBox visibilityCombo = new JComboBox();
+
+ // Constants used when generating upload request
+ private static final String API_VERSION = "0.6";
+ private static final String BOUNDARY = "----------------------------d10f7aa230e8";
+ private static final String LINE_END = "\r\n";
+ private static final String uploadTraceText = tr("Upload Trace");
+
+ // Filename and current date. Date will be used as fallback if filename not available
+ private String datename = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
+ private String filename = "";
+
+ private boolean cancelled = false;
+
+ public UploadDataGui() {
+ // Initalizes ExtendedDialog
+ super(Main.parent,
+ tr("Upload Traces"),
+ new String[] {uploadTraceText, tr("Cancel")},
+ true
+ );
+ JPanel content = initComponents();
+ autoSelectTrace();
+ setContent(content);
+ setButtonIcons(new String[] { "uploadtrace.png", "cancel.png" });
+ setupDialog();
+
+ buttons.get(0).setEnabled(!checkForGPXLayer());
+ }
+
+ /**
+ * Sets up the dialog window elements
+ * @return JPanel with components
+ */
+ private JPanel initComponents() {
+ JLabel visibilityLabel = new JLabel(tr("Visibility"));
+ visibilityLabel.setToolTipText(tr("Defines the visibility of your trace for other OSM users."));
+ for(visibility v : visibility.values()) {
+ visibilityCombo.addItem((String)v.description);
+ }
+ UrlLabel visiUrl = new UrlLabel(tr("http://wiki.openstreetmap.org/wiki/Visibility_of_GPS_traces"), tr("(What does that mean?)"));
+
+ JLabel descriptionLabel = new JLabel(tr("Description"));
+ descriptionField.setToolTipText(tr("Please enter Description about your trace."));
+
+ JLabel tagsLabel = new JLabel(tr("Tags (comma delimited)"));
+ tagsField.setToolTipText(tr("Please enter tags about your trace."));
+
+ JPanel p = new JPanel(new GridBagLayout());
+
+ OutputDisplay.setMaxWidth(findMaxDialogSize().width-10);
+ p.add(OutputDisplay, GBC.eol());
+
+ p.add(tagsLabel, GBC.eol().insets(0,10,0,0));
+ p.add(tagsField, GBC.eol().fill(GBC.HORIZONTAL));
+
+ p.add(descriptionLabel, GBC.eol().insets(0,10,0,0));
+ p.add(descriptionField, GBC.eol().fill(GBC.HORIZONTAL));
+
+ p.add(visibilityLabel, GBC.std().insets(0,10,0,0));
+ p.add(visiUrl, GBC.eol().insets(0,10,0,0));
+ p.add(visibilityCombo, GBC.eol());
+
+ return p;
+ }
+
+ /**
+ * This function will automatically select a GPX layer if it's the only one.
+ * If a GPX layer is found, its filename will be parsed and displayed
+ */
+ private void autoSelectTrace() {
+ // If no GPX layer is selected, select one for the user if there is only one GPX layer
+ if(Main.map != null && Main.map.mapView != null) {
+ MapView mv=Main.map.mapView;
+ if(!(mv.getActiveLayer() instanceof GpxLayer)) {
+ Layer lastLayer=null;
+ int layerCount=0;
+ for (Layer l : mv.getAllLayers()) {
+ if(l instanceof GpxLayer) {
+ lastLayer = l;
+ layerCount++;
+ }
+ }
+ if(layerCount == 1) mv.setActiveLayer(lastLayer);
+ }
+
+ if(mv.getActiveLayer() instanceof GpxLayer) {
+ GpxData data=((GpxLayer)Main.map.mapView.getActiveLayer()).data;
+ try {
+ filename = data.storageFile.getName()
+ .replaceAll("[&?/\\\\]"," ").replaceAll("(\\.[^.]*)$","");
+ } catch(Exception e) { }
+ descriptionField.setText(getFilename());
+ OutputDisplay.setText(tr("Selected track: {0}", getFilename()));
+ }
+ }
+ }
+
+ /**
+ * This is the actual workhorse that manages the upload.
+ * @param String Description of the GPX track being uploaded
+ * @param String Tags assosciated with the GPX track being uploaded
+ * @param boolean Shall the GPX track be public
+ * @param GpxData The GPX Data to upload
+ */
+ private void upload(String description, String tags, String visi, GpxData gpxData, ProgressMonitor progressMonitor) throws IOException {
+ progressMonitor.beginTask(null);
+ try {
+ if(checkForErrors(username, password, description, gpxData))
+ return;
+
+ // Clean description/tags from disallowed chars
+ description = description.replaceAll("[&?/\\\\]"," ");
+ tags = tags.replaceAll("[&?/\\\\.;]"," ");
+
+ // Set progress dialog to indeterminate while connecting
+ progressMonitor.indeterminateSubTask(tr("Connecting..."));
+
+ try {
+ // Generate data for upload
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeGpxFile(baos, "file", gpxData);
+ writeField(baos, "description", description);
+ writeField(baos, "tags", (tags != null && tags.length() > 0) ? tags : "");
+ writeField(baos, "visibility", visi);
+ writeString(baos, "--" + BOUNDARY + "--" + LINE_END);
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ HttpURLConnection conn = setupConnection(baos.size());
+
+ progressMonitor.setTicksCount(baos.size());
+ progressMonitor.subTask(null);
+
+ try {
+ flushToServer(bais, conn.getOutputStream(), progressMonitor);
+ } catch(Exception e) {}
+
+ if(cancelled) {
+ conn.disconnect();
+ OutputDisplay.setText(tr("Upload cancelled"));
+ buttons.get(0).setEnabled(true);
+ cancelled = false;
+ } else {
+ boolean success = finishUpConnection(conn);
+ buttons.get(0).setEnabled(!success);
+ if(success)
+ buttons.get(1).setText(tr("Close"));
+ }
+ } catch(Exception e) {
+ OutputDisplay.setText(tr("Error while uploading"));
+ e.printStackTrace();
+ }
+ } finally {
+ progressMonitor.finishTask();
+ }
+ }
+
+ /**
+ * This function sets up the upload URL and logs in using the username and password given
+ * in the preferences.
+ * @param int The length of the content to be sent to the server
+ * @return HttpURLConnection The set up conenction
+ */
+ private HttpURLConnection setupConnection(int contentLength) throws Exception {
+ // Encode username and password
+ CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
+ String auth = username + ":" + password;
+ ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth));
+
+ // Upload URL
+ URL url = new URL("http://www.openstreetmap.org/api/" + API_VERSION + "/gpx/create");
+
+ // Set up connection and log in
+ HttpURLConnection c = (HttpURLConnection) url.openConnection();
+ c.setFixedLengthStreamingMode(contentLength);
+ c.setConnectTimeout(15000);
+ c.setRequestMethod("POST");
+ c.setDoOutput(true);
+ c.addRequestProperty("Authorization", "Basic " + Base64.encode(bytes));
+ c.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
+ c.addRequestProperty("Connection", "close"); // counterpart of keep-alive
+ c.addRequestProperty("Expect", "");
+ c.connect();
+
+ return c;
+ }
+
+ /**
+ * This function checks if the given connection finished up fine, closes it and returns the result.
+ * It also posts the result (or errors) to OutputDisplay.
+
+ * @param HttpURLConnection The connection to check/finish up
+ */
+ private boolean finishUpConnection(HttpURLConnection c) throws Exception {
+ String returnMsg = c.getResponseMessage();
+ boolean success = returnMsg.equals("OK");
+
+ if (c.getResponseCode() != 200) {
+ if (c.getHeaderField("Error") != null)
+ returnMsg += "\n" + c.getHeaderField("Error");
+ }
+
+ OutputDisplay.setText(success
+ ? tr("GPX upload was successful")
+ : tr("Upload failed. Server returned the following message: ") + returnMsg);
+
+ c.disconnect();
+ return success;
+ }
+
+ /**
+ * Uploads a given InputStream to a given OutputStream and sets progress
+ * @param InputSteam
+ * @param OutputStream
+ */
+ private void flushToServer(InputStream in, OutputStream out, ProgressMonitor progressMonitor) throws Exception {
+ // Upload in 10 kB chunks
+ byte[] buffer = new byte[10000];
+ int nread;
+ int cur = 0;
+ synchronized (in) {
+ while ((nread = in.read(buffer, 0, buffer.length)) >= 0) {
+ out.write(buffer, 0, nread);
+ cur += nread;
+ out.flush();
+ progressMonitor.worked(nread);
+ progressMonitor.subTask(getProgressText(cur, progressMonitor));
+
+ if(cancelled)
+ break;
+ }
+ }
+ if(!cancelled)
+ out.flush();
+ progressMonitor.subTask("Waiting for server reply...");
+ buffer = null;
+ }
+
+ /**
+ * Generates the output string displayed in the PleaseWaitDialog.
+ * @param int Bytes already uploaded
+ * @return String Message
+ */
+ private String getProgressText(int cur, ProgressMonitor progressMonitor) {
+ int max = progressMonitor.getTicksCount();
+ int percent = Math.round(cur * 100 / max);
+ return tr("Uploading GPX track: {0}% ({1} of {2})",
+ percent, formatBytes(cur), formatBytes(max));
+ }
+
+ /**
+ * Nicely calculates given bytes into MB, kB and B (with units)
+ * @param int Bytes
+ * @return String
+ */
+ private String formatBytes(int bytes) {
+ return (bytes > 1000 * 1000
+ // Rounds to 2 decimal places
+ ? new DecimalFormat("0.00")
+ .format((double)Math.round(bytes/(1000*10))/100) + " MB"
+ : (bytes > 1000
+ ? Math.round(bytes/1000) + " kB"
+ : bytes + " B"));
+ }
+
+ /**
+ * Checks for common errors and displays them in OutputDisplay if it finds any.
+ * Returns whether errors have been found or not.
+ * @param String OSM username
+ * @param String OSM password
+ * @param String GPX track description
+ * @param GpxData the GPX data to upload
+ * @return boolean true if errors have been found
+ */
+ private boolean checkForErrors(String username, String password,
+ String description, GpxData gpxData) {
+ String errors="";
+ if(description == null || description.length() == 0)
+ errors += tr("No description provided. Please provide some description.");
+
+ if(gpxData == null)
+ errors += tr("No GPX layer selected. Cannot upload a trace.");
+
+ if(username == null || username.length() == 0)
+ errors += tr("No username provided.");
+
+ if(password == null || password.length() == 0)
+ errors += tr("No password provided.");
+
+ OutputDisplay.setText(errors);
+ return errors.length() > 0;
+ }
+
+ /**
+ * Checks if a GPX layer is selected and returns the result. Also writes an error
+ * message to OutputDisplay if result is false.
+ * @return boolean True, if /no/ GPX layer is selected
+ */
+ private boolean checkForGPXLayer() {
+ if(Main.map == null
+ || Main.map.mapView == null
+ || Main.map.mapView.getActiveLayer() == null
+ || !(Main.map.mapView.getActiveLayer() instanceof GpxLayer)) {
+ OutputDisplay.setText(tr("No GPX layer selected. Cannot upload a trace."));
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * This creates the uploadTask that does the actual work and hands it to the main.worker to be executed.
+ */
+ private void setupUpload() {
+ if(checkForGPXLayer()) return;
+
+ // Disable Upload button so users can't just upload that track again
+ buttons.get(0).setEnabled(false);
+
+ PleaseWaitRunnable uploadTask = new PleaseWaitRunnable(tr("Uploading GPX Track")){
+ @Override protected void realRun() throws IOException {
+ upload(descriptionField.getText(),
+ tagsField.getText(),
+ visibility.desc2visi(visibilityCombo.getSelectedItem()).toString(),
+ ((GpxLayer)Main.map.mapView.getActiveLayer()).data,
+ progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)
+ );
+ }
+ @Override protected void finish() {}
+ @Override protected void cancel() {
+ cancelled = true;
+ }
+ };
+
+ Main.worker.execute(uploadTask);
+ }
+
+ /**
+ * Writes textfields (like in webbrowser) to the given ByteArrayOutputStream
+ * @param ByteArrayOutputStream
+ * @param String The name of the "textbox"
+ * @param String The value to write
+ */
+ private void writeField(ByteArrayOutputStream baos, String name, String value) throws IOException {
+ writeBoundary(baos);
+ writeString(baos, "Content-Disposition: form-data; name=\"" + name + "\"");
+ writeLineEnd(baos);
+ writeLineEnd(baos);
+ baos.write(value.getBytes("UTF-8"));
+ writeLineEnd(baos);
+ }
+
+ /**
+ * Writes gpxData (= file field in webbrowser) to the given ByteArrayOutputStream
+ * @param ByteArrayOutputStream
+ * @param String The name of the "upload field"
+ * @param GpxData The GPX data to upload
+ */
+ private void writeGpxFile(ByteArrayOutputStream baos, String name, GpxData gpxData) throws IOException {
+ writeBoundary(baos);
+ writeString(baos, "Content-Disposition: form-data; name=\"" + name + "\"; ");
+ writeString(baos, "filename=\"" + getFilename() + ".gpx" + "\"");
+ writeLineEnd(baos);
+ writeString(baos, "Content-Type: application/octet-stream");
+ writeLineEnd(baos);
+ writeLineEnd(baos);
+ new GpxWriter(baos).write(gpxData);
+ writeLineEnd(baos);
+ }
+
+ /**
+ * Writes a String to the given ByteArrayOutputStream
+ * @param ByteArrayOutputStream
+ * @param String
+ */
+ private void writeString(ByteArrayOutputStream baos, String s) {
+ try {
+ baos.write(s.getBytes());
+ } catch(Exception e) {}
+ }
+
+ /**
+ * Writes a newline to the given ByteArrayOutputStream
+ * @param ByteArrayOutputStream
+ */
+ private void writeLineEnd(ByteArrayOutputStream baos) {
+ writeString(baos, LINE_END);
+ }
+
+ /**
+ * Writes a boundary line to the given ByteArrayOutputStream
+ * @param ByteArrayOutputStream
+ */
+ private void writeBoundary(ByteArrayOutputStream baos) {
+ writeString(baos, "--" + BOUNDARY);
+ writeLineEnd(baos);
+ }
+
+ /**
+ * Returns the filename of the GPX file to be upload. If not available, returns current date
+ * as an alternative
+ * @param String
+ */
+ private String getFilename() {
+ return filename.equals("") ? datename : filename;
+ }
+
+ /**
+ * Overrides the default actions. Will not close the window when upload trace is clicked
+ */
+ @Override protected void buttonAction(ActionEvent evt) {
+ String a = evt.getActionCommand();
+ if(uploadTraceText.equals(a))
+ setupUpload();
+ else
+ setVisible(false);
+ }
+}
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java
new file mode 100644
index 0000000..aa5dea7
--- /dev/null
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright by Subhodip Biswas
+ * This program is free software and licensed under GPL.
+ *
+ */
+
+package org.openstreetmap.josm.plugins.DirectUpload;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.tools.Shortcut;
+/**
+ *
+ * @author subhodip
+ */
+public class UploadDataGuiPlugin extends Plugin{
+ UploadAction openaction;
+
+ public UploadDataGuiPlugin() {
+ openaction = new UploadAction();
+ Main.main.menu.toolsMenu.add(openaction);
+ }
+
+ class UploadAction extends JosmAction {
+
+ public UploadAction(){
+ super(tr("Upload Traces"), "UploadAction", tr("Uploads traces to openstreetmap.org"),
+ Shortcut.registerShortcut("tools:uploadtraces", tr("Tool: {0}", tr("Upload Traces")),
+ KeyEvent.VK_G, Shortcut.GROUP_MENU), false);
+ }
+ public void actionPerformed(ActionEvent e) {
+ UploadDataGui go = new UploadDataGui();
+ go.setVisible(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/agpifoj/.classpath b/agpifoj/.classpath
index b5edc13..07c06bb 100644
--- a/agpifoj/.classpath
+++ b/agpifoj/.classpath
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+ <classpathentry kind="lib" path="/JOSM/lib/metadata-extractor-2.3.1-nosun.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/agpifoj/build.xml b/agpifoj/build.xml
index 9008ceb..8a54e59 100644
--- a/agpifoj/build.xml
+++ b/agpifoj/build.xml
@@ -33,7 +33,7 @@
<attribute name="Plugin-Description" value="Another geotag plugin for JOSM. Correlates pictures with GPS tracks or import EXIF geotagged pictures."/>
<attribute name="Plugin-Early" value="false"/>
<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/AgPifoJ"/>
- <attribute name="Plugin-Mainversion" value="2166"/>
+ <attribute name="Plugin-Mainversion" value="2450"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java
index b8010f8..789230c 100644
--- a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java
+++ b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java
@@ -8,7 +8,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import static org.openstreetmap.josm.tools.I18n.trn;
import java.awt.Component;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
@@ -20,14 +20,17 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.CachedLatLon;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
@@ -101,11 +104,16 @@ public class AgpifojLayer extends Layer {
private AgpifojLayer layer;
private final File[] selection;
private HashSet<String> loadedDirectories = new HashSet<String>();
- private String errorMessage = "";
+ private LinkedHashSet<String> errorMessages;
+
+ protected void rememberError(String message) {
+ this.errorMessages.add(message);
+ }
public Loader(File[] selection) {
super(tr("Extracting GPS locations from EXIF"));
this.selection = selection;
+ errorMessages = new LinkedHashSet<String>();
}
@Override protected void realRun() throws IOException {
@@ -115,12 +123,12 @@ public class AgpifojLayer extends Layer {
try {
addRecursiveFiles(files, selection);
} catch(NullPointerException npe) {
- errorMessage += tr("One of the selected files was null !!!");
+ rememberError(tr("One of the selected files was null"));
}
if (cancelled) {
return;
- }
+ }
progressMonitor.subTask(tr("Read photos..."));
progressMonitor.setTicksCount(files.size());
@@ -155,9 +163,6 @@ public class AgpifojLayer extends Layer {
}
layer = new AgpifojLayer(data);
files.clear();
- if (errorMessage != null && ! errorMessage.trim().equals("")) {
- progressMonitor.setErrorMessage(errorMessage);
- }
}
private void addRecursiveFiles(List<File> files, File[] sel) {
@@ -178,8 +183,8 @@ public class AgpifojLayer extends Layer {
canonical = f.getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
- errorMessage += tr("Unable to get canonical path for directory {0}\n",
- f.getAbsolutePath());
+ rememberError(tr("Unable to get canonical path for directory {0}\n",
+ f.getAbsolutePath()));
}
if (canonical == null || loadedDirectories.contains(canonical)) {
@@ -195,10 +200,11 @@ public class AgpifojLayer extends Layer {
addRecursiveFiles(files, children);
} catch(NullPointerException npe) {
npe.printStackTrace();
- errorMessage += tr("Found null file in directory {0}\n", f.getPath());
+
+ rememberError(tr("Found null file in directory {0}\n", f.getPath()));
}
} else {
- errorMessage += tr("Error while getting files from directory {0}\n", f.getPath());
+ rememberError(tr("Error while getting files from directory {0}\n", f.getPath()));
}
} else {
@@ -211,7 +217,31 @@ public class AgpifojLayer extends Layer {
}
}
+ protected String formatErrorMessages() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("<html>");
+ if (errorMessages.size() == 1) {
+ sb.append(errorMessages.iterator().next());
+ } else {
+ sb.append("<ul>");
+ for (String msg: errorMessages) {
+ sb.append("<li>").append(msg).append("</li>");
+ }
+ sb.append("/ul>");
+ }
+ sb.append("</html>");
+ return sb.toString();
+ }
+
@Override protected void finish() {
+ if (!errorMessages.isEmpty()) {
+ JOptionPane.showMessageDialog(
+ Main.parent,
+ formatErrorMessages(),
+ tr("Error"),
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
if (layer != null) {
Main.main.addLayer(layer);
layer.hook_up_mouse_events(); // Main.map.mapView should exist
@@ -331,7 +361,7 @@ public class AgpifojLayer extends Layer {
}
@Override
- public void paint(Graphics g, MapView mv) {
+ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
int iconWidth = icon.getIconWidth() / 2;
int iconHeight = icon.getIconHeight() / 2;
diff --git a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java
index 6d46151..602b989 100644
--- a/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java
+++ b/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java
@@ -95,7 +95,8 @@ public class CorrelateGpxWithImages implements ActionListener {
this.file = file;
}
- public String toString() {
+ @Override
+ public String toString() {
return name;
}
}
@@ -171,7 +172,7 @@ public class CorrelateGpxWithImages implements ActionListener {
} catch (SAXException x) {
x.printStackTrace();
JOptionPane.showMessageDialog(
- Main.parent,
+ Main.parent,
tr("Error while parsing {0}",sel.getName())+": "+x.getMessage(),
tr("Error"),
JOptionPane.ERROR_MESSAGE
@@ -180,7 +181,7 @@ public class CorrelateGpxWithImages implements ActionListener {
} catch (IOException x) {
x.printStackTrace();
JOptionPane.showMessageDialog(
- Main.parent,
+ Main.parent,
tr("Could not read \"{0}\"",sel.getName())+"\n"+x.getMessage(),
tr("Error"),
JOptionPane.ERROR_MESSAGE
@@ -385,8 +386,8 @@ public class CorrelateGpxWithImages implements ActionListener {
boolean isOk = false;
while (! isOk) {
int answer = JOptionPane.showConfirmDialog(
- Main.parent, panel,
- tr("Synchronize time from a photo of the GPS receiver"),
+ Main.parent, panel,
+ tr("Synchronize time from a photo of the GPS receiver"),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE
);
@@ -636,11 +637,8 @@ public class CorrelateGpxWithImages implements ActionListener {
// Search whether an other layer has yet defined some bounding box.
// If none, we'll zoom to the bounding box of the layer with the photos.
- Collection<Layer> layerCol = Main.map.mapView.getAllLayers();
- Iterator<Layer> layerIter = layerCol.iterator();
boolean boundingBoxedLayerFound = false;
- while (layerIter.hasNext()) {
- Layer l = layerIter.next();
+ for (Layer l: Main.map.mapView.getAllLayers()) {
if (l != yLayer) {
BoundingXYVisitor bbox = new BoundingXYVisitor();
l.visitBoundingBox(bbox);
@@ -656,7 +654,7 @@ public class CorrelateGpxWithImages implements ActionListener {
Main.map.mapView.recalculateCenterScale(bbox);
}
- Main.main.map.repaint();
+ Main.map.repaint();
JOptionPane.showMessageDialog(Main.parent, tr("Found {0} matches of {1} in GPX track {2}", matched, dateImgLst.size(), selectedGpx.name),
tr("GPX Track loaded"),
@@ -885,7 +883,7 @@ public class CorrelateGpxWithImages implements ActionListener {
// Settings are only saved temporarily to the layer.
ExtendedDialog d = new ExtendedDialog(Main.parent,
tr("Adjust timezone and offset"),
- new String[] { tr("Close"), tr("Default Values") }
+ new String[] { tr("Close"), tr("Default Values") }
);
d.setContent(p);
diff --git a/measurement/.classpath b/cadastre-fr/.classpath
similarity index 94%
copy from measurement/.classpath
copy to cadastre-fr/.classpath
index 05929d7..17b8e6a 100644
--- a/measurement/.classpath
+++ b/cadastre-fr/.classpath
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
<classpathentry kind="output" path="build"/>
</classpath>
diff --git a/cadastre-fr/.project b/cadastre-fr/.project
new file mode 100644
index 0000000..5a26fbf
--- /dev/null
+++ b/cadastre-fr/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>JOSM-Cadastre-fr</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/cadastre-fr/CONTRIBUTION b/cadastre-fr/CONTRIBUTION
new file mode 100644
index 0000000..f1b8674
--- /dev/null
+++ b/cadastre-fr/CONTRIBUTION
@@ -0,0 +1,4 @@
+The French Cadastre plugin is designed and coded by Pieren <pieren3 at gmail.com>
+and is inspired by the wmsplugin for the special handling required by
+the french cadastre WMS www.cadatre.gouv.fr.
+This particular WMS provides vectorized or raster images for only one municipality at a time.
\ No newline at end of file
diff --git a/cadastre-fr/LICENSE b/cadastre-fr/LICENSE
new file mode 100644
index 0000000..fe31ef6
--- /dev/null
+++ b/cadastre-fr/LICENSE
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/cadastre-fr/README b/cadastre-fr/README
new file mode 100644
index 0000000..4561b8e
--- /dev/null
+++ b/cadastre-fr/README
@@ -0,0 +1,26 @@
+cadastre-fr is a plugin for JOSM that is able to handle the
+french land registry WMS. This WMS is not a complete standard WMS
+since it is not providing most of the standard services. It is also
+just delivering data for one municipality at a time. A special request
+is required at the beginning to specify which municipality is desired.
+
+The plugin is today only requesting PNG images on user request only. The
+images can be stored in a cache file and reloaded later. The grabed images
+can be done at any zoom level and the drawing method will always select the
+best images (highest scale) to be displayed in front of worst images
+(lowest scale).
+
+Also the plugin is able to transform original images to add the transparency
+or replace the default background by the JOSM background color. Enabling
+transparency gives the possibility to use several layers, each layer with a
+different municipality, making contrbutor's work much easier.
+
+Access to the french land registry WMS has been granted by the autorithies (DFFiP).
+It's opened not only for OSM but for all GIS applications. A new version of the
+WMS will take place mid 2009.
+
+Announce here (english):
+http://lists.openstreetmap.org/pipermail/talk/2009-January/033289.html
+First announcement here (french):
+http://lists.openstreetmap.org/pipermail/talk-fr/2009-January/005790.html
+
diff --git a/livegps/build.xml b/cadastre-fr/build.xml
similarity index 81%
copy from livegps/build.xml
copy to cadastre-fr/build.xml
index 1f7acd1..78ae672 100644
--- a/livegps/build.xml
+++ b/cadastre-fr/build.xml
@@ -1,4 +1,4 @@
-<project name="livegps" default="dist" basedir=".">
+<project name="cadastre-fr" default="dist" basedir=".">
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="plugin.build.dir" value="build"/>
@@ -20,13 +20,13 @@
</copy>
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
- <attribute name="Author" value="Frederik Ramm"/>
- <attribute name="Plugin-Class" value="livegps.LiveGpsPlugin"/>
+ <attribute name="Author" value="Pieren"/>
+ <attribute name="Plugin-Class" value="cadastre_fr.CadastrePlugin"/>
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
- <attribute name="Plugin-Description" value="Support live GPS input (moving dot) through a connection to gpsd server."/>
- <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/LiveGPS"/>
- <attribute name="Plugin-Mainversion" value="1890"/>
- <attribute name="Plugin-Stage" value="50"/>
+ <attribute name="Plugin-Description" value="A special handler for the French land registry WMS server."/>
+ <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/FR:JOSM/Fr:Plugin/Cadastre"/>
+ <attribute name="Plugin-Mainversion" value="2509"/>
+ <attribute name="Plugin-Stage" value="60"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/cadastre-fr/dist-lastest/readme.txt b/cadastre-fr/dist-lastest/readme.txt
new file mode 100644
index 0000000..4cfe411
--- /dev/null
+++ b/cadastre-fr/dist-lastest/readme.txt
@@ -0,0 +1,4 @@
+Here you find a temporary version of the plugin which works with josm-latest.jar
+where the official plugin location (in ../../dist/) works with josm-tested.jar.
+This build is committed manually and is not supported by the plugin manager of JOSM.
+Therefor, installation has to be done manually.
diff --git a/cadastre-fr/images/cadastre_small.png b/cadastre-fr/images/cadastre_small.png
new file mode 100644
index 0000000..ce02348
Binary files /dev/null and b/cadastre-fr/images/cadastre_small.png differ
diff --git a/cadastre-fr/images/load.png b/cadastre-fr/images/load.png
new file mode 100644
index 0000000..2019dc8
Binary files /dev/null and b/cadastre-fr/images/load.png differ
diff --git a/cadastre-fr/images/mapmode/adjustxywms.png b/cadastre-fr/images/mapmode/adjustxywms.png
new file mode 100644
index 0000000..2fa1964
Binary files /dev/null and b/cadastre-fr/images/mapmode/adjustxywms.png differ
diff --git a/cadastre-fr/images/mapmode/adjustzwms.png b/cadastre-fr/images/mapmode/adjustzwms.png
new file mode 100644
index 0000000..3a5fced
Binary files /dev/null and b/cadastre-fr/images/mapmode/adjustzwms.png differ
diff --git a/cadastre-fr/images/preferences/cadastrewms.gif b/cadastre-fr/images/preferences/cadastrewms.gif
new file mode 100644
index 0000000..1eccb90
Binary files /dev/null and b/cadastre-fr/images/preferences/cadastrewms.gif differ
diff --git a/cadastre-fr/images/preferences/sel_box_1.png b/cadastre-fr/images/preferences/sel_box_1.png
new file mode 100644
index 0000000..01d49f6
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_1.png differ
diff --git a/cadastre-fr/images/preferences/sel_box_2.png b/cadastre-fr/images/preferences/sel_box_2.png
new file mode 100644
index 0000000..d91f5af
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_2.png differ
diff --git a/cadastre-fr/images/preferences/sel_box_3.png b/cadastre-fr/images/preferences/sel_box_3.png
new file mode 100644
index 0000000..5b140c5
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_3.png differ
diff --git a/cadastre-fr/images/preferences/sel_box_4.png b/cadastre-fr/images/preferences/sel_box_4.png
new file mode 100644
index 0000000..d7dabc2
Binary files /dev/null and b/cadastre-fr/images/preferences/sel_box_4.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_1.png b/cadastre-fr/images/preferences/unsel_box_1.png
new file mode 100644
index 0000000..8bb171a
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_1.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_2.png b/cadastre-fr/images/preferences/unsel_box_2.png
new file mode 100644
index 0000000..cb942a7
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_2.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_3.png b/cadastre-fr/images/preferences/unsel_box_3.png
new file mode 100644
index 0000000..a743628
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_3.png differ
diff --git a/cadastre-fr/images/preferences/unsel_box_4.png b/cadastre-fr/images/preferences/unsel_box_4.png
new file mode 100644
index 0000000..c99de83
Binary files /dev/null and b/cadastre-fr/images/preferences/unsel_box_4.png differ
diff --git a/cadastre-fr/images/save.png b/cadastre-fr/images/save.png
new file mode 100644
index 0000000..2949b3f
Binary files /dev/null and b/cadastre-fr/images/save.png differ
diff --git a/cadastre-fr/src/META-INF/MANIFEST.MF b/cadastre-fr/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..cfc5d09
--- /dev/null
+++ b/cadastre-fr/src/META-INF/MANIFEST.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Plugin-Class: cadastre_fr.CadastrePlugin
+Plugin-Description: A special handler for the French land registry WMS server
+Plugin-Version: 0.8
+Plugin-Stage: 60
+Plugin-Author: Pieren;<pieren3 at gmail.com>
diff --git a/cadastre-fr/src/cadastre_fr/CacheControl.java b/cadastre-fr/src/cadastre_fr/CacheControl.java
new file mode 100644
index 0000000..9ace78e
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheControl.java
@@ -0,0 +1,210 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+/**
+ * This class handles the WMS layer cache mechanism. The design is oriented for a good performance (no
+ * wait status on GUI, fast saving even in big file). A separate thread is created for each WMS
+ * layer to not suspend the GUI until disk I/O is terminated (a file for the cache can take
+ * several MB's). If the cache file already exists, new images are just appended to the file
+ * (performance). Since we use the ObjectStream methods, it is required to modify the standard
+ * ObjectOutputStream in order to have objects appended readable (otherwise a stream header
+ * is inserted before each append and an exception is raised at objects read).
+ */
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.projection.LambertCC9Zones;
+import org.openstreetmap.josm.data.projection.UTM_20N_France_DOM;
+
+public class CacheControl implements Runnable {
+
+ public static final String cLambertCC9Z = "CC";
+
+ public static final String cUTM20N = "UTM";
+
+ public class ObjectOutputStreamAppend extends ObjectOutputStream {
+ public ObjectOutputStreamAppend(OutputStream out) throws IOException {
+ super(out);
+ }
+ protected void writeStreamHeader() throws IOException {
+ reset();
+ }
+ }
+
+ public static boolean cacheEnabled = true;
+
+ public static int cacheSize = 500;
+
+
+ public WMSLayer wmsLayer = null;
+
+ private ArrayList<GeorefImage> imagesToSave = new ArrayList<GeorefImage>();
+ private Lock imagesLock = new ReentrantLock();
+
+ public CacheControl(WMSLayer wmsLayer) {
+ cacheEnabled = Main.pref.getBoolean("cadastrewms.enableCaching", true);
+ this.wmsLayer = wmsLayer;
+ try {
+ cacheSize = Integer.parseInt(Main.pref.get("cadastrewms.cacheSize", String.valueOf(CadastrePreferenceSetting.DEFAULT_CACHE_SIZE)));
+ } catch (NumberFormatException e) {
+ cacheSize = CadastrePreferenceSetting.DEFAULT_CACHE_SIZE;
+ }
+ File path = new File(CadastrePlugin.cacheDir);
+ if (!path.exists())
+ path.mkdirs();
+ else // check directory capacity
+ checkDirSize(path);
+ new Thread(this).start();
+ }
+
+ private void checkDirSize(File path) {
+ long size = 0;
+ long oldestFileDate = Long.MAX_VALUE;
+ int oldestFile = 0;
+ File[] files = path.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ size += files[i].length();
+ if (files[i].lastModified() < oldestFileDate) {
+ oldestFile = i;
+ oldestFileDate = files[i].lastModified();
+ }
+ }
+ if (size > cacheSize*1024*1024) {
+ System.out.println("Delete oldest file \""+ files[oldestFile].getName()
+ + "\" in cache dir to stay under the limit of " + cacheSize + " MB.");
+ files[oldestFile].delete();
+ checkDirSize(path);
+ }
+ }
+
+ public boolean loadCacheIfExist() {
+ try {
+ String extension = String.valueOf((wmsLayer.getLambertZone() + 1));
+ if (Main.proj instanceof LambertCC9Zones)
+ extension = cLambertCC9Z + extension;
+ else if (Main.proj instanceof UTM_20N_France_DOM)
+ extension = cUTM20N + extension;
+ File file = new File(CadastrePlugin.cacheDir + wmsLayer.getName() + "." + extension);
+ if (file.exists()) {
+ JOptionPane pane = new JOptionPane(
+ tr("Location \"{0}\" found in cache.\n"+
+ "Load cache first ?\n"+
+ "(No = new cache)", wmsLayer.getName()),
+ JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION, null);
+ // this below is a temporary workaround to fix the "always on top" issue
+ JDialog dialog = pane.createDialog(Main.parent, tr("Select Feuille"));
+ CadastrePlugin.prepareDialog(dialog);
+ dialog.setVisible(true);
+ int reply = (Integer)pane.getValue();
+ // till here
+
+ if (reply == JOptionPane.OK_OPTION && loadCache(file, wmsLayer.getLambertZone())) {
+ return true;
+ } else {
+ delete(file);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+ return false;
+ }
+
+ public void deleteCacheFile() {
+ try {
+ String extension = String.valueOf((wmsLayer.getLambertZone() + 1));
+ if (Main.proj instanceof LambertCC9Zones)
+ extension = cLambertCC9Z + extension;
+ delete(new File(CadastrePlugin.cacheDir + wmsLayer.getName() + "." + extension));
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private void delete(File file) {
+ System.out.println("Delete file "+file);
+ if (file.exists())
+ file.delete();
+ while (file.exists()) // wait until file is really gone (otherwise appends to existing one)
+ CadastrePlugin.safeSleep(500);
+ }
+
+ public boolean loadCache(File file, int currentLambertZone) {
+ boolean successfulRead = false;
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ successfulRead = wmsLayer.read(ois, currentLambertZone);
+ ois.close();
+ fis.close();
+ } catch (Exception ex) {
+ ex.printStackTrace(System.out);
+ JOptionPane.showMessageDialog(Main.parent, tr("Error loading file.\nProbably an old version of the cache file."), tr("Error"), JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ if (successfulRead && wmsLayer.isRaster()) {
+ // serialized raster bufferedImage hangs-up on Java6. Recreate them here
+ wmsLayer.images.get(0).image = RasterImageModifier.fixRasterImage(wmsLayer.images.get(0).image);
+ }
+ return successfulRead;
+ }
+
+
+ public synchronized void saveCache(GeorefImage image) {
+ imagesLock.lock();
+ this.imagesToSave.add(image);
+ this.notify();
+ imagesLock.unlock();
+ }
+
+ /**
+ * Thread saving the grabbed images in background.
+ */
+ public synchronized void run() {
+ for (;;) {
+ imagesLock.lock();
+ // copy locally the images to save for better performance
+ ArrayList<GeorefImage> images = new ArrayList<GeorefImage>(imagesToSave);
+ imagesToSave.clear();
+ imagesLock.unlock();
+ if (images != null && !images.isEmpty()) {
+ String extension = String.valueOf((wmsLayer.getLambertZone() + 1));
+ if (Main.proj instanceof LambertCC9Zones)
+ extension = cLambertCC9Z + extension;
+ else if (Main.proj instanceof UTM_20N_France_DOM)
+ extension = cUTM20N + extension;
+ File file = new File(CadastrePlugin.cacheDir + wmsLayer.getName() + "." + extension);
+ try {
+ if (file.exists()) {
+ ObjectOutputStreamAppend oos = new ObjectOutputStreamAppend(
+ new BufferedOutputStream(new FileOutputStream(file, true)));
+ for (GeorefImage img : images) {
+ oos.writeObject(img);
+ }
+ oos.close();
+ } else {
+ ObjectOutputStream oos = new ObjectOutputStream(
+ new BufferedOutputStream(new FileOutputStream(file)));
+ wmsLayer.write(oos);
+ for (GeorefImage img : images) {
+ oos.writeObject(img);
+ }
+ oos.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ }
+ }
+ try {wait();} catch (InterruptedException e) {
+ e.printStackTrace(System.out);
+ }
+ }
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/CacheFileLambert4ZoneFilter.java b/cadastre-fr/src/cadastre_fr/CacheFileLambert4ZoneFilter.java
new file mode 100644
index 0000000..3757b54
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheFileLambert4ZoneFilter.java
@@ -0,0 +1,52 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import java.io.File;
+import javax.swing.filechooser.FileFilter;
+
+public class CacheFileLambert4ZoneFilter extends FileFilter {
+
+ /**
+ * Derived from ExtensionFileFilter writen by imi
+ */
+ private final String extension;
+ private final String description;
+
+ public static CacheFileLambert4ZoneFilter[] filters = {
+ new CacheFileLambert4ZoneFilter("1", tr("Lambert Zone {0} cache file (.{0})", 1)),
+ new CacheFileLambert4ZoneFilter("2", tr("Lambert Zone {0} cache file (.{0})", 2)),
+ new CacheFileLambert4ZoneFilter("3", tr("Lambert Zone {0} cache file (.{0})", 3)),
+ new CacheFileLambert4ZoneFilter("4", tr("Lambert Zone {0} cache file (.{0})", 4))
+ };
+
+ /**
+ * Construct an extension file filter by giving the extension to check after.
+ *
+ */
+ private CacheFileLambert4ZoneFilter(String extension, String description) {
+ this.extension = extension;
+ this.description = description;
+ }
+
+ public boolean acceptName(String filename) {
+ String name = filename.toLowerCase();
+ for (String ext : extension.split(","))
+ if (name.endsWith("." + ext))
+ return true;
+ return false;
+ }
+
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.isDirectory())
+ return true;
+ return acceptName(pathname.getName());
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CacheFileLambert9ZoneFilter.java b/cadastre-fr/src/cadastre_fr/CacheFileLambert9ZoneFilter.java
new file mode 100644
index 0000000..b177eca
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheFileLambert9ZoneFilter.java
@@ -0,0 +1,59 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class CacheFileLambert9ZoneFilter extends FileFilter {
+
+ /**
+ * Derived from ExtensionFileFilter writen by imi
+ */
+ private final String extension;
+ private final String description;
+
+ public static CacheFileLambert9ZoneFilter[] filters = {
+ new CacheFileLambert9ZoneFilter("cc1", tr("Lambert CC9 Zone {0} cache file (.CC{0})",1)),
+ new CacheFileLambert9ZoneFilter("cc2", tr("Lambert CC9 Zone {0} cache file (.CC{0})",2)),
+ new CacheFileLambert9ZoneFilter("cc3", tr("Lambert CC9 Zone {0} cache file (.CC{0})",3)),
+ new CacheFileLambert9ZoneFilter("cc4", tr("Lambert CC9 Zone {0} cache file (.CC{0})",4)),
+ new CacheFileLambert9ZoneFilter("cc5", tr("Lambert CC9 Zone {0} cache file (.CC{0})",5)),
+ new CacheFileLambert9ZoneFilter("cc6", tr("Lambert CC9 Zone {0} cache file (.CC{0})",6)),
+ new CacheFileLambert9ZoneFilter("cc7", tr("Lambert CC9 Zone {0} cache file (.CC{0})",7)),
+ new CacheFileLambert9ZoneFilter("cc8", tr("Lambert CC9 Zone {0} cache file (.CC{0})",8)),
+ new CacheFileLambert9ZoneFilter("cc9", tr("Lambert CC9 Zone {0} cache file (.CC{0})",9))
+ };
+
+ /**
+ * Construct an extension file filter by giving the extension to check after.
+ *
+ */
+ private CacheFileLambert9ZoneFilter(String extension, String description) {
+ this.extension = extension;
+ this.description = description;
+ }
+
+ public boolean acceptName(String filename) {
+ String name = filename.toLowerCase();
+ for (String ext : extension.split(","))
+ if (name.endsWith("." + ext))
+ return true;
+ return false;
+ }
+
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.isDirectory())
+ return true;
+ return acceptName(pathname.getName());
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CacheFileUTM20NFilter.java b/cadastre-fr/src/cadastre_fr/CacheFileUTM20NFilter.java
new file mode 100644
index 0000000..cc30d0f
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CacheFileUTM20NFilter.java
@@ -0,0 +1,53 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class CacheFileUTM20NFilter extends FileFilter {
+
+ /**
+ * Derived from ExtensionFileFilter writen by imi
+ */
+ private final String extension;
+ private final String description;
+
+ public static CacheFileUTM20NFilter[] filters = {
+ new CacheFileUTM20NFilter("utm1", tr("Guadeloupe Fort-Marigot cache file (.UTM1)")),
+ new CacheFileUTM20NFilter("utm2", tr("Guadeloupe Ste-Anne cache file (.UTM2)")),
+ new CacheFileUTM20NFilter("utm3", tr("Martinique Fort Desaix cache file (.UTM3)"))
+ };
+
+ /**
+ * Construct an extension file filter by giving the extension to check after.
+ *
+ */
+ private CacheFileUTM20NFilter(String extension, String description) {
+ this.extension = extension;
+ this.description = description;
+ }
+
+ public boolean acceptName(String filename) {
+ String name = filename.toLowerCase();
+ for (String ext : extension.split(","))
+ if (name.endsWith("." + ext))
+ return true;
+ return false;
+ }
+
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.isDirectory())
+ return true;
+ return acceptName(pathname.getName());
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastreGrabber.java b/cadastre-fr/src/cadastre_fr/CadastreGrabber.java
new file mode 100644
index 0000000..aad2577
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastreGrabber.java
@@ -0,0 +1,100 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.ProgressInputStream;
+
+public class CadastreGrabber {
+
+ public final static double epsilon = 1e-11;
+
+ private CadastreInterface wmsInterface = new CadastreInterface();
+
+ public GeorefImage grab(WMSLayer wmsLayer, EastNorth lambertMin, EastNorth lambertMax) throws IOException, OsmTransferException {
+
+ try {
+ URL url = null;
+ if (wmsLayer.isRaster())
+ url = getURLRaster(wmsLayer, lambertMin, lambertMax);
+ else
+ url = getURLVector(lambertMin, lambertMax);
+ BufferedImage img = grab(url);
+ ImageModifier imageModified;
+ if (wmsLayer.isRaster())
+ imageModified = new RasterImageModifier(img);
+ else
+ imageModified = new VectorImageModifier(img);
+ return new GeorefImage(imageModified.bufferedImage, lambertMin, lambertMax);
+ } catch (MalformedURLException e) {
+ throw (IOException) new IOException(tr("CadastreGrabber: Illegal url.")).initCause(e);
+ }
+ }
+
+ private URL getURLRaster(WMSLayer wmsLayer, EastNorth lambertMin, EastNorth lambertMax) throws MalformedURLException {
+ // GET /scpc/wms?version=1.1&request=GetMap&layers=CDIF:PMC at QH4480001701&format=image/png&bbox=-1186,0,13555,8830&width=576&height=345&exception=application/vnd.ogc.se_inimage&styles= HTTP/1.1
+ final int cRasterX = 1000; // keep width constant and adjust width to original image proportions
+ String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+ str += "&layers=CDIF:PMC@";
+ str += wmsLayer.getCodeCommune();
+ str += "&format=image/png";
+ str += "&bbox=";
+ str += wmsLayer.eastNorth2raster(lambertMin, lambertMax);
+ //str += "&width=800&height=800"; // maximum allowed by wms server
+ str += "&width="+cRasterX+"&height="; // maximum allowed by wms server (576/345, 800/378, 1000/634)
+ str += (int)(cRasterX*(wmsLayer.communeBBox.max.getY() - wmsLayer.communeBBox.min.getY())/(wmsLayer.communeBBox.max.getX() - wmsLayer.communeBBox.min.getX()));
+ str += "&exception=application/vnd.ogc.se_inimage&styles=";
+ return new URL(str.replace(" ", "%20"));
+ }
+
+ private URL getURLVector(EastNorth lambertMin, EastNorth lambertMax) throws MalformedURLException {
+ String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+ str += "&layers=CDIF:LS3,CDIF:LS2,CDIF:LS1,CDIF:PARCELLE,CDIF:NUMERO";
+ str += ",CDIF:PT3,CDIF:PT2,CDIF:PT1,CDIF:LIEUDIT";
+ str += ",CDIF:SUBSECTION";
+ str += ",CDIF:SECTION";
+ str += ",CDIF:COMMUNE";
+ str += "&format=image/png";
+ //str += "&format=image/jpeg";
+ str += "&bbox="+lambertMin.east()+",";
+ str += lambertMin.north() + ",";
+ str += lambertMax.east() + ",";
+ str += lambertMax.north();
+ //str += "&width=800&height=600"; // maximum allowed by wms server
+ str += "&width=1000&height=800"; // maximum allowed by wms server
+ //str += "&exception=application/vnd.ogc.se_inimage"; // used by normal client but not required
+ str += "&styles=LS3_90,LS2_90,LS1_90,PARCELLE_90,NUMERO_90,PT3_90,PT2_90,PT1_90,LIEUDIT_90";
+ str += ",SUBSECTION_90";
+ str += ",SECTION_90";
+ str += ",COMMUNE_90";
+ System.out.println("URL="+str);
+ return new URL(str.replace(" ", "%20"));
+ }
+
+ private BufferedImage grab(URL url) throws IOException, OsmTransferException {
+ wmsInterface.urlConn = (HttpURLConnection)url.openConnection();
+ wmsInterface.urlConn.setRequestMethod("GET");
+ wmsInterface.setCookie();
+ InputStream is = new ProgressInputStream(wmsInterface.urlConn, NullProgressMonitor.INSTANCE);
+ BufferedImage img = ImageIO.read(is);
+ is.close();
+ return img;
+ }
+
+ public CadastreInterface getWmsInterface() {
+ return wmsInterface;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastreInterface.java b/cadastre-fr/src/cadastre_fr/CadastreInterface.java
new file mode 100644
index 0000000..b030d7e
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastreInterface.java
@@ -0,0 +1,508 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Vector;
+
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+
+public class CadastreInterface {
+ public boolean downloadCancelled = false;
+ public HttpURLConnection urlConn = null;
+
+ private String cookie;
+ private String interfaceRef = null;
+ private String lastWMSLayerName = null;
+ private URL searchFormURL;
+ private Vector<String> listOfCommunes = new Vector<String>();
+ private Vector<String> listOfTA = new Vector<String>();
+ class PlanImage {
+ String name;
+ String ref;
+ PlanImage(String name, String ref) {
+ this.name = name;
+ this.ref = ref;
+ }
+ }
+ private Vector<PlanImage> listOfFeuilles = new Vector<PlanImage>();
+
+ final String baseURL = "http://www.cadastre.gouv.fr";
+ final String cImageFormat = "Cette commune est au format ";
+ final String cCommuneListStart = "<select name=\"codeCommune\"";
+ final String cCommuneListEnd = "</select>";
+ final String c0ptionListStart = "<option value=\"";
+ final String cOptionListEnd = "</option>";
+ final String cBBoxCommunStart = "new GeoBox(";
+ final String cBBoxCommunEnd = ")";
+
+ final String cInterfaceVector = "afficherCarteCommune.do";
+ final String cInterfaceRasterTA = "afficherCarteTa.do";
+ final String cInterfaceRasterFeuille = "afficherCarteFeuille.do";
+ final String cImageLinkStart = "title=\"image\"><a href=\"#\" onClick=\"popup('afficherCarteFeuille.do?f=";
+ final String cImageNameStart = ">Feuille ";
+
+ public boolean retrieveInterface(WMSLayer wmsLayer) throws DuplicateLayerException {
+ if (wmsLayer.getName().equals(""))
+ return false;
+ // open the session with the French Cadastre web front end
+ downloadCancelled = false;
+ try {
+ if (cookie == null || !wmsLayer.getName().equals(lastWMSLayerName)) {
+ getCookie();
+ getInterface(wmsLayer);
+ this.lastWMSLayerName = wmsLayer.getName();
+ }
+ openInterface();
+ } catch (IOException e) {
+ /*JOptionPane.showMessageDialog(Main.parent,
+ tr("Town/city {0} not found or not available\n" +
+ "or action canceled", wmsLayer.getLocation()));*/
+ JOptionPane pane = new JOptionPane(
+ tr("Town/city {0} not found or not available\n" +
+ "or action canceled", wmsLayer.getLocation()),
+ JOptionPane.INFORMATION_MESSAGE);
+ // this below is a temporary workaround to fix the "always on top" issue
+ JDialog dialog = pane.createDialog(Main.parent, tr("Select commune"));
+ CadastrePlugin.prepareDialog(dialog);
+ dialog.setVisible(true);
+ // till here
+ return false;
+ }
+ return true;
+ }
+
+ private void getCookie() throws IOException {
+ try {
+ // first, get the cookie from Cadastre to allow next downloads
+ searchFormURL = new URL(baseURL + "/scpc/rechercherPlan.do");
+ urlConn = (HttpURLConnection)searchFormURL.openConnection();
+ urlConn.setRequestMethod("GET");
+ urlConn.connect();
+ if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException("Cannot get Cadastre cookie.");
+ }
+ System.out.println("GET "+searchFormURL);
+ BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+ while(in.readLine() != null) {} // read the buffer otherwise we sent POST too early
+ String headerName=null;
+ for (int i=1; (headerName = urlConn.getHeaderFieldKey(i))!=null; i++) {
+ if (headerName.equals("Set-Cookie")) {
+ cookie = urlConn.getHeaderField(i);
+ cookie = cookie.substring(0, cookie.indexOf(";"));
+ System.out.println("Cookie="+cookie);
+ }
+ }
+ } catch (MalformedURLException e) {
+ throw (IOException) new IOException(
+ "Illegal url.").initCause(e);
+ }
+ }
+
+ public void resetCookie() {
+ lastWMSLayerName = null;
+ }
+
+ public void resetCookieIfNewLayer(String newWMSLayerName) {
+ if (!newWMSLayerName.equals(lastWMSLayerName)) {
+ resetCookie();
+ }
+ }
+
+ public void setCookie() {
+ this.urlConn.setRequestProperty("Cookie", this.cookie);
+ }
+
+ public void setCookie(HttpURLConnection urlConn) {
+ urlConn.setRequestProperty("Cookie", this.cookie);
+ }
+
+ private void getInterface(WMSLayer wmsLayer) throws IOException, DuplicateLayerException {
+ // first attempt : search for given name without codeCommune
+ interfaceRef = postForm(wmsLayer, "");
+ // second attempt either from known codeCommune (e.g. from cache) or from ComboBox
+ if (interfaceRef == null) {
+ if (!wmsLayer.getCodeCommune().equals("")) {
+ // codeCommune is already known (from previous request or from cache on disk)
+ interfaceRef = postForm(wmsLayer, wmsLayer.getCodeCommune());
+ } else {
+ if (listOfCommunes.size() > 1) {
+ // commune unknown, prompt the list of communes from
+ // server and try with codeCommune
+ wmsLayer.setCodeCommune(selectCommuneDialog());
+ checkLayerDuplicates(wmsLayer);
+ interfaceRef = postForm(wmsLayer, wmsLayer.getCodeCommune());
+ }
+ if (listOfCommunes.size() == 1 && wmsLayer.isRaster()) {
+ // commune known but raster format. Select "Feuille" (non-georeferenced image) from list.
+ int res = selectFeuilleDialog();
+ if (res != -1) {
+ // TODO
+ wmsLayer.setCodeCommune(listOfFeuilles.elementAt(res).name);
+ checkLayerDuplicates(wmsLayer);
+ interfaceRef = buildRasterFeuilleInterfaceRef(wmsLayer.getCodeCommune());
+ }
+ }
+ }
+ }
+
+ if (interfaceRef == null)
+ throw new IOException("Town/city " + wmsLayer.getLocation() + " not found.");
+ }
+
+ private void openInterface() throws IOException {
+ try {
+ // finally, open the interface on server side giving access to the wms server
+ String lines = null;
+ String ln = null;
+ URL interfaceURL = new URL(baseURL + "/scpc/"+interfaceRef);
+ urlConn = (HttpURLConnection)interfaceURL.openConnection();
+ urlConn.setRequestMethod("GET");
+ setCookie();
+ urlConn.connect();
+ if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException("Cannot open Cadastre interface. GET response:"+urlConn.getResponseCode());
+ }
+ System.out.println("GET "+interfaceURL);
+ BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+ // read the buffer otherwise we sent POST too early
+ while ((ln = in.readLine()) != null) {
+ lines += ln;
+ }
+ } catch (MalformedURLException e) {
+ throw (IOException) new IOException(
+ "CadastreGrabber: Illegal url.").initCause(e);
+ }
+ }
+
+ /**
+ * Post the form with the commune name and check the returned answer which is embedded
+ * in HTTP XML packets. This function doesn't use an XML parser yet but that would be a good idea
+ * for the next releases.
+ * Two possibilities :
+ * - either the commune name matches and we receive an URL starting with "afficherCarteCommune.do" or
+ * - we don't receive a single answer but a list of possible values. This answer looks like:
+ * <select name="codeCommune" class="long erreur" id="codeCommune">
+ * <option value="">Choisir</option>
+ * <option value="50061" >COLMARS - 04370</option>
+ * <option value="QK066" >COLMAR - 68000</option>
+ * </select>
+ * The returned string is the interface name used in further requests, e.g. "afficherCarteCommune.do?c=QP224"
+ * where QP224 is the code commune known by the WMS (or "afficherCarteTa.do?c=..." for raster images).
+ *
+ * @param location
+ * @param codeCommune
+ * @return retURL url to available code commune in the cadastre; "" if not found
+ * @throws IOException
+ */
+ private String postForm(WMSLayer wmsLayer, String codeCommune) throws IOException {
+ try {
+ String ln = null;
+ String lines = null;
+ listOfCommunes.clear();
+ listOfTA.clear();
+ // send a POST request with a city/town/village name
+ String content = "numerovoie=";
+ content += "&indiceRepetition=";
+ content += "&nomvoie=";
+ content += "&lieuDit=";
+ if (codeCommune == "") {
+ content += "&ville=" + new String(java.net.URLEncoder.encode(wmsLayer.getLocation(), "UTF-8"));
+ content += "&codePostal=";
+ } else {
+ content += "&codeCommune=" + codeCommune;
+ }
+ content += "&codeDepartement=";
+ content += "&nbResultatParPage=10";
+ urlConn = (HttpURLConnection)searchFormURL.openConnection();
+ urlConn.setRequestMethod("POST");
+ urlConn.setDoOutput(true);
+ urlConn.setDoInput(true);
+ setCookie();
+ OutputStream wr = urlConn.getOutputStream();
+ wr.write(content.getBytes());
+ System.out.println("POST "+content);
+ wr.flush();
+ wr.close();
+ BufferedReader rd = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+ while ((ln = rd.readLine()) != null) {
+ lines += ln;
+ }
+ rd.close();
+ urlConn.disconnect();
+ if (lines != null) {
+ if (lines.indexOf(cImageFormat) != -1) {
+ int i = lines.indexOf(cImageFormat);
+ int j = lines.indexOf(".", i);
+ wmsLayer.setRaster(lines.substring(i+cImageFormat.length(), j).equals("image"));
+ }
+ if (!wmsLayer.isRaster() && lines.indexOf(cInterfaceVector) != -1) { // "afficherCarteCommune.do"
+ // shall be something like: interfaceRef = "afficherCarteCommune.do?c=X2269";
+ lines = lines.substring(lines.indexOf(cInterfaceVector),lines.length());
+ lines = lines.substring(0, lines.indexOf("'"));
+ System.out.println("interface ref.:"+lines);
+ return lines;
+ } else if (wmsLayer.isRaster() && lines.indexOf(cInterfaceRasterTA) != -1) { // "afficherCarteTa.do"
+ // list of values parsed in listOfFeuilles (list all non-georeferenced images)
+ lines = getFeuillesList();
+ if (!downloadCancelled) {
+ parseFeuillesList(lines);
+ if (listOfFeuilles.size() > 0) {
+ int res = selectFeuilleDialog();
+ if (res != -1) {
+ wmsLayer.setCodeCommune(listOfFeuilles.elementAt(res).name);
+ checkLayerDuplicates(wmsLayer);
+ interfaceRef = buildRasterFeuilleInterfaceRef(wmsLayer.getCodeCommune());
+ wmsLayer.setCodeCommune(listOfFeuilles.elementAt(res).ref);
+ lines = buildRasterFeuilleInterfaceRef(listOfFeuilles.elementAt(res).ref);
+ System.out.println("interface ref.:"+lines);
+ return lines;
+ }
+ }
+ }
+ return null;
+ } else if (lines.indexOf(cCommuneListStart) != -1 && lines.indexOf(cCommuneListEnd) != -1) {
+ // list of values parsed in listOfCommunes
+ int i = lines.indexOf(cCommuneListStart);
+ int j = lines.indexOf(cCommuneListEnd, i);
+ parseCommuneList(lines.substring(i, j));
+ }
+ }
+ } catch (MalformedURLException e) {
+ throw (IOException) new IOException(
+ "Illegal url.").initCause(e);
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private void parseCommuneList(String input) {
+ if (input.indexOf(c0ptionListStart) != -1) {
+ while (input.indexOf("<option value=\"") != -1) {
+ int i = input.indexOf(c0ptionListStart);
+ int j = input.indexOf(cOptionListEnd, i+c0ptionListStart.length());
+ int k = input.indexOf("\"", i+c0ptionListStart.length());
+ if (j != -1 && k > (i + c0ptionListStart.length())) {
+ String lov = new String(input.substring(i+c0ptionListStart.length()-1, j));
+ if (lov.indexOf(">") != -1) {
+ System.out.println("parse "+lov);
+ listOfCommunes.add(lov);
+ } else
+ System.err.println("unable to parse commune string:"+lov);
+ }
+ input = input.substring(j+cOptionListEnd.length());
+ }
+ }
+ }
+
+ private String getFeuillesList() {
+ // get all images in one html page
+ String ln = null;
+ String lines = null;
+ HttpURLConnection urlConn2 = null;
+ try {
+ URL getAllImagesURL = new URL(baseURL + "/scpc/listerFeuillesParcommune.do?keepVolatileSession=&offset=2000");
+ urlConn2 = (HttpURLConnection)getAllImagesURL.openConnection();
+ setCookie(urlConn2);
+ urlConn2.connect();
+ System.out.println("GET "+getAllImagesURL);
+ BufferedReader rd = new BufferedReader(new InputStreamReader(urlConn2.getInputStream()));
+ while ((ln = rd.readLine()) != null) {
+ lines += ln;
+ }
+ rd.close();
+ urlConn2.disconnect();
+ //System.out.println("GET="+lines);
+ } catch (IOException e) {
+ listOfFeuilles.clear();
+ e.printStackTrace();
+ }
+ return lines;
+ }
+
+ private void parseFeuillesList(String input) {
+ listOfFeuilles.clear();
+ while (input.indexOf(cImageLinkStart) != -1) {
+ input = input.substring(input.indexOf(cImageLinkStart)+cImageLinkStart.length());
+ String refFeuille = input.substring(0, input.indexOf("'"));
+ String nameFeuille = input.substring(
+ input.indexOf(cImageNameStart)+cImageNameStart.length(),
+ input.indexOf(" -"));
+ listOfFeuilles.add(new PlanImage(nameFeuille, refFeuille));
+ }
+ }
+
+ private String selectCommuneDialog() {
+ JPanel p = new JPanel(new GridBagLayout());
+ String[] communeList = new String[listOfCommunes.size() + 1];
+ communeList[0] = tr("Choose from...");
+ for (int i = 0; i < listOfCommunes.size(); i++) {
+ communeList[i + 1] = listOfCommunes.elementAt(i).substring(listOfCommunes.elementAt(i).indexOf(">")+1);
+ }
+ JComboBox inputCommuneList = new JComboBox(communeList);
+ p.add(inputCommuneList, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 0, 0, 0));
+ JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null) {
+ private static final long serialVersionUID = 1L;
+ };
+ //pane.createDialog(Main.parent, tr("Select commune")).setVisible(true);
+ // this below is a temporary workaround to fix the "always on top" issue
+ JDialog dialog = pane.createDialog(Main.parent, tr("Select commune"));
+ CadastrePlugin.prepareDialog(dialog);
+ dialog.setVisible(true);
+ // till here
+ if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+ return null;
+ String result = listOfCommunes.elementAt(inputCommuneList.getSelectedIndex()-1);
+ return result.substring(1, result.indexOf(">")-2);
+ }
+
+ private int selectFeuilleDialog() {
+ JPanel p = new JPanel(new GridBagLayout());
+ Vector<String> ImageNames = new Vector<String>();
+ for (PlanImage src : listOfFeuilles) {
+ ImageNames.add(src.name);
+ }
+ JComboBox inputFeuilleList = new JComboBox(ImageNames);
+ p.add(inputFeuilleList, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 0, 0, 0));
+ JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null);
+ //pane.createDialog(Main.parent, tr("Select Feuille")).setVisible(true);
+ // this below is a temporary workaround to fix the "always on top" issue
+ JDialog dialog = pane.createDialog(Main.parent, tr("Select Feuille"));
+ CadastrePlugin.prepareDialog(dialog);
+ dialog.setVisible(true);
+ // till here
+ if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+ return -1;
+ int result = inputFeuilleList.getSelectedIndex();
+ return result;
+ }
+
+ private String buildRasterFeuilleInterfaceRef(String codeCommune) {
+ return cInterfaceRasterFeuille + "?f=" + codeCommune;
+ }
+
+ /**
+ * Retrieve the bounding box size in pixels of the whole commune (point 0,0 at top, left corner)
+ * and store it in given wmsLayer
+ * In case of raster image, we also check in the same http request if the image is already georeferenced
+ * and store the result in the wmsLayer as well.
+ * @param wmsLayer the WMSLayer where the commune data and images are stored
+ * @throws IOException
+ */
+ public void retrieveCommuneBBox(WMSLayer wmsLayer) throws IOException {
+ if (interfaceRef == null)
+ return;
+ String ln = null;
+ String line = null;
+ // send GET opening normally the small window with the commune overview
+ String content = baseURL + "/scpc/" + interfaceRef;
+ content += "&dontSaveLastForward&keepVolatileSession=";
+ searchFormURL = new URL(content);
+ urlConn = (HttpURLConnection)searchFormURL.openConnection();
+ urlConn.setRequestMethod("GET");
+ setCookie();
+ urlConn.connect();
+ if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException("Cannot get Cadastre response.");
+ }
+ System.out.println("GET "+searchFormURL);
+ BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
+ while ((ln = in.readLine()) != null) {
+ line += ln;
+ }
+ in.close();
+ urlConn.disconnect();
+ parseBBoxCommune(wmsLayer, line);
+ if (wmsLayer.isRaster() && !wmsLayer.isAlreadyGeoreferenced()) {
+ parseGeoreferences(wmsLayer, line);
+ }
+ }
+
+ private void parseBBoxCommune(WMSLayer wmsLayer, String input) {
+ if (input.indexOf(cBBoxCommunStart) != -1) {
+ input = input.substring(input.indexOf(cBBoxCommunStart));
+ int i = input.indexOf(",");
+ double minx = Double.parseDouble(input.substring(cBBoxCommunStart.length(), i));
+ int j = input.indexOf(",", i+1);
+ double miny = Double.parseDouble(input.substring(i+1, j));
+ int k = input.indexOf(",", j+1);
+ double maxx = Double.parseDouble(input.substring(j+1, k));
+ int l = input.indexOf(cBBoxCommunEnd, k+1);
+ double maxy = Double.parseDouble(input.substring(k+1, l));
+ wmsLayer.setCommuneBBox( new EastNorthBound(new EastNorth(minx,miny), new EastNorth(maxx,maxy)));
+ }
+ }
+
+ private void parseGeoreferences(WMSLayer wmsLayer, String input) {
+ if (input.lastIndexOf(cBBoxCommunStart) != -1) {
+ input = input.substring(input.lastIndexOf(cBBoxCommunStart));
+ input = input.substring(input.indexOf(cBBoxCommunEnd)+cBBoxCommunEnd.length());
+ int i = input.indexOf(",");
+ int j = input.indexOf(",", i+1);
+ double angle = Double.parseDouble(input.substring(i+1, j));
+ int k = input.indexOf(",", j+1);
+ double scale_origin = Double.parseDouble(input.substring(j+1, k));
+ int l = input.indexOf(",", k+1);
+ double dpi = Double.parseDouble(input.substring(k+1, l));
+ int m = input.indexOf(",", l+1);
+ double fX = Double.parseDouble(input.substring(l+1, m));
+ int n = input.indexOf(",", m+1);
+ double fY = Double.parseDouble(input.substring(m+1, n));
+ int o = input.indexOf(",", n+1);
+ double X0 = Double.parseDouble(input.substring(n+1, o));
+ int p = input.indexOf(",", o+1);
+ double Y0 = Double.parseDouble(input.substring(o+1, p));
+ if (X0 != 0.0 && Y0 != 0) {
+ wmsLayer.setAlreadyGeoreferenced(true);
+ wmsLayer.fX = fX;
+ wmsLayer.fY = fY;
+ wmsLayer.angle = angle;
+ wmsLayer.X0 = X0;
+ wmsLayer.Y0 = Y0;
+ }
+ System.out.println("parse georef:"+angle+","+scale_origin+","+dpi+","+fX+","+
+ fY+","+X0+","+Y0);
+ }
+ }
+
+ private void checkLayerDuplicates(WMSLayer wmsLayer) throws DuplicateLayerException {
+ if (Main.map != null) {
+ for (Layer l : Main.map.mapView.getAllLayers()) {
+ if (l instanceof WMSLayer && l.getName().equals(wmsLayer.getName()) && (l != wmsLayer)) {
+ System.out.println("Try to grab into a new layer when "+wmsLayer.getName()+" is already opened.");
+ // remove the duplicated layer
+ Main.map.mapView.removeLayer(wmsLayer);
+ throw new DuplicateLayerException();
+ }
+ }
+ }
+ }
+
+ public void cancel() {
+ if (urlConn != null) {
+ urlConn.setConnectTimeout(1);
+ urlConn.setReadTimeout(1);
+ //urlConn.disconnect();
+ }
+ downloadCancelled = true;
+ lastWMSLayerName = null;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastrePlugin.java b/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
new file mode 100644
index 0000000..635f3fc
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
@@ -0,0 +1,302 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JDialog;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.KeyStroke;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.actions.UploadAction;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.data.projection.*;
+
+/**
+ *
+ * Plugin to access the French Cadastre WMS server at www.cadastre.gouv.fr This
+ * WMS server requires some specific handling like retrieving a cookie for a
+ * limitation in case of no activity, or the request to the server shall provide
+ * a city/town/village code.
+ *
+ * @author Pieren <pieren3 at gmail.com>,
+ * @author <matthieu.lochegnies at gmail.com> for the extension to codeCommune
+ * @version 0.8
+ * History:
+ * 0.1 17-Jun-2008 first prototype using a first Lambert projection impl. in core
+ * 0.2 22-Jun-2008 first stable version
+ * 0.3 24-Jun-2008 add code departement
+ * 0.4 06-Jul-2008 - add images scales, icons, menu items disabling
+ * - remove dependencies of wmsplugin
+ * - add option to force a Lambert zone (for median locations)
+ * - add auto-sourcing
+ * 0.5 16-Aug-2008 - add transparency in layer (allowing multiple wms layers displayed together)
+ * - no overlapping of grabbed images if transparency is enabled
+ * - set one layer per location
+ * - use utf-8 charset in POST request to server
+ * - improve the preferences setting dialog
+ * - cancel the current download is now possible
+ * - add automatic images caching and load on request (+ manage cache directory size)
+ * - enable auto-sourcing only if a WMS layer is used
+ * 0.6 18-Aug-2008 - suppress the null-exception message after the dialog 'open a layer first'
+ * - process the overlapping images when cache is loaded from disk
+ * - save the last 'new location request' text again in preferences
+ * - avoid duplicate layers with same name
+ * - set text input for new locations in upper case
+ * - the cache directory is configurable in "cadastrewms.cacheDir"
+ * - improve configuration change updates
+ * 0.7 24-Aug-2008 - mask images only if transparency enabled
+ * - validate projection name by Lambert.toString() method
+ * 0.8 25-Jan-2009 - display returned list of communes if direct name is not recognized by server
+ * - new possible grab factor of 100 square meters fixed size
+ * - minor fixes due to changes in JOSM core classes
+ * - first draft of raster image support
+ * 0.9 05-Feb-2009 - grab vectorized full commune bbox, save in file, convert to OSM way
+ * and simplify
+ * 1.0 18-Feb-2009 - fix various bugs in color management and preference dialog
+ * - increase PNG picture size requested to WMS (800x1000)
+ * - set 4th grab scale fixed size configurable (from 25 to 1000 meters)
+ * 1.1 11-Jun-2009 - fixed a null exception error when trying to displace a vectorized layer
+ * - propose to use shortcut F11 for grabbing
+ * 1.2 16-Aug-2009 - implementation of raster image grabbing, cropping and georeferencing (not the
+ * overview rasters (Tableau d'assemblage) but directly small units (Feuille)
+ * 1.3 23-Aug-2009 - improve georeferencing action cancellation
+ * - fixed bug of raster image loaded from cache not working on Java1.6
+ * - improve mouse click bounce detection during georeferencing process
+ * 1.4 25-Oct-2009 - add support for new Lambert CC 9 Zones projection
+ * - add optional crosspieces display on raster image layers
+ * - add automatic raster images georeferencing when WMS provides data
+ * - re-implement manual adjustment mode in raster image layer
+ * 1.5 21-Nov-2009 - major changes in projection in core : no magical zone prediction anymore for
+ * Lambert 4 and 9 zones; grid translation implemented for Lambert 4 zones;
+ * support of subprojections in preferences for zones setting and UTM20N
+ * - removed autosourcing of empty new nodes
+ * 1.6 28-Nov-2009 - Fix minor issues if Grab is called without layer (possible since projection rework)
+ */
+public class CadastrePlugin extends Plugin {
+ static String VERSION = "1.6";
+
+ static JMenu cadastreJMenu;
+
+ public static CadastreGrabber cadastreGrabber = new CadastreGrabber();
+
+ public static String source = "";
+
+ // true if the checkbox "auto-sourcing" is set in the plugin menu
+ public static boolean autoSourcing = false;
+
+ // true when the plugin is first used, e.g. grab from WMS or download cache file
+ public static boolean pluginUsed = false;
+
+ public static String cacheDir = null;
+
+ public static boolean alterColors = false;
+
+ public static boolean backgroundTransparent = false;
+
+ public static float transparency = 1.0f;
+
+ public static boolean drawBoundaries = false;
+
+ static private boolean menuEnabled = false;
+
+ /**
+ * Creates the plugin and setup the default settings if necessary
+ *
+ * @throws Exception
+ */
+ public CadastrePlugin() throws Exception {
+ System.out.println("Pluging \"cadastre-fr\" started...");
+ if (Main.pref.get("cadastrewms.cacheDir").equals(""))
+ cacheDir = Main.pref.getPreferencesDir()+"plugins/cadastrewms/";
+ else {
+ cacheDir = Main.pref.get("cadastrewms.cacheDir");
+ if (cacheDir.charAt(cacheDir.length()-1) != '\\' )
+ cacheDir += '\\';
+ }
+ System.out.println("current cache directory: "+cacheDir);
+
+ refreshConfiguration();
+
+ UploadAction.registerUploadHook(new CheckSourceUploadHook());
+
+ }
+
+ public static void refreshMenu() {
+ MainMenu menu = Main.main.menu;
+
+ if (cadastreJMenu == null) {
+ cadastreJMenu = menu.addMenu(marktr("Cadastre"), KeyEvent.VK_C, menu.defaultMenuPos, ht("/Plugin/CadastreFr"));
+ JosmAction grab = new MenuActionGrab();
+ JMenuItem menuGrab = new JMenuItem(grab);
+ KeyStroke ks = grab.getShortcut().getKeyStroke();
+ if (ks != null) {
+ menuGrab.setAccelerator(ks);
+ }
+ JMenuItem menuActionGrabPlanImage = new JMenuItem(new MenuActionGrabPlanImage());
+ JMenuItem menuSettings = new JMenuItem(new MenuActionNewLocation());
+ final JCheckBoxMenuItem menuSource = new JCheckBoxMenuItem(tr("Auto sourcing"));
+ menuSource.setSelected(autoSourcing);
+ menuSource.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent ev) {
+ Main.pref.put("cadastrewms.autosourcing", menuSource.isSelected());
+ autoSourcing = menuSource.isSelected();
+ }
+ });
+
+ JMenuItem menuResetCookie = new JMenuItem(new MenuActionResetCookie());
+ //JMenuItem menuLambertZone = new JMenuItem(new MenuActionLambertZone());
+ JMenuItem menuLoadFromCache = new JMenuItem(new MenuActionLoadFromCache());
+ // temporary disabled:
+ //JMenuItem menuActionBoundaries = new JMenuItem(new MenuActionBoundaries());
+ //JMenuItem menuActionBuildings = new JMenuItem(new MenuActionBuildings());
+
+ cadastreJMenu.add(menuGrab);
+ cadastreJMenu.add(menuActionGrabPlanImage);
+ cadastreJMenu.add(menuSettings);
+ cadastreJMenu.add(menuSource);
+ cadastreJMenu.add(menuResetCookie);
+ //cadastreJMenu.add(menuLambertZone);
+ cadastreJMenu.add(menuLoadFromCache);
+ // all SVG features disabled until official WMS is released
+ //cadastreJMenu.add(menuActionBoundaries);
+ //cadastreJMenu.add(menuActionBuildings);
+ }
+ setEnabledAll(menuEnabled);
+ }
+
+ public static void refreshConfiguration() {
+ source = checkSourceMillesime();
+ autoSourcing = Main.pref.getBoolean("cadastrewms.autosourcing", true);
+ alterColors = Main.pref.getBoolean("cadastrewms.alterColors");
+ drawBoundaries = Main.pref.getBoolean("cadastrewms.drawBoundaries", false);
+ if (alterColors) {
+ backgroundTransparent = Main.pref.getBoolean("cadastrewms.backgroundTransparent");
+ transparency = Float.parseFloat(Main.pref.get("cadastrewms.brightness", "1.0f"));
+ } else {
+ backgroundTransparent = false;
+ transparency = 1.0f;
+ }
+ // overwrite F11 shortcut used from the beginning by this plugin and recently used
+ // for full-screen switch in JOSM core
+ int i = 0;
+ String p = Main.pref.get("shortcut.shortcut."+i, null);
+ boolean alreadyRedefined = false;
+ while (p != null) {
+ String[] s = p.split(";");
+ alreadyRedefined = alreadyRedefined || s[0].equals("menu:view:fullscreen");
+ i++;
+ p = Main.pref.get("shortcut.shortcut."+i, null);
+ }
+ if (!alreadyRedefined) {
+ int reply = JOptionPane.showConfirmDialog(null,
+ tr("Plugin cadastre-fr used traditionaly for grabbing the key shortcut F11\n"+
+ "which is currently allocated for full-screen switch by default\n"+
+ "Would you like to restore F11 for grab action ?"),
+ tr("Restore grab shortcut F11"),
+ JOptionPane.YES_NO_OPTION);
+ if (reply == JOptionPane.OK_OPTION) {
+ System.out.println("redefine fullscreen shortcut F11 to shift+F11");
+ Main.pref.put("shortcut.shortcut."+i, "menu:view:fullscreen;Toggle Full Screen view;122;5;122;64;false;true");
+ JOptionPane.showMessageDialog(Main.parent,tr("JOSM is stopped for the change to take effect."));
+ System.exit(0);
+ }
+ } else
+ System.out.println("shortcut F11 already redefined; do not change");
+
+ refreshMenu();
+ }
+
+ @Override
+ public PreferenceSetting getPreferenceSetting() {
+ return new CadastrePreferenceSetting();
+ }
+
+ private static void setEnabledAll(boolean isEnabled) {
+ for (int i = 0; i < cadastreJMenu.getItemCount(); i++) {
+ JMenuItem item = cadastreJMenu.getItem(i);
+ if (item != null)
+ if (item.getText().equals(MenuActionGrabPlanImage.name) /*||
+ item.getText().equals(MenuActionGrab.name) ||
+ item.getText().equals(MenuActionBoundaries.name) ||
+ item.getText().equals(MenuActionBuildings.name)*/) {
+ item.setEnabled(isEnabled);
+ }
+ }
+ menuEnabled = isEnabled;
+ }
+
+ public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+ if (cadastreJMenu != null) {
+ if (oldFrame == null && newFrame != null) {
+ setEnabledAll(true);
+ Main.map.addMapMode(new IconToggleButton
+ (new WMSAdjustAction(Main.map)));
+ } else if (oldFrame != null && newFrame == null) {
+ setEnabledAll(false);
+ //Lambert.layoutZone = -1;
+ //LambertCC9Zones.layoutZone = -1;
+ }
+ }
+ }
+
+ public static boolean isCadastreProjection() {
+ return Main.proj.toString().equals(new Lambert().toString())
+ || Main.proj.toString().equals(new UTM_20N_France_DOM().toString())
+ || Main.proj.toString().equals(new GaussLaborde_Reunion().toString())
+ || Main.proj.toString().equals(new LambertCC9Zones().toString());
+ }
+
+ public static void safeSleep(long milliseconds) {
+ try {
+ Thread.sleep(milliseconds);
+ } catch (InterruptedException e) {}
+ }
+
+ // See OptionPaneUtil
+ // FIXME: this is a temporary solution.
+ public static void prepareDialog(JDialog dialog) {
+ if (Main.pref.getBoolean("window-handling.option-pane-always-on-top", true)) {
+ try {
+ dialog.setAlwaysOnTop(true);
+ } catch(SecurityException e) {
+ System.out.println(tr("Warning: failed to put option pane dialog always on top. Exception was: {0}", e.toString()));
+ }
+ }
+ dialog.setModal(true);
+ dialog.toFront();
+ dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ }
+
+ private static String checkSourceMillesime() {
+ java.util.Calendar calendar = java.util.Calendar.getInstance();
+ int currentYear = calendar.get(java.util.Calendar.YEAR);
+ String src = Main.pref.get("cadastrewms.source",
+ "cadastre-dgi-fr source : Direction G\u00e9n\u00e9rale des Imp\u00f4ts - Cadastre. Mise \u00e0 jour : AAAA");
+ String srcYear = src.substring(src.lastIndexOf(" ")+1);
+ Integer year = null;
+ try {
+ year = Integer.decode(srcYear);
+ } catch (NumberFormatException e) {}
+ if (srcYear.equals("AAAA") || (year != null && year < currentYear)) {
+ System.out.println("Replace source year "+srcYear+" by current year "+currentYear);
+ src = src.substring(0, src.lastIndexOf(" ")+1)+currentYear;
+ Main.pref.put("cadastrewms.source", src);
+ }
+ return src;
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java b/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
new file mode 100644
index 0000000..972e7d0
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
@@ -0,0 +1,280 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.*;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.I18n;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Preference settings for the French Cadastre plugin
+ *
+ * @author Pieren <pieren3 at gmail.com>
+ */
+public class CadastrePreferenceSetting implements PreferenceSetting {
+
+ static final int TRANS_MIN = 1;
+ static final int TRANS_MAX = 10;
+ private JSlider sliderTrans = new JSlider(JSlider.HORIZONTAL, TRANS_MIN, TRANS_MAX, TRANS_MAX);
+
+ private JTextField sourcing = new JTextField(20);
+
+ private JCheckBox alterColors = new JCheckBox(tr("Replace original background by JOSM background color."));
+
+ private JCheckBox reversGrey = new JCheckBox(tr("Reverse grey colors (for black backgrounds)."));
+
+ private JCheckBox transparency = new JCheckBox(tr("Set background transparent."));
+
+ private JCheckBox drawBoundaries = new JCheckBox(tr("Draw boundaries of downloaded data."));
+
+ private JCheckBox disableImageCropping = new JCheckBox(tr("Disable image cropping during georeferencing."));
+
+ private JRadioButton grabMultiplier1 = new JRadioButton("", true);
+
+ private JRadioButton grabMultiplier2 = new JRadioButton("", true);
+
+ private JRadioButton grabMultiplier3 = new JRadioButton("", true);
+
+ private JRadioButton grabMultiplier4 = new JRadioButton("", true);
+
+ private JRadioButton crosspiece1 = new JRadioButton("off");
+
+ private JRadioButton crosspiece2 = new JRadioButton("50m");
+
+ private JRadioButton crosspiece3 = new JRadioButton("100m");
+
+ static final int DEFAULT_SQUARE_SIZE = 100;
+ private JTextField grabMultiplier4Size = new JTextField(5);
+
+ private JCheckBox enableCache = new JCheckBox(tr("Enable automatic caching."));
+
+ static final int DEFAULT_CACHE_SIZE = 500;
+ JLabel jLabelCacheSize = new JLabel(tr("Max. cache size (in MB)"));
+ private JTextField cacheSize = new JTextField(20);
+
+ static final String DEFAULT_RASTER_DIVIDER = "5";
+ private JTextField rasterDivider = new JTextField(10);
+
+ static final int DEFAULT_CROSSPIECES = 0;
+
+ public void addGui(final PreferenceDialog gui) {
+
+ String description = tr("A special handler of the French cadastre wms at www.cadastre.gouv.fr" + "<BR><BR>"
+ + "Please read the Terms and Conditions of Use here (in French): <br>"
+ + "<a href=\"http://www.cadastre.gouv.fr/scpc/html/CU_01_ConditionsGenerales_fr.html\"> "
+ + "http://www.cadastre.gouv.fr/scpc/html/CU_01_ConditionsGenerales_fr.html</a> <BR>"
+ + "before any upload of data created by this plugin.");
+ JPanel cadastrewms = gui.createPreferenceTab("cadastrewms.gif", I18n.tr("French cadastre WMS"), description);
+
+ // option to automatically set the source tag when uploading
+ sourcing.setText(CadastrePlugin.source);
+ sourcing.setToolTipText(tr("<html>Value of key \"source\" when autosourcing is enabled</html>"));
+ JLabel jLabelSource = new JLabel(tr("Source"));
+ cadastrewms.add(jLabelSource, GBC.eop().insets(0, 0, 0, 0));
+ cadastrewms.add(sourcing, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+
+ // option to alter the original colors of the wms images
+ alterColors.setSelected(Main.pref.getBoolean("cadastrewms.alterColors", false));
+ alterColors.setToolTipText(tr("Replace the original white background by the backgound color defined in JOSM preferences."));
+ cadastrewms.add(alterColors, GBC.eop().insets(0, 0, 0, 0));
+
+ // option to reverse the grey colors (to see texts background)
+ reversGrey.setSelected(Main.pref.getBoolean("cadastrewms.invertGrey", false));
+ reversGrey.setToolTipText(tr("Invert the original black and white colors (and all intermediate greys). Useful for texts on dark backgrounds."));
+ cadastrewms.add(reversGrey, GBC.eop().insets(00, 0, 0, 0));
+
+ // option to enable transparency
+ transparency.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ sliderTrans.setEnabled(transparency.isSelected());
+ }
+ });
+ transparency.setSelected(Main.pref.getBoolean("cadastrewms.backgroundTransparent", false));
+ transparency.setToolTipText(tr("Allows multiple layers stacking"));
+ cadastrewms.add(transparency, GBC.eop().insets(0, 0, 0, 0));
+
+ // slider for transparency level
+ sliderTrans.setSnapToTicks(true);
+ sliderTrans.setToolTipText(tr("Set WMS layers transparency. Right is opaque, left is transparent."));
+ sliderTrans.setMajorTickSpacing(10);
+ sliderTrans.setMinorTickSpacing(1);
+ sliderTrans.setValue((int)(Float.parseFloat(Main.pref.get("cadastrewms.brightness", "1.0f"))*10));
+ sliderTrans.setPaintTicks(true);
+ sliderTrans.setPaintLabels(false);
+ sliderTrans.setEnabled(transparency.isSelected());
+ cadastrewms.add(sliderTrans, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 250, 0));
+
+ // option to draw boundaries of downloaded data
+ drawBoundaries.setSelected(Main.pref.getBoolean("cadastrewms.drawBoundaries", false));
+ drawBoundaries.setToolTipText(tr("Draw a rectangle around downloaded data from WMS server."));
+ cadastrewms.add(drawBoundaries, GBC.eop().insets(0, 0, 0, 5));
+
+ // separator
+ cadastrewms.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
+
+ // the vectorized images multiplier
+ JLabel jLabelScale = new JLabel(tr("Vector images grab multiplier:"));
+ cadastrewms.add(jLabelScale, GBC.std().insets(0, 5, 10, 0));
+ ButtonGroup bgGrabMultiplier = new ButtonGroup();
+ ActionListener multiplierActionListener = new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ AbstractButton button = (AbstractButton) actionEvent.getSource();
+ grabMultiplier4Size.setEnabled(button == grabMultiplier4);
+ }
+ };
+ grabMultiplier1.setIcon(ImageProvider.get("preferences", "unsel_box_1"));
+ grabMultiplier1.setSelectedIcon(ImageProvider.get("preferences", "sel_box_1"));
+ grabMultiplier1.addActionListener( multiplierActionListener);
+ grabMultiplier2.setIcon(ImageProvider.get("preferences", "unsel_box_2"));
+ grabMultiplier2.setSelectedIcon(ImageProvider.get("preferences", "sel_box_2"));
+ grabMultiplier2.addActionListener( multiplierActionListener);
+ grabMultiplier2.setToolTipText(tr("Grab smaller images (higher quality but use more memory)"));
+ grabMultiplier3.setIcon(ImageProvider.get("preferences", "unsel_box_3"));
+ grabMultiplier3.setSelectedIcon(ImageProvider.get("preferences", "sel_box_3"));
+ grabMultiplier3.addActionListener( multiplierActionListener);
+ grabMultiplier3.setToolTipText(tr("Grab smaller images (higher quality but use more memory)"));
+ grabMultiplier4.setIcon(ImageProvider.get("preferences", "unsel_box_4"));
+ grabMultiplier4.setSelectedIcon(ImageProvider.get("preferences", "sel_box_4"));
+ grabMultiplier4.addActionListener( multiplierActionListener);
+ grabMultiplier4.setToolTipText(tr("Fixed size square (default is 100m)"));
+ bgGrabMultiplier.add(grabMultiplier1);
+ bgGrabMultiplier.add(grabMultiplier2);
+ bgGrabMultiplier.add(grabMultiplier3);
+ bgGrabMultiplier.add(grabMultiplier4);
+ String currentScale = Main.pref.get("cadastrewms.scale", "1");
+ if (currentScale.equals(Scale.X1.value))
+ grabMultiplier1.setSelected(true);
+ if (currentScale.equals(Scale.X2.value))
+ grabMultiplier2.setSelected(true);
+ if (currentScale.equals(Scale.X3.value))
+ grabMultiplier3.setSelected(true);
+ if (currentScale.equals(Scale.SQUARE_100M.value))
+ grabMultiplier4.setSelected(true);
+ cadastrewms.add(grabMultiplier1, GBC.std().insets(5, 0, 5, 0));
+ cadastrewms.add(grabMultiplier2, GBC.std().insets(5, 0, 5, 0));
+ cadastrewms.add(grabMultiplier3, GBC.std().insets(5, 0, 5, 0));
+ cadastrewms.add(grabMultiplier4, GBC.std().insets(5, 0, 5, 0));
+ int squareSize = getNumber("cadastrewms.squareSize", DEFAULT_SQUARE_SIZE);
+ grabMultiplier4Size.setText(String.valueOf(squareSize));
+ grabMultiplier4Size.setToolTipText(tr("Fixed size (from 25 to 1000 meters)"));
+ grabMultiplier4Size.setEnabled(currentScale.equals(Scale.SQUARE_100M.value));
+ cadastrewms.add(grabMultiplier4Size, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 0, 5));
+
+ // separator
+ cadastrewms.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
+
+ // for raster images (not vectorized), image grab divider (from 1 to 10)
+ String savedRasterDivider = Main.pref.get("cadastrewms.rasterDivider", DEFAULT_RASTER_DIVIDER);
+ JLabel jLabelRasterDivider = new JLabel(tr("Raster images grab multiplier:"));
+ rasterDivider.setText(savedRasterDivider);
+ rasterDivider.setToolTipText("Raster image grab division, from 1 to 10; 10 is very high definition");
+ cadastrewms.add(jLabelRasterDivider, GBC.std().insets(0, 5, 10, 0));
+ cadastrewms.add(rasterDivider, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 200, 5));
+ // option to disable image cropping during raster image georeferencing
+ disableImageCropping.setSelected(Main.pref.getBoolean("cadastrewms.noImageCropping", false));
+ disableImageCropping.setToolTipText(tr("Disable image cropping during georeferencing."));
+ cadastrewms.add(disableImageCropping, GBC.eop().insets(0, 0, 0, 0));
+ // the crosspiece display
+ JLabel jLabelCrosspieces = new JLabel(tr("Display crosspieces:"));
+ cadastrewms.add(jLabelCrosspieces, GBC.std().insets(0, 0, 10, 0));
+ ButtonGroup bgCrosspieces = new ButtonGroup();
+ int crosspieces = getNumber("cadastrewms.crosspieces", DEFAULT_CROSSPIECES);
+ if (crosspieces == 0) crosspiece1.setSelected(true);
+ if (crosspieces == 1) crosspiece2.setSelected(true);
+ if (crosspieces == 2) crosspiece3.setSelected(true);
+ bgCrosspieces.add(crosspiece1);
+ bgCrosspieces.add(crosspiece2);
+ bgCrosspieces.add(crosspiece3);
+ cadastrewms.add(crosspiece1, GBC.std().insets(5, 0, 5, 0));
+ cadastrewms.add(crosspiece2, GBC.std().insets(5, 0, 5, 0));
+ cadastrewms.add(crosspiece3, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 0, 5));
+
+ // separator
+ cadastrewms.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL));
+
+ // option to enable automatic caching
+ enableCache.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ jLabelCacheSize.setEnabled(enableCache.isSelected());
+ cacheSize.setEnabled(enableCache.isSelected());
+ }
+ });
+ enableCache.setSelected(Main.pref.getBoolean("cadastrewms.enableCaching", true));
+ enableCache.setToolTipText(tr("Replace the original white background by the backgound color defined in JOSM preferences."));
+ cadastrewms.add(enableCache, GBC.eop().insets(0, 0, 0, 0));
+
+ // option to fix the cache size(in MB)
+ int size = getNumber("cadastrewms.cacheSize", DEFAULT_CACHE_SIZE);
+ cacheSize.setText(String.valueOf(size));
+ cacheSize.setToolTipText(tr("Oldest files are automatically deleted when this size is exceeded"));
+ cadastrewms.add(jLabelCacheSize, GBC.std().insets(20, 0, 0, 0));
+ cadastrewms.add(cacheSize, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 200, 5));
+
+ cadastrewms.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+
+ }
+
+ public boolean ok() {
+ Main.pref.put("cadastrewms.source", sourcing.getText());
+ CadastrePlugin.source = sourcing.getText();
+ Main.pref.put("cadastrewms.alterColors", alterColors.isSelected());
+ Main.pref.put("cadastrewms.invertGrey", reversGrey.isSelected());
+ Main.pref.put("cadastrewms.backgroundTransparent", transparency.isSelected());
+ Main.pref.put("cadastrewms.brightness", Float.toString((float)sliderTrans.getValue()/10));
+ Main.pref.put("cadastrewms.drawBoundaries", drawBoundaries.isSelected());
+ if (grabMultiplier1.isSelected())
+ Main.pref.put("cadastrewms.scale", Scale.X1.toString());
+ else if (grabMultiplier2.isSelected())
+ Main.pref.put("cadastrewms.scale", Scale.X2.toString());
+ else if (grabMultiplier3.isSelected())
+ Main.pref.put("cadastrewms.scale", Scale.X3.toString());
+ else {
+ Main.pref.put("cadastrewms.scale", Scale.SQUARE_100M.toString());
+ try {
+ int squareSize = Integer.parseInt(grabMultiplier4Size.getText());
+ if (squareSize >= 25 && squareSize <= 1000)
+ Main.pref.put("cadastrewms.squareSize", grabMultiplier4Size.getText());
+ } catch (NumberFormatException e) { // ignore the last input
+ }
+ }
+ try {
+ int i = Integer.parseInt(rasterDivider.getText());
+ if (i > 0 && i < 11)
+ Main.pref.put("cadastrewms.rasterDivider", String.valueOf(i));
+ } catch (NumberFormatException e) { // ignore the last input
+ }
+ Main.pref.put("cadastrewms.noImageCropping", disableImageCropping.isSelected());
+ if (crosspiece1.isSelected()) Main.pref.put("cadastrewms.crosspieces", "0");
+ else if (crosspiece2.isSelected()) Main.pref.put("cadastrewms.crosspieces", "1");
+ else if (crosspiece3.isSelected()) Main.pref.put("cadastrewms.crosspieces", "2");
+ Main.pref.put("cadastrewms.enableCaching", enableCache.isSelected());
+
+ // spread data into objects instead of restarting the application
+ try {
+ CacheControl.cacheSize = Integer.parseInt(cacheSize.getText());
+ Main.pref.put("cadastrewms.cacheSize", String.valueOf(CacheControl.cacheSize));
+ } catch (NumberFormatException e) { // ignore the last input
+ }
+ CacheControl.cacheEnabled = enableCache.isSelected();
+ CadastrePlugin.refreshConfiguration();
+ CadastrePlugin.refreshMenu();
+
+ return false;
+ }
+
+ private int getNumber(String pref_parameter, int def_value) {
+ try {
+ return Integer.parseInt(Main.pref.get(pref_parameter, String.valueOf(def_value)));
+ } catch (NumberFormatException e) {
+ return def_value;
+ }
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java b/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java
new file mode 100644
index 0000000..712a5b5
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/CheckSourceUploadHook.java
@@ -0,0 +1,92 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.upload.UploadHook;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.data.APIDataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
+import org.openstreetmap.josm.tools.GBC;
+
+/**
+ * This hook is called at JOSM upload and will check if new nodes and ways provide
+ * a tag "source=". If not and if auto-sourcing is enabled, it will add
+ * automatically a tag "source"="Cadastre..." as defined in the plugin preferences.
+ */
+public class CheckSourceUploadHook implements UploadHook
+{
+ /** Serializable ID */
+ private static final long serialVersionUID = -1;
+
+ /**
+ * Add the tag "source" if it doesn't exist for all new Nodes and Ways before uploading
+ */
+ public boolean checkUpload(APIDataSet apiDataSet)
+ {
+ if (CadastrePlugin.autoSourcing && CadastrePlugin.pluginUsed && !apiDataSet.getPrimitivesToAdd().isEmpty()) {
+ Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
+ for (OsmPrimitive osm : apiDataSet.getPrimitivesToAdd()) {
+ if ((osm instanceof Way && (osm.getKeys().size() == 0 || !tagSourceExist(osm)))
+ || (osm instanceof Node && osm.getKeys().size() > 0 && !tagSourceExist(osm))) {
+ sel.add(osm);
+ }
+ }
+ if (!sel.isEmpty()) {
+ displaySource(sel);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check whenever one of the keys of the object is "source"
+ * @param OsmPrimitive
+ * @return true if one of keys is "source"
+ */
+ private boolean tagSourceExist(OsmPrimitive osm) {
+ for (String key : osm.keySet()) {
+ if (key.equals("source") ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Displays a screen with the list of objects which will be tagged with
+ * source="cadastre.." if it is approved.
+ * @param sel the list of elements added without a key "source"
+ */
+ private void displaySource(Collection<OsmPrimitive> sel)
+ {
+ if (!sel.isEmpty()) {
+ JPanel p = new JPanel(new GridBagLayout());
+ OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
+ p.add(new JLabel(tr("Auto-tag source added:")), GBC.eol());
+ JList l = new JList(sel.toArray());
+ l.setCellRenderer(renderer);
+ l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10);
+ p.add(new JScrollPane(l), GBC.eol().fill());
+ boolean bContinue = JOptionPane.showConfirmDialog(Main.parent, p, tr("Add \"source=...\" to elements?"),
+ JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
+ if (bContinue)
+ Main.main.undoRedo.add(new ChangePropertyCommand(sel, "source", CadastrePlugin.source));
+ }
+
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadSVGBuilding.java b/cadastre-fr/src/cadastre_fr/DownloadSVGBuilding.java
new file mode 100644
index 0000000..ab36189
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadSVGBuilding.java
@@ -0,0 +1,276 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.ProgressInputStream;
+
+public class DownloadSVGBuilding extends PleaseWaitRunnable {
+
+ private WMSLayer wmsLayer;
+ private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+ private CadastreInterface wmsInterface;
+ private String svg = null;
+ private static EastNorthBound currentView = null;
+ private EastNorthBound viewBox = null;
+
+ public DownloadSVGBuilding(WMSLayer wmsLayer) {
+ super(tr("Downloading {0}", wmsLayer.getName()));
+
+ this.wmsLayer = wmsLayer;
+ this.wmsInterface = grabber.getWmsInterface();
+ }
+
+ @Override
+ public void realRun() throws IOException, OsmTransferException {
+ progressMonitor.indeterminateSubTask(tr("Contacting WMS Server..."));
+ try {
+ if (wmsInterface.retrieveInterface(wmsLayer)) {
+ svg = grabBoundary(currentView);
+ if (svg == null)
+ return;
+ getViewBox(svg);
+ if (viewBox == null)
+ return;
+ createBuildings(svg);
+ }
+ } catch (DuplicateLayerException e) {
+ System.err.println("removed a duplicated layer");
+ }
+ }
+
+ @Override
+ protected void cancel() {
+ grabber.getWmsInterface().cancel();
+ }
+
+ @Override
+ protected void finish() {
+ }
+
+ private boolean getViewBox(String svg) {
+ double[] box = new SVGParser().getViewBox(svg);
+ if (box != null) {
+ viewBox = new EastNorthBound(new EastNorth(box[0], box[1]),
+ new EastNorth(box[0]+box[2], box[1]+box[3]));
+ return true;
+ }
+ System.out.println("Unable to parse SVG data (viewBox)");
+ return false;
+ }
+
+ /**
+ * The svg contains more than one commune boundary defined by path elements. So detect
+ * which path element is the best fitting to the viewBox and convert it to OSM objects
+ */
+ private void createBuildings(String svg) {
+ String[] SVGpaths = new SVGParser().getClosedPaths(svg);
+ ArrayList<ArrayList<EastNorth>> eastNorths = new ArrayList<ArrayList<EastNorth>>();
+
+ // convert SVG nodes to eastNorth coordinates
+ for (int i=0; i< SVGpaths.length; i++) {
+ ArrayList<EastNorth> eastNorth = new ArrayList<EastNorth>();
+ createNodes(SVGpaths[i], eastNorth);
+ if (eastNorth.size() > 2)
+ eastNorths.add(eastNorth);
+ }
+
+ // create nodes and closed ways
+ DataSet svgDataSet = new DataSet();
+ for (ArrayList<EastNorth> path : eastNorths) {
+ Way wayToAdd = new Way();
+ for (EastNorth eastNorth : path) {
+ Node nodeToAdd = new Node(Main.proj.eastNorth2latlon(eastNorth));
+ // check if new node is not already created by another new path
+ Node nearestNewNode = checkNearestNode(nodeToAdd, svgDataSet.getNodes());
+ if (nearestNewNode == nodeToAdd)
+ svgDataSet.addPrimitive(nearestNewNode);
+ wayToAdd.addNode(nearestNewNode); // either a new node or an existing one
+ }
+ wayToAdd.addNode(wayToAdd.getNode(0)); // close the way
+ svgDataSet.addPrimitive(wayToAdd);
+ }
+
+ // TODO remove small boxes (4 nodes with less than 1 meter distance)
+ /*
+ for (Way w : svgDataSet.ways)
+ if (w.nodes.size() == 5)
+ for (int i = 0; i < w.nodes.size()-2; i++) {
+ if (w.nodes.get(i).eastNorth.distance(w.nodes.get(i+1).eastNorth))
+ }*/
+
+ // simplify ways and check if we can reuse existing OSM nodes
+ for (Way wayToAdd : svgDataSet.getWays())
+ new SimplifyWay().simplifyWay(wayToAdd, svgDataSet, 0.5);
+ // check if the new way or its nodes is already in OSM layer
+ for (Node n : svgDataSet.getNodes()) {
+ Node nearestNewNode = checkNearestNode(n, Main.main.getCurrentDataSet().getNodes());
+ if (nearestNewNode != n) {
+ // replace the SVG node by the OSM node
+ for (Way w : svgDataSet.getWays()) {
+ int replaced = 0;
+ for (Node node : w.getNodes())
+ if (node == n) {
+ node = nearestNewNode;
+ replaced++;
+ }
+ if (w.getNodesCount() == replaced)
+ w.setDeleted(true);
+ }
+ n.setDeleted(true);
+ }
+
+ }
+
+ Collection<Command> cmds = new LinkedList<Command>();
+ for (Node node : svgDataSet.getNodes())
+ if (!node.isDeleted())
+ cmds.add(new AddCommand(node));
+ for (Way way : svgDataSet.getWays())
+ if (!way.isDeleted())
+ cmds.add(new AddCommand(way));
+ Main.main.undoRedo.add(new SequenceCommand(tr("Create buildings"), cmds));
+ Main.map.repaint();
+ }
+
+ private void createNodes(String SVGpath, ArrayList<EastNorth> eastNorth) {
+ // looks like "M981283.38 368690.15l143.81 72.46 155.86 ..."
+ String[] coor = SVGpath.split("[MlZ ]"); //coor[1] is x, coor[2] is y
+ double dx = Double.parseDouble(coor[1]);
+ double dy = Double.parseDouble(coor[2]);
+ for (int i=3; i<coor.length; i+=2){
+ if (coor[i].equals("")) {
+ eastNorth.clear(); // some paths are just artifacts
+ return;
+ }
+ double east = dx+=Double.parseDouble(coor[i]);
+ double north = dy+=Double.parseDouble(coor[i+1]);
+ eastNorth.add(new EastNorth(east,north));
+ }
+ // flip the image (svg using a reversed Y coordinate system)
+ double pivot = viewBox.min.getY() + (viewBox.max.getY() - viewBox.min.getY()) / 2;
+ for (EastNorth en : eastNorth) {
+ en.setLocation(en.east(), 2 * pivot - en.north());
+ }
+ return;
+ }
+
+ /**
+ * Check if node can be reused.
+ * @param nodeToAdd the candidate as new node
+ * @return the already existing node (if any), otherwise the new node candidate.
+ */
+ private Node checkNearestNode(Node nodeToAdd, Collection<Node> nodes) {
+ double epsilon = 0.05; // smallest distance considering duplicate node
+ for (Node n : nodes) {
+ if (!n.isDeleted() && !n.incomplete) {
+ double dist = n.getEastNorth().distance(nodeToAdd.getEastNorth());
+ if (dist < epsilon) {
+ return n;
+ }
+ }
+ }
+ return nodeToAdd;
+ }
+
+ private String grabBoundary(EastNorthBound bbox) throws IOException, OsmTransferException {
+ try {
+ URL url = null;
+ url = getURLsvg(bbox);
+ return grabSVG(url);
+ } catch (MalformedURLException e) {
+ throw (IOException) new IOException(tr("CadastreGrabber: Illegal url.")).initCause(e);
+ }
+ }
+
+ private URL getURLsvg(EastNorthBound bbox) throws MalformedURLException {
+ String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+ str += "&layers=";
+ str += "CDIF:LS2";
+ str += "&format=image/svg";
+ str += "&bbox="+bbox.min.east()+",";
+ str += bbox.min.north() + ",";
+ str += bbox.max.east() + ",";
+ str += bbox.max.north();
+ str += "&width=800&height=600"; // maximum allowed by wms server
+ str += "&exception=application/vnd.ogc.se_inimage";
+ str += "&styles=";
+ str += "LS2_90";
+ System.out.println("URL="+str);
+ return new URL(str.replace(" ", "%20"));
+ }
+
+ private String grabSVG(URL url) throws IOException, OsmTransferException {
+ wmsInterface.urlConn = (HttpURLConnection)url.openConnection();
+ wmsInterface.urlConn.setRequestMethod("GET");
+ wmsInterface.setCookie();
+ InputStream is = new ProgressInputStream(wmsInterface.urlConn, NullProgressMonitor.INSTANCE);
+ File file = new File(CadastrePlugin.cacheDir + "building.svg");
+ String svg = new String();
+ try {
+ if (file.exists())
+ file.delete();
+ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, true));
+ InputStreamReader isr =new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line="";
+ while ( null!=(line=br.readLine())){
+ line += "\n";
+ bos.write(line.getBytes());
+ svg += line;
+ }
+ bos.close();
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ }
+ is.close();
+ return svg;
+ }
+
+ public static void download(WMSLayer wmsLayer) {
+ MapView mv = Main.map.mapView;
+ currentView = new EastNorthBound(mv.getEastNorth(0, mv.getHeight()),
+ mv.getEastNorth(mv.getWidth(), 0));
+ if ((currentView.max.east() - currentView.min.east()) > 1000 ||
+ (currentView.max.north() - currentView.min.north() > 1000)) {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("To avoid cadastre WMS overload,\nbuilding import size is limited to 1 km2 max."));
+ return;
+ }
+ if (CadastrePlugin.autoSourcing == false) {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("Please, enable auto-sourcing and check cadastre millesime."));
+ return;
+ }
+ Main.worker.execute(new DownloadSVGBuilding(wmsLayer));
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadSVGTask.java b/cadastre-fr/src/cadastre_fr/DownloadSVGTask.java
new file mode 100644
index 0000000..bba3fcb
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadSVGTask.java
@@ -0,0 +1,222 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.ProgressInputStream;
+/**
+ * Grab the SVG administrative boundaries of the active commune layer (cadastre),
+ * isolate the SVG path of the concerned commune (other municipalities are also
+ * downloaded in the SVG data), convert to OSM nodes and way plus simplify.
+ * Thanks to Frederic Rodrigo for his help.
+ */
+public class DownloadSVGTask extends PleaseWaitRunnable {
+
+ private WMSLayer wmsLayer;
+ private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+ private CadastreInterface wmsInterface;
+ private String svg = null;
+ private EastNorthBound viewBox = null;
+
+ public DownloadSVGTask(WMSLayer wmsLayer) {
+ super(tr("Downloading {0}", wmsLayer.getName()));
+
+ this.wmsLayer = wmsLayer;
+ this.wmsInterface = grabber.getWmsInterface();
+ }
+
+ @Override
+ public void realRun() throws IOException, OsmTransferException {
+ progressMonitor.indeterminateSubTask(tr("Contacting WMS Server..."));
+ try {
+ if (wmsInterface.retrieveInterface(wmsLayer)) {
+ svg = grabBoundary(wmsLayer.getCommuneBBox());
+ if (svg == null)
+ return;
+ progressMonitor.indeterminateSubTask(tr("Extract SVG ViewBox..."));
+ getViewBox(svg);
+ if (viewBox == null)
+ return;
+ progressMonitor.indeterminateSubTask(tr("Extract best fitting boundary..."));
+ createWay(svg);
+ }
+ } catch (DuplicateLayerException e) {
+ System.err.println("removed a duplicated layer");
+ }
+ }
+
+ @Override
+ protected void cancel() {
+ grabber.getWmsInterface().cancel();
+ }
+
+ @Override
+ protected void finish() {
+ }
+
+ private boolean getViewBox(String svg) {
+ double[] box = new SVGParser().getViewBox(svg);
+ if (box != null) {
+ viewBox = new EastNorthBound(new EastNorth(box[0], box[1]),
+ new EastNorth(box[0]+box[2], box[1]+box[3]));
+ return true;
+ }
+ System.out.println("Unable to parse SVG data (viewBox)");
+ return false;
+ }
+
+ /**
+ * The svg contains more than one commune boundary defined by path elements. So detect
+ * which path element is the best fitting to the viewBox and convert it to OSM objects
+ */
+ private void createWay(String svg) {
+ String[] SVGpaths = new SVGParser().getClosedPaths(svg);
+ ArrayList<Double> fitViewBox = new ArrayList<Double>();
+ ArrayList<ArrayList<EastNorth>> eastNorths = new ArrayList<ArrayList<EastNorth>>();
+ for (int i=0; i< SVGpaths.length; i++) {
+ ArrayList<EastNorth> eastNorth = new ArrayList<EastNorth>();
+ fitViewBox.add( createNodes(SVGpaths[i], eastNorth) );
+ eastNorths.add(eastNorth);
+ }
+ // the smallest fitViewBox indicates the best fitting path in viewBox
+ Double min = Collections.min(fitViewBox);
+ int bestPath = fitViewBox.indexOf(min);
+ List<Node> nodeList = new ArrayList<Node>();
+ for (EastNorth eastNorth : eastNorths.get(bestPath)) {
+ nodeList.add(new Node(Main.proj.eastNorth2latlon(eastNorth)));
+ }
+ Way wayToAdd = new Way();
+ Collection<Command> cmds = new LinkedList<Command>();
+ for (Node node : nodeList) {
+ cmds.add(new AddCommand(node));
+ wayToAdd.addNode(node);
+ }
+ wayToAdd.addNode(wayToAdd.getNode(0)); // close the circle
+
+ // simplify the way
+ double threshold = Double.parseDouble(Main.pref.get("cadastrewms.simplify-way-boundary", "1.0"));
+ new SimplifyWay().simplifyWay(wayToAdd, Main.main.getCurrentDataSet(), threshold);
+
+ cmds.add(new AddCommand(wayToAdd));
+ Main.main.undoRedo.add(new SequenceCommand(tr("Create boundary"), cmds));
+ Main.map.repaint();
+ }
+
+ private double createNodes(String SVGpath, ArrayList<EastNorth> eastNorth) {
+ // looks like "M981283.38 368690.15l143.81 72.46 155.86 ..."
+ String[] coor = SVGpath.split("[MlZ ]"); //coor[1] is x, coor[2] is y
+ double dx = Double.parseDouble(coor[1]);
+ double dy = Double.parseDouble(coor[2]);
+ double minY = Double.MAX_VALUE;
+ double minX = Double.MAX_VALUE;
+ double maxY = Double.MIN_VALUE;
+ double maxX = Double.MIN_VALUE;
+ for (int i=3; i<coor.length; i+=2){
+ double east = dx+=Double.parseDouble(coor[i]);
+ double north = dy+=Double.parseDouble(coor[i+1]);
+ eastNorth.add(new EastNorth(east,north));
+ minX = minX > east ? east : minX;
+ minY = minY > north ? north : minY;
+ maxX = maxX < east ? east : maxX;
+ maxY = maxY < north ? north : maxY;
+ }
+ // flip the image (svg using a reversed Y coordinate system)
+ double pivot = viewBox.min.getY() + (viewBox.max.getY() - viewBox.min.getY()) / 2;
+ for (EastNorth en : eastNorth) {
+ en.setLocation(en.east(), 2 * pivot - en.north());
+ }
+ return Math.abs(minX - viewBox.min.getX())+Math.abs(maxX - viewBox.max.getX())
+ +Math.abs(minY - viewBox.min.getY())+Math.abs(maxY - viewBox.max.getY());
+ }
+
+ private String grabBoundary(EastNorthBound bbox) throws IOException, OsmTransferException {
+ try {
+ URL url = null;
+ url = getURLsvg(bbox);
+ return grabSVG(url);
+ } catch (MalformedURLException e) {
+ throw (IOException) new IOException(tr("CadastreGrabber: Illegal url.")).initCause(e);
+ }
+ }
+
+ private URL getURLsvg(EastNorthBound bbox) throws MalformedURLException {
+ String str = new String(wmsInterface.baseURL+"/scpc/wms?version=1.1&request=GetMap");
+ str += "&layers=";
+ str += "CDIF:COMMUNE";
+ str += "&format=image/svg";
+ str += "&bbox="+bbox.min.east()+",";
+ str += bbox.min.north() + ",";
+ str += bbox.max.east() + ",";
+ str += bbox.max.north();
+ str += "&width=800&height=600"; // maximum allowed by wms server
+ str += "&styles=";
+ str += "COMMUNE_90";
+ System.out.println("URL="+str);
+ return new URL(str.replace(" ", "%20"));
+ }
+
+ private String grabSVG(URL url) throws IOException, OsmTransferException {
+ wmsInterface.urlConn = (HttpURLConnection)url.openConnection();
+ wmsInterface.urlConn.setRequestMethod("GET");
+ wmsInterface.setCookie();
+ InputStream is = new ProgressInputStream(wmsInterface.urlConn, NullProgressMonitor.INSTANCE);
+ File file = new File(CadastrePlugin.cacheDir + "boundary.svg");
+ String svg = new String();
+ try {
+ if (file.exists())
+ file.delete();
+ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, true));
+ InputStreamReader isr =new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line="";
+ while ( null!=(line=br.readLine())){
+ line += "\n";
+ bos.write(line.getBytes());
+ svg += line;
+ }
+ bos.close();
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ }
+ is.close();
+ return svg;
+ }
+
+ public static void download(WMSLayer wmsLayer) {
+ if (CadastrePlugin.autoSourcing == false) {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("Please, enable auto-sourcing and check cadastre millesime."));
+ return;
+ }
+ Main.worker.execute(new DownloadSVGTask(wmsLayer));
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java b/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java
new file mode 100644
index 0000000..adb0d6d
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java
@@ -0,0 +1,122 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+
+public class DownloadWMSPlanImage {
+
+ private Future<Task> task = null;
+ private WMSLayer wmsLayer;
+ private Bounds bounds;
+ private boolean dontGeoreference = false;
+
+ private class Task extends PleaseWaitRunnable {
+ private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+ public Task(WMSLayer wmsLayer, Bounds bounds) {
+ super(tr("Downloading {0}", wmsLayer.getName()));
+ }
+
+ @Override
+ public void realRun() throws IOException {
+ progressMonitor.indeterminateSubTask(tr("Contacting cadastre WMS ..."));
+ try {
+ if (grabber.getWmsInterface().retrieveInterface(wmsLayer)) {
+ if (!wmsLayer.images.isEmpty()) {
+ //JOptionPane.showMessageDialog(Main.parent,tr("Image already loaded"));
+ JOptionPane pane = new JOptionPane(
+ tr("Image already loaded")
+ , JOptionPane.INFORMATION_MESSAGE);
+ // this below is a temporary workaround to fix the "always on top" issue
+ JDialog dialog = pane.createDialog(Main.parent, "");
+ CadastrePlugin.prepareDialog(dialog);
+ dialog.setVisible(true);
+ // till here
+ dontGeoreference = true;
+ } else if (grabber.getWmsInterface().downloadCancelled){
+ // do nothing
+ } else {
+ // first time we grab an image for this layer
+ if (CacheControl.cacheEnabled) {
+ if (wmsLayer.getCacheControl().loadCacheIfExist()) {
+ dontGeoreference = true;
+ Main.map.mapView.repaint();
+ return;
+ }
+ }
+ if (wmsLayer.isRaster()) {
+ // set raster image commune bounding box based on current view (before adjustment)
+ grabber.getWmsInterface().retrieveCommuneBBox(wmsLayer);
+ wmsLayer.setRasterBounds(bounds);
+ // grab new images from wms server into active layer
+ wmsLayer.grab(grabber, bounds);
+ if (grabber.getWmsInterface().downloadCancelled) {
+ wmsLayer.images.clear();
+ Main.map.mapView.repaint();
+ } else {
+ // next steps follow in method finish() when download is terminated
+ wmsLayer.joinRasterImages();
+ }
+ } else {
+ /*JOptionPane.showMessageDialog(Main.parent,tr("Municipality vectorized !\n"+
+ "Use the normal Cadastre Grab menu."));*/
+ JOptionPane pane = new JOptionPane(
+ tr("Municipality vectorized !\nUse the normal Cadastre Grab menu.")
+ , JOptionPane.INFORMATION_MESSAGE);
+ // this below is a temporary workaround to fix the "always on top" issue
+ JDialog dialog = pane.createDialog(Main.parent, "");
+ CadastrePlugin.prepareDialog(dialog);
+ dialog.setVisible(true);
+ // till here
+ }
+ }
+ }
+ } catch (DuplicateLayerException e) {
+ // we tried to grab onto a duplicated layer (removed)
+ System.err.println("removed a duplicated layer");
+ }
+ }
+
+ @Override
+ protected void cancel() {
+ grabber.getWmsInterface().cancel();
+ dontGeoreference = true;
+ }
+
+ @Override
+ protected void finish() {
+ }
+ }
+
+ public void download(WMSLayer wmsLayer) {
+ MapView mv = Main.map.mapView;
+ Bounds bounds = new Bounds(mv.getLatLon(0, mv.getHeight()), mv.getLatLon(mv.getWidth(), 0));
+
+ //Main.worker.execute(new DownloadWMSPlanImage(wmsLayer, bounds));
+ Task t = new Task(wmsLayer, bounds);
+ this.wmsLayer = wmsLayer;
+ this.bounds = bounds;
+ task = Main.worker.submit(t, t);
+ }
+
+ public boolean waitFinished() {
+ if (task != null) {
+ try {
+ task.get();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return dontGeoreference;
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/DownloadWMSVectorImage.java b/cadastre-fr/src/cadastre_fr/DownloadWMSVectorImage.java
new file mode 100644
index 0000000..9bed6c8
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DownloadWMSVectorImage.java
@@ -0,0 +1,82 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+
+public class DownloadWMSVectorImage extends PleaseWaitRunnable {
+
+ private WMSLayer wmsLayer;
+
+ private Bounds bounds;
+
+ private CadastreGrabber grabber = CadastrePlugin.cadastreGrabber;
+
+ public DownloadWMSVectorImage(WMSLayer wmsLayer, Bounds bounds) {
+ super(tr("Downloading {0}", wmsLayer.getName()));
+
+ this.wmsLayer = wmsLayer;
+ this.bounds = bounds;
+ }
+
+ @Override
+ public void realRun() throws IOException {
+ progressMonitor.indeterminateSubTask(tr("Contacting WMS Server..."));
+ try {
+ if (grabber.getWmsInterface().retrieveInterface(wmsLayer)) {
+ boolean useFactor = true;
+ if (wmsLayer.images.isEmpty()) {
+ // first time we grab an image for this layer
+ if (CacheControl.cacheEnabled) {
+ if (wmsLayer.getCacheControl().loadCacheIfExist()) {
+ Main.map.mapView.zoomTo(wmsLayer.getCommuneBBox().toBounds());
+ //Main.map.mapView.repaint();
+ return;
+ }
+ }
+ if (wmsLayer.isRaster()) {
+ // set raster image commune bounding box based on current view (before adjustment)
+ wmsLayer.setRasterBounds(bounds);
+ } else {
+ // set vectorized commune bounding box by opening the standard web window
+ grabber.getWmsInterface().retrieveCommuneBBox(wmsLayer);
+ // if it is the first layer, use the communeBBox as grab bbox (and not divided)
+ if (Main.map.mapView.getAllLayers().size() == 1 ) {
+ bounds = wmsLayer.getCommuneBBox().toBounds();
+ Main.map.mapView.zoomTo(bounds);
+ useFactor = false;
+ }
+ }
+ }
+ // grab new images from wms server into active layer
+ wmsLayer.grab(grabber, bounds, useFactor);
+ }
+ } catch (DuplicateLayerException e) {
+ // we tried to grab onto a duplicated layer (removed)
+ System.err.println("removed a duplicated layer");
+ }
+ }
+
+ @Override
+ protected void cancel() {
+ grabber.getWmsInterface().cancel();
+ }
+
+ @Override
+ protected void finish() {
+ }
+
+ public static void download(WMSLayer wmsLayer) {
+ MapView mv = Main.map.mapView;
+ Bounds bounds = new Bounds(mv.getLatLon(0, mv.getHeight()), mv.getLatLon(mv.getWidth(), 0));
+
+ Main.worker.execute(new DownloadWMSVectorImage(wmsLayer, bounds));
+
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java b/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java
new file mode 100644
index 0000000..22e491f
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/DuplicateLayerException.java
@@ -0,0 +1,5 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+class DuplicateLayerException extends Exception {
+ private static final long serialVersionUID = 1L;}
diff --git a/cadastre-fr/src/cadastre_fr/EastNorthBound.java b/cadastre-fr/src/cadastre_fr/EastNorthBound.java
new file mode 100644
index 0000000..d0e051c
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/EastNorthBound.java
@@ -0,0 +1,42 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.io.Serializable;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.EastNorth;
+
+public class EastNorthBound implements Serializable {
+
+ private static final long serialVersionUID = 8451650309216472069L;
+
+ public EastNorth min, max;
+ public EastNorthBound(EastNorth min, EastNorth max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ public boolean contains(EastNorth eastNorth) {
+ if (eastNorth.east() < min.east() || eastNorth.north() < min.north())
+ return false;
+ if (eastNorth.east() > max.east() || eastNorth.north() > max.north())
+ return false;
+ return true;
+ }
+
+ public EastNorthBound interpolate(EastNorthBound en2, double proportion) {
+ EastNorthBound enb = new EastNorthBound(this.min.interpolate(en2.min, proportion),
+ this.max.interpolate(en2.max, proportion));
+ return enb;
+ }
+
+ public Bounds toBounds() {
+ return new Bounds(Main.proj.eastNorth2latlon(min), Main.proj.eastNorth2latlon(max));
+ }
+
+ @Override public String toString() {
+ return "EastNorthBound[" + min.east() + "," + min.north() + "," + max.east() + "," + max.north() + "]";
+ }
+}
+
diff --git a/cadastre-fr/src/cadastre_fr/GeorefImage.java b/cadastre-fr/src/cadastre_fr/GeorefImage.java
new file mode 100644
index 0000000..a24918f
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/GeorefImage.java
@@ -0,0 +1,233 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Point;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+
+public class GeorefImage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public EastNorth min;
+ public EastNorth max;
+ public BufferedImage image;
+
+ private double pixelPerEast;
+ private double pixelPerNorth;
+
+ public GeorefImage(BufferedImage img, EastNorth min, EastNorth max) {
+ image = img;
+ this.min = min;
+ this.max = max;
+ updatePixelPer();
+ }
+
+ public static GraphicsConfiguration getDefaultConfiguration() {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice gd = ge.getDefaultScreenDevice();
+ return gd.getDefaultConfiguration();
+ }
+
+ private void getNewBounding(EastNorth min, EastNorth max, EastNorth c, EastNorth d) {
+ EastNorth pt[] = new EastNorth[4];
+ pt[0] = min;
+ pt[1] = max;
+ pt[2] = c;
+ pt[3] = d;
+ double smallestEast = Double.MAX_VALUE;
+ double smallestNorth = Double.MAX_VALUE;
+ double highestEast = Double.MIN_VALUE;
+ double highestNorth = Double.MIN_VALUE;
+ for(int i=0; i<=3; i++) {
+ smallestEast = Math.min(pt[i].east(), smallestEast);
+ smallestNorth = Math.min(pt[i].north(), smallestNorth);
+ highestEast = Math.max(pt[i].east(), highestEast);
+ highestNorth = Math.max(pt[i].north(), highestNorth);
+ }
+ min.setLocation(smallestEast, smallestNorth);
+ max.setLocation(highestEast, highestNorth);
+ }
+
+ public boolean contains(EastNorth en) {
+ return min.east() <= en.east() && en.east() <= max.east() && min.north() <= en.north()
+ && en.north() <= max.north();
+ }
+
+ public void paint(Graphics2D g, NavigatableComponent nc, boolean backgroundTransparent, float transparency,
+ boolean drawBoundaries) {
+ if (image == null || min == null || max == null)
+ return;
+
+ Point minPt = nc.getPoint(min), maxPt = nc.getPoint(max);
+
+ if (!g.hitClip(minPt.x, maxPt.y, maxPt.x - minPt.x, minPt.y - maxPt.y))
+ return;
+
+ if (backgroundTransparent && transparency < 1.0f)
+ g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparency));
+ if (drawBoundaries) {
+ g.setColor(Color.green);
+ g.drawRect(minPt.x, maxPt.y, maxPt.x - minPt.x, minPt.y - maxPt.y);
+ }
+ g.drawImage(image, minPt.x, maxPt.y, maxPt.x, minPt.y, // dest
+ 0, 0, image.getWidth(), image.getHeight(), // src
+ null);
+ if (backgroundTransparent && transparency < 1.0f)
+ g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
+ }
+
+ /**
+ * Is the given bbox overlapping this image ?
+ */
+ public boolean overlap(GeorefImage georefImage) {
+ if (this.contains(georefImage.min) || this.contains(georefImage.max))
+ return true;
+ if (this.contains(new EastNorth(georefImage.min.east(), georefImage.max.north()))
+ || this.contains(new EastNorth(georefImage.max.east(), georefImage.min.north())))
+ return true;
+ return false;
+ }
+
+ /**
+ * Make all pixels masked by the given georefImage transparent in this image
+ *
+ * @param georefImage
+ */
+ public void withdraw(GeorefImage georefImage) {
+ double minMaskEast = (georefImage.min.east() > this.min.east()) ? georefImage.min.east() : this.min.east();
+ double maxMaskEast = (georefImage.max.east() < this.max.east()) ? georefImage.max.east() : this.max.east();
+ double minMaskNorth = (georefImage.min.north() > this.min.north()) ? georefImage.min.north() : this.min.north();
+ double maxMaskNorth = (georefImage.max.north() < this.max.north()) ? georefImage.max.north() : this.max.north();
+ if ((maxMaskNorth - minMaskNorth) > 0 && (maxMaskEast - minMaskEast) > 0) {
+ double pixelPerEast = (max.east() - min.east()) / image.getWidth();
+ double pixelPerNorth = (max.north() - min.north()) / image.getHeight();
+ int minXMaskPixel = (int) ((minMaskEast - min.east()) / pixelPerEast);
+ int minYMaskPixel = (int) ((max.north() - maxMaskNorth) / pixelPerNorth);
+ int widthXMaskPixel = Math.abs((int) ((maxMaskEast - minMaskEast) / pixelPerEast));
+ int heightYMaskPixel = Math.abs((int) ((maxMaskNorth - minMaskNorth) / pixelPerNorth));
+ Graphics g = image.getGraphics();
+ for (int x = minXMaskPixel; x < minXMaskPixel + widthXMaskPixel; x++)
+ for (int y = minYMaskPixel; y < minYMaskPixel + heightYMaskPixel; y++)
+ image.setRGB(x, y, VectorImageModifier.cadastreBackgroundTransp);
+ g.dispose();
+ }
+ }
+
+ /**
+ * Method required by BufferedImage serialization.
+ * Save only primitives to keep cache independent of software changes.
+ */
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ max = new EastNorth(in.readDouble(), in.readDouble());
+ min = new EastNorth(in.readDouble(), in.readDouble());
+ image = (BufferedImage) ImageIO.read(ImageIO.createImageInputStream(in));
+ updatePixelPer();
+ }
+
+ /**
+ * Method required by BufferedImage serialization.
+ * Cache uses only primitives to stay independent of software changes.
+ */
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.writeDouble(max.getX());
+ out.writeDouble(max.getY());
+ out.writeDouble(min.getX());
+ out.writeDouble(min.getY());
+ ImageIO.write(image, "png", ImageIO.createImageOutputStream(out));
+ }
+
+ private void updatePixelPer() {
+ pixelPerEast = image.getWidth()/(max.east()-min.east());
+ pixelPerNorth = image.getHeight()/(max.north()-min.north());
+ }
+
+ public double getPixelPerEast() {
+ return pixelPerEast;
+ }
+
+ public double getPixelPerNorth() {
+ return pixelPerNorth;
+ }
+
+ @Override
+ public String toString() {
+ return "GeorefImage[min=" + min + ", max=" + max + ", image" + image + "]";
+ }
+
+ /*
+ * Following methods are used for affine transformation of two points p1 and p2
+ */
+ /**
+ * Add a translation (dx, dy) to this image min,max coordinates
+ * @param dx delta added to X image coordinate
+ * @param dy delta added to Y image coordinate
+ */
+ public void shear(double dx, double dy) {
+ min = new EastNorth(min.east() + dx, min.north() + dy);
+ max = new EastNorth(max.east() + dx, max.north() + dy);
+ }
+
+ /**
+ * Change this image scale by moving the min,max coordinates around an anchor
+ * @param anchor
+ * @param proportion
+ */
+ public void scale(EastNorth anchor, double proportion) {
+ min = anchor.interpolate(min, proportion);
+ max = anchor.interpolate(max, proportion);
+ updatePixelPer();
+ }
+
+ /**
+ * Rotate this image and its min/max coordinates around anchor point
+ * @param anchor anchor of rotation
+ * @param angle angle of rotation (in radians)
+ */
+ public void rotate(EastNorth anchor, double angle) {
+ EastNorth min2 = new EastNorth(min.east(), max.north());
+ EastNorth max2 = new EastNorth(max.east(), min.north());
+ min = min.rotate(anchor, angle);
+ max = max.rotate(anchor, angle);
+ min2 = min2.rotate(anchor, angle);
+ max2 = max2.rotate(anchor, angle);
+ getNewBounding(min, max, min2, max2);
+ image = tilt(image, angle);
+ }
+
+ /**
+ * Rotate by copying original buffered image into a new one with new dimensions
+ * @param image
+ * @param angle
+ * @return
+ */
+ public static BufferedImage tilt(BufferedImage image, double angle) {
+ double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle));
+ int w = image.getWidth(), h = image.getHeight();
+ int neww = (int)Math.floor(w*cos+h*sin), newh = (int)Math.floor(h*cos+w*sin);
+ GraphicsConfiguration gc = getDefaultConfiguration();
+ BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT);
+ Graphics2D g = result.createGraphics();
+ g.translate((neww-w)/2, (newh-h)/2);
+ g.rotate(angle, w/2, h/2);
+ g.drawRenderedImage(image, null);
+ g.dispose();
+ return result;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/ImageModifier.java b/cadastre-fr/src/cadastre_fr/ImageModifier.java
new file mode 100644
index 0000000..a0c86c7
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/ImageModifier.java
@@ -0,0 +1,16 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.image.BufferedImage;
+
+public abstract class ImageModifier {
+ /**
+ * Current background color used by cadastre.gouv.fr
+ */
+ //public static int cadastreBackgroundTransp = 1; // original white but transparent
+
+ private static final long serialVersionUID = 1L;
+
+ public BufferedImage bufferedImage;
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionBoundaries.java b/cadastre-fr/src/cadastre_fr/MenuActionBoundaries.java
new file mode 100644
index 0000000..74dd071
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionBoundaries.java
@@ -0,0 +1,37 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionBoundaries extends JosmAction {
+
+ public static String name = "Administrative boundary";
+
+ private static final long serialVersionUID = 1L;
+ private WMSLayer wmsLayer = null;
+
+ public MenuActionBoundaries() {
+ super(tr(name), "cadastre_small", tr("Extract commune boundary"), null, false);
+ }
+
+ public void actionPerformed(ActionEvent arg0) {
+ wmsLayer = WMSDownloadAction.getLayer();
+ if (wmsLayer != null) {
+ if (wmsLayer.isRaster()) {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("Only on vectorized layers"), tr("Error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ DownloadSVGTask.download(wmsLayer);
+ }
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionBuildings.java b/cadastre-fr/src/cadastre_fr/MenuActionBuildings.java
new file mode 100644
index 0000000..857885b
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionBuildings.java
@@ -0,0 +1,37 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionBuildings extends JosmAction {
+
+ public static String name = "Building footprints";
+
+ private static final long serialVersionUID = 1L;
+ private WMSLayer wmsLayer = null;
+
+ public MenuActionBuildings() {
+ super(tr(name), "cadastre_small", tr("Extract building footprints"), null, false);
+ }
+
+ public void actionPerformed(ActionEvent arg0) {
+ wmsLayer = WMSDownloadAction.getLayer();
+ if (wmsLayer != null) {
+ if (wmsLayer.isRaster()) {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("Only on vectorized layers"), tr("Error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ DownloadSVGBuilding.download(wmsLayer);
+ }
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionGrab.java b/cadastre-fr/src/cadastre_fr/MenuActionGrab.java
new file mode 100644
index 0000000..24028d4
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionGrab.java
@@ -0,0 +1,46 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class MenuActionGrab extends JosmAction {
+
+ /**
+ * Action calling the wms grabber for cadastre.gouv.fr
+ */
+ private static final long serialVersionUID = 1L;
+
+ public static String name = "Cadastre grab";
+
+ public MenuActionGrab() {
+ super(tr(name), "cadastre_small", tr("Download Image from French Cadastre WMS"),
+ Shortcut.registerShortcut("cadastre:grab", tr("Cadastre: {0}", tr("Download Image from French Cadastre WMS")),
+ KeyEvent.VK_F11, Shortcut.GROUP_DIRECT), false);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (Main.map != null) {
+ if (CadastrePlugin.isCadastreProjection()) {
+ WMSLayer wmsLayer = WMSDownloadAction.getLayer();
+ if (wmsLayer != null)
+ DownloadWMSVectorImage.download(wmsLayer);
+ } else {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("To enable the cadastre WMS plugin, change\n"
+ + "the current projection to one of the cadastre\n"
+ + "projections and retry"));
+ }
+ } else
+ new MenuActionNewLocation().actionPerformed(e);
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java b/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java
new file mode 100644
index 0000000..506d555
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java
@@ -0,0 +1,348 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.ArrayList;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+
+public class MenuActionGrabPlanImage extends JosmAction implements Runnable, MouseListener {
+
+ /**
+ * Action calling the wms grabber for non georeferenced images called "plan image"
+ */
+ private static final long serialVersionUID = 1L;
+
+ public static String name = "Georeference an image";
+
+ private DownloadWMSPlanImage downloadWMSPlanImage;
+ private WMSLayer wmsLayer;
+ private int countMouseClicked = 0;
+ private int mode = 0;
+ private int cGetCorners = 1;
+ private int cGetLambertCrosspieces = 2;
+ private EastNorth ea1;
+ private long mouseClickedTime = 0;
+ private EastNorth georefpoint1;
+ private EastNorth georefpoint2;
+ /**
+ * The time which needs to pass between two clicks during georeferencing, in milliseconds
+ */
+ private int initialClickDelay;
+
+ public MenuActionGrabPlanImage() {
+ super(tr(name), "cadastre_small", tr("Grab non-georeferenced image"), null, false);
+ }
+
+ public void actionCompleted() {
+ countMouseClicked = 0;
+ mode = 0;
+ mouseClickedTime = System.currentTimeMillis();
+ }
+
+ public void actionInterrupted() {
+ actionCompleted();
+ wmsLayer = null;
+ }
+
+ @Override
+ protected void updateEnabledState() {
+ if (wmsLayer == null || Main.map == null || Main.map.mapView == null) return;
+ if (countMouseClicked == 0 && mode == 0) return;
+ for (Layer l : Main.map.mapView.getAllLayersAsList())
+ if (l == wmsLayer)
+ return;
+ JOptionPane.showMessageDialog(Main.parent, tr("Georeferencing interrupted"));
+ actionInterrupted();
+ }
+
+ public void actionPerformed(ActionEvent ae) {
+ if (Main.map != null) {
+ if (CadastrePlugin.isCadastreProjection()) {
+ //wmsLayer = WMSDownloadAction.getLayer();
+ wmsLayer = new MenuActionNewLocation().addNewLayer(new ArrayList<WMSLayer>());
+ if (wmsLayer == null) return;
+ downloadWMSPlanImage = new DownloadWMSPlanImage();
+ downloadWMSPlanImage.download(wmsLayer);
+ initialClickDelay = Main.pref.getInteger("cadastrewms.georef-click-delay",200);
+ // download sub-images of the cadastre scan and join them into one single
+ Main.worker.execute(this);
+ } else {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("To enable the cadastre WMS plugin, change\n"
+ + "the current projection to one of the cadastre\n"
+ + "projections and retry"));
+ }
+ }
+ }
+
+ public void run() {
+ // wait until plan image is fully loaded and joined into one single image
+ boolean loadedFromCache = downloadWMSPlanImage.waitFinished();
+ if (wmsLayer.images.size() == 1 && !loadedFromCache) {
+ int reply = JOptionPane.CANCEL_OPTION;
+ if (wmsLayer.isAlreadyGeoreferenced()) {
+ reply = JOptionPane.showConfirmDialog(null,
+ tr("This image contains georeference data.\n"+
+ "Do you want to use them ?"),
+ null,
+ JOptionPane.YES_NO_OPTION);
+ }
+ if (reply == JOptionPane.OK_OPTION) {
+ transformGeoreferencedImg();
+ } else {
+ mouseClickedTime = System.currentTimeMillis();
+ Main.map.mapView.addMouseListener(this);
+ if (Main.pref.getBoolean("cadastrewms.noImageCropping", false) == false)
+ startCropping();
+ else
+ startGeoreferencing();
+ }
+ } else // action cancelled or image loaded from cache (and already georeferenced)
+ Main.map.repaint();
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ if (System.currentTimeMillis() - mouseClickedTime < initialClickDelay) {
+ System.out.println("mouse click bounce detected");
+ return; // mouse click anti-bounce
+ }
+ else
+ mouseClickedTime = System.currentTimeMillis();
+ countMouseClicked++;
+ EastNorth ea = Main.proj.latlon2eastNorth(Main.map.mapView.getLatLon(e.getX(), e.getY()));
+ System.out.println("clic:"+countMouseClicked+" ,"+ea+", mode:"+mode);
+ // ignore clicks outside the image
+ if (ea.east() < wmsLayer.images.get(0).min.east() || ea.east() > wmsLayer.images.get(0).max.east()
+ || ea.north() < wmsLayer.images.get(0).min.north() || ea.north() > wmsLayer.images.get(0).max.north())
+ return;
+ if (mode == cGetCorners) {
+ if (countMouseClicked == 1) {
+ ea1 = ea;
+ continueCropping();
+ }
+ if (countMouseClicked == 2) {
+ wmsLayer.cropImage(ea1, ea);
+ Main.map.mapView.repaint();
+ startGeoreferencing();
+ }
+ } else if (mode == cGetLambertCrosspieces) {
+ if (countMouseClicked == 1) {
+ ea1 = ea;
+ if (inputLambertPosition())
+ continueGeoreferencing();
+ }
+ if (countMouseClicked == 2) {
+ if (inputLambertPosition()) {
+ Main.map.mapView.removeMouseListener(this);
+ affineTransform(ea1, ea, georefpoint1, georefpoint2);
+ wmsLayer.saveNewCache();
+ Main.map.mapView.repaint();
+ actionCompleted();
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * @return false if all operations are canceled
+ */
+ private boolean startCropping() {
+ mode = cGetCorners;
+ countMouseClicked = 0;
+ Object[] options = { "OK", "Cancel" };
+ int ret = JOptionPane.showOptionDialog( null,
+ tr("Click first corner for image cropping\n(two points required)"),
+ tr("Image cropping"),
+ JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, options, options[0]);
+ if (ret == JOptionPane.OK_OPTION) {
+ mouseClickedTime = System.currentTimeMillis();
+ } else
+ if (canceledOrRestartCurrAction("image cropping"))
+ return startCropping();
+ return true;
+ }
+
+ /**
+ *
+ * @return false if all operations are canceled
+ */
+ private boolean continueCropping() {
+ Object[] options = { "OK", "Cancel" };
+ int ret = JOptionPane.showOptionDialog( null,
+ tr("Click second corner for image cropping"),
+ tr("Image cropping"),
+ JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, options, options[0]);
+ if (ret != JOptionPane.OK_OPTION) {
+ if (canceledOrRestartCurrAction("image cropping"))
+ return startCropping();
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @return false if all operations are canceled
+ */
+ private boolean startGeoreferencing() {
+ countMouseClicked = 0;
+ mode = cGetLambertCrosspieces;
+ Object[] options = { "OK", "Cancel" };
+ int ret = JOptionPane.showOptionDialog( null,
+ tr("Click first Lambert crosspiece for georeferencing\n(two points required)"),
+ tr("Image georeferencing"),
+ JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, options, options[0]);
+ if (ret == JOptionPane.OK_OPTION) {
+ mouseClickedTime = System.currentTimeMillis();
+ } else
+ if (canceledOrRestartCurrAction("georeferencing"))
+ return startGeoreferencing();
+ return true;
+ }
+
+ /**
+ *
+ * @return false if all operations are canceled
+ */
+ private boolean continueGeoreferencing() {
+ Object[] options = { "OK", "Cancel" };
+ int ret = JOptionPane.showOptionDialog( null,
+ tr("Click second Lambert crosspiece for georeferencing"),
+ tr("Image georeferencing"),
+ JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, options, options[0]);
+ if (ret != JOptionPane.OK_OPTION) {
+ if (canceledOrRestartCurrAction("georeferencing"))
+ return startGeoreferencing();
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @return false if all operations are canceled
+ */
+ private boolean canceledOrRestartCurrAction(String action) {
+ Object[] options = { "Cancel", "Retry" };
+ int selectedValue = JOptionPane.showOptionDialog( null,
+ tr("Do you want to cancel completely\n"+
+ "or just retry "+action+" ?"), "",
+ JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
+ null, options, options[0]);
+ if (selectedValue == 0) { // "Cancel"
+ // remove layer
+ Main.map.mapView.removeLayer(wmsLayer);
+ wmsLayer = null;
+ Main.map.mapView.removeMouseListener(this);
+ return false;
+ } else
+ countMouseClicked = 0;
+ return true;
+ }
+
+ private boolean inputLambertPosition() {
+ JLabel labelEnterPosition = new JLabel(tr("Enter cadastre east,north position"));
+ JLabel labelWarning = new JLabel(tr("(Warning: verify north with arrow !!)"));
+ JPanel p = new JPanel(new GridBagLayout());
+ JLabel labelEast = new JLabel(tr("East"));
+ JLabel labelNorth = new JLabel(tr("North"));
+ final JTextField inputEast = new JTextField();
+ final JTextField inputNorth = new JTextField();
+ p.add(labelEnterPosition, GBC.eol());
+ p.add(labelWarning, GBC.eol());
+ p.add(labelEast, GBC.std().insets(0, 0, 10, 0));
+ p.add(inputEast, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
+ p.add(labelNorth, GBC.std().insets(0, 0, 10, 0));
+ p.add(inputNorth, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
+ JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null);
+ String number;
+ if (countMouseClicked == 1) number = "first";
+ else number = "second";
+ pane.createDialog(Main.parent, tr("Set {0} Lambert coordinates",number)).setVisible(true);
+ if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue())) {
+ if (canceledOrRestartCurrAction("georeferencing"))
+ startGeoreferencing();
+ return false;
+ }
+ if (inputEast.getText().length() != 0 && inputNorth.getText().length() != 0) {
+ try {
+ double e = Double.parseDouble(inputEast.getText());
+ double n = Double.parseDouble(inputNorth.getText());
+ if (countMouseClicked == 1)
+ georefpoint1 = new EastNorth(e, n);
+ else
+ georefpoint2 = new EastNorth(e, n);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Use point org1 as anchor for scale, then move org1 to dst1, then rotate org2 on dst2
+ * around org1/dst1 anchor
+ * @param org1 first point at original coordinate system (the grabbed image)
+ * @param org2 second point "
+ * @param dst1 first point at final destination coordinate system (the real east/north coordinate system)
+ * @param dst2 second point "
+ */
+ private void affineTransform(EastNorth org1, EastNorth org2, EastNorth dst1, EastNorth dst2) {
+ double angle = dst1.heading(dst2) - org1.heading(org2);
+ double proportion = dst1.distance(dst2)/org1.distance(org2);
+ // move
+ double dx = dst1.getX() - org1.getX();
+ double dy = dst1.getY() - org1.getY();
+ wmsLayer.images.get(0).shear(dx, dy);
+ org1 = org1.add(dx, dy); // org1=dst1 now
+ org2 = org2.add(dx, dy);
+ // rotate : org1(=dst1 now) is anchor for rotation and scale
+ wmsLayer.images.get(0).rotate(dst1, angle);
+ org2 = org2.rotate(dst1, angle);
+ // scale image from anchor org1(=dst1 now)
+ wmsLayer.images.get(0).scale(dst1, proportion);
+ }
+
+ private void transformGeoreferencedImg() {
+ georefpoint1 = new EastNorth(wmsLayer.X0, wmsLayer.Y0);
+ georefpoint2 = new EastNorth(wmsLayer.X0+wmsLayer.fX*wmsLayer.communeBBox.max.getX(),
+ wmsLayer.Y0+wmsLayer.fY*wmsLayer.communeBBox.max.getX());
+ ea1 = new EastNorth(wmsLayer.images.get(0).min.east(), wmsLayer.images.get(0).max.north());
+ EastNorth ea2 = wmsLayer.images.get(0).max;
+ affineTransform(ea1, ea2, georefpoint1, georefpoint2);
+ wmsLayer.saveNewCache();
+ Main.map.mapView.repaint();
+ }
+
+ public void mouseEntered(MouseEvent arg0) {
+ }
+
+ public void mouseExited(MouseEvent arg0) {
+ }
+
+ public void mousePressed(MouseEvent arg0) {
+ }
+
+ public void mouseReleased(MouseEvent arg0) {
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java b/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java
new file mode 100644
index 0000000..81ec64b
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionLoadFromCache.java
@@ -0,0 +1,117 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.projection.Lambert;
+import org.openstreetmap.josm.data.projection.LambertCC9Zones;
+import org.openstreetmap.josm.data.projection.UTM_20N_France_DOM;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+public class MenuActionLoadFromCache extends JosmAction {
+ private static final long serialVersionUID = 1L;
+
+ public static String name = "Load layer from cache";
+
+ public MenuActionLoadFromCache() {
+ super(tr(name), "cadastre_small", tr("Load location from cache (only if cache is enabled)"), null, false);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ JFileChooser fc = createAndOpenFileChooser();
+ if (fc == null)
+ return;
+
+ File[] files = fc.getSelectedFiles();
+ int layoutZone = getCurrentProjZone();
+ nextFile:
+ for (File file : files) {
+ if (file.exists()) {
+ String filename = file.getName();
+ String ext = (filename.lastIndexOf(".")==-1)?"":filename.substring(filename.lastIndexOf(".")+1,filename.length());
+ if ((ext.length() == 3 && ext.substring(0, CacheControl.cLambertCC9Z.length()).equals(CacheControl.cLambertCC9Z) &&
+ !(Main.proj instanceof LambertCC9Zones))
+ || (ext.length() == 4 && ext.substring(0, CacheControl.cUTM20N.length()).equals(CacheControl.cUTM20N) &&
+ !(Main.proj instanceof UTM_20N_France_DOM))
+ || (ext.length() == 1) && !(Main.proj instanceof Lambert)) {
+ JOptionPane.showMessageDialog(Main.parent, tr("{0} not allowed with the current projection", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+ continue;
+ } else {
+ String location = filename.substring(0, filename.lastIndexOf("."));
+ if (ext.length() == 3 && ext.substring(0, CacheControl.cLambertCC9Z.length()).equals(CacheControl.cLambertCC9Z))
+ ext = ext.substring(2);
+ else if (ext.length() == 4 && ext.substring(0, CacheControl.cUTM20N.length()).equals(CacheControl.cUTM20N))
+ ext = ext.substring(3);
+ // check the extension and its compatibility with current projection
+ try {
+ int cacheZone = Integer.parseInt(ext) - 1;
+ if (cacheZone >=0 && cacheZone <= 9) {
+ if (cacheZone != layoutZone) {
+ JOptionPane.showMessageDialog(Main.parent, tr("Cannot load cache {0} which is not compatible with current projection zone", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+ continue nextFile;
+ } else
+ System.out.println("Load cache " + filename);
+ }
+ } catch (NumberFormatException ex) {
+ JOptionPane.showMessageDialog(Main.parent, tr("Selected file {0} is not a cache file from this plugin (invalid extension)", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+ continue nextFile;
+ }
+ // check if the selected cache is not already displayed
+ if (Main.map != null) {
+ for (Layer l : Main.map.mapView.getAllLayers()) {
+ if (l instanceof WMSLayer && l.getName().equals(location)) {
+ JOptionPane.showMessageDialog(Main.parent, tr("The location {0} is already on screen. Cache not loaded.", filename), tr("Error"), JOptionPane.ERROR_MESSAGE);
+ continue nextFile;
+ }
+ }
+ }
+ // create layer and load cache
+ WMSLayer wmsLayer = new WMSLayer("", "", Integer.parseInt(ext)-1);
+ if (wmsLayer.getCacheControl().loadCache(file, layoutZone))
+ Main.main.addLayer(wmsLayer);
+
+ }
+ }
+ }
+
+ }
+
+ protected static JFileChooser createAndOpenFileChooser() {
+ JFileChooser fc = new JFileChooser(new File(CadastrePlugin.cacheDir));
+ fc.setMultiSelectionEnabled(true);
+ int layoutZone = new MenuActionLoadFromCache().getCurrentProjZone();
+ if (layoutZone != -1) {
+ if (Main.proj instanceof Lambert)
+ fc.addChoosableFileFilter(CacheFileLambert4ZoneFilter.filters[layoutZone]);
+ else if (Main.proj instanceof LambertCC9Zones)
+ fc.addChoosableFileFilter(CacheFileLambert9ZoneFilter.filters[layoutZone]);
+ else if (Main.proj instanceof UTM_20N_France_DOM)
+ fc.addChoosableFileFilter(CacheFileUTM20NFilter.filters[layoutZone]);
+ }
+ fc.setAcceptAllFileFilterUsed(false);
+
+ int answer = fc.showOpenDialog(Main.parent);
+ if (answer != JFileChooser.APPROVE_OPTION)
+ return null;
+
+ return fc;
+ }
+
+ private int getCurrentProjZone() {
+ int zone = -1;
+ if (Main.proj instanceof LambertCC9Zones)
+ zone = ((LambertCC9Zones)Main.proj).getLayoutZone();
+ else if (Main.proj instanceof Lambert)
+ zone = ((Lambert)Main.proj).getLayoutZone();
+ else if (Main.proj instanceof UTM_20N_France_DOM)
+ zone = ((UTM_20N_France_DOM)Main.proj).getCurrentGeodesic();
+ return zone;
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java b/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
new file mode 100644
index 0000000..1d40d5e
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
@@ -0,0 +1,99 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.projection.Lambert;
+import org.openstreetmap.josm.data.projection.LambertCC9Zones;
+import org.openstreetmap.josm.data.projection.UTM_20N_France_DOM;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+
+public class MenuActionNewLocation extends JosmAction {
+
+ private static final long serialVersionUID = 1L;
+
+ public MenuActionNewLocation() {
+ super(tr("Change location"), "cadastre_small", tr("Set a new location for the next request"), null, false);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ WMSLayer wmsLayer = addNewLayer(new ArrayList<WMSLayer>());
+ if (wmsLayer != null)
+ DownloadWMSVectorImage.download(wmsLayer);
+ }
+
+ public WMSLayer addNewLayer(ArrayList<WMSLayer> existingLayers) {
+ String location = "";
+ String codeDepartement = "";
+ String codeCommune = "";
+ boolean resetCookie = false;
+ JLabel labelSectionNewLocation = new JLabel(tr("Add a new layer"));
+ JPanel p = new JPanel(new GridBagLayout());
+ JLabel labelLocation = new JLabel(tr("Location"));
+ final JTextField inputTown = new JTextField( Main.pref.get("cadastrewms.location") );
+ inputTown.setToolTipText(tr("<html>Enter the town,village or city name.<br>"
+ + "Use the syntax and punctuation known by www.cadastre.gouv.fr .</html>"));
+
+ p.add(labelSectionNewLocation, GBC.eol());
+ p.add(labelLocation, GBC.std().insets(10, 0, 0, 0));
+ p.add(inputTown, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+ JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void selectInitialValue() {
+ inputTown.requestFocusInWindow();
+ inputTown.selectAll();
+ }
+ };
+ pane.createDialog(Main.parent, tr("Add new layer")).setVisible(true);
+ if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
+ return null;
+
+ WMSLayer wmsLayer = null;
+ if (!inputTown.getText().equals("")) {
+ location = inputTown.getText().toUpperCase();
+ resetCookie = true;
+ Main.pref.put("cadastrewms.location", location);
+ Main.pref.put("cadastrewms.codeCommune", codeCommune);
+ if (Main.map != null) {
+ for (Layer l : Main.map.mapView.getAllLayers()) {
+ if (l instanceof WMSLayer && l.getName().equalsIgnoreCase(location + codeDepartement)) {
+ return null;
+ }
+ }
+ }
+ // add the layer if it doesn't exist
+ int zone = -1;
+ if (Main.proj instanceof LambertCC9Zones)
+ zone = ((LambertCC9Zones)Main.proj).getLayoutZone();
+ else if (Main.proj instanceof Lambert)
+ zone = ((Lambert)Main.proj).getLayoutZone();
+ else if (Main.proj instanceof UTM_20N_France_DOM)
+ zone = ((UTM_20N_France_DOM)Main.proj).getCurrentGeodesic();
+ wmsLayer = new WMSLayer(location, codeCommune, zone);
+ Main.main.addLayer(wmsLayer);
+ System.out.println("Add new layer with Location:" + inputTown.getText());
+ } else if (existingLayers != null && existingLayers.size() > 0 && Main.map.mapView.getActiveLayer() instanceof WMSLayer) {
+ wmsLayer = (WMSLayer)Main.map.mapView.getActiveLayer();
+ resetCookie = true;
+ }
+
+ if (resetCookie)
+ CadastrePlugin.cadastreGrabber.getWmsInterface().resetCookieIfNewLayer(wmsLayer.getName());
+ return wmsLayer;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java b/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java
new file mode 100644
index 0000000..bf50dbf
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionResetCookie.java
@@ -0,0 +1,24 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionResetCookie extends JosmAction {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public MenuActionResetCookie() {
+ super(tr("Reset cookie"), "cadastre_small", tr("Get a new cookie (session timeout)"), null, false);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ CadastrePlugin.cadastreGrabber.getWmsInterface().resetCookie();
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionSaveRasterAs.java b/cadastre-fr/src/cadastre_fr/MenuActionSaveRasterAs.java
new file mode 100644
index 0000000..13d9cf4
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/MenuActionSaveRasterAs.java
@@ -0,0 +1,74 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class MenuActionSaveRasterAs extends JosmAction {
+
+ public static String name = "Save image as PNG";
+
+ private static final long serialVersionUID = 1L;
+
+ private WMSLayer wmsLayer;
+
+ public class FiltrePng extends FileFilter {
+ @Override
+ public boolean accept(File file) {
+ if (file.isDirectory()) {
+ return true;
+ }
+ return file.getName().toLowerCase().endsWith(".png");
+ }
+
+ @Override
+ public String getDescription() {
+ return tr("PNG files (*.png)");
+ }
+
+ }
+
+ FiltrePng filtrePng = new FiltrePng();
+
+ public MenuActionSaveRasterAs(WMSLayer wmsLayer) {
+ super(tr(name), "save", tr("Export as PNG format (only raster images)"), null, false);
+ this.wmsLayer = wmsLayer;
+ }
+
+ public void actionPerformed(ActionEvent arg0) {
+ File file;
+ JFileChooser fc = new JFileChooser();
+ fc.setFileFilter(filtrePng);
+ int returnVal = fc.showSaveDialog(Main.parent);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ file = fc.getSelectedFile();
+ if (!file.getName().endsWith(".png"))
+ file = new File(file.getParent(), file.getName()+".png");
+ BufferedImage bi = wmsLayer.images.get(0).image;
+ try {
+ ImageIO.write(bi, "png", file);
+/*
+ FileOutputStream flux = new FileOutputStream(file);
+ BufferedOutputStream fluxBuf = new BufferedOutputStream(flux);
+ JPEGImageEncoder codec = JPEGCodec.createJPEGEncoder(fluxBuf, JPEGCodec.getDefaultJPEGEncodeParam(bi));
+ codec.encode(bi);
+ fluxBuf.close();
+*/
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/RasterImageModifier.java b/cadastre-fr/src/cadastre_fr/RasterImageModifier.java
new file mode 100644
index 0000000..fd1470a
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/RasterImageModifier.java
@@ -0,0 +1,104 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.awt.image.ComponentColorModel;
+
+import org.openstreetmap.josm.Main;
+
+public class RasterImageModifier extends ImageModifier {
+
+ private int cadastreBackground = -1; // white
+
+ public static int cadastreBackgroundTransp = 16777215; // original white but transparent
+
+ private boolean transparencyEnabled = false;
+
+ public RasterImageModifier(BufferedImage bi) {
+ bufferedImage = bi;
+ transparencyEnabled = Main.pref.getBoolean("cadastrewms.backgroundTransparent");
+ if (transparencyEnabled)
+ makeTransparent();
+ if (Main.pref.getBoolean("cadastrewms.invertGrey"))
+ invertGrey();
+ }
+
+ /**
+ * Invert black/white/grey pixels (to change original black characters to white).
+ */
+ private void invertGrey() {
+ int w = bufferedImage.getWidth();
+ int h = bufferedImage.getHeight();
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int pixel = bufferedImage.getRGB(x, y);
+ if ((!transparencyEnabled && pixel != cadastreBackground)
+ || (transparencyEnabled && pixel != cadastreBackgroundTransp)) {
+ bufferedImage.setRGB(x, y, reverseIfGrey(pixel));
+ }
+ }
+ }
+ }
+
+ /**
+ * Reverse the grey value if the pixel is grey (light grey becomes dark grey)
+ * Used for texts.
+ * @param pixel
+ * @return
+ */
+ private int reverseIfGrey(int pixel) {
+ Color col = new Color(pixel);
+ int r = col.getRed();
+ int g = col.getGreen();
+ int b = col.getBlue();
+ if ((b == r) && (b == g)) {
+ pixel = (0x00 << 32) + ((byte) (255 - r) << 16) + ((byte) (255 - r) << 8) + ((byte) (255 - r));
+ }
+ return pixel;
+ }
+
+ private void makeTransparent() {
+ if (bufferedImage.getColorModel() instanceof ComponentColorModel) {
+ int width = bufferedImage.getWidth();
+ int height = bufferedImage.getHeight();
+ BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int rgb = bufferedImage.getRGB(x, y);
+ Color c = new Color(rgb);
+ int r = c.getRed();
+ int g = c.getGreen();
+ int b = c.getBlue();
+ Color maskedColor;
+ if (rgb == cadastreBackground) {
+ maskedColor = new Color(r, g, b, 0x00); // transparent
+ } else {
+ maskedColor = new Color(r, g, b, 0xFF); // opaque
+ }
+ //maskedColor = new Color(r, g, b, alpha);
+ bi.setRGB(x, y, maskedColor.getRGB());
+ }
+ }
+ bufferedImage = bi;
+ }
+ return;
+ }
+
+ /**
+ * Temporary fix for Java6 which doesn't de-serialize correctly cached image on disk.
+ * Recreate a new raster image based on what is loaded/serialized from disk cache.
+ * @param img
+ * @return new image
+ */
+ public static BufferedImage fixRasterImage(BufferedImage img) {
+ int width = img.getWidth();
+ int height = img.getHeight();
+ BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ int rgbArray[] = new int[width * height];
+ img.getRGB(0, 0, width, height, rgbArray, 0, width);
+ bi.setRGB(0, 0, width, height, rgbArray, 0, width);
+ return bi;
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/SVGParser.java b/cadastre-fr/src/cadastre_fr/SVGParser.java
new file mode 100644
index 0000000..d3ae0ad
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/SVGParser.java
@@ -0,0 +1,65 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.util.ArrayList;
+
+/**
+ * This class is not intended to be a real SVG parser. It's also not using existing
+ * xml parsers. It's just extracting the required strings from an SVG file coming
+ * from the French land registry cadastre.gouv.fr
+ *
+ */
+public class SVGParser {
+
+ private String cViewBoxStart = "viewBox=\"";
+ private String cViewBoxEnd = "\"";
+ private String cPathStart = "<path d=\"";
+ private String cClosedPathEnd = "\"/>";
+
+ /**
+ * The SVG viewBox looks like this:
+ * viewBox="969780.0 320377.11 5466.130000000005 2846.429999999993"
+ * @param svg the SVG XML data
+ * @return double [x,y,dx,dy] of viewBox; null if parsing failed
+ */
+ public double[] getViewBox(String svg) {
+ int s = svg.indexOf(cViewBoxStart)+cViewBoxStart.length();
+ int e = svg.indexOf(cViewBoxEnd, s);
+ if (s != -1 && e != -1) {
+ try {
+ String str = svg.substring(s, e);
+ String [] viewBox = str.split(" ");
+ double[] dbox = new double[4];
+ for (int i = 0; i<4; i++)
+ dbox[i] = Double.parseDouble(viewBox[i]);
+ return dbox;
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Closed SVG paths are finishing with a "Z" at the end of the moves list.
+ * @param svg
+ * @return
+ */
+ public String [] getClosedPaths(String svg) {
+ ArrayList<String> path = new ArrayList<String>();
+ int i = 0;
+ while (svg.indexOf(cPathStart, i) != -1) {
+ int s = svg.indexOf(cPathStart, i) + cViewBoxStart.length();
+ int e = svg.indexOf(cClosedPathEnd, s);
+ if (s != -1 && e != -1) {
+ String onePath = svg.substring(s, e);
+ if (onePath.indexOf("Z") != -1) // only closed SVG path
+ path.add(onePath);
+ } else
+ break;
+ i = e;
+ }
+ return path.toArray(new String[ path.size() ]);
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/Scale.java b/cadastre-fr/src/cadastre_fr/Scale.java
new file mode 100644
index 0000000..49d2373
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/Scale.java
@@ -0,0 +1,29 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+/**
+ * List of possible grab factors each time we call the grab action.
+ * X1 means that only one bounding box is grabbed where X2 means that the current
+ * view is split in 2x2 bounding boxes and X3 is 3x3 boxes.
+ * SQUARE_100M is a special value where bounding boxes have a fixed size of 100x100 meters
+ * and east,north are rounded to the lowest 100 meter as well, thus none of the bounding boxes
+ * are overlapping each others.
+ */
+public enum Scale {
+ X1("1"),
+ X2("2"),
+ X3("3"),
+ SQUARE_100M("4");
+
+ /**
+ * value is the string equivalent stored in the preferences file
+ */
+ public final String value;
+
+ Scale(String value) {
+ this.value = value;
+ }
+ public String toString() {
+ return value;
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/SimplifyWay.java b/cadastre-fr/src/cadastre_fr/SimplifyWay.java
new file mode 100644
index 0000000..d6a8b35
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/SimplifyWay.java
@@ -0,0 +1,128 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.util.ArrayList;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+
+
+/**
+ * Imported from plugin UtilsPlugin
+ * @author
+ *
+ */
+public class SimplifyWay {
+ public void simplifyWay(Way w, DataSet dataSet, double threshold) {
+ Way wnew = new Way(w);
+
+ int toI = wnew.getNodesCount() - 1;
+ for (int i = wnew.getNodesCount() - 1; i >= 0; i--) {
+ CollectBackReferencesVisitor backRefsV = new CollectBackReferencesVisitor(dataSet, false);
+ backRefsV.visit(wnew.getNode(i));
+ boolean used = false;
+ if (backRefsV.getData().size() == 1) {
+ used = Collections.frequency(w.getNodes(), wnew.getNode(i)) > 1;
+ } else {
+ backRefsV.getData().remove(w);
+ used = !backRefsV.getData().isEmpty();
+ }
+ if (!used)
+ used = wnew.getNode(i).isTagged();
+
+ if (used) {
+ simplifyWayRange(wnew, i, toI, threshold);
+ toI = i;
+ }
+ }
+ simplifyWayRange(wnew, 0, toI, threshold);
+
+ HashSet<Node> delNodes = new HashSet<Node>();
+ delNodes.addAll(w.getNodes());
+ delNodes.removeAll(wnew.getNodes());
+
+ if (wnew.getNodesCount() != w.getNodesCount()) {
+ Collection<Command> cmds = new LinkedList<Command>();
+ cmds.add(new ChangeCommand(w, wnew));
+ cmds.add(new DeleteCommand(delNodes));
+ Main.main.undoRedo.add(new SequenceCommand(trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds));
+ Main.map.repaint();
+ }
+ }
+
+ public void simplifyWayRange(Way wnew, int from, int to, double thr) {
+ if (to - from >= 2) {
+ ArrayList<Node> ns = new ArrayList<Node>();
+ simplifyWayRange(wnew, from, to, ns, thr);
+ List<Node> nodes = wnew.getNodes();
+ for (int j = to - 1; j > from; j--)
+ nodes.remove(j);
+ nodes.addAll(from+1, ns);
+ wnew.setNodes(nodes);
+ }
+ }
+
+ /*
+ * Takes an interval [from,to] and adds nodes from (from,to) to ns.
+ * (from and to are indices of wnew.nodes.)
+ */
+ public void simplifyWayRange(Way wnew, int from, int to, ArrayList<Node> ns, double thr) {
+ Node fromN = wnew.getNode(from), toN = wnew.getNode(to);
+
+ int imax = -1;
+ double xtemax = 0;
+ for (int i = from + 1; i < to; i++) {
+ Node n = wnew.getNode(i);
+ double xte = Math.abs(EARTH_RAD
+ * xtd(fromN.getCoor().lat() * Math.PI / 180, fromN.getCoor().lon() * Math.PI / 180, toN.getCoor().lat() * Math.PI
+ / 180, toN.getCoor().lon() * Math.PI / 180, n.getCoor().lat() * Math.PI / 180, n.getCoor().lon() * Math.PI
+ / 180));
+ if (xte > xtemax) {
+ xtemax = xte;
+ imax = i;
+ }
+ }
+
+ if (imax != -1 && xtemax >= thr) {
+ simplifyWayRange(wnew, from, imax, ns, thr);
+ ns.add(wnew.getNode(imax));
+ simplifyWayRange(wnew, imax, to, ns, thr);
+ }
+ }
+ public static double EARTH_RAD = 6378137.0;
+ /* From Aviaton Formulary v1.3
+ * http://williams.best.vwh.net/avform.htm
+ */
+ public static double dist(double lat1, double lon1, double lat2, double lon2) {
+ return 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) + Math.cos(lat1) * Math.cos(lat2)
+ * Math.pow(Math.sin((lon1 - lon2) / 2), 2)));
+ }
+
+ public static double course(double lat1, double lon1, double lat2, double lon2) {
+ return Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
+ * Math.cos(lat2) * Math.cos(lon1 - lon2))
+ % (2 * Math.PI);
+ }
+ public static double xtd(double lat1, double lon1, double lat2, double lon2, double lat3, double lon3) {
+ double dist_AD = dist(lat1, lon1, lat3, lon3);
+ double crs_AD = course(lat1, lon1, lat3, lon3);
+ double crs_AB = course(lat1, lon1, lat2, lon2);
+ return Math.asin(Math.sin(dist_AD) * Math.sin(crs_AD - crs_AB));
+ }
+
+}
diff --git a/cadastre-fr/src/cadastre_fr/VectorImageModifier.java b/cadastre-fr/src/cadastre_fr/VectorImageModifier.java
new file mode 100644
index 0000000..befd3bc
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/VectorImageModifier.java
@@ -0,0 +1,112 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.WritableRaster;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.ColorHelper;
+
+public class VectorImageModifier extends ImageModifier {
+
+ private int cadastreBackground = -1; // white
+
+ public static int cadastreBackgroundTransp = 1; // original white but transparent
+
+ private boolean withBackground = false;
+
+ private int backgroundPixel = 0;
+
+ private int backgroundSampleX, backgroundSampleY;
+
+ public VectorImageModifier(BufferedImage bi) {
+ bufferedImage = bi;
+ if (Main.pref.getBoolean("cadastrewms.backgroundTransparent"))
+ makeTransparent();
+ else if (Main.pref.getBoolean("cadastrewms.alterColors"))
+ replaceBackground();
+ if (Main.pref.getBoolean("cadastrewms.invertGrey"))
+ invertGrey();
+ }
+
+ /**
+ * Replace the background color by the josm color.background color.
+ */
+ private void replaceBackground() {
+ int w = bufferedImage.getWidth();
+ int h = bufferedImage.getHeight();
+ int josmBackgroundColor = ColorHelper.html2color(Main.pref.get("color.background", "#000000")).getRGB();
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int pixel = bufferedImage.getRGB(x, y);
+ if (pixel == cadastreBackground) {
+ bufferedImage.setRGB(x, y, josmBackgroundColor);
+ if (!withBackground)
+ withBackground = true;
+ backgroundSampleX = x;
+ backgroundSampleY = y;
+ }
+ }
+ }
+ }
+
+ /**
+ * Invert black/white/grey pixels (to change original black characters to white).
+ */
+ private void invertGrey() {
+ int w = bufferedImage.getWidth();
+ int h = bufferedImage.getHeight();
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int pixel = bufferedImage.getRGB(x, y);
+ if (pixel != cadastreBackground) {
+ bufferedImage.setRGB(x, y, reverseIfGrey(pixel));
+ }
+ }
+ }
+ }
+
+ /**
+ * Reverse the grey value if the pixel is grey (light grey becomes dark grey)
+ * Used for texts.
+ * @param pixel
+ * @return
+ */
+ private int reverseIfGrey(int pixel) {
+ Color col = new Color(pixel);
+ int r = col.getRed();
+ int g = col.getGreen();
+ int b = col.getBlue();
+ if ((b == r) && (b == g)) {
+ pixel = (0x00 << 32) + ((byte) (255 - r) << 16) + ((byte) (255 - r) << 8) + ((byte) (255 - r));
+ }
+ return pixel;
+ }
+
+ private void makeTransparent() {
+ ColorModel colorModel = bufferedImage.getColorModel();
+ if (bufferedImage.getColorModel() instanceof IndexColorModel) {
+ // vector image (IndexColorModel)
+ IndexColorModel icm = (IndexColorModel) colorModel;
+ WritableRaster raster = bufferedImage.getRaster();
+ // pixel is offset in ICM's palette
+ if (withBackground)
+ backgroundPixel = raster.getSample(backgroundSampleX, backgroundSampleY, 0);
+ else
+ backgroundPixel = 1; // default Cadastre background sample
+ int size = icm.getMapSize();
+ byte[] reds = new byte[size];
+ byte[] greens = new byte[size];
+ byte[] blues = new byte[size];
+ icm.getReds(reds);
+ icm.getGreens(greens);
+ icm.getBlues(blues);
+ IndexColorModel icm2 = new IndexColorModel(colorModel.getPixelSize(), size, reds, greens, blues,
+ backgroundPixel);
+ bufferedImage = new BufferedImage(icm2, raster, bufferedImage.isAlphaPremultiplied(), null);
+ }
+ return;
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java b/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java
new file mode 100644
index 0000000..95cb1d8
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/WMSAdjustAction.java
@@ -0,0 +1,159 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Cursor;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+public class WMSAdjustAction extends MapMode implements
+ MouseListener, MouseMotionListener{
+
+ private static final long serialVersionUID = 1L;
+ private ArrayList<WMSLayer> modifiedLayers = new ArrayList<WMSLayer>();
+ WMSLayer selectedLayer;
+ private boolean rasterMoved;
+ private EastNorth prevEastNorth;
+ enum Mode { moveXY, moveZ, rotate}
+ private Mode mode = null;
+
+ public WMSAdjustAction(MapFrame mapFrame) {
+ super(tr("Adjust WMS"), "adjustxywms",
+ tr("Adjust the position of the WMS layer (raster images only)"), mapFrame,
+ ImageProvider.getCursor("normal", "move"));
+ }
+
+ @Override public void enterMode() {
+ if (Main.map != null) {
+ selectedLayer = null;
+ WMSLayer possibleLayer = null;
+ int cRasterLayers = 0;
+ for (Layer l : Main.map.mapView.getAllLayers()) {
+ if (l instanceof WMSLayer && ((WMSLayer)l).isRaster()) {
+ possibleLayer = (WMSLayer)l;
+ cRasterLayers++;
+ }
+ }
+ Layer activeLayer = Main.map.mapView.getActiveLayer();
+ if (activeLayer instanceof WMSLayer && ((WMSLayer)activeLayer).isRaster()) {
+ selectedLayer = (WMSLayer)activeLayer;
+ } else if (cRasterLayers == 1) {
+ selectedLayer = possibleLayer;
+ }
+ if (selectedLayer != null) {
+ super.enterMode();
+ Main.map.mapView.addMouseListener(this);
+ Main.map.mapView.addMouseMotionListener(this);
+ rasterMoved = false;
+ } else {
+ JOptionPane.showMessageDialog(Main.parent,tr("This mode works only if active layer is\n"
+ +"a cadastre \"plan image\" (raster image)"));
+ }
+ }
+ }
+
+ @Override public void exitMode() {
+ super.exitMode();
+ Main.map.mapView.removeMouseListener(this);
+ Main.map.mapView.removeMouseMotionListener(this);
+ if (rasterMoved && CacheControl.cacheEnabled) {
+ int reply = JOptionPane.showConfirmDialog(null,
+ "Save the changes in cache ?",
+ "Update cache",
+ JOptionPane.YES_NO_OPTION);
+ if (reply == JOptionPane.OK_OPTION) {
+ saveModifiedLayers();
+ }
+ }
+ modifiedLayers.clear();
+ selectedLayer = null;
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (e.getButton() != MouseEvent.BUTTON1)
+ return;
+ boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+ // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
+ boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
+ if (shift)
+ mode = Mode.moveZ;
+ else if (ctrl)
+ mode = Mode.rotate;
+ else
+ mode = Mode.moveXY;
+ rasterMoved = true;
+ prevEastNorth = Main.map.mapView.getEastNorth(e.getX(), e.getY());
+ Main.map.mapView.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+ }
+
+ @Override public void mouseDragged(MouseEvent e) {
+ EastNorth newEastNorth = Main.map.mapView.getEastNorth(e.getX(),e.getY());
+ if (mode == Mode.moveXY) {
+ displace(prevEastNorth, newEastNorth);
+ } else if (mode == Mode.moveZ) {
+ resize(newEastNorth);
+ } else if (mode == Mode.rotate) {
+ rotate(prevEastNorth, newEastNorth);
+ }
+ if (!modifiedLayers.contains(selectedLayer))
+ modifiedLayers.add(selectedLayer);
+ Main.map.mapView.repaint();
+ prevEastNorth = newEastNorth;
+ }
+
+ private void displace(EastNorth start, EastNorth end) {
+ selectedLayer.displace(end.east()-start.east(), end.north()-start.north());
+ }
+
+ private void resize(EastNorth newEastNorth) {
+ EastNorth center = selectedLayer.getRasterCenter();
+ double dPrev = prevEastNorth.distance(center.east(), center.north());
+ double dNew = newEastNorth.distance(center.east(), center.north());
+ selectedLayer.resize(center, dNew/dPrev);
+ }
+
+ private void rotate(EastNorth start, EastNorth end) {
+ EastNorth pivot = selectedLayer.getRasterCenter();
+ double startAngle = Math.atan2(start.east()-pivot.east(), start.north()-pivot.north());
+ double endAngle = Math.atan2(end.east()-pivot.east(), end.north()-pivot.north());
+ double rotationAngle = endAngle - startAngle;
+ selectedLayer.rotate(pivot, rotationAngle);
+ }
+
+ @Override public void mouseReleased(MouseEvent e) {
+ //Main.map.mapView.repaint();
+ Main.map.mapView.setCursor(Cursor.getDefaultCursor());
+ prevEastNorth = null;
+ mode = null;
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+ public void mouseExited(MouseEvent e) {
+ }
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ @Override public void mouseClicked(MouseEvent e) {
+ }
+
+ private void saveModifiedLayers() {
+ for (WMSLayer wmsLayer : modifiedLayers) {
+ wmsLayer.saveNewCache();
+ }
+ }
+}
diff --git a/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java b/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java
new file mode 100644
index 0000000..082f76d
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/WMSDownloadAction.java
@@ -0,0 +1,51 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+public class WMSDownloadAction extends JosmAction {
+
+ private static final long serialVersionUID = 1L;
+
+ public WMSDownloadAction(String layerName) {
+ super(layerName, "wmsmenu", tr("Download WMS tile from {0}",layerName), null, false);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ DownloadWMSVectorImage.download(getLayer());
+ }
+
+ public static WMSLayer getLayer() {
+ // check if we already have a layer created. if not, create; if yes, reuse.
+ ArrayList<WMSLayer> existingWMSlayers = new ArrayList<WMSLayer>();
+ if (Main.map != null) {
+ Layer activeLayer = Main.map.mapView.getActiveLayer();
+ if (activeLayer instanceof WMSLayer)
+ return (WMSLayer) activeLayer;
+ for (Layer l : Main.map.mapView.getAllLayers()) {
+ if (l instanceof WMSLayer) {
+ existingWMSlayers.add((WMSLayer)l);
+ }
+ }
+ if (existingWMSlayers.size() == 1)
+ return existingWMSlayers.get(0);
+ if (existingWMSlayers.size() == 0)
+ return new MenuActionNewLocation().addNewLayer(existingWMSlayers);
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("More than one WMS layer present\nSelect one of them first, then retry"));
+ } else {
+ return new MenuActionNewLocation().addNewLayer(existingWMSlayers);
+ }
+ return null;
+ }
+};
+
diff --git a/cadastre-fr/src/cadastre_fr/WMSLayer.java b/cadastre-fr/src/cadastre_fr/WMSLayer.java
new file mode 100644
index 0000000..5a9972c
--- /dev/null
+++ b/cadastre-fr/src/cadastre_fr/WMSLayer.java
@@ -0,0 +1,613 @@
+// License: GPL. v2 and later. Copyright 2008-2009 by Pieren <pieren3 at gmail.com> and others
+package cadastre_fr;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.io.OsmTransferException;
+
+/**
+ * This is a layer that grabs the current screen from the French cadastre WMS
+ * server. The data fetched this way is tiled and managed to the disc to reduce
+ * server load.
+ */
+public class WMSLayer extends Layer implements ImageObserver {
+
+ Component[] component = null;
+
+ private int lambertZone = -1;
+
+ protected static final Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(
+ CadastrePlugin.class.getResource("/images/cadastre_small.png")));
+
+ protected Vector<GeorefImage> images = new Vector<GeorefImage>();
+
+ protected final int serializeFormatVersion = 2;
+
+ private ArrayList<EastNorthBound> dividedBbox = new ArrayList<EastNorthBound>();
+
+ private CacheControl cacheControl = null;
+
+ private String location = "";
+
+ private String codeCommune = "";
+
+ public EastNorthBound communeBBox = new EastNorthBound(new EastNorth(0,0), new EastNorth(0,0));
+
+ private boolean isRaster = false;
+
+ private boolean isAlreadyGeoreferenced = false;
+
+ public double X0, Y0, angle, fX, fY;
+
+ private EastNorth rasterMin;
+ private EastNorth rasterMax;
+ private double rasterRatio;
+
+ private JMenuItem saveAsPng;
+
+ public WMSLayer() {
+ this(tr("Blank Layer"), "", -1);
+ }
+
+ public WMSLayer(String location, String codeCommune, int lambertZone) {
+ super(buildName(location, codeCommune));
+ this.location = location;
+ this.codeCommune = codeCommune;
+ this.lambertZone = lambertZone;
+ // enable auto-sourcing option
+ CadastrePlugin.pluginUsed = true;
+ }
+
+ private static String buildName(String location, String codeCommune) {
+ String ret = new String(location.toUpperCase());
+ if (codeCommune != null && !codeCommune.equals(""))
+ ret += "(" + codeCommune + ")";
+ return ret;
+ }
+
+ private String rebuildName() {
+ return buildName(this.location.toUpperCase(), this.codeCommune);
+ }
+
+ public void grab(CadastreGrabber grabber, Bounds b) throws IOException {
+ grab(grabber, b, true);
+ }
+
+ public void grab(CadastreGrabber grabber, Bounds b, boolean useFactor) throws IOException {
+ if (useFactor) {
+ if (isRaster) {
+ b = new Bounds(Main.proj.eastNorth2latlon(rasterMin), Main.proj.eastNorth2latlon(rasterMax));
+ divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.rasterDivider",
+ CadastrePreferenceSetting.DEFAULT_RASTER_DIVIDER)));
+ } else
+ divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.scale", Scale.X1.toString())));
+ } else
+ divideBbox(b, 1);
+
+ for (EastNorthBound n : dividedBbox) {
+ GeorefImage newImage;
+ try {
+ newImage = grabber.grab(this, n.min, n.max);
+ } catch (IOException e) {
+ System.out.println("Download action cancelled by user or server did not respond");
+ break;
+ } catch (OsmTransferException e) {
+ System.out.println("OSM transfer failed");
+ break;
+ }
+ if (grabber.getWmsInterface().downloadCancelled) {
+ System.out.println("Download action cancelled by user");
+ break;
+ }
+ if (CadastrePlugin.backgroundTransparent) {
+ for (GeorefImage img : images) {
+ if (img.overlap(newImage))
+ // mask overlapping zone in already grabbed image
+ img.withdraw(newImage);
+ else
+ // mask overlapping zone in new image only when new
+ // image covers completely the existing image
+ newImage.withdraw(img);
+ }
+ }
+ images.add(newImage);
+ saveToCache(newImage);
+ Main.map.mapView.repaint();
+ }
+ }
+
+ /**
+ *
+ * @param b the original bbox, usually the current bbox on screen
+ * @param factor 1 = source bbox 1:1
+ * 2 = source bbox divided by 2x2 smaller boxes
+ * 3 = source bbox divided by 3x3 smaller boxes
+ * 4 = hard coded size of boxes (100 meters) rounded allowing
+ * grabbing of next contiguous zone
+ */
+ private void divideBbox(Bounds b, int factor) {
+ EastNorth lambertMin = Main.proj.latlon2eastNorth(b.getMin());
+ EastNorth lambertMax = Main.proj.latlon2eastNorth(b.getMax());
+ double minEast = lambertMin.east();
+ double minNorth = lambertMin.north();
+ double dEast = (lambertMax.east() - minEast) / factor;
+ double dNorth = (lambertMax.north() - minNorth) / factor;
+ dividedBbox.clear();
+ if (factor < 4 || isRaster) {
+ for (int xEast = 0; xEast < factor; xEast++)
+ for (int xNorth = 0; xNorth < factor; xNorth++) {
+ dividedBbox.add(new EastNorthBound(new EastNorth(minEast + xEast * dEast, minNorth + xNorth * dNorth),
+ new EastNorth(minEast + (xEast + 1) * dEast, minNorth + (xNorth + 1) * dNorth)));
+ }
+ } else {
+ // divide to fixed size squares
+ int cSquare = Integer.parseInt(Main.pref.get("cadastrewms.squareSize", "100"));
+ minEast = minEast - minEast % cSquare;
+ minNorth = minNorth - minNorth % cSquare;
+ for (int xEast = (int)minEast; xEast < lambertMax.east(); xEast+=cSquare)
+ for (int xNorth = (int)minNorth; xNorth < lambertMax.north(); xNorth+=cSquare) {
+ dividedBbox.add(new EastNorthBound(new EastNorth(xEast, xNorth),
+ new EastNorth(xEast + cSquare, xNorth + cSquare)));
+ }
+ }
+ }
+
+ @Override
+ public Icon getIcon() {
+ return icon;
+ }
+
+ @Override
+ public String getToolTipText() {
+ String str = tr("WMS layer ({0}), {1} tile(s) loaded", getName(), images.size());
+ if (isRaster) {
+ str += "\n"+tr("Is not vectorized.");
+ str += "\n"+tr("Raster size: {0}", communeBBox);
+ } else
+ str += "\n"+tr("Is vectorized.");
+ str += "\n"+tr("Commune bbox: {0}", communeBBox);
+ return str;
+ }
+
+ @Override
+ public boolean isMergable(Layer other) {
+ return false;
+ }
+
+ @Override
+ public void mergeFrom(Layer from) {
+ }
+
+ @Override
+ public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
+ synchronized(this){
+ for (GeorefImage img : images)
+ img.paint(g, mv, CadastrePlugin.backgroundTransparent,
+ CadastrePlugin.transparency, CadastrePlugin.drawBoundaries);
+ }
+ if (this.isRaster) {
+ paintCrosspieces(g, mv);
+ }
+ }
+
+ @Override
+ public void visitBoundingBox(BoundingXYVisitor v) {
+ for (GeorefImage img : images) {
+ v.visit(img.min);
+ v.visit(img.max);
+ }
+ }
+
+ @Override
+ public Object getInfoComponent() {
+ return getToolTipText();
+ }
+
+ @Override
+ public Component[] getMenuEntries() {
+ saveAsPng = new JMenuItem(new MenuActionSaveRasterAs(this));
+ saveAsPng.setEnabled(isRaster);
+ component = new Component[] { new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+ new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+ new JMenuItem(new MenuActionLoadFromCache()),
+ saveAsPng,
+ new JMenuItem(new LayerListPopup.InfoAction(this)),
+
+ };
+ return component;
+ }
+
+ public GeorefImage findImage(EastNorth eastNorth) {
+ // Iterate in reverse, so we return the image which is painted last.
+ // (i.e. the topmost one)
+ for (int i = images.size() - 1; i >= 0; i--) {
+ if (images.get(i).contains(eastNorth)) {
+ return images.get(i);
+ }
+ }
+ return null;
+ }
+
+ public boolean isOverlapping(Bounds bounds) {
+ GeorefImage georefImage =
+ new GeorefImage(new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB ), // not really important
+ Main.proj.latlon2eastNorth(bounds.getMin()),
+ Main.proj.latlon2eastNorth(bounds.getMax()));
+ for (GeorefImage img : images) {
+ if (img.overlap(georefImage))
+ return true;
+ }
+ return false;
+ }
+
+ public void saveToCache(GeorefImage image) {
+ if (CacheControl.cacheEnabled && !isRaster()) {
+ getCacheControl().saveCache(image);
+ }
+ }
+
+ public void saveNewCache() {
+ if (CacheControl.cacheEnabled) {
+ getCacheControl().deleteCacheFile();
+ for (GeorefImage image : images)
+ getCacheControl().saveCache(image);
+ }
+ }
+
+ public CacheControl getCacheControl() {
+ if (cacheControl == null)
+ cacheControl = new CacheControl(this);
+ return cacheControl;
+ }
+
+ /**
+ * Convert the eastNorth input coordinates to raster coordinates.
+ * The original raster size is [0,0,12286,8730] where 0,0 is the upper left corner and
+ * 12286,8730 is the approx. raster max size.
+ * @return the raster coordinates for the wms server request URL (minX,minY,maxX,maxY)
+ */
+ public String eastNorth2raster(EastNorth min, EastNorth max) {
+ double minX = (min.east() - rasterMin.east()) / rasterRatio;
+ double minY = (min.north() - rasterMin.north()) / rasterRatio;
+ double maxX = (max.east() - rasterMin.east()) / rasterRatio;
+ double maxY = (max.north() - rasterMin.north()) / rasterRatio;
+ return minX+","+minY+","+maxX+","+maxY;
+ }
+
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ setName(rebuildName());
+ }
+
+ public String getCodeCommune() {
+ return codeCommune;
+ }
+
+ public void setCodeCommune(String codeCommune) {
+ this.codeCommune = codeCommune;
+ setName(rebuildName());
+ }
+
+ public boolean isRaster() {
+ return isRaster;
+ }
+
+ public void setRaster(boolean isRaster) {
+ this.isRaster = isRaster;
+ if (saveAsPng != null)
+ saveAsPng.setEnabled(isRaster);
+ }
+
+ public boolean isAlreadyGeoreferenced() {
+ return isAlreadyGeoreferenced;
+ }
+
+ public void setAlreadyGeoreferenced(boolean isAlreadyGeoreferenced) {
+ this.isAlreadyGeoreferenced = isAlreadyGeoreferenced;
+ }
+
+ /**
+ * Set raster positions used for grabbing and georeferencing.
+ * rasterMin is the Eaast North of bottom left corner raster image on the screen when image is grabbed.
+ * The bounds width and height are the raster width and height. The image width matches the current view
+ * and the image height is adapted.
+ * Required: the communeBBox must be set (normally it is catched by CadastreInterface and saved by DownloadWMSPlanImage)
+ * @param bounds the current main map view boundaries
+ */
+ public void setRasterBounds(Bounds bounds) {
+ EastNorth rasterCenter = Main.proj.latlon2eastNorth(bounds.getCenter());
+ EastNorth eaMin = Main.proj.latlon2eastNorth(bounds.getMin());
+ EastNorth eaMax = Main.proj.latlon2eastNorth(bounds.getMax());
+ double rasterSizeX = communeBBox.max.getX() - communeBBox.min.getX();
+ double rasterSizeY = communeBBox.max.getY() - communeBBox.min.getY();
+ double ratio = rasterSizeY/rasterSizeX;
+ // keep same ratio on screen as WMS bbox (stored in communeBBox)
+ rasterMin = new EastNorth(eaMin.getX(), rasterCenter.getY()-(eaMax.getX()-eaMin.getX())*ratio/2);
+ rasterMax = new EastNorth(eaMax.getX(), rasterCenter.getY()+(eaMax.getX()-eaMin.getX())*ratio/2);
+ rasterRatio = (rasterMax.getX()-rasterMin.getX())/rasterSizeX;
+ }
+
+ /**
+ * Called by CacheControl when a new cache file is created on disk.
+ * Save only primitives to keep cache independent of software changes.
+ * @param oos
+ * @throws IOException
+ */
+ public void write(ObjectOutputStream oos) throws IOException {
+ oos.writeInt(this.serializeFormatVersion);
+ oos.writeObject(this.location); // String
+ oos.writeObject(this.codeCommune); // String
+ oos.writeInt(this.lambertZone);
+ oos.writeBoolean(this.isRaster);
+ if (this.isRaster) {
+ oos.writeDouble(this.rasterMin.getX());
+ oos.writeDouble(this.rasterMin.getY());
+ oos.writeDouble(this.rasterMax.getX());
+ oos.writeDouble(this.rasterMax.getY());
+ oos.writeDouble(this.rasterRatio);
+ }
+ oos.writeDouble(this.communeBBox.min.getX());
+ oos.writeDouble(this.communeBBox.min.getY());
+ oos.writeDouble(this.communeBBox.max.getX());
+ oos.writeDouble(this.communeBBox.max.getY());
+ }
+
+ /**
+ * Called by CacheControl when a cache file is read from disk.
+ * Cache uses only primitives to stay independent of software changes.
+ * @param ois
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ public boolean read(ObjectInputStream ois, int currentLambertZone) throws IOException, ClassNotFoundException {
+ int sfv = ois.readInt();
+ if (sfv != this.serializeFormatVersion) {
+ JOptionPane.showMessageDialog(Main.parent, tr("Unsupported cache file version; found {0}, expected {1}\nCreate a new one.",
+ sfv, this.serializeFormatVersion), tr("Cache Format Error"), JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ this.setLocation((String) ois.readObject());
+ this.setCodeCommune((String) ois.readObject());
+ this.lambertZone = ois.readInt();
+ this.setRaster(ois.readBoolean());
+ if (this.isRaster) {
+ double X = ois.readDouble();
+ double Y = ois.readDouble();
+ this.rasterMin = new EastNorth(X, Y);
+ X = ois.readDouble();
+ Y = ois.readDouble();
+ this.rasterMax = new EastNorth(X, Y);
+ this.rasterRatio = ois.readDouble();
+ }
+ double minX = ois.readDouble();
+ double minY = ois.readDouble();
+ double maxX = ois.readDouble();
+ double maxY = ois.readDouble();
+ this.communeBBox = new EastNorthBound(new EastNorth(minX, minY), new EastNorth(maxX, maxY));
+ if (this.lambertZone != currentLambertZone && currentLambertZone != -1) {
+ JOptionPane.showMessageDialog(Main.parent, tr("Lambert zone {0} in cache "+
+ "incompatible with current Lambert zone {1}",
+ this.lambertZone+1, currentLambertZone), tr("Cache Lambert Zone Error"), JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ synchronized(this){
+ boolean EOF = false;
+ try {
+ while (!EOF) {
+ GeorefImage newImage = (GeorefImage) ois.readObject();
+ for (GeorefImage img : this.images) {
+ if (CadastrePlugin.backgroundTransparent) {
+ if (img.overlap(newImage))
+ // mask overlapping zone in already grabbed image
+ img.withdraw(newImage);
+ else
+ // mask overlapping zone in new image only when
+ // new image covers completely the existing image
+ newImage.withdraw(img);
+ }
+ }
+ this.images.add(newImage);
+ }
+ } catch (EOFException ex) {
+ // expected exception when all images are read
+ }
+ }
+ System.out.println("Cache loaded for location "+location+" with "+images.size()+" images");
+ return true;
+ }
+
+ /**
+ * Join the grabbed images into one single.
+ * Works only for images grabbed from non-georeferenced images (Feuilles cadastrales)(same amount of
+ * images in x and y)
+ */
+ public void joinRasterImages() {
+ if (images.size() > 1) {
+ EastNorth min = images.get(0).min;
+ EastNorth max = images.get(images.size()-1).max;
+ int oldImgWidth = images.get(0).image.getWidth();
+ int oldImgHeight = images.get(0).image.getHeight();
+ int newWidth = oldImgWidth*(int)Math.sqrt(images.size());
+ int newHeight = oldImgHeight*(int)Math.sqrt(images.size());
+ BufferedImage new_img = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = new_img.getGraphics();
+ // Coordinate (0,0) is on top,left corner where images are grabbed from bottom left
+ int rasterDivider = (int)Math.sqrt(images.size());
+ for (int h = 0; h < rasterDivider; h++) {
+ for (int v = 0; v < rasterDivider; v++) {
+ int newx = h*oldImgWidth;
+ int newy = newHeight - oldImgHeight - (v*oldImgHeight);
+ int j = h*rasterDivider + v;
+ g.drawImage(images.get(j).image, newx, newy, this);
+ }
+ }
+ synchronized(this) {
+ images.clear();
+ images.add(new GeorefImage(new_img, min, max));
+ }
+ }
+ }
+
+ /**
+ * Image cropping based on two EN coordinates pointing to two corners in diagonal
+ * Because it's coming from user mouse clics, we have to sort de positions first.
+ * Works only for raster image layer (only one image in collection).
+ * Updates layer georeferences.
+ * @param en1
+ * @param en2
+ */
+ public void cropImage(EastNorth en1, EastNorth en2){
+ // adj1 is corner bottom, left
+ EastNorth adj1 = new EastNorth(en1.east() <= en2.east() ? en1.east() : en2.east(),
+ en1.north() <= en2.north() ? en1.north() : en2.north());
+ // adj2 is corner top, right
+ EastNorth adj2 = new EastNorth(en1.east() > en2.east() ? en1.east() : en2.east(),
+ en1.north() > en2.north() ? en1.north() : en2.north());
+ // s1 and s2 have 0,0 at top, left where all EastNorth coord. have 0,0 at bottom, left
+ int sx1 = (int)((adj1.getX() - images.get(0).min.getX())*images.get(0).getPixelPerEast());
+ int sy1 = (int)((images.get(0).max.getY() - adj2.getY())*images.get(0).getPixelPerNorth());
+ int sx2 = (int)((adj2.getX() - images.get(0).min.getX())*images.get(0).getPixelPerEast());
+ int sy2 = (int)((images.get(0).max.getY() - adj1.getY())*images.get(0).getPixelPerNorth());
+ int newWidth = Math.abs(sx2 - sx1);
+ int newHeight = Math.abs(sy2 - sy1);
+ BufferedImage new_img = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = new_img.getGraphics();
+ g.drawImage(images.get(0).image, 0, 0, newWidth-1, newHeight-1,
+ sx1, sy1, sx2, sy2,
+ this);
+ images.set(0, new GeorefImage(new_img, adj1, adj2));
+ // important: update the layer georefs !
+ rasterMin = adj1;
+ rasterMax = adj2;
+ rasterRatio = (rasterMax.getX()-rasterMin.getX())/(communeBBox.max.getX() - communeBBox.min.getX());
+ setCommuneBBox(new EastNorthBound(new EastNorth(0,0), new EastNorth(newWidth-1,newHeight-1)));
+ }
+
+ public EastNorthBound getCommuneBBox() {
+ return communeBBox;
+ }
+
+ public void setCommuneBBox(EastNorthBound entireCommune) {
+ this.communeBBox = entireCommune;
+// if (Main.proj instanceof LambertCC9Zones)
+// setLambertCC9Zone(communeBBox.min.north());
+ }
+
+ /**
+ * Method required by ImageObserver when drawing an image
+ */
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
+ return false;
+ }
+
+ public int getLambertZone() {
+ return lambertZone;
+ }
+
+// public void setLambertCC9Zone(double north) {
+// int lambertZone = LambertCC9Zones.north2ZoneNumber(north);
+// this.lambertZone = lambertZone;
+// if (((LambertCC9Zones)Main.proj).getLayoutZone() != lambertZone) {
+// String currentZone = MenuActionLambertZone.lambert9zones[((LambertCC9Zones)Main.proj).getLayoutZone()+1];
+// String destZone = MenuActionLambertZone.lambert9zones[lambertZone+1];
+// if (Main.map.mapView.getAllLayers().size() == 1) {
+// /* Enable this code below when JOSM will have a proper support of dynamic projection change
+// *
+// System.out.println("close all layers and change current Lambert zone from "+LambertCC9Zones.layoutZone+" to "+lambertZone);
+// Bounds b = null;
+// if (Main.map != null && Main.map.mapView != null)
+// b = Main.map.mapView.getRealBounds();
+// LambertCC9Zones.layoutZone = lambertZone;
+// Main.map.mapView.zoomTo(b);
+// */
+// } else {
+// JOptionPane.showMessageDialog(Main.parent, tr("Current layer is in Lambert CC9 Zone \"{0}\"\n"+
+// "where the commune is in Lambert CC9 Zone \"{1}\".\n"+
+// "Upload your changes, close all layers and change\n"+
+// "manually the Lambert zone from the Cadastre menu"
+// , currentZone, destZone));
+// }
+// }
+// }
+
+ public EastNorth getRasterCenter() {
+ return new EastNorth((images.get(0).max.east()+images.get(0).min.east())/2,
+ (images.get(0).max.north()+images.get(0).min.north())/2);
+ }
+
+ public void displace(double dx, double dy) {
+ this.rasterMin = new EastNorth(rasterMin.east() + dx, rasterMin.north() + dy);
+ images.get(0).shear(dx, dy);
+ }
+
+ public void resize(EastNorth rasterCenter, double proportion) {
+ this.rasterMin = rasterMin.interpolate(rasterCenter, proportion);
+ images.get(0).scale(rasterCenter, proportion);
+ }
+
+ public void rotate(EastNorth rasterCenter, double angle) {
+ this.rasterMin = rasterMin.rotate(rasterCenter, angle);
+ images.get(0).rotate(rasterCenter, angle);
+ }
+
+ private void paintCrosspieces(Graphics g, MapView mv) {
+ String crosspieces = Main.pref.get("cadastrewms.crosspieces", "0");
+ if (!crosspieces.equals("0")) {
+ int modulo = 50;
+ if (crosspieces.equals("2")) modulo = 100;
+ EastNorthBound currentView = new EastNorthBound(mv.getEastNorth(0, mv.getHeight()),
+ mv.getEastNorth(mv.getWidth(), 0));
+ int minX = ((int)currentView.min.east()/modulo+1)*modulo;
+ int minY = ((int)currentView.min.north()/modulo+1)*modulo;
+ int maxX = ((int)currentView.max.east()/modulo)*modulo;
+ int maxY = ((int)currentView.max.north()/modulo)*modulo;
+ int size=(maxX-minX)/modulo;
+ if (size<20) {
+ int px= size > 10 ? 2 : Math.abs(12-size);
+ g.setColor(Color.green);
+ for (int x=minX; x<=maxX; x+=modulo) {
+ for (int y=minY; y<=maxY; y+=modulo) {
+ Point p = mv.getPoint(new EastNorth(x,y));
+ g.drawLine(p.x-px, p.y, p.x+px, p.y);
+ g.drawLine(p.x, p.y-px, p.x, p.y+px);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/measurement/.classpath b/colorscheme/.classpath
similarity index 94%
copy from measurement/.classpath
copy to colorscheme/.classpath
index 05929d7..17b8e6a 100644
--- a/measurement/.classpath
+++ b/colorscheme/.classpath
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
<classpathentry kind="output" path="build"/>
</classpath>
diff --git a/colorscheme/.project b/colorscheme/.project
new file mode 100644
index 0000000..f9fda49
--- /dev/null
+++ b/colorscheme/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>JOSM-colorscheme</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/livegps/.classpath b/editgpx/.classpath
similarity index 85%
copy from livegps/.classpath
copy to editgpx/.classpath
index 81a65e8..7d87ead 100644
--- a/livegps/.classpath
+++ b/editgpx/.classpath
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0.15"/>
+ <classpathentry kind="src" path="src-common"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/editgpx/.project b/editgpx/.project
new file mode 100644
index 0000000..569f47e
--- /dev/null
+++ b/editgpx/.project
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>editgpx</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+ <linkedResources>
+ <link>
+ <name>src-common</name>
+ <type>2</type>
+ <location>/Users/leo/dev/josm/core/src</location>
+ </link>
+ </linkedResources>
+</projectDescription>
diff --git a/slippymap/build.xml b/editgpx/build.xml
similarity index 83%
copy from slippymap/build.xml
copy to editgpx/build.xml
index a0eed67..b991a6e 100644
--- a/slippymap/build.xml
+++ b/editgpx/build.xml
@@ -1,4 +1,4 @@
-<project name="slippymap" default="dist" basedir=".">
+<project name="editgpx" default="dist" basedir=".">
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="plugin.build.dir" value="build"/>
@@ -20,12 +20,12 @@
</copy>
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
- <attribute name="Author" value="Frederik Ramm"/>
- <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.slippymap.SlippyMapPlugin"/>
+ <attribute name="Author" value="Martin Garbe" />
+ <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.editgpx.EditGpxPlugin" />
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
- <attribute name="Plugin-Description" value="Displays a slippy map grid in JOSM. Can load tiles from slippy map as background and request updates."/>
- <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/SlippyMap"/>
- <attribute name="Plugin-Mainversion" value="2196"/>
+ <attribute name="Plugin-Description" value="Allows the user to anonymize timestamps and delete parts of huge GPX tracks very fast." />
+ <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/EditGpx" />
+ <attribute name="Plugin-Mainversion" value="2450" />
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/editgpx/images/editgpx_layer.png b/editgpx/images/editgpx_layer.png
new file mode 100644
index 0000000..5744c27
Binary files /dev/null and b/editgpx/images/editgpx_layer.png differ
diff --git a/editgpx/images/mapmode/editgpx_mode.png b/editgpx/images/mapmode/editgpx_mode.png
new file mode 100644
index 0000000..b8b06ad
Binary files /dev/null and b/editgpx/images/mapmode/editgpx_mode.png differ
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
new file mode 100644
index 0000000..de997af
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
@@ -0,0 +1,223 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+
+import javax.swing.AbstractAction;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JMenuItem;
+import javax.swing.JSeparator;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+
+public class EditGpxLayer extends Layer {
+
+ private static Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(EditGpxPlugin.class.getResource("/images/editgpx_layer.png")));
+ private DataSet dataSet;
+ private GPXLayerImportAction layerImport;
+
+ public EditGpxLayer(String str, DataSet ds) {
+ super(str);
+ dataSet = ds;
+ layerImport = new GPXLayerImportAction(dataSet);
+ }
+
+ /**
+ * check if dataSet is empty
+ * if so show import dialog to user
+ */
+ public void initializeImport() {
+ try {
+ if(dataSet.getNodes().isEmpty() ) {
+ layerImport.activateImport();
+ }
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public Icon getIcon() {
+ return icon;
+ }
+
+ @Override
+ public Object getInfoComponent() {
+ return getToolTipText();
+ }
+
+ @Override
+ public Component[] getMenuEntries() {
+ return new Component[] {
+ new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+ new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+ new JSeparator(),
+ new JMenuItem(layerImport),
+ new JMenuItem(new ConvertToGpxLayerAction()),
+ new JMenuItem(new ConvertToAnonTimeGpxLayerAction()),
+ new JSeparator(),
+ new JMenuItem(new LayerListPopup.InfoAction(this))};
+ }
+
+ @Override
+ public String getToolTipText() {
+ return tr("Layer for editing GPX tracks");
+ }
+
+ @Override
+ public boolean isMergable(Layer other) {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public void mergeFrom(Layer from) {
+ // TODO
+ }
+
+ @Override
+ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
+ g.setColor(Color.yellow);
+
+ //don't iterate through dataSet whiling making changes
+ synchronized(layerImport.importing) {
+ for(Node n: dataSet.getNodes()) {
+ if (!n.isDeleted()) {
+ Point pnt = Main.map.mapView.getPoint(n.getEastNorth());
+ g.drawOval(pnt.x - 2, pnt.y - 2, 4, 4);
+ }
+ }
+ }
+ }
+
+
+ public void reset(){
+ //TODO implement a reset
+ }
+
+
+ @Override
+ public void visitBoundingBox(BoundingXYVisitor v) {
+ // TODO Auto-generated method stub
+ }
+
+
+ /**
+ * convert a DataSet to GPX
+ *
+ * @param boolean anonTime If true set all time and date in GPX to 01/01/1970 00:00 ?
+ * @return GPXData
+ */
+ private GpxData toGpxData(boolean anonTime) {
+ GpxData gpxData = new GpxData();
+ HashSet<Node> doneNodes = new HashSet<Node>();
+ //add all ways
+ for (Way w : dataSet.getWays()) {
+ if (w.incomplete || w.isDeleted()) continue;
+ GpxTrack trk = new GpxTrack();
+ gpxData.tracks.add(trk);
+
+ if (w.get("name") != null)
+ trk.attr.put("name", w.get("name"));
+
+ ArrayList<WayPoint> trkseg = null;
+ for (Node n : w.getNodes()) {
+ if (n.incomplete || n.isDeleted()) {
+ trkseg = null;
+ continue;
+ }
+
+ Date tstamp = n.getTimestamp();
+
+ if (trkseg == null) {
+ trkseg = new ArrayList<WayPoint>();
+ trk.trackSegs.add(trkseg);
+ }
+ doneNodes.add(n);
+
+ WayPoint wpt = new WayPoint(n.getCoor());
+ if (anonTime) {
+ wpt.attr.put("time", "1970-01-01T00:00:00Z");
+ } else {
+ wpt.attr.put("time", DateUtils.fromDate(tstamp));
+ }
+ wpt.setTime();
+
+ trkseg.add(wpt);
+ }
+ }
+
+ // add nodes as waypoints
+ for (Node n : dataSet.getNodes()) {
+ if (n.incomplete || n.isDeleted() || doneNodes.contains(n)) continue;
+
+ Date tstamp = n.getTimestamp();
+
+ WayPoint wpt = new WayPoint(n.getCoor());
+ if (anonTime) {
+ wpt.attr.put("time", "1970-01-01T00:00:00Z");
+ } else {
+ wpt.attr.put("time", DateUtils.fromDate(tstamp));
+ }
+ wpt.setTime();
+
+ if (n.getKeys() != null && n.keySet().contains("name")) {
+ wpt.attr.put("name", n.get("name"));
+ }
+ gpxData.waypoints.add(wpt);
+ }
+ return gpxData;
+ }
+
+ //context item "Convert to GPX layer"
+ public class ConvertToGpxLayerAction extends AbstractAction {
+ public ConvertToGpxLayerAction() {
+ super(tr("Convert to GPX layer"), ImageProvider.get("converttogpx"));
+ }
+ public void actionPerformed(ActionEvent e) {
+ Main.main.addLayer(new GpxLayer(toGpxData(false), tr("Converted from: {0}", getName())));
+ Main.main.removeLayer(EditGpxLayer.this);
+ }
+ }
+
+ //context item "Convert to GPX layer with anonymised time"
+ public class ConvertToAnonTimeGpxLayerAction extends AbstractAction {
+ public ConvertToAnonTimeGpxLayerAction() {
+ super(tr("Convert to GPX layer with anonymised time"), ImageProvider.get("converttogpx"));
+ }
+ public void actionPerformed(ActionEvent e) {
+ Main.main.addLayer(new GpxLayer(toGpxData(true), tr("Converted from: {0}", getName())));
+ Main.main.removeLayer(EditGpxLayer.this);
+ }
+ }
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java
new file mode 100644
index 0000000..d62935e
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java
@@ -0,0 +1,134 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+
+
+public class EditGpxMode extends MapMode {
+
+ private static final long serialVersionUID = 7940589057093872411L;
+ Point pointPressed;
+ DataSet dataSet;
+ MapFrame mapFrame;
+ Rectangle oldRect;
+ MapFrame frame;
+
+ public EditGpxMode(MapFrame mapFrame, String name, String desc, DataSet ds) {
+ super(name, "editgpx_mode.png", desc, mapFrame, Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+ dataSet = ds;
+ }
+
+ @Override public void enterMode() {
+ super.enterMode();
+ Main.map.mapView.addMouseListener(this);
+ Main.map.mapView.addMouseMotionListener(this);
+ }
+
+ @Override public void exitMode() {
+ super.exitMode();
+ Main.map.mapView.removeMouseListener(this);
+ Main.map.mapView.removeMouseMotionListener(this);
+ }
+
+
+ @Override public void mousePressed(MouseEvent e) {
+ pointPressed = new Point(e.getPoint());
+ }
+
+
+ @Override public void mouseDragged(MouseEvent e) {
+ if ( (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK) {
+ //if button1 is hold, draw the rectangle.
+ paintRect(pointPressed, e.getPoint());
+ }
+ }
+
+ @Override public void mouseReleased(MouseEvent e) {
+ if (e.getButton() != MouseEvent.BUTTON1) {
+ return;
+ } else {
+ Point pointReleased = e.getPoint();
+
+ Rectangle r = createRect(pointReleased, pointPressed);
+
+ //go through nodes and mark the ones in the selection rect as deleted
+ for (Node n: dataSet.getNodes()) {
+ Point p = Main.map.mapView.getPoint(n);
+ if (r.contains(p)) {
+ n.setDeleted(true);
+ }
+ }
+ oldRect = null;
+ Main.map.mapView.repaint();
+ }
+ }
+
+ /**
+ * create rectangle out of two given corners
+ */
+ public Rectangle createRect(Point p1, Point p2) {
+ int x,y,w,h;
+ if (p1.x == p2.x && p1.y == p2.y) {
+ //if p1 and p2 same points draw a small rectangle around them
+ x = p1.x -1;
+ y = p1.y -1;
+ w = 3;
+ h = 3;
+ } else {
+ if (p1.x < p2.x){
+ x = p1.x;
+ w = p2.x-p1.x;
+ } else {
+ x = p2.x;
+ w = p1.x-p2.x;
+ }
+ if (p1.y < p2.y) {
+ y = p1.y;
+ h = p2.y-p1.y;
+ } else {
+ y = p2.y;
+ h = p1.y-p2.y;
+ }
+ }
+ return new Rectangle(x,y,w,h);
+ }
+
+ /**
+ * Draw a selection rectangle on screen.
+ */
+ private void paintRect(Point p1, Point p2) {
+ Graphics g = frame.getGraphics();//Main.map.mapView.getGraphics();
+
+ Rectangle r = oldRect;
+ if (r != null) {
+ //overwrite old rct
+ g.setXORMode(Color.BLACK);
+ g.setColor(Color.WHITE);
+ g.drawRect(r.x,r.y,r.width,r.height);
+ }
+
+ g.setXORMode(Color.BLACK);
+ g.setColor(Color.WHITE);
+ r = createRect(p1,p2);
+ g.drawRect(r.x,r.y,r.width,r.height);
+ oldRect = r;
+ }
+
+
+ public void setFrame(MapFrame mapFrame) {
+ frame = mapFrame;
+ }
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java
new file mode 100644
index 0000000..400eb55
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java
@@ -0,0 +1,115 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
+import org.openstreetmap.josm.plugins.Plugin;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * Provides an editable GPX layer. Editable layer here means the deletion of points is supported.
+ * This plugin can be used to prepare tracks for upload to OSM eg. delete uninteresting parts
+ * of the track.
+ * Additionally while converting the track back to a normal GPX layer the time can be made
+ * anonymous. This feature sets all time stamps to 1970-01-01 00:00.
+ *
+ * TODO:
+ * - BUG: when importing eGpxLayer is shown as RawGpxLayer??
+ * - BUG: after deletion of layer not all data is deleted (eg dataset)
+ * - implement reset if user made mistake while marking
+ *
+ *
+ */
+public class EditGpxPlugin extends Plugin {
+
+ private IconToggleButton btn;
+ private EditGpxMode mode;
+ protected static EditGpxLayer eGpxLayer;
+ protected static DataSet dataSet;
+ public static boolean active = false;
+
+ public EditGpxPlugin() {
+ dataSet = new DataSet();
+ mode = new EditGpxMode(Main.map, "editgpx", tr("edit gpx tracks"), dataSet);
+
+ btn = new IconToggleButton(mode);
+ btn.setVisible(true);
+ }
+
+ /**
+ * initialize button. if button is pressed create new layer.
+ */
+ @Override
+ public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+ if(oldFrame == null && newFrame != null) {
+ mode.setFrame(newFrame);
+
+ if(Main.map != null)
+ Main.map.addMapMode(btn);
+
+ active = btn.isSelected();
+
+ btn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ active = btn.isSelected();
+ if (active) {
+ Main.worker.execute(new Runnable() {
+ public void run() {
+ updateLayer();
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * create new layer, add listeners and try importing gpx data.
+ */
+ private void updateLayer() {
+ if(eGpxLayer == null) {
+ eGpxLayer = new EditGpxLayer(tr("EditGpx"), dataSet);
+ Main.main.addLayer(eGpxLayer);
+ Layer.listeners.add(new LayerChangeListener(){
+
+ public void activeLayerChange(final Layer oldLayer, final Layer newLayer) {
+ if(newLayer instanceof EditGpxLayer)
+ EditGpxPlugin.eGpxLayer = (EditGpxLayer)newLayer;
+ }
+
+ public void layerAdded(final Layer newLayer) {
+ }
+
+ public void layerRemoved(final Layer oldLayer) {
+ if(oldLayer == eGpxLayer) {
+ eGpxLayer = null;
+ //dataSet = new DataSet();
+
+ }
+ }
+ });
+
+ eGpxLayer.initializeImport();
+ }
+ Main.map.mapView.repaint();
+ }
+
+ public static ImageIcon loadIcon(String name) {
+ URL url = EditGpxPlugin.class.getResource("/images/editgpx.png");
+ return new ImageIcon(url);
+ }
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java
new file mode 100644
index 0000000..be1d608
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java
@@ -0,0 +1,143 @@
+/**
+ * License: GPL. Copyright 2008. Martin Garbe (leo at running-sheep dot com)
+ *
+ * other source from mesurement plugin written by Raphael Mack
+ *
+ */
+package org.openstreetmap.josm.plugins.editgpx;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.Collection;
+
+import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Import GPX data from available layers
+ *
+ *
+ */
+class GPXLayerImportAction extends AbstractAction {
+
+
+ private static final long serialVersionUID = 5794897888911798168L;
+ private DataSet dataSet;
+ public Object importing = new Object(); //used for synchronization
+
+ public GPXLayerImportAction(DataSet ds) {
+ //TODO what is icon at the end?
+ super(tr("Import path from GPX layer"), ImageProvider.get("dialogs", "edit"));
+ this.dataSet = ds;
+ }
+
+ /**
+ * shows a list of GPX layers. if user selects one the data from this layer is
+ * imported.
+ */
+ public void activateImport() {
+ Box panel = Box.createVerticalBox();
+ DefaultListModel dModel= new DefaultListModel();
+
+ final JList layerList = new JList(dModel);
+ Collection<Layer> data = Main.map.mapView.getAllLayers();
+ Layer lastLayer = null;
+ int layerCnt = 0;
+
+ for (Layer l : data){
+ if(l instanceof GpxLayer){
+ dModel.addElement(l);
+ lastLayer = l;
+ layerCnt++;
+ }
+ }
+ if(layerCnt == 1){
+ layerList.setSelectedValue(lastLayer, true);
+ }
+ if(layerCnt > 0){
+ layerList.setCellRenderer(new DefaultListCellRenderer(){
+ @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ Layer layer = (Layer)value;
+ JLabel label = (JLabel)super.getListCellRendererComponent(list,
+ layer.getName(), index, isSelected, cellHasFocus);
+ Icon icon = layer.getIcon();
+ label.setIcon(icon);
+ label.setToolTipText(layer.getToolTipText());
+ return label;
+ }
+ });
+
+ JCheckBox dropFirst = new JCheckBox(tr("Drop existing path"));
+
+ panel.add(layerList);
+ panel.add(dropFirst);
+
+ final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
+ @Override public void selectInitialValue() {
+ layerList.requestFocusInWindow();
+ }
+ };
+ final JDialog dlg = optionPane.createDialog(Main.parent, tr("Import path from GPX layer"));
+ dlg.setVisible(true);
+
+ Object answer = optionPane.getValue();
+ if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
+ (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
+ return;
+ }
+
+ GpxLayer gpx = (GpxLayer)layerList.getSelectedValue();
+
+ synchronized(importing) {
+ for (GpxTrack trk : gpx.data.tracks) {
+ for (Collection<WayPoint> segment : trk.trackSegs) {
+ Way w = new Way();
+ for (WayPoint p : segment) {
+ Node n = new Node(p.getCoor());
+ String timestr = p.getString("time");
+ if(timestr != null)
+ n.setTimestamp(DateUtils.fromString(timestr));
+ dataSet.addPrimitive(n);
+ w.addNode(n); //TODO what to do with these while deletion
+ }
+ dataSet.addPrimitive(w);
+ }
+ }
+ }
+ Main.map.mapView.repaint();
+
+ } else {
+ // no gps layer
+ JOptionPane.showMessageDialog(Main.parent,tr("No GPX data layer found."));
+ }
+ }
+
+ /**
+ * called when pressing "Import.." from context menu of EditGpx layer
+ *
+ */
+ public void actionPerformed(ActionEvent arg0) {
+ activateImport();
+ }
+}
diff --git a/agpifoj/.classpath b/lakewalker/.classpath
similarity index 82%
copy from agpifoj/.classpath
copy to lakewalker/.classpath
index b5edc13..227cb45 100644
--- a/agpifoj/.classpath
+++ b/lakewalker/.classpath
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+ <classpathentry kind="output" path="build"/>
+</classpath>
diff --git a/lakewalker/.project b/lakewalker/.project
new file mode 100644
index 0000000..9dd9576
--- /dev/null
+++ b/lakewalker/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>josm-lakewalker</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/lakewalker/README b/lakewalker/README
new file mode 100644
index 0000000..f09b1c6
--- /dev/null
+++ b/lakewalker/README
@@ -0,0 +1,23 @@
+README FOR Lakewalker
+=======================
+
+The Lakewalker plugin can be used to generate traces of lakes, rivers or shoreline from
+LANDSAT imagery (or theoretically any imagery that is provided on a WMS server).
+
+Currently the plugin features 2 modes of operation: Native and Classic. The classic mode
+requires the Python lakewalker.py script to be downloaded into the lakewalker directory
+within the JOSM plugin directory.
+
+Common Features:
+ - On-screen feedback to Lakewalker progress with cancel
+ - Tag ways appropriately
+ - limit ways to length
+ - Manage Landsat image cache from Prefs screen
+
+Native Features:
+ - Doesn't require Python
+
+Classic Features:
+ - Access all lakewalker options via preferences
+ - Read data in thread
+ - ...
\ No newline at end of file
diff --git a/measurement/build.xml b/lakewalker/build.xml
similarity index 83%
copy from measurement/build.xml
copy to lakewalker/build.xml
index 8833eb3..e2662af 100644
--- a/measurement/build.xml
+++ b/lakewalker/build.xml
@@ -1,4 +1,4 @@
-<project name="measurement" default="dist" basedir=".">
+<project name="lakewalker" default="dist" basedir=".">
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="plugin.build.dir" value="build"/>
@@ -20,10 +20,11 @@
</copy>
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
- <attribute name="Author" value="Raphael Mack, Reza Mohammadi"/>
- <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.measurement.MeasurementPlugin"/>
+ <attribute name="Author" value="Brent Easton, Jason Reid"/>
+ <attribute name="Main-Class" value="org.openstreetmap.josm.plugins.lakewalker.LakewalkerApp"/>
+ <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.lakewalker.LakewalkerPlugin" />
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
- <attribute name="Plugin-Description" value="Provide a measurement dialog and a layer to measure length and angle of segments, area surrounded by a (simple) closed way and create measurement paths (which also can be imported from a gps layer)."/>
+ <attribute name="Plugin-Description" value="Helps vectorizing WMS images." />
<attribute name="Plugin-Mainversion" value="2012"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
diff --git a/lakewalker/images/cursor/modifier/lakewalker-sml.png b/lakewalker/images/cursor/modifier/lakewalker-sml.png
new file mode 100644
index 0000000..2479605
Binary files /dev/null and b/lakewalker/images/cursor/modifier/lakewalker-sml.png differ
diff --git a/lakewalker/images/lakewalker-sml.png b/lakewalker/images/lakewalker-sml.png
new file mode 100644
index 0000000..2479605
Binary files /dev/null and b/lakewalker/images/lakewalker-sml.png differ
diff --git a/lakewalker/images/preferences/lakewalker.png b/lakewalker/images/preferences/lakewalker.png
new file mode 100644
index 0000000..d951ac5
Binary files /dev/null and b/lakewalker/images/preferences/lakewalker.png differ
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/BooleanConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/BooleanConfigurer.java
new file mode 100644
index 0000000..05d90b1
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/BooleanConfigurer.java
@@ -0,0 +1,86 @@
+/*
+ * $Id: BooleanConfigurer.java 705 2005-09-15 13:24:50 +0000 (Thu, 15 Sep 2005) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+/**
+ * Configurer for Boolean values
+ */
+public class BooleanConfigurer extends Configurer {
+ private javax.swing.JCheckBox box;
+
+ public BooleanConfigurer() {
+ this(false);
+ }
+
+ public BooleanConfigurer(boolean val) {
+ this(null, "", val);
+ }
+
+ public BooleanConfigurer(String key, String name, Boolean val) {
+ super(key, name, val);
+ }
+
+ public BooleanConfigurer(String key, String name, boolean val) {
+ super(key, name, val ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ public BooleanConfigurer(String key, String name) {
+ this(key, name, Boolean.FALSE);
+ }
+
+ public String getValueString() {
+ return booleanValue().toString();
+ }
+
+ public void setValue(Object o) {
+ super.setValue(o);
+ if (box != null
+ && !o.equals(new Boolean(box.isSelected()))) {
+ box.setSelected(booleanValue().booleanValue());
+ }
+ }
+
+ public void setValue(String s) {
+ setValue(Boolean.valueOf(s));
+ }
+
+ public void setName(String s) {
+ super.setName(s);
+ if (box != null) {
+ box.setText(s);
+ }
+ }
+
+ public java.awt.Component getControls() {
+ if (box == null) {
+ box = new javax.swing.JCheckBox(getName());
+ box.setSelected(booleanValue().booleanValue());
+ box.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent e) {
+ setValue(new Boolean(box.isSelected()));
+ }
+ });
+ }
+ return box;
+ }
+
+ public Boolean booleanValue() {
+ return (Boolean) value;
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Configurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Configurer.java
new file mode 100644
index 0000000..8cdd72e
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Configurer.java
@@ -0,0 +1,145 @@
+/*
+ * $Id: Configurer.java 2175 2007-06-04 04:19:59 +0000 (Mon, 04 Jun 2007) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import java.beans.PropertyChangeListener;
+
+/**
+ * A property editor class. Wraps an Object value and provides
+ * methods for saving and restoring the Object from a String. Also
+ * includes a {@link java.awt.Component} that can be placed into a
+ * property editing window, allowing the user to change the value
+ * interactively.
+ * */
+public abstract class Configurer {
+// FIXME: maybe parameterize this so that value can have the right type
+// in subclasses?
+ public static final String NAME_PROPERTY = "Configurer.name";
+ // public static final String VALUE_PROPERTY = "value";
+
+ /** A String the uniquely identifies this property */
+ protected String key;
+ /** A String that provides a short description of the property to the user */
+ protected String name;
+ /** The value */
+ protected Object value;
+ protected java.beans.PropertyChangeSupport changeSupport;
+ /** When noUpdate is true, setting the value programmatically will not
+ * result in an update of the GUI Component */
+ protected boolean noUpdate = false;
+ /** When frozen is true, setting the value programmatically will not
+ * result in a PropertyChangeEvent being fired */
+ protected boolean frozen = false;
+
+ public Configurer(String key, String name) {
+ this(key, name, null);
+ }
+
+ public Configurer(String key, String name, Object val) {
+ this.key = key;
+ this.name = name;
+ changeSupport = new java.beans.PropertyChangeSupport(this);
+ setValue(val);
+ }
+
+ /**
+ * Unique identifier
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Plain English description of the Object
+ */
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String s) {
+ String oldName = name;
+ name = s;
+ if (!frozen) {
+ changeSupport.firePropertyChange(NAME_PROPERTY, oldName, name);
+ }
+ }
+
+ /**
+ * The Object value
+ * May be null if the Object has not been initialized
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * @return a String representation of the Object value
+ */
+ public abstract String getValueString();
+
+ /**
+ * Set the Object value
+ */
+ public void setValue(Object o) {
+ Object oldValue = getValue();
+ value = o;
+ if (!frozen) {
+ changeSupport.firePropertyChange(key, oldValue, value);
+ }
+ }
+
+ /**
+ * If true, then don't fire PropertyChangeEvents when the value is reset
+ */
+ public void setFrozen(boolean val) {
+ frozen = val;
+ }
+
+ public boolean isFrozen() {
+ return frozen;
+ }
+
+ /**
+ * Fire a PropertyChangeEvent as if the value had been set from null
+ */
+ public void fireUpdate() {
+ changeSupport.firePropertyChange(key, null, value);
+ }
+
+ /**
+ * Set the Object value from a String
+ */
+ public abstract void setValue(String s);
+
+ /**
+ * GUI interface for setting the option in an editing window
+ */
+ public abstract java.awt.Component getControls();
+
+ /**
+ * Add a listener to be notified when the Object state changes
+ */
+ public void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
+ changeSupport.addPropertyChangeListener(l);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener l) {
+ changeSupport.removePropertyChangeListener(l);
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/DoubleConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/DoubleConfigurer.java
new file mode 100644
index 0000000..4ced3f7
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/DoubleConfigurer.java
@@ -0,0 +1,63 @@
+/*
+ * $Id: DoubleConfigurer.java 5 2003-10-02 15:11:38 +0000 (Thu, 02 Oct 2003) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+/**
+ * A Configurer for Double values
+ */
+public class DoubleConfigurer extends StringConfigurer {
+
+ public DoubleConfigurer() {
+ super();
+ }
+
+ public DoubleConfigurer(String key, String name) {
+ this(key, name, new Double(0));
+ }
+
+ public DoubleConfigurer(String key, String name, Double val) {
+ super(key, name, val == null ? null : val.toString());
+ }
+
+ public void setValue(String s) {
+ Double d = null;
+ try {
+ d = Double.valueOf(s);
+ }
+ catch (NumberFormatException e) {
+ d = null;
+ }
+ if (d != null)
+ setValue(d);
+ }
+
+ public void setValue(Object o) {
+ if (!noUpdate && nameField != null && o != null) {
+ nameField.setText(o.toString());
+ }
+ super.setValue(o);
+ }
+
+ public String getValueString() {
+ if (value == null || value.equals("")) {
+ return null;
+ }
+ return value.toString();
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/IntConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/IntConfigurer.java
new file mode 100644
index 0000000..210b56b
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/IntConfigurer.java
@@ -0,0 +1,73 @@
+/*
+ * $Id: IntConfigurer.java 874 2006-03-15 14:20:56 +0000 (Wed, 15 Mar 2006) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+/**
+ * A Configurer for Integer values
+ */
+public class IntConfigurer extends StringConfigurer {
+
+ public IntConfigurer() {
+ super();
+ }
+
+ public IntConfigurer(String key, String name) {
+ this(key, name, new Integer(0));
+ }
+
+ public IntConfigurer(String key, String name, Integer val) {
+ super(key, name);
+ if (val != null) {
+ setValue(val);
+ }
+ }
+
+ public void setValue(String s) {
+ Integer i = null;
+ try {
+ i = Integer.valueOf(s);
+ }
+ catch (NumberFormatException e) {
+ i = null;
+ }
+ if (i != null) {
+ setValue(i);
+ }
+ }
+
+ public int getIntValue(int defaultValue) {
+ if (getValue() instanceof Integer) {
+ return ((Integer)getValue()).intValue();
+ }
+ else {
+ return defaultValue;
+ }
+ }
+
+ public void setValue(Object o) {
+ if (!noUpdate && nameField != null && o != null) {
+ nameField.setText(o.toString());
+ }
+ super.setValue(o);
+ }
+
+ public String getValueString() {
+ return value == null ? null : value.toString();
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Lakewalker.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Lakewalker.java
new file mode 100644
index 0000000..3903c23
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/Lakewalker.java
@@ -0,0 +1,470 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public class Lakewalker {
+ protected Collection<Command> commands = new LinkedList<Command>();
+ protected Collection<Way> ways = new ArrayList<Way>();
+ protected boolean cancel;
+
+ private int waylen;
+ private int maxnode;
+ private int threshold;
+ private double epsilon;
+ private int resolution;
+ private int tilesize;
+ private String startdir;
+ private String wmslayer;
+
+ private File workingdir;
+
+ private int[] dirslat = new int[] {0,1,1,1,0,-1,-1,-1};
+ private int[] dirslon = new int[] {1,1,0,-1,-1,-1,0,1};
+
+ double start_radius_big = 0.001;
+ double start_radius_small = 0.0002;
+
+ public Lakewalker(int waylen, int maxnode, int threshold, double epsilon, int resolution, int tilesize, String startdir, String wmslayer, File workingdir){
+ this.waylen = waylen;
+ this.maxnode = maxnode;
+ this.threshold = threshold;
+ this.epsilon = epsilon;
+ this.resolution = resolution;
+ this.tilesize = tilesize;
+ this.startdir = startdir;
+ this.wmslayer = wmslayer;
+
+ this.workingdir = workingdir;
+ }
+
+ /**
+ * east = 0
+ * northeast = 1
+ * north = 2
+ * northwest = 3
+ * west = 4
+ * southwest = 5
+ * south = 6
+ * southeast = 7
+ */
+ private int getDirectionIndex(String direction) throws ArrayIndexOutOfBoundsException{
+ int i=0;
+ if(direction.equals("East") || direction.equals("east")){
+ i = 0;
+ } else if(direction.equals("Northeast") || direction.equals("northeast")){
+ i = 1;
+ } else if(direction.equals("North") || direction.equals("north")){
+ i = 2;
+ } else if(direction.equals("Northwest") || direction.equals("northwest")){
+ i = 3;
+ } else if(direction.equals("West") || direction.equals("west")){
+ i = 4;
+ } else if(direction.equals("Southwest") || direction.equals("southwest")){
+ i = 5;
+ } else if(direction.equals("South") || direction.equals("south")){
+ i = 6;
+ } else if(direction.equals("Southeast") || direction.equals("southeast")){
+ i = 7;
+ } else {
+ throw new ArrayIndexOutOfBoundsException(tr("Direction index '{0}' not found",direction));
+ }
+ return i;
+ }
+
+ /**
+ * Do a trace
+ *
+ * @param lat
+ * @param lon
+ * @param tl_lon
+ * @param br_lon
+ * @param tl_lat
+ * @param br_lat
+ */
+ public ArrayList<double[]> trace(double lat, double lon, double tl_lon, double br_lon, double tl_lat, double br_lat, ProgressMonitor progressMonitor) throws LakewalkerException {
+
+ progressMonitor.beginTask(null);
+
+ try {
+
+ LakewalkerWMS wms = new LakewalkerWMS(this.resolution, this.tilesize, this.wmslayer, this.workingdir);
+ LakewalkerBBox bbox = new LakewalkerBBox(tl_lat,tl_lon,br_lat,br_lon);
+
+ Boolean detect_loop = false;
+
+ ArrayList<double[]> nodelist = new ArrayList<double[]>();
+
+ int[] xy = geo_to_xy(lat,lon,this.resolution);
+
+ if(!bbox.contains(lat, lon)){
+ throw new LakewalkerException(tr("The starting location was not within the bbox"));
+ }
+
+ int v;
+
+ progressMonitor.indeterminateSubTask(tr("Looking for shoreline..."));
+
+ while(true){
+ double[] geo = xy_to_geo(xy[0],xy[1],this.resolution);
+ if(bbox.contains(geo[0],geo[1])==false){
+ break;
+ }
+
+ v = wms.getPixel(xy[0], xy[1], progressMonitor.createSubTaskMonitor(0, false));
+ if(v > this.threshold){
+ break;
+ }
+
+ int delta_lat = this.dirslat[getDirectionIndex(this.startdir)];
+ int delta_lon = this.dirslon[getDirectionIndex(this.startdir)];
+
+ xy[0] = xy[0]+delta_lon;
+ xy[1] = xy[1]+delta_lat;
+
+ }
+
+ int[] startxy = new int[] {xy[0], xy[1]};
+ double[] startgeo = xy_to_geo(xy[0],xy[1],this.resolution);
+
+ //System.out.printf("Found shore at lat %.4f lon %.4f\n",lat,lon);
+
+ int last_dir = this.getDirectionIndex(this.startdir);
+
+ for(int i = 0; i < this.maxnode; i++){
+
+ // Print a counter
+ if(i % 250 == 0){
+ progressMonitor.indeterminateSubTask(tr("{0} nodes so far...",i));
+ //System.out.println(i+" nodes so far...");
+ }
+
+ // Some variables we need
+ int d;
+ int test_x=0;
+ int test_y=0;
+ int new_dir = 0;
+
+ // Loop through all the directions we can go
+ for(d = 1; d <= this.dirslat.length; d++){
+
+ // Decide which direction we want to look at from this pixel
+ new_dir = (last_dir + d + 4) % 8;
+
+ test_x = xy[0] + this.dirslon[new_dir];
+ test_y = xy[1] + this.dirslat[new_dir];
+
+ double[] geo = xy_to_geo(test_x,test_y,this.resolution);
+
+ if(!bbox.contains(geo[0], geo[1])){
+ System.out.println("Outside bbox");
+ break;
+ }
+
+ v = wms.getPixel(test_x, test_y, progressMonitor.createSubTaskMonitor(0, false));
+ if(v > this.threshold){
+ break;
+ }
+
+ if(d == this.dirslat.length-1){
+ System.out.println("Got stuck");
+ break;
+ }
+ }
+
+ // Remember this direction
+ last_dir = new_dir;
+
+ // Set the pixel we found as current
+ xy[0] = test_x;
+ xy[1] = test_y;
+
+ // Break the loop if we managed to get back to our starting point
+ if(xy[0] == startxy[0] && xy[1] == startxy[1]){
+ break;
+ }
+
+ // Store this node
+ double[] geo = xy_to_geo(xy[0],xy[1],this.resolution);
+ nodelist.add(geo);
+ //System.out.println("Adding node at "+xy[0]+","+xy[1]+" ("+geo[1]+","+geo[0]+")");
+
+ // Check if we got stuck in a loop
+ double start_proximity = Math.pow((geo[0] - startgeo[0]),2) + Math.pow((geo[1] - startgeo[1]),2);
+
+ if(detect_loop){
+ if(start_proximity < Math.pow(start_radius_small,2)){
+ System.out.println("Detected loop");
+ break;
+ }
+ }else{
+ if(start_proximity > Math.pow(start_radius_big,2)){
+ detect_loop = true;
+ }
+ }
+ }
+
+ return nodelist;
+ } finally {
+ progressMonitor.finishTask();
+ }
+ }
+
+ /**
+ * Remove duplicate nodes from the list
+ *
+ * @param nodes
+ * @return
+ */
+ public ArrayList<double[]> duplicateNodeRemove(ArrayList<double[]> nodes){
+
+ if(nodes.size() <= 1){
+ return nodes;
+ }
+
+ double lastnode[] = new double[] {nodes.get(0)[0], nodes.get(0)[1]};
+
+ for(int i = 1; i < nodes.size(); i++){
+ double[] thisnode = new double[] {nodes.get(i)[0], nodes.get(i)[1]};
+
+ if(thisnode[0] == lastnode[0] && thisnode[1] == lastnode[1]){
+ // Remove the node
+ nodes.remove(i);
+
+ // Shift back one index
+ i = i - 1;
+ }
+ lastnode = thisnode;
+ }
+
+ return nodes;
+ }
+
+ /**
+ * Reduce the number of vertices based on their proximity to each other
+ *
+ * @param nodes
+ * @param proximity
+ * @return
+ */
+ public ArrayList<double[]> vertexReduce(ArrayList<double[]> nodes, double proximity){
+
+ // Check if node list is empty
+ if(nodes.size()<=1){
+ return nodes;
+ }
+
+ double[] test_v = nodes.get(0);
+ ArrayList<double[]> reducednodes = new ArrayList<double[]>();
+
+ double prox_sq = Math.pow(proximity, 2);
+
+ for(int v = 0; v < nodes.size(); v++){
+ if(Math.pow(nodes.get(v)[0] - test_v[0],2) + Math.pow(nodes.get(v)[1] - test_v[1],2) > prox_sq){
+ reducednodes.add(nodes.get(v));
+ test_v = nodes.get(v);
+ }
+ }
+
+ return reducednodes;
+ }
+
+ public double pointLineDistance(double[] p1, double[] p2, double[] p3){
+
+ double x0 = p1[0];
+ double y0 = p1[1];
+ double x1 = p2[0];
+ double y1 = p2[1];
+ double x2 = p3[0];
+ double y2 = p3[1];
+
+ if(x2 == x1 && y2 == y1){
+ return Math.sqrt(Math.pow(x1-x0,2) + Math.pow(y1-y0,2));
+ } else {
+ return Math.abs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2));
+ }
+ }
+
+ /*
+ public ArrayList<double[]> douglasPeuckerNR(ArrayList<double[]> nodes, double epsilon){
+ command_stack = [(0, len(nodes) - 1)]
+
+ Vector result_stack = new Vector();
+
+ while(command_stack.size() > 0){
+ cmd = command_stack.pop();
+ if(type(cmd) == tuple){
+ (start, end) = cmd
+ (node, dist) = dp_findpoint(nodes, start, end)
+ if(dist > epsilon){
+ command_stack.append("+")
+ command_stack.append((start, node))
+ command_stack.append((node, end))
+ } else {
+ result_stack.append((start, end))
+ }
+ } elseif(cmd == "+"){
+ first = result_stack.pop()
+ second = result_stack.pop()
+ if(first[-1] == second[0]){
+ result_stack.append(first + second[1:])
+ //print "Added %s and %s; result is %s" % (first, second, result_stack[-1])
+ }else {
+ error("ERROR: Cannot connect nodestrings!")
+ #print first
+ #print second
+ return;
+ }
+ } else {
+ error("ERROR: Can't understand command \"%s\"" % (cmd,))
+ return
+
+ if(len(result_stack) == 1){
+ return [nodes[x] for x in result_stack[0]];
+ } else {
+ error("ERROR: Command stack is empty but result stack has %d nodes!" % len(result_stack));
+ return;
+ }
+
+ farthest_node = None
+ farthest_dist = 0
+ first = nodes[0]
+ last = nodes[-1]
+
+ for(i in xrange(1, len(nodes) - 1){
+ d = point_line_distance(nodes[i], first, last)
+ if(d > farthest_dist){
+ farthest_dist = d
+ farthest_node = i
+ }
+ }
+ if(farthest_dist > epsilon){
+ seg_a = douglas_peucker(nodes[0:farthest_node+1], epsilon)
+ seg_b = douglas_peucker(nodes[farthest_node:-1], epsilon)
+ //print "Minimized %d nodes to %d + %d nodes" % (len(nodes), len(seg_a), len(seg_b))
+ nodes = seg_a[:-1] + seg_b
+ } else {
+ return [nodes[0], nodes[-1]];
+ }
+ return nodes;
+ }
+ */
+
+ public ArrayList<double[]> douglasPeucker(ArrayList<double[]> nodes, double epsilon, int depth){
+
+ // Check if node list is empty
+ if(nodes.size()<=1 || depth > 500){
+ return nodes;
+ }
+
+ int farthest_node = -1;
+ double farthest_dist = 0;
+ double[] first = nodes.get(0);
+ double[] last = nodes.get(nodes.size()-1);
+
+ ArrayList<double[]> new_nodes = new ArrayList<double[]>();
+
+ double d = 0;
+
+ for(int i = 1; i < nodes.size(); i++){
+ d = pointLineDistance(nodes.get(i),first,last);
+ if(d>farthest_dist){
+ farthest_dist = d;
+ farthest_node = i;
+ }
+ }
+
+ ArrayList<double[]> seg_a = new ArrayList<double[]>();
+ ArrayList<double[]> seg_b = new ArrayList<double[]>();
+
+ if(farthest_dist > epsilon){
+ seg_a = douglasPeucker(sublist(nodes,0,farthest_node+1),epsilon, depth+1);
+ seg_b = douglasPeucker(sublist(nodes,farthest_node,nodes.size()-1),epsilon,depth+1);
+
+ new_nodes.addAll(seg_a);
+ new_nodes.addAll(seg_b);
+ } else {
+ new_nodes.add(nodes.get(0));
+ new_nodes.add(nodes.get(nodes.size()-1));
+ }
+ return new_nodes;
+ }
+
+ private ArrayList<double[]> sublist(ArrayList<double[]> l, int i, int f) throws ArrayIndexOutOfBoundsException {
+ ArrayList<double[]> sub = new ArrayList<double[]>();
+
+ if(f<i || i < 0 || f < 0 || f > l.size()){
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ for(int j = i; j < f; j++){
+ sub.add(l.get(j));
+ }
+ return sub;
+ }
+
+ public double[] xy_to_geo(int x, int y, double resolution){
+ double[] geo = new double[2];
+ geo[0] = y / resolution;
+ geo[1] = x / resolution;
+ return geo;
+ }
+
+ public int[] geo_to_xy(double lat, double lon, double resolution){
+ int[] xy = new int[2];
+
+ xy[0] = (int)Math.floor(lon * resolution + 0.5);
+ xy[1] = (int)Math.floor(lat * resolution + 0.5);
+
+ return xy;
+ }
+
+ /*
+ * User has hit the cancel button
+ */
+ public void cancel() {
+ cancel = true;
+ }
+
+ /**
+ * Class to do checking of whether the point is within our bbox
+ *
+ * @author Jason Reid
+ */
+ private class LakewalkerBBox {
+
+ private double top = 90;
+ private double left = -180;
+ private double bottom = -90;
+ private double right = 180;
+
+ protected LakewalkerBBox(double top, double left, double bottom, double right){
+ this.left = left;
+ this.right = right;
+ this.top = top;
+ this.bottom = bottom;
+ }
+
+ protected Boolean contains(double lat, double lon){
+ if(lat >= this.top || lat <= this.bottom){
+ return false;
+ }
+ if(lon >= this.right || lon <= this.left){
+ return false;
+ }
+ if((this.right - this.left) % 360 == 0){
+ return true;
+ }
+ return (lon - this.left) % 360 <= (this.right - this.left) % 360;
+ }
+ }
+}
+
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerAction.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerAction.java
new file mode 100644
index 0000000..0524c97
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerAction.java
@@ -0,0 +1,324 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Cursor;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.xml.sax.SAXException;
+
+/**
+ * Interface to Darryl Shpak's Lakewalker module
+ *
+ * @author Brent Easton
+ */
+class LakewalkerAction extends JosmAction implements MouseListener {
+
+ private static final long serialVersionUID = 1L;
+ protected String name;
+ protected Cursor oldCursor;
+ protected Thread executeThread;
+ protected boolean cancel;
+ protected boolean active = false;
+
+ protected Collection<Command> commands = new LinkedList<Command>();
+ protected Collection<Way> ways = new ArrayList<Way>();
+
+ public LakewalkerAction(String name) {
+ super(name, "lakewalker-sml", tr("Lake Walker."),
+ Shortcut.registerShortcut("tools:lakewalker", tr("Tool: {0}", tr("Lake Walker")),
+ KeyEvent.VK_L, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
+ this.name = name;
+ setEnabled(true);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if(Main.map == null || Main.map.mapView == null || active)
+ return;
+
+ active = true;
+ oldCursor = Main.map.mapView.getCursor();
+ Main.map.mapView.setCursor(ImageProvider.getCursor("crosshair", "lakewalker-sml"));
+ Main.map.mapView.addMouseListener(this);
+ }
+
+ /**
+ * check for presence of cache folder and trim cache to specified size.
+ * size/age limit is on a per folder basis.
+ */
+ private void cleanupCache() {
+ final long maxCacheAge = System.currentTimeMillis()-Main.pref.getInteger(LakewalkerPreferences.PREF_MAXCACHEAGE, 100)*24*60*60*1000L;
+ final long maxCacheSize = Main.pref.getInteger(LakewalkerPreferences.PREF_MAXCACHESIZE, 300)*1024*1024;
+
+ for (String wmsFolder : LakewalkerPreferences.WMSLAYERS) {
+ String wmsCacheDirName = Main.pref.getPreferencesDir()+"plugins/Lakewalker/"+wmsFolder;
+ File wmsCacheDir = new File(wmsCacheDirName);
+
+ if (wmsCacheDir.exists() && wmsCacheDir.isDirectory()) {
+ File wmsCache[] = wmsCacheDir.listFiles();
+
+ // sort files by date (most recent first)
+ Arrays.sort(wmsCache, new Comparator<File>() {
+ public int compare(File f1, File f2) {
+ return (int)(f2.lastModified()-f1.lastModified());
+ }
+ });
+
+ // delete aged or oversized, keep newest. Once size/age limit was reached delete all older files
+ long folderSize = 0;
+ boolean quickdelete = false;
+ for (File cacheEntry : wmsCache) {
+ if (!cacheEntry.isFile()) continue;
+ if (!quickdelete) {
+ folderSize += cacheEntry.length();
+ if (folderSize > maxCacheSize) {
+ quickdelete = true;
+ } else if (cacheEntry.lastModified() < maxCacheAge) {
+ quickdelete = true;
+ }
+ }
+
+ if (quickdelete) {
+ cacheEntry.delete();
+ }
+ }
+
+ } else {
+ // create cache directory
+ if (!wmsCacheDir.mkdirs()) {
+ JOptionPane.showMessageDialog(Main.parent, tr("Error creating cache directory: {0}", wmsCacheDirName));
+ }
+ }
+ }
+ }
+
+ protected void lakewalk(Point clickPoint){
+ /**
+ * Positional data
+ */
+ final LatLon pos = Main.map.mapView.getLatLon(clickPoint.x, clickPoint.y);
+ final LatLon topLeft = Main.map.mapView.getLatLon(0, 0);
+ final LatLon botRight = Main.map.mapView.getLatLon(Main.map.mapView.getWidth(),
+ Main.map.mapView.getHeight());
+
+ /**
+ * Cache/working directory location
+ */
+ final File working_dir = new File(Main.pref.getPreferencesDir(), "plugins/Lakewalker");
+
+ /*
+ * Collect options
+ */
+ final int waylen = Main.pref.getInteger(LakewalkerPreferences.PREF_MAX_SEG, 500);
+ final int maxnode = Main.pref.getInteger(LakewalkerPreferences.PREF_MAX_NODES, 50000);
+ final int threshold = Main.pref.getInteger(LakewalkerPreferences.PREF_THRESHOLD_VALUE, 90);
+ final double epsilon = Main.pref.getDouble(LakewalkerPreferences.PREF_EPSILON, 0.0003);
+ final int resolution = Main.pref.getInteger(LakewalkerPreferences.PREF_LANDSAT_RES, 4000);
+ final int tilesize = Main.pref.getInteger(LakewalkerPreferences.PREF_LANDSAT_SIZE, 2000);
+ final String startdir = Main.pref.get(LakewalkerPreferences.PREF_START_DIR, "east");
+ final String wmslayer = Main.pref.get(LakewalkerPreferences.PREF_WMS, "IR1");
+
+ try {
+ PleaseWaitRunnable lakewalkerTask = new PleaseWaitRunnable(tr("Tracing")){
+ @Override protected void realRun() throws SAXException {
+ progressMonitor.subTask(tr("checking cache..."));
+ cleanupCache();
+ processnodelist(pos, topLeft, botRight, waylen, maxnode, threshold,
+ epsilon,resolution,tilesize,startdir,wmslayer, working_dir,
+ progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+ }
+ @Override protected void finish() {
+ }
+
+ @Override protected void cancel() {
+ LakewalkerAction.this.cancel();
+ }
+ };
+ Thread executeThread = new Thread(lakewalkerTask);
+ executeThread.start();
+ }
+ catch (Exception ex) {
+ System.out.println("Exception caught: " + ex.getMessage());
+ }
+ }
+
+ private void processnodelist(LatLon pos, LatLon topLeft, LatLon botRight, int waylen, int maxnode, int threshold, double epsilon, int resolution, int tilesize, String startdir, String wmslayer, File workingdir, ProgressMonitor progressMonitor){
+ progressMonitor.beginTask(null, 3);
+ try {
+ ArrayList<double[]> nodelist = new ArrayList<double[]>();
+
+ Lakewalker lw = new Lakewalker(waylen,maxnode,threshold,epsilon,resolution,tilesize,startdir,wmslayer,workingdir);
+ try {
+ nodelist = lw.trace(pos.lat(),pos.lon(),topLeft.lon(),botRight.lon(),topLeft.lat(),botRight.lat(),
+ progressMonitor.createSubTaskMonitor(1, false));
+ } catch(LakewalkerException e){
+ System.out.println(e.getError());
+ }
+
+ System.out.println(nodelist.size()+" nodes generated");
+
+ /**
+ * Run the nodelist through a vertex reduction algorithm
+ */
+
+ progressMonitor.subTask(tr("Running vertex reduction..."));
+
+ nodelist = lw.vertexReduce(nodelist, epsilon);
+
+ //System.out.println("After vertex reduction "+nodelist.size()+" nodes remain.");
+
+ /**
+ * And then through douglas-peucker approximation
+ */
+
+ progressMonitor.worked(1);
+ progressMonitor.subTask(tr("Running Douglas-Peucker approximation..."));
+
+ nodelist = lw.douglasPeucker(nodelist, epsilon, 0);
+
+ //System.out.println("After Douglas-Peucker approximation "+nodelist.size()+" nodes remain.");
+
+ /**
+ * And then through a duplicate node remover
+ */
+
+ progressMonitor.worked(1);
+ progressMonitor.subTask(tr("Removing duplicate nodes..."));
+
+ nodelist = lw.duplicateNodeRemove(nodelist);
+
+ //System.out.println("After removing duplicate nodes, "+nodelist.size()+" nodes remain.");
+
+
+ // if for some reason (image loading failed, ...) nodelist is empty, no more processing required.
+ if (nodelist.size() == 0) {
+ return;
+ }
+
+ /**
+ * Turn the arraylist into osm nodes
+ */
+
+ Way way = new Way();
+ Node n = null;
+ Node fn = null;
+
+ double eastOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_EAST_OFFSET, 0.0);
+ double northOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_NORTH_OFFSET, 0.0);
+
+ int nodesinway = 0;
+
+ for(int i = 0; i< nodelist.size(); i++){
+ if (cancel) {
+ return;
+ }
+
+ try {
+ LatLon ll = new LatLon(nodelist.get(i)[0]+northOffset, nodelist.get(i)[1]+eastOffset);
+ n = new Node(ll);
+ if(fn==null){
+ fn = n;
+ }
+ commands.add(new AddCommand(n));
+
+ } catch (Exception ex) {
+ }
+
+ way.addNode(n);
+
+ if(nodesinway > Main.pref.getInteger(LakewalkerPreferences.PREF_MAX_SEG, 500)){
+ String waytype = Main.pref.get(LakewalkerPreferences.PREF_WAYTYPE, "water");
+
+ if(!waytype.equals("none")){
+ way.put("natural",waytype);
+ }
+
+ way.put("source", Main.pref.get(LakewalkerPreferences.PREF_SOURCE, "Landsat"));
+ commands.add(new AddCommand(way));
+
+ way = new Way();
+
+ way.addNode(n);
+
+ nodesinway = 0;
+ }
+ nodesinway++;
+ }
+
+
+ String waytype = Main.pref.get(LakewalkerPreferences.PREF_WAYTYPE, "water");
+
+ if(!waytype.equals("none")){
+ way.put("natural",waytype);
+ }
+
+ way.put("source", Main.pref.get(LakewalkerPreferences.PREF_SOURCE, "Landsat"));
+
+ way.addNode(fn);
+
+ commands.add(new AddCommand(way));
+
+ if (!commands.isEmpty()) {
+ Main.main.undoRedo.add(new SequenceCommand(tr("Lakewalker trace"), commands));
+ Main.main.getCurrentDataSet().setSelected(ways);
+ } else {
+ System.out.println("Failed");
+ }
+
+ commands = new LinkedList<Command>();
+ ways = new ArrayList<Way>();
+ } finally {
+ progressMonitor.finishTask();
+ }
+ }
+
+ public void cancel() {
+ cancel = true;
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ if(active)
+ {
+ active = false;
+ Main.map.mapView.removeMouseListener(this);
+ Main.map.mapView.setCursor(oldCursor);
+ lakewalk(e.getPoint());
+ }
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerApp.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerApp.java
new file mode 100644
index 0000000..11560c7
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerApp.java
@@ -0,0 +1,59 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+
+public class LakewalkerApp {
+ public static void main(String[] args){
+ double lat = 52.31384;
+ double lon = -79.135;
+ double toplat = 52.3165;
+ double botlat = 52.3041;
+ double leftlon = -79.1442;
+ double rightlon = -79.1093;
+
+ // ?lat=39.15579999999999&lon=2.9411&zoom=12&layers=B000F000F
+ lat = 39.1422;
+ lon = 2.9102;
+
+ toplat = 39.2229;
+ botlat = 39.0977;
+ leftlon = 2.8560;
+ rightlon = 3.0462;
+
+ int waylen = 250;
+ int maxnode = 5000;
+ int threshold = 100;
+ double dp = 0.0003;
+ int tilesize = 2000;
+ int resolution = 4000;
+ String startdir = "East";
+ String wmslayer = "IR2";
+
+ File working_dir = new File("Lakewalker");
+
+ ArrayList<double[]> nodelist = null;
+
+ Lakewalker lw = new Lakewalker(waylen,maxnode,threshold,dp,resolution,tilesize,startdir,wmslayer,working_dir);
+ try {
+ nodelist = lw.trace(lat,lon,leftlon,rightlon,toplat,botlat, NullProgressMonitor.INSTANCE);
+ } catch(LakewalkerException e){
+ System.out.println(e.getError());
+ }
+
+ System.out.println(nodelist.size()+" nodes generated");
+
+ nodelist = lw.vertexReduce(nodelist, dp);
+
+ System.out.println("After vertex reduction, "+nodelist.size()+" nodes remain.");
+
+ nodelist = lw.douglasPeucker(nodelist, dp, 0);
+
+ System.out.println("After dp approximation, "+nodelist.size()+" nodes remain.");
+
+
+
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerException.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerException.java
new file mode 100644
index 0000000..48409a7
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerException.java
@@ -0,0 +1,21 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+class LakewalkerException extends Exception {
+ String error;
+
+ public LakewalkerException(){
+ super();
+ this.error = tr("An unknown error has occurred");
+ }
+
+ public LakewalkerException(String err){
+ super();
+ this.error = err;
+ }
+
+ public String getError(){
+ return this.error;
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPlugin.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPlugin.java
new file mode 100644
index 0000000..b4c8395
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPlugin.java
@@ -0,0 +1,25 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+
+/**
+ * Interface to Darryl Shpak's Lakewalker python module
+ *
+ * @author Brent Easton
+ */
+public class LakewalkerPlugin extends Plugin {
+ public LakewalkerPlugin() {
+ MainMenu.add(Main.main.menu.toolsMenu, new LakewalkerAction(tr("Lake Walker")));
+ }
+
+ public PreferenceSetting getPreferenceSetting()
+ {
+ return new LakewalkerPreferences();
+ }
+
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPreferences.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPreferences.java
new file mode 100644
index 0000000..a4f5f3d
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerPreferences.java
@@ -0,0 +1,160 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import javax.swing.Box;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.I18n;
+
+public class LakewalkerPreferences implements PreferenceSetting {
+ public static final String[] DIRECTIONS = new String[]
+ {marktr("east"), marktr("northeast"), marktr("north"), marktr("northwest"),
+ marktr("west"), marktr("southwest"), marktr("south"), marktr("southeast")};
+ public static final String[] WAYTYPES = new String[]
+ {marktr("water"), marktr("coastline"), marktr("land"), marktr("none")};
+ public static final String[] WMSLAYERS = new String[] {"IR1", "IR2", "IR3"};
+
+ public static final String PREF_MAX_SEG = "lakewalker.max_segs_in_way";
+ public static final String PREF_MAX_NODES = "lakewalker.max_nodes";
+ public static final String PREF_THRESHOLD_VALUE = "lakewalker.thresholdvalue";
+ public static final String PREF_EPSILON = "lakewalker.epsilon";
+ public static final String PREF_LANDSAT_RES = "lakewalker.landsat_res";
+ public static final String PREF_LANDSAT_SIZE = "lakewalker.landsat_size";
+ public static final String PREF_EAST_OFFSET = "lakewalker.east_offset";
+ public static final String PREF_NORTH_OFFSET = "lakewalker.north_offset";
+ public static final String PREF_START_DIR = "lakewalker.startdir";
+ public static final String PREF_WAYTYPE = "lakewalker.waytype";
+ public static final String PREF_WMS = "lakewalker.wms";
+ public static final String PREF_SOURCE = "lakewalker.source";
+ public static final String PREF_MAXCACHESIZE = "lakewalker.maxcachesize";
+ public static final String PREF_MAXCACHEAGE = "lakewalker.maxcacheage";
+
+ protected IntConfigurer maxSegsConfig = new IntConfigurer();
+ protected JLabel maxSegsLabel = new JLabel(tr("Maximum number of segments per way"));
+ protected IntConfigurer maxNodesConfig = new IntConfigurer();
+ protected JLabel maxNodesLabel = new JLabel(tr("Maximum number of nodes in initial trace"));
+ protected IntConfigurer thresholdConfig = new IntConfigurer();
+ protected JLabel thresholdLabel = new JLabel(tr("Maximum gray value to count as water (0-255)"));
+ protected DoubleConfigurer epsilonConfig = new DoubleConfigurer();
+ protected JLabel epsilonLabel = new JLabel(tr("Line simplification accuracy (degrees)"));
+ protected IntConfigurer landsatResConfig = new IntConfigurer();
+ protected JLabel landsatResLabel = new JLabel(tr("Resolution of Landsat tiles (pixels per degree)"));
+ protected IntConfigurer landsatSizeConfig = new IntConfigurer();
+ protected JLabel landsatSizeLabel = new JLabel(tr("Size of Landsat tiles (pixels)"));
+ protected DoubleConfigurer eastOffsetConfig = new DoubleConfigurer();
+ protected JLabel eastOffsetLabel = new JLabel(tr("Shift all traces to east (degrees)"));
+ protected DoubleConfigurer northOffsetConfig = new DoubleConfigurer();
+ protected JLabel northOffsetLabel = new JLabel(tr("Shift all traces to north (degrees)"));
+ protected StringEnumConfigurer startDirConfig = new StringEnumConfigurer(DIRECTIONS);
+ protected JLabel startDirLabel = new JLabel(tr("Direction to search for land"));
+ protected StringEnumConfigurer lakeTypeConfig = new StringEnumConfigurer(WAYTYPES);
+ protected JLabel lakeTypeLabel = new JLabel(tr("Tag ways as"));
+ protected StringEnumConfigurer wmsConfig = new StringEnumConfigurer(WMSLAYERS);
+ protected JLabel wmsLabel = new JLabel(tr("WMS Layer"));
+ protected IntConfigurer maxCacheSizeConfig = new IntConfigurer();
+ protected JLabel maxCacheSizeLabel = new JLabel(tr("Maximum cache size (MB)"));
+ protected IntConfigurer maxCacheAgeConfig = new IntConfigurer();
+ protected JLabel maxCacheAgeLabel = new JLabel(tr("Maximum cache age (days)"));
+ protected StringConfigurer sourceConfig = new StringConfigurer();
+ protected JLabel sourceLabel = new JLabel(tr("Source text"));
+
+ public void addGui(PreferenceDialog gui) {
+ maxSegsConfig.setToolTipText(tr("Maximum number of segments allowed in each generated way. Default 250."));
+ maxNodesConfig.setToolTipText(tr("Maximum number of nodes to generate before bailing out (before simplifying lines). Default 50000."));
+ thresholdConfig.setToolTipText(tr("Maximum gray value to accept as water (based on Landsat IR-1 data). Can be in the range 0-255. Default 90."));
+ epsilonConfig.setToolTipText(tr("Accuracy of Douglas-Peucker line simplification, measured in degrees.<br>Lower values give more nodes, and more accurate lines. Default 0.0003."));
+ landsatResConfig.setToolTipText(tr("Resolution of Landsat tiles, measured in pixels per degree. Default 4000."));
+ landsatSizeConfig.setToolTipText(tr("Size of one landsat tile, measured in pixels. Default 2000."));
+ eastOffsetConfig.setToolTipText(tr("Offset all points in East direction (degrees). Default 0."));
+ northOffsetConfig.setToolTipText(tr("Offset all points in North direction (degrees). Default 0."));
+ startDirConfig.setToolTipText(tr("Direction to search for land. Default east."));
+ lakeTypeConfig.setToolTipText(tr("Tag ways as water, coastline, land or nothing. Default is water."));
+ wmsConfig.setToolTipText(tr("Which WMS layer to use for tracing against. Default is IR1."));
+ maxCacheSizeConfig.setToolTipText(tr("Maximum size of each cache directory in bytes. Default is 300MB"));
+ maxCacheAgeConfig.setToolTipText(tr("Maximum age of each cached file in days. Default is 100"));
+ sourceConfig.setToolTipText(tr("Data source text. Default is Landsat."));
+
+ String description = tr("A plugin to trace water bodies on Landsat imagery.");
+ JPanel prefPanel = gui.createPreferenceTab("lakewalker.png", I18n.tr("Lakewalker Plugin Preferences"), description);
+ buildPreferences(prefPanel);
+
+ maxSegsConfig.setValue(Main.pref.getInteger(PREF_MAX_SEG, 500));
+ maxNodesConfig.setValue(Main.pref.getInteger(PREF_MAX_NODES, 50000));
+ thresholdConfig.setValue(Main.pref.getInteger(PREF_THRESHOLD_VALUE, 90));
+ epsilonConfig.setValue(Main.pref.getDouble(PREF_EPSILON, 0.0003));
+ landsatResConfig.setValue(Main.pref.getInteger(PREF_LANDSAT_RES, 4000));
+ landsatSizeConfig.setValue(Main.pref.getInteger(PREF_LANDSAT_SIZE, 2000));
+ eastOffsetConfig.setValue(Main.pref.getDouble(PREF_EAST_OFFSET, 0.0));
+ northOffsetConfig.setValue(Main.pref.getDouble(PREF_NORTH_OFFSET, 0.0));
+ startDirConfig.setValue(Main.pref.get(PREF_START_DIR, "east"));
+ lakeTypeConfig.setValue(Main.pref.get(PREF_WAYTYPE, "water"));
+ wmsConfig.setValue(Main.pref.get(PREF_WMS, "IR1"));
+ sourceConfig.setValue(Main.pref.get(PREF_SOURCE, "Landsat"));
+ maxCacheSizeConfig.setValue(Main.pref.getInteger(PREF_MAXCACHESIZE, 300));
+ maxCacheAgeConfig.setValue(Main.pref.getInteger(PREF_MAXCACHEAGE, 100));
+ }
+
+ public void buildPreferences(JPanel prefPanel) {
+ GBC labelConstraints = GBC.std().insets(10,5,5,0);
+ GBC dataConstraints = GBC.eol().insets(0,5,0,0).fill(GBC.HORIZONTAL);
+
+ prefPanel.add(maxSegsLabel, labelConstraints);
+ prefPanel.add(maxSegsConfig.getControls(), dataConstraints);
+ prefPanel.add(maxNodesLabel, labelConstraints);
+ prefPanel.add(maxNodesConfig.getControls(), dataConstraints);
+ prefPanel.add(thresholdLabel, labelConstraints);
+ prefPanel.add(thresholdConfig.getControls(), dataConstraints);
+ prefPanel.add(epsilonLabel, labelConstraints);
+ prefPanel.add(epsilonConfig.getControls(), dataConstraints);
+ prefPanel.add(landsatResLabel, labelConstraints);
+ prefPanel.add(landsatResConfig.getControls(), dataConstraints);
+ prefPanel.add(landsatSizeLabel, labelConstraints);
+ prefPanel.add(landsatSizeConfig.getControls(), dataConstraints);
+ prefPanel.add(eastOffsetLabel, labelConstraints);
+ prefPanel.add(eastOffsetConfig.getControls(), dataConstraints);
+ prefPanel.add(northOffsetLabel, labelConstraints);
+ prefPanel.add(northOffsetConfig.getControls(), dataConstraints);
+ prefPanel.add(startDirLabel, labelConstraints);
+ prefPanel.add(startDirConfig.getControls(), dataConstraints);
+ prefPanel.add(lakeTypeLabel, labelConstraints);
+ prefPanel.add(lakeTypeConfig.getControls(), dataConstraints);
+ prefPanel.add(wmsLabel, labelConstraints);
+ prefPanel.add(wmsConfig.getControls(), dataConstraints);
+ prefPanel.add(maxCacheSizeLabel, labelConstraints);
+ prefPanel.add(maxCacheSizeConfig.getControls(), dataConstraints);
+ prefPanel.add(maxCacheAgeLabel, labelConstraints);
+ prefPanel.add(maxCacheAgeConfig.getControls(), dataConstraints);
+ prefPanel.add(sourceLabel, labelConstraints);
+ prefPanel.add(sourceConfig.getControls(), dataConstraints);
+
+ prefPanel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+ }
+
+ /*
+ * Save entered preference values on OK button
+ */
+ public boolean ok() {
+ Main.pref.put(PREF_MAX_SEG, maxSegsConfig.getValueString());
+ Main.pref.put(PREF_MAX_NODES, maxNodesConfig.getValueString());
+ Main.pref.put(PREF_THRESHOLD_VALUE, thresholdConfig.getValueString());
+ Main.pref.put(PREF_EPSILON, epsilonConfig.getValueString());
+ Main.pref.put(PREF_LANDSAT_RES, landsatResConfig.getValueString());
+ Main.pref.put(PREF_LANDSAT_SIZE, landsatSizeConfig.getValueString());
+ Main.pref.put(PREF_EAST_OFFSET, eastOffsetConfig.getValueString());
+ Main.pref.put(PREF_NORTH_OFFSET, northOffsetConfig.getValueString());
+ Main.pref.put(PREF_START_DIR, startDirConfig.getValueString());
+ Main.pref.put(PREF_WAYTYPE, lakeTypeConfig.getValueString());
+ Main.pref.put(PREF_WMS, wmsConfig.getValueString());
+ Main.pref.put(PREF_MAXCACHESIZE, maxCacheSizeConfig.getValueString());
+ Main.pref.put(PREF_MAXCACHEAGE, maxCacheAgeConfig.getValueString());
+ Main.pref.put(PREF_SOURCE, sourceConfig.getValueString());
+ return false;
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerReader.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerReader.java
new file mode 100644
index 0000000..4add238
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerReader.java
@@ -0,0 +1,128 @@
+/* LakewalkerReader.java
+ *
+ * Read and process data from a Lakwalker python module
+ *
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public class LakewalkerReader {
+ protected Collection<Command> commands = new LinkedList<Command>();
+ protected Collection<Way> ways = new ArrayList<Way>();
+ protected boolean cancel;
+
+ /*
+ * Read the data
+ */
+ public void read(BufferedReader input, ProgressMonitor progressMonitor) {
+ /*
+ * Lakewalker will output data it stdout. Each line has a code in
+ * character 1 indicating the type of data on the line:
+ *
+ * m text - Status message l name [size] - Access landsat image name. size
+ * is returned if it needs to be downloaded. e text - Error message s nnn -
+ * Start node data stream, nnn seperate tracings to follow t nnn - Start
+ * tracing, nnn nodes to follow x [o] - End of Tracing. o indicates do not
+ * connect last node to first n lat lon [o] - Node. o indicates it is an
+ * open node (not connected to the previous node) z - End of data stream
+ */
+
+ progressMonitor.beginTask(null);
+ try {
+ Way way = new Way();
+ String line;
+ progressMonitor.indeterminateSubTask(tr("Initializing"));
+ double eastOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_EAST_OFFSET, 0.0);
+ double northOffset = Main.pref.getDouble(LakewalkerPreferences.PREF_NORTH_OFFSET, 0.0);
+ char option = ' ';
+
+ try {
+ Node n = null; // The current node being created
+ Node tn = null; // The last node of the previous way
+ Node fn = null; // Node to hold the first node in the trace
+
+ while ((line = input.readLine()) != null) {
+ if (cancel)
+ return;
+ System.out.println(line);
+ option = line.charAt(0);
+ switch (option) {
+ case 'n':
+ String[] tokens = line.split(" ");
+
+ if(tn==null){
+ try {
+ LatLon ll = new LatLon(Double.parseDouble(tokens[1])+northOffset,
+ Double.parseDouble(tokens[2])+eastOffset);
+ n = new Node(ll);
+ if(fn==null)
+ fn = n;
+ commands.add(new AddCommand(n));
+ }
+ catch (Exception ex) {}
+ } else {
+ // If there is a last node, and this node has the same coordinates
+ // then we substitute for the previous node
+ n = tn;
+ tn = null;
+ }
+ way.addNode(n);
+ break;
+ case 's':
+ progressMonitor.indeterminateSubTask(line.substring(2));
+ break;
+ case 'x':
+ String waytype = Main.pref.get(LakewalkerPreferences.PREF_WAYTYPE, "water");
+
+ if(!waytype.equals("none"))
+ way.put("natural",waytype);
+ way.put("source", Main.pref.get(LakewalkerPreferences.PREF_SOURCE, "Landsat"));
+ commands.add(new AddCommand(way));
+ break;
+ case 't':
+ way = new Way();
+ tn = n;
+ break;
+ case 'e':
+ cancel = true;
+ break;
+ }
+ }
+ input.close();
+
+ // Add the start node to the end of the trace to form a closed shape
+ way.addNode(fn);
+ }
+ catch (Exception ex) { }
+
+ if (!commands.isEmpty()) {
+ Main.main.undoRedo.add(new SequenceCommand(tr("Lakewalker trace"), commands));
+ Main.main.getCurrentDataSet().setSelected(ways);
+ }
+ } finally {
+ progressMonitor.finishTask();
+ }
+ }
+
+ /*
+ * User has hit the cancel button
+ */
+ public void cancel() {
+ cancel = true;
+ }
+}
\ No newline at end of file
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerWMS.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerWMS.java
new file mode 100644
index 0000000..8871fff
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/LakewalkerWMS.java
@@ -0,0 +1,234 @@
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public class LakewalkerWMS {
+
+ private BufferedImage image;
+ private int imagex;
+ private int imagey;
+
+ // Vector to cache images in memory
+ private Vector<BufferedImage> images = new Vector<BufferedImage>();
+ // Hashmap to hold the mapping of cached images
+ private HashMap<String,Integer> imageindex = new HashMap<String,Integer>();
+
+ private int resolution;
+ private int tilesize;
+
+ private String wmslayer;
+
+ private File working_dir;
+
+ public LakewalkerWMS(int resolution, int tilesize, String wmslayer, File workdir){
+ this.resolution = resolution;
+ this.tilesize = tilesize;
+ this.working_dir = workdir;
+ this.wmslayer = wmslayer;
+ }
+
+ public BufferedImage getTile(int x, int y, ProgressMonitor progressMonitor) throws LakewalkerException {
+ progressMonitor.beginTask(tr("Downloading image tile..."));
+ try {
+ String layer = "global_mosaic_base";
+
+ int[] bottom_left_xy = new int[2];
+ bottom_left_xy[0] = floor(x,this.tilesize);
+ bottom_left_xy[1] = floor(y,this.tilesize);
+
+ int[] top_right_xy = new int[2];
+ top_right_xy[0] = bottom_left_xy[0] + this.tilesize;
+ top_right_xy[1] = bottom_left_xy[1] + this.tilesize;
+
+ double[] topright_geo = xy_to_geo(top_right_xy[0],top_right_xy[1],this.resolution);
+ double[] bottomleft_geo = xy_to_geo(bottom_left_xy[0],bottom_left_xy[1],this.resolution);
+
+ String filename = this.wmslayer+"/landsat_"+this.resolution+"_"+this.tilesize+
+ "_xy_"+bottom_left_xy[0]+"_"+bottom_left_xy[1]+".png";
+
+ // The WMS server only understands decimal points using periods, so we need
+ // to convert to a locale that uses that to build the proper URL
+ NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
+ DecimalFormat df = (DecimalFormat)nf;
+ df.applyLocalizedPattern("0.000000");
+
+ String urlloc = "http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&layers="+layer+
+ "&styles="+wmslayer+"&srs=EPSG:4326&format=image/png"+
+ "&bbox="+df.format(bottomleft_geo[1])+","+df.format(bottomleft_geo[0])+
+ ","+df.format(topright_geo[1])+","+df.format(topright_geo[0])+
+ "&width="+this.tilesize+"&height="+this.tilesize;
+
+ File file = new File(this.working_dir,filename);
+
+ // Calculate the hashmap key
+ String hashkey = Integer.toString(bottom_left_xy[0])+":"+Integer.toString(bottom_left_xy[1]);
+
+ // See if this image is already loaded
+ if(this.image != null){
+ if(this.imagex != bottom_left_xy[0] || this.imagey != bottom_left_xy[1]){
+
+ // Check if this image exists in the hashmap
+ if(this.imageindex.containsKey(hashkey)){
+ // Store which image we have
+ this.imagex = bottom_left_xy[0];
+ this.imagey = bottom_left_xy[1];
+
+ // Retrieve from cache
+ this.image = this.images.get(this.imageindex.get(hashkey));
+ return this.image;
+ } else {
+ this.image = null;
+ }
+ } else {
+ return this.image;
+ }
+ }
+
+ try {
+ System.out.println("Looking for image in disk cache: "+filename);
+
+ // Read from a file
+ this.image = ImageIO.read(file);
+
+ this.images.add(this.image);
+ this.imageindex.put(hashkey,this.images.size()-1);
+
+ } catch(FileNotFoundException e){
+ System.out.println("Could not find cached image, downloading.");
+ } catch(IOException e){
+ System.out.println(e.getMessage());
+ } catch(Exception e){
+ System.out.println(e.getMessage());
+ }
+
+ if(this.image == null){
+ /**
+ * Try downloading the image
+ */
+ try {
+ System.out.println("Downloading from "+urlloc);
+
+ // Read from a URL
+ URL url = new URL(urlloc);
+ this.image = ImageIO.read(url); // this can return null!
+ } catch(MalformedURLException e){
+ System.out.println(e.getMessage());
+ } catch(IOException e){
+ System.out.println(e.getMessage());
+ } catch(Exception e){
+ System.out.println(e.getMessage());
+ }
+
+ if (this.image != null) {
+ this.images.add(this.image);
+ this.imageindex.put(hashkey,this.images.size()-1);
+
+ this.saveimage(file,this.image);
+ }
+ }
+
+ this.imagex = bottom_left_xy[0];
+ this.imagey = bottom_left_xy[1];
+
+ if(this.image == null){
+ throw new LakewalkerException(tr("Could not acquire image"));
+ }
+
+ return this.image;
+ } finally {
+ progressMonitor.finishTask();
+ }
+ }
+
+ public void saveimage(File file, BufferedImage image){
+ /**
+ * Save the image to the cache
+ */
+ try {
+ ImageIO.write(image, "png", file);
+ System.out.println("Saved image to cache");
+ } catch(Exception e){
+ System.out.println(e.getMessage());
+ }
+ }
+
+ public int getPixel(int x, int y, ProgressMonitor progressMonitor) throws LakewalkerException{
+ // Get the previously shown text
+
+
+ BufferedImage image = null;
+
+ try {
+ image = this.getTile(x,y, progressMonitor);
+ } catch(LakewalkerException e){
+ System.out.println(e.getError());
+ throw new LakewalkerException(e.getMessage());
+ }
+
+ int tx = floor(x,this.tilesize);
+ int ty = floor(y,this.tilesize);
+
+ int pixel_x = (x-tx);
+ int pixel_y = (this.tilesize-1)-(y-ty);
+
+ //System.out.println("("+x+","+y+") maps to ("+pixel_x+","+pixel_y+") by ("+tx+", "+ty+")");
+
+ int rgb = image.getRGB(pixel_x,pixel_y);
+
+ int pixel;
+
+ int r = (rgb >> 16) & 0xff;
+ int g = (rgb >> 8) & 0xff;
+ int b = (rgb >> 0) & 0xff;
+
+ pixel = (int)((0.30 * r) + (0.59 * b) + (0.11 * g));
+
+ return pixel;
+ }
+
+ public int floor(int num, int precision){
+ double dnum = num/(double)precision;
+ BigDecimal val = new BigDecimal(dnum) ;
+ val = val.setScale(0, BigDecimal.ROUND_FLOOR);
+ return val.intValue()*precision;
+ }
+
+ public double floor(double num) {
+ BigDecimal val = new BigDecimal(num) ;
+ val = val.setScale(0, BigDecimal.ROUND_FLOOR);
+ return val.doubleValue() ;
+ }
+
+ public double[] xy_to_geo(int x, int y, double resolution){
+ double[] geo = new double[2];
+ geo[0] = y / resolution;
+ geo[1] = x / resolution;
+ return geo;
+ }
+
+ public int[] geo_to_xy(double lat, double lon, double resolution){
+ int[] xy = new int[2];
+
+ xy[0] = (int)Math.floor(lon * resolution + 0.5);
+ xy[1] = (int)Math.floor(lat * resolution + 0.5);
+
+ return xy;
+ }
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringConfigurer.java
new file mode 100644
index 0000000..49e2bb6
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringConfigurer.java
@@ -0,0 +1,81 @@
+/*
+ * $Id: StringConfigurer.java 2073 2007-05-10 14:34:31 +0000 (Thu, 10 May 2007) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+/**
+ * A Configurer for String values
+ */
+public class StringConfigurer extends Configurer {
+ protected JPanel p;
+ protected JTextField nameField = new JTextField(12);
+
+ public StringConfigurer() {
+ this(null, "");
+ }
+
+ public StringConfigurer(String key, String name) {
+ this(key, name, "");
+ }
+
+ public StringConfigurer(String key, String name, String val) {
+ super(key, name, val);
+ }
+
+ public String getValueString() {
+ return (String) value;
+ }
+
+ public void setValue(String s) {
+ if (!noUpdate && nameField != null) {
+ nameField.setText(s);
+ }
+ setValue((Object) s);
+ }
+
+ public void setToolTipText(String s) {
+ nameField.setToolTipText(s);
+ }
+
+ public java.awt.Component getControls() {
+ if (p == null) {
+ p = new JPanel();
+ p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
+ p.add(new JLabel(getName()));
+ nameField.setMaximumSize
+ (new java.awt.Dimension(nameField.getMaximumSize().width,
+ nameField.getPreferredSize().height));
+ nameField.setText(getValueString());
+ p.add(nameField);
+ nameField.addKeyListener(new java.awt.event.KeyAdapter() {
+ public void keyReleased(java.awt.event.KeyEvent evt) {
+ noUpdate = true;
+ setValue(nameField.getText());
+ noUpdate = false;
+ }
+ });
+ }
+ return p;
+ }
+
+}
diff --git a/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringEnumConfigurer.java b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringEnumConfigurer.java
new file mode 100644
index 0000000..b19acba
--- /dev/null
+++ b/lakewalker/src/org/openstreetmap/josm/plugins/lakewalker/StringEnumConfigurer.java
@@ -0,0 +1,107 @@
+/*
+ * $Id: StringEnumConfigurer.java 2472 2007-10-01 04:10:19 +0000 (Mon, 01 Oct 2007) rodneykinney $
+ *
+ * Copyright (c) 2000-2007 by Rodney Kinney, Brent Easton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, copies are available
+ * at http://www.opensource.org.
+ */
+/*
+ * Created by IntelliJ IDEA.
+ * User: rkinney
+ * Date: Jul 20, 2002
+ * Time: 3:52:36 AM
+ * To change template for new class use
+ * Code Style | Class Templates options (Tools | IDE Options).
+ */
+package org.openstreetmap.josm.plugins.lakewalker;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.Box;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+
+/**
+ * A Configurer that returns a String from among a list of possible values
+ */
+public class StringEnumConfigurer extends Configurer {
+ protected String[] validValues;
+ protected String[] transValues;
+ protected JComboBox box;
+ protected Box panel;
+ protected String tooltipText = "";
+
+ public StringEnumConfigurer(String key, String name, String[] validValues) {
+ super(key, name);
+ this.validValues = validValues;
+ transValues = new String[validValues.length];
+ for(int i = 0; i < validValues.length; ++i)
+ transValues[i] = tr(validValues[i]);
+ }
+
+ public StringEnumConfigurer(String[] validValues) {
+ this(null, "", validValues);
+ }
+
+ public void setToolTipText(String s) {
+ tooltipText = s;
+ }
+ public Component getControls() {
+ if (panel == null) {
+ panel = Box.createHorizontalBox();
+ panel.add(new JLabel(name));
+ box = new JComboBox(transValues);
+ box.setToolTipText(tooltipText);
+ box.setMaximumSize(new Dimension(box.getMaximumSize().width,box.getPreferredSize().height));
+ setValue(value);
+ box.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ noUpdate = true;
+ setValue(box.getSelectedIndex());
+ noUpdate = false;
+ }
+ });
+ panel.add(box);
+ }
+ return panel;
+ }
+ public void setValue(Object o) {
+ if(o == null)
+ o = new Integer(0);
+ super.setValue(o);
+ if(!noUpdate && box != null)
+ box.setSelectedIndex((Integer)o);
+ }
+
+ public void setValue(String s) {
+ Integer n = 0;
+ for (int i = 0; i < transValues.length; ++i)
+ {
+ if (transValues[i].equals(s) || validValues[i].equals(s)){
+ n = i;
+ break;
+ }
+ }
+ setValue(n);
+ }
+
+ public String getValueString() {
+ return validValues[(Integer)value];
+ }
+}
diff --git a/livegps/.classpath b/livegps/.classpath
index 81a65e8..b5edc13 100644
--- a/livegps/.classpath
+++ b/livegps/.classpath
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0.15"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/livegps/build.xml b/livegps/build.xml
index 1f7acd1..3a36827 100644
--- a/livegps/build.xml
+++ b/livegps/build.xml
@@ -25,7 +25,7 @@
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
<attribute name="Plugin-Description" value="Support live GPS input (moving dot) through a connection to gpsd server."/>
<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/LiveGPS"/>
- <attribute name="Plugin-Mainversion" value="1890"/>
+ <attribute name="Plugin-Mainversion" value="2450"/>
<attribute name="Plugin-Stage" value="50"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
diff --git a/livegps/src/livegps/LiveGpsLayer.java b/livegps/src/livegps/LiveGpsLayer.java
index a38c003..3d46b89 100644
--- a/livegps/src/livegps/LiveGpsLayer.java
+++ b/livegps/src/livegps/LiveGpsLayer.java
@@ -3,7 +3,7 @@ package livegps;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Color;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Point;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -13,13 +13,13 @@ import java.util.Collection;
import java.util.Date;
import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.gpx.GpxData;
import org.openstreetmap.josm.data.gpx.GpxTrack;
import org.openstreetmap.josm.data.gpx.WayPoint;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.layer.GpxLayer;
-import org.openstreetmap.josm.tools.ColorHelper;
public class LiveGpsLayer extends GpxLayer implements PropertyChangeListener {
public static final String LAYER_NAME = tr("LiveGPS layer");
@@ -100,12 +100,12 @@ public class LiveGpsLayer extends GpxLayer implements PropertyChangeListener {
autocenter = ac;
}
- @Override public void paint(Graphics g, MapView mv)
+ @Override public void paint(Graphics2D g, MapView mv, Bounds bounds)
{
//System.out.println("in paint");
synchronized (LiveGpsLock.class) {
//System.out.println("in synced paint");
- super.paint(g, mv);
+ super.paint(g, mv, bounds);
// int statusHeight = 50;
// Rectangle mvs = mv.getBounds();
// mvs.y = mvs.y + mvs.height - statusHeight;
diff --git a/livegps/src/livegps/LiveGpsPlugin.java b/livegps/src/livegps/LiveGpsPlugin.java
index 5b29083..bad5b19 100644
--- a/livegps/src/livegps/LiveGpsPlugin.java
+++ b/livegps/src/livegps/LiveGpsPlugin.java
@@ -1,5 +1,6 @@
package livegps;
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.marktr;
import static org.openstreetmap.josm.tools.I18n.tr;
@@ -97,7 +98,7 @@ public class LiveGpsPlugin extends Plugin implements LayerChangeListener
public LiveGpsPlugin()
{
MainMenu menu = Main.main.menu;
- lgpsmenu = menu.addMenu(marktr("LiveGPS"), KeyEvent.VK_G, menu.defaultMenuPos);
+ lgpsmenu = menu.addMenu(marktr("LiveGPS"), KeyEvent.VK_G, menu.defaultMenuPos, ht("/Plugin/LiveGPS"));
JosmAction captureAction = new CaptureAction();
lgpscapture = new JCheckBoxMenuItem(captureAction);
diff --git a/measurement/.classpath b/measurement/.classpath
index 05929d7..17b8e6a 100644
--- a/measurement/.classpath
+++ b/measurement/.classpath
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
<classpathentry kind="output" path="build"/>
</classpath>
diff --git a/measurement/build.xml b/measurement/build.xml
index 8833eb3..527d1ec 100644
--- a/measurement/build.xml
+++ b/measurement/build.xml
@@ -24,7 +24,7 @@
<attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.measurement.MeasurementPlugin"/>
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
<attribute name="Plugin-Description" value="Provide a measurement dialog and a layer to measure length and angle of segments, area surrounded by a (simple) closed way and create measurement paths (which also can be imported from a gps layer)."/>
- <attribute name="Plugin-Mainversion" value="2012"/>
+ <attribute name="Plugin-Mainversion" value="2450"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java b/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java
index 34e8066..75602fd 100644
--- a/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java
+++ b/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementLayer.java
@@ -4,7 +4,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Color;
import java.awt.Component;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
@@ -28,6 +28,7 @@ import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.gpx.GpxTrack;
import org.openstreetmap.josm.data.gpx.WayPoint;
@@ -70,7 +71,7 @@ public class MeasurementLayer extends Layer {
}
- @Override public void paint(Graphics g, final MapView mv) {
+ @Override public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
g.setColor(Color.green);
Point l = null;
for(WayPoint p:points){
diff --git a/measurement/.classpath b/routing/.classpath
similarity index 68%
copy from measurement/.classpath
copy to routing/.classpath
index 05929d7..beca5e1 100644
--- a/measurement/.classpath
+++ b/routing/.classpath
@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sun-jdk-1.5.0"/>
+ <classpathentry kind="lib" path="lib/jgrapht-jdk1.5.jar"/>
+ <classpathentry kind="lib" path="lib/log4j-1.2.15.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
<classpathentry kind="output" path="build"/>
</classpath>
diff --git a/routing/.project b/routing/.project
new file mode 100644
index 0000000..29a1851
--- /dev/null
+++ b/routing/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>JOSM-routing</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/surveyor/build.xml b/routing/build.xml
similarity index 65%
copy from surveyor/build.xml
copy to routing/build.xml
index 18a9d5e..28e873a 100644
--- a/surveyor/build.xml
+++ b/routing/build.xml
@@ -1,29 +1,36 @@
-<project name="surveyor" default="dist" basedir=".">
+<project name="routing" default="dist" basedir=".">
+ <!-- Define some properties -->
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="plugin.build.dir" value="build"/>
<property name="plugin.jar" value="${plugin.dist.dir}/${ant.project.name}.jar"/>
- <property name="livegpsplugin.jar" value="${plugin.dist.dir}/livegps.jar"/>
+ <property name="jgrapht" value="lib/jgrapht-jdk1.5.jar"/>
+ <property name="log4j" value="lib/log4j-1.2.15.jar"/>
<property name="ant.build.javac.target" value="1.5"/>
+ <!-- Some initializations for several other targets -->
<target name="init">
<mkdir dir="${plugin.build.dir}"/>
</target>
- <target name="compile" depends="init">
+ <!-- Compile sources -->
+ <target name="compile" depends="init" description="Compile sources">
<echo message="creating ${plugin.jar}"/>
<javac srcdir="src" debug="true" destdir="${plugin.build.dir}">
<compilerarg value="-Xlint:deprecation"/>
<compilerarg value="-Xlint:unchecked"/>
<classpath>
<pathelement location="${josm}"/>
- <pathelement location="${livegpsplugin.jar}"/>
+ <pathelement location="${jgrapht}"/>
+ <pathelement location="${log4j}"/>
</classpath>
</javac>
</target>
- <target name="dist" depends="compile,revision">
+ <!-- Generate distribution -->
+ <target name="dist" depends="compile,revision" description="Generate distribution">
+ <unjar dest="${plugin.build.dir}" src="${jgrapht}"/>
+ <unjar dest="${plugin.build.dir}" src="${log4j}"/>
<copy todir="${plugin.build.dir}/">
<fileset dir="resources">
<include name="*.xml"/>
- <include name="audio/*"/>
</fileset>
</copy>
<copy todir="${plugin.build.dir}/images">
@@ -31,14 +38,13 @@
</copy>
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
- <attribute name="Author" value="Christof Dallermassl"/>
- <attribute name="Plugin-Class" value="at.dallermassl.josm.plugin.surveyor.SurveyorPlugin"/>
+ <attribute name="Author" value="Jose Vidal <vidalfree at gmail.com>, Juangui Jordán <juangui at gmail.com>"/>
+ <attribute name="Plugin-Class" value="com.innovant.josm.plugin.routing.RoutingPlugin"/>
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
- <attribute name="Plugin-Description" value="Allow adding markers/nodes on current gps positions."/>
- <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Surveyor"/>
- <attribute name="Plugin-Mainversion" value="2012"/>
- <attribute name="Plugin-Requires" value="livegps"/>
- <attribute name="Plugin-Stage" value="60"/>
+ <attribute name="Plugin-Description" value="Provides routing capabilities."/>
+ <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Routing"/>
+ <attribute name="Plugin-Mainversion" value="2450"/>
+ <attribute name="Plugin-Stage" value="50"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/routing/images/dialogs/routing.png b/routing/images/dialogs/routing.png
new file mode 100644
index 0000000..d817b85
Binary files /dev/null and b/routing/images/dialogs/routing.png differ
diff --git a/routing/images/layer/routing_small.png b/routing/images/layer/routing_small.png
new file mode 100644
index 0000000..f03698f
Binary files /dev/null and b/routing/images/layer/routing_small.png differ
diff --git a/routing/images/mapmode/add.png b/routing/images/mapmode/add.png
new file mode 100644
index 0000000..24d0c18
Binary files /dev/null and b/routing/images/mapmode/add.png differ
diff --git a/routing/images/mapmode/move.png b/routing/images/mapmode/move.png
new file mode 100644
index 0000000..21b873f
Binary files /dev/null and b/routing/images/mapmode/move.png differ
diff --git a/routing/images/mapmode/remove.png b/routing/images/mapmode/remove.png
new file mode 100644
index 0000000..1617590
Binary files /dev/null and b/routing/images/mapmode/remove.png differ
diff --git a/routing/images/mapmode/routing.png b/routing/images/mapmode/routing.png
new file mode 100644
index 0000000..d817b85
Binary files /dev/null and b/routing/images/mapmode/routing.png differ
diff --git a/routing/images/preferences/routing.png b/routing/images/preferences/routing.png
new file mode 100644
index 0000000..d817b85
Binary files /dev/null and b/routing/images/preferences/routing.png differ
diff --git a/routing/images/routing/endflag.png b/routing/images/routing/endflag.png
new file mode 100644
index 0000000..b3a715a
Binary files /dev/null and b/routing/images/routing/endflag.png differ
diff --git a/routing/images/routing/middleflag.png b/routing/images/routing/middleflag.png
new file mode 100644
index 0000000..9661b0e
Binary files /dev/null and b/routing/images/routing/middleflag.png differ
diff --git a/routing/images/routing/startflag.png b/routing/images/routing/startflag.png
new file mode 100644
index 0000000..fd743fc
Binary files /dev/null and b/routing/images/routing/startflag.png differ
diff --git a/routing/resources/log4j.xml b/routing/resources/log4j.xml
new file mode 100644
index 0000000..0921e1a
--- /dev/null
+++ b/routing/resources/log4j.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE log4j:configuration SYSTEM "dtds/org/apache/log4j/xml/log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+ <appender name="console" class="org.apache.log4j.ConsoleAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%c] %-5p - %m%n" />
+ </layout>
+ </appender>
+
+ <logger name="com.innovant.josm.plugin.routing.RoutingModel">
+ <level value="trace"/>
+ </logger>
+
+
+ <root>
+ <level value="debuger"></level>
+ <appender-ref ref="console"/>
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/routing/src/com/innovant/josm/jrt/core/EdgeIterator.java b/routing/src/com/innovant/josm/jrt/core/EdgeIterator.java
new file mode 100644
index 0000000..c46f127
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/EdgeIterator.java
@@ -0,0 +1,9 @@
+package com.innovant.josm.jrt.core;
+
+public interface EdgeIterator {
+
+ public boolean hasNext();
+
+ public RoutingEdge next();
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java b/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java
new file mode 100644
index 0000000..dcdeeec
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+package com.innovant.josm.jrt.core;
+
+public enum PreferencesKeys {
+ KEY_ACTIVE_ROUTE_COLOR ("routing.active.route.color"),
+ KEY_INACTIVE_ROUTE_COLOR ("routing.inactive.route.color"),
+ KEY_ROUTE_WIDTH ("routing.route.width"),
+ KEY_ROUTE_SELECT ("routing.route.select");
+
+ public final String key;
+ PreferencesKeys (String key) {
+ this.key=key;
+ }
+
+ public String getKey() {return key;};
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingEdge.java b/routing/src/com/innovant/josm/jrt/core/RoutingEdge.java
new file mode 100644
index 0000000..af36d81
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingEdge.java
@@ -0,0 +1,27 @@
+package com.innovant.josm.jrt.core;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+public interface RoutingEdge {
+
+ public LatLon fromLatLon();
+
+ public LatLon toLatLon();
+
+ public Object fromV();
+
+ public Object toV();
+
+ public double getLength();
+
+ public void setLength(double length);
+
+ public double getSpeed();
+
+ public void setSpeed(double speed);
+
+ public boolean isOneway();
+
+ public void setOneway(boolean isOneway);
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingGraph.java b/routing/src/com/innovant/josm/jrt/core/RoutingGraph.java
new file mode 100644
index 0000000..ef5b316
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingGraph.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.jrt.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.jgrapht.Graph;
+import org.jgrapht.alg.BellmanFordShortestPath;
+import org.jgrapht.alg.DijkstraShortestPath;
+import org.jgrapht.graph.AsUndirectedGraph;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+/**
+ * Class utility to work with graph routers.
+ *
+ * @author Juangui
+ * @author Jose Vidal
+ */
+public class RoutingGraph {
+
+ /**
+ * Routing Profile
+ */
+ private RoutingProfile routingProfile;
+
+ /**
+ * Diferent algorithms to apply to the graph.
+ */
+ public enum Algorithm {
+ ROUTING_ALG_DIJKSTRA, ROUTING_ALG_BELLMANFORD
+ };
+
+ /**
+ * Search criteria for the route.
+ */
+ public enum RouteType {FASTEST,SHORTEST};
+
+ /**
+ *
+ */
+ private RouteType routeType;
+
+ /**
+ * Associated Osm DataSet
+ */
+ private DataSet data;
+
+ /**
+ * Logger.
+ */
+ static Logger logger = Logger.getLogger(RoutingGraph.class);
+
+ /**
+ * Graph state
+ * <code>true</code> Graph in memory.
+ * <code>false</code> Graph not created.
+ */
+// public boolean graphState;
+
+ /**
+ * OSM Graph.
+ */
+// private DirectedWeightedMultigraph<Node, OsmEdge> graph;
+// private WeightedMultigraph<Node, OsmEdge> graph;
+ private Graph<Node, OsmEdge> graph;
+ private RoutingGraphDelegator rgDelegator=null;
+
+ /**
+ * Speeds
+ */
+ private Map<String,Double> waySpeeds;
+
+ /**
+ * Default Constructor.
+ */
+ public RoutingGraph(DataSet data) {
+// this.graphState = false;
+ this.graph = null;
+ this.data = data;
+ routeType=RouteType.SHORTEST;
+ routingProfile=new RoutingProfile("default");
+ routingProfile.setOnewayUse(true); // Don't ignore oneways by default
+ this.setWaySpeeds(routingProfile.getWaySpeeds());
+ logger.debug("Created RoutingGraph");
+ }
+
+ /**
+ * Create OSM graph for routing
+ *
+ * @return
+ */
+ public void createGraph() {
+
+ logger.debug("Creating Graph...");
+ graph = new DirectedWeightedMultigraph<Node, OsmEdge>(OsmEdge.class);
+ rgDelegator=new RoutingGraphDelegator(graph);
+ rgDelegator.setRouteType(this.routeType);
+ // iterate all ways and segments for all nodes:
+ for (Way way : data.getWays()) {
+ if (way != null && !way.isDeleted() && this.isvalidWay(way)) {
+ Node from = null;
+ for (Node to : way.getNodes()) {
+ // Ignore the node if deleted
+ if (!to.isDeleted()) {
+ graph.addVertex(to);
+ if (from != null) {
+ addEdge(way, from, to);
+ if (!isOneWay(way)){
+ addEdge(way, to, from);}
+ }
+ from = to;
+ }
+ }
+ }
+ }
+// graph.vertexSet().size();
+ logger.debug("End Create Graph");
+ logger.debug("Vertex: "+graph.vertexSet().size());
+ logger.debug("Edges: "+graph.edgeSet().size());
+ }
+
+ /**
+ * Compute weight and add edge to the graph
+ * @param way
+ * @param from
+ * @param to
+ */
+ private void addEdge(Way way,Node from, Node to) {
+ double length = from.getCoor().greatCircleDistance(to.getCoor());
+
+ OsmEdge edge = new OsmEdge(way, from, to);
+ edge.setSpeed(12.1);
+ graph.addEdge(from, to, edge);
+ // weight = getWeight(way);
+ double weight = getWeight(way, length);
+ setWeight(edge, length);
+ logger.debug("edge for way " + way.getId()
+ + "(from node " + from.getId() + " to node "
+ + to.getId() + ") has weight: " + weight);
+ //((GraphDelegator<Node,OsmEdge>) graph).setEdgeWeight(edge, weight);
+ ((DirectedWeightedMultigraph<Node,OsmEdge>)graph).setEdgeWeight(edge, weight);
+ }
+
+ /**
+ * Set the weight for the given segment depending on the highway type
+ * and the length of the segment. The higher the value, the less it is used
+ * in routing.
+ *
+ * @param way
+ * the way.
+ * @return
+ */
+ private void setWeight(OsmEdge osmedge, double length) {
+
+ osmedge.setLength(length);
+ if (this.waySpeeds.containsKey(osmedge.getWay().get("highway")))
+ osmedge.setSpeed(this.waySpeeds.get(osmedge.getWay().get("highway")));
+
+ }
+
+ /**
+ * Returns the weight for the given segment depending on the highway type
+ * and the length of the segment. The higher the value, the less it is used
+ * in routing.
+ *
+ * @param way
+ * the way.
+ * @return
+ */
+ private double getWeight(Way way, double length) {
+ // Default speed if no setting is found
+ double speed = 1;
+
+ switch (routeType) {
+ case SHORTEST:
+ // Same speed for all types of ways
+ if (this.waySpeeds.containsKey("residential"))
+ speed=this.waySpeeds.get("residential");
+ break;
+ case FASTEST:
+ // Each type of way may have a different speed
+ if (this.waySpeeds.containsKey(way.get("highway")))
+ speed=this.waySpeeds.get(way.get("highway"));
+ logger.debug("Speed="+speed);
+ break;
+ default:
+ break;
+ }
+ // Return the time spent to traverse the way
+ return length / speed;
+ }
+
+ /**
+ * Check is One Way.
+ *
+ * @param way
+ * the way.
+ * @return <code>true</code> is a one way. <code>false</code> is not a one
+ * way.
+ */
+ private boolean isOneWay(Way way) {
+ // FIXME: oneway=-1 is ignored for the moment!
+ return way.get("oneway") != null
+ || "motorway".equals(way.get("highway"));
+ }
+
+ /**
+ * Check if a Way is correct.
+ *
+ * @param way
+ * The way.
+ * @return <code>true</code> is valid. <code>false</code> is not valid.
+ */
+ public boolean isvalidWay(Way way) {
+ if (!way.isTagged())
+ return false;
+
+ return way.get("highway") != null || way.get("junction") != null
+ || way.get("service") != null;
+
+ }
+
+ public boolean isvalidNode(Node node) {
+ return true;
+ }
+
+ /**
+ * Apply selected routing algorithm to the graph.
+ *
+ * @param nodes
+ * Nodes used to calculate path.
+ * @param algorithm
+ * Algorithm used to compute the path,
+ * RoutingGraph.Algorithm.ROUTING_ALG_DIJKSTRA or
+ * RoutingGraph.Algorithm.ROUTING_ALG_BELLMANFORD
+ * @return new path.
+ */
+ public List<OsmEdge> applyAlgorithm(List<Node> nodes, Algorithm algorithm) {
+ List<OsmEdge> path = new ArrayList<OsmEdge>();
+ Graph<Node,OsmEdge> g;
+ double totalWeight = 0;
+
+ if (graph == null)
+ this.createGraph();
+ logger.debug("apply algorithm between nodes ");
+
+ for (Node node : nodes) {
+ logger.debug(node.getId());
+ }
+ logger.debug("-----------------------------------");
+
+ // Assign the graph or an undirected view of the graph to g,
+ // depending on whether oneway tags are used or not
+ if (routingProfile.isOnewayUsed())
+ g = graph;
+ else
+ g = new AsUndirectedGraph<Node, OsmEdge>((DirectedWeightedMultigraph<Node,OsmEdge>)graph);
+ //TODO: Problemas no tiene encuenta el tema de oneway.
+ switch (algorithm) {
+ case ROUTING_ALG_DIJKSTRA:
+ logger.debug("Using Dijkstra algorithm");
+ DijkstraShortestPath<Node, OsmEdge> routingk = null;
+ for (int index = 1; index < nodes.size(); ++index) {
+ routingk = new DijkstraShortestPath<Node, OsmEdge>(rgDelegator, nodes
+ .get(index - 1), nodes.get(index));
+ if (routingk.getPathEdgeList() == null) {
+ logger.debug("no path found!");
+ break;
+ }
+ path.addAll(routingk.getPathEdgeList());
+ totalWeight += routingk.getPathLength();
+ }
+ break;
+ case ROUTING_ALG_BELLMANFORD:
+ logger.debug("Using Bellman Ford algorithm");
+ for (int index = 1; index < nodes.size(); ++index) {
+ path = BellmanFordShortestPath.findPathBetween(rgDelegator, nodes
+ .get(index - 1), nodes.get(index));
+ if (path == null) {
+ logger.debug("no path found!");
+ return null;
+ }
+ }
+ break;
+ default:
+ logger.debug("Wrong algorithm");
+ break;
+ }
+
+ logger.debug("shortest path found: " + path + "\nweight: "
+ + totalWeight);
+ return path;
+ }
+
+ /**
+ * Return the number of vertices.
+ * @return the number of vertices.
+ */
+ public int getVertexCount(){
+ int value=0;
+ if (graph!=null) value=graph.vertexSet().size();
+ return value;
+ }
+
+ /**
+ * Return the number of edges.
+ * @return the number of edges.
+ */
+ public int getEdgeCount(){
+ int value=0;
+ if (graph!=null) value=graph.edgeSet().size();
+ return value;
+ }
+
+ /**
+ * @param routeType the routeType to set
+ */
+ public void setTypeRoute(RouteType routetype) {
+ this.routeType = routetype;
+ this.rgDelegator.setRouteType(routetype);
+ }
+
+ /**
+ * @return the routeType
+ */
+ public RouteType getTypeRoute() {
+ return routeType;
+ }
+
+ public Map<String, Double> getWaySpeeds() {
+ return waySpeeds;
+ }
+
+ public void setWaySpeeds(Map<String, Double> waySpeeds) {
+ this.waySpeeds = waySpeeds;
+ }
+
+ public void resetGraph() {
+ graph=null;
+ }
+
+ public RoutingProfile getRoutingProfile() {
+ return routingProfile;
+ }
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingGraphDelegator.java b/routing/src/com/innovant/josm/jrt/core/RoutingGraphDelegator.java
new file mode 100644
index 0000000..bc3c40c
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingGraphDelegator.java
@@ -0,0 +1,60 @@
+/**
+ *
+ */
+package com.innovant.josm.jrt.core;
+
+import org.apache.log4j.Logger;
+import org.jgrapht.Graph;
+import org.jgrapht.graph.GraphDelegator;
+import org.openstreetmap.josm.data.osm.Node;
+
+import com.innovant.josm.jrt.core.RoutingGraphDelegator;
+import com.innovant.josm.jrt.core.RoutingGraph.RouteType;
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+/**
+ * @author jose
+ *
+ */
+public class RoutingGraphDelegator extends GraphDelegator<Node, OsmEdge> {
+
+ /**
+ * Logger.
+ */
+ static Logger logger = Logger.getLogger(RoutingGraphDelegator.class);
+
+ /**
+ *
+ */
+ private RouteType routeType;
+
+ public RoutingGraphDelegator(Graph<Node, OsmEdge> arg0) {
+ super(arg0);
+ }
+
+
+ public RouteType getRouteType() {
+ return routeType;
+ }
+
+ public void setRouteType(RouteType routeType) {
+ this.routeType = routeType;
+ }
+
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public double getEdgeWeight(OsmEdge edge) {
+ double weight=Double.MAX_VALUE;
+
+ if (routeType==RouteType.SHORTEST) weight=edge.getLength();
+ if (routeType==RouteType.FASTEST) weight=edge.getLength() / edge.getSpeed();
+ // Return the time spent to traverse the way
+ return weight;
+ }
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/core/RoutingProfile.java b/routing/src/com/innovant/josm/jrt/core/RoutingProfile.java
new file mode 100644
index 0000000..da8be74
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/core/RoutingProfile.java
@@ -0,0 +1,161 @@
+package com.innovant.josm.jrt.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+
+
+/**
+ * This class holds information about a routing profile.
+ *
+ * A routing profile specifies the type of vehicle that will go through the route
+ * and the conditions with respect to the traversal of different types of edges
+ *
+ * For instance, a pedestrian can traverse streets in both directions, walk through
+ * pedestrian ways and almost all types of ways except motorways, climb steps, and
+ * can ignore turn restrictions, while a handicapped person would have the same profile
+ * except for climbing steps. A car can drive at the maximum allowed speed of the way,
+ * and can not use cycleways nor pedestrian ways, while a bicycle can, but its maximum
+ * speed for any type of way would be around 50km/h.
+ *
+ * When combined with public transit data, information of which types of transport modes
+ * are allowed for the vehicle can be stored in the profile. For instance, bicycles are
+ * usually allowed to travel on board of trains, trams and subways.
+ *
+ * @author juangui
+ *
+ */
+public class RoutingProfile {
+ /**
+ * logger
+ */
+ static Logger logger = Logger.getLogger(RoutingProfile.class);
+ /**
+ * True if oneway is used for routing (i.e. for cars).
+ */
+ private boolean useOneway;
+
+ /**
+ * True if turn restrictions are used for routing (i.e. for cars).
+ */
+ private boolean useRestrictions;
+
+ /**
+ * True if maximum allowed speed of ways is considered for routing (i.e. for cars).
+ */
+ private boolean useMaxAllowedSpeed;
+
+ /**
+ * Name of the routing profile, for identification issues (i.e. "pedestrian").
+ */
+ private String name;
+
+ /**
+ * Holds traverse speed for each type of way, using the type as key.
+ * A speed of zero means that this type of way cannot be traversed.
+ */
+ private Map<String,Double> waySpeeds;
+
+
+
+ /**
+ * Holds permission of use for each type of transport mode, using the mode as key.
+ */
+ private Map<String,Boolean> allowedModes;
+
+ /**
+ * Constructor
+ * @param name The name for the routing profile. Please use a name that is
+ * self descriptive, i.e., something that an application user would
+ * understand (like "pedestrian", "motorbike", "bicycle", etc.)
+ */
+ public RoutingProfile(String name) {
+ logger.debug("Init RoutingProfile with name: "+name);
+ this.name = name;
+ waySpeeds=new HashMap<String,Double>();
+ Map<String,String> prefs=Main.pref.getAllPrefix("routing.profile."+name+".speed");
+ for(String key:prefs.keySet()){
+ waySpeeds.put((key.split("\\.")[4]), Double.valueOf(prefs.get(key)));
+ }
+ for (String key:waySpeeds.keySet())
+ logger.debug(key+ "-- speed: "+waySpeeds.get(key));
+ logger.debug("End init RoutingProfile with name: "+name);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setOnewayUse(boolean useOneway) {
+ this.useOneway = useOneway;
+ }
+
+ public boolean isOnewayUsed() {
+ return useOneway;
+ }
+
+ public void setRestrictionsUse(boolean useRestrictions) {
+ this.useRestrictions = useRestrictions;
+ }
+
+ public boolean isRestrictionsUsed() {
+ return useRestrictions;
+ }
+
+ public void setMaxAllowedSpeedUse(boolean useMaxAllowedSpeed) {
+ this.useMaxAllowedSpeed = useMaxAllowedSpeed;
+ }
+
+ public boolean isMaxAllowedSpeedUsed() {
+ return useMaxAllowedSpeed;
+ }
+
+ public void setWayTypeSpeed(String type, double speed) {
+ waySpeeds.put(type, speed);
+ }
+
+ public void setTransportModePermission(String mode, boolean permission) {
+ allowedModes.put(mode, permission);
+ }
+
+ /**
+ * Return whether the driving profile specifies that a particular type of way
+ * can be traversed
+ * @param type Key for the way type
+ * @return True if the way type can be traversed
+ */
+ public boolean isWayTypeAllowed(String type) {
+ if (waySpeeds.get(type) != 0.0)
+ return true;
+ return false;
+ }
+
+ /**
+ * Return whether the driving profile specifies that a particular type of transport
+ * mode can be used
+ * @param mode Key for the way type
+ * @return True if the way type can be traversed
+ */
+ public boolean isTransportModeAllowed(String mode) {
+ return allowedModes.get(mode);
+ }
+
+ public double getSpeed(String key){
+ if(!waySpeeds.containsKey(key)) return 0.0;
+ return waySpeeds.get(key);
+ }
+
+ public Map<String, Double> getWaySpeeds() {
+ return waySpeeds;
+ }
+
+ public void setWaySpeeds(Map<String, Double> waySpeeds) {
+ this.waySpeeds = waySpeeds;
+ }
+}
diff --git a/routing/src/com/innovant/josm/jrt/gtfs/GTFSTransportModes.java b/routing/src/com/innovant/josm/jrt/gtfs/GTFSTransportModes.java
new file mode 100644
index 0000000..b9c5581
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/gtfs/GTFSTransportModes.java
@@ -0,0 +1,57 @@
+package com.innovant.josm.jrt.gtfs;
+
+/**
+ * Constants for parsing GTFS and to use in Routing Profiles
+ * @author juangui
+ * TODO Using integers is suitable to parse gtfs feeds but
+ * Routing Profile keys should be Strings
+ */
+public class GTFSTransportModes {
+
+ /**
+ * 0 - Tram, Streetcar, Light rail. Any light rail or street level system within
+ * a metropolitan area.
+ */
+ public static final int TRAM = 0;
+ public static final int STREETCAR = 0;
+ public static final int LIGHT_RAIL = 0;
+
+ /**
+ * 1 - Subway, Metro. Any underground rail system within a metropolitan area.
+ */
+ public static final int SUBWAY = 1;
+ public static final int METRO = 1;
+
+ /**
+ * 2 - Rail. Used for intercity or long-distance travel.
+ */
+ public static final int RAIL = 2;
+
+ /**
+ * 3 - Bus. Used for short- and long-distance bus routes.
+ */
+ public static final int BUS = 3;
+
+ /**
+ * 4 - Ferry. Used for short- and long-distance boat service.
+ */
+ public static final int FERRY = 4;
+
+ /**
+ * 5 - Cable car. Used for street-level cable cars where the cable runs beneath the car.
+ */
+ public static final int CABLE_CAR = 5;
+
+ /**
+ * 6 - Gondola, Suspended cable car. Typically used for aerial cable cars where
+ * the car is suspended from the cable.
+ */
+ public static final int GONDOLA = 6;
+ public static final int SUSPENDED_CABLE_CAR = 6;
+
+ /**
+ * 7 - Funicular. Any rail system designed for steep inclines.
+ */
+ public static final int FUNICULAR = 7;
+
+}
diff --git a/routing/src/com/innovant/josm/jrt/osm/OsmEdge.java b/routing/src/com/innovant/josm/jrt/osm/OsmEdge.java
new file mode 100644
index 0000000..fbf531e
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/osm/OsmEdge.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+package com.innovant.josm.jrt.osm;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+
+/**
+ * Class that represents an edge of the graph.
+ * @author jose
+ */
+public class OsmEdge extends DefaultWeightedEdge {
+ /**
+ * Serial
+ */
+ private static final long serialVersionUID = 1L;
+ /**
+ * Way associated
+ */
+ private Way way;
+ /**
+ * Nodes in the edge
+ */
+ private Node from, to;
+ /**
+ * Length edge
+ */
+ private double length;
+ /**
+ * Speed edge.
+ */
+ private double speed;
+
+
+/**
+ * Constructor
+ * @param way
+ * @param length
+ */
+ public OsmEdge(Way way, Node from, Node to) {
+ super();
+ this.way = way;
+ this.from = from;
+ this.to = to;
+ this.length = from.getCoor().greatCircleDistance(to.getCoor());
+ }
+
+ /**
+ * @return the way
+ */
+ public Way getWay() {
+ return this.way;
+ }
+
+ public EastNorth fromEastNorth() {
+ return this.from.getEastNorth();
+ }
+
+ public EastNorth toEastNorth() {
+ return this.to.getEastNorth();
+ }
+
+ /**
+ * Returns length of segment in meters
+ * @return length of segment in meters.
+ */
+ public double getLength() {
+ return length;
+ }
+
+ public void setLength(double length) {
+ this.length = length;
+}
+
+public double getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(double speed) {
+ this.speed = speed;
+ }
+}
diff --git a/routing/src/com/innovant/josm/jrt/osm/OsmWayTypes.java b/routing/src/com/innovant/josm/jrt/osm/OsmWayTypes.java
new file mode 100644
index 0000000..0a110ce
--- /dev/null
+++ b/routing/src/com/innovant/josm/jrt/osm/OsmWayTypes.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.jrt.osm;
+
+/**
+ * @author jvidal
+ *
+ */
+public enum OsmWayTypes {
+ MOTORWAY ("motorway",120),
+ MOTORWAY_LINK ("motorway_link",120),
+ TRUNK ("trunk",120),
+ TRUNK_LINK ("trunk_link",120),
+ PRIMARY ("primary",100),
+ PRIMARY_LINK ("primary_link",100),
+ SECONDARY ("secondary",90),
+ TERTIARY ("tertiary",90),
+ UNCLASSIFIED ("unclassified",50),
+ ROAD ("road",100),
+ RESIDENTIAL ("residential",50),
+ LIVING_STREET ("living_street",30),
+ SERVICE ("service",30),
+ TRACK ("track",50),
+ PEDESTRIAN ("pedestrian",30),
+ BUS_GUIDEWAY ("bus_guideway",50),
+ PATH ("path",40),
+ CYCLEWAY ("cycleway",40),
+ FOOTWAY ("footway",20),
+ BRIDLEWAY ("bridleway",40),
+ BYWAY ("byway",50),
+ STEPS ("steps",10);
+
+ /**
+ * Default Constructor
+ * @param tag
+ */
+ OsmWayTypes(String tag,int speed) {
+ this.tag = tag;
+ this.speed = speed;
+ }
+
+ /**
+ * Tag
+ */
+ private final String tag;
+ private final int speed;
+
+ /**
+ * @return
+ */
+ public String getTag() {return tag;};
+ public int getSpeed() {return speed;};
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java b/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
new file mode 100644
index 0000000..a53c3d7
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Stroke;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.Icon;
+import javax.swing.JMenuItem;
+import javax.swing.JSeparator;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.tools.ColorHelper;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.jrt.core.PreferencesKeys;
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+
+/**
+ * A JOSM layer that encapsulates the representation of the shortest path.
+ * @author juangui
+ * @author Jose Vidal
+ */
+public class RoutingLayer extends Layer {
+
+ /**
+ * Logger
+ */
+ static Logger logger = Logger.getLogger(RoutingLayer.class);
+
+ /**
+ * Constant
+ */
+ private static final double ARROW_PHI = Math.toRadians(20);
+
+ /**
+ * Routing Model
+ */
+ private RoutingModel routingModel;
+
+ /**
+ * Start, Middle and End icons
+ */
+ private Icon startIcon,middleIcon,endIcon;
+
+ /**
+ * Associated OSM layer
+ */
+ private OsmDataLayer dataLayer;
+
+ /**
+ * Default constructor
+ * @param name Layer name.
+ */
+ public RoutingLayer(String name, OsmDataLayer dataLayer) {
+ super(name);
+ logger.debug("Creating Routing Layer...");
+ if(startIcon == null) startIcon = ImageProvider.get("routing", "startflag");
+ if(middleIcon == null) middleIcon = ImageProvider.get("routing", "middleflag");
+ if(endIcon == null) endIcon = ImageProvider.get("routing", "endflag");
+ this.dataLayer = dataLayer;
+ this.routingModel = new RoutingModel(dataLayer.data);
+ logger.debug("Routing Layer created.");
+ }
+
+ /**
+ * Getter Routing Model.
+ * @return the routingModel
+ */
+ public RoutingModel getRoutingModel() {
+ return this.routingModel;
+ }
+
+ /**
+ * Gets associated data layer
+ * @return OsmDataLayer associated to the RoutingLayer
+ */
+ public OsmDataLayer getDataLayer() {
+ return dataLayer;
+ }
+
+ /**
+ * Gets nearest node belonging to a highway tagged way
+ * @param p Point on the screen
+ * @return The nearest highway node, in the range of the snap distance
+ */
+ public final Node getNearestHighwayNode(Point p) {
+ Node nearest = null;
+ double minDist = 0;
+ for (Way w : dataLayer.data.getWays()) {
+ if (w.isDeleted() || w.incomplete || w.get("highway")==null) continue;
+ for (Node n : w.getNodes()) {
+ if (n.isDeleted() || n.incomplete) continue;
+
+ Point P = Main.map.mapView.getPoint(n);
+ double dist = p.distanceSq(P);
+ if (dist < NavigatableComponent.snapDistance) {
+ if ((nearest == null) || (dist < minDist)) {
+ nearest = n;
+ minDist = dist;
+ }
+ }
+ }
+ }
+ return nearest;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#getIcon()
+ */
+ @Override
+ public Icon getIcon() {
+ Icon icon = ImageProvider.get("layer", "routing_small");
+ return icon;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#getInfoComponent()
+ */
+ @Override
+ public Object getInfoComponent() {
+ String info = "<html>"
+ + "<body>"
+ +"Graph Vertex: "+this.routingModel.routingGraph.getVertexCount()+"<br/>"
+ +"Graph Edges: "+this.routingModel.routingGraph.getEdgeCount()+"<br/>"
+ + "</body>"
+ + "</html>";
+ return info;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#getMenuEntries()
+ */
+ @Override
+ public Component[] getMenuEntries() {
+ Collection<Component> components = new ArrayList<Component>();
+ components.add(new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)));
+// components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this)));
+ components.add(new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)));
+ components.add(new JSeparator());
+ components.add(new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)));
+ components.add(new JSeparator());
+ components.add(new JMenuItem(new LayerListPopup.InfoAction(this)));
+ return components.toArray(new Component[0]);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#getToolTipText()
+ */
+ @Override
+ public String getToolTipText() {
+ String tooltip = this.routingModel.routingGraph.getVertexCount() + " vertices, "
+ + this.routingModel.routingGraph.getEdgeCount() + " edges";
+ return tooltip;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#isMergable(org.openstreetmap.josm.gui.layer.Layer)
+ */
+ @Override
+ public boolean isMergable(Layer other) {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#mergeFrom(org.openstreetmap.josm.gui.layer.Layer)
+ */
+ @Override
+ public void mergeFrom(Layer from) {
+ // This layer is not mergable, so do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#paint(java.awt.Graphics, org.openstreetmap.josm.gui.MapView)
+ */
+ @Override
+ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
+ boolean isActiveLayer = (mv.getActiveLayer().equals(this));
+ // Get routing nodes (start, middle, end)
+ List<Node> nodes = routingModel.getSelectedNodes();
+ if(nodes == null || nodes.size() == 0) {
+ logger.debug("no nodes selected");
+ return;
+ }
+
+ // Get path stroke color from preferences
+ // Color is different for active and inactive layers
+ String colorString;
+ if (isActiveLayer) {
+ if (Main.pref.hasKey(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key))
+ colorString = Main.pref.get(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key);
+ else {
+ colorString = ColorHelper.color2html(Color.RED);
+ Main.pref.put(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key, colorString);
+ }
+ } else {
+ if (Main.pref.hasKey(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key))
+ colorString = Main.pref.get(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key);
+ else {
+ colorString = ColorHelper.color2html(Color.decode("#dd2222"));
+ Main.pref.put(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key, colorString);
+ }
+ }
+ Color color = ColorHelper.html2color(colorString);
+
+ // Get path stroke width from preferences
+ String widthString = Main.pref.get(PreferencesKeys.KEY_ROUTE_WIDTH.key);
+ if (widthString.length() == 0) {
+ widthString = "8";
+ // FIXME add after good width is found: Main.pref.put(KEY_ROUTE_WIDTH, widthString);
+ }
+ int width = Integer.parseInt(widthString);
+
+ // Paint routing path
+ List<OsmEdge> routeEdges = routingModel.getRouteEdges();
+ if(routeEdges != null) {
+ for(OsmEdge edge : routeEdges) {
+ drawEdge(g, mv, edge, color, width, true);
+ }
+ }
+
+ // paint start icon
+ Node node = nodes.get(0);
+ Point screen = mv.getPoint(node);
+ startIcon.paintIcon(mv, g, screen.x - startIcon.getIconWidth()/2,
+ screen.y - startIcon.getIconHeight());
+
+ // paint middle icons
+ for(int index = 1; index < nodes.size() - 1; ++index) {
+ node = nodes.get(index);
+ screen = mv.getPoint(node);
+ middleIcon.paintIcon(mv, g, screen.x - startIcon.getIconWidth()/2,
+ screen.y - middleIcon.getIconHeight());
+ }
+ // paint end icon
+ if(nodes.size() > 1) {
+ node = nodes.get(nodes.size() - 1);
+ screen = mv.getPoint(node);
+ endIcon.paintIcon(mv, g, screen.x - startIcon.getIconWidth()/2,
+ screen.y - endIcon.getIconHeight());
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#visitBoundingBox(org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor)
+ */
+ @Override
+ public void visitBoundingBox(BoundingXYVisitor v) {
+ for (Node node : routingModel.getSelectedNodes()) {
+ v.visit(node);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer#destroy()
+ */
+ @Override
+ public void destroy() {
+ routingModel.reset();
+// layerAdded = false;
+ }
+
+ /**
+ * Draw a line with the given color.
+ */
+ private void drawEdge(Graphics g, MapView mv, OsmEdge edge, Color col, int width,
+ boolean showDirection) {
+ g.setColor(col);
+ Point from;
+ Point to;
+ from = mv.getPoint(edge.fromEastNorth());
+ to = mv.getPoint(edge.toEastNorth());
+
+ Graphics2D g2d = (Graphics2D)g;
+ Stroke oldStroke = g2d.getStroke();
+ g2d.setStroke(new BasicStroke(width)); // thickness
+ g.drawLine(from.x, from.y, to.x, to.y);
+ if (showDirection) {
+ double t = Math.atan2(to.y-from.y, to.x-from.x) + Math.PI;
+ g.drawLine(to.x,to.y, (int)(to.x + 10*Math.cos(t-ARROW_PHI)), (int)(to.y + 10*Math.sin(t-ARROW_PHI)));
+ g.drawLine(to.x,to.y, (int)(to.x + 10*Math.cos(t+ARROW_PHI)), (int)(to.y + 10*Math.sin(t+ARROW_PHI)));
+ }
+ g2d.setStroke(oldStroke);
+ }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/RoutingModel.java b/routing/src/com/innovant/josm/plugin/routing/RoutingModel.java
new file mode 100644
index 0000000..553721c
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/RoutingModel.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+
+import com.innovant.josm.jrt.core.RoutingGraph;
+import com.innovant.josm.jrt.core.RoutingGraph.Algorithm;
+import com.innovant.josm.jrt.osm.OsmEdge;
+
+
+/**
+ * This class holds all the routing data and operations
+ * @author juangui
+ * @author Jose Vidal
+ *
+ */
+public class RoutingModel {
+
+ /**
+ * Logger
+ */
+ static Logger logger = Logger.getLogger(RoutingModel.class);
+
+ /**
+ * Graph to calculate route
+ */
+ public RoutingGraph routingGraph=null;
+
+ /**
+ * List of nodes that the route has to traverse
+ */
+ private List<Node> nodes=null;
+
+ private List<OsmEdge> path=null;
+ /**
+ * Flag to advise about changes in the selected nodes.
+ */
+ private boolean changeNodes=false;
+ /**
+ * Default Constructor.
+ */
+ public RoutingModel(DataSet data) {
+ nodes = new ArrayList<Node>();
+System.out.println("gr " + data);
+ routingGraph = new RoutingGraph(data);
+ }
+
+ /**
+ * Method that returns the selected nodes to calculate route.
+ * @return the selectedNodes
+ */
+ public List<Node> getSelectedNodes() {
+ return nodes;
+ }
+
+ /**
+ * Adds a node to the route node list.
+ * @param node the node to add.
+ */
+ public void addNode(Node node) {
+ nodes.add(node);
+ this.changeNodes=true;
+ }
+
+ /**
+ * Removes a node from the route node list.
+ * @param index the index of the node to remove.
+ */
+ public void removeNode(int index) {
+ if (nodes.size()>index) {
+ nodes.remove(index);
+ this.changeNodes=true;
+ }
+ }
+
+ /**
+ * Inserts a node in the route node list.
+ * @param index the index where the node will be inserted
+ * @param node the node to be inserted
+ */
+ public void insertNode(int index, Node node) {
+ if (nodes.size()>=index) {
+ nodes.add(index, node);
+ this.changeNodes=true;
+ }
+ }
+
+ /**
+ * Reverse list of nodes
+ */
+ public void reverseNodes() {
+ List<Node> aux = new ArrayList<Node>();
+ for (Node n : nodes) {
+ aux.add(0,n);
+ }
+ nodes = aux;
+ this.changeNodes=true;
+ }
+
+ /**
+ * Get the edges of the route.
+ * @return A list of edges forming the shortest path
+ */
+ public List<OsmEdge> getRouteEdges() {
+ if (this.changeNodes || path==null)
+ {
+ path=this.routingGraph.applyAlgorithm(nodes, Algorithm.ROUTING_ALG_DIJKSTRA);
+ this.changeNodes=false;
+ }
+ return path;
+ }
+
+ /**
+ * Marks that some node or the node order has changed so the path should be computed again
+ */
+ public void setNodesChanged() {
+ this.changeNodes = true;
+ }
+
+ /**
+ * Resets all data.
+ */
+ public void reset() {
+ nodes.clear();
+ this.changeNodes=true;
+ }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/RoutingPlugin.java b/routing/src/com/innovant/josm/plugin/routing/RoutingPlugin.java
new file mode 100644
index 0000000..d91443b
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/RoutingPlugin.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+
+import com.innovant.josm.plugin.routing.actions.AddRouteNodeAction;
+import com.innovant.josm.plugin.routing.actions.MoveRouteNodeAction;
+import com.innovant.josm.plugin.routing.actions.RemoveRouteNodeAction;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+import com.innovant.josm.plugin.routing.gui.RoutingMenu;
+import com.innovant.josm.plugin.routing.gui.RoutingPreferenceDialog;
+
+/**
+ * The main class of the routing plugin
+ * @author juangui
+ * @author Jose Vidal
+ * @author cdaller
+ *
+ * @version 0.3
+ */
+public class RoutingPlugin extends Plugin implements LayerChangeListener {
+ /**
+ * Logger
+ */
+ static Logger logger = Logger.getLogger(RoutingPlugin.class);
+
+ /**
+ * The list of routing layers
+ */
+ private ArrayList<RoutingLayer> layers;
+
+ /**
+ * The side dialog where nodes are listed
+ */
+ private RoutingDialog routingDialog;
+
+ /**
+ * Preferences Settings Dialog.
+ */
+ private PreferenceSetting preferenceSettings;
+
+ /**
+ * MapMode for adding route nodes.
+ * We use this field to enable or disable the mode automatically.
+ */
+ private AddRouteNodeAction addRouteNodeAction;
+
+ /**
+ * MapMode for removing route nodes.
+ * We use this field to enable or disable the mode automatically.
+ */
+ private RemoveRouteNodeAction removeRouteNodeAction;
+
+ /**
+ * MapMode for moving route nodes.
+ * We use this field to enable or disable the mode automatically.
+ */
+ private MoveRouteNodeAction moveRouteNodeAction;
+
+ /**
+ * IconToggleButton for adding route nodes, we use this field to show or hide the button.
+ */
+ private IconToggleButton addRouteNodeButton;
+
+ /**
+ * IconToggleButton for removing route nodes, we use this field to show or hide the button.
+ */
+ private IconToggleButton removeRouteNodeButton;
+
+ /**
+ * IconToggleButton for moving route nodes, we use this field to show or hide the button.
+ */
+ private IconToggleButton moveRouteNodeButton;
+
+ /**
+ * IconToggleButton for moving route nodes, we use this field to show or hide the button.
+ */
+ private RoutingMenu menu;
+
+ /**
+ * Reference for the plugin class (as if it were a singleton)
+ */
+ private static RoutingPlugin plugin;
+
+ /**
+ * Default Constructor
+ */
+ public RoutingPlugin() {
+ super();
+ plugin = this; // Assign reference to the plugin class
+ DOMConfigurator.configure("log4j.xml");
+ logger.debug("Loading routing plugin...");
+ preferenceSettings=new RoutingPreferenceDialog();
+ // Create side dialog
+ routingDialog = new RoutingDialog();
+ // Initialize layers list
+ layers = new ArrayList<RoutingLayer>();
+ // Add menu
+ menu = new RoutingMenu();
+ // Register this class as LayerChangeListener
+ Layer.listeners.add(this);
+ logger.debug("Finished loading plugin");
+ }
+
+ /**
+ * Provides static access to the plugin instance, to enable access to the plugin methods
+ * @return the instance of the plugin
+ */
+ public static RoutingPlugin getInstance() {
+ return plugin;
+ }
+
+ /**
+ * Get the routing side dialog
+ * @return The instance of the routing side dialog
+ */
+ public RoutingDialog getRoutingDialog() {
+ return routingDialog;
+ }
+
+ public void addLayer() {
+ OsmDataLayer osmLayer = Main.map.mapView.getEditLayer();
+ RoutingLayer layer = new RoutingLayer(tr("Routing") + " [" + osmLayer.getName() + "]", osmLayer);
+ layers.add(layer);
+ Main.main.addLayer(layer);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.plugins.Plugin#mapFrameInitialized(org.openstreetmap.josm.gui.MapFrame, org.openstreetmap.josm.gui.MapFrame)
+ */
+ @Override
+ public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+ if(newFrame != null) {
+ // Create plugin map modes
+ addRouteNodeAction = new AddRouteNodeAction(newFrame);
+ removeRouteNodeAction = new RemoveRouteNodeAction(newFrame);
+ moveRouteNodeAction = new MoveRouteNodeAction(newFrame);
+ // Create plugin buttons and add them to the toolbar
+ addRouteNodeButton = new IconToggleButton(addRouteNodeAction);
+ removeRouteNodeButton = new IconToggleButton(removeRouteNodeAction);
+ moveRouteNodeButton = new IconToggleButton(moveRouteNodeAction);
+ newFrame.addMapMode(addRouteNodeButton);
+ newFrame.addMapMode(removeRouteNodeButton);
+ newFrame.addMapMode(moveRouteNodeButton);
+ newFrame.toolGroup.add(addRouteNodeButton);
+ newFrame.toolGroup.add(removeRouteNodeButton);
+ newFrame.toolGroup.add(moveRouteNodeButton);
+ // Hide them by default
+ addRouteNodeButton.setVisible(false);
+ removeRouteNodeButton.setVisible(false);
+ moveRouteNodeButton.setVisible(false);
+ // Enable menu
+ menu.enableStartItem();
+ newFrame.addToggleDialog(routingDialog);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener#activeLayerChange(org.openstreetmap.josm.gui.layer.Layer, org.openstreetmap.josm.gui.layer.Layer)
+ */
+ public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+ routingDialog.refresh();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener#layerAdded(org.openstreetmap.josm.gui.layer.Layer)
+ */
+ public void layerAdded(Layer newLayer) {
+ // Add button(s) to the tool bar when the routing layer is added
+ if (newLayer instanceof RoutingLayer) {
+ addRouteNodeButton.setVisible(true);
+ removeRouteNodeButton.setVisible(true);
+ moveRouteNodeButton.setVisible(true);
+ menu.enableRestOfItems();
+ // Set layer on top and select layer, also refresh toggleDialog to reflect selection
+ Main.map.mapView.moveLayer(newLayer, 0);
+ logger.debug("Added routing layer.");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener#layerRemoved(org.openstreetmap.josm.gui.layer.Layer)
+ */
+ public void layerRemoved(Layer oldLayer) {
+ if ((oldLayer instanceof RoutingLayer) & (layers.size()==1)) {
+ // Remove button(s) from the tool bar when the last routing layer is removed
+ addRouteNodeButton.setVisible(false);
+ removeRouteNodeButton.setVisible(false);
+ moveRouteNodeButton.setVisible(false);
+ menu.disableRestOfItems();
+ layers.remove(oldLayer);
+ logger.debug("Removed routing layer.");
+ } else if (oldLayer instanceof OsmDataLayer) {
+ // Remove all associated routing layers
+ // Convert to Array to prevent ConcurrentModificationException when removing layers from ArrayList
+ // FIXME: can't remove associated routing layers without triggering exceptions in some cases
+ RoutingLayer[] layersArray = layers.toArray(new RoutingLayer[0]);
+ for (int i=0;i<layersArray.length;i++) {
+ if (layersArray[i].getDataLayer().equals(oldLayer)) {
+ try {
+ // Remove layer
+ Main.map.mapView.removeLayer(layersArray[i]);
+ } catch (IllegalArgumentException e) {
+ }
+ }
+ }
+ }
+ // Reload RoutingDialog table model
+ routingDialog.refresh();
+ }
+
+ /* (non-Javadoc)
+ * @see org.openstreetmap.josm.plugins.Plugin#getPreferenceSetting()
+ */
+ @Override
+ public PreferenceSetting getPreferenceSetting() {
+ return preferenceSettings;
+ }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/actions/AddRouteNodeAction.java b/routing/src/com/innovant/josm/plugin/routing/actions/AddRouteNodeAction.java
new file mode 100644
index 0000000..3c6b626
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/actions/AddRouteNodeAction.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.MouseEvent;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+
+/**
+ * Accounts for the selection or unselection of the routing tool in the tool bar,
+ * and the mouse events when this tool is selected
+ * @author Juangui
+ * @author Jose Vidal
+ *
+ */
+public class AddRouteNodeAction extends MapMode {
+ /**
+ * Serial.
+ */
+ private static final long serialVersionUID = 1L;
+ /**
+ * Logger.
+ */
+ static Logger logger = Logger.getLogger(AddRouteNodeAction.class);
+ /**
+ * Routing Dialog.
+ */
+ private RoutingDialog routingDialog;
+
+ /**
+ * Constructor
+ * @param mapFrame
+ */
+ public AddRouteNodeAction(MapFrame mapFrame) {
+ // TODO Use constructor with shortcut
+ super(tr("Routing"), "add",
+ tr("Click to add destination."),
+ mapFrame, ImageProvider.getCursor("crosshair", null));
+ this.routingDialog = RoutingPlugin.getInstance().getRoutingDialog();
+ }
+
+ @Override public void enterMode() {
+ super.enterMode();
+ Main.map.mapView.addMouseListener(this);
+ }
+
+ @Override public void exitMode() {
+ super.exitMode();
+ Main.map.mapView.removeMouseListener(this);
+ }
+
+ @Override public void mouseClicked(MouseEvent e) {
+ // If left button is clicked
+ if (e.getButton() == MouseEvent.BUTTON1) {
+ // Search for nearest highway node
+ Node node = null;
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ node = layer.getNearestHighwayNode(e.getPoint());
+ if(node == null) {
+ logger.debug("no selected node");
+ return;
+ }
+ logger.debug("selected node " + node);
+ layer.getRoutingModel().addNode(node);
+ routingDialog.addNode(node);
+ }
+ }
+ Main.map.repaint();
+ }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/actions/MoveRouteNodeAction.java b/routing/src/com/innovant/josm/plugin/routing/actions/MoveRouteNodeAction.java
new file mode 100644
index 0000000..98ca21e
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/actions/MoveRouteNodeAction.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Point;
+import java.awt.event.MouseEvent;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+
+/**
+ * Accounts for the selection or unselection of the routing tool in the tool bar,
+ * and the mouse events when this tool is selected
+ * @author Juangui
+ * @author Jose Vidal
+ *
+ */
+public class MoveRouteNodeAction extends MapMode {
+ /**
+ * Serial.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Square of the distance radius where route nodes can be selected for dragging
+ */
+ private static final int DRAG_SQR_RADIUS = 100;
+
+ /**
+ * Logger.
+ */
+ static Logger logger = Logger.getLogger(RoutingLayer.class);
+
+ /**
+ * Routing Dialog.
+ */
+ private RoutingDialog routingDialog;
+
+ /**
+ * Index of dragged node
+ */
+ private int index;
+
+ /**
+ * Constructor
+ * @param mapFrame
+ */
+ public MoveRouteNodeAction(MapFrame mapFrame) {
+ // TODO Use constructor with shortcut
+ super(tr("Routing"), "move",
+ tr("Click and drag to move destination"),
+ mapFrame, ImageProvider.getCursor("normal", "move"));
+ this.routingDialog = RoutingPlugin.getInstance().getRoutingDialog();
+ }
+
+ @Override public void enterMode() {
+ super.enterMode();
+ Main.map.mapView.addMouseListener(this);
+ }
+
+ @Override public void exitMode() {
+ super.exitMode();
+ Main.map.mapView.removeMouseListener(this);
+ }
+
+ @Override public void mousePressed(MouseEvent e) {
+ // If left button is pressed
+ if (e.getButton() == MouseEvent.BUTTON1) {
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = layer.getRoutingModel();
+ // Search for the nearest node in the list
+ List<Node> nl = routingModel.getSelectedNodes();
+ index = -1;
+ double dmax = DRAG_SQR_RADIUS; // maximum distance, in pixels
+ for (int i=0;i<nl.size();i++) {
+ Node node = nl.get(i);
+ double d = Main.map.mapView.getPoint(node).distanceSq(e.getPoint());
+ if (d < dmax) {
+ dmax = d;
+ index = i;
+ }
+ }
+ if (index>=0)
+ logger.debug("Moved from node " + nl.get(index));
+ }
+ }
+ }
+
+ @Override public void mouseReleased(MouseEvent e) {
+ // If left button is released and a route node is being dragged
+ if ((e.getButton() == MouseEvent.BUTTON1) && (index>=0)) {
+ searchAndReplaceNode(e.getPoint());
+ }
+ }
+
+ @Override public void mouseDragged(MouseEvent e) {
+ }
+
+ private void searchAndReplaceNode(Point point) {
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = layer.getRoutingModel();
+ // Search for nearest highway node
+ Node node = null;
+ node = layer.getNearestHighwayNode(point);
+ if (node == null) {
+ logger.debug("Didn't found a close node to move to.");
+ return;
+ }
+ logger.debug("Moved to node " + node);
+ routingModel.removeNode(index);
+ routingDialog.removeNode(index);
+ routingModel.insertNode(index, node);
+ routingDialog.insertNode(index, node);
+ Main.map.repaint();
+ }
+ }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/actions/RemoveRouteNodeAction.java b/routing/src/com/innovant/josm/plugin/routing/actions/RemoveRouteNodeAction.java
new file mode 100644
index 0000000..172031b
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/actions/RemoveRouteNodeAction.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.MouseEvent;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+import com.innovant.josm.plugin.routing.gui.RoutingDialog;
+
+/**
+ * Accounts for the selection or unselection of the remove route nodes tool in the tool bar,
+ * and the mouse events when this tool is selected
+ * @author Juangui
+ * @author Jose Vidal
+ *
+ */
+public class RemoveRouteNodeAction extends MapMode {
+ /**
+ * Serial.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Square of the distance radius where route nodes can be removed
+ */
+ private static final int REMOVE_SQR_RADIUS = 100;
+
+ /**
+ * Logger.
+ */
+ static Logger logger = Logger.getLogger(RoutingLayer.class);
+ /**
+ * Routing Dialog.
+ */
+ private RoutingDialog routingDialog;
+
+ public RemoveRouteNodeAction(MapFrame mapFrame) {
+ // TODO Use constructor with shortcut
+ super(tr("Routing"), "remove",
+ tr("Click to remove destination"),
+ mapFrame, ImageProvider.getCursor("normal", "delete"));
+ this.routingDialog = RoutingPlugin.getInstance().getRoutingDialog();
+ }
+
+ @Override public void enterMode() {
+ super.enterMode();
+ Main.map.mapView.addMouseListener(this);
+ }
+
+ @Override public void exitMode() {
+ super.exitMode();
+ Main.map.mapView.removeMouseListener(this);
+ }
+
+ @Override public void mouseClicked(MouseEvent e) {
+ // If left button is clicked
+ if (e.getButton() == MouseEvent.BUTTON1) {
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = layer.getRoutingModel();
+ // Search for the nearest node in the list
+ List<Node> nl = routingModel.getSelectedNodes();
+ int index = -1;
+ double dmax = REMOVE_SQR_RADIUS; // maximum distance, in pixels
+ for (int i=0;i<nl.size();i++) {
+ Node node = nl.get(i);
+ double d = Main.map.mapView.getPoint(node).distanceSq(e.getPoint());
+ if (d < dmax) {
+ dmax = d;
+ index = i;
+ }
+ }
+ // If found a close node, remove it and recalculate route
+ if (index >= 0) {
+ // Remove node
+ logger.debug("Removing node " + nl.get(index));
+ routingModel.removeNode(index);
+ routingDialog.removeNode(index);
+ Main.map.repaint();
+ } else {
+ logger.debug("Can't find a node to remove.");
+ }
+ }
+ }
+ }
+
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingDialog.java b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingDialog.java
new file mode 100644
index 0000000..ed86883
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingDialog.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.ComponentOrientation;
+import java.awt.Font;
+import java.awt.event.KeyEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultListModel;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.border.EtchedBorder;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.tools.Shortcut;
+
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+
+
+/**
+ * @author jose
+ *
+ */
+public class RoutingDialog extends ToggleDialog {
+
+ private DefaultListModel model;
+ private JList jList = null;
+ private JScrollPane jScrollPane = null;
+
+ /**
+ * Serial UID
+ */
+ private static final long serialVersionUID = 8625615652900341987L;
+
+ public RoutingDialog() {
+ super(tr("Routing"), "routing", tr("Open a list of routing nodes"),
+ Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Routing")), KeyEvent.VK_R, Shortcut.GROUP_LAYER), 150);
+ model= new DefaultListModel();
+ this.setSize(456, 292);
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
+ this.setName("PrincipalDialog");
+ this.setFont(new Font("Dialog", Font.PLAIN, 12));
+ this.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+ this.add(getJScrollPane(), null);
+
+ }
+
+ /**
+ * This method initializes jScrollPane
+ *
+ * @return javax.swing.JScrollPane
+ */
+ private JScrollPane getJScrollPane() {
+ if (jScrollPane == null) {
+ jScrollPane = new JScrollPane();
+ jScrollPane.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
+ jScrollPane.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+ jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ jScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ jScrollPane.setName("nList");
+ jScrollPane.setViewportBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
+ jScrollPane.setViewportView(getJList());
+ }
+ return jScrollPane;
+ }
+
+ /**
+ * This method initializes jList
+ *
+ * @return javax.swing.JList
+ */
+ private JList getJList() {
+ if (jList == null) {
+ jList = new JList();
+ jList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+ jList.setModel(model);
+ }
+ return jList;
+ }
+
+ /**
+ * Remove item from the list of nodes
+ * @param index
+ */
+ public void removeNode(int index) {
+ model.remove(index);
+ }
+
+ /**
+ * Add item to the list of nodes
+ * @param obj
+ */
+ public void addNode(Node n) {
+ model.addElement(n.getId()+" ["+n.getCoor().toDisplayString()+"]");
+ }
+
+ /**
+ * Insert item to the list of nodes
+ * @param index
+ * @param obj
+ */
+ public void insertNode(int index, Node n) {
+ model.insertElementAt(n.getId()+" ["+n.getCoor().toDisplayString()+"]", index);
+ }
+
+ /**
+ * Clear list of nodes
+ */
+ public void clearNodes() {
+ model.clear();
+ }
+
+ public void refresh() {
+ clearNodes();
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer routingLayer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = routingLayer.getRoutingModel();
+ for (Node n : routingModel.getSelectedNodes()) {
+ addNode(n);
+ }
+ }
+ }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingMenu.java b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingMenu.java
new file mode 100644
index 0000000..29dff73
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingMenu.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.gui;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JRadioButtonMenuItem;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainMenu;
+
+import com.innovant.josm.jrt.core.RoutingGraph.RouteType;
+import com.innovant.josm.plugin.routing.RoutingLayer;
+import com.innovant.josm.plugin.routing.RoutingModel;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+
+/**
+ * The menu bar from this plugin
+ * @author jvidal
+ *
+ */
+public class RoutingMenu extends JMenu {
+
+ /**
+ * Default serial version UID
+ */
+ private static final long serialVersionUID = 3559922048225708480L;
+
+ private JMenuItem startMI;
+ private JMenuItem reverseMI;
+ private JMenuItem clearMI;
+ private JMenu criteriaM;
+ private JMenu menu;
+
+ /**
+ * @param s
+ */
+ public RoutingMenu() {
+ MainMenu mm = Main.main.menu;
+ menu = mm.addMenu(marktr("Routing"), KeyEvent.VK_O, mm.defaultMenuPos, ht("/Plugin/Routing"));
+
+ startMI = new JMenuItem(tr("Add routing layer"));
+ startMI.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ RoutingPlugin.getInstance().addLayer();
+ }
+ });
+ menu.add(startMI);
+
+ menu.addSeparator();
+ ButtonGroup group = new ButtonGroup();
+
+ criteriaM = new JMenu(tr("Criteria"));
+
+ JRadioButtonMenuItem rshorter = new JRadioButtonMenuItem(tr("Shortest"));
+ rshorter.setSelected(true);
+ rshorter.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = layer.getRoutingModel();
+ if (e.getStateChange()==ItemEvent.SELECTED) {
+ routingModel.routingGraph.setTypeRoute(RouteType.SHORTEST);
+ } else {
+ routingModel.routingGraph.setTypeRoute(RouteType.FASTEST);
+ }
+ // routingModel.routingGraph.resetGraph();
+ // routingModel.routingGraph.createGraph();
+ //TODO: Change this way
+ //FIXME: do not change node but recalculate routing.
+ routingModel.setNodesChanged();
+ Main.map.repaint();
+ }
+ }
+
+ });
+
+ JRadioButtonMenuItem rfaster = new JRadioButtonMenuItem(tr("Fastest"));
+ group.add(rshorter);
+ group.add(rfaster);
+ criteriaM.add(rshorter);
+ criteriaM.add(rfaster);
+
+ criteriaM.addSeparator();
+ JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem("Ignore oneways");
+ cbmi.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = layer.getRoutingModel();
+ if (e.getStateChange()==ItemEvent.SELECTED)
+ routingModel.routingGraph.getRoutingProfile().setOnewayUse(false);
+ else
+ routingModel.routingGraph.getRoutingProfile().setOnewayUse(true);
+ routingModel.setNodesChanged();
+ Main.map.repaint();
+ }
+ }
+ });
+ criteriaM.add(cbmi);
+ menu.add(criteriaM);
+
+ menu.addSeparator();
+ reverseMI = new JMenuItem(tr("Reverse route"));
+ reverseMI.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = layer.getRoutingModel();
+ routingModel.reverseNodes();
+ Main.map.repaint();
+ }
+ }
+ });
+ menu.add(reverseMI);
+
+ clearMI = new JMenuItem(tr("Clear route"));
+ clearMI.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (Main.map.mapView.getActiveLayer() instanceof RoutingLayer) {
+ RoutingLayer layer = (RoutingLayer)Main.map.mapView.getActiveLayer();
+ RoutingModel routingModel = layer.getRoutingModel();
+ // Reset routing nodes and paths
+ routingModel.reset();
+ RoutingPlugin.getInstance().getRoutingDialog().clearNodes();
+ Main.map.repaint();
+ }
+ }
+ });
+ menu.add(clearMI);
+
+ // Initially disabled
+ disableAllItems();
+ }
+
+ public void disableAllItems() {
+ startMI.setEnabled(false);
+ reverseMI.setEnabled(false);
+ clearMI.setEnabled(false);
+ criteriaM.setEnabled(false);
+ }
+
+ public void enableStartItem() {
+ startMI.setEnabled(true);
+ }
+
+ public void enableRestOfItems() {
+ reverseMI.setEnabled(true);
+ clearMI.setEnabled(true);
+ criteriaM.setEnabled(true);
+ }
+
+ public void disableRestOfItems() {
+ reverseMI.setEnabled(false);
+ clearMI.setEnabled(false);
+ criteriaM.setEnabled(false);
+ }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java
new file mode 100644
index 0000000..488f700
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java
@@ -0,0 +1,235 @@
+/*
+ *
+ * Copyright (C) 2008 Innovant
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
+ *
+ * For more information, please contact:
+ *
+ * Innovant
+ * juangui at gmail.com
+ * vidalfree at gmail.com
+ *
+ * http://public.grupoinnovant.com/blog
+ *
+ */
+
+package com.innovant.josm.plugin.routing.gui;
+
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.DefaultTableModel;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.apache.log4j.Logger;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+
+import com.innovant.josm.jrt.osm.OsmWayTypes;
+import com.innovant.josm.plugin.routing.RoutingPlugin;
+
+public class RoutingPreferenceDialog implements PreferenceSetting {
+
+ /**
+ * Logger
+ */
+ static Logger logger = Logger.getLogger(RoutingPreferenceDialog.class);
+
+ private Map<String, String> orig;
+ private DefaultTableModel model;
+
+ /**
+ * Constructor
+ */
+ public RoutingPreferenceDialog() {
+ super();
+ readPreferences();
+ }
+
+ public void addGui(final PreferenceDialog gui) {
+
+ JPanel principal = gui.createPreferenceTab("routing",
+ tr("Routing Plugin Preferences"), tr("Configure routing preferences."));
+
+ JPanel p = new JPanel();
+ p.setLayout(new GridBagLayout());
+
+ model = new DefaultTableModel(new String[] { tr("Highway type"),
+ tr("Speed (Km/h)") }, 0) {
+ private static final long serialVersionUID = 4253339034781567453L;
+
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return column != 0;
+ }
+ };
+ final JTable list = new JTable(model);
+ loadSpeeds(model);
+
+ JScrollPane scroll = new JScrollPane(list);
+
+ p.add(scroll, GBC.eol().fill(GBC.BOTH));
+ scroll.setPreferredSize(new Dimension(200, 200));
+
+ JButton add = new JButton(tr("Add"));
+ p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
+ p.add(add, GBC.std().insets(0, 5, 0, 0));
+ add.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ JPanel p = new JPanel(new GridBagLayout());
+ p.add(new JLabel(tr("Weight")), GBC.std().insets(0, 0, 5, 0));
+ JComboBox key = new JComboBox();
+ for (OsmWayTypes pk : OsmWayTypes.values())
+ key.addItem(pk.getTag());
+ JTextField value = new JTextField(10);
+ p.add(key, GBC.eop().insets(5, 0, 0, 0).fill(GBC.HORIZONTAL));
+ p.add(new JLabel(tr("Value")), GBC.std().insets(0, 0, 5, 0));
+ p.add(value, GBC.eol().insets(5, 0, 0, 0).fill(GBC.HORIZONTAL));
+ int answer = JOptionPane.showConfirmDialog(gui, p,
+ tr("Enter weight values"),
+ JOptionPane.OK_CANCEL_OPTION);
+ if (answer == JOptionPane.OK_OPTION) {
+ model
+ .addRow(new String[] {
+ key.getSelectedItem().toString(),
+ value.getText() });
+ }
+ }
+ });
+
+ JButton delete = new JButton(tr("Delete"));
+ p.add(delete, GBC.std().insets(0, 5, 0, 0));
+ delete.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (list.getSelectedRow() == -1)
+ JOptionPane.showMessageDialog(gui,
+ tr("Please select the row to delete."));
+ else {
+ Integer i;
+ while ((i = list.getSelectedRow()) != -1)
+ model.removeRow(i);
+ }
+ }
+ });
+
+ JButton edit = new JButton(tr("Edit"));
+ p.add(edit, GBC.std().insets(5, 5, 5, 0));
+ edit.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ edit(gui, list);
+ }
+ });
+
+ JTabbedPane Opciones = new JTabbedPane();
+ Opciones.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+
+ Opciones.addTab("Profile", null, p, null);
+// Opciones.addTab("Preferences", new JPanel());
+
+ list.addMouseListener(new MouseAdapter(){
+ @Override public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2)
+ edit(gui, list);
+ }
+ });
+
+ principal.add(Opciones, GBC.eol().fill(GBC.BOTH));
+
+ }
+
+ public boolean ok() {
+ for (int i = 0; i < model.getRowCount(); ++i) {
+ String value = model.getValueAt(i, 1).toString();
+ if (value.length() != 0) {
+ String key = model.getValueAt(i, 0).toString();
+ String origValue = orig.get(key);
+ if (origValue == null || !origValue.equals(value))
+ Main.pref.put(key, value);
+ orig.remove(key); // processed.
+ }
+ }
+ for (Entry<String, String> e : orig.entrySet())
+ Main.pref.put(e.getKey(), null);
+ return false;
+ }
+
+ private void edit(final PreferenceDialog gui, final JTable list) {
+ if (list.getSelectedRowCount() != 1) {
+ JOptionPane.showMessageDialog(gui,
+ tr("Please select the row to edit."));
+ return;
+ }
+ String v = JOptionPane.showInputDialog(tr("New value for {0}", model
+ .getValueAt(list.getSelectedRow(), 0)), model.getValueAt(list
+ .getSelectedRow(), 1));
+ if (v != null)
+ model.setValueAt(v, list.getSelectedRow(), 1);
+ }
+
+ private void loadSpeeds(DefaultTableModel model) {
+ // Read dialog values from preferences
+ readPreferences();
+ // Put these values in the model
+ for (String tag : orig.keySet()) {
+ model.addRow(new String[] { tag, orig.get(tag) });
+ }
+ }
+
+ private void readPreferences() {
+ orig = Main.pref.getAllPrefix("routing.profile.default.speed");
+ if (orig.size() == 0) { // defaults
+ logger.debug("Loading Default Preferences.");
+ for (OsmWayTypes owt : OsmWayTypes.values()) {
+ Main.pref.putInteger("routing.profile.default.speed."
+ + owt.getTag(), owt.getSpeed());
+ }
+ orig = Main.pref.getAllPrefix("routing.profile.default.speed");
+ }
+ else logger.debug("Default preferences already exist.");
+ }
+
+ private String getKeyTag(String tag) {
+ return tag.split(".", 5)[4];
+ }
+
+ private String getTypeTag(String tag) {
+ return tag.split(".", 5)[3];
+ }
+
+ private String getNameTag(String tag) {
+ return tag.split(".", 5)[2];
+ }
+}
diff --git a/routing/src/com/innovant/josm/plugin/routing/package.html b/routing/src/com/innovant/josm/plugin/routing/package.html
new file mode 100644
index 0000000..d45fa25
--- /dev/null
+++ b/routing/src/com/innovant/josm/plugin/routing/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Package</title>
+</head>
+<body bgcolor="white">
+Main classes of Plugin.
+</body>
+</html>
diff --git a/slippymap/.classpath b/slippymap/.classpath
index 32eb8e6..17b8e6a 100644
--- a/slippymap/.classpath
+++ b/slippymap/.classpath
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry combineaccessrules="false" kind="src" path="/josm"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
<classpathentry kind="output" path="build"/>
</classpath>
diff --git a/slippymap/build.xml b/slippymap/build.xml
index a0eed67..b387b4e 100644
--- a/slippymap/build.xml
+++ b/slippymap/build.xml
@@ -25,7 +25,7 @@
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
<attribute name="Plugin-Description" value="Displays a slippy map grid in JOSM. Can load tiles from slippy map as background and request updates."/>
<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/SlippyMap"/>
- <attribute name="Plugin-Mainversion" value="2196"/>
+ <attribute name="Plugin-Mainversion" value="2450"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
index e33a6d5..663a982 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
@@ -4,8 +4,8 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Color;
import java.awt.Component;
-import java.awt.Dimension;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
@@ -15,22 +15,29 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.ImageObserver;
import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
+import java.util.HashSet;
import java.util.LinkedList;
-import java.util.TreeSet;
+import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Icon;
+import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
+import org.openstreetmap.gui.jmapviewer.JobDispatcher;
+import org.openstreetmap.gui.jmapviewer.MemoryTileCache;
+import org.openstreetmap.gui.jmapviewer.OsmFileCacheTileLoader;
+import org.openstreetmap.gui.jmapviewer.Tile;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
@@ -48,73 +55,141 @@ import org.openstreetmap.josm.tools.ImageProvider;
* @author Dave Hansen <dave at sr71.net>
*
*/
-public class SlippyMapLayer extends Layer implements ImageObserver,
- PreferenceChangedListener {
+public class SlippyMapLayer extends Layer implements PreferenceChangedListener, ImageObserver,
+ TileLoaderListener {
+ boolean debug = false;
+ void out(String s)
+ {
+ Main.debug(s);
+ }
+
+ protected MemoryTileCache tileCache;
+ protected TileSource tileSource;
+ protected TileLoader tileLoader;
+ JobDispatcher jobDispatcher = JobDispatcher.getInstance();
+
+ HashSet<Tile> tileRequestsOutstanding = new HashSet<Tile>();
+ public synchronized void tileLoadingFinished(Tile tile, boolean success)
+ {
+ tile.setLoaded(true);
+ needRedraw = true;
+ Main.map.repaint(100);
+ tileRequestsOutstanding.remove(tile);
+ if (debug)
+ out("tileLoadingFinished() tile: " + tile + " success: " + success);
+ }
+ public TileCache getTileCache()
+ {
+ return tileCache;
+ }
+ void clearTileCache()
+ {
+ if (debug)
+ out("clearing tile storage");
+ tileCache = new MemoryTileCache();
+ tileCache.setCacheSize(2000);
+ }
+
/**
* Actual zoom lvl. Initial zoom lvl is set to
* {@link SlippyMapPreferences#getMinZoomLvl()}.
*/
- public int currentZoomLevel = SlippyMapPreferences.getMinZoomLvl();
- private Hashtable<SlippyMapKey, SlippyMapTile> tileStorage = null;
+ public int currentZoomLevel;
- Point[][] pixelpos = new Point[21][21];
LatLon lastTopLeft;
LatLon lastBotRight;
private Image bufferImage;
- private SlippyMapTile clickedTile;
+ private Tile clickedTile;
private boolean needRedraw;
private JPopupMenu tileOptionMenu;
+ JCheckBoxMenuItem autoZoomPopup;
+ Tile showMetadataTile;
- static void debug(String msg)
+ void redraw()
{
+ needRedraw = true;
+ Main.map.repaint();
+ }
+ void newTileStorage()
+ {
+ int origZoom = currentZoomLevel;
+ tileSource = SlippyMapPreferences.getMapSource();
+ // The minimum should also take care of integer parsing
+ // errors which would leave us with a zoom of -1 otherwise
+ if (tileSource.getMaxZoom() < currentZoomLevel)
+ currentZoomLevel = tileSource.getMaxZoom();
+ if (tileSource.getMinZoom() > currentZoomLevel)
+ currentZoomLevel = tileSource.getMinZoom();
+ if (currentZoomLevel != origZoom) {
+ out("changed currentZoomLevel loading new tile store from " + origZoom + " to " + currentZoomLevel);
+ out("tileSource.getMinZoom(): " + tileSource.getMinZoom());
+ out("tileSource.getMaxZoom(): " + tileSource.getMaxZoom());
+ SlippyMapPreferences.setLastZoom(currentZoomLevel);
+ }
+ clearTileCache();
+ //tileLoader = new OsmTileLoader(this);
+ tileLoader = new OsmFileCacheTileLoader(this);
}
+
@SuppressWarnings("serial")
public SlippyMapLayer() {
super(tr("Slippy Map"));
+
setBackgroundLayer(true);
+ this.setVisible(true);
- clearTileStorage();
+ currentZoomLevel = SlippyMapPreferences.getLastZoom();
+ newTileStorage();
tileOptionMenu = new JPopupMenu();
+
+ autoZoomPopup = new JCheckBoxMenuItem();
+ autoZoomPopup.setAction(new AbstractAction(tr("Auto Zoom")) {
+ public void actionPerformed(ActionEvent ae) {
+ boolean new_state = !SlippyMapPreferences.getAutozoom();
+ SlippyMapPreferences.setAutozoom(new_state);
+ }
+ });
+ autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
+ tileOptionMenu.add(autoZoomPopup);
+
tileOptionMenu.add(new JMenuItem(new AbstractAction(tr("Load Tile")) {
public void actionPerformed(ActionEvent ae) {
if (clickedTile != null) {
- loadSingleTile(clickedTile);
- needRedraw = true;
- Main.map.repaint();
+ loadTile(clickedTile);
+ redraw();
}
}
}));
tileOptionMenu.add(new JMenuItem(new AbstractAction(
- tr("Show Tile Status")) {
+ tr("Show Tile Info")) {
public void actionPerformed(ActionEvent ae) {
+ out("info tile: " + clickedTile);
if (clickedTile != null) {
- clickedTile.loadMetadata();
- needRedraw = true;
- Main.map.repaint();
+ showMetadataTile = clickedTile;
+ redraw();
}
}
}));
+ /* FIXME
tileOptionMenu.add(new JMenuItem(new AbstractAction(
tr("Request Update")) {
public void actionPerformed(ActionEvent ae) {
if (clickedTile != null) {
clickedTile.requestUpdate();
- needRedraw = true;
- Main.map.repaint();
+ redraw();
}
}
- }));
+ }));*/
tileOptionMenu.add(new JMenuItem(new AbstractAction(
tr("Load All Tiles")) {
public void actionPerformed(ActionEvent ae) {
- loadAllTiles();
- needRedraw = true;
- Main.map.repaint();
+ loadAllTiles(true);
+ redraw();
}
}));
@@ -123,8 +198,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
new AbstractAction(tr("Increase zoom")) {
public void actionPerformed(ActionEvent ae) {
increaseZoomLevel();
- needRedraw = true;
- Main.map.repaint();
+ redraw();
}
}));
@@ -132,19 +206,34 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
new AbstractAction(tr("Decrease zoom")) {
public void actionPerformed(ActionEvent ae) {
decreaseZoomLevel();
- Main.map.repaint();
+ redraw();
}
}));
+ // FIXME: currently ran in errors
+
+ tileOptionMenu.add(new JMenuItem(
+ new AbstractAction(tr("Snap to tile size")) {
+ public void actionPerformed(ActionEvent ae) {
+ if (lastImageScale == null) {
+ out("please wait for a tile to be loaded before snapping");
+ return;
+ }
+ double new_factor = Math.sqrt(lastImageScale);
+ if (debug)
+ out("tile snap: scale was: " + lastImageScale + ", new factor: " + new_factor);
+ Main.map.mapView.zoomToFactor(new_factor);
+ redraw();
+ }
+ }));
+ // end of adding menu commands
+
tileOptionMenu.add(new JMenuItem(
new AbstractAction(tr("Flush Tile Cache")) {
public void actionPerformed(ActionEvent ae) {
System.out.print("flushing all tiles...");
- for (SlippyMapTile t : tileStorage.values()) {
- t.dropImage();
- }
+ clearTileCache();
System.out.println("done");
- shrinkTileStorage(0);
}
}));
// end of adding menu commands
@@ -157,8 +246,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
if (e.getButton() != MouseEvent.BUTTON3)
return;
clickedTile = getTileForPixelpos(e.getX(), e.getY());
- tileOptionMenu.show(e.getComponent(), e.getX(), e
- .getY());
+ tileOptionMenu.show(e.getComponent(), e.getX(), e.getY());
}
});
@@ -179,6 +267,32 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
Main.pref.listener.add(this);
}
+ void zoomChanged()
+ {
+ if (debug)
+ out("zoomChanged(): " + currentZoomLevel);
+ needRedraw = true;
+ jobDispatcher.cancelOutstandingJobs();
+ tileRequestsOutstanding.clear();
+ SlippyMapPreferences.setLastZoom(currentZoomLevel);
+ }
+
+ int getMaxZoomLvl()
+ {
+ int ret = SlippyMapPreferences.getMaxZoomLvl();
+ if (tileSource.getMaxZoom() < ret)
+ ret = tileSource.getMaxZoom();
+ return ret;
+ }
+
+ int getMinZoomLvl()
+ {
+ int ret = SlippyMapPreferences.getMinZoomLvl();
+ if (tileSource.getMinZoom() > ret)
+ ret = tileSource.getMinZoom();
+ return ret;
+ }
+
/**
* Zoom in, go closer to map.
*
@@ -186,8 +300,9 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
*/
public boolean zoomIncreaseAllowed()
{
- boolean zia = currentZoomLevel < SlippyMapPreferences.getMaxZoomLvl();
- this.debug("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + SlippyMapPreferences.getMaxZoomLvl() );
+ boolean zia = currentZoomLevel < this.getMaxZoomLvl();
+ if (debug)
+ out("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + this.getMaxZoomLvl() );
return zia;
}
public boolean increaseZoomLevel()
@@ -195,11 +310,12 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
lastImageScale = null;
if (zoomIncreaseAllowed()) {
currentZoomLevel++;
- this.debug("increasing zoom level to: " + currentZoomLevel);
- needRedraw = true;
+ if (debug)
+ out("increasing zoom level to: " + currentZoomLevel);
+ zoomChanged();
} else {
System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+
- "MaxZoomLvl ("+SlippyMapPreferences.getMaxZoomLvl()+") reached.");
+ "MaxZoomLvl ("+this.getMaxZoomLvl()+") reached.");
return false;
}
return true;
@@ -212,15 +328,16 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
*/
public boolean zoomDecreaseAllowed()
{
- return currentZoomLevel > SlippyMapPreferences.getMinZoomLvl();
+ return currentZoomLevel > this.getMinZoomLvl();
}
public boolean decreaseZoomLevel() {
- int minZoom = SlippyMapPreferences.getMinZoomLvl();
+ int minZoom = this.getMinZoomLvl();
lastImageScale = null;
if (zoomDecreaseAllowed()) {
- this.debug("decreasing zoom level to: " + currentZoomLevel);
+ if (debug)
+ out("decreasing zoom level to: " + currentZoomLevel);
currentZoomLevel--;
- needRedraw = true;
+ zoomChanged();
} else {
System.err.println("current zoom lvl couldnt be decreased. MinZoomLvl("+minZoom+") reached.");
return false;
@@ -228,137 +345,61 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
return true;
}
- public void clearTileStorage() {
- // when max zoom lvl is begin saved, this method is called and probably
- // the setting isnt saved yet.
- int maxZoom = SlippyMapPreferences.getMaxZoomLvl();
- tileStorage = new Hashtable<SlippyMapKey, SlippyMapTile>();
- checkTileStorage();
- }
-
- class TileTimeComp implements Comparator<SlippyMapTile> {
- public int compare(SlippyMapTile s1, SlippyMapTile s2) {
- long t1 = s1.access_time();
- long t2 = s2.access_time();
- if (s1 == s2)
- return 0;
- if (t1 == t2) {
- t1 = s1.hashCode();
- t2 = s2.hashCode();
- }
- if (t1 < t2)
- return -1;
- return 1;
- }
- }
-
- /**
- * <p>
- * Check if tiles.size() is not more than maxNrTiles.
- * If yes, oldest tiles by timestamp are flushed from
- * the cache.
- * </p>
+ /*
+ * We use these for quick, hackish calculations. They
+ * are temporary only and intentionally not inserted
+ * into the tileCache.
*/
- public void shrinkTileStorage(int maxNrTiles)
- {
- TreeSet<SlippyMapTile> tiles = new TreeSet<SlippyMapTile>(new TileTimeComp());
- tiles.addAll(tileStorage.values());
- int nr_to_drop = tiles.size() - maxNrTiles;
- if (nr_to_drop <= 0) {
- this.debug("total of " + tiles.size() + " loaded tiles, size OK (< " + maxNrTiles + ")");
- return;
- }
- this.debug("total of " + tiles.size() + " tiles, need to flush " + nr_to_drop + " tiles");
- for (SlippyMapTile t : tiles) {
- if (nr_to_drop <= 0)
- break;
- t.dropImage();
- nr_to_drop--;
- //tileStorage.remove(t.getKey());
- }
- }
- long lastCheck = 0;
- public void checkTileStorage() {
- long now = System.currentTimeMillis();
- if (now - lastCheck < 1000)
- return;
- lastCheck = now;
- shrinkTileStorage(200);
- }
-
- LinkedList<SlippyMapTile> downloadQueue = new LinkedList<SlippyMapTile>();
- LinkedList<Image> downloadList = new LinkedList<Image>();
- int simultaneousTileDownloads = 5;
- int maxQueueSize = 50;
- synchronized void markDone(Image i)
- {
- boolean inList = downloadList.remove(i);
- if (!inList) {
- Main.debug("ERROR: downloaded image was not queued");
- return;
- }
- //System.out.print("currently downloading: " + downloadList.size() +
- // " queue size: " + downloadQueue.size() +" \r");
- if (downloadQueue.size() > 0)
- loadSingleTile(downloadQueue.getLast());
- }
- synchronized void loadSingleTile(SlippyMapTile tile)
- {
- // this moves the tile to the front of the line
- if (downloadQueue.contains(tile))
- downloadQueue.remove(tile);
- downloadQueue.addLast(tile);
- // We assume that a queue larger than this is
- // too big and the downloads at the end of it
- // too old to be relevant.
- while (downloadQueue.size() > maxQueueSize)
- downloadQueue.removeFirst();
- if (downloadList.size() > simultaneousTileDownloads)
- return;
- //System.out.print("currently downloading: " + downloadList.size() +
- // " queue size: " + downloadQueue.size() + " \r");
- while (!downloadQueue.isEmpty()) {
- // This is a FIFO queue. The reasoning is
- // that we want to draw the most recently
- // requested images now. We may have panned
- // or zoomed away
- tile = downloadQueue.removeLast();
- Image img = tile.loadImage();
- if (imageLoaded(img))
- continue;
- Toolkit.getDefaultToolkit().prepareImage(img, -1, -1, this);
- downloadList.add(img);
- break;
- }
+ synchronized Tile tempCornerTile(Tile t) {
+ int x = t.getXtile() + 1;
+ int y = t.getYtile() + 1;
+ int zoom = t.getZoom();
+ Tile tile = getTile(x, y, zoom);
+ if (tile != null)
+ return tile;
+ return new Tile(tileSource, x, y, zoom);
}
-
- synchronized SlippyMapTile getTile(int x, int y, int zoom) {
- SlippyMapKey key = new SlippyMapKey(x, y, zoom);
- if (!key.valid) {
- return null;
+ synchronized Tile getOrCreateTile(int x, int y, int zoom) {
+ Tile tile = getTile(x, y, zoom);
+ if (tile == null) {
+ tile = new Tile(tileSource, x, y, zoom);
+ tileCache.addTile(tile);
+ tile.loadPlaceholderFromCache(tileCache);
}
- return tileStorage.get(key);
+ return tile;
}
- synchronized SlippyMapTile putTile(SlippyMapTile tile, int x, int y, int zoom) {
- SlippyMapKey key = new SlippyMapKey(x, y, zoom);
- if (!key.valid) {
- return null;
- }
- return tileStorage.put(key, tile);
+ /*
+ * This can and will return null for tiles that are not
+ * already in the cache.
+ */
+ synchronized Tile getTile(int x, int y, int zoom) {
+ int max = (1 << zoom);
+ if (x < 0 || x >= max || y < 0 || y >= max)
+ return null;
+ Tile tile = tileCache.getTile(tileSource, x, y, zoom);
+ return tile;
}
- synchronized SlippyMapTile getOrCreateTile(int x, int y, int zoom) {
- SlippyMapTile tile = getTile(x, y, zoom);
- if (tile != null) {
- return tile;
- }
- tile = new SlippyMapTile(x, y, zoom);
- putTile(tile, x, y, zoom);
- return tile;
+ synchronized boolean loadTile(Tile tile)
+ {
+ if (tile == null)
+ return false;
+ if (tile.hasError())
+ return false;
+ if (tile.isLoaded())
+ return false;
+ if (tile.isLoading())
+ return false;
+ if (tileRequestsOutstanding.contains(tile))
+ return false;
+ tileRequestsOutstanding.add(tile);
+ jobDispatcher.addJob(tileLoader.createTileLoaderJob(tileSource,
+ tile.getXtile(), tile.getYtile(), tile.getZoom()));
+ return true;
}
- void loadAllTiles() {
+ void loadAllTiles(boolean force) {
MapView mv = Main.map.mapView;
LatLon topLeft = mv.getLatLon(0, 0);
LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
@@ -367,17 +408,11 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
// if there is more than 18 tiles on screen in any direction, do not
// load all tiles!
- if (ts.tilesSpanned() > (18*18)) {
+ if (ts.tooLarge()) {
System.out.println("Not downloading all tiles because there is more than 18 tiles on an axis!");
return;
}
-
- for (Tile t : ts.allTiles()) {
- SlippyMapTile tile = getOrCreateTile(t.x, t.y, currentZoomLevel);
- if (tile.getImage() == null) {
- this.loadSingleTile(tile);
- }
- }//end of for Tile t
+ ts.loadAllTiles(force);
}
/*
@@ -385,13 +420,38 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
* a 100x100 image being scaled to 50x50 would return 0.25.
*/
Image lastScaledImage = null;
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
+ boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
+ needRedraw = true;
+ if (debug)
+ out("imageUpdate() done: " + done + " calling repaint");
+ Main.map.repaint(done ? 0 : 100);
+ return !done;
+ }
+ boolean imageLoaded(Image i) {
+ if (i == null)
+ return false;
+ int status = Toolkit.getDefaultToolkit().checkImage(i, -1, -1, this);
+ if ((status & ALLBITS) != 0)
+ return true;
+ return false;
+ }
+ Image getLoadedTileImage(Tile tile)
+ {
+ if (!tile.isLoaded())
+ return null;
+ Image img = tile.getImage();
+ if (!imageLoaded(img))
+ return null;
+ return img;
+ }
- double getImageScaling(Image img, Point p0, Point p1) {
+ double getImageScaling(Image img, Rectangle r) {
int realWidth = -1;
int realHeight = -1;
- if (img != null) {
+ if (img != null) {
realWidth = img.getHeight(this);
- realWidth = img.getWidth(this);
+ realWidth = img.getWidth(this);
}
if (realWidth == -1 || realHeight == -1) {
/*
@@ -401,7 +461,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
* guess.
*/
if (lastScaledImage != null) {
- return getImageScaling(lastScaledImage, p0, p1);
+ return getImageScaling(lastScaledImage, r);
}
realWidth = 256;
realHeight = 256;
@@ -413,8 +473,8 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
* the millions, so make this a double to prevent integer
* overflows.
*/
- double drawWidth = p1.x - p0.x;
- double drawHeight = p1.x - p0.x;
+ double drawWidth = r.width;
+ double drawHeight = r.height;
// stem.out.println("drawWidth: " + drawWidth + " drawHeight: " +
// drawHeight);
@@ -424,133 +484,226 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
return drawArea / realArea;
}
- boolean imageLoaded(Image i) {
- if (i == null)
- return false;
+ LatLon tileLatLon(Tile t)
+ {
+ int zoom = t.getZoom();
+ return new LatLon(tileYToLat(t.getYtile(), zoom),
+ tileXToLon(t.getXtile(), zoom));
+ }
- int status = Toolkit.getDefaultToolkit().checkImage(i, -1, -1, this);
- if ((status & ALLBITS) != 0)
- return true;
- return false;
+ int paintFromOtherZooms(Graphics g, Tile topLeftTile, Tile botRightTile)
+ {
+ LatLon topLeft = tileLatLon(topLeftTile);
+ LatLon botRight = tileLatLon(botRightTile);
+
+
+ /*
+ * Go looking for tiles in zoom levels *other* than the current
+ * one. Even if they might look bad, they look better than a
+ * blank tile.
+ *
+ * Make darn sure that the tilesCache can either hold all of
+ * these "fake" tiles or that they don't get inserted in it to
+ * begin with.
+ */
+ //int otherZooms[] = {-5, -4, -3, 2, -2, 1, -1};
+ int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5};
+ int painted = 0;
+ debug = true;
+ for (int zoomOff : otherZooms) {
+ int zoom = currentZoomLevel + zoomOff;
+ if ((zoom < this.getMinZoomLvl()) ||
+ (zoom > this.getMaxZoomLvl())) {
+ continue;
+ }
+ TileSet ts = new TileSet(topLeft, botRight, zoom);
+ int zoom_painted = 0;
+ this.paintTileImages(g, ts, zoom, null);
+ if (debug && zoom_painted > 0)
+ out("painted " + zoom_painted + "/"+ ts.size() +
+ " tiles from zoom("+zoomOff+"): " + zoom);
+ painted += zoom_painted;
+ if (zoom_painted >= ts.size()) {
+ if (debug)
+ out("broke after drawing " + zoom_painted + "/"+ ts.size() + " at zoomOff: " + zoomOff);
+ break;
+ }
+ }
+ debug = false;
+ return painted;
+ }
+ Rectangle tileToRect(Tile t1)
+ {
+ /*
+ * We need to get a box in which to draw, so advance by one tile in
+ * each direction to find the other corner of the box.
+ * Note: this somewhat pollutes the tile cache
+ */
+ Tile t2 = tempCornerTile(t1);
+ Rectangle rect = new Rectangle(pixelPos(t1));
+ rect.add(pixelPos(t2));
+ return rect;
}
+ // 'source' is the pixel coordinates for the area that
+ // the img is capable of filling in. However, we probably
+ // only want a portion of it.
+ //
+ // 'border' is the screen cordinates that need to be drawn.
+ // We must not draw outside of it.
+ void drawImageInside(Graphics g, Image sourceImg, Rectangle source, Rectangle border)
+ {
+ Rectangle target = source;
+
+ // If a border is specified, only draw the intersection
+ // if what we have combined with what we are supposed
+ // to draw.
+ if (border != null) {
+ target = source.intersection(border);
+ if (debug)
+ out("source: " + source + "\nborder: " + border + "\nintersection: " + target);
+ }
+
+ // All of the rectangles are in screen coordinates. We need
+ // to how these correlate to the sourceImg pixels. We could
+ // avoid doing this by scaling the image up to the 'source' size,
+ // but this should be cheaper.
+ //
+ // In some projections, x any y are scaled differently enough to
+ // cause a pixel or two of fudge. Calculate them separately.
+ double imageYScaling = sourceImg.getHeight(this) / source.getHeight();
+ double imageXScaling = sourceImg.getWidth(this) / source.getWidth();
+
+ // How many pixels into the 'source' rectangle are we drawing?
+ int screen_x_offset = target.x - source.x;
+ int screen_y_offset = target.y - source.y;
+ // And how many pixels into the image itself does that
+ // correlate to?
+ int img_x_offset = (int)(screen_x_offset * imageXScaling);
+ int img_y_offset = (int)(screen_y_offset * imageYScaling);
+ // Now calculate the other corner of the image that we need
+ // by scaling the 'target' rectangle's dimensions.
+ int img_x_end = img_x_offset + (int)(target.getWidth() * imageXScaling);
+ int img_y_end = img_y_offset + (int)(target.getHeight() * imageYScaling);
+
+ if (debug) {
+ out("drawing image into target rect: " + target);
+ }
+ g.drawImage(sourceImg,
+ target.x, target.y,
+ target.x + target.width, target.y + target.height,
+ img_x_offset, img_y_offset,
+ img_x_end, img_y_end,
+ this);
+ float fadeBackground = SlippyMapPreferences.getFadeBackground();
+ if (fadeBackground != 0f) {
+ // dimm by painting opaque rect...
+ g.setColor(new Color(1f, 1f, 1f, fadeBackground));
+ g.fillRect(target.x, target.y,
+ target.width, target.height);
+ }
+ }
Double lastImageScale = null;
- int paintTileImages(Graphics g, TileSet ts, int zoom) {
- int paintedTiles = 0;
+ // This function is called for several zoom levels, not just
+ // the current one. It should not trigger any tiles to be
+ // downloaded. It should also avoid polluting the tile cache
+ // with any tiles since these tiles are not mandatory.
+ //
+ // The "border" tile tells us the boundaries of where we may
+ // draw. It will not be from the zoom level that is being
+ // drawn currently. If drawing the currentZoomLevel,
+ // border is null and we draw the entire tile set.
+ List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
+ Rectangle borderRect = null;
+ if (border != null)
+ borderRect = tileToRect(border);
+ List<Tile> missedTiles = new LinkedList<Tile>();
boolean imageScaleRecorded = false;
- Image img = null;
- for (Tile t : ts.allTiles()) {
- SlippyMapTile tile = getTile(t.x, t.y, zoom);
- if (tile == null) {
- // Don't trigger tile loading if this isn't
- // the exact zoom level we're looking for
- if (zoom != currentZoomLevel)
- continue;
- tile = getOrCreateTile(t.x, t.y, zoom);
- if (SlippyMapPreferences.getAutoloadTiles())
- loadSingleTile(tile);
- }
- img = tile.getImage();
- if (img == null)
+ for (Tile tile : ts.allTiles()) {
+ Image img = getLoadedTileImage(tile);
+ if (img == null) {
+ if (debug)
+ out("missed tile: " + tile);
+ missedTiles.add(tile);
continue;
- if ((zoom != currentZoomLevel) && !tile.isDownloaded())
- continue;
- Point p = t.pixelPos(zoom);
- /*
- * We need to get a box in which to draw, so advance by one tile in
- * each direction to find the other corner of the box
- */
- Tile t2 = new Tile(t.x + 1, t.y + 1);
- Point p2 = t2.pixelPos(zoom);
- if (imageLoaded(img)) {
- g.drawImage(img, p.x, p.y, p2.x - p.x, p2.y - p.y, this);
- paintedTiles++;
}
- if (img == null)
+ Rectangle sourceRect = tileToRect(tile);
+ if (borderRect != null && !sourceRect.intersects(borderRect))
continue;
+ drawImageInside(g, img, sourceRect, borderRect);
if (!imageScaleRecorded && zoom == currentZoomLevel) {
- lastImageScale = new Double(getImageScaling(img, p, p2));
+ lastImageScale = new Double(getImageScaling(img, sourceRect));
imageScaleRecorded = true;
}
- float fadeBackground = SlippyMapPreferences.getFadeBackground();
- if (fadeBackground != 0f) {
- // dimm by painting opaque rect...
- g.setColor(new Color(1f, 1f, 1f, fadeBackground));
- g.fillRect(p.x, p.y, p2.x - p.x, p2.y - p.y);
- }
}// end of for
- return paintedTiles;
+ return missedTiles;
}
- void paintTileText(TileSet ts, Graphics g, MapView mv, int zoom, Tile t) {
+ void paintTileText(TileSet ts, Tile tile, Graphics g, MapView mv, int zoom, Tile t) {
int fontHeight = g.getFontMetrics().getHeight();
-
- SlippyMapTile tile = getTile(t.x, t.y, zoom);
- if (tile == null) {
+ if (tile == null)
return;
- }
- if (tile.getImage() == null) {
- loadSingleTile(tile);
- }
- Point p = t.pixelPos(zoom);
+ Point p = pixelPos(t);
int texty = p.y + 2 + fontHeight;
if (SlippyMapPreferences.getDrawDebug()) {
- g.drawString("x=" + t.x + " y=" + t.y + " z=" + zoom + "", p.x + 2, texty);
+ g.drawString("x=" + t.getXtile() + " y=" + t.getYtile() + " z=" + zoom + "", p.x + 2, texty);
texty += 1 + fontHeight;
- if ((t.x % 32 == 0) && (t.y % 32 == 0)) {
- g.drawString("x=" + t.x / 32 + " y=" + t.y / 32 + " z=7", p.x + 2, texty);
+ if ((t.getXtile() % 32 == 0) && (t.getYtile() % 32 == 0)) {
+ g.drawString("x=" + t.getXtile() / 32 + " y=" + t.getYtile() / 32 + " z=7", p.x + 2, texty);
texty += 1 + fontHeight;
}
}// end of if draw debug
- String md = tile.getMetadata();
- if (md != null) {
- g.drawString(md, p.x + 2, texty);
- texty += 1 + fontHeight;
+ if (tile == showMetadataTile) {
+ String md = tile.toString();
+ if (md != null) {
+ g.drawString(md, p.x + 2, texty);
+ texty += 1 + fontHeight;
+ }
}
String tileStatus = tile.getStatus();
- Image tileImage = tile.getImage();
- if (!imageLoaded(tileImage)) {
+ if (!tile.isLoaded()) {
g.drawString(tr("image " + tileStatus), p.x + 2, texty);
texty += 1 + fontHeight;
}
- /*
- We already do repaint when the images load
- we don't need to poll like this
- */
- if (!imageLoaded(tileImage)) {
- needRedraw = true;
- Main.map.repaint(100);
- }
+ int xCursor = -1;
+ int yCursor = -1;
if (SlippyMapPreferences.getDrawDebug()) {
- if (ts.leftTile(t)) {
- if (t.y % 32 == 31) {
+ if (yCursor < t.getYtile()) {
+ if (t.getYtile() % 32 == 31) {
g.fillRect(0, p.y - 1, mv.getWidth(), 3);
} else {
g.drawLine(0, p.y, mv.getWidth(), p.y);
}
+ yCursor = t.getYtile();
+ }
+ // This draws the vertical lines for the entire
+ // column. Only draw them for the top tile in
+ // the column.
+ if (xCursor < t.getXtile()) {
+ if (SlippyMapPreferences.getDrawDebug()) {
+ if (t.getXtile() % 32 == 0) {
+ // level 7 tile boundary
+ g.fillRect(p.x - 1, 0, 3, mv.getHeight());
+ } else {
+ g.drawLine(p.x, 0, p.x, mv.getHeight());
+ }
+ }
+ xCursor = t.getXtile();
}
- }// /end of if draw debug
- }
-
- private class Tile {
- public int x;
- public int y;
-
- Tile(int x, int y) {
- this.x = x;
- this.y = y;
}
+ }
- public Point pixelPos(int zoom) {
- double lon = tileXToLon(this.x, zoom);
- LatLon tmpLL = new LatLon(tileYToLat(this.y, zoom), lon);
- MapView mv = Main.map.mapView;
- return mv.getPoint(tmpLL);
- }
+ public Point pixelPos(LatLon ll) {
+ return Main.map.mapView.getPoint(ll);
+ }
+ public Point pixelPos(Tile t) {
+ double lon = tileXToLon(t.getXtile(), t.getZoom());
+ LatLon tmpLL = new LatLon(tileYToLat(t.getYtile(), t.getZoom()), lon);
+ return pixelPos(tmpLL);
}
private class TileSet {
int z12x0, z12x1, z12y0, z12y1;
@@ -559,10 +712,10 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
TileSet(LatLon topLeft, LatLon botRight, int zoom) {
this.zoom = zoom;
- z12x0 = lonToTileX(topLeft.lon(), zoom) - 1;
- z12y0 = latToTileY(topLeft.lat(), zoom) - 1;
- z12x1 = lonToTileX(botRight.lon(), zoom) + 1;
- z12y1 = latToTileY(botRight.lat(), zoom) + 1;
+ z12x0 = lonToTileX(topLeft.lon(), zoom);
+ z12y0 = latToTileY(topLeft.lat(), zoom);
+ z12x1 = lonToTileX(botRight.lon(), zoom);
+ z12y1 = latToTileY(botRight.lat(), zoom);
if (z12x0 > z12x1) {
int tmp = z12x0;
z12x0 = z12x1;
@@ -579,49 +732,93 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
if (z12x1 > tileMax) z12x1 = tileMax;
if (z12y1 > tileMax) z12y1 = tileMax;
}
+ boolean tooSmall() {
+ return this.tilesSpanned() < 2.1;
+ }
+ boolean tooLarge() {
+ return this.tilesSpanned() > 10;
+ }
+ boolean insane() {
+ return this.tilesSpanned() > 100;
+ }
double tilesSpanned() {
- int x_span = z12x1 - z12x0;
- int y_span = z12y1 - z12y0;
- return Math.sqrt(1.0 * x_span * y_span);
+ return Math.sqrt(1.0 * this.size());
+ }
+
+ double size() {
+ double x_span = z12x1 - z12x0 + 1.0;
+ double y_span = z12y1 - z12y0 + 1.0;
+ return x_span * y_span;
}
/*
- * This is pretty silly. Should probably just be implemented as an
- * iterator to keep from having to instantiate all these tiles.
+ * Get all tiles represented by this TileSet that are
+ * already in the tileCache.
*/
List<Tile> allTiles()
{
+ return this.allTiles(false);
+ }
+ private List<Tile> allTiles(boolean create)
+ {
List<Tile> ret = new ArrayList<Tile>();
+ // Don't even try to iterate over the set.
+ // Someone created a crazy number of them
+ if (this.insane())
+ return ret;
for (int x = z12x0; x <= z12x1; x++) {
for (int y = z12y0; y <= z12y1; y++) {
- Tile t = new Tile(x % tileMax, y % tileMax);
- ret.add(t);
+ Tile t;
+ if (create)
+ t = getOrCreateTile(x % tileMax, y % tileMax, zoom);
+ else
+ t = getTile(x % tileMax, y % tileMax, zoom);
+ if (t != null)
+ ret.add(t);
}
}
return ret;
}
-
+ void loadAllTiles(boolean force)
+ {
+ List<Tile> tiles = this.allTiles(true);
+ boolean autoload = SlippyMapPreferences.getAutoloadTiles();
+ if (!autoload && !force)
+ return;
+ int nr_queued = 0;
+ for (Tile t : tiles) {
+ if (loadTile(t))
+ nr_queued++;
+ }
+ if (debug)
+ if (nr_queued > 0)
+ out("queued to load: " + nr_queued + "/" + tiles.size() + " tiles at zoom: " + zoom);
+ }
boolean topTile(Tile t) {
- if (t.y == z12y0 )
+ if (t.getYtile() == z12y0 )
return true;
return false;
}
boolean leftTile(Tile t) {
- if (t.x == z12x0 )
+ if (t.getXtile() == z12x0 )
return true;
return false;
}
}
+ boolean autoZoomEnabled()
+ {
+ return autoZoomPopup.isSelected();
+ }
/**
*/
@Override
- public void paint(Graphics g, MapView mv) {
- long start = System.currentTimeMillis();
+ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
+ //long start = System.currentTimeMillis();
LatLon topLeft = mv.getLatLon(0, 0);
LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
- Graphics oldg = g;
+ Graphics2D oldg = g;
if (botRight.lon() == 0.0 || botRight.lat() == 0) {
Main.debug("still initializing??");
@@ -633,7 +830,8 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
&& mv.getWidth() == bufferImage.getWidth(null) && mv.getHeight() == bufferImage.getHeight(null)
&& !needRedraw) {
- this.debug("drawing buffered image");
+ if (debug)
+ out("drawing buffered image");
g.drawImage(bufferImage, 0, 0, null);
return;
}
@@ -642,122 +840,125 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
lastTopLeft = topLeft;
lastBotRight = botRight;
bufferImage = mv.createImage(mv.getWidth(), mv.getHeight());
- g = bufferImage.getGraphics();
+ g = (Graphics2D) bufferImage.getGraphics();
- TileSet ts = new TileSet(topLeft, botRight, currentZoomLevel);
int zoom = currentZoomLevel;
-
- if (zoomDecreaseAllowed() && (ts.tilesSpanned() > 18)) {
- this.debug("too many tiles, decreasing zoom from " + currentZoomLevel);
- if (decreaseZoomLevel())
- this.paint(oldg, mv);
- return;
- }
- if (zoomIncreaseAllowed() && (ts.tilesSpanned() < 1.0)) {
- this.debug("doesn't even cover one tile (" + ts.tilesSpanned()
- + "), increasing zoom from " + currentZoomLevel);
- if (increaseZoomLevel())
- this.paint(oldg, mv);
- return;
+ TileSet ts = new TileSet(topLeft, botRight, zoom);
+
+ if (autoZoomEnabled()) {
+ if (zoomDecreaseAllowed() && ts.tooLarge()) {
+ if (debug)
+ out("too many tiles, decreasing zoom from " + currentZoomLevel);
+ if (decreaseZoomLevel())
+ this.paint(oldg, mv, bounds);
+ return;
+ }
+ if (zoomIncreaseAllowed() && ts.tooSmall()) {
+ if (debug)
+ out("too zoomed in, (" + ts.tilesSpanned()
+ + "), increasing zoom from " + currentZoomLevel);
+ if (increaseZoomLevel())
+ this.paint(oldg, mv, bounds);
+ return;
+ }
}
- if (ts.tilesSpanned() <= 0) {
- System.out.println("doesn't even cover one tile, increasing zoom from " + currentZoomLevel);
- if (increaseZoomLevel()) {
- this.paint(oldg, mv);
- }
- return;
+ // Too many tiles... refuse to draw
+ if (!ts.tooLarge()) {
+ //out("size: " + ts.size() + " spanned: " + ts.tilesSpanned());
+ ts.loadAllTiles(false);
}
int fontHeight = g.getFontMetrics().getHeight();
g.setColor(Color.DARK_GRAY);
- /*
- * Go looking for tiles in zoom levels *other* than the current
- * one. Even if they might look bad, they look better than a
- * blank tile.
- */
- int otherZooms[] = {-5, -4, -3, 2, -2, 1, -1};
- for (int zoomOff : otherZooms) {
- int zoom2 = currentZoomLevel + zoomOff;
- if ((zoom2 < SlippyMapPreferences.getMinZoomLvl()) ||
- (zoom2 > SlippyMapPreferences.getMaxZoomLvl())) {
- continue;
+ List<Tile> missedTiles = this.paintTileImages(g, ts, currentZoomLevel, null);
+ int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5};
+ for (int zoomOffset : otherZooms) {
+ if (!autoZoomEnabled())
+ break;
+ if (!SlippyMapPreferences.getAutoloadTiles())
+ break;
+ int newzoom = currentZoomLevel + zoomOffset;
+ if (missedTiles.size() <= 0)
+ break;
+ List<Tile> newlyMissedTiles = new LinkedList<Tile>();
+ for (Tile missed : missedTiles) {
+ Tile t2 = tempCornerTile(missed);
+ LatLon topLeft2 = tileLatLon(missed);
+ LatLon botRight2 = tileLatLon(t2);
+ TileSet ts2 = new TileSet(topLeft2, botRight2, newzoom);
+ if (ts2.tooLarge())
+ continue;
+ newlyMissedTiles.addAll(this.paintTileImages(g, ts2, newzoom, missed));
}
- TileSet ts2 = new TileSet(topLeft, botRight, zoom2);
- int painted = this.paintTileImages(g, ts2, zoom2);
- //if (painted > 0)
- // System.out.println("painted " + painted + " tiles from zoom: " + zoom2);
+ missedTiles = newlyMissedTiles;
+ }
+ if (debug && missedTiles.size() > 0) {
+ out("still missed "+missedTiles.size()+" in the end");
}
- /*
- * Save this for last since it will get painted over all the others
- */
- this.paintTileImages(g, ts, currentZoomLevel);
g.setColor(Color.red);
+ // The current zoom tileset is guaranteed to have all of
+ // its tiles
for (Tile t : ts.allTiles()) {
- // This draws the vertical lines for the entire
- // column. Only draw them for the top tile in
- // the column.
- if (ts.topTile(t)) {
- Point p = t.pixelPos(currentZoomLevel);
- if (SlippyMapPreferences.getDrawDebug()) {
- if (t.x % 32 == 0) {
- // level 7 tile boundary
- g.fillRect(p.x - 1, 0, 3, mv.getHeight());
- } else {
- g.drawLine(p.x, 0, p.x, mv.getHeight());
- }
- }
- }
- this.paintTileText(ts, g, mv, currentZoomLevel, t);
+ this.paintTileText(ts, t, g, mv, currentZoomLevel, t);
}
- float fadeBackground = SlippyMapPreferences.getFadeBackground();
oldg.drawImage(bufferImage, 0, 0, null);
- if (lastImageScale != null) {
+ if (autoZoomEnabled() && lastImageScale != null) {
// If each source image pixel is being stretched into > 3
// drawn pixels, zoom in... getting too pixelated
if (lastImageScale > 3 && zoomIncreaseAllowed()) {
- if (SlippyMapPreferences.getAutozoom()) {
- this.debug("autozoom increase: scale: " + lastImageScale);
- increaseZoomLevel();
- }
- this.paint(oldg, mv);
+ if (debug)
+ out("autozoom increase: scale: " + lastImageScale);
+ increaseZoomLevel();
+ this.paint(oldg, mv, bounds);
// If each source image pixel is being squished into > 0.32
// of a drawn pixels, zoom out.
} else if ((lastImageScale < 0.45) && (lastImageScale > 0) && zoomDecreaseAllowed()) {
- if (SlippyMapPreferences.getAutozoom()) {
- this.debug("autozoom decrease: scale: " + lastImageScale);
- decreaseZoomLevel();
- }
- this.paint(oldg, mv);
+ if (debug)
+ out("autozoom decrease: scale: " + lastImageScale);
+ decreaseZoomLevel();
+ this.paint(oldg, mv, bounds);
}
}
- g.setColor(Color.black);
- g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
+ //g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
+ oldg.setColor(Color.black);
+ if (ts.insane()) {
+ oldg.drawString("zoom in to load any tiles", 120, 120);
+ } else if (ts.tooLarge()) {
+ oldg.drawString("zoom in to load more tiles", 120, 120);
+ } else if (ts.tooSmall()) {
+ oldg.drawString("increase zoom level to see more detail", 120, 120);
+ }
}// end of paint method
/**
* This isn't very efficient, but it is only used when the
* user right-clicks on the map.
*/
- SlippyMapTile getTileForPixelpos(int px, int py) {
+ Tile getTileForPixelpos(int px, int py) {
+ if (debug)
+ out("getTileForPixelpos("+px+", "+py+")");
MapView mv = Main.map.mapView;
Point clicked = new Point(px, py);
LatLon topLeft = mv.getLatLon(0, 0);
LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
- TileSet ts = new TileSet(topLeft, botRight, currentZoomLevel);
int z = currentZoomLevel;
+ TileSet ts = new TileSet(topLeft, botRight, z);
+ if (!ts.tooLarge())
+ ts.loadAllTiles(false); // make sure there are tile objects for all tiles
Tile clickedTile = null;
Point p1 = null, p2 = null;
for (Tile t1 : ts.allTiles()) {
- Tile t2 = new Tile(t1.x+1, t1.y+1);
- p1 = t1.pixelPos(z);
- p2 = t2.pixelPos(z);
- Rectangle r = new Rectangle(p1,new Dimension(p2.x, p2.y));
+ Tile t2 = tempCornerTile(t1);
+ Rectangle r = new Rectangle(pixelPos(t1));
+ r.add(pixelPos(t2));
+ if (debug)
+ out("r: " + r + " clicked: " + clicked);
if (!r.contains(clicked))
continue;
clickedTile = t1;
@@ -765,11 +966,9 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
}
if (clickedTile == null)
return null;
- System.out.println("clicked on tile: " + clickedTile.x + " " + clickedTile.y +
+ System.out.println("clicked on tile: " + clickedTile.getXtile() + " " + clickedTile.getYtile() +
" scale: " + lastImageScale + " currentZoomLevel: " + currentZoomLevel);
- SlippyMapTile tile = getOrCreateTile(clickedTile.x, clickedTile.y, currentZoomLevel);
- checkTileStorage();
- return tile;
+ return clickedTile;
}
@Override
@@ -832,64 +1031,9 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
return x * 45.0 / Math.pow(2.0, zoom - 3) - 180.0;
}
- private SlippyMapTile imgToTile(Image img) {
- // we use the enumeration to avoid ConcurrentUpdateExceptions
- // with other users of the tileStorage
- Enumeration<SlippyMapTile> e = tileStorage.elements();
- while (e.hasMoreElements()) {
- SlippyMapTile t = e.nextElement();
- if (t.getImageNoTimestamp() != img) {
- continue;
- }
- return t;
- }
- return null;
- }
-
private static int nr_loaded = 0;
private static int at_zoom = -1;
- public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
- boolean error = (infoflags & ERROR) != 0;
- boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
- boolean success = ((infoflags & (ALLBITS)) != 0);
- SlippyMapTile imageTile = imgToTile(img);
- if (success || error) {
- this.debug("tile done loading: " + imageTile);
- markDone(img);
- }
- if (imageTile == null) {
- System.err.println("no tile for image");
- return false;
- }
-
- if ((infoflags & ERROR) != 0) {
- String url; // = "unknown";
- url = imageTile.getImageURL().toString();
- System.err.println("imageUpdate(" + img + ") error " + url + ")");
- }
- if (((infoflags & ALLBITS) != 0)) {
- int z = imageTile.getZoom();
- if (z == at_zoom) {
- nr_loaded++;
- } else {
- //System.out.println("downloaded " + nr_loaded + " at: " + at_zoom + " now going to " + z +
- // " class: " + img.getClass());
- nr_loaded = 0;
- at_zoom = z;
- }
- }
- if ((infoflags & SOMEBITS) != 0) {
- // if (y%100 == 0)
- //System.out.println("imageUpdate("+img+") SOMEBITS ("+x+","+y+")");
- }
- // Repaint immediately if we are done, otherwise batch up
- // repaint requests every 100 milliseconds
- needRedraw = true;
- Main.map.repaint(done ? 0 : 100);
- return !done;
- }
-
/*
* (non-Javadoc)
*
@@ -903,8 +1047,12 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
// when fade background changed, no need to clear tile storage
// TODO move this code to SlippyMapPreferences class.
if (!key.equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) {
- clearTileStorage();
+ autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
+ }
+ if (key.equals(SlippyMapPreferences.PREFERENCE_TILE_SOURCE)) {
+ newTileStorage();
}
+ redraw();
}
}
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java
index 3ea6ea8..13b956e 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java
@@ -9,6 +9,13 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.util.Collection;
+
+import org.openstreetmap.gui.jmapviewer.*;
+import org.openstreetmap.gui.jmapviewer.interfaces.*;
import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
@@ -28,18 +35,21 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
private JCheckBox autozoomActive = new JCheckBox(tr("autozoom"));
private JCheckBox autoloadTiles = new JCheckBox(tr("autoload tiles"));
- private JSpinner maxZoomLvl = new JSpinner();
+ private JSpinner maxZoomLvl;
+ private JSpinner minZoomLvl = new JSpinner();
private JSlider fadeBackground = new JSlider(0, 100);
public void addGui(PreferenceDialog gui)
{
+ minZoomLvl = new JSpinner(new SpinnerNumberModel(SlippyMapPreferences.DEFAULT_MIN_ZOOM, SlippyMapPreferences.MIN_ZOOM, SlippyMapPreferences.MAX_ZOOM, 1));
+ maxZoomLvl = new JSpinner(new SpinnerNumberModel(SlippyMapPreferences.DEFAULT_MAX_ZOOM, SlippyMapPreferences.MIN_ZOOM, SlippyMapPreferences.MAX_ZOOM, 1));
//String description = tr("A plugin that adds to JOSM new layer. This layer could render external tiles.");
JPanel slippymapTab = gui.createPreferenceTab("slippymap.png", tr("SlippyMap"), tr("Settings for the SlippyMap plugin."));
- String[] allMapUrls = SlippyMapPreferences.getAllMapUrls();
- tileSourceCombo = new JComboBox(allMapUrls);
- tileSourceCombo.setEditable(true);
- String source = SlippyMapPreferences.getMapUrl();
- tileSourceCombo.setSelectedItem(source);
+ Collection<TileSource> allSources = SlippyMapPreferences.getAllMapSources();
+ //Collection<String> allSources = SlippyMapPreferences.getAllMapNames();
+ tileSourceCombo = new JComboBox(allSources.toArray());
+ //tileSourceCombo.setEditable(true);
+ tileSourceCombo.setSelectedItem(SlippyMapPreferences.getMapSource());
slippymapTab.add(new JLabel(tr("Tile Sources")), GBC.std());
slippymapTab.add(GBC.glue(5, 0), GBC.std());
slippymapTab.add(tileSourceCombo, GBC.eol().fill(GBC.HORIZONTAL));
@@ -52,20 +62,51 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
slippymapTab.add(autoloadTiles, GBC.eol().fill(GBC.HORIZONTAL));
+ slippymapTab.add(new JLabel(tr("Min zoom lvl: ")), GBC.std());
+ slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+ slippymapTab.add(this.minZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
+
slippymapTab.add(new JLabel(tr("Max zoom lvl: ")), GBC.std());
slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
slippymapTab.add(this.maxZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
-
+
slippymapTab.add(new JLabel(tr("Fade background: ")), GBC.std());
slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
slippymapTab.add(this.fadeBackground, GBC.eol().fill(GBC.HORIZONTAL));
slippymapTab.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+ tileSourceCombo.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ //Main.debug("updating spinner models because of tileSourceCombo");
+ updateSpinnerModels();
+ }
+ });
+
this.loadSettings();
+ updateSpinnerModels();
}
+ void updateSpinnerModel(JSpinner s, int min, int max)
+ {
+ int val = (Integer)s.getValue();
+ //Main.debug("updating spinner model val: " + val + " " + min + "->" + max);
+ val = Math.min(max, val);
+ val = Math.max(min, val);
+ SpinnerNumberModel model = new SpinnerNumberModel(val, min, max, 1);
+ s.setModel(model);
+ }
+
+ void updateSpinnerModels()
+ {
+ TileSource ts = (TileSource)this.tileSourceCombo.getSelectedItem();
+ int min = ts.getMinZoom();
+ int max = ts.getMaxZoom();
+ updateSpinnerModel(minZoomLvl, min, max);
+ updateSpinnerModel(maxZoomLvl, min, max);
+ }
+
/**
* <p>
* Load settings from {@link SlippyMapPreferences} class. Loaded preferences are stored to local GUI components.
@@ -74,6 +115,7 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
* <li>autozoom - {@link #autozoomActive} - {@link SlippyMapPreferences#getAutozoom()}</li>
* <li>autoload - {@link #autoloadTiles} - {@link SlippyMapPreferences#getAutoloadTiles()}</li>
* <li>maxZoomLvl - {@link #maxZoomLvl} - {@link SlippyMapPreferences#getMaxZoomLvl()}</li>
+ * <li>minZoomLvl - {@link #minZoomLvl} - {@link SlippyMapPreferences#getMaxZoomLvl()}</li>
* </ul>
* </p>
*/
@@ -81,6 +123,7 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
this.autozoomActive.setSelected(SlippyMapPreferences.getAutozoom());
this.autoloadTiles.setSelected(SlippyMapPreferences.getAutoloadTiles());
this.maxZoomLvl.setValue(SlippyMapPreferences.getMaxZoomLvl());
+ this.minZoomLvl.setValue(SlippyMapPreferences.getMinZoomLvl());
this.fadeBackground.setValue(Math.round(SlippyMapPreferences.getFadeBackground()*100f));
}
@@ -94,10 +137,11 @@ public class SlippyMapPreferenceSetting implements PreferenceSetting {
*/
public boolean ok()
{
- SlippyMapPreferences.setMapUrl(this.tileSourceCombo.getSelectedItem().toString());
+ SlippyMapPreferences.setMapSource((TileSource)this.tileSourceCombo.getSelectedItem());
SlippyMapPreferences.setAutozoom(this.autozoomActive.isSelected());
SlippyMapPreferences.setAutoloadTiles(this.autoloadTiles.isSelected());
SlippyMapPreferences.setMaxZoomLvl((Integer)this.maxZoomLvl.getValue());
+ SlippyMapPreferences.setMinZoomLvl((Integer)this.minZoomLvl.getValue());
SlippyMapPreferences.setFadeBackground(this.fadeBackground.getValue()/100f);
//restart isn't required
return false;
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
index ebae7ce..814a0ef 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
@@ -1,43 +1,65 @@
package org.openstreetmap.josm.plugins.slippymap;
import org.openstreetmap.josm.Main;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import org.openstreetmap.gui.jmapviewer.*;
+import org.openstreetmap.gui.jmapviewer.interfaces.*;
/**
* Preferences for Slippy Map Tiles
- *
+ *
* @author Hakan Tandogan <hakan at gurkensalat.com>
* @author LuVar <lubomir.varga at freemap.sk>
- *
+ *
*/
public class SlippyMapPreferences
{
public static final String PREFERENCE_PREFIX = "slippymap";
- public static final String PREFERENCE_TILE_URL = PREFERENCE_PREFIX + ".tile_url";
+ public static final String PREFERENCE_TILE_CUSTOM_SOURCE = PREFERENCE_PREFIX + ".custom_tile_source_";
+ public static final String PREFERENCE_TILE_SOURCE = PREFERENCE_PREFIX + ".tile_source";
public static final String PREFERENCE_AUTOZOOM = PREFERENCE_PREFIX + ".autozoom";
public static final String PREFERENCE_AUTOLOADTILES = PREFERENCE_PREFIX + ".autoload_tiles";
public static final String PREFERENCE_MIN_ZOOM_LVL = PREFERENCE_PREFIX + ".min_zoom_lvl";
public static final String PREFERENCE_MAX_ZOOM_LVL = PREFERENCE_PREFIX + ".max_zoom_lvl";
+ public static final String PREFERENCE_LAST_ZOOM = PREFERENCE_PREFIX + ".last_zoom_lvl";
public static final String PREFERENCE_FADE_BACKGROUND = PREFERENCE_PREFIX + ".fade_background";
public static final String PREFERENCE_DRAW_DEBUG = PREFERENCE_PREFIX + ".draw_debug";
-
- public static String getMapUrl()
- {
- String url = Main.pref.get(PREFERENCE_TILE_URL);
- if (url == null || "".equals(url))
- {
- url = "http://tah.openstreetmap.org/Tiles/tile"; // t at h
- Main.pref.put(PREFERENCE_TILE_URL, url);
- }
+ public static final int MAX_ZOOM = 30;
+ public static final int MIN_ZOOM = 2;
+ public static final int DEFAULT_MAX_ZOOM = 20;
+ public static final int DEFAULT_MIN_ZOOM = 2;
- return url;
+ public static TileSource getMapSource()
+ {
+ String name = Main.pref.get(PREFERENCE_TILE_SOURCE);
+ return getMapSource(name);
+ }
+ public static TileSource getMapSource(String name)
+ {
+ List<TileSource> sources = SlippyMapPreferences.getAllMapSources();
+ TileSource source = sources.get(0);
+ if (name == null || "".equals(name)) {
+ name = source.getName();
+ Main.pref.put(PREFERENCE_TILE_SOURCE, name);
+ }
+ for (TileSource s : sources) {
+ if (!name.equals(s.getName()))
+ continue;
+ source = s;
+ break;
+ }
+ return source;
}
-
- public static void setMapUrl(String mapUrl) {
- Main.pref.put(SlippyMapPreferences.PREFERENCE_TILE_URL, mapUrl);
+
+ public static void setMapSource(TileSource source) {
+ Main.pref.put(SlippyMapPreferences.PREFERENCE_TILE_SOURCE, source.getName());
}
-
+
public static boolean getAutozoom()
{
String autozoom = Main.pref.get(PREFERENCE_AUTOZOOM);
@@ -50,15 +72,28 @@ public class SlippyMapPreferences
return Boolean.parseBoolean(autozoom);
}
-
+
public static void setAutozoom(boolean autozoom) {
Main.pref.put(SlippyMapPreferences.PREFERENCE_AUTOZOOM, autozoom);
}
-
+
public static void setDrawDebug(boolean drawDebug) {
Main.pref.put(SlippyMapPreferences.PREFERENCE_DRAW_DEBUG, drawDebug);
}
-
+
+ public static void setLastZoom(int zoom) {
+ Main.pref.put(SlippyMapPreferences.PREFERENCE_LAST_ZOOM, ""+zoom);
+ }
+ public static int getLastZoom() {
+ int ret = -1;
+ String pref = Main.pref.get(SlippyMapPreferences.PREFERENCE_LAST_ZOOM);
+ try {
+ ret = Integer.parseInt(pref);
+ } catch (NumberFormatException e) {
+ }
+ return ret;
+ }
+
public static boolean getDrawDebug()
{
String drawDebug = Main.pref.get(PREFERENCE_DRAW_DEBUG);
@@ -71,7 +106,7 @@ public class SlippyMapPreferences
return Boolean.parseBoolean(drawDebug);
}
-
+
public static boolean getAutoloadTiles()
{
String autoloadTiles = Main.pref.get(PREFERENCE_AUTOLOADTILES);
@@ -84,13 +119,13 @@ public class SlippyMapPreferences
return Boolean.parseBoolean(autoloadTiles);
}
-
+
public static void setFadeBackground(float fadeBackground) {
Main.pref.put(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND, fadeBackground + "");
}
/**
- *
+ *
* @return number between 0 and 1, inclusive
*/
public static float getFadeBackground() {
@@ -101,7 +136,7 @@ public class SlippyMapPreferences
fadeBackground = "0.0";
Main.pref.put(PREFERENCE_FADE_BACKGROUND, fadeBackground);
}
-
+
float parsed;
try {
parsed = Float.parseFloat(fadeBackground);
@@ -120,100 +155,200 @@ public class SlippyMapPreferences
}
return parsed;
}
-
+
public static void setAutoloadTiles(boolean autoloadTiles) {
Main.pref.put(SlippyMapPreferences.PREFERENCE_AUTOLOADTILES, autoloadTiles);
}
-
- public static int getMaxZoomLvl()
- {
- String maxZoomLvl = Main.pref.get(PREFERENCE_MAX_ZOOM_LVL);
- if (maxZoomLvl == null || "".equals(maxZoomLvl))
- {
- maxZoomLvl = "17";
- Main.pref.put(PREFERENCE_MAX_ZOOM_LVL, maxZoomLvl);
- }
-
- int navrat;
+ private static int getIntPref(String prefName, int def)
+ {
+ int pref;
try {
- navrat = Integer.parseInt(maxZoomLvl);
+ //Should we use Main.pref.getInteger(str)?
+ pref = Main.pref.getInteger(prefName, def);
} catch (Exception ex) {
- throw new RuntimeException("Problem while converting string to int. Converting value of prefetrences " + PREFERENCE_MAX_ZOOM_LVL + ". Value=\"" + maxZoomLvl + "\". Should be an integer. Error: " + ex.getMessage(), ex);
+ String str = Main.pref.get(prefName);
+ Main.pref.put(prefName, null);
+ throw new RuntimeException("Problem while converting string to int. "
+ + "Converting value of preferences "
+ + prefName + ". Value=\"" + str
+ + "\". Should be an integer. Error: "
+ + ex.getMessage(), ex);
}
- if(navrat > 30) {
- System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
- navrat = 30;
- }
- //if(navrat < SlippyMapPreferences.getMinZoomLvl()) {
- // System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
- // navrat = SlippyMapPreferences.getMinZoomLvl();
- //}
- return navrat;
+ return pref;
}
-
- public static void setMaxZoomLvl(int maxZoomLvl) {
- if(maxZoomLvl > 30) {
+
+ static int checkMaxZoomLvl(int maxZoomLvl)
+ {
+ if(maxZoomLvl > MAX_ZOOM) {
System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
- maxZoomLvl = 30;
+ maxZoomLvl = MAX_ZOOM;
}
- if(maxZoomLvl < SlippyMapPreferences.getMinZoomLvl()) {
+ if(maxZoomLvl < SlippyMapPreferences.__getMinZoomLvl()) {
System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
- maxZoomLvl = SlippyMapPreferences.getMinZoomLvl();
+ maxZoomLvl = SlippyMapPreferences.__getMinZoomLvl();
}
- Main.pref.put(SlippyMapPreferences.PREFERENCE_MAX_ZOOM_LVL, "" + maxZoomLvl);
+ TileSource ts = getMapSource();
+ if (ts != null && ts.getMaxZoom() < SlippyMapPreferences.__getMinZoomLvl()) {
+ System.err.println("decreasing maxZoomLvl to match new tile source");
+ maxZoomLvl = ts.getMaxZoom();
+ }
+ return maxZoomLvl;
}
-
- public static int getMinZoomLvl()
- {
- String minZoomLvl = Main.pref.get(PREFERENCE_MIN_ZOOM_LVL);
- if (minZoomLvl == null || "".equals(minZoomLvl))
- {
- minZoomLvl = "" + (SlippyMapPreferences.getMaxZoomLvl() - 4);
- Main.pref.put(PREFERENCE_MIN_ZOOM_LVL, minZoomLvl);
- }
+ public static int getMaxZoomLvl()
+ {
+ int maxZoomLvl = getIntPref(PREFERENCE_MAX_ZOOM_LVL, DEFAULT_MAX_ZOOM);
+ return checkMaxZoomLvl(maxZoomLvl);
+ }
- int navrat;
- try {
- navrat = Integer.parseInt(minZoomLvl);
- } catch (Exception ex) {
- throw new RuntimeException("Problem while converting string to int. Converting value of prefetrences " + PREFERENCE_MIN_ZOOM_LVL + ". Value=\"" + minZoomLvl + "\". Should be an integer. Error: " + ex.getMessage(), ex);
- }
- if(navrat < 2) {
- System.err.println("minZoomLvl shouldnt be lees than 2! Setting to 2.");
- navrat = 2;
- }
- //if(navrat > SlippyMapPreferences.getMaxZoomLvl()) {
- // System.err.println("minZoomLvl shouldnt be more than maxZoomLvl! Setting to maxZoomLvl.");
- // navrat = SlippyMapPreferences.getMaxZoomLvl();
- //}
- return navrat;
+ public static void setMaxZoomLvl(int maxZoomLvl) {
+ maxZoomLvl = checkMaxZoomLvl(maxZoomLvl);
+ Main.pref.put(SlippyMapPreferences.PREFERENCE_MAX_ZOOM_LVL, "" + maxZoomLvl);
}
-
- public static void setMinZoomLvl(int minZoomLvl) {
- if(minZoomLvl < 2) {
- System.err.println("minZoomLvl shouldnt be lees than 2! Setting to 2.");
- minZoomLvl = 2;
+
+ static int checkMinZoomLvl(int minZoomLvl)
+ {
+ if(minZoomLvl < MIN_ZOOM) {
+ System.err.println("minZoomLvl shouldnt be lees than "+MIN_ZOOM+"! Setting to that.");
+ minZoomLvl = MIN_ZOOM;
}
if(minZoomLvl > SlippyMapPreferences.getMaxZoomLvl()) {
System.err.println("minZoomLvl shouldnt be more than maxZoomLvl! Setting to maxZoomLvl.");
minZoomLvl = SlippyMapPreferences.getMaxZoomLvl();
}
+ return minZoomLvl;
+ }
+
+ private static int __getMinZoomLvl()
+ {
+ // We can use this internally
+ return getIntPref(PREFERENCE_MIN_ZOOM_LVL, DEFAULT_MIN_ZOOM);
+ }
+ public static int getMinZoomLvl()
+ {
+ return checkMinZoomLvl(__getMinZoomLvl());
+ }
+
+ public static void setMinZoomLvl(int minZoomLvl) {
+ minZoomLvl = checkMinZoomLvl(minZoomLvl);
Main.pref.put(SlippyMapPreferences.PREFERENCE_MIN_ZOOM_LVL, "" + minZoomLvl);
}
-
- public static String[] getAllMapUrls()
+
+ public static class Coastline extends OsmTileSource.AbstractOsmTileSource {
+ public Coastline() {
+ super("Coastline", "http://hypercube.telascience.org/tiles/1.0.0/coastline");
+ }
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+ }
+ public static class FreeMapySk extends OsmTileSource.AbstractOsmTileSource {
+ public FreeMapySk() {
+ super("freemapy.sk", "http://www.freemap.sk/layers/allinone/?");
+ }
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+ }
+ public static class FreeMapySkPokus extends OsmTileSource.AbstractOsmTileSource {
+ public FreeMapySkPokus() {
+ super("freemapy.sk pokus 2", "http://www.freemap.sk/layers/tiles/?");
+ }
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+ }
+
+ public static class NearMap extends OsmTileSource.AbstractOsmTileSource {
+ public NearMap() {
+ super("NearMap Australia", "http://www.nearmap.com/maps/hl=en&nml=Vert&");
+ }
+
+ public int getMaxZoom() {
+ return 21;
+ }
+
+ public String getTilePath(int zoom, int tilex, int tiley) {
+ return "z=" + zoom + "&x=" + tilex + "&y=" + tiley;
+ }
+
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+ }
+
+ public static class Custom extends OsmTileSource.AbstractOsmTileSource {
+ public Custom(String name, String url) {
+ super(name, url);
+ }
+ public Custom(String name, String url, String extension) {
+ super(name, url);
+ this.extension = extension;
+ }
+ String extension;
+ @Override
+ public String getExtension() {
+ if (extension == null)
+ return super.getExtension();
+ return extension;
+ }
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+ }
+
+ public static List<TileSource> getCustomSources()
{
- String[] defaultTileSources = new String[]
- {
- "http://tah.openstreetmap.org/Tiles/tile", // t at h
- "http://tah.openstreetmap.org/Tiles/maplint", // maplint
- "http://tile.openstreetmap.org", // mapnik
- "http://hypercube.telascience.org/tiles/1.0.0/coastline", // coastline
- "http://www.freemap.sk/layers/allinone/?", //freemapy.sk
- "http://www.freemap.sk/layers/tiles/?", //freemapy.sk pokus 2
- };
- return defaultTileSources;
+ List<TileSource> ret = new ArrayList<TileSource>();
+ Map<String, String> customSources = Main.pref.getAllPrefix(PREFERENCE_TILE_CUSTOM_SOURCE);
+ for (String key : customSources.keySet()) {
+ String short_key = key.replaceFirst(PREFERENCE_TILE_CUSTOM_SOURCE, "");
+ // slippymap.custom_tile_source_1.name=OOC layer
+ // slippymap.custom_tile_source_1.url=http://a.ooc.openstreetmap.org/npe
+ // slippymap.custom_tile_source_1.ext=png
+
+ if (!(short_key.endsWith("name")))
+ continue;
+ String url_key = short_key.replaceFirst("name","url");
+ String ext_key = short_key.replaceFirst("name","ext");
+ String name = customSources.get(key);
+ String url = customSources.get(PREFERENCE_TILE_CUSTOM_SOURCE + url_key);
+ String ext = customSources.get(PREFERENCE_TILE_CUSTOM_SOURCE + ext_key);
+ // ext may be null, but that's OK
+ System.out.println("found new tile source: '" +name+"' url:'"+url+"'"+"' ext:'"+ext+"'");
+ ret.add(new Custom(name, url, ext));
+ }
+ return ret;
+ }
+
+ public static ArrayList<TileSource> sources = null;
+ public static List<TileSource> getAllMapSources()
+ {
+ if (sources != null)
+ return sources;
+ sources = new ArrayList<TileSource>();
+ // first here is the default if the user does not set one
+ sources.add(new OsmTileSource.Mapnik());
+ sources.add(new OsmTileSource.CycleMap());
+ sources.add(new OsmTileSource.TilesAtHome());
+ sources.add(new Coastline());
+ sources.add(new FreeMapySkPokus());
+ sources.add(new FreeMapySk());
+ sources.add(new NearMap());
+ sources.addAll(getCustomSources());
+ // Probably need to either add these or let users add them somehow
+ // "http://hypercube.telascience.org/tiles/1.0.0/coastline", // coastline
+ // "http://www.freemap.sk/layers/allinone/?", //freemapy.sk
+ // "http://www.freemap.sk/layers/tiles/?", //freemapy.sk pokus 2
+ return sources;
+ }
+
+ public static TileSource getSourceNamed(String name)
+ {
+ for (TileSource s : getAllMapSources())
+ if (s.getName().equals(name))
+ return s;
+ return null;
}
}
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java
index 7e100ef..e69de29 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapTile.java
@@ -1,188 +0,0 @@
-package org.openstreetmap.josm.plugins.slippymap;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.Image;
-import java.awt.Toolkit;
-import java.awt.image.ImageObserver;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-
-/**
- * Class that contains information about one single slippy map tile.
- *
- * @author Frederik Ramm <frederik at remote.org>
- * @author LuVar <lubomir.varga at freemap.sk>
- * @author Dave Hansen <dave at sr71.net>
- *
- */
-public class SlippyMapTile implements ImageObserver
-{
- private Image tileImage;
- private long timestamp;
-
- private int x;
- private int y;
- private int z;
- // Setting this to pending is a bit of a hack
- // as it requires knowledge that SlippyMapLayer
- // will put this tile in a queue before it calls
- // loadImage(). But, this gives the best message
- // to the user.
- private String status = "pending download";
-
- private boolean imageDownloaded = false;
-
- private String metadata;
-
- public SlippyMapTile(int x, int y, int z)
- {
- this.x = x;
- this.y = y;
- this.z = z;
- timestamp = System.currentTimeMillis();
- }
-
- public String getStatus()
- {
- return status;
- }
- public String getMetadata()
- {
- return metadata;
- }
-
- public URL getImageURL()
- {
- try
- {
- return new URL(SlippyMapPreferences.getMapUrl() + "/" + z + "/" + x + "/" + y + ".png");
- }
- catch (MalformedURLException mfu)
- {
- mfu.printStackTrace();
- }
- return null;
- }
-
- public Image loadImage()
- {
- // We do not update the timestamp in this function
- // The download code prioritizes the most recent
- // downloads and will download the oldest tiles last.
- URL imageURL = this.getImageURL();
- tileImage = Toolkit.getDefaultToolkit().createImage(imageURL);
- Toolkit.getDefaultToolkit().prepareImage(tileImage, -1, -1, this);
- Toolkit.getDefaultToolkit().sync();
- status = "being downloaded";
- return tileImage;
- }
- public String toString()
- {
- return "SlippyMapTile{zoom=" + z + " (" + x + "," + y + ") '" + status + "'}";
- }
- synchronized public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
- {
- if ((infoflags & ALLBITS) != 0) {
- imageDownloaded = true;
- status = "downloaded";
- if (tileImage == null) {
- System.out.println("completed null'd image: " + this.toString());
- }
- tileImage = img;
- return false;
- }
- return true;
- }
-
- public Image getImageNoTimestamp() {
- return tileImage;
- }
-
- public Image getImage()
- {
- timestamp = System.currentTimeMillis();
- return tileImage;
- }
-
- public int getZoom() {
- return z;
- }
-
- synchronized public void dropImage()
- {
- if(tileImage != null) {
- tileImage.flush();
- status = "dropped";
- }
- tileImage = null;
- // This should work in theory but doesn't seem to actually
- // reduce the X server memory usage
- //tileImage.flush();
- imageDownloaded = false;
- }
-
- public boolean isDownloaded() {
- return imageDownloaded;
- }
-
- public void loadMetadata()
- {
- try
- {
- URL dev = new URL(
- "http://tah.openstreetmap.org/Tiles/info_short.php?x=" + x
- + "&y=" + y + "&z=" + z + "/layer=tile");
- URLConnection devc = dev.openConnection();
- BufferedReader in = new BufferedReader(new InputStreamReader(devc
- .getInputStream()));
- metadata = tr(in.readLine());
- }
- catch (Exception ex)
- {
- metadata = tr("error loading metadata" + ex.toString());
- }
-
- }
-
- public void requestUpdate()
- {
- if (z != 12) {
- metadata = tr("error requesting update: not zoom-level 12");
- }
- try
- {
- URL dev = new URL("http://tah.openstreetmap.org/Request/create/?x=" + x
- + "&y=" + y + "&priority=1&src=slippymap_plugin");
- URLConnection devc = dev.openConnection();
- BufferedReader in = new BufferedReader(new InputStreamReader(devc
- .getInputStream()));
- timestamp = System.currentTimeMillis();
- metadata = tr("requested: {0}", tr(in.readLine()));
- }
- catch (Exception ex)
- {
- metadata = tr("error requesting update");
- }
- }
-
- public long access_time()
- {
- return timestamp;
- }
-
- public boolean equals(Object o)
- {
- if (!(o instanceof SlippyMapTile))
- return false;
- SlippyMapTile other = (SlippyMapTile) o;
- return (this.x == other.x && this.y == other.y && this.z == other.z);
- }
- SlippyMapKey getKey()
- {
- return new SlippyMapKey(x, y, z);
- }
-}
diff --git a/surveyor/build.xml b/surveyor/build.xml
index 18a9d5e..a435964 100644
--- a/surveyor/build.xml
+++ b/surveyor/build.xml
@@ -36,7 +36,7 @@
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
<attribute name="Plugin-Description" value="Allow adding markers/nodes on current gps positions."/>
<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Surveyor"/>
- <attribute name="Plugin-Mainversion" value="2012"/>
+ <attribute name="Plugin-Mainversion" value="2401"/>
<attribute name="Plugin-Requires" value="livegps"/>
<attribute name="Plugin-Stage" value="60"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
diff --git a/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java b/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java
index b8b580a..5b772cf 100644
--- a/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java
+++ b/surveyor/src/at/dallermassl/josm/plugin/surveyor/AutoSaveEditLayerTimerTask.java
@@ -20,7 +20,6 @@ import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.io.OsmWriter;
-import org.openstreetmap.josm.io.XmlWriter;
/**
* @author cdaller
@@ -54,7 +53,7 @@ public class AutoSaveEditLayerTimerTask extends TimerTask {
File tmpFile = new File(file.getAbsoluteFile()+".tmp");
System.out.println("AutoSaving osm data to file " + file.getAbsolutePath());
synchronized(LiveGpsLock.class) {
- OsmWriter w = new OsmWriter(new PrintWriter(new FileOutputStream(tmpFile)), false, dataset.version);
+ OsmWriter w = new OsmWriter(new PrintWriter(new FileOutputStream(tmpFile)), false, dataset.getVersion());
w.header();
w.writeDataSources(dataset);
w.writeContent(dataset);
diff --git a/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java b/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java
index fc5995a..6629050 100644
--- a/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java
+++ b/surveyor/src/at/dallermassl/josm/plugin/surveyor/action/SetNodeAction.java
@@ -69,7 +69,7 @@ public class SetNodeAction implements SurveyorAction {
}
node.put("created_by", "JOSM-surveyor-plugin");
synchronized(LiveGpsLock.class) {
- Main.map.mapView.getEditLayer().data.nodes.add(node);
+ Main.map.mapView.getEditLayer().data.addPrimitive(node);
Main.main.getCurrentDataSet().setSelected(node);
}
Main.map.repaint();
diff --git a/svn-info.xml b/svn-info.xml
index 2d681c9..3d703d4 100644
--- a/svn-info.xml
+++ b/svn-info.xml
@@ -3,16 +3,16 @@
<entry
kind="dir"
path="plugins"
- revision="18465">
+ revision="18946">
<url>http://svn.openstreetmap.org/applications/editors/josm/plugins</url>
<repository>
<root>http://svn.openstreetmap.org</root>
<uuid>b9d5c4c9-76e1-0310-9c85-f3177eceb1e4</uuid>
</repository>
<commit
- revision="18453">
-<author>daeron</author>
-<date>2009-11-04T14:17:43.184975Z</date>
+ revision="18946">
+<author>stoecker</author>
+<date>2009-12-04T21:55:59.367019Z</date>
</commit>
</entry>
</info>
diff --git a/utilsplugin/build.xml b/utilsplugin/build.xml
index 3f9871e..f2664d2 100644
--- a/utilsplugin/build.xml
+++ b/utilsplugin/build.xml
@@ -24,7 +24,7 @@
<attribute name="Plugin-Class" value="UtilsPlugin.UtilsPlugin"/>
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
<attribute name="Plugin-Description" value="Several utilities that make your life easier: e.g. simplify way, join areas, jump to position."/>
- <attribute name="Plugin-Mainversion" value="2166"/>
+ <attribute name="Plugin-Mainversion" value="2402"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
diff --git a/utilsplugin/josm-utilsplugin.launch b/utilsplugin/josm-utilsplugin.launch
new file mode 100644
index 0000000..a51a882
--- /dev/null
+++ b/utilsplugin/josm-utilsplugin.launch
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/JOSM/src/org/openstreetmap/josm/gui/MainApplication.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 6"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.openstreetmap.josm.gui.MainApplication"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="JOSM-utilsplugin"/>
+</launchConfiguration>
diff --git a/utilsplugin/src/UtilsPlugin/JoinAreasAction.java b/utilsplugin/src/UtilsPlugin/JoinAreasAction.java
index bcae225..397d74a 100644
--- a/utilsplugin/src/UtilsPlugin/JoinAreasAction.java
+++ b/utilsplugin/src/UtilsPlugin/JoinAreasAction.java
@@ -169,7 +169,8 @@ public class JoinAreasAction extends JosmAction {
if(joinAreas(selWays[0], selWays[ways == 2 ? 1 : 0])) {
Main.map.mapView.repaint();
- DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
+ DataSet ds = Main.main.getCurrentDataSet();
+ ds.fireSelectionChanged();
} else
JOptionPane.showMessageDialog(Main.parent, tr("No intersection found. Nothing was changed."));
}
@@ -441,7 +442,7 @@ public class JoinAreasAction extends JosmAction {
*/
private ArrayList<RelationRole> removeFromRelations(OsmPrimitive osm) {
ArrayList<RelationRole> result = new ArrayList<RelationRole>();
- for (Relation r : Main.main.getCurrentDataSet().relations) {
+ for (Relation r : Main.main.getCurrentDataSet().getRelations()) {
if (r.isDeleted() || r.incomplete) continue;
for (RelationMember rm : r.getMembers()) {
if (rm.getMember() != osm) continue;
diff --git a/utilsplugin/src/UtilsPlugin/JumpToAction.java b/utilsplugin/src/UtilsPlugin/JumpToAction.java
index b85711a..b2dfb26 100644
--- a/utilsplugin/src/UtilsPlugin/JumpToAction.java
+++ b/utilsplugin/src/UtilsPlugin/JumpToAction.java
@@ -121,8 +121,8 @@ public class JumpToAction extends JosmAction implements MouseListener {
if(!url.hasFocus()) return;
Bounds b = OsmUrlToBounds.parse(url.getText());
if (b != null) {
- lat.setText(Double.toString((b.min.lat() + b.max.lat())/2));
- lon.setText(Double.toString((b.min.lon() + b.max.lon())/2));
+ lat.setText(Double.toString((b.getMin().lat() + b.getMax().lat())/2));
+ lon.setText(Double.toString((b.getMin().lon() + b.getMax().lon())/2));
int zoomLvl = 16;
String[] args = url.getText().substring(url.getText().indexOf('?')+1).split("&");
diff --git a/validator/build.xml b/validator/build.xml
index aa74d50..ed54538 100644
--- a/validator/build.xml
+++ b/validator/build.xml
@@ -1,4 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+** This is the build.xml for the validator plugin
+**
+** Usage
+** =====
+** To build it run
+**
+** > ant dist
+**
+** To install the generated plugin locally (in your default plugin directory) run
+**
+** > ant install
+**
+** To build against the core in ../../core, create a correct manifest and deploy to
+** SVN,
+** set the properties commit.message and plugin.main.version
+** and run
+** > ant publish
+**
+**
+-->
<project name="validator" default="dist" basedir=".">
+
+ <!--
+ ** update before publishing
+ -->
+ <property name="commit.message" value="Updated build.xml" />
+ <property name="plugin.main.version" value="2452" />
+
+
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="plugin.build.dir" value="build"/>
@@ -25,7 +55,7 @@
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
<attribute name="Plugin-Description" value="An OSM data validator. It checks for problems in data, and provides fixes for the common ones. Spellcheck integrated for tag names."/>
<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/index.php/JOSM/Plugins/Validator"/>
- <attribute name="Plugin-Mainversion" value="2168"/>
+ <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
@@ -53,4 +83,81 @@
</condition>
<copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
</target>
+
+ <!--
+ ************************** Publishing the plugin ***********************************
+ -->
+ <!--
+ ** extracts the JOSM release for the JOSM version in ../core and saves it in the
+ ** property ${coreversion.info.entry.revision}
+ **
+ -->
+ <target name="core-info">
+ <exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="info"/>
+ <arg value="--xml"/>
+ <arg value="../../core"/>
+ </exec>
+ <xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
+ <echo>Building against core revision ${coreversion.info.entry.revision}.</echo>
+ <echo>Plugin-Mainversion is set to ${plugin.main.version}.</echo>
+ <delete file="core.info.xml" />
+ </target>
+
+ <!--
+ ** commits the source tree for this plugin
+ -->
+ <target name="commit-current">
+ <echo>Commiting the plugin source with message '${commit.message}' ...</echo>
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="-m '${commit.message}'"/>
+ <arg value="commit"/>
+ <arg value="."/>
+ </exec>
+ </target>
+
+ <!--
+ ** updates (svn up) the source tree for this plugin
+ -->
+ <target name="update-current">
+ <echo>Updating plugin source ...</echo>
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="up"/>
+ <arg value="."/>
+ </exec>
+ <echo>Updating ${plugin.jar} ...</echo>
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="up"/>
+ <arg value="../dist/${plugin.jar}"/>
+ </exec>
+ </target>
+
+ <!--
+ ** commits the plugin.jar
+ -->
+ <target name="commit-dist">
+ <echo>
+***** Properties of published ${plugin.jar} *****
+Commit message : '${commit.message}'
+Plugin-Mainversion: ${plugin.main.version}
+JOSM build version: ${coreversion.info.entry.revision}
+Plugin-Version : ${version.entry.commit.revision}
+***** / Properties of published ${plugin.jar} *****
+
+Now commiting ${plugin.jar} ...
+</echo>
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="-m '${commit.message}'"/>
+ <arg value="commit"/>
+ <arg value="${plugin.jar}"/>
+ </exec>
+ </target>
+
+ <target name="publish" depends="core-info,commit-current,update-current,clean,dist,commit-dist">
+ </target>
</project>
diff --git a/validator/ignoretags.cfg b/validator/ignoretags.cfg
index 028cec5..c28a228 100644
--- a/validator/ignoretags.cfg
+++ b/validator/ignoretags.cfg
@@ -248,13 +248,13 @@ T:religion=pastafarian|denomination=EVKdFSMiD
T:religion=christian|denomination=foursquare
T:religion=christian|denomination=greek_orthodox
T:religion=jewish|denomination=hasidic
-T:religion=jewish|denomination=humanistic
+T:religion=jewish|denomination=humanistic
T:religion=muslim|denomination=ibadi
T:religion=muslim|denomination=ismaili
T:religion=christian|denomination=jehovahs_witness
T:religion=christian|denomination=kabbalah
-T:religion=christian|denomination=karaite
-T:religion=jewish|denomination=liberal
+T:religion=christian|denomination=karaite
+T:religion=jewish|denomination=liberal
T:religion=christian|denomination=living_waters_church
T:religion=christian|denomination=lutheran
T:religion=christian|denomination=maronite
@@ -269,23 +269,23 @@ T:religion=christian|denomination=nondenominational
T:religion=jewish|denomination=nondenominational
T:religion=muslim|denomination=nondenominational
T:religion=christian|denomination=old_catholic
-T:religion=christian|denomination=orthodox
-T:religion=jewish|denomination=orthodox
+T:religion=christian|denomination=orthodox
+T:religion=jewish|denomination=orthodox
T:religion=christian|denomination=pentecostal
T:religion=christian|denomination=presbyterian
-T:religion=jewish|denomination=progressive
+T:religion=jewish|denomination=progressive
T:religion=christian|denomination=protestant
T:religion=christian|denomination=quaker
T:religion=jewish|denomination=reconstructionist
T:religion=jewish|denomination=reform
-T:religion=jewish|denomination=renewal
+T:religion=jewish|denomination=renewal
T:religion=christian|denomination=roman_catholic
T:religion=christian|denomination=russian_orthodox
T:religion=christian|denomination=salvation_army
T:religion=jewish|denomination=samaritan
T:religion=christian|denomination=seventh_day_adventist
T:religion=muslim|denomination=shia
-T:religion=muslim|denomination=sunni
+T:religion=muslim|denomination=sunni
T:religion=jewish|denomination=ultra_orthodox
T:religion=christian|denomination=united
T:religion=christian|denomination=united_reformed
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java b/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java
index 58ac364..6de2c33 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ErrorLayer.java
@@ -3,7 +3,7 @@ package org.openstreetmap.josm.plugins.validator;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Component;
-import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.util.Enumeration;
import java.util.List;
@@ -14,6 +14,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
@@ -26,7 +27,7 @@ import org.openstreetmap.josm.tools.ImageProvider;
/**
* A layer showing error messages.
- *
+ *
* @author frsantos
*/
public class ErrorLayer extends Layer implements LayerChangeListener {
@@ -53,7 +54,7 @@ public class ErrorLayer extends Layer implements LayerChangeListener {
*/
@SuppressWarnings("unchecked")
@Override
- public void paint(final Graphics g, final MapView mv) {
+ public void paint(final Graphics2D g, final MapView mv, Bounds bounds) {
DefaultMutableTreeNode root = plugin.validationDialog.tree.getRoot();
if (root == null || root.getChildCount() == 0)
return;
@@ -113,7 +114,7 @@ public class ErrorLayer extends Layer implements LayerChangeListener {
@Override
public Component[] getMenuEntries() {
return new Component[] { new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
- new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+ new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
new JSeparator(),
new JMenuItem(new RenameLayerAction(null, this)), new JSeparator(),
new JMenuItem(new LayerListPopup.InfoAction(this)) };
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java b/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java
index d4a0326..dc1a730 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/GridLayer.java
@@ -3,6 +3,7 @@ package org.openstreetmap.josm.plugins.validator;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
@@ -12,6 +13,7 @@ import javax.swing.JSeparator;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -53,7 +55,7 @@ public class GridLayer extends Layer
* Draw the grid and highlight all cells acuppied by any selected primitive.
*/
@Override
- public void paint(final Graphics g, final MapView mv)
+ public void paint(final Graphics2D g, final MapView mv, Bounds bounds)
{
if( !Main.pref.hasKey(PreferenceEditor.PREF_DEBUG + ".grid") )
return;
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java b/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
index b09371b..a916459 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
@@ -39,6 +39,7 @@ import org.openstreetmap.josm.plugins.validator.tests.CrossingWays;
import org.openstreetmap.josm.plugins.validator.tests.DuplicateNode;
import org.openstreetmap.josm.plugins.validator.tests.DuplicateWay;
import org.openstreetmap.josm.plugins.validator.tests.DuplicatedWayNodes;
+import org.openstreetmap.josm.plugins.validator.tests.NameMismatch;
import org.openstreetmap.josm.plugins.validator.tests.NodesWithSameName;
import org.openstreetmap.josm.plugins.validator.tests.OverlappingWays;
import org.openstreetmap.josm.plugins.validator.tests.SelfIntersectingWay;
@@ -97,6 +98,7 @@ public class OSMValidatorPlugin extends Plugin implements LayerChangeListener {
TagChecker.class, // ID 1201 .. 1299
UnconnectedWays.class, // ID 1301 .. 1399
DuplicateWay.class, // ID 1401 .. 1499
+ NameMismatch.class, // ID 1501 .. 1599
};
/**
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/Test.java b/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
index 4ef3790..26d738a 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
@@ -1,5 +1,7 @@
package org.openstreetmap.josm.plugins.validator;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -8,6 +10,7 @@ import javax.swing.JCheckBox;
import javax.swing.JPanel;
import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.BackreferencedDataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
@@ -16,7 +19,6 @@ import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.tools.GBC;
-import static org.openstreetmap.josm.tools.I18n.tr;
/**
* Parent class for all validation tests.
@@ -55,11 +57,16 @@ public class Test extends AbstractVisitor
/** Whether the test is run on a partial selection data */
protected boolean partialSelection;
-
+
/** the progress monitor to use */
protected ProgressMonitor progressMonitor;
/**
+ * the data structure with child->parent references
+ */
+ protected BackreferencedDataSet backreferenceDataSet;
+
+ /**
* Constructor
* @param name Name of the test
* @param description Description of the test
@@ -87,18 +94,19 @@ public class Test extends AbstractVisitor
public static void initialize(OSMValidatorPlugin plugin) throws Exception {}
/**
- * Start the test using a given progress monitor
- *
- * @param progressMonitor the progress monitor
+ * Start the test using a given progress monitor
+ *
+ * @param progressMonitor the progress monitor
*/
public void startTest(ProgressMonitor progressMonitor) {
- if (progressMonitor == null) {
- this.progressMonitor = NullProgressMonitor.INSTANCE;
- } else {
- this.progressMonitor = progressMonitor;
- }
- this.progressMonitor.beginTask(tr("Running test {0}", name));
- errors = new ArrayList<TestError>(30);
+ backreferenceDataSet = new BackreferencedDataSet();
+ if (progressMonitor == null) {
+ this.progressMonitor = NullProgressMonitor.INSTANCE;
+ } else {
+ this.progressMonitor = progressMonitor;
+ }
+ this.progressMonitor.beginTask(tr("Running test {0}", name));
+ errors = new ArrayList<TestError>(30);
}
/**
@@ -124,8 +132,9 @@ public class Test extends AbstractVisitor
* actions and destroy the used structures
*/
public void endTest() {
- progressMonitor.finishTask();
- progressMonitor = null;
+ progressMonitor.finishTask();
+ progressMonitor = null;
+ backreferenceDataSet = null;
}
/**
@@ -136,7 +145,7 @@ public class Test extends AbstractVisitor
*/
public void visit(Collection<OsmPrimitive> selection)
{
- progressMonitor.setTicksCount(selection.size());
+ progressMonitor.setTicksCount(selection.size());
for (OsmPrimitive p : selection) {
if( p.isUsable() )
p.visit(this);
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java b/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
index cd85619..47aa3b6 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
@@ -152,7 +152,7 @@ public class TestError {
String ignorestring = getIgnoreSubGroup();
for (OsmPrimitive o : primitives) {
// ignore data not yet uploaded
- if (o.getId() == 0)
+ if (o.isNew())
return null;
String type = "u";
if (o instanceof Way)
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
index 341ce90..547928f 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
@@ -139,7 +139,7 @@ public class ValidateAction extends JosmAction {
public void run() {
plugin.validationDialog.tree.setErrors(errors);
plugin.validationDialog.setVisible(true);
- DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
+ Main.main.getCurrentDataSet().fireSelectionChanged();
}
};
if (SwingUtilities.isEventDispatchThread()) {
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java
index 5736c2f..6b24967 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ValidateUploadHook.java
@@ -120,7 +120,7 @@ public class ValidateUploadHook implements UploadHook
{
plugin.validationDialog.tree.setErrors(errors);
plugin.validationDialog.setVisible(true);
- DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
+ Main.main.getCurrentDataSet().fireSelectionChanged();
}
return res == JOptionPane.YES_OPTION;
}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java b/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
index e5efd64..f7f2edf 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
@@ -142,7 +142,7 @@ public class ValidatorDialog extends ToggleDialog implements ActionListener, Sel
return;
Set<DefaultMutableTreeNode> processedNodes = new HashSet<DefaultMutableTreeNode>();
-
+
DuplicateNode.clearBackreferences();
LinkedList<TestError> errorsToFix = new LinkedList<TestError>();
for (TreePath path : selectionPaths) {
@@ -159,11 +159,11 @@ public class ValidatorDialog extends ToggleDialog implements ActionListener, Sel
processedNodes.add(childNode);
Object nodeInfo = childNode.getUserObject();
if (nodeInfo instanceof TestError) {
- errorsToFix.add((TestError)nodeInfo);
+ errorsToFix.add((TestError)nodeInfo);
}
}
}
-
+
// run fix task asynchronously
//
FixTask fixTask = new FixTask(errorsToFix);
@@ -444,78 +444,78 @@ public class ValidatorDialog extends ToggleDialog implements ActionListener, Sel
HashSet<OsmPrimitive> filter = new HashSet<OsmPrimitive>(newSelection);
tree.setFilter(filter);
}
-
+
/**
- * Task for fixing a collection of {@see TestError}s. Can be run asynchronously.
- *
+ * Task for fixing a collection of {@see TestError}s. Can be run asynchronously.
+ *
*
*/
class FixTask extends PleaseWaitRunnable {
- private Collection<TestError> testErrors;
- private boolean canceled;
- private LinkedList<Command> fixCommands;
-
-
- public FixTask(Collection<TestError> testErrors) {
- super(tr("Fixing errors ..."), false /* don't ignore exceptions */);
- this.testErrors = testErrors == null ? new ArrayList<TestError> (): testErrors;
- fixCommands = new LinkedList<Command>();
- }
-
- @Override
- protected void cancel() {
- this.canceled = true;
- }
-
- @Override
- protected void finish() {
- // do nothing
- }
-
- @Override
- protected void realRun() throws SAXException, IOException,
- OsmTransferException {
- ProgressMonitor monitor = getProgressMonitor();
- try {
- monitor.setTicksCount(testErrors.size());
- int i=0;
- for (TestError error: testErrors) {
- i++;
- monitor.subTask(tr("Fixing ({0}/{1}): ''{2}''", i, testErrors.size(),error.getMessage()));
- if (this.canceled)
- return;
- final Command fixCommand = error.getFix();
+ private Collection<TestError> testErrors;
+ private boolean canceled;
+ private LinkedList<Command> fixCommands;
+
+
+ public FixTask(Collection<TestError> testErrors) {
+ super(tr("Fixing errors ..."), false /* don't ignore exceptions */);
+ this.testErrors = testErrors == null ? new ArrayList<TestError> (): testErrors;
+ fixCommands = new LinkedList<Command>();
+ }
+
+ @Override
+ protected void cancel() {
+ this.canceled = true;
+ }
+
+ @Override
+ protected void finish() {
+ // do nothing
+ }
+
+ @Override
+ protected void realRun() throws SAXException, IOException,
+ OsmTransferException {
+ ProgressMonitor monitor = getProgressMonitor();
+ try {
+ monitor.setTicksCount(testErrors.size());
+ int i=0;
+ for (TestError error: testErrors) {
+ i++;
+ monitor.subTask(tr("Fixing ({0}/{1}): ''{2}''", i, testErrors.size(),error.getMessage()));
+ if (this.canceled)
+ return;
+ final Command fixCommand = error.getFix();
if (fixCommand != null) {
- fixCommands.add(fixCommand);
- SwingUtilities.invokeAndWait(
- new Runnable() {
- public void run() {
- Main.main.undoRedo.addNoRedraw(fixCommand);
- }
- }
- );
+ fixCommands.add(fixCommand);
+ SwingUtilities.invokeAndWait(
+ new Runnable() {
+ public void run() {
+ Main.main.undoRedo.addNoRedraw(fixCommand);
+ }
+ }
+ );
error.setIgnored(true);
}
monitor.worked(1);
- }
- monitor.subTask(tr("Updating map ..."));
- SwingUtilities.invokeAndWait(new Runnable() {
- public void run() {
- Main.main.undoRedo.afterAdd();
- Main.map.repaint();
- tree.resetErrors();
- DataSet.fireSelectionChanged(Main.main.getCurrentDataSet().getSelected());
- }
- });
- } catch(InterruptedException e) {
- // FIXME: signature of realRun should have a generic checked exception we
- // could throw here
- throw new RuntimeException(e);
- } catch(InvocationTargetException e) {
- throw new RuntimeException(e);
- } finally {
- monitor.finishTask();
- }
- }
+ }
+ monitor.subTask(tr("Updating map ..."));
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ Main.main.undoRedo.afterAdd();
+ Main.map.repaint();
+ tree.resetErrors();
+ Main.main.getCurrentDataSet().fireSelectionChanged();
+ }
+ });
+ } catch(InterruptedException e) {
+ // FIXME: signature of realRun should have a generic checked exception we
+ // could throw here
+ throw new RuntimeException(e);
+ } catch(InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } finally {
+ monitor.finishTask();
+ }
+ }
}
}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java
index 3f248b3..0c808ad 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/Coastlines.java
@@ -2,31 +2,36 @@ package org.openstreetmap.josm.plugins.validator.tests;
import static org.openstreetmap.josm.tools.I18n.tr;
-import java.awt.geom.Point2D;
+import java.awt.geom.Area;
import java.util.*;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.plugins.validator.Severity;
import org.openstreetmap.josm.plugins.validator.Test;
import org.openstreetmap.josm.plugins.validator.TestError;
-import org.openstreetmap.josm.plugins.validator.util.Bag;
-import org.openstreetmap.josm.plugins.validator.util.Util;
/**
* Check coastlines for errors
*
* @author frsantos
+ * @author Teemu Koskinen
*/
public class Coastlines extends Test
{
- protected static int UNORDERED_COASTLINES = 901;
+ protected static int UNORDERED_COASTLINE = 901;
+ protected static int REVERSED_COASTLINE = 902;
+ protected static int UNCONNECTED_COASTLINE = 903;
- /** All ways, grouped by cells */
- Map<Point2D,List<Way>> _cellWays;
- /** The already detected errors */
- Bag<Way, Way> _errorWays;
+ private List<Way> coastlines;
+
+ private Area downloadedArea = null;
/**
* Constructor
@@ -41,50 +46,157 @@ public class Coastlines extends Test
public void startTest(ProgressMonitor monitor)
{
super.startTest(monitor);
- _cellWays = new HashMap<Point2D,List<Way>>(1000);
- _errorWays = new Bag<Way, Way>();
+
+ OsmDataLayer layer = Main.map.mapView.getEditLayer();
+
+ if (layer != null)
+ downloadedArea = layer.data.getDataSourceArea();
+
+ coastlines = new LinkedList<Way>();
}
@Override
public void endTest()
{
- super.endTest();
- _cellWays = null;
- _errorWays = null;
+ for (Way c1 : coastlines) {
+ Node head = c1.firstNode();
+ Node tail = c1.lastNode();
+
+ if (head.equals(tail))
+ continue;
+
+ int headWays = 0;
+ int tailWays = 0;
+ boolean headReversed = false;
+ boolean tailReversed = false;
+ boolean headUnordered = false;
+ boolean tailUnordered = false;
+ Way next = null;
+ Way prev = null;
+
+ for (Way c2 : coastlines) {
+ if (c1 == c2)
+ continue;
+
+ if (c2.containsNode(head)) {
+ headWays++;
+ next = c2;
+
+ if (head.equals(c2.firstNode()))
+ headReversed = true;
+ else if (!head.equals(c2.lastNode()))
+ headUnordered = true;
+ }
+
+ if (c2.containsNode(tail)) {
+ tailWays++;
+ prev = c2;
+
+ if (tail.equals(c2.lastNode()))
+ tailReversed = true;
+ else if (!tail.equals(c2.firstNode()))
+ tailUnordered = true;
+ }
+ }
+
+
+ List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
+ primitives.add(c1);
+
+ if (headWays == 0 || tailWays == 0) {
+ List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
+
+ System.out.println("Unconnected coastline: " + c1.getId());
+ if (headWays == 0 && (downloadedArea == null || downloadedArea.contains(head.getCoor()))) {
+ System.out.println("headways: " +headWays+ " node: " + head.toString());
+ highlight.add(head);
+ }
+ if (tailWays == 0 && (downloadedArea == null || downloadedArea.contains(tail.getCoor()))) {
+ System.out.println("tailways: " +tailWays+ " tail: " + tail.toString());
+ highlight.add(tail);
+ }
+
+ if (highlight.size() > 0)
+ errors.add(new TestError(this, Severity.ERROR, tr("Unconnected coastline"),
+ UNCONNECTED_COASTLINE, primitives, highlight));
+ }
+
+ boolean unordered = false;
+ boolean reversed = false;
+
+ if (headWays == 1 && headReversed && tailWays == 1 && tailReversed)
+ reversed = true;
+
+ if (headWays > 1 || tailWays > 1)
+ unordered = true;
+ else if (headUnordered || tailUnordered)
+ unordered = true;
+ else if (reversed && next == prev)
+ unordered = true;
+
+ if (unordered) {
+ List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
+
+ System.out.println("Unordered coastline: " + c1.toString());
+ if (headWays > 1 || headUnordered || reversed) {
+ System.out.println("head: " + head.toString());
+ highlight.add(head);
+ }
+ if (tailWays > 1 || tailUnordered || reversed) {
+ System.out.println("tail: " + tail.toString());
+ highlight.add(tail);
+ }
+
+ errors.add(new TestError(this, Severity.ERROR, tr("Unordered coastline"),
+ UNORDERED_COASTLINE, primitives, highlight));
+ }
+ else if (reversed) {
+ errors.add(new TestError(this, Severity.ERROR, tr("Reversed coastline"),
+ REVERSED_COASTLINE, primitives));
+ }
+ }
+
+ coastlines = null;
+ downloadedArea = null;
+
+ super.endTest();
}
@Override
- public void visit(Way w)
+ public void visit(Way way)
{
- if( !w.isUsable() )
+ if (!way.isUsable())
return;
- String natural = w.get("natural");
- if( natural == null || !natural.equals("coastline") )
+ String natural = way.get("natural");
+ if (natural == null || !natural.equals("coastline"))
return;
- List<List<Way>> cellWays = Util.getWaysInCell(w, _cellWays);
- for( List<Way> ways : cellWays)
- {
- for( Way w2 : ways)
- {
- if( _errorWays.contains(w, w2) || _errorWays.contains(w2, w) )
- continue;
+ coastlines.add(way);
+ }
- String natural2 = w.get("natural");
- if( natural2 == null || !natural2.equals("coastline") )
- continue;
+ @Override
+ public Command fixError(TestError testError) {
+ if (isFixable(testError)) {
+ Way way = (Way) testError.getPrimitives().iterator().next();
+ Way newWay = new Way(way);
- if( w.getNodes().get(0).equals(w2.getNodes().get(0)) || w.getNodes().get(w.getNodesCount() - 1).equals(w2.getNodes().get(w2.getNodesCount() - 1)))
- {
- List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
- primitives.add(w);
- primitives.add(w2);
- errors.add( new TestError(this, Severity.ERROR, tr("Unordered coastline"), UNORDERED_COASTLINES, primitives) );
- _errorWays.add(w, w2);
- }
- }
- ways.add(w);
+ List<Node> nodesCopy = newWay.getNodes();
+ Collections.reverse(nodesCopy);
+ newWay.setNodes(nodesCopy);
+
+ return new ChangeCommand(way, newWay);
}
+
+ return null;
+ }
+
+ @Override
+ public boolean isFixable(TestError testError) {
+ if (testError.getTester() instanceof Coastlines) {
+ return (testError.getCode() == REVERSED_COASTLINE);
+ }
+
+ return false;
}
}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java
index db5b090..3c5d1d1 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/CrossingWays.java
@@ -73,14 +73,13 @@ public class CrossingWays extends Test
return;
String coastline1 = w.get("natural");
- boolean isCoastline1 = coastline1 != null && (coastline1.equals("water") || coastline1.equals("coastline"));
+ boolean isCoastline1 = "water".equals(coastline1) || "coastline".equals(coastline1);
String railway1 = w.get("railway");
- boolean isSubway1 = railway1 != null && railway1.equals("subway");
- boolean isBuilding = false;
- if(w.get("building") != null)
- isBuilding = true;
-
- if( w.get("highway") == null && w.get("waterway") == null && (railway1 == null || isSubway1) && !isCoastline1 && !isBuilding)
+ boolean isSubway1 = "subway".equals(railway1);
+ boolean isTram1 = "tram".equals(railway1);
+ boolean isBuilding = (w.get("building") != null);
+
+ if( w.get("highway") == null && w.get("waterway") == null && (railway1 == null || isSubway1 || isTram1) && !isCoastline1 && !isBuilding)
return;
String layer1 = w.get("layer");
@@ -108,6 +107,7 @@ public class CrossingWays extends Test
if( !es1.intersects(es2) ) continue;
if( isSubway1 && "subway".equals(railway2)) continue;
+ if( isTram1 && "tram".equals(railway2)) continue;
boolean isCoastline2 = coastline2 != null && (coastline2.equals("water") || coastline2.equals("coastline"));
if( isCoastline1 != isCoastline2 ) continue;
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
index 9a63fc6..539468f 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
@@ -3,8 +3,8 @@ package org.openstreetmap.josm.plugins.validator.tests;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.geom.Area;
-
import java.util.Collection;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -30,21 +30,20 @@ import org.openstreetmap.josm.plugins.validator.util.Bag;
* @author frsantos
*/
public class DuplicateNode extends Test{
-
+
private static BackreferencedDataSet backreferences;
-
+
public static BackreferencedDataSet getBackreferenceDataSet() {
if (backreferences == null) {
- backreferences = new BackreferencedDataSet(Main.main.getEditLayer().data);
- backreferences.build();
+ backreferences = new BackreferencedDataSet();
}
return backreferences;
}
-
+
public static void clearBackreferences() {
backreferences = null;
}
-
+
protected static int DUPLICATE_NODE = 1;
/** Bag of all nodes */
@@ -109,7 +108,7 @@ public class DuplicateNode extends Test{
public Command fixError(TestError testError)
{
Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>(testError.getPrimitives());
- LinkedList<Node> nodes = new LinkedList<Node>(OsmPrimitive.getFilteredList(sel, Node.class));
+ LinkedHashSet<Node> nodes = new LinkedHashSet<Node>(OsmPrimitive.getFilteredList(sel, Node.class));
Node target = MergeNodesAction.selectTargetNode(nodes);
if(checkAndConfirmOutlyingDeletes(nodes))
return MergeNodesAction.mergeNodes(Main.main.getEditLayer(),getBackreferenceDataSet(), nodes, target);
@@ -127,11 +126,11 @@ public class DuplicateNode extends Test{
* Check whether user is about to delete data outside of the download area.
* Request confirmation if he is.
*/
- private static boolean checkAndConfirmOutlyingDeletes(LinkedList<Node> del) {
+ private static boolean checkAndConfirmOutlyingDeletes(LinkedHashSet<Node> del) {
Area a = Main.main.getCurrentDataSet().getDataSourceArea();
if (a != null) {
for (OsmPrimitive osm : del) {
- if (osm instanceof Node && osm.getId() != 0) {
+ if (osm instanceof Node && !osm.isNew()) {
Node n = (Node) osm;
if (!a.contains(n.getCoor())) {
return ConditionalOptionPaneUtil.showConfirmationDialog(
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java
index 98a0cc6..4711fa3 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateWay.java
@@ -5,8 +5,8 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
-import java.util.Vector;
import java.util.Map;
+import java.util.Vector;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.command.Command;
@@ -14,8 +14,8 @@ import org.openstreetmap.josm.command.DeleteCommand;
import org.openstreetmap.josm.command.SequenceCommand;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.plugins.validator.Severity;
import org.openstreetmap.josm.plugins.validator.Test;
@@ -119,7 +119,7 @@ public class DuplicateWay extends Test
// Only one way will be kept - the one with lowest positive ID, if such exist
// or one "at random" if no such exists. Rest of the ways will be deleted
for (Way w: ways) {
- if (w.getId() > 0) {
+ if (!w.isNew()) {
if (idToKeep == 0 || w.getId() < idToKeep) idToKeep = w.getId();
}
}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/NameMismatch.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NameMismatch.java
new file mode 100644
index 0000000..999db32
--- /dev/null
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NameMismatch.java
@@ -0,0 +1,109 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+
+/**
+ * Check for missing name:* translations.
+ * <p>
+ * This test finds multilingual objects whose 'name' attribute is not
+ * equal to any 'name:*' attribute and not a composition of some
+ * 'name:*' attributes separated by ' - '.
+ * <p>
+ * For example, a node with name=Europe, name:de=Europa should have
+ * name:en=Europe to avoid triggering this test. An object with
+ * name='Suomi - Finland' should have at least name:fi=Suomi and
+ * name:sv=Finland to avoid a warning (name:et=Soome would not
+ * matter). Also, complain if an object has some name:* attribute but
+ * no name.
+ *
+ * @author Skela
+ */
+public class NameMismatch extends Test {
+ protected static final int NAME_MISSING = 1501;
+ protected static final int NAME_TRANSLATION_MISSING = 1502;
+
+ public NameMismatch() {
+ super(tr("Missing name:* translation."),
+ tr("This test finds multilingual objects whose 'name' attribute is not equal to some 'name:*' attribute and not a composition of 'name:*' attributes, e.g., Italia - Italien - Italy."));
+ }
+
+ /**
+ * Report a missing translation.
+ *
+ * @param p The primitive whose translation is missing
+ */
+ private void missingTranslation(OsmPrimitive p) {
+ errors.add(new TestError(this, Severity.OTHER,
+ tr("A name:* translation is missing."),
+ NAME_TRANSLATION_MISSING, p));
+ }
+
+ /**
+ * Check a primitive for a name mismatch.
+ *
+ * @param p The primitive to be tested
+ */
+ public void check(OsmPrimitive p) {
+ HashSet<String> names = new HashSet<String>();
+
+ for (Entry<String, String> entry : p.entrySet()) {
+ if (entry.getKey().startsWith("name:")) {
+ String name_s = entry.getValue();
+ if (name_s != null) {
+ names.add(name_s);
+ }
+ }
+ }
+
+ if (names.isEmpty()) return;
+
+ String name = p.get("name");
+
+ if (name == null) {
+ errors.add(new TestError(this, Severity.OTHER,
+ tr("A name is missing, even though name:* exists."),
+ NAME_MISSING, p));
+ return;
+ }
+
+ if (names.contains(name)) return;
+ /* If name is not equal to one of the name:*, it should be a
+ composition of some (not necessarily all) name:* labels.
+ Check if this is the case. */
+
+ String split_names[] = name.split(" - ");
+ if (split_names.length == 1) {
+ /* The name is not composed of multiple parts. Complain. */
+ missingTranslation(p);
+ return;
+ }
+
+ /* Check that each part corresponds to a translated name:*. */
+ for (String n : split_names) {
+ if (!names.contains(n)) {
+ missingTranslation(p);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Checks a name mismatch in all primitives.
+ *
+ * @param selection The primitives to be tested
+ */
+ @Override public void visit(Collection<OsmPrimitive> selection) {
+ for (OsmPrimitive p : selection)
+ if (!p.isDeleted() && !p.incomplete)
+ check(p);
+ }
+}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java
index 6e2c103..831b4a0 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/NodesWithSameName.java
@@ -5,6 +5,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.Map;
import java.util.List;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.ArrayList;
@@ -25,7 +26,7 @@ public class NodesWithSameName extends Test {
}
@Override public void startTest(ProgressMonitor monitor) {
- super.startTest(monitor);
+ super.startTest(monitor);
namesToNodes = new HashMap<String, List<Node>>();
}
@@ -34,7 +35,12 @@ public class NodesWithSameName extends Test {
String name = n.get("name");
String sign = n.get("traffic_sign");
- if (name == null || (sign != null && sign.equals("city_limit"))) return;
+ String highway = n.get("highway");
+ if (name == null
+ || (sign != null && sign.equals("city_limit"))
+ || (highway != null && highway.equals("bus_stop"))) {
+ return;
+ }
List<Node> nodes = namesToNodes.get(name);
if (nodes == null)
@@ -46,8 +52,17 @@ public class NodesWithSameName extends Test {
@Override public void endTest() {
for (List<Node> nodes : namesToNodes.values()) {
if (nodes.size() > 1) {
- errors.add(new TestError(this, Severity.OTHER,
- tr("Nodes with same name"), SAME_NAME, nodes));
+ // Report the same-name nodes, unless each has a unique ref=*.
+ HashSet<String> refs = new HashSet<String>();
+
+ for (Node n : nodes) {
+ String ref = n.get("ref");
+ if (ref == null || !refs.add(ref)) {
+ errors.add(new TestError(this, Severity.OTHER,
+ tr("Nodes with same name"), SAME_NAME, nodes));
+ break;
+ }
+ }
}
}
super.endTest();
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java
index c5ecc16..dc8f206 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java
@@ -439,15 +439,19 @@ public class TagChecker extends Test
}
}
}
- if(checkPaint && p.errors != null)
+ if(checkPaint)
{
- for(String s: p.errors)
+ List<String> pe = p.getDataSet().getErrors(p);
+ if(pe != null)
{
- /* passing translated text also to original string, as we already
- translated the stuff before. Makes the ignore file language dependend. */
- errors.add( new TestError(this, Severity.WARNING, tr("Painting problem"),
- s, s, PAINT, p) );
- withErrors.add(p, "P");
+ for(String s: pe)
+ {
+ /* passing translated text also to original string, as we already
+ translated the stuff before. Makes the ignore file language dependend. */
+ errors.add( new TestError(this, Severity.WARNING, tr("Painting problem"),
+ s, s, PAINT, p) );
+ withErrors.add(p, "P");
+ }
}
}
@@ -555,7 +559,7 @@ public class TagChecker extends Test
@Override
public void startTest(ProgressMonitor monitor)
{
- super.startTest(monitor);
+ super.startTest(monitor);
checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS, true);
if( isBeforeUpload )
checkKeys = checkKeys && Main.pref.getBoolean(PREF_CHECK_KEYS_BEFORE_UPLOAD, true);
@@ -580,7 +584,7 @@ public class TagChecker extends Test
@Override
public void visit(Collection<OsmPrimitive> selection)
{
- if( checkKeys || checkValues || checkComplex)
+ if( checkKeys || checkValues || checkComplex || checkPaint || checkFixmes)
super.visit(selection);
}
@@ -621,11 +625,11 @@ public class TagChecker extends Test
addSrcButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String source = JOptionPane.showInputDialog(
- Main.parent,
- tr("TagChecker source"),
- tr("TagChecker source"),
- JOptionPane.QUESTION_MESSAGE
- );
+ Main.parent,
+ tr("TagChecker source"),
+ tr("TagChecker source"),
+ JOptionPane.QUESTION_MESSAGE
+ );
if (source != null)
((DefaultListModel)Sources.getModel()).addElement(source);
Sources.clearSelection();
@@ -652,19 +656,19 @@ public class TagChecker extends Test
else
{
JOptionPane.showMessageDialog(
- Main.parent,
- tr("Please select the row to edit."),
- tr("Information"),
- JOptionPane.INFORMATION_MESSAGE
- );
+ Main.parent,
+ tr("Please select the row to edit."),
+ tr("Information"),
+ JOptionPane.INFORMATION_MESSAGE
+ );
}
}
else {
- String source = (String)JOptionPane.showInputDialog(Main.parent,
- tr("TagChecker source"),
- tr("TagChecker source"),
- JOptionPane.QUESTION_MESSAGE, null, null,
- Sources.getSelectedValue());
+ String source = (String)JOptionPane.showInputDialog(Main.parent,
+ tr("TagChecker source"),
+ tr("TagChecker source"),
+ JOptionPane.QUESTION_MESSAGE, null, null,
+ Sources.getSelectedValue());
if (source != null)
((DefaultListModel)Sources.getModel()).setElementAt(source, row);
}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java
index 506e55c..59d8739 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnclosedWays.java
@@ -10,6 +10,7 @@ import java.util.List;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.plugins.validator.Severity;
@@ -35,7 +36,7 @@ public class UnclosedWays extends Test {
@Override
public void startTest(ProgressMonitor monitor) {
- super.startTest(monitor);
+ super.startTest(monitor);
_errorWays = new Bag<Way, Way>();
}
@@ -48,26 +49,22 @@ public class UnclosedWays extends Test {
private String type;
private String etype;
private int mode;
- private boolean force;
- public void set(boolean f, int m, String text, String desc) {
+ public void set(int m, String text, String desc) {
etype = MessageFormat.format(text, desc);
type = tr(text, tr(desc));
mode = m;
- force = f;
}
- public void set(boolean f, int m, String text) {
+ public void set(int m, String text) {
etype = text;
type = tr(text);
mode = m;
- force = f;
}
@Override
public void visit(Way w) {
String test;
- force = false; /* force even if end-to-end distance is long */
type = etype = null;
mode = 0;
@@ -75,53 +72,56 @@ public class UnclosedWays extends Test {
return;
test = w.get("natural");
- if (test != null)
- set(!"coastline".equals(test), 1101, marktr("natural type {0}"), test);
+ if (test != null && !"coastline".equals(test) && !"cliff".equals(test))
+ set(1101, marktr("natural type {0}"), test);
test = w.get("landuse");
if (test != null)
- set(true, 1102, marktr("landuse type {0}"), test);
+ set(1102, marktr("landuse type {0}"), test);
test = w.get("amenities");
if (test != null)
- set(true, 1103, marktr("amenities type {0}"), test);
+ set(1103, marktr("amenities type {0}"), test);
test = w.get("sport");
if (test != null && !test.equals("water_slide"))
- set(true, 1104, marktr("sport type {0}"), test);
+ set(1104, marktr("sport type {0}"), test);
test = w.get("tourism");
if (test != null)
- set(true, 1105, marktr("tourism type {0}"), test);
+ set(1105, marktr("tourism type {0}"), test);
test = w.get("shop");
if (test != null)
- set(true, 1106, marktr("shop type {0}"), test);
+ set(1106, marktr("shop type {0}"), test);
test = w.get("leisure");
if (test != null)
- set(true, 1107, marktr("leisure type {0}"), test);
+ set(1107, marktr("leisure type {0}"), test);
test = w.get("waterway");
if (test != null && test.equals("riverbank"))
- set(true, 1108, marktr("waterway type {0}"), test);
+ set(1108, marktr("waterway type {0}"), test);
Boolean btest = OsmUtils.getOsmBoolean(w.get("building"));
if (btest != null && btest)
- set(true, 1120, marktr("building"));
+ set(1120, marktr("building"));
btest = OsmUtils.getOsmBoolean(w.get("area"));
if (btest != null && btest)
- set(true, 1130, marktr("area"));
-
- if (type != null && !w.isClosed())
- {
- Node f = w.getNode(0);
- Node l = w.getNode(w.getNodesCount() - 1);
- if(force || f.getCoor().greatCircleDistance(l.getCoor()) < 10000)
- {
- List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
- List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
- primitives.add(w);
- // The important parts of an unclosed way are the first and
- // the last node which should be connected, therefore we highlight them
- highlight.add(f);
- highlight.add(l);
- errors.add(new TestError(this, Severity.WARNING, tr("Unclosed way"), type, etype, mode, primitives,
- highlight));
- _errorWays.add(w, w);
+ set(1130, marktr("area"));
+
+ if (type != null && !w.isClosed()) {
+ for (OsmPrimitive parent: this.backreferenceDataSet.getParents(w)) {
+ if (parent instanceof Relation && "multipolygon".equals(parent.get("type")))
+ return;
}
+ Node f = w.firstNode();
+ Node l = w.lastNode();
+
+ List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
+ List<OsmPrimitive> highlight = new ArrayList<OsmPrimitive>();
+ primitives.add(w);
+
+ // The important parts of an unclosed way are the first and
+ // the last node which should be connected, therefore we highlight them
+ highlight.add(f);
+ highlight.add(l);
+
+ errors.add(new TestError(this, Severity.WARNING, tr("Unclosed way"),
+ type, etype, mode, primitives, highlight));
+ _errorWays.add(w, w);
}
}
}
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java
index 6666394..ce93a3d 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java
@@ -4,15 +4,25 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.BBox;
+import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.data.osm.QuadBuckets;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.plugins.validator.PreferenceEditor;
@@ -35,6 +45,10 @@ public class UnconnectedWays extends Test
Set<Node> endnodes_highway; // nodes at end of way
Set<Node> middlenodes; // nodes in middle of way
Set<Node> othernodes; // nodes appearing at least twice
+ //NodeSearchCache nodecache;
+ QuadBuckets<Node> nodecache;
+ Area ds_area;
+ DataSet ds;
double mindist;
double minmiddledist;
@@ -50,7 +64,7 @@ public class UnconnectedWays extends Test
@Override
public void startTest(ProgressMonitor monitor)
{
- super.startTest(monitor);
+ super.startTest(monitor);
ways = new HashSet<MyWaySegment>();
endnodes = new HashSet<Node>();
endnodes_highway = new HashSet<Node>();
@@ -58,128 +72,253 @@ public class UnconnectedWays extends Test
othernodes = new HashSet<Node>();
mindist = Main.pref.getDouble(PREFIX + ".node_way_distance", 10.0)/6378135.0;
minmiddledist = Main.pref.getDouble(PREFIX + ".way_way_distance", 0.0)/6378135.0;
+ this.ds = Main.main.getCurrentDataSet();
+ this.ds_area = ds.getDataSourceArea();
}
@Override
public void endTest()
{
- Area a = Main.main.getCurrentDataSet().getDataSourceArea();
+ //Area a = Main.ds.getDataSourceArea();
Map<Node, Way> map = new HashMap<Node, Way>();
- for(Node en : endnodes_highway)
- {
- Boolean isexit = OsmUtils.getOsmBoolean(en.get("noexit"));
- if("turning_circle".equals(en.get("highway")) ||
- (isexit != null && isexit) || en.get("barrier") != null)
- continue;
- for(MyWaySegment s : ways)
- {
- if(!s.isBoundary && !s.isAbandoned && s.highway && s.nearby(en, mindist) && (a == null || a.contains(en.getCoor())))
- map.put(en, s.w);
+ long last = -1;
+ for (int iter = 0; iter < 1; iter++) {
+ last = System.currentTimeMillis();
+ long last_print = -1;
+ int nr = 0;
+ Collection<MyWaySegment> tmp_ways = ways;
+ for(MyWaySegment s : tmp_ways) {
+ nr++;
+ long now = System.currentTimeMillis();
+ if (now - last_print > 200) {
+ //System.err.println("processing segment nr: " + nr + " of " + ways.size());
+ last_print = now;
+ }
+ for(Node en : s.nearbyNodes(mindist)) {
+ if (en == null)
+ continue;
+ //if("turning_circle".equals(en.get("highway")) ||
+ // (isexit != null && isexit) || en.get("barrier") != null)
+ // c4++;
+ if(!s.highway)
+ continue;
+ if (!endnodes_highway.contains(en))
+ continue;
+ Boolean isexit = OsmUtils.getOsmBoolean(en.get("noexit"));
+ if("turning_circle".equals(en.get("highway")) ||
+ "bus_stop".equals(en.get("highway")) ||
+ (isexit != null && isexit) || en.get("barrier") != null)
+ continue;
+ // There's a small false-positive here. Imagine an intersection
+ // like a 't'. If the top part of the 't' is short enough, it
+ // will trigger the node at the very top of the 't' to be unconnected
+ // to the way that "crosses" the 't'. We should probably check that
+ // the ways to which 'en' belongs are not connected to 's.w'.
+ map.put(en, s.w);
}
}
- if(map.size() > 0)
+ //System.out.println("p1 elapsed: " + (System.currentTimeMillis()-last));
+ last = System.currentTimeMillis();
+ }
+ for(Map.Entry<Node, Way> error : map.entrySet())
{
- for(Map.Entry<Node, Way> error : map.entrySet())
- {
- errors.add(new TestError(this, Severity.WARNING,
- tr("Way end node near other highway"), UNCONNECTED_WAYS,
- Arrays.asList(error.getKey(), error.getValue())));
- }
+ errors.add(new TestError(this, Severity.WARNING,
+ tr("Way end node near other highway"), UNCONNECTED_WAYS,
+ Arrays.asList(error.getKey(), error.getValue())));
}
map.clear();
- for(Node en : endnodes_highway)
+ for(MyWaySegment s : ways)
{
- for(MyWaySegment s : ways)
+ for(Node en : s.nearbyNodes(mindist))
{
- if(!s.isBoundary && !s.isAbandoned && !s.highway && s.nearby(en, mindist) && !s.isArea() && (a == null || a.contains(en.getCoor())))
+ if (endnodes_highway.contains(en) && !s.highway && !s.isArea()) {
map.put(en, s.w);
- }
- }
- for(Node en : endnodes)
- {
- for(MyWaySegment s : ways)
- {
- if(!s.isBoundary && !s.isAbandoned && s.nearby(en, mindist) && !s.isArea() && (a == null || a.contains(en.getCoor())))
+ } else if (endnodes.contains(en) && !s.isArea()) {
map.put(en, s.w);
+ }
}
}
- if(map.size() > 0)
+ //System.out.println("p2 elapsed: " + (System.currentTimeMillis()-last));
+ last = System.currentTimeMillis();
+ for(Map.Entry<Node, Way> error : map.entrySet())
{
- for(Map.Entry<Node, Way> error : map.entrySet())
- {
- errors.add(new TestError(this, Severity.WARNING,
- tr("Way end node near other way"), UNCONNECTED_WAYS,
- Arrays.asList(error.getKey(), error.getValue())));
- }
+ errors.add(new TestError(this, Severity.WARNING,
+ tr("Way end node near other way"), UNCONNECTED_WAYS,
+ Arrays.asList(error.getKey(), error.getValue())));
}
/* the following two use a shorter distance */
if(minmiddledist > 0.0)
{
map.clear();
- for(Node en : middlenodes)
+ for(MyWaySegment s : ways)
{
- for(MyWaySegment s : ways)
+ for(Node en : s.nearbyNodes(minmiddledist))
{
- if(!s.isBoundary && !s.isAbandoned && s.nearby(en, minmiddledist) && (a == null || a.contains(en.getCoor())))
- map.put(en, s.w);
+ if (!middlenodes.contains(en))
+ continue;
+ map.put(en, s.w);
}
}
- if(map.size() > 0)
+ //System.out.println("p3 elapsed: " + (System.currentTimeMillis()-last));
+ last = System.currentTimeMillis();
+ for(Map.Entry<Node, Way> error : map.entrySet())
{
- for(Map.Entry<Node, Way> error : map.entrySet())
- {
- errors.add(new TestError(this, Severity.OTHER,
- tr("Way node near other way"), UNCONNECTED_WAYS,
- Arrays.asList(error.getKey(), error.getValue())));
- }
+ errors.add(new TestError(this, Severity.OTHER,
+ tr("Way node near other way"), UNCONNECTED_WAYS,
+ Arrays.asList(error.getKey(), error.getValue())));
}
map.clear();
- for(Node en : othernodes)
+ for(MyWaySegment s : ways)
{
- for(MyWaySegment s : ways)
+ for(Node en : s.nearbyNodes(minmiddledist))
{
- if(!s.isBoundary && !s.isAbandoned && s.nearby(en, minmiddledist) && (a == null || a.contains(en.getCoor())))
- map.put(en, s.w);
+ if (!othernodes.contains(en))
+ continue;
+ map.put(en, s.w);
}
}
- if(map.size() > 0)
+ //System.out.println("p4 elapsed: " + (System.currentTimeMillis()-last));
+ last = System.currentTimeMillis();
+ for(Map.Entry<Node, Way> error : map.entrySet())
{
- for(Map.Entry<Node, Way> error : map.entrySet())
- {
- errors.add(new TestError(this, Severity.OTHER,
- tr("Connected way end node near other way"), UNCONNECTED_WAYS,
- Arrays.asList(error.getKey(), error.getValue())));
- }
+ errors.add(new TestError(this, Severity.OTHER,
+ tr("Connected way end node near other way"), UNCONNECTED_WAYS,
+ Arrays.asList(error.getKey(), error.getValue())));
}
}
ways = null;
endnodes = null;
super.endTest();
+ //System.out.println("p99 elapsed: " + (System.currentTimeMillis()-last));
+ last = System.currentTimeMillis();
}
private class MyWaySegment
{
- private Line2D line;
- public Way w;
- public Boolean isAbandoned = false;
- public Boolean isBoundary = false;
- public Boolean highway;
+ private final Line2D line;
+ public final Way w;
+ public final boolean isAbandoned;
+ public final boolean isBoundary;
+ public final boolean highway;
+ private final double len;
+ private Set<Node> nearbyNodeCache;
+ double nearbyNodeCacheDist = -1.0;
+ final Node n1;
+ final Node n2;
public MyWaySegment(Way w, Node n1, Node n2)
{
this.w = w;
String railway = w.get("railway");
- this.isAbandoned = railway != null && railway.equals("abandoned");
- this.highway = w.get("highway") != null || (railway != null && !isAbandoned);
- this.isBoundary = w.get("boundary") != null && w.get("boundary").equals("administrative") && !this.highway;
+ String highway = w.get("highway");
+ this.isAbandoned = "abandoned".equals(railway) || "yes".equals(w.get("disused"));
+ this.highway = (highway != null || railway != null) && !isAbandoned;
+ this.isBoundary = !this.highway && "administrative".equals(w.get("boundary"));
line = new Line2D.Double(n1.getEastNorth().east(), n1.getEastNorth().north(),
- n2.getEastNorth().east(), n2.getEastNorth().north());
+ n2.getEastNorth().east(), n2.getEastNorth().north());
+ len = line.getP1().distance(line.getP2());
+ this.n1 = n1;
+ this.n2 = n2;
}
public boolean nearby(Node n, double dist)
{
- return !w.containsNode(n)
- && line.ptSegDist(n.getEastNorth().east(), n.getEastNorth().north()) < dist;
+// return !w.containsNode(n)
+// && line.ptSegDist(n.getEastNorth().east(), n.getEastNorth().north()) < dist;
+ if (w == null)
+ Main.debug("way null");
+ if (w.containsNode(n))
+ return false;
+ EastNorth coord = n.getEastNorth();
+ if (coord == null)
+ return false;
+ Point2D p = new Point2D.Double(coord.east(), coord.north());
+ if (line.getP1().distance(p) > len+dist)
+ return false;
+ if (line.getP2().distance(p) > len+dist)
+ return false;
+ return line.ptSegDist(p) < dist;
+ }
+ public List<LatLon> getBounds(double fudge)
+ {
+ double x1 = n1.getCoor().lon();
+ double x2 = n2.getCoor().lon();
+ if (x1 > x2) {
+ double tmpx = x1;
+ x1 = x2;
+ x2 = tmpx;
+ }
+ double y1 = n1.getCoor().lat();
+ double y2 = n2.getCoor().lat();
+ if (y1 > y2) {
+ double tmpy = y1;
+ y1 = y2;
+ y2 = tmpy;
+ }
+ LatLon topLeft = new LatLon(y2+fudge, x1-fudge);
+ LatLon botRight = new LatLon(y1-fudge, x2+fudge);
+ List<LatLon> ret = new ArrayList<LatLon>();
+ ret.add(topLeft);
+ ret.add(botRight);
+ return ret;
+ }
+
+ public Collection<Node> nearbyNodes(double dist)
+ {
+ // If you're looking for nodes that are farther
+ // away that we looked for last time, the cached
+ // result is no good
+ if (dist > nearbyNodeCacheDist) {
+ //if (nearbyNodeCacheDist != -1)
+ // System.out.println("destroyed MyWaySegment nearby node cache:" + dist + " > " + nearbyNodeCacheDist);
+ nearbyNodeCache = null;
+ }
+ if (nearbyNodeCache != null) {
+ // If we've cached an aread greater than the
+ // one now being asked for...
+ if (nearbyNodeCacheDist > dist) {
+ //System.out.println("had to trim MyWaySegment nearby node cache.");
+ // Used the cached result and trim out
+ // the nodes that are not in the smaller
+ // area, but keep the old larger cache.
+ Set<Node> trimmed = new HashSet<Node>(nearbyNodeCache);
+ for (Node n : new HashSet<Node>(nearbyNodeCache)) {
+ if (!nearby(n, dist))
+ trimmed.remove(n);
+ }
+ return trimmed;
+ }
+ return nearbyNodeCache;
+ }
+ /*
+ * We know that any point near the line must be at
+ * least as close as the other end of the line, plus
+ * a little fudge for the distance away ('dist').
+ */
+
+ // This needs to be a hash set because the searches
+ // overlap a bit and can return duplicate nodes.
+ nearbyNodeCache = null;
+ List<LatLon> bounds = this.getBounds(dist);
+ List<Node> found_nodes = ds.searchNodes(new BBox(bounds.get(0), bounds.get(1)));
+ if (found_nodes == null)
+ return Collections.emptySet();
+
+ for (Node n : found_nodes) {
+ if (!nearby(n, dist) ||
+ (ds_area != null && !ds_area.contains(n.getCoor())))
+ continue;
+ // It is actually very rare for us to find a node
+ // so defer as much of the work as possible, like
+ // allocating the hash set
+ if (nearbyNodeCache == null)
+ nearbyNodeCache = new HashSet<Node>();
+ nearbyNodeCache.add(n);
+ }
+ nearbyNodeCacheDist = dist;
+ if (nearbyNodeCache == null)
+ nearbyNodeCache = Collections.emptySet();
+ return nearbyNodeCache;
}
public boolean isArea() {
@@ -189,32 +328,49 @@ public class UnconnectedWays extends Test
}
}
- @Override
- public void visit(Way w)
+ List<MyWaySegment> getWaySegments(Way w)
{
- if( !w.isUsable() )
- return;
+ List<MyWaySegment> ret = new ArrayList<MyWaySegment>();
+ if (!w.isUsable()
+ || w.get("barrier") != null
+ || "cliff".equals(w.get("natural")))
+ return ret;
+
int size = w.getNodesCount();
if(size < 2)
- return;
+ return ret;
for(int i = 1; i < size; ++i)
{
if(i < size-1)
addNode(w.getNode(i), middlenodes);
- ways.add(new MyWaySegment(w, w.getNode(i-1), w.getNode(i)));
+ MyWaySegment ws = new MyWaySegment(w, w.getNode(i-1), w.getNode(i));
+ if (ws.isBoundary || ws.isAbandoned)
+ continue;
+ ret.add(ws);
}
+ return ret;
+ }
+
+ @Override
+ public void visit(Way w)
+ {
+ ways.addAll(getWaySegments(w));
Set<Node> set = endnodes;
if(w.get("highway") != null || w.get("railway") != null)
set = endnodes_highway;
- addNode(w.getNode(0), set);
- addNode(w.getNode(size-1), set);
+ addNode(w.firstNode(), set);
+ addNode(w.lastNode(), set);
+ }
+ @Override
+ public void visit(Node n)
+ {
}
private void addNode(Node n, Set<Node> s)
{
- Boolean m = middlenodes.contains(n);
- Boolean e = endnodes.contains(n);
- Boolean eh = endnodes_highway.contains(n);
- Boolean o = othernodes.contains(n);
+ boolean m = middlenodes.contains(n);
+ boolean e = endnodes.contains(n);
+ boolean eh = endnodes_highway.contains(n);
+ boolean o = othernodes.contains(n);
if(!m && !e && !o && !eh)
s.add(n);
else if(!o)
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java
index b9fb80e..6c6e09e 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java
@@ -57,7 +57,7 @@ public class UntaggedNode extends Test
p.visit(this);
}
}
- for (Way w : Main.main.getCurrentDataSet().ways) {
+ for (Way w : Main.main.getCurrentDataSet().getWays()) {
visit(w);
}
} else {
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java
index 29183eb..0f75208 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedWay.java
@@ -89,7 +89,7 @@ public class UntaggedWay extends Test
if( !hasName && !isRoundabout)
errors.add( new TestError(this, Severity.WARNING, tr("Unnamed ways"), UNNAMED_WAY, w) );
- else if(isRoundabout)
+ else if(isRoundabout)
errors.add( new TestError(this, Severity.WARNING, tr("Unnamed junction"), UNNAMED_JUNCTION, w) );
}
}
@@ -114,9 +114,9 @@ public class UntaggedWay extends Test
@Override
public void startTest(ProgressMonitor monitor)
{
- super.startTest(monitor);
+ super.startTest(monitor);
multipolygonways = new LinkedList<Way>();
- for (final Relation r : Main.main.getCurrentDataSet().relations)
+ for (Relation r : Main.main.getCurrentDataSet().getRelations())
{
if(r.isUsable() && "multipolygon".equals(r.get("type")))
{
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java b/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java
index 1ab0996..4614099 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/tests/WronglyOrderedWays.java
@@ -57,6 +57,8 @@ public class WronglyOrderedWays extends Test {
if( !w.isUsable() )
return;
+ if (w.getNodesCount() <= 0)
+ return;
String natural = w.get("natural");
if( natural == null)
diff --git a/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java b/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java
index 435644e..60733ea 100644
--- a/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java
+++ b/validator/src/org/openstreetmap/josm/plugins/validator/util/NameVisitor.java
@@ -13,6 +13,7 @@ import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
import org.openstreetmap.josm.tools.ImageProvider;
/**
@@ -42,7 +43,7 @@ public class NameVisitor extends AbstractVisitor {
* is displayed.
*/
public void visit(Node n) {
- name = n.getName();
+ name = n.getDisplayName(DefaultNameFormatter.getInstance());
addId(n);
icon = ImageProvider.get("data", "node");
className = "node";
@@ -54,7 +55,7 @@ public class NameVisitor extends AbstractVisitor {
* is displayed with x being the number of nodes in the way.
*/
public void visit(Way w) {
- name = w.getName();
+ name = w.getDisplayName(DefaultNameFormatter.getInstance());
addId(w);
icon = ImageProvider.get("data", "way");
className = "way";
@@ -64,7 +65,7 @@ public class NameVisitor extends AbstractVisitor {
/**
*/
public void visit(Relation e) {
- name = e.getName();
+ name = e.getDisplayName(DefaultNameFormatter.getInstance());
addId(e);
icon = ImageProvider.get("data", "relation");
className = "relation";
diff --git a/validator/tagchecker.cfg b/validator/tagchecker.cfg
index 1438909..e16209d 100644
--- a/validator/tagchecker.cfg
+++ b/validator/tagchecker.cfg
@@ -74,6 +74,6 @@ relation : E : type != * # relation withou
node : I : amenity == /restaurant|cafe|fast_food/ && name != * # restaurant without name
#way : I : highway != * && railway != * && waterway != * && name == * # unusual named way type
-* : W : natural == water && waterway == * # unusual tag combination
+#* : W : natural == water && waterway == * # unusual tag combination
* : W : highway == * && waterway == * # unusual tag combination
* : W : highway == * && natural == * # unusual tag combination
diff --git a/wmsplugin/.classpath b/wmsplugin/.classpath
index 2f5994b..663bc1b 100644
--- a/wmsplugin/.classpath
+++ b/wmsplugin/.classpath
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="lib" path="C:/prj.ht/josm-snapshot-467.jar"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="output" path="build"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+ <classpathentry kind="output" path="build"/>
+</classpath>
diff --git a/wmsplugin/build.xml b/wmsplugin/build.xml
index 099377d..67a3121 100644
--- a/wmsplugin/build.xml
+++ b/wmsplugin/build.xml
@@ -17,22 +17,26 @@
**
** To build against the core in ../../core, create a correct manifest and deploy to
** SVN, run
-** - set the property commit.message
-** - set the property josm.reference.release to lowest JOSM release number this
+** - set the property commit.message
+** - set the property plugin.main.version to lowest JOSM release number this
** plugin build is compatible with
** > ant deploy
**
**
-->
<project name="wmsplugin" default="dist" basedir=".">
- <property name="josm" location="../../core/dist/josm-custom.jar"/>
+
+
+ <property name="commit.message" value="fixed JOSM issue #3940" />
+ <property name="plugin.main.version" value="2450" />
+
+
+ <property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
<property name="plugin.build.dir" value="build"/>
<property name="plugin.jar" value="${plugin.dist.dir}/${ant.project.name}.jar"/>
<property name="ant.build.javac.target" value="1.5"/>
- <property name="commit.message" value="fixing JOSM issue #3186" />
- <property name="josm.reference.release" value="2196" />
-
+
<target name="init">
<mkdir dir="${plugin.build.dir}"/>
</target>
@@ -55,7 +59,7 @@
<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
<attribute name="Plugin-Description" value="Display georeferenced images as background in JOSM (WMS servers, Yahoo, ...)."/>
<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/WMSPlugin"/>
- <attribute name="Plugin-Mainversion" value="${josm.reference.release}"/>
+ <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
<attribute name="de_Plugin-Link" value="http://wiki.openstreetmap.org/wiki/DE:JOSM/Plugins/WMSPlugin"/>
</manifest>
@@ -84,56 +88,81 @@
</condition>
<copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
</target>
-
- <target name="core-info">
- <exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
- <env key="LANG" value="C"/>
- <arg value="info"/>
- <arg value="--xml"/>
- <arg value="../../core"/>
- </exec>
- <xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
- <echo>Building against core revision ${coreversion.info.entry.revision} ...</echo>
- <delete file="core.info.xml" />
- </target>
-
- <target name="commit-current">
- <echo>Commiting the plugin source ...</echo>
- <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <!--
+ ************************** Publishing the plugin ***********************************
+ -->
+ <!--
+ ** extracts the JOSM release for the JOSM version in ../core and saves it in the
+ ** property ${coreversion.info.entry.revision}
+ **
+ -->
+ <target name="core-info">
+ <exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
<env key="LANG" value="C"/>
- <arg value="commit"/>
- <arg value="-m "${commit.message}""/>
- <arg value="."/>
- </exec>
- </target>
+ <arg value="info"/>
+ <arg value="--xml"/>
+ <arg value="../../core"/>
+ </exec>
+ <xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
+ <echo>Building against core revision ${coreversion.info.entry.revision}.</echo>
+ <echo>Plugin-Mainversion is set to ${plugin.main.version}.</echo>
+ <delete file="core.info.xml" />
+ </target>
-
- <target name="update-current">
- <echo>Updating basedir ...</echo>
- <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
- <env key="LANG" value="C"/>
- <arg value="up"/>
- <arg value="."/>
- </exec>
- <echo>Updating ${plugin.jar} ...</echo>
- <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
- <env key="LANG" value="C"/>
- <arg value="up"/>
- <arg value="${plugin.jar}"/>
- </exec>
- </target>
-
- <target name="commit-dist">
- <echo>Commiting ${plugin.jar} ...</echo>
+ <!--
+ ** commits the source tree for this plugin
+ -->
+ <target name="commit-current">
+ <echo>Commiting the plugin source with message '${commit.message}' ...</echo>
<exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
<env key="LANG" value="C"/>
<arg value="commit"/>
- <arg value="-m "${commit.message}""/>
- <arg value="${plugin.jar}"/>
+ <arg value="-m '${commit.message}'"/>
+ <arg value="."/>
+ </exec>
+ </target>
+
+ <!--
+ ** updates (svn up) the source tree for this plugin
+ -->
+ <target name="update-current">
+ <echo>Updating plugin source ...</echo>
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="up"/>
+ <arg value="."/>
+ </exec>
+ <echo>Updating ${plugin.jar} ...</echo>
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="up"/>
+ <arg value="../dist/${plugin.jar}"/>
</exec>
- </target>
-
- <target name="deploy" depends="core-info,commit-current,update-current,clean,dist,commit-dist">
- </target>
+ </target>
+
+ <!--
+ ** commits the plugin.jar
+ -->
+ <target name="commit-dist">
+ <echo>
+ ***** Properties of published ${plugin.jar} *****
+ Commit message : '${commit.message}'
+ Plugin-Mainversion: ${plugin.main.version}
+ JOSM build version: ${coreversion.info.entry.revision}
+ Plugin-Version : ${version.entry.commit.revision}
+ ***** / Properties of published ${plugin.jar} *****
+
+ Now commiting ${plugin.jar} ...
+ </echo>
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="-m '${commit.message}'"/>
+ <arg value="commit"/>
+ <arg value="${plugin.jar}"/>
+ </exec>
+ </target>
+
+ <target name="publish" depends="core-info,commit-current,update-current,clean,dist,commit-dist">
+ </target>
</project>
diff --git a/wmsplugin/sources.cfg b/wmsplugin/sources.cfg
index 5dd428b..a0c11e3 100644
--- a/wmsplugin/sources.cfg
+++ b/wmsplugin/sources.cfg
@@ -4,9 +4,6 @@
#
true;Landsat;http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&layers=global_mosaic&styles=&format=image/jpeg&
true;Open Aerial Map;http://openaerialmap.org/wms/?VERSION=1.0&request=GetMap&layers=world&styles=&format=image/jpeg&
-# fails with division by zero error
-false;NPE Maps;http://nick.dev.openstreetmap.org/openpaths/freemap.php?layers=npe&
-false;NPE Maps (Tim);http://dev.openstreetmap.org/~timsc/wms2/map.php?
#
# different forms for web access
# must be html:<url>
@@ -18,14 +15,38 @@ false;TilesAtHome;html:http://josm.openstreetmap.de/wmsplugin/TilesAtHome.html?
#
# only for Germany
false;Streets NRW Geofabrik.de;http://tools.geofabrik.de/osmi/view/strassennrw/josmwms?
+#
+#
+# only for North America
# Terraserver USCG - High resolution maps
false;Terraserver Topo;http://terraservice.net/ogcmap.ashx?version=1.1.1&request=GetMap&Layers=drg&styles=&format=image/jpeg&
false;Terraserver Urban;http://terraservice.net/ogcmap.ashx?version=1.1.1&request=GetMap&Layers=urbanarea&styles=&format=image/jpeg&
+#
+#
# only for Czech Republic
false;Czech CUZK:KM;http://wms.cuzk.cz/wms.asp?service=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=kn,def_budovy,prehledky&FORMAT=image/png&TRANSPARENT=TRUE&
false;Czech UHUL:ORTOFOTO;http://geoportal2.uhul.cz/cgi-bin/oprl.asp?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=Ortofoto_cb&STYLES=default&FORMAT=image/jpeg&TRANSPARENT=TRUE&
#
#
+# only for GB
+# fails with division by zero error
+false;NPE Maps;http://nick.dev.openstreetmap.org/openpaths/freemap.php?layers=npe&
+false;NPE Maps (Tim);http://dev.openstreetmap.org/~timsc/wms2/map.php?
+false;7th Series (OS7);http://ooc.openstreetmap.org/wms/map.php?source=os7&
+#
+#
+# only for Japan
+false;MLIT Japan (ORTHO);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+false;MLIT Japan (ORTHO01);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO01&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+false;MLIT Japan (ORTHO02);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO02&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+false;MLIT Japan (ORTHO03);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRasterTileMap?VERSION=1.3.0&REQUEST=GetMap&LAYERS=ORTHO03&STYLES=Default&CRS=EPSG:4612&BBOX={s},{w},{n},{e}&WIDTH={width}&HEIGHT={height}&FORMAT=image/png&BGCOLOR=OxFFFFFF
+#
+#
+# only for Italy
+false;Lodi - Italy;http://sit.provincia.lodi.it/mapserver/mapserv.exe?map=ortofoto_wgs84.map&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=Terraitaly%20Ortofoto%202007&STYLES=%2C%2C&FORMAT=image/png&TRANSPARENT=TRUE&
+false;Sicily - Italy;http://88.53.214.52/arcgis/services/OrtofotoATA_20072008_f33/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=CRS:84&LAYERS=0&STYLES=default&FORMAT=image/jpeg&
+#
+#
# URLS must be designed to append arguments directly behind. So the URLS should either end with '?' or '&'
# Following arguments are added: width, height, bbox, srs (projection method)
# srs is only added when no srs is given already (In this case the projection is checked
@@ -37,3 +58,4 @@ false;Czech UHUL:ORTOFOTO;http://geoportal2.uhul.cz/cgi-bin/oprl.asp?SERVICE=WMS
# {bbox} is replaced by bounding box using projected coordinates
# {width} is requested display width
# {height} is requested display height
+# {w},{s},{n},{e} are replaced by corresponding coordinates
diff --git a/wmsplugin/src/wmsplugin/GeorefImage.java b/wmsplugin/src/wmsplugin/GeorefImage.java
index 2e6f87b..3f62193 100644
--- a/wmsplugin/src/wmsplugin/GeorefImage.java
+++ b/wmsplugin/src/wmsplugin/GeorefImage.java
@@ -136,10 +136,10 @@ public class GeorefImage implements Serializable {
min = (EastNorth) in.readObject();
boolean hasImage = in.readBoolean();
if (hasImage)
- image = (BufferedImage) ImageIO.read(ImageIO.createImageInputStream(in));
+ image = (BufferedImage) ImageIO.read(ImageIO.createImageInputStream(in));
else {
- in.readObject(); // read null from input stream
- image = null;
+ in.readObject(); // read null from input stream
+ image = null;
}
}
@@ -147,10 +147,10 @@ public class GeorefImage implements Serializable {
out.writeObject(max);
out.writeObject(min);
if(image == null) {
- out.writeBoolean(false);
+ out.writeBoolean(false);
out.writeObject(null);
} else {
- out.writeBoolean(true);
+ out.writeBoolean(true);
ImageIO.write(image, "png", ImageIO.createImageOutputStream(out));
}
}
diff --git a/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java b/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java
deleted file mode 100644
index f02f4fc..0000000
--- a/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package wmsplugin;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.event.ActionEvent;
-import javax.swing.*;
-import org.openstreetmap.josm.actions.JosmAction;
-
-
-
-public class Help_WMSmenuAction extends JosmAction {
-
- /**
- *
- */
-
-
- public Help_WMSmenuAction() {
- //super("Help / About");
- super(tr("help"), "help", tr("Help / About"), null, false);
-
- }
-
- public void actionPerformed(ActionEvent e) {
- //todo - put this into a txt file?
- String helptext =
- tr("You can add, edit and delete WMS entries in the WMSplugin Preference Tab - " +
- "these will then show up in the WMS menu.\n\n"+
-
- "You can also do this manually in the Advanced Preferences, using the following schema:\n"+
- "wmsplugin.url.1.name=Landsat\n"+
- "wmsplugin.url.1.url=http://onearth.jpl.nasa.gov....\n"+
- "wmsplugin.url.2.name=NPE Maps... etc\n\n"+
-
- "Full WMS URL input format example (landsat)\n"+
- "http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&\n"+
- "layers=global_mosaic&styles=&srs=EPSG:4326&format=image/jpeg\n\n"+
-
- "For Metacarta's Map Rectifier http://labs.metacarta.com/rectifier/ , you only need to input the relevant 'id'.\n" +
- "To add a Metacarta Map Rectifier menu item, manually create the URL like in this example, " +
- "replacing 73 with your image id:\n" +
- "http://labs.metacarta.com/rectifier/wms.cgi?id=73\n" +
- "&srs=EPSG:4326&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png\n\n" +
-
- "Note: Make sure the image is suitable, copyright-wise, if in doubt, don't use.");
-
- JTextPane tp = new JTextPane();
- JScrollPane js = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
- JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
-
-
- js.getViewport().add(tp);
- JFrame jf = new JFrame(tr("WMS Plugin Help"));
- jf.getContentPane().add(js);
- jf.pack();
- jf.setSize(400,500);
- jf.setVisible(true);
- tp.setText(helptext);
- }
-}
diff --git a/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java b/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
index b588ac9..26e99d2 100644
--- a/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
+++ b/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
@@ -154,7 +154,7 @@ public class Map_Rectifier_WMSmenuAction extends JosmAction {
// This repeatedly shows the dialog in case there has been an error.
// The loop is break;-ed if the users cancels
outer: while(true) {
- diag.showDialog();
+ diag.showDialog();
int answer = diag.getValue();
// Break loop when the user cancels
if(answer != 1)
diff --git a/wmsplugin/src/wmsplugin/WMSAdjustAction.java b/wmsplugin/src/wmsplugin/WMSAdjustAction.java
index 03b2a10..369041c 100644
--- a/wmsplugin/src/wmsplugin/WMSAdjustAction.java
+++ b/wmsplugin/src/wmsplugin/WMSAdjustAction.java
@@ -9,6 +9,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.List;
+import java.util.logging.Logger;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
@@ -29,9 +30,9 @@ import org.openstreetmap.josm.tools.GBC;
import org.openstreetmap.josm.tools.ImageProvider;
-public class WMSAdjustAction extends MapMode implements
- MouseListener, MouseMotionListener{
-
+public class WMSAdjustAction extends MapMode implements MouseListener, MouseMotionListener{
+ static private final Logger logger = Logger.getLogger(WMSAdjustAction.class.getName());
+
GeorefImage selectedImage;
boolean mouseDown;
EastNorth prevEastNorth;
@@ -48,19 +49,19 @@ public class WMSAdjustAction extends MapMode implements
@Override public void enterMode() {
super.enterMode();
if (!hasWMSLayersToAdjust()) {
- warnNoWMSLayers();
- return;
+ warnNoWMSLayers();
+ return;
}
List<WMSLayer> wmsLayers = Main.map.mapView.getLayersOfType(WMSLayer.class);
if (wmsLayers.size() == 1) {
- adjustingLayer = wmsLayers.get(0);
+ adjustingLayer = wmsLayers.get(0);
} else {
- adjustingLayer = (WMSLayer)askAdjustLayer(Main.map.mapView.getLayersOfType(WMSLayer.class));
+ adjustingLayer = (WMSLayer)askAdjustLayer(Main.map.mapView.getLayersOfType(WMSLayer.class));
}
if (adjustingLayer == null)
- return;
+ return;
if (!adjustingLayer.isVisible()) {
- adjustingLayer.setVisible(true);
+ adjustingLayer.setVisible(true);
}
Main.map.mapView.addMouseListener(this);
Main.map.mapView.addMouseMotionListener(this);
@@ -173,10 +174,10 @@ public class WMSAdjustAction extends MapMode implements
pnl.add(layerList, GBC.eol());
ExtendedDialog diag = new ExtendedDialog(
- Main.parent,
- tr("Select WMS layer"),
- new String[] { tr("Start adjusting"),tr("Cancel") }
- );
+ Main.parent,
+ tr("Select WMS layer"),
+ new String[] { tr("Start adjusting"),tr("Cancel") }
+ );
diag.setContent(pnl);
diag.setButtonIcons(new String[] { "mapmode/adjustwms", "cancel" });
diag.showDialog();
@@ -193,7 +194,7 @@ public class WMSAdjustAction extends MapMode implements
*/
protected void warnNoWMSLayers() {
JOptionPane.showMessageDialog(
- Main.parent,
+ Main.parent,
tr("There are currently no WMS layer to adjust."),
tr("No layers to adjust"),
JOptionPane.WARNING_MESSAGE
@@ -206,15 +207,13 @@ public class WMSAdjustAction extends MapMode implements
* @return true if there is at least one WMS layer
*/
protected boolean hasWMSLayersToAdjust() {
- if (Main.map == null) return false;
- if (Main.map.mapView == null) return false;
- return ! Main.map.mapView.getLayersOfType(WMSLayer.class).isEmpty();
+ if (Main.map == null) return false;
+ if (Main.map.mapView == null) return false;
+ return ! Main.map.mapView.getLayersOfType(WMSLayer.class).isEmpty();
}
-
-
- @Override
- protected void updateEnabledState() {
- setEnabled(hasWMSLayersToAdjust());
- }
+ @Override
+ protected void updateEnabledState() {
+ setEnabled(hasWMSLayersToAdjust());
+ }
}
diff --git a/wmsplugin/src/wmsplugin/WMSGrabber.java b/wmsplugin/src/wmsplugin/WMSGrabber.java
index eecb3c9..e157667 100644
--- a/wmsplugin/src/wmsplugin/WMSGrabber.java
+++ b/wmsplugin/src/wmsplugin/WMSGrabber.java
@@ -33,10 +33,10 @@ import org.openstreetmap.josm.io.ProgressInputStream;
public class WMSGrabber extends Grabber {
- public static boolean isUrlWithPatterns(String url) {
- return url != null && url.contains("{") && url.contains("}");
- }
-
+ public static boolean isUrlWithPatterns(String url) {
+ return url != null && url.contains("{") && url.contains("}");
+ }
+
protected String baseURL;
private final boolean urlWithPatterns;
@@ -70,7 +70,7 @@ public class WMSGrabber extends Grabber {
}
image.downloadingStarted = false;
} catch(Exception e) {
- e.printStackTrace();
+ e.printStackTrace();
throw new Exception(e.getMessage() + "\nImage couldn't be fetched: " + (url != null ? url.toString() : ""));
}
}
@@ -101,17 +101,21 @@ public class WMSGrabber extends Grabber {
if (urlWithPatterns) {
str = str.replaceAll("\\{proj\\}", myProj)
.replaceAll("\\{bbox\\}", bbox)
+ .replaceAll("\\{w\\}", latLonFormat.format(w))
+ .replaceAll("\\{s\\}", latLonFormat.format(s))
+ .replaceAll("\\{e\\}", latLonFormat.format(e))
+ .replaceAll("\\{n\\}", latLonFormat.format(n))
.replaceAll("\\{width\\}", String.valueOf(wi))
.replaceAll("\\{height\\}", String.valueOf(ht));
} else {
str += "bbox=" + bbox
+ getProjection(baseURL, false)
+ "&width=" + wi + "&height=" + ht;
- if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
- System.out.println(tr("Warning: The base URL ''{0}'' for a WMS service doesn't have a trailing '&' or a trailing '?'.", baseURL));
- System.out.println(tr("Warning: Fetching WMS tiles is likely to fail. Please check you preference settings."));
- System.out.println(tr("Warning: The complete URL is ''{0}''.", str));
- }
+ if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
+ System.out.println(tr("Warning: The base URL ''{0}'' for a WMS service doesn't have a trailing '&' or a trailing '?'.", baseURL));
+ System.out.println(tr("Warning: Fetching WMS tiles is likely to fail. Please check you preference settings."));
+ System.out.println(tr("Warning: The complete URL is ''{0}''.", str));
+ }
}
return new URL(str.replace(" ", "%20"));
}
diff --git a/wmsplugin/src/wmsplugin/WMSLayer.java b/wmsplugin/src/wmsplugin/WMSLayer.java
index f514def..e9b8aff 100644
--- a/wmsplugin/src/wmsplugin/WMSLayer.java
+++ b/wmsplugin/src/wmsplugin/WMSLayer.java
@@ -4,6 +4,7 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Component;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.io.File;
@@ -26,6 +27,7 @@ import javax.swing.JSeparator;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.DiskAccessAction;
import org.openstreetmap.josm.actions.SaveActionBase;
+import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.ProjectionBounds;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
@@ -38,391 +40,427 @@ import org.openstreetmap.josm.tools.ImageProvider;
/**
* This is a layer that grabs the current screen from an WMS server. The data
- * fetched this way is tiled and managerd to the disc to reduce server load.
+ * fetched this way is tiled and managed to the disc to reduce server load.
*/
public class WMSLayer extends Layer {
- protected static final Icon icon =
- new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png")));
-
- public int messageNum = 5; //limit for messages per layer
- protected MapView mv;
- protected String resolution;
- protected boolean stopAfterPaint = false;
- protected int ImageSize = 500;
- protected int dax = 10;
- protected int day = 10;
- protected int minZoom = 3;
- protected double dx = 0.0;
- protected double dy = 0.0;
- protected double pixelPerDegree;
- protected GeorefImage[][] images = new GeorefImage[dax][day];
- JCheckBoxMenuItem startstop = new JCheckBoxMenuItem(tr("Automatic downloading"), true);
- protected JCheckBoxMenuItem alphaChannel = new JCheckBoxMenuItem(new ToggleAlphaAction());
- protected String baseURL;
- protected String cookies;
- protected final int serializeFormatVersion = 5;
-
- private ExecutorService executor = null;
-
- /** set to true if this layer uses an invalid base url */
- private boolean usesInvalidUrl = false;
- /** set to true if the user confirmed to use an potentially invalid WMS base url */
- private boolean isInvalidUrlConfirmed = false;
-
- public WMSLayer() {
- this(tr("Blank Layer"), null, null);
- initializeImages();
- mv = Main.map.mapView;
- }
-
- public WMSLayer(String name, String baseURL, String cookies) {
- super(name);
- alphaChannel.setSelected(Main.pref.getBoolean("wmsplugin.alpha_channel"));
- setBackgroundLayer(true); /* set global background variable */
- initializeImages();
- this.baseURL = baseURL;
- this.cookies = cookies;
- WMSGrabber.getProjection(baseURL, true);
- mv = Main.map.mapView;
- resolution = mv.getDist100PixelText();
- pixelPerDegree = getPPD();
-
- executor = Executors.newFixedThreadPool(3);
- if (!baseURL.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(baseURL)) {
- if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
- if (!confirmMalformedUrl(baseURL)) {
- System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", baseURL));
- usesInvalidUrl = true;
- setName(getName() + tr("(deactivated)"));
- return;
- } else {
- isInvalidUrlConfirmed = true;
- }
- }
- }
- }
-
- public double getDx(){
- return dx;
- }
-
- public double getDy(){
- return dy;
- }
-
- @Override
- public void destroy() {
- try {
- executor.shutdownNow();
- // Might not be initalized, so catch NullPointer as well
- } catch(Exception x) {
- x.printStackTrace();
- }
- }
-
- public double getPPD(){
- ProjectionBounds bounds = mv.getProjectionBounds();
- return mv.getWidth() / (bounds.max.east() - bounds.min.east());
- }
-
- public void initializeImages() {
- images = new GeorefImage[dax][day];
- for(int x = 0; x<dax; ++x) {
- for(int y = 0; y<day; ++y) {
- images[x][y]= new GeorefImage(false);
- }
- }
- }
-
- @Override public Icon getIcon() {
- return icon;
- }
-
- @Override public String getToolTipText() {
- if(startstop.isSelected())
- return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution);
- else
- return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution);
- }
-
- @Override public boolean isMergable(Layer other) {
- return false;
- }
-
- @Override public void mergeFrom(Layer from) {
- }
-
- private ProjectionBounds XYtoBounds (int x, int y) {
- return new ProjectionBounds(
- new EastNorth( x * ImageSize / pixelPerDegree, y * ImageSize / pixelPerDegree),
- new EastNorth((x + 1) * ImageSize / pixelPerDegree, (y + 1) * ImageSize / pixelPerDegree));
- }
-
- private int modulo (int a, int b) {
- return a % b >= 0 ? a%b : a%b+b;
- }
-
- @Override public void paint(Graphics g, final MapView mv) {
- if(baseURL == null) return;
- if (usesInvalidUrl && !isInvalidUrlConfirmed) return;
-
- if( !startstop.isSelected() || (pixelPerDegree / getPPD() > minZoom) ){ //don't download when it's too outzoomed
- for(int x = 0; x<dax; ++x) {
- for(int y = 0; y<day; ++y) {
- images[modulo(x,dax)][modulo(y,day)].paint(g, mv, dx, dy);
- }
- }
- } else {
- downloadAndPaintVisible(g, mv);
- }
- }
-
- public void displace(double dx, double dy) {
- this.dx += dx;
- this.dy += dy;
- }
-
- protected boolean confirmMalformedUrl(String url) {
- if (isInvalidUrlConfirmed)
- return true;
- String msg = tr("<html>The base URL<br>"
- + "''{0}''<br>"
- + "for this WMS layer does neither end with a ''&'' nor with a ''?''.<br>"
- + "This is likely to lead to invalid WMS request. You should check your<br>"
- + "preference settings.<br>"
- + "Do you want to fetch WMS tiles anyway?",
- url);
- String [] options = new String[] {
- tr("Yes, fetch images"),
- tr("No, abort")
- };
- int ret = JOptionPane.showOptionDialog(
- Main.parent,
- msg,
- tr("Invalid URL?"),
- JOptionPane.YES_NO_OPTION,
- JOptionPane.WARNING_MESSAGE,
- null,
- options, options[1]
- );
- switch(ret) {
- case JOptionPane.YES_OPTION: return true;
- default: return false;
- }
- }
- protected void downloadAndPaintVisible(Graphics g, final MapView mv){
- if (usesInvalidUrl)
- return;
- ProjectionBounds bounds = mv.getProjectionBounds();
- int bminx= (int)Math.floor (((bounds.min.east() - dx) * pixelPerDegree) / ImageSize );
- int bminy= (int)Math.floor (((bounds.min.north() - dy) * pixelPerDegree) / ImageSize );
- int bmaxx= (int)Math.ceil (((bounds.max.east() - dx) * pixelPerDegree) / ImageSize );
- int bmaxy= (int)Math.ceil (((bounds.max.north() - dy) * pixelPerDegree) / ImageSize );
-
- if((bmaxx - bminx > dax) || (bmaxy - bminy > day)){
- JOptionPane.showMessageDialog(
- Main.parent,
- tr("The requested area is too big. Please zoom in a little, or change resolution"),
- tr("Error"),
- JOptionPane.ERROR_MESSAGE
- );
- return;
- }
-
- for(int x = bminx; x<bmaxx; ++x) {
- for(int y = bminy; y<bmaxy; ++y){
- GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
- g.drawRect(x, y, dax, bminy);
- if(!img.paint(g, mv, dx, dy) && !img.downloadingStarted){
- img.downloadingStarted = true;
- img.image = null;
- img.flushedResizedCachedInstance();
- Grabber gr = WMSPlugin.getGrabber(XYtoBounds(x,y), img, mv, this);
- gr.setPriority(1);
- executor.submit(gr);
- }
- }
- }
- }
-
- @Override public void visitBoundingBox(BoundingXYVisitor v) {
- for(int x = 0; x<dax; ++x) {
- for(int y = 0; y<day; ++y)
- if(images[x][y].image!=null){
- v.visit(images[x][y].min);
- v.visit(images[x][y].max);
- }
- }
- }
-
- @Override public Object getInfoComponent() {
- return getToolTipText();
- }
-
- @Override public Component[] getMenuEntries() {
- return new Component[]{
- new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
- new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
- new JSeparator(),
- new JMenuItem(new LoadWmsAction()),
- new JMenuItem(new SaveWmsAction()),
- new JSeparator(),
- startstop,
- alphaChannel,
- new JMenuItem(new ChangeResolutionAction()),
- new JMenuItem(new ReloadErrorTilesAction()),
- new JMenuItem(new DownloadAction()),
- new JSeparator(),
- new JMenuItem(new LayerListPopup.InfoAction(this))
- };
- }
-
- public GeorefImage findImage(EastNorth eastNorth) {
- for(int x = 0; x<dax; ++x) {
- for(int y = 0; y<day; ++y)
- if(images[x][y].image!=null && images[x][y].min!=null && images[x][y].max!=null)
- if(images[x][y].contains(eastNorth, dx, dy))
- return images[x][y];
- }
- return null;
- }
-
- public class DownloadAction extends AbstractAction {
- public DownloadAction() {
- super(tr("Download visible tiles"));
- }
- public void actionPerformed(ActionEvent ev) {
- downloadAndPaintVisible(mv.getGraphics(), mv);
- }
- }
-
- public class ChangeResolutionAction extends AbstractAction {
- public ChangeResolutionAction() {
- super(tr("Change resolution"));
- }
- public void actionPerformed(ActionEvent ev) {
- initializeImages();
- resolution = mv.getDist100PixelText();
- pixelPerDegree = getPPD();
- mv.repaint();
- }
- }
-
- public class ReloadErrorTilesAction extends AbstractAction {
- public ReloadErrorTilesAction() {
- super(tr("Reload erroneous tiles"));
- }
- public void actionPerformed(ActionEvent ev) {
- // Delete small files, because they're probably blank tiles.
- // See https://josm.openstreetmap.de/ticket/2307
- WMSPlugin.cache.customCleanUp(CacheFiles.CLEAN_SMALL_FILES, 2048);
-
- for (int x = 0; x < dax; ++x) {
- for (int y = 0; y < day; ++y) {
- GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
- if(img.failed){
- img.image = null;
- img.flushedResizedCachedInstance();
- img.downloadingStarted = false;
- img.failed = false;
- mv.repaint();
- }
- }
- }
- }
- }
-
- public class ToggleAlphaAction extends AbstractAction {
- public ToggleAlphaAction() {
- super(tr("Alpha channel"));
- }
- public void actionPerformed(ActionEvent ev) {
- JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource();
- boolean alphaChannel = checkbox.isSelected();
- Main.pref.put("wmsplugin.alpha_channel", alphaChannel);
-
- // clear all resized cached instances and repaint the layer
- for (int x = 0; x < dax; ++x) {
- for (int y = 0; y < day; ++y) {
- GeorefImage img = images[modulo(x, dax)][modulo(y, day)];
- img.flushedResizedCachedInstance();
- }
- }
- mv.repaint();
- }
- }
-
- public class SaveWmsAction extends AbstractAction {
- public SaveWmsAction() {
- super(tr("Save WMS layer to file"), ImageProvider.get("save"));
- }
- public void actionPerformed(ActionEvent ev) {
- File f = SaveActionBase.createAndOpenSaveFileChooser(
- tr("Save WMS layer"), ".wms");
- try {
- if (f != null) {
- ObjectOutputStream oos = new ObjectOutputStream(
- new FileOutputStream(f)
- );
- oos.writeInt(serializeFormatVersion);
- oos.writeInt(dax);
- oos.writeInt(day);
- oos.writeInt(ImageSize);
- oos.writeDouble(pixelPerDegree);
- oos.writeObject(getName());
- oos.writeObject(baseURL);
- oos.writeObject(images);
- oos.close();
- }
- } catch (Exception ex) {
- ex.printStackTrace(System.out);
- }
- }
- }
-
- public class LoadWmsAction extends AbstractAction {
- public LoadWmsAction() {
- super(tr("Load WMS layer from file"), ImageProvider.get("load"));
- }
- public void actionPerformed(ActionEvent ev) {
- JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true,
- false, tr("Load WMS layer"), "wms");
- if(fc == null) return;
- File f = fc.getSelectedFile();
- if (f == null) return;
- try
- {
- FileInputStream fis = new FileInputStream(f);
- ObjectInputStream ois = new ObjectInputStream(fis);
- int sfv = ois.readInt();
- if (sfv != serializeFormatVersion) {
- JOptionPane.showMessageDialog(Main.parent,
- tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion),
- tr("File Format Error"),
- JOptionPane.ERROR_MESSAGE);
- return;
- }
- startstop.setSelected(false);
- dax = ois.readInt();
- day = ois.readInt();
- ImageSize = ois.readInt();
- pixelPerDegree = ois.readDouble();
- setName((String)ois.readObject());
- baseURL = (String) ois.readObject();
- images = (GeorefImage[][])ois.readObject();
- ois.close();
- fis.close();
- mv.repaint();
- }
- catch (Exception ex) {
- // FIXME be more specific
- ex.printStackTrace(System.out);
- JOptionPane.showMessageDialog(Main.parent,
- tr("Error loading file"),
- tr("Error"),
- JOptionPane.ERROR_MESSAGE);
- return;
- }
- }
- }
+ protected static final Icon icon =
+ new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png")));
+
+ public int messageNum = 5; //limit for messages per layer
+ protected MapView mv;
+ protected String resolution;
+ protected boolean stopAfterPaint = false;
+ protected int ImageSize = 500;
+ protected int dax = 10;
+ protected int day = 10;
+ protected int minZoom = 3;
+ protected double dx = 0.0;
+ protected double dy = 0.0;
+ protected double pixelPerDegree;
+ protected GeorefImage[][] images = new GeorefImage[dax][day];
+ JCheckBoxMenuItem startstop = new JCheckBoxMenuItem(tr("Automatic downloading"), true);
+ protected JCheckBoxMenuItem alphaChannel = new JCheckBoxMenuItem(new ToggleAlphaAction());
+ protected String baseURL;
+ protected String cookies;
+ protected final int serializeFormatVersion = 5;
+
+ private ExecutorService executor = null;
+
+ /** set to true if this layer uses an invalid base url */
+ private boolean usesInvalidUrl = false;
+ /** set to true if the user confirmed to use an potentially invalid WMS base url */
+ private boolean isInvalidUrlConfirmed = false;
+
+ public WMSLayer() {
+ this(tr("Blank Layer"), null, null);
+ initializeImages();
+ mv = Main.map.mapView;
+ }
+
+ public WMSLayer(String name, String baseURL, String cookies) {
+ super(name);
+ alphaChannel.setSelected(Main.pref.getBoolean("wmsplugin.alpha_channel"));
+ setBackgroundLayer(true); /* set global background variable */
+ initializeImages();
+ this.baseURL = baseURL;
+ this.cookies = cookies;
+ WMSGrabber.getProjection(baseURL, true);
+ mv = Main.map.mapView;
+
+ // quick hack to predefine the PixelDensity to reuse the cache
+ int codeIndex = getName().indexOf("#PPD=");
+ if (codeIndex != -1) {
+ pixelPerDegree = Double.valueOf(getName().substring(codeIndex+5));
+ } else {
+ pixelPerDegree = getPPD();
+ }
+ resolution = mv.getDist100PixelText();
+ pixelPerDegree = getPPD();
+
+ executor = Executors.newFixedThreadPool(3);
+ if (baseURL != null && !baseURL.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(baseURL)) {
+ if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
+ if (!confirmMalformedUrl(baseURL)) {
+ System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", baseURL));
+ usesInvalidUrl = true;
+ setName(getName() + tr("(deactivated)"));
+ return;
+ } else {
+ isInvalidUrlConfirmed = true;
+ }
+ }
+ }
+ }
+
+ public double getDx(){
+ return dx;
+ }
+
+ public double getDy(){
+ return dy;
+ }
+
+ @Override
+ public void destroy() {
+ try {
+ executor.shutdownNow();
+ // Might not be initialized, so catch NullPointer as well
+ } catch(Exception x) {
+ x.printStackTrace();
+ }
+ }
+
+ public double getPPD(){
+ ProjectionBounds bounds = mv.getProjectionBounds();
+ return mv.getWidth() / (bounds.max.east() - bounds.min.east());
+ }
+
+ public void initializeImages() {
+ images = new GeorefImage[dax][day];
+ for(int x = 0; x<dax; ++x) {
+ for(int y = 0; y<day; ++y) {
+ images[x][y]= new GeorefImage(false);
+ }
+ }
+ }
+
+ @Override public Icon getIcon() {
+ return icon;
+ }
+
+ @Override public String getToolTipText() {
+ if(startstop.isSelected())
+ return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution);
+ else
+ return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution);
+ }
+
+ @Override public boolean isMergable(Layer other) {
+ return false;
+ }
+
+ @Override public void mergeFrom(Layer from) {
+ }
+
+ private ProjectionBounds XYtoBounds (int x, int y) {
+ return new ProjectionBounds(
+ new EastNorth( x * ImageSize / pixelPerDegree, y * ImageSize / pixelPerDegree),
+ new EastNorth((x + 1) * ImageSize / pixelPerDegree, (y + 1) * ImageSize / pixelPerDegree));
+ }
+
+ private int modulo (int a, int b) {
+ return a % b >= 0 ? a%b : a%b+b;
+ }
+
+ @Override public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
+ if(baseURL == null) return;
+ if (usesInvalidUrl && !isInvalidUrlConfirmed) return;
+
+ if( !startstop.isSelected() || (pixelPerDegree / getPPD() > minZoom) ){ //don't download when it's too outzoomed
+ for(int x = 0; x<dax; ++x) {
+ for(int y = 0; y<day; ++y) {
+ images[modulo(x,dax)][modulo(y,day)].paint(g, mv, dx, dy);
+ }
+ }
+ } else {
+ downloadAndPaintVisible(g, mv);
+ }
+ }
+
+ public void displace(double dx, double dy) {
+ this.dx += dx;
+ this.dy += dy;
+ }
+
+ protected boolean confirmMalformedUrl(String url) {
+ if (isInvalidUrlConfirmed)
+ return true;
+ String msg = tr("<html>The base URL<br>"
+ + "''{0}''<br>"
+ + "for this WMS layer does neither end with a ''&'' nor with a ''?''.<br>"
+ + "This is likely to lead to invalid WMS request. You should check your<br>"
+ + "preference settings.<br>"
+ + "Do you want to fetch WMS tiles anyway?",
+ url);
+ String [] options = new String[] {
+ tr("Yes, fetch images"),
+ tr("No, abort")
+ };
+ int ret = JOptionPane.showOptionDialog(
+ Main.parent,
+ msg,
+ tr("Invalid URL?"),
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE,
+ null,
+ options, options[1]
+ );
+ switch(ret) {
+ case JOptionPane.YES_OPTION: return true;
+ default: return false;
+ }
+ }
+ protected void downloadAndPaintVisible(Graphics g, final MapView mv){
+ if (usesInvalidUrl)
+ return;
+ ProjectionBounds bounds = mv.getProjectionBounds();
+ int bminx= (int)Math.floor (((bounds.min.east() - dx) * pixelPerDegree) / ImageSize );
+ int bminy= (int)Math.floor (((bounds.min.north() - dy) * pixelPerDegree) / ImageSize );
+ int bmaxx= (int)Math.ceil (((bounds.max.east() - dx) * pixelPerDegree) / ImageSize );
+ int bmaxy= (int)Math.ceil (((bounds.max.north() - dy) * pixelPerDegree) / ImageSize );
+
+ if((bmaxx - bminx > dax) || (bmaxy - bminy > day)){
+ JOptionPane.showMessageDialog(
+ Main.parent,
+ tr("The requested area is too big. Please zoom in a little, or change resolution"),
+ tr("Error"),
+ JOptionPane.ERROR_MESSAGE
+ );
+ return;
+ }
+
+ for(int x = bminx; x<bmaxx; ++x) {
+ for(int y = bminy; y<bmaxy; ++y){
+ GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
+ g.drawRect(x, y, dax, bminy);
+ if(!img.paint(g, mv, dx, dy) && !img.downloadingStarted){
+ img.downloadingStarted = true;
+ img.image = null;
+ img.flushedResizedCachedInstance();
+ Grabber gr = WMSPlugin.getGrabber(XYtoBounds(x,y), img, mv, this);
+ gr.setPriority(1);
+ executor.submit(gr);
+ }
+ }
+ }
+ }
+
+ @Override public void visitBoundingBox(BoundingXYVisitor v) {
+ for(int x = 0; x<dax; ++x) {
+ for(int y = 0; y<day; ++y)
+ if(images[x][y].image!=null){
+ v.visit(images[x][y].min);
+ v.visit(images[x][y].max);
+ }
+ }
+ }
+
+ @Override public Object getInfoComponent() {
+ return getToolTipText();
+ }
+
+ @Override public Component[] getMenuEntries() {
+ return new Component[]{
+ new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),
+ new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),
+ new JSeparator(),
+ new JMenuItem(new LoadWmsAction()),
+ new JMenuItem(new SaveWmsAction()),
+ new JMenuItem(new BookmarkWmsAction()),
+ new JSeparator(),
+ startstop,
+ alphaChannel,
+ new JMenuItem(new ChangeResolutionAction()),
+ new JMenuItem(new ReloadErrorTilesAction()),
+ new JMenuItem(new DownloadAction()),
+ new JSeparator(),
+ new JMenuItem(new LayerListPopup.InfoAction(this))
+ };
+ }
+
+ public GeorefImage findImage(EastNorth eastNorth) {
+ for(int x = 0; x<dax; ++x) {
+ for(int y = 0; y<day; ++y)
+ if(images[x][y].image!=null && images[x][y].min!=null && images[x][y].max!=null)
+ if(images[x][y].contains(eastNorth, dx, dy))
+ return images[x][y];
+ }
+ return null;
+ }
+
+ public class DownloadAction extends AbstractAction {
+ public DownloadAction() {
+ super(tr("Download visible tiles"));
+ }
+ public void actionPerformed(ActionEvent ev) {
+ downloadAndPaintVisible(mv.getGraphics(), mv);
+ }
+ }
+
+ public class ChangeResolutionAction extends AbstractAction {
+ public ChangeResolutionAction() {
+ super(tr("Change resolution"));
+ }
+ public void actionPerformed(ActionEvent ev) {
+ initializeImages();
+ resolution = mv.getDist100PixelText();
+ pixelPerDegree = getPPD();
+ mv.repaint();
+ }
+ }
+
+ public class ReloadErrorTilesAction extends AbstractAction {
+ public ReloadErrorTilesAction() {
+ super(tr("Reload erroneous tiles"));
+ }
+ public void actionPerformed(ActionEvent ev) {
+ // Delete small files, because they're probably blank tiles.
+ // See https://josm.openstreetmap.de/ticket/2307
+ WMSPlugin.cache.customCleanUp(CacheFiles.CLEAN_SMALL_FILES, 4096);
+
+ for (int x = 0; x < dax; ++x) {
+ for (int y = 0; y < day; ++y) {
+ GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
+ if(img.failed){
+ img.image = null;
+ img.flushedResizedCachedInstance();
+ img.downloadingStarted = false;
+ img.failed = false;
+ mv.repaint();
+ }
+ }
+ }
+ }
+ }
+
+ public class ToggleAlphaAction extends AbstractAction {
+ public ToggleAlphaAction() {
+ super(tr("Alpha channel"));
+ }
+ public void actionPerformed(ActionEvent ev) {
+ JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource();
+ boolean alphaChannel = checkbox.isSelected();
+ Main.pref.put("wmsplugin.alpha_channel", alphaChannel);
+
+ // clear all resized cached instances and repaint the layer
+ for (int x = 0; x < dax; ++x) {
+ for (int y = 0; y < day; ++y) {
+ GeorefImage img = images[modulo(x, dax)][modulo(y, day)];
+ img.flushedResizedCachedInstance();
+ }
+ }
+ mv.repaint();
+ }
+ }
+
+ public class SaveWmsAction extends AbstractAction {
+ public SaveWmsAction() {
+ super(tr("Save WMS layer to file"), ImageProvider.get("save"));
+ }
+ public void actionPerformed(ActionEvent ev) {
+ File f = SaveActionBase.createAndOpenSaveFileChooser(
+ tr("Save WMS layer"), ".wms");
+ try {
+ if (f != null) {
+ ObjectOutputStream oos = new ObjectOutputStream(
+ new FileOutputStream(f)
+ );
+ oos.writeInt(serializeFormatVersion);
+ oos.writeInt(dax);
+ oos.writeInt(day);
+ oos.writeInt(ImageSize);
+ oos.writeDouble(pixelPerDegree);
+ oos.writeObject(getName());
+ oos.writeObject(baseURL);
+ oos.writeObject(images);
+ oos.close();
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace(System.out);
+ }
+ }
+ }
+
+ public class LoadWmsAction extends AbstractAction {
+ public LoadWmsAction() {
+ super(tr("Load WMS layer from file"), ImageProvider.get("load"));
+ }
+ public void actionPerformed(ActionEvent ev) {
+ JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true,
+ false, tr("Load WMS layer"), "wms");
+ if(fc == null) return;
+ File f = fc.getSelectedFile();
+ if (f == null) return;
+ try
+ {
+ FileInputStream fis = new FileInputStream(f);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ int sfv = ois.readInt();
+ if (sfv != serializeFormatVersion) {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion),
+ tr("File Format Error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ startstop.setSelected(false);
+ dax = ois.readInt();
+ day = ois.readInt();
+ ImageSize = ois.readInt();
+ pixelPerDegree = ois.readDouble();
+ setName((String)ois.readObject());
+ baseURL = (String) ois.readObject();
+ images = (GeorefImage[][])ois.readObject();
+ ois.close();
+ fis.close();
+ mv.repaint();
+ }
+ catch (Exception ex) {
+ // FIXME be more specific
+ ex.printStackTrace(System.out);
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("Error loading file"),
+ tr("Error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ }
+ }
+ /**
+ * This action will add a WMS layer menu entry with the current WMS layer URL and name extended by the current resolution.
+ * When using the menu entry again, the WMS cache will be used properly.
+ */
+ public class BookmarkWmsAction extends AbstractAction {
+ public BookmarkWmsAction() {
+ super(tr("Set WMS Bookmark"));
+ }
+ public void actionPerformed(ActionEvent ev) {
+ int i = 0;
+ while (Main.pref.hasKey("wmsplugin.url."+i+".url")) {
+ i++;
+ }
+ String baseName;
+ // cut old parameter
+ int parameterIndex = getName().indexOf("#PPD=");
+ if (parameterIndex != -1) {
+ baseName = getName().substring(0,parameterIndex);
+ }
+ else {
+ baseName = getName();
+ }
+ Main.pref.put("wmsplugin.url."+ i +".url",baseURL );
+ Main.pref.put("wmsplugin.url."+String.valueOf(i)+".name", baseName + "#" + getPPD() );
+ WMSPlugin.refreshMenu();
+ }
+ }
}
diff --git a/wmsplugin/src/wmsplugin/WMSPlugin.java b/wmsplugin/src/wmsplugin/WMSPlugin.java
index 313676e..81732ed 100644
--- a/wmsplugin/src/wmsplugin/WMSPlugin.java
+++ b/wmsplugin/src/wmsplugin/WMSPlugin.java
@@ -1,5 +1,6 @@
package wmsplugin;
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.marktr;
import static org.openstreetmap.josm.tools.I18n.tr;
@@ -50,13 +51,13 @@ public class WMSPlugin extends Plugin {
static boolean doOverlap = false;
static int overlapEast = 14;
static int overlapNorth = 4;
-
+
// remember state of menu item to restore on changed preferences
static private boolean menuEnabled = false;
-
+
protected void initExporterAndImporter() {
- ExtensionFileFilter.exporters.add(new WMSLayerExporter());
- ExtensionFileFilter.importers.add(new WMSLayerImporter());
+ ExtensionFileFilter.exporters.add(new WMSLayerExporter());
+ ExtensionFileFilter.importers.add(new WMSLayerImporter());
}
public WMSPlugin() {
@@ -93,19 +94,19 @@ public class WMSPlugin extends Plugin {
TreeSet<String> keys = new TreeSet<String>(prefs.keySet());
- // Here we load the settings for "overlap" checkbox and spinboxes.
-
- try {
- doOverlap = Boolean.valueOf(prefs.get("wmsplugin.url.overlap"));
- } catch (Exception e) {} // If sth fails, we drop to default settings.
-
- try {
- overlapEast = Integer.valueOf(prefs.get("wmsplugin.url.overlapEast"));
- } catch (Exception e) {} // If sth fails, we drop to default settings.
-
- try {
- overlapNorth = Integer.valueOf(prefs.get("wmsplugin.url.overlapNorth"));
- } catch (Exception e) {} // If sth fails, we drop to default settings.
+ // Here we load the settings for "overlap" checkbox and spinboxes.
+
+ try {
+ doOverlap = Boolean.valueOf(prefs.get("wmsplugin.url.overlap"));
+ } catch (Exception e) {} // If sth fails, we drop to default settings.
+
+ try {
+ overlapEast = Integer.valueOf(prefs.get("wmsplugin.url.overlapEast"));
+ } catch (Exception e) {} // If sth fails, we drop to default settings.
+
+ try {
+ overlapNorth = Integer.valueOf(prefs.get("wmsplugin.url.overlapNorth"));
+ } catch (Exception e) {} // If sth fails, we drop to default settings.
// And then the names+urls of WMS servers
int prefid = 0;
@@ -168,7 +169,7 @@ public class WMSPlugin extends Plugin {
MainMenu menu = Main.main.menu;
if (wmsJMenu == null)
- wmsJMenu = menu.addMenu(marktr("WMS"), KeyEvent.VK_W, menu.defaultMenuPos);
+ wmsJMenu = menu.addMenu(marktr("WMS"), KeyEvent.VK_W, menu.defaultMenuPos, ht("/Plugin/WMS"));
else
wmsJMenu.removeAll();
@@ -186,8 +187,6 @@ public class WMSPlugin extends Plugin {
Main.main.addLayer(new WMSLayer());
}
}));
- wmsJMenu.addSeparator();
- wmsJMenu.add(new JMenuItem(new Help_WMSmenuAction()));
setEnabledAll(menuEnabled);
}
diff --git a/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java b/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
index bdd155b..9d21e3e 100644
--- a/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
+++ b/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
@@ -58,7 +58,7 @@ public class WMSPreferenceEditor implements PreferenceSetting {
final DefaultTableModel modeldef = new DefaultTableModel(
new String[]{tr("Menu Name (Default)"), tr("WMS URL (Default)")}, 0);
final JTable listdef = new JTable(modeldef){
- @Override
+ @Override
public boolean isCellEditable(int row,int column){return false;}
};
JScrollPane scrolldef = new JScrollPane(listdef);
@@ -84,10 +84,10 @@ public class WMSPreferenceEditor implements PreferenceSetting {
p.add(new JLabel(tr("WMS URL")), GBC.std().insets(0,0,5,0));
p.add(value, GBC.eol().insets(5,0,0,0).fill(GBC.HORIZONTAL));
int answer = JOptionPane.showConfirmDialog(
- gui, p,
- tr("Enter a menu name and WMS URL"),
- JOptionPane.OK_CANCEL_OPTION,
- JOptionPane.QUESTION_MESSAGE);
+ gui, p,
+ tr("Enter a menu name and WMS URL"),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE);
if (answer == JOptionPane.OK_OPTION) {
model.addRow(new String[]{key.getText(), value.getText()});
}
@@ -116,35 +116,35 @@ public class WMSPreferenceEditor implements PreferenceSetting {
int[] lines = listdef.getSelectedRows();
if (lines.length == 0) {
JOptionPane.showMessageDialog(
- gui,
- tr("Please select at least one row to copy."),
- tr("Information"),
- JOptionPane.INFORMATION_MESSAGE
- );
+ gui,
+ tr("Please select at least one row to copy."),
+ tr("Information"),
+ JOptionPane.INFORMATION_MESSAGE
+ );
return;
}
outer: for(int i = 0; i < lines.length; i++) {
- String c1 = modeldef.getValueAt(lines[i], 0).toString();
- String c2 = modeldef.getValueAt(lines[i], 1).toString();
-
- // Check if an entry with exactly the same values already
- // exists
- for(int j = 0; j < model.getRowCount(); j++) {
- if(c1.equals(model.getValueAt(j, 0).toString())
- && c2.equals(model.getValueAt(j, 1).toString())) {
- // Select the already existing row so the user has
- // some feedback in case an entry exists
- list.getSelectionModel().setSelectionInterval(j, j);
- list.scrollRectToVisible(list.getCellRect(j, 0, true));
- continue outer;
- }
- }
-
- model.addRow(new String[] {c1, c2});
- int lastLine = model.getRowCount() - 1;
- list.getSelectionModel().setSelectionInterval(lastLine, lastLine);
- list.scrollRectToVisible(list.getCellRect(lastLine, 0, true));
+ String c1 = modeldef.getValueAt(lines[i], 0).toString();
+ String c2 = modeldef.getValueAt(lines[i], 1).toString();
+
+ // Check if an entry with exactly the same values already
+ // exists
+ for(int j = 0; j < model.getRowCount(); j++) {
+ if(c1.equals(model.getValueAt(j, 0).toString())
+ && c2.equals(model.getValueAt(j, 1).toString())) {
+ // Select the already existing row so the user has
+ // some feedback in case an entry exists
+ list.getSelectionModel().setSelectionInterval(j, j);
+ list.scrollRectToVisible(list.getCellRect(j, 0, true));
+ continue outer;
+ }
+ }
+
+ model.addRow(new String[] {c1, c2});
+ int lastLine = model.getRowCount() - 1;
+ list.getSelectionModel().setSelectionInterval(lastLine, lastLine);
+ list.scrollRectToVisible(list.getCellRect(lastLine, 0, true));
}
}
});
@@ -181,7 +181,7 @@ public class WMSPreferenceEditor implements PreferenceSetting {
overlapPanel.add(labelNorth);
overlapPanel.add(spinNorth);
- p.add(overlapPanel);
+ p.add(overlapPanel);
}
public boolean ok() {
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/josm-plugins.git
More information about the Pkg-grass-devel
mailing list