Source: layer/WmsLayer.js

  1. /*
  2. * Copyright 2003-2006, 2009, 2017, 2020 United States Government, as represented
  3. * by the Administrator of the National Aeronautics and Space Administration.
  4. * All rights reserved.
  5. *
  6. * The NASAWorldWind/WebWorldWind platform is licensed under the Apache License,
  7. * Version 2.0 (the "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License
  9. * at http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software distributed
  12. * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  13. * CONDITIONS OF ANY KIND, either express or implied. See the License for the
  14. * specific language governing permissions and limitations under the License.
  15. *
  16. * NASAWorldWind/WebWorldWind also contains the following 3rd party Open Source
  17. * software:
  18. *
  19. * ES6-Promise – under MIT License
  20. * libtess.js – SGI Free Software License B
  21. * Proj4 – under MIT License
  22. * JSZip – under MIT License
  23. *
  24. * A complete listing of 3rd Party software notices and licenses included in
  25. * WebWorldWind can be found in the WebWorldWind 3rd-party notices and licenses
  26. * PDF found in code directory.
  27. */
  28. /**
  29. * @exports WmsLayer
  30. */
  31. define([
  32. '../error/ArgumentError',
  33. '../geom/Location',
  34. '../util/Logger',
  35. '../util/PeriodicTimeSequence',
  36. '../geom/Sector',
  37. '../layer/TiledImageLayer',
  38. '../util/WmsUrlBuilder'
  39. ],
  40. function (ArgumentError,
  41. Location,
  42. Logger,
  43. PeriodicTimeSequence,
  44. Sector,
  45. TiledImageLayer,
  46. WmsUrlBuilder) {
  47. "use strict";
  48. /**
  49. * Constructs a WMS image layer.
  50. * @alias WmsLayer
  51. * @constructor
  52. * @augments TiledImageLayer
  53. * @classdesc Displays a WMS image layer.
  54. * @param {{}} config Specifies configuration information for the layer. Must contain the following
  55. * properties:
  56. * <ul>
  57. * <li>service: {String} The URL of the WMS server.</li>
  58. * <li>layerNames: {String} A comma separated list of the names of the WMS layers to include in this layer.</li>
  59. * <li>sector: {Sector} The sector spanned by this layer.</li>
  60. * <li>levelZeroDelta: {Location} The level-zero tile delta to use for this layer.</li>
  61. * <li>numLevels: {Number} The number of levels to make for this layer.</li>
  62. * <li>format: {String} The mime type of the image format to request, e.g., image/png.</li>
  63. * <li>size: {Number} The size in pixels of tiles for this layer.</li>
  64. * <li>coordinateSystem (optional): {String} The coordinate system to use for this layer, e.g., EPSG:4326.</li>
  65. * <li>styleNames (optional): {String} A comma separated list of the styles to include in this layer.</li>
  66. * </ul>
  67. * The function [WmsLayer.formLayerConfiguration]{@link WmsLayer#formLayerConfiguration} will create an
  68. * appropriate configuration object given a {@link WmsLayerCapabilities} object.
  69. * @param {String} timeString The time parameter passed to the WMS server when imagery is requested. May be
  70. * null, in which case no time parameter is passed to the server.
  71. * @throws {ArgumentError} If the specified configuration is null or undefined.
  72. */
  73. var WmsLayer = function (config, timeString) {
  74. if (!config) {
  75. throw new ArgumentError(
  76. Logger.logMessage(Logger.LEVEL_SEVERE, "WmsLayer", "constructor", "No configuration specified."));
  77. }
  78. var cachePath = config.service + config.layerNames + config.styleNames;
  79. if (timeString) {
  80. cachePath = cachePath + timeString;
  81. }
  82. TiledImageLayer.call(this, config.sector, config.levelZeroDelta, config.numLevels, config.format,
  83. cachePath, config.size, config.size);
  84. this.displayName = config.title;
  85. this.pickEnabled = false;
  86. this.urlBuilder = new WmsUrlBuilder(config.service, config.layerNames, config.styleNames, config.version,
  87. timeString);
  88. if (config.coordinateSystem) {
  89. this.urlBuilder.crs = config.coordinateSystem;
  90. }
  91. /**
  92. * The time string passed to this layer's constructor.
  93. * @type {String}
  94. * @readonly
  95. */
  96. this.timeString = timeString;
  97. };
  98. WmsLayer.prototype = Object.create(TiledImageLayer.prototype);
  99. /**
  100. * Forms a configuration object for a specified {@link WmsLayerCapabilities} layer description. The
  101. * configuration object created and returned is suitable for passing to the WmsLayer constructor.
  102. * <p>
  103. * This method also parses any time dimensions associated with the layer and returns them in the
  104. * configuration object's "timeSequences" property. This property is a mixed array of Date objects
  105. * and {@link PeriodicTimeSequence} objects describing the dimensions found.
  106. * @param wmsLayerCapabilities {WmsLayerCapabilities} The WMS layer capabilities to create a configuration for.
  107. * @returns {{}} A configuration object.
  108. * @throws {ArgumentError} If the specified WMS layer capabilities is null or undefined.
  109. */
  110. WmsLayer.formLayerConfiguration = function (wmsLayerCapabilities) {
  111. var config = {
  112. title: wmsLayerCapabilities.title,
  113. version: wmsLayerCapabilities.capability.capsDoc.version
  114. };
  115. // Determine the layer's sector.
  116. var bbox = wmsLayerCapabilities.geographicBoundingBox || wmsLayerCapabilities.latLonBoundingBox;
  117. if (bbox && bbox.westBoundLongitude) {
  118. config.sector = new Sector(bbox.southBoundLatitude, bbox.northBoundLatitude,
  119. bbox.westBoundLongitude, bbox.eastBoundLongitude);
  120. } else if (bbox && bbox.minx) {
  121. config.sector = new Sector(bbox.miny, bbox.maxy, bbox.minx, bbox.maxx);
  122. } else {
  123. config.sector = Sector.FULL_SPHERE;
  124. }
  125. // Determine level 0 delta.
  126. config.levelZeroDelta = new Location(36, 36); // TODO: How to determine best delta
  127. // Determine number of levels.
  128. config.numLevels = 19; // TODO: How to determine appropriate num levels
  129. config.size = 256;
  130. // Assign layer name.
  131. config.layerNames = wmsLayerCapabilities.name;
  132. // Determine image format
  133. var getMapInfo = wmsLayerCapabilities.capability.request.getMap,
  134. formats = getMapInfo.formats;
  135. if (formats.indexOf("image/png") >= 0) {
  136. config.format = "image/png";
  137. } else if (formats.indexOf("image/jpeg") >= 0) {
  138. config.format = "image/jpeg";
  139. } else if (formats.indexOf("image/tiff") >= 0) {
  140. config.format = "image/tiff";
  141. } else if (formats.indexOf("image/gif") >= 0) {
  142. config.format = "image/gif";
  143. }
  144. // Determine the GetMap service address.
  145. config.service = getMapInfo.getUrl;
  146. // Determine the coordinate system to use.
  147. var coordinateSystems = wmsLayerCapabilities.crses; // WMS 1.3.0 and greater
  148. if (!coordinateSystems) {
  149. coordinateSystems = wmsLayerCapabilities.srses; // WMS 1.1.1 and lower
  150. }
  151. if (coordinateSystems) {
  152. if ((coordinateSystems.indexOf("EPSG:4326") >= 0) || (coordinateSystems.indexOf("epsg:4326") >= 0)) {
  153. config.coordinateSystem = "EPSG:4326";
  154. } else if ((coordinateSystems.indexOf("CRS84") >= 0) || (coordinateSystems.indexOf("CRS:84") >= 0)) {
  155. config.coordinateSystem = "CRS:84";
  156. }
  157. }
  158. var dimensions = WmsLayer.parseTimeDimensions(wmsLayerCapabilities);
  159. if (dimensions && dimensions.length > 0) {
  160. config.timeSequences = dimensions;
  161. }
  162. return config;
  163. };
  164. WmsLayer.parseTimeDimensions = function (wmsLayerCapabilities) {
  165. var dimensions = wmsLayerCapabilities.extents || wmsLayerCapabilities.dimensions,
  166. parsedDimensions = null;
  167. if (dimensions) {
  168. parsedDimensions = [];
  169. for (var i = 0; i < dimensions.length; i++) {
  170. var dimension = dimensions[i];
  171. if (dimension.name.toLowerCase() === "time" &&
  172. (!dimension.units || dimension.units.toLowerCase() === "iso8601")) {
  173. var individualDimensions = dimension.content.split(",");
  174. for (var j = 0; j < individualDimensions.length; j++) {
  175. var individualDimension = individualDimensions[j],
  176. splitDimension = individualDimension.split("/");
  177. if (splitDimension.length === 1) {
  178. parsedDimensions.push(new Date(individualDimension));
  179. } else if (splitDimension.length === 3) {
  180. parsedDimensions.push(new PeriodicTimeSequence(individualDimension));
  181. }
  182. }
  183. }
  184. }
  185. }
  186. return parsedDimensions;
  187. };
  188. return WmsLayer;
  189. });