當前位置:首頁 » 網頁前端 » web瓦片貼圖
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

web瓦片貼圖

發布時間: 2023-02-24 08:07:37

㈠ 如何在Web上直接瀏覽大規模OSGB格式傾斜模型(二):轉換OSGB

歡迎關注公號【 三維網格3D 】,第一時間獲取最新文章

上一篇( 如何在Web上直接瀏覽大規模OSGB格式傾斜模型(一):解析OSGB )已經貼出了解析結果,讓我們對OSGB傾斜模型文件內部結構有了比較清晰的認識,本篇我們將解析結果轉成three.js對象,並實現單個模型的顯示。

為什麼轉成three.js對象

從解析結果看,Osg對象的節點關系用three.js對象表達更容易,轉換結果可以直接用three.js顯示,通過我們團隊的Cesium和three.js融合方案,可以輕松將成果用於Cesium開發的項目,既可以單獨使用,又可以擁有GIS能力。

需要轉換的Osg類型

解析得到的Osg對象,Type屬性指示對象的類型,我們只是要轉換傾斜攝影模型文件的Osg對象,而不是做一個通用的轉換工具,所以先將類型做一下分類,只轉換目前所用測試數據(100多G的測試數據)已知的類型。

PS:我們團隊內部使用的版本,經過一年多的應用和改進,支持了更多類型的轉換,填了許多坑,而且支持osgb、osgt、osg、osgjs四種格式文件,載入速度和健壯性都得到保障。歡迎咨詢~

節點類

osg::Node

osg::Geode

osg::Group

osg::LOD

osg::PagedLOD

osg::MatrixTransform

幾何類

osg::Geometry

osg::OsgArray

材質類

osg::StateSet

osg::Material

osg::StateAttribute

osg::Texture

osg::Texture2D

osg::Image

節點類主要考慮osg::MatrixTransform,因為這個節點主要的信息就是變換參數,會影響模型的位置、旋轉、縮放等。

接下來我們著重介紹轉換幾何和材質這兩類,LOD和PagedLOD參數不多,而且都是調度相關的,我們放在下一篇一起介紹。

轉換幾何對象

幾何數據存儲在Osg::Geometry,包含索引屬性PrimitiveSetList、位置坐標屬性VertexArray、紋理坐標屬性TexCoordArray。我們將其轉換為THREE.BufferGeometry。

varbufferGeometry=newTHREE.BufferGeometry();

頂點屬性轉換

頂點屬性值是一個二維數組,我們直接調用flat方法,轉為一維數組即可。

varpositions=newFloat32Array(osgGeometry.VertexArray.flat());

varuvs=newFloat32Array(osgGeometry.TexCoordArray.flat());

bufferGeometry.setAttribute('position',newTHREE.BufferAttribute(positions,3));

bufferGeometry.setAttribute('uv',newTHREE.BufferAttribute(uvs,2));

索引轉換

索引類型有兩種,有value指定,這里我們只對索引類型為4的情況(即繪圖最小單元為四邊形)進行特殊處理,其餘的默認最小繪制單元為三角形。

varprimitiveSet=osgGeometry.PrimitiveSetList[0]

varindices=primitiveSet.data;

if(primitiveSet.value==4) {

letnewIndices=[];

for(leti=0; i<indices.length; i+=4) {

leti0=indices[i],

i1=indices[i+1],

i2=indices[i+2],

i3=indices[i+3];

newIndices.push(

i0, i1, i3,

i1, i2, i3

);

}

indices=newIndices;

}

bufferGeometry.setIndex(indices);

轉換材質對象

傾斜攝影模型的材質最重要的就是貼圖,作為原理介紹,我們不處理其他材質參數,直取貼圖即可,當然工程應用上我們還是需要處理一些情況,保證其可用、可靠、健壯。

材質對象存儲在Osg::Geometry的StateSet屬性,該屬性類型為Osg::StateSet,而我們要的貼圖存儲在Osg::StateSet的TextureAttributeList屬性,一般就一張圖。

傾斜模型已經包含了光影信息,為此我們將材質轉為THREE.MeshBasicMaterial。

varmaterial=newTHREE.MeshBasicMaterial({

side:THREE.DoubleSide

});

處理貼圖

貼圖一般是jpg或者png格式,我們這里只處理這兩種格式。需要注意的是圖片數據為Uint8Array類型,我們需要先轉為Blob,然後通過URL.createObjectURL創建url,最後使用THREE.TextureLoader載入。

varosgStateSet=osgGeometry.StateSet;

varosgImage=osgStateSet.TextureAttributeList[0].value.StateAttribute.Image

varfileName=osgImage.Name;

constisJPEG=fileName.search(/\.jpe?g($|\?)/i)>0

constisPNG=fileName.search(/\.png($|\?)/i)>0

if(!isPNG&&!isJPEG)return;

varmimeType=isPNG?'image/png':'image/jpeg';

varimageUri=newBlob([osgImage.Data], { type:mimeType });

imageUri=URL.createObjectURL(imageUri)

vartexture=newTHREE.TextureLoader().load(imageUri, ()=>{

texture.needsUpdate=true

})

創建網格

最後,使用轉換好的幾何體和材質創建模型網格。這里要注意,傾斜攝影模型是z軸朝上(z-up),而three.js渲染是y軸朝上(y-up),所以還需將網格繞x軸旋轉y-90°才得到正常的渲染效果。

varmesh=newTHREE.Mesh(geometry, material);

mesh.rotation.x=-Math.PI/2

轉換結果

顯示效果

額,看起來並沒有那麼好看~畢竟這是單個瓦片最不清晰的那層,簡化到不能看了。

下一篇,我們將主要介紹如果實現PagedLOD,來載入大規模傾斜攝影模型,敬請期待

歡迎關注公號【 三維網格3D 】,第一時間獲取最新文章

㈡ WebGIS中的坐標系和瓦片地圖

本文主要介紹坐標系和瓦片地圖的相關知識, 他們是進行WebGIS開發的基礎。

坐標系分為地理坐標系和投影坐標系,他們的定義如下:
地理坐標系 (Geographic Coordinate System):
    是使用三維球面來定義地球表面位置,以實現通過經緯度對地球表面點位引用的坐標系。包括角度測量單位、本初子午線和參考橢球體三部分。

投影坐標系 (Projection Coordinate System):
    是使用基於X,Y值的坐標系統來描述地球上某個點所處的位置。它由地理坐標系和投影方法兩個要素所決定。

    地球表面是崎嶇不平的,人們為了精確表示地球表面的位置,引入了 旋轉橢球體 的概念。即用一個規則的旋轉橢球體去逼近真實的地球表面。一個旋轉橢球體的參數主要有以下三個:長半軸、短半軸、扁率。定義了這三個參數,也就唯一確定了一個旋轉橢球體。

    定義了橢球體的形狀後,還需要確定橢球體的位置。橢球體表面與真實地球表面存在差異,並且在世界的不同地區,這種差異也不盡相同。因此橢球體的定位直接決定了地理坐標與真實位置的誤差。橢球體定位就是需要確定 大地基準面 ,從而確定橢球體與地球的相對位置。有以下兩類大地基準面:

    確定了旋轉橢球體的 形狀 位置 ,那麼地理坐標系的基礎就確定了。接下來需要定義地球上任意一點的地理坐標表示方法。

    地理坐標,就是用經緯度表示地面點位的球面坐標。在大地測量學中,對於地理坐標系統中的經緯度有三種提法:天文經緯度、大地經緯度和地心經緯度。其中使用較多的是大地經緯度,其使用大地坐標(L,B,h)表示地面點在橢球面上的位置三個要素,他們的定義如下:

圖示:

    這樣就完成了地理坐標系的定義,地球上任意一點都能獲得經緯度坐標了。

    在橢球面上表示的地球上物體的坐標,會給實際使用帶來一些麻煩。更多的時候我們希望將地物展現在平面上,這時就需要引入投影坐標系的概念。

    在地球橢球面和平面之間建立點與點之間函數關系的數學方法,稱為 地圖投影
    地圖投影的一般公式為:x = F(λ,φ), y = G(λ,φ)
    確定了投影方法後,也就確定了函數F和G,只要知道地面點的經緯度(λ,φ),便可以在投影平面上找到相對應的平面位置(x,y)。

投影方法有以下幾類:

    以上兩種方法都要進行分帶投影。即按一定的間隔選取經線作為投影的中央經線,中央經線兩側一定范圍內的地區按所選中央經線進行投影。這樣做的目的是減小投影變形,方便在工程中使用。

具體的投影方法請點擊小標題查看。

    選擇一個地理坐標系,以及一個地圖投影方法,就唯一確定了一個投影坐標系,從而可以使用平面坐標表示地球上物體的位置了。

    在Web地圖領域,使用最為廣泛的坐標系統就是 WGS84 Web Mercator 。谷歌地圖、Virtual Earth、Bing Maps、網路地圖、Mapabc、ArcGIS Online等都是採用這種坐標系。作為一個投影坐標系,需要兩個基本的要素,一個是地理坐標系,還有一個是投影方法。我們分別來看:

    從名字可以看出,WGS84 Web Mercator坐標系採用的地理坐標系是WGS84坐標系,它屬於地心坐標系,坐標系的原點位於地球質心,其基本參數如下:

    從名字上可以看出,WGS84 Web Mercator坐標系的投影方法和Mercator(墨卡托)投影有關,但是這個投影方法和不是標準的墨卡托投影。他們之間的區別在於,WGS84 Web Mercator在投影時將地球橢球當做圓球看待,這會導致本來是等角投影的墨卡托投影變得不再等角了,而是近似等角,也就是出現角度變形。

    以赤道為標准緯線,以本初子午線為中央經線,分別得到X軸和Y軸。兩者的交點設為原點,規定緯度向北為正,向南為負;經度向東為正,向西為負。

對應於經緯度的范圍就是:

    討論坐標系不得不提到EPSG,EPSG的英文全稱是European Petroleum Survey Group,中文名稱為歐洲石油調查組織。這個組織成立於1986年,2005年並入IOGP(International Association of Oil & Gas Procers),中文名稱為國際油氣生產者協會。EPSG對幾乎所有常用的坐標系統都進行了編號,統一了坐標系的表示,於是我們經常會看到使用EPSG編號來指代某一坐標系。

以下是幾個常用坐標系的EPSG編號和單位:

    至於為何WGS84 Web Mercator有兩個編號,這裡面還是有一段故事的,可以去 這里 查看。

    查詢全部的EPSG編號和詳細信息請訪問 EPSG官網 。

    互聯網地圖服務,常常通過採用構建瓦片地圖的方式,加快用戶的訪問,減少數據傳輸量。具體而言,瓦片地圖就是對投影後的地圖在不同尺度(層)下進行切片,每個尺度得到的地圖切片數量不同、表示範圍不同、詳細程度不同,但是圖片的尺寸相同(一般為256*256),最終構成一個「瓦片金字塔「」。根據用戶所瀏覽的區域范圍,自動確定所要返回的切片層級,在滿足用戶查詢需求的同時,保證了地圖傳輸的效率。

    在投影坐標系的選擇上,目前主流的地圖服務提供商基本都選擇的是WGS84 Web Mercator坐標系。但是在如何對投影後的地圖進行切片並編號時,不同廠商之間存在較大的差異。

    以地圖左上角為原點,X軸向右,Y軸向下,從0開始分別進行編號。Z的取值范圍為[0, 18],在第z級別,x,y方向的瓦片個數均為:2 z 個,即x,y取值范圍是[0 , 2 z -1]。

    WMTS較為特殊,WMTS中的TileMatrix對應於z,TileRow對應於y,TileCol對應於x。編號方式和谷歌與OSM相同。

    以地圖左下角為原點,X軸向右,Y軸向上,從0開始分別進行編號。Z的編碼規則與谷歌地圖相同。

z=1時,這兩種瓦片的編號如下圖所示。

    微軟Bing地圖Z的編碼規則與谷歌相同,同一層級的瓦片不用XY兩個維度表示,而只用一個整數表示,該整數服從四叉樹編碼規則(QuadTree)。

    網路地圖的瓦片定義的方式比較獨特,原點的位置在經緯度都為0的地方,X向左為正,向右為負;Y向上為正,向下為負。切分的方式不像上述3種方法在每一級進行二等分,而是通過定義每一級的 地圖解析度 ,確定每一級應該劃分的行列數。地圖解析度的表達式為:2 18-z ,其含義是每個像素所對應的實際長度。由此,可得每一級應該劃分的行列數為:2πR/(256*2 18-z ),其中R為地球的半徑,單位是米。

參考: https://blog.csdn.net/lxxlxx888/article/details/51897838

    本文記錄了與WebGIS相關的坐標系和瓦片地圖的知識,說明了他們直接的相互關系。希望WebGIS開發者有所幫助。

㈢ CAD/DWG圖Web可視化一站式解決方案-唯傑地圖-vjmap

DWG 圖是 AutoCAD 是 私有 格式,只能在CAD軟體上編輯查看,如何發布至Web上做數據展示,GIS分析應用開發,一直是業內頭疼的事情。

傳統的辦法採用的解析AutoCAD圖形繪制,並封裝成 AcitveX 控制項,在Web顯示。但這需要Web端安裝插件,並且只支持windows平台,而更重要的 Chrome 已不再支持ActiveX技術。

隨著新技術的發展,html5技術已非常成熟,在渲染方面技術非常成熟,越來越多的方案採用html5渲染方式。但CAD與GIS在數據結構上存在較大的差異,CAD數據類型較為豐富,支持簡單點、線、面、多義線、橢圓、塊、文字等多種數據類型,而轉換到GIS中,只轉換為點、線、面、注記等類型。所以會導致存在著和真實CAD圖形繪制差異的問題。

唯傑地圖 https://vjmap.com 採用後端解析CAD的圖形直接渲染成GIS瓦片數據;前端採用最新的WebGL技術,載入渲染出的柵格和矢量數據,完美解決上述問題;

唯傑地圖 為用戶自定義地圖格式WebGIS可視化顯示開發提供的一站式解決方案, 完全兼容dwg格式 、 23d效果完美切換 、 高性能webgl渲染 、 個性化地圖定製 、 矢量柵格瓦片全支持 、 跨平台私有化部署 。支持的格式如常用的AutoCAD的DWG格式文件、GeoJSON等常用GIS文件格式,它使用WebGL矢量圖塊和自定義樣式呈現互動式地圖, 提供了全新的大數據可視化、實時流數據可視化功能,通過本產品可快速實現瀏覽器和移動端上美觀、流暢的地圖呈現與空間分析,可幫助您在網站中構建功能豐富、交互性強、可定製的地圖應用。

通過 Service 對象的 openMap 方法可打開伺服器上面已存在的地圖,或者通過 fileid 傳入 http 網路路徑或伺服器本地路徑打開一幅 DWG 格式的 CAD圖形 .

獲取上面打開地圖返回的 地理范圍 建立 坐標系 ,然後創建 地圖對象

當滑鼠在地圖的某個實體上面時,需要高亮顯示或者點擊查看,在 矢量瓦片 模式下可調用 ;在 柵格瓦片 下,可調用 enableLayerClickHighlight

地圖提供了在地圖之上繪制覆蓋物的能力,比如點標記、純文本標記、折線、圓、多邊形、矩形等圖形,在繪制中可以對點標記進行標題、圖標等進行設置,對折線可以進行顏色、寬度等屬性進行設置,對於面(圓、多邊形、矩形)同樣可以進行填充色、邊框色、寬度等很多自定義屬性進行設置。

增加一個marker覆蓋物

創建很多正方形拉伸圖層

效果如下:

demo地址 可訪問 https://vjmap.com/demo/#/demo/map/overlay/fillextrusion/fillextrusion

㈣ 從零打造一個Web地圖引擎

說到地圖,大家一定很熟悉,平時應該都使用過網路地圖、高德地圖、騰訊地圖等,如果涉及到地圖相關的開發需求,也有很多選擇,比如前面的幾個地圖都會提供一套 js API ,此外也有一些開源地圖框架可以使用,比如 OpenLayers 、 Leaflet 等。

那麼大家有沒有想過這些地圖是怎麼渲染出來的呢,為什麼根據一個經緯度就能顯示對應的地圖呢,不知道沒關系,本文會帶各位從零實現一個簡單的地圖引擎,來幫助大家了解 GIS 基礎知識及 Web 地圖的實現原理。

首先我們去高德地圖上選個經緯度,作為我們後期的地圖中心點,打開 高德坐標拾取 工具,隨便選擇一個點:

筆者選擇了杭州的雷峰塔,經緯度為: [120.148732,30.231006] 。

地圖瓦片我們使用高德的在線瓦片,地址如下:

目前各大地圖廠商的瓦片服務遵循的規則是有不同的:

谷歌和 TMS 的瓦片區別可以通過該地址可視化的查看: 地圖瓦片 。

雖然規范不同,但原理基本是一致的,都是把地球投影成一個巨大的正方形世界平面圖,然後按照四叉樹進行分層切割,比如第一層,只有一張瓦片,顯示整個世界的信息,所以基本只能看到洲和海的名稱和邊界線,第二層,切割成四張瓦片,顯示信息稍微多了一點,以此類推,就像一個金字塔一樣,底層解析度最高,顯示的細節最多,瓦片數也最多,頂層解析度最低,顯示的信息很少,瓦片數量相對也最少:

每一層的瓦片數量計算公式:

十八層就需要 68719476736 張瓦片,所以一套地圖瓦片整體數量是非常龐大的。

瓦片切好以後,通過行列號和縮放層級來保存,所以可以看到瓦片地址中有三個變數: x 、 y 、 z

通過這三個變數就可以定位到一張瓦片,比如下面這個地址,行號為 109280 ,列號為 53979 ,縮放層級為 17 :

對應的瓦片為:

關於瓦片的更多信息可以閱讀 瓦片地圖原理 。

高德地圖使用的是 GCJ-02坐標系 ,也稱火星坐標系,由中國國家測繪局在02年發布,是在GPS坐標( WGS-84 坐標系)基礎上經加密後而來,也就是增加了非線性的偏移,讓你摸不準真實位置,為了國家安全,國內地圖服務商都需要使用 GCJ-02坐標系 。

WGS-84 坐標系是國際通用的標准, EPSG 編號為 EPSG:4326 ,通常GPS設備獲取到的原始經緯度和國外的地圖廠商使用的都是 WGS-84 坐標系。

這兩種坐標系都是地理坐標系,球面坐標,單位為 度 ,這種坐標方便在地球上定位,但是不方便展示和進行面積距離計算,我們印象中的地圖都是平面的,所以就有了另外一種平面坐標系,平面坐標系是通過投影的方式從地理坐標系中轉換過來,所以也稱為投影坐標系,通常單位為 米 ,投影坐標系根據投影方式的不同存在多種,在 Web 開發的場景里通常使用的是 Web墨卡托投影 ,編號為 EPSG:3857 ,它基於 墨卡托投影 ,把 WGS-84 坐標系投影成正方形:

這是通過舍棄了南北 85.051129緯度 以上的地區實現的,因為它是正方形,所以一個大的正方形可以很方便的被分割為更小的正方形。

坐標系更詳細的信息可參考 GIS之坐標系統 , EPSG:3857 的詳細信息可參考 EPSG:3857 。

上一節里我們簡單介紹了一下坐標系,按照 Web 地圖的標准,我們的地圖引擎也選擇支持 EPSG:3857 投影,但是我們通過高德工具獲取到的是火星坐標系的經緯度坐標,所以第一步要把經緯度坐標轉換為 Web墨卡托 投影坐標,這里為了簡單,先直接把火星坐標當做 WGS-84 坐標,後面再來看這個問題。

轉換方法網上一搜就有:

3857 坐標有了,它的單位是 米 ,那麼怎麼轉換成瓦片的行列號呢,這就涉及到 解析度 的概念了,即地圖上一像素代表實際多少米,解析度如果能從地圖廠商的文檔里獲取是最好的,如果找不到,也可以簡單計算一下(如果使用計算出來的也不行,那就只能求助搜索引擎了),我們知道地球半徑是 6378137 米, 3857 坐標系把地球當做正圓球體來處理,所以可以算出地球周長,投影是貼著地球赤道的:

所以投影成正方形的世界平面圖後的邊長代表的就是地球的周長,前面我們也知道了每一層級的瓦片數量的計算方式,而一張瓦片的大小一般是 256*256 像素,所以用地球周長除以展開後的世界平面圖的邊長就知道了地圖上每像素代表實際多少米:

地球周長算出來是 40075016.68557849 ,可以看到 OpenLayers 就是這么計算的:

3857 坐標的單位是 米 ,那麼把坐標除以解析度就可以得到對應的像素坐標,再除以 256 ,就可以得到瓦片的行列號:

函數如下:

接下來我們把層級固定為 17 ,那麼解析度 resolution 就是 1.194328566955879 ,雷峰塔的經緯度轉成 3857 的坐標為: [13374895.665697495, 3533278.205310311] ,使用上面的函數計算出來行列號為: [43744, 11556] ,我們把這幾個數據代入瓦片的地址里進行訪問:

一片空白,這是為啥呢,其實是因為原點不一樣, 4326 和 3857 坐標系的原點在赤道和本初子午線相交點,非洲邊上的海里,而瓦片的原點在左上角:

再來看下圖會更容易理解:

3857 坐標系的原點相當於在世界平面圖的中間,向右為 x 軸正方向,向上為 y 軸正方向,而瓦片地圖的原點在左上角,所以我們需要根據圖上【綠色虛線】的距離計算出【橙色實線】的距離,這也很簡單,水平坐標就是水平綠色虛線的長度加上世界平面圖的一半,垂直坐標就是世界平面圖的一半減去垂直綠色虛線的長度,世界平面圖的一半也就是地球周長的一半,修改 getTileRowAndCol 函數:

這次計算出來的瓦片行列號為 [109280, 53979] ,代入瓦片地址:

結果如下:

可以看到雷峰塔出來了。

我們現在能根據一個經緯度找到對應的瓦片,但是這還不夠,我們的目標是要能在瀏覽器上顯示出來,這就需要解決兩個問題,一個是載入多少塊瓦片,二是計算每一塊瓦片的顯示位置。

渲染瓦片我們使用 canvas 畫布,模板如下:

地圖畫布容器 map 的大小我們很容易獲取:

地圖中心點我們設在畫布中間,另外中心點的經緯度 center 和縮放層級 zoom 因為都是我們自己設定的,所以也是已知的,那麼我們可以計算出中心坐標對應的瓦片:

縮放層級還是設為 17 ,中心點還是使用雷峰塔的經緯度,那麼對應的瓦片行列號前面我們已經計算過了,為 [109280, 53979] 。

中心坐標對應的瓦片行列號知道了,那麼該瓦片左上角在世界平面圖中的像素位置我們也就知道了:

計算出來為 [27975680, 13818624] 。這個坐標怎麼轉換到屏幕上呢,請看下圖:

中心經緯度的瓦片我們計算出來了,瓦片左上角的像素坐標也知道了,然後我們再計算出中心經緯度本身對應的像素坐標,那麼和瓦片左上角的差值就可以計算出來,最後我們把畫布的原點移動到畫布中間(畫布默認原點為左上角,x軸正方向向右,y軸正方向向下),也就是把中心經緯度作為坐標原點,那麼中心瓦片的顯示位置就是這個差值。

補充一下將經緯度轉換成像素的方法:

計算中心經緯度對應的像素坐標:

計算差值:

最後通過 canvas 來把中心瓦片渲染出來:

這里先來看看 getTileUrl 方法的實現:

這里隨機了四個子域: webrd01 、 webrd02 、 webrd03 、 webrd04 ,這是因為瀏覽器對於同一域名同時請求的資源是有數量限制的,而當地圖層級變大後需要載入的瓦片數量會比較多,那麼均勻分散到各個子域下去請求可以更快的渲染出所有瓦片,減少排隊等待時間,基本所有地圖廠商的瓦片服務地址都支持多個子域。

為了方便看到中心點的位置,我們再額外渲染兩條中心輔助線,效果如下:

可以看到中心點確實是雷峰塔,當然這只是渲染了中心瓦片,我們要的是瓦片鋪滿整個畫布,對於其他瓦片我們都可以根據中心瓦片計算出來,比如中心瓦片左邊的一塊,它的計算如下:

所以我們只要計算出中心瓦片四個方向各需要幾塊瓦片,然後用一個雙重循環即可計算出畫布需要的所有瓦片,計算需要的瓦片數量很簡單,請看下圖:

畫布寬高的一半減去中心瓦片占據的空間即可得到該方向剩餘的空間,然後除以瓦片的尺寸就知道需要幾塊瓦片了:

我們把中心瓦片作為原點,坐標為 [0, 0] ,來個雙重循環掃描一遍即可渲染出所有瓦片:

效果如下:

很完美。

拖動可以這么考慮,前面已經實現了渲染指定經緯度的瓦片,當我們按住進行拖動時,可以知道滑鼠滑動的距離,然後把該距離,也就是像素轉換成經緯度的數值,最後我們再更新當前中心點的經緯度,並清空畫布,調用之前的方法重新渲染,不停重繪造成是在移動的視覺假象。

監聽滑鼠相關事件:

在 onMousemove 方法里計算拖動後的中心經緯度及重新渲染畫布:

movementX 和 movementY 屬性能獲取本次和上一次滑鼠事件中的移動值,兼容性不是很好,不過自己計算該值也很簡單,詳細請移步 MDN 。乘以當前解析度把 像素 換算成 米 ,然後把當前中心點經緯度也轉成 3857 的 米 坐標,偏移本次移動的距離,最後再轉回 4326 的經緯度坐標作為更新後的中心點即可。

為什麼 x 是減, y 是加呢,很簡單,我們滑鼠向右和向下移動時距離是正的,相應的地圖會向右或向下移動, 4326 坐標系向右和向上為正方向,那麼地圖向右移動時,中心點顯然是相對來說是向左移了,因為向右為正方向,所以中心點經度方向就是減少了,所以是減去移動的距離,而地圖向下移動,中心點相對來說是向上移了,因為向上為正方向,所以中心點緯度方向就是增加了,所以加上移動的距離。

更新完中心經緯度,然後清空畫布重新繪制:

效果如下:

可以看到已經凌亂了,這是為啥呢,其實是因為圖片載入是一個非同步的過程,我們滑鼠移動過程中,會不斷的計算出要載入的瓦片進行載入,但是可能上一批瓦片還沒載入完成,滑鼠已經移動到新的位置了,又計算出一批新的瓦片進行載入,此時上一批瓦片可能載入完成並渲染出來了,但是這些瓦片有些可能已經被移除畫布,不需要顯示,有些可能還在畫布內,但是使用的還是之前的位置,渲染出來也是不對的,同時新的一批瓦片可能也載入完成並渲染出來,自然導致了最終顯示的錯亂。

知道原因就簡單了,首先我們加個緩存對象,因為在拖動過程中,很多瓦片只是位置變了,不需要重新載入,同一個瓦片載入一次,後續只更新它的位置即可;另外再設置一個對象來記錄當前畫布上應該顯示的瓦片,防止不應該出現的瓦片渲染出來:

因為需要記錄瓦片的位置、載入狀態等信息,我們創建一個瓦片類:

然後修改之前的雙重循環渲染瓦片的邏輯:

效果如下:

可以看到,拖動已經正常了,當然,上述實現還是很粗糙的,需要優化的地方很多,比如:

1.一般會先排個序,優先載入中心瓦片

2.緩存的瓦片越來越多肯定也會影響性能,所以還需要一些清除策略

這些問題有興趣的可以自行思考。

拖動是實時更新中心點經緯度,那麼縮放自然更新縮放層級就行了:

效果如下:

功能是有了,不過效果很一般,因為我們平常使用的地圖縮放都是有一個放大或縮小的過渡動畫,而這個是直接空白然後重新渲染,不仔細看都不知道是放大還是縮小。

所以我們不妨加個過渡效果,當我們滑鼠滾動後,先將畫布放大或縮小,動畫結束後再根據最終的縮放值來渲染需要的瓦片。

畫布默認縮放值為 1 ,放大則在此基礎上乘以 2 倍,縮小則除以 2 ,然後動畫到目標值,動畫期間設置畫布的縮放值及清空畫布,重新繪制畫布上的已有瓦片,達到放大或縮小的視覺效果,動畫結束後再調用 renderTiles 重新渲染最終縮放值需要的瓦片。

效果如下:

雖然效果還是一般,不過至少能看出來是在放大還是縮小。

前面還遺留了一個小問題,即我們把高德工具上選出的經緯度直接當做 4326 經緯度,前面也講過,它們之間是存在偏移的,比如手機 GPS 獲取到的經緯度一般都是 84 坐標,直接在高德地圖顯示,會發現和你實際位置不一樣,所以就需要進行一個轉換,有一些工具可以幫你做些事情,比如 Gcoord 、 coordtransform 等。

上述效果看著比較一般,其實只要在上面的基礎上稍微加一點瓦片的淡出動畫,效果就會好很多,目前一般都是使用 canvas 來渲染 2D 地圖,如果自己實現動畫不太方便,也有一些強大的 canvas 庫可以選擇,筆者最後使用 Konva.js 庫重做了一版,加入了瓦片淡出動畫,最終效果如下:

另外只要搞清楚各個地圖的瓦片規則,就能稍加修改支持更多的地圖瓦片:

具體實現限於篇幅不再展開,有興趣的可以閱讀本文源碼。

本文詳細的介紹了一個簡單的 web 地圖開發過程,上述實現原理僅是筆者的個人思路,不代表 openlayers 等框架的原理,因為筆者也是 GIS 的初學者,所以難免會有問題,或更好的實現,歡迎指出。

在線 demo : https://wanglin2.github.io/web_map_demo/

完整源碼: https://github.com/wanglin2/web_map_demo