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 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 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)