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