/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools 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.jgrasstools.nww.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.Border;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.jgrasstools.nww.layers.defaults.raster.OSMMapnikLayer;
import org.jgrasstools.nww.utils.NwwUtilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gov.nasa.worldwind.Model;
import gov.nasa.worldwind.View;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.WorldWindow;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.awt.WorldWindowGLCanvas;
import gov.nasa.worldwind.awt.WorldWindowGLJPanel;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.Box;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.globes.Earth;
import gov.nasa.worldwind.globes.EarthFlat;
import gov.nasa.worldwind.globes.GeographicProjection;
import gov.nasa.worldwind.globes.projections.ProjectionEquirectangular;
import gov.nasa.worldwind.globes.projections.ProjectionMercator;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.layers.LayerList;
import gov.nasa.worldwind.terrain.ZeroElevationModel;
import gov.nasa.worldwind.view.orbit.OrbitView;
import gov.nasa.worldwind.view.orbit.OrbitViewLimits;
/**
* The main NWW panel.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
@SuppressWarnings("serial")
public class NwwPanel extends JPanel {
private static final Logger logger = LoggerFactory.getLogger(NwwPanel.class);
private WorldWindow wwd;
protected StatusBar statusBar;
private double lastElevation = Double.NaN;
public static Component createNwwPanel( boolean useWwGlCanvas ) {
try {
return new NwwPanel(useWwGlCanvas);
} catch (UnsatisfiedLinkError ule) {
logger.error("error", ule);
String msg = "<html><b><font color=red size=+1>";
msg += "<p>An error occurred while loading the native NWW libraries,</p>";
msg += "<p>check your installation.</p><p></p>";
msg += "<i><p>The error is: </p><p>" + ule.getMessage();
msg += "</p></i></font></b>";
JLabel errorLabel = new JLabel(msg);
Border paddingBorder = BorderFactory.createEmptyBorder(10, 10, 10, 10);
errorLabel.setBorder(paddingBorder);
return errorLabel;
} catch (Exception e) {
logger.error("error", e);
}
return null;
}
protected NwwPanel( boolean useWwGlCanvas ) {
super(new BorderLayout());
// Configuration.setValue(AVKey.INITIAL_LATITUDE, gpsLogShps[0].y);
// Configuration.setValue(AVKey.INITIAL_LONGITUDE, gpsLogShps[0].x);
// Configuration.setValue(AVKey.INITIAL_ALTITUDE, 1000);
// Configuration.setValue(AVKey.INITIAL_PITCH, 45);
if (useWwGlCanvas) {
wwd = new WorldWindowGLCanvas();
} else {
wwd = new WorldWindowGLJPanel();
}
((Component) wwd).setPreferredSize(new Dimension(500, 500));
Model model = (Model) WorldWind.createConfigurationComponent(AVKey.MODEL_CLASS_NAME);
this.getWwd().setModel(model);
LayerList layers = model.getLayers();
List<Layer> addBack = new ArrayList<>();
Iterator<Layer> layerIterator = layers.iterator();
List<String> namesToKeep = NwwUtilities.LAYERS_TO_KEEP_FROM_ORIGNALNWW;
while( layerIterator.hasNext() ) {
Layer layer = layerIterator.next();
if (namesToKeep.contains(layer.getName())) {
addBack.add(layer);
}
}
layers.clear();
layers.addAll(addBack);
this.add((Component) this.getWwd(), BorderLayout.CENTER);
this.statusBar = new StatusBar();
this.add(statusBar, BorderLayout.PAGE_END);
this.statusBar.setEventSource(getWwd());
}
public ViewControlsLayer addViewControls( double scale, boolean showZoomControls, boolean showPanControls,
boolean showheadingControls, boolean showPitchControls, boolean showVerticalExaggerationControls ) {
ViewControlsLayer viewControlsLayer = new ViewControlsLayer();
viewControlsLayer.setScale(scale);
viewControlsLayer.setShowZoomControls(showZoomControls);
viewControlsLayer.setShowPanControls(showPanControls);
viewControlsLayer.setShowHeadingControls(showheadingControls);
viewControlsLayer.setShowPitchControls(showPitchControls);
viewControlsLayer.setShowVeControls(showVerticalExaggerationControls);
addLayer(viewControlsLayer);
getWwd().addSelectListener(new ViewControlsSelectListener(getWwd(), viewControlsLayer));
return viewControlsLayer;
}
public ViewControlsLayer addViewControls() {
return addViewControls(2, true, false, true, false, false);
}
public void addOsmLayer() {
addLayer(new OSMMapnikLayer());
}
public void setZoomLimits( double minZoom, double maxZoom ) {
View view = getWwd().getView();
if (view != null && view instanceof OrbitView) {
OrbitView oView = (OrbitView) view;
OrbitViewLimits orbitViewLimits = oView.getOrbitViewLimits();
orbitViewLimits.setZoomLimits(minZoom, maxZoom);
}
}
public void setPitchLimits( Angle minAngle, Angle maxangle ) {
View view = getWwd().getView();
if (view != null && view instanceof OrbitView) {
OrbitView oView = (OrbitView) view;
OrbitViewLimits orbitViewLimits = oView.getOrbitViewLimits();
orbitViewLimits.setPitchLimits(minAngle, maxangle);
}
}
/**
* Move to a given location.
*
* @param lon
* the longitude.
* @param lat
* the latitude.
* @param elev
* the eye elevation.
* @param azimuth
* if supplied, the map is rotated to follow that angle.
* @param animate
* if <code>true</code>, it animates to the position.
*/
public synchronized Position goTo( Double lon, Double lat, Double elev, Double azimuth, boolean animate ) {
View view = getWwd().getView();
view.stopAnimations();
view.stopMovement();
Position eyePosition;
if (lon == null || lat == null) {
Position currentEyePosition = wwd.getView().getCurrentEyePosition();
if (currentEyePosition != null) {
lat = currentEyePosition.latitude.degrees;
lon = currentEyePosition.longitude.degrees;
} else {
return null;
}
}
if (elev == null) {
// use the current
elev = wwd.getView().getCurrentEyePosition().getAltitude();
}
if (Double.isNaN(elev)) {
if (!Double.isNaN(lastElevation)) {
elev = lastElevation;
} else {
elev = NwwUtilities.DEFAULT_ELEV;
}
}
eyePosition = NwwUtilities.toPosition(lat, lon, elev);
if (animate) {
view.goTo(eyePosition, elev);
} else {
view.setEyePosition(eyePosition);
}
if (azimuth != null) {
Angle heading = Angle.fromDegrees(azimuth);
view.setHeading(heading);
}
lastElevation = elev;
return eyePosition;
}
/**
* Move to see a given sector.
*
* @param sector
* the sector to go to.
* @param animate
* if <code>true</code>, it animates to the position.
*/
public void goTo( Sector sector, boolean animate ) {
View view = getWwd().getView();
view.stopAnimations();
view.stopMovement();
if (sector == null) {
return;
}
// Create a bounding box for the specified sector in order to estimate
// its size in model coordinates.
Box extent = Sector.computeBoundingBox(getWwd().getModel().getGlobe(),
getWwd().getSceneController().getVerticalExaggeration(), sector);
// Estimate the distance between the center position and the eye
// position that is necessary to cause the sector to
// fill a viewport with the specified field of view. Note that we change
// the distance between the center and eye
// position here, and leave the field of view constant.
Angle fov = view.getFieldOfView();
double zoom = extent.getRadius() / fov.cosHalfAngle() / fov.tanHalfAngle();
// Configure OrbitView to look at the center of the sector from our
// estimated distance. This causes OrbitView to
// animate to the specified position over several seconds. To affect
// this change immediately use the following:
if (animate) {
view.goTo(new Position(sector.getCentroid(), 0d), zoom);
} else {
((OrbitView) wwd.getView()).setCenterPosition(new Position(sector.getCentroid(), 0d));
((OrbitView) wwd.getView()).setZoom(zoom);
}
}
/**
* Set the globe as flat.
*
* @param doMercator
* if <code>true</code>, mercator is used as opposed to lat/long.
*/
public void setFlatGlobe( boolean doMercator ) {
EarthFlat globe = new EarthFlat();
globe.setElevationModel(new ZeroElevationModel());
wwd.getModel().setGlobe(globe);
wwd.getView().stopMovement();
GeographicProjection projection;
if (doMercator) {
projection = new ProjectionMercator();
} else {
projection = new ProjectionEquirectangular();
}
globe.setProjection(projection);
wwd.redraw();
}
/**
* Set the globe as sphere.
*/
public void setSphereGlobe() {
Earth globe = new Earth();
wwd.getModel().setGlobe(globe);
wwd.getView().stopMovement();
wwd.redraw();
}
/**
* Set the globe as flat sphere.
*/
public void setFlatSphereGlobe() {
Earth globe = new Earth();
globe.setElevationModel(new ZeroElevationModel());
wwd.getModel().setGlobe(globe);
wwd.getView().stopMovement();
wwd.redraw();
}
public ReferencedEnvelope getViewportBounds() {
View view = wwd.getView();
Position posUL = view.computePositionFromScreenPoint(0, 0);
Position posLR = view.computePositionFromScreenPoint(getWidth(), getHeight());
if (posLR != null && posUL != null) {
double west = posUL.longitude.degrees;
double north = posUL.latitude.degrees;
double east = posLR.longitude.degrees;
double south = posLR.latitude.degrees;
ReferencedEnvelope env = new ReferencedEnvelope(west, east, south, north, DefaultGeographicCRS.WGS84);
return env;
} else {
return null;// new ReferencedEnvelope(-180, 180, -90, 90,
// DefaultGeographicCRS.WGS84);
}
}
public WorldWindow getWwd() {
return wwd;
}
public void addLayer( Layer layer ) {
getWwd().getModel().getLayers().add(layer);
}
public void removeLayer( Layer layer ) {
LayerList layers = getWwd().getModel().getLayers();
layers.remove(layer);
}
public void shutdown() {
if (wwd != null) {
wwd.shutdown();
}
}
// TODO check these old zoom tos
// public void zoomTo(Sector sector, boolean animate) {
// if (sector != null) {
// double sectorWidth = sector.getDeltaLonDegrees();
// LatLon centroid = sector.getCentroid();
//
// zoomTo(sectorWidth, centroid, animate);
// }
// }
//
// public void zoomTo(ReferencedEnvelope env, boolean animate) {
// double sectorWidth = env.getWidth();
// Coordinate centre = env.centre();
// LatLon centroid = NwwUtilities.toLatLon(centre.y, centre.x);
//
// zoomTo(sectorWidth, centroid, animate);
// }
//
// private void zoomTo(double width, LatLon centroid, boolean animate) {
// View view = getWwd().getView();
// double altitude = view.getCurrentEyePosition().getAltitude();
// ReferencedEnvelope viewportBounds = getViewportBounds();
// double newAltitude;
// if (viewportBounds != null) {
// double viewWidth = viewportBounds.getWidth();
// newAltitude = width * altitude / viewWidth;
// } else {
// newAltitude = altitude / 3;
// }
// goTo(centroid.longitude.degrees, centroid.latitude.degrees, newAltitude,
// null, animate);
// }
}