1.引言
由于Arcgis for javascript API不可以繪制Geojson,并且提供的Popup一般只可以彈出一個,在很多專題圖制作中,會遇到不少的麻煩。因此本文結合了兩個現有的Arcgis for javascript API擴充庫,對其進行改造達到繪制Geojson并同時彈出多個Popup的目的。
目前已有的兩個擴充庫github地址(可供單獨使用):
1.繪制Geojson的擴充庫:https://github.com/Esri/geojson-layer-js
2.多個Popup顯示的擴充庫:https://github.com/nickcam/PopupExtended
本文實現的效果圖:
?
? ? ? ? ? ? ? ? ? 圖1 上海5個地點的部分預報屬性 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖2 上海某三條航線的部分預報屬性
2. 各類依賴庫引入及前端HTML
首先需要先載入需要用的常用js、Arcgis及兩個擴充庫的js及部分css(下載地址見其github):
<!DOCTYPE html> <html> <head><title>Add GeoJSON and Display Multiple Popup</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=7,IE=9"><meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"><!-- ArcGIS API for JavaScript CSS--><link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css"><link rel="stylesheet" href="http://js.arcgis.com/3.13/dijit/themes/claro/claro.css"><!-- Web Framework CSS - Bootstrap (getbootstrap.com) and Bootstrap-map-js (github.com/esri/bootstrap-map-js) --><link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="./css/bootstrap.min.css"><!-- PopExtendCss --><link href="./vendor/ncam/PopupExtended.css" rel="stylesheet" /><!-- PopupExtended references --><script>var dojoConfig = {parseOnLoad: false,async: true,tlmSiblingOfDojo: false,packages: [{name: "ncam",location: location.pathname.replace(/\/[^/]+$/, '') + "ncam" }]};</script><!-- ArcGIS API for JavaScript library references --><script src="//js.arcgis.com/3.10"></script><!-- Terraformer reference --><script src="./vendor/terraformer/terraformer.min.js"></script> <script src="./vendor/terraformer-arcgis-parser/terraformer-arcgis-parser.min.js"></script><!-- other reference --><script src="./vendor/jquery.js"></script> </head> <body></body> </html>
加入底圖所需要的div與圖層切換的Button:
<body><div id="mapDiv"></div><button type="line" id="shanghaiPoint" class="btn btn-default buttonRight" style="top:20px;right:20px">上海區各點</button><button type="point" id="threeLine" class="btn btn-default buttonRight" style="top:70px;right:20px">三條航線</button> </body>
3.置入Popupextended并擴充geojsonlayer.js
然后從geojsonlayer.js源碼入手,開始將PopupExtended擴展其中,讓我們新構建的geojsonlayer直接可擁有多個Popup。在geojsonlayer.js的constructor中很容易可以找出infotemplate的set方法:
// Default popupif (options.infoTemplate !== false) {this.setInfoTemplate(options.infoTemplate || new InfoTemplate("GeoJSON Data", "${*}"));}
很明顯,geojsonlayer初始化時通過options傳入參數進行判斷并構造,所以實現本文目的的大致思路是將這里的setInfoTemplate替換成可以擴展的PopupExtended:
if (options.infoTemplate !== false) {//① create a PopupTemplatevar template = new PopupTemplate({title: "{name}",fieldInfos: [{ fieldName: "Id", label: "Id", visible: true },{ fieldName: "publishdate", label: "觀測日期", visible: true },{ fieldName: "waveheight", label: "浪高", visible: true },{ fieldName: "wavedirection", label: "浪向", visible: true },{ fieldName: "windspeed", label: "風速", visible: true },{ fieldName: "winddirection", label: "風向", visible: true },{ fieldName: "comfort", label: "等級", visible: true }],extended: { actions: [{ text: " IconText", className: "iconText", title: "Custom action with an icon and Text", click: function (feature) { alert("Icon Text clicked on " + "id: " + feature.attributes.id + " " + feature.attributes.name); } },{ text: "", className: "iconOnly", title: "Custom action only using an icon", click: function (feature) { alert("Icon action clicked on " + "id: " + feature.attributes.id + " " + feature.attributes.name); } } ],//uses a pretty bad custom theme defined in PopupExtended.css.scaleSelected: 1.6}});//② create a extend for basemapvar extendedPopup = new PopupExtended({extended: {themeClass: "light",draggable: true,defaultWidth: 250,actions: [{text: "其他", className: "defaultAction", title: "Default action added in extendedPopup properties.",click: function (feature) { alert("clicked feature - " + feature.attributes); }}],hideOnOffClick: false,multiple: true,},highlight: false,//titleInBody: false,}, dojo.create("div"));//③set the map to use the exteneded popup extendedPopup.setMap(options.baseMap);options.baseMap.infoWindow = extendedPopup;this.setInfoTemplate(options.infoTemplate || template);}
由上段代碼可見,引入Popup給GeoJSON共分為三步:①實例化一個你需要的PopupTemplate(這里起名為template),可指定你需要展示的主題、數據項及擴展的一些交互action;②實例化一個PopupExtended并設置一些默認的Popup屬性;③將實例化的PopupExtended——extendedPopup的Map設置為baseMap,并將baseMap的infowindow設置為extendedPopup,最后將geojsonlayer的infoTemplate設置為新構建的template。這樣便可以實現對置入底圖的geojsonlayer進行多個infoTemplate展示需求了。源代碼見github:展示效果如圖3:
?
圖3 對geojsonlayer擴充Popupextended后的顯示效果
如若只需增加多個Popup至geojsonlayer的話,以上部分足以實現了。
4.增加新的Attributes及調整Popup的樣式
由于設計上的需求,筆者需要從其他地址獲取觀測點的部分觀測值,并且筆者的老師覺得應該加一些icon給屬性,美化展示效果,所以需要重新構造兩部分:①獲取并為graphics增加新的attributes;②重構geojsonlayer的infoTemplate的content。
4.1 獲取并為graphics增加新的attributes:
通過在button上利用fetch及Promise.all來同時獲取6個點或3條航線的數據,并傳入至初始化geojsonlayer的函數內;
$("#shanghaiPoint").click(function(){// if first init geojsonlayerif(firstPointInit){var requestBZ = 'http://wx.dhybzx.org:18080/forecast/shanghai_sea_env/80';var requestWGQ = 'http://wx.dhybzx.org:18080/forecast/shanghai_sea_env/81';var requestHS = 'http://wx.dhybzx.org:18080/forecast/shanghai_sea_env/82';var requestLCG = 'http://wx.dhybzx.org:18080/forecast/shanghai_sea_env/83';var requestJHG = 'http://wx.dhybzx.org:18080/forecast/shanghai_sea_env/84';var requestJSW = 'http://wx.dhybzx.org:18080/forecast/shanghai_sea_env/85';var urls = [requestBZ, requestWGQ,requestHS,requestLCG,requestJHG,requestJSW]Promise.all(urls.map(url =>fetch(url).then(resp => resp.json()))).then(results => {var tempJson = {"堡鎮":results[0][0],"外高橋":results[1][0],"橫沙":results[2][0],"蘆潮港":results[3][0],"金匯港":results[4][0],"金山衛":results[5][0]}addGeoJsonToMap("./data/six_point.json",tempJson)});}else{//geojsonlayer has been initialaddGeoJsonToMap("./data/six_point.json")}})
這里的Promise.all采用了ES2015的箭頭函數,兼容性問題需要自己考慮,也可以手動改成ES5支持的。將額外的attributes組裝成tempJson后傳入至初始化方法addGeoJsonToMap內。
4.2 重構geojsonlayer的infoTemplate的content:
在geojsonlayer.js內繼續做一部分修改,注釋掉實例化template中的fieldInfos屬性及值,并且為geojsonlayer的infoTemplate設置新的content,代碼如下:
var template = new PopupTemplate({title: "{name}",// fieldInfos: [// { fieldName: "Id", label: "Id", visible: true },// { fieldName: "publishdate", label: "觀測日期", visible: true },// { fieldName: "waveheight", label: "浪高", visible: true },// { fieldName: "wavedirection", label: "浪向", visible: true },// { fieldName: "windspeed", label: "風速", visible: true },// { fieldName: "winddirection", label: "風向", visible: true },// { fieldName: "comfort", label: "等級", visible: true }// ], extended: { actions: [{ text: " IconText", className: "iconText", title: "Custom action with an icon and Text", click: function (feature) { alert("Icon Text clicked on " + "id: " + feature.attributes.id + " " + feature.attributes.name); } },{ text: "", className: "iconOnly", title: "Custom action only using an icon", click: function (feature) { alert("Icon action clicked on " + "id: " + feature.attributes.id + " " + feature.attributes.name); } } ],//uses a pretty bad custom theme defined in PopupExtended.css.scaleSelected: 1.6}});//create a extend for basemapvar extendedPopup = new PopupExtended({extended: {themeClass: "light",draggable: true,defaultWidth: 250,actions: [{text: "其他", className: "defaultAction", title: "Default action added in extendedPopup properties.",click: function (feature) { alert("clicked feature - " + feature.attributes); }}],hideOnOffClick: false,multiple: true,},highlight: false,//titleInBody: false,}, dojo.create("div"));//set the map to use the exteneded popup extendedPopup.setMap(options.baseMap);options.baseMap.infoWindow = extendedPopup;this.setInfoTemplate(options.infoTemplate || template);this.infoTemplate.setContent("<b class='popupTitle'>${name}</b>" +"<div class='hzLine'></div>"+"<div class='popupContent'>"+"<i class='glyphicon glyphicon-calendar'></i><b>日期: </b> ${publishdate}<br/>"+"<i class='glyphicon glyphicon-resize-vertical'></i><b>浪高: </b> ${waveheight}<br/>" +"<i class='glyphicon glyphicon-random'></i><b>浪向: </b> ${wavedirection}<br/>"+"<i class='glyphicon glyphicon-share'></i><b>風速: </b> ${windspeed}<br/>" +"<i class='glyphicon glyphicon-transfer'></i><b>風向: </b> ${winddirection}<br/>"+"<i class='glyphicon glyphicon-export'></i><b>等級: </b> ${comfort}<br/>"+"</div>");
額外的屬性和新的infoTemplate樣式構造完成,但存在一個問題,即額外的attributes必須要在geojsonlayer繪制好后再進行設置并展示,arcgis提供了layer的layer-add及layer-add-result事件,但是無法監控到graphics是否已經增入至geojsonlayer內,所以必須要再做一些改進,使額外的屬性能夠在graphics繪制完畢后再添加進去。具體方法分為兩步:1)初始化geojsonlayer時,將showAllPopup方法傳入其構造函數內;2)在grahics添加至layer后,調用showAllPopup方法,顯示所有的Popup。前端代碼如下:
//add GeoJSON to baseMap , constuct show&hide popup method and add other attribute to graphicsfunction addGeoJsonToMap(url,otherJson){require(["esri/map","./src/geojsonlayer.js","esri/geometry/Point", "esri/SpatialReference","dojo/on","dojo/dom","dojo/domReady!"],function (Map, GeoJsonLayer, Point, SpatialReference,on, dom) {var hasThisLayer=false;otherJson=otherJson?otherJson:"";hideAllPopup()//judge layer has been initmap.getLayersVisibleAtScale().forEach(function(item){if(item._url==url&&item.dataType=="geojson"){console.log(item)item.show();console.log("dd")showAllPopup(item);hasThisLayer=true;// map.setExtent(item.extent)}else if(item._url!=url&&item.dataType=="geojson"){item.hide();}})if(!hasThisLayer){addGeoJsonLayer(url); }//show all popupsfunction showAllPopup(layer){......}//hide all popupsfunction hideAllPopup(){.......}//add other attribute to grpahics for popupfunction addAttrToGrpahics(item,type){.......}// Add the layerfunction addGeoJsonLayer(url) {// Create the layervar geoJsonLayer = new GeoJsonLayer({baseMap:map,url: url,onLayerLoaded:function(layer){showAllPopup(layer);} });// Add to mapgeoJsonLayer.dataType="geojson"; map.addLayer(geoJsonLayer);}});}
并且在geojsonlayer.js的constructor內加入:
this._onLayerLoaded = options.onLayerLoaded;
在最后的_addGraphics方法中onLoad方法后,加入:
if (this._onLayerLoaded) this._onLayerLoaded(this);
利用show/hide方法,控制popup顯示及隱藏。
//open all popup layer.graphics.forEach(function(item){if(firstPointInit&&otherJson[item.attributes.name]){addAttrToGrpahics(item,layer.graphics[0].geometry.type)}var loc = map.toScreen(item.geometry);map.infoWindow.setFeatures([item]);map.infoWindow.show(loc); }) //hide all popup var tempLength = map.infoWindow.openPopups.length; for(var i=0;i<tempLength;i++){map.infoWindow.openPopups[0].hide() }
5. 結論
至此,本文已經完成了在Arcgis for javascript API中實現Geojson的繪制,并同時展示其多個Popup的需求。最終的展示效果如圖1、2。源代碼見筆者的github:https://github.com/EasonXu818/Add-GeoJSON-Multiple-Popups。
?