(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? (module.exports = factory()) : typeof define === 'function' && define.amd ? define(factory) : ((global = typeof globalThis !== 'undefined' ? globalThis : global || self), (global.mapboxgl1.WMTSLayer = factory(global))); })(window, function (global) { function clamp(value, min, max) { return value < min ? min : value > max ? max : value; } var caches = { data: {}, get: function (key) { return this.data[key]; }, put: function (key, value) { this.data[key] = value; }, clear: function () { this.data = {}; }, }; var lib = mapboxgl1; var transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII='; var transparentImage = (function () { var canvas = document.createElement('canvas'); canvas.width = 256; canvas.height = 256; var context = canvas.getContext('2d'); context.fillStyle = 'rgba(0,0,0,0)'; context.fillRect(0, 0, 256, 256); return canvas; })(); var vetexShaderSource = ` uniform mat4 u_Matrix; uniform vec4 u_Translate; attribute vec3 a_Position; attribute vec2 a_UV; varying vec2 v_UV; void main(){ v_UV = a_UV; gl_Position = u_Matrix * vec4( (a_Position.xy + u_Translate.xy), 0.0 ,1.0 ); } `; var fragmentShaderSource = ` #ifdef GL_ES precision mediump float; #endif varying vec2 v_UV; uniform sampler2D texture; void main(){ vec4 textureColor = texture2D(texture,v_UV); gl_FragColor = textureColor; } `; function WMTSLayer(options) { this._options = Object.assign( { minzoom: 3, maxzoom: 22, tileSize: 256, }, options ); this._extent = this._options.extent || [-180, -90, 180, 90]; this.map = null; this._transform = null; this._program = null; this._gl = null; this.layerId = null; //当前可视区域的切片 this._tiles = {}; } WMTSLayer.prototype = { constructor: WMTSLayer, addTo: function (map, layerId) { this.map = map; this._transform = map.transform; this.layerId = layerId; map.addLayer({ id: this.layerId, type: 'custom', onAdd: (function (_this) { return function (map, gl) { return _this._onAdd(map, gl, this); }; })(this), render: (function (_this) { return function (gl, matrix) { return _this._render(gl, matrix, this); }; })(this), }); map.on('remove', function () { caches.clear(); }); }, _onAdd: function (map, gl) { var _this = this; this._gl = gl; this.transparentTexture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, this.transparentTexture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, transparentImage); gl.bindTexture(gl.TEXTURE_2D, null); var vetexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vetexShader, vetexShaderSource); gl.compileShader(vetexShader); if (!gl.getShaderParameter(vetexShader, gl.COMPILE_STATUS)) { throw 'Shader Compile Error: ' + gl.getShaderInfoLog(vetexShader); } var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentShaderSource); gl.compileShader(fragmentShader); if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { throw 'Shader Compile Error: ' + gl.getShaderInfoLog(fragmentShader); } var program = (this._program = gl.createProgram()); gl.attachShader(program, vetexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); /** * 属性 */ var attributes = (this._attributes = { aPosition: { name: 'a_Position', location: gl.getAttribLocation(this._program, 'a_Position'), }, aUV: { name: 'a_UV', location: gl.getAttribLocation(this._program, 'a_UV'), }, }); /** * 缓冲区 */ this._buffers = { aPositionBuffer: { buffer: gl.createBuffer(), size: 3, attribute: attributes['aPosition'], points: new Float32Array(3 * 6), update: function (extent) {}, update1: function (extent) { gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); var centerMecatorExtent = extent; var minx = centerMecatorExtent[0], miny = centerMecatorExtent[1], maxx = centerMecatorExtent[2], maxy = centerMecatorExtent[3]; var points = this.points; (points[0] = minx), (points[1] = maxy), (points[2] = 0.0), (points[3] = maxx), (points[4] = maxy), (points[5] = 0.0), (points[6] = minx), (points[7] = miny), (points[8] = 0.0), (points[9] = maxx), (points[10] = maxy), (points[11] = 0.0), (points[12] = minx), (points[13] = miny), (points[14] = 0.0), (points[15] = maxx), (points[16] = miny), (points[17] = 0.0); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.enableVertexAttribArray(this.attribute.location); gl.vertexAttribPointer(this.attribute.location, this.size, gl.FLOAT, false, 0, 0); }, }, aUVBuffer: { buffer: gl.createBuffer(), size: 2, attribute: attributes['aUV'], points: new Float32Array([0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1]), hasBufferData: false, update: function () { gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); if (!this.hasBufferData) { gl.bufferData(gl.ARRAY_BUFFER, this.points, gl.STATIC_DRAW); this.hasBufferData = true; } gl.enableVertexAttribArray(this.attribute.location); gl.vertexAttribPointer(this.attribute.location, this.size, gl.FLOAT, false, 0, 0); }, }, }; /** * 变量 */ this._uniforms = { uMatrix: { value: null, location: gl.getUniformLocation(this._program, 'u_Matrix'), update: function (matrix) { if (this.value !== matrix) { gl.uniformMatrix4fv(this.location, false, matrix); } }, }, uTranslate: { value: [0, 0], location: gl.getUniformLocation(this._program, 'u_Translate'), update: function () {}, }, uTexture: { value: null, location: gl.getUniformLocation(this._program, 'texture'), update: function () {}, }, }; }, /** * 渲染 * @param {*} gl * @param {*} matrix */ _render: function (gl, matrix) { if (this._program) { var transform = this._transform; var options = this._options; var tileSize = options.tileSize || 256; var z = transform.coveringZoomLevel({ tileSize: tileSize, roundZoom: true, }); this.realz = z; z = z < 5 ? 5 : z; this.z = z; if (options.minzoom !== undefined && z < options.minzoom) { z = 0; } if (options.maxzoom !== undefined && z > options.maxzoom) { z = options.maxzoom; } var resolution = (this.resolution = this.getResolutionFromZ(z)); var center = transform.center; //var centerCoord = lib.MercatorCoordinate.fromLngLat(transform.center); var maxx = clamp(center.lng + resolution * tileSize, -180, 180); var miny = clamp(center.lat - resolution * tileSize, -90, 90); var minx = clamp(center.lng, -180, 180), maxy = clamp(center.lat, -90, 90); var leftBottom = lib.MercatorCoordinate.fromLngLat([minx, miny]); var topRight = lib.MercatorCoordinate.fromLngLat([maxx, maxy]); this.centerMecatorExtent = [leftBottom.x, leftBottom.y, topRight.x, topRight.y]; gl.useProgram(this._program); gl.enable(gl.BLEND); gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); for (let key in this._uniforms) { this._uniforms[key].update(matrix); } for (let key in this._buffers) { this._buffers[key].update(); } this.calcTilesInView(); this.renderTiles(); } }, renderTiles() { var gl = this._gl; var tiles = this._tiles; var tile; for (var key in tiles) { tile = tiles[key]; tile.calcExtent(); this._buffers.aPositionBuffer.update1(tile.extent); gl.uniform4fv(this._uniforms.uTranslate.location, tile.translate); gl.activeTexture(gl.TEXTURE0); if (tile.texture) { gl.bindTexture(gl.TEXTURE_2D, tile.texture); } else { gl.bindTexture(gl.TEXTURE_2D, this.transparentTexture); } gl.uniform1i(this._uniforms.uTexture.location, 0); gl.drawArrays(gl.TRIANGLES, 0, 6); } }, /** * 计算当前可视范围内的切片 */ calcTilesInView: function () { var z = this.z; var options = this._options; var tileSize = options.tileSize || 256; var resolution = this.resolution; var extent = this._extent; var tileRes = resolution * tileSize; var viewExtent = this.getViewExtent(); var startX = Math.floor((viewExtent[0] - extent[0]) / tileRes); var startY = Math.floor((extent[3] - viewExtent[3]) / tileRes); var endX = Math.ceil((viewExtent[2] - extent[0]) / tileRes); var endY = Math.ceil((extent[3] - viewExtent[1]) / tileRes); // startX = startX < 20 ? 20 : startX; startY = startY < 1 ? 1 : startY; // endX = endX < 31 ? 31 : endX; //endY = endY < 20 ? 20 : endY; if (this.realz < 5) { endY = endY > 10 ? 10 : endY; } var i, j, key, tile; var tiles = this._tiles; var newTiles = {}; for (i = startX; i < endX; i++) { for (j = startY; j < endY; j++) { key = this._key(z, i, j); if (!tiles[key]) { caches.get(key); if (caches.get(key)) { newTiles[key] = caches.get(key); } else { tile = new Tile(z, i, j, resolution, this); newTiles[key] = tile; } } else { newTiles[key] = tiles[key]; delete tiles[key]; } } } for (var key in tiles) { if (tiles[key].request) { tiles[key].request.cancel(); } } this._tiles = newTiles; }, _key: function (z, x, y) { return z + '/' + x + '/' + y; }, /** * 计算分辨率 */ getResolutionFromZ: function (z) { // return [ // // 1.25764139776733, 0.628820698883665, 0.251528279553466, 0.125764139776733, 0.0628820698883665, 0.0251528279553466, // // 0.0125764139776733, 0.00628820698883665, 0.00251528279553466, 0.00125764139776733, 0.000628820698883665, 0.000251528279553466, // // 0.000125764139776733, 0.0000628820698883665, 0.0000251528279553466, 0.0000125764139776733, 0.00000628820698883665, // // 0.00000251528279553466, 0.00000125764139776733, 0.000000628820698883665, 0.000000251528279553466, // // 0.7031250000000002, 0.3515625000000001, 0.17578125000000006, 0.043945312500000014, 0.021972656250000007, 0.010986328125000003, // // 0.005493164062500002, 0.002746582031250001, 0.0013732910156250004, 6.866455078125002e-4, 3.433227539062501e-4, // // 1.7166137695312505e-4, 8.583068847656253e-5, 4.2915344238281264e-5, 2.1457672119140632e-5, 1.0728836059570316e-5, // // 5.364418029785158e-6, 2.682209014892579e-6, 1.3411045074462895e-6, // ][z]; return 0.7031250000000002 / Math.pow(2, z); // return 1.4062500000000002 / Math.pow(2,z); }, /** * 计算extent */ getViewExtent: function () { var transform = this._transform; var bounds = [ transform.pointLocation(new lib.Point(0, 0)), transform.pointLocation(new lib.Point(transform.width, 0)), transform.pointLocation(new lib.Point(transform.width, transform.height)), transform.pointLocation(new lib.Point(0, transform.height)), ]; var minx, miny, maxx, maxy; for (var i = 0, point = null; i < bounds.length; i++) { point = bounds[i]; if (i == 0) { minx = point.lng; miny = point.lat; maxx = point.lng; i; maxy = point.lat; } else { if (minx > point.lng) minx = point.lng; if (miny > point.lat) miny = point.lat; if (maxx < point.lng) maxx = point.lng; if (maxy < point.lat) maxy = point.lat; } } return [clamp(minx, -180, 180), clamp(miny, -90, 90), clamp(maxx, -180, 180), clamp(maxy, -90, 90)]; }, /** * 重绘 */ repaint: function () { this.map.triggerRepaint(); }, hide: function () { this.map.setLayoutProperty(this.layerId, 'visibility', 'none'); }, show: function () { this.map.setLayoutProperty(this.layerId, 'visibility', 'visible'); }, }; /** * 请求 * @param {*} url * @param {*} callback * @param {*} async */ var getImage = (function () { var MAX_REQUEST_NUM = 16; var requestNum = 0; var requestQuenes = []; function getImage(url, callback) { if (requestNum > MAX_REQUEST_NUM) { var quene = { url: url, callback: callback, canceled: false, cancel: function () { this.canceled = true; }, }; requestQuenes.push(quene); return quene; } var advanced = false; var advanceImageRequestQueue = function () { if (advanced) { return; } advanced = true; requestNum--; while (requestQuenes.length && requestNum < MAX_REQUEST_NUM) { // eslint-disable-line var request = requestQuenes.shift(); var url = request.url; var callback = request.callback; var canceled = request.canceled; if (!canceled) { request.cancel = getImage(url, callback).cancel; } } }; requestNum++; var req = get(url, function (error, data) { advanceImageRequestQueue(); if (!error) { var URL = window.URL || window.webkitURL; var blob = new Blob([data], { type: 'image/png' }); var blobUrl = URL.createObjectURL(blob); var image = new Image(); image.src = blobUrl; image.onload = function () { callback(image); URL.revokeObjectURL(image.src); }; image.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl; } }); return { cancel: function () { req.abort(); }, }; } function get(url, callback, async) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, async === false ? false : true); xhr.responseType = 'arraybuffer'; xhr.onabort = function (event) { callback(true, null); }; xhr.onload = function (event) { if (!xhr.status || (xhr.status >= 200 && xhr.status < 300)) { var source; source = xhr.response; if (source) { try { source = eval('(' + source + ')'); } catch (e) {} } if (source) { callback(false, source); } else { callback(false, null); } } }; xhr.onerror = function (e) { callback(true, null); }; xhr.send(null); return xhr; } return getImage; })(); function Tile(z, x, y, resolution, layer) { this._resolution = resolution; this._layer = layer; this._coord = [z, x, y]; this._gl = layer._gl; this._url = layer._options.url; this.texture = null; this.loaded = false; this.tileSize = layer._options.tileSize; this.worldExtent = layer._extent; this.subdomains = layer._options.subdomains || []; this.extent = [0, 0, 0, 0]; this.translate = [0, 0, 0, 0]; this._load(); } Tile.prototype = { constructor: Tile, calcExtent: function () { var gl = this._gl; var worldExtent = this.worldExtent; var tileSize = this.tileSize; var resolution = this._resolution; var coord = this._coord; var x = coord[1], y = coord[2]; var maxTileNum = Math.ceil((worldExtent[3] - worldExtent[1]) / resolution / tileSize); var minx = clamp(x * tileSize * resolution - worldExtent[2], -180, 180); var maxx = clamp(minx + tileSize * resolution, -180, 180); var maxy = clamp(worldExtent[3] - y * tileSize * resolution, -90, 90); var miny = clamp(maxy - tileSize * resolution, -90, 90); var y1 = y + 1; y1 = y1 > maxTileNum ? maxTileNum : y1; maxy1 = worldExtent[3] - y1 * tileSize * resolution; var bl = lib.MercatorCoordinate.fromLngLat([minx, miny]); var tr = lib.MercatorCoordinate.fromLngLat([maxx, maxy]); this.extent[0] = bl.x; this.extent[1] = bl.y; this.extent[2] = tr.x; this.extent[3] = tr.y; //var centerMecatorExtent = this._layer.centerMecatorExtent; // if(!this.translate){ // this.translate = [0,0,0,0]; // } // this.translate[0] = bl.x - centerMecatorExtent[0]; // this.translate[1] = bl.y - centerMecatorExtent[1]; // this.translate[2] = tr.x - centerMecatorExtent[2]; // this.translate[3] = tr.y - centerMecatorExtent[3]; }, _load: function () { var gl = this._gl; var _this = this; var z = this._coord[0]; var x = this._coord[1]; var y = this._coord[2]; var url = this._url .replace('{s}', this.subdomains[Math.floor(Math.random() * this.subdomains.length)]) .replace('{x}', x) .replace('{y}', y) .replace('{z}', z); this.request = getImage(url, function (img) { delete _this.request; if (_this._gl) { var texture = (_this.texture = gl.createTexture()); gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); gl.bindTexture(gl.TEXTURE_2D, null); caches.put(z + '/' + x + '/' + y, _this); _this.loaded = true; _this._layer.repaint(); } }); }, }; return WMTSLayer; });