(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._tiles = {}; } WMTSLayer.prototype = { constructor:WMTSLayer, addTo:function(map){ this.map = map; this._transform = map.transform; this._layerId = "vectorTileLayer_"+Date.now(); 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.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; })));