From 4df27c19d30f12c92f9c54542e3e101463de9069 Mon Sep 17 00:00:00 2001 From: Mark Peterson Date: Sun, 29 Aug 2021 17:11:34 -0500 Subject: [PATCH] WIP --- .idea/runConfigurations/apps_Collada.xml | 4 +- .../examples_BasicExample.xml | 7 - .idea/runConfigurations/examples_Pyramid.xml | 7 + examples/Pyramid.html | 47 ++++ examples/Pyramid.js | 72 +++++ src/WorldWind.js | 6 + src/globe/Tessellator.js | 2 +- src/globe/TiledElevationCoverage.js | 18 +- src/layer/PyramidLayer.js | 54 ++++ src/layer/PyramidMetaData.js | 250 ++++++++++++++++++ src/layer/TiledImageLayer.js | 6 +- src/render/FramebufferTileController.js | 2 +- src/shapes/SurfaceShapeTileBuilder.js | 2 +- src/util/LevelSet.js | 9 +- src/util/Tile.js | 98 ++++--- 15 files changed, 521 insertions(+), 63 deletions(-) delete mode 100644 .idea/runConfigurations/examples_BasicExample.xml create mode 100644 .idea/runConfigurations/examples_Pyramid.xml create mode 100644 examples/Pyramid.html create mode 100644 examples/Pyramid.js create mode 100644 src/layer/PyramidLayer.js create mode 100644 src/layer/PyramidMetaData.js diff --git a/.idea/runConfigurations/apps_Collada.xml b/.idea/runConfigurations/apps_Collada.xml index b6fe4e95..aae9e35f 100644 --- a/.idea/runConfigurations/apps_Collada.xml +++ b/.idea/runConfigurations/apps_Collada.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/.idea/runConfigurations/examples_BasicExample.xml b/.idea/runConfigurations/examples_BasicExample.xml deleted file mode 100644 index ce1f050d..00000000 --- a/.idea/runConfigurations/examples_BasicExample.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/examples_Pyramid.xml b/.idea/runConfigurations/examples_Pyramid.xml new file mode 100644 index 00000000..ccc972db --- /dev/null +++ b/.idea/runConfigurations/examples_Pyramid.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/examples/Pyramid.html b/examples/Pyramid.html new file mode 100644 index 00000000..b944c73e --- /dev/null +++ b/examples/Pyramid.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + +
+ +
+
+

Projection

+ +
+

Layers

+
+
+
+

Destination

+ +
+
+ + Your browser does not support HTML5 Canvas. + +
+
+
+ + \ No newline at end of file diff --git a/examples/Pyramid.js b/examples/Pyramid.js new file mode 100644 index 00000000..3806249f --- /dev/null +++ b/examples/Pyramid.js @@ -0,0 +1,72 @@ +/* + * Copyright 2003-2006, 2009, 2017, 2020 United States Government, as represented + * by the Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License + * at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * NASAWorldWind/WebWorldWind also contains the following 3rd party Open Source + * software: + * + * ES6-Promise – under MIT License + * libtess.js – SGI Free Software License B + * Proj4 – under MIT License + * JSZip – under MIT License + * + * A complete listing of 3rd Party software notices and licenses included in + * WebWorldWind can be found in the WebWorldWind 3rd-party notices and licenses + * PDF found in code directory. + */ +/** + * Illustrates how to build a basic WorldWind globe. + */ +requirejs(['./WorldWindShim', + './LayerManager'], + function (WorldWind, + LayerManager) { + "use strict"; + + // Tell WorldWind to log only warnings and errors. + WorldWind.Logger.setLoggingLevel(WorldWind.Logger.LEVEL_WARNING); + + // Create the WorldWindow. + var wwd = new WorldWind.WorldWindow("canvasOne"); + + // Create and add layers to the WorldWindow. + var layers = [ + // Imagery layers. + {layer: new WorldWind.BMNGLayer(), enabled: true}, + // Add atmosphere layer on top of all base layers. + {layer: new WorldWind.AtmosphereLayer(), enabled: true}, + // WorldWindow UI layers. + {layer: new WorldWind.CompassLayer(), enabled: true}, + {layer: new WorldWind.CoordinatesDisplayLayer(wwd), enabled: true}, + {layer: new WorldWind.ViewControlsLayer(wwd), enabled: true} + ]; + + for (var l = 0; l < layers.length; l++) { + layers[l].layer.enabled = layers[l].enabled; + wwd.addLayer(layers[l].layer); + } + const pyramidMetaData = new WorldWind.PyramidMetaData("../../standalonedata/Earth/melrose/melrose.xml"); + + const xmlLoaded = function (metaData) { + wwd.addLayer(new WorldWind.PyramidLayer("../../standalonedata/Earth/melrose", metaData)); + wwd.redraw(); + }; + pyramidMetaData.load(xmlLoaded); + + wwd.navigator.lookAtLocation = new Location(34.3, -103.8); + wwd.navigator.range = 3000; + + // Create a layer manager for controlling layer visibility. + var layerManager = new LayerManager(wwd); + }); \ No newline at end of file diff --git a/src/WorldWind.js b/src/WorldWind.js index 46c0059a..4cac32c8 100644 --- a/src/WorldWind.js +++ b/src/WorldWind.js @@ -215,6 +215,8 @@ define([ // PLEASE KEEP ALL THIS IN ALPHABETICAL ORDER BY MODULE NAME (not direc './projections/ProjectionPolarEquidistant', './projections/ProjectionUPS', './projections/ProjectionWgs84', + './layer/PyramidLayer', + './layer/PyramidMetaData', './geom/Rectangle', './render/Renderable', './layer/RenderableLayer', @@ -493,6 +495,8 @@ define([ // PLEASE KEEP ALL THIS IN ALPHABETICAL ORDER BY MODULE NAME (not direc ProjectionPolarEquidistant, ProjectionUPS, ProjectionWgs84, + PyramidLayer, + PyramidMetaData, Rectangle, Renderable, RenderableLayer, @@ -1008,6 +1012,8 @@ define([ // PLEASE KEEP ALL THIS IN ALPHABETICAL ORDER BY MODULE NAME (not direc WorldWind['ProjectionPolarEquidistant'] = ProjectionPolarEquidistant; WorldWind['ProjectionUPS'] = ProjectionUPS; WorldWind['ProjectionWgs84'] = ProjectionWgs84; + WorldWind['PyramidLayer'] = PyramidLayer; + WorldWind['PyramidMetaData'] = PyramidMetaData; WorldWind['Rectangle'] = Rectangle; WorldWind['Renderable'] = Renderable; WorldWind['RenderableLayer'] = RenderableLayer; diff --git a/src/globe/Tessellator.js b/src/globe/Tessellator.js index f1020b3d..d1e9ac09 100644 --- a/src/globe/Tessellator.js +++ b/src/globe/Tessellator.js @@ -675,7 +675,7 @@ define(['../geom/Angle', Tessellator.prototype.createTopLevelTiles = function (dc) { this.topLevelTiles[dc.globeStateKey] = []; - Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles[dc.globeStateKey]); + Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles[dc.globeStateKey], this.levels.tileOrigin); }; Tessellator.prototype.addTileOrDescendants = function (dc, tile) { diff --git a/src/globe/TiledElevationCoverage.js b/src/globe/TiledElevationCoverage.js index 80f23e59..2fc4d5f1 100644 --- a/src/globe/TiledElevationCoverage.js +++ b/src/globe/TiledElevationCoverage.js @@ -289,8 +289,11 @@ define(['../util/AbsentResourceList', var level = this.levels.lastLevel(), deltaLat = level.tileDelta.latitude, deltaLon = level.tileDelta.longitude, - r = Tile.computeRow(deltaLat, latitude), - c = Tile.computeColumn(deltaLon, longitude), + origin = this.levels.tileOrigin, + latOrigin = origin.latitude, + lonOrigin = origin.longitude, + r = Tile.computeRow(deltaLat, latitude, latOrigin), + c = Tile.computeColumn(deltaLon, longitude, lonOrigin), tileKey, image = null; @@ -478,10 +481,13 @@ define(['../util/AbsentResourceList', var deltaLat = level.tileDelta.latitude, deltaLon = level.tileDelta.longitude, - firstRow = Tile.computeRow(deltaLat, this.currentSector.minLatitude), - lastRow = Tile.computeLastRow(deltaLat, this.currentSector.maxLatitude), - firstCol = Tile.computeColumn(deltaLon, this.currentSector.minLongitude), - lastCol = Tile.computeLastColumn(deltaLon, this.currentSector.maxLongitude); + origin = this.levels.tileOrigin, + latOrigin = origin.latitude, + lonOrigin = origin.longitude, + firstRow = Tile.computeRow(deltaLat, this.currentSector.minLatitude, latOrigin), + lastRow = Tile.computeRow(deltaLat, this.currentSector.maxLatitude, latOrigin), + firstCol = Tile.computeColumn(deltaLon, this.currentSector.minLongitude, lonOrigin), + lastCol = Tile.computeColumn(deltaLon, this.currentSector.maxLongitude, lonOrigin); for (var row = firstRow; row <= lastRow; row++) { for (var col = firstCol; col <= lastCol; col++) { diff --git a/src/layer/PyramidLayer.js b/src/layer/PyramidLayer.js new file mode 100644 index 00000000..cc1fd90a --- /dev/null +++ b/src/layer/PyramidLayer.js @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2006, 2009, 2017, 2020 United States Government, as represented + * by the Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License + * at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * NASAWorldWind/WebWorldWind also contains the following 3rd party Open Source + * software: + * + * ES6-Promise – under MIT License + * libtess.js – SGI Free Software License B + * Proj4 – under MIT License + * JSZip – under MIT License + * + * A complete listing of 3rd Party software notices and licenses included in + * WebWorldWind can be found in the WebWorldWind 3rd-party notices and licenses + * PDF found in code directory. + */ +/** + * @exports PyramidLayer + */ +define([ + './TiledImageLayer', + './PyramidMetaData', + '../util/WWUtil', + '../util/LevelRowColumnUrlBuilder' + ], + function (TiledImageLayer, + PyramidMetaData, + WWUtil, + LevelRowColumnUrlBuilder) { + "use strict"; + + var PyramidLayer = function (pathToData, metaData) { + TiledImageLayer.call(this, metaData.getSector(), metaData.getLevelZeroTileDelta(), metaData.getLevelCount(), + metaData.getImageFormat(), WWUtil.urlPath("/" + pathToData), + metaData.getTileWidth(), metaData.getTileHeight(), metaData.getTileOrigin()); + this.displayName = metaData.getDisplayName(); + this.pickEnabled = false; + this.urlBuilder = new LevelRowColumnUrlBuilder(null, pathToData); + }; + PyramidLayer.prototype = Object.create(TiledImageLayer.prototype); + + return PyramidLayer; + }); diff --git a/src/layer/PyramidMetaData.js b/src/layer/PyramidMetaData.js new file mode 100644 index 00000000..6c7be68d --- /dev/null +++ b/src/layer/PyramidMetaData.js @@ -0,0 +1,250 @@ +/* + * Copyright 2003-2006, 2009, 2017, 2020 United States Government, as represented + * by the Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License + * at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * NASAWorldWind/WebWorldWind also contains the following 3rd party Open Source + * software: + * + * ES6-Promise – under MIT License + * libtess.js – SGI Free Software License B + * Proj4 – under MIT License + * JSZip – under MIT License + * + * A complete listing of 3rd Party software notices and licenses included in + * WebWorldWind can be found in the WebWorldWind 3rd-party notices and licenses + * PDF found in code directory. + */ +/** + * @exports PyramidMetaData + */ +define([ + '../util/Logger', + '../util/XmlDocument', + '../geom/Location', + '../geom/Sector' + ], + function (Logger, + XmlDocument, + Location, + Sector) { + "use strict"; + + const PyramidAttrs = { + Text: "#text", + DisplayName: "DisplayName", + FormatSuffix: "FormatSuffix", + NumLevels: "NumLevels", + NumEmpty: "numEmpty", + Count: "count", + SectorElement: "Sector", + SouthWest: "SouthWest", + NorthEast: "NorthEast", + LatLon: "LatLon", + Units: "units", + Degrees: "degrees", + Latitude: "latitude", + Longitude: "longitude", + TileOrigin: "TileOrigin", + TileSize: "TileSize", + Dimension: "Dimension", + Height: "height", + Width: "width", + LevelZeroTileDelta: "LevelZeroTileDelta", + ImageFormat: "ImageFormat" + } + + var PyramidMetaData = function (url) { + this.url = url; + this.levelCount = 10; + this.numEmptyLevels = 0; + this.sector = Sector.FULL_SPHERE; + this.tileOrigin = new Location(-90, -180); + this.tileWidth = 512; + this.tileHeight = 512; + this.imageFormat = "image/png"; + }; + + // Location.fromRadians = function (latitudeRadians, longitudeRadians) { + // return new Location(latitudeRadians * Angle.RADIANS_TO_DEGREES, longitudeRadians * Angle.RADIANS_TO_DEGREES); + // }; + + PyramidMetaData.prototype.getChildValue = function (children, attr) { + const childNodes = Array.prototype.slice.call(children); + for (const childNode of childNodes) { + if (childNode.nodeName === attr) { + if (attr === PyramidAttrs.Text) { + return childNode.nodeValue; + } + return childNode; + } + } + return null; + }; + + PyramidMetaData.prototype.getLatLon = function (children) { + const latLon = this.getChildValue(children, PyramidAttrs.LatLon); + if (latLon) { + const latitude = latLon.attributes.getNamedItem(PyramidAttrs.Latitude); + const longitude = latLon.attributes.getNamedItem(PyramidAttrs.Longitude); + if (!latitude || !longitude) { + return null; + } + const units = latLon.attributes.getNamedItem(PyramidAttrs.Units); + if (!units || units.value === PyramidAttrs.Degrees) { + return new Location(parseFloat(latitude.value), parseFloat(longitude.value)); + } else { + return Location.fromRadians(parseFloat(latitude.value), parseFloat(longitude.value)); + } + } + return null; + }; + PyramidMetaData.prototype.processElement = function (element) { + switch (element.nodeName) { + case PyramidAttrs.DisplayName: + this.displayName = this.getChildValue(element.childNodes, PyramidAttrs.Text); + break; + case PyramidAttrs.FormatSuffix: + this.formatSuffix = this.getChildValue(element.childNodes, PyramidAttrs.Text); + break; + case PyramidAttrs.NumLevels: { + let attr = element.attributes.getNamedItem(PyramidAttrs.NumEmpty); + if (attr) { + this.numEmptyLevels = parseInt(attr.value); + } + attr = element.attributes.getNamedItem(PyramidAttrs.Count); + if (attr) { + this.levelCount = parseInt(attr.value); + } + } + break; + case PyramidAttrs.SectorElement: { + const southWest = this.getChildValue(element.childNodes, PyramidAttrs.SouthWest); + let swLatLon; + if (southWest) { + swLatLon = this.getLatLon(southWest.childNodes); + } + const northEast = this.getChildValue(element.childNodes, PyramidAttrs.NorthEast); + let neLatLon; + if (northEast) { + neLatLon = this.getLatLon(northEast.childNodes); + } + if (swLatLon && neLatLon) { + this.sector = new Sector(swLatLon.latitude, neLatLon.latitude, + swLatLon.longitude, neLatLon.longitude); + } + + } + break; + case PyramidAttrs.TileOrigin: { + const origin = this.getLatLon(element.childNodes); + if (origin) { + this.tileOrigin = origin; + } + } + break; + case PyramidAttrs.TileSize: { + const dimension = this.getChildValue(element.childNodes, PyramidAttrs.Dimension); + if (dimension) { + let attr = dimension.attributes.getNamedItem(PyramidAttrs.Height); + if (attr) { + this.tileHeight = parseInt(attr.value); + } + attr = dimension.attributes.getNamedItem(PyramidAttrs.Width); + if (attr) { + this.tileWidth = parseInt(attr.value); + } + } + } + break; + case PyramidAttrs.LevelZeroTileDelta: { + const delta = this.getLatLon(element.childNodes); + if (delta) { + this.levelZeroTileDelta = delta; + } + } + break; + case PyramidAttrs.ImageFormat: + this.imageFormat = this.getChildValue(element.childNodes, PyramidAttrs.Text); + break; + default: { + const childNodes = Array.prototype.slice.call(element.childNodes); + for (const childNode of childNodes) { + this.processElement(childNode); + } + } + break; + } + }; + + PyramidMetaData.prototype.parseDocument = function (xml) { + const document = new XmlDocument(xml).dom(); + this.processElement(document.documentElement); + }; + + PyramidMetaData.prototype.load = function (callback) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + const xhr = new XMLHttpRequest(); + xhr.open("GET", self.url, true); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + self.parseDocument(xhr.responseText); + if (callback) { + callback(self); + } + } + }; + xhr.onerror = function () { + Logger.logMessage(Logger.LEVEL_WARNING, "PyramidMetaData", "load", "Document retrieval failed. " + " " + self.url); + }; + xhr.ontimeout = function () { + Logger.logMessage(Logger.LEVEL_WARNING, "PyramidMetaData", "load", "Document retrieval timed out. " + " " + self.url); + }; + xhr.send(null); + }; + + PyramidMetaData.prototype.getDisplayName = function () { + return this.displayName; + }; + + PyramidMetaData.prototype.getSector = function () { + return this.sector; + }; + + PyramidMetaData.prototype.getLevelZeroTileDelta = function () { + return this.levelZeroTileDelta; + }; + + PyramidMetaData.prototype.getLevelCount = function () { + return this.levelCount; + }; + + PyramidMetaData.prototype.getImageFormat = function () { + return this.imageFormat; + }; + + PyramidMetaData.prototype.getTileWidth = function () { + return this.tileWidth; + }; + + PyramidMetaData.prototype.getTileHeight = function () { + return this.tileHeight; + }; + + PyramidMetaData.prototype.getTileOrigin = function () { + return this.tileOrigin; + }; + + return PyramidMetaData; + }); \ No newline at end of file diff --git a/src/layer/TiledImageLayer.js b/src/layer/TiledImageLayer.js index a7202edb..3e2952a0 100644 --- a/src/layer/TiledImageLayer.js +++ b/src/layer/TiledImageLayer.js @@ -87,7 +87,7 @@ define([ * null or undefined, or if the specified number of levels, tile width or tile height is less than 1. * */ - var TiledImageLayer = function (sector, levelZeroDelta, numLevels, imageFormat, cachePath, tileWidth, tileHeight) { + var TiledImageLayer = function (sector, levelZeroDelta, numLevels, imageFormat, cachePath, tileWidth, tileHeight, tileOrigin) { if (!sector) { throw new ArgumentError( Logger.logMessage(Logger.LEVEL_SEVERE, "TiledImageLayer", "constructor", "missingSector")); @@ -135,7 +135,7 @@ define([ */ this.retrievalQueueSize = WorldWind.configuration.layerRetrievalQueueSize; - this.levels = new LevelSet(sector, levelZeroDelta, numLevels, tileWidth, tileHeight); + this.levels = new LevelSet(sector, levelZeroDelta, numLevels, tileWidth, tileHeight, tileOrigin); /** * Controls the level of detail switching for this layer. The next highest resolution level is @@ -353,7 +353,7 @@ define([ // Documented in superclass. TiledImageLayer.prototype.createTopLevelTiles = function (dc) { this.topLevelTiles = []; - Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles); + Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles, this.levels.tileOrigin); }; // Intentionally not documented. diff --git a/src/render/FramebufferTileController.js b/src/render/FramebufferTileController.js index 71370beb..c2078b53 100644 --- a/src/render/FramebufferTileController.js +++ b/src/render/FramebufferTileController.js @@ -220,7 +220,7 @@ define([ // Internal. Intentionally not documented. FramebufferTileController.prototype.createTopLevelTiles = function () { - Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles); + Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles, this.levels.tileOrigin); }; // Internal. Intentionally not documented. diff --git a/src/shapes/SurfaceShapeTileBuilder.js b/src/shapes/SurfaceShapeTileBuilder.js index 19fe14e6..f9579677 100644 --- a/src/shapes/SurfaceShapeTileBuilder.js +++ b/src/shapes/SurfaceShapeTileBuilder.js @@ -497,7 +497,7 @@ define([ }; SurfaceShapeTileBuilder.prototype.createTopLevelTiles = function () { - Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles); + Tile.createTilesForLevel(this.levels.firstLevel(), this, this.topLevelTiles, this.levels.tileOrigin); }; /** diff --git a/src/util/LevelSet.js b/src/util/LevelSet.js index a211d144..7092a5de 100644 --- a/src/util/LevelSet.js +++ b/src/util/LevelSet.js @@ -57,7 +57,7 @@ define([ * delta values are less than or equal to zero, or any of the number-of-levels, tile-width or tile-height * arguments are less than 1. */ - var LevelSet = function (sector, levelZeroDelta, numLevels, tileWidth, tileHeight) { + var LevelSet = function (sector, levelZeroDelta, numLevels, tileWidth, tileHeight, tileOrigin) { if (!sector) { throw new ArgumentError( Logger.logMessage(Logger.LEVEL_SEVERE, "LevelSet", "constructor", "missingSector")); @@ -101,6 +101,13 @@ define([ */ this.levelZeroDelta = levelZeroDelta; + if (tileOrigin) { + this.tileOrigin = tileOrigin; + } + else { + this.tileOrigin = new Location(-90, -180); + } + /** * The number of levels in this level set. * @type {Number} diff --git a/src/util/Tile.js b/src/util/Tile.js index 3dbf041c..84f6ea05 100644 --- a/src/util/Tile.js +++ b/src/util/Tile.js @@ -433,14 +433,19 @@ define([ * @param {Number} latitude The tile's minimum latitude. * @returns {Number} The computed row number. */ - Tile.computeRow = function (delta, latitude) { - var row = Math.floor((latitude + 90) / delta); - - // If latitude is at the end of the grid, subtract 1 from the computed row to return the last row. - if (latitude == 90) { - row -= 1; + Tile.computeRow = function (delta, latitude, origin) { + // var row = Math.floor((latitude + 90) / delta); + // + // // If latitude is at the end of the grid, subtract 1 from the computed row to return the last row. + // if (latitude == 90) { + // row -= 1; + // } + // + // return row; + let row = Math.floor((latitude - origin) / delta); + if ((latitude - origin) === 180) { + row = row - 1; } - return row; }; @@ -450,14 +455,23 @@ define([ * @param {Number} longitude The tile's minimum longitude. * @returns {Number} The computed column number. */ - Tile.computeColumn = function (delta, longitude) { - var col = Math.floor((longitude + 180) / delta); - - // If longitude is at the end of the grid, subtract 1 from the computed column to return the last column. - if (longitude == 180) { - col -= 1; + Tile.computeColumn = function (delta, longitude, origin) { + // var col = Math.floor((longitude + 180) / delta); + // + // // If longitude is at the end of the grid, subtract 1 from the computed column to return the last column. + // if (longitude == 180) { + // col -= 1; + // } + // + // return col; + let gridLongitude = longitude - origin; + if (gridLongitude < 0) { + gridLongitude = 360 + gridLongitude; + } + let col = Math.floor(gridLongitude / delta); + if ((longitude - origin) === 360) { + col = col - 1; } - return col; }; @@ -467,16 +481,16 @@ define([ * @param {Number} maxLatitude The tile's maximum latitude in degrees. * @returns {Number} The computed row number. */ - Tile.computeLastRow = function (delta, maxLatitude) { - var row = Math.ceil((maxLatitude + 90) / delta - 1); - - // If max latitude is in the first row, set the max row to 0. - if (maxLatitude + 90 < delta) { - row = 0; - } - - return row; - }; + // Tile.computeLastRow = function (delta, maxLatitude) { + // var row = Math.ceil((maxLatitude + 90) / delta - 1); + // + // // If max latitude is in the first row, set the max row to 0. + // if (maxLatitude + 90 < delta) { + // row = 0; + // } + // + // return row; + // }; /** * Computes the last column number for a tile within a level given the tile's maximum longitude. @@ -484,16 +498,16 @@ define([ * @param {Number} maxLongitude The tile's maximum longitude in degrees. * @returns {Number} The computed column number. */ - Tile.computeLastColumn = function (delta, maxLongitude) { - var col = Math.ceil((maxLongitude + 180) / delta - 1); - - // If max longitude is in the first column, set the max column to 0. - if (maxLongitude + 180 < delta) { - col = 0; - } - - return col; - }; + // Tile.computeLastColumn = function (delta, maxLongitude) { + // var col = Math.ceil((maxLongitude + 180) / delta - 1); + // + // // If max longitude is in the first column, set the max column to 0. + // if (maxLongitude + 180 < delta) { + // col = 0; + // } + // + // return col; + // }; /** * Computes a sector spanned by a tile with the specified level number, row and column. @@ -533,7 +547,7 @@ define([ * @param {Tile[]} result An array in which to return the results. * @throws {ArgumentError} If any argument is null or undefined. */ - Tile.createTilesForLevel = function (level, tileFactory, result) { + Tile.createTilesForLevel = function (level, tileFactory, result,origin) { if (!level) { throw new ArgumentError( Logger.logMessage(Logger.LEVEL_SEVERE, "Tile", "createTilesForLevel", "missingLevel")); @@ -554,14 +568,16 @@ define([ deltaLon = level.tileDelta.longitude, sector = level.sector, - firstRow = Tile.computeRow(deltaLat, sector.minLatitude), - lastRow = Tile.computeRow(deltaLat, sector.maxLatitude), + latOrigin = origin.latitude, + lonOrigin = origin.longitude, + firstRow = Tile.computeRow(deltaLat, sector.minLatitude, latOrigin), + lastRow = Tile.computeRow(deltaLat, sector.maxLatitude, latOrigin), - firstCol = Tile.computeColumn(deltaLon, sector.minLongitude), - lastCol = Tile.computeColumn(deltaLon, sector.maxLongitude), + firstCol = Tile.computeColumn(deltaLon, sector.minLongitude, lonOrigin), + lastCol = Tile.computeColumn(deltaLon, sector.maxLongitude, lonOrigin), - firstRowLat = -90 + firstRow * deltaLat, - firstRowLon = -180 + firstCol * deltaLon, + firstRowLat = latOrigin + firstRow * deltaLat, + firstRowLon = lonOrigin + firstCol * deltaLon, minLat = firstRowLat, minLon,