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

gov.nasa.worldwind.terrain.WMSBasicElevationModel Maven / Gradle / Ivy

The 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.avlist.*;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.ogc.wms.WMSCapabilities;
import gov.nasa.worldwind.retrieve.*;
import gov.nasa.worldwind.util.*;
import org.w3c.dom.*;

import java.io.*;
import java.net.*;
import java.util.List;

/**
 * @author tag
 * @version $Id: WMSBasicElevationModel.java 2050 2014-06-09 18:52:26Z tgaskins $
 */
public class WMSBasicElevationModel extends BasicElevationModel
{
    private static final String[] formatOrderPreference = new String[]
        {
            "application/bil32", "application/bil16", "application/bil", "image/bil", "image/png", "image/tiff"
        };

    public WMSBasicElevationModel(AVList params)
    {
        super(params);
    }

    public WMSBasicElevationModel(Element domElement, AVList params)
    {
        this(wmsGetParamsFromDocument(domElement, params));
    }

    public WMSBasicElevationModel(WMSCapabilities caps, AVList params)
    {
        this(wmsGetParamsFromCapsDoc(caps, params));
    }

    public WMSBasicElevationModel(String restorableStateInXml)
    {
        super(wmsRestorableStateToParams(restorableStateInXml));

        RestorableSupport rs;
        try
        {
            rs = RestorableSupport.parse(restorableStateInXml);
        }
        catch (Exception e)
        {
            // Parsing the document specified by stateInXml failed.
            String message = Logging.getMessage("generic.ExceptionAttemptingToParseStateXml", restorableStateInXml);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message, e);
        }

        this.doRestoreState(rs, null);
    }

    protected static AVList wmsGetParamsFromDocument(Element domElement, AVList params)
    {
        if (domElement == null)
        {
            String message = Logging.getMessage("nullValue.DocumentIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (params == null)
            params = new AVListImpl();

        DataConfigurationUtils.getWMSLayerConfigParams(domElement, params);
        BasicElevationModel.getBasicElevationModelConfigParams(domElement, params);
        wmsSetFallbacks(params);

        params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(params.getStringValue(AVKey.WMS_VERSION), params));

        return params;
    }

    protected static AVList wmsGetParamsFromCapsDoc(WMSCapabilities caps, AVList params)
    {
        if (caps == null)
        {
            String message = Logging.getMessage("nullValue.WMSCapabilities");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (params == null)
        {
            String message = Logging.getMessage("nullValue.ElevationModelConfigParams");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        String wmsVersion;
        try
        {
            wmsVersion = caps.getVersion();
            getWMSElevationModelConfigParams(caps, formatOrderPreference, params);
        }
        catch (IllegalArgumentException e)
        {
            String message = Logging.getMessage("WMS.MissingLayerParameters");
            Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
            throw new IllegalArgumentException(message, e);
        }
        catch (WWRuntimeException e)
        {
            String message = Logging.getMessage("WMS.MissingCapabilityValues");
            Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
            throw new IllegalArgumentException(message, e);
        }

        wmsSetFallbacks(params);

        params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(wmsVersion, params));

        return params;
    }

    protected static void wmsSetFallbacks(AVList params)
    {
        if (params.getValue(AVKey.LEVEL_ZERO_TILE_DELTA) == null)
        {
            Angle delta = Angle.fromDegrees(20);
            params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(delta, delta));
        }

        if (params.getValue(AVKey.TILE_WIDTH) == null)
            params.setValue(AVKey.TILE_WIDTH, 150);

        if (params.getValue(AVKey.TILE_HEIGHT) == null)
            params.setValue(AVKey.TILE_HEIGHT, 150);

        if (params.getValue(AVKey.FORMAT_SUFFIX) == null)
            params.setValue(AVKey.FORMAT_SUFFIX, ".bil");

        if (params.getValue(AVKey.MISSING_DATA_SIGNAL) == null)
            params.setValue(AVKey.MISSING_DATA_SIGNAL, -9999d);

        if (params.getValue(AVKey.NUM_LEVELS) == null)
            params.setValue(AVKey.NUM_LEVELS, 18); // approximately 20 cm per pixel

        if (params.getValue(AVKey.NUM_EMPTY_LEVELS) == null)
            params.setValue(AVKey.NUM_EMPTY_LEVELS, 0);
    }

    // TODO: consolidate common code in WMSTiledImageLayer.URLBuilder and WMSBasicElevationModel.URLBuilder
    protected static class URLBuilder implements TileUrlBuilder
    {
        protected static final String MAX_VERSION = "1.3.0";

        private final String layerNames;
        private final String styleNames;
        private final String imageFormat;
        private final String wmsVersion;
        private final String crs;
        protected String URLTemplate = null;

        protected URLBuilder(String version, AVList params)
        {
            Double d = (Double) params.getValue(AVKey.MISSING_DATA_SIGNAL);

            this.layerNames = params.getStringValue(AVKey.LAYER_NAMES);
            this.styleNames = params.getStringValue(AVKey.STYLE_NAMES);
            this.imageFormat = params.getStringValue(AVKey.IMAGE_FORMAT);

            String coordSystemKey;
            String defaultCS;
            if (version == null || WWUtil.compareVersion(version, "1.3.0") >= 0) // version 1.3.0 or greater
            {
                this.wmsVersion = MAX_VERSION;
                coordSystemKey = "&crs=";
                defaultCS = "CRS:84"; // would like to do EPSG:4326 but that's incompatible with our old WMS server, see WWJ-474
            }
            else
            {
                this.wmsVersion = version;
                coordSystemKey = "&srs=";
                defaultCS = "EPSG:4326";
            }

            String coordinateSystem = params.getStringValue(AVKey.COORDINATE_SYSTEM);
            this.crs = coordSystemKey + (coordinateSystem != null ? coordinateSystem : defaultCS);
        }

        public URL getURL(gov.nasa.worldwind.util.Tile tile, String altImageFormat) throws MalformedURLException
        {
            StringBuffer sb;
            if (this.URLTemplate == null)
            {
                sb = new StringBuffer(tile.getLevel().getService());

                if (!sb.toString().toLowerCase().contains("service=wms"))
                    sb.append("service=WMS");
                sb.append("&request=GetMap");
                sb.append("&version=");
                sb.append(this.wmsVersion);
                sb.append(this.crs);
                sb.append("&layers=");
                sb.append(this.layerNames);
                sb.append("&styles=");
                sb.append(this.styleNames != null ? this.styleNames : "");
                sb.append("&format=");
                if (altImageFormat == null)
                    sb.append(this.imageFormat);
                else
                    sb.append(altImageFormat);

                this.URLTemplate = sb.toString();
            }
            else
            {
                sb = new StringBuffer(this.URLTemplate);
            }

            sb.append("&width=");
            sb.append(tile.getWidth());
            sb.append("&height=");
            sb.append(tile.getHeight());

            Sector s = tile.getSector();
            sb.append("&bbox=");
            // The order of the coordinate specification matters, and it changed with WMS 1.3.0.
            if (WWUtil.compareVersion(this.wmsVersion, "1.1.1") <= 0 || this.crs.contains("CRS:84"))
            {
                // 1.1.1 and earlier and CRS:84 use lon/lat order
                sb.append(s.getMinLongitude().getDegrees());
                sb.append(",");
                sb.append(s.getMinLatitude().getDegrees());
                sb.append(",");
                sb.append(s.getMaxLongitude().getDegrees());
                sb.append(",");
                sb.append(s.getMaxLatitude().getDegrees());
            }
            else
            {
                // 1.3.0 uses lat/lon ordering
                sb.append(s.getMinLatitude().getDegrees());
                sb.append(",");
                sb.append(s.getMinLongitude().getDegrees());
                sb.append(",");
                sb.append(s.getMaxLatitude().getDegrees());
                sb.append(",");
                sb.append(s.getMaxLongitude().getDegrees());
            }

            sb.append("&"); // terminate the query string

            return new java.net.URL(sb.toString().replace(" ", "%20"));
        }
    }

    //**************************************************************//
    //********************  Configuration  *************************//
    //**************************************************************//

    /**
     * Parses WMSBasicElevationModel configuration parameters from a specified WMS Capabilities source. This writes
     * output as key-value pairs to params. Supported key and parameter names are: 
     * 
ParameterValueType
{@link AVKey#ELEVATION_MAX}WMS layer's * maximum extreme elevationDouble
{@link AVKey#ELEVATION_MIN}WMS layer's * minimum extreme elevationDouble
{@link AVKey#DATA_TYPE}Translate WMS layer's * image format to a matching data typeString
This also parses common WMS layer * parameters by invoking {@link DataConfigurationUtils#getWMSLayerConfigParams(gov.nasa.worldwind.ogc.wms.WMSCapabilities, * String[], gov.nasa.worldwind.avlist.AVList)}. * * @param caps the WMS Capabilities source to parse for WMSBasicElevationModel configuration * parameters. * @param formatOrderPreference an ordered array of preferred image formats, or null to use the default format. * @param params the output key-value pairs which recieve the WMSBasicElevationModel configuration * parameters. * * @return a reference to params. * * @throws IllegalArgumentException if either the document or params are null, or if params does not contain the * required key-value pairs. * @throws gov.nasa.worldwind.exception.WWRuntimeException * if the Capabilities document does not contain any of the required information. */ public static AVList getWMSElevationModelConfigParams(WMSCapabilities caps, String[] formatOrderPreference, AVList params) { if (caps == null) { String message = Logging.getMessage("nullValue.WMSCapabilities"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (params == null) { String message = Logging.getMessage("nullValue.ElevationModelConfigParams"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Get common WMS layer parameters. DataConfigurationUtils.getWMSLayerConfigParams(caps, formatOrderPreference, params); // Attempt to extract the WMS layer names from the specified parameters. String layerNames = params.getStringValue(AVKey.LAYER_NAMES); if (layerNames == null || layerNames.length() == 0) { String message = Logging.getMessage("nullValue.WMSLayerNames"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } String[] names = layerNames.split(","); if (names == null || names.length == 0) { String message = Logging.getMessage("nullValue.WMSLayerNames"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Get the layer's extreme elevations. Double[] extremes = caps.getLayerExtremeElevations(names); Double d = (Double) params.getValue(AVKey.ELEVATION_MIN); if (d == null && extremes != null && extremes[0] != null) params.setValue(AVKey.ELEVATION_MIN, extremes[0]); d = (Double) params.getValue(AVKey.ELEVATION_MAX); if (d == null && extremes != null && extremes[1] != null) params.setValue(AVKey.ELEVATION_MAX, extremes[1]); // Compute the internal pixel type from the image format. if (params.getValue(AVKey.DATA_TYPE) == null && params.getValue(AVKey.IMAGE_FORMAT) != null) { String s = WWIO.makeDataTypeForMimeType(params.getValue(AVKey.IMAGE_FORMAT).toString()); if (s != null) params.setValue(AVKey.DATA_TYPE, s); } // Use the default data type. if (params.getValue(AVKey.DATA_TYPE) == null) params.setValue(AVKey.DATA_TYPE, AVKey.INT16); // Use the default byte order. if (params.getValue(AVKey.BYTE_ORDER) == null) params.setValue(AVKey.BYTE_ORDER, AVKey.LITTLE_ENDIAN); return params; } /** * Appends WMS basic elevation model configuration elements to the superclass configuration document. * * @param params configuration parameters describing this WMS basic elevation model. * * @return a WMS basic elevation model configuration document. */ protected Document createConfigurationDocument(AVList params) { Document doc = super.createConfigurationDocument(params); if (doc == null || doc.getDocumentElement() == null) return doc; DataConfigurationUtils.createWMSLayerConfigElements(params, doc.getDocumentElement()); return doc; } //**************************************************************// //******************** Composition ***************************// //**************************************************************// protected static class ElevationCompositionTile extends ElevationTile { private int width; private int height; private File file; public ElevationCompositionTile(Sector sector, Level level, int width, int height) throws IOException { super(sector, level, -1, -1); // row and column aren't used and need to signal that this.width = width; this.height = height; this.file = File.createTempFile(WWIO.DELETE_ON_EXIT_PREFIX, level.getFormatSuffix()); } @Override public int getWidth() { return this.width; } @Override public int getHeight() { return this.height; } @Override public String getPath() { return this.file.getPath(); } public File getFile() { return this.file; } } 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() || tileWidth > latlons.size()) { String msg = Logging.getMessage("ElevationModel.ElevationsBufferTooSmall", latlons.size()); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } ElevationCompositionTile tile = new ElevationCompositionTile(sector, this.getLevels().getLastLevel(), tileWidth, latlons.size() / tileWidth); this.downloadElevations(tile); tile.setElevations(this.readElevations(tile.getFile().toURI().toURL()), this); for (int i = 0; i < latlons.size(); i++) { LatLon ll = latlons.get(i); if (ll == null) continue; double value = this.lookupElevation(ll.getLatitude(), ll.getLongitude(), tile); // If an elevation at the given location is available, then write that elevation to the destination buffer. // Otherwise do nothing. if (value != this.getMissingDataSignal()) buffer[i] = value; } } protected void downloadElevations(ElevationCompositionTile tile) throws Exception { URL url = tile.getResourceURL(); Retriever retriever = new HTTPRetriever(url, new CompositionRetrievalPostProcessor(tile.getFile())); retriever.setConnectTimeout(10000); retriever.setReadTimeout(60000); retriever.call(); } protected static class CompositionRetrievalPostProcessor extends AbstractRetrievalPostProcessor { // Note: Requested data is never marked as absent because the caller may want to continually re-try retrieval protected File outFile; public CompositionRetrievalPostProcessor(File outFile) { this.outFile = outFile; } protected File doGetOutputFile() { return this.outFile; } @Override protected boolean overwriteExistingFile() { return true; } @Override protected boolean isDeleteOnExit(File outFile) { return outFile.getPath().contains(WWIO.DELETE_ON_EXIT_PREFIX); } } //**************************************************************// //******************** Restorable Support ********************// //**************************************************************// public void getRestorableStateForAVPair(String key, Object value, RestorableSupport rs, RestorableSupport.StateObject context) { if (value instanceof URLBuilder) { rs.addStateValueAsString(context, "wms.Version", ((URLBuilder) value).wmsVersion); rs.addStateValueAsString(context, "wms.Crs", ((URLBuilder) value).crs); } else { super.getRestorableStateForAVPair(key, value, rs, context); } } protected static AVList wmsRestorableStateToParams(String stateInXml) { if (stateInXml == null) { String message = Logging.getMessage("nullValue.StringIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } RestorableSupport rs; try { rs = RestorableSupport.parse(stateInXml); } catch (Exception e) { // Parsing the document specified by stateInXml failed. String message = Logging.getMessage("generic.ExceptionAttemptingToParseStateXml", stateInXml); Logging.logger().severe(message); throw new IllegalArgumentException(message, e); } AVList params = new AVListImpl(); wmsRestoreStateForParams(rs, null, params); return params; } protected static void wmsRestoreStateForParams(RestorableSupport rs, RestorableSupport.StateObject context, AVList params) { // Invoke the BasicElevationModel functionality. restoreStateForParams(rs, null, params); String s = rs.getStateValueAsString(context, AVKey.IMAGE_FORMAT); if (s != null) params.setValue(AVKey.IMAGE_FORMAT, s); s = rs.getStateValueAsString(context, AVKey.TITLE); if (s != null) params.setValue(AVKey.TITLE, s); s = rs.getStateValueAsString(context, AVKey.DISPLAY_NAME); if (s != null) params.setValue(AVKey.DISPLAY_NAME, s); RestorableSupport.adjustTitleAndDisplayName(params); s = rs.getStateValueAsString(context, AVKey.LAYER_NAMES); if (s != null) params.setValue(AVKey.LAYER_NAMES, s); s = rs.getStateValueAsString(context, AVKey.STYLE_NAMES); if (s != null) params.setValue(AVKey.STYLE_NAMES, s); s = rs.getStateValueAsString(context, "wms.Version"); params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(s, params)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy