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

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.

There is a newer version: 2.0.0-986
Show newest version
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.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 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 latlons, double targetResolution, double[] buffer) { return this.doGetElevations(sector, latlons, targetResolution, buffer, false); } protected double doGetElevations(Sector sector, List 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 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 - 2024 Weber Informatics LLC | Privacy Policy