All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.gov.nasa.worldwindx.examples.kml.KMLViewer Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show newest version
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwindx.examples.kml;

import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.retrieve.RetrievalService;
import gov.nasa.worldwindx.examples.ApplicationTemplate;
import gov.nasa.worldwindx.examples.util.*;
import gov.nasa.worldwind.util.layertree.*;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.ogc.kml.*;
import gov.nasa.worldwind.ogc.kml.impl.KMLController;
import gov.nasa.worldwind.render.Offset;
import gov.nasa.worldwind.util.*;

import javax.swing.*;
import javax.swing.filechooser.*;
import javax.xml.stream.XMLStreamException;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.net.URL;

/**
 * An example application that allows the user to import a KML or KMZ file as a layer. The contents of the file are
 * displayed in a feature tree. Click on KML features in the tree to navigate the view to the feature. Clicking on
 * features on the globe will open an info balloon for the feature, if the feature provides a description. Use the File
 * menu to open a document from a local file or from a URL.
 *
 * @author tag
 * @version $Id: KMLViewer.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class KMLViewer extends ApplicationTemplate
{
    public static class AppFrame extends ApplicationTemplate.AppFrame
    {
        protected LayerTree layerTree;
        protected RenderableLayer hiddenLayer;

        protected HotSpotController hotSpotController;
        protected KMLApplicationController kmlAppController;
        protected BalloonController balloonController;

        public AppFrame()
        {
            super(true, false, false); // Don't include the layer panel; we're using the on-screen layer tree.

            // Add the on-screen layer tree, refreshing model with the WorldWindow's current layer list. We
            // intentionally refresh the tree's model before adding the layer that contains the tree itself. This
            // prevents the tree's layer from being displayed in the tree itself.
            this.layerTree = new LayerTree(new Offset(20d, 160d, AVKey.PIXELS, AVKey.INSET_PIXELS));
            this.layerTree.getModel().refresh(this.getWwd().getModel().getLayers());

            // Set up a layer to display the on-screen layer tree in the WorldWindow. This layer is not displayed in
            // the layer tree's model. Doing so would enable the user to hide the layer tree display with no way of
            // bringing it back.
            this.hiddenLayer = new RenderableLayer();
            this.hiddenLayer.addRenderable(this.layerTree);
            this.getWwd().getModel().getLayers().add(this.hiddenLayer);

            // Add a controller to handle input events on the layer selector and on browser balloons.
            this.hotSpotController = new HotSpotController(this.getWwd());

            // Add a controller to handle common KML application events.
            this.kmlAppController = new KMLApplicationController(this.getWwd());

            // Add a controller to display balloons when placemarks are clicked. We override the method addDocumentLayer
            // so that loading a KML document by clicking a KML balloon link displays an entry in the on-screen layer
            // tree.
            this.balloonController = new BalloonController(this.getWwd())
            {
                @Override
                protected void addDocumentLayer(KMLRoot document)
                {
                    addKMLLayer(document);
                }
            };

            // Give the KML app controller a reference to the BalloonController so that the app controller can open
            // KML feature balloons when feature's are selected in the on-screen layer tree.
            this.kmlAppController.setBalloonController(balloonController);

            // Size the World Window to take up the space typically used by the layer panel.
            Dimension size = new Dimension(1400, 800);
            this.setPreferredSize(size);
            this.pack();
            WWUtil.alignComponent(null, this, AVKey.CENTER);

            makeMenu(this);

            // Set up to receive SSLHandshakeExceptions that occur during resource retrieval.
            WorldWind.getRetrievalService().setSSLExceptionListener(new RetrievalService.SSLExceptionListener()
            {
                public void onException(Throwable e, String path)
                {
                    System.out.println(path);
                    System.out.println(e);
                }
            });
        }

        /**
         * Adds the specified kmlRoot to this app frame's WorldWindow as a new
         * Layer, and adds a new KMLLayerTreeNode for the kmlRoot to this app
         * frame's on-screen layer tree.
         * 

* This expects the kmlRoot's AVKey.DISPLAY_NAME field to contain a display name * suitable for use as a layer name. * * @param kmlRoot the KMLRoot to add a new layer for. */ protected void addKMLLayer(KMLRoot kmlRoot) { // Create a KMLController to adapt the KMLRoot to the World Wind renderable interface. KMLController kmlController = new KMLController(kmlRoot); // Adds a new layer containing the KMLRoot to the end of the WorldWindow's layer list. This // retrieves the layer name from the KMLRoot's DISPLAY_NAME field. RenderableLayer layer = new RenderableLayer(); layer.setName((String) kmlRoot.getField(AVKey.DISPLAY_NAME)); layer.addRenderable(kmlController); this.getWwd().getModel().getLayers().add(layer); // Adds a new layer tree node for the KMLRoot to the on-screen layer tree, and makes the new node visible // in the tree. This also expands any tree paths that represent open KML containers or open KML network // links. KMLLayerTreeNode layerNode = new KMLLayerTreeNode(layer, kmlRoot); this.layerTree.getModel().addLayer(layerNode); this.layerTree.makeVisible(layerNode.getPath()); layerNode.expandOpenContainers(this.layerTree); // Listens to refresh property change events from KML network link nodes. Upon receiving such an event this // expands any tree paths that represent open KML containers. When a KML network link refreshes, its tree // node replaces its children with new nodes created from the refreshed content, then sends a refresh // property change event through the layer tree. By expanding open containers after a network link refresh, // we ensure that the network link tree view appearance is consistent with the KML specification. layerNode.addPropertyChangeListener(AVKey.RETRIEVAL_STATE_SUCCESSFUL, new PropertyChangeListener() { public void propertyChange(final PropertyChangeEvent event) { if (event.getSource() instanceof KMLNetworkLinkTreeNode) { // Manipulate the tree on the EDT. SwingUtilities.invokeLater(new Runnable() { public void run() { ((KMLNetworkLinkTreeNode) event.getSource()).expandOpenContainers(layerTree); getWwd().redraw(); } }); } } }); } } /** A Thread that loads a KML file and displays it in an AppFrame. */ public static class WorkerThread extends Thread { /** Indicates the source of the KML file loaded by this thread. Initialized during construction. */ protected Object kmlSource; /** Indicates the AppFrame the KML file content is displayed in. Initialized during construction. */ protected AppFrame appFrame; /** * Creates a new worker thread from a specified kmlSource and appFrame. * * @param kmlSource the source of the KML file to load. May be a {@link File}, a {@link URL}, or an {@link * java.io.InputStream}, or a {@link String} identifying a file path or URL. * @param appFrame the AppFrame in which to display the KML source. */ public WorkerThread(Object kmlSource, AppFrame appFrame) { this.kmlSource = kmlSource; this.appFrame = appFrame; } /** * Loads this worker thread's KML source into a new {@link gov.nasa.worldwind.ogc.kml.KMLRoot}, * then adds the new KMLRoot to this worker thread's AppFrame. The * KMLRoot's AVKey.DISPLAY_NAME field contains a display name created from either the * KML source or the KML root feature name. *

* If loading the KML source fails, this prints the exception and its stack trace to the standard error stream, * but otherwise does nothing. */ public void run() { try { KMLRoot kmlRoot = this.parse(); // Set the document's display name kmlRoot.setField(AVKey.DISPLAY_NAME, formName(this.kmlSource, kmlRoot)); // Schedule a task on the EDT to add the parsed document to a layer final KMLRoot finalKMLRoot = kmlRoot; SwingUtilities.invokeLater(new Runnable() { public void run() { appFrame.addKMLLayer(finalKMLRoot); } }); } catch (Exception e) { e.printStackTrace(); } } /** * Parse the KML document. * * @return The parsed document. * * @throws IOException if the document cannot be read. * @throws XMLStreamException if document cannot be parsed. */ protected KMLRoot parse() throws IOException, XMLStreamException { // KMLRoot.createAndParse will attempt to parse the document using a namespace aware parser, but if that // fails due to a parsing error it will try again using a namespace unaware parser. Note that this second // step may require the document to be read from the network again if the kmlSource is a stream. return KMLRoot.createAndParse(this.kmlSource); } } protected static String formName(Object kmlSource, KMLRoot kmlRoot) { KMLAbstractFeature rootFeature = kmlRoot.getFeature(); if (rootFeature != null && !WWUtil.isEmpty(rootFeature.getName())) return rootFeature.getName(); if (kmlSource instanceof File) return ((File) kmlSource).getName(); if (kmlSource instanceof URL) return ((URL) kmlSource).getPath(); if (kmlSource instanceof String && WWIO.makeURL((String) kmlSource) != null) return WWIO.makeURL((String) kmlSource).getPath(); return "KML Layer"; } protected static void makeMenu(final AppFrame appFrame) { final JFileChooser fileChooser = new JFileChooser(); fileChooser.setMultiSelectionEnabled(true); fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("KML/KMZ File", "kml", "kmz")); JMenuBar menuBar = new JMenuBar(); appFrame.setJMenuBar(menuBar); JMenu fileMenu = new JMenu("File"); menuBar.add(fileMenu); JMenuItem openFileMenuItem = new JMenuItem(new AbstractAction("Open File...") { public void actionPerformed(ActionEvent actionEvent) { try { int status = fileChooser.showOpenDialog(appFrame); if (status == JFileChooser.APPROVE_OPTION) { for (File file : fileChooser.getSelectedFiles()) { new WorkerThread(file, appFrame).start(); } } } catch (Exception e) { e.printStackTrace(); } } }); fileMenu.add(openFileMenuItem); JMenuItem openURLMenuItem = new JMenuItem(new AbstractAction("Open URL...") { public void actionPerformed(ActionEvent actionEvent) { try { String status = JOptionPane.showInputDialog(appFrame, "URL"); if (!WWUtil.isEmpty(status)) { new WorkerThread(status.trim(), appFrame).start(); } } catch (Exception e) { e.printStackTrace(); } } }); fileMenu.add(openURLMenuItem); } public static void main(String[] args) { //noinspection UnusedDeclaration final AppFrame af = (AppFrame) start("World Wind KML Viewer", AppFrame.class); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy