/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.examples;
import gov.nasa.worldwind.WorldWindow;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.wms.*;
import org.w3c.dom.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.xml.parsers.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
/**
* @author tag
* @version $Id: WMSLayersPanel.java 4231 2008-01-17 06:02:04Z tgaskins $
*/
public class WMSLayersPanel extends JPanel
{
private static class LayerInfo
{
private Capabilities caps;
private AVListImpl params = new AVListImpl();
private String getTitle()
{
return params.getStringValue(AVKey.TITLE);
}
}
private final WorldWindow wwd;
private final URI serverURI;
private final Dimension size;
private final Thread loadingThread;
private final TreeSet<LayerInfo> layerInfos = new TreeSet<LayerInfo>(new Comparator<LayerInfo>()
{
public int compare(LayerInfo infoA, LayerInfo infoB)
{
String nameA = infoA.getTitle();
String nameB = infoB.getTitle();
return nameA.compareTo(nameB);
}
});
public WMSLayersPanel(WorldWindow wwd, String server, Dimension size) throws URISyntaxException
{
super(new BorderLayout());
// See if the server name is a valid URI. Throw an exception if not.
this.serverURI = new URI(server.trim()); // throws an exception if server name is not a valid uri.
this.wwd = wwd;
this.size = size;
this.setPreferredSize(this.size);
this.makeProgressPanel();
// Thread off a retrieval of the server's capabilities document and update of this panel.
this.loadingThread = new Thread(new Runnable()
{
public void run()
{
load();
}
});
this.loadingThread.setPriority(Thread.MIN_PRIORITY);
this.loadingThread.start();
}
private void load()
{
Capabilities caps;
try
{
// Retrieve the server's capabilities document and parse it into a DOM.
// Set up the DOM.
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc;
// Request the capabilities document from the server.
CapabilitiesRequest req = new CapabilitiesRequest(serverURI);
doc = docBuilder.parse(req.toString());
// Parse the DOM as a capabilities document.
caps = Capabilities.parse(doc);
}
catch (Exception e)
{
e.printStackTrace();
return;
}
// Gather up all the named layers and make a world wind layer for each.
final Element[] namedLayerCaps = caps.getNamedLayers();
if (namedLayerCaps == null)
return;
try
{
for (Element layerCaps : namedLayerCaps)
{
Element[] styles = caps.getLayerStyles(layerCaps);
if (styles == null)
{
LayerInfo layerInfo = createLayerInfo(caps, layerCaps, null);
WMSLayersPanel.this.layerInfos.add(layerInfo);
}
else
{
for (Element style : styles)
{
LayerInfo layerInfo = createLayerInfo(caps, layerCaps, style);
WMSLayersPanel.this.layerInfos.add(layerInfo);
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
return;
}
// Fill the panel with the layer titles.
EventQueue.invokeLater(new Runnable()
{
public void run()
{
WMSLayersPanel.this.removeAll();
makeLayerInfosPanel(layerInfos);
}
});
}
public String getServerDisplayString()
{
return this.serverURI.getHost();
}
private LayerInfo createLayerInfo(Capabilities caps, Element layerCaps, Element style)
{
// Create the layer info specified by the layer's capabilities entry and the selected style.
LayerInfo linfo = new LayerInfo();
linfo.caps = caps;
linfo.params = new AVListImpl();
linfo.params.setValue(AVKey.LAYER_NAMES, caps.getLayerName(layerCaps));
if (style != null)
linfo.params.setValue(AVKey.STYLE_NAMES, caps.getStyleName(layerCaps, style));
linfo.params.setValue(AVKey.TITLE, makeTitle(linfo));
return linfo;
}
private void makeLayerInfosPanel(Collection<LayerInfo> layerInfos)
{
// Create the panel holding the layer names.
JPanel layersPanel = new JPanel(new GridLayout(0, 1, 0, 15));
layersPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// Add the server's layers to the panel.
for (LayerInfo layerInfo : layerInfos)
{
addLayerInfoPanel(layersPanel, WMSLayersPanel.this.wwd, layerInfo);
}
// Put the name panel in a scroll bar.
JScrollPane scrollPane = new JScrollPane(layersPanel);
scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
scrollPane.setPreferredSize(size);
// Add the scroll bar and name panel to a titled panel that will resize with the main window.
JPanel westPanel = new JPanel(new GridLayout(0, 1, 0, 10));
westPanel.setBorder(
new CompoundBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9), new TitledBorder("Layers")));
westPanel.add(scrollPane);
this.add(westPanel, BorderLayout.CENTER);
this.revalidate();
}
private void addLayerInfoPanel(JPanel layersPanel, WorldWindow wwd, LayerInfo linfo)
{
// Give a layer a button and label and add it to the layer names panel.
LayerInfoAction action = new LayerInfoAction(linfo, wwd);
JCheckBox jcb = new JCheckBox(action);
jcb.setSelected(false);
layersPanel.add(jcb);
}
private class LayerInfoAction extends AbstractAction
{
private WorldWindow wwd;
private LayerInfo layerInfo;
private Layer layer;
private String restorableState;
public LayerInfoAction(LayerInfo linfo, WorldWindow wwd)
{
super(linfo.getTitle());
// Capture info we'll need later to control the layer.
this.wwd = wwd;
this.layerInfo = linfo;
}
public void actionPerformed(ActionEvent actionEvent)
{
// If the layer is selected, add it to the world window's current model, else remove it from the model.
if (((JCheckBox) actionEvent.getSource()).isSelected())
{
if (this.restorableState == null)
{
this.layer = new WMSTiledImageLayer(layerInfo.caps, layerInfo.params);
this.layer.setEnabled(false);
this.layer.setEnabled(true);
// Some wms servers are slow, so increase the timeouts and limits used by world wind's retrievers.
this.layer.setValue(AVKey.URL_CONNECT_TIMEOUT, 30000);
this.layer.setValue(AVKey.URL_READ_TIMEOUT, 30000);
this.layer.setValue(AVKey.RETRIEVAL_QUEUE_STALE_REQUEST_LIMIT, 60000);
ApplicationTemplate.insertBeforePlacenames(this.wwd, layer);
WMSLayersPanel.this.firePropertyChange("LayersPanelUpdated", null, layer);
this.restorableState = this.layer.getRestorableState();
}
else
{
this.layer = WMSTiledImageLayer.fromRestorableState(this.restorableState);
ApplicationTemplate.insertBeforePlacenames(this.wwd, layer);
WMSLayersPanel.this.firePropertyChange("LayersPanelUpdated", null, layer);
}
}
else
{
this.layer.setEnabled(false);
this.wwd.getModel().getLayers().remove(layer);
WMSLayersPanel.this.firePropertyChange("LayersPanelUpdated", layer, null);
}
// Tell the world window to update.
wwd.redraw();
}
}
private static String makeTitle(LayerInfo layerInfo)
{
String layerNames = layerInfo.params.getStringValue(AVKey.LAYER_NAMES);
String styleNames = layerInfo.params.getStringValue(AVKey.STYLE_NAMES);
String[] lNames = layerNames.split(",");
String[] sNames = styleNames != null ? styleNames.split(",") : null;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lNames.length; i++)
{
if (sb.length() > 0)
sb.append(", ");
String layerName = lNames[i];
Element layer = layerInfo.caps.getLayerByName(layerName);
String layerTitle = layerInfo.caps.getLayerTitle(layer);
sb.append(layerTitle != null ? layerTitle : layerName);
if (sNames == null || sNames.length <= i)
continue;
String styleName = sNames[i];
Element style = layerInfo.caps.getLayerStyleByName(layer, styleName);
if (style == null)
continue;
sb.append(" : ");
String styleTitle = layerInfo.caps.getStyleTitle(layer, style);
sb.append(styleTitle != null ? styleTitle : styleName);
}
return sb.toString();
}
private void makeProgressPanel()
{
// Create the panel holding the progress bar during loading.
JPanel outerPanel = new JPanel(new BorderLayout());
outerPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
outerPanel.setPreferredSize(this.size);
JPanel innerPanel = new JPanel(new BorderLayout());
innerPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
innerPanel.add(progressBar, BorderLayout.CENTER);
JButton cancelButton = new JButton("Cancel");
innerPanel.add(cancelButton, BorderLayout.EAST);
cancelButton.addActionListener(new AbstractAction()
{
public void actionPerformed(ActionEvent actionEvent)
{
if (loadingThread.isAlive())
loadingThread.interrupt();
Container c = WMSLayersPanel.this.getParent();
c.remove(WMSLayersPanel.this);
}
});
outerPanel.add(innerPanel, BorderLayout.NORTH);
this.add(outerPanel, BorderLayout.CENTER);
this.revalidate();
}
}