diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
index 506f468c86..c02064ba01 100644
--- a/nbproject/build-impl.xml
+++ b/nbproject/build-impl.xml
@@ -119,43 +119,7 @@ is divided into following sections:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Must set platform.home
- Must set platform.bootcp
- Must set platform.java
- Must set platform.javac
-
- The J2SE Platform is not correctly set up.
- Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files.
- Either open the project in the IDE and setup the Platform with the same name or add it manually.
- For example like this:
- ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file)
- or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used)
-
+
@@ -278,6 +242,20 @@ is divided into following sections:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -365,7 +343,7 @@ is divided into following sections:
-
+
@@ -416,7 +394,7 @@ is divided into following sections:
-
+
@@ -458,7 +436,7 @@ is divided into following sections:
-
+
@@ -537,7 +515,7 @@ is divided into following sections:
-
+
@@ -565,7 +543,7 @@ is divided into following sections:
-
+
@@ -641,7 +619,7 @@ is divided into following sections:
-
+
@@ -872,9 +850,6 @@ is divided into following sections:
-
-
-
@@ -924,7 +899,7 @@ is divided into following sections:
-
+
@@ -958,7 +933,7 @@ is divided into following sections:
-
+
@@ -990,7 +965,7 @@ is divided into following sections:
-
+
@@ -1224,7 +1199,7 @@ is divided into following sections:
To run this application from the command line without Ant, try:
- ${platform.java} -jar "${dist.jar.resolved}"
+ java -jar "${dist.jar.resolved}"
@@ -1326,8 +1301,8 @@ is divided into following sections:
-
-
+
+
@@ -1520,19 +1495,16 @@ is divided into following sections:
-
-
-
-
+
-
+
-
+
diff --git a/nbproject/configs/AnnotationControls.properties b/nbproject/configs/AnnotationControls.properties
new file mode 100644
index 0000000000..ef944d1651
--- /dev/null
+++ b/nbproject/configs/AnnotationControls.properties
@@ -0,0 +1 @@
+main.class=gov.nasa.worldwindx.examples.AnnotationControls
diff --git a/nbproject/configs/Cones.properties b/nbproject/configs/Cones.properties
new file mode 100644
index 0000000000..9832191404
--- /dev/null
+++ b/nbproject/configs/Cones.properties
@@ -0,0 +1 @@
+main.class=gov.nasa.worldwindx.examples.Cones
diff --git a/nbproject/configs/EGM2008Offsets.properties b/nbproject/configs/EGM2008Offsets.properties
new file mode 100644
index 0000000000..646d1236d5
--- /dev/null
+++ b/nbproject/configs/EGM2008Offsets.properties
@@ -0,0 +1 @@
+main.class=gov.nasa.worldwindx.examples.EGM2008Offsets
diff --git a/nbproject/configs/EGM96Offsets.properties b/nbproject/configs/EGM96Offsets.properties
new file mode 100644
index 0000000000..55026b5809
--- /dev/null
+++ b/nbproject/configs/EGM96Offsets.properties
@@ -0,0 +1 @@
+main.class=gov.nasa.worldwindx.examples.EGM96Offsets
diff --git a/nbproject/configs/ExportImageOrElevations.properties b/nbproject/configs/ExportImageOrElevations.properties
new file mode 100644
index 0000000000..301d17b18c
--- /dev/null
+++ b/nbproject/configs/ExportImageOrElevations.properties
@@ -0,0 +1 @@
+main.class=gov.nasa.worldwindx.examples.ExportImageOrElevations
diff --git a/nbproject/configs/Placemarks.properties b/nbproject/configs/Placemarks.properties
new file mode 100644
index 0000000000..b2f33a4ace
--- /dev/null
+++ b/nbproject/configs/Placemarks.properties
@@ -0,0 +1 @@
+main.class=gov.nasa.worldwindx.examples.Placemarks
diff --git a/nbproject/configs/ScreenImageDragging.properties b/nbproject/configs/ScreenImageDragging.properties
new file mode 100644
index 0000000000..a033a21e1d
--- /dev/null
+++ b/nbproject/configs/ScreenImageDragging.properties
@@ -0,0 +1 @@
+main.class=gov.nasa.worldwindx.examples.ScreenImageDragging
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
index 6a15e776fa..f7555ac948 100644
--- a/nbproject/genfiles.properties
+++ b/nbproject/genfiles.properties
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=ed839dc9
+build.xml.data.CRC32=6106e3d1
build.xml.script.CRC32=b2ee8dee
-build.xml.stylesheet.CRC32=f85dc8f2@1.102.0.48
+build.xml.stylesheet.CRC32=f85dc8f2@1.108.0.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=ed839dc9
-nbproject/build-impl.xml.script.CRC32=2e7bce4f
-nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.102.0.48
+nbproject/build-impl.xml.data.CRC32=6106e3d1
+nbproject/build-impl.xml.script.CRC32=43da2f27
+nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.108.0.48
diff --git a/nbproject/project.properties b/nbproject/project.properties
index 1adab5d73f..fab181db51 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -104,7 +104,7 @@ manifest.custom.permissions=
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
-platform.active=JDK_11
+platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
diff --git a/nbproject/project.xml b/nbproject/project.xml
index cba6c32199..e27d90a9ba 100644
--- a/nbproject/project.xml
+++ b/nbproject/project.xml
@@ -4,7 +4,6 @@
WorldWindJava
-
diff --git a/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java b/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java
index 479cf7fbe6..d5ccf17d09 100644
--- a/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java
+++ b/src/gov/nasa/worldwind/globes/EllipsoidalGlobe.java
@@ -2,25 +2,25 @@
* Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All rights reserved.
- *
+ *
* The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
- *
+ *
* NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
* software:
- *
+ *
* Jackson Parser – Licensed under Apache 2.0
* GDAL – Licensed under MIT
* JOGL – Licensed under Berkeley Software Distribution (BSD)
* Gluegen – Licensed under Berkeley Software Distribution (BSD)
- *
+ *
* A complete listing of 3rd Party software notices and licenses included in
* NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
* notices and licenses PDF found in code directory.
@@ -132,7 +132,10 @@ public Globe getGlobe()
return this.globe;
}
- @SuppressWarnings({"RedundantIfStatement"})
+ @SuppressWarnings(
+ {
+ "RedundantIfStatement"
+ })
@Override
public boolean equals(Object o)
{
@@ -145,8 +148,8 @@ public boolean equals(Object o)
if (Double.compare(stateKey.verticalExaggeration, verticalExaggeration) != 0)
return false;
- if (elevationModel != null ? !elevationModel.equals(stateKey.elevationModel) :
- stateKey.elevationModel != null)
+ if (elevationModel != null ? !elevationModel.equals(stateKey.elevationModel)
+ : stateKey.elevationModel != null)
return false;
if (globe != null ? !globe.equals(stateKey.globe) : stateKey.globe != null)
return false;
@@ -238,7 +241,6 @@ public double getRadiusAt(Angle latitude, Angle longitude)
// observing that the length of the ellipsoidal point at the specified latitude and longitude indicates the
// radius at that location. The formula for the length of the ellipsoidal point was then converted into the
// simplified form below.
-
double sinLat = Math.sin(latitude.radians);
double rpm = this.equatorialRadius / Math.sqrt(1.0 - this.es * sinLat * sinLat);
@@ -294,7 +296,10 @@ public double[] getMinAndMaxElevations(Angle latitude, Angle longitude)
}
return this.elevationModel != null ? this.elevationModel.getExtremeElevations(latitude, longitude)
- : new double[] {0, 0};
+ : new double[]
+ {
+ 0, 0
+ };
}
public double[] getMinAndMaxElevations(Sector sector)
@@ -306,7 +311,10 @@ public double[] getMinAndMaxElevations(Sector sector)
throw new IllegalArgumentException(message);
}
- return this.elevationModel != null ? this.elevationModel.getExtremeElevations(sector) : new double[] {0, 0};
+ return this.elevationModel != null ? this.elevationModel.getExtremeElevations(sector) : new double[]
+ {
+ 0, 0
+ };
}
public Extent getExtent()
@@ -347,7 +355,6 @@ protected Intersection[] intersect(Line line, double equRadius, double polRadius
return null;
// Taken from Lengyel, 2Ed., Section 5.2.3, page 148.
-
double m = equRadius / polRadius; // "ratio of the x semi-axis length to the y semi-axis length"
double n = 1d; // "ratio of the x semi-axis length to the z semi-axis length"
double m2 = m * m;
@@ -373,16 +380,25 @@ protected Intersection[] intersect(Line line, double equRadius, double polRadius
if (discriminant == 0)
{
Vec4 p = line.getPointAt((-b - discriminantRoot) / (2 * a));
- return new Intersection[] {new Intersection(p, true)};
+ return new Intersection[]
+ {
+ new Intersection(p, true)
+ };
}
else // (discriminant > 0)
{
Vec4 near = line.getPointAt((-b - discriminantRoot) / (2 * a));
Vec4 far = line.getPointAt((-b + discriminantRoot) / (2 * a));
if (c >= 0) // Line originates outside the Globe.
- return new Intersection[] {new Intersection(near, false), new Intersection(far, false)};
+ return new Intersection[]
+ {
+ new Intersection(near, false), new Intersection(far, false)
+ };
else // Line originates inside the Globe.
- return new Intersection[] {new Intersection(far, false)};
+ return new Intersection[]
+ {
+ new Intersection(far, false)
+ };
}
}
@@ -492,7 +508,10 @@ public double[] getElevations(Sector sector, List extends LatLon> latLons, dou
double[] elevations)
{
if (this.elevationModel == null)
- return new double[] {0};
+ return new double[]
+ {
+ 0
+ };
double[] resolution = this.elevationModel.getElevations(sector, latLons, targetResolution, elevations);
@@ -508,6 +527,16 @@ public double[] getElevations(Sector sector, List extends LatLon> latLons, dou
return resolution;
}
+ /**
+ * Get the EGM96 offset class for this globe.
+ *
+ * @return The EGM96 offset class for this globe.
+ */
+ public EGM96 getEGM96()
+ {
+ return this.egm96;
+ }
+
public double getElevation(Angle latitude, Angle longitude)
{
if (latitude == null || longitude == null)
@@ -909,7 +938,8 @@ protected Vec4 geodeticToEllipsoidal(Angle latitude, Angle longitude, double met
double cosLon = Math.cos(longitude.radians);
double sinLon = Math.sin(longitude.radians);
- double rpm = // getRadius (in meters) of vertical in prime meridian
+ double rpm
+ = // getRadius (in meters) of vertical in prime meridian
this.equatorialRadius / Math.sqrt(1.0 - this.es * sinLat * sinLat);
double x = (rpm + metersElevation) * cosLat * sinLon;
@@ -1035,7 +1065,6 @@ protected void geodeticToCartesian(Sector sector, int numLat, int numLon, double
//
// return Position.fromRadians(lat, lon, elevation);
// }
-
/**
* Compute the geographic position to corresponds to a Cartesian point.
*
@@ -1059,7 +1088,10 @@ protected Position cartesianToGeodetic(Vec4 cart)
*
* @see #geodeticToEllipsoidal(gov.nasa.worldwind.geom.Angle, gov.nasa.worldwind.geom.Angle, double)
*/
- @SuppressWarnings({"SuspiciousNameCombination"})
+ @SuppressWarnings(
+ {
+ "SuspiciousNameCombination"
+ })
protected Position ellipsoidalToGeodetic(Vec4 cart)
{
// Contributed by Nathan Kronenfeld. Integrated 1/24/2011. Brings this calculation in line with Vermeille's
@@ -1370,4 +1402,4 @@ public static ElevationModel makeElevationModel(String key, String defaultValue)
Object configSource = Configuration.getStringValue(key, defaultValue);
return (ElevationModel) BasicFactory.create(AVKey.ELEVATION_MODEL_FACTORY, configSource);
}
-}
\ No newline at end of file
+}
diff --git a/src/gov/nasa/worldwind/util/EGM2008.java b/src/gov/nasa/worldwind/util/EGM2008.java
new file mode 100644
index 0000000000..f8e64d9900
--- /dev/null
+++ b/src/gov/nasa/worldwind/util/EGM2008.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
+ * software:
+ *
+ * Jackson Parser – Licensed under Apache 2.0
+ * GDAL – Licensed under MIT
+ * JOGL – Licensed under Berkeley Software Distribution (BSD)
+ * Gluegen – Licensed under Berkeley Software Distribution (BSD)
+ *
+ * A complete listing of 3rd Party software notices and licenses included in
+ * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
+ * notices and licenses PDF found in code directory.
+ */
+
+package gov.nasa.worldwind.util;
+
+import gov.nasa.worldwind.cache.BasicMemoryCache;
+import gov.nasa.worldwind.geom.Angle;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+public class EGM2008
+{
+ public static final int N_ROW_MARKERS = 2; // The beginning and end of each row of latitude data is a flag of some sort
+ public static final int N_LONGITUDE_COLS = 8640 + N_ROW_MARKERS; // Number of float32s in a row of data in the data file.
+ public static final int N_LATITUDE_ROWS = 4321; // Number of rows.
+ public static final double GRID_RESOLUTION = 2.5d / 60d; // 2.5 minute grid
+ public static final double CELL_AREA = GRID_RESOLUTION * GRID_RESOLUTION;
+ protected static final long CACHE_SIZE = EGM2008.N_LONGITUDE_COLS * 4 * 45 * 15; // Cache 15 degrees worth of offsets.
+ public static final int N_LAT_ROW_BYTES = N_LONGITUDE_COLS * 4; // Offsets are float32
+
+ protected String offsetsFilePath;
+ protected BufferWrapper deltas;
+ protected final BasicMemoryCache offsetCache;
+
+ protected class GridCell
+ {
+ public double x1;
+ public double y1;
+ public double x2;
+ public double y2;
+
+ public GridCell(double x1, double y1)
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x1 + EGM2008.GRID_RESOLUTION;
+ this.y2 = y1 + EGM2008.GRID_RESOLUTION;
+ }
+
+ public GridCell()
+ {
+ this(0, 0);
+ }
+
+ public GridCell intersect(GridCell that)
+ {
+ GridCell intersection = new GridCell();
+ intersection.x1 = Math.max(this.x1, that.x1);
+ intersection.x2 = Math.min(this.x2, that.x2);
+ intersection.y1 = Math.max(this.y1, that.y1);
+ intersection.y2 = Math.min(this.y2, that.y2);
+ return intersection;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%5.2f,%5.2f,%5.2f,%5.2f", x1, y1, x2, y2);
+ }
+
+ public double area()
+ {
+ return (this.x2 - this.x1) * (this.y2 - this.y1);
+ }
+ }
+
+ /**
+ * Allows the retrieval of geoid offsets from the EGM2008 2.5 Minute Interpolation Grid sourced from the
+ * National Geospatial-Intelligence Agency Office of Geomatics (https://earth-info.nga.mil/).
+ *
+ * The EGM2008 data path. This data file is not included in the SDK due to its size. The data may be downloaded here:
+ * https://builds.worldwind.arc.nasa.gov/artifactory/EGM2008-Data/egm2008_25.dat
+ *
+ * @param offsetsFilePath a path pointing to a file with the geoid offsets.
+ */
+ public EGM2008(String offsetsFilePath)
+ {
+ if (offsetsFilePath == null)
+ {
+ String msg = Logging.getMessage("nullValue.PathIsNull");
+ Logging.logger().severe(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ File test = new File(offsetsFilePath);
+ if (test.exists())
+ {
+ this.offsetsFilePath = offsetsFilePath;
+ }
+ else
+ {
+ Class c = EGM2008.class;
+ URL url = c.getResource("/" + offsetsFilePath);
+ if (url != null)
+ {
+ test = WWIO.getFileForLocalAddress(url);
+ this.offsetsFilePath = test.getAbsolutePath();
+ }
+ else
+ {
+ this.offsetsFilePath = null;
+ }
+ }
+ this.offsetCache = new BasicMemoryCache((EGM2008.CACHE_SIZE * 8) / 10, EGM2008.CACHE_SIZE);
+ this.offsetCache.setName(EGM2008.class.getName());
+ }
+
+ public float getOffset(Angle lat, Angle lon) throws IOException
+ {
+ return this.getOffset((float) lat.degrees, (float) lon.degrees);
+ }
+
+ public int getLatRow(double lat)
+ {
+ // Compute the row in the data file corresponding to a given latitude.
+ // Latitude row zero in the data corresponds to 90 degrees latitude (north pole) and increases southward
+ // Longitude column zero in the data corresponds to 0 degrees of longitude and increases eastward
+ float lat180 = 90f - (float) lat;
+ return (int) Math.floor(lat180 / EGM2008.GRID_RESOLUTION);
+ }
+
+ public int getLonCol(double lon)
+ {
+ // Compute the column in the data file corresponding to a given latitude and longitude.
+ // Latitude row zero in the data corresponds to 90 degrees latitude (north pole) and increases southward
+ // Longitude column zero in the data corresponds to 0 degrees of longitude and increases eastward
+ float lon360 = (float) lon;
+ if (lon < 0)
+ {
+ lon360 = lon360 + 360;
+ }
+ return (int) Math.floor(lon360 / EGM2008.GRID_RESOLUTION);
+ }
+
+ public float[][] getLatRows(int latRow) throws IOException
+ {
+ int[] interpRowIndices =
+ {
+ latRow, latRow + 1
+ };
+ float[][] latDataArray = new float[2][];
+ boolean retrievalRequired = false;
+ for (int i = 0; i < interpRowIndices.length; i++)
+ {
+ if (interpRowIndices[i] < EGM2008.N_LATITUDE_ROWS)
+ {
+ float[] latData = (float[]) this.offsetCache.getObject(interpRowIndices[i]);
+ latDataArray[i] = latData;
+ if (latData == null)
+ {
+ retrievalRequired = true;
+ }
+ }
+ }
+ if (retrievalRequired)
+ {
+ try (RandomAccessFile offsetFile = new RandomAccessFile(this.offsetsFilePath, "r"))
+ {
+ for (int i = 0; i < interpRowIndices.length; i++)
+ {
+ if (interpRowIndices[i] < EGM2008.N_LATITUDE_ROWS && latDataArray[i] == null)
+ {
+ offsetFile.seek(interpRowIndices[i] * EGM2008.N_LAT_ROW_BYTES);
+ byte[] latByteData = new byte[EGM2008.N_LAT_ROW_BYTES];
+ offsetFile.read(latByteData);
+ ByteBuffer latByteBuffer = ByteBuffer.wrap(latByteData).order(ByteOrder.LITTLE_ENDIAN);
+ FloatBuffer latFloatBuffer = latByteBuffer.asFloatBuffer();
+ float[] latData = new float[EGM2008.N_LONGITUDE_COLS];
+ latFloatBuffer.get(latData);
+ this.offsetCache.add(interpRowIndices[i], latData, EGM2008.N_LAT_ROW_BYTES);
+ latDataArray[i] = latData;
+ }
+ }
+ }
+ }
+ return latDataArray;
+ }
+
+ public float getOffset(double lat, double lon) throws IOException
+ {
+ if (this.offsetsFilePath == null)
+ {
+ return 0f;
+ }
+ int latRow = this.getLatRow(lat);
+ int lonCol = this.getLonCol(lon);
+
+ float[][] latDataArray = getLatRows(latRow);
+
+ float baseOffset = latDataArray[0][lonCol + EGM2008.N_ROW_MARKERS / 2];
+ if (latDataArray[1] == null)
+ {
+ return baseOffset;
+ }
+
+ // Interpolate with surrounding offset cells
+ float lat180 = 90f - (float) lat;
+ float lon360 = (float) lon;
+ if (lon < 0)
+ {
+ lon360 = lon360 + 360;
+ }
+ GridCell offsetCell = new GridCell(lon360, lat180);
+ double baseLat = ((double) latRow) * EGM2008.GRID_RESOLUTION;
+ double baseLon = ((double) lonCol) * EGM2008.GRID_RESOLUTION;
+ float interpOffset = 0;
+ for (int x = 0; x < 2; x++)
+ {
+ double cellLon = baseLon + ((double) x) * EGM2008.GRID_RESOLUTION;
+ for (int y = 0; y < 2; y++)
+ {
+ float cellOffset = latDataArray[y][lonCol + EGM2008.N_ROW_MARKERS / 2 + x];
+ double cellLat = baseLat + ((double) y) * EGM2008.GRID_RESOLUTION;
+ GridCell interpCell = new GridCell(cellLon, cellLat);
+ GridCell intersection = offsetCell.intersect(interpCell);
+ interpOffset += cellOffset * (intersection.area() / EGM2008.CELL_AREA);
+ }
+ }
+ return interpOffset;
+ }
+
+ public boolean isEGMDataAvailable()
+ {
+ return this.offsetsFilePath != null;
+ }
+}
diff --git a/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java b/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java
new file mode 100644
index 0000000000..f2b403ca0d
--- /dev/null
+++ b/src/gov/nasa/worldwindx/examples/EGM2008Offsets.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
+ * software:
+ *
+ * Jackson Parser – Licensed under Apache 2.0
+ * GDAL – Licensed under MIT
+ * JOGL – Licensed under Berkeley Software Distribution (BSD)
+ * Gluegen – Licensed under Berkeley Software Distribution (BSD)
+ *
+ * A complete listing of 3rd Party software notices and licenses included in
+ * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
+ * notices and licenses PDF found in code directory.
+ */
+package gov.nasa.worldwindx.examples;
+
+import gov.nasa.worldwind.Model;
+import gov.nasa.worldwind.avlist.AVKey;
+import gov.nasa.worldwind.geom.LatLon;
+import gov.nasa.worldwind.geom.Position;
+import gov.nasa.worldwind.geom.Sector;
+import gov.nasa.worldwind.globes.Earth;
+import gov.nasa.worldwind.globes.Globe;
+import gov.nasa.worldwind.layers.RenderableLayer;
+import gov.nasa.worldwind.render.PointPlacemark;
+import gov.nasa.worldwind.util.EGM2008;
+import java.awt.Dimension;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import javax.swing.SwingUtilities;
+
+/**
+ * Shows how to apply EGM2008 offsets to Earth elevations.
+ *
+ * This EGM2008 data file is not included in the SDK due to its size. The data may be downloaded here:
+ * https://builds.worldwind.arc.nasa.gov/artifactory/EGM2008-Data/egm2008_25.dat
+ *
+ * This example looks for the EGM2008 data in the WorldWind src/config folder by default.
+ */
+public class EGM2008Offsets extends ApplicationTemplate
+{
+ public static class AppFrame extends ApplicationTemplate.AppFrame
+ {
+ /**
+ * Attempt to retrieve the best elevations for a specified list of locations. The elevations returned are the best currently
+ * available for the data set and the area bounding the locations. Since the necessary elevation data might not
+ * be in memory at the time of the call, this method iterates until the necessary elevation data is in memory
+ * and can be used to determine the locations elevations.
+ *
+ * @param locations a list of locations to determine elevations for
+ */
+ public void loadBestElevations(ArrayList locations)
+ {
+ Globe globe = this.getWwd().getModel().getGlobe();
+ ArrayList sectors = new ArrayList<>();
+ ArrayList> locationsList = new ArrayList<>();
+ double delta = 0.0001;
+ for (LatLon ll : locations)
+ {
+ double lat = ll.latitude.degrees;
+ double lon = ll.longitude.degrees;
+ sectors.add(Sector.fromDegrees(lat, lat + delta, lon, lon + delta));
+ ArrayList sectorLocations = new ArrayList<>();
+ sectorLocations.add(ll);
+ sectorLocations.add(LatLon.fromDegrees(lat + delta, lon + delta));
+ locationsList.add(sectorLocations);
+ }
+
+ double[] targetResolutions = new double[sectors.size()];
+ double[] actualResolutions = new double[sectors.size()];
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ targetResolutions[i] = globe.getElevationModel().getBestResolution(sectors.get(i));
+ }
+ boolean resolutionsAchieved = false;
+ double[] elevations = new double[2];
+ while (!resolutionsAchieved)
+ {
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ actualResolutions[i] = globe.getElevations(sectors.get(i), locationsList.get(i), targetResolutions[i], elevations);
+ }
+
+ resolutionsAchieved = true;
+ for (int i = 0, len = actualResolutions.length; i < len && resolutionsAchieved; i++)
+ {
+ resolutionsAchieved = actualResolutions[i] <= targetResolutions[i];
+ }
+ if (!resolutionsAchieved)
+ {
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public AppFrame()
+ {
+ Model m = this.wwjPanel.getWwd().getModel();
+ Earth earth = (Earth) m.getGlobe();
+ final RenderableLayer layer = new RenderableLayer();
+ double[] locations = new double[]
+ {
+ 37.0, -119.0,
+ 36.0, -117.016667,
+ 89.0, 0.0,
+ -80.0, 0.0,
+ -90.0, 0.0
+ };
+ EGM2008 egm2008Offsets = new EGM2008("config/egm2008_25.dat");
+ boolean egmAvailable = egm2008Offsets.isEGMDataAvailable();
+ if (!egmAvailable)
+ {
+ System.out.println("*** EGM 2008 data not available.");
+ }
+
+ // Run the elevation query in a separate thread to avoid locking up the user interface
+ Thread t = new Thread(() ->
+ {
+ ArrayList elevLocations = new ArrayList<>();
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ elevLocations.add(LatLon.fromDegrees(locations[i], locations[i + 1]));
+ }
+
+ loadBestElevations(elevLocations);
+
+ try
+ {
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ Position pos = Position.fromDegrees(locations[i], locations[i + 1], 0);
+ PointPlacemark placemark = new PointPlacemark(pos);
+ String label = String.format("lat: %7.4f, lon: %7.4f", locations[i], locations[i + 1]);
+ float egmOffset = egm2008Offsets.getOffset(pos.latitude, pos.longitude);
+ double elevation = earth.getElevation(pos.latitude, pos.longitude);
+ if (egmAvailable)
+ {
+ placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM2008 Offset: %7.4f\nEllipsoid elevation:%7.4f\nEGM2008 Adjusted elevation: %7.4f",
+ egmOffset, elevation, elevation - egmOffset));
+ }
+ else
+ {
+ placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM2008 Offset: N/A\nEllipsoid elevation:%7.4f\nEGM2008 Adjusted elevation: N/A",
+ elevation));
+ }
+ placemark.setLabelText(label);
+ layer.addRenderable(placemark);
+ }
+ }
+ catch (IOException iex)
+ {
+ iex.printStackTrace();
+ }
+
+ SwingUtilities.invokeLater(() ->
+ {
+ System.out.println("Elevations retrieved");
+ getWwd().redraw();
+ });
+ });
+ t.start();
+
+ try
+ {
+ // Test offsets for some coordinates
+ float lat = 47;
+ float lon = -94;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 37;
+ lon = -119;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ // Try previous coordinates to verify caching
+ lat = 47;
+ lon = -94;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 37;
+ lon = -119;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 47.02f;
+ lon = -94.02f;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ float gridResolution = (float) EGM2008.GRID_RESOLUTION;
+ lat = 47 + gridResolution;
+ lon = -94 - gridResolution;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 36.0f;
+ lon = -117.0f;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ lat = 36.0f;
+ lon = -117.041666666667f;
+ System.out.println(lat + "," + lon + "," + egm2008Offsets.getOffset(lat, lon));
+
+ System.out.println();
+ }
+ catch (IOException iex)
+ {
+ iex.printStackTrace();
+ }
+
+ this.wwjPanel.toolTipController.setAnnotationSize(new Dimension(500, 0));
+ insertBeforeCompass(getWwd(), layer);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ ApplicationTemplate.start("WorldWind EGM2008 Offsets", AppFrame.class);
+ }
+}
diff --git a/src/gov/nasa/worldwindx/examples/EGM96Offsets.java b/src/gov/nasa/worldwindx/examples/EGM96Offsets.java
index a64d11b1bc..39b7adf6b0 100644
--- a/src/gov/nasa/worldwindx/examples/EGM96Offsets.java
+++ b/src/gov/nasa/worldwindx/examples/EGM96Offsets.java
@@ -2,39 +2,50 @@
* Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All rights reserved.
- *
+ *
* The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
- *
+ *
* NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
* software:
- *
+ *
* Jackson Parser – Licensed under Apache 2.0
* GDAL – Licensed under MIT
* JOGL – Licensed under Berkeley Software Distribution (BSD)
* Gluegen – Licensed under Berkeley Software Distribution (BSD)
- *
+ *
* A complete listing of 3rd Party software notices and licenses included in
* NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
* notices and licenses PDF found in code directory.
*/
-
package gov.nasa.worldwindx.examples;
import gov.nasa.worldwind.Model;
+import gov.nasa.worldwind.avlist.AVKey;
+import gov.nasa.worldwind.geom.LatLon;
+import gov.nasa.worldwind.geom.Position;
+import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.globes.Earth;
+import gov.nasa.worldwind.globes.Globe;
+import gov.nasa.worldwind.layers.RenderableLayer;
+import gov.nasa.worldwind.render.PointPlacemark;
+import gov.nasa.worldwind.util.EGM96;
+import java.awt.Dimension;
import java.io.IOException;
+import java.util.ArrayList;
+import javax.swing.SwingUtilities;
/**
* Shows how to apply EGM96 offsets to the Earth.
+ *
* @author tag
* @version $Id: EGM96Offsets.java 1501 2013-07-11 15:59:11Z tgaskins $
*/
@@ -42,17 +53,118 @@ public class EGM96Offsets extends ApplicationTemplate
{
public static class AppFrame extends ApplicationTemplate.AppFrame
{
+ /**
+ * Attempt to retrieve the best elevations for a specified list of locations. The elevations returned are the best currently
+ * available for the data set and the area bounding the locations. Since the necessary elevation data might not
+ * be in memory at the time of the call, this method iterates until the necessary elevation data is in memory
+ * and can be used to determine the locations elevations.
+ *
+ * @param locations a list of locations to determine elevations for
+ */
+ public void loadBestElevations(ArrayList locations)
+ {
+ Globe globe = this.getWwd().getModel().getGlobe();
+ ArrayList sectors = new ArrayList<>();
+ ArrayList> locationsList = new ArrayList<>();
+ double delta = 0.0001;
+ for (LatLon ll : locations)
+ {
+ double lat = ll.latitude.degrees;
+ double lon = ll.longitude.degrees;
+ sectors.add(Sector.fromDegrees(lat, lat + delta, lon, lon + delta));
+ ArrayList sectorLocations = new ArrayList<>();
+ sectorLocations.add(ll);
+ sectorLocations.add(LatLon.fromDegrees(lat + delta, lon + delta));
+ locationsList.add(sectorLocations);
+ }
+
+ double[] targetResolutions = new double[sectors.size()];
+ double[] actualResolutions = new double[sectors.size()];
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ targetResolutions[i] = globe.getElevationModel().getBestResolution(sectors.get(i));
+ }
+ boolean resolutionsAchieved = false;
+ double[] elevations = new double[2];
+ while (!resolutionsAchieved)
+ {
+ for (int i = 0, len = sectors.size(); i < len; i++)
+ {
+ actualResolutions[i] = globe.getElevations(sectors.get(i), locationsList.get(i), targetResolutions[i], elevations);
+ }
+
+ resolutionsAchieved = true;
+ for (int i = 0, len = actualResolutions.length; i < len && resolutionsAchieved; i++)
+ {
+ resolutionsAchieved = actualResolutions[i] <= targetResolutions[i];
+ }
+ if (!resolutionsAchieved)
+ {
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
public AppFrame()
{
Model m = this.wwjPanel.getWwd().getModel();
+ Earth earth = (Earth) m.getGlobe();
try
{
- ((Earth) m.getGlobe()).applyEGMA96Offsets("config/EGM96.dat");
+ earth.applyEGMA96Offsets("config/EGM96.dat");
}
catch (IOException e)
{
e.printStackTrace();
}
+ final RenderableLayer layer = new RenderableLayer();
+ double[] locations = new double[]
+ {
+ 37.0, -119.0,
+ 36.0, -117.016667,
+ 89.0, 0.0,
+ -80.0, 0.0,
+ -90.0, 0.0
+ };
+ Thread t = new Thread(() ->
+ {
+ ArrayList elevLocations = new ArrayList<>();
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ elevLocations.add(LatLon.fromDegrees(locations[i], locations[i + 1]));
+ }
+
+ loadBestElevations(elevLocations);
+
+ EGM96 egm96Offsets = earth.getEGM96();
+ for (int i = 0; i < locations.length; i += 2)
+ {
+ Position pos = Position.fromDegrees(locations[i], locations[i + 1], 0);
+ PointPlacemark placemark = new PointPlacemark(pos);
+ String label = String.format("lat: %7.4f, lon: %7.4f", locations[i], locations[i + 1]);
+ placemark.setValue(AVKey.DISPLAY_NAME, String.format("EGM96 Offset: %7.4f\nEGM96 Adjusted elevation: %7.4f",
+ egm96Offsets.getOffset(pos.latitude, pos.longitude),
+ earth.getElevation(pos.latitude, pos.longitude)));
+ placemark.setLabelText(label);
+ layer.addRenderable(placemark);
+ }
+ SwingUtilities.invokeLater(() ->
+ {
+ System.out.println("Elevations retrieved");
+ getWwd().redraw();
+ });
+ });
+ t.start();
+
+ this.wwjPanel.toolTipController.setAnnotationSize(new Dimension(500, 0));
+ insertBeforeCompass(getWwd(), layer);
}
}
diff --git a/src/gov/nasa/worldwindx/examples/util/ToolTipController.java b/src/gov/nasa/worldwindx/examples/util/ToolTipController.java
index 0cadd531d9..05e58a7e63 100644
--- a/src/gov/nasa/worldwindx/examples/util/ToolTipController.java
+++ b/src/gov/nasa/worldwindx/examples/util/ToolTipController.java
@@ -2,25 +2,25 @@
* Copyright 2006-2009, 2017, 2020 United States Government, as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All rights reserved.
- *
+ *
* The NASA World Wind Java (WWJ) platform is licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
- *
+ *
* NASA World Wind Java (WWJ) also contains the following 3rd party Open Source
* software:
- *
+ *
* Jackson Parser – Licensed under Apache 2.0
* GDAL – Licensed under MIT
* JOGL – Licensed under Berkeley Software Distribution (BSD)
* Gluegen – Licensed under Berkeley Software Distribution (BSD)
- *
+ *
* A complete listing of 3rd Party software notices and licenses included in
* NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party
* notices and licenses PDF found in code directory.
@@ -34,6 +34,7 @@
import gov.nasa.worldwindx.examples.ApplicationTemplate;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.util.*;
+import java.awt.Dimension;
/**
* Controls display of tool tips on picked objects. Any shape implementing {@link AVList} can participate. Shapes
@@ -52,6 +53,7 @@ public class ToolTipController implements SelectListener, Disposable
protected Object lastHoverObject;
protected AnnotationLayer layer;
protected ToolTipAnnotation annotation;
+ protected Dimension annotationSize = null;
/**
* Create a controller for a specified {@link WorldWindow} that displays tool tips on hover and/or rollover.
@@ -84,6 +86,16 @@ public ToolTipController(WorldWindow wwd)
this.wwd.addSelectListener(this);
}
+ public Dimension getAnnotationSize()
+ {
+ return this.annotationSize;
+ }
+
+ public void setAnnotationSize(Dimension value)
+ {
+ this.annotationSize = value;
+ }
+
public void dispose()
{
this.wwd.removeSelectListener(this);
@@ -91,14 +103,14 @@ public void dispose()
protected String getHoverText(SelectEvent event)
{
- return event.getTopObject() != null && event.getTopObject() instanceof AVList ?
- ((AVList) event.getTopObject()).getStringValue(this.hoverKey) : null;
+ return event.getTopObject() != null && event.getTopObject() instanceof AVList
+ ? ((AVList) event.getTopObject()).getStringValue(this.hoverKey) : null;
}
protected String getRolloverText(SelectEvent event)
{
- return event.getTopObject() != null && event.getTopObject() instanceof AVList ?
- ((AVList) event.getTopObject()).getStringValue(this.rolloverKey) : null;
+ return event.getTopObject() != null && event.getTopObject() instanceof AVList
+ ? ((AVList) event.getTopObject()).getStringValue(this.rolloverKey) : null;
}
public void selected(SelectEvent event)
@@ -167,6 +179,7 @@ protected void showToolTip(SelectEvent event, String text)
else
{
annotation = new ToolTipAnnotation(text);
+ annotation.getAttributes().setSize(this.annotationSize);
}
if (layer == null)