VRTheWorldTerrainProvider.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. import when from '../ThirdParty/when.js';
  2. import Credit from './Credit.js';
  3. import defaultValue from './defaultValue.js';
  4. import defined from './defined.js';
  5. import defineProperties from './defineProperties.js';
  6. import DeveloperError from './DeveloperError.js';
  7. import Ellipsoid from './Ellipsoid.js';
  8. import Event from './Event.js';
  9. import GeographicTilingScheme from './GeographicTilingScheme.js';
  10. import getImagePixels from './getImagePixels.js';
  11. import HeightmapTerrainData from './HeightmapTerrainData.js';
  12. import CesiumMath from './Math.js';
  13. import Rectangle from './Rectangle.js';
  14. import Resource from './Resource.js';
  15. import TerrainProvider from './TerrainProvider.js';
  16. import TileProviderError from './TileProviderError.js';
  17. function DataRectangle(rectangle, maxLevel) {
  18. this.rectangle = rectangle;
  19. this.maxLevel = maxLevel;
  20. }
  21. /**
  22. * A {@link TerrainProvider} that produces terrain geometry by tessellating height maps
  23. * retrieved from a {@link http://vr-theworld.com/|VT MÄK VR-TheWorld server}.
  24. *
  25. * @alias VRTheWorldTerrainProvider
  26. * @constructor
  27. *
  28. * @param {Object} options Object with the following properties:
  29. * @param {Resource|String} options.url The URL of the VR-TheWorld TileMap.
  30. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid. If this parameter is not
  31. * specified, the WGS84 ellipsoid is used.
  32. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
  33. *
  34. *
  35. * @example
  36. * var terrainProvider = new Cesium.VRTheWorldTerrainProvider({
  37. * url : 'https://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/'
  38. * });
  39. * viewer.terrainProvider = terrainProvider;
  40. *
  41. * @see TerrainProvider
  42. */
  43. function VRTheWorldTerrainProvider(options) {
  44. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  45. //>>includeStart('debug', pragmas.debug);
  46. if (!defined(options.url)) {
  47. throw new DeveloperError('options.url is required.');
  48. }
  49. //>>includeEnd('debug');
  50. var resource = Resource.createIfNeeded(options.url);
  51. this._resource = resource;
  52. this._errorEvent = new Event();
  53. this._ready = false;
  54. this._readyPromise = when.defer();
  55. this._terrainDataStructure = {
  56. heightScale : 1.0 / 1000.0,
  57. heightOffset : -1000.0,
  58. elementsPerHeight : 3,
  59. stride : 4,
  60. elementMultiplier : 256.0,
  61. isBigEndian : true,
  62. lowestEncodedHeight : 0,
  63. highestEncodedHeight : 256 * 256 * 256 - 1
  64. };
  65. var credit = options.credit;
  66. if (typeof credit === 'string') {
  67. credit = new Credit(credit);
  68. }
  69. this._credit = credit;
  70. this._tilingScheme = undefined;
  71. this._rectangles = [];
  72. var that = this;
  73. var metadataError;
  74. var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
  75. function metadataSuccess(xml) {
  76. var srs = xml.getElementsByTagName('SRS')[0].textContent;
  77. if (srs === 'EPSG:4326') {
  78. that._tilingScheme = new GeographicTilingScheme({ellipsoid : ellipsoid});
  79. } else {
  80. metadataFailure('SRS ' + srs + ' is not supported.');
  81. return;
  82. }
  83. var tileFormat = xml.getElementsByTagName('TileFormat')[0];
  84. that._heightmapWidth = parseInt(tileFormat.getAttribute('width'), 10);
  85. that._heightmapHeight = parseInt(tileFormat.getAttribute('height'), 10);
  86. that._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap(ellipsoid, Math.min(that._heightmapWidth, that._heightmapHeight), that._tilingScheme.getNumberOfXTilesAtLevel(0));
  87. var dataRectangles = xml.getElementsByTagName('DataExtent');
  88. for (var i = 0; i < dataRectangles.length; ++i) {
  89. var dataRectangle = dataRectangles[i];
  90. var west = CesiumMath.toRadians(parseFloat(dataRectangle.getAttribute('minx')));
  91. var south = CesiumMath.toRadians(parseFloat(dataRectangle.getAttribute('miny')));
  92. var east = CesiumMath.toRadians(parseFloat(dataRectangle.getAttribute('maxx')));
  93. var north = CesiumMath.toRadians(parseFloat(dataRectangle.getAttribute('maxy')));
  94. var maxLevel = parseInt(dataRectangle.getAttribute('maxlevel'), 10);
  95. that._rectangles.push(new DataRectangle(new Rectangle(west, south, east, north), maxLevel));
  96. }
  97. that._ready = true;
  98. that._readyPromise.resolve(true);
  99. }
  100. function metadataFailure(e) {
  101. var message = defaultValue(e, 'An error occurred while accessing ' + that._resource.url + '.');
  102. metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata);
  103. }
  104. function requestMetadata() {
  105. when(that._resource.fetchXML(), metadataSuccess, metadataFailure);
  106. }
  107. requestMetadata();
  108. }
  109. defineProperties(VRTheWorldTerrainProvider.prototype, {
  110. /**
  111. * Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing
  112. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  113. * are passed an instance of {@link TileProviderError}.
  114. * @memberof VRTheWorldTerrainProvider.prototype
  115. * @type {Event}
  116. */
  117. errorEvent : {
  118. get : function() {
  119. return this._errorEvent;
  120. }
  121. },
  122. /**
  123. * Gets the credit to display when this terrain provider is active. Typically this is used to credit
  124. * the source of the terrain. This function should not be called before {@link VRTheWorldTerrainProvider#ready} returns true.
  125. * @memberof VRTheWorldTerrainProvider.prototype
  126. * @type {Credit}
  127. */
  128. credit : {
  129. get : function() {
  130. return this._credit;
  131. }
  132. },
  133. /**
  134. * Gets the tiling scheme used by this provider. This function should
  135. * not be called before {@link VRTheWorldTerrainProvider#ready} returns true.
  136. * @memberof VRTheWorldTerrainProvider.prototype
  137. * @type {GeographicTilingScheme}
  138. */
  139. tilingScheme : {
  140. get : function() {
  141. //>>includeStart('debug', pragmas.debug);
  142. if (!this.ready) {
  143. throw new DeveloperError('requestTileGeometry must not be called before ready returns true.');
  144. }
  145. //>>includeEnd('debug');
  146. return this._tilingScheme;
  147. }
  148. },
  149. /**
  150. * Gets a value indicating whether or not the provider is ready for use.
  151. * @memberof VRTheWorldTerrainProvider.prototype
  152. * @type {Boolean}
  153. */
  154. ready : {
  155. get : function() {
  156. return this._ready;
  157. }
  158. },
  159. /**
  160. * Gets a promise that resolves to true when the provider is ready for use.
  161. * @memberof VRTheWorldTerrainProvider.prototype
  162. * @type {Promise.<Boolean>}
  163. * @readonly
  164. */
  165. readyPromise : {
  166. get : function() {
  167. return this._readyPromise.promise;
  168. }
  169. },
  170. /**
  171. * Gets a value indicating whether or not the provider includes a water mask. The water mask
  172. * indicates which areas of the globe are water rather than land, so they can be rendered
  173. * as a reflective surface with animated waves. This function should not be
  174. * called before {@link VRTheWorldTerrainProvider#ready} returns true.
  175. * @memberof VRTheWorldTerrainProvider.prototype
  176. * @type {Boolean}
  177. */
  178. hasWaterMask : {
  179. get : function() {
  180. return false;
  181. }
  182. },
  183. /**
  184. * Gets a value indicating whether or not the requested tiles include vertex normals.
  185. * This function should not be called before {@link VRTheWorldTerrainProvider#ready} returns true.
  186. * @memberof VRTheWorldTerrainProvider.prototype
  187. * @type {Boolean}
  188. */
  189. hasVertexNormals : {
  190. get : function() {
  191. return false;
  192. }
  193. }
  194. });
  195. /**
  196. * Requests the geometry for a given tile. This function should not be called before
  197. * {@link VRTheWorldTerrainProvider#ready} returns true. The result includes terrain
  198. * data and indicates that all child tiles are available.
  199. *
  200. * @param {Number} x The X coordinate of the tile for which to request geometry.
  201. * @param {Number} y The Y coordinate of the tile for which to request geometry.
  202. * @param {Number} level The level of the tile for which to request geometry.
  203. * @param {Request} [request] The request object. Intended for internal use only.
  204. * @returns {Promise.<TerrainData>|undefined} A promise for the requested geometry. If this method
  205. * returns undefined instead of a promise, it is an indication that too many requests are already
  206. * pending and the request will be retried later.
  207. */
  208. VRTheWorldTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) {
  209. //>>includeStart('debug', pragmas.debug);
  210. if (!this.ready) {
  211. throw new DeveloperError('requestTileGeometry must not be called before ready returns true.');
  212. }
  213. //>>includeEnd('debug');
  214. var yTiles = this._tilingScheme.getNumberOfYTilesAtLevel(level);
  215. var resource = this._resource.getDerivedResource({
  216. url : level + '/' + x + '/' + (yTiles - y - 1) + '.tif',
  217. queryParameters : {
  218. cesium : true
  219. },
  220. request : request
  221. });
  222. var promise = resource.fetchImage({
  223. preferImageBitmap: true
  224. });
  225. if (!defined(promise)) {
  226. return undefined;
  227. }
  228. var that = this;
  229. return when(promise)
  230. .then(function(image) {
  231. return new HeightmapTerrainData({
  232. buffer : getImagePixels(image),
  233. width : that._heightmapWidth,
  234. height : that._heightmapHeight,
  235. childTileMask : getChildMask(that, x, y, level),
  236. structure : that._terrainDataStructure
  237. });
  238. });
  239. };
  240. /**
  241. * Gets the maximum geometric error allowed in a tile at a given level.
  242. *
  243. * @param {Number} level The tile level for which to get the maximum geometric error.
  244. * @returns {Number} The maximum geometric error.
  245. */
  246. VRTheWorldTerrainProvider.prototype.getLevelMaximumGeometricError = function(level) {
  247. //>>includeStart('debug', pragmas.debug);
  248. if (!this.ready) {
  249. throw new DeveloperError('requestTileGeometry must not be called before ready returns true.');
  250. }
  251. //>>includeEnd('debug');
  252. return this._levelZeroMaximumGeometricError / (1 << level);
  253. };
  254. var rectangleScratch = new Rectangle();
  255. function getChildMask(provider, x, y, level) {
  256. var tilingScheme = provider._tilingScheme;
  257. var rectangles = provider._rectangles;
  258. var parentRectangle = tilingScheme.tileXYToRectangle(x, y, level);
  259. var childMask = 0;
  260. for (var i = 0; i < rectangles.length && childMask !== 15; ++i) {
  261. var rectangle = rectangles[i];
  262. if (rectangle.maxLevel <= level) {
  263. continue;
  264. }
  265. var testRectangle = rectangle.rectangle;
  266. var intersection = Rectangle.intersection(testRectangle, parentRectangle, rectangleScratch);
  267. if (defined(intersection)) {
  268. // Parent tile is inside this rectangle, so at least one child is, too.
  269. if (isTileInRectangle(tilingScheme, testRectangle, x * 2, y * 2, level + 1)) {
  270. childMask |= 4; // northwest
  271. }
  272. if (isTileInRectangle(tilingScheme, testRectangle, x * 2 + 1, y * 2, level + 1)) {
  273. childMask |= 8; // northeast
  274. }
  275. if (isTileInRectangle(tilingScheme, testRectangle, x * 2, y * 2 + 1, level + 1)) {
  276. childMask |= 1; // southwest
  277. }
  278. if (isTileInRectangle(tilingScheme, testRectangle, x * 2 + 1, y * 2 + 1, level + 1)) {
  279. childMask |= 2; // southeast
  280. }
  281. }
  282. }
  283. return childMask;
  284. }
  285. function isTileInRectangle(tilingScheme, rectangle, x, y, level) {
  286. var tileRectangle = tilingScheme.tileXYToRectangle(x, y, level);
  287. return defined(Rectangle.intersection(tileRectangle, rectangle, rectangleScratch));
  288. }
  289. /**
  290. * Determines whether data for a tile is available to be loaded.
  291. *
  292. * @param {Number} x The X coordinate of the tile for which to request geometry.
  293. * @param {Number} y The Y coordinate of the tile for which to request geometry.
  294. * @param {Number} level The level of the tile for which to request geometry.
  295. * @returns {Boolean} Undefined if not supported, otherwise true or false.
  296. */
  297. VRTheWorldTerrainProvider.prototype.getTileDataAvailable = function(x, y, level) {
  298. return undefined;
  299. };
  300. /**
  301. * Makes sure we load availability data for a tile
  302. *
  303. * @param {Number} x The X coordinate of the tile for which to request geometry.
  304. * @param {Number} y The Y coordinate of the tile for which to request geometry.
  305. * @param {Number} level The level of the tile for which to request geometry.
  306. * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded
  307. */
  308. VRTheWorldTerrainProvider.prototype.loadTileDataAvailability = function(x, y, level) {
  309. return undefined;
  310. };
  311. export default VRTheWorldTerrainProvider;