关于mapbox:简单使用-MapboxGL-自定义图层绘制带贴图的矩形

写文目标不是常常用,可能会遗记根本用法,自留。 贴图 成果 留神点形成矩形的两个三角形须要应用 mapboxgl.MercatorCoordinate.fromLngLat 办法转换至 WebGL 中的场景世界坐标,留神案例中是如何设置三角形程序的,要逆时针render 函数是每一帧绘制的调用函数,每一帧都要设置一次 program、texture、vertexBuffer,能力触发 draw,并且尤为留神矩阵 uniform 的地址,也是每一帧都要获取最新的(gl.getUniformLocation(this.program, 'u_matrix'))贴图的长宽尺寸要用 2 的次幂源码拜访令牌请应用本人的,代码不做解释,根本的正文已齐备。 页面局部(省略,请本人补全): <link href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" rel="stylesheet"><script src="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js"></script><style>#map { width: 90vw; height: 90vh;}</style><div id="map"></div><script> // js 局部,见下</script>js 局部: mapboxgl.accessToken = '你的令牌'const map = new mapboxgl.Map({ container: 'map', zoom: 7, center: [112.5, 22.5], style: 'mapbox://styles/mapbox/light-v10', // 开启 WebGL 的 msaa(抗锯齿) antialias: true})const vertexSource = /* glsl */`uniform mat4 u_matrix;attribute vec3 a_pos;attribute vec2 a_uv;varying vec2 v_uv;void main() { v_uv = a_uv; gl_Position = u_matrix * vec4(a_pos, 1.0);}`const fragmentSource = /* glsl */`precision mediump float;varying vec2 v_uv;uniform sampler2D u_sampler;void main() { gl_FragColor = texture2D(u_sampler, v_uv);}`// create a custom style layer to implement the WebGL contentconst customLayer = { id: 'highlight', type: 'custom', // 当图层增加时调用的函数 // https://docs.mapbox.com/mapbox-gl-js/api/#styleimageinterface#onadd onAdd: function (map, /** @type {WebGLRenderingContext} */gl) { // 创立、编译顶点着色器 const vertexShader = gl.createShader(gl.VERTEX_SHADER) gl.shaderSource(vertexShader, vertexSource) gl.compileShader(vertexShader) // 创立、编译片元着色器 const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) gl.shaderSource(fragmentShader, fragmentSource) gl.compileShader(fragmentShader) // 创立着色器程序,链接片元和顶点着色器 this.program = gl.createProgram() gl.attachShader(this.program, vertexShader) gl.attachShader(this.program, fragmentShader) gl.linkProgram(this.program) // 把 vertexAttributes 和 uniform 在着色器中的地位保留下来 this.aPos = gl.getAttribLocation(this.program, 'a_pos') this.aUv = gl.getAttribLocation(this.program, 'a_uv') this.uSamplerLoc = gl.getUniformLocation(this.program, 'u_sampler') // 四个点用于定义一个矩形 const p1 = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 112.5494384765625, lat: 22.268764039073968 }, 10) const p2 = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 114.0216064453125, lat: 22.268764039073968 }, 10) const p3 = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 114.0216064453125, lat: 23.28171917560002 }, 10) const p4 = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 112.5494384765625, lat: 23.28171917560002 }, 10) // 加载贴图并创立纹理,在加载结束的回调函数中上载贴图数据 const img = new Image() img.src = './img.png' this.texture = gl.createTexture() img.onload = () => { // bind 和 storei 操作必须等纹理解码实现 gl.bindTexture(gl.TEXTURE_2D, this.texture) gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true) // 上载数据 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img) } // 创立 VBO this.buffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer) gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([ // position, uv p1.x, p1.y, p1.z, 0, 0, p2.x, p2.y, p2.z, 1, 0, p3.x, p3.y, p3.z, 1, 1, p1.x, p1.y, p1.z, 0, 0, p3.x, p3.y, p3.z, 1, 1, p4.x, p4.y, p4.z, 0, 1, ]), gl.STATIC_DRAW ) }, // 每帧运行 // https://docs.mapbox.com/mapbox-gl-js/api/#map.event:render render: function (/** @type {WebGLRenderingContext} */gl, matrix) { // 每帧都要指定用哪个着色器程序 gl.useProgram(this.program) // 每帧都要传递 uniform gl.uniformMatrix4fv( gl.getUniformLocation(this.program, 'u_matrix'), false, matrix ) // 每帧都要绑定纹理参数 gl.bindTexture(gl.TEXTURE_2D, this.texture) 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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) // 每帧都要绑定 VBO,并启用 vertexAttributes、设置 vertexAttributes 的参数 gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer) gl.enableVertexAttribArray(this.aPos) gl.enableVertexAttribArray(this.aUv) gl.vertexAttribPointer(this.aPos, 3, gl.FLOAT, false, 20, 0) gl.vertexAttribPointer(this.aUv, 2, gl.FLOAT, false, 20, 12) // 如果你用不着透明度,能够不执行这两行 // gl.enable(gl.BLEND) // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) // 触发绘制 gl.drawArrays(gl.TRIANGLES, 0, 6) }}map.on('load', () => { map.addLayer(customLayer)})可改良点上述例子在增加进 map 之后,图片还是没有申请、解码实现的,其回调函数还没有执行,如果你不去动一下地图,比方点击一下或挪动一下视角,是不会更新的,有可能是黑黑的一个框 —— 纹理还没筹备好,只能画黑框框。 ...

July 20, 2022 · 2 min · jiezi

关于mapbox:mapboxgl-地图样式-重分类渲染

上回,咱们在《mapboxgl 地图款式 - 惟一值渲染》中理解到case、match、get等表达式,通过表达式来实现了惟一值渲染。 在理论状况下,咱们还常常须要进行重分类渲染,将某范畴的值重分为一类,并将另一个范畴重分为其它类。 明天咱们持续理解新的表达式来实现重分类渲染。 重分类效果图: 形式一:应用step表达式"fill-color":[ "step", ["get","population"], "#ffd0a6", 50, "#ffaa7f", 100, "#ff704e", 150, "#f04040", 200, "#b50a09"]下面表达式的意思是: get获取属性值population,小于50,色彩是#ffd0a6大于等于50,但小于100时,色彩是#ffaa7f大于等于100,但小于150时,色彩是#ff704e大于等于150,但小于200时,色彩是#f04040大于等于200,色彩是#b50a09看到这里是不是奇怪step是起什么作用的?step译为步,一步一步就是分段的意思,它产生阶梯式后果,把一段值归为一类,小于50的是一类,大于等于50又小于100是一类,每一类step都会输入一个值,在效果图中展现为50万人口以下地区是一个色彩,大于等于50又小于100万人口区间的地区为另一个色彩。 咱们在下篇渐变色渲染的文章中将会理解interpolate表达式,与step表达式产生的阶梯式后果相同,interpolate表达式将会产生间断后果。翻译成js是: function getColor(feature){ //feature是geojosn格局中的Feature if(feature.properties.population<50){ return '#ffd0a6' } else if(feature.properties.population<100){ return '#ffaa7f' } ... else{ return '#b50a09' }}step表达式语法规定: "step"是表达式的名称["get","adcode"]是输出值,必须为数值类型或者是数值表达式"#ffd0a6"是输入值50是判断值...(依据理论状况两两呈现的输入值、判断值)"#b50a09"是输入值step表达式有5个必须参数,并且不能乱序:表达式的名称、输出值、输入值、判断值,... ...,输入值(省略局部为输入值、判断值,在省略局部里如果呈现了,就必须两两呈现)。也就是说除了表达式的名称和输出值以外,起码还须要一个输入值、一个判断值、再加一个输入值。 //必须参数"fill-color":[ "step", //表达式的名称 ["get","population"], //输出值 "#ffd0a6", //输入值 50, //判断值 "#b50a09" //输入值]step表达式写起来较为简洁,但须要留神的一点是判断值必须遵循升序规定。 在线示例:http://gisarmory.xyz/blog/index.html?demo=mapboxglStyleReclass2 形式二:应用 case 表达式case表达式相似js里的if判断语句。表达式的实现成果比拟依赖于属性值,通常咱们先应用get表达式去获取属性值,再去判断这个属性值,以此达到在同一图层上实现不同的展现成果。 "fill-color":[ "case", ['boolean',['<',["get","population"],50]],"#ffd0a6", ['boolean',['<',["get","population"],100]],"#ffaa7f", ['boolean',['<',["get","population"],150]],"#ff704e", ['boolean',['<',["get","population"],200]],"#f04040", '#b50a09']下面表达式的意思是: t获取属性值population,小于50,色彩是#ffd0a6大于等于50,但小于100时,色彩是#ffaa7f大于等于100,但小于150时,色彩是#ff704e大于等于150,但小于200时,色彩是#f04040大于等于200,色彩是#b50a09在线示例:http://gisarmory.xyz/blog/index.html?demo=mapboxglStyleReclass1 翻译成js是: function getColor(feature){ //feature是geojosn格局中的Feature if(feature.properties.population<50){ return '#ffd0a6' } else if(feature.properties.population<100){ return '#ffaa7f' } ... else{ return '#b50a09' }}case表达式写起来较为繁琐,但它对判断值没有升序这种要求,只有是true或false就行了。 ...

March 11, 2022 · 1 min · jiezi

关于mapbox:mapbox地图对接gis图层时坐标系不一致

问题形容当我的项目须要对接GIS图层时,发现它的坐标系为EPSG:4490,而mapbox反对的坐标系是EPSG:3857。mapbox对接wms时bbox截取的地图块的经纬度范畴,在对接时可将其EPSG:3857通过拦挡申请转换为EPSG:4490的坐标系,获取失去GIS对应的图层瓦片。 EPSG:4326大地坐标系,WGS84EPSG:4490大地坐标系,cgcs2000EPSG:3857投影坐标系,墨卡托投影形式1: 通过全局拦挡fetch申请的形式function injectFetch() { const newFetch = Object.getOwnPropertyDescriptor(window, 'fetch'); Object.defineProperty(window, 'fetch', { value(a, b) { if (a instanceof Request && a.url.includes('/GISServices/')) { const u = new URL(a.url); // 解析 bbox 参数 const bbox = u.searchParams.get('bbox') ?? ''; const [lon1, lat1, lon2, lat2] = bbox.split(','); // 坐标系转换 : 墨卡托->GPS const p1 = mercator2LonLat([Number(lon1), Number(lat1)]); const p2 = mercator2LonLat([Number(lon2), Number(lat2)]); const newBBOX = p1.concat(p2).join(','); u.searchParams.set('bbox', newBBOX); Object.defineProperty(a, 'url', { value: decodeURIComponent(u.toString()), }); } return newFetch.value.apply(this, [a, b]); }, });}代理拦挡申请的形式可通过node写一个代理服务,拦挡瓦片申请服务。参考webpack的配置代理服务的形式,通过http-proxy-middleware来实现,而后可通过把node服务打包为exe后部署服务器来实现。坐标系简介通常有两种坐标系 天文坐标系(geographic coordinate systems) 和 投影坐标系(projected coordinate systems) ...

February 19, 2022 · 1 min · jiezi

关于mapbox:mapbox自定义样式根据高度着色

mapbox依据地形高度渲色彩这里分为两种形式来实现,别离对应实用于设计师的mapbox studio配置形式,和实用于开发者的代码更改形式,常规先看最终成果 查看交互式的最终成果实用于设计人员的形式先新建一个地图,根底款式任意抉择 先设置高度,这样就有了线稿个别的轮廓 而后咱们新建一个图层,留神这一步抉择如图所示的内容能够拿到地形高度信息,随后在type里设置为fill-extrusion 而后设置色彩就和上色一样,规定由您任意定制 实用于开发者的形式须要留神如果您应用了本人的DEM源,您须要更改源和高度获取形式为您所应用数据源的配套值map.on('load',()=>{ map.addSource('mapbox-dem', { "type": "vector", "url": "mapbox://mapbox.mapbox-terrain-v2" }); map.addLayer({ id:'tinting-layer', 'source': 'mapbox-dem', 'source-layer': 'contour', 'type': 'fill-extrusion', paint:{ "fill-extrusion-color":[ "interpolate", ["linear"], ["get", "ele"], -410, "hsl(253, 98%, 42%)", 0, "hsl(237, 94%, 49%)", 1000, "hsl(213, 96%, 64%)", 2000, "hsl(177, 100%, 50%)", 3000, "hsl(119, 100%, 53%)", 4000, "hsl(98, 98%, 69%)", 5000, "hsl(69, 95%, 56%)", 6000, "hsl(27, 95%, 56%)", 7000, "hsl(0, 96%, 64%)" ], "fill-extrusion-height":[ "interpolate", ["linear"], ["get", "ele"], 0, 0, 8840, 8840 ] } })})查看交互式的最终成果

December 19, 2021 · 1 min · jiezi

关于mapbox:mapboxgl-互联网地图纠偏插件二

前段时间写的mapboxgl 互联网地图纠偏插件(一)存在地图旋转时瓦片错位的问题。 这次没有再跟 mapboxgl 的变换矩阵较劲,而是另辟蹊径应用 mapboxgl 的自定义图层,从新写了一套加载瓦片的办法来实现地图纠偏。 上面把我这次打怪降级的心路历程分享一下,或者对你也有启发。 文中波及一些 webgl 的常识细节,没有接触过 webgl 的同学,能够参考看上一次给大家举荐的电子书 《WebGL编程指南》,这次再附上一个蕴含书中所有示例的 github 库,会很有帮忙。 书接上回在钻研偏移矩阵问题束手无策时,发现用天地图的栅格瓦片没有偏移的问题,因为天地图是大地2000坐标,能够间接在 wgs84 坐标地图上应用,根本没有误差。 尝试后感觉,能够倒是能够,但就是配色有点丑,能够先作为一个保底计划,高德瓦片的纠偏还要持续钻研。 话说《WebGL编程指南》这本书看完后,始终想写个读书笔记,但又感觉光写笔记太干燥,就想着联合地图看无能点啥。 mapboxgl 通过自定图层接口反对 webgl 的扩大,这个接口的益处是,对简单的变换矩阵进行了封装,对外应用大家相熟的 web 墨卡托坐标,并提供了经纬度坐标和 web墨卡托坐标转换的接口 。 查看 mapboxgl 的官网示例时,忽然来了灵感,能够用这个接口本人写个加载栅格瓦片的程序,这样就能绕开 mapboxgl 简单的框架,更容易实现对瓦片纠偏,呈现问题也更好解决,对整体更有掌控感。 技术路线剖析: 用这个思路来实现纠偏,要搞定两大问题,一个是如何用 webgl 实现显示瓦片的性能,另一个是如何计算瓦片在屏幕上的显示地位。 如何用 webgl 显示瓦片在 webgl 中,图形的根底是三角形,要绘制正方形的瓦片,须要用两个三角形拼成一个正方形,再把图片贴到这个正方形上,就能实现地图瓦片的显示。这个过程中,图片被称为纹理,贴图被称为纹理贴图。实现成果如下(图片地位是轻易写的): 这里有两点要留神: 1、要留神图片的跨域问题,须要通过设置图片的跨域属性来解决。 2、要留神顶点坐标的程序,正确的程序为:左上、左下、右上、右下,不然图片会像穿衣服一样,各种穿反,前后反,左右反 外围代码如下: var picLoad = false; var tileLayer = { id: 'tileLayer', type: 'custom', //增加图层时调用 onAdd: function (map, gl) { var vertexSource = "" + "uniform mat4 u_matrix;" + "attribute vec2 a_pos;" + "attribute vec2 a_TextCoord;" + "varying vec2 v_TextCoord;" + "void main() {" + " gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);" + " v_TextCoord = a_TextCoord;" + "}"; var fragmentSource = "" + "precision mediump float;" + "uniform sampler2D u_Sampler; " + "varying vec2 v_TextCoord; " + "void main() {" + " gl_FragColor = texture2D(u_Sampler, v_TextCoord);" + "}"; //初始化顶点着色器 var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexSource); gl.compileShader(vertexShader); //初始化片元着色器 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentSource); gl.compileShader(fragmentShader); //初始化着色器程序 var program = this.program = gl.createProgram(); gl.attachShader(this.program, vertexShader); gl.attachShader(this.program, fragmentShader); gl.linkProgram(this.program); //获取顶点地位变量 var a_Pos = gl.getAttribLocation(this.program, "a_pos"); var a_TextCoord = gl.getAttribLocation(this.program, 'a_TextCoord'); //设置图形顶点坐标 var leftTop = mapboxgl.MercatorCoordinate.fromLngLat({lng: 110,lat: 40}); var rightTop = mapboxgl.MercatorCoordinate.fromLngLat({lng: 120,lat: 40}); var leftBottom = mapboxgl.MercatorCoordinate.fromLngLat({lng: 110,lat: 30}); var rightBottom = mapboxgl.MercatorCoordinate.fromLngLat({lng: 120,lat: 30}); //顶点坐标放入webgl缓冲区中 var attrData = new Float32Array([ leftTop.x, leftTop.y, 0.0, 1.0, leftBottom.x, leftBottom.y, 0.0, 0.0, rightTop.x, rightTop.y, 1.0, 1.0, rightBottom.x, rightBottom.y, 1.0, 0.0 ]) var FSIZE = attrData.BYTES_PER_ELEMENT; this.buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); gl.bufferData(gl.ARRAY_BUFFER, attrData, gl.STATIC_DRAW); //设置从缓冲区获取顶点数据的规定 gl.vertexAttribPointer(a_Pos, 2, gl.FLOAT, false, FSIZE * 4, 0); gl.vertexAttribPointer(a_TextCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2); //激活顶点数据缓冲区 gl.enableVertexAttribArray(a_Pos); gl.enableVertexAttribArray(a_TextCoord); var _this = this; var img = this.img = new Image(); img.onload = () => { // 创立纹理对象 _this.texture = gl.createTexture(); //向target绑定纹理对象 gl.bindTexture(gl.TEXTURE_2D, _this.texture); //对纹理进行Y轴反转 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); //配置纹理图像 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, this.img); picLoad = true; }; img.crossOrigin = true; //设置容许跨域 img.src = "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x=843&y=386&z=10"; }, //渲染,地图界面变动时会调用这个办法,会调用若干次(变动时的每一帧都调用) render: function (gl, matrix) { if(picLoad){ //利用着色程序 //必须写到这里,不能写到onAdd中,不然gl中的着色程序可能不是下面写的,会导致上面的变量获取不到 gl.useProgram(this.program); //向target绑定纹理对象 gl.bindTexture(gl.TEXTURE_2D, this.texture); //开启0号纹理单元 gl.activeTexture(gl.TEXTURE0); //配置纹理参数 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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.MIRRORED_REPEAT); // 获取纹理的存储地位 var u_Sampler = gl.getUniformLocation(this.program, 'u_Sampler'); //将0号纹理传递给着色器 gl.uniform1i(u_Sampler, 0); //给地位变换矩阵赋值 gl.uniformMatrix4fv(gl.getUniformLocation(this.program, "u_matrix"), false, matrix); //绘制图形 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); } } }; map.on('load', function () { map.addLayer(tileLayer); });下面是加载一个瓦片,上面看一下如何加载多个瓦片,这个问题看似简略,但对于webgl不相熟的同学有可能会走弯路,我本人在钻研时,就遇到了上面几个问题: ...

July 6, 2021 · 3 min · jiezi

关于mapbox:mapboxgl绘制3D线

最近遇到个需要,应用mapboxgl绘制行政区划图层,要求把行政区划拔高做出平面成果,以便突出显示。 拿到这个需要后,感觉很简略呀,只须要用fill-extrusion形式绘制就能够啦,实现进去是这个样子的 成果有点丑,并且这里有个问题就是我的数据是区县数据,而绘制出的成果却没有辨别出各个区县的边界于是从上面两个方向做优化解决: 各区县按不同色彩辨别增加区县边界首先尝试不同区县按色彩辨别。这个简略,只须要设置fill-extrusion-color即可 设置完成果如下 成果好多了。 接下来持续尝试增加区县边界,之前也看到过相似成果,感觉应该也好实现 然鹅。。。 通过一番查找,发现发现mapboxgl能够对面进行拔高解决,但没有对线做拔高解决的办法,也就是说不反对绘制3D线。这可如何是好 既然线数据不能做拔高解决,那么把线解决成面总能够吧 于是从这个思路登程,按上面两步来操作 对行政区划边界进行缓冲,这里须要用到turf.js的缓冲办法获取到缓冲后的边界面数据,再用fill-extrusion形式绘制 嗯,成果还不错 在绘制边线缓冲面时,须要留神上面两点: fill-extrusion-height设置的值须要比面数据的略微高出一些,否则显示时会有遮蔽问题fill-extrusion-base(底部高度)参数能够设置为与后面绘制面的fill-extrusion-height参数统一,如果也采纳默认0的话,边线就像一面墙,会感觉很丑其实,这里还有个问题,因为这里的边界是按缓冲面的形式绘制,所以在地图缩放的时候边界的宽度不会像线那样按固定像素宽度显示,会呈现放大地图的时候边线越来越宽,放大地图的时候边线变越来越窄的问题 解决思路:按各层级分辨率别离对行政区划边界做缓冲计算,而后再别离对应显示在各个层级 各层级的resolutions,也就是各比例尺下地图分辨率,也就是一个像素代表的地图单位,这里要按米为单位进行缓冲,用的是EPSG:900913的分辨率,也就是各比例尺下一个像素代表多少米外围代码如下 因为拿到数据的只有行政区划的geojson格局面数据,而缓冲时须要用的是线数据,因而须要做面转线解决。 最终成果如下 总结当对行政区划面数据做平面展现时,仅用fill-extrusion形式绘制,成果不好,无奈显示边线mapboxgl能够对面进行拔高解决,但没有对线做拔高解决的办法,也就是说不反对绘制3D线通过对边线数据缓冲,获取缓冲前面数据,当做边线应用因为单次缓冲半径固定,从而失去的缓冲面对大小固定,会呈现地图缩放边线宽度也随着缩放的问题通过resolutions,逐级对边线解决,按层级显示,能够失去较好的显示成果在线示例在线示例:http://gisarmory.xyz/blog/index.html?demo=MapboxGL3DLine 代码地址:http://gisarmory.xyz/blog/index.html?source=MapboxGL3DLine 原文地址:http://gisarmory.xyz/blog/index.html?blog=MapboxGL3DLine 关注《GIS兵器库》, 只给你网上搜不到的GIS常识技能 本文章采纳 常识共享署名-非商业性应用-雷同形式共享 4.0 国内许可协定 进行许可。欢送转载、应用、从新公布,但务必保留文章署名《GIS兵器库》(蕴含链接:  http://gisarmory.xyz/blog/),不得用于商业目标,基于本文批改后的作品务必以雷同的许可公布。

July 2, 2021 · 1 min · jiezi