| 1 | // License: GPL. For details, see LICENSE file. |
|---|
| 2 | package org.openstreetmap.josm.actions; |
|---|
| 3 | |
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
|---|
| 5 | |
|---|
| 6 | import java.awt.GridBagConstraints; |
|---|
| 7 | import java.awt.GridBagLayout; |
|---|
| 8 | import java.awt.event.ActionEvent; |
|---|
| 9 | import java.awt.event.KeyEvent; |
|---|
| 10 | import java.util.ArrayList; |
|---|
| 11 | import java.util.regex.Matcher; |
|---|
| 12 | import java.util.regex.Pattern; |
|---|
| 13 | |
|---|
| 14 | import javax.swing.ButtonGroup; |
|---|
| 15 | import javax.swing.JLabel; |
|---|
| 16 | import javax.swing.JOptionPane; |
|---|
| 17 | import javax.swing.JPanel; |
|---|
| 18 | import javax.swing.JRadioButton; |
|---|
| 19 | import javax.swing.JTextField; |
|---|
| 20 | |
|---|
| 21 | import org.openstreetmap.josm.Main; |
|---|
| 22 | import org.openstreetmap.josm.data.imagery.ImageryInfo; |
|---|
| 23 | import org.openstreetmap.josm.gui.ExtendedDialog; |
|---|
| 24 | import org.openstreetmap.josm.gui.layer.WMSLayer; |
|---|
| 25 | import org.openstreetmap.josm.tools.GBC; |
|---|
| 26 | import org.openstreetmap.josm.tools.Shortcut; |
|---|
| 27 | import org.openstreetmap.josm.tools.UrlLabel; |
|---|
| 28 | import org.openstreetmap.josm.tools.Utils; |
|---|
| 29 | |
|---|
| 30 | public class Map_Rectifier_WMSmenuAction extends JosmAction { |
|---|
| 31 | /** |
|---|
| 32 | * Class that bundles all required information of a rectifier service |
|---|
| 33 | */ |
|---|
| 34 | public static class RectifierService { |
|---|
| 35 | private final String name; |
|---|
| 36 | private final String url; |
|---|
| 37 | private final String wmsUrl; |
|---|
| 38 | private final Pattern urlRegEx; |
|---|
| 39 | private final Pattern idValidator; |
|---|
| 40 | public JRadioButton btn; |
|---|
| 41 | /** |
|---|
| 42 | * @param name: Name of the rectifing service |
|---|
| 43 | * @param url: URL to the service where users can register, upload, etc. |
|---|
| 44 | * @param wmsUrl: URL to the WMS server where JOSM will grab the images. Insert __s__ where the ID should be placed |
|---|
| 45 | * @param urlRegEx: a regular expression that determines if a given URL is one of the service and returns the WMS id if so |
|---|
| 46 | * @param idValidator: regular expression that checks if a given ID is syntactically valid |
|---|
| 47 | */ |
|---|
| 48 | public RectifierService(String name, String url, String wmsUrl, String urlRegEx, String idValidator) { |
|---|
| 49 | this.name = name; |
|---|
| 50 | this.url = url; |
|---|
| 51 | this.wmsUrl = wmsUrl; |
|---|
| 52 | this.urlRegEx = Pattern.compile(urlRegEx); |
|---|
| 53 | this.idValidator = Pattern.compile(idValidator); |
|---|
| 54 | } |
|---|
| 55 | |
|---|
| 56 | public boolean isSelected() { |
|---|
| 57 | return btn.isSelected(); |
|---|
| 58 | } |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | /** |
|---|
| 62 | * List of available rectifier services. May be extended from the outside |
|---|
| 63 | */ |
|---|
| 64 | public ArrayList<RectifierService> services = new ArrayList<RectifierService>(); |
|---|
| 65 | |
|---|
| 66 | public Map_Rectifier_WMSmenuAction() { |
|---|
| 67 | super(tr("Rectified Image..."), |
|---|
| 68 | "OLmarker", |
|---|
| 69 | tr("Download Rectified Images From Various Services"), |
|---|
| 70 | Shortcut.registerShortcut("imagery:rectimg", |
|---|
| 71 | tr("Imagery: {0}", tr("Rectified Image...")), |
|---|
| 72 | KeyEvent.CHAR_UNDEFINED, Shortcut.NONE), |
|---|
| 73 | true |
|---|
| 74 | ); |
|---|
| 75 | |
|---|
| 76 | // Add default services |
|---|
| 77 | services.add( |
|---|
| 78 | new RectifierService("Metacarta Map Rectifier", |
|---|
| 79 | "http://labs.metacarta.com/rectifier/", |
|---|
| 80 | "http://labs.metacarta.com/rectifier/wms.cgi?id=__s__&srs=EPSG:4326" |
|---|
| 81 | + "&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png&", |
|---|
| 82 | // This matches more than the "classic" WMS link, so users can pretty much |
|---|
| 83 | // copy any link as long as it includes the ID |
|---|
| 84 | "labs\\.metacarta\\.com/(?:.*?)(?:/|=)([0-9]+)(?:\\?|/|\\.|$)", |
|---|
| 85 | "^[0-9]+$") |
|---|
| 86 | ); |
|---|
| 87 | services.add( |
|---|
| 88 | // TODO: Change all links to mapwarper.net once the project has moved. |
|---|
| 89 | // The RegEx already matches the new URL and old URLs will be forwarded |
|---|
| 90 | // to make the transition as smooth as possible for the users |
|---|
| 91 | new RectifierService("Geothings Map Warper", |
|---|
| 92 | "http://warper.geothings.net/", |
|---|
| 93 | "http://warper.geothings.net/maps/wms/__s__?request=GetMap&version=1.1.1" |
|---|
| 94 | + "&styles=&format=image/png&srs=epsg:4326&exceptions=application/vnd.ogc.se_inimage&", |
|---|
| 95 | // This matches more than the "classic" WMS link, so users can pretty much |
|---|
| 96 | // copy any link as long as it includes the ID |
|---|
| 97 | "(?:mapwarper\\.net|warper\\.geothings\\.net)/(?:.*?)/([0-9]+)(?:\\?|/|\\.|$)", |
|---|
| 98 | "^[0-9]+$") |
|---|
| 99 | ); |
|---|
| 100 | |
|---|
| 101 | // This service serves the purpose of "just this once" without forcing the user |
|---|
| 102 | // to commit the link to the preferences |
|---|
| 103 | |
|---|
| 104 | // Clipboard content gets trimmed, so matching whitespace only ensures that this |
|---|
| 105 | // service will never be selected automatically. |
|---|
| 106 | services.add(new RectifierService(tr("Custom WMS Link"), "", "", "^\\s+$", "")); |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | @Override |
|---|
| 110 | public void actionPerformed(ActionEvent e) { |
|---|
| 111 | if (!isEnabled()) return; |
|---|
| 112 | JPanel panel = new JPanel(new GridBagLayout()); |
|---|
| 113 | panel.add(new JLabel(tr("Supported Rectifier Services:")), GBC.eol()); |
|---|
| 114 | |
|---|
| 115 | JTextField tfWmsUrl = new JTextField(30); |
|---|
| 116 | |
|---|
| 117 | String clip = Utils.getClipboardContent(); |
|---|
| 118 | clip = clip == null ? "" : clip.trim(); |
|---|
| 119 | ButtonGroup group = new ButtonGroup(); |
|---|
| 120 | |
|---|
| 121 | JRadioButton firstBtn = null; |
|---|
| 122 | for(RectifierService s : services) { |
|---|
| 123 | JRadioButton serviceBtn = new JRadioButton(s.name); |
|---|
| 124 | if(firstBtn == null) { |
|---|
| 125 | firstBtn = serviceBtn; |
|---|
| 126 | } |
|---|
| 127 | // Checks clipboard contents against current service if no match has been found yet. |
|---|
| 128 | // If the contents match, they will be inserted into the text field and the corresponding |
|---|
| 129 | // service will be pre-selected. |
|---|
| 130 | if(!clip.equals("") && tfWmsUrl.getText().equals("") |
|---|
| 131 | && (s.urlRegEx.matcher(clip).find() || s.idValidator.matcher(clip).matches())) { |
|---|
| 132 | serviceBtn.setSelected(true); |
|---|
| 133 | tfWmsUrl.setText(clip); |
|---|
| 134 | } |
|---|
| 135 | s.btn = serviceBtn; |
|---|
| 136 | group.add(serviceBtn); |
|---|
| 137 | if(!s.url.equals("")) { |
|---|
| 138 | panel.add(serviceBtn, GBC.std()); |
|---|
| 139 | panel.add(new UrlLabel(s.url, tr("Visit Homepage")), GBC.eol().anchor(GridBagConstraints.EAST), 2); |
|---|
| 140 | } else { |
|---|
| 141 | panel.add(serviceBtn, GBC.eol().anchor(GridBagConstraints.WEST)); |
|---|
| 142 | } |
|---|
| 143 | } |
|---|
| 144 | |
|---|
| 145 | // Fallback in case no match was found |
|---|
| 146 | if(tfWmsUrl.getText().equals("") && firstBtn != null) { |
|---|
| 147 | firstBtn.setSelected(true); |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | panel.add(new JLabel(tr("WMS URL or Image ID:")), GBC.eol()); |
|---|
| 151 | panel.add(tfWmsUrl, GBC.eol().fill(GridBagConstraints.HORIZONTAL)); |
|---|
| 152 | |
|---|
| 153 | ExtendedDialog diag = new ExtendedDialog(Main.parent, |
|---|
| 154 | tr("Add Rectified Image"), |
|---|
| 155 | |
|---|
| 156 | new String[] {tr("Add Rectified Image"), tr("Cancel")}); |
|---|
| 157 | diag.setContent(panel); |
|---|
| 158 | diag.setButtonIcons(new String[] {"OLmarker.png", "cancel.png"}); |
|---|
| 159 | |
|---|
| 160 | // This repeatedly shows the dialog in case there has been an error. |
|---|
| 161 | // The loop is break;-ed if the users cancels |
|---|
| 162 | outer: while(true) { |
|---|
| 163 | diag.showDialog(); |
|---|
| 164 | int answer = diag.getValue(); |
|---|
| 165 | // Break loop when the user cancels |
|---|
| 166 | if(answer != 1) { |
|---|
| 167 | break; |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | String text = tfWmsUrl.getText().trim(); |
|---|
| 171 | // Loop all services until we find the selected one |
|---|
| 172 | for(RectifierService s : services) { |
|---|
| 173 | if(!s.isSelected()) { |
|---|
| 174 | continue; |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | // We've reached the custom WMS URL service |
|---|
| 178 | // Just set the URL and hope everything works out |
|---|
| 179 | if(s.wmsUrl.equals("")) { |
|---|
| 180 | addWMSLayer(s.name + " (" + text + ")", text); |
|---|
| 181 | break outer; |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | // First try to match if the entered string as an URL |
|---|
| 185 | Matcher m = s.urlRegEx.matcher(text); |
|---|
| 186 | if(m.find()) { |
|---|
| 187 | String id = m.group(1); |
|---|
| 188 | String newURL = s.wmsUrl.replaceAll("__s__", id); |
|---|
| 189 | String title = s.name + " (" + id + ")"; |
|---|
| 190 | addWMSLayer(title, newURL); |
|---|
| 191 | break outer; |
|---|
| 192 | } |
|---|
| 193 | // If not, look if it's a valid ID for the selected service |
|---|
| 194 | if(s.idValidator.matcher(text).matches()) { |
|---|
| 195 | String newURL = s.wmsUrl.replaceAll("__s__", text); |
|---|
| 196 | String title = s.name + " (" + text + ")"; |
|---|
| 197 | addWMSLayer(title, newURL); |
|---|
| 198 | break outer; |
|---|
| 199 | } |
|---|
| 200 | |
|---|
| 201 | // We've found the selected service, but the entered string isn't suitable for |
|---|
| 202 | // it. So quit checking the other radio buttons |
|---|
| 203 | break; |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | // and display an error message. The while(true) ensures that the dialog pops up again |
|---|
| 207 | JOptionPane.showMessageDialog(Main.parent, |
|---|
| 208 | tr("Couldn''t match the entered link or id to the selected service. Please try again."), |
|---|
| 209 | tr("No valid WMS URL or id"), |
|---|
| 210 | JOptionPane.ERROR_MESSAGE); |
|---|
| 211 | diag.setVisible(true); |
|---|
| 212 | } |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | /** |
|---|
| 216 | * Adds a WMS Layer with given title and URL |
|---|
| 217 | * @param title: Name of the layer as it will shop up in the layer manager |
|---|
| 218 | * @param url: URL to the WMS server |
|---|
| 219 | */ |
|---|
| 220 | private void addWMSLayer(String title, String url) { |
|---|
| 221 | Main.main.addLayer(new WMSLayer(new ImageryInfo(title, url))); |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | @Override |
|---|
| 225 | protected void updateEnabledState() { |
|---|
| 226 | setEnabled(Main.map != null && Main.map.mapView != null && !Main.map.mapView.getAllLayers().isEmpty()); |
|---|
| 227 | } |
|---|
| 228 | } |
|---|