/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.snap.rcp.layermanager.layersrc.wms; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.rcp.SnapApp; import org.esa.snap.ui.layer.AbstractLayerSourceAssistantPage; import org.esa.snap.ui.layer.LayerSourcePageContext; import org.geotools.data.ows.CRSEnvelope; import org.geotools.data.ows.Layer; import org.geotools.data.ows.StyleImpl; import org.geotools.data.ows.WMSCapabilities; import org.geotools.referencing.CRS; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.border.EmptyBorder; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.WeakHashMap; class WmsAssistantPage2 extends AbstractLayerSourceAssistantPage { private JLabel infoLabel; private JTree layerTree; private CoordinateReferenceSystem modelCRS; WmsAssistantPage2() { super("Select Layer"); } @Override public boolean performFinish() { WmsLayerSource.insertWmsLayer(getContext()); return true; } @Override public AbstractLayerSourceAssistantPage getNextPage() { return new WmsAssistantPage3(); } @Override public boolean hasNextPage() { return true; } @Override public boolean validatePage() { return getContext().getPropertyValue(WmsLayerSource.PROPERTY_NAME_SELECTED_LAYER) != null; } @Override public Component createPageComponent() { JPanel panel = new JPanel(new BorderLayout(4, 4)); panel.setBorder(new EmptyBorder(4, 4, 4, 4)); panel.add(new JLabel("Available layers:"), BorderLayout.NORTH); LayerSourcePageContext context = getContext(); modelCRS = (CoordinateReferenceSystem) context.getLayerContext().getCoordinateReferenceSystem(); WMSCapabilities wmsCapabilities = (WMSCapabilities) context.getPropertyValue( WmsLayerSource.PROPERTY_NAME_WMS_CAPABILITIES); layerTree = new JTree(new WmsTreeModel(wmsCapabilities.getLayer())); layerTree.setRootVisible(false); layerTree.setShowsRootHandles(true); layerTree.setExpandsSelectedPaths(true); layerTree.setCellRenderer(new MyDefaultTreeCellRenderer()); layerTree.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); layerTree.getSelectionModel().addTreeSelectionListener(new LayerTreeSelectionListener()); panel.add(new JScrollPane(layerTree), BorderLayout.CENTER); infoLabel = new JLabel(" "); panel.add(infoLabel, BorderLayout.SOUTH); getContext().setPropertyValue(WmsLayerSource.PROPERTY_NAME_SELECTED_LAYER, null); return panel; } @SuppressWarnings({"unchecked"}) private String getMatchingCRSCode(Layer layer) { Set<String> srsSet = layer.getSrs(); String modelSRS = CRS.toSRS(modelCRS); if (modelSRS != null) { for (String srs : srsSet) { try { final CoordinateReferenceSystem crs = CRS.decode(srs,true); if (CRS.equalsIgnoreMetadata(crs, modelCRS)) { return srs; } } catch (FactoryException ignore) { } } } return null; } static String getLatLonBoundingBoxText(CRSEnvelope bbox) { if (bbox == null) { return "Lon = ?° ... ?°, Lat = ?° ... ?°"; } return String.format("Lon = %.3f° ... %.3f°, Lat = %.3f° ... %.3f°", bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox.getMaxY()); } private static class MyDefaultTreeCellRenderer extends DefaultTreeCellRenderer { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { JLabel label = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); String text; if (value instanceof Layer) { String title; Layer layer = (Layer) value; title = layer.getTitle(); if (title == null) { title = layer.getName(); } if (title == null) { title = layer.toString(); } StringBuilder sb = new StringBuilder(String.format("<html><b>%s</b>", title)); Layer[] children = layer.getChildren(); if (children.length > 1) { sb.append(String.format(" (%d children)", children.length)); } else if (children.length == 1) { sb.append(" (1 child)"); } text = sb.append("</html>").toString(); } else if (value instanceof WMSCapabilities) { WMSCapabilities capabilities = (WMSCapabilities) value; text = String.format("<html><b>%s</b></html>", capabilities.getService().getName()); } else { text = String.format("<html><b>%s</b></html>", value); } label.setText(text); return label; } } private class LayerTreeSelectionListener implements TreeSelectionListener { @Override public void valueChanged(TreeSelectionEvent e) { LayerSourcePageContext context = getContext(); TreePath selectedLayerPath = layerTree.getSelectionModel().getSelectionPath(); Layer selectedLayer = (Layer) selectedLayerPath.getLastPathComponent(); if (selectedLayer != null) { String crsCode = getMatchingCRSCode(selectedLayer); if (crsCode == null) { infoLabel.setForeground(Color.RED.darker()); infoLabel.setText("Coordinate system not supported."); } else { RasterDataNode raster = SnapApp.getDefault().getSelectedProductSceneView().getRaster(); AffineTransform g2mTransform = Product.findImageToModelTransform(raster.getGeoCoding()); Rectangle2D bounds = g2mTransform.createTransformedShape( new Rectangle(0, 0, raster.getRasterWidth(), raster.getRasterHeight())).getBounds2D(); CRSEnvelope crsEnvelope = new CRSEnvelope(crsCode, bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY()); context.setPropertyValue(WmsLayerSource.PROPERTY_NAME_CRS_ENVELOPE, crsEnvelope); List<StyleImpl> styles = selectedLayer.getStyles(); if (!styles.isEmpty()) { context.setPropertyValue(WmsLayerSource.PROPERTY_NAME_SELECTED_STYLE, styles.get(0)); } else { context.setPropertyValue(WmsLayerSource.PROPERTY_NAME_SELECTED_STYLE, null); } context.setPropertyValue(WmsLayerSource.PROPERTY_NAME_SELECTED_LAYER, selectedLayer); infoLabel.setForeground(Color.DARK_GRAY); infoLabel.setText(getLatLonBoundingBoxText(selectedLayer.getLatLonBoundingBox())); } } else { infoLabel.setForeground(Color.DARK_GRAY); infoLabel.setText(""); } context.updateState(); } } private static class WmsTreeModel implements TreeModel { private final WeakHashMap<TreeModelListener, Object> treeModelListeners; private Layer rootLayer; private WmsTreeModel(Layer rootLayer) { this.rootLayer = rootLayer; treeModelListeners = new WeakHashMap<>(); } @Override public Object getRoot() { return rootLayer; } @Override public Object getChild(Object parent, int index) { Layer layer = (Layer) parent; return layer.getChildren()[index]; } @Override public int getChildCount(Object parent) { Layer layer = (Layer) parent; return layer.getChildren().length; } @Override public boolean isLeaf(Object node) { Layer layer = (Layer) node; return layer.getChildren() != null && layer.getChildren().length == 0; } @Override public int getIndexOfChild(Object parent, Object child) { Layer layer = (Layer) parent; int index = Arrays.binarySearch(layer.getChildren(), child); return index < 0 ? -1 : index; } @Override public void valueForPathChanged(TreePath path, Object newValue) { fireTreeNodeChanged(path); } @Override public void addTreeModelListener(TreeModelListener l) { treeModelListeners.put(l, ""); } @Override public void removeTreeModelListener(TreeModelListener l) { treeModelListeners.remove(l); } protected void fireTreeNodeChanged(TreePath treePath) { TreeModelEvent event = new TreeModelEvent(this, treePath); for (TreeModelListener treeModelListener : treeModelListeners.keySet()) { treeModelListener.treeNodesChanged(event); } } } }