[josm-plugins] 199/369: Imported Upstream version 0.0.svn24572
Bas Couwenberg
sebastic at xs4all.nl
Sat Oct 18 12:03:42 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 dfa9415d366b5a26458ef4c09463f6dd6cc79a1c
Author: David Paleino <dapal at debian.org>
Date: Mon Dec 6 11:09:18 2010 +0100
Imported Upstream version 0.0.svn24572
---
DirectUpload/build.xml | 2 +-
.../josm/plugins/DirectUpload/UploadDataGui.java | 242 +++---
.../plugins/DirectUpload/UploadDataGuiPlugin.java | 49 +-
.../plugins/DirectUpload/UploadOsmConnection.java | 83 ++
editgpx/.classpath | 2 +-
editgpx/.project | 2 +-
editgpx/.settings/org.eclipse.jdt.core.prefs | 260 ++++++
editgpx/.settings/org.eclipse.jdt.ui.prefs | 56 +-
editgpx/build.xml | 11 +-
.../josm/plugins/editgpx/EditGpxLayer.java | 2 +-
.../josm/plugins/editgpx/data/EditGpxData.java | 4 +-
.../josm/plugins/editgpx/data/EditGpxTrack.java | 9 +-
{editgpx => imagery}/.classpath | 4 +-
{editgpx => imagery}/.project | 9 +-
imagery/GPL-v2.0.txt | 339 ++++++++
imagery/GPL-v3.0.txt | 674 +++++++++++++++
imagery/README | 27 +
imagery/build.xml | 258 ++++++
imagery/images/OLmarker.png | Bin 0 -> 549 bytes
imagery/images/bing_maps.png | Bin 0 -> 4295 bytes
imagery/images/blankmenu.png | Bin 0 -> 1529 bytes
imagery/images/cursor/modifier/move.png | Bin 0 -> 192 bytes
imagery/images/imagery.png | Bin 0 -> 9455 bytes
imagery/images/imagery_menu.png | Bin 0 -> 1780 bytes
imagery/images/imagery_small.png | Bin 0 -> 880 bytes
imagery/images/load.png | Bin 0 -> 883 bytes
imagery/images/mapmode/adjustimg.png | Bin 0 -> 1515 bytes
imagery/images/preferences/imagery.png | Bin 0 -> 6831 bytes
imagery/images/save.png | Bin 0 -> 845 bytes
imagery/images_nodist/imagery.xcf | Bin 0 -> 17352 bytes
imagery/sources.cfg | 69 ++
.../plugins/imagery/AddImageryLayerAction.java | 25 +
.../josm/plugins/imagery/ImageryAdjustAction.java | 207 +++++
.../josm/plugins/imagery/ImageryInfo.java | 185 ++++
.../josm/plugins/imagery/ImageryLayer.java | 81 ++
.../josm/plugins/imagery/ImageryLayerInfo.java | 104 +++
.../josm/plugins/imagery/ImageryPlugin.java | 273 ++++++
.../plugins/imagery/ImageryPreferenceEditor.java | 540 ++++++++++++
.../josm/plugins/imagery/ImageryPreferences.java | 28 +
.../josm/plugins/imagery/ImageryRemoteHandler.java | 108 +++
.../plugins/imagery/tms/BingAerialTileSource.java | 191 +++++
.../josm/plugins/imagery/tms/TMSKey.java | 78 ++
.../josm/plugins/imagery/tms/TMSLayer.java | 319 +++++--
.../josm/plugins/imagery/tms/TMSPreferences.java | 82 ++
.../josm/plugins/imagery/tms/TMSTileSource.java | 22 +
.../imagery/tms/TemplatedTMSTileSource.java | 30 +
.../josm/plugins/imagery/wms/AddWMSLayerPanel.java | 530 ++++++++++++
.../josm/plugins/imagery/wms/GeorefImage.java | 248 ++++++
.../josm/plugins/imagery/wms/Grabber.java | 115 +++
.../josm/plugins/imagery/wms/HTMLGrabber.java | 46 +
.../imagery/wms/Map_Rectifier_WMSmenuAction.java | 238 ++++++
.../josm/plugins/imagery/wms/WMSAdapter.java | 40 +
.../josm/plugins/imagery/wms/WMSGrabber.java | 203 +++++
.../josm/plugins/imagery/wms}/WMSLayer.java | 111 ++-
.../josm/plugins/imagery/wms/WMSRequest.java | 102 +++
.../plugins/imagery/wms/io/WMSLayerExporter.java | 13 +
.../plugins/imagery/wms/io/WMSLayerImporter.java | 14 +
routing/build.xml | 2 +-
.../innovant/josm/plugin/routing/RoutingLayer.java | 3 +-
.../routing/gui/RoutingPreferenceDialog.java | 6 +-
slippymap/.classpath | 2 +-
slippymap/build.xml | 4 +-
slippymap/images/bing_maps.png | Bin 0 -> 4295 bytes
.../josm/plugins/slippymap/SlippyMapLayer.java | 99 ++-
.../josm/plugins/slippymap/SlippyMapPlugin.java | 135 +--
.../plugins/slippymap/SlippyMapPreferences.java | 170 +++-
svn-info.xml | 8 +-
wmsplugin/build.xml | 2 +-
wmsplugin/sources.cfg | 3 +
wmsplugin/src/wmsplugin/AddWMSLayerPanel.java | 928 ++++++++++-----------
wmsplugin/src/wmsplugin/WMSLayer.java | 8 +
wmsplugin/src/wmsplugin/WMSLayerInfo.java | 3 +-
72 files changed, 6523 insertions(+), 885 deletions(-)
diff --git a/DirectUpload/build.xml b/DirectUpload/build.xml
index 555c2c4..16c1ea7 100644
--- a/DirectUpload/build.xml
+++ b/DirectUpload/build.xml
@@ -26,7 +26,7 @@
-->
<project name="DirectUpload" default="dist" basedir=".">
- <property name="commit.message" value="Changed constructor signature of plugin main class" />
+ <property name="commit.message" value="applied JOSM Ticket 4498 (patch by ax) - oauth support for gpx upload (I accidentally committed parts of the path in [24236])" />
<property name="plugin.main.version" value="3338" />
<property name="josm" location="../../core/dist/josm-custom.jar"/>
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java
index 29775e6..a1fed59 100644
--- a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGui.java
@@ -19,38 +19,34 @@ 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.Collections;
import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
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.gui.widgets.HistoryComboBox;
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
+ * @author subhodip, xeen, ax
*/
public class UploadDataGui extends ExtendedDialog {
+
/**
* This enum contains the possible values for the visibility field and their
* explanation. Provides some methods for easier handling.
@@ -86,19 +82,14 @@ public class UploadDataGui extends ExtendedDialog {
}
}
-
- // 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();
+ private HistoryComboBox descriptionField;
+ private HistoryComboBox tagsField;
+ private JComboBox visibilityCombo;
// Constants used when generating upload request
private static final String API_VERSION = "0.6";
@@ -106,12 +97,8 @@ public class UploadDataGui extends ExtendedDialog {
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,
@@ -120,12 +107,13 @@ public class UploadDataGui extends ExtendedDialog {
true
);
JPanel content = initComponents();
- autoSelectTrace();
+ GpxData gpxData = UploadOsmConnection.getInstance().autoSelectTrace();
+ initTitleAndDescriptionFromGpxData(gpxData); // this is changing some dialog elements, so it (probably) must be before the following
setContent(content);
setButtonIcons(new String[] { "uploadtrace.png", "cancel.png" });
setupDialog();
- buttons.get(0).setEnabled(!checkForGPXLayer());
+ buttons.get(0).setEnabled(gpxData != null);
}
/**
@@ -133,19 +121,40 @@ public class UploadDataGui extends ExtendedDialog {
* @return JPanel with components
*/
private JPanel initComponents() {
+ // visibilty
JLabel visibilityLabel = new JLabel(tr("Visibility"));
visibilityLabel.setToolTipText(tr("Defines the visibility of your trace for other OSM users."));
+
+ visibilityCombo = new JComboBox();
+ visibilityCombo.setEditable(false);
for(visibility v : visibility.values()) {
visibilityCombo.addItem(v.description);
}
+ visibilityCombo.setSelectedItem(visibility.valueOf(Main.pref.get("directupload.visibility.last-used", visibility.PRIVATE.name())).description);
UrlLabel visiUrl = new UrlLabel(tr("http://wiki.openstreetmap.org/wiki/Visibility_of_GPS_traces"), tr("(What does that mean?)"));
+ // description
JLabel descriptionLabel = new JLabel(tr("Description"));
+ descriptionField = new HistoryComboBox();
descriptionField.setToolTipText(tr("Please enter Description about your trace."));
-
+
+ List<String> descHistory = new LinkedList<String>(Main.pref.getCollection("directupload.description.history", new LinkedList<String>()));
+ // we have to reverse the history, because ComboBoxHistory will reverse it againin addElement()
+ // XXX this should be handled in HistoryComboBox
+ Collections.reverse(descHistory);
+ descriptionField.setPossibleItems(descHistory);
+
+ // tags
JLabel tagsLabel = new JLabel(tr("Tags (comma delimited)"));
+ tagsField = new HistoryComboBox();
tagsField.setToolTipText(tr("Please enter tags about your trace."));
+ List<String> tagsHistory = new LinkedList<String>(Main.pref.getCollection("directupload.tags.history", new LinkedList<String>()));
+ // we have to reverse the history, because ComboBoxHistory will reverse it againin addElement()
+ // XXX this should be handled in HistoryComboBox
+ Collections.reverse(tagsHistory);
+ tagsField.setPossibleItems(tagsHistory);
+
JPanel p = new JPanel(new GridBagLayout());
OutputDisplay.setMaxWidth(findMaxDialogSize().width-10);
@@ -164,36 +173,18 @@ public class UploadDataGui extends ExtendedDialog {
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()));
- }
- }
+ private void initTitleAndDescriptionFromGpxData(GpxData gpxData) {
+ String description, title;
+ try {
+ description = gpxData.storageFile.getName().replaceAll("[&?/\\\\]"," ").replaceAll("(\\.[^.]*)$","");
+ title = tr("Selected track: {0}", gpxData.storageFile.getName());
+ }
+ catch(Exception e) {
+ description = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
+ title = tr("No GPX layer selected. Cannot upload a trace.");
+ }
+ OutputDisplay.setText(title);
+ descriptionField.setText(description);
}
/**
@@ -204,53 +195,54 @@ public class UploadDataGui extends ExtendedDialog {
* @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);
+ progressMonitor.beginTask(tr("Uploading trace ..."));
try {
- if(checkForErrors(username, password, description, gpxData))
+ if (checkForErrors(description, gpxData)) {
return;
+ }
// Clean description/tags from disallowed chars
- description = description.replaceAll("[&?/\\\\]"," ");
- tags = tags.replaceAll("[&?/\\\\.;]"," ");
+ 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"));
+ // 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);
+
+ flushToServer(bais, conn.getOutputStream(), progressMonitor);
+
+ 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 {
+ }
+ catch (Exception e) {
+ OutputDisplay.setText(tr("Error while uploading"));
+ e.printStackTrace();
+ }
+ finally {
progressMonitor.finishTask();
}
}
@@ -262,10 +254,6 @@ public class UploadDataGui extends ExtendedDialog {
* @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");
@@ -276,7 +264,10 @@ public class UploadDataGui extends ExtendedDialog {
c.setConnectTimeout(15000);
c.setRequestMethod("POST");
c.setDoOutput(true);
- c.addRequestProperty("Authorization", "Basic " + Base64.encode(bytes));
+ // unfortunately, addAuth() is protected, so we need to subclass OsmConnection
+ // XXX make addAuth public.
+ UploadOsmConnection.getInstance().addAuthHack(c);
+
c.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
c.addRequestProperty("Connection", "close"); // counterpart of keep-alive
c.addRequestProperty("Expect", "");
@@ -366,14 +357,11 @@ public class UploadDataGui extends ExtendedDialog {
/**
* 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) {
+ private boolean checkForErrors(String description, GpxData gpxData) {
String errors="";
if(description == null || description.length() == 0)
errors += tr("No description provided. Please provide some description.");
@@ -381,48 +369,37 @@ public class UploadDataGui extends ExtendedDialog {
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;
+ final GpxData gpxData = UploadOsmConnection.getInstance().autoSelectTrace();
+ if (gpxData == null) {
+ return;
+ }
// Disable Upload button so users can't just upload that track again
buttons.get(0).setEnabled(false);
+
+ // save history
+ Main.pref.put("directupload.visibility.last-used", visibility.desc2visi(visibilityCombo.getSelectedItem().toString()).name());
+
+ descriptionField.addCurrentItemToHistory();
+ Main.pref.putCollection("directupload.description.history", descriptionField.getHistory());
+
+ tagsField.addCurrentItemToHistory();
+ Main.pref.putCollection("directupload.tags.history", tagsField.getHistory());
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,
+ gpxData,
progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)
);
}
@@ -459,7 +436,7 @@ public class UploadDataGui extends ExtendedDialog {
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" + "\"");
+ writeString(baos, "filename=\"" + gpxData.storageFile.getName() + "\"");
writeLineEnd(baos);
writeString(baos, "Content-Type: application/octet-stream");
writeLineEnd(baos);
@@ -497,15 +474,6 @@ public class UploadDataGui extends ExtendedDialog {
}
/**
- * 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(int buttonIndex, ActionEvent evt) {
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java
index 0fb7e68..e7d0008 100644
--- a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadDataGuiPlugin.java
@@ -10,19 +10,19 @@ import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
-import java.util.List;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.JosmAction;
-import org.openstreetmap.josm.gui.layer.GpxLayer;
import org.openstreetmap.josm.plugins.Plugin;
import org.openstreetmap.josm.plugins.PluginInformation;
import org.openstreetmap.josm.tools.Shortcut;
+
/**
*
- * @author subhodip
+ * @author subhodip, ax
*/
-public class UploadDataGuiPlugin extends Plugin{
+public class UploadDataGuiPlugin extends Plugin {
+
UploadAction openaction;
public UploadDataGuiPlugin(PluginInformation info) {
@@ -31,35 +31,30 @@ public class UploadDataGuiPlugin extends Plugin{
Main.main.menu.toolsMenu.add(openaction);
}
- class UploadAction extends JosmAction{
+ 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);
+ 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);
}
- @Override
- protected void updateEnabledState() {
- // enable button if there is "one active GpxLayer" or "exactly one GpxLayer in the list of all layers available"
- if(Main.map == null
- || Main.map.mapView == null
- || Main.map.mapView.getActiveLayer() == null
- || !(Main.map.mapView.getActiveLayer() instanceof GpxLayer)) {
- setEnabled(false);
- } else {
- setEnabled(true);
- }
-
- if(Main.map != null && Main.map.mapView.getNumLayers() > 1) {
- List<GpxLayer> list = Main.map.mapView.getLayersOfType(GpxLayer.class);
- if (list.size() == 1)
- setEnabled(true);
- }
-
- }
+ // because LayerListDialog doesn't provide a way to hook into "layer selection changed"
+ // but the layer selection (*not* activation) is how we determine the layer to be uploaded
+ // we have to let the upload trace menu always be enabled
+// @Override
+// protected void updateEnabledState() {
+// // enable button if ... @see autoSelectTrace()
+// if (UploadOsmConnection.getInstance().autoSelectTrace() == null) {
+// setEnabled(false);
+// } else {
+// setEnabled(true);
+// }
+// }
}
-}
\ No newline at end of file
+}
diff --git a/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadOsmConnection.java b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadOsmConnection.java
new file mode 100644
index 0000000..e4622b4
--- /dev/null
+++ b/DirectUpload/src/org/openstreetmap/josm/plugins/DirectUpload/UploadOsmConnection.java
@@ -0,0 +1,83 @@
+// ...
+
+package org.openstreetmap.josm.plugins.DirectUpload;
+
+import java.net.HttpURLConnection;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.io.OsmConnection;
+import org.openstreetmap.josm.io.OsmTransferException;
+
+/**
+ * Work-around and utility class for DirectUpload.
+ *
+ * @author ax
+ */
+public class UploadOsmConnection extends OsmConnection {
+
+ // singleton, see http://en.wikipedia.org/wiki/Singleton_pattern#Traditional_simple_way
+ private static final UploadOsmConnection INSTANCE = new UploadOsmConnection();
+
+ // Private constructor prevents instantiation from other classes
+ private UploadOsmConnection() {
+ }
+
+ public static UploadOsmConnection getInstance() {
+ return UploadOsmConnection.INSTANCE;
+ }
+
+ // make protected OsmConnection::addAuth() available to others
+ public void addAuthHack(HttpURLConnection connection) throws OsmTransferException {
+ addAuth(connection);
+ }
+
+ /**
+ * find which gpx layer holds the trace to upload. layers are tried in this order:
+ *
+ * 1. selected (*not* active - think "zoom to layer"), from first to last
+ * 2. not selectd - if there is only one
+ * 3. active
+ *
+ * @return data of the selected gpx layer, or null if there is none
+ */
+ GpxData autoSelectTrace() {
+ if (Main.map != null && Main.map.mapView != null) {
+ MapView mv = Main.map.mapView;
+// List<Layer> allLayers = new ArrayList<Layer>(mv.getAllLayersAsList()); // modifiable
+ List<Layer> selectedLayers = LayerListDialog.getInstance().getModel().getSelectedLayers();
+ List<GpxLayer> gpxLayersRemaining = mv.getLayersOfType(GpxLayer.class);
+ gpxLayersRemaining.removeAll(selectedLayers);
+ GpxLayer traceLayer = null;
+ // find the first gpx layer inside selected layers
+ for (Layer l : LayerListDialog.getInstance().getModel().getSelectedLayers()) {
+ if (l instanceof GpxLayer) {
+ traceLayer = (GpxLayer) l;
+ break;
+ }
+ }
+ if (traceLayer == null) {
+ // if there is none, try the none selected gpx layers. if there is only one, use it.
+ if (gpxLayersRemaining.size() == 1) {
+ traceLayer = gpxLayersRemaining.get(0);
+ }
+ // active layer
+ else if (mv.getActiveLayer() instanceof GpxLayer) {
+ traceLayer = (GpxLayer) mv.getActiveLayer();
+ }
+ }
+
+ if (traceLayer != null) {
+ GpxData data = traceLayer.data;
+ return data;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/editgpx/.classpath b/editgpx/.classpath
index 4a8596f..d4528ee 100644
--- a/editgpx/.classpath
+++ b/editgpx/.classpath
@@ -2,7 +2,7 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry excluding="src/" including="images/" kind="src" path=""/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
+ <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/editgpx/.project b/editgpx/.project
index 569f47e..3142f1a 100644
--- a/editgpx/.project
+++ b/editgpx/.project
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>editgpx</name>
+ <name>editgpx_svn</name>
<comment></comment>
<projects>
</projects>
diff --git a/editgpx/.settings/org.eclipse.jdt.core.prefs b/editgpx/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7bf9d9c
--- /dev/null
+++ b/editgpx/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,260 @@
+#Sat Oct 16 19:18:33 CEST 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/editgpx/.settings/org.eclipse.jdt.ui.prefs b/editgpx/.settings/org.eclipse.jdt.ui.prefs
index 4a3398d..079a653 100644
--- a/editgpx/.settings/org.eclipse.jdt.ui.prefs
+++ b/editgpx/.settings/org.eclipse.jdt.ui.prefs
@@ -1,6 +1,60 @@
-#Tue May 25 21:33:38 CEST 2010
+#Sat Oct 16 19:07:29 CEST 2010
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Eclipse [built-in] + spaces for indent
+formatter_settings_version=11
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
diff --git a/editgpx/build.xml b/editgpx/build.xml
index 326bed0..2801816 100644
--- a/editgpx/build.xml
+++ b/editgpx/build.xml
@@ -21,7 +21,7 @@
** set the properties commit.message and plugin.main.version
** and run
** > ant publish
-**
+**
**
-->
<project name="editgpx" default="dist" basedir=".">
@@ -31,8 +31,10 @@
<property name="plugin.main.version" value="3408" />
- <property name="josm" location="../../core/dist/josm-custom.jar"/>
- <property name="plugin.dist.dir" value="../../dist"/>
+ <!-- <property name="josm" location="../../core/dist/josm-custom.jar"/> -->
+ <property name="josm" location="../JOSM/dist/josm-custom.jar"/>
+ <!-- <property name="plugin.dist.dir" value="../../dist"/>-->
+ <property name="plugin.dist.dir" value=".."/>
<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"/>
@@ -100,7 +102,8 @@
<env key="LANG" value="C"/>
<arg value="info"/>
<arg value="--xml"/>
- <arg value="../../core"/>
+ <!-- <arg value="../../core"/>-->
+ <arg value="../JOSM"/>
</exec>
<xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
<echo>Building against core revision ${coreversion.info.entry.revision}.</echo>
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
index 0b471a9..cb5be17 100644
--- a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
@@ -136,7 +136,7 @@ public class EditGpxLayer extends Layer {
* @return GPXData
*/
private GpxData toGpxData(boolean anonTime) {
- return data.createGpxData();
+ return data.createGpxData(anonTime);
}
//context item "Convert to GPX layer"
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxData.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxData.java
index 3c89c5e..c0b2395 100644
--- a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxData.java
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxData.java
@@ -42,12 +42,12 @@ public class EditGpxData {
return tracks;
}
- public GpxData createGpxData() {
+ public GpxData createGpxData(boolean anonTime) {
GpxData result = new GpxData();
for (EditGpxTrack track: tracks) {
if (!track.isDeleted()) {
- GpxTrack newTrack = track.createGpxTrack();
+ GpxTrack newTrack = track.createGpxTrack(anonTime);
if (!newTrack.getSegments().isEmpty()) {
result.tracks.add(newTrack);
}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrack.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrack.java
index a539863..7388e3a 100644
--- a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrack.java
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrack.java
@@ -31,7 +31,7 @@ public class EditGpxTrack {
return attributes;
}
- public GpxTrack createGpxTrack() {
+ public GpxTrack createGpxTrack(boolean anonTime) {
Collection<Collection<WayPoint>> wayPoints = new ArrayList<Collection<WayPoint>>();
@@ -39,6 +39,13 @@ public class EditGpxTrack {
if (!segment.isDeleted()) {
List<WayPoint> points = segment.getNonDeletedWaypoints();
if (!points.isEmpty()) {
+ if (anonTime) {
+ // convert to anonymous time
+ for (WayPoint w : points) {
+ w.attr.put("time", "1970-00-00T00:00:00.000Z");
+ w.setTime();
+ }
+ }
wayPoints.add(points);
}
}
diff --git a/editgpx/.classpath b/imagery/.classpath
similarity index 65%
copy from editgpx/.classpath
copy to imagery/.classpath
index 4a8596f..560d90f 100644
--- a/editgpx/.classpath
+++ b/imagery/.classpath
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry excluding="src/" including="images/" kind="src" path=""/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/remotecontrol"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/editgpx/.project b/imagery/.project
similarity index 65%
copy from editgpx/.project
copy to imagery/.project
index 569f47e..d92129d 100644
--- a/editgpx/.project
+++ b/imagery/.project
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>editgpx</name>
+ <name>imagery</name>
<comment></comment>
<projects>
</projects>
@@ -14,11 +14,4 @@
<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/imagery/GPL-v2.0.txt b/imagery/GPL-v2.0.txt
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/imagery/GPL-v2.0.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, 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 Lesser 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 Street, 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 Lesser General
+Public License instead of this License.
diff --git a/imagery/GPL-v3.0.txt b/imagery/GPL-v3.0.txt
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/imagery/GPL-v3.0.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU 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 Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/imagery/README b/imagery/README
new file mode 100644
index 0000000..dbfb2d0
--- /dev/null
+++ b/imagery/README
@@ -0,0 +1,27 @@
+This plugin is an union of slippymap plugin and wmsplugin.
+Combined by Upliner, licensed under the GNU GPL v2 or later.
+
+WMSPlugin authors:
+==========================================================================
+This plugin has been created by tim <chippy2005 at gmail.com>
+and has received major contributions from Frederik Ramm
+<frederik at remote.org>. It is based on the "Landsat" plugin
+by Nick Whitelegg <Nick.Whitelegg at solent.ac.uk> and includes
+some code from Jonathan Stott <jonathan at jstott.me.uk>, Gabriel Ebner
+<ge at gabrielebner.at> and Ulf Lamping <ulf.lamping at web.de>.
+The automatic tiles downloading and Yahoo downloader made by Petr Dlouhý <petr.dlouhy at email.cz>
+
+This plugin is licensed under the GNU GPL v2 or later.
+==========================================================================
+
+Slippymap plugin authors:
+==========================================================================
+A plugin for displaying a slippy map grid, with various server interaction
+options (download tiles, request tile updates etc.)
+
+Author: Frederik Ramm <frederik at remote.org>
+ Lubomir Varga <lubomir.varga at freemap.sk> or <luvar at plaintext.sk>
+Public Domain.
+
+Software with a little bit of customisation, fade background feature, autozoom, autoload tiles e.t.c. Just a begining of the best plugin for josm ;-)
+==========================================================================
diff --git a/imagery/build.xml b/imagery/build.xml
new file mode 100644
index 0000000..54c54e8
--- /dev/null
+++ b/imagery/build.xml
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+** This is a template build file for a JOSM plugin.
+**
+** Maintaining versions
+** ====================
+** see README.template
+**
+** Usage
+** =====
+** To build it run
+**
+** > ant dist
+**
+** To install the generated plugin locally (in you default plugin directory) run
+**
+** > ant install
+**
+** The generated plugin jar is not automatically available in JOSMs plugin configuration
+** dialog. You have to check it in first.
+**
+** Use the ant target 'publish' to check in the plugin and make it available to other
+** JOSM users:
+** set the properties commit.message and plugin.main.version
+** and run
+** > ant publish
+**
+**
+-->
+<project name="imagery" default="dist" basedir=".">
+
+ <!-- enter the SVN commit message -->
+ <property name="commit.message" value="Commit message" />
+ <!-- enter the *lowest* JOSM version this plugin is currently compatible with -->
+ <property name="plugin.main.version" value="3687" />
+
+
+ <!--
+ ************************************************
+ ** should not be necessary to change the following properties
+ -->
+ <property name="josm" location="../../core/dist/josm-custom.jar"/>
+ <property name="remotecontrol" location="../../dist/remotecontrol.jar" />
+ <property name="plugin.build.dir" value="build"/>
+ <property name="plugin.src.dir" value="src"/>
+ <!-- this is the directory where the plugin jar is copied to -->
+ <property name="plugin.dist.dir" value="../../dist"/>
+ <property name="ant.build.javac.target" value="1.5"/>
+ <property name="plugin.dist.dir" value="../../dist"/>
+ <property name="plugin.jar" value="${plugin.dist.dir}/${ant.project.name}.jar"/>
+
+ <!--
+ **********************************************************
+ ** init - initializes the build
+ **********************************************************
+ -->
+ <target name="init">
+ <mkdir dir="${plugin.build.dir}"/>
+ </target>
+
+ <!--
+ **********************************************************
+ ** compile - complies the source tree
+ **********************************************************
+ -->
+ <target name="compile" depends="init">
+ <echo message="compiling sources for ${plugin.jar} ... "/>
+ <javac srcdir="src" classpath="${josm};${remotecontrol}" debug="true" destdir="${plugin.build.dir}">
+ <compilerarg value="-Xlint:deprecation"/>
+ <compilerarg value="-Xlint:unchecked"/>
+ </javac>
+ </target>
+
+ <!--
+ **********************************************************
+ ** dist - creates the plugin jar
+ **********************************************************
+ -->
+ <target name="dist" depends="compile,revision">
+ <echo message="creating ${ant.project.name}.jar ... "/>
+ <copy todir="${plugin.build.dir}/resources">
+ <fileset dir="resources"/>
+ </copy>
+ <copy todir="${plugin.build.dir}/images">
+ <fileset dir="images"/>
+ </copy>
+ <copy todir="${plugin.build.dir}">
+ <fileset dir=".">
+ <include name="README" />
+ <include name="LICENSE" />
+ </fileset>
+ </copy>
+ <jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
+ <!--
+ ************************************************
+ ** configure these properties. Most of them will be copied to the plugins
+ ** manifest file. Property values will also show up in the list available
+ ** plugins: http://josm.openstreetmap.de/wiki/Plugins.
+ **
+ ************************************************
+ -->
+ <manifest>
+ <attribute name="Author" value="Tim Waters, Petr Dlouhý, Frederik Ramm, Upliner and others"/>
+ <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.imagery.ImageryPlugin"/>
+ <attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
+ <attribute name="Plugin-Description" value="Experimental union of SlippyMap plugin and WMSPlugin"/>
+ <attribute name="Plugin-Icon" value="images/imagery.png"/>
+ <attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/WMSPlugin" />
+ <attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
+ <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
+ </manifest>
+ </jar>
+ </target>
+
+ <!--
+ **********************************************************
+ ** revision - extracts the current revision number for the
+ ** file build.number and stores it in the XML property
+ ** version.*
+ **********************************************************
+ -->
+ <target name="revision">
+
+ <exec append="false" output="REVISION" executable="svn" failifexecutionfails="false">
+ <env key="LANG" value="C"/>
+ <arg value="info"/>
+ <arg value="--xml"/>
+ <arg value="."/>
+ </exec>
+ <xmlproperty file="REVISION" prefix="version" keepRoot="false" collapseAttributes="true"/>
+ <delete file="REVISION"/>
+ </target>
+
+ <!--
+ **********************************************************
+ ** clean - clean up the build environment
+ **********************************************************
+ -->
+ <target name="clean">
+ <delete dir="${plugin.build.dir}"/>
+ <delete file="${plugin.jar}"/>
+ </target>
+
+ <!--
+ **********************************************************
+ ** install - install the plugin in your local JOSM installation
+ **********************************************************
+ -->
+ <target name="install" depends="dist">
+ <property environment="env"/>
+ <condition property="josm.plugins.dir" value="${env.APPDATA}/JOSM/plugins" else="${user.home}/.josm/plugins">
+ <and>
+ <os family="windows"/>
+ </and>
+ </condition>
+ <copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
+ </target>
+
+
+ <!--
+ ************************** 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="commit"/>
+ <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>
+
+ <!--
+ ** 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>
+
+ <!-- ** make sure svn is present as a command line tool ** -->
+ <target name="ensure-svn-present">
+ <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false" failonerror="false" resultproperty="svn.exit.code">
+ <env key="LANG" value="C" />
+ <arg value="--version" />
+ </exec>
+ <fail message="Fatal: command 'svn --version' failed. Please make sure svn is installed on your system.">
+ <!-- return code not set at all? Most likely svn isn't installed -->
+ <condition>
+ <not>
+ <isset property="svn.exit.code" />
+ </not>
+ </condition>
+ </fail>
+ <fail message="Fatal: command 'svn --version' failed. Please make sure a working copy of svn is installed on your system.">
+ <!-- error code from SVN? Most likely svn is not what we are looking on this system -->
+ <condition>
+ <isfailure code="${svn.exit.code}" />
+ </condition>
+ </fail>
+ </target>
+
+ <target name="publish" depends="ensure-svn-present,core-info,commit-current,update-current,clean,dist,commit-dist">
+ </target>
+</project>
diff --git a/imagery/images/OLmarker.png b/imagery/images/OLmarker.png
new file mode 100644
index 0000000..b1917f2
Binary files /dev/null and b/imagery/images/OLmarker.png differ
diff --git a/imagery/images/bing_maps.png b/imagery/images/bing_maps.png
new file mode 100644
index 0000000..ae4367e
Binary files /dev/null and b/imagery/images/bing_maps.png differ
diff --git a/imagery/images/blankmenu.png b/imagery/images/blankmenu.png
new file mode 100644
index 0000000..640fbde
Binary files /dev/null and b/imagery/images/blankmenu.png differ
diff --git a/imagery/images/cursor/modifier/move.png b/imagery/images/cursor/modifier/move.png
new file mode 100644
index 0000000..54a0b84
Binary files /dev/null and b/imagery/images/cursor/modifier/move.png differ
diff --git a/imagery/images/imagery.png b/imagery/images/imagery.png
new file mode 100644
index 0000000..447fbf2
Binary files /dev/null and b/imagery/images/imagery.png differ
diff --git a/imagery/images/imagery_menu.png b/imagery/images/imagery_menu.png
new file mode 100644
index 0000000..a7a657b
Binary files /dev/null and b/imagery/images/imagery_menu.png differ
diff --git a/imagery/images/imagery_small.png b/imagery/images/imagery_small.png
new file mode 100644
index 0000000..770713c
Binary files /dev/null and b/imagery/images/imagery_small.png differ
diff --git a/imagery/images/load.png b/imagery/images/load.png
new file mode 100644
index 0000000..2019dc8
Binary files /dev/null and b/imagery/images/load.png differ
diff --git a/imagery/images/mapmode/adjustimg.png b/imagery/images/mapmode/adjustimg.png
new file mode 100644
index 0000000..b592bd0
Binary files /dev/null and b/imagery/images/mapmode/adjustimg.png differ
diff --git a/imagery/images/preferences/imagery.png b/imagery/images/preferences/imagery.png
new file mode 100644
index 0000000..115b1e5
Binary files /dev/null and b/imagery/images/preferences/imagery.png differ
diff --git a/imagery/images/save.png b/imagery/images/save.png
new file mode 100644
index 0000000..2949b3f
Binary files /dev/null and b/imagery/images/save.png differ
diff --git a/imagery/images_nodist/imagery.xcf b/imagery/images_nodist/imagery.xcf
new file mode 100644
index 0000000..7eab2db
Binary files /dev/null and b/imagery/images_nodist/imagery.xcf differ
diff --git a/imagery/sources.cfg b/imagery/sources.cfg
new file mode 100644
index 0000000..3fd66c2
--- /dev/null
+++ b/imagery/sources.cfg
@@ -0,0 +1,69 @@
+# OUTDATED - only for old plugins
+# See http://josm.openstreetmap.de/wiki/Maps for newer data.
+#
+# FORMAT
+# default(true or false);Name;URL
+# NOTE: default items should be common and worldwide
+#
+true;Landsat;wms:http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&layers=global_mosaic&styles=&format=image/jpeg&
+true;Landsat (mirror);wms:http://irs.gis-lab.info/?layers=landsat&
+false;Open Aerial Map;wms:http://openaerialmap.org/wms/?VERSION=1.0&request=GetMap&layers=world&styles=&format=image/jpeg&
+#
+# different forms of imagery
+true;Bing sat;bing:bing
+true;Yahoo Sat;html:http://josm.openstreetmap.de/wmsplugin/YahooDirect.html?
+true;OpenStreetMap;tms:http://tile.openstreetmap.org/
+false;OpenCycleMap;tms:http://tile.opencyclemap.org/cycle/
+false;TilesAtHome;tms:http://tah.openstreetmap.org/Tiles/tile/
+#
+#
+# only for Germany
+false;Streets NRW Geofabrik.de;wms:http://tools.geofabrik.de/osmi/view/strassennrw/josmwms?
+#
+#
+# only for North America
+# Terraserver USCG - High resolution maps
+false;Terraserver Topo;wms:http://terraservice.net/ogcmap.ashx?version=1.1.1&request=GetMap&Layers=drg&styles=&format=image/jpeg&
+false;Terraserver Urban;wms: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;wms:http://wms.cuzk.cz/wms.asp?service=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=parcelni_cisla_i,obrazy_parcel_i,RST_KMD_I,hranice_parcel_i,DEF_BUDOVY,RST_KN_I,dalsi_p_mapy_i,prehledka_kat_prac,prehledka_kat_uz,prehledka_kraju-linie&FORMAT=image/png&transparent=TRUE&
+false;Czech UHUL:ORTOFOTO;wms: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;wms:http://nick.dev.openstreetmap.org/openpaths/freemap.php?layers=npe&
+false;NPE Maps (Tim);wms:http://dev.openstreetmap.org/~timsc/wms2/map.php?
+false;7th Series (OS7);wms:http://ooc.openstreetmap.org/wms/map.php?source=os7&
+#
+#
+# only for Japan
+false;MLIT Japan (ORTHO);wms: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);wms: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);wms: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);wms: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;wms: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;wms:http://88.53.214.52/sitr/services/WGS84_F33/Ortofoto_ATA20072008_f33/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=CRS:84&LAYERS=0&STYLES=default&FORMAT=image/jpeg&
+false;PCN 2006 - Italy;wms:http://wms.pcn.minambiente.it/cgi-bin/mapserv.exe?map=/ms_ogc/service/ortofoto_colore_06.map&LAYERS=ortofoto_colore_06_32,ortofoto_colore_06_33&REQUEST=GetMap&VERSION=1.1.1&FORMAT=image%2Fjpeg&
+#
+# only for France
+false;SPOTMaps (France);wms:http://spotmaps.youmapps.org/cgi-bin/mapserv?map=/home/ortho/ortho.map&service=wms&version=1.1.1&srs=EPSG:4326&request=GetMap&layers=spotmaps4osm&format=image/jpeg&FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=demo&;http://www.youmapps.org/licenses/EULA-OSM-J-{lang}.html
+#
+#
+# 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
+# and an error is issued when they mismatch).
+#
+# If more specific URL design is needed, then patterns are supported as well. If
+# patterns are found no other arguments are added:
+# {proj} is replaced by projection
+# {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/imagery/src/org/openstreetmap/josm/plugins/imagery/AddImageryLayerAction.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/AddImageryLayerAction.java
new file mode 100644
index 0000000..a78f56c
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/AddImageryLayerAction.java
@@ -0,0 +1,25 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class AddImageryLayerAction extends JosmAction {
+
+ private final ImageryInfo info;
+
+ public AddImageryLayerAction(ImageryInfo info) {
+ super(info.getMenuName(), "imagery_menu", tr("Add imagery layer {0}",info.getName()), null, false);
+ putValue("toolbar", "imagery_" + info.getToolbarName());
+ this.info = info;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ ImageryLayer wmsLayer = ImageryLayer.create(info);
+ Main.main.addLayer(wmsLayer);
+ }
+};
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryAdjustAction.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryAdjustAction.java
new file mode 100644
index 0000000..d6b2acf
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryAdjustAction.java
@@ -0,0 +1,207 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.GridBagLayout;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.List;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.Icon;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+
+public class ImageryAdjustAction extends MapMode implements MouseListener, MouseMotionListener{
+
+ boolean mouseDown;
+ EastNorth prevEastNorth;
+ private ImageryLayer adjustingLayer;
+
+ public ImageryAdjustAction(MapFrame mapFrame) {
+ super(tr("Adjust imagery"), "adjustimg",
+ tr("Adjust the position of the selected imagery layer"), mapFrame,
+ ImageProvider.getCursor("normal", "move"));
+ }
+
+ @Override public void enterMode() {
+ super.enterMode();
+ if (!hasLayersToAdjust()) {
+ warnNoImageryLayers();
+ return;
+ }
+ List<ImageryLayer> imageryLayers = Main.map.mapView.getLayersOfType(ImageryLayer.class);
+ if (imageryLayers.size() == 1) {
+ adjustingLayer = imageryLayers.get(0);
+ } else {
+ adjustingLayer = (ImageryLayer)askAdjustLayer(Main.map.mapView.getLayersOfType(ImageryLayer.class));
+ }
+ if (adjustingLayer == null)
+ return;
+ if (!adjustingLayer.isVisible()) {
+ adjustingLayer.setVisible(true);
+ }
+ 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);
+ adjustingLayer = null;
+ }
+
+ @Override public void mousePressed(MouseEvent e) {
+ if (e.getButton() != MouseEvent.BUTTON1)
+ return;
+
+ if (adjustingLayer.isVisible()) {
+ prevEastNorth=Main.map.mapView.getEastNorth(e.getX(),e.getY());
+ Main.map.mapView.setCursor
+ (Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+ }
+ }
+
+ @Override public void mouseDragged(MouseEvent e) {
+ if (adjustingLayer == null || prevEastNorth == null) return;
+ EastNorth eastNorth =
+ Main.map.mapView.getEastNorth(e.getX(),e.getY());
+ adjustingLayer.displace(
+ eastNorth.east()-prevEastNorth.east(),
+ eastNorth.north()-prevEastNorth.north()
+ );
+ prevEastNorth = eastNorth;
+ Main.map.mapView.repaint();
+ }
+
+ @Override public void mouseReleased(MouseEvent e) {
+ Main.map.mapView.repaint();
+ Main.map.mapView.setCursor(Cursor.getDefaultCursor());
+ prevEastNorth = null;
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ @Override public void mouseClicked(MouseEvent e) {
+ }
+
+ @Override public boolean layerIsSupported(Layer l) {
+ return hasLayersToAdjust();
+ }
+
+ /**
+ * the list cell renderer used to render layer list entries
+ *
+ */
+ static public class LayerListCellRenderer extends DefaultListCellRenderer {
+
+ protected boolean isActiveLayer(Layer layer) {
+ if (Main.map == null)
+ return false;
+ if (Main.map.mapView == null)
+ return false;
+ return Main.map.mapView.getActiveLayer() == layer;
+ }
+
+ @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;
+ }
+ }
+
+ /**
+ * Prompts the user with a list of imagery layers which can be adjusted
+ *
+ * @param adjustableLayers the list of adjustable layers
+ * @return the selected layer; null, if no layer was selected
+ */
+ protected Layer askAdjustLayer(List<? extends Layer> adjustableLayers) {
+ JComboBox layerList = new JComboBox();
+ layerList.setRenderer(new LayerListCellRenderer());
+ layerList.setModel(new DefaultComboBoxModel(adjustableLayers.toArray()));
+ layerList.setSelectedIndex(0);
+
+ JPanel pnl = new JPanel();
+ pnl.setLayout(new GridBagLayout());
+ pnl.add(new JLabel(tr("Please select the imagery layer to adjust.")), GBC.eol());
+ pnl.add(layerList, GBC.eol());
+
+ ExtendedDialog diag = new ExtendedDialog(
+ Main.parent,
+ tr("Select imagery layer"),
+ new String[] { tr("Start adjusting"),tr("Cancel") }
+ );
+ diag.setContent(pnl);
+ diag.setButtonIcons(new String[] { "mapmode/adjustimg", "cancel" });
+ diag.showDialog();
+ int decision = diag.getValue();
+ if (decision != 1)
+ return null;
+ Layer adjustLayer = (Layer) layerList.getSelectedItem();
+ return adjustLayer;
+ }
+
+ /**
+ * Displays a warning message if there are no imagery layers to adjust
+ *
+ */
+ protected void warnNoImageryLayers() {
+ JOptionPane.showMessageDialog(
+ Main.parent,
+ tr("There are currently no imagery layer to adjust."),
+ tr("No layers to adjust"),
+ JOptionPane.WARNING_MESSAGE
+ );
+ }
+
+ /**
+ * Replies true if there is at least one WMS layer
+ *
+ * @return true if there is at least one WMS layer
+ */
+ protected boolean hasLayersToAdjust() {
+ if (Main.map == null) return false;
+ if (Main.map.mapView == null) return false;
+ return ! Main.map.mapView.getLayersOfType(ImageryLayer.class).isEmpty();
+ }
+
+ @Override
+ protected void updateEnabledState() {
+ setEnabled(hasLayersToAdjust());
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryInfo.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryInfo.java
new file mode 100644
index 0000000..88c50b7
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryInfo.java
@@ -0,0 +1,185 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Class that stores info about a WMS server.
+ *
+ * @author Frederik Ramm <frederik at remote.org>
+ */
+public class ImageryInfo implements Comparable<ImageryInfo> {
+ public enum ImageryType {
+ WMS("wms"),
+ TMS("tms"),
+ HTML("html"),
+ BING("bing");
+
+ private String urlString;
+ ImageryType(String urlString) {
+ this.urlString = urlString;
+ }
+ public String getUrlString() {
+ return urlString;
+ }
+ }
+
+ String name;
+ String url=null;
+ String cookies = null;
+ String eulaAcceptanceRequired = null;
+ ImageryType imageryType = ImageryType.WMS;
+ double pixelPerDegree = 0.0;
+ int maxZoom = 0;
+
+ public ImageryInfo(String name) {
+ this.name=name;
+ }
+
+ public ImageryInfo(String name, String url) {
+ this.name=name;
+ setURL(url);
+ }
+
+ public ImageryInfo(String name, String url, String eulaAcceptanceRequired) {
+ this.name=name;
+ setURL(url);
+ this.eulaAcceptanceRequired = eulaAcceptanceRequired;
+ }
+
+ public ImageryInfo(String name, String url, String eulaAcceptanceRequired, String cookies) {
+ this.name=name;
+ setURL(url);
+ this.cookies=cookies;
+ }
+
+ public ImageryInfo(String name, String url, String cookies, double pixelPerDegree) {
+ this.name=name;
+ setURL(url);
+ this.cookies=cookies;
+ this.pixelPerDegree=pixelPerDegree;
+ }
+
+ public ArrayList<String> getInfoArray() {
+ String e2 = null;
+ String e3 = null;
+ String e4 = null;
+ if(url != null && !url.isEmpty()) e2 = getFullURL();
+ if(cookies != null && !cookies.isEmpty()) e3 = cookies;
+ if(imageryType == ImageryType.WMS) {
+ if(pixelPerDegree != 0.0) e4 = String.valueOf(pixelPerDegree);
+ } else {
+ if(maxZoom != 0) e4 = String.valueOf(maxZoom);
+ }
+ if(e4 != null && e3 == null) e3 = "";
+ if(e3 != null && e2 == null) e2 = "";
+
+ ArrayList<String> res = new ArrayList<String>();
+ res.add(name);
+ if(e2 != null) res.add(e2);
+ if(e3 != null) res.add(e3);
+ if(e4 != null) res.add(e4);
+ return res;
+ }
+
+ public ImageryInfo(Collection<String> list) {
+ ArrayList<String> array = new ArrayList<String>(list);
+ this.name=array.get(0);
+ if(array.size() >= 2) setURL(array.get(1));
+ if(array.size() >= 3) this.cookies=array.get(2);
+ if(imageryType == ImageryType.WMS && array.size() >= 4) this.pixelPerDegree=Double.valueOf(array.get(3));
+ if(imageryType == ImageryType.TMS && array.size() >= 4) this.maxZoom=Integer.valueOf(array.get(3));
+ }
+
+ public ImageryInfo(ImageryInfo i) {
+ this.name=i.name;
+ this.url=i.url;
+ this.cookies=i.cookies;
+ this.imageryType=i.imageryType;
+ this.pixelPerDegree=i.pixelPerDegree;
+ }
+
+ @Override
+ public int compareTo(ImageryInfo in)
+ {
+ int i = name.compareTo(in.name);
+ if(i == 0)
+ i = url.compareTo(in.url);
+ if(i == 0)
+ i = Double.compare(pixelPerDegree, in.pixelPerDegree);
+ return i;
+ }
+
+ public boolean equalsBaseValues(ImageryInfo in)
+ {
+ return url.equals(in.url);
+ }
+
+ public void setPixelPerDegree(double ppd) {
+ this.pixelPerDegree = ppd;
+ }
+
+ public void setURL(String url) {
+ for (ImageryType type : ImageryType.values()) {
+ if (url.startsWith(type.getUrlString() + ":")) {
+ this.url = url.substring(type.getUrlString().length() + 1);
+ this.imageryType = type;
+ return;
+ }
+ }
+
+ // Default imagery type is WMS
+ this.url = url;
+ this.imageryType = ImageryType.WMS;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getURL() {
+ return this.url;
+ }
+
+ public String getCookies() {
+ return this.cookies;
+ }
+
+ public double getPixelPerDegree() {
+ return this.pixelPerDegree;
+ }
+
+ public int getMaxZoom() {
+ return this.maxZoom;
+ }
+
+ public String getFullURL() {
+ return imageryType.getUrlString() + ":" + url;
+ }
+
+ public String getToolbarName()
+ {
+ String res = name;
+ if(pixelPerDegree != 0.0)
+ res += "#PPD="+pixelPerDegree;
+ return res;
+ }
+
+ public String getMenuName()
+ {
+ String res = name;
+ if(pixelPerDegree != 0.0)
+ res += " ("+pixelPerDegree+")";
+ else if(maxZoom != 0)
+ res += " (z"+maxZoom+")";
+ return res;
+ }
+
+ public ImageryType getImageryType() {
+ return imageryType;
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryLayer.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryLayer.java
new file mode 100644
index 0000000..3187256
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryLayer.java
@@ -0,0 +1,81 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import java.awt.Toolkit;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.ProjectionBounds;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo.ImageryType;
+import org.openstreetmap.josm.plugins.imagery.tms.TMSLayer;
+import org.openstreetmap.josm.plugins.imagery.wms.WMSLayer;
+
+public abstract class ImageryLayer extends Layer {
+ protected static final Icon icon =
+ new ImageIcon(Toolkit.getDefaultToolkit().createImage(ImageryPlugin.class.getResource("/images/imagery_small.png")));
+
+ protected ImageryInfo info;
+ protected MapView mv;
+
+ protected double dx = 0.0;
+ protected double dy = 0.0;
+
+ public ImageryLayer(ImageryInfo info) {
+ super(info.getName());
+ this.info = info;
+ this.mv = Main.map.mapView;
+ }
+
+
+ public double getPPD(){
+ ProjectionBounds bounds = mv.getProjectionBounds();
+ return mv.getWidth() / (bounds.max.east() - bounds.min.east());
+ }
+
+ public void displace(double dx, double dy) {
+ this.dx += dx;
+ this.dy += dy;
+ }
+
+ public double getDx() {
+ return dx;
+ }
+
+ public double getDy() {
+ return dy;
+ }
+
+ public ImageryInfo getInfo() {
+ return info;
+ }
+
+ @Override
+ public Icon getIcon() {
+ return icon;
+ }
+
+ @Override
+ public boolean isMergable(Layer other) {
+ return false;
+ }
+
+ @Override
+ public void mergeFrom(Layer from) {
+ }
+
+ @Override
+ public Object getInfoComponent() {
+ return getToolTipText();
+ }
+
+ public static ImageryLayer create(ImageryInfo info) {
+ if (info.imageryType == ImageryType.WMS || info.imageryType == ImageryType.HTML) {
+ return new WMSLayer(info);
+ } else if (info.imageryType == ImageryType.TMS || info.imageryType == ImageryType.BING) {
+ return new TMSLayer(info);
+ } else throw new AssertionError();
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryLayerInfo.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryLayerInfo.java
new file mode 100644
index 0000000..a648566
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryLayerInfo.java
@@ -0,0 +1,104 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.io.MirroredInputStream;
+
+public class ImageryLayerInfo {
+ ArrayList<ImageryInfo> layers = new ArrayList<ImageryInfo>();
+ ArrayList<ImageryInfo> defaultLayers = new ArrayList<ImageryInfo>();
+ private final static String[] DEFAULT_LAYER_SITES
+ = { "http://josm.openstreetmap.de/maps"};
+
+ public void load() {
+ layers.clear();
+ Collection<String> defaults = Main.pref.getCollection(
+ "imagery.layers.default", Collections.<String>emptySet());
+ for(Collection<String> c : Main.pref.getArray("imagery.layers",
+ Collections.<Collection<String>>emptySet())) {
+ layers.add(new ImageryInfo(c));
+ }
+
+ ArrayList<String> defaultsSave = new ArrayList<String>();
+ for(String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES)))
+ {
+ try
+ {
+ MirroredInputStream s = new MirroredInputStream(source, ImageryPlugin.instance.getPluginDir(), -1);
+ InputStreamReader r;
+ try
+ {
+ r = new InputStreamReader(s, "UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ r = new InputStreamReader(s);
+ }
+ BufferedReader reader = new BufferedReader(r);
+ String line;
+ while((line = reader.readLine()) != null)
+ {
+ String val[] = line.split(";");
+ if(!line.startsWith("#") && (val.length == 3 || val.length == 4)) {
+ boolean force = "true".equals(val[0]);
+ String name = tr(val[1]);
+ String url = val[2];
+ String eulaAcceptanceRequired = null;
+ if (val.length == 4) {
+ // 4th parameter optional for license agreement (EULA)
+ eulaAcceptanceRequired = val[3];
+ }
+ defaultLayers.add(new ImageryInfo(name, url, eulaAcceptanceRequired));
+
+ if(force) {
+ defaultsSave.add(url);
+ if(!defaults.contains(url)) {
+ for(ImageryInfo i : layers) {
+ if(url.equals(i.url))
+ force = false;
+ }
+ if(force)
+ layers.add(new ImageryInfo(name, url));
+ }
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ Main.pref.putCollection("imagery.layers.default", defaultsSave.size() > 0
+ ? defaultsSave : defaults);
+ Collections.sort(layers);
+ save();
+ }
+
+ public void add(ImageryInfo info) {
+ layers.add(info);
+ }
+
+ public void remove(ImageryInfo info) {
+ layers.remove(info);
+ }
+
+ public void save() {
+ LinkedList<Collection<String>> coll = new LinkedList<Collection<String>>();
+ for (ImageryInfo info : layers) {
+ coll.add(info.getInfoArray());
+ }
+ Main.pref.putArray("imagery.layers", coll);
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPlugin.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPlugin.java
new file mode 100644
index 0000000..393a8be
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPlugin.java
@@ -0,0 +1,273 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+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.KeyEvent;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.PluginHandler;
+import org.openstreetmap.josm.plugins.PluginInformation;
+import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.plugins.imagery.wms.Map_Rectifier_WMSmenuAction;
+import org.openstreetmap.josm.plugins.imagery.wms.WMSAdapter;
+import org.openstreetmap.josm.plugins.imagery.wms.WMSLayer;
+import org.openstreetmap.josm.plugins.imagery.wms.io.WMSLayerExporter;
+import org.openstreetmap.josm.plugins.imagery.wms.io.WMSLayerImporter;
+
+public class ImageryPlugin extends Plugin {
+
+ JMenu imageryJMenu;
+
+ public static ImageryPlugin instance;
+ public static WMSAdapter wmsAdapter = new WMSAdapter();
+
+ public ImageryLayerInfo info = new ImageryLayerInfo();
+
+ // remember state of menu item to restore on changed preferences
+ private boolean menuEnabled = false;
+
+ /***************************************************************
+ * Remote control initialization:
+ * If you need remote control in some other plug-in
+ * copy this stuff and the call to initRemoteControl below
+ * and replace the RequestHandler subclass in initRemoteControl
+ ***************************************************************/
+
+ /** name of remote control plugin */
+ private final String REMOTECONTROL_NAME = "remotecontrol";
+
+ /* if necessary change these version numbers to ensure compatibility */
+
+ /** RemoteControlPlugin older than this SVN revision is not compatible */
+ final int REMOTECONTROL_MIN_REVISION = 22734;
+ /** WMSPlugin needs this specific API major version of RemoteControlPlugin */
+ final int REMOTECONTROL_NEED_API_MAJOR = 1;
+ /** All API minor versions starting from this should be compatible */
+ final int REMOTECONTROL_MIN_API_MINOR = 0;
+
+ /* these fields will contain state and version of remote control plug-in */
+ boolean remoteControlAvailable = false;
+ boolean remoteControlCompatible = true;
+ boolean remoteControlInitialized = false;
+ int remoteControlRevision = 0;
+ int remoteControlApiMajor = 0;
+ int remoteControlApiMinor = 0;
+ int remoteControlProtocolMajor = 0;
+ int remoteControlProtocolMinor = 0;
+
+ /**
+ * Check if remote control plug-in is available and if its version is
+ * high enough and register remote control command for this plug-in.
+ */
+ private void initRemoteControl() {
+ for(PluginProxy pp: PluginHandler.pluginList)
+ {
+ PluginInformation info = pp.getPluginInformation();
+ if(REMOTECONTROL_NAME.equals(info.name))
+ {
+ remoteControlAvailable = true;
+ remoteControlRevision = Integer.parseInt(info.version);
+ if(REMOTECONTROL_MIN_REVISION > remoteControlRevision)
+ {
+ remoteControlCompatible = false;
+ }
+ }
+ }
+
+ if(remoteControlAvailable && remoteControlCompatible)
+ {
+ Plugin plugin =
+ (Plugin) PluginHandler.getPlugin(REMOTECONTROL_NAME);
+ try {
+ Method method;
+ method = plugin.getClass().getMethod("getVersion");
+ Object obj = method.invoke(plugin);
+ if((obj != null ) && (obj instanceof int[]))
+ {
+ int[] versions = (int[]) obj;
+ if(versions.length >= 4)
+ {
+ remoteControlApiMajor = versions[0];
+ remoteControlApiMinor = versions[1];
+ remoteControlProtocolMajor = versions[2];
+ remoteControlProtocolMinor = versions[3];
+ }
+ }
+
+ if((remoteControlApiMajor != REMOTECONTROL_NEED_API_MAJOR) ||
+ (remoteControlApiMinor < REMOTECONTROL_MIN_API_MINOR))
+ {
+ remoteControlCompatible = false;
+ }
+ if(remoteControlCompatible)
+ {
+ System.out.println(this.getClass().getSimpleName() + ": initializing remote control");
+ method = plugin.getClass().getMethod("addRequestHandler", String.class, Class.class);
+ // replace command and class when you copy this to some other plug-in
+ // for compatibility with old remotecontrol add leading "/"
+ method.invoke(plugin, "/" + ImageryRemoteHandler.command, ImageryRemoteHandler.class);
+ remoteControlInitialized = true;
+ }
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+ if(remoteControlAvailable)
+ {
+ String msg = null;
+
+ if(remoteControlCompatible)
+ {
+ if(!remoteControlInitialized)
+ {
+ msg = tr("Could not initialize remote control.");
+ }
+ }
+ else
+ {
+ msg = tr("Remote control plugin is not compatible with {0}.",
+ this.getClass().getSimpleName());
+ }
+
+ if(msg != null)
+ {
+ String additionalMessage = tr("{0} will work but remote control for this plugin is disabled.\n"
+ + "You should update the plugins.",
+ this.getClass().getSimpleName());
+ String versionMessage = tr("Current version of \"{1}\": {2}, internal version {3}. "
+ + "Need version {4}, internal version {5}.\n"
+ + "If updating the plugins does not help report a bug for \"{0}\".",
+ this.getClass().getSimpleName(),
+ REMOTECONTROL_NAME,
+ ""+remoteControlRevision,
+ (remoteControlApiMajor != 0) ?
+ ""+remoteControlApiMajor+"."+remoteControlApiMinor :
+ tr("unknown"),
+ ""+REMOTECONTROL_MIN_REVISION,
+ ""+REMOTECONTROL_NEED_API_MAJOR+"."+REMOTECONTROL_MIN_API_MINOR );
+
+ String title = tr("{0}: Problem with remote control",
+ this.getClass().getSimpleName());
+
+ System.out.println(this.getClass().getSimpleName() + ": " +
+ msg + "\n" + versionMessage);
+
+ JOptionPane.showMessageDialog(
+ Main.parent,
+ msg + "\n" + additionalMessage,
+ title,
+ JOptionPane.WARNING_MESSAGE
+ );
+ }
+ }
+
+ if(!remoteControlAvailable) {
+ System.out.println(this.getClass().getSimpleName() + ": remote control not available");
+ }
+ }
+
+ /***************************************
+ * end of remote control initialization
+ ***************************************/
+
+ protected void initExporterAndImporter() {
+ ExtensionFileFilter.exporters.add(new WMSLayerExporter());
+ ExtensionFileFilter.importers.add(new WMSLayerImporter());
+ }
+
+ public ImageryPlugin(PluginInformation info) {
+ super(info);
+ instance = this;
+ this.info.load();
+ refreshMenu();
+ initRemoteControl();
+ }
+
+ public void addLayer(ImageryInfo info) {
+ this.info.add(info);
+ this.info.save();
+ refreshMenu();
+ }
+
+ public void refreshMenu() {
+ MainMenu menu = Main.main.menu;
+
+ if (imageryJMenu == null)
+ imageryJMenu = menu.addMenu(marktr("Imagery"), KeyEvent.VK_W, menu.defaultMenuPos, ht("/Plugin/Imagery"));
+ else
+ imageryJMenu.removeAll();
+
+ // for each configured WMSInfo, add a menu entry.
+ for (final ImageryInfo u : info.layers) {
+ imageryJMenu.add(new JMenuItem(new AddImageryLayerAction(u)));
+ }
+ imageryJMenu.addSeparator();
+ imageryJMenu.add(new JMenuItem(new Map_Rectifier_WMSmenuAction()));
+
+ imageryJMenu.addSeparator();
+ imageryJMenu.add(new JMenuItem(new
+ JosmAction(tr("Blank Layer"), "blankmenu", tr("Open a blank WMS layer to load data from a file"), null, false) {
+ @Override
+ public void actionPerformed(ActionEvent ev) {
+ Main.main.addLayer(new WMSLayer());
+ }
+ }));
+ setEnabledAll(menuEnabled);
+ }
+
+ private void setEnabledAll(boolean isEnabled) {
+ for(int i=0; i < imageryJMenu.getItemCount(); i++) {
+ JMenuItem item = imageryJMenu.getItem(i);
+
+ if(item != null) item.setEnabled(isEnabled);
+ }
+ menuEnabled = isEnabled;
+ }
+
+ @Override
+ public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+ if (oldFrame==null && newFrame!=null) {
+ setEnabledAll(true);
+ Main.map.addMapMode(new IconToggleButton
+ (new ImageryAdjustAction(Main.map)));
+ } else if (oldFrame!=null && newFrame==null ) {
+ setEnabledAll(false);
+ }
+ }
+
+ @Override
+ public PreferenceSetting getPreferenceSetting() {
+ return new ImageryPreferenceEditor();
+ }
+
+ @Override
+ public String getPluginDir()
+ {
+ return new File(Main.pref.getPluginsDirectory(), "imagery").getPath();
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferenceEditor.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferenceEditor.java
new file mode 100644
index 0000000..92e1a43
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferenceEditor.java
@@ -0,0 +1,540 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trc;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Locale;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JColorChooser;
+import javax.swing.JComboBox;
+import javax.swing.JEditorPane;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableColumnModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo.ImageryType;
+import org.openstreetmap.josm.plugins.imagery.tms.TMSPreferences;
+import org.openstreetmap.josm.plugins.imagery.wms.AddWMSLayerPanel;
+import org.openstreetmap.josm.plugins.imagery.wms.WMSAdapter;
+import org.openstreetmap.josm.tools.ColorHelper;
+import org.openstreetmap.josm.tools.GBC;
+
+public class ImageryPreferenceEditor implements PreferenceSetting {
+ private ImageryLayerTableModel model;
+ private JComboBox browser;
+
+ // Common settings
+ private Color colFadeColor;
+ private JButton btnFadeColor;
+ private JSlider fadeAmount = new JSlider(0, 100);
+ private JCheckBox remoteCheckBox;
+ boolean allowRemoteControl = true;
+
+ // WMS Settings
+ JCheckBox overlapCheckBox;
+ JSpinner spinEast;
+ JSpinner spinNorth;
+ JSpinner spinSimConn;
+ WMSAdapter wmsAdapter = ImageryPlugin.wmsAdapter;
+ ImageryPlugin plugin = ImageryPlugin.instance;
+
+ //TMS settings controls
+ private JCheckBox autozoomActive = new JCheckBox();
+ private JCheckBox autoloadTiles = new JCheckBox();
+ private JSpinner minZoomLvl;
+ private JSpinner maxZoomLvl;
+
+
+ private JPanel buildImageryProvidersPanel(final PreferenceTabbedPane gui) {
+ final JPanel p = new JPanel(new GridBagLayout());
+ model = new ImageryLayerTableModel();
+ final JTable list = new JTable(model) {
+ @Override
+ public String getToolTipText(MouseEvent e) {
+ java.awt.Point p = e.getPoint();
+ return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
+ }
+ };
+ JScrollPane scroll = new JScrollPane(list);
+ p.add(scroll, GBC.eol().fill(GridBagConstraints.BOTH));
+ scroll.setPreferredSize(new Dimension(200, 200));
+
+ final ImageryDefaultLayerTableModel modeldef = new ImageryDefaultLayerTableModel();
+ final JTable listdef = new JTable(modeldef) {
+ @Override
+ public String getToolTipText(MouseEvent e) {
+ java.awt.Point p = e.getPoint();
+ return (String) modeldef.getValueAt(rowAtPoint(p), columnAtPoint(p));
+ }
+ };
+ JScrollPane scrolldef = new JScrollPane(listdef);
+ // scrolldef is added after the buttons so it's clearer the buttons
+ // control the top list and not the default one
+ scrolldef.setPreferredSize(new Dimension(200, 200));
+
+ TableColumnModel mod = listdef.getColumnModel();
+ mod.getColumn(1).setPreferredWidth(800);
+ mod.getColumn(0).setPreferredWidth(200);
+ mod = list.getColumnModel();
+ mod.getColumn(2).setPreferredWidth(50);
+ mod.getColumn(1).setPreferredWidth(800);
+ mod.getColumn(0).setPreferredWidth(200);
+
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+
+ JButton add = new JButton(tr("Add"));
+ buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0));
+ add.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ AddWMSLayerPanel p = new AddWMSLayerPanel();
+ int answer = JOptionPane.showConfirmDialog(
+ gui, p,
+ tr("Add Imagery URL"),
+ JOptionPane.OK_CANCEL_OPTION);
+ if (answer == JOptionPane.OK_OPTION) {
+ model.addRow(new ImageryInfo(p.getUrlName(), p.getUrl()));
+ }
+ }
+ });
+
+ JButton delete = new JButton(tr("Delete"));
+ buttonPanel.add(delete, GBC.std().insets(0, 5, 0, 0));
+ delete.addActionListener(new ActionListener() {
+ @Override
+ 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 copy = new JButton(tr("Copy Selected Default(s)"));
+ buttonPanel.add(copy, GBC.std().insets(0, 5, 0, 0));
+ copy.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ 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);
+ return;
+ }
+
+ outer: for (int i = 0; i < lines.length; i++) {
+ ImageryInfo info = modeldef.getRow(lines[i]);
+
+ // Check if an entry with exactly the same values already
+ // exists
+ for (int j = 0; j < model.getRowCount(); j++) {
+ if (info.equalsBaseValues(model.getRow(j))) {
+ // 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;
+ }
+ }
+
+ if (info.eulaAcceptanceRequired != null) {
+ if (!confirmeEulaAcceptance(gui, info.eulaAcceptanceRequired))
+ continue outer;
+ }
+
+ model.addRow(new ImageryInfo(info));
+ int lastLine = model.getRowCount() - 1;
+ list.getSelectionModel().setSelectionInterval(lastLine, lastLine);
+ list.scrollRectToVisible(list.getCellRect(lastLine, 0, true));
+ }
+ }
+ });
+
+ p.add(buttonPanel);
+ p.add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
+ // Add default item list
+ p.add(scrolldef, GBC.eol().insets(0, 5, 0, 0).fill(GridBagConstraints.BOTH));
+
+ return p;
+ }
+
+ private JPanel buildCommonSettingsPanel(final PreferenceTabbedPane gui) {
+ final JPanel p = new JPanel(new GridBagLayout());
+
+ this.colFadeColor = ImageryPreferences.getFadeColor();
+ this.btnFadeColor = new JButton();
+ this.btnFadeColor.setBackground(colFadeColor);
+ this.btnFadeColor.setText(ColorHelper.color2html(colFadeColor));
+
+ this.btnFadeColor.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JColorChooser chooser = new JColorChooser(colFadeColor);
+ int answer = JOptionPane.showConfirmDialog(
+ gui, chooser,
+ tr("Choose a color for {0}", tr("imagery fade")),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.PLAIN_MESSAGE);
+ if (answer == JOptionPane.OK_OPTION) {
+ colFadeColor = chooser.getColor();
+ btnFadeColor.setBackground(colFadeColor);
+ btnFadeColor.setText(ColorHelper.color2html(colFadeColor));
+ }
+ }
+ });
+
+ p.add(new JLabel(tr("Fade Color: ")), GBC.std());
+ p.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+ p.add(this.btnFadeColor, GBC.eol().fill(GBC.HORIZONTAL));
+
+ p.add(new JLabel(tr("Fade amount: ")), GBC.std());
+ p.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+ p.add(this.fadeAmount, GBC.eol().fill(GBC.HORIZONTAL));
+ this.fadeAmount.setValue(ImageryPreferences.PROP_FADE_AMOUNT.get());
+
+ allowRemoteControl = ImageryPreferences.PROP_REMOTE_CONTROL.get();
+ remoteCheckBox = new JCheckBox(tr("Allow remote control (reqires remotecontrol plugin)"), allowRemoteControl);
+ p.add(remoteCheckBox,GBC.eol().fill(GBC.HORIZONTAL));
+
+ return p;
+ }
+
+ private JPanel buildWMSSettingsPanel() {
+ final JPanel p = new JPanel(new GridBagLayout());
+ browser = new JComboBox(new String[] {
+ "webkit-image {0}",
+ "gnome-web-photo --mode=photo --format=png {0} /dev/stdout",
+ "gnome-web-photo-fixed {0}",
+ "webkit-image-gtk {0}"});
+ browser.setEditable(true);
+ browser.setSelectedItem(Main.pref.get("wmsplugin.browser", "webkit-image {0}"));
+ p.add(new JLabel(tr("Downloader:")), GBC.eol().fill(GBC.HORIZONTAL));
+ p.add(browser);
+
+ // Overlap
+ p.add(Box.createHorizontalGlue(), GBC.eol().fill(GBC.HORIZONTAL));
+
+ overlapCheckBox = new JCheckBox(tr("Overlap tiles"), wmsAdapter.PROP_OVERLAP.get());
+ JLabel labelEast = new JLabel(tr("% of east:"));
+ JLabel labelNorth = new JLabel(tr("% of north:"));
+ spinEast = new JSpinner(new SpinnerNumberModel(wmsAdapter.PROP_OVERLAP_EAST.get(), 1, 50, 1));
+ spinNorth = new JSpinner(new SpinnerNumberModel(wmsAdapter.PROP_OVERLAP_NORTH.get(), 1, 50, 1));
+
+ JPanel overlapPanel = new JPanel(new FlowLayout());
+ overlapPanel.add(overlapCheckBox);
+ overlapPanel.add(labelEast);
+ overlapPanel.add(spinEast);
+ overlapPanel.add(labelNorth);
+ overlapPanel.add(spinNorth);
+
+ p.add(overlapPanel);
+
+ // Simultaneous connections
+ p.add(Box.createHorizontalGlue(), GBC.eol().fill(GBC.HORIZONTAL));
+ JLabel labelSimConn = new JLabel(tr("Simultaneous connections"));
+ spinSimConn = new JSpinner(new SpinnerNumberModel(wmsAdapter.PROP_SIMULTANEOUS_CONNECTIONS.get(), 1, 30, 1));
+ JPanel overlapPanelSimConn = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ overlapPanelSimConn.add(labelSimConn);
+ overlapPanelSimConn.add(spinSimConn);
+ p.add(overlapPanelSimConn, GBC.eol().fill(GBC.HORIZONTAL));
+
+ return p;
+ }
+
+ private JPanel buildTMSSettingsPanel() {
+ JPanel tmsTab = new JPanel(new GridBagLayout());
+ minZoomLvl = new JSpinner(new SpinnerNumberModel(TMSPreferences.DEFAULT_MIN_ZOOM, TMSPreferences.MIN_ZOOM, TMSPreferences.MAX_ZOOM, 1));
+ maxZoomLvl = new JSpinner(new SpinnerNumberModel(TMSPreferences.DEFAULT_MAX_ZOOM, TMSPreferences.MIN_ZOOM, TMSPreferences.MAX_ZOOM, 1));
+
+ tmsTab.add(new JLabel(tr("Auto zoom by default: ")), GBC.std());
+ tmsTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+ tmsTab.add(autozoomActive, GBC.eol().fill(GBC.HORIZONTAL));
+
+ tmsTab.add(new JLabel(tr("Autoload tiles by default: ")), GBC.std());
+ tmsTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+ tmsTab.add(autoloadTiles, GBC.eol().fill(GBC.HORIZONTAL));
+
+ tmsTab.add(new JLabel(tr("Min zoom lvl: ")), GBC.std());
+ tmsTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+ tmsTab.add(this.minZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
+
+ tmsTab.add(new JLabel(tr("Max zoom lvl: ")), GBC.std());
+ tmsTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+ tmsTab.add(this.maxZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
+
+ tmsTab.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+
+ this.autozoomActive.setSelected(TMSPreferences.PROP_DEFAULT_AUTOZOOM.get());
+ this.autoloadTiles.setSelected(TMSPreferences.PROP_DEFAULT_AUTOLOAD.get());
+ this.maxZoomLvl.setValue(TMSPreferences.getMaxZoomLvl(null));
+ this.minZoomLvl.setValue(TMSPreferences.getMinZoomLvl(null));
+ return tmsTab;
+ }
+
+ private void addSettingsSection(final JPanel p, String name, JPanel section) {
+ final JLabel lbl = new JLabel(name);
+ lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
+ p.add(lbl,GBC.std());
+ p.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 0));
+ p.add(section,GBC.eol().insets(20,5,0,10));
+ }
+
+ private Component buildSettingsPanel(final PreferenceTabbedPane gui) {
+ final JPanel p = new JPanel(new GridBagLayout());
+ p.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+ addSettingsSection(p, tr("Common Settings"), buildCommonSettingsPanel(gui));
+ addSettingsSection(p, tr("WMS Settings"), buildWMSSettingsPanel());
+ addSettingsSection(p, tr("TMS Settings"), buildTMSSettingsPanel());
+
+ p.add(new JPanel(),GBC.eol().fill(GBC.BOTH));
+ return new JScrollPane(p);
+ }
+
+ @Override
+ public void addGui(final PreferenceTabbedPane gui) {
+ JPanel p = gui.createPreferenceTab("imagery", tr("Imagery Preferences"), tr("Modify list of imagery layers displayed in the Imagery menu"));
+ JTabbedPane pane = new JTabbedPane();
+ pane.add(buildImageryProvidersPanel(gui));
+ pane.add(buildSettingsPanel(gui));
+ pane.setTitleAt(0, tr("Imagery providers"));
+ pane.setTitleAt(1, tr("Settings"));
+ p.add(pane,GBC.std().fill(GBC.BOTH));
+ }
+
+ @Override
+ public boolean ok() {
+ plugin.info.save();
+ plugin.refreshMenu();
+
+ wmsAdapter.PROP_OVERLAP.put(overlapCheckBox.getModel().isSelected());
+ wmsAdapter.PROP_OVERLAP_EAST.put((Integer) spinEast.getModel().getValue());
+ wmsAdapter.PROP_OVERLAP_NORTH.put((Integer) spinNorth.getModel().getValue());
+ wmsAdapter.PROP_SIMULTANEOUS_CONNECTIONS.put((Integer) spinSimConn.getModel().getValue());
+ allowRemoteControl = remoteCheckBox.getModel().isSelected();
+
+ Main.pref.put("wmsplugin.browser", browser.getEditor().getItem().toString());
+
+
+ TMSPreferences.PROP_DEFAULT_AUTOZOOM.put(this.autozoomActive.isSelected());
+ TMSPreferences.PROP_DEFAULT_AUTOLOAD.put(this.autoloadTiles.isSelected());
+ TMSPreferences.setMaxZoomLvl((Integer)this.maxZoomLvl.getValue());
+ TMSPreferences.setMinZoomLvl((Integer)this.minZoomLvl.getValue());
+
+ ImageryPreferences.PROP_REMOTE_CONTROL.put(allowRemoteControl);
+ ImageryPreferences.PROP_FADE_AMOUNT.put(this.fadeAmount.getValue());
+ ImageryPreferences.setFadeColor(this.colFadeColor);
+
+ return false;
+ }
+
+ /**
+ * Updates a server URL in the preferences dialog. Used by other plugins.
+ *
+ * @param server
+ * The server name
+ * @param url
+ * The server URL
+ */
+ public void setServerUrl(String server, String url) {
+ for (int i = 0; i < model.getRowCount(); i++) {
+ if (server.equals(model.getValueAt(i, 0).toString())) {
+ model.setValueAt(url, i, 1);
+ return;
+ }
+ }
+ model.addRow(new String[] { server, url });
+ }
+
+ /**
+ * Gets a server URL in the preferences dialog. Used by other plugins.
+ *
+ * @param server
+ * The server name
+ * @return The server URL
+ */
+ public String getServerUrl(String server) {
+ for (int i = 0; i < model.getRowCount(); i++) {
+ if (server.equals(model.getValueAt(i, 0).toString())) {
+ return model.getValueAt(i, 1).toString();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The table model for the WMS layer
+ *
+ */
+ class ImageryLayerTableModel extends DefaultTableModel {
+ public ImageryLayerTableModel() {
+ setColumnIdentifiers(new String[] { tr("Menu Name"), tr("Imagery URL"), trc("layer", "Zoom") });
+ }
+
+ public ImageryInfo getRow(int row) {
+ return plugin.info.layers.get(row);
+ }
+
+ public void addRow(ImageryInfo i) {
+ plugin.info.add(i);
+ int p = getRowCount() - 1;
+ fireTableRowsInserted(p, p);
+ }
+
+ @Override
+ public void removeRow(int i) {
+ plugin.info.remove(getRow(i));
+ fireTableRowsDeleted(i, i);
+ }
+
+ @Override
+ public int getRowCount() {
+ return plugin.info.layers.size();
+ }
+
+ @Override
+ public Object getValueAt(int row, int column) {
+ ImageryInfo info = plugin.info.layers.get(row);
+ switch (column) {
+ case 0:
+ return info.name;
+ case 1:
+ return info.getFullURL();
+ case 2:
+ return (info.imageryType == ImageryType.WMS) ? (info.pixelPerDegree == 0.0 ? "" : info.pixelPerDegree)
+ : (info.maxZoom == 0 ? "" : info.maxZoom);
+ }
+ return null;
+ }
+
+ @Override
+ public void setValueAt(Object o, int row, int column) {
+ ImageryInfo info = plugin.info.layers.get(row);
+ switch (column) {
+ case 0:
+ info.name = (String) o;
+ case 1:
+ info.setURL((String)o);
+ case 2:
+ info.pixelPerDegree = 0;
+ info.maxZoom = 0;
+ try {
+ if(info.imageryType == ImageryType.WMS)
+ info.pixelPerDegree = Double.parseDouble((String) o);
+ else
+ info.maxZoom = Integer.parseInt((String) o);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return true;
+ }
+ }
+
+ /**
+ * The table model for the WMS layer
+ *
+ */
+ class ImageryDefaultLayerTableModel extends DefaultTableModel {
+ public ImageryDefaultLayerTableModel() {
+ setColumnIdentifiers(new String[] { tr("Menu Name (Default)"), tr("Imagery URL (Default)") });
+ }
+
+ public ImageryInfo getRow(int row) {
+ return plugin.info.defaultLayers.get(row);
+ }
+
+ @Override
+ public int getRowCount() {
+ return plugin.info.defaultLayers.size();
+ }
+
+ @Override
+ public Object getValueAt(int row, int column) {
+ ImageryInfo info = plugin.info.defaultLayers.get(row);
+ switch (column) {
+ case 0:
+ return info.name;
+ case 1:
+ return info.getFullURL();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+ }
+
+ private boolean confirmeEulaAcceptance(PreferenceTabbedPane gui, String eulaUrl) {
+ URL url = null;
+ try {
+ url = new URL(eulaUrl.replaceAll("\\{lang\\}", Locale.getDefault().toString()));
+ JEditorPane htmlPane = null;
+ try {
+ htmlPane = new JEditorPane(url);
+ } catch (IOException e1) {
+ // give a second chance with a default Locale 'en'
+ try {
+ url = new URL(eulaUrl.replaceAll("\\{lang\\}", "en"));
+ htmlPane = new JEditorPane(url);
+ } catch (IOException e2) {
+ JOptionPane.showMessageDialog(gui ,tr("EULA license URL not available: {0}", eulaUrl));
+ return false;
+ }
+ }
+ Box box = Box.createVerticalBox();
+ htmlPane.setEditable(false);
+ JScrollPane scrollPane = new JScrollPane(htmlPane);
+ scrollPane.setPreferredSize(new Dimension(400, 400));
+ box.add(scrollPane);
+ int option = JOptionPane.showConfirmDialog(Main.parent, box, tr("Please abort if you are not sure"), JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE);
+ if (option == JOptionPane.YES_OPTION) {
+ return true;
+ }
+ } catch (MalformedURLException e2) {
+ JOptionPane.showMessageDialog(gui ,tr("Malformed URL for the EULA licence: {0}", eulaUrl));
+ }
+ return false;
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferences.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferences.java
new file mode 100644
index 0000000..c55505d
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferences.java
@@ -0,0 +1,28 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import java.awt.Color;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.data.preferences.IntegerProperty;
+
+public class ImageryPreferences {
+
+ public static final BooleanProperty PROP_REMOTE_CONTROL = new BooleanProperty("imagery.remotecontrol", true);
+ public static final IntegerProperty PROP_FADE_AMOUNT = new IntegerProperty("imagery.fade_amount", 0);
+
+ public static Color getFadeColor() {
+ return Main.pref.getColor("imagery.fade", Color.white);
+ }
+
+ public static Color getFadeColorWithAlpha() {
+ Color c = getFadeColor();
+ return new Color(c.getRed(),c.getGreen(),c.getBlue(),PROP_FADE_AMOUNT.get()*255/100);
+ }
+
+ public static void setFadeColor(Color color) {
+ Main.pref.putColor("imagery.fade", color);
+ }
+
+
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryRemoteHandler.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryRemoteHandler.java
new file mode 100644
index 0000000..8e2ee05
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryRemoteHandler.java
@@ -0,0 +1,108 @@
+package org.openstreetmap.josm.plugins.imagery;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.remotecontrol.PermissionPrefWithDefault;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandler;
+import org.openstreetmap.josm.plugins.remotecontrol.RequestHandlerErrorException;
+
+public class ImageryRemoteHandler extends RequestHandler {
+
+ public static final String command = "imagery";
+
+ @Override
+ public String getPermissionMessage() {
+ return tr("Remote Control has been asked to load an imagery layer from the following URL:") +
+ "<br>" + args.get("url");
+ }
+
+ @Override
+ public PermissionPrefWithDefault getPermissionPref()
+ {
+ return new PermissionPrefWithDefault(
+ "imagery.remotecontrol",
+ true,
+ "RemoteControl: Imagery forbidden by preferences");
+ }
+
+ @Override
+ protected String[] getMandatoryParams()
+ {
+ return new String[] { "url" };
+ }
+
+ @Override
+ protected void handleRequest() throws RequestHandlerErrorException {
+ if (Main.map == null) //Avoid exception when creating ImageryLayer with null MapFrame
+ throw new RequestHandlerErrorException();
+ String url = args.get("url");
+ String title = args.get("title");
+ if((title == null) || (title.length() == 0))
+ {
+ title = tr("Remote imagery");
+ }
+ String cookies = args.get("cookies");
+ ImageryLayer imgLayer = ImageryLayer.create(new ImageryInfo(title, url, cookies));
+ Main.main.addLayer(imgLayer);
+
+ }
+
+ @Override
+ public void parseArgs() {
+ StringTokenizer st = new StringTokenizer(request, "&?");
+ HashMap<String, String> args = new HashMap<String, String>();
+ // skip first element which is the command
+ if(st.hasMoreTokens()) st.nextToken();
+ while (st.hasMoreTokens()) {
+ String param = st.nextToken();
+ int eq = param.indexOf("=");
+ if (eq > -1)
+ {
+ String key = param.substring(0, eq);
+ /* "url=" terminates normal parameters
+ * and will be handled separately
+ */
+ if("url".equals(key)) break;
+
+ String value = param.substring(eq + 1);
+ // urldecode all normal values
+ try {
+ value = URLDecoder.decode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ args.put(key,
+ value);
+ }
+ }
+ // url as second or later parameter
+ int urlpos = request.indexOf("&url=");
+ // url as first (and only) parameter
+ if(urlpos < 0) urlpos = request.indexOf("?url=");
+ // url found?
+ if(urlpos >= 0) {
+ // URL value
+ String value = request.substring(urlpos + 5);
+ // allow skipping URL decoding with urldecode=false
+ String urldecode = args.get("urldecode");
+ if((urldecode == null) || (Boolean.valueOf(urldecode) == true))
+ {
+ try {
+ value = URLDecoder.decode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ args.put("url", value);
+ }
+ this.args = args;
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/BingAerialTileSource.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/BingAerialTileSource.java
new file mode 100644
index 0000000..c98f354
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/BingAerialTileSource.java
@@ -0,0 +1,191 @@
+package org.openstreetmap.josm.plugins.imagery.tms;
+
+import java.awt.Image;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.openstreetmap.gui.jmapviewer.OsmTileSource;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+public class BingAerialTileSource extends OsmTileSource.AbstractOsmTileSource {
+ private static String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU";
+ private static List<Attribution> attributions;
+
+ public BingAerialTileSource() {
+ super("Bing Aerial Maps", "http://ecn.t2.tiles.virtualearth.net/tiles/");
+
+ attributions = loadAttributionText();
+ System.err.println("Added " + attributions.size() + " attributions.");
+ }
+
+ class Attribution {
+ String attribution;
+ int minZoom;
+ int maxZoom;
+ Bounds bounds;
+ }
+
+ class AttrHandler extends DefaultHandler {
+
+ private String string;
+ private Attribution curr;
+ private List<Attribution> attributions = new ArrayList<Attribution>();
+ private double southLat;
+ private double northLat;
+ private double eastLon;
+ private double westLon;
+ private boolean inCoverage = false;
+
+ @Override
+ public void startElement(String uri, String stripped, String tagName,
+ Attributes attrs) throws SAXException {
+ if("ImageryProvider".equals(tagName)) {
+ curr = new Attribution();
+ } else if("CoverageArea".equals(tagName)) {
+ inCoverage = true;
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+ string = new String(ch, start, length);
+ }
+
+ @Override
+ public void endElement(String uri, String stripped, String tagName)
+ throws SAXException {
+ if("ImageryProvider".equals(tagName)) {
+ attributions.add(curr);
+ } else if("Attribution".equals(tagName)) {
+ curr.attribution = string;
+ } else if(inCoverage && "ZoomMin".equals(tagName)) {
+ curr.minZoom = Integer.parseInt(string);
+ } else if(inCoverage && "ZoomMax".equals(tagName)) {
+ curr.maxZoom = Integer.parseInt(string);
+ } else if(inCoverage && "SouthLatitude".equals(tagName)) {
+ southLat = Double.parseDouble(string);
+ } else if(inCoverage && "NorthLatitude".equals(tagName)) {
+ northLat = Double.parseDouble(string);
+ } else if(inCoverage && "EastLongitude".equals(tagName)) {
+ eastLon = Double.parseDouble(string);
+ } else if(inCoverage && "WestLongitude".equals(tagName)) {
+ westLon = Double.parseDouble(string);
+ } else if("BoundingBox".equals(tagName)) {
+ curr.bounds = new Bounds(northLat, westLon, southLat, eastLon);
+ } else if("CoverageArea".equals(tagName)) {
+ inCoverage = false;
+ }
+ string = "";
+ }
+ }
+
+ private List<Attribution> loadAttributionText() {
+ try {
+ URL u = new URL("http://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/0,0?zl=1&mapVersion=v1&key="+API_KEY+"&include=ImageryProviders&output=xml");
+ InputStream stream = u.openStream();
+ XMLReader parser = XMLReaderFactory.createXMLReader();
+ AttrHandler handler = new AttrHandler();
+ parser.setContentHandler(handler);
+ parser.parse(new InputSource(stream));
+ return handler.attributions;
+ } catch (IOException e) {
+ System.err.println("Could not open Bing aerials attribution metadata.");
+ } catch (SAXException e) {
+ System.err.println("Could not parse Bing aerials attribution metadata.");
+ e.printStackTrace();
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int getMaxZoom() {
+ return 22;
+ }
+
+ @Override
+ public String getExtension() {
+ return("jpeg");
+ }
+
+ @Override
+ public String getTilePath(int zoom, int tilex, int tiley) {
+ String quadtree = computeQuadTree(zoom, tilex, tiley);
+ return "/tiles/a" + quadtree + "." + getExtension() + "?g=587";
+ }
+
+ @Override
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+
+ @Override
+ public boolean requiresAttribution() {
+ return true;
+ }
+
+ @Override
+ public Image getAttributionImage() {
+ return ImageProvider.get("bing_maps").getImage();
+ }
+
+ @Override
+ public String getAttributionLinkURL() {
+ //return "http://bing.com/maps"
+ // FIXME: I've set attributionLinkURL temporarily to ToU URL to comply with bing ToU
+ // (the requirement is that we have such a link at the bottom of the window)
+ return "http://go.microsoft.com/?linkid=9710837";
+ }
+
+ @Override
+ public String getTermsOfUseURL() {
+ return "http://opengeodata.org/microsoft-imagery-details";
+ }
+
+ @Override
+ public String getAttributionText(int zoom, LatLon topLeft, LatLon botRight) {
+ Bounds windowBounds = new Bounds(topLeft, botRight);
+ StringBuilder a = new StringBuilder();
+ for (Attribution attr : attributions) {
+ Bounds attrBounds = attr.bounds;
+ if(zoom <= attr.maxZoom && zoom >= attr.minZoom) {
+ if(windowBounds.getMin().lon() < attrBounds.getMax().lon()
+ && windowBounds.getMax().lon() > attrBounds.getMin().lon()
+ && windowBounds.getMax().lat() < attrBounds.getMin().lat()
+ && windowBounds.getMin().lat() > attrBounds.getMax().lat()) {
+ a.append(attr.attribution);
+ a.append(" ");
+ }
+ }
+ }
+ return a.toString();
+ }
+
+ static String computeQuadTree(int zoom, int tilex, int tiley) {
+ StringBuilder k = new StringBuilder();
+ for(int i = zoom; i > 0; i--) {
+ char digit = 48;
+ int mask = 1 << (i - 1);
+ if ((tilex & mask) != 0) {
+ digit += 1;
+ }
+ if ((tiley & mask) != 0) {
+ digit += 2;
+ }
+ k.append(digit);
+ }
+ return k.toString();
+ }
+}
\ No newline at end of file
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSKey.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSKey.java
new file mode 100644
index 0000000..7cb2b8e
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSKey.java
@@ -0,0 +1,78 @@
+/**
+ *
+ */
+package org.openstreetmap.josm.plugins.imagery.tms;
+
+/**
+ * <p>
+ * Key for map tile. Key have just X and Y value. It have overriden {@link #hashCode()},
+ * {@link #equals(Object)} and also {@link #toString()}.
+ * </p>
+ *
+ * @author LuVar <lubomir.varga at freemap.sk>
+ * @author Dave Hansen <dave at sr71.net>
+ *
+ */
+public class TMSKey {
+ private final int x;
+ private final int y;
+ private final int level;
+
+ /**
+ * <p>
+ * Constructs key for hashmaps for some tile describedy by X and Y position. X and Y are tiles
+ * positions on discrete map.
+ * </p>
+ *
+ * @param x x position in tiles table
+ * @param y y position in tiles table
+ */
+ public final boolean valid;
+ public TMSKey(int x, int y, int level) {
+ this.x = x;
+ this.y = y;
+ this.level = level;
+ if (level <= 0 || x < 0 || y < 0) {
+ this.valid = false;
+ System.err.println("invalid TMSKey("+level+", "+x+", "+y+")");
+ } else {
+ this.valid = true;
+ }
+ }
+
+ /**
+ * <p>
+ * Returns true ONLY if x and y are equals.
+ * </p>
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TMSKey) {
+ TMSKey smk = (TMSKey) obj;
+ if((smk.x == this.x) && (smk.y == this.y) && (smk.level == this.level)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return return new Integer(this.x + this.y * 10000).hashCode();
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return new Integer(this.x + this.y * 10000 + this.level * 100000).hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "TMSKey(x=" + this.x + ",y=" + this.y + ",level=" + level + ")";
+ }
+
+}
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSLayer.java
similarity index 76%
copy from slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
copy to imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSLayer.java
index 1a1fb83..173a8da 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSLayer.java
@@ -1,8 +1,9 @@
-package org.openstreetmap.josm.plugins.slippymap;
+package org.openstreetmap.josm.plugins.imagery.tms;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Color;
+import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
@@ -12,15 +13,20 @@ import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.awt.font.TextAttribute;
+import java.awt.geom.Rectangle2D;
import java.awt.image.ImageObserver;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
-import javax.swing.Icon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
@@ -37,6 +43,7 @@ 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.coor.EastNorth;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
import org.openstreetmap.josm.gui.MapView;
@@ -44,7 +51,10 @@ import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo;
+import org.openstreetmap.josm.plugins.imagery.ImageryLayer;
+import org.openstreetmap.josm.plugins.imagery.ImageryPreferences;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo.ImageryType;
/**
* Class that displays a slippy map layer.
@@ -54,8 +64,7 @@ import org.openstreetmap.josm.tools.ImageProvider;
* @author Dave Hansen <dave at sr71.net>
*
*/
-public class SlippyMapLayer extends Layer implements ImageObserver,
- TileLoaderListener {
+public class TMSLayer extends ImageryLayer implements ImageObserver, TileLoaderListener {
boolean debug = false;
void out(String s)
{
@@ -68,6 +77,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
JobDispatcher jobDispatcher = JobDispatcher.getInstance();
HashSet<Tile> tileRequestsOutstanding = new HashSet<Tile>();
+ @Override
public synchronized void tileLoadingFinished(Tile tile, boolean success)
{
tile.setLoaded(true);
@@ -77,6 +87,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
if (debug)
out("tileLoadingFinished() tile: " + tile + " success: " + success);
}
+ @Override
public TileCache getTileCache()
{
return tileCache;
@@ -91,18 +102,31 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
/**
* Actual zoom lvl. Initial zoom lvl is set to
- * {@link SlippyMapPreferences#getMinZoomLvl()}.
*/
public int currentZoomLevel;
- LatLon lastTopLeft;
- LatLon lastBotRight;
+ EastNorth lastTopLeft;
+ EastNorth lastBotRight;
private Image bufferImage;
private Tile clickedTile;
private boolean needRedraw;
private JPopupMenu tileOptionMenu;
JCheckBoxMenuItem autoZoomPopup;
+ JCheckBoxMenuItem autoLoadPopup;
Tile showMetadataTile;
+ private Image attrImage;
+ private String attrTermsUrl;
+ private Rectangle attrImageBounds, attrToUBounds;
+ private static final Font ATTR_FONT = new Font("Arial", Font.PLAIN, 10);
+ private static final Font ATTR_LINK_FONT;
+ static {
+ HashMap<TextAttribute, Integer> aUnderline = new HashMap<TextAttribute, Integer>();
+ aUnderline.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
+ ATTR_LINK_FONT = ATTR_FONT.deriveFont(aUnderline);
+ }
+
+ protected boolean autoZoom;
+ protected boolean autoLoad;
void redraw()
{
@@ -110,50 +134,90 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
Main.map.repaint();
}
- void newTileStorage()
+ private void initTileSource(TileSource tileSource)
{
- 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);
+ this.tileSource = tileSource;
+ boolean requireAttr = tileSource.requiresAttribution();
+ if(requireAttr) {
+ attrImage = tileSource.getAttributionImage();
+ if(attrImage == null) {
+ System.out.println("Attribution image was null.");
+ } else {
+ System.out.println("Got an attribution image " + attrImage.getHeight(this) + "x" + attrImage.getWidth(this));
+ }
+
+ attrTermsUrl = tileSource.getTermsOfUseURL();
}
+
+ currentZoomLevel = getBestZoom();
+ if (currentZoomLevel > getMaxZoomLvl())
+ currentZoomLevel = getMaxZoomLvl();
+ if (currentZoomLevel < getMinZoomLvl())
+ currentZoomLevel = getMinZoomLvl();
clearTileCache();
- //tileLoader = new OsmTileLoader(this);
+ //tileloader = new OsmTileLoader(this);
tileLoader = new OsmFileCacheTileLoader(this);
}
+ @Override
+ public void displace(double dx, double dy) {
+ super.displace(dx, dy);
+ needRedraw = true;
+ }
+
+ private double getPPDeg() {
+ return mv.getWidth()/(mv.getLatLon(mv.getWidth(), mv.getHeight()/2).lon()-mv.getLatLon(0, mv.getHeight()/2).lon());
+ }
+
+ private int getBestZoom() {
+ double ret = Math.log(getPPDeg()*360/tileSource.getTileSize())/Math.log(2);
+ System.out.println("Detected best zoom " + ret);
+ return (int)Math.round(ret);
+ }
+
@SuppressWarnings("serial")
- public SlippyMapLayer() {
- super(tr("Slippy Map"));
+ public TMSLayer(ImageryInfo info) {
+ super(info);
setBackgroundLayer(true);
this.setVisible(true);
- currentZoomLevel = SlippyMapPreferences.getLastZoom();
- newTileStorage();
+ if (info.getImageryType() == ImageryType.TMS) {
+ if(isUrlWithPatterns(info.getURL())) {
+ initTileSource(new TemplatedTMSTileSource(info.getName(), info.getURL(), info.getMaxZoom()));
+ } else {
+ initTileSource(new TMSTileSource(info.getName(),info.getURL(), info.getMaxZoom()));
+ }
+ } else if (info.getImageryType() == ImageryType.BING) {
+ initTileSource(new BingAerialTileSource());
+ } else throw new IllegalStateException("cannot create TMSLayer with non-TMS ImageryInfo");
tileOptionMenu = new JPopupMenu();
+ autoZoom = TMSPreferences.PROP_DEFAULT_AUTOZOOM.get();
autoZoomPopup = new JCheckBoxMenuItem();
autoZoomPopup.setAction(new AbstractAction(tr("Auto Zoom")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
- boolean new_state = !SlippyMapPreferences.getAutozoom();
- SlippyMapPreferences.setAutozoom(new_state);
+ autoZoom = !autoZoom;
}
});
- autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
+ autoZoomPopup.setSelected(autoZoom);
tileOptionMenu.add(autoZoomPopup);
+ autoLoad = TMSPreferences.PROP_DEFAULT_AUTOLOAD.get();
+ autoLoadPopup = new JCheckBoxMenuItem();
+ autoLoadPopup.setAction(new AbstractAction(tr("Auto load tiles")) {
+ @Override
+ public void actionPerformed(ActionEvent ae) {
+ autoLoad= !autoLoad;
+ }
+ });
+ autoLoadPopup.setSelected(autoLoad);
+ tileOptionMenu.add(autoLoadPopup);
+
tileOptionMenu.add(new JMenuItem(new AbstractAction(tr("Load Tile")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
if (clickedTile != null) {
loadTile(clickedTile);
@@ -164,6 +228,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
tileOptionMenu.add(new JMenuItem(new AbstractAction(
tr("Show Tile Info")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
out("info tile: " + clickedTile);
if (clickedTile != null) {
@@ -186,6 +251,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
tileOptionMenu.add(new JMenuItem(new AbstractAction(
tr("Load All Tiles")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
loadAllTiles(true);
redraw();
@@ -195,6 +261,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
// increase and decrease commands
tileOptionMenu.add(new JMenuItem(
new AbstractAction(tr("Increase zoom")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
increaseZoomLevel();
redraw();
@@ -203,6 +270,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
tileOptionMenu.add(new JMenuItem(
new AbstractAction(tr("Decrease zoom")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
decreaseZoomLevel();
redraw();
@@ -213,6 +281,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
tileOptionMenu.add(new JMenuItem(
new AbstractAction(tr("Snap to tile size")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
if (lastImageScale == null) {
out("please wait for a tile to be loaded before snapping");
@@ -229,6 +298,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
tileOptionMenu.add(new JMenuItem(
new AbstractAction(tr("Flush Tile Cache")) {
+ @Override
public void actionPerformed(ActionEvent ae) {
System.out.print("flushing all tiles...");
clearTileCache();
@@ -238,26 +308,54 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
// end of adding menu commands
SwingUtilities.invokeLater(new Runnable() {
+ @Override
public void run() {
Main.map.mapView.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
- if (e.getButton() != MouseEvent.BUTTON3)
- return;
- clickedTile = getTileForPixelpos(e.getX(), e.getY());
- tileOptionMenu.show(e.getComponent(), e.getX(), e.getY());
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ clickedTile = getTileForPixelpos(e.getX(), e.getY());
+ tileOptionMenu.show(e.getComponent(), e.getX(), e.getY());
+ } else if (e.getButton() == MouseEvent.BUTTON1) {
+ if(!tileSource.requiresAttribution()) {
+ return;
+ }
+
+ if(attrImageBounds.contains(e.getPoint())) {
+ try {
+ java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
+ desktop.browse(new URI(tileSource.getAttributionLinkURL()));
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } catch (URISyntaxException e1) {
+ e1.printStackTrace();
+ }
+ } else if(attrToUBounds.contains(e.getPoint())) {
+ try {
+ java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
+ desktop.browse(new URI(tileSource.getTermsOfUseURL()));
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } catch (URISyntaxException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
}
});
MapView.addLayerChangeListener(new LayerChangeListener() {
+ @Override
public void activeLayerChange(Layer oldLayer, Layer newLayer) {
//
}
+ @Override
public void layerAdded(Layer newLayer) {
//
}
+ @Override
public void layerRemoved(Layer oldLayer) {
MapView.removeLayerChangeListener(this);
}
@@ -266,6 +364,10 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
});
}
+ public static boolean isUrlWithPatterns(String url) {
+ return url != null && url.contains("{") && url.contains("}");
+ }
+
void zoomChanged()
{
if (debug)
@@ -273,23 +375,20 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
needRedraw = true;
jobDispatcher.cancelOutstandingJobs();
tileRequestsOutstanding.clear();
- SlippyMapPreferences.setLastZoom(currentZoomLevel);
}
int getMaxZoomLvl()
{
- int ret = SlippyMapPreferences.getMaxZoomLvl();
- if (tileSource.getMaxZoom() < ret)
- ret = tileSource.getMaxZoom();
- return ret;
+ if (info.getMaxZoom() != 0) {
+ return TMSPreferences.checkMaxZoomLvl(info.getMaxZoom(), tileSource);
+ } else {
+ return TMSPreferences.getMaxZoomLvl(tileSource);
+ }
}
int getMinZoomLvl()
{
- int ret = SlippyMapPreferences.getMinZoomLvl();
- if (tileSource.getMinZoom() > ret)
- ret = tileSource.getMinZoom();
- return ret;
+ return TMSPreferences.getMinZoomLvl(tileSource);
}
/**
@@ -400,8 +499,8 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
void loadAllTiles(boolean force) {
MapView mv = Main.map.mapView;
- LatLon topLeft = mv.getLatLon(0, 0);
- LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
+ EastNorth topLeft = mv.getEastNorth(0, 0);
+ EastNorth botRight = mv.getEastNorth(mv.getWidth(), mv.getHeight());
TileSet ts = new TileSet(topLeft, botRight, currentZoomLevel);
@@ -419,6 +518,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
* a 100x100 image being scaled to 50x50 would return 0.25.
*/
Image lastScaledImage = null;
+ @Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
needRedraw = true;
@@ -594,10 +694,9 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
img_x_offset, img_y_offset,
img_x_end, img_y_end,
this);
- float fadeBackground = SlippyMapPreferences.getFadeBackground();
- if (fadeBackground != 0f) {
+ if (ImageryPreferences.PROP_FADE_AMOUNT.get() != 0) {
// dimm by painting opaque rect...
- g.setColor(new Color(1f, 1f, 1f, fadeBackground));
+ g.setColor(ImageryPreferences.getFadeColorWithAlpha());
g.fillRect(target.x, target.y,
target.width, target.height);
}
@@ -645,7 +744,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
Point p = pixelPos(t);
int texty = p.y + 2 + fontHeight;
- if (SlippyMapPreferences.getDrawDebug()) {
+ if (TMSPreferences.PROP_DRAW_DEBUG.get()) {
g.drawString("x=" + t.getXtile() + " y=" + t.getYtile() + " z=" + zoom + "", p.x + 2, texty);
texty += 1 + fontHeight;
if ((t.getXtile() % 32 == 0) && (t.getYtile() % 32 == 0)) {
@@ -670,7 +769,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
int xCursor = -1;
int yCursor = -1;
- if (SlippyMapPreferences.getDrawDebug()) {
+ if (TMSPreferences.PROP_DRAW_DEBUG.get()) {
if (yCursor < t.getYtile()) {
if (t.getYtile() % 32 == 31) {
g.fillRect(0, p.y - 1, mv.getWidth(), 3);
@@ -683,34 +782,46 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
// 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());
- }
+ 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();
}
}
}
- public Point pixelPos(LatLon ll) {
- return Main.map.mapView.getPoint(ll);
+ private Point pixelPos(LatLon ll) {
+ return Main.map.mapView.getPoint(Main.proj.latlon2eastNorth(ll).add(getDx(), getDy()));
}
- public Point pixelPos(Tile t) {
+ private 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 LatLon getShiftedLatLon(EastNorth en) {
+ return Main.proj.eastNorth2latlon(en.add(-getDx(), -getDy()));
+ }
private class TileSet {
int z12x0, z12x1, z12y0, z12y1;
int zoom;
int tileMax = -1;
+ /**
+ * Create a TileSet by EastNorth bbox taking a layer shift in account
+ */
+ TileSet(EastNorth topLeft, EastNorth botRight, int zoom) {
+ this(getShiftedLatLon(topLeft), getShiftedLatLon(botRight),zoom);
+ }
+
+ /**
+ * Create a TileSet by known LatLon bbox without layer shift correction
+ */
TileSet(LatLon topLeft, LatLon botRight, int zoom) {
this.zoom = zoom;
+
z12x0 = lonToTileX(topLeft.lon(), zoom);
z12y0 = latToTileY(topLeft.lat(), zoom);
z12x1 = lonToTileX(botRight.lon(), zoom);
@@ -781,8 +892,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
void loadAllTiles(boolean force)
{
List<Tile> tiles = this.allTiles(true);
- boolean autoload = SlippyMapPreferences.getAutoloadTiles();
- if (!autoload && !force)
+ if (!autoLoad && !force)
return;
int nr_queued = 0;
for (Tile t : tiles) {
@@ -800,24 +910,25 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
{
if (az_disable)
return false;
- return autoZoomPopup.isSelected();
+ return autoZoom;
}
/**
*/
@Override
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());
+ EastNorth topLeft = mv.getEastNorth(0, 0);
+ EastNorth botRight = mv.getEastNorth(mv.getWidth(), mv.getHeight());
Graphics2D oldg = g;
- if (botRight.lon() == 0.0 || botRight.lat() == 0) {
+ if (botRight.east() == 0.0 || botRight.north() == 0) {
Main.debug("still initializing??");
// probably still initializing
return;
}
- if (lastTopLeft != null && lastBotRight != null && topLeft.equalsEpsilon(lastTopLeft)
- && botRight.equalsEpsilon(lastBotRight) && bufferImage != null
+
+ if (lastTopLeft != null && lastBotRight != null && topLeft.equals(lastTopLeft)
+ && botRight.equals(lastBotRight) && bufferImage != null
&& mv.getWidth() == bufferImage.getWidth(null) && mv.getHeight() == bufferImage.getHeight(null)
&& !needRedraw) {
@@ -880,7 +991,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
for (int zoomOffset : otherZooms) {
if (!autoZoomEnabled())
break;
- if (!SlippyMapPreferences.getAutoloadTiles())
+ if (!autoLoad)
break;
int newzoom = currentZoomLevel + zoomOffset;
if (missedTiles.size() <= 0)
@@ -907,6 +1018,53 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
for (Tile t : ts.allTiles()) {
this.paintTileText(ts, t, g, mv, currentZoomLevel, t);
}
+
+ if (tileSource.requiresAttribution()) {
+ // Draw attribution
+ Font font = g.getFont();
+ g.setFont(ATTR_LINK_FONT);
+
+ // Draw terms of use text
+ Rectangle2D termsStringBounds = g.getFontMetrics().getStringBounds("Background Terms of Use", g);
+ int textHeight = (int) termsStringBounds.getHeight() - 5;
+ int textWidth = (int) termsStringBounds.getWidth();
+ int termsTextY = mv.getHeight() - textHeight;
+ if(attrTermsUrl != null) {
+ int x = 2;
+ int y = mv.getHeight() - textHeight;
+ attrToUBounds = new Rectangle(x, y, textWidth, textHeight);
+ g.setColor(Color.black);
+ g.drawString("Background Terms of Use", x+1, y+1);
+ g.setColor(Color.white);
+ g.drawString("Background Terms of Use", x, y);
+ }
+
+ // Draw attribution logo
+ int imgWidth = attrImage.getWidth(this);
+ if(attrImage != null) {
+ int x = 2;
+ int height = attrImage.getHeight(this);
+ int y = termsTextY - height - textHeight - 5;
+ attrImageBounds = new Rectangle(x, y, imgWidth, height);
+ g.drawImage(attrImage, x, y, this);
+ }
+
+ g.setFont(ATTR_FONT);
+ String attributionText = tileSource.getAttributionText(currentZoomLevel,
+ getShiftedLatLon(topLeft), getShiftedLatLon(botRight));
+ Rectangle2D stringBounds = g.getFontMetrics().getStringBounds(attributionText, g);
+ {
+ int x = mv.getWidth() - (int) stringBounds.getWidth();
+ int y = mv.getHeight() - textHeight;
+ g.setColor(Color.black);
+ g.drawString(attributionText, x+1, y+1);
+ g.setColor(Color.white);
+ g.drawString(attributionText, x, y);
+ }
+
+ g.setFont(font);
+ }
+
oldg.drawImage(bufferImage, 0, 0, null);
if (autoZoomEnabled() && lastImageScale != null) {
@@ -946,8 +1104,8 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
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());
+ EastNorth topLeft = mv.getEastNorth(0, 0);
+ EastNorth botRight = mv.getEastNorth(mv.getWidth(), mv.getHeight());
int z = currentZoomLevel;
TileSet ts = new TileSet(topLeft, botRight, z);
@@ -973,16 +1131,6 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
}
@Override
- public Icon getIcon() {
- return ImageProvider.get("slippymap");
- }
-
- @Override
- public Object getInfoComponent() {
- return null;
- }
-
- @Override
public Action[] getMenuEntries() {
return new Action[] {
LayerListDialog.getInstance().createShowHideLayerAction(),
@@ -1000,15 +1148,6 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
}
@Override
- public boolean isMergable(Layer other) {
- return false;
- }
-
- @Override
- public void mergeFrom(Layer from) {
- }
-
- @Override
public void visitBoundingBox(BoundingXYVisitor v) {
}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSPreferences.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSPreferences.java
new file mode 100644
index 0000000..b2d62a3
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSPreferences.java
@@ -0,0 +1,82 @@
+package org.openstreetmap.josm.plugins.imagery.tms;
+
+import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.data.preferences.IntegerProperty;
+
+/**
+ * Preferences for Slippy Map Tiles
+ *
+ * @author Hakan Tandogan <hakan at gurkensalat.com>
+ * @author LuVar <lubomir.varga at freemap.sk>
+ * @author Upliner <upliner at gmail.com>
+ *
+ */
+public class TMSPreferences
+{
+ public static final String PREFERENCE_PREFIX = "imagery.tms";
+
+ public static final int MAX_ZOOM = 30;
+ public static final int MIN_ZOOM = 2;
+ public static final int DEFAULT_MAX_ZOOM = 18;
+ public static final int DEFAULT_MIN_ZOOM = 2;
+
+ public static final BooleanProperty PROP_DEFAULT_AUTOZOOM = new BooleanProperty(PREFERENCE_PREFIX + ".default_autozoom", true);
+ public static final BooleanProperty PROP_DEFAULT_AUTOLOAD = new BooleanProperty(PREFERENCE_PREFIX + ".default_autoload", true);
+ public static final IntegerProperty PROP_MIN_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".min_zoom_lvl", DEFAULT_MIN_ZOOM);
+ public static final IntegerProperty PROP_MAX_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".max_zoom_lvl", DEFAULT_MAX_ZOOM);
+ public static final BooleanProperty PROP_DRAW_DEBUG = new BooleanProperty(PREFERENCE_PREFIX + ".draw_debug", false);
+
+ static int checkMaxZoomLvl(int maxZoomLvl, TileSource ts)
+ {
+ if(maxZoomLvl > MAX_ZOOM) {
+ System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
+ maxZoomLvl = MAX_ZOOM;
+ }
+ if(maxZoomLvl < PROP_MIN_ZOOM_LVL.get()) {
+ System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
+ maxZoomLvl = PROP_MIN_ZOOM_LVL.get();
+ }
+ if (ts != null && ts.getMaxZoom() != 0 && ts.getMaxZoom() < maxZoomLvl) {
+ maxZoomLvl = ts.getMaxZoom();
+ }
+ return maxZoomLvl;
+ }
+
+ public static int getMaxZoomLvl(TileSource ts)
+ {
+ return checkMaxZoomLvl(PROP_MAX_ZOOM_LVL.get(), ts);
+ }
+
+ public static void setMaxZoomLvl(int maxZoomLvl) {
+ maxZoomLvl = checkMaxZoomLvl(maxZoomLvl, null);
+ PROP_MAX_ZOOM_LVL.put(maxZoomLvl);
+ }
+
+ static int checkMinZoomLvl(int minZoomLvl, TileSource ts)
+ {
+ if(minZoomLvl < MIN_ZOOM) {
+ System.err.println("minZoomLvl shouldnt be lees than "+MIN_ZOOM+"! Setting to that.");
+ minZoomLvl = MIN_ZOOM;
+ }
+ if(minZoomLvl > PROP_MAX_ZOOM_LVL.get()) {
+ System.err.println("minZoomLvl shouldnt be more than maxZoomLvl! Setting to maxZoomLvl.");
+ minZoomLvl = getMaxZoomLvl(ts);
+ }
+ if (ts != null && ts.getMinZoom() > minZoomLvl) {
+ System.err.println("increasomg minZoomLvl to match tile source");
+ minZoomLvl = ts.getMinZoom();
+ }
+ return minZoomLvl;
+ }
+
+ public static int getMinZoomLvl(TileSource ts)
+ {
+ return checkMinZoomLvl(PROP_MIN_ZOOM_LVL.get(), ts);
+ }
+
+ public static void setMinZoomLvl(int minZoomLvl) {
+ minZoomLvl = checkMinZoomLvl(minZoomLvl, null);
+ PROP_MIN_ZOOM_LVL.put(minZoomLvl);
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSTileSource.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSTileSource.java
new file mode 100644
index 0000000..fcd2878
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSTileSource.java
@@ -0,0 +1,22 @@
+package org.openstreetmap.josm.plugins.imagery.tms;
+
+import org.openstreetmap.gui.jmapviewer.OsmTileSource;
+
+public class TMSTileSource extends OsmTileSource.AbstractOsmTileSource {
+ private int maxZoom;
+
+ public TMSTileSource(String name, String url, int maxZoom) {
+ super(name, url);
+ this.maxZoom = maxZoom;
+ }
+
+ @Override
+ public int getMaxZoom() {
+ return (maxZoom == 0) ? super.getMaxZoom() : maxZoom;
+ }
+
+ @Override
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+}
\ No newline at end of file
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TemplatedTMSTileSource.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TemplatedTMSTileSource.java
new file mode 100644
index 0000000..5fc00b5
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TemplatedTMSTileSource.java
@@ -0,0 +1,30 @@
+package org.openstreetmap.josm.plugins.imagery.tms;
+
+import org.openstreetmap.gui.jmapviewer.OsmTileSource;
+
+public class TemplatedTMSTileSource extends OsmTileSource.AbstractOsmTileSource {
+ private int maxZoom;
+
+ public TemplatedTMSTileSource(String name, String url, int maxZoom) {
+ super(name, url);
+ this.maxZoom = maxZoom;
+ }
+
+ public String getTileUrl(int zoom, int tilex, int tiley) {
+ return this.BASE_URL
+ .replaceAll("\\{zoom\\}", Integer.toString(zoom))
+ .replaceAll("\\{x\\}", Integer.toString(tilex))
+ .replaceAll("\\{y\\}", Integer.toString(tiley));
+
+ }
+
+ @Override
+ public int getMaxZoom() {
+ return (maxZoom == 0) ? super.getMaxZoom() : maxZoom;
+ }
+
+ @Override
+ public TileUpdate getTileUpdate() {
+ return TileUpdate.IfNoneMatch;
+ }
+}
\ No newline at end of file
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/AddWMSLayerPanel.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/AddWMSLayerPanel.java
new file mode 100644
index 0000000..a66fb68
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/AddWMSLayerPanel.java
@@ -0,0 +1,530 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.HeadlessException;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.JTree;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.MutableTreeNode;
+import javax.swing.tree.TreePath;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
+import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
+import org.openstreetmap.josm.tools.GBC;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+public class AddWMSLayerPanel extends JPanel {
+ private List<LayerDetails> selectedLayers;
+ private URL serviceUrl;
+ private LayerDetails selectedLayer;
+
+ private JTextField menuName;
+ private JTextArea resultingLayerField;
+ private MutableTreeNode treeRootNode;
+ private DefaultTreeModel treeData;
+ private JTree layerTree;
+ private JButton showBoundsButton;
+
+ private boolean previouslyShownUnsupportedCrsError = false;
+
+ public AddWMSLayerPanel() {
+ JPanel wmsFetchPanel = new JPanel(new GridBagLayout());
+ menuName = new JTextField(40);
+ menuName.setText(tr("Unnamed WMS Layer"));
+ final JTextArea serviceUrl = new JTextArea(3, 40);
+ serviceUrl.setLineWrap(true);
+ serviceUrl.setText("http://sample.com/wms?");
+ wmsFetchPanel.add(new JLabel(tr("Menu Name")), GBC.std().insets(0,0,5,0));
+ wmsFetchPanel.add(menuName, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
+ wmsFetchPanel.add(new JLabel(tr("Service URL")), GBC.std().insets(0,0,5,0));
+ JScrollPane scrollPane = new JScrollPane(serviceUrl,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ wmsFetchPanel.add(scrollPane, GBC.eop().insets(5,0,0,0));
+ JButton getLayersButton = new JButton(tr("Get Layers"));
+ getLayersButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Cursor beforeCursor = getCursor();
+ try {
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ attemptGetCapabilities(serviceUrl.getText());
+ } finally {
+ setCursor(beforeCursor);
+ }
+ }
+ });
+ wmsFetchPanel.add(getLayersButton, GBC.eop().anchor(GridBagConstraints.EAST));
+
+ treeRootNode = new DefaultMutableTreeNode();
+ treeData = new DefaultTreeModel(treeRootNode);
+ layerTree = new JTree(treeData);
+ layerTree.setCellRenderer(new LayerTreeCellRenderer());
+ layerTree.addTreeSelectionListener(new TreeSelectionListener() {
+
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ TreePath[] selectionRows = layerTree.getSelectionPaths();
+ if(selectionRows == null) {
+ showBoundsButton.setEnabled(false);
+ selectedLayer = null;
+ return;
+ }
+
+ selectedLayers = new LinkedList<LayerDetails>();
+ for (TreePath i : selectionRows) {
+ Object userObject = ((DefaultMutableTreeNode) i.getLastPathComponent()).getUserObject();
+ if(userObject instanceof LayerDetails) {
+ LayerDetails detail = (LayerDetails) userObject;
+ if(!detail.isSupported()) {
+ layerTree.removeSelectionPath(i);
+ if(!previouslyShownUnsupportedCrsError) {
+ JOptionPane.showMessageDialog(null, tr("That layer does not support any of JOSM's projections,\n" +
+ "so you can not use it. This message will not show again."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ previouslyShownUnsupportedCrsError = true;
+ }
+ } else if(detail.ident != null) {
+ selectedLayers.add(detail);
+ }
+ }
+ }
+
+ if (!selectedLayers.isEmpty()) {
+ resultingLayerField.setText(buildGetMapUrl());
+
+ if(selectedLayers.size() == 1) {
+ showBoundsButton.setEnabled(true);
+ selectedLayer = selectedLayers.get(0);
+ }
+ } else {
+ showBoundsButton.setEnabled(false);
+ selectedLayer = null;
+ }
+ }
+ });
+ wmsFetchPanel.add(new JScrollPane(layerTree), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
+
+ JPanel layerManipulationButtons = new JPanel();
+ showBoundsButton = new JButton(tr("Show Bounds"));
+ showBoundsButton.setEnabled(false);
+ showBoundsButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if(selectedLayer.bounds != null) {
+ SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
+ mapPanel.setBoundingBox(selectedLayer.bounds);
+ JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
+ } else {
+ JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ });
+ layerManipulationButtons.add(showBoundsButton);
+
+ wmsFetchPanel.add(layerManipulationButtons, GBC.eol().insets(0,0,5,0));
+ wmsFetchPanel.add(new JLabel(tr("WMS URL")), GBC.std().insets(0,0,5,0));
+ resultingLayerField = new JTextArea(3, 40);
+ resultingLayerField.setLineWrap(true);
+ wmsFetchPanel.add(new JScrollPane(resultingLayerField, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
+
+ add(wmsFetchPanel);
+ }
+
+ private String buildRootUrl() {
+ StringBuilder a = new StringBuilder(serviceUrl.getProtocol());
+ a.append("://");
+ a.append(serviceUrl.getHost());
+ if(serviceUrl.getPort() != -1) {
+ a.append(":");
+ a.append(serviceUrl.getPort());
+ }
+ a.append(serviceUrl.getPath());
+ a.append("?");
+ if(serviceUrl.getQuery() != null) {
+ a.append(serviceUrl.getQuery());
+ if (!serviceUrl.getQuery().isEmpty() && !serviceUrl.getQuery().endsWith("&")) {
+ a.append("&");
+ }
+ }
+ return a.toString();
+ }
+
+ private String buildGetMapUrl() {
+ StringBuilder a = new StringBuilder();
+ a.append(buildRootUrl());
+ a.append("FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=");
+ a.append(commaSepLayerList());
+ a.append("&");
+
+ return a.toString();
+ }
+
+ private String commaSepLayerList() {
+ StringBuilder b = new StringBuilder();
+
+ Iterator<LayerDetails> iterator = selectedLayers.iterator();
+ while (iterator.hasNext()) {
+ LayerDetails layerDetails = iterator.next();
+ b.append(layerDetails.ident);
+ if(iterator.hasNext()) {
+ b.append(",");
+ }
+ }
+
+ return b.toString();
+ }
+
+ private void showError(String incomingData, Exception e) {
+ JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ System.err.println("Could not parse WMS layer list. Incoming data:");
+ System.err.println(incomingData);
+ e.printStackTrace();
+ }
+
+ private void attemptGetCapabilities(String serviceUrlStr) {
+ URL getCapabilitiesUrl = null;
+ try {
+ if (!serviceUrlStr.trim().contains("capabilities")) {
+ // If the url doesn't already have GetCapabilities, add it in
+ getCapabilitiesUrl = new URL(serviceUrlStr + "VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities");
+ } else {
+ // Otherwise assume it's a good URL and let the subsequent error
+ // handling systems deal with problems
+ getCapabilitiesUrl = new URL(serviceUrlStr);
+ }
+ serviceUrl = new URL(serviceUrlStr);
+ } catch (HeadlessException e) {
+ return;
+ } catch (MalformedURLException e) {
+ JOptionPane.showMessageDialog(this, tr("Invalid service URL."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ String incomingData;
+ try {
+ URLConnection openConnection = getCapabilitiesUrl.openConnection();
+ InputStream inputStream = openConnection.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
+ String line;
+ StringBuilder ba = new StringBuilder();
+ while((line = br.readLine()) != null) {
+ ba.append(line);
+ ba.append("\n");
+ }
+ incomingData = ba.toString();
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(this, tr("Could not retrieve WMS layer list."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ Document document;
+ try {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setValidating(false);
+ builderFactory.setNamespaceAware(true);
+ DocumentBuilder builder = builderFactory.newDocumentBuilder();
+ builder.setEntityResolver(new EntityResolver() {
+ @Override
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+ System.out.println("Ignoring DTD " + publicId + ", " + systemId);
+ return new InputSource(new StringReader(""));
+ }
+ });
+ document = builder.parse(new InputSource(new StringReader(incomingData)));
+ } catch (ParserConfigurationException e) {
+ showError(incomingData, e);
+ return;
+ } catch (SAXException e) {
+ showError(incomingData, e);
+ return;
+ } catch (IOException e) {
+ showError(incomingData, e);
+ return;
+ }
+
+ // Some WMS service URLs specify a different base URL for their GetMap service
+ Element child = getChild(document.getDocumentElement(), "Capability");
+ child = getChild(child, "Request");
+ child = getChild(child, "GetMap");
+ child = getChild(child, "DCPType");
+ child = getChild(child, "HTTP");
+ child = getChild(child, "Get");
+ child = getChild(child, "OnlineResource");
+ if (child != null) {
+ String baseURL = child.getAttribute("xlink:href");
+ if(baseURL != null) {
+ try {
+ System.out.println("GetCapabilities specifies a different service URL: " + baseURL);
+ serviceUrl = new URL(baseURL);
+ } catch (MalformedURLException e1) {
+ }
+ }
+ }
+
+ try {
+ treeRootNode.setUserObject(getCapabilitiesUrl.getHost());
+ Element capabilityElem = getChild(document.getDocumentElement(), "Capability");
+ List<Element> children = getChildren(capabilityElem, "Layer");
+ List<LayerDetails> layers = parseLayers(children, new HashSet<String>());
+ updateTreeList(layers);
+ } catch(Exception e) {
+ showError(incomingData, e);
+ return;
+ }
+ }
+
+ private void updateTreeList(List<LayerDetails> layers) {
+ addLayersToTreeData(treeRootNode, layers);
+ layerTree.expandRow(0);
+ }
+
+ private void addLayersToTreeData(MutableTreeNode parent, List<LayerDetails> layers) {
+ for (LayerDetails layerDetails : layers) {
+ DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(layerDetails);
+ addLayersToTreeData(treeNode, layerDetails.children);
+ treeData.insertNodeInto(treeNode, parent, 0);
+ }
+ }
+
+ private List<LayerDetails> parseLayers(List<Element> children, Set<String> parentCrs) {
+ List<LayerDetails> details = new LinkedList<LayerDetails>();
+ for (Element element : children) {
+ details.add(parseLayer(element, parentCrs));
+ }
+ return details;
+ }
+
+ private LayerDetails parseLayer(Element element, Set<String> parentCrs) {
+ String name = getChildContent(element, "Title", null, null);
+ String ident = getChildContent(element, "Name", null, null);
+
+ // The set of supported CRS/SRS for this layer
+ Set<String> crsList = new HashSet<String>();
+ // ...including this layer's already-parsed parent projections
+ crsList.addAll(parentCrs);
+
+ // Parse the CRS/SRS pulled out of this layer's XML element
+ // I think CRS and SRS are the same at this point
+ List<Element> crsChildren = getChildren(element, "CRS");
+ crsChildren.addAll(getChildren(element, "SRS"));
+ for (Element child : crsChildren) {
+ String crs = (String) getContent(child);
+ if(crs != null) {
+ String upperCase = crs.trim().toUpperCase();
+ crsList.add(upperCase);
+ }
+ }
+
+ // Check to see if any of the specified projections are supported by JOSM
+ boolean josmSupportsThisLayer = false;
+ for (String crs : crsList) {
+ josmSupportsThisLayer |= isProjSupported(crs);
+ }
+
+ Bounds bounds = null;
+ Element bboxElem = getChild(element, "EX_GeographicBoundingBox");
+ if(bboxElem != null) {
+ // Attempt to use EX_GeographicBoundingBox for bounding box
+ double left = Double.parseDouble(getChildContent(bboxElem, "westBoundLongitude", null, null));
+ double top = Double.parseDouble(getChildContent(bboxElem, "northBoundLatitude", null, null));
+ double right = Double.parseDouble(getChildContent(bboxElem, "eastBoundLongitude", null, null));
+ double bot = Double.parseDouble(getChildContent(bboxElem, "southBoundLatitude", null, null));
+ bounds = new Bounds(bot, left, top, right);
+ } else {
+ // If that's not available, try LatLonBoundingBox
+ bboxElem = getChild(element, "LatLonBoundingBox");
+ if(bboxElem != null) {
+ double left = Double.parseDouble(bboxElem.getAttribute("minx"));
+ double top = Double.parseDouble(bboxElem.getAttribute("maxy"));
+ double right = Double.parseDouble(bboxElem.getAttribute("maxx"));
+ double bot = Double.parseDouble(bboxElem.getAttribute("miny"));
+ bounds = new Bounds(bot, left, top, right);
+ }
+ }
+
+ List<Element> layerChildren = getChildren(element, "Layer");
+ List<LayerDetails> childLayers = parseLayers(layerChildren, crsList);
+
+ return new LayerDetails(name, ident, crsList, josmSupportsThisLayer, bounds, childLayers);
+ }
+
+ private boolean isProjSupported(String crs) {
+ for (Projection proj : Projection.allProjections) {
+ if (proj instanceof ProjectionSubPrefs) {
+ if (((ProjectionSubPrefs) proj).getPreferencesFromCode(crs) == null) {
+ return true;
+ }
+ } else {
+ if (proj.toCode().equals(crs)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public String getUrlName() {
+ return menuName.getText();
+ }
+
+ public String getUrl() {
+ return resultingLayerField.getText();
+ }
+
+ public static void main(String[] args) {
+ JFrame f = new JFrame("Test");
+ f.setContentPane(new AddWMSLayerPanel());
+ f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ f.pack();
+ f.setVisible(true);
+ }
+
+ private static String getChildContent(Element parent, String name, String missing, String empty) {
+ Element child = getChild(parent, name);
+ if (child == null) {
+ return missing;
+ } else {
+ String content = (String) getContent(child);
+ return (content != null) ? content : empty;
+ }
+ }
+
+ private static Object getContent(Element element) {
+ NodeList nl = element.getChildNodes();
+ StringBuffer content = new StringBuffer();
+ for (int i = 0; i < nl.getLength(); i++) {
+ Node node = nl.item(i);
+ switch (node.getNodeType()) {
+ case Node.ELEMENT_NODE:
+ return node;
+ case Node.CDATA_SECTION_NODE:
+ case Node.TEXT_NODE:
+ content.append(node.getNodeValue());
+ break;
+ }
+ }
+ return content.toString().trim();
+ }
+
+ private static List<Element> getChildren(Element parent, String name) {
+ List<Element> retVal = new LinkedList<Element>();
+ for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
+ if (child instanceof Element && name.equals(child.getNodeName())) {
+ retVal.add((Element) child);
+ }
+ }
+ return retVal;
+ }
+
+ private static Element getChild(Element parent, String name) {
+ if (parent == null) {
+ return null;
+ }
+ for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
+ if (child instanceof Element && name.equals(child.getNodeName())) {
+ return (Element) child;
+ }
+ }
+ return null;
+ }
+
+ class LayerDetails {
+
+ private String name;
+ private String ident;
+ private List<LayerDetails> children;
+ private Bounds bounds;
+ private boolean supported;
+
+ public LayerDetails(String name, String ident, Set<String> crsList,
+ boolean supportedLayer, Bounds bounds,
+ List<LayerDetails> childLayers) {
+ this.name = name;
+ this.ident = ident;
+ this.supported = supportedLayer;
+ this.children = childLayers;
+ this.bounds = bounds;
+ }
+
+ public boolean isSupported() {
+ return this.supported;
+ }
+
+ @Override
+ public String toString() {
+ if(this.name == null || this.name.isEmpty()) {
+ return this.ident;
+ } else {
+ return this.name;
+ }
+ }
+
+ }
+
+ class LayerTreeCellRenderer extends DefaultTreeCellRenderer {
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean sel, boolean expanded, boolean leaf, int row,
+ boolean hasFocus) {
+ super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
+ row, hasFocus);
+ DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+ Object userObject = treeNode.getUserObject();
+ if (userObject instanceof LayerDetails) {
+ LayerDetails layer = (LayerDetails) userObject;
+ setEnabled(layer.isSupported());
+ }
+ return this;
+ }
+ }
+
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/GeorefImage.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/GeorefImage.java
new file mode 100644
index 0000000..a18639b
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/GeorefImage.java
@@ -0,0 +1,248 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Image;
+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 java.lang.ref.SoftReference;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+import org.openstreetmap.josm.plugins.imagery.ImageryPreferences;
+
+public class GeorefImage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public enum State { IMAGE, NOT_IN_CACHE, FAILED};
+
+ private WMSLayer layer;
+ private State state;
+
+ private BufferedImage image;
+ private SoftReference<BufferedImage> reImg;
+ private int xIndex;
+ private int yIndex;
+
+ private static final Color transparentColor = new Color(0,0,0,0);
+ private Color fadeColor = transparentColor;
+
+ public EastNorth getMin() {
+ return layer.getEastNorth(xIndex, yIndex);
+ }
+
+ public EastNorth getMax() {
+ return layer.getEastNorth(xIndex+1, yIndex+1);
+ }
+
+
+ public GeorefImage(WMSLayer layer) {
+ this.layer = layer;
+ }
+
+ public void changePosition(int xIndex, int yIndex) {
+ if (!equalPosition(xIndex, yIndex)) {
+ this.xIndex = xIndex;
+ this.yIndex = yIndex;
+ this.image = null;
+ flushedResizedCachedInstance();
+ }
+ }
+
+ public boolean equalPosition(int xIndex, int yIndex) {
+ return this.xIndex == xIndex && this.yIndex == yIndex;
+ }
+
+ public void changeImage(State state, BufferedImage image) {
+ flushedResizedCachedInstance();
+ this.image = image;
+ this.state = state;
+
+ switch (state) {
+ case FAILED:
+ {
+ BufferedImage img = createImage();
+ Graphics g = img.getGraphics();
+ g.setColor(Color.RED);
+ g.fillRect(0, 0, img.getWidth(), img.getHeight());
+ g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(36.0f));
+ g.setColor(Color.BLACK);
+ g.drawString(tr("Exception occurred"), 10, img.getHeight()/2);
+ this.image = img;
+ break;
+ }
+ case NOT_IN_CACHE:
+ {
+ BufferedImage img = createImage();
+ Graphics g = img.getGraphics();
+ g.setColor(Color.GRAY);
+ g.fillRect(0, 0, img.getWidth(), img.getHeight());
+ Font font = g.getFont();
+ Font tempFont = font.deriveFont(Font.PLAIN).deriveFont(36.0f);
+ g.setFont(tempFont);
+ g.setColor(Color.BLACK);
+ g.drawString(tr("Not in cache"), 10, img.getHeight()/2);
+ g.setFont(font);
+ this.image = img;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ private BufferedImage createImage() {
+ return new BufferedImage(layer.getBaseImageWidth(), layer.getBaseImageHeight(), BufferedImage.TYPE_INT_RGB);
+ }
+
+ public boolean paint(Graphics g, NavigatableComponent nc, int xIndex, int yIndex, int leftEdge, int bottomEdge) {
+ if (image == null)
+ return false;
+
+ if(!(this.xIndex == xIndex && this.yIndex == yIndex)){
+ return false;
+ }
+
+ int left = layer.getImageX(xIndex);
+ int bottom = layer.getImageY(yIndex);
+ int width = layer.getImageWidth(xIndex);
+ int height = layer.getImageHeight(yIndex);
+
+ int x = left - leftEdge;
+ int y = nc.getHeight() - (bottom - bottomEdge) - height;
+
+ // This happens if you zoom outside the world
+ if(width == 0 || height == 0)
+ return false;
+
+ // TODO: implement per-layer fade color
+ Color newFadeColor;
+ if (ImageryPreferences.PROP_FADE_AMOUNT.get() == 0)
+ newFadeColor = transparentColor;
+ else
+ newFadeColor = ImageryPreferences.getFadeColorWithAlpha();
+
+ BufferedImage img = reImg == null?null:reImg.get();
+ if(img != null && img.getWidth() == width && img.getHeight() == height && fadeColor.equals(newFadeColor)) {
+ g.drawImage(img, x, y, null);
+ return true;
+ }
+
+ fadeColor = newFadeColor;
+
+ boolean alphaChannel = WMSLayer.PROP_ALPHA_CHANNEL.get() && getImage().getTransparency() != Transparency.OPAQUE;
+
+ try {
+ if(img != null) img.flush();
+ long freeMem = Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory();
+ //System.out.println("Free Memory: "+ (freeMem/1024/1024) +" MB");
+ // Notice that this value can get negative due to integer overflows
+ //System.out.println("Img Size: "+ (width*height*3/1024/1024) +" MB");
+
+ int multipl = alphaChannel ? 4 : 3;
+ // This happens when requesting images while zoomed out and then zooming in
+ // Storing images this large in memory will certainly hang up JOSM. Luckily
+ // traditional rendering is as fast at these zoom levels, so it's no loss.
+ // Also prevent caching if we're out of memory soon
+ if(width > 2000 || height > 2000 || width*height*multipl > freeMem) {
+ fallbackDraw(g, getImage(), x, y, width, height, alphaChannel);
+ } else {
+ // We haven't got a saved resized copy, so resize and cache it
+ img = new BufferedImage(width, height, alphaChannel?BufferedImage.TYPE_INT_ARGB:BufferedImage.TYPE_3BYTE_BGR);
+ img.getGraphics().drawImage(getImage(),
+ 0, 0, width, height, // dest
+ 0, 0, getImage().getWidth(null), getImage().getHeight(null), // src
+ null);
+ if (!alphaChannel) {
+ drawFadeRect(img.getGraphics(), 0, 0, width, height);
+ }
+ img.getGraphics().dispose();
+ g.drawImage(img, x, y, null);
+ reImg = new SoftReference<BufferedImage>(img);
+ }
+ } catch(Exception e) {
+ fallbackDraw(g, getImage(), x, y, width, height, alphaChannel);
+ }
+ return true;
+ }
+
+ private void fallbackDraw(Graphics g, Image img, int x, int y, int width, int height, boolean alphaChannel) {
+ flushedResizedCachedInstance();
+ g.drawImage(
+ img, x, y, x + width, y + height,
+ 0, 0, img.getWidth(null), img.getHeight(null),
+ null);
+ if (!alphaChannel) { //FIXME: fading for layers with alpha channel currently is not supported
+ drawFadeRect(g, x, y, width, height);
+ }
+ }
+
+ private void drawFadeRect(Graphics g, int x, int y, int width, int height) {
+ if (fadeColor != transparentColor) {
+ g.setColor(fadeColor);
+ g.fillRect(x, y, width, height);
+ }
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ state = (State) in.readObject();
+ boolean hasImage = in.readBoolean();
+ if (hasImage)
+ image = (ImageIO.read(ImageIO.createImageInputStream(in)));
+ else {
+ in.readObject(); // read null from input stream
+ image = null;
+ }
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.writeObject(state);
+ if(getImage() == null) {
+ out.writeBoolean(false);
+ out.writeObject(null);
+ } else {
+ out.writeBoolean(true);
+ ImageIO.write(getImage(), "png", ImageIO.createImageOutputStream(out));
+ }
+ }
+
+ public void flushedResizedCachedInstance() {
+ if (reImg != null) {
+ BufferedImage img = reImg.get();
+ if (img != null) {
+ img.flush();
+ }
+ }
+ reImg = null;
+ }
+
+
+ public BufferedImage getImage() {
+ return image;
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public int getXIndex() {
+ return xIndex;
+ }
+
+ public int getYIndex() {
+ return yIndex;
+ }
+
+ public void setLayer(WMSLayer layer) {
+ this.layer = layer;
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/Grabber.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/Grabber.java
new file mode 100644
index 0000000..346f26f
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/Grabber.java
@@ -0,0 +1,115 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.ProjectionBounds;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.io.CacheFiles;
+import org.openstreetmap.josm.plugins.imagery.ImageryPlugin;
+import org.openstreetmap.josm.plugins.imagery.wms.GeorefImage.State;
+
+abstract public class Grabber implements Runnable {
+ protected final MapView mv;
+ protected final WMSLayer layer;
+ protected final CacheFiles cache;
+
+ protected ProjectionBounds b;
+ protected Projection proj;
+ protected double pixelPerDegree;
+ protected WMSRequest request;
+ protected volatile boolean canceled;
+
+ Grabber(MapView mv, WMSLayer layer, CacheFiles cache) {
+ this.mv = mv;
+ this.layer = layer;
+ this.cache = cache;
+ }
+
+ private void updateState(WMSRequest request) {
+ b = new ProjectionBounds(
+ layer.getEastNorth(request.getXIndex(), request.getYIndex()),
+ layer.getEastNorth(request.getXIndex() + 1, request.getYIndex() + 1));
+ if (b.min != null && b.max != null && ImageryPlugin.wmsAdapter.PROP_OVERLAP.get()) {
+ double eastSize = b.max.east() - b.min.east();
+ double northSize = b.max.north() - b.min.north();
+
+ double eastCoef = ImageryPlugin.wmsAdapter.PROP_OVERLAP_EAST.get() / 100.0;
+ double northCoef = ImageryPlugin.wmsAdapter.PROP_OVERLAP_NORTH.get() / 100.0;
+
+ this.b = new ProjectionBounds( new EastNorth(b.min.east(),
+ b.min.north()),
+ new EastNorth(b.max.east() + eastCoef * eastSize,
+ b.max.north() + northCoef * northSize));
+ }
+
+ this.proj = Main.proj;
+ this.pixelPerDegree = request.getPixelPerDegree();
+ this.request = request;
+ }
+
+ abstract void fetch(WMSRequest request) throws Exception; // the image fetch code
+
+ int width(){
+ return layer.getBaseImageWidth();
+ }
+ int height(){
+ return layer.getBaseImageHeight();
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ if (canceled) {
+ return;
+ }
+ WMSRequest request = layer.getRequest();
+ if (request == null) {
+ return;
+ }
+ updateState(request);
+ if(!loadFromCache(request)){
+ attempt(request);
+ }
+ if (request.getState() != null) {
+ layer.finishRequest(request);
+ mv.repaint();
+ }
+ }
+ }
+
+ protected void attempt(WMSRequest request){ // try to fetch the image
+ int maxTries = 5; // n tries for every image
+ for (int i = 1; i <= maxTries; i++) {
+ if (canceled) {
+ return;
+ }
+ try {
+ if (!layer.requestIsValid(request)) {
+ return;
+ }
+ fetch(request);
+ break; // break out of the retry loop
+ } catch (Exception e) {
+ try { // sleep some time and then ask the server again
+ Thread.sleep(random(1000, 2000));
+ } catch (InterruptedException e1) {}
+
+ if(i == maxTries) {
+ e.printStackTrace();
+ request.finish(State.FAILED, null);
+ }
+ }
+ }
+ }
+
+ public static int random(int min, int max) {
+ return (int)(Math.random() * ((max+1)-min) ) + min;
+ }
+
+ abstract public boolean loadFromCache(WMSRequest request);
+
+ public void cancel() {
+ canceled = true;
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/HTMLGrabber.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/HTMLGrabber.java
new file mode 100644
index 0000000..16c9bcb
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/HTMLGrabber.java
@@ -0,0 +1,46 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.io.CacheFiles;
+
+public class HTMLGrabber extends WMSGrabber {
+ HTMLGrabber(MapView mv, WMSLayer layer, CacheFiles cache) {
+ super(mv, layer, cache);
+ }
+
+ @Override
+ protected BufferedImage grab(URL url) throws IOException {
+ String urlstring = url.toExternalForm();
+
+ System.out.println("Grabbing HTML " + url);
+
+ ArrayList<String> cmdParams = new ArrayList<String>();
+ StringTokenizer st = new StringTokenizer(MessageFormat.format(
+ Main.pref.get("wmsplugin.browser", "webkit-image {0}"), urlstring));
+ while( st.hasMoreTokens() )
+ cmdParams.add(st.nextToken());
+
+ ProcessBuilder builder = new ProcessBuilder( cmdParams);
+
+ Process browser;
+ try {
+ browser = builder.start();
+ } catch(IOException ioe) {
+ throw new IOException( "Could not start browser. Please check that the executable path is correct.\n" + ioe.getMessage() );
+ }
+
+ BufferedImage img = ImageIO.read(browser.getInputStream());
+ cache.saveImg(urlstring, img);
+ return img;
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/Map_Rectifier_WMSmenuAction.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/Map_Rectifier_WMSmenuAction.java
new file mode 100644
index 0000000..b4f8a26
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/Map_Rectifier_WMSmenuAction.java
@@ -0,0 +1,238 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Toolkit;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.UrlLabel;
+
+public class Map_Rectifier_WMSmenuAction extends JosmAction {
+ /**
+ * Class that bundles all required information of a rectifier service
+ */
+ public static class RectifierService {
+ private final String name;
+ private final String url;
+ private final String wmsUrl;
+ private final Pattern urlRegEx;
+ private final Pattern idValidator;
+ public JRadioButton btn;
+ /**
+ * @param name: Name of the rectifing service
+ * @param url: URL to the service where users can register, upload, etc.
+ * @param wmsUrl: URL to the WMS server where JOSM will grab the images. Insert __s__ where the ID should be placed
+ * @param urlRegEx: a regular expression that determines if a given URL is one of the service and returns the WMS id if so
+ * @param idValidator: regular expression that checks if a given ID is syntactically valid
+ */
+ public RectifierService(String name, String url, String wmsUrl, String urlRegEx, String idValidator) {
+ this.name = name;
+ this.url = url;
+ this.wmsUrl = wmsUrl;
+ this.urlRegEx = Pattern.compile(urlRegEx);
+ this.idValidator = Pattern.compile(idValidator);
+ }
+
+ public boolean isSelected() {
+ return btn.isSelected();
+ }
+ }
+
+ /**
+ * List of available rectifier services. May be extended from the outside
+ */
+ public ArrayList<RectifierService> services = new ArrayList<RectifierService>();
+
+ public Map_Rectifier_WMSmenuAction() {
+ super(tr("Rectified Image..."),
+ "OLmarker",
+ tr("Download Rectified Images From Various Services"),
+ Shortcut.registerShortcut("wms:rectimg",
+ tr("WMS: {0}", tr("Rectified Image...")),
+ KeyEvent.VK_R,
+ Shortcut.GROUP_NONE),
+ true
+ );
+
+ // Add default services
+ services.add(
+ new RectifierService("Metacarta Map Rectifier",
+ "http://labs.metacarta.com/rectifier/",
+ "http://labs.metacarta.com/rectifier/wms.cgi?id=__s__&srs=EPSG:4326"
+ + "&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png&",
+ // This matches more than the "classic" WMS link, so users can pretty much
+ // copy any link as long as it includes the ID
+ "labs\\.metacarta\\.com/(?:.*?)(?:/|=)([0-9]+)(?:\\?|/|\\.|$)",
+ "^[0-9]+$")
+ );
+ services.add(
+ // TODO: Change all links to mapwarper.net once the project has moved.
+ // The RegEx already matches the new URL and old URLs will be forwarded
+ // to make the transition as smooth as possible for the users
+ new RectifierService("Geothings Map Warper",
+ "http://warper.geothings.net/",
+ "http://warper.geothings.net/maps/wms/__s__?request=GetMap&version=1.1.1"
+ + "&styles=&format=image/png&srs=epsg:4326&exceptions=application/vnd.ogc.se_inimage&",
+ // This matches more than the "classic" WMS link, so users can pretty much
+ // copy any link as long as it includes the ID
+ "(?:mapwarper\\.net|warper\\.geothings\\.net)/(?:.*?)/([0-9]+)(?:\\?|/|\\.|$)",
+ "^[0-9]+$")
+ );
+
+ // This service serves the purpose of "just this once" without forcing the user
+ // to commit the link to the preferences
+
+ // Clipboard content gets trimmed, so matching whitespace only ensures that this
+ // service will never be selected automatically.
+ services.add(new RectifierService(tr("Custom WMS Link"), "", "", "^\\s+$", ""));
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JPanel panel = new JPanel(new GridBagLayout());
+ panel.add(new JLabel(tr("Supported Rectifier Services:")), GBC.eol());
+
+ JTextField tfWmsUrl = new JTextField(30);
+
+ String clip = getClipboardContents();
+ ButtonGroup group = new ButtonGroup();
+
+ JRadioButton firstBtn = null;
+ for(RectifierService s : services) {
+ JRadioButton serviceBtn = new JRadioButton(s.name);
+ if(firstBtn == null)
+ firstBtn = serviceBtn;
+ // Checks clipboard contents against current service if no match has been found yet.
+ // If the contents match, they will be inserted into the text field and the corresponding
+ // service will be pre-selected.
+ if(!clip.equals("") && tfWmsUrl.getText().equals("")
+ && (s.urlRegEx.matcher(clip).find() || s.idValidator.matcher(clip).matches())) {
+ serviceBtn.setSelected(true);
+ tfWmsUrl.setText(clip);
+ }
+ s.btn = serviceBtn;
+ group.add(serviceBtn);
+ if(!s.url.equals("")) {
+ panel.add(serviceBtn, GBC.std());
+ panel.add(new UrlLabel(s.url, tr("Visit Homepage")), GBC.eol().anchor(GridBagConstraints.EAST));
+ } else
+ panel.add(serviceBtn, GBC.eol().anchor(GridBagConstraints.WEST));
+ }
+
+ // Fallback in case no match was found
+ if(tfWmsUrl.getText().equals("") && firstBtn != null)
+ firstBtn.setSelected(true);
+
+ panel.add(new JLabel(tr("WMS URL or Image ID:")), GBC.eol());
+ panel.add(tfWmsUrl, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
+
+ ExtendedDialog diag = new ExtendedDialog(Main.parent,
+ tr("Add Rectified Image"),
+
+ new String[] {tr("Add Rectified Image"), tr("Cancel")});
+ diag.setContent(panel);
+ diag.setButtonIcons(new String[] {"OLmarker.png", "cancel.png"});
+
+ // 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();
+ int answer = diag.getValue();
+ // Break loop when the user cancels
+ if(answer != 1)
+ break;
+
+ String text = tfWmsUrl.getText().trim();
+ // Loop all services until we find the selected one
+ for(RectifierService s : services) {
+ if(!s.isSelected())
+ continue;
+
+ // We've reached the custom WMS URL service
+ // Just set the URL and hope everything works out
+ if(s.wmsUrl.equals("")) {
+ addWMSLayer(s.name + " (" + text + ")", text);
+ break outer;
+ }
+
+ // First try to match if the entered string as an URL
+ Matcher m = s.urlRegEx.matcher(text);
+ if(m.find()) {
+ String id = m.group(1);
+ String newURL = s.wmsUrl.replaceAll("__s__", id);
+ String title = s.name + " (" + id + ")";
+ addWMSLayer(title, newURL);
+ break outer;
+ }
+ // If not, look if it's a valid ID for the selected service
+ if(s.idValidator.matcher(text).matches()) {
+ String newURL = s.wmsUrl.replaceAll("__s__", text);
+ String title = s.name + " (" + text + ")";
+ addWMSLayer(title, newURL);
+ break outer;
+ }
+
+ // We've found the selected service, but the entered string isn't suitable for
+ // it. So quit checking the other radio buttons
+ break;
+ }
+
+ // and display an error message. The while(true) ensures that the dialog pops up again
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("Couldn't match the entered link or id to the selected service. Please try again."),
+ tr("No valid WMS URL or id"),
+ JOptionPane.ERROR_MESSAGE);
+ diag.setVisible(true);
+ }
+ }
+
+ /**
+ * Adds a WMS Layer with given title and URL
+ * @param title: Name of the layer as it will shop up in the layer manager
+ * @param url: URL to the WMS server
+ */
+ private void addWMSLayer(String title, String url) {
+ Main.main.addLayer(new WMSLayer(new ImageryInfo(title, url)));
+ }
+
+ /**
+ * Helper function that extracts a String from the Clipboard if available.
+ * Returns an empty String otherwise
+ * @return String Clipboard contents if available
+ */
+ private String getClipboardContents() {
+ String result = "";
+ Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
+
+ if(contents == null || !contents.isDataFlavorSupported(DataFlavor.stringFlavor))
+ return "";
+
+ try {
+ result = (String)contents.getTransferData(DataFlavor.stringFlavor);
+ } catch(Exception ex) {
+ return "";
+ }
+ return result.trim();
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSAdapter.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSAdapter.java
new file mode 100644
index 0000000..cebf2ef
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSAdapter.java
@@ -0,0 +1,40 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.io.CacheFiles;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo.ImageryType;
+import org.openstreetmap.josm.plugins.imagery.wms.io.WMSLayerExporter;
+import org.openstreetmap.josm.plugins.imagery.wms.io.WMSLayerImporter;
+
+// WMSPlugin-specific functions
+public class WMSAdapter {
+ CacheFiles cache = new CacheFiles("wmsplugin");
+
+ public final IntegerProperty PROP_SIMULTANEOUS_CONNECTIONS = new IntegerProperty("wmsplugin.simultaneousConnections", 3);
+ public final BooleanProperty PROP_OVERLAP = new BooleanProperty("wmsplugin.url.overlap", false);
+ public final IntegerProperty PROP_OVERLAP_EAST = new IntegerProperty("wmsplugin.url.overlapEast", 14);
+ public final IntegerProperty PROP_OVERLAP_NORTH = new IntegerProperty("wmsplugin.url.overlapNorth", 4);
+
+ protected void initExporterAndImporter() {
+ ExtensionFileFilter.exporters.add(new WMSLayerExporter());
+ ExtensionFileFilter.importers.add(new WMSLayerImporter());
+ }
+
+ public WMSAdapter() {
+ cache.setExpire(CacheFiles.EXPIRE_MONTHLY, false);
+ cache.setMaxSize(70, false);
+ initExporterAndImporter();
+ }
+
+ public Grabber getGrabber(MapView mv, WMSLayer layer){
+ if(layer.getInfo().getImageryType() == ImageryType.HTML)
+ return new HTMLGrabber(mv, layer, cache);
+ else if(layer.getInfo().getImageryType() == ImageryType.WMS)
+ return new WMSGrabber(mv, layer, cache);
+ else throw new IllegalStateException("WMSAdapter.getGrabber() called for non-WMS layer type");
+ }
+
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSGrabber.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSGrabber.java
new file mode 100644
index 0000000..d0d199c
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSGrabber.java
@@ -0,0 +1,203 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+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.net.URLConnection;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.imageio.ImageIO;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.Mercator;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.io.CacheFiles;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.ProgressInputStream;
+import org.openstreetmap.josm.plugins.imagery.wms.GeorefImage.State;
+
+
+public class WMSGrabber extends Grabber {
+ public static boolean isUrlWithPatterns(String url) {
+ return url != null && url.contains("{") && url.contains("}");
+ }
+
+ protected String baseURL;
+ private final boolean urlWithPatterns;
+
+ WMSGrabber(MapView mv, WMSLayer layer, CacheFiles cache) {
+ super(mv, layer, cache);
+ this.baseURL = layer.getInfo().getURL();
+ /* URL containing placeholders? */
+ urlWithPatterns = isUrlWithPatterns(baseURL);
+ }
+
+ @Override
+ void fetch(WMSRequest request) throws Exception{
+ URL url = null;
+ try {
+ url = getURL(
+ b.min.east(), b.min.north(),
+ b.max.east(), b.max.north(),
+ width(), height());
+ request.finish(State.IMAGE, grab(url));
+
+ } catch(Exception e) {
+ e.printStackTrace();
+ throw new Exception(e.getMessage() + "\nImage couldn't be fetched: " + (url != null ? url.toString() : ""));
+ }
+ }
+
+ public static final NumberFormat latLonFormat = new DecimalFormat("###0.0000000",
+ new DecimalFormatSymbols(Locale.US));
+
+ protected URL getURL(double w, double s,double e,double n,
+ int wi, int ht) throws MalformedURLException {
+ String myProj = Main.proj.toCode();
+ if(Main.proj instanceof Mercator) // don't use mercator code directly
+ {
+ LatLon sw = Main.proj.eastNorth2latlon(new EastNorth(w, s));
+ LatLon ne = Main.proj.eastNorth2latlon(new EastNorth(e, n));
+ myProj = "EPSG:4326";
+ s = sw.lat();
+ w = sw.lon();
+ n = ne.lat();
+ e = ne.lon();
+ }
+
+ String str = baseURL;
+ String bbox = latLonFormat.format(w) + ","
+ + latLonFormat.format(s) + ","
+ + latLonFormat.format(e) + ","
+ + latLonFormat.format(n);
+
+ 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));
+ }
+ }
+ return new URL(str.replace(" ", "%20"));
+ }
+
+ static public String getProjection(String baseURL, Boolean warn)
+ {
+ String projname = Main.proj.toCode();
+ if(Main.proj instanceof Mercator) // don't use mercator code
+ projname = "EPSG:4326";
+ String res = "";
+ try
+ {
+ Matcher m = Pattern.compile(".*srs=([a-z0-9:]+).*").matcher(baseURL.toLowerCase());
+ if(m.matches())
+ {
+ projname = projname.toLowerCase();
+ if(!projname.equals(m.group(1)) && warn)
+ {
+ JOptionPane.showMessageDialog(Main.parent,
+ tr("The projection ''{0}'' in URL and current projection ''{1}'' mismatch.\n"
+ + "This may lead to wrong coordinates.",
+ m.group(1), projname),
+ tr("Warning"),
+ JOptionPane.WARNING_MESSAGE);
+ }
+ }
+ else
+ res ="&srs="+projname;
+ }
+ catch(Exception e)
+ {
+ }
+ return res;
+ }
+
+ @Override
+ public boolean loadFromCache(WMSRequest request) {
+ URL url = null;
+ try{
+ url = getURL(
+ b.min.east(), b.min.north(),
+ b.max.east(), b.max.north(),
+ width(), height());
+ } catch(Exception e) {
+ return false;
+ }
+ BufferedImage cached = cache.getImg(url.toString());
+ if((!request.isReal() && !layer.hasAutoDownload()) || cached != null){
+ if(cached == null){
+ request.finish(State.NOT_IN_CACHE, null);
+ return true;
+ }
+ request.finish(State.IMAGE, cached);
+ return true;
+ }
+ return false;
+ }
+
+ protected BufferedImage grab(URL url) throws IOException, OsmTransferException {
+ System.out.println("Grabbing WMS " + url);
+
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ if(layer.getInfo().getCookies() != null && !layer.getInfo().getCookies().equals(""))
+ conn.setRequestProperty("Cookie", layer.getInfo().getCookies());
+ conn.setRequestProperty("User-Agent", Main.pref.get("wmsplugin.user_agent", Version.getInstance().getAgentString()));
+ conn.setConnectTimeout(Main.pref.getInteger("wmsplugin.timeout.connect", 30) * 1000);
+ conn.setReadTimeout(Main.pref.getInteger("wmsplugin.timeout.read", 30) * 1000);
+
+ String contentType = conn.getHeaderField("Content-Type");
+ if( conn.getResponseCode() != 200
+ || contentType != null && !contentType.startsWith("image") ) {
+ throw new IOException(readException(conn));
+ }
+
+ InputStream is = new ProgressInputStream(conn, null);
+ BufferedImage img = ImageIO.read(is);
+ is.close();
+
+ cache.saveImg(url.toString(), img);
+ return img;
+ }
+
+ protected String readException(URLConnection conn) throws IOException {
+ StringBuilder exception = new StringBuilder();
+ InputStream in = conn.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+
+ String line = null;
+ while( (line = br.readLine()) != null) {
+ // filter non-ASCII characters and control characters
+ exception.append(line.replaceAll("[^\\p{Print}]", ""));
+ exception.append('\n');
+ }
+ return exception.toString();
+ }
+}
diff --git a/wmsplugin/src/wmsplugin/WMSLayer.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSLayer.java
similarity index 90%
copy from wmsplugin/src/wmsplugin/WMSLayer.java
copy to imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSLayer.java
index 716c2f2..de20cbc 100644
--- a/wmsplugin/src/wmsplugin/WMSLayer.java
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSLayer.java
@@ -1,11 +1,10 @@
-package wmsplugin;
+package org.openstreetmap.josm.plugins.imagery.wms;
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;
import java.io.FileInputStream;
@@ -22,8 +21,6 @@ import java.util.concurrent.locks.ReentrantLock;
import javax.swing.AbstractAction;
import javax.swing.Action;
-import javax.swing.Icon;
-import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
@@ -43,24 +40,23 @@ 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.CacheFiles;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo.ImageryType;
+import org.openstreetmap.josm.plugins.imagery.ImageryLayer;
+import org.openstreetmap.josm.plugins.imagery.ImageryPlugin;
+import org.openstreetmap.josm.plugins.imagery.wms.GeorefImage.State;
import org.openstreetmap.josm.tools.ImageProvider;
-import wmsplugin.GeorefImage.State;
-
/**
* This is a layer that grabs the current screen from an WMS server. The data
* fetched this way is tiled and managed to the disc to reduce server load.
*/
-public class WMSLayer extends Layer implements PreferenceChangedListener {
-
- protected static final Icon icon =
- new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png")));
+public class WMSLayer extends ImageryLayer implements PreferenceChangedListener {
public static final BooleanProperty PROP_ALPHA_CHANNEL = new BooleanProperty("wmsplugin.alpha_channel", true);
- WMSPlugin plugin = WMSPlugin.instance;
+ WMSAdapter plugin = ImageryPlugin.wmsAdapter;
public int messageNum = 5; //limit for messages per layer
- protected MapView mv;
protected String resolution;
protected int imageSize = 500;
protected int dax = 10;
@@ -68,14 +64,10 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
protected int daStep = 5;
protected int minZoom = 3;
- protected double dx = 0.0;
- protected double dy = 0.0;
-
protected GeorefImage[][] images;
protected final int serializeFormatVersion = 5;
protected boolean autoDownloadEnabled = true;
protected boolean settingsChanged;
- protected WMSInfo info;
// Image index boundary for current view
private volatile int bminx;
@@ -103,26 +95,25 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
private boolean isInvalidUrlConfirmed = false;
public WMSLayer() {
- this(new WMSInfo(tr("Blank Layer")));
+ this(new ImageryInfo(tr("Blank Layer")));
}
- public WMSLayer(WMSInfo info) {
- super(info.name);
+ public WMSLayer(ImageryInfo info) {
+ super(info);
setBackgroundLayer(true); /* set global background variable */
initializeImages();
- this.info = new WMSInfo(info);
- mv = Main.map.mapView;
- if(this.info.pixelPerDegree == 0.0)
+ this.info = new ImageryInfo(info);
+ if(this.info.getPixelPerDegree() == 0.0)
this.info.setPixelPerDegree(getPPD());
resolution = mv.getDist100PixelText();
- if(info.url != null) {
- WMSGrabber.getProjection(info.url, true);
+ if(info.getURL() != null) {
+ WMSGrabber.getProjection(info.getURL(), true);
startGrabberThreads();
- if(!info.url.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(info.url)) {
- if (!(info.url.endsWith("&") || info.url.endsWith("?"))) {
- if (!confirmMalformedUrl(info.url)) {
- System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", info.url));
+ if(info.getImageryType() == ImageryType.WMS && !WMSGrabber.isUrlWithPatterns(info.getURL())) {
+ if (!(info.getURL().endsWith("&") || info.getURL().endsWith("?"))) {
+ if (!confirmMalformedUrl(info.getURL())) {
+ System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", info.getURL()));
usesInvalidUrl = true;
setName(getName() + tr("(deactivated)"));
return;
@@ -138,7 +129,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public void doSetName(String name) {
setName(name);
- info.name = name;
+ info.setName(name);
}
public boolean hasAutoDownload(){
@@ -172,10 +163,6 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
}
}
- @Override public Icon getIcon() {
- return icon;
- }
-
@Override public String getToolTipText() {
if(autoDownloadEnabled)
return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution);
@@ -196,11 +183,11 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
private boolean zoomIsTooBig() {
//don't download when it's too outzoomed
- return info.pixelPerDegree / getPPD() > minZoom;
+ return info.getPixelPerDegree() / getPPD() > minZoom;
}
@Override public void paint(Graphics2D g, final MapView mv, Bounds b) {
- if(info.url == null || (usesInvalidUrl && !isInvalidUrlConfirmed)) return;
+ if(info.getURL() == null || (usesInvalidUrl && !isInvalidUrlConfirmed)) return;
settingsChanged = false;
@@ -253,40 +240,35 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
}
}
- public double getPPD(){
- ProjectionBounds bounds = mv.getProjectionBounds();
- return mv.getWidth() / (bounds.max.east() - bounds.min.east());
- }
-
+ @Override
public void displace(double dx, double dy) {
+ super.displace(dx, dy);
settingsChanged = true;
- this.dx += dx;
- this.dy += dy;
}
public int getImageXIndex(double coord) {
- return (int)Math.floor( ((coord - dx) * info.pixelPerDegree) / imageSize);
+ return (int)Math.floor( ((coord - dx) * info.getPixelPerDegree()) / imageSize);
}
public int getImageYIndex(double coord) {
- return (int)Math.floor( ((coord - dy) * info.pixelPerDegree) / imageSize);
+ return (int)Math.floor( ((coord - dy) * info.getPixelPerDegree()) / imageSize);
}
public int getImageX(int imageIndex) {
- return (int)(imageIndex * imageSize * (getPPD() / info.pixelPerDegree) + dx * getPPD());
+ return (int)(imageIndex * imageSize * (getPPD() / info.getPixelPerDegree()) + dx * getPPD());
}
public int getImageY(int imageIndex) {
- return (int)(imageIndex * imageSize * (getPPD() / info.pixelPerDegree) + dy * getPPD());
+ return (int)(imageIndex * imageSize * (getPPD() / info.getPixelPerDegree()) + dy * getPPD());
}
public int getImageWidth(int xIndex) {
- int overlap = (int)(plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_EAST.get() * imageSize * getPPD() / info.pixelPerDegree / 100:0);
+ int overlap = (int)(plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_EAST.get() * imageSize * getPPD() / info.getPixelPerDegree() / 100:0);
return getImageX(xIndex + 1) - getImageX(xIndex) + overlap;
}
public int getImageHeight(int yIndex) {
- int overlap = (int)(plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_NORTH.get() * imageSize * getPPD() / info.pixelPerDegree / 100:0);
+ int overlap = (int)(plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_NORTH.get() * imageSize * getPPD() / info.getPixelPerDegree() / 100:0);
return getImageY(yIndex + 1) - getImageY(yIndex) + overlap;
}
@@ -316,7 +298,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
* @return Real EastNorth of given tile. dx/dy is not counted in
*/
public EastNorth getEastNorth(int xIndex, int yIndex) {
- return new EastNorth((xIndex * imageSize) / info.pixelPerDegree, (yIndex * imageSize) / info.pixelPerDegree);
+ return new EastNorth((xIndex * imageSize) / info.getPixelPerDegree(), (yIndex * imageSize) / info.getPixelPerDegree());
}
@@ -351,7 +333,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
for(int y = bminy; y<=bmaxy; ++y){
GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
if (!img.paint(g, mv, x, y, leftEdge, bottomEdge)) {
- WMSRequest request = new WMSRequest(x, y, info.pixelPerDegree, real);
+ WMSRequest request = new WMSRequest(x, y, info.getPixelPerDegree(), real);
addRequest(request);
}
}
@@ -368,10 +350,6 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
}
}
- @Override public Object getInfoComponent() {
- return getToolTipText();
- }
-
@Override public Action[] getMenuEntries() {
return new Action[]{
LayerListDialog.getInstance().createActivateLayerAction(this),
@@ -409,7 +387,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
* @return -1 if request is no longer needed, otherwise priority of request (lower number <=> more important request)
*/
private int getRequestPriority(WMSRequest request) {
- if (request.getPixelPerDegree() != info.pixelPerDegree) {
+ if (request.getPixelPerDegree() != info.getPixelPerDegree()) {
return -1;
}
if (bminx > request.getXIndex()
@@ -519,6 +497,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public DownloadAction() {
super(tr("Download visible tiles"));
}
+ @Override
public void actionPerformed(ActionEvent ev) {
if (zoomIsTooBig()) {
JOptionPane.showMessageDialog(
@@ -537,6 +516,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public ChangeResolutionAction() {
super(tr("Change resolution"));
}
+ @Override
public void actionPerformed(ActionEvent ev) {
initializeImages();
resolution = mv.getDist100PixelText();
@@ -550,6 +530,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public ReloadErrorTilesAction() {
super(tr("Reload erroneous tiles"));
}
+ @Override
public void actionPerformed(ActionEvent ev) {
// Delete small files, because they're probably blank tiles.
// See https://josm.openstreetmap.de/ticket/2307
@@ -559,7 +540,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
for (int y = 0; y < day; ++y) {
GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
if(img.getState() == State.FAILED){
- addRequest(new WMSRequest(img.getXIndex(), img.getYIndex(), info.pixelPerDegree, true));
+ addRequest(new WMSRequest(img.getXIndex(), img.getYIndex(), info.getPixelPerDegree(), true));
mv.repaint();
}
}
@@ -571,6 +552,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public ToggleAlphaAction() {
super(tr("Alpha channel"));
}
+ @Override
public void actionPerformed(ActionEvent ev) {
JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource();
boolean alphaChannel = checkbox.isSelected();
@@ -585,11 +567,13 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
}
mv.repaint();
}
+ @Override
public Component createMenuComponent() {
JCheckBoxMenuItem item = new JCheckBoxMenuItem(this);
item.setSelected(PROP_ALPHA_CHANNEL.get());
return item;
}
+ @Override
public boolean supportLayers(List<Layer> layers) {
return layers.size() == 1 && layers.get(0) instanceof WMSLayer;
}
@@ -599,6 +583,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public SaveWmsAction() {
super(tr("Save WMS layer to file"), ImageProvider.get("save"));
}
+ @Override
public void actionPerformed(ActionEvent ev) {
File f = SaveActionBase.createAndOpenSaveFileChooser(
tr("Save WMS layer"), ".wms");
@@ -611,8 +596,8 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
oos.writeInt(dax);
oos.writeInt(day);
oos.writeInt(imageSize);
- oos.writeDouble(info.pixelPerDegree);
- oos.writeObject(info.name);
+ oos.writeDouble(info.getPixelPerDegree());
+ oos.writeObject(info.getName());
oos.writeObject(info.getFullURL());
oos.writeObject(images);
oos.close();
@@ -627,6 +612,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public LoadWmsAction() {
super(tr("Load WMS layer from file"), ImageProvider.get("load"));
}
+ @Override
public void actionPerformed(ActionEvent ev) {
JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true,
false, tr("Load WMS layer"), "wms");
@@ -664,7 +650,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
}
settingsChanged = true;
mv.repaint();
- if(info.url != null)
+ if(info.getURL() != null)
{
startGrabberThreads();
}
@@ -689,8 +675,9 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
public BookmarkWmsAction() {
super(tr("Set WMS Bookmark"));
}
+ @Override
public void actionPerformed(ActionEvent ev) {
- plugin.addLayer(new WMSInfo(info));
+ ImageryPlugin.instance.addLayer(new ImageryInfo(info));
}
}
@@ -700,16 +687,19 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
super(tr("Automatic downloading"));
}
+ @Override
public Component createMenuComponent() {
JCheckBoxMenuItem item = new JCheckBoxMenuItem(this);
item.setSelected(autoDownloadEnabled);
return item;
}
+ @Override
public boolean supportLayers(List<Layer> layers) {
return layers.size() == 1 && layers.get(0) instanceof WMSLayer;
}
+ @Override
public void actionPerformed(ActionEvent e) {
autoDownloadEnabled = !autoDownloadEnabled;
if (autoDownloadEnabled) {
@@ -773,6 +763,7 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
}
}
+ @Override
public void preferenceChanged(PreferenceChangeEvent event) {
if (event.getKey().equals(plugin.PROP_SIMULTANEOUS_CONNECTIONS.getKey())) {
cancelGrabberThreads(true);
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSRequest.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSRequest.java
new file mode 100644
index 0000000..8024fb2
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/WMSRequest.java
@@ -0,0 +1,102 @@
+package org.openstreetmap.josm.plugins.imagery.wms;
+
+import java.awt.image.BufferedImage;
+
+import org.openstreetmap.josm.plugins.imagery.wms.GeorefImage.State;
+
+public class WMSRequest implements Comparable<WMSRequest> {
+ private final int xIndex;
+ private final int yIndex;
+ private final double pixelPerDegree;
+ private final boolean real; // Download even if autodownloading is disabled
+ private int priority;
+ // Result
+ private State state;
+ private BufferedImage image;
+
+ public WMSRequest(int xIndex, int yIndex, double pixelPerDegree, boolean real) {
+ this.xIndex = xIndex;
+ this.yIndex = yIndex;
+ this.pixelPerDegree = pixelPerDegree;
+ this.real = real;
+ }
+
+ public void finish(State state, BufferedImage image) {
+ this.state = state;
+ this.image = image;
+ }
+
+ public int getXIndex() {
+ return xIndex;
+ }
+
+ public int getYIndex() {
+ return yIndex;
+ }
+
+ public double getPixelPerDegree() {
+ return pixelPerDegree;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(pixelPerDegree);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + xIndex;
+ result = prime * result + yIndex;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ WMSRequest other = (WMSRequest) obj;
+ if (Double.doubleToLongBits(pixelPerDegree) != Double
+ .doubleToLongBits(other.pixelPerDegree))
+ return false;
+ if (xIndex != other.xIndex)
+ return false;
+ if (yIndex != other.yIndex)
+ return false;
+ return true;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ @Override
+ public int compareTo(WMSRequest o) {
+ return priority - o.priority;
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public BufferedImage getImage() {
+ return image;
+ }
+
+ @Override
+ public String toString() {
+ return "WMSRequest [xIndex=" + xIndex + ", yIndex=" + yIndex
+ + ", pixelPerDegree=" + pixelPerDegree + "]";
+ }
+
+ public boolean isReal() {
+ return real;
+ }
+}
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/io/WMSLayerExporter.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/io/WMSLayerExporter.java
new file mode 100644
index 0000000..53d9dbd
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/io/WMSLayerExporter.java
@@ -0,0 +1,13 @@
+package org.openstreetmap.josm.plugins.imagery.wms.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.io.FileExporter;
+
+public class WMSLayerExporter extends FileExporter{
+
+ public WMSLayerExporter() {
+ super(new ExtensionFileFilter("wms", "wms", tr("WMS Files (*.wms)")));
+ }
+}
\ No newline at end of file
diff --git a/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/io/WMSLayerImporter.java b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/io/WMSLayerImporter.java
new file mode 100644
index 0000000..4e497f6
--- /dev/null
+++ b/imagery/src/org/openstreetmap/josm/plugins/imagery/wms/io/WMSLayerImporter.java
@@ -0,0 +1,14 @@
+package org.openstreetmap.josm.plugins.imagery.wms.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.io.FileImporter;
+
+public class WMSLayerImporter extends FileImporter{
+
+ public WMSLayerImporter() {
+ super(new ExtensionFileFilter("wms", "wms", tr("WMS Files (*.wms)")));
+ }
+
+}
diff --git a/routing/build.xml b/routing/build.xml
index ecc98b4..72ae19d 100644
--- a/routing/build.xml
+++ b/routing/build.xml
@@ -27,7 +27,7 @@
<project name="routing" default="dist" basedir=".">
<property name="commit.message" value="Changed the constructor signature of the plugin main class" />
- <property name="plugin.main.version" value="3408" />
+ <property name="plugin.main.version" value="3600" />
<!-- Define some properties -->
<property name="josm" location="../../core/dist/josm-custom.jar"/>
diff --git a/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java b/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
index 6cd0eab..cc879bc 100644
--- a/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
+++ b/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
@@ -130,6 +130,7 @@ public class RoutingLayer extends Layer {
*/
public final Node getNearestHighwayNode(Point p) {
Node nearest = null;
+ int snapDistance = NavigatableComponent.PROP_SNAP_DISTANCE.get();
double minDist = 0;
for (Way w : dataLayer.data.getWays()) {
if (w.isDeleted() || w.isIncomplete() || w.get("highway")==null) continue;
@@ -138,7 +139,7 @@ public class RoutingLayer extends Layer {
Point P = Main.map.mapView.getPoint(n);
double dist = p.distanceSq(P);
- if (dist < NavigatableComponent.snapDistance) {
+ if (dist < snapDistance) {
if ((nearest == null) || (dist < minDist)) {
nearest = n;
minDist = dist;
diff --git a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java
index 61b7f94..0bd178f 100644
--- a/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java
+++ b/routing/src/com/innovant/josm/plugin/routing/gui/RoutingPreferenceDialog.java
@@ -28,6 +28,8 @@
package com.innovant.josm.plugin.routing.gui;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.GridBagLayout;
@@ -50,17 +52,13 @@ 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.gui.preferences.PreferenceTabbedPane;
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 {
diff --git a/slippymap/.classpath b/slippymap/.classpath
index 17b8e6a..663bc1b 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/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
+ <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/slippymap/build.xml b/slippymap/build.xml
index 3151356..ebcfec7 100644
--- a/slippymap/build.xml
+++ b/slippymap/build.xml
@@ -26,8 +26,8 @@
-->
<project name="slippymap" default="dist" basedir=".">
- <property name="commit.message" value="Added haiti imagery tile source" />
- <property name="plugin.main.version" value="3408" />
+ <property name="commit.message" value="change ToU to the osm specific one (like it is done in Potlatch 2)" />
+ <property name="plugin.main.version" value="3687" />
<property name="josm" location="../../core/dist/josm-custom.jar"/>
<property name="plugin.dist.dir" value="../../dist"/>
diff --git a/slippymap/images/bing_maps.png b/slippymap/images/bing_maps.png
new file mode 100644
index 0000000..ae4367e
Binary files /dev/null and b/slippymap/images/bing_maps.png differ
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
index 1a1fb83..daae3ef 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
@@ -3,6 +3,7 @@ package org.openstreetmap.josm.plugins.slippymap;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Color;
+import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
@@ -12,8 +13,14 @@ import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.awt.font.TextAttribute;
+import java.awt.geom.Rectangle2D;
import java.awt.image.ImageObserver;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -103,6 +110,16 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
private JPopupMenu tileOptionMenu;
JCheckBoxMenuItem autoZoomPopup;
Tile showMetadataTile;
+ private Image attrImage;
+ private String attrTermsUrl;
+ private Rectangle attrImageBounds, attrToUBounds;
+ private static final Font ATTR_FONT = new Font("Arial", Font.PLAIN, 10);
+ private static final Font ATTR_LINK_FONT;
+ static {
+ HashMap<TextAttribute, Integer> aUnderline = new HashMap<TextAttribute, Integer>();
+ aUnderline.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
+ ATTR_LINK_FONT = ATTR_FONT.deriveFont(aUnderline);
+ }
void redraw()
{
@@ -114,6 +131,18 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
{
int origZoom = currentZoomLevel;
tileSource = SlippyMapPreferences.getMapSource();
+ boolean requireAttr = tileSource.requiresAttribution();
+ if(requireAttr) {
+ attrImage = tileSource.getAttributionImage();
+ if(attrImage == null) {
+ System.out.println("Attribution image was null.");
+ } else {
+ System.out.println("Got an attribution image " + attrImage.getHeight(this) + "x" + attrImage.getWidth(this));
+ }
+
+ attrTermsUrl = tileSource.getTermsOfUseURL();
+ }
+
// The minimum should also take care of integer parsing
// errors which would leave us with a zoom of -1 otherwise
if (tileSource.getMaxZoom() < currentZoomLevel)
@@ -242,10 +271,34 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
Main.map.mapView.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
- if (e.getButton() != MouseEvent.BUTTON3)
- return;
- clickedTile = getTileForPixelpos(e.getX(), e.getY());
- tileOptionMenu.show(e.getComponent(), e.getX(), e.getY());
+ if (e.getButton() == MouseEvent.BUTTON3) {
+ clickedTile = getTileForPixelpos(e.getX(), e.getY());
+ tileOptionMenu.show(e.getComponent(), e.getX(), e.getY());
+ } else if (e.getButton() == MouseEvent.BUTTON1) {
+ if(!tileSource.requiresAttribution()) {
+ return;
+ }
+
+ if(attrImageBounds.contains(e.getPoint())) {
+ try {
+ java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
+ desktop.browse(new URI(tileSource.getAttributionLinkURL()));
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } catch (URISyntaxException e1) {
+ e1.printStackTrace();
+ }
+ } else if(attrToUBounds.contains(e.getPoint())) {
+ try {
+ java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
+ desktop.browse(new URI(tileSource.getTermsOfUseURL()));
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } catch (URISyntaxException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
}
});
@@ -816,6 +869,7 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
// probably still initializing
return;
}
+
if (lastTopLeft != null && lastBotRight != null && topLeft.equalsEpsilon(lastTopLeft)
&& botRight.equalsEpsilon(lastBotRight) && bufferImage != null
&& mv.getWidth() == bufferImage.getWidth(null) && mv.getHeight() == bufferImage.getHeight(null)
@@ -907,6 +961,43 @@ public class SlippyMapLayer extends Layer implements ImageObserver,
for (Tile t : ts.allTiles()) {
this.paintTileText(ts, t, g, mv, currentZoomLevel, t);
}
+
+ if (tileSource.requiresAttribution()) {
+ // Draw attribution
+ g.setColor(Color.white);
+ Font font = g.getFont();
+ g.setFont(ATTR_LINK_FONT);
+
+ // Draw terms of use text
+ Rectangle2D termsStringBounds = g.getFontMetrics().getStringBounds("Background Terms of Use", g);
+ int textHeight = (int) termsStringBounds.getHeight() - 5;
+ int textWidth = (int) termsStringBounds.getWidth();
+ int termsTextY = mv.getHeight() - textHeight;
+ if(attrTermsUrl != null) {
+ int x = 2;
+ int y = mv.getHeight() - textHeight;
+ attrToUBounds = new Rectangle(x, y, textWidth, textHeight);
+ g.drawString("Background Terms of Use", x, y);
+ }
+
+ // Draw attribution logo
+ int imgWidth = attrImage.getWidth(this);
+ if(attrImage != null) {
+ int x = 2;
+ int height = attrImage.getHeight(this);
+ int y = termsTextY - height - textHeight - 5;
+ attrImageBounds = new Rectangle(x, y, imgWidth, height);
+ g.drawImage(attrImage, x, y, this);
+ }
+
+ g.setFont(ATTR_FONT);
+ String attributionText = tileSource.getAttributionText(currentZoomLevel, topLeft, botRight);
+ Rectangle2D stringBounds = g.getFontMetrics().getStringBounds(attributionText, g);
+ g.drawString(attributionText, mv.getWidth() - (int) stringBounds.getWidth(), mv.getHeight() - textHeight);
+
+ g.setFont(font);
+ }
+
oldg.drawImage(bufferImage, 0, 0, null);
if (autoZoomEnabled() && lastImageScale != null) {
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPlugin.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPlugin.java
index 008210a..018e184 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPlugin.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPlugin.java
@@ -1,11 +1,18 @@
package org.openstreetmap.josm.plugins.slippymap;
+import java.util.ArrayList;
import java.util.List;
+import org.openstreetmap.gui.jmapviewer.OsmTileSource.CycleMap;
+import org.openstreetmap.gui.jmapviewer.OsmTileSource.Mapnik;
+import org.openstreetmap.gui.jmapviewer.OsmTileSource.TilesAtHome;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
+import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser.TileSourceProvider;
import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
import org.openstreetmap.josm.plugins.Plugin;
import org.openstreetmap.josm.plugins.PluginInformation;
@@ -18,67 +25,79 @@ import org.openstreetmap.josm.plugins.PluginInformation;
*/
public class SlippyMapPlugin extends Plugin implements PreferenceChangedListener
{
- public SlippyMapPlugin(PluginInformation info)
- {
- super(info);
- Main.pref.addPreferenceChangeListener(this);
- }
+ public SlippyMapPlugin(PluginInformation info)
+ {
+ super(info);
+ Main.pref.addPreferenceChangeListener(this);
+ SlippyMapBBoxChooser.addTileSourceProvider(new TileSourceProvider() {
+ public List<TileSource> getTileSources() {
+ List<TileSource> result = new ArrayList<TileSource>();
+ for (TileSource ts: SlippyMapPreferences.getAllMapSources()) {
+ if (ts instanceof Mapnik || ts instanceof CycleMap || ts instanceof TilesAtHome) {
+ continue; // Already included in default list
+ }
+ result.add(ts);
+ }
+ return result;
+ }
+ });
+ }
- @Override
- public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame)
- {
- if (newFrame != null && SlippyMapPreferences.getMapSource() != SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
- SlippyMapLayer smlayer;
- smlayer = new SlippyMapLayer();
- Main.main.addLayer(smlayer);
- }
- }
+ @Override
+ public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame)
+ {
+ if (newFrame != null && SlippyMapPreferences.getMapSource() != SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
+ SlippyMapLayer smlayer;
+ smlayer = new SlippyMapLayer();
+ Main.main.addLayer(smlayer);
+ }
+ }
- /*
- * (non-Javadoc)
- *
- * @see org.openstreetmap.josm.plugins.Plugin#getPreferenceSetting()
- */
- @Override
- public PreferenceSetting getPreferenceSetting()
- {
- return new SlippyMapPreferenceSetting();
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openstreetmap.josm.plugins.Plugin#getPreferenceSetting()
+ */
+ @Override
+ public PreferenceSetting getPreferenceSetting()
+ {
+ return new SlippyMapPreferenceSetting();
+ }
- /*
- * (non-Javadoc)
- *
- * @seeorg.openstreetmap.josm.data.Preferences.PreferenceChangedListener#
- * preferenceChanged(java.lang.String, java.lang.String)
- */
- public void preferenceChanged(PreferenceChangeEvent event) {
- if (!Main.isDisplayingMapView()) {
- return;
- }
- List<SlippyMapLayer> layes = Main.map.mapView.getLayersOfType(SlippyMapLayer.class);
- assert layes.size() <= 1;
- SlippyMapLayer layer = layes.isEmpty()?null:layes.get(0);
+ /*
+ * (non-Javadoc)
+ *
+ * @seeorg.openstreetmap.josm.data.Preferences.PreferenceChangedListener#
+ * preferenceChanged(java.lang.String, java.lang.String)
+ */
+ public void preferenceChanged(PreferenceChangeEvent event) {
+ if (!Main.isDisplayingMapView()) {
+ return;
+ }
+ List<SlippyMapLayer> layes = Main.map.mapView.getLayersOfType(SlippyMapLayer.class);
+ assert layes.size() <= 1;
+ SlippyMapLayer layer = layes.isEmpty()?null:layes.get(0);
- if (event.getKey().equals(SlippyMapPreferences.PREFERENCE_TILE_SOURCE)) {
- if (layer == null && SlippyMapPreferences.getMapSource() != SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
- Main.map.mapView.addLayer(new SlippyMapLayer());
- } else if (layer != null && SlippyMapPreferences.getMapSource() == SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
- Main.map.mapView.removeLayer(layer);
- } else if (layer == null && SlippyMapPreferences.getMapSource() == SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
- // Do nothing
- } else {
- layer.newTileStorage();
- }
- } else if (event.getKey().startsWith(SlippyMapPreferences.PREFERENCE_PREFIX) && layer != null) {
- // System.err.println(this + ".preferenceChanged('" + key + "', '"
- // + newValue + "') called");
- // when fade background changed, no need to clear tile storage
- // TODO move this code to SlippyMapPreferences class.
- if (!event.getKey().equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) {
- layer.autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
- }
- layer.redraw();
- }
- }
+ if (event.getKey().equals(SlippyMapPreferences.PREFERENCE_TILE_SOURCE)) {
+ if (layer == null && SlippyMapPreferences.getMapSource() != SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
+ Main.map.mapView.addLayer(new SlippyMapLayer());
+ } else if (layer != null && SlippyMapPreferences.getMapSource() == SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
+ Main.map.mapView.removeLayer(layer);
+ } else if (layer == null && SlippyMapPreferences.getMapSource() == SlippyMapPreferences.NO_DEFAULT_TILE_SOURCE) {
+ // Do nothing
+ } else {
+ layer.newTileStorage();
+ }
+ } else if (event.getKey().startsWith(SlippyMapPreferences.PREFERENCE_PREFIX) && layer != null) {
+ // System.err.println(this + ".preferenceChanged('" + key + "', '"
+ // + newValue + "') called");
+ // when fade background changed, no need to clear tile storage
+ // TODO move this code to SlippyMapPreferences class.
+ if (!event.getKey().equals(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND)) {
+ layer.autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
+ }
+ layer.redraw();
+ }
+ }
}
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
index f945fac..7f76413 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
@@ -2,7 +2,12 @@ package org.openstreetmap.josm.plugins.slippymap;
import static org.openstreetmap.josm.tools.I18n.tr;
+import java.awt.Image;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -10,6 +15,15 @@ import org.openstreetmap.gui.jmapviewer.OsmTileSource;
import org.openstreetmap.gui.jmapviewer.OsmTileSource.AbstractOsmTileSource;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
/**
* Preferences for Slippy Map Tiles
@@ -275,27 +289,168 @@ public class SlippyMapPreferences
}
}
- public static class NearMap extends OsmTileSource.AbstractOsmTileSource {
- public NearMap() {
- super("NearMap Australia", "http://www.nearmap.com/maps/hl=en&nml=Vert&");
+ public static class BingAerial extends OsmTileSource.AbstractOsmTileSource {
+ private static String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU";
+ private static List<Attribution> attributions;
+
+ public BingAerial() {
+ super("Bing Aerial Maps", "http://ecn.t2.tiles.virtualearth.net/tiles/");
+
+ attributions = loadAttributionText();
+ System.err.println("Added " + attributions.size() + " attributions.");
+ }
+
+ class Attribution {
+ String attribution;
+ int minZoom;
+ int maxZoom;
+ Bounds bounds;
+ }
+
+ class AttrHandler extends DefaultHandler {
+
+ private String string;
+ private Attribution curr;
+ private List<Attribution> attributions = new ArrayList<Attribution>();
+ private double southLat;
+ private double northLat;
+ private double eastLon;
+ private double westLon;
+ private boolean inCoverage = false;
+
+ public void startElement(String uri, String stripped, String tagName,
+ Attributes attrs) throws SAXException {
+ if("ImageryProvider".equals(tagName)) {
+ curr = new Attribution();
+ } else if("CoverageArea".equals(tagName)) {
+ inCoverage = true;
+ }
+ }
+
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+ string = new String(ch, start, length);
+ }
+
+ public void endElement(String uri, String stripped, String tagName)
+ throws SAXException {
+ if("ImageryProvider".equals(tagName)) {
+ attributions.add(curr);
+ } else if("Attribution".equals(tagName)) {
+ curr.attribution = string;
+ } else if(inCoverage && "ZoomMin".equals(tagName)) {
+ curr.minZoom = Integer.parseInt(string);
+ } else if(inCoverage && "ZoomMax".equals(tagName)) {
+ curr.maxZoom = Integer.parseInt(string);
+ } else if(inCoverage && "SouthLatitude".equals(tagName)) {
+ southLat = Double.parseDouble(string);
+ } else if(inCoverage && "NorthLatitude".equals(tagName)) {
+ northLat = Double.parseDouble(string);
+ } else if(inCoverage && "EastLongitude".equals(tagName)) {
+ eastLon = Double.parseDouble(string);
+ } else if(inCoverage && "WestLongitude".equals(tagName)) {
+ westLon = Double.parseDouble(string);
+ } else if("BoundingBox".equals(tagName)) {
+ curr.bounds = new Bounds(northLat, westLon, southLat, eastLon);
+ } else if("CoverageArea".equals(tagName)) {
+ inCoverage = false;
+ }
+ string = "";
+ }
+ }
+
+ private List<Attribution> loadAttributionText() {
+ try {
+ URL u = new URL("http://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/0,0?zl=1&mapVersion=v1&key="+API_KEY+"&include=ImageryProviders&output=xml");
+ InputStream stream = u.openStream();
+ XMLReader parser = XMLReaderFactory.createXMLReader();
+ AttrHandler handler = new AttrHandler();
+ parser.setContentHandler(handler);
+ parser.parse(new InputSource(stream));
+ return handler.attributions;
+ } catch (IOException e) {
+ System.err.println("Could not open Bing aerials attribution metadata.");
+ } catch (SAXException e) {
+ System.err.println("Could not parse Bing aerials attribution metadata.");
+ e.printStackTrace();
+ }
+ return Collections.emptyList();
}
@Override
public int getMaxZoom() {
- return 21;
+ return 22;
+ }
+
+ @Override
+ public String getExtension() {
+ return("jpeg");
}
@Override
public String getTilePath(int zoom, int tilex, int tiley) {
- return "z=" + zoom + "&x=" + tilex + "&y=" + tiley;
+ String quadtree = computeQuadTree(zoom, tilex, tiley);
+ return "/tiles/a" + quadtree + "." + getExtension() + "?g=587";
}
public TileUpdate getTileUpdate() {
return TileUpdate.IfNoneMatch;
}
+
+ public boolean requiresAttribution() {
+ return true;
+ }
+
+ public Image getAttributionImage() {
+ return ImageProvider.get("bing_maps").getImage();
+ }
+
+ public String getAttributionLinkURL() {
+ //return "http://bing.com/maps"
+ // FIXME: I've set attributionLinkURL temporarily to ToU URL to comply with bing ToU
+ // (the requirement is that we have such a link at the bottom of the window)
+ return "http://opengeodata.org/microsoft-imagery-details";
+ }
+
+ public String getTermsOfUseURL() {
+ return "http://opengeodata.org/microsoft-imagery-details";
+ }
+
+ public String getAttributionText(int zoom, LatLon topLeft, LatLon botRight) {
+ Bounds windowBounds = new Bounds(topLeft, botRight);
+ StringBuilder a = new StringBuilder();
+ for (Attribution attr : attributions) {
+ Bounds attrBounds = attr.bounds;
+ if(zoom <= attr.maxZoom && zoom >= attr.minZoom) {
+ if(windowBounds.getMin().lon() < attrBounds.getMax().lon()
+ && windowBounds.getMax().lon() > attrBounds.getMin().lon()
+ && windowBounds.getMax().lat() < attrBounds.getMin().lat()
+ && windowBounds.getMin().lat() > attrBounds.getMax().lat()) {
+ a.append(attr.attribution);
+ a.append(" ");
+ }
+ }
+ }
+ return a.toString();
+ }
}
-
+ private static String computeQuadTree(int zoom, int tilex, int tiley) {
+ StringBuilder k = new StringBuilder();
+ for(int i = zoom; i > 0; i--) {
+ char digit = 48;
+ int mask = 1 << (i - 1);
+ if ((tilex & mask) != 0) {
+ digit += 1;
+ }
+ if ((tiley & mask) != 0) {
+ digit += 2;
+ }
+ k.append(digit);
+ }
+ return k.toString();
+ }
+
public static class HaitiImagery extends OsmTileSource.AbstractOsmTileSource {
public HaitiImagery() {
super("HaitiImagery", "http://gravitystorm.dev.openstreetmap.org/imagery/haiti");
@@ -399,10 +554,11 @@ public class SlippyMapPreferences
sources.add(new OsmTileSource.Mapnik());
sources.add(new OsmTileSource.CycleMap());
sources.add(new OsmTileSource.TilesAtHome());
+ // *PLEASE* do not enable BingAerial until we have legal approval.
+ sources.add(new BingAerial());
sources.add(new Coastline());
sources.add(new FreeMapySkPokus());
sources.add(new FreeMapySk());
- sources.add(new NearMap());
sources.add(new HaitiImagery());
sources.addAll(getCustomSources());
// Probably need to either add these or let users add them somehow
diff --git a/svn-info.xml b/svn-info.xml
index 468d4bf..bb78072 100644
--- a/svn-info.xml
+++ b/svn-info.xml
@@ -3,16 +3,16 @@
<entry
kind="dir"
path="plugins"
- revision="23541">
+ revision="24608">
<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="23541">
-<author>upliner</author>
-<date>2010-10-10T18:01:10.304771Z</date>
+ revision="24602">
+<author>malcolmh</author>
+<date>2010-12-05T20:42:28.719021Z</date>
</commit>
</entry>
</info>
diff --git a/wmsplugin/build.xml b/wmsplugin/build.xml
index cd59e06..8f7356d 100644
--- a/wmsplugin/build.xml
+++ b/wmsplugin/build.xml
@@ -27,7 +27,7 @@
<project name="wmsplugin" default="dist" basedir=".">
- <property name="commit.message" value="fixed josm bug 4671 - wms url for sicily has changed" />
+ <property name="commit.message" value="Add getDx() and getDy() methods to WMSLayer to enable other plugins to determine WMS layer shift, see #5565" />
<property name="plugin.main.version" value="3530" />
<property name="josm" location="../../core/dist/josm-custom.jar" />
diff --git a/wmsplugin/sources.cfg b/wmsplugin/sources.cfg
index 6740c95..32c037b 100644
--- a/wmsplugin/sources.cfg
+++ b/wmsplugin/sources.cfg
@@ -1,3 +1,6 @@
+# OUTDATED - only for old plugins
+# See http://josm.openstreetmap.de/wiki/Maps for newer data.
+#
# FORMAT
# default(true or false);Name;URL
# NOTE: default items should be common and worldwide
diff --git a/wmsplugin/src/wmsplugin/AddWMSLayerPanel.java b/wmsplugin/src/wmsplugin/AddWMSLayerPanel.java
index 2722cdf..b6a1a85 100644
--- a/wmsplugin/src/wmsplugin/AddWMSLayerPanel.java
+++ b/wmsplugin/src/wmsplugin/AddWMSLayerPanel.java
@@ -58,469 +58,469 @@ import org.xml.sax.SAXException;
public class AddWMSLayerPanel extends JPanel {
- private List<LayerDetails> selectedLayers;
- private URL serviceUrl;
- private LayerDetails selectedLayer;
-
- private JTextField menuName;
- private JTextArea resultingLayerField;
- private MutableTreeNode treeRootNode;
- private DefaultTreeModel treeData;
- private JTree layerTree;
- private JButton showBoundsButton;
-
- private boolean previouslyShownUnsupportedCrsError = false;
-
- public AddWMSLayerPanel() {
- JPanel wmsFetchPanel = new JPanel(new GridBagLayout());
- menuName = new JTextField(40);
- menuName.setText(tr("Unnamed WMS Layer"));
- final JTextArea serviceUrl = new JTextArea(3, 40);
- serviceUrl.setLineWrap(true);
- serviceUrl.setText("http://sample.com/wms?");
- wmsFetchPanel.add(new JLabel(tr("Menu Name")), GBC.std().insets(0,0,5,0));
- wmsFetchPanel.add(menuName, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
- wmsFetchPanel.add(new JLabel(tr("Service URL")), GBC.std().insets(0,0,5,0));
- JScrollPane scrollPane = new JScrollPane(serviceUrl,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
- JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
- wmsFetchPanel.add(scrollPane, GBC.eop().insets(5,0,0,0));
- JButton getLayersButton = new JButton(tr("Get Layers"));
- getLayersButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- Cursor beforeCursor = getCursor();
- try {
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- attemptGetCapabilities(serviceUrl.getText());
- } finally {
- setCursor(beforeCursor);
- }
- }
- });
- wmsFetchPanel.add(getLayersButton, GBC.eop().anchor(GridBagConstraints.EAST));
-
- treeRootNode = new DefaultMutableTreeNode();
- treeData = new DefaultTreeModel(treeRootNode);
- layerTree = new JTree(treeData);
- layerTree.setCellRenderer(new LayerTreeCellRenderer());
- layerTree.addTreeSelectionListener(new TreeSelectionListener() {
-
- public void valueChanged(TreeSelectionEvent e) {
- TreePath[] selectionRows = layerTree.getSelectionPaths();
- if(selectionRows == null) {
- showBoundsButton.setEnabled(false);
- selectedLayer = null;
- return;
- }
-
- selectedLayers = new LinkedList<LayerDetails>();
- for (TreePath i : selectionRows) {
- Object userObject = ((DefaultMutableTreeNode) i.getLastPathComponent()).getUserObject();
- if(userObject instanceof LayerDetails) {
- LayerDetails detail = (LayerDetails) userObject;
- if(!detail.isSupported()) {
- layerTree.removeSelectionPath(i);
- if(!previouslyShownUnsupportedCrsError) {
- JOptionPane.showMessageDialog(null, tr("That layer does not support any of JOSM's projections,\n" +
- "so you can not use it. This message will not show again."),
- tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
- previouslyShownUnsupportedCrsError = true;
- }
- } else if(detail.ident != null) {
- selectedLayers.add(detail);
- }
- }
- }
-
- if (!selectedLayers.isEmpty()) {
- resultingLayerField.setText(buildGetMapUrl());
-
- if(selectedLayers.size() == 1) {
- showBoundsButton.setEnabled(true);
- selectedLayer = selectedLayers.get(0);
- }
- } else {
- showBoundsButton.setEnabled(false);
- selectedLayer = null;
- }
- }
- });
- wmsFetchPanel.add(new JScrollPane(layerTree), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
-
- JPanel layerManipulationButtons = new JPanel();
- showBoundsButton = new JButton(tr("Show Bounds"));
- showBoundsButton.setEnabled(false);
- showBoundsButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- if(selectedLayer.bounds != null) {
- SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
- mapPanel.setBoundingBox(selectedLayer.bounds);
- JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
- } else {
- JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
- tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
- }
- }
- });
- layerManipulationButtons.add(showBoundsButton);
-
- wmsFetchPanel.add(layerManipulationButtons, GBC.eol().insets(0,0,5,0));
- wmsFetchPanel.add(new JLabel(tr("WMS URL")), GBC.std().insets(0,0,5,0));
- resultingLayerField = new JTextArea(3, 40);
- resultingLayerField.setLineWrap(true);
- wmsFetchPanel.add(new JScrollPane(resultingLayerField, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
-
- add(wmsFetchPanel);
- }
-
- private String buildRootUrl() {
- StringBuilder a = new StringBuilder(serviceUrl.getProtocol());
- a.append("://");
- a.append(serviceUrl.getHost());
- if(serviceUrl.getPort() != -1) {
- a.append(":");
- a.append(serviceUrl.getPort());
- }
- a.append(serviceUrl.getPath());
- a.append("?");
- if(serviceUrl.getQuery() != null) {
- a.append(serviceUrl.getQuery());
- if (!serviceUrl.getQuery().isEmpty() && !serviceUrl.getQuery().endsWith("&")) {
- a.append("&");
- }
- }
- return a.toString();
- }
-
- private String buildGetMapUrl() {
- StringBuilder a = new StringBuilder();
- a.append(buildRootUrl());
- a.append("FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=");
- a.append(commaSepLayerList());
- a.append("&");
-
- return a.toString();
- }
-
- private String commaSepLayerList() {
- StringBuilder b = new StringBuilder();
-
- Iterator<LayerDetails> iterator = selectedLayers.iterator();
- while (iterator.hasNext()) {
- LayerDetails layerDetails = iterator.next();
- b.append(layerDetails.ident);
- if(iterator.hasNext()) {
- b.append(",");
- }
- }
-
- return b.toString();
- }
-
- private void showError(String incomingData, Exception e) {
- JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
- tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
- System.err.println("Could not parse WMS layer list. Incoming data:");
- System.err.println(incomingData);
- e.printStackTrace();
- }
-
- private void attemptGetCapabilities(String serviceUrlStr) {
- URL getCapabilitiesUrl = null;
- try {
- if (!serviceUrlStr.trim().contains("capabilities")) {
- // If the url doesn't already have GetCapabilities, add it in
- getCapabilitiesUrl = new URL(serviceUrlStr + "VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities");
- } else {
- // Otherwise assume it's a good URL and let the subsequent error
- // handling systems deal with problems
- getCapabilitiesUrl = new URL(serviceUrlStr);
- }
- serviceUrl = new URL(serviceUrlStr);
- } catch (HeadlessException e) {
- return;
- } catch (MalformedURLException e) {
- JOptionPane.showMessageDialog(this, tr("Invalid service URL."),
- tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- String incomingData;
- try {
- URLConnection openConnection = getCapabilitiesUrl.openConnection();
- InputStream inputStream = openConnection.getInputStream();
- BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
- String line;
- StringBuilder ba = new StringBuilder();
- while((line = br.readLine()) != null) {
- ba.append(line);
- ba.append("\n");
- }
- incomingData = ba.toString();
- } catch (IOException e) {
- JOptionPane.showMessageDialog(this, tr("Could not retrieve WMS layer list."),
- tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- Document document;
- try {
- DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
- builderFactory.setValidating(false);
- builderFactory.setNamespaceAware(true);
- DocumentBuilder builder = builderFactory.newDocumentBuilder();
- builder.setEntityResolver(new EntityResolver() {
- public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
- System.out.println("Ignoring DTD " + publicId + ", " + systemId);
- return new InputSource(new StringReader(""));
- }
- });
- document = builder.parse(new InputSource(new StringReader(incomingData)));
- } catch (ParserConfigurationException e) {
- showError(incomingData, e);
- return;
- } catch (SAXException e) {
- showError(incomingData, e);
- return;
- } catch (IOException e) {
- showError(incomingData, e);
- return;
- }
-
- // Some WMS service URLs specify a different base URL for their GetMap service
- Element child = getChild(document.getDocumentElement(), "Capability");
- child = getChild(child, "Request");
- child = getChild(child, "GetMap");
- child = getChild(child, "DCPType");
- child = getChild(child, "HTTP");
- child = getChild(child, "Get");
- child = getChild(child, "OnlineResource");
- if (child != null) {
- String baseURL = child.getAttribute("xlink:href");
- if(baseURL != null) {
- try {
- System.out.println("GetCapabilities specifies a different service URL: " + baseURL);
- serviceUrl = new URL(baseURL);
- } catch (MalformedURLException e1) {
- }
- }
- }
-
- try {
- treeRootNode.setUserObject(getCapabilitiesUrl.getHost());
- Element capabilityElem = getChild(document.getDocumentElement(), "Capability");
- List<Element> children = getChildren(capabilityElem, "Layer");
- List<LayerDetails> layers = parseLayers(children, new HashSet<String>());
- updateTreeList(layers);
- } catch(Exception e) {
- showError(incomingData, e);
- return;
- }
- }
-
- private void updateTreeList(List<LayerDetails> layers) {
- addLayersToTreeData(treeRootNode, layers);
- layerTree.expandRow(0);
- }
-
- private void addLayersToTreeData(MutableTreeNode parent, List<LayerDetails> layers) {
- for (LayerDetails layerDetails : layers) {
- DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(layerDetails);
- addLayersToTreeData(treeNode, layerDetails.children);
- treeData.insertNodeInto(treeNode, parent, 0);
- }
- }
-
- private List<LayerDetails> parseLayers(List<Element> children, Set<String> parentCrs) {
- List<LayerDetails> details = new LinkedList<LayerDetails>();
- for (Element element : children) {
- details.add(parseLayer(element, parentCrs));
- }
- return details;
- }
-
- private LayerDetails parseLayer(Element element, Set<String> parentCrs) {
- String name = getChildContent(element, "Title", null, null);
- String ident = getChildContent(element, "Name", null, null);
-
- // The set of supported CRS/SRS for this layer
- Set<String> crsList = new HashSet<String>();
- // ...including this layer's already-parsed parent projections
- crsList.addAll(parentCrs);
-
- // Parse the CRS/SRS pulled out of this layer's XML element
- // I think CRS and SRS are the same at this point
- List<Element> crsChildren = getChildren(element, "CRS");
- crsChildren.addAll(getChildren(element, "SRS"));
- for (Element child : crsChildren) {
- String crs = (String) getContent(child);
- if(crs != null) {
- String upperCase = crs.trim().toUpperCase();
- crsList.add(upperCase);
- }
- }
-
- // Check to see if any of the specified projections are supported by JOSM
- boolean josmSupportsThisLayer = false;
- for (String crs : crsList) {
- josmSupportsThisLayer |= isProjSupported(crs);
- }
-
- Bounds bounds = null;
- Element bboxElem = getChild(element, "EX_GeographicBoundingBox");
- if(bboxElem != null) {
- // Attempt to use EX_GeographicBoundingBox for bounding box
- double left = Double.parseDouble(getChildContent(bboxElem, "westBoundLongitude", null, null));
- double top = Double.parseDouble(getChildContent(bboxElem, "northBoundLatitude", null, null));
- double right = Double.parseDouble(getChildContent(bboxElem, "eastBoundLongitude", null, null));
- double bot = Double.parseDouble(getChildContent(bboxElem, "southBoundLatitude", null, null));
- bounds = new Bounds(bot, left, top, right);
- } else {
- // If that's not available, try LatLonBoundingBox
- bboxElem = getChild(element, "LatLonBoundingBox");
- if(bboxElem != null) {
- double left = Double.parseDouble(bboxElem.getAttribute("minx"));
- double top = Double.parseDouble(bboxElem.getAttribute("maxy"));
- double right = Double.parseDouble(bboxElem.getAttribute("maxx"));
- double bot = Double.parseDouble(bboxElem.getAttribute("miny"));
- bounds = new Bounds(bot, left, top, right);
- }
- }
-
- List<Element> layerChildren = getChildren(element, "Layer");
- List<LayerDetails> childLayers = parseLayers(layerChildren, crsList);
-
- return new LayerDetails(name, ident, crsList, josmSupportsThisLayer, bounds, childLayers);
- }
-
- private boolean isProjSupported(String crs) {
- for (Projection proj : Projection.allProjections) {
- if (proj instanceof ProjectionSubPrefs) {
- if (((ProjectionSubPrefs) proj).getPreferencesFromCode(crs) == null) {
- return true;
- }
- } else {
- if (proj.toCode().equals(crs)) {
- return true;
- }
- }
- }
- return false;
- }
-
- public String getUrlName() {
- return menuName.getText();
- }
-
- public String getUrl() {
- return resultingLayerField.getText();
- }
-
- public static void main(String[] args) {
- JFrame f = new JFrame("Test");
- f.setContentPane(new AddWMSLayerPanel());
- f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- f.pack();
- f.setVisible(true);
- }
-
- private static String getChildContent(Element parent, String name, String missing, String empty) {
- Element child = getChild(parent, name);
- if (child == null) {
- return missing;
- } else {
- String content = (String) getContent(child);
- return (content != null) ? content : empty;
- }
- }
-
- private static Object getContent(Element element) {
- NodeList nl = element.getChildNodes();
- StringBuffer content = new StringBuffer();
- for (int i = 0; i < nl.getLength(); i++) {
- Node node = nl.item(i);
- switch (node.getNodeType()) {
- case Node.ELEMENT_NODE:
- return node;
- case Node.CDATA_SECTION_NODE:
- case Node.TEXT_NODE:
- content.append(node.getNodeValue());
- break;
- }
- }
- return content.toString().trim();
- }
-
- private static List<Element> getChildren(Element parent, String name) {
- List<Element> retVal = new LinkedList<Element>();
- for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
- if (child instanceof Element && name.equals(child.getNodeName())) {
- retVal.add((Element) child);
- }
- }
- return retVal;
- }
-
- private static Element getChild(Element parent, String name) {
- if (parent == null) {
- return null;
- }
- for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
- if (child instanceof Element && name.equals(child.getNodeName())) {
- return (Element) child;
- }
- }
- return null;
- }
-
- class LayerDetails {
-
- private String name;
- private String ident;
- private List<LayerDetails> children;
- private Bounds bounds;
- private boolean supported;
-
- public LayerDetails(String name, String ident, Set<String> crsList,
- boolean supportedLayer, Bounds bounds,
- List<LayerDetails> childLayers) {
- this.name = name;
- this.ident = ident;
- this.supported = supportedLayer;
- this.children = childLayers;
- this.bounds = bounds;
- }
-
- public boolean isSupported() {
- return this.supported;
- }
-
- @Override
- public String toString() {
- if(this.name == null || this.name.isEmpty()) {
- return this.ident;
- } else {
- return this.name;
- }
- }
-
- }
-
- class LayerTreeCellRenderer extends DefaultTreeCellRenderer {
- @Override
- public Component getTreeCellRendererComponent(JTree tree, Object value,
- boolean sel, boolean expanded, boolean leaf, int row,
- boolean hasFocus) {
- super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
- row, hasFocus);
- DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
- Object userObject = treeNode.getUserObject();
- if (userObject instanceof LayerDetails) {
- LayerDetails layer = (LayerDetails) userObject;
- setEnabled(layer.isSupported());
- }
- return this;
- }
- }
+ private List<LayerDetails> selectedLayers;
+ private URL serviceUrl;
+ private LayerDetails selectedLayer;
+
+ private JTextField menuName;
+ private JTextArea resultingLayerField;
+ private MutableTreeNode treeRootNode;
+ private DefaultTreeModel treeData;
+ private JTree layerTree;
+ private JButton showBoundsButton;
+
+ private boolean previouslyShownUnsupportedCrsError = false;
+
+ public AddWMSLayerPanel() {
+ JPanel wmsFetchPanel = new JPanel(new GridBagLayout());
+ menuName = new JTextField(40);
+ menuName.setText(tr("Unnamed WMS Layer"));
+ final JTextArea serviceUrl = new JTextArea(3, 40);
+ serviceUrl.setLineWrap(true);
+ serviceUrl.setText("http://sample.com/wms?");
+ wmsFetchPanel.add(new JLabel(tr("Menu Name")), GBC.std().insets(0,0,5,0));
+ wmsFetchPanel.add(menuName, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
+ wmsFetchPanel.add(new JLabel(tr("Service URL")), GBC.std().insets(0,0,5,0));
+ JScrollPane scrollPane = new JScrollPane(serviceUrl,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ wmsFetchPanel.add(scrollPane, GBC.eop().insets(5,0,0,0));
+ JButton getLayersButton = new JButton(tr("Get Layers"));
+ getLayersButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Cursor beforeCursor = getCursor();
+ try {
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ attemptGetCapabilities(serviceUrl.getText());
+ } finally {
+ setCursor(beforeCursor);
+ }
+ }
+ });
+ wmsFetchPanel.add(getLayersButton, GBC.eop().anchor(GridBagConstraints.EAST));
+
+ treeRootNode = new DefaultMutableTreeNode();
+ treeData = new DefaultTreeModel(treeRootNode);
+ layerTree = new JTree(treeData);
+ layerTree.setCellRenderer(new LayerTreeCellRenderer());
+ layerTree.addTreeSelectionListener(new TreeSelectionListener() {
+
+ public void valueChanged(TreeSelectionEvent e) {
+ TreePath[] selectionRows = layerTree.getSelectionPaths();
+ if(selectionRows == null) {
+ showBoundsButton.setEnabled(false);
+ selectedLayer = null;
+ return;
+ }
+
+ selectedLayers = new LinkedList<LayerDetails>();
+ for (TreePath i : selectionRows) {
+ Object userObject = ((DefaultMutableTreeNode) i.getLastPathComponent()).getUserObject();
+ if(userObject instanceof LayerDetails) {
+ LayerDetails detail = (LayerDetails) userObject;
+ if(!detail.isSupported()) {
+ layerTree.removeSelectionPath(i);
+ if(!previouslyShownUnsupportedCrsError) {
+ JOptionPane.showMessageDialog(null, tr("That layer does not support any of JOSM's projections,\n" +
+ "so you can not use it. This message will not show again."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ previouslyShownUnsupportedCrsError = true;
+ }
+ } else if(detail.ident != null) {
+ selectedLayers.add(detail);
+ }
+ }
+ }
+
+ if (!selectedLayers.isEmpty()) {
+ resultingLayerField.setText(buildGetMapUrl());
+
+ if(selectedLayers.size() == 1) {
+ showBoundsButton.setEnabled(true);
+ selectedLayer = selectedLayers.get(0);
+ }
+ } else {
+ showBoundsButton.setEnabled(false);
+ selectedLayer = null;
+ }
+ }
+ });
+ wmsFetchPanel.add(new JScrollPane(layerTree), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
+
+ JPanel layerManipulationButtons = new JPanel();
+ showBoundsButton = new JButton(tr("Show Bounds"));
+ showBoundsButton.setEnabled(false);
+ showBoundsButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if(selectedLayer.bounds != null) {
+ SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
+ mapPanel.setBoundingBox(selectedLayer.bounds);
+ JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
+ } else {
+ JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ });
+ layerManipulationButtons.add(showBoundsButton);
+
+ wmsFetchPanel.add(layerManipulationButtons, GBC.eol().insets(0,0,5,0));
+ wmsFetchPanel.add(new JLabel(tr("WMS URL")), GBC.std().insets(0,0,5,0));
+ resultingLayerField = new JTextArea(3, 40);
+ resultingLayerField.setLineWrap(true);
+ wmsFetchPanel.add(new JScrollPane(resultingLayerField, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
+
+ add(wmsFetchPanel);
+ }
+
+ private String buildRootUrl() {
+ StringBuilder a = new StringBuilder(serviceUrl.getProtocol());
+ a.append("://");
+ a.append(serviceUrl.getHost());
+ if(serviceUrl.getPort() != -1) {
+ a.append(":");
+ a.append(serviceUrl.getPort());
+ }
+ a.append(serviceUrl.getPath());
+ a.append("?");
+ if(serviceUrl.getQuery() != null) {
+ a.append(serviceUrl.getQuery());
+ if (!serviceUrl.getQuery().isEmpty() && !serviceUrl.getQuery().endsWith("&")) {
+ a.append("&");
+ }
+ }
+ return a.toString();
+ }
+
+ private String buildGetMapUrl() {
+ StringBuilder a = new StringBuilder();
+ a.append(buildRootUrl());
+ a.append("FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=");
+ a.append(commaSepLayerList());
+ a.append("&");
+
+ return a.toString();
+ }
+
+ private String commaSepLayerList() {
+ StringBuilder b = new StringBuilder();
+
+ Iterator<LayerDetails> iterator = selectedLayers.iterator();
+ while (iterator.hasNext()) {
+ LayerDetails layerDetails = iterator.next();
+ b.append(layerDetails.ident);
+ if(iterator.hasNext()) {
+ b.append(",");
+ }
+ }
+
+ return b.toString();
+ }
+
+ private void showError(String incomingData, Exception e) {
+ JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ System.err.println("Could not parse WMS layer list. Incoming data:");
+ System.err.println(incomingData);
+ e.printStackTrace();
+ }
+
+ private void attemptGetCapabilities(String serviceUrlStr) {
+ URL getCapabilitiesUrl = null;
+ try {
+ if (!serviceUrlStr.trim().contains("capabilities")) {
+ // If the url doesn't already have GetCapabilities, add it in
+ getCapabilitiesUrl = new URL(serviceUrlStr + "VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities");
+ } else {
+ // Otherwise assume it's a good URL and let the subsequent error
+ // handling systems deal with problems
+ getCapabilitiesUrl = new URL(serviceUrlStr);
+ }
+ serviceUrl = new URL(serviceUrlStr);
+ } catch (HeadlessException e) {
+ return;
+ } catch (MalformedURLException e) {
+ JOptionPane.showMessageDialog(this, tr("Invalid service URL."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ String incomingData;
+ try {
+ URLConnection openConnection = getCapabilitiesUrl.openConnection();
+ InputStream inputStream = openConnection.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
+ String line;
+ StringBuilder ba = new StringBuilder();
+ while((line = br.readLine()) != null) {
+ ba.append(line);
+ ba.append("\n");
+ }
+ incomingData = ba.toString();
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(this, tr("Could not retrieve WMS layer list."),
+ tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ Document document;
+ try {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setValidating(false);
+ builderFactory.setNamespaceAware(true);
+ DocumentBuilder builder = builderFactory.newDocumentBuilder();
+ builder.setEntityResolver(new EntityResolver() {
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+ System.out.println("Ignoring DTD " + publicId + ", " + systemId);
+ return new InputSource(new StringReader(""));
+ }
+ });
+ document = builder.parse(new InputSource(new StringReader(incomingData)));
+ } catch (ParserConfigurationException e) {
+ showError(incomingData, e);
+ return;
+ } catch (SAXException e) {
+ showError(incomingData, e);
+ return;
+ } catch (IOException e) {
+ showError(incomingData, e);
+ return;
+ }
+
+ // Some WMS service URLs specify a different base URL for their GetMap service
+ Element child = getChild(document.getDocumentElement(), "Capability");
+ child = getChild(child, "Request");
+ child = getChild(child, "GetMap");
+ child = getChild(child, "DCPType");
+ child = getChild(child, "HTTP");
+ child = getChild(child, "Get");
+ child = getChild(child, "OnlineResource");
+ if (child != null) {
+ String baseURL = child.getAttribute("xlink:href");
+ if(baseURL != null) {
+ try {
+ System.out.println("GetCapabilities specifies a different service URL: " + baseURL);
+ serviceUrl = new URL(baseURL);
+ } catch (MalformedURLException e1) {
+ }
+ }
+ }
+
+ try {
+ treeRootNode.setUserObject(getCapabilitiesUrl.getHost());
+ Element capabilityElem = getChild(document.getDocumentElement(), "Capability");
+ List<Element> children = getChildren(capabilityElem, "Layer");
+ List<LayerDetails> layers = parseLayers(children, new HashSet<String>());
+ updateTreeList(layers);
+ } catch(Exception e) {
+ showError(incomingData, e);
+ return;
+ }
+ }
+
+ private void updateTreeList(List<LayerDetails> layers) {
+ addLayersToTreeData(treeRootNode, layers);
+ layerTree.expandRow(0);
+ }
+
+ private void addLayersToTreeData(MutableTreeNode parent, List<LayerDetails> layers) {
+ for (LayerDetails layerDetails : layers) {
+ DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(layerDetails);
+ addLayersToTreeData(treeNode, layerDetails.children);
+ treeData.insertNodeInto(treeNode, parent, 0);
+ }
+ }
+
+ private List<LayerDetails> parseLayers(List<Element> children, Set<String> parentCrs) {
+ List<LayerDetails> details = new LinkedList<LayerDetails>();
+ for (Element element : children) {
+ details.add(parseLayer(element, parentCrs));
+ }
+ return details;
+ }
+
+ private LayerDetails parseLayer(Element element, Set<String> parentCrs) {
+ String name = getChildContent(element, "Title", null, null);
+ String ident = getChildContent(element, "Name", null, null);
+
+ // The set of supported CRS/SRS for this layer
+ Set<String> crsList = new HashSet<String>();
+ // ...including this layer's already-parsed parent projections
+ crsList.addAll(parentCrs);
+
+ // Parse the CRS/SRS pulled out of this layer's XML element
+ // I think CRS and SRS are the same at this point
+ List<Element> crsChildren = getChildren(element, "CRS");
+ crsChildren.addAll(getChildren(element, "SRS"));
+ for (Element child : crsChildren) {
+ String crs = (String) getContent(child);
+ if(crs != null) {
+ String upperCase = crs.trim().toUpperCase();
+ crsList.add(upperCase);
+ }
+ }
+
+ // Check to see if any of the specified projections are supported by JOSM
+ boolean josmSupportsThisLayer = false;
+ for (String crs : crsList) {
+ josmSupportsThisLayer |= isProjSupported(crs);
+ }
+
+ Bounds bounds = null;
+ Element bboxElem = getChild(element, "EX_GeographicBoundingBox");
+ if(bboxElem != null) {
+ // Attempt to use EX_GeographicBoundingBox for bounding box
+ double left = Double.parseDouble(getChildContent(bboxElem, "westBoundLongitude", null, null));
+ double top = Double.parseDouble(getChildContent(bboxElem, "northBoundLatitude", null, null));
+ double right = Double.parseDouble(getChildContent(bboxElem, "eastBoundLongitude", null, null));
+ double bot = Double.parseDouble(getChildContent(bboxElem, "southBoundLatitude", null, null));
+ bounds = new Bounds(bot, left, top, right);
+ } else {
+ // If that's not available, try LatLonBoundingBox
+ bboxElem = getChild(element, "LatLonBoundingBox");
+ if(bboxElem != null) {
+ double left = Double.parseDouble(bboxElem.getAttribute("minx"));
+ double top = Double.parseDouble(bboxElem.getAttribute("maxy"));
+ double right = Double.parseDouble(bboxElem.getAttribute("maxx"));
+ double bot = Double.parseDouble(bboxElem.getAttribute("miny"));
+ bounds = new Bounds(bot, left, top, right);
+ }
+ }
+
+ List<Element> layerChildren = getChildren(element, "Layer");
+ List<LayerDetails> childLayers = parseLayers(layerChildren, crsList);
+
+ return new LayerDetails(name, ident, crsList, josmSupportsThisLayer, bounds, childLayers);
+ }
+
+ private boolean isProjSupported(String crs) {
+ for (Projection proj : Projection.allProjections) {
+ if (proj instanceof ProjectionSubPrefs) {
+ if (((ProjectionSubPrefs) proj).getPreferencesFromCode(crs) == null) {
+ return true;
+ }
+ } else {
+ if (proj.toCode().equals(crs)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public String getUrlName() {
+ return menuName.getText();
+ }
+
+ public String getUrl() {
+ return resultingLayerField.getText();
+ }
+
+ public static void main(String[] args) {
+ JFrame f = new JFrame("Test");
+ f.setContentPane(new AddWMSLayerPanel());
+ f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ f.pack();
+ f.setVisible(true);
+ }
+
+ private static String getChildContent(Element parent, String name, String missing, String empty) {
+ Element child = getChild(parent, name);
+ if (child == null) {
+ return missing;
+ } else {
+ String content = (String) getContent(child);
+ return (content != null) ? content : empty;
+ }
+ }
+
+ private static Object getContent(Element element) {
+ NodeList nl = element.getChildNodes();
+ StringBuffer content = new StringBuffer();
+ for (int i = 0; i < nl.getLength(); i++) {
+ Node node = nl.item(i);
+ switch (node.getNodeType()) {
+ case Node.ELEMENT_NODE:
+ return node;
+ case Node.CDATA_SECTION_NODE:
+ case Node.TEXT_NODE:
+ content.append(node.getNodeValue());
+ break;
+ }
+ }
+ return content.toString().trim();
+ }
+
+ private static List<Element> getChildren(Element parent, String name) {
+ List<Element> retVal = new LinkedList<Element>();
+ for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
+ if (child instanceof Element && name.equals(child.getNodeName())) {
+ retVal.add((Element) child);
+ }
+ }
+ return retVal;
+ }
+
+ private static Element getChild(Element parent, String name) {
+ if (parent == null) {
+ return null;
+ }
+ for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
+ if (child instanceof Element && name.equals(child.getNodeName())) {
+ return (Element) child;
+ }
+ }
+ return null;
+ }
+
+ class LayerDetails {
+
+ private String name;
+ private String ident;
+ private List<LayerDetails> children;
+ private Bounds bounds;
+ private boolean supported;
+
+ public LayerDetails(String name, String ident, Set<String> crsList,
+ boolean supportedLayer, Bounds bounds,
+ List<LayerDetails> childLayers) {
+ this.name = name;
+ this.ident = ident;
+ this.supported = supportedLayer;
+ this.children = childLayers;
+ this.bounds = bounds;
+ }
+
+ public boolean isSupported() {
+ return this.supported;
+ }
+
+ @Override
+ public String toString() {
+ if(this.name == null || this.name.isEmpty()) {
+ return this.ident;
+ } else {
+ return this.name;
+ }
+ }
+
+ }
+
+ class LayerTreeCellRenderer extends DefaultTreeCellRenderer {
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean sel, boolean expanded, boolean leaf, int row,
+ boolean hasFocus) {
+ super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
+ row, hasFocus);
+ DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+ Object userObject = treeNode.getUserObject();
+ if (userObject instanceof LayerDetails) {
+ LayerDetails layer = (LayerDetails) userObject;
+ setEnabled(layer.isSupported());
+ }
+ return this;
+ }
+ }
}
diff --git a/wmsplugin/src/wmsplugin/WMSLayer.java b/wmsplugin/src/wmsplugin/WMSLayer.java
index 716c2f2..60a9007 100644
--- a/wmsplugin/src/wmsplugin/WMSLayer.java
+++ b/wmsplugin/src/wmsplugin/WMSLayer.java
@@ -264,6 +264,14 @@ public class WMSLayer extends Layer implements PreferenceChangedListener {
this.dy += dy;
}
+ public double getDx() {
+ return dx;
+ }
+
+ public double getDy() {
+ return dy;
+ }
+
public int getImageXIndex(double coord) {
return (int)Math.floor( ((coord - dx) * info.pixelPerDegree) / imageSize);
}
diff --git a/wmsplugin/src/wmsplugin/WMSLayerInfo.java b/wmsplugin/src/wmsplugin/WMSLayerInfo.java
index 812c60f..519d7d7 100644
--- a/wmsplugin/src/wmsplugin/WMSLayerInfo.java
+++ b/wmsplugin/src/wmsplugin/WMSLayerInfo.java
@@ -20,8 +20,7 @@ import org.openstreetmap.josm.io.MirroredInputStream;
public class WMSLayerInfo {
ArrayList<WMSInfo> layers = new ArrayList<WMSInfo>();
ArrayList<WMSInfo> defaultLayers = new ArrayList<WMSInfo>();
- private final static String[] DEFAULT_LAYER_SITES = {
- "http://svn.openstreetmap.org/applications/editors/josm/plugins/wmsplugin/sources.cfg"};
+ private final static String[] DEFAULT_LAYER_SITES = { "http://josm.openstreetmap.de/maps"};
public void load() {
layers.clear();
--
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