• Arcgis


    利用Arcgis for javascript API绘制GeoJSON并同时弹出多个Popup

    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 popup
                if (options.infoTemplate !== false) {
                    this.setInfoTemplate(options.infoTemplate || new InfoTemplate("GeoJSON Data", "${*}"));
                }

      很明显,geojsonlayer初始化时通过options传入参数进行判断并构造,所以实现本文目的的大致思路是将这里的setInfoTemplate替换成可以扩展的PopupExtended:

    复制代码
    if (options.infoTemplate !== false) {
                    //① create a PopupTemplate
                    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 basemap
                    var 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 geojsonlayer
                if(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 initial
                     addGeoJsonToMap("./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 basemap
                    var 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 graphics
            function 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 init
                       map.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 popups
                       function showAllPopup(layer){
                          ......
                       }
    
                       //hide all popups
                       function hideAllPopup(){
                         .......
                       }
    
                       //add other attribute to grpahics for popup
                       function addAttrToGrpahics(item,type){
                         .......
                       }
                    
    
                    // Add the layer
                    function addGeoJsonLayer(url) {
                        // Create the layer
                        var geoJsonLayer = new GeoJsonLayer({
                            baseMap:map,
                            url: url,
                            onLayerLoaded:function(layer){
                                showAllPopup(layer);
                            }              
                        });
    
                        // Add to map
                        geoJsonLayer.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

  • 相关阅读:
    jquery-ui.min.js:5 Uncaught TypeError: b.nodeName.toLowerCase is not a function
    HTTP::Request
    LWP::UserAgent
    perl json模块
    perl 处理perl返回的json
    perl中 wx返回的json需要encode_utf8($d);
    perl malformed JSON string, neither tag, array, object, number, string or atom, at character offset
    encode_json 会对给定的Perl的数据结构转换为一个UTF-8 encoded, binary string.
    为什么出现Wide character in print at a14.pl line 41
    perl encode_json 会产生 UTF-8 (binary) string decode_json 需要一个 UTF-8 (binary) string
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6621815.html
Copyright © 2020-2023  润新知