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