001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.Dimension;
008import java.awt.GraphicsEnvironment;
009import java.awt.GridBagLayout;
010import java.awt.event.ActionEvent;
011import java.io.IOException;
012import java.net.MalformedURLException;
013import java.util.HashSet;
014import java.util.Set;
015
016import javax.swing.JComboBox;
017import javax.swing.JOptionPane;
018import javax.swing.JPanel;
019import javax.swing.JScrollPane;
020
021import org.openstreetmap.josm.Main;
022import org.openstreetmap.josm.data.imagery.ImageryInfo;
023import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
024import org.openstreetmap.josm.gui.ExtendedDialog;
025import org.openstreetmap.josm.gui.layer.AlignImageryPanel;
026import org.openstreetmap.josm.gui.layer.ImageryLayer;
027import org.openstreetmap.josm.gui.preferences.imagery.WMSLayerTree;
028import org.openstreetmap.josm.gui.util.GuiHelper;
029import org.openstreetmap.josm.io.imagery.WMSImagery;
030import org.openstreetmap.josm.io.imagery.WMSImagery.LayerDetails;
031import org.openstreetmap.josm.io.imagery.WMSImagery.WMSGetCapabilitiesException;
032import org.openstreetmap.josm.tools.GBC;
033import org.openstreetmap.josm.tools.ImageProvider;
034import org.openstreetmap.josm.tools.ImageProvider.ImageResourceCallback;
035import org.openstreetmap.josm.tools.ImageResource;
036
037/**
038 * Action displayed in imagery menu to add a new imagery layer.
039 * @since 3715
040 */
041public class AddImageryLayerAction extends JosmAction implements AdaptableAction {
042    private final transient ImageryInfo info;
043
044    /**
045     * Constructs a new {@code AddImageryLayerAction} for the given {@code ImageryInfo}.
046     * If an http:// icon is specified, it is fetched asynchronously.
047     * @param info The imagery info
048     */
049    public AddImageryLayerAction(ImageryInfo info) {
050        super(info.getMenuName(), /* ICON */"imagery_menu", tr("Add imagery layer {0}", info.getName()), null, false, false);
051        putValue("toolbar", "imagery_" + info.getToolbarName());
052        putValue("help", ht("/Preferences/Imagery"));
053        this.info = info;
054        installAdapters();
055
056        // change toolbar icon from if specified
057        String icon = info.getIcon();
058        if (icon != null) {
059            new ImageProvider(icon).setOptional(true).getInBackground(new ImageResourceCallback() {
060                        @Override
061                        public void finished(final ImageResource result) {
062                            if (result != null) {
063                                GuiHelper.runInEDT(new Runnable() {
064                                    @Override
065                                    public void run() {
066                                        result.attachImageIcon(AddImageryLayerAction.this);
067                                    }
068                                });
069                            }
070                        }
071                    });
072        }
073    }
074
075    @Override
076    public void actionPerformed(ActionEvent e) {
077        if (!isEnabled()) return;
078        try {
079            final ImageryInfo infoToAdd = ImageryType.WMS_ENDPOINT.equals(info.getImageryType())
080                    ? getWMSLayerInfo() : info;
081            if (infoToAdd != null) {
082                Main.getLayerManager().addLayer(ImageryLayer.create(infoToAdd));
083                AlignImageryPanel.addNagPanelIfNeeded(infoToAdd);
084            }
085        } catch (IllegalArgumentException ex) {
086            if (ex.getMessage() == null || ex.getMessage().isEmpty()) {
087                throw ex;
088            } else {
089                JOptionPane.showMessageDialog(Main.parent,
090                        ex.getMessage(), tr("Error"),
091                        JOptionPane.ERROR_MESSAGE);
092            }
093        }
094    }
095
096    protected ImageryInfo getWMSLayerInfo() {
097        try {
098            assert ImageryType.WMS_ENDPOINT.equals(info.getImageryType());
099            final WMSImagery wms = new WMSImagery();
100            wms.attemptGetCapabilities(info.getUrl());
101
102            final WMSLayerTree tree = new WMSLayerTree();
103            tree.updateTree(wms);
104            final JComboBox<String> formats = new JComboBox<>(wms.getFormats().toArray(new String[0]));
105            formats.setSelectedItem(wms.getPreferredFormats());
106            formats.setToolTipText(tr("Select image format for WMS layer"));
107
108            if (!GraphicsEnvironment.isHeadless()) {
109                if (1 != new ExtendedDialog(Main.parent, tr("Select WMS layers"), new String[]{tr("Add layers"), tr("Cancel")}) { {
110                    final JScrollPane scrollPane = new JScrollPane(tree.getLayerTree());
111                    scrollPane.setPreferredSize(new Dimension(400, 400));
112                    final JPanel panel = new JPanel(new GridBagLayout());
113                    panel.add(scrollPane, GBC.eol().fill());
114                    panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
115                    setContent(panel);
116                } }.showDialog().getValue()) {
117                    return null;
118                }
119            }
120
121            final String url = wms.buildGetMapUrl(
122                    tree.getSelectedLayers(), (String) formats.getSelectedItem());
123            Set<String> supportedCrs = new HashSet<>();
124            boolean first = true;
125            StringBuilder layersString = new StringBuilder();
126            for (LayerDetails layer: tree.getSelectedLayers()) {
127                if (first) {
128                    supportedCrs.addAll(layer.getProjections());
129                    first = false;
130                }
131                layersString.append(layer.name);
132                layersString.append(", ");
133                supportedCrs.retainAll(layer.getProjections());
134            }
135
136            ImageryInfo ret = new ImageryInfo(info.getName(), url, "wms", info.getEulaAcceptanceRequired(), info.getCookies());
137            if (layersString.length() > 2) {
138                ret.setName(ret.getName() + ' ' + layersString.substring(0, layersString.length() - 2));
139            }
140            ret.setServerProjections(supportedCrs);
141            return ret;
142        } catch (MalformedURLException ex) {
143            if (!GraphicsEnvironment.isHeadless()) {
144                JOptionPane.showMessageDialog(Main.parent, tr("Invalid service URL."),
145                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
146            }
147            Main.error(ex, false);
148        } catch (IOException ex) {
149            if (!GraphicsEnvironment.isHeadless()) {
150                JOptionPane.showMessageDialog(Main.parent, tr("Could not retrieve WMS layer list."),
151                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
152            }
153            Main.error(ex, false);
154        } catch (WMSGetCapabilitiesException ex) {
155            if (!GraphicsEnvironment.isHeadless()) {
156                JOptionPane.showMessageDialog(Main.parent, tr("Could not parse WMS layer list."),
157                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
158            }
159            Main.error(ex, "Could not parse WMS layer list. Incoming data:\n"+ex.getIncomingData());
160        }
161        return null;
162    }
163
164    @Override
165    protected void updateEnabledState() {
166        ImageryType type = info.getImageryType();
167        // never enable blacklisted entries. Do not add same imagery layer twice (fix #2519)
168        if (info.isBlacklisted()) {
169            setEnabled(false);
170        } else if (type == ImageryType.TMS || type == ImageryType.BING || type == ImageryType.SCANEX) {
171            setEnabled(true);
172        } else {
173            setEnabled(!Main.getLayerManager().getLayers().isEmpty());
174        }
175    }
176}