簡介
公司項目需要用到離線GIS應(yīng)用,因此做了一個詳細(xì)的技術(shù)調(diào)研。時間大概兩天,方案選型確定,到服務(wù)的搭建,及包含具體應(yīng)用場景需要的換膚功能。
首先方案大概是有3個。
1.采用拼接地圖資源采用image方式渲染整個地圖,然后在每個層級的圖片上進(jìn)行點位標(biāo)注,和數(shù)據(jù)加載。優(yōu)點,加載速度快,直接渲染業(yè)務(wù)需要的地圖部分即可,缺點前端工作量大,不利于后續(xù)產(chǎn)品化的發(fā)展。
2.采用開源的openstreetmap,缺點openstreetmap開源的地圖不夠詳細(xì),沒有需要展示的詳細(xì)資源。
3.采用arcgis方式,下載最新地圖的瓦片google,高德等等,自己搭建離線gis服務(wù)。缺點,項目應(yīng)用需要搭建單獨的gis服務(wù),優(yōu)點利于后續(xù)產(chǎn)品化的發(fā)展,不用單獨再造輪子。一次搭建,后續(xù)項目可以直接應(yīng)用。
大體需要的工具及服務(wù), geowebcache+全能電子地圖下載器+leaflet
geowebcache為arcgis提供基礎(chǔ)的服務(wù),簡單說就是把所有的地圖資源放到這個服務(wù)里面,供你來調(diào)用加載,渲染。
全能電子地圖下載器,為你提供基礎(chǔ)的地圖資源(png)圖片。
leaflet是地圖組件,就是提供地圖內(nèi)的標(biāo)點,范圍等等的一個組件??梢詮墓倬W(wǎng)看到,不做過多闡述。
本文只對第三點展開討論。從0開始的搭建,部署,及展示,以及產(chǎn)品后續(xù)需要的換膚功能。服務(wù)搭建參考
1.https://www.cnblogs.com/luxiaoxun/p/5022333.html
2.https://www.cnblogs.com/ChineseMoonGod/p/6934928.html
雖然參考博客園的帖子,但是還會單獨重新寫一遍搭建流程,方便后面小伙伴整體參考吧,會比之前更詳細(xì)。
服務(wù)搭建(geowebcache):
服務(wù)下載地址:
https://www.geowebcache.org/


這里要說明,我下載了最新版本,但是地圖服務(wù)一直沒加載出來,具體就是下載了相應(yīng)的瓦片,但是整個界面是空白。因為時間倉促,這里不得不降級,采用了1.8.0,1.9.0版本均可以正常顯示,具體原因未知。后續(xù)采用了1.9.0版本。具體調(diào)研的版本有:

。
1.運行服務(wù)
geowebcache下載之后是一個war包,就是我們傳統(tǒng)的web服務(wù)。放在tomcat的webapps目錄下首先運行起來。

服務(wù)運行效果
地址:http://localhost:8080/geowebcache

2.修改配置
打開此文件


配置完之后重啟tomcat,會看到

到這里我們暫時停止配置,下載我們需要的地圖瓦片。
3.地圖資源下載
地圖資源下載我采用的是,全能電子地圖下載器(破解版)。

工具操作不做過多闡述,應(yīng)該看看就懂了。
下載好瓦片后,需要進(jìn)行地圖拼接

選擇此參數(shù)

拼接完成后會生成以下文件


_alllayers內(nèi)存放就是我們地圖的瓦片資源,就是一堆png圖片。實際地圖渲染的效果就是在服務(wù)資源內(nèi)將png拼在在一起,并且加載web內(nèi)。

每個層級的地圖瓦片資源。
備注:之前參考,說conf.cdi,和conf.xml需要打開,然后保存為unt-8無bom格式,我就直接跟著操作了,具體有bom,和無bom區(qū)別我沒有具體操作過。操作流程就是,用nodepad++打開兩個文件,然后再保存一下。

。
4.繼續(xù)修改配置
打開我們上一步自動生成的,geowebcache.xml文件。
添加如下幾行字符

<arcgisLayer>
<name>ARCGIS-Demo</name>
<tilingScheme>D:\\GisMap\\Layer\\conf.xml</tilingScheme>
<tileCachePath>D:\\GisMap\\Layer\\_alllayers</tileCachePath>
</arcgisLayer>
很明顯conf.xml,_alllayers就是我們之前拼接完地圖的絕對路徑。
接下來重啟服務(wù),查看效果。

可以看到我們剛剛配置的服務(wù),點擊png即可預(yù)覽效果


5.對外提供服務(wù)
正常應(yīng)用時,肯定不是預(yù)覽效果就達(dá)到了我們的目的。我們需要調(diào)用類似,百度,google地圖那種,在我們項目內(nèi)加載服務(wù),并且根絕第三方的接口進(jìn)行地圖相關(guān)的操作,例如標(biāo)點位信息,做一些面積圖處理等等。
服務(wù)調(diào)用腳本如下:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Leaflet - Offline Demo</title>
<link rel="stylesheet" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
</head>
<body>
<div id="map" style="height:100vh;background-color: #033345" ></div>
<script type="text/javascript">
//地圖的中心
var mapCenter = new L.LatLng(28.80748,104.593578);
var map = new L.Map('map', {
center : mapCenter,
zoom:5,//默認(rèn)展開的地圖層級,注意根據(jù)業(yè)務(wù)需求自己調(diào)整。
maxZoom:18,//最大層級,注意根據(jù)業(yè)務(wù)需求自己調(diào)整。
minZoom:15,//最小層級,注意根據(jù)業(yè)務(wù)需求自己調(diào)整。
attribution : '高德普通地圖'
});
var wmsLayer = L.tileLayer.wms("http://localhost:8080/geowebcache/service/wms", {
layers: 'ARCGIS-Demo1',//注意這里要和geowebcache.xml內(nèi)的 <name>ARCGIS-Demo</name>對應(yīng)
format: 'image/png'
});
wmsLayer.addTo(map);
//點位標(biāo)記
var marker = new L.Marker([28.80748,104.593578]);
var marker1 = new L.Marker([28.814023,104.571005]);
map.addLayer(marker);
map.addLayer(marker1);
marker.bindPopup("<br>五糧液廠區(qū)!</br><button onclick='alert(1);'>進(jìn)入</button></p>").openPopup();
marker1.bindPopup("<p>紅若小學(xué)!</br><button onclick='alert(2);'>進(jìn)入</button></p>").openPopup();
//標(biāo)記面積
var latlngs2 = [[28.812089,104.589441],[28.810779,104.583208],[28.808918,104.579024],[28.808072,104.579432],[28.805609,104.579432],[28.790829,104.591148]
,[28.802412,104.599473]]; //區(qū)域坐標(biāo)組
var areaLayer = L.polygon(latlngs2, {color: 'red',fillColor:'blue',weight:1}).addTo(map); //添加到地圖
</script>
</body>
</html>
這里有兩點要說明
1.name屬性
layers的name必須配置正確,否則地圖不加載。

2.地圖中心點
地圖點位中心的坐標(biāo),就是mapcenter節(jié)點的配置,如果不正確,也會導(dǎo)致看不到地圖。這里我用的高德在線的取點坐標(biāo)進(jìn)行標(biāo)記。
地址:https://lbs.amap.com/console/show/picker
要注意坐標(biāo)不要配置反了,例如


5.查看服務(wù)運行效果
這樣已經(jīng)達(dá)到,調(diào)用地圖服務(wù)資源返回到我們的內(nèi)部應(yīng)用中。

6.換膚功能
因為不用應(yīng)用場景下,可能要求地圖的皮膚顏色不同。這里我調(diào)研,發(fā)現(xiàn)沒有直接可應(yīng)用的地圖換膚資源。因此就得動腦子了,因為我們_alllayers文件目錄內(nèi)存儲的是默認(rèn)的所有瓦片資源。那我應(yīng)該用腳本,把每個對應(yīng)png的色值重新調(diào)整,再保存到目錄內(nèi),然后調(diào)用即可。腳本內(nèi)容如下:
# *_*coding:utf-8 *_*
from PIL import Image
import os
#
# 遍歷文件夾
def walkFile(file):
for root, dirs, files in os.walk(file):
# root 表示當(dāng)前正在訪問的文件夾路徑
# dirs 表示該文件夾下的子目錄名list
# files 表示該文件夾下的文件list
# 遍歷文件
for f in files:
path = os.path.join(root, str(f));
i = 0
j = 0
# path = path.replace("\\","\\")
img = Image.open(path) # 讀取系統(tǒng)的內(nèi)照片
# img = Image.open("D:\\GisMap\\Layer1\\_alllayers\\L05\\R0000000A\\C0000001A.png") # 讀取系統(tǒng)的內(nèi)照片
print (img.size) # 打印圖片大小
print (img.getpixel((4, 4)))
width = img.size[0] # 長度
height = img.size[1] # 寬度
for i in range(0, width): # 遍歷所有長度的點
for j in range(0, height): # 遍歷所有寬度的點
data = (img.getpixel((i, j))) # 打印該圖片的所有點
if isinstance(data, tuple) and len(data) == 3:
print (data) # 打印每個像素點的顏色RGBA的值(r,g,b,alpha)
print (data[0]) # 打印RGBA的r值
if (data[0] >= 170 and data[1] >= 170 and data[2] >= 170): # RGBA的r值大于170,并且g值大于170,并且b值大于170
img.putpixel((i, j), (3,52,71,255)) # 則這些像素點的顏色改成大紅色
elif (data[0] >= 68 and data[1] >= 68 and data[2] >= 68 and data[0] >= 111 and data[1] >= 111 and data[2] >= 111): # RGBA的r值大于170,并且g值大于170,并且b值大于170
img.putpixel((i, j), (0,94,94,255)) # 則這些像素點的顏色改成大紅色
elif (data[0] >= 0 and data[1] >= 0 and data[2] >= 0 and data[0] <= 68 and data[1]<= 68 and data[2]<= 68): # RGBA的r值大于170,并且g值大于170,并且b值大于170
img.putpixel((i, j), (0, 94, 94, 255)) # 則這些像素點的顏色改成大紅色
img = img.convert("RGB") # 把圖片強制轉(zhuǎn)成RGB
img.save(path) # 保存修改像素點后的圖片
def main():
walkFile("D:\\GisMap\\Layer1\\_alllayers")
if __name__ == '__main__':
main()
這里我是隨便在高德的皮膚中,取了幾個顏色進(jìn)行替換。但是這里有個遺留問題就是,單線程對每個像素進(jìn)行替換,效率還是蠻差的。這里還是有很大的優(yōu)化空間,多線程,對每個層級不同的瓦片資源進(jìn)行批量更新。
高德官方皮膚截圖如下:

腳本處理效果如下:

這里只是粗略的進(jìn)行幾個大的色值替換,同樣也有很大的優(yōu)化空間。
7換膚資源切換
同理,因為我們處理好了另一個皮膚的所有文件后,保存在一個新的_alllayers文件夾內(nèi)。

再次編輯geowebcache.xml,增加新皮膚的節(jié)點信息,然后重啟服務(wù)。
<arcgisLayer>
<name>ARCGIS-Demo1</name>
<tilingScheme>D:\\GisMap\\Layer1\\conf.xml</tilingScheme>
<tileCachePath>D:\\GisMap\\Layer1\\_alllayers</tileCachePath>
</arcgisLayer>
