Ignore:
Timestamp:
2010-10-03T16:13:54+02:00 (14 years ago)
Author:
jttt
Message:

Fix #5511 NPE in WMS Plugin when retrieving layer list for WMS server

File:
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/AddWMSLayerPanel.java

    r23304 r23454  
    5959
    6060public class AddWMSLayerPanel extends JPanel {
    61     private List<LayerDetails> selectedLayers;
    62     private URL serviceUrl;
    63     private LayerDetails selectedLayer;
    64 
    65     private JTextField menuName;
    66     private JTextArea resultingLayerField;
    67     private MutableTreeNode treeRootNode;
    68     private DefaultTreeModel treeData;
    69     private JTree layerTree;
    70     private JButton showBoundsButton;
    71 
    72     private boolean previouslyShownUnsupportedCrsError = false;
    73 
    74     public AddWMSLayerPanel() {
    75         JPanel wmsFetchPanel = new JPanel(new GridBagLayout());
    76         menuName = new JTextField(40);
    77         menuName.setText(tr("Unnamed WMS Layer"));
    78         final JTextArea serviceUrl = new JTextArea(3, 40);
    79         serviceUrl.setLineWrap(true);
    80         serviceUrl.setText("http://sample.com/wms?");
    81         wmsFetchPanel.add(new JLabel(tr("Menu Name")), GBC.std().insets(0,0,5,0));
    82         wmsFetchPanel.add(menuName, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
    83         wmsFetchPanel.add(new JLabel(tr("Service URL")), GBC.std().insets(0,0,5,0));
    84         JScrollPane scrollPane = new JScrollPane(serviceUrl,
    85                 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
    86                 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    87         wmsFetchPanel.add(scrollPane, GBC.eop().insets(5,0,0,0));
    88         JButton getLayersButton = new JButton(tr("Get Layers"));
    89         getLayersButton.addActionListener(new ActionListener() {
    90             public void actionPerformed(ActionEvent e) {
    91                 Cursor beforeCursor = getCursor();
    92                 try {
    93                     setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    94                     attemptGetCapabilities(serviceUrl.getText());
    95                 } finally {
    96                     setCursor(beforeCursor);
    97                 }
    98             }
    99         });
    100         wmsFetchPanel.add(getLayersButton, GBC.eop().anchor(GridBagConstraints.EAST));
    101 
    102         treeRootNode = new DefaultMutableTreeNode();
    103         treeData = new DefaultTreeModel(treeRootNode);
    104         layerTree = new JTree(treeData);
    105         layerTree.setCellRenderer(new LayerTreeCellRenderer());
    106         layerTree.addTreeSelectionListener(new TreeSelectionListener() {
    107 
    108             public void valueChanged(TreeSelectionEvent e) {
    109                 TreePath[] selectionRows = layerTree.getSelectionPaths();
    110                 if(selectionRows == null) {
    111                     showBoundsButton.setEnabled(false);
    112                     selectedLayer = null;
    113                     return;
    114                 }
    115 
    116                 selectedLayers = new LinkedList<LayerDetails>();
    117                 for (TreePath i : selectionRows) {
    118                     Object userObject = ((DefaultMutableTreeNode) i.getLastPathComponent()).getUserObject();
    119                     if(userObject instanceof LayerDetails) {
    120                         LayerDetails detail = (LayerDetails) userObject;
    121                         if(!detail.isSupported()) {
    122                             layerTree.removeSelectionPath(i);
    123                             if(!previouslyShownUnsupportedCrsError) {
    124                                 JOptionPane.showMessageDialog(null, tr("That layer does not support any of JOSM's projections,\n" +
    125                                 "so you can not use it. This message will not show again."),
    126                                 tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    127                                 previouslyShownUnsupportedCrsError = true;
    128                             }
    129                         } else if(detail.ident != null) {
    130                             selectedLayers.add(detail);
    131                         }
    132                     }
    133                 }
    134 
    135                 if (!selectedLayers.isEmpty()) {
    136                     resultingLayerField.setText(buildGetMapUrl());
    137 
    138                     if(selectedLayers.size() == 1) {
    139                         showBoundsButton.setEnabled(true);
    140                         selectedLayer = selectedLayers.get(0);
    141                     }
    142                 } else {
    143                     showBoundsButton.setEnabled(false);
    144                     selectedLayer = null;
    145                 }
    146             }
    147         });
    148         wmsFetchPanel.add(new JScrollPane(layerTree), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
    149 
    150         JPanel layerManipulationButtons = new JPanel();
    151         showBoundsButton = new JButton(tr("Show Bounds"));
    152         showBoundsButton.setEnabled(false);
    153         showBoundsButton.addActionListener(new ActionListener() {
    154             public void actionPerformed(ActionEvent e) {
    155                 if(selectedLayer.bounds != null) {
    156                     SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
    157                     mapPanel.setBoundingBox(selectedLayer.bounds);
    158                     JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
    159                 } else {
    160                     JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
    161                             tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    162                 }
    163             }
    164         });
    165         layerManipulationButtons.add(showBoundsButton);
    166 
    167         wmsFetchPanel.add(layerManipulationButtons, GBC.eol().insets(0,0,5,0));
    168         wmsFetchPanel.add(new JLabel(tr("WMS URL")), GBC.std().insets(0,0,5,0));
    169         resultingLayerField = new JTextArea(3, 40);
    170         resultingLayerField.setLineWrap(true);
    171         wmsFetchPanel.add(new JScrollPane(resultingLayerField, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
    172 
    173         add(wmsFetchPanel);
    174     }
    175 
    176     private String buildRootUrl() {
    177         StringBuilder a = new StringBuilder(serviceUrl.getProtocol());
    178         a.append("://");
    179         a.append(serviceUrl.getHost());
    180         if(serviceUrl.getPort() != -1) {
    181             a.append(":");
    182             a.append(serviceUrl.getPort());
    183         }
    184         a.append(serviceUrl.getPath());
    185         a.append("?");
    186         if(serviceUrl.getQuery() != null) {
    187             a.append(serviceUrl.getQuery());
    188             if (!serviceUrl.getQuery().isEmpty() && !serviceUrl.getQuery().endsWith("&")) {
    189                 a.append("&");
    190             }
    191         }
    192         return a.toString();
    193     }
    194 
    195     private String buildGetMapUrl() {
    196         StringBuilder a = new StringBuilder();
    197         a.append(buildRootUrl());
    198         a.append("FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=");
    199         a.append(commaSepLayerList());
    200         a.append("&");
    201 
    202         return a.toString();
    203     }
    204 
    205     private String commaSepLayerList() {
    206         StringBuilder b = new StringBuilder();
    207 
    208         Iterator<LayerDetails> iterator = selectedLayers.iterator();
    209         while (iterator.hasNext()) {
    210             LayerDetails layerDetails = iterator.next();
    211             b.append(layerDetails.ident);
    212             if(iterator.hasNext()) {
    213                 b.append(",");
    214             }
    215         }
    216 
    217         return b.toString();
    218     }
    219 
    220     private void attemptGetCapabilities(String serviceUrlStr) {
    221         URL getCapabilitiesUrl = null;
    222         try {
    223             if (!serviceUrlStr.trim().contains("capabilities")) {
    224                 // If the url doesn't already have GetCapabilities, add it in
    225                 getCapabilitiesUrl = new URL(serviceUrlStr + "VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities");
    226             } else {
    227                 // Otherwise assume it's a good URL and let the subsequent error
    228                 // handling systems deal with problems
    229                 getCapabilitiesUrl = new URL(serviceUrlStr);
    230             }
    231             serviceUrl = new URL(serviceUrlStr);
    232         } catch (HeadlessException e) {
    233             return;
    234         } catch (MalformedURLException e) {
    235             JOptionPane.showMessageDialog(this, tr("Invalid service URL."),
    236                     tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    237             return;
    238         }
    239 
    240         String incomingData;
    241         try {
    242             URLConnection openConnection = getCapabilitiesUrl.openConnection();
    243             InputStream inputStream = openConnection.getInputStream();
    244             BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
    245             String line;
    246             StringBuilder ba = new StringBuilder();
    247             while((line = br.readLine()) != null) {
    248                 ba.append(line);
    249                 ba.append("\n");
    250             }
    251             incomingData = ba.toString();
    252         } catch (IOException e) {
    253             JOptionPane.showMessageDialog(this, tr("Could not retrieve WMS layer list."),
    254                     tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    255             return;
    256         }
    257 
    258         Document document;
    259         try {
    260             DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
    261             builderFactory.setValidating(false);
    262             builderFactory.setNamespaceAware(true);
    263             DocumentBuilder builder = builderFactory.newDocumentBuilder();
    264             builder.setEntityResolver(new EntityResolver() {
    265                 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    266                     System.out.println("Ignoring DTD " + publicId + ", " + systemId);
    267                     return new InputSource(new StringReader(""));
    268                 }
    269             });
    270             document = builder.parse(new InputSource(new StringReader(incomingData)));
    271         } catch (ParserConfigurationException e) {
    272             JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
    273                     tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    274             return;
    275         } catch (SAXException e) {
    276             JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
    277                     tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    278             return;
    279         } catch (IOException e) {
    280             JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
    281                     tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    282             return;
    283         }
    284 
    285         // Some WMS service URLs specify a different base URL for their GetMap service
    286         Element child = getChild(document.getDocumentElement(), "Capability");
    287         child = getChild(child, "Request");
    288         child = getChild(child, "GetMap");
    289         child = getChild(child, "DCPType");
    290         child = getChild(child, "HTTP");
    291         child = getChild(child, "Get");
    292         child = getChild(child, "OnlineResource");
    293         String baseURL = child.getAttribute("xlink:href");
    294         if(baseURL != null) {
    295             try {
    296                 System.out.println("GetCapabilities specifies a different service URL: " + baseURL);
    297                 serviceUrl = new URL(baseURL);
    298             } catch (MalformedURLException e1) {
    299             }
    300         }
    301 
    302         try {
    303             treeRootNode.setUserObject(getCapabilitiesUrl.getHost());
    304             Element capabilityElem = getChild(document.getDocumentElement(), "Capability");
    305             List<Element> children = getChildren(capabilityElem, "Layer");
    306             List<LayerDetails> layers = parseLayers(children, new HashSet<String>());
    307             updateTreeList(layers);
    308         } catch(Exception e) {
    309             JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
    310                     tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    311             e.printStackTrace();
    312             return;
    313         }
    314     }
    315 
    316     private void updateTreeList(List<LayerDetails> layers) {
    317         addLayersToTreeData(treeRootNode, layers);
    318         layerTree.expandRow(0);
    319     }
    320 
    321     private void addLayersToTreeData(MutableTreeNode parent, List<LayerDetails> layers) {
    322         for (LayerDetails layerDetails : layers) {
    323             DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(layerDetails);
    324             addLayersToTreeData(treeNode, layerDetails.children);
    325             treeData.insertNodeInto(treeNode, parent, 0);
    326         }
    327     }
    328 
    329     private List<LayerDetails> parseLayers(List<Element> children, Set<String> parentCrs) {
    330         List<LayerDetails> details = new LinkedList<LayerDetails>();
    331         for (Element element : children) {
    332             details.add(parseLayer(element, parentCrs));
    333         }
    334         return details;
    335     }
    336 
    337     private LayerDetails parseLayer(Element element, Set<String> parentCrs) {
    338         String name = getChildContent(element, "Title", null, null);
    339         String ident = getChildContent(element, "Name", null, null);
    340 
    341         // The set of supported CRS/SRS for this layer
    342         Set<String> crsList = new HashSet<String>();
    343         // ...including this layer's already-parsed parent projections
    344         crsList.addAll(parentCrs);
    345 
    346         // Parse the CRS/SRS pulled out of this layer's XML element
    347         // I think CRS and SRS are the same at this point
    348         List<Element> crsChildren = getChildren(element, "CRS");
    349         crsChildren.addAll(getChildren(element, "SRS"));
    350         for (Element child : crsChildren) {
    351             String crs = (String) getContent(child);
    352             if(crs != null) {
    353                 String upperCase = crs.trim().toUpperCase();
    354                 crsList.add(upperCase);
    355             }
    356         }
    357 
    358         // Check to see if any of the specified projections are supported by JOSM
    359         boolean josmSupportsThisLayer = false;
    360         for (String crs : crsList) {
    361             josmSupportsThisLayer |= isProjSupported(crs);
    362         }
    363 
    364         Bounds bounds = null;
    365         Element bboxElem = getChild(element, "EX_GeographicBoundingBox");
    366         if(bboxElem != null) {
    367             // Attempt to use EX_GeographicBoundingBox for bounding box
    368             double left = Double.parseDouble(getChildContent(bboxElem, "westBoundLongitude", null, null));
    369             double top = Double.parseDouble(getChildContent(bboxElem, "northBoundLatitude", null, null));
    370             double right = Double.parseDouble(getChildContent(bboxElem, "eastBoundLongitude", null, null));
    371             double bot = Double.parseDouble(getChildContent(bboxElem, "southBoundLatitude", null, null));
    372             bounds = new Bounds(bot, left, top, right);
    373         } else {
    374             // If that's not available, try LatLonBoundingBox
    375             bboxElem = getChild(element, "LatLonBoundingBox");
    376             if(bboxElem != null) {
    377                 double left = Double.parseDouble(bboxElem.getAttribute("minx"));
    378                 double top = Double.parseDouble(bboxElem.getAttribute("maxy"));
    379                 double right = Double.parseDouble(bboxElem.getAttribute("maxx"));
    380                 double bot = Double.parseDouble(bboxElem.getAttribute("miny"));
    381                 bounds = new Bounds(bot, left, top, right);
    382             }
    383         }
    384 
    385         List<Element> layerChildren = getChildren(element, "Layer");
    386         List<LayerDetails> childLayers = parseLayers(layerChildren, crsList);
    387 
    388         return new LayerDetails(name, ident, crsList, josmSupportsThisLayer, bounds, childLayers);
    389     }
    390 
    391     private boolean isProjSupported(String crs) {
    392         for (Projection proj : Projection.allProjections) {
    393             if (proj instanceof ProjectionSubPrefs) {
    394                 if (((ProjectionSubPrefs) proj).getPreferencesFromCode(crs) == null) {
    395                     return true;
    396                 }
    397             } else {
    398                 if (proj.toCode().equals(crs)) {
    399                     return true;
    400                 }
    401             }
    402         }
    403         return false;
    404     }
    405 
    406     public String getUrlName() {
    407         return menuName.getText();
    408     }
    409 
    410     public String getUrl() {
    411         return resultingLayerField.getText();
    412     }
    413 
    414     public static void main(String[] args) {
    415         JFrame f = new JFrame("Test");
    416         f.setContentPane(new AddWMSLayerPanel());
    417         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    418         f.pack();
    419         f.setVisible(true);
    420     }
    421 
    422     private static String getChildContent(Element parent, String name, String missing, String empty) {
    423         Element child = getChild(parent, name);
    424         if (child == null) {
    425             return missing;
    426         } else {
    427             String content = (String) getContent(child);
    428             return (content != null) ? content : empty;
    429         }
    430     }
    431 
    432     private static Object getContent(Element element) {
    433         NodeList nl = element.getChildNodes();
    434         StringBuffer content = new StringBuffer();
    435         for (int i = 0; i < nl.getLength(); i++) {
    436             Node node = nl.item(i);
    437             switch (node.getNodeType()) {
    438             case Node.ELEMENT_NODE:
    439                 return node;
    440             case Node.CDATA_SECTION_NODE:
    441             case Node.TEXT_NODE:
    442                 content.append(node.getNodeValue());
    443                 break;
    444             }
    445         }
    446         return content.toString().trim();
    447     }
    448 
    449     private static List<Element> getChildren(Element parent, String name) {
    450         List<Element> retVal = new LinkedList<Element>();
    451         for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
    452             if (child instanceof Element && name.equals(child.getNodeName())) {
    453                 retVal.add((Element) child);
    454             }
    455         }
    456         return retVal;
    457     }
    458 
    459     private static Element getChild(Element parent, String name) {
    460         for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
    461             if (child instanceof Element && name.equals(child.getNodeName())) {
    462                 return (Element) child;
    463             }
    464         }
    465         return null;
    466     }
    467 
    468     class LayerDetails {
    469 
    470         private String name;
    471         private String ident;
    472         private Set<String> crsList;
    473         private List<LayerDetails> children;
    474         private Bounds bounds;
    475         private boolean supported;
    476 
    477         public LayerDetails(String name, String ident, Set<String> crsList,
    478                 boolean supportedLayer, Bounds bounds,
    479                 List<LayerDetails> childLayers) {
    480             this.name = name;
    481             this.ident = ident;
    482             this.crsList = crsList;
    483             this.supported = supportedLayer;
    484             this.children = childLayers;
    485             this.bounds = bounds;
    486         }
    487 
    488         public boolean isSupported() {
    489             return this.supported;
    490         }
    491 
    492         @Override
    493         public String toString() {
    494             if(this.name == null || this.name.isEmpty()) {
    495                 return this.ident;
    496             } else {
    497                 return this.name;
    498             }
    499         }
    500 
    501     }
    502 
    503     class LayerTreeCellRenderer extends DefaultTreeCellRenderer {
    504         @Override
    505         public Component getTreeCellRendererComponent(JTree tree, Object value,
    506                 boolean sel, boolean expanded, boolean leaf, int row,
    507                 boolean hasFocus) {
    508             super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
    509                     row, hasFocus);
    510             DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
    511             Object userObject = treeNode.getUserObject();
    512             if (userObject instanceof LayerDetails) {
    513                 LayerDetails layer = (LayerDetails) userObject;
    514                 setEnabled(layer.isSupported());
    515             }
    516             return this;
    517         }
    518     }
     61        private List<LayerDetails> selectedLayers;
     62        private URL serviceUrl;
     63        private LayerDetails selectedLayer;
     64
     65        private JTextField menuName;
     66        private JTextArea resultingLayerField;
     67        private MutableTreeNode treeRootNode;
     68        private DefaultTreeModel treeData;
     69        private JTree layerTree;
     70        private JButton showBoundsButton;
     71
     72        private boolean previouslyShownUnsupportedCrsError = false;
     73
     74        public AddWMSLayerPanel() {
     75                JPanel wmsFetchPanel = new JPanel(new GridBagLayout());
     76                menuName = new JTextField(40);
     77                menuName.setText(tr("Unnamed WMS Layer"));
     78                final JTextArea serviceUrl = new JTextArea(3, 40);
     79                serviceUrl.setLineWrap(true);
     80                serviceUrl.setText("http://sample.com/wms?");
     81                wmsFetchPanel.add(new JLabel(tr("Menu Name")), GBC.std().insets(0,0,5,0));
     82                wmsFetchPanel.add(menuName, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
     83                wmsFetchPanel.add(new JLabel(tr("Service URL")), GBC.std().insets(0,0,5,0));
     84                JScrollPane scrollPane = new JScrollPane(serviceUrl,
     85                                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
     86                                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
     87                wmsFetchPanel.add(scrollPane, GBC.eop().insets(5,0,0,0));
     88                JButton getLayersButton = new JButton(tr("Get Layers"));
     89                getLayersButton.addActionListener(new ActionListener() {
     90                        public void actionPerformed(ActionEvent e) {
     91                                Cursor beforeCursor = getCursor();
     92                                try {
     93                                        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
     94                                        attemptGetCapabilities(serviceUrl.getText());
     95                                } finally {
     96                                        setCursor(beforeCursor);
     97                                }
     98                        }
     99                });
     100                wmsFetchPanel.add(getLayersButton, GBC.eop().anchor(GridBagConstraints.EAST));
     101
     102                treeRootNode = new DefaultMutableTreeNode();
     103                treeData = new DefaultTreeModel(treeRootNode);
     104                layerTree = new JTree(treeData);
     105                layerTree.setCellRenderer(new LayerTreeCellRenderer());
     106                layerTree.addTreeSelectionListener(new TreeSelectionListener() {
     107
     108                        public void valueChanged(TreeSelectionEvent e) {
     109                                TreePath[] selectionRows = layerTree.getSelectionPaths();
     110                                if(selectionRows == null) {
     111                                        showBoundsButton.setEnabled(false);
     112                                        selectedLayer = null;
     113                                        return;
     114                                }
     115
     116                                selectedLayers = new LinkedList<LayerDetails>();
     117                                for (TreePath i : selectionRows) {
     118                                        Object userObject = ((DefaultMutableTreeNode) i.getLastPathComponent()).getUserObject();
     119                                        if(userObject instanceof LayerDetails) {
     120                                                LayerDetails detail = (LayerDetails) userObject;
     121                                                if(!detail.isSupported()) {
     122                                                        layerTree.removeSelectionPath(i);
     123                                                        if(!previouslyShownUnsupportedCrsError) {
     124                                                                JOptionPane.showMessageDialog(null, tr("That layer does not support any of JOSM's projections,\n" +
     125                                                                "so you can not use it. This message will not show again."),
     126                                                                tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
     127                                                                previouslyShownUnsupportedCrsError = true;
     128                                                        }
     129                                                } else if(detail.ident != null) {
     130                                                        selectedLayers.add(detail);
     131                                                }
     132                                        }
     133                                }
     134
     135                                if (!selectedLayers.isEmpty()) {
     136                                        resultingLayerField.setText(buildGetMapUrl());
     137
     138                                        if(selectedLayers.size() == 1) {
     139                                                showBoundsButton.setEnabled(true);
     140                                                selectedLayer = selectedLayers.get(0);
     141                                        }
     142                                } else {
     143                                        showBoundsButton.setEnabled(false);
     144                                        selectedLayer = null;
     145                                }
     146                        }
     147                });
     148                wmsFetchPanel.add(new JScrollPane(layerTree), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
     149
     150                JPanel layerManipulationButtons = new JPanel();
     151                showBoundsButton = new JButton(tr("Show Bounds"));
     152                showBoundsButton.setEnabled(false);
     153                showBoundsButton.addActionListener(new ActionListener() {
     154                        public void actionPerformed(ActionEvent e) {
     155                                if(selectedLayer.bounds != null) {
     156                                        SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
     157                                        mapPanel.setBoundingBox(selectedLayer.bounds);
     158                                        JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
     159                                } else {
     160                                        JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
     161                                                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
     162                                }
     163                        }
     164                });
     165                layerManipulationButtons.add(showBoundsButton);
     166
     167                wmsFetchPanel.add(layerManipulationButtons, GBC.eol().insets(0,0,5,0));
     168                wmsFetchPanel.add(new JLabel(tr("WMS URL")), GBC.std().insets(0,0,5,0));
     169                resultingLayerField = new JTextArea(3, 40);
     170                resultingLayerField.setLineWrap(true);
     171                wmsFetchPanel.add(new JScrollPane(resultingLayerField, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
     172
     173                add(wmsFetchPanel);
     174        }
     175
     176        private String buildRootUrl() {
     177                StringBuilder a = new StringBuilder(serviceUrl.getProtocol());
     178                a.append("://");
     179                a.append(serviceUrl.getHost());
     180                if(serviceUrl.getPort() != -1) {
     181                        a.append(":");
     182                        a.append(serviceUrl.getPort());
     183                }
     184                a.append(serviceUrl.getPath());
     185                a.append("?");
     186                if(serviceUrl.getQuery() != null) {
     187                        a.append(serviceUrl.getQuery());
     188                        if (!serviceUrl.getQuery().isEmpty() && !serviceUrl.getQuery().endsWith("&")) {
     189                                a.append("&");
     190                        }
     191                }
     192                return a.toString();
     193        }
     194
     195        private String buildGetMapUrl() {
     196                StringBuilder a = new StringBuilder();
     197                a.append(buildRootUrl());
     198                a.append("FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=");
     199                a.append(commaSepLayerList());
     200                a.append("&");
     201
     202                return a.toString();
     203        }
     204
     205        private String commaSepLayerList() {
     206                StringBuilder b = new StringBuilder();
     207
     208                Iterator<LayerDetails> iterator = selectedLayers.iterator();
     209                while (iterator.hasNext()) {
     210                        LayerDetails layerDetails = iterator.next();
     211                        b.append(layerDetails.ident);
     212                        if(iterator.hasNext()) {
     213                                b.append(",");
     214                        }
     215                }
     216
     217                return b.toString();
     218        }
     219
     220        private void showError(String incomingData, Exception e) {
     221                JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
     222                                tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
     223                System.err.println("Could not parse WMS layer list. Incoming data:");
     224                System.err.println(incomingData);
     225                e.printStackTrace();
     226        }
     227
     228        private void attemptGetCapabilities(String serviceUrlStr) {
     229                URL getCapabilitiesUrl = null;
     230                try {
     231                        if (!serviceUrlStr.trim().contains("capabilities")) {
     232                                // If the url doesn't already have GetCapabilities, add it in
     233                                getCapabilitiesUrl = new URL(serviceUrlStr + "VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities");
     234                        } else {
     235                                // Otherwise assume it's a good URL and let the subsequent error
     236                                // handling systems deal with problems
     237                                getCapabilitiesUrl = new URL(serviceUrlStr);
     238                        }
     239                        serviceUrl = new URL(serviceUrlStr);
     240                } catch (HeadlessException e) {
     241                        return;
     242                } catch (MalformedURLException e) {
     243                        JOptionPane.showMessageDialog(this, tr("Invalid service URL."),
     244                                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
     245                        return;
     246                }
     247
     248                String incomingData;
     249                try {
     250                        URLConnection openConnection = getCapabilitiesUrl.openConnection();
     251                        InputStream inputStream = openConnection.getInputStream();
     252                        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
     253                        String line;
     254                        StringBuilder ba = new StringBuilder();
     255                        while((line = br.readLine()) != null) {
     256                                ba.append(line);
     257                                ba.append("\n");
     258                        }
     259                        incomingData = ba.toString();
     260                } catch (IOException e) {
     261                        JOptionPane.showMessageDialog(this, tr("Could not retrieve WMS layer list."),
     262                                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
     263                        return;
     264                }
     265
     266                Document document;
     267                try {
     268                        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
     269                        builderFactory.setValidating(false);
     270                        builderFactory.setNamespaceAware(true);
     271                        DocumentBuilder builder = builderFactory.newDocumentBuilder();
     272                        builder.setEntityResolver(new EntityResolver() {
     273                                public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
     274                                        System.out.println("Ignoring DTD " + publicId + ", " + systemId);
     275                                        return new InputSource(new StringReader(""));
     276                                }
     277                        });
     278                        document = builder.parse(new InputSource(new StringReader(incomingData)));
     279                } catch (ParserConfigurationException e) {
     280                        showError(incomingData, e);
     281                        return;
     282                } catch (SAXException e) {
     283                        showError(incomingData, e);
     284                        return;
     285                } catch (IOException e) {
     286                        showError(incomingData, e);
     287                        return;
     288                }
     289
     290                // Some WMS service URLs specify a different base URL for their GetMap service
     291                Element child = getChild(document.getDocumentElement(), "Capability");
     292                child = getChild(child, "Request");
     293                child = getChild(child, "GetMap");
     294                child = getChild(child, "DCPType");
     295                child = getChild(child, "HTTP");
     296                child = getChild(child, "Get");
     297                child = getChild(child, "OnlineResource");
     298                if (child != null) {
     299                        String baseURL = child.getAttribute("xlink:href");
     300                        if(baseURL != null) {
     301                                try {
     302                                        System.out.println("GetCapabilities specifies a different service URL: " + baseURL);
     303                                        serviceUrl = new URL(baseURL);
     304                                } catch (MalformedURLException e1) {
     305                                }
     306                        }
     307                }
     308
     309                try {
     310                        treeRootNode.setUserObject(getCapabilitiesUrl.getHost());
     311                        Element capabilityElem = getChild(document.getDocumentElement(), "Capability");
     312                        List<Element> children = getChildren(capabilityElem, "Layer");
     313                        List<LayerDetails> layers = parseLayers(children, new HashSet<String>());
     314                        updateTreeList(layers);
     315                } catch(Exception e) {
     316                        showError(incomingData, e);
     317                        return;
     318                }
     319        }
     320
     321        private void updateTreeList(List<LayerDetails> layers) {
     322                addLayersToTreeData(treeRootNode, layers);
     323                layerTree.expandRow(0);
     324        }
     325
     326        private void addLayersToTreeData(MutableTreeNode parent, List<LayerDetails> layers) {
     327                for (LayerDetails layerDetails : layers) {
     328                        DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(layerDetails);
     329                        addLayersToTreeData(treeNode, layerDetails.children);
     330                        treeData.insertNodeInto(treeNode, parent, 0);
     331                }
     332        }
     333
     334        private List<LayerDetails> parseLayers(List<Element> children, Set<String> parentCrs) {
     335                List<LayerDetails> details = new LinkedList<LayerDetails>();
     336                for (Element element : children) {
     337                        details.add(parseLayer(element, parentCrs));
     338                }
     339                return details;
     340        }
     341
     342        private LayerDetails parseLayer(Element element, Set<String> parentCrs) {
     343                String name = getChildContent(element, "Title", null, null);
     344                String ident = getChildContent(element, "Name", null, null);
     345
     346                // The set of supported CRS/SRS for this layer
     347                Set<String> crsList = new HashSet<String>();
     348                // ...including this layer's already-parsed parent projections
     349                crsList.addAll(parentCrs);
     350
     351                // Parse the CRS/SRS pulled out of this layer's XML element
     352                // I think CRS and SRS are the same at this point
     353                List<Element> crsChildren = getChildren(element, "CRS");
     354                crsChildren.addAll(getChildren(element, "SRS"));
     355                for (Element child : crsChildren) {
     356                        String crs = (String) getContent(child);
     357                        if(crs != null) {
     358                                String upperCase = crs.trim().toUpperCase();
     359                                crsList.add(upperCase);
     360                        }
     361                }
     362
     363                // Check to see if any of the specified projections are supported by JOSM
     364                boolean josmSupportsThisLayer = false;
     365                for (String crs : crsList) {
     366                        josmSupportsThisLayer |= isProjSupported(crs);
     367                }
     368
     369                Bounds bounds = null;
     370                Element bboxElem = getChild(element, "EX_GeographicBoundingBox");
     371                if(bboxElem != null) {
     372                        // Attempt to use EX_GeographicBoundingBox for bounding box
     373                        double left = Double.parseDouble(getChildContent(bboxElem, "westBoundLongitude", null, null));
     374                        double top = Double.parseDouble(getChildContent(bboxElem, "northBoundLatitude", null, null));
     375                        double right = Double.parseDouble(getChildContent(bboxElem, "eastBoundLongitude", null, null));
     376                        double bot = Double.parseDouble(getChildContent(bboxElem, "southBoundLatitude", null, null));
     377                        bounds = new Bounds(bot, left, top, right);
     378                } else {
     379                        // If that's not available, try LatLonBoundingBox
     380                        bboxElem = getChild(element, "LatLonBoundingBox");
     381                        if(bboxElem != null) {
     382                                double left = Double.parseDouble(bboxElem.getAttribute("minx"));
     383                                double top = Double.parseDouble(bboxElem.getAttribute("maxy"));
     384                                double right = Double.parseDouble(bboxElem.getAttribute("maxx"));
     385                                double bot = Double.parseDouble(bboxElem.getAttribute("miny"));
     386                                bounds = new Bounds(bot, left, top, right);
     387                        }
     388                }
     389
     390                List<Element> layerChildren = getChildren(element, "Layer");
     391                List<LayerDetails> childLayers = parseLayers(layerChildren, crsList);
     392
     393                return new LayerDetails(name, ident, crsList, josmSupportsThisLayer, bounds, childLayers);
     394        }
     395
     396        private boolean isProjSupported(String crs) {
     397                for (Projection proj : Projection.allProjections) {
     398                        if (proj instanceof ProjectionSubPrefs) {
     399                                if (((ProjectionSubPrefs) proj).getPreferencesFromCode(crs) == null) {
     400                                        return true;
     401                                }
     402                        } else {
     403                                if (proj.toCode().equals(crs)) {
     404                                        return true;
     405                                }
     406                        }
     407                }
     408                return false;
     409        }
     410
     411        public String getUrlName() {
     412                return menuName.getText();
     413        }
     414
     415        public String getUrl() {
     416                return resultingLayerField.getText();
     417        }
     418
     419        public static void main(String[] args) {
     420                JFrame f = new JFrame("Test");
     421                f.setContentPane(new AddWMSLayerPanel());
     422                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     423                f.pack();
     424                f.setVisible(true);
     425        }
     426
     427        private static String getChildContent(Element parent, String name, String missing, String empty) {
     428                Element child = getChild(parent, name);
     429                if (child == null) {
     430                        return missing;
     431                } else {
     432                        String content = (String) getContent(child);
     433                        return (content != null) ? content : empty;
     434                }
     435        }
     436
     437        private static Object getContent(Element element) {
     438                NodeList nl = element.getChildNodes();
     439                StringBuffer content = new StringBuffer();
     440                for (int i = 0; i < nl.getLength(); i++) {
     441                        Node node = nl.item(i);
     442                        switch (node.getNodeType()) {
     443                        case Node.ELEMENT_NODE:
     444                                return node;
     445                        case Node.CDATA_SECTION_NODE:
     446                        case Node.TEXT_NODE:
     447                                content.append(node.getNodeValue());
     448                                break;
     449                        }
     450                }
     451                return content.toString().trim();
     452        }
     453
     454        private static List<Element> getChildren(Element parent, String name) {
     455                List<Element> retVal = new LinkedList<Element>();
     456                for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
     457                        if (child instanceof Element && name.equals(child.getNodeName())) {
     458                                retVal.add((Element) child);
     459                        }
     460                }
     461                return retVal;
     462        }
     463
     464        private static Element getChild(Element parent, String name) {
     465                if (parent == null) {
     466                        return null;
     467                }
     468                for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
     469                        if (child instanceof Element && name.equals(child.getNodeName())) {
     470                                return (Element) child;
     471                        }
     472                }
     473                return null;
     474        }
     475
     476        class LayerDetails {
     477
     478                private String name;
     479                private String ident;
     480                private List<LayerDetails> children;
     481                private Bounds bounds;
     482                private boolean supported;
     483
     484                public LayerDetails(String name, String ident, Set<String> crsList,
     485                                boolean supportedLayer, Bounds bounds,
     486                                List<LayerDetails> childLayers) {
     487                        this.name = name;
     488                        this.ident = ident;
     489                        this.supported = supportedLayer;
     490                        this.children = childLayers;
     491                        this.bounds = bounds;
     492                }
     493
     494                public boolean isSupported() {
     495                        return this.supported;
     496                }
     497
     498                @Override
     499                public String toString() {
     500                        if(this.name == null || this.name.isEmpty()) {
     501                                return this.ident;
     502                        } else {
     503                                return this.name;
     504                        }
     505                }
     506
     507        }
     508
     509        class LayerTreeCellRenderer extends DefaultTreeCellRenderer {
     510                @Override
     511                public Component getTreeCellRendererComponent(JTree tree, Object value,
     512                                boolean sel, boolean expanded, boolean leaf, int row,
     513                                boolean hasFocus) {
     514                        super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
     515                                        row, hasFocus);
     516                        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
     517                        Object userObject = treeNode.getUserObject();
     518                        if (userObject instanceof LayerDetails) {
     519                                LayerDetails layer = (LayerDetails) userObject;
     520                                setEnabled(layer.isSupported());
     521                        }
     522                        return this;
     523                }
     524        }
    519525
    520526}
Note: See TracChangeset for help on using the changeset viewer.