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

src.gov.nasa.worldwindx.examples.lineofsight.LinesOfSight 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.lineofsight;

import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwindx.examples.*;
import gov.nasa.worldwind.formats.shapefile.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.terrain.*;
import gov.nasa.worldwind.util.VecBuffer;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.filechooser.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.*;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Computes and displays line-of-sight intersections for terrain and renderables. Uses a {@link Terrain} object in order
 * to determine accurate intersections relative to the highest-resolution elevation data associated with a specified
 * globe.
 * 

* This class uses a {@link gov.nasa.worldwindx.examples.lineofsight.TerrainLineIntersector} and a {@link * gov.nasa.worldwindx.examples.lineofsight.ShapeLineIntersector} to compute the intersections. *

* Usage:
Shift-click: Calculate lines of sight for a position.
Ctrl-click: Cancel the running * computation.
Alt-click: Re-run the most recent line of sight calculation. * * @author tag * @version $Id: LinesOfSight.java 1171 2013-02-11 21:45:02Z dcollins $ */ public class LinesOfSight extends ApplicationTemplate { /** The width and height in degrees of the grid used to calculate intersections. */ protected static final Angle GRID_RADIUS = Angle.fromDegrees(0.005); /** The number of cells along each edge of the grid. */ protected static final int GRID_DIMENSION = 10; // cells per side protected static final int REFERENCE_POSITION_HEIGHT = 5; protected static final int GRID_POSITION_HEIGHT = 1; /** The desired terrain resolution to use in the intersection calculations. */ protected static final Double TARGET_RESOLUTION = 20d; // meters, or null for globe's highest resolution protected static final int NUM_TERRAIN_THREADS = 1; // set to 1 to run terrain intersections synchronously protected static final int NUM_SHAPE_THREADS = 1; // set to 1 to run shape intersections synchronously /** The size of the Terrain's cache. * */ protected static final long CACHE_SIZE = (long) 150e6; protected static final boolean SHOW_ONLY_FIRST_INTERSECTIONS = true; public static class AppFrame extends ApplicationTemplate.AppFrame { private static final Cursor WaitCursor = new Cursor(Cursor.WAIT_CURSOR); protected HighResolutionTerrain terrain; protected TerrainLineIntersector terrainIntersector; protected ShapeLineIntersector shapeIntersector; protected RenderableLayer gridLayer; protected RenderableLayer intersectionsLayer; protected RenderableLayer sightLinesLayer; protected RenderableLayer tilesLayer; protected Thread calculationDispatchThread; protected JProgressBar progressBar; protected java.util.List grid; protected Position referencePosition; protected Vec4 referencePoint; protected long startTime, endTime; // for reporting calculation duration protected Position previousCurrentPosition; protected java.util.Timer updateProgressTimer; protected AtomicInteger numPositionsProcessed = new AtomicInteger(); protected RenderableLayer renderableLayer = new RenderableLayer(); protected ShapeAttributes sightLineAttributes; protected PointPlacemarkAttributes intersectionPointAttributes; protected PointPlacemarkAttributes gridPointAttributes; protected PointPlacemarkAttributes selectedLocationAttributes; public AppFrame() { super(true, true, false); this.makeMenu(); this.updateProgressTimer = new java.util.Timer(); // Display a progress bar. this.progressBar = new JProgressBar(0, 100); this.progressBar.setBorder(new EmptyBorder(0, 10, 0, 10)); this.progressBar.setBorderPainted(false); this.progressBar.setStringPainted(true); this.layerPanel.add(this.progressBar, BorderLayout.SOUTH); // Create the layer showing the grid. this.gridLayer = new RenderableLayer(); this.gridLayer.setName("Grid"); this.getWwd().getModel().getLayers().add(this.gridLayer); // Create the layer showing the intersections. this.intersectionsLayer = new RenderableLayer(); this.intersectionsLayer.setName("Intersections"); this.getWwd().getModel().getLayers().add(this.intersectionsLayer); // Create the layer showing the sight lines. this.sightLinesLayer = new RenderableLayer(); this.sightLinesLayer.setName("Sight Lines"); this.getWwd().getModel().getLayers().add(this.sightLinesLayer); getLayerPanel().update(getWwd()); // Create a Terrain object that uses high-resolution elevation data to compute intersections. this.terrain = new HighResolutionTerrain(this.getWwd().getModel().getGlobe(), TARGET_RESOLUTION); this.terrain.setCacheCapacity(CACHE_SIZE); // larger cache speeds up repeat calculations // Create the intersectors for terrain and shapes. this.terrainIntersector = new TerrainLineIntersector(this.terrain, NUM_TERRAIN_THREADS); this.shapeIntersector = new ShapeLineIntersector(this.terrain, NUM_SHAPE_THREADS); // Set up a mouse handler to generate a grid and start intersection calculations when the user shift-clicks. this.getWwd().getInputHandler().addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent mouseEvent) { // Control-Click cancels any currently running operation. if ((mouseEvent.getModifiers() & ActionEvent.CTRL_MASK) != 0) { if (calculationDispatchThread != null && calculationDispatchThread.isAlive()) calculationDispatchThread.interrupt(); return; } // Alt-Click repeats the most recent calculations. if ((mouseEvent.getModifiers() & ActionEvent.ALT_MASK) != 0) { if (previousCurrentPosition == null) return; mouseEvent.consume(); // tell the rest of WW that this event has been processed computeAndShow(previousCurrentPosition); return; } // Perform the intersection tests in response to Shift-Click. if ((mouseEvent.getModifiers() & ActionEvent.SHIFT_MASK) == 0) return; mouseEvent.consume(); // tell the rest of WW that this event has been processed final Position pos = getWwd().getCurrentPosition(); if (pos == null) return; computeAndShow(pos); } }); // Display the grid points in yellow. this.gridPointAttributes = new PointPlacemarkAttributes(); this.gridPointAttributes.setLineMaterial(Material.YELLOW); this.gridPointAttributes.setScale(6d); this.gridPointAttributes.setUsePointAsDefaultImage(true); // Display the center point in red. this.selectedLocationAttributes = new PointPlacemarkAttributes(); this.selectedLocationAttributes.setLineMaterial(Material.RED); this.selectedLocationAttributes.setScale(8d); this.selectedLocationAttributes.setUsePointAsDefaultImage(true); // Display the sight lines as green lines. this.sightLineAttributes = new BasicShapeAttributes(); this.sightLineAttributes.setDrawOutline(true); this.sightLineAttributes.setDrawInterior(false); this.sightLineAttributes.setOutlineMaterial(Material.GREEN); this.sightLineAttributes.setOutlineOpacity(0.6); // Display the intersections as CYAN points. this.intersectionPointAttributes = new PointPlacemarkAttributes(); this.intersectionPointAttributes.setLineMaterial(Material.CYAN); this.intersectionPointAttributes.setScale(10d); this.intersectionPointAttributes.setUsePointAsDefaultImage(true); } protected void computeAndShow(final Position curPos) { this.previousCurrentPosition = curPos; SwingUtilities.invokeLater(new Runnable() { public void run() { setCursor(WaitCursor); } }); // Dispatch the calculation threads in a separate thread to avoid locking up the user interface. this.calculationDispatchThread = new Thread(new Runnable() { public void run() { try { performIntersectionTests(curPos); } catch (InterruptedException e) { System.out.println("Operation was interrupted"); } catch (Exception e) { e.printStackTrace(); } } }); this.calculationDispatchThread.start(); } protected void performIntersectionTests(final Position curPos) throws InterruptedException { // Compute the position of the selected location (incorporate its height). this.referencePosition = new Position(curPos.getLatitude(), curPos.getLongitude(), REFERENCE_POSITION_HEIGHT); this.referencePoint = this.terrain.getSurfacePoint(this.referencePosition); // Form the grid. Sector sector = this.computeGridSector(curPos, GRID_RADIUS.degrees); this.grid = buildGrid(sector, GRID_POSITION_HEIGHT, GRID_DIMENSION, GRID_DIMENSION); // Set the line definitions. this.terrainIntersector.setReferencePosition(this.referencePosition); this.terrainIntersector.setPositions(this.grid); // Add the renderables, if any, to the shape intersector. if (this.renderableLayer.getNumRenderables() > 0) { this.shapeIntersector.setReferencePosition(this.referencePosition); this.shapeIntersector.setPositions(this.grid); this.shapeIntersector.setRenderables(this.renderableLayer.getRenderables()); } // On the EDT, show the grid. SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(0); progressBar.setString(null); clearLayers(); showGrid(grid, referencePosition); getWwd().redraw(); } }); if (this.updateProgressTimer != null) this.updateProgressTimer.cancel(); this.updateProgressTimer = new java.util.Timer(); this.updateProgressTimer.schedule(new TimerTask() { public void run() { updateProgress(); } }, 500, 250); // Perform the intersection calculations. this.startTime = System.currentTimeMillis(); this.computeIntersections(); } protected void clearLayers() { this.intersectionsLayer.removeAllRenderables(); this.sightLinesLayer.removeAllRenderables(); this.gridLayer.removeAllRenderables(); } protected void computeIntersections() { Thread terrainThread = new Thread(this.terrainIntersector); terrainThread.start(); if (this.shapeIntersector.hasRenderables()) { Thread shapeThread = new Thread(this.shapeIntersector); shapeThread.start(); } } protected Sector computeGridSector(Position curPos, double gridRadius) { return Sector.fromDegrees( curPos.getLatitude().degrees - gridRadius, curPos.getLatitude().degrees + gridRadius, curPos.getLongitude().degrees - gridRadius, curPos.getLongitude().degrees + gridRadius); } protected List buildGrid(Sector sector, double height, int nRows, int nCols) { java.util.List grid = new ArrayList((nRows) * (nCols)); double dLat = sector.getDeltaLatDegrees() / (nCols - 1); double dLon = sector.getDeltaLonDegrees() / (nRows - 1); for (int j = 0; j < nRows; j++) { double lat = j == nRows - 1 ? sector.getMaxLatitude().degrees : sector.getMinLatitude().degrees + j * dLat; for (int i = 0; i < nCols; i++) { double lon = i == nCols - 1 ? sector.getMaxLongitude().degrees : sector.getMinLongitude().degrees + i * dLon; grid.add(Position.fromDegrees(lat, lon, height)); } } return grid; } /** Keeps the progress meter current. When calculations are complete, displays the results. */ protected synchronized void updateProgress() { int totalNum = this.grid.size(); int numPositionsProcessed = this.terrainIntersector.getNumProcessedPositions(); if (this.renderableLayer.getNumRenderables() > 0) { totalNum += this.grid.size(); numPositionsProcessed += this.shapeIntersector.getNumProcessedPositions(); } final int progress = (int) (100d * numPositionsProcessed / (double) totalNum); // On the EDT, update the progress bar and if calculations are complete, update the World Window. SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(progress); if (progress >= 100) { endTime = System.currentTimeMillis(); updateProgressTimer.cancel(); updateProgressTimer = null; setCursor(Cursor.getDefaultCursor()); progressBar.setString((endTime - startTime) + " ms"); showResults(); System.out.printf("Calculation time %d milliseconds\n", endTime - startTime); } } }); } /** Updates the World Wind model with the new intersection locations and sight lines. */ protected void showResults() { this.intersectionsLayer.removeAllRenderables(); this.sightLinesLayer.removeAllRenderables(); for (Position position : this.grid) { this.showIntersectionsForPosition(position); } this.getWwd().redraw(); } protected void showIntersectionsForPosition(Position position) { List tIntersections = this.terrainIntersector.getIntersections(position); List sIntersections = this.shapeIntersector.getIntersections(position); if (tIntersections == null && sIntersections == null) { this.showNonIntersection(position); return; } Queue sortedIntersections = Intersection.sort(this.referencePoint, tIntersections, sIntersections); if (sortedIntersections.size() == 0) { this.showSightLine(position); } else if (SHOW_ONLY_FIRST_INTERSECTIONS) { this.showSightLine(sortedIntersections.peek().getIntersectionPosition()); this.showIntersection(sortedIntersections.peek()); } else { this.showSightLine(position); this.showIntersections(sortedIntersections); } } protected void showSightLine(Position position) { Position refPosAbsolute = this.referencePosition; Path path = new Path(refPosAbsolute, position); path.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); path.setAttributes(this.sightLineAttributes); this.sightLinesLayer.addRenderable(path); } protected void showIntersection(Intersection losi) { PointPlacemark pm = new PointPlacemark(losi.getIntersectionPosition()); pm.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); pm.setAttributes(this.intersectionPointAttributes); pm.setValue(AVKey.DISPLAY_NAME, losi.getIntersectionPosition().toString()); this.intersectionsLayer.addRenderable(pm); } protected void showIntersections(Queue intersections) { for (Intersection losi : intersections) { PointPlacemark pm = new PointPlacemark(losi.getIntersectionPosition()); pm.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); pm.setAttributes(this.intersectionPointAttributes); pm.setValue(AVKey.DISPLAY_NAME, losi.getIntersectionPosition().toString()); this.intersectionsLayer.addRenderable(pm); } } protected void showNonIntersection(Position position) { Path path = new Path(this.referencePosition, position); path.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); path.setAttributes(this.sightLineAttributes); this.sightLinesLayer.addRenderable(path); } protected void showNonIntersections(Collection positions) { for (Position pos : positions) { Path path = new Path(this.referencePosition, pos); path.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); path.setAttributes(this.sightLineAttributes); this.sightLinesLayer.addRenderable(path); } } protected void showGrid(java.util.List grid, Position cPos) { this.gridLayer.removeAllRenderables(); // Display the grid points in yellow. PointPlacemarkAttributes gridPointAttributes; gridPointAttributes = new PointPlacemarkAttributes(); gridPointAttributes.setLineMaterial(Material.YELLOW); gridPointAttributes.setScale(6d); gridPointAttributes.setUsePointAsDefaultImage(true); for (Position p : grid) { PointPlacemark pm = new PointPlacemark(p); pm.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); pm.setAttributes(this.gridPointAttributes); pm.setLineEnabled(true); pm.setValue(AVKey.DISPLAY_NAME, p.toString()); this.gridLayer.addRenderable(pm); } showCenterPoint(cPos); } protected void showCenterPoint(Position cPos) { // Display the center point in red. PointPlacemarkAttributes selectedLocationAttributes; selectedLocationAttributes = new PointPlacemarkAttributes(); selectedLocationAttributes.setLineMaterial(Material.RED); selectedLocationAttributes.setScale(8d); selectedLocationAttributes.setUsePointAsDefaultImage(true); PointPlacemark pm = new PointPlacemark(cPos); pm.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); pm.setAttributes(this.selectedLocationAttributes); pm.setValue(AVKey.DISPLAY_NAME, cPos.toString()); pm.setLineEnabled(true); this.gridLayer.addRenderable(pm); } /** Makes the menu for loading shapes from shapefiles. */ protected void makeMenu() { final JFileChooser fileChooser = new JFileChooser(); fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("ESRI Shapefiles", "shp")); JMenuBar menuBar = new JMenuBar(); this.setJMenuBar(menuBar); JMenu fileMenu = new JMenu("File"); menuBar.add(fileMenu); JMenuItem openMenuItem = new JMenuItem(new AbstractAction("Open File...") { public void actionPerformed(ActionEvent actionEvent) { try { int status = fileChooser.showOpenDialog(AppFrame.this); if (status == JFileChooser.APPROVE_OPTION) { Thread t = new ShapeLoaderThread(fileChooser.getSelectedFile(), getWwd(), renderableLayer, AppFrame.this.layerPanel); t.start(); } } catch (Exception e) { e.printStackTrace(); } } }); fileMenu.add(openMenuItem); } } public static class ShapeLoaderThread extends Thread { protected File file; protected WorldWindow wwd; protected LayerPanel layerPanel; protected RenderableLayer layer; protected ShapeAttributes buildingAttributes; public ShapeLoaderThread(File file, WorldWindow wwd, RenderableLayer layer, LayerPanel layerPanel) { this.file = file; this.wwd = wwd; this.layer = layer; this.layerPanel = layerPanel; // Display the buildings slightly transparent. this.buildingAttributes = new BasicShapeAttributes(); this.buildingAttributes.setDrawOutline(true); this.buildingAttributes.setDrawInterior(true); this.buildingAttributes.setInteriorMaterial(Material.LIGHT_GRAY); this.buildingAttributes.setInteriorOpacity(0.4); } public void run() { Shapefile sf = new Shapefile(this.file); try { while (sf.hasNext()) { ShapefileRecord r = sf.nextRecord(); if (r == null) continue; if (r.getNumberOfPoints() < 4) continue; this.layer.addRenderable(this.makeShape(r)); } } finally { sf.close(); } SwingUtilities.invokeLater(new Runnable() { public void run() { insertBeforePlacenames(wwd, layer); layerPanel.update(wwd); } }); } protected ExtrudedPolygon makeShape(ShapefileRecord record) { Double height = null; Object o = record.getAttributes().getValue("Height"); if (o != null) { height = Double.parseDouble(o.toString()); } ExtrudedPolygon pgon = new ExtrudedPolygon(); pgon.setSideAttributes(this.buildingAttributes); pgon.setCapAttributes(this.buildingAttributes); VecBuffer vb = record.getPointBuffer(0); pgon.setOuterBoundary(vb.getLocations(), height); return pgon; } } public static void main(String[] args) { // Adjust configuration values before instantiation ApplicationTemplate.start("World Wind Terrain Intersections", AppFrame.class); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy