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.data.imagery.ImageryInfo;
020import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
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.Logging;
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("{0} Make sure OSM has the permission to use this service", "1.")), GBC.eol());
049        add(new JLabel(tr("{0} Enter GetCapabilities URL", "2.")), GBC.eol());
050        add(rawUrl, GBC.eol().fill());
051        rawUrl.setLineWrap(true);
052        JButton getLayers = new JButton(tr("{0} Get layers", "3."));
053        add(getLayers, GBC.eop().fill());
054
055        add(new JLabel(tr("{0} Select layers", "4.")), GBC.eol());
056        add(endpoint, GBC.eol().fill());
057        add(new JScrollPane(tree.getLayerTree()), GBC.eol().fill().weight(1, 100));
058
059        showBounds.setEnabled(false);
060        add(showBounds, GBC.eop().fill());
061
062        add(new JLabel(tr("{0} Select image format", "5.")), GBC.eol());
063        add(formats, GBC.eol().fill());
064
065        wmsInstruction = new JLabel(tr("{0} Edit generated {1} URL (optional)", "6.", "WMS"));
066        add(wmsInstruction, GBC.eol());
067        wmsInstruction.setLabelFor(wmsUrl);
068        add(wmsUrl, GBC.eop().fill());
069        wmsUrl.setLineWrap(true);
070
071        add(new JLabel(tr("{0} Enter name for this layer", "7.")), GBC.eol());
072        add(name, GBC.eop().fill());
073
074        getLayers.addActionListener(e -> {
075            try {
076                wms.attemptGetCapabilities(rawUrl.getText());
077                tree.updateTree(wms);
078                List<String> wmsFormats = wms.getFormats();
079                formats.setModel(new DefaultComboBoxModel<>(wmsFormats.toArray(new String[0])));
080                formats.setSelectedItem(wms.getPreferredFormats());
081            } catch (MalformedURLException ex1) {
082                Logging.log(Logging.LEVEL_ERROR, ex1);
083                JOptionPane.showMessageDialog(getParent(), tr("Invalid service URL."),
084                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
085            } catch (IOException ex2) {
086                Logging.log(Logging.LEVEL_ERROR, ex2);
087                JOptionPane.showMessageDialog(getParent(), tr("Could not retrieve WMS layer list."),
088                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
089            } catch (WMSImagery.WMSGetCapabilitiesException ex3) {
090                String incomingData = ex3.getIncomingData() != null ? ex3.getIncomingData().trim() : "";
091                String title = tr("WMS Error");
092                StringBuilder message = new StringBuilder(tr("Could not parse WMS layer list."));
093                Logging.log(Logging.LEVEL_ERROR, "Could not parse WMS layer list. Incoming data:\n"+incomingData, ex3);
094                if ((incomingData.startsWith("<html>") || incomingData.startsWith("<HTML>"))
095                  && (incomingData.endsWith("</html>") || incomingData.endsWith("</HTML>"))) {
096                    GuiHelper.notifyUserHtmlError(this, title, message.toString(), incomingData);
097                } else {
098                    if (ex3.getMessage() != null) {
099                        message.append('\n').append(ex3.getMessage());
100                    }
101                    JOptionPane.showMessageDialog(getParent(), message.toString(), title, JOptionPane.ERROR_MESSAGE);
102                }
103            }
104        });
105
106        endpoint.addItemListener(e -> {
107            tree.getLayerTree().setEnabled(!endpoint.isSelected());
108            showBounds.setEnabled(!endpoint.isSelected());
109            wmsInstruction.setEnabled(!endpoint.isSelected());
110            formats.setEnabled(!endpoint.isSelected());
111            wmsUrl.setEnabled(!endpoint.isSelected());
112            if (endpoint.isSelected()) {
113                URL url = wms.getServiceUrl();
114                if (url != null) {
115                    name.setText(url.getHost());
116                }
117            } else {
118                onLayerSelectionChanged();
119            }
120        });
121
122        tree.getLayerTree().addPropertyChangeListener("selectedLayers", evt -> onLayerSelectionChanged());
123
124        formats.addActionListener(e -> onLayerSelectionChanged());
125
126        showBounds.addActionListener(e -> {
127            if (tree.getSelectedLayers().get(0).bounds != null) {
128                SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
129                mapPanel.setBoundingBox(tree.getSelectedLayers().get(0).bounds);
130                JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
131            } else {
132                JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
133                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
134            }
135        });
136
137        registerValidableComponent(endpoint);
138        registerValidableComponent(rawUrl);
139        registerValidableComponent(wmsUrl);
140    }
141
142    protected final void onLayerSelectionChanged() {
143        if (wms.getServiceUrl() != null) {
144            wmsUrl.setText(wms.buildGetMapUrl(tree.getSelectedLayers(), (String) formats.getSelectedItem()));
145            name.setText(wms.getServiceUrl().getHost() + ": " + Utils.join(", ", tree.getSelectedLayers()));
146        }
147        showBounds.setEnabled(tree.getSelectedLayers().size() == 1);
148    }
149
150    @Override
151    public ImageryInfo getImageryInfo() {
152        final ImageryInfo info;
153        if (endpoint.isSelected()) {
154            info = new ImageryInfo(getImageryName(), getImageryRawUrl());
155            info.setImageryType(ImageryInfo.ImageryType.WMS_ENDPOINT);
156        } else {
157            info = wms.toImageryInfo(getImageryName(), tree.getSelectedLayers());
158            info.setUrl(getWmsUrl());
159            info.setImageryType(ImageryType.WMS);
160        }
161        return info;
162    }
163
164    protected final String getWmsUrl() {
165        return sanitize(wmsUrl.getText(), ImageryInfo.ImageryType.WMS);
166    }
167
168    @Override
169    protected boolean isImageryValid() {
170        if (getImageryName().isEmpty()) {
171            return false;
172        }
173        if (endpoint.isSelected()) {
174            return !getImageryRawUrl().isEmpty();
175        } else {
176            return !getWmsUrl().isEmpty();
177        }
178    }
179}