src.gov.nasa.worldwind.terrain.WMSBasicElevationModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
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.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 1957 2014-04-23 23:32:39Z 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:
* Parameter Value Type {@link AVKey#ELEVATION_MAX} WMS layer's
* maximum extreme elevation Double {@link AVKey#ELEVATION_MIN} WMS layer's
* minimum extreme elevation Double {@link AVKey#DATA_TYPE} Translate WMS layer's
* image format to a matching data type String
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 ***************************//
//**************************************************************//
private 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));
}
}