001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.imagery; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.event.ActionListener; 007import java.io.IOException; 008import java.net.MalformedURLException; 009import java.nio.file.InvalidPathException; 010import java.util.Collection; 011import java.util.List; 012import java.util.stream.Collectors; 013 014import javax.swing.DefaultComboBoxModel; 015import javax.swing.JButton; 016import javax.swing.JCheckBox; 017import javax.swing.JComboBox; 018import javax.swing.JLabel; 019import javax.swing.JOptionPane; 020import javax.swing.JScrollPane; 021 022import org.openstreetmap.josm.data.imagery.DefaultLayer; 023import org.openstreetmap.josm.data.imagery.ImageryInfo; 024import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 025import org.openstreetmap.josm.data.imagery.LayerDetails; 026import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser; 027import org.openstreetmap.josm.gui.util.GuiHelper; 028import org.openstreetmap.josm.gui.widgets.JosmTextArea; 029import org.openstreetmap.josm.io.imagery.WMSImagery; 030import org.openstreetmap.josm.tools.GBC; 031import org.openstreetmap.josm.tools.Logging; 032import org.openstreetmap.josm.tools.Utils; 033 034/** 035 * An imagery panel used to add WMS imagery sources. 036 * @since 2599 037 */ 038public class AddWMSLayerPanel extends AddImageryPanel { 039 040 private transient WMSImagery wms; 041 private final JCheckBox endpoint = new JCheckBox(tr("Store WMS endpoint only, select layers at usage")); 042 private final JCheckBox setDefaultLayers = new JCheckBox(tr("Use selected layers as default")); 043 private final transient WMSLayerTree tree = new WMSLayerTree(); 044 private final JComboBox<String> formats = new JComboBox<>(); 045 private final JosmTextArea wmsUrl = new JosmTextArea(3, 40).transferFocusOnTab(); 046 private final JButton showBounds = new JButton(tr("Show bounds")); 047 048 /** 049 * Constructs a new {@code AddWMSLayerPanel}. 050 */ 051 public AddWMSLayerPanel() { 052 053 add(new JLabel(tr("{0} Make sure OSM has the permission to use this service", "1.")), GBC.eol()); 054 add(new JLabel(tr("{0} Enter GetCapabilities URL", "2.")), GBC.eol()); 055 add(rawUrl, GBC.eol().fill(GBC.HORIZONTAL)); 056 rawUrl.setLineWrap(true); 057 JButton getLayers = new JButton(tr("{0} Get layers", "3.")); 058 add(getLayers, GBC.eop().fill(GBC.HORIZONTAL)); 059 060 add(new JLabel(tr("{0} Select layers", "4.")), GBC.eol()); 061 062 add(endpoint, GBC.eol()); 063 setDefaultLayers.setEnabled(false); 064 add(setDefaultLayers, GBC.eol()); 065 add(new JScrollPane(tree.getLayerTree()), GBC.eol().fill()); 066 067 showBounds.setEnabled(false); 068 add(showBounds, GBC.eop().fill(GBC.HORIZONTAL)); 069 070 add(new JLabel(tr("{0} Select image format", "5.")), GBC.eol()); 071 add(formats, GBC.eol().fill(GBC.HORIZONTAL)); 072 073 addCommonSettings(); 074 075 JLabel wmsInstruction = new JLabel(tr("{0} Edit generated {1} URL (optional)", "6.", "WMS")); 076 add(wmsInstruction, GBC.eol()); 077 wmsInstruction.setLabelFor(wmsUrl); 078 add(wmsUrl, GBC.eop().fill(GBC.HORIZONTAL)); 079 wmsUrl.setLineWrap(true); 080 081 add(new JLabel(tr("{0} Enter name for this layer", "7.")), GBC.eol()); 082 add(name, GBC.eop().fill(GBC.HORIZONTAL)); 083 084 getLayers.addActionListener(e -> { 085 try { 086 wms = new WMSImagery(rawUrl.getText(), getCommonHeaders()); 087 tree.updateTree(wms); 088 Collection<String> wmsFormats = wms.getFormats(); 089 formats.setModel(new DefaultComboBoxModel<>(wmsFormats.toArray(new String[0]))); 090 formats.setSelectedItem(wms.getPreferredFormat()); 091 } catch (MalformedURLException | InvalidPathException ex1) { 092 Logging.log(Logging.LEVEL_ERROR, ex1); 093 JOptionPane.showMessageDialog(getParent(), tr("Invalid service URL."), 094 tr("WMS Error"), JOptionPane.ERROR_MESSAGE); 095 } catch (IOException ex2) { 096 Logging.log(Logging.LEVEL_ERROR, ex2); 097 JOptionPane.showMessageDialog(getParent(), tr("Could not retrieve WMS layer list."), 098 tr("WMS Error"), JOptionPane.ERROR_MESSAGE); 099 } catch (WMSImagery.WMSGetCapabilitiesException ex3) { 100 String incomingData = ex3.getIncomingData() != null ? ex3.getIncomingData().trim() : ""; 101 String title = tr("WMS Error"); 102 StringBuilder message = new StringBuilder(tr("Could not parse WMS layer list.")); 103 Logging.log(Logging.LEVEL_ERROR, "Could not parse WMS layer list. Incoming data:\n"+incomingData, ex3); 104 if ((incomingData.startsWith("<html>") || incomingData.startsWith("<HTML>")) 105 && (incomingData.endsWith("</html>") || incomingData.endsWith("</HTML>"))) { 106 GuiHelper.notifyUserHtmlError(this, title, message.toString(), incomingData); 107 } else { 108 if (ex3.getMessage() != null) { 109 message.append('\n').append(ex3.getMessage()); 110 } 111 JOptionPane.showMessageDialog(getParent(), message.toString(), title, JOptionPane.ERROR_MESSAGE); 112 } 113 } 114 }); 115 116 ActionListener availabilityManagerAction = a -> { 117 setDefaultLayers.setEnabled(endpoint.isSelected()); 118 boolean enabled = !endpoint.isSelected() || setDefaultLayers.isSelected(); 119 tree.getLayerTree().setEnabled(enabled); 120 showBounds.setEnabled(enabled); 121 wmsInstruction.setEnabled(enabled); 122 formats.setEnabled(enabled); 123 wmsUrl.setEnabled(enabled); 124 if (endpoint.isSelected() && !setDefaultLayers.isSelected() && wms != null) { 125 name.setText(wms.buildRootUrlWithoutCapabilities()); 126 } 127 onLayerSelectionChanged(); 128 }; 129 130 endpoint.addActionListener(availabilityManagerAction); 131 setDefaultLayers.addActionListener(availabilityManagerAction); 132 133 tree.getLayerTree().addPropertyChangeListener("selectedLayers", evt -> onLayerSelectionChanged()); 134 135 formats.addActionListener(e -> onLayerSelectionChanged()); 136 137 showBounds.addActionListener(e -> { 138 if (tree.getSelectedLayers().get(0).getBounds() != null) { 139 SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser(); 140 mapPanel.setBoundingBox(tree.getSelectedLayers().get(0).getBounds()); 141 JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE); 142 } else { 143 JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."), 144 tr("WMS Error"), JOptionPane.ERROR_MESSAGE); 145 } 146 }); 147 148 registerValidableComponent(endpoint); 149 registerValidableComponent(rawUrl); 150 registerValidableComponent(wmsUrl); 151 registerValidableComponent(setDefaultLayers); 152 } 153 154 protected final void onLayerSelectionChanged() { 155 if (wms != null && wms.buildRootUrl() != null) { 156 wmsUrl.setText(wms.buildGetMapUrl( 157 tree.getSelectedLayers().stream().map(LayerDetails::getName).collect(Collectors.toList()), 158 (List<String>) null, 159 (String) formats.getSelectedItem(), 160 true // TODO: ask user about transparency 161 ) 162 ); 163 name.setText(wms.buildRootUrlWithoutCapabilities() + ": " + Utils.join(", ", tree.getSelectedLayers())); 164 } 165 showBounds.setEnabled(tree.getSelectedLayers().size() == 1); 166 } 167 168 @Override 169 public ImageryInfo getImageryInfo() { 170 ImageryInfo info = null; 171 if (endpoint.isSelected()) { 172 info = new ImageryInfo(getImageryName(), getImageryRawUrl()); 173 info.setImageryType(ImageryInfo.ImageryType.WMS_ENDPOINT); 174 if (setDefaultLayers.isSelected()) { 175 info.setDefaultLayers(tree.getSelectedLayers().stream() 176 .map(x -> new DefaultLayer( 177 ImageryInfo.ImageryType.WMS_ENDPOINT, 178 x.getName(), 179 "", // TODO: allow selection of styles 180 "")) 181 .collect(Collectors.toList())); 182 info.setServerProjections(wms.getServerProjections(tree.getSelectedLayers())); 183 } 184 } else { 185 if (wms != null && wms.buildRootUrl() != null) { 186 // TODO: ask user about transparency 187 info = wms.toImageryInfo( 188 getImageryName(), tree.getSelectedLayers(), (List<String>) null, (String) formats.getSelectedItem(), true); 189 } else { 190 info = new ImageryInfo(getImageryName(), getWmsUrl()); 191 } 192 info.setImageryType(ImageryType.WMS); 193 } 194 info.setGeoreferenceValid(getCommonIsValidGeoreference()); 195 info.setCustomHttpHeaders(getCommonHeaders()); 196 return info; 197 } 198 199 protected final String getWmsUrl() { 200 return sanitize(wmsUrl.getText(), ImageryInfo.ImageryType.WMS); 201 } 202 203 @Override 204 protected boolean isImageryValid() { 205 if (getImageryName().isEmpty()) { 206 return false; 207 } 208 if (setDefaultLayers.isSelected() && (tree == null || tree.getSelectedLayers().isEmpty())) { 209 return false; 210 } 211 if (endpoint.isSelected()) { 212 return !getImageryRawUrl().isEmpty(); 213 } else { 214 return !getWmsUrl().isEmpty(); 215 } 216 } 217}