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.GridBagLayout;
009import java.awt.event.ActionEvent;
010import java.io.IOException;
011import java.net.MalformedURLException;
012import java.util.HashSet;
013import java.util.Set;
014
015import javax.swing.JComboBox;
016import javax.swing.JOptionPane;
017import javax.swing.JPanel;
018import javax.swing.JScrollPane;
019
020import org.openstreetmap.josm.Main;
021import org.openstreetmap.josm.data.imagery.ImageryInfo;
022import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
023import org.openstreetmap.josm.gui.ExtendedDialog;
024import org.openstreetmap.josm.gui.layer.AlignImageryPanel;
025import org.openstreetmap.josm.gui.layer.ImageryLayer;
026import org.openstreetmap.josm.gui.preferences.imagery.WMSLayerTree;
027import org.openstreetmap.josm.gui.util.GuiHelper;
028import org.openstreetmap.josm.io.imagery.WMSImagery;
029import org.openstreetmap.josm.io.imagery.WMSImagery.LayerDetails;
030import org.openstreetmap.josm.tools.GBC;
031import org.openstreetmap.josm.tools.ImageProvider;
032import org.openstreetmap.josm.tools.ImageProvider.ImageResourceCallback;
033import org.openstreetmap.josm.tools.ImageResource;
034
035/**
036 * Action displayed in imagery menu to add a new imagery layer.
037 * @since 3715
038 */
039public class AddImageryLayerAction extends JosmAction implements AdaptableAction {
040    private final transient ImageryInfo info;
041
042    /**
043     * Constructs a new {@code AddImageryLayerAction} for the given {@code ImageryInfo}.
044     * If an http:// icon is specified, it is fetched asynchronously.
045     * @param info The imagery info
046     */
047    public AddImageryLayerAction(ImageryInfo info) {
048        super(info.getMenuName(), /* ICON */"imagery_menu", tr("Add imagery layer {0}", info.getName()), null, false, false);
049        putValue("toolbar", "imagery_" + info.getToolbarName());
050        putValue("help", ht("/Preferences/Imagery"));
051        this.info = info;
052        installAdapters();
053
054        // change toolbar icon from if specified
055        try {
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        } catch (Exception ex) {
073            throw new RuntimeException(ex.getMessage(), ex);
074        }
075    }
076
077    @Override
078    public void actionPerformed(ActionEvent e) {
079        if (!isEnabled()) return;
080        try {
081            final ImageryInfo infoToAdd = ImageryType.WMS_ENDPOINT.equals(info.getImageryType())
082                    ? getWMSLayerInfo() : info;
083            if (infoToAdd != null) {
084                Main.main.addLayer(ImageryLayer.create(infoToAdd));
085                AlignImageryPanel.addNagPanelIfNeeded(infoToAdd);
086            }
087        } catch (IllegalArgumentException ex) {
088            if (ex.getMessage() == null || ex.getMessage().isEmpty()) {
089                throw ex;
090            } else {
091                JOptionPane.showMessageDialog(Main.parent,
092                        ex.getMessage(), tr("Error"),
093                        JOptionPane.ERROR_MESSAGE);
094            }
095        }
096    }
097
098    protected ImageryInfo getWMSLayerInfo() {
099        try {
100            assert ImageryType.WMS_ENDPOINT.equals(info.getImageryType());
101            final WMSImagery wms = new WMSImagery();
102            wms.attemptGetCapabilities(info.getUrl());
103
104            final WMSLayerTree tree = new WMSLayerTree();
105            tree.updateTree(wms);
106            final JComboBox<String> formats = new JComboBox<>(wms.getFormats().toArray(new String[0]));
107            formats.setSelectedItem(wms.getPreferredFormats());
108            formats.setToolTipText(tr("Select image format for WMS layer"));
109
110            if (1 != new ExtendedDialog(Main.parent, tr("Select WMS layers"), new String[]{tr("Add layers"), tr("Cancel")}) { {
111                final JScrollPane scrollPane = new JScrollPane(tree.getLayerTree());
112                scrollPane.setPreferredSize(new Dimension(400, 400));
113                final JPanel panel = new JPanel(new GridBagLayout());
114                panel.add(scrollPane, GBC.eol().fill());
115                panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
116                setContent(panel);
117            } }.showDialog().getValue()) {
118                return null;
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            JOptionPane.showMessageDialog(Main.parent, tr("Invalid service URL."),
144                    tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
145        } catch (IOException ex) {
146            JOptionPane.showMessageDialog(Main.parent, tr("Could not retrieve WMS layer list."),
147                    tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
148        } catch (WMSImagery.WMSGetCapabilitiesException ex) {
149            JOptionPane.showMessageDialog(Main.parent, tr("Could not parse WMS layer list."),
150                    tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
151            Main.error("Could not parse WMS layer list. Incoming data:\n"+ex.getIncomingData());
152        }
153        return null;
154    }
155
156    @Override
157    protected void updateEnabledState() {
158        ImageryType type = info.getImageryType();
159        // never enable blacklisted entries. Do not add same imagery layer twice (fix #2519)
160        if (info.isBlacklisted()) {
161            setEnabled(false);
162        } else if (type == ImageryType.TMS || type == ImageryType.BING || type == ImageryType.SCANEX) {
163            setEnabled(true);
164        } else if (Main.isDisplayingMapView() && !Main.map.mapView.getAllLayers().isEmpty()) {
165            setEnabled(true);
166        } else {
167            setEnabled(false);
168        }
169    }
170}