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

Last change on this file since 14932 was 14866, checked in by GerdP, 5 years ago

fix some sonar issues

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