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