/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.wms;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.util.*;
import org.w3c.dom.*;
import java.net.*;
import java.util.*;
/**
* @author tag
* @version $Id$
*/
public class WMSTiledImageLayer extends BasicTiledImageLayer
{
private AVList creationParams;
public static AVList xmlStateToParams(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();
String s = rs.getStateValueAsString(AVKey.IMAGE_FORMAT);
if (s != null)
params.setValue(AVKey.IMAGE_FORMAT, s);
s = rs.getStateValueAsString(AVKey.DATA_CACHE_NAME);
if (s != null)
params.setValue(AVKey.DATA_CACHE_NAME, s);
s = rs.getStateValueAsString(AVKey.SERVICE);
if (s != null)
params.setValue(AVKey.SERVICE, s);
s = rs.getStateValueAsString(AVKey.TITLE);
if (s != null)
params.setValue(AVKey.TITLE, s);
s = rs.getStateValueAsString(AVKey.DATASET_NAME);
if (s != null)
params.setValue(AVKey.DATASET_NAME, s);
s = rs.getStateValueAsString(AVKey.FORMAT_SUFFIX);
if (s != null)
params.setValue(AVKey.FORMAT_SUFFIX, s);
s = rs.getStateValueAsString(AVKey.LAYER_NAMES);
if (s != null)
params.setValue(AVKey.LAYER_NAMES, s);
s = rs.getStateValueAsString(AVKey.STYLE_NAMES);
if (s != null)
params.setValue(AVKey.STYLE_NAMES, s);
Integer i = rs.getStateValueAsInteger(AVKey.NUM_EMPTY_LEVELS);
if (i != null)
params.setValue(AVKey.NUM_EMPTY_LEVELS, i);
i = rs.getStateValueAsInteger(AVKey.NUM_LEVELS);
if (i != null)
params.setValue(AVKey.NUM_LEVELS, i);
i = rs.getStateValueAsInteger(AVKey.TILE_WIDTH);
if (i != null)
params.setValue(AVKey.TILE_WIDTH, i);
i = rs.getStateValueAsInteger(AVKey.TILE_HEIGHT);
if (i != null)
params.setValue(AVKey.TILE_HEIGHT, i);
Double d = rs.getStateValueAsDouble(AVKey.EXPIRY_TIME);
if (d != null)
params.setValue(AVKey.EXPIRY_TIME, Math.round(d));
Double lat = rs.getStateValueAsDouble(AVKey.LEVEL_ZERO_TILE_DELTA + ".Latitude");
Double lon = rs.getStateValueAsDouble(AVKey.LEVEL_ZERO_TILE_DELTA + ".Longitude");
if (lat != null && lon != null)
params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, LatLon.fromDegrees(lat, lon));
Double minLat = rs.getStateValueAsDouble(AVKey.SECTOR + ".MinLatitude");
Double minLon = rs.getStateValueAsDouble(AVKey.SECTOR + ".MinLongitude");
Double maxLat = rs.getStateValueAsDouble(AVKey.SECTOR + ".MaxLatitude");
Double maxLon = rs.getStateValueAsDouble(AVKey.SECTOR + ".MaxLongitude");
if (minLat != null && minLon != null && maxLat != null && maxLon != null)
params.setValue(AVKey.SECTOR, Sector.fromDegrees(minLat, maxLat, minLon, maxLon));
params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(rs.getStateValueAsString("wms.Version"), params));
return params;
}
public static WMSTiledImageLayer fromRestorableState(String stateInXml)
{
return new WMSTiledImageLayer(stateInXml);
}
public WMSTiledImageLayer(String stateInXml)
{
this(xmlStateToParams(stateInXml));
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);
}
RestorableSupport.StateObject so = rs.getStateObject("avlist");
if (so != null)
{
RestorableSupport.StateObject[] avpairs = rs.getAllStateObjects(so, "");
for (RestorableSupport.StateObject avp : avpairs)
{
if (avp != null)
this.setValue(avp.getName(), avp.getValue());
}
}
}
public WMSTiledImageLayer(AVList params)
{
super(params);
this.creationParams = params.copy();
}
public WMSTiledImageLayer(Capabilities caps, AVList params)
{
super(initParams(caps, params));
this.setUseTransparentTextures(true);
this.setName(
makeTitle(caps, params.getStringValue(AVKey.LAYER_NAMES), params.getStringValue(AVKey.STYLE_NAMES)));
this.creationParams = params.copy();
}
private static AVList initParams(Capabilities 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.LayerConfigParams");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
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);
}
for (String name : names)
{
if (caps.getLayerByName(name) == null)
{
String message = Logging.getMessage("WMS.LayerNameMissing", name);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
}
params.setValue(AVKey.DATASET_NAME, layerNames);
String mapRequestURIString = caps.getGetMapRequestGetURL();
mapRequestURIString = fixGetMapString(mapRequestURIString);
if (params.getValue(AVKey.SERVICE) == null)
params.setValue(AVKey.SERVICE, mapRequestURIString);
mapRequestURIString = params.getStringValue(AVKey.SERVICE);
if (mapRequestURIString == null || mapRequestURIString.length() == 0)
{
Logging.logger().severe("WMS.RequestMapURLMissing");
throw new IllegalArgumentException(Logging.getMessage("WMS.RequestMapURLMissing"));
}
String styleNames = params.getStringValue(AVKey.STYLE_NAMES);
if (params.getValue(AVKey.DATA_CACHE_NAME) == null)
{
try
{
URI mapRequestURI = new URI(mapRequestURIString);
String cacheName = WWIO.formPath(mapRequestURI.getAuthority(), mapRequestURI.getPath(), layerNames,
styleNames);
params.setValue(AVKey.DATA_CACHE_NAME, cacheName);
}
catch (URISyntaxException e)
{
String message = Logging.getMessage("WMS.RequestMapURLBad", mapRequestURIString);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
}
// Determine image format to request.
if (params.getStringValue(AVKey.IMAGE_FORMAT) == null)
{
String imageFormat = chooseImageFormat(caps);
params.setValue(AVKey.IMAGE_FORMAT, imageFormat);
}
if (params.getStringValue(AVKey.IMAGE_FORMAT) == null)
{
Logging.logger().severe("WMS.NoImageFormats");
throw new IllegalArgumentException(Logging.getMessage("WMS.NoImageFormats"));
}
// Determine bounding sector.
Sector sector = (Sector) params.getValue(AVKey.SECTOR);
if (sector == null)
{
for (String name : names)
{
BoundingBox bb = caps.getLayerGeographicBoundingBox(caps.getLayerByName(name));
if (bb == null)
{
Logging.logger().log(java.util.logging.Level.SEVERE, "WMS.NoGeographicBoundingBoxForLayer", name);
continue;
}
sector = Sector.union(sector, Sector.fromDegrees(
clamp(bb.getMiny(), -90d, 90d),
clamp(bb.getMaxy(), -90d, 90d),
clamp(bb.getMinx(), -180d, 180d),
clamp(bb.getMaxx(), -180d, 180d)));
}
if (sector == null)
{
Logging.logger().severe("WMS.NoGeographicBoundingBox");
throw new IllegalArgumentException(Logging.getMessage("WMS.NoGeographicBoundingBox"));
}
params.setValue(AVKey.SECTOR, sector);
}
if (params.getValue(AVKey.LEVEL_ZERO_TILE_DELTA) == null)
{
Angle delta = Angle.fromDegrees(36);
params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(delta, delta));
}
if (params.getValue(AVKey.TILE_WIDTH) == null)
params.setValue(AVKey.TILE_WIDTH, 512);
if (params.getValue(AVKey.TILE_HEIGHT) == null)
params.setValue(AVKey.TILE_HEIGHT, 512);
if (params.getValue(AVKey.FORMAT_SUFFIX) == null)
params.setValue(AVKey.FORMAT_SUFFIX, ".dds");
if (params.getValue(AVKey.NUM_LEVELS) == null)
params.setValue(AVKey.NUM_LEVELS, 14); // approximately 0.5 meters per pixel
if (params.getValue(AVKey.NUM_EMPTY_LEVELS) == null)
params.setValue(AVKey.NUM_EMPTY_LEVELS, 0);
// TODO: adjust for subsetable, fixedimage, etc.
params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(caps.getVersion(), params));
return params;
}
private static double clamp(double v, double min, double max)
{
return v < min ? min : v > max ? max : v;
}
private static String fixGetMapString(String gms)
{
gms = gms.trim();
int qMarkIndex = gms.indexOf("?");
if (qMarkIndex < 0)
gms += "?";
else if (qMarkIndex != gms.length() - 1)
if (gms.lastIndexOf("&") != gms.length() - 1)
gms += "&";
return gms;
}
private static String makeTitle(Capabilities caps, String layerNames, String styleNames)
{
String[] lNames = layerNames.split(",");
String[] sNames = styleNames != null ? styleNames.split(",") : null;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lNames.length; i++)
{
if (sb.length() > 0)
sb.append(", ");
String layerName = lNames[i];
Element layer = caps.getLayerByName(layerName);
String layerTitle = caps.getLayerTitle(layer);
sb.append(layerTitle != null ? layerTitle : layerName);
if (sNames == null || sNames.length <= i)
continue;
String styleName = sNames[i];
Element style = caps.getLayerStyleByName(layer, styleName);
if (style == null)
continue;
sb.append(" : ");
String styleTitle = caps.getStyleTitle(layer, style);
sb.append(styleTitle != null ? styleTitle : styleName);
}
return sb.toString();
}
private static final String[] formatOrderPreference = new String[]
{
"image/dds", "image/png", "image/jpeg"
};
private static String chooseImageFormat(Capabilities caps)
{
String[] formats = caps.getGetMapFormats();
if (formats == null || formats.length == 0)
return null;
for (String s : formatOrderPreference)
{
for (String f : formats)
{
if (f.equalsIgnoreCase(s))
return f;
}
}
return formats[0]; // none recognized; just use the first in the caps list
}
private RestorableSupport makeRestorableState(AVList params)
{
RestorableSupport rs = RestorableSupport.newRestorableSupport();
// Creating a new RestorableSupport failed. RestorableSupport logged the problem, so just return null.
if (rs == null)
return null;
for (Map.Entry<String, Object> p : params.getEntries())
{
if (p.getValue() instanceof LatLon)
{
rs.addStateValueAsDouble(p.getKey() + ".Latitude", ((LatLon) p.getValue()).getLatitude().degrees);
rs.addStateValueAsDouble(p.getKey() + ".Longitude", ((LatLon) p.getValue()).getLongitude().degrees);
}
else if (p.getValue() instanceof Sector)
{
rs.addStateValueAsDouble(p.getKey() + ".MinLatitude", ((Sector) p.getValue()).getMinLatitude().degrees);
rs.addStateValueAsDouble(p.getKey() + ".MaxLatitude", ((Sector) p.getValue()).getMaxLatitude().degrees);
rs.addStateValueAsDouble(p.getKey() + ".MinLongitude",
((Sector) p.getValue()).getMinLongitude().degrees);
rs.addStateValueAsDouble(p.getKey() + ".MaxLongitude",
((Sector) p.getValue()).getMaxLongitude().degrees);
}
else if (p.getValue() instanceof URLBuilder)
{
rs.addStateValueAsString("wms.Version", ((URLBuilder) p.getValue()).wmsVersion);
rs.addStateValueAsString("wms.Crs", ((URLBuilder) p.getValue()).crs);
}
else
{
rs.addStateValueAsString(p.getKey(), p.getValue().toString());
}
}
rs.addStateValueAsBoolean("wms.UseTransparentTextures", this.isUseTransparentTextures());
rs.addStateValueAsString("wms.LayerName", this.getName());
rs.addStateValueAsBoolean("wms.LayerEnabled", this.isEnabled());
RestorableSupport.StateObject so = rs.addStateObject("avlist");
for (Map.Entry<String, Object> p : this.getEntries())
{
if (p.getValue().equals(AVKey.CONSTRUCTION_PARAMETERS))
continue;
rs.addStateValueAsString(so, p.getKey(), p.getValue().toString());
}
return rs;
}
public String getRestorableState()
{
return this.makeRestorableState(this.creationParams).getStateAsXml();
}
public void restoreState(String stateInXml)
{
String message = Logging.getMessage("RestorableSupport.RestoreRequiresConstructor");
Logging.logger().severe(message);
throw new UnsupportedOperationException(message);
}
private static class URLBuilder implements TileUrlBuilder
{
private 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;
public String URLTemplate = null;
private URLBuilder(String version, AVList params)
{
this.layerNames = params.getStringValue(AVKey.LAYER_NAMES);
this.styleNames = params.getStringValue(AVKey.STYLE_NAMES);
this.imageFormat = params.getStringValue(AVKey.IMAGE_FORMAT);
if (version == null || version.compareTo(MAX_VERSION) >= 0)
{
this.wmsVersion = MAX_VERSION;
this.crs = "&crs=CRS:84";
}
else
{
this.wmsVersion = version;
this.crs = "&srs=EPSG:4326";
}
}
public URL getURL(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 : "default");
sb.append("&width=");
sb.append(tile.getLevel().getTileWidth());
sb.append("&height=");
sb.append(tile.getLevel().getTileHeight());
sb.append("&format=");
if (altImageFormat == null)
sb.append(this.imageFormat);
else
sb.append(altImageFormat);
sb.append("&transparent=TRUE");
sb.append("&bgcolor=0x000000");
this.URLTemplate = sb.toString();
}
else
{
sb = new StringBuffer(this.URLTemplate);
}
Sector s = tile.getSector();
sb.append("&bbox=");
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());
sb.append("&"); // terminate the query string
return new java.net.URL(sb.toString().replace(" ", "%20"));
}
//
// private URL getImageTileURL(WMSImageTile tile) throws MalformedURLException
// {
// StringBuffer 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 : "default");
// sb.append("&width=");
// sb.append(tile.imageWidth);
// sb.append("&height=");
// sb.append(tile.imageHeight);
// sb.append("&format=");
// sb.append(tile.imageFormat);
// sb.append("&transparent=TRUE");
// sb.append("&bgcolor=0x000000");
//
// sb.append("&bbox=");
// sb.append(tile.getSector().getMinLongitude().getDegrees());
// sb.append(",");
// sb.append(tile.getSector().getMinLatitude().getDegrees());
// sb.append(",");
// sb.append(tile.getSector().getMaxLongitude().getDegrees());
// sb.append(",");
// sb.append(tile.getSector().getMaxLatitude().getDegrees());
// sb.append("&"); // terminate the query string
//
// return new java.net.URL(sb.toString().replace(" ", "%20"));
// }
}
//
// @Override
// protected URL getImageURL(TextureTile tile) throws MalformedURLException
// {
// StringBuffer 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 : "default");
// sb.append("&width=");
// sb.append(tile.imageWidth);
// sb.append("&height=");
// sb.append(tile.imageHeight);
// sb.append("&format=");
// sb.append(tile.imageFormat);
// sb.append("&transparent=TRUE");
// sb.append("&bgcolor=0x000000");
//
// sb.append("&bbox=");
// sb.append(tile.getSector().getMinLongitude().getDegrees());
// sb.append(",");
// sb.append(tile.getSector().getMinLatitude().getDegrees());
// sb.append(",");
// sb.append(tile.getSector().getMaxLongitude().getDegrees());
// sb.append(",");
// sb.append(tile.getSector().getMaxLatitude().getDegrees());
// sb.append("&"); // terminate the query string
//
// return new java.net.URL(sb.toString().replace(" ", "%20"));
// }
//
// private static class WMSImageTile extends TextureTile
// {
// private final String imageFormat;
// private final int imageWidth;
// private final int imageHeight;
//
// private WMSImageTile(Sector sector, Level level, String imageFormat, int imageWidth, int imageHeight)
// {
// super(sector, level, 0, 0);
//
// this.imageFormat = imageFormat;
// this.imageWidth = imageWidth;
// this.imageHeight = imageHeight;
// }
// }
//
// public BufferedImage getImage(Sector sector, int imageSize, String imageFormat) throws Exception
// {
// if (sector == null)
// {
// String message = Logging.getMessage("nullValue.SectorIsNull");
// Logging.logger().severe(message);
// throw new IllegalStateException(message);
// }
//
// if (imageSize <= 0)
// {
// String message = Logging.getMessage("generic.InvalidImageSize", imageSize, imageSize);
// Logging.logger().severe(message);
// throw new IllegalStateException(message);
// }
//
// if (imageFormat == null)
// {
// String message = Logging.getMessage("nullValue.ImageFomat");
// Logging.logger().severe(message);
// throw new IllegalStateException(message);
// }
//
// WMSImageTile tile =
// new WMSImageTile(sector, this.getLevels().getLastLevel(), imageFormat, imageSize, imageSize);
//
// File imageFile = File.createTempFile("LandPrintPreviewImage", makeSuffixFromContentType(imageFormat));
// imageFile.deleteOnExit();
//
// // Retrieve it from the wms server.
// this.downloadImage(tile.getResourceURL(), imageFile);
//
// // Try to read from disk after retrieving it from the server.
// if (!imageFile.exists())
// {
// String message =
// Logging.getMessage("layers.TiledImageLayer.ImageUnavailable", tile.getResourceURL().toString());
// Logging.logger().warning(message);
// return null;
// }
//
// BufferedImage image = ImageIO.read(imageFile);
// if (image == null)
// {
// String message = Logging.getMessage("generic.ImageFormatUnsupported", imageFormat);
// throw new RuntimeException(message);
// }
//
// return image;
// }
//
// public static String makeSuffixFromContentType(String contentType)
// {
// if (contentType == null)
// {
// String message = Logging.getMessage("nullValue.ImageFomat");
// Logging.logger().severe(message);
// throw new IllegalStateException(message);
// }
//
// if (!contentType.contains("/") || contentType.endsWith("/"))
// {
// String message = Logging.getMessage("generic.InvalidImageFormat");
// Logging.logger().severe(message);
// throw new IllegalStateException(message);
// }
//
// String suffix = contentType.substring(contentType.lastIndexOf("/") + 1);
//
// return "." + suffix.replaceFirst("jpeg", "jpg");
// }
//
// private void downloadImage(URL imageURL, File imageFile) throws Exception
// {
// if (!"http".equalsIgnoreCase(imageURL.getProtocol()))
// {
// String message = Logging.getMessage("layers.TextureLayer.UnknownRetrievalProtocol", imageURL);
// throw new RuntimeException(message);
// }
//
// Retriever retriever = new HTTPRetriever(imageURL, new HttpRetrievalPostProcessor(imageFile));
// retriever.setConnectTimeout(10000);
// retriever.setReadTimeout(20000);
// retriever.call();
// }
//
// private class HttpRetrievalPostProcessor implements RetrievalPostProcessor
// {
// private File imageFile;
//
// public HttpRetrievalPostProcessor(File imageFile)
// {
// this.imageFile = imageFile;
// }
//
// public ByteBuffer run(Retriever retriever)
// {
// if (!retriever.getState().equals(Retriever.RETRIEVER_STATE_SUCCESSFUL))
// return null;
//
// HTTPRetriever htr = (HTTPRetriever) retriever;
// if (htr.getResponseCode() == HttpURLConnection.HTTP_NO_CONTENT)
// return null;
//
// if (htr.getResponseCode() != HttpURLConnection.HTTP_OK)
// return null;
//
// URLRetriever r = (URLRetriever) retriever;
// ByteBuffer buffer = r.getBuffer();
//
// try
// {
// WWIO.saveBuffer(buffer, this.imageFile);
// return buffer;
// }
// catch (IOException e)
// {
// e.printStackTrace(); // TODO: log error
// return null;
// }
// }
// }
}