免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
WebGIS底圖疊加不同坐標(biāo)系的Image

原創(chuàng) freegiser Spatial Data 2021-03-18 17:30

收錄于合集

#MapboxGL5
#氣象7
#WebGIS19
#GIS17

一 背景

WebGIS中非常常見(jiàn)的一個(gè)功能,就是疊加地理相關(guān)的Image到底圖上,眾所周知,底圖是有地理坐標(biāo)系的,Image通常也是基于某個(gè)地理坐標(biāo)系制圖的產(chǎn)物,如果底圖坐標(biāo)系和Image坐標(biāo)系不一致,那么他們疊加肯定就會(huì)發(fā)生位置的變形與偏移:

      不同投影疊加形變

上圖,地理底圖一般是墨卡托投影(epsg:3857),假設(shè)疊加的圖片是生產(chǎn)單位按照WGS84經(jīng)緯度(epsg:4326)制圖輸出的,并且已知這個(gè)圖片真實(shí)地理范圍是[84minx,84miny,84maxx,84maxy],常規(guī)思路就是計(jì)算84的范圍對(duì)應(yīng)底圖的墨卡托的范圍[mkt_minx,mkt_miny,mkt_maxx,mkt_maxy],然后工工整整把這個(gè)圖像貼到底圖這個(gè)范圍區(qū)間里,效果如上圖,非常明顯的投影形變。

看到這很多朋友會(huì)問(wèn):有方法讓與底圖坐標(biāo)系不同的Image正確疊加底圖嗎?答案只有一個(gè):讓image和底圖的坐標(biāo)系相同

那么下個(gè)問(wèn)題:如何能讓image和底圖坐標(biāo)系相同?

一個(gè)是逐像素轉(zhuǎn)換,這不是本篇話(huà)題,計(jì)算量比較大,性能不好,通常不被采用。

另一個(gè)方法是:將圖像分解三角形進(jìn)行投影轉(zhuǎn)換(很熟悉的味道啊,圖像?三角形?對(duì)應(yīng)webgl的紋理和三角片?)。

image投影到與底圖一致

二 技術(shù)資料

openlayers目前原生就已經(jīng)支持image重投影了,demo如下:

https://openlayers.org/en/latest/examples/reprojection-image.html

具體實(shí)現(xiàn)技術(shù)原理參考如下博客:

https://www.jianshu.com/p/98da62d88e83
Openlayers raster reprojection 柵格重投影原理分析

只不過(guò)ol目前的源碼里是用canvas實(shí)現(xiàn)的,在leaflet的里也有大神提供了一個(gè)webgl實(shí)現(xiàn)的圖層,網(wǎng)速慢要等圖像加載完畢:

https://ivansanchez.gitlab.io/Leaflet.ImageOverlay.Arrugator/demo.html

本質(zhì)上ol和leaflet思路都是圖像拆分三角形,三角形整體投影(相比逐像素投影效率高非常多),很像數(shù)學(xué)里的積分概念,拆分三角形越多,那么計(jì)算越精確,可以想象,三角形足夠的的多,是不是等于非常精確,和逐像素一樣了?

下面我們主要基于leaflet的這個(gè)webgl方案做下簡(jiǎn)單說(shuō)明,開(kāi)發(fā)者是基于arrugator庫(kù)實(shí)現(xiàn)三角形切分的:

https://gitlab.com/IvanSanchez/arrugator

這個(gè)庫(kù)使用很簡(jiǎn)單:

第一步:定義圖像坐標(biāo)系

proj4.defs(    "EPSG:25833",    "+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs");// 定義投影轉(zhuǎn)換規(guī)則,如從圖像的25833轉(zhuǎn)底圖的3857坐標(biāo)系const projector = proj4("EPSG:25833", "EPSG:3857").forward;

第二步:定義圖像的控制坐標(biāo)點(diǎn)(圖像對(duì)應(yīng)的地理bbox)與對(duì)應(yīng)紋理的uv坐標(biāo)?

//圖像的地理bbox坐標(biāo)const controlPoints = [    [ -183622.3, 7996344.0 ], // top-left    [ -183622.3, 6396344.0 ], // bottom-left    [ 1416377.7, 7996344.0 ], // top-right    [ 1416377.7, 6396344.0 ], // bottom-right];//紋理uv坐標(biāo),注意top left這些方向與地理的一一對(duì)應(yīng)const sourceUV = [    [ 0, 0 ],  // top-left    [ 0, 1 ],  // bottom-left    [ 1, 0 ],  // top-right    [ 1, 1 ],  // bottom-right];

第三步:計(jì)算三角形

let arruga = new Arrugator(    forward,    controlPoints,    sourceUV,    [ [ 0, 1, 3 ], [ 0, 3, 2 ] ]  // 這里寫(xiě)死不變);// 根據(jù)閾值切分三角形const epsilon = 0.0000000001; // 不同坐標(biāo)系這個(gè)閾值是不同的,實(shí)際使用可以自行調(diào)試arruga.lowerEpsilon(epsilon);// 結(jié)果輸出let arrugado = arruga.output();

這里有個(gè)問(wèn)題,閾值設(shè)置多少合適?開(kāi)發(fā)者可以根據(jù)實(shí)際的坐標(biāo)系轉(zhuǎn)換時(shí)候,調(diào)試以下代碼,先保證這個(gè)條件成立,然后在這個(gè)條件成立的基礎(chǔ)上,epsilon越小,三角形切分越多,那么耗時(shí)多點(diǎn),結(jié)果正確點(diǎn)唄。

例如:peek().epsilon是0.1,那么你傳閾值必須要小于0.1,大于的話(huà)就不切分了其實(shí),那我可以設(shè)置0.01,0.001都可以的,自己調(diào)試效果和性能,取個(gè)折衷數(shù)值較好,就是不能設(shè)置太小追求精確度,但犧牲性能,中庸點(diǎn)好。

這個(gè)庫(kù)最終輸出的其實(shí)有三個(gè)東西:

  • projected:切分的小三角形投影后的頂點(diǎn)。

  • uv: 切分的小三角形uv紋理頂點(diǎn)。

  • trigs:切分后的小三角形頂點(diǎn)索引,就是webgl里的那個(gè)indices。

拿到地理頂點(diǎn),uv頂點(diǎn),indices的繪制索引,再加一張image作為紋理,使用webgl渲染簡(jiǎn)直是水到渠成的事。

三  mapboxgl實(shí)踐

由于mapboxgl本身不支持image的投影轉(zhuǎn)換,我們使用arrugator來(lái)幫我們實(shí)現(xiàn)這個(gè)功能,選擇openlayers這個(gè)例子來(lái)改寫(xiě)到mapbox上。

https://openlayers.org/en/latest/examples/reprojection-image.html

首先對(duì)Image三角形切分:

import test_map from '../../images/2000px-British_National_Grid.svg.png';import proj4 from 'proj4';import Arrugator from 'arrugator';
// 墨卡托投影的左上角坐標(biāo),對(duì)應(yīng)mapbox左上角起始坐標(biāo)[0,0]const origin=[ -20037508.342789244,20037508.342789244];proj4.defs( 'EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 ' + '+x_0=400000 +y_0=-100000 +ellps=airy ' + '+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 ' + '+units=m +no_defs' );// 定義轉(zhuǎn)換規(guī)則const projector = proj4("EPSG:27700", "EPSG:3857").forward;// 改寫(xiě)坐標(biāo)轉(zhuǎn)換函數(shù)// 原因是mapbox的墨卡托坐標(biāo)是0-1區(qū)間,并且對(duì)應(yīng)地理范圍與標(biāo)準(zhǔn)3857不同。function forward(coors{   // 墨卡托坐標(biāo)   const coor_3857 = projector(coors);   // 墨卡托坐標(biāo)轉(zhuǎn)換到 0-1區(qū)間,origin對(duì)應(yīng)mapbox 0 0點(diǎn),所以這么寫(xiě) const mapbox_coor1 = Math.abs((coor_3857[0]-origin[0])/(20037508.342789244*2)); const mapbox_coor2 = Math.abs((coor_3857[1]-origin[1])/(20037508.342789244*2));   return [mapbox_coor1,mapbox_coor2];}const epsilon = 0.00000000001;const controlPoints = [ [ 0, 1300000 ], // top-left [ 0, 0 ], // bottom-left [ 700000, 1300000 ], // top-right [ 700000, 0 ], // bottom-right];//紋理uv坐標(biāo)const sourceUV = [ [ 0, 0 ], // top-left [ 0, 1 ], // bottom-left [ 1, 0 ], // top-right [ 1, 1 ], // bottom-right];let arruga = new Arrugator(    forward, controlPoints, sourceUV, [ [ 0, 1, 3 ], [ 0, 3, 2 ] ] );arruga.lowerEpsilon(epsilon);// arrugado里存了頂點(diǎn),索引 let arrugado = arruga.output();

然后構(gòu)建一個(gè)自定義圖層,部分webgl自封裝的方法,就不展開(kāi)說(shuō)了:

class ImageLayer {    // 傳入圖像和切分三角形頂點(diǎn)和索引    constructor(img, arrugado) {        this.id = 'test_layer';        this.type = 'custom';        this.renderingMode = '2d';        this.img = img;        this.arrugado = arrugado;        this.map;        this.gl;    }    onAdd(m, gl) {        this.map = m;        this.gl = gl;             // 墨卡托坐標(biāo)轉(zhuǎn)mapbox的墨卡托坐標(biāo)        let pos = this.arrugado.projected.flat();        // uv紋理        let uv = this.arrugado.uv.flat();        // 三角形index        let trigs = arrugado.trigs.flat();        // 著色器代碼        const vs = `#version 300 es        layout(location=0) in vec2 a_position;        layout(location=1) in vec2 a_uv;        uniform mat4 u_worldViewProjection;        out vec2 v_uv;        void main() {            gl_Position = u_worldViewProjection*vec4(a_position, 0.0,1.0);            v_uv = a_uv;        }`;        const fs = `#version 300 es        precision highp int;        precision highp float;        uniform sampler2D u_image;        in vec2 v_uv;        out vec4 outColor;        void main() {            // 根據(jù)紋理坐標(biāo)獲取顏色            vec4  texel= texture(u_image, v_uv);            if (texel.r > 0.95 && texel.g > 0.95 && texel.b > 0.95) {                texel = vec4(0.6, 0.0, 0.0, 1.0);             }            else {                outColor= vec4(texel.r*0.4,texel.g*0.4,texel.b*0.4,0.4);            }        }`;        // 定義渲染model        this.drawModel = createModel(gl, vs, fs);        //定義vao對(duì)象,綁定頂點(diǎn),索引        this.vao = gl.createVertexArray();        gl.bindVertexArray(this.vao);                const positionBuffer = createBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(pos));        bindAttribute(gl, positionBuffer, 02);        const uvBuffer = createBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(uv));        bindAttribute(gl, uvBuffer, 1, 2);        const indexBuffer = createBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(trigs));        this.position_count = trigs.length;
        // 綁定結(jié)束         gl.bindBuffer(gl.ARRAY_BUFFER, null);        gl.bindVertexArray(null);        //構(gòu)造紋理并綁定 const texture = createTexture2D(gl, { data: this.img, mipLevel: 0, internalFormat: gl.RGBA,//webgl中格式 srcFormat: gl.RGBA,//輸入數(shù)據(jù)源格式 type: gl.UNSIGNED_BYTE, height: 2000, width: 3696, parameters: { [ gl.TEXTURE_MAG_FILTER ]: gl.LINEAR, [ gl.TEXTURE_MIN_FILTER ]: gl.LINEAR, [ gl.TEXTURE_WRAP_S ]: gl.CLAMP_TO_EDGE, [ gl.TEXTURE_WRAP_T ]: gl.CLAMP_TO_EDGE } }); //drawProgram綁定紋理,不能綁定單元0 gl.useProgram(this.drawModel.program); bindTexture2D(gl, texture, 3);        gl.uniform1i(this.drawModel.u_image, 3); } render(gl, matrix) { gl.useProgram(this.drawModel.program); //設(shè)置unifrom gl.uniformMatrix4fv(this.drawModel.u_worldViewProjection, false, matrix); //綁定頂點(diǎn)vao        gl.bindVertexArray(this.vao); gl.drawElements(gl.TRIANGLES, this.position_count, gl.UNSIGNED_SHORT, 0); //如果取消綁定,會(huì)報(bào)錯(cuò)GL_INVALID_OPERATION: Insufficient buffer size. gl.bindVertexArray(null); } onRemove(map, gl) {        // 。。。    }}

第三步:新建圖層,疊加地圖

    let img = new Image();    img.src = test_map;    img.onload = function () {        let layer = new ImageLayer(img, arrugado);        var container = document.createElement('div');        const width = 1600, height = 900;        container.style.width = `${width}px`;        container.style.height = `${height}px`;        document.body.appendChild(container);        // 兼容webgl2        mapboxglExtension(mapboxgl);                var map = new mapboxgl.Map({            container: container,            style: 'mapbox://styles/mapbox/dark-v10',            center: [ 0, 65 ],            zoom: 4,            antialias: true        });               map.on('load', () => {            map.addLayer(layer);        });    }

至于怎么樣讓mapboxgl支持webgl2,為什么紋理單元不能寫(xiě)0,為什么這么多手動(dòng)綁定解綁webgl資源對(duì)象,參考下文《使用WebGL2進(jìn)行Mapbox自定義圖層開(kāi)發(fā)》

https://mp.weixin.qq.com/s?__biz=Mzg2OTUxMzM2MA==&mid=2247483684&idx=1&sn=cbec2c833fa0a2a30e3ee0d6063fbf0c&chksm=ce9aa0dbf9ed29cd1b65bebf5e773eb8b4005c3d347b033426d0c4b65807ab74934268d9df88&token=1580692165&lang=zh_CN#rd

效果如下圖:

四 應(yīng)用延申

這個(gè)image重投影一般在哪用咧?比如氣象領(lǐng)域,很多產(chǎn)品出的柵格圖像,想要疊加到地圖上,坐標(biāo)系可能圖像用4326經(jīng)緯度,底圖是墨卡托,這時(shí)候疊加歪了,就可以使用這個(gè)庫(kù)去轉(zhuǎn)換,例如下圖:

轉(zhuǎn)換前

轉(zhuǎn)換后

轉(zhuǎn)換前,日本列島的溫度都疊加底圖上的日本區(qū)域,都飄上面去了,越往北偏移越大其實(shí),赤道那近似,這個(gè)就是投影變換的原理了,球面轉(zhuǎn)平面的關(guān)系,這里講不清楚,自行研究。轉(zhuǎn)換后,可以看到疊加的基本嚴(yán)絲合縫了。

收錄于合集 #GIS

 17

上一篇為什么說(shuō)shp不適合在企業(yè)級(jí)WebGIS中使用下一篇讓前端地理密集型計(jì)算飛起來(lái)的若干方案測(cè)試

文章已于2021-03-18修改

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
L7: L7 是由螞蟻金服 AntV 數(shù)據(jù)可視化團(tuán)隊(duì)推出的基于 WebGL 的開(kāi)源大規(guī)模地理空間數(shù)據(jù)可視分析開(kāi)發(fā)框架。
史上最懂你需求的地圖,OSM具體用法教程帶你玩轉(zhuǎn)世界
建筑學(xué)必知的N個(gè)地圖素材網(wǎng)站
數(shù)據(jù)可視化:地圖使用案例
CAD與GIS集成說(shuō)明(2022-08-19)
有了這5個(gè)高清地圖網(wǎng)站,出個(gè)區(qū)位分析算個(gè)啥(文末附區(qū)位分析圖制作神器)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服