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

Last change on this file since 13853 was 13839, checked in by wiktorn, 6 years ago

Sonar fixes and javadoc

  • AddImageryLayerAction - do not use {{ }} initialization as it may be source of leaks
  • AbstractTileSourceLayer - do not expose partially initialized object, first set all the values, then set the class field
  • WMTSCapabilities - add javadocs
  • Property svn:eol-style set to native
File size: 10.4 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.ArrayList;
14import java.util.Collection;
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 DefaultLayer layerId = new WMTSTileSource(info).userSelectLayer();
107 if (layerId != null) {
108 ImageryInfo copy = new ImageryInfo(info);
109 List<DefaultLayer> defaultLayers = new ArrayList<>(1);
110 defaultLayers.add(layerId);
111 copy.setDefaultLayers(defaultLayers);
112 return copy;
113 }
114 return null;
115 } else {
116 return info;
117 }
118 default:
119 return info;
120 }
121 } catch (MalformedURLException ex) {
122 handleException(ex, tr("Invalid service URL."), tr("WMS Error"), null);
123 } catch (IOException ex) {
124 handleException(ex, tr("Could not retrieve WMS layer list."), tr("WMS Error"), null);
125 } catch (WMSGetCapabilitiesException ex) {
126 handleException(ex, tr("Could not parse WMS layer list."), tr("WMS Error"),
127 "Could not parse WMS layer list. Incoming data:\n" + ex.getIncomingData());
128 } catch (WMTSGetCapabilitiesException ex) {
129 handleException(ex, tr("Could not parse WMTS layer list."), tr("WMTS Error"),
130 "Could not parse WMTS layer list.");
131 }
132 return null;
133 }
134
135 @Override
136 public void actionPerformed(ActionEvent e) {
137 if (!isEnabled()) return;
138 ImageryLayer layer = null;
139 try {
140 final ImageryInfo infoToAdd = convertImagery(info);
141 if (infoToAdd != null) {
142 layer = ImageryLayer.create(infoToAdd);
143 getLayerManager().addLayer(layer);
144 AlignImageryPanel.addNagPanelIfNeeded(infoToAdd);
145 }
146 } catch (IllegalArgumentException | ReportedException ex) {
147 if (ex.getMessage() == null || ex.getMessage().isEmpty() || GraphicsEnvironment.isHeadless()) {
148 throw ex;
149 } else {
150 Logging.error(ex);
151 JOptionPane.showMessageDialog(Main.parent, ex.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
152 if (layer != null) {
153 getLayerManager().removeLayer(layer);
154 }
155 }
156 }
157 }
158
159 /**
160 * Asks user to choose a WMS layer from a WMS endpoint.
161 * @param info the WMS endpoint.
162 * @return chosen WMS layer, or null
163 * @throws IOException if any I/O error occurs while contacting the WMS endpoint
164 * @throws WMSGetCapabilitiesException if the WMS getCapabilities request fails
165 */
166 protected static ImageryInfo getWMSLayerInfo(ImageryInfo info) throws IOException, WMSGetCapabilitiesException {
167 try {
168 CheckParameterUtil.ensureThat(ImageryType.WMS_ENDPOINT.equals(info.getImageryType()), "wms_endpoint imagery type expected");
169 final WMSImagery wms = new WMSImagery(info.getUrl());
170
171 final WMSLayerTree tree = new WMSLayerTree();
172 tree.updateTree(wms);
173
174 Collection<String> wmsFormats = wms.getFormats();
175 final JComboBox<String> formats = new JComboBox<>(wmsFormats.toArray(new String[0]));
176 formats.setSelectedItem(wms.getPreferredFormat());
177 formats.setToolTipText(tr("Select image format for WMS layer"));
178
179 if (!GraphicsEnvironment.isHeadless()) {
180 ExtendedDialog dialog = new ExtendedDialog(Main.parent, tr("Select WMS layers"), tr("Add layers"), tr("Cancel"));
181 final JScrollPane scrollPane = new JScrollPane(tree.getLayerTree());
182 scrollPane.setPreferredSize(new Dimension(400, 400));
183 final JPanel panel = new JPanel(new GridBagLayout());
184 panel.add(scrollPane, GBC.eol().fill());
185 panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
186 dialog.setContent(panel);
187
188 if (dialog.showDialog().getValue() != 1) {
189 return null;
190 }
191 }
192
193 final String url = wms.buildGetMapUrl(
194 tree.getSelectedLayers().stream().map(LayerDetails::getName).collect(Collectors.toList()),
195 (List<String>) null,
196 (String) formats.getSelectedItem(),
197 true // TODO: ask the user if transparent layer is wanted
198 );
199
200 String selectedLayers = tree.getSelectedLayers().stream()
201 .map(LayerDetails::getName)
202 .collect(Collectors.joining(", "));
203 ImageryInfo ret = new ImageryInfo(info.getName() + selectedLayers,
204 url,
205 "wms",
206 info.getEulaAcceptanceRequired(),
207 info.getCookies());
208
209 ret.setServerProjections(wms.getServerProjections(tree.getSelectedLayers()));
210
211 return ret;
212 } catch (MalformedURLException ex) {
213 handleException(ex, tr("Invalid service URL."), tr("WMS Error"), null);
214 } catch (IOException ex) {
215 handleException(ex, tr("Could not retrieve WMS layer list."), tr("WMS Error"), null);
216 } catch (WMSGetCapabilitiesException ex) {
217 handleException(ex, tr("Could not parse WMS layer list."), tr("WMS Error"),
218 "Could not parse WMS layer list. Incoming data:\n" + ex.getIncomingData());
219 }
220 return null;
221 }
222
223 private static void handleException(Exception ex, String uiMessage, String uiTitle, String logMessage) {
224 if (!GraphicsEnvironment.isHeadless()) {
225 JOptionPane.showMessageDialog(Main.parent, uiMessage, uiTitle, JOptionPane.ERROR_MESSAGE);
226 }
227 Logging.log(Logging.LEVEL_ERROR, logMessage, ex);
228 }
229
230 @Override
231 protected void updateEnabledState() {
232 if (info.isBlacklisted()) {
233 setEnabled(false);
234 } else {
235 setEnabled(true);
236 }
237 }
238
239 @Override
240 public String toString() {
241 return "AddImageryLayerAction [info=" + info + ']';
242 }
243}
Note: See TracBrowser for help on using the repository browser.