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