source: josm/trunk/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java@ 13891

Last change on this file since 13891 was 13879, checked in by wiktorn, 6 years ago

Add layer name/title to WMTS layers when user selects layer

see: #15988

  • Property svn:eol-style set to native
File size: 10.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Dimension;
8import java.awt.GraphicsEnvironment;
9import java.awt.GridBagLayout;
10import java.awt.event.ActionEvent;
11import java.io.IOException;
12import java.net.MalformedURLException;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
16import java.util.stream.Collectors;
17
18import javax.swing.JComboBox;
19import javax.swing.JOptionPane;
20import javax.swing.JPanel;
21import javax.swing.JScrollPane;
22
23import org.openstreetmap.josm.Main;
24import org.openstreetmap.josm.data.imagery.DefaultLayer;
25import org.openstreetmap.josm.data.imagery.ImageryInfo;
26import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
27import org.openstreetmap.josm.data.imagery.LayerDetails;
28import org.openstreetmap.josm.data.imagery.WMTSTileSource;
29import org.openstreetmap.josm.data.imagery.WMTSTileSource.WMTSGetCapabilitiesException;
30import org.openstreetmap.josm.gui.ExtendedDialog;
31import org.openstreetmap.josm.gui.layer.AlignImageryPanel;
32import org.openstreetmap.josm.gui.layer.ImageryLayer;
33import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
34import org.openstreetmap.josm.gui.preferences.imagery.WMSLayerTree;
35import org.openstreetmap.josm.gui.util.GuiHelper;
36import org.openstreetmap.josm.io.imagery.WMSImagery;
37import org.openstreetmap.josm.io.imagery.WMSImagery.WMSGetCapabilitiesException;
38import org.openstreetmap.josm.tools.CheckParameterUtil;
39import org.openstreetmap.josm.tools.GBC;
40import org.openstreetmap.josm.tools.ImageProvider;
41import org.openstreetmap.josm.tools.Logging;
42import org.openstreetmap.josm.tools.bugreport.ReportedException;
43
44/**
45 * Action displayed in imagery menu to add a new imagery layer.
46 * @since 3715
47 */
48public class AddImageryLayerAction extends JosmAction implements AdaptableAction {
49 private final transient ImageryInfo info;
50
51 static class SelectWmsLayersDialog extends ExtendedDialog {
52 SelectWmsLayersDialog(WMSLayerTree tree, JComboBox<String> formats) {
53 super(Main.parent, tr("Select WMS layers"), tr("Add layers"), tr("Cancel"));
54 final JScrollPane scrollPane = new JScrollPane(tree.getLayerTree());
55 scrollPane.setPreferredSize(new Dimension(400, 400));
56 final JPanel panel = new JPanel(new GridBagLayout());
57 panel.add(scrollPane, GBC.eol().fill());
58 panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
59 setContent(panel);
60 }
61 }
62
63 /**
64 * Constructs a new {@code AddImageryLayerAction} for the given {@code ImageryInfo}.
65 * If an http:// icon is specified, it is fetched asynchronously.
66 * @param info The imagery info
67 */
68 public AddImageryLayerAction(ImageryInfo info) {
69 super(info.getMenuName(), /* ICON */"imagery_menu", tr("Add imagery layer {0}", info.getName()), null,
70 true, ToolbarPreferences.IMAGERY_PREFIX + info.getToolbarName(), false);
71 putValue("help", ht("/Preferences/Imagery"));
72 setTooltip(info.getToolTipText().replaceAll("</?html>", ""));
73 this.info = info;
74 installAdapters();
75
76 // change toolbar icon from if specified
77 String icon = info.getIcon();
78 if (icon != null) {
79 new ImageProvider(icon).setOptional(true).getResourceAsync(result -> {
80 if (result != null) {
81 GuiHelper.runInEDT(() -> result.attachImageIcon(this));
82 }
83 });
84 }
85 }
86
87 /**
88 * Converts general ImageryInfo to specific one, that does not need any user action to initialize
89 * see: https://josm.openstreetmap.de/ticket/13868
90 * @param info ImageryInfo that will be converted (or returned when no conversion needed)
91 * @return ImageryInfo object that's ready to be used to create TileSource
92 */
93 private ImageryInfo convertImagery(ImageryInfo info) {
94 try {
95 switch(info.getImageryType()) {
96 case WMS_ENDPOINT:
97 // convert to WMS type
98 if (info.getDefaultLayers() == null || info.getDefaultLayers().isEmpty()) {
99 return getWMSLayerInfo(info);
100 } else {
101 return info;
102 }
103 case WMTS:
104 // specify which layer to use
105 if (info.getDefaultLayers() == null || info.getDefaultLayers().isEmpty()) {
106 WMTSTileSource tileSource = new WMTSTileSource(info);
107 DefaultLayer layerId = tileSource.userSelectLayer();
108 if (layerId != null) {
109 ImageryInfo copy = new ImageryInfo(info);
110 copy.setDefaultLayers(Collections.singletonList(layerId));
111 String layerName = tileSource.getLayers().stream()
112 .filter(x -> x.getIdentifier().equals(layerId.getLayerName()))
113 .map(x -> x.getUserTitle())
114 .findFirst()
115 .orElse("");
116 copy.setName(copy.getName() + ": " + layerName);
117 return copy;
118 }
119 return null;
120 } else {
121 return info;
122 }
123 default:
124 return info;
125 }
126 } catch (MalformedURLException ex) {
127 handleException(ex, tr("Invalid service URL."), tr("WMS Error"), null);
128 } catch (IOException ex) {
129 handleException(ex, tr("Could not retrieve WMS layer list."), tr("WMS Error"), null);
130 } catch (WMSGetCapabilitiesException ex) {
131 handleException(ex, tr("Could not parse WMS layer list."), tr("WMS Error"),
132 "Could not parse WMS layer list. Incoming data:\n" + ex.getIncomingData());
133 } catch (WMTSGetCapabilitiesException ex) {
134 handleException(ex, tr("Could not parse WMTS layer list."), tr("WMTS Error"),
135 "Could not parse WMTS layer list.");
136 }
137 return null;
138 }
139
140 @Override
141 public void actionPerformed(ActionEvent e) {
142 if (!isEnabled()) return;
143 ImageryLayer layer = null;
144 try {
145 final ImageryInfo infoToAdd = convertImagery(info);
146 if (infoToAdd != null) {
147 layer = ImageryLayer.create(infoToAdd);
148 getLayerManager().addLayer(layer);
149 AlignImageryPanel.addNagPanelIfNeeded(infoToAdd);
150 }
151 } catch (IllegalArgumentException | ReportedException ex) {
152 if (ex.getMessage() == null || ex.getMessage().isEmpty() || GraphicsEnvironment.isHeadless()) {
153 throw ex;
154 } else {
155 Logging.error(ex);
156 JOptionPane.showMessageDialog(Main.parent, ex.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
157 if (layer != null) {
158 getLayerManager().removeLayer(layer);
159 }
160 }
161 }
162 }
163
164 /**
165 * Asks user to choose a WMS layer from a WMS endpoint.
166 * @param info the WMS endpoint.
167 * @return chosen WMS layer, or null
168 * @throws IOException if any I/O error occurs while contacting the WMS endpoint
169 * @throws WMSGetCapabilitiesException if the WMS getCapabilities request fails
170 */
171 protected static ImageryInfo getWMSLayerInfo(ImageryInfo info) throws IOException, WMSGetCapabilitiesException {
172 try {
173 CheckParameterUtil.ensureThat(ImageryType.WMS_ENDPOINT.equals(info.getImageryType()), "wms_endpoint imagery type expected");
174 final WMSImagery wms = new WMSImagery(info.getUrl(), info.getCustomHttpHeaders());
175
176 final WMSLayerTree tree = new WMSLayerTree();
177 tree.updateTree(wms);
178
179 Collection<String> wmsFormats = wms.getFormats();
180 final JComboBox<String> formats = new JComboBox<>(wmsFormats.toArray(new String[0]));
181 formats.setSelectedItem(wms.getPreferredFormat());
182 formats.setToolTipText(tr("Select image format for WMS layer"));
183
184 if (!GraphicsEnvironment.isHeadless()) {
185 ExtendedDialog dialog = new ExtendedDialog(Main.parent, tr("Select WMS layers"), tr("Add layers"), tr("Cancel"));
186 final JScrollPane scrollPane = new JScrollPane(tree.getLayerTree());
187 scrollPane.setPreferredSize(new Dimension(400, 400));
188 final JPanel panel = new JPanel(new GridBagLayout());
189 panel.add(scrollPane, GBC.eol().fill());
190 panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
191 dialog.setContent(panel);
192
193 if (dialog.showDialog().getValue() != 1) {
194 return null;
195 }
196 }
197
198 final String url = wms.buildGetMapUrl(
199 tree.getSelectedLayers().stream().map(LayerDetails::getName).collect(Collectors.toList()),
200 (List<String>) null,
201 (String) formats.getSelectedItem(),
202 true // TODO: ask the user if transparent layer is wanted
203 );
204
205 String selectedLayers = tree.getSelectedLayers().stream()
206 .map(LayerDetails::getName)
207 .collect(Collectors.joining(", "));
208 // Use full copy of original Imagery info to copy all attributes. Only overwrite what's different
209 ImageryInfo ret = new ImageryInfo(info);
210 ret.setUrl(url);
211 ret.setImageryType(ImageryType.WMS);
212 ret.setName(info.getName() + selectedLayers);
213 ret.setServerProjections(wms.getServerProjections(tree.getSelectedLayers()));
214 return ret;
215 } catch (MalformedURLException ex) {
216 handleException(ex, tr("Invalid service URL."), tr("WMS Error"), null);
217 } catch (IOException ex) {
218 handleException(ex, tr("Could not retrieve WMS layer list."), tr("WMS Error"), null);
219 } catch (WMSGetCapabilitiesException ex) {
220 handleException(ex, tr("Could not parse WMS layer list."), tr("WMS Error"),
221 "Could not parse WMS layer list. Incoming data:\n" + ex.getIncomingData());
222 }
223 return null;
224 }
225
226 private static void handleException(Exception ex, String uiMessage, String uiTitle, String logMessage) {
227 if (!GraphicsEnvironment.isHeadless()) {
228 JOptionPane.showMessageDialog(Main.parent, uiMessage, uiTitle, JOptionPane.ERROR_MESSAGE);
229 }
230 Logging.log(Logging.LEVEL_ERROR, logMessage, ex);
231 }
232
233 @Override
234 protected void updateEnabledState() {
235 if (info.isBlacklisted()) {
236 setEnabled(false);
237 } else {
238 setEnabled(true);
239 }
240 }
241
242 @Override
243 public String toString() {
244 return "AddImageryLayerAction [info=" + info + ']';
245 }
246}
Note: See TracBrowser for help on using the repository browser.