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        try {
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.getImageIcon(AddImageryLayerAction.this);
067                                        }
068                                    });
069                                }
070                            }
071                        });
072            }
073        } catch (Exception ex) {
074            throw new RuntimeException(ex.getMessage(), ex);
075        }
076    }
077
078    @Override
079    public void actionPerformed(ActionEvent e) {
080        if (!isEnabled()) return;
081        try {
082            final ImageryInfo infoToAdd = ImageryType.WMS_ENDPOINT.equals(info.getImageryType())
083                    ? getWMSLayerInfo() : info;
084            if (infoToAdd != null) {
085                Main.main.addLayer(ImageryLayer.create(infoToAdd));
086                AlignImageryPanel.addNagPanelIfNeeded(infoToAdd);
087            }
088        } catch (IllegalArgumentException ex) {
089            if (ex.getMessage() == null || ex.getMessage().isEmpty()) {
090                throw ex;
091            } else {
092                JOptionPane.showMessageDialog(Main.parent,
093                        ex.getMessage(), tr("Error"),
094                        JOptionPane.ERROR_MESSAGE);
095            }
096        }
097    }
098
099    protected ImageryInfo getWMSLayerInfo() {
100        try {
101            assert ImageryType.WMS_ENDPOINT.equals(info.getImageryType());
102            final WMSImagery wms = new WMSImagery();
103            wms.attemptGetCapabilities(info.getUrl());
104
105            final WMSLayerTree tree = new WMSLayerTree();
106            tree.updateTree(wms);
107            final JComboBox<String> formats = new JComboBox<>(wms.getFormats().toArray(new String[0]));
108            formats.setSelectedItem(wms.getPreferredFormats());
109            formats.setToolTipText(tr("Select image format for WMS layer"));
110
111            if (!GraphicsEnvironment.isHeadless()) {
112                if (1 != new ExtendedDialog(Main.parent, tr("Select WMS layers"), new String[]{tr("Add layers"), tr("Cancel")}) { {
113                    final JScrollPane scrollPane = new JScrollPane(tree.getLayerTree());
114                    scrollPane.setPreferredSize(new Dimension(400, 400));
115                    final JPanel panel = new JPanel(new GridBagLayout());
116                    panel.add(scrollPane, GBC.eol().fill());
117                    panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
118                    setContent(panel);
119                } }.showDialog().getValue()) {
120                    return null;
121                }
122            }
123
124            final String url = wms.buildGetMapUrl(
125                    tree.getSelectedLayers(), (String) formats.getSelectedItem());
126            Set<String> supportedCrs = new HashSet<>();
127            boolean first = true;
128            StringBuilder layersString = new StringBuilder();
129            for (LayerDetails layer: tree.getSelectedLayers()) {
130                if (first) {
131                    supportedCrs.addAll(layer.getProjections());
132                    first = false;
133                }
134                layersString.append(layer.name);
135                layersString.append(", ");
136                supportedCrs.retainAll(layer.getProjections());
137            }
138
139            ImageryInfo ret = new ImageryInfo(info.getName(), url, "wms", info.getEulaAcceptanceRequired(), info.getCookies());
140            if (layersString.length() > 2) {
141                ret.setName(ret.getName() + ' ' + layersString.substring(0, layersString.length() - 2));
142            }
143            ret.setServerProjections(supportedCrs);
144            return ret;
145        } catch (MalformedURLException ex) {
146            if (!GraphicsEnvironment.isHeadless()) {
147                JOptionPane.showMessageDialog(Main.parent, tr("Invalid service URL."),
148                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
149            }
150            Main.error(ex, false);
151        } catch (IOException ex) {
152            if (!GraphicsEnvironment.isHeadless()) {
153                JOptionPane.showMessageDialog(Main.parent, tr("Could not retrieve WMS layer list."),
154                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
155            }
156            Main.error(ex, false);
157        } catch (WMSImagery.WMSGetCapabilitiesException ex) {
158            if (!GraphicsEnvironment.isHeadless()) {
159                JOptionPane.showMessageDialog(Main.parent, tr("Could not parse WMS layer list."),
160                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
161            }
162            Main.error("Could not parse WMS layer list. Incoming data:\n"+ex.getIncomingData());
163        }
164        return null;
165    }
166
167    @Override
168    protected void updateEnabledState() {
169        ImageryType type = info.getImageryType();
170        // never enable blacklisted entries. Do not add same imagery layer twice (fix #2519)
171        if (info.isBlacklisted()) {
172            setEnabled(false);
173        } else if (type == ImageryType.TMS || type == ImageryType.BING || type == ImageryType.SCANEX) {
174            setEnabled(true);
175        } else if (Main.isDisplayingMapView() && !Main.map.mapView.getAllLayers().isEmpty()) {
176            setEnabled(true);
177        } else {
178            setEnabled(false);
179        }
180    }
181}