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