日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WMTS服务及地图瓦片原理

發布時間:2023/12/18 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WMTS服务及地图瓦片原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

WMTS,web map tile service,網絡地圖瓦片服務;TMS,tile map service,瓦片地圖服務。名稱雖然不一致,但指的都是地圖瓦片服務,TMS形成更早,不過WMTS有官方OGC蓋章認可。

WMTS的目的是,更高效快速的加載渲染地圖數據。如果海量的地圖數據以矢量的形式傳輸到客戶端,在客戶端渲染,首先需要消耗大量的網絡流量,其次對客戶端的CPU也是很大的負荷。考慮到這些情況,WMTS提出預渲染圖塊的模式,在服務端將地圖渲染好,并根據比例尺分割不同的柵格圖塊,根據客戶端的請求,傳輸這些圖塊,提供給客戶端顯示。

目前,大部分PC端、手機端的地圖底圖使用的都是這種柵格瓦片。

?

在有數據的前提下,瓦片地圖的生產,大概要經過三個環節:

1.數據進行Web-Mercator投影,并進行配圖。

2.分層級渲染數據,并切分渲染后的柵格成果為地圖瓦片。

3.地圖瓦片分發。

?

一、數據投影

在數據庫中,地理數據都是以地心坐標系(如WGS84坐標系)存儲的,首先需要將以經緯度形式存儲的三維數據映射到二維平面上,映射方法既是Web-Mercator投影,它是一種正軸等角圓柱投影。

?

Web-Mercator投影,將世界坐標調整為左上角為(0, 0),右下角為(256, 256)的正方形,假設地圖投影在一個256像素*256像素的圖幅上:

?

其中x和y是像素坐標,λ是經度,φ是緯度,pixel是像素,zoom level是地圖瓦片比例尺層級。

Web-Mercator投影的數據覆蓋范圍在經度[-180°,180°],緯度[-85.051129°, 85.051129°]之間,這有兩個好處,其一是避免將極點投影到無窮遠處,其二是能將整個投影地圖變成正方形。

更多坐標系與投影相關內容,可以參閱:https://blog.csdn.net/sinat_41310868/article/details/115551276

?

二、地圖配圖

當對數據進行投影之后,就需要根據數據類型、比例尺等內容對數據進行風格配置,因為不同比例尺級別,顯示的內容詳略不一,也需要分層級對數據進行概化。

例如鐵路需要顯示為黑白相間的線段,水系需要顯示為填充為淺藍色,邊線為深藍色的面;當比例尺層級大于18的時候,才顯示一些細部的道路和POI等。

?

地圖數據分層級配圖渲染比較復雜細致,更多內容這里就不贅述了,可以參閱《ARCGIS地圖配圖策略》文檔:https://wenku.baidu.com/view/63dd06f1b0717fd5360cdc76.html

三、地圖切片

地圖切片是本文重點,各家圖商地圖切片方式基本一致。

地圖瓦片的基本原理:

1.瓦片尺寸通常為256*256像素。

2.地圖的最小zoom level是0,這時候,整個世界地圖是一張瓦片,根據Web-Mercator投影公式可知,在zoom level=0的時候,經度[-180°,180°],緯度[-85.051129°, 85.051129°]區間的數據可以被投影在一個256*256像素的正方形圖片上。

3.zoom level越大,組成世界地圖的瓦片數越多,可以展示的地圖內容越詳細。

4.某一zoom level下的地圖瓦片是由它的上一層級的各瓦片切割成的4個瓦片組成,形成了瓦片金字塔。

5.瓦片通常都是png格式的,命名格式通常為z(zoom level)/x/y.png。

?

官方WMTS標準中,規定的瓦片地圖Web-Mercator投影坐標系原點為東經180°,南緯85.05°,x軸向右,y軸向上,瓦片等級(zoom level)最小為0,最大為24。

參見:https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification

?

但在這個標準出臺前,谷歌的瓦片地圖規范已經實行多年,并深受認可,OSM、高德、騰訊、geoq等都使用谷歌的瓦片地圖規范,谷歌瓦片地圖規范,坐標原點為東經180°,北緯85.05°,x軸向右,y軸向下,瓦片等級(zoom level)最小為0,最大為21。

參見:https://developers.google.com/maps/documentation/javascript/coordinates

?

以下截圖、公式和代碼都參考的是谷歌瓦片地圖規范,既y軸是向下的,由北向南。

?

經緯度坐標與瓦片的轉換,最關鍵的參數是zoom level(z)。

瓦片z=0,x=0,y=0,對應的坐標范圍是經度[-180,180],緯度[-85.05,85.05],對應的瓦片尺寸是256像素*246像素。

經緯度坐標(lng,lat)轉瓦片坐標(tileX,tileY)的公式如下:

tileX=int( (lng+180)/360*2^z)

tileY=(1- arsinh(tan(lat*π/180))/π) *2^(z-1)

其中z是zoom level,計算結果取整數。

其中反雙曲正弦函數arsinh(x),等價于ln(x+(x^2+1)^0.5)。

瓦片坐標(tileX,tileY)轉經緯度坐標(lng,lat)公式,計算結果為該瓦片對應的最小經度和最大緯度:

lng=tileX/2^z * 360-180

lat=arctan(sinh(π*(1-2*tileY/2^z)))*180/π

其中,z是zoom level,雙曲函數sinh(x)=(e^x-e^(-x))*0.5。

經緯度坐標(lng,lat)轉像素坐標(pixelX,pixelY)公式:

pixelX=(lng+180)/360*2^z*256%256

pixelY=(1-ln(tan(lat*π/180)+sec(lat*π/180))/(2*π))*2^z*256%256

?

公式中設定瓦片圖塊尺寸為256*256pixel,z是zoom level,web墨卡托平面坐標除以像素分辨率,再除以256取余就是像素坐標。

瓦片坐標(tileX,tileY)的像素坐標(pixelX,pixelY)轉經緯度坐標(lng,lat)公式:

lng=(tileX+pixelX/256)/2^z*360-180

lat=arctan(sinh(π-2*π*(tileY+pixelY/256)/2^z))*180/π

其中,z是zoom level。

在zoom level為3的時候,瓦片坐標與經緯度坐標的對照,其中dlng是最大經度與最小經度之差,dlat是最大緯度與最小緯度之差:

?

在zoom level為4的時候,瓦片坐標與經緯度坐標的對照:

?

經緯度、瓦片編號、像素坐標之間轉換的python代碼為:

import math # 像素分辨率 def getResolution(level):return 156543.03*math.pow(2,-level) # 經緯度轉瓦片 def lnglatToTile(lng,lat,level):tileX=int((lng+180)/360*math.pow(2,level))tileY=int((1-math.asinh(math.tan(math.radians(lat)))/math.pi)*math.pow(2,level-1))return tileX,tileY # 瓦片轉經緯度 def tileToLnglat(tileX,tileY,level):lng=tileX/math.pow(2,level)*360-180lat=math.degrees(math.atan(math.sinh(math.pi*(1-2*tileY/math.pow(2,level)))))return lng,lat # 經緯度轉像素 def lnglatToPixel(lng,lat,level):pixelX=round((lng+180)/360*math.pow(2,level)*256%256)pixelY=round((1-math.log(math.tan(math.radians(lat))+1/math.cos(math.radians(lat)))/(2*math.pi))*math.pow(2,level)*256%256)return pixelX,pixelY # 瓦片和像素轉經緯度 def pixelToLnglat(tileX,tileY,pixelX,pixelY,level):lng=(tileX+pixelX/256)/math.pow(2,level)*360-180lat=math.degrees(math.atan(math.sinh(math.pi-2*math.pi*(tileY+pixelY/256)/math.pow(2,level))))return lng,latif __name__ == '__main__':# 北京坐標[116.391305,39.905530]print(getResolution(3))print(lnglatToTile(116.37,39.640,10))print(tileToLnglat(843, 388,10))print(lnglatToPixel(116.37,39.640,10))print(pixelToLnglat(843, 388,2,256,10))

在WGS84參考橢球中,赤道長度為40075.016686 km,在zoom level為0的時候,假設瓦片大小為256*256像素,1個像素對應的實際地理空間尺寸為156543.03米,計算公式為:

40075.016686*1000/256≈6378137.0 * 2 * pi / 256≈156543.03m/pixel

由此可知,在各zoom level下的像素分辨率(1個像素對應實際空間尺寸)計算公式為:

resolution=156543.03/2^z

如果知道電腦屏幕的dpi,既電腦屏幕每英寸包含的像素個數,既可計算電腦屏幕上的地圖比例尺:

scale = 1 : (dpi * 1/0.0254 in/m * resolution)

地球赤道上,各zoomlevel下的瓦片的像素分辨率與屏幕比例尺:

?

四、瓦片分發

地圖瓦片的目的是優化數據傳輸和渲染的性能。

地圖瓦片的分發有如下特點:

1.后端會把渲染好的瓦片存儲為png格式,每個png格式的瓦片文件是256*256像素的。

2.每個zoom level是一個目錄,每列是一個子目錄,并且該列中的每個瓦片是一個文件。

3.每個瓦片的文件名為/z(zoom level)/x/y.png,客戶端通過拼接域名+文件名來請求瓦片。

4.因為服務器對客戶端的IP有連接數量的限制,所以地圖瓦片的服務器會提供幾個子域,瀏覽器均勻請求這些子域,來繞過IP限制,例如OSM地圖服務有三個子域,a.tile、b.tile、c.tile,它們指向的是同一個CDN(內容傳送網絡)。

大部分地圖瓦片都使用這種模式進行數據分發。

可以看一個OSM地圖的例子:

https://a.tile.openstreetmap.org/13/6745/3103.png

https://b.tile.openstreetmap.org/13/6745/3103.png

https://c.tile.openstreetmap.org/13/6745/3103.png

通過上面三個網址,都能獲取下圖的瓦片,從圖上可以看出,這個瓦片的x是6745,y是3103,zoom level是13。

?

?

五、瓦片拼接顯示

瓦片地圖在客戶端的顯示,就是網格平鋪,裝載對應瓦片的過程。

首先,在HTML文檔中,會先建立一個裝載地圖容器的DIV,設定地圖容器的中心坐標和zoom level,根據中心坐標、zoom level和DIV的長寬,計算出地理空間覆蓋范圍,進而計算出需要加載多少瓦片,加載哪些瓦片。

然后,就要在這個DIV中創建一些與瓦片尺寸一致的DOM(文檔對象模型),根據像素坐標與地理坐標的對應關系,計算出DOM與地圖瓦片的對應關系,請求瓦片的URL,將瓦片圖像加載進入DOM。

最后,在前端,瓦片通過DOM的拼接呈現出來。

瓦片的顯示大概就是這個流程。

下面的代碼是用leaflet模擬了地圖瓦片切片和組裝的過程,大概是那么個意思:

<!DOCTYPE html><html>?<head><title>瓦片原理-OSM</title><meta charset="utf-8" /><link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" /><script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script><style>body {padding: 0;margin: 0;}html, body, #map {height: 100%;width: 100%;}</style></head><body><div id="map"></div></body><script>var map = new L.Map('map', {? center: [41, 115], zoom: 0});var tiles = new L.GridLayer();tiles.createTile = function(coords) {console.log('coords:');console.log(coords);// 創建DOM裝載瓦片var tile = L.DomUtil.create('canvas', 'leaflet-tile');var ctx = tile.getContext('2d');// 獲取瓦片尺寸var size = this.getTileSize();console.log('size:');console.log(size);tile.width = size.x;tile.height = size.y;// 計算瓦片左上角的像素坐標var nwPoint = coords.scaleBy(size);// 計算瓦片左上角的地理坐標var nw = map.unproject(nwPoint, coords.z);// 計算瓦片右下角的像素坐標seCoords = new L.Point(coords.x+1,coords.y+1);seCoords.z = coords.z;console.log('seCoords:');console.log(seCoords);// 計算瓦片右下角的地理坐標var sePoint = seCoords.scaleBy(size);var se = map.unproject(sePoint, coords.z);// 加載瓦片圖像var img = new Image();var subdomains = ['a', 'b', 'c'];var index = Math.abs(coords.x + coords.y) % subdomains.length;img.src = 'http://'+subdomains[index]+'.tile.openstreetmap.org/'+coords.z+'/'+coords.x+'/'+coords.y+'.png';console.log(img);ctx.drawImage(img,0,0,size.x,size.y);console.log(ctx);// 標識對應的瓦片信息// 背景顏色ctx.fillStyle = 'white';// 背景尺寸ctx.fillRect(0, 0, size.x, 90);// 字體顏色ctx.fillStyle = 'black';ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom level: ' + coords.z, 20, 20);ctx.fillText('minlng: ' + nw.lng + ', minlat: ' + se.lat, 20, 40);ctx.fillText('maxlng: ' + se.lng + ', matlat: ' + nw.lat, 20, 60);var dlng = se.lng - nw.lng;var dlat = nw.lat - se.lat;ctx.fillText('dlng: ' + dlng + ', dlat: ' + dlat, 20, 80);ctx.strokeStyle = 'red';ctx.beginPath();ctx.moveTo(0, 0);ctx.lineTo(size.x-1, 0);ctx.lineTo(size.x-1, size.y-1);ctx.lineTo(0, size.y-1);ctx.closePath();ctx.stroke();return tile;};// 加載OSM瓦片代碼// L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {//??? attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>'// }).addTo(map);tiles.addTo(map);</script></html>

?

六、總結

地圖投影和地圖瓦片的原理并不復雜,了解原理后,實踐中遇到的很多疑難雜癥,就可以有解決依據了(本來想說迎刃而解,但發現,解決起來,也挺費勁)。

不管如何,了解原理,總歸會走得更遠。

?

總結

以上是生活随笔為你收集整理的WMTS服务及地图瓦片原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。