esri-leaflet入門教程(3)-自定義底圖
by 李遠祥
在前面的篇章我們已經了解到了怎么樣使用FlatUI、leaflet和esri-leaflet 去搭建簡單的地圖應用界面了。如果有著手寫代碼的朋友可能已經發現第一個Hello World 程序,使用的就是esri 的 Topographic 樣式底圖。底圖這東西從來都是個好東西,放在互聯網領域就是一個免費而好用的資源,而在專業GIS領域,它就是一個權威和標準,所有的地圖和數據資源都必須疊加在底圖之上。
那么,問題來了。esri-leaflet提供的是自家的底圖,其服務器放在美國,而且是英文樣式,在中國區域的數據也不全,基本上無法使用。而大部分的國內用戶要么考慮國內的免費地圖,如天地圖、高德等或者是內網用戶使用自家發布的底圖服務,那就無法直接通過 L.esri.basemapLayer 方法進行加載了,因為底圖使用的是關鍵字的方式進行代入,而且是唯一替換的。也有人說可以變通一下,不使用底圖,而是使用其 L.esri.tiledMapLayer 加載ArcGIS的切片服務。但這種方式比較麻煩,因為每次切換都必須重新控制圖層的順序,尤其是在底圖之前疊加了數據之后,會讓程序變得尤其復雜。
leaf本身就是一個非常優秀的地圖框架,其優美的接口設計以及良好的擴展性,確實值得稱贊。仔細想一下,esri都能在其基礎上定義自己的basemap,為什么我們不自己去動手做一下呢,只要動手,就會擴展底圖其實并不困難。在esri-leaflet的介紹章節中,我們可以找到其源碼下載的地址,并下載api的源碼。筆者試用的是 esri-leaflet-2.0.7 版本,解壓后可以看到其目錄結構如下圖
其中筆者直接使用的是js腳本就是在dist中分發出來的 esri-leaflet.js 文件,經過了混淆之后,這個文件非常小。不過這不是我們研究的對象,要看清楚esri是怎么擴展leaflet的底圖,就必須參考 src 目錄下的源碼,其中一個就是Layers目錄,里面的js都是對leaflet的圖層接口進行適配,如下圖,找到 BasemapLayer.js 文件,用文本編輯器打開,就可以窺探其內部。
通過這個代碼片段,可以看到esri的BasemapLayer 其實就是擴展自leaflet的 L.TileLayer ,并且遵循其編寫規范。以其“Streests”底圖為例子,可以看到其底圖定義的結構
Streets: {urlTemplate: tilePRotocol + '//{s}.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',options: {minZoom: 1,maxZoom: 19,subdomains: ['server', 'services'],attribution: 'USGS, NOAA',attributionUrl: 'https://static.arcgis.com/attribution/World_Street_Map'}}其中Streets 為底圖的名稱,這對應的就是底圖選擇接口中的key值,urlTemplate 對應的就是地圖服務路徑,當然了,這個路徑不完全是ArcGIS Server發布的切片服務地址,需要在原來完整的路徑后面補上 /tile/{z}/{y}/{x} 其基本寫法就是 “【ArcGIS切片服務路徑】/tile/{z}/{y}/{x} “ ;而接下來的 options 參數配置就是使用底圖時的一些配置參數,例如指定最大和最小的比例尺,子服務器選擇等,這些都是可以定義的。如果想了解得更加什么,那就直接到leaflet官網上去看 其 tileLayer 接口,附上參考網址 http://leafletjs.com/reference-1.0.3.html#tilelayer 可以看到相關的說明,跟esri擴展的BasemapLayer是一樣的。
了解完原理之后,那接下來就非常好辦了,那就直接進入動手環節。如果想自己也像esri一樣去適配自己的底圖,可以參考它的寫法。當然了,如果技術到家,可以自行編寫一個適配的js ,例如前面說到在leaflet官網上有一個名叫“Tao Huang” 的人就自行寫了一個適配中國天地圖、高德地圖以及geoq地圖的擴展。如果需要考慮到程序的優美和可讀,建議自行編寫js。但如果想簡單粗暴的實現,筆者也有一個非常快捷的方式,就是直接修改混淆過的 esri-leaflet.js 文件,在其基礎上加入自定義的底圖服務。
在IDE中打開esri-leaflet.js ,筆者使用的就是HBuilder ,因為它里面有一個功能可以幫助我們重新排版代碼,如下圖
整理之后原本那些混淆的代碼就會以一種程序員可以閱讀的格式出現了。其他的一些JS IDE 也是可以實現的,主要是看使用習慣。整理后可以看到代碼已經變得比較整齊了。通過查找關鍵字定位到前面所說的BasemapLayer擴展的位置,如下圖
esri-leaflet 的所有底圖服務都在這里重新定義了一遍。所以,接下來只需要依葫蘆畫瓢,將 Streets代碼塊拷貝多次,并在這個基礎上進行修改就行了。為了適配國內免費的底圖服務,可以直接在此追加上筆者以下的代碼
Streets: {urlTemplate: yt + "//{s}.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}",options: {minZoom: 1,maxZoom: 19,subdomains: ["server", "services"],attribution: "USGS, NOAA",attributionUrl: "https://static.arcgis.com/attribution/World_Street_Map"}},//自定義中國灰色地圖(ArcGIS Server切片服務,來自arcgisonline.cn)chinagray: {urlTemplate: "http://cache1.arcgisonline.cn/ArcGIS/rest/services/ChinaOnlineStreetGray/MapServer/tile/{z}/{y}/{x}",options: {minZoom: 1,maxZoom: 19,}},//自定義中國彩色地圖(ArcGIS Server切片服務,來自arcgisonline.cn)chinacolor: {urlTemplate: "http://cache1.arcgisonline.cn/ArcGIS/rest/services/ChinaOnlineStreetColor/MapServer/tile/{z}/{y}/{x}",options: {minZoom: 1,maxZoom: 19}},//天地圖矢量地圖服務TianDiTuVec: {urlTemplate: "http://t{s}.tianditu.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}",options: {minZoom: 1,maxZoom: 19,subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']}},//天地圖矢量標注服務TianDiTuVec_A: {urlTemplate: "http://t{s}.tianditu.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}",options: {minZoom: 1,maxZoom: 19,subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']}},//天地圖衛星底圖服務TianDiTuSat: {urlTemplate: "http://t{s}.tianditu.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}",options: {minZoom: 1,maxZoom: 19,subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']}},//天地圖衛星影像標注TianDiTuSat_A: {urlTemplate: "http://t{s}.tianditu.cn/DataServer?T=cia_w&X={x}&Y={y}&L={z}",options: {minZoom: 1,maxZoom: 19,subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']}},//高德矢量地圖服務GaodeVec: {urlTemplate: "http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",options: {minZoom: 1,maxZoom: 19,subdomains: ['0', '1', '2', '3', '4']}},//高德矢量地圖服務GaodeVec_A: {urlTemplate: "http://webst0{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}",options: {minZoom: 1,maxZoom: 19,subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']}},這個代碼都是比較好閱讀的了,如果還有自己發布的ArcGIS Server切片服務作為底圖,可以按照這個方式追加進去。追加完之后,唯一要記住的就是這些底圖的名稱(底圖的名稱也是可以自定義,所以,一定要定義一個比較好記的名稱,方便自己也方便別人)。
接下來就看怎么調用了。首先底圖的調用還是使用esri-leaflet的方式進行調用,畢竟我們是直接在其js上面追加的,所以規范還是要保持一致。
map = L.map("mapDiv").setView([23.1, 113.2], 9);//定位在廣州customBaselayer = L.esri.basemapLayer('chinacolor').addTo(map);代碼中的chinacolor就是之前定義的底圖的名稱。這樣在加載的時候就變成了彩色版的中國地圖了。如下圖
下一步馬上就是實現底圖的切換功能了。按照esri-leaflet 和leaflet上的例子,都是使用L.control 來實現,于界面上來看非常的丑陋。所以,我們采用bootstrap 或 FlatUI 的菜單來實現。這樣會比較美觀。先來看看以下定義的這個切換方法
//切換基礎底圖var map;var customBaselayer; //底圖圖層var layerLabels; //底圖標注function setBaseMap(idName){var mapid= idName.id;if(customBaselayer){map.removeLayer(customBaselayer);}if(layerLabels){map.removeLayer(layerLabels);}if(mapid=="chinagray"){customBaselayer= L.esri.basemapLayer("chinagray");map.addLayer(customBaselayer);}else if(mapid=="chinacolor"){customBaselayer= L.esri.basemapLayer("chinacolor");map.addLayer(customBaselayer);}else if(mapid=="TianDiTuVec"){customBaselayer= L.esri.basemapLayer("TianDiTuVec");map.addLayer(customBaselayer);layerLabels = L.esri.basemapLayer("TianDiTuVec_A");map.addLayer(layerLabels);}else if(mapid=="TianDiTuSat"){customBaselayer= L.esri.basemapLayer("TianDiTuSat");map.addLayer(customBaselayer);layerLabels = L.esri.basemapLayer("TianDiTuSat_A");map.addLayer(layerLabels);}else if(mapid=="GaodeVec"){customBaselayer= L.esri.basemapLayer("GaodeVec");map.addLayer(customBaselayer);layerLabels = L.esri.basemapLayer("GaodeVec_A");map.addLayer(layerLabels);}}這段代碼是在全局設置了map和custombaselayer,這樣方便我們在其他地方調用。setBaseMap 方法里面帶的參數就是dom對象,只要觸發了點擊,就傳觸發的dom給該方法,該方法通過dom的id來判斷。所以,無論是菜單還是按鈕或者是標簽等,都必須給一個與底圖名稱一樣的id名稱。先來判斷一下是否有底圖,如果有就先移除后添加。還有就是天地圖和高德地圖都是講切片和標注分開的,因此,要對這類切片和標注分開的地圖服務進行處理,在切換的時候同時加上其標注服務。
接下來再看在HTML頁面代碼中的菜單,在其標簽上加入調用代碼,如下
<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">底圖切換 <b class="caret"></b></a><ul class="dropdown-menu"><li><a id="chinacolor" href='javaScr </li><li><a id="chinagray" href='Javascript:void(0)' onclick='setBaseMap(this)'>中國地圖灰色版</a></li><li><a id="TianDiTuVec" href='JavaScript:void(0)' onclick='setBaseMap(this)'>天地圖矢量地圖</a></li><li><a id="TianDiTuSat" href='JavaScript:void(0)' onclick='setBaseMap(this)'>天地圖影像地圖</a></li><li><a id="GaodeVec" href='JavaScript:void(0)' onclick='setBaseMap(this)'>高德地圖</a></li></ul></li>簡單的修改,就可以使用esri-leaflet來實現arcgisonline.cn 、天地圖、高德地圖的切換了。效果如下圖所示
總結:leaflet是一個非常優秀的框架,擴展性非常好。如果想好好的使用,可以通過參考它官網上的plugins ,通過參考別人的代碼去進一步完善其功能。而筆者在本章中使用的方式也就是直接參考esri在GitHub上上傳的源碼,并修改其發布的代碼版本,也算是一種投機取巧的方法吧。
新聞熱點
疑難解答