
src.gov.nasa.worldwind.terrain.CompoundElevationModel Maven / Gradle / Ivy
Go to download
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.terrain;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.ElevationModel;
import gov.nasa.worldwind.util.Logging;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author tag
* @version $Id: CompoundElevationModel.java 1978 2014-04-30 17:37:11Z tgaskins $
*/
public class CompoundElevationModel extends AbstractElevationModel
{
protected CopyOnWriteArrayList elevationModels = new CopyOnWriteArrayList();
public void dispose()
{
for (ElevationModel child : this.elevationModels)
{
if (child != null)
child.dispose();
}
}
/**
* Returns true if this CompoundElevationModel contains the specified ElevationModel, and false otherwise.
*
* @param em the ElevationModel to test.
*
* @return true if the ElevationModel is in this CompoundElevationModel; false otherwise.
*
* @throws IllegalArgumentException if the ElevationModel is null.
*/
public boolean containsElevationModel(ElevationModel em)
{
if (em == null)
{
String msg = Logging.getMessage("nullValue.ElevationModelIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
// Check if the elevation model is one of the models in our list.
if (this.elevationModels.contains(em))
return true;
// Check if the elevation model is a child of any CompoundElevationModels in our list.
for (ElevationModel child : this.elevationModels)
{
if (child instanceof CompoundElevationModel)
{
if (((CompoundElevationModel) child).containsElevationModel(em))
return true;
}
}
return false;
}
public void addElevationModel(ElevationModel em)
{
if (em == null)
{
String msg = Logging.getMessage("nullValue.ElevationModelIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
this.elevationModels.add(em);
}
public void addElevationModel(int index, ElevationModel em)
{
if (em == null)
{
String msg = Logging.getMessage("nullValue.ElevationModelIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
this.elevationModels.add(index, em); // the list's add method will throw exception for invalid index
}
public void removeElevationModel(ElevationModel em)
{
if (em == null)
{
String msg = Logging.getMessage("nullValue.ElevationModelIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
for (ElevationModel child : this.elevationModels)
{
if (child instanceof CompoundElevationModel)
((CompoundElevationModel) child).removeElevationModel(em);
}
this.elevationModels.remove(em);
}
public void removeElevationModel(int index)
{
if (index < 0 || index >= this.elevationModels.size())
{
String msg = Logging.getMessage("generic.indexOutOfRange", index);
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
this.elevationModels.remove(index);
}
public void setElevationModel(int index, ElevationModel em)
{
if (em == null)
{
String msg = Logging.getMessage("nullValue.ElevationModelIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (index < 0 || index >= this.elevationModels.size())
{
String msg = Logging.getMessage("generic.indexOutOfRange", index);
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
this.elevationModels.set(index, em);
}
public List getElevationModels()
{
return new ArrayList(this.elevationModels);
}
/**
* Recursively specifies the time of the most recent dataset update for this compound model, and for each elevation
* model contained within this compound model. Any cached data older than this time is invalid.
*
* @param expiryTime the expiry time of any cached data, expressed as a number of milliseconds beyond the epoch.
*
* @see System#currentTimeMillis() for a description of milliseconds beyond the epoch.
*/
public void setExpiryTime(long expiryTime)
{
super.setExpiryTime(expiryTime);
for (ElevationModel em : this.elevationModels)
{
em.setExpiryTime(expiryTime);
}
}
public double getMaxElevation() // TODO: probably want to cache the min and max rather than always compute them
{
double max = -Double.MAX_VALUE;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
double m = em.getMaxElevation();
if (m > max)
max = m;
}
return max == -Double.MAX_VALUE ? 0 : max;
}
public double getMinElevation()
{
double min = Double.MAX_VALUE;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
double m = em.getMinElevation();
if (m < min)
min = m;
}
return min == Double.MAX_VALUE ? 0 : min;
}
public double[] getExtremeElevations(Angle latitude, Angle longitude)
{
if (latitude == null || longitude == null)
{
String msg = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
double[] retVal = null;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
double[] minmax = em.getExtremeElevations(latitude, longitude);
if (retVal == null)
{
retVal = new double[] {minmax[0], minmax[1]};
}
else
{
if (minmax[0] < retVal[0])
retVal[0] = minmax[0];
if (minmax[1] > retVal[1])
retVal[1] = minmax[1];
}
}
return retVal == null ? new double[] {0, 0} : retVal;
}
public double[] getExtremeElevations(Sector sector)
{
if (sector == null)
{
String msg = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
double[] retVal = null;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
int c = em.intersects(sector);
if (c < 0) // no intersection
continue;
double[] minmax = em.getExtremeElevations(sector);
if (retVal == null)
{
retVal = new double[] {minmax[0], minmax[1]};
}
else
{
if (minmax[0] < retVal[0])
retVal[0] = minmax[0];
if (minmax[1] > retVal[1])
retVal[1] = minmax[1];
}
}
return retVal == null ? new double[] {0, 0} : retVal;
}
public double getBestResolution(Sector sector)
{
double res = 0;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
if (sector != null && em.intersects(sector) < 0) // sector does not intersect elevation model
continue;
double r = em.getBestResolution(sector);
if (r < res || res == 0)
res = r;
}
return res != 0 ? res : Double.MAX_VALUE;
}
public double getDetailHint(Sector sector)
{
if (sector == null)
{
String msg = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
// Find the first elevation model intersecting the sector, starting with the hightest resolution. Return the
// detail hint for that elevation model.
for (int i = this.elevationModels.size() - 1; i >= 0; i--) // iterate from highest resolution to lowest
{
ElevationModel em = this.elevationModels.get(i);
if (!em.isEnabled())
continue;
int c = em.intersects(sector);
if (c != -1)
return em.getDetailHint(sector);
}
// No elevation model intersects the sector. Return a default detail hint.
return 0.0;
}
public int intersects(Sector sector)
{
if (sector == null)
{
String msg = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
boolean intersects = false;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
int c = em.intersects(sector);
if (c == 0) // sector fully contained in the elevation model. no need to test further
return 0;
if (c == 1)
intersects = true;
}
return intersects ? 1 : -1;
}
public boolean contains(Angle latitude, Angle longitude)
{
if (latitude == null || longitude == null)
{
String message = Logging.getMessage("nullValue.LatLonIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
if (em.contains(latitude, longitude))
return true;
}
return false;
}
public double getUnmappedElevation(Angle latitude, Angle longitude)
{
if (latitude == null || longitude == null)
{
String message = Logging.getMessage("nullValue.LatLonIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Find the best elevation available at the specified (latitude, longitude) coordinates.
Double value = this.missingDataFlag;
for (int i = this.elevationModels.size() - 1; i >= 0; i--) // iterate from highest resolution to lowest
{
ElevationModel em = this.elevationModels.get(i);
if (!em.isEnabled())
continue;
if (!em.contains(latitude, longitude))
continue;
double emValue = em.getUnmappedElevation(latitude, longitude);
// Since we're working from highest resolution to lowest, we return the first value that's not a missing
// data flag. Check this against the current ElevationModel's missing data flag, which might be different
// from our own.
if (emValue != em.getMissingDataSignal())
{
value = emValue;
break;
}
}
return value;
}
/**
* {@inheritDoc}
*
* NOTE: This method returns only unmapped elevations if the compound model contains more than one elevation model.
* This enables the compound model's lower resolution elevation models to specify missing data values for the higher
* resolution elevation models.
*
* @param sector the sector in question.
* @param latlons the locations to return elevations for. If a location is null, the output buffer for that
* location is not modified.
* @param targetResolution the desired horizontal resolution, in radians, of the raster or other elevation sample
* from which elevations are drawn. (To compute radians from a distance, divide the distance
* by the radius of the globe, ensuring that both the distance and the radius are in the
* same units.)
* @param buffer an array in which to place the returned elevations. The array must be pre-allocated and
* contain at least as many elements as the list of locations.
*
* @return the resolution achieved, in radians, or {@link Double#MAX_VALUE} if individual elevations cannot be
* determined for all of the locations.
*/
public double getElevations(Sector sector, List extends LatLon> latlons, double targetResolution, double[] buffer)
{
return this.doGetElevations(sector, latlons, targetResolution, buffer, false);
}
/**
* {@inheritDoc}
*
* NOTE: This method returns only unmapped elevations if the compound model contains more than one elevation model.
* This enables the compound model's lower resolution elevation models to specify missing data values for the higher
* resolution elevation models.
*
* @param sector the sector in question.
* @param latlons the locations to return elevations for. If a location is null, the output buffer for that
* location is not modified.
* @param targetResolution the desired horizontal resolution, in radians, of the raster or other elevation sample
* from which elevations are drawn. (To compute radians from a distance, divide the distance
* by the radius of the globe, ensuring that both the distance and the radius are in the
* same units.)
* @param buffer an array in which to place the returned elevations. The array must be pre-allocated and
* contain at least as many elements as the list of locations.
*
* @return the resolution achieved, in radians, or {@link Double#MAX_VALUE} if individual elevations cannot be
* determined for all of the locations.
*/
public double getUnmappedElevations(Sector sector, List extends LatLon> latlons, double targetResolution,
double[] buffer)
{
return this.doGetElevations(sector, latlons, targetResolution, buffer, false);
}
protected double doGetElevations(Sector sector, List extends LatLon> latlons, double targetResolution,
double[] buffer, boolean mapMissingData)
{
if (sector == null)
{
String msg = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (latlons == null)
{
String msg = Logging.getMessage("nullValue.LatLonListIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (buffer == null)
{
String msg = Logging.getMessage("nullValue.ElevationsBufferIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (buffer.length < latlons.size())
{
String msg = Logging.getMessage("ElevationModel.ElevationsBufferTooSmall", latlons.size());
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
// Fill the buffer with ElevationModel contents from lowest resolution to highest, potentially overwriting
// values at each step. ElevationModels are expected to leave the buffer untouched for locations outside their
// coverage area.
double resolutionAchieved = 0;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
int c = em.intersects(sector);
if (c < 0) // no intersection
continue;
double r;
if (mapMissingData || this.elevationModels.size() == 1)
r = em.getElevations(sector, latlons, targetResolution, buffer);
else
r = em.getUnmappedElevations(sector, latlons, targetResolution, buffer);
if (r < resolutionAchieved || resolutionAchieved == 0)
resolutionAchieved = r;
}
return resolutionAchieved;
}
public void composeElevations(Sector sector, List extends LatLon> latlons, int tileWidth,
double[] buffer) throws Exception
{
if (sector == null)
{
String msg = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (latlons == null)
{
String msg = Logging.getMessage("nullValue.LatLonListIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (buffer == null)
{
String msg = Logging.getMessage("nullValue.ElevationsBufferIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (buffer.length < latlons.size())
{
String msg = Logging.getMessage("ElevationModel.ElevationsBufferTooSmall", latlons.size());
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
// Fill the buffer with ElevationModel contents from back to front, potentially overwriting values at each step.
// ElevationModels are expected to leave the buffer untouched when data is missing at a location.
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
int c = em.intersects(sector);
if (c < 0) // no intersection
continue;
em.composeElevations(sector, latlons, tileWidth, buffer);
}
}
public void setNetworkRetrievalEnabled(boolean networkRetrievalEnabled)
{
super.setNetworkRetrievalEnabled(networkRetrievalEnabled);
for (ElevationModel em : this.elevationModels)
{
em.setNetworkRetrievalEnabled(networkRetrievalEnabled);
}
}
@Override
public double getLocalDataAvailability(Sector sector, Double targetResolution)
{
int models = 0;
double availability = 0;
for (ElevationModel em : this.elevationModels)
{
if (!em.isEnabled())
continue;
if (em.intersects(sector) >= 0)
{
availability += em.getLocalDataAvailability(sector, targetResolution);
models++;
}
}
return models > 0 ? availability / models : 1d;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy