protected static AVList wmsGetParamsFromCapsDoc(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);
    }

    try {
      DataConfigurationUtils.getWMSLayerParams(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);
    }

    setFallbacks(params);

    // Setup WMS URL builder.
    params.setValue(AVKey.WMS_VERSION, caps.getVersion());
    params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(params));
    // Setup default WMS tiled image layer behaviors.
    params.setValue(AVKey.USE_TRANSPARENT_TEXTURES, true);

    return params;
  }
  /**
   * Parses basic elevation model parameters from a specified DOM document. This also parses
   * LevelSet parameters by invoking {@link
   * gov.nasa.worldwind.util.DataConfigurationUtils#getLevelSetParams(org.w3c.dom.Element,
   * gov.nasa.worldwind.avlist.AVList)}. This writes output as key-value pairs to params. If a
   * parameter from the XML document already exists in params, that parameter is ignored. Supported
   * key and parameter names are:
   *
   * <table>
   * <th><td>Key</td><td>Name</td><td>Type</td></th> <tr><td>{@link AVKey#SERVICE_NAME}</td><td>Service/@serviceName</td><td>String</td></tr>
   * <tr><td>{@link AVKey#PIXEL_TYPE}</td><td>DataType</td><td>String</td></tr> <tr><td>{@link
   * AVKey#BYTE_ORDER}</td><td>DataType/@byteOrder</td><td>String</td></tr> <tr><td>{@link
   * AVKey#ELEVATION_EXTREMES_FILE}</td><td>ExtremeElevations/FileName</td><td>String</td></tr> <tr><td>{@link
   * AVKey#ELEVATION_MAX}</td><td>ExtremeElevations/@max</td><td>Double</td></tr> <tr><td>{@link
   * AVKey#ELEVATION_MIN}</td><td>ExtremeElevations/@min</td><td>Double</td></tr> </table>
   *
   * @param domElement the XML document root to parse for basic elevation model parameters.
   * @param params the output key-value pairs which recieve the basic elevation model parameters. A
   *     null reference is permitted.
   * @return a reference to params, or a new AVList if params is null.
   * @throws IllegalArgumentException if the document is null.
   */
  public static AVList getBasicElevationModelParams(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();

    XPath xpath = WWXML.makeXPath();

    // LevelSet properties.
    DataConfigurationUtils.getLevelSetParams(domElement, params);

    // Service properties.
    WWXML.checkAndSetStringParam(
        domElement, params, AVKey.SERVICE_NAME, "Service/@serviceName", xpath);
    WWXML.checkAndSetBooleanParam(
        domElement,
        params,
        AVKey.RETRIEVE_PROPERTIES_FROM_SERVICE,
        "RetrievePropertiesFromService",
        xpath);

    // Image format properties.
    if (params.getValue(AVKey.PIXEL_TYPE) == null) {
      String s = WWXML.getText(domElement, "DataType/@type", xpath);
      if (s != null && s.length() > 0) {
        s = WWXML.parseDataType(s);
        if (s != null && s.length() > 0) params.setValue(AVKey.PIXEL_TYPE, s);
      }
    }

    if (params.getValue(AVKey.BYTE_ORDER) == null) {
      String s = WWXML.getText(domElement, "DataType/@byteOrder", xpath);
      if (s != null && s.length() > 0) {
        s = WWXML.parseByteOrder(s);
        if (s != null && s.length() > 0) params.setValue(AVKey.BYTE_ORDER, s);
      }
    }

    // Elevation data properties.
    WWXML.checkAndSetStringParam(
        domElement, params, AVKey.ELEVATION_EXTREMES_FILE, "ExtremeElevations/FileName", xpath);
    WWXML.checkAndSetDoubleParam(
        domElement, params, AVKey.ELEVATION_MAX, "ExtremeElevations/@max", xpath);
    WWXML.checkAndSetDoubleParam(
        domElement, params, AVKey.ELEVATION_MIN, "ExtremeElevations/@min", xpath);

    return params;
  }
  protected static void wmsRestoreStateToParams(
      RestorableSupport rs, RestorableSupport.StateObject context, AVList params) {
    // Invoke the BasicTiledImageLayer functionality.
    restoreStateForParams(rs, context, params);
    // Parse any legacy WMSTiledImageLayer state values.
    legacyWmsRestoreStateToParams(rs, context, 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");
    if (s != null) params.setValue(AVKey.WMS_VERSION, s);
    params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(params));
  }
  protected static void legacyWmsRestoreStateToParams(
      RestorableSupport rs, RestorableSupport.StateObject context, AVList params) {
    // WMSTiledImageLayer has historically used a different format for storing LatLon and Sector
    // properties
    // in the restorable state XML documents. Although WMSTiledImageLayer no longer writes these
    // properties,
    // we must provide support for reading them here.
    Double lat = rs.getStateValueAsDouble(context, AVKey.LEVEL_ZERO_TILE_DELTA + ".Latitude");
    Double lon = rs.getStateValueAsDouble(context, 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(context, AVKey.SECTOR + ".MinLatitude");
    Double minLon = rs.getStateValueAsDouble(context, AVKey.SECTOR + ".MinLongitude");
    Double maxLat = rs.getStateValueAsDouble(context, AVKey.SECTOR + ".MaxLatitude");
    Double maxLon = rs.getStateValueAsDouble(context, AVKey.SECTOR + ".MaxLongitude");
    if (minLat != null && minLon != null && maxLat != null && maxLon != null)
      params.setValue(AVKey.SECTOR, Sector.fromDegrees(minLat, maxLat, minLon, maxLon));
  }
  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();

    LayerConfiguration.getWMSTiledImageLayerParams(domElement, params);
    BasicTiledImageLayer.getParamsFromDocument(domElement, params);

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

    return params;
  }
  /**
   * Parses WMS elevation model parameters from a specified WMS {@link Capabilities} document. This
   * also parses common WMS layer parameters by invoking {@link
   * DataConfigurationUtils#getWMSLayerParams(gov.nasa.worldwind.wms.Capabilities, String[],
   * gov.nasa.worldwind.avlist.AVList)}. This writes output as key-value pairs to params. Supported
   * key and parameter names are:
   *
   * <table> <th><td>Key</td><td>Value</td><td>Type</td></th> <tr><td>{@link
   * AVKey#ELEVATION_MAX}</td><td>WMS layer's maximum extreme elevation</td><td>Double</td></tr> <tr><td>{@link
   * AVKey#ELEVATION_MIN}</td><td>WMS layer's minimum extreme elevation</td><td>Double</td></tr> <tr><td>{@link
   * AVKey#PIXEL_TYPE}</td><td>Translate WMS layer's image format to a matching pixel type</td><td>String</td></tr>
   * </table>
   *
   * @param caps the WMS Capabilities document to parse for WMS layer 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 WMS layer 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 getWMSElevationModelParams(
      Capabilities 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);
    }

    DataConfigurationUtils.getWMSLayerParams(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(caps, 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]);

    // Determine the internal pixel type from the image format.
    if (params.getValue(AVKey.PIXEL_TYPE) == null) {
      String imageFormat = params.getStringValue(AVKey.IMAGE_FORMAT);
      if (imageFormat != null) {
        if (imageFormat.equals("application/bil32"))
          params.setValue(AVKey.PIXEL_TYPE, AVKey.FLOAT32);
        else if (imageFormat.equals("application/bil16"))
          params.setValue(AVKey.PIXEL_TYPE, AVKey.INT16);
        else if (imageFormat.equals("application/bil"))
          params.setValue(AVKey.PIXEL_TYPE, AVKey.INT16);
        else if (imageFormat.equals("image/bil")) params.setValue(AVKey.PIXEL_TYPE, AVKey.INT16);
      }
    }

    // Use the default pixel type.
    if (params.getValue(AVKey.PIXEL_TYPE) == null) params.setValue(AVKey.PIXEL_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;
  }