// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions;
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.imagery.DefaultLayer;
import org.openstreetmap.josm.data.imagery.ImageryInfo;
import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
import org.openstreetmap.josm.data.imagery.WMTSTileSource;
import org.openstreetmap.josm.gui.ExtendedDialog;
import org.openstreetmap.josm.gui.layer.AlignImageryPanel;
import org.openstreetmap.josm.gui.layer.ImageryLayer;
import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
import org.openstreetmap.josm.gui.preferences.imagery.WMSLayerTree;
import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.io.imagery.WMSImagery;
import org.openstreetmap.josm.io.imagery.WMSImagery.LayerDetails;
import org.openstreetmap.josm.io.imagery.WMSImagery.WMSGetCapabilitiesException;
import org.openstreetmap.josm.tools.CheckParameterUtil;
import org.openstreetmap.josm.tools.GBC;
import org.openstreetmap.josm.tools.ImageProvider;
/**
* Action displayed in imagery menu to add a new imagery layer.
* @since 3715
*/
public class AddImageryLayerAction extends JosmAction implements AdaptableAction {
private final transient ImageryInfo info;
static class SelectWmsLayersDialog extends ExtendedDialog {
SelectWmsLayersDialog(WMSLayerTree tree, JComboBox<String> formats) {
super(Main.parent, tr("Select WMS layers"), new String[]{tr("Add layers"), tr("Cancel")});
final JScrollPane scrollPane = new JScrollPane(tree.getLayerTree());
scrollPane.setPreferredSize(new Dimension(400, 400));
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(scrollPane, GBC.eol().fill());
panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
setContent(panel);
}
}
/**
* Constructs a new {@code AddImageryLayerAction} for the given {@code ImageryInfo}.
* If an http:// icon is specified, it is fetched asynchronously.
* @param info The imagery info
*/
public AddImageryLayerAction(ImageryInfo info) {
super(info.getMenuName(), /* ICON */"imagery_menu", tr("Add imagery layer {0}", info.getName()), null,
true, ToolbarPreferences.IMAGERY_PREFIX + info.getToolbarName(), false);
putValue("help", ht("/Preferences/Imagery"));
setTooltip(info.getToolTipText().replaceAll("</?html>", ""));
this.info = info;
installAdapters();
// change toolbar icon from if specified
String icon = info.getIcon();
if (icon != null) {
new ImageProvider(icon).setOptional(true).getResourceAsync().thenAccept(result -> {
if (result != null) {
GuiHelper.runInEDT(() -> result.attachImageIcon(this));
}
});
}
}
/**
* Converts general ImageryInfo to specific one, that does not need any user action to initialize
* see: https://josm.openstreetmap.de/ticket/13868
* @param info ImageryInfo that will be converted (or returned when no conversion needed)
* @return ImageryInfo object that's ready to be used to create TileSource
*/
private ImageryInfo convertImagery(ImageryInfo info) {
try {
switch(info.getImageryType()) {
case WMS_ENDPOINT:
// convert to WMS type
return getWMSLayerInfo();
case WMTS:
// specify which layer to use
DefaultLayer layerId = new WMTSTileSource(info).userSelectLayer();
if (layerId != null) {
ImageryInfo copy = new ImageryInfo(info);
Collection<DefaultLayer> defaultLayers = new ArrayList<>(1);
defaultLayers.add(layerId);
copy.setDefaultLayers(defaultLayers);
return copy;
}
// layer not selected - refuse to add
return null;
default:
return info;
}
} catch (MalformedURLException ex) {
if (!GraphicsEnvironment.isHeadless()) {
JOptionPane.showMessageDialog(Main.parent, tr("Invalid service URL."),
tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
}
Main.error(ex, false);
} catch (IOException ex) {
if (!GraphicsEnvironment.isHeadless()) {
JOptionPane.showMessageDialog(Main.parent, tr("Could not retrieve WMS layer list."),
tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
}
Main.error(ex, false);
} catch (WMSGetCapabilitiesException ex) {
if (!GraphicsEnvironment.isHeadless()) {
JOptionPane.showMessageDialog(Main.parent, tr("Could not parse WMS layer list."),
tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
}
Main.error(ex, "Could not parse WMS layer list. Incoming data:\n"+ex.getIncomingData());
}
return null;
}
@Override
public void actionPerformed(ActionEvent e) {
if (!isEnabled()) return;
try {
final ImageryInfo infoToAdd = convertImagery(info);
if (infoToAdd != null) {
Main.getLayerManager().addLayer(ImageryLayer.create(infoToAdd));
AlignImageryPanel.addNagPanelIfNeeded(infoToAdd);
}
} catch (IllegalArgumentException ex) {
if (ex.getMessage() == null || ex.getMessage().isEmpty() || GraphicsEnvironment.isHeadless()) {
throw ex;
} else {
JOptionPane.showMessageDialog(Main.parent,
ex.getMessage(), tr("Error"),
JOptionPane.ERROR_MESSAGE);
}
}
}
protected ImageryInfo getWMSLayerInfo() throws IOException, WMSGetCapabilitiesException {
CheckParameterUtil.ensureThat(ImageryType.WMS_ENDPOINT.equals(info.getImageryType()), "wms_endpoint imagery type expected");
final WMSImagery wms = new WMSImagery();
wms.attemptGetCapabilities(info.getUrl());
final WMSLayerTree tree = new WMSLayerTree();
tree.updateTree(wms);
List<String> wmsFormats = wms.getFormats();
final JComboBox<String> formats = new JComboBox<>(wmsFormats.toArray(new String[wmsFormats.size()]));
formats.setSelectedItem(wms.getPreferredFormats());
formats.setToolTipText(tr("Select image format for WMS layer"));
if (!GraphicsEnvironment.isHeadless() && 1 != new SelectWmsLayersDialog(tree, formats).showDialog().getValue()) {
return null;
}
final String url = wms.buildGetMapUrl(
tree.getSelectedLayers(), (String) formats.getSelectedItem());
Set<String> supportedCrs = new HashSet<>();
boolean first = true;
StringBuilder layersString = new StringBuilder();
for (LayerDetails layer: tree.getSelectedLayers()) {
if (first) {
supportedCrs.addAll(layer.getProjections());
first = false;
}
layersString.append(layer.name);
layersString.append(", ");
supportedCrs.retainAll(layer.getProjections());
}
// copy all information from WMS
ImageryInfo ret = new ImageryInfo(info);
// and update according to user choice
ret.setUrl(url);
ret.setImageryType(ImageryType.WMS);
if (layersString.length() > 2) {
ret.setName(ret.getName() + ' ' + layersString.substring(0, layersString.length() - 2));
}
ret.setServerProjections(supportedCrs);
return ret;
}
@Override
protected void updateEnabledState() {
if (info.isBlacklisted()) {
setEnabled(false);
} else {
setEnabled(true);
}
}
@Override
public String toString() {
return "AddImageryLayerAction [info=" + info + ']';
}
}