001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.imagery;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.IOException;
007import java.net.MalformedURLException;
008import java.net.URL;
009import java.util.List;
010
011import javax.swing.DefaultComboBoxModel;
012import javax.swing.JButton;
013import javax.swing.JCheckBox;
014import javax.swing.JComboBox;
015import javax.swing.JLabel;
016import javax.swing.JOptionPane;
017import javax.swing.JScrollPane;
018
019import org.openstreetmap.josm.Main;
020import org.openstreetmap.josm.data.imagery.ImageryInfo;
021import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
022import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
023import org.openstreetmap.josm.gui.util.GuiHelper;
024import org.openstreetmap.josm.gui.widgets.JosmTextArea;
025import org.openstreetmap.josm.io.imagery.WMSImagery;
026import org.openstreetmap.josm.tools.GBC;
027import org.openstreetmap.josm.tools.Utils;
028
029/**
030 * An imagery panel used to add WMS imagery sources.
031 * @since 2599
032 */
033public class AddWMSLayerPanel extends AddImageryPanel {
034
035    private final transient WMSImagery wms = new WMSImagery();
036    private final JCheckBox endpoint = new JCheckBox(tr("Store WMS endpoint only, select layers at usage"));
037    private final transient WMSLayerTree tree = new WMSLayerTree();
038    private final JComboBox<String> formats = new JComboBox<>();
039    private final JLabel wmsInstruction;
040    private final JosmTextArea wmsUrl = new JosmTextArea(3, 40).transferFocusOnTab();
041    private final JButton showBounds = new JButton(tr("Show bounds"));
042
043    /**
044     * Constructs a new {@code AddWMSLayerPanel}.
045     */
046    public AddWMSLayerPanel() {
047
048        add(new JLabel(tr("1. Enter service URL")), GBC.eol());
049        add(rawUrl, GBC.eol().fill());
050        rawUrl.setLineWrap(true);
051        JButton getLayers = new JButton(tr("Get layers"));
052        add(getLayers, GBC.eop().fill());
053
054        add(new JLabel(tr("2. Select layers")), GBC.eol());
055        add(endpoint, GBC.eol().fill());
056        add(new JScrollPane(tree.getLayerTree()), GBC.eol().fill().weight(1, 100));
057
058        showBounds.setEnabled(false);
059        add(showBounds, GBC.eop().fill());
060
061        add(new JLabel(tr("3. Select image format")), GBC.eol());
062        add(formats, GBC.eol().fill());
063
064        wmsInstruction = new JLabel(tr("4. Verify generated WMS URL"));
065        add(wmsInstruction, GBC.eol());
066        wmsInstruction.setLabelFor(wmsUrl);
067        add(wmsUrl, GBC.eop().fill());
068        wmsUrl.setLineWrap(true);
069
070        add(new JLabel(tr("5. Enter name for this layer")), GBC.eol());
071        add(name, GBC.eop().fill());
072
073        getLayers.addActionListener(e -> {
074            try {
075                wms.attemptGetCapabilities(rawUrl.getText());
076                tree.updateTree(wms);
077                List<String> wmsFormats = wms.getFormats();
078                formats.setModel(new DefaultComboBoxModel<>(wmsFormats.toArray(new String[wmsFormats.size()])));
079                formats.setSelectedItem(wms.getPreferredFormats());
080            } catch (MalformedURLException ex1) {
081                Main.error(ex1, false);
082                JOptionPane.showMessageDialog(getParent(), tr("Invalid service URL."),
083                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
084            } catch (IOException ex2) {
085                Main.error(ex2, false);
086                JOptionPane.showMessageDialog(getParent(), tr("Could not retrieve WMS layer list."),
087                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
088            } catch (WMSImagery.WMSGetCapabilitiesException ex3) {
089                String incomingData = ex3.getIncomingData().trim();
090                String title = tr("WMS Error");
091                String message = tr("Could not parse WMS layer list.");
092                Main.error(ex3, "Could not parse WMS layer list. Incoming data:\n"+incomingData);
093                if ((incomingData.startsWith("<html>") || incomingData.startsWith("<HTML>"))
094                  && (incomingData.endsWith("</html>") || incomingData.endsWith("</HTML>"))) {
095                    GuiHelper.notifyUserHtmlError(this, title, message, incomingData);
096                } else {
097                    if (ex3.getMessage() != null) {
098                        message += '\n' + ex3.getMessage();
099                    }
100                    JOptionPane.showMessageDialog(getParent(), message, title, JOptionPane.ERROR_MESSAGE);
101                }
102            }
103        });
104
105        endpoint.addItemListener(e -> {
106            tree.getLayerTree().setEnabled(!endpoint.isSelected());
107            showBounds.setEnabled(!endpoint.isSelected());
108            wmsInstruction.setEnabled(!endpoint.isSelected());
109            formats.setEnabled(!endpoint.isSelected());
110            wmsUrl.setEnabled(!endpoint.isSelected());
111            if (endpoint.isSelected()) {
112                URL url = wms.getServiceUrl();
113                if (url != null) {
114                    name.setText(url.getHost());
115                }
116            } else {
117                onLayerSelectionChanged();
118            }
119        });
120
121        tree.getLayerTree().addPropertyChangeListener("selectedLayers", evt -> onLayerSelectionChanged());
122
123        formats.addActionListener(e -> onLayerSelectionChanged());
124
125        showBounds.addActionListener(e -> {
126            if (tree.getSelectedLayers().get(0).bounds != null) {
127                SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
128                mapPanel.setBoundingBox(tree.getSelectedLayers().get(0).bounds);
129                JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
130            } else {
131                JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
132                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
133            }
134        });
135
136        registerValidableComponent(endpoint);
137        registerValidableComponent(rawUrl);
138        registerValidableComponent(wmsUrl);
139    }
140
141    protected final void onLayerSelectionChanged() {
142        if (wms.getServiceUrl() != null) {
143            wmsUrl.setText(wms.buildGetMapUrl(tree.getSelectedLayers(), (String) formats.getSelectedItem()));
144            name.setText(wms.getServiceUrl().getHost() + ": " + Utils.join(", ", tree.getSelectedLayers()));
145        }
146        showBounds.setEnabled(tree.getSelectedLayers().size() == 1);
147    }
148
149    @Override
150    public ImageryInfo getImageryInfo() {
151        final ImageryInfo info;
152        if (endpoint.isSelected()) {
153            info = new ImageryInfo(getImageryName(), getImageryRawUrl());
154            info.setImageryType(ImageryInfo.ImageryType.WMS_ENDPOINT);
155        } else {
156            info = wms.toImageryInfo(getImageryName(), tree.getSelectedLayers());
157            info.setUrl(getWmsUrl());
158            info.setImageryType(ImageryType.WMS);
159        }
160        return info;
161    }
162
163    protected final String getWmsUrl() {
164        return sanitize(wmsUrl.getText(), ImageryInfo.ImageryType.WMS);
165    }
166
167    @Override
168    protected boolean isImageryValid() {
169        if (getImageryName().isEmpty()) {
170            return false;
171        }
172        if (endpoint.isSelected()) {
173            return !getImageryRawUrl().isEmpty();
174        } else {
175            return !getWmsUrl().isEmpty();
176        }
177    }
178}