UNPKG

leaflet-topography

Version:

a set of tools for calculating and visualizing topography in leafletjs

131 lines (111 loc) 81.3 kB
/* * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). * This devtool is not neither made for production nor for readable output files. * It uses "eval()" calls to create a separate source file in the browser devtools. * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) * or disable the default devtool with "devtool: false". * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("leaflet")); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["Topography"] = factory(require("leaflet")); else root["Topography"] = factory(root["L"]); })(self, function(__WEBPACK_EXTERNAL_MODULE_leaflet__) { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ "./src/TopoLayer.ts": /*!**************************!*\ !*** ./src/TopoLayer.ts ***! \**************************/ /*! namespace exports */ /*! export default [provided] [no usage info] [missing usage info prevents renaming] */ /*! other exports [not provided] [no usage info] */ /*! runtime requirements: __webpack_require__, __webpack_require__.n, __webpack_exports__, __webpack_require__.r, __webpack_require__.d, __webpack_require__.* */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! leaflet */ \"leaflet\");\n/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(leaflet__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\n/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config */ \"./src/config.ts\");\n/* harmony import */ var _workers__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./workers */ \"./src/workers/index.ts\");\n\n\n\n\n\nvar uniqueId = function () {\n var lastId = 0;\n return function () {\n return ++lastId;\n };\n}();\n\nvar TopoLayer = leaflet__WEBPACK_IMPORTED_MODULE_0__.GridLayer.extend({\n /**\n * Before layer gets added to map.\n * Add worker initialization to beforeAdd Method\n */\n beforeAdd: function beforeAdd(map) {\n var _this = this;\n\n // error if there's no token\n var _this$options = this.options,\n token = _this$options.token,\n tilesUrl = _this$options.tilesUrl;\n\n if (!token && !_config__WEBPACK_IMPORTED_MODULE_1__._config.token && !tilesUrl && !_config__WEBPACK_IMPORTED_MODULE_1__._config.tilesUrl) {\n throw new Error('Cannot initialize TopoLayer without mapbox token or custom tilesUrl');\n }\n\n map._addZoomLimit(this); // object to hold canvas contexts as they are created and updated\n\n\n this._contexts = {}; // array to recieve worker objects when they get created\n\n this._workers = []; // grab topotype from options\n\n var topotype = this.options.topotype; // create workers\n\n for (var i = 0; i < 16; i++) {\n var number = i < 9 ? \"0\".concat(i + 1) : i + 1;\n this._workers[i] = new Worker(_workers__WEBPACK_IMPORTED_MODULE_2__.default[topotype], {\n name: \"Worker \".concat(topotype, \" \").concat(number)\n });\n\n this._workers[i].onmessage = function (e) {\n return _this.updateTile(e, _this);\n };\n }\n },\n onRemove: function onRemove(map) {\n // terminate all workers when layer is removed\n for (var i = 0; i < 16; i++) {\n this._workers[i].terminate();\n } // original leaflet code:\n\n\n this._removeAllTiles();\n\n leaflet__WEBPACK_IMPORTED_MODULE_0__.DomUtil.remove(this._container);\n\n map._removeZoomLimit(this);\n\n this._container = null;\n this._tileZoom = undefined;\n },\n\n /**\n * Returns the height function to be used in topolayer webworkers\n * @returns function.toSTring() or null\n */\n heightFunctionString: function heightFunctionString() {\n var _this$options$customi;\n\n if ((_this$options$customi = this.options.customization) === null || _this$options$customi === void 0 ? void 0 : _this$options$customi.heightFunction) {\n var _this$options$customi2;\n\n return (_this$options$customi2 = this.options.customization) === null || _this$options$customi2 === void 0 ? void 0 : _this$options$customi2.heightFunction.toString();\n }\n\n return _config__WEBPACK_IMPORTED_MODULE_1__._config.heightFunction.toString();\n },\n\n /**\n * createTile method required - creates a new tile of the gridlayer\n */\n createTile: function createTile(coords) {\n var _this2 = this;\n\n var tilesUrl = this.options.tilesUrl || _config__WEBPACK_IMPORTED_MODULE_1__._config.tilesUrl;\n var token = this.options.token || _config__WEBPACK_IMPORTED_MODULE_1__._config.token;\n var tile = leaflet__WEBPACK_IMPORTED_MODULE_0__.DomUtil.create('canvas', 'leaflet-tile');\n var size = this.getTileSize();\n tile.width = size.x;\n tile.height = size.y;\n tile.style.opacity = '0';\n tile.style.transition = 'opacity 500ms';\n var ctx = tile.getContext('2d');\n var demCtx;\n var id = uniqueId();\n this._contexts[id] = ctx; // define a new image element and its attributes\n\n var demImg = new Image();\n var x = coords.x,\n y = coords.y,\n z = coords.z;\n demImg.crossOrigin = '*';\n\n demImg.onload = function () {\n var c = document.createElement('canvas');\n c.width = c.height = 256;\n demCtx = c.getContext('2d');\n demCtx.drawImage(demImg, 0, 0);\n redraw();\n };\n\n demImg.src = tilesUrl ? tilesUrl.replace('{z}', \"\".concat(z)).replace('{y}', \"\".concat(y)).replace('{x}', \"\".concat(x)) : \"https://api.mapbox.com/v4/mapbox.terrain-rgb/\".concat(z, \"/\").concat(x, \"/\").concat(y, \".pngraw?access_token=\").concat(token);\n\n var redraw = function redraw() {\n var raster = demCtx.getImageData(0, 0, 256, 256);\n var data = {\n id: id,\n raster: raster,\n RainbowAsString: _utils__WEBPACK_IMPORTED_MODULE_3__.Rainbow.toString(),\n heightFunction: _this2.heightFunctionString(),\n customization: _this2.options.customization\n };\n var workerIndex = (x + y) % _this2._workers.length;\n\n _this2._workers[workerIndex].postMessage(data);\n };\n\n return tile;\n },\n updateTile: function updateTile(e, instance) {\n var ctx = instance._contexts[e.data.id];\n var imgData = ctx.createImageData(256, 256);\n var shades = e.data.shades;\n imgData.data.set(shades);\n ctx.putImageData(imgData, 0, 0);\n ctx.canvas.style.opacity = '1';\n }\n});\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (TopoLayer);\n\n//# sourceURL=webpack://Topography/./src/TopoLayer.ts?"); /***/ }), /***/ "./src/config.ts": /*!***********************!*\ !*** ./src/config.ts ***! \***********************/ /*! namespace exports */ /*! export _config [provided] [no usage info] [missing usage info prevents renaming] */ /*! export _tileCache [provided] [no usage info] [missing usage info prevents renaming] */ /*! export default [provided] [no usage info] [missing usage info prevents renaming] */ /*! other exports [not provided] [no usage info] */ /*! runtime requirements: __webpack_exports__, __webpack_require__.r, __webpack_require__.d, __webpack_require__.* */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"_tileCache\": () => /* binding */ _tileCache,\n/* harmony export */ \"_config\": () => /* binding */ _config,\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n// default cache for saving tiles\nvar _tileCache = {}; // function to set the _config of L.Topography\n\nvar configure = function configure(userConfig) {\n var newConfig = Object.assign(_config, userConfig);\n _config = newConfig;\n return _config;\n}; // configuration object, should not be modified directly, use config function below\n\n\nvar _config = {\n scale: 14,\n spread: 4,\n priority: 'storage',\n saveTile: function saveTile(name, tileData) {\n _tileCache[name] = tileData;\n },\n retrieveTile: function retrieveTile(tileName) {\n return _tileCache[tileName];\n },\n heightFunction: function heightFunction(R, G, B) {\n return -10000 + (R * 256 * 256 + G * 256 + B) * 0.1;\n }\n};\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (configure);\n\n//# sourceURL=webpack://Topography/./src/config.ts?"); /***/ }), /***/ "./src/getTopography.ts": /*!******************************!*\ !*** ./src/getTopography.ts ***! \******************************/ /*! namespace exports */ /*! export default [provided] [no usage info] [missing usage info prevents renaming] */ /*! other exports [not provided] [no usage info] */ /*! runtime requirements: __webpack_require__, __webpack_require__.n, __webpack_exports__, __webpack_require__.r, __webpack_require__.d, __webpack_require__.* */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! leaflet */ \"leaflet\");\n/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(leaflet__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config */ \"./src/config.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\nfunction ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }\n\nfunction _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err); } _next(undefined); }); }; }\n\n\n\n\n\n/**\n * Takes in an L.LatLng and returns { elevation, slope, aspect }\n * @param {Object} latlng | L.LatLng\n * @param userOptions | user options\n */\nfunction getTopography(_x, _x2) {\n return _getTopography.apply(this, arguments);\n}\n\nfunction _getTopography() {\n _getTopography = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(latlng, userOptions) {\n var options, tilesUrl, scale, spread, priority, token, retrieveTile, saveTile, getElevation, _getElevation, getRGBfromImgData, getTileCoord, point, pixelDiff, projectedN, projectedS, projectedE, projectedW, N, S, E, W, elevation, eleN, eleS, eleE, eleW, dx, dy, dzdx, dzdy, resolution, slope, aspect;\n\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n getTileCoord = function _getTileCoord2(projectedPoint) {\n return {\n X: Math.floor(projectedPoint.x / 256),\n Y: Math.floor(projectedPoint.y / 256),\n Z: scale\n };\n };\n\n getRGBfromImgData = function _getRGBfromImgData(imgData, x, y) {\n var index = y * imgData.width + x;\n var i = index * 4;\n var d = imgData.data;\n return {\n R: d[i],\n G: d[i + 1],\n B: d[i + 2],\n A: d[i + 3]\n };\n };\n\n _getElevation = function _getElevation3() {\n _getElevation = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(point) {\n var _getTileCoord, X, Y, Z, tileName, tile, xyPositionOnTile, RGBA, canvas, c, pixelData, _RGBA, R, G, B;\n\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n //\n _getTileCoord = getTileCoord(point), X = _getTileCoord.X, Y = _getTileCoord.Y, Z = _getTileCoord.Z;\n tileName = \"X\".concat(X, \"Y\").concat(Y, \"Z\").concat(Z); // get the tile from the cache\n\n tile = retrieveTile(tileName); // if tile doesn't yet exist, fetch it, wait until its fetched, and rerun this function\n\n if (tile) {\n _context.next = 9;\n break;\n }\n\n _context.next = 6;\n return (0,_utils__WEBPACK_IMPORTED_MODULE_2__.fetchDEMTile)({\n X: X,\n Y: Y,\n Z: Z\n }, {\n tilesUrl: tilesUrl,\n priority: priority,\n token: token,\n saveTile: saveTile\n });\n\n case 6:\n _context.next = 8;\n return getElevation(point);\n\n case 8:\n return _context.abrupt(\"return\", _context.sent);\n\n case 9:\n xyPositionOnTile = {\n x: Math.floor(point.x) - X * 256,\n y: Math.floor(point.y) - Y * 256\n };\n\n if (priority === 'speed') {\n // Tile data already saved as Uint8ClampedArray, just need to pull the RGBA values, quick for high volumes\n RGBA = getRGBfromImgData(tile, xyPositionOnTile.x, xyPositionOnTile.y);\n } else {\n // if (priority === \"storage\")\n // Tile data in form of ImageBitMap, need to call .getImageData for coordinate, much slower for high volumes\n canvas = document.createElement('canvas');\n canvas.width = canvas.height = 256;\n c = canvas.getContext('2d');\n c.drawImage(tile, 0, 0);\n pixelData = c.getImageData(xyPositionOnTile.x, xyPositionOnTile.y, 1, 1).data;\n RGBA = {\n R: pixelData[0],\n G: pixelData[1],\n B: pixelData[2],\n A: pixelData[3]\n };\n }\n\n _RGBA = RGBA, R = _RGBA.R, G = _RGBA.G, B = _RGBA.B;\n return _context.abrupt(\"return\", _config__WEBPACK_IMPORTED_MODULE_1__._config.heightFunction ? _config__WEBPACK_IMPORTED_MODULE_1__._config.heightFunction(R, G, B) : -10000 + (R * 256 * 256 + G * 256 + B) * 0.1);\n\n case 13:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }));\n return _getElevation.apply(this, arguments);\n };\n\n getElevation = function _getElevation2(_x3) {\n return _getElevation.apply(this, arguments);\n };\n\n //\n // SETUP:\n // merge options from configuration _config with option passed in current function call\n options = Object.assign(_config__WEBPACK_IMPORTED_MODULE_1__._config, userOptions);\n tilesUrl = options.tilesUrl, scale = options.scale, spread = options.spread, priority = options.priority, token = options.token, retrieveTile = options.retrieveTile, saveTile = options.saveTile; // Sound alarms if certain config options are not given by user\n\n if (!(!tilesUrl && !token)) {\n _context2.next = 8;\n break;\n }\n\n throw new Error(\"You must provide either a 'tilesUrl' or 'token' proerty in leaflet-topography config / options\");\n\n case 8:\n // -------------------------------------------------------------- //\n // //\n // Central getTopography function using mapbox: //\n // //\n // -------------------------------------------------------------- //\n point = leaflet__WEBPACK_IMPORTED_MODULE_0__.CRS.EPSG3857.latLngToPoint(latlng, scale);\n pixelDiff = spread;\n projectedN = _objectSpread(_objectSpread({}, point), {}, {\n y: point.y - pixelDiff\n }), projectedS = _objectSpread(_objectSpread({}, point), {}, {\n y: point.y + pixelDiff\n }), projectedE = _objectSpread(_objectSpread({}, point), {}, {\n x: point.x + pixelDiff\n }), projectedW = _objectSpread(_objectSpread({}, point), {}, {\n x: point.x - pixelDiff\n }); // @ts-ignore - ts complaining at me about projectedXs not being proper L.Point types\n\n N = leaflet__WEBPACK_IMPORTED_MODULE_0__.CRS.EPSG3857.pointToLatLng(projectedN, scale); // @ts-ignore\n\n S = leaflet__WEBPACK_IMPORTED_MODULE_0__.CRS.EPSG3857.pointToLatLng(projectedS, scale); // @ts-ignore\n\n E = leaflet__WEBPACK_IMPORTED_MODULE_0__.CRS.EPSG3857.pointToLatLng(projectedE, scale); // @ts-ignore\n\n W = leaflet__WEBPACK_IMPORTED_MODULE_0__.CRS.EPSG3857.pointToLatLng(projectedW, scale);\n _context2.next = 17;\n return getElevation({\n x: point.x,\n y: point.y\n });\n\n case 17:\n elevation = _context2.sent;\n _context2.next = 20;\n return getElevation(projectedN);\n\n case 20:\n eleN = _context2.sent;\n _context2.next = 23;\n return getElevation(projectedS);\n\n case 23:\n eleS = _context2.sent;\n _context2.next = 26;\n return getElevation(projectedE);\n\n case 26:\n eleE = _context2.sent;\n _context2.next = 29;\n return getElevation(projectedW);\n\n case 29:\n eleW = _context2.sent;\n dx = E.distanceTo(W), dy = N.distanceTo(S);\n dzdx = (eleE - eleW) / dx, dzdy = (eleN - eleS) / dy;\n resolution = (dx + dy) / 2;\n slope = Math.atan(Math.sqrt(Math.pow(dzdx, 2) + Math.pow(dzdy, 2))) * (180 / Math.PI);\n aspect = dx !== 0 ? (Math.atan2(dzdy, dzdx) * (180 / Math.PI) + 180) % 360 : (90 * (dy > 0 ? 1 : -1) + 180) % 360;\n return _context2.abrupt(\"return\", {\n elevation: elevation,\n slope: slope,\n aspect: aspect,\n resolution: resolution\n });\n\n case 36:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }));\n return _getTopography.apply(this, arguments);\n}\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (getTopography);\n\n//# sourceURL=webpack://Topography/./src/getTopography.ts?"); /***/ }), /***/ "./src/index.ts": /*!**********************!*\ !*** ./src/index.ts ***! \**********************/ /*! namespace exports */ /*! export TopoLayer [provided] [maybe used in main (runtime-defined)] [usage prevents renaming] -> ./src/TopoLayer.ts .default */ /*! export Topography [provided] [maybe used in main (runtime-defined)] [usage prevents renaming] */ /*! export configure [provided] [maybe used in main (runtime-defined)] [usage prevents renaming] -> ./src/config.ts .default */ /*! export default [provided] [maybe used in main (runtime-defined)] [usage prevents renaming] */ /*! export getTopography [provided] [maybe used in main (runtime-defined)] [usage prevents renaming] -> ./src/getTopography.ts .default */ /*! other exports [not provided] [maybe used in main (runtime-defined)] */ /*! runtime requirements: __webpack_require__, __webpack_require__.n, __webpack_exports__, __webpack_require__.d, __webpack_require__.r, __webpack_require__.* */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Topography\": () => /* binding */ Topography,\n/* harmony export */ \"getTopography\": () => /* reexport safe */ _getTopography__WEBPACK_IMPORTED_MODULE_1__.default,\n/* harmony export */ \"TopoLayer\": () => /* reexport safe */ _TopoLayer__WEBPACK_IMPORTED_MODULE_2__.default,\n/* harmony export */ \"configure\": () => /* reexport safe */ _config__WEBPACK_IMPORTED_MODULE_3__.default,\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! leaflet */ \"leaflet\");\n/* harmony import */ var leaflet__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(leaflet__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _getTopography__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./getTopography */ \"./src/getTopography.ts\");\n/* harmony import */ var _TopoLayer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./TopoLayer */ \"./src/TopoLayer.ts\");\n/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./config */ \"./src/config.ts\");\n/* harmony import */ var _preload__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./preload */ \"./src/preload.ts\");\n\n\n\n\n // if in node dev environment, expect and use L to be available as peer dependency\n// if in non-module environment, expect L to be available as global object\n\nvar Leaflet = leaflet__WEBPACK_IMPORTED_MODULE_0__ || window.L;\nvar Topography = {\n getTopography: _getTopography__WEBPACK_IMPORTED_MODULE_1__.default,\n TopoLayer: _TopoLayer__WEBPACK_IMPORTED_MODULE_2__.default,\n configure: _config__WEBPACK_IMPORTED_MODULE_3__.default,\n preload: _preload__WEBPACK_IMPORTED_MODULE_4__.default,\n _tileCache: _config__WEBPACK_IMPORTED_MODULE_3__._tileCache,\n _config: _config__WEBPACK_IMPORTED_MODULE_3__._config\n};\nLeaflet.Topography = Topography;\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Topography);\n\n//# sourceURL=webpack://Topography/./src/index.ts?"); /***/ }), /***/ "./src/preload.ts": /*!************************!*\ !*** ./src/preload.ts ***! \************************/ /*! namespace exports */ /*! export default [provided] [no usage info] [missing usage info prevents renaming] */ /*! other exports [not provided] [no usage info] */ /*! runtime requirements: __webpack_require__, __webpack_require__.n, __webpack_exports__, __webpack_require__.r, __webpack_require__.d, __webpack_require__.* */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n/* harmony import */ var xyz_affair__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! xyz-affair */ \"./node_modules/xyz-affair/index.js\");\n/* harmony import */ var xyz_affair__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(xyz_affair__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config */ \"./src/config.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ \"./src/utils.ts\");\nfunction asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }\n\nfunction _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err); } _next(undefined); }); }; }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\n\nfunction _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === \"string\") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === \"Object\" && o.constructor) n = o.constructor.name; if (n === \"Map\" || n === \"Set\") return Array.from(o); if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }\n\nfunction _iterableToArray(iter) { if (typeof Symbol !== \"undefined\" && Symbol.iterator in Object(iter)) return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }\n\nfunction _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }\n\n\n\n\n/**\n * Takes in array of LatLngBounds objects and returns array of XYZ coordinate objects\n * for all maptiles in those bounds\n * @param {Array} latLngBoundsArray | Array of LatLngBounds objects\n * @param {Number} scale | Map zoom value for which you want to get tile coords\n */\n\nfunction getTileCoords(latLngBoundsArray, scale) {\n var allTileCoordsUnfiltered = [];\n latLngBoundsArray.forEach(function (latlngBounds) {\n var south = latlngBounds.getSouth(),\n north = latlngBounds.getNorth(),\n east = latlngBounds.getEast(),\n west = latlngBounds.getWest();\n var boundsAsArray = [[west, south], [east, north]];\n var tileCoords = xyz_affair__WEBPACK_IMPORTED_MODULE_0___default()(boundsAsArray, scale);\n allTileCoordsUnfiltered = [].concat(_toConsumableArray(allTileCoordsUnfiltered), _toConsumableArray(tileCoords));\n }); // filter duplicate values\n\n var filteredTileCoords = allTileCoordsUnfiltered.filter(function (elem, index, self) {\n return self.findIndex(function (t) {\n return t.x === elem.x && t.y === elem.y && t.z === elem.z;\n }) === index;\n });\n return filteredTileCoords;\n}\n\nvar preload = /*#__PURE__*/function () {\n var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(bounds, userOptions) {\n var options, tilesUrl, token, scale, priority, saveTile, tileCoords;\n return regeneratorRuntime.wrap(function _callee2$(_context2) {\n while (1) {\n switch (_context2.prev = _context2.next) {\n case 0:\n options = Object.assign(_config__WEBPACK_IMPORTED_MODULE_1__._config, userOptions);\n tilesUrl = options.tilesUrl, token = options.token, scale = options.scale, priority = options.priority, saveTile = options.saveTile;\n tileCoords = getTileCoords(bounds, scale);\n tileCoords.forEach( /*#__PURE__*/function () {\n var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(_ref2) {\n var x, y, z, coord;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n x = _ref2.x, y = _ref2.y, z = _ref2.z;\n coord = {\n X: x,\n Y: y,\n Z: z\n };\n _context.next = 4;\n return (0,_utils__WEBPACK_IMPORTED_MODULE_2__.fetchDEMTile)(coord, {\n tilesUrl: tilesUrl,\n priority: priority,\n token: token,\n saveTile: saveTile\n });\n\n case 4:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }));\n\n return function (_x3) {\n return _ref3.apply(this, arguments);\n };\n }());\n\n case 4:\n case \"end\":\n return _context2.stop();\n }\n }\n }, _callee2);\n }));\n\n return function preload(_x, _x2) {\n return _ref.apply(this, arguments);\n };\n}();\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (preload);\n\n//# sourceURL=webpack://Topography/./src/preload.ts?"); /***/ }), /***/ "./src/utils.ts": /*!**********************!*\ !*** ./src/utils.ts ***! \**********************/ /*! namespace exports */ /*! export Rainbow [provided] [no usage info] [missing usage info prevents renaming] */ /*! export fetchDEMTile [provided] [no usage info] [missing usage info prevents renaming] */ /*! export loadImage [provided] [no usage info] [missing usage info prevents renaming] */ /*! other exports [not provided] [no usage info] */ /*! runtime requirements: __webpack_require__.r, __webpack_exports__, __webpack_require__.d, __webpack_require__.* */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"fetchDEMTile\": () => /* binding */ fetchDEMTile,\n/* harmony export */ \"loadImage\": () => /* binding */ loadImage,\n/* harmony export */ \"Rainbow\": () => /* binding */ Rainbow\n/* harmony export */ });\nfunction asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }\n\nfunction _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err); } _next(undefined); }); }; }\n\n/**\n * Takes in a tile coordinate, fetches the tile image, and saves it to the cache in the form of\n * either an ImageData array or an ImageBitman, depending on options.priority\n * @param {Object} tileCoord\n */\nfunction fetchDEMTile(_x, _x2) {\n return _fetchDEMTile.apply(this, arguments);\n}\n/**\n * Takes in image src url as string, returns promise that resolves when image is loaded\n * @param {String} src\n */\n\nfunction _fetchDEMTile() {\n _fetchDEMTile = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(tileCoord, options) {\n var tilesUrl, token, priority, saveTile, X, Y, Z, imageUrl, tileName, transferCanvas, c;\n return regeneratorRuntime.wrap(function _callee$(_context) {\n while (1) {\n switch (_context.prev = _context.next) {\n case 0:\n tilesUrl = options.tilesUrl, token = options.token, priority = options.priority, saveTile = options.saveTile;\n X = tileCoord.X, Y = tileCoord.Y, Z = tileCoord.Z;\n imageUrl = tilesUrl ? tilesUrl.replace('{z}', \"\".concat(Z)).replace('{y}', \"\".concat(Y)).replace('{x}', \"\".concat(X)) : \"https://api.mapbox.com/v4/mapbox.terrain-rgb/\".concat(Z, \"/\").concat(X, \"/\").concat(Y, \".pngraw?access_token=\").concat(token);\n tileName = \"X\".concat(X, \"Y\").concat(Y, \"Z\").concat(Z); // Create a canvas, so I can write the image data to it and then call getImageData on it\n\n transferCanvas = document.createElement('canvas');\n transferCanvas.width = transferCanvas.height = 256;\n c = transferCanvas.getContext('2d');\n _context.next = 9;\n return loadImage(imageUrl).then(function (image) {\n if (priority === 'speed') {\n //\n // MORE STORAGE BUT MUCH FASTER\n // Draw the image to a canvas and then use Canvas2DContext.getImageData to pull the RGBA data\n // in the form of a Uint8Clamped array for the entire tile\n c.drawImage(image, 0, 0, 256, 256);\n var pixelData = c.getImageData(0, 0, 256, 256);\n saveTile(tileName, pixelData);\n } else {\n //\n // if (priority === \"storage\")\n // LESS STORAGE NEEDED BUT MUCH SLOWER:\n // Write the image to an ImageBitMap and then call .getImageData for each pixel inside the getElevation function\n createImageBitmap(image, 0, 0, 256, 256).then(function (ibm) {\n return saveTile(tileName, ibm);\n });\n }\n });\n\n case 9:\n case \"end\":\n return _context.stop();\n }\n }\n }, _callee);\n }));\n return _fetchDEMTile.apply(this, arguments);\n}\n\nfunction loadImage(src) {\n return new Promise(function (resolve, reject) {\n var img = new Image();\n img.crossOrigin = '*';\n img.addEventListener('load', function () {\n return resolve(img);\n });\n img.addEventListener('error', function (err) {\n return reject(err);\n });\n img.src = src;\n });\n}\n/*\nRainbowVis-JS \nReleased under Eclipse Public License - v 1.0\n*/\n\nfunction Rainbow() {\n 'use strict';\n\n var gradients = null;\n var minNum = 0;\n var maxNum = 100;\n var colours = ['ff0000', 'ffff00', '00ff00', '0000ff'];\n setColours(colours);\n\n function setColours(spectrum) {\n if (spectrum.length < 2) {\n throw new Error('Rainbow must have two or more colours.');\n } else {\n var increment = (maxNum - minNum) / (spectrum.length - 1);\n var firstGradient = new ColourGradient();\n firstGradient.setGradient(spectrum[0], spectrum[1]);\n firstGradient.setNumberRange(minNum, minNum + increment);\n gradients = [firstGradient];\n\n for (var i = 1; i < spectrum.length - 1; i++) {\n var colourGradient = new ColourGradient();\n colourGradient.setGradient(spectrum[i], spectrum[i + 1]);\n colourGradient.setNumberRange(minNum + increment * i, minNum + increment * (i + 1));\n gradients[i] = colourGradient;\n }\n\n colours = spectrum;\n }\n }\n\n this.setSpectrum = function () {\n setColours(arguments);\n return this;\n };\n\n this.setSpectrumByArray = function (array) {\n setColours(array);\n return this;\n };\n\n this.colourAt = function (number) {\n if (isNaN(number)) {\n throw new TypeError(number + ' is not a number');\n } else if (gradients.length === 1) {\n return gradients[0].colourAt(number);\n } else {\n var segment = (maxNum - minNum) / gradients.length;\n var index = Math.min(Math.floor((Math.max(number, minNum) - minNum) / segment), gradients.length - 1);\n return gradients[index].colourAt(number);\n }\n };\n\n this.colorAt = this.colourAt;\n\n this.setNumberRange = function (minNumber, maxNumber) {\n if (maxNumber > minNumber) {\n minNum = minNumber;\n maxNum = maxNumber;\n setColours(colours);\n } else {\n throw new RangeError('maxNumber (' + maxNumber + ') is not greater than minNumber (' + minNumber + ')');\n }\n\n return this;\n };\n\n function ColourGradient() {\n 'use strict';\n\n var startColour = 'ff0000';\n var endColour = '0000ff';\n var minNum = 0;\n var maxNum = 100;\n\n this.setGradient = function (colourStart, colourEnd) {\n startColour = getHexColour(colourStart);\n endColour = getHexColour(colourEnd);\n };\n\n this.setNumberRange = function (minNumber, maxNumber) {\n if (maxNumber > minNumber) {\n minNum = minNumber;\n maxNum = maxNumber;\n } else {\n throw new RangeError('maxNumber (' + maxNumber + ') is not greater than minNumber (' + minNumber + ')');\n }\n };\n\n this.colourAt = function (number) {\n return calcHex(number, startColour.substring(0, 2), endColour.substring(0, 2)) + calcHex(number, startColour.substring(2, 4), endColour.substring(2, 4)) + calcHex(number, startColour.substring(4, 6), endColour.substring(4, 6));\n };\n\n function calcHex(number, channelStart_Base16, channelEnd_Base16) {\n var num = number;\n\n if (num < minNum) {\n num = minNum;\n }\n\n if (num > maxNum) {\n num = maxNum;\n }\n\n var numRange = maxNum - minNum;\n var cStart_Base10 = parseInt(channelStart_Base16, 16);\n var cEnd_Base10 = parseInt(channelEnd_Base16, 16);\n var cPerUnit = (cEnd_Base10 - cStart_Base10) / numRange;\n var c_Base10 = Math.round(cPerUnit * (num - minNum) + cStart_Base10);\n return formatHex(c_Base10.toString(16));\n }\n\n function formatHex(hex) {\n if (hex.length === 1) {\n return '0' + hex;\n } else {\n return hex;\n }\n }\n\n function isHexColour(string) {\n var regex = /^#?[0-9a-fA-F]{6}$/i;\n return regex.test(string);\n }\n\n function getHexColour(string) {\n if (isHexColour(string)) {\n return string.substring(string.length - 6, string.length);\n } else {\n var name = string.toLowerCase();\n\n if (colourNames.hasOwnProperty(name)) {\n return colourNames[name];\n }\n\n throw new Error(string + ' is not a valid colour.');\n }\n } // Extended list of CSS colornames s taken from\n // http://www.w3.org/TR/css3-color/#svg-color\n\n\n var colourNames = {\n aliceblue: 'F0F8FF',\n antiquewhite: 'FAEBD7',\n aqua: '00FFFF',\n aquamarine: '7FFFD4',\n azure: 'F0FFFF',\n beige: 'F5F5DC',\n bisque: 'FFE4C4',\n black: '000000',\n blanchedalmond: 'FFEBCD',\n blue: '0000FF',\n blueviolet: '8A2BE2',\n brown: 'A52A2A',\n burlywood: 'DEB887',\n cadetblue: '5F9EA0',\n chartreuse: '7FFF00',\n chocolate: 'D2691E',\n coral: 'FF7F50',\n cornflowerblue: '6495ED',\n cornsilk: 'FFF8DC',\n crimson: 'DC143C',\n cyan: '00FFFF',\n darkblue: '00008B',\n darkcyan: '008B8B',\n darkgoldenrod: 'B8860B',\n darkgray: 'A9A9A9',\n darkgreen: '006400',\n darkgrey: 'A9A9A9',\n darkkhaki: 'BDB76B',\n darkmagenta: '8B008B',\n darkolivegreen: '556B2F',\n darkorange: 'FF8C00',\n darkorchid: '9932CC',\n darkred: '8B0000',\n darksalmon: 'E9967A',\n darkseagreen: '8FBC8F',\n darkslateblue: '483D8B',\n darkslategray: '2F4F4F',\n darkslategrey: '2F4F4F',\n darkturquoise: '00CED1',\n darkviolet: '9400D3',\n deeppink: 'FF1493',\n deepskyblue: '00BFFF',\n dimgray: '696969',\n dimgrey: '696969',\n dodgerblue: '1E90FF',\n firebrick: 'B22222',\n floralwhite: 'FFFAF0',\n forestgreen: '228B22',\n fuchsia: 'FF00FF',\n gainsboro: 'DCDCDC',\n ghostwhite: 'F8F8FF',\n gold: 'FFD700',\n goldenrod: 'DAA520',\n gray: '808080',\n green: '008000',\n greenyellow: 'ADFF2F',\n grey: '808080',\n honeydew: 'F0FFF0',\n hotpink: 'FF69B4',\n indianred: 'CD5C5C',\n indigo: '4B0082',\n ivory: 'FFFFF0',\n khaki: 'F0E68C',\n lavender: 'E6E6FA',\n lavenderblush: 'FFF0F5',\n lawngreen: '7CFC00',\n lemonchiffon: 'FFFACD',\n lightblue: 'ADD8E6',\n lightcoral: 'F08080',\n lightcyan: 'E0FFFF',\n lightgoldenrodyellow: 'FAFAD2',\n lightgray: 'D3D3D3',\n lightgreen: '90EE90',\n lightgrey: 'D3D3D3',\n lightpink: 'FFB6C1',\n lightsalmon: 'FFA07A',\n lightseagreen: '20B2AA',\n lightskyblue: '87CEFA',\n lightslategray: '778899',\n lightslategrey: '778899',\n lightsteelblue: 'B0C4DE',\n lightyellow: 'FFFFE0',\n lime: '00FF00',\n limegreen: '32CD32',\n linen: 'FAF0E6',\n magenta: 'FF00FF',\n maroon: '800000',\n mediumaquamarine: '66CDAA',\n mediumblue: '0000CD',\n mediumorchid: 'BA55D3',\n mediumpurple: '9370DB',\n mediumseagreen: '3CB371',\n mediumslateblue: '7B68EE',\n mediumspringgreen: '00FA9A',\n mediumturquoise: '48D1CC',\n mediumvioletred: 'C71585',\n midnightblue: '191970',\n mintcream: 'F5FFFA',\n mistyrose: 'FFE4E1',\n moccasin: 'FFE4B5',\n navajowhite: 'FFDEAD',\n navy: '000080',\n oldlace: 'FDF5E6',\n olive: '808000',\n olivedrab: '6B8E23',\n orange: 'FFA500',\n orangered: 'FF4500',\n orchid: 'DA70D6',\n palegoldenrod: 'EEE8AA',\n palegreen: '98FB98',\n paleturquoise: 'AFEEEE',\n palevioletred: 'DB7093',\n papayawhip: 'FFEFD5',\n peachpuff: 'FFDAB9',\n peru: 'CD853F',\n pink: 'FFC0CB',\n plum: 'DDA0DD',\n powderblue: 'B0E0E6',\n purple: '800080',\n red: 'FF0000',\n rosybrown: 'BC8F8F',\n royalblue: '4169E1',\n saddlebrown: '8B4513',\n salmon: 'FA8072',\n sandybrown: 'F4A460',\n seagreen: '2E8B57',\n seashell: 'FFF5EE',\n sienna: 'A0522D',\n silver: 'C0C0C0',\n skyblue: '87CEEB',\n slateblue: '6A5ACD',\n slategray: '708090',\n slategrey: '708090',\n snow: 'FFFAFA',\n springgreen: '00FF7F',\n steelblue: '4682B4',\n tan: 'D2B48C',\n teal: '008080',\n thistle: 'D8BFD8',\n tomato: 'FF6347',\n turquoise: '40E0D0',\n violet: 'EE82EE',\n wheat: 'F5DEB3',\n white: 'FFFFFF',\n whitesmoke: 'F5F5F5',\n yellow: 'FFFF00',\n yellowgreen: '9ACD32'\n };\n }\n}\n\n//# sourceURL=webpack://Topography/./src/utils.ts?"); /***/ }), /***/ "./src/workers/aspect.blob.ts": /*!************************************!*\ !*** ./src/workers/aspect.blob.ts ***! \************************************/ /*! namespace exports */ /*! export default [provided] [no usage info] [missing usage info prevents renaming] */ /*! other exports [not provided] [no usage info] */ /*! runtime requirements: __webpack_exports__, __webpack_require__.r, __webpack_require__.d, __webpack_require__.* */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n// @ts-nocheck\n// Build a worker from an anonymous function body\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (URL.createObjectURL(new Blob(['(', function () {\n self.slopeaspects = {};\n\n onmessage = function onmessage(e) {\n var _e$data = e.data,\n customization = _e$data.customization,\n RainbowAsString = _e$data.RainbowAsString,\n heightFunctionAsString = _e$data.heightFunction;\n var rainbowCreator = new Function('return ' + RainbowAsString);\n var Rainbow = rainbowCreator();\n var heightFunctionCreator = new Function('return ' + heightFunctionAsString);\n var heightFunction = heightFunctionCreator();\n\n if (e.data.raster) {\n var data = e.data.raster.data;\n self.slopeaspects[e.data.id] = raster2slopeaspect(data, heightFunction);\n self.shades = shading(Rainbow, self.slopeaspects[e.data.id].slopes, self.slopeaspects[e.data.id].aspects, customization);\n }\n\n postMessage({\n id: e.data.id,\n message: 'from worker',\n ele: self.slopeaspects[e.data.id],\n shades: self.shades\n });\n };\n\n function raster2dem(data, heightFunction) {\n var dem = new Int16Array(256 * 256);\n var x, y, i, j;\n\n var height = heightFunction || function (R, G, B) {\n return -10000 + (R * 256 * 256 + G * 256 + B) * 0.1;\n };\n\n for (x = 0; x < 256; x++) {\n for (y = 0; y < 256; y++) {\n i = x + y * 256;\n j = i * 4;\n dem[i] = height(data[j], data[j + 1], data[j + 2]);\n }\n }\n\n return dem;\n }\n\n function raster2slopeaspect(raster) {\n var dem = raster2dem(raster);\n var aspects = new Float32Array(256 * 256);\n var slopes = new Float32Array(256 * 256);\n var x, y, dx, dy, i;\n\n for (x = 1; x < 255; x++) {\n for (y = 1; y < 255; y++) {\n i = y * 256 + x;\n dx = (dem[i - 255] + 2 * dem[i + 1] + dem[i + 257] - (dem[i - 257] + 2 * dem[i - 1] + dem[i + 255])) / 8;\n dy = (dem[i + 255] + 2 * dem[i + 256] + dem[i + 257] - (dem[i - 257] + 2 * dem[i - 256] + dem[i - 255])) / 8;\n aspects[i] = dx !== 0 ? 90 - Math.atan2(dy, -dx) * (180 / Math.PI) : 90 - 90 * (dy > 0 ? 1 : -1);\n slopes[i] = Math.atan(Math.sqrt(dx * dx + dy * dy)) * 180 / Math.PI;\n }\n }\n /** Shameless Hack:\n * When calculating slope, we can't get values\n * that are on the edge of a tile, as we can't\n * get their neighbors. Their neighbors are on\n * a different tile. Rather than trying to coordinate\n * data between tiles, this loop takes the pixel\n * 2 pixels deep from each edge and copies it to\n * the edge pixel. The hack is 1px wide and barely\n * visible\n */\n\n\n for (x = 0; x < 256; x++) {\n for (y = 0; y < 256; y++) {\n i = y * 256 + x;\n\n if (x === 0) {\n j = y * 256 + x + 1;\n aspects[i] = aspects[j];\n slopes[i] = slopes[j];\n }\n\n if (x === 255) {\n j = y * 256 + x - 1;\n aspects[i] = aspects[j];\n slopes[i] = slopes[j];\n }\n\n if (y === 0) {\n j = (y + 1) * 256 + x;\n aspects[i] = aspects[j];\n slopes[i] = slopes[j];\n }\n\n if (y === 255) {\n j = (y - 1) * 256 + x;\n aspects[i] = aspects[j];\n slopes[i] = slopes[j];\n }\n }\n }\n\n return {\n slopes: slopes,\n aspects: aspects\n };\n }\n\n function shading(Rainbow, slopes, aspects, customization) {\n var continuous, userColors, userBreakpoints, fallback;\n\n if (customization) {\n continuous = customization.continuous === undefined ? true : customization.continuous;\n userColors = customization.colors;\n userBreakpoints = customization.breakpoints;\n fallback = customization.fallback;\n }\n\n function hexToR(h) {\n return parseInt(cutHex(h).substring(0, 2), 16);\n }\n\n function hexToG(h) {\n return parseInt(cutHex(h).substring(2, 4), 16);\n }\n\n function hexToB(h) {\n return parseInt(cutHex(h).substring(