专栏首页ArcGIS JS API开发ArcGIS JS API 4.14实现地图加载图片

ArcGIS JS API 4.14实现地图加载图片

主要介绍如何用ArcGIS JS API 4.14实现在二维地图中添加图片的操作。

需求描述

将一张图片叠加到ArcGIS地图上是现在很多项目的一个广泛需求,通过查阅网上资料后发现这种需求目前只有四种方法可以实现,因为ArcGIS JS API官网并没有提供相应的图片类图层来让我们实例化图片图层,但是JS API官网有说明后期会增加这类API,就是不知道是什么时间了。既然目前没有这类API图层,那我们就自己来找找其他的路子吧,刚才提到过实现将图片叠加到地图上的方法目前有四种,分别如下:

  • 通过ArcGIS知乎上有人提到过的使用Graphic来实现,其实就是将图片当做一个Symbol;
  • 通过类似于ArcGIS JS API 3.X中的MapImage模块来实现;
  • 通过扩展MapImageLayer来实现;
  • 通过JS API官网上的BaseDynamicLayer这个类来实现

接下来我们就看看各种方法到底能不能做,或者说可以做成什么效果。具体操作步骤开始介绍之前,我们先给大家透露一下的是,本文是通过第四种方法来实现的,即通过BaseDynamicLayer这个类来实现,最终效果如下:

各种方案实现分析及具体操作步骤

通过Graphic的形式实现

这种方式是我在网上找到的第一种实现方式,也是最简单的一种,它的原理其实就是将图片作为一个Symbol符号,因为ArcGIS JS API的符号类已经支持图片符号了,所以将图片作为一个符号,然后添加到实例化Graphic图层的构造函数中,最终将实例化后的Graphic图层添加到地图上,具体代码如下所示:

      require(["esri/Map", 
            "esri/views/MapView",
            "esri/Graphic"
        ], function(Map, MapView, Graphic) {
        var map = new Map({
          basemap: "satellite"
        });
        var view = new MapView({
          container: "viewDiv", 
          map: map, 
          zoom: 4, 
          center: [15, 65] 
        });
        var polyline = {
            type: "polyline", 
            paths: [
                [91.0761406150, 29.5803130630]
            ]
        };
        var polylineAtt = {
            Name: "Keystone Pipeline",
            Owner: "TransCanada"
        };
        var pictureSymbol = {
            type: "picture-marker",  
            url: "./test.jpg",
            width: "240px",
            height: "240px",
        };
        var polylineGraphic = new Graphic({
            geometry: polyline,
            symbol: pictureSymbol,
            attributes: polylineAtt
        });
        view.graphics.add(polylineGraphic);
      });

通过以上代码实现的最终效果如下:

由以上效果可看出,这并不是我们想要的结果,虽然通过此方法我们将图片添加到了地图上,但是随着地图的缩放,图片并不会缩放,它还是保持着原来的尺寸大小。虽然可以通过监听view视图层的缩放事件来动态的调整图片的大小,但是这种做法觉得有点蠢,并且后期效果可能并不怎么样。所以这种方法被淘汰,目前暂不考虑。

通过类似于ArcGIS JS API 3.X中的MapImage模块来实现

在ArcGIS JS API 3.X和ArcGIS JS API 4.X中都有MapImage模块,在3.X版本中可以通过这个模块来实例化一个图片信息类,然后再将这个图片信息类通过MapImageLayer的addImage方法添加到MapImageLayer图层中,最后将MapImageLayer图层添加到地图上,这就完成了图片和地图的叠加,代码如下所示:

      require(["esri/Map", 
            "esri/views/MapView",
            "esri/layers/MapImageLayer",
            "esri/layers/support/MapImage"
        ], function(Map, MapView, MapImageLayer, MapImage) {
        var map = new Map({
          basemap: "satellite"
        });
        var view = new MapView({
          container: "viewDiv", 
          map: map, 
          zoom: 4, 
          center: [15, 65] 
        });
        //实例化图层
        var mapimageLayer = new MapImageLayer({
            id: 'imageLayer_xuqw'
        });
        map.add(mapimageLayer);
        var image = new MapImage({  
            'extent': 
                { 
                    'xmin': -8864908, 
                    'ymin': 3885443, 
                    'xmax': -8762763, 
                    'ymax': 3976997, 
                    'spatialReference': 
                        { 
                            'wkid': 3857 
                        }
                },  
            'href': './test.jpg'  
        });
        mapimageLayer.addImage(image); 
      });

以上代码看起来似乎是那么完美,我们只需要在MapImage类实例化的时候传入图片的范围信息和url地址就可以,但是偏偏就这么巧,ArcGIS JS API 4.X版本的MapImageLayer并没有addImage()这个方法,所以我们只能放弃这种方法。但是不死心的同学可能还要会问,既然没有addImage()这个方法,那为什么官网还要将MapImage这个类写出来呢,既然用不了的话干脆不对外公布不就行了吗,这个问题其实在GeoNet上也有官方回复了,说是因为编写失误……

通过扩展MapImageLayer来实现

这种思路其实蛮好的,主要是看到了徐磊大佬的文章收到了启发,文章地址如下:

https://www.jianshu.com/p/cc744f1ad6bb?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-timeline&from=timeline

感兴趣的同学可以去这篇文章里好好学习下,文章最后也是放出来了github地址和源码,写的很详细,最后我也是根据扩展的图层类测试成功了,效果如下:

以上截图中,叠加到地图上的图片是会跟随着地图的缩放进行相应的大小调整及绘制,效果还挺不错,但是考虑到目前项目中仅仅这样一个小小的需求就去做扩展图层的操作,未免有点动作幅度太大,太花心思和费精力了,感觉不值,其实就是自己懒啦,哈哈哈,所以我还想再找找其它的方法,这不,就找到了第四种方法。

通过JS API官网上的BaseDynamicLayer类来实现

在不懈的努力寻找下,终于找到了BaseDynamicLayer这个类,这个类允许我们自定义扩展图层,所以我们就可以通过这个类简单的扩展一下图片叠加的图层,来实现地图上图片的叠加。其实所用原理就是通过canvas绘制技术,获取到图片的范围后将它的范围坐标信息转换为屏幕坐标,再实例化canvas句柄来绘制图片。 扩展出的叠加图片的自定义类代码如下:

//自定义叠加图片图层
var CustomImageOverlayLayer = BaseDynamicLayer.createSubclass({
    properties: {
        picUrl: null,
        extent: null,
        image: null,
        canvas: null,
    },
 
    // Override the getImageUrl() method to generate URL
    // to an image for a given extent, width, and height.
    getImageUrl: function (extent, width, height) {
        //新Image对象,可以理解为DOM 
        if (!this.image) {
            this.image = new Image();
        }
        this.image.src = this.picUrl;
 
        // 创建canvas DOM元素,并设置其宽高和图片一样   
        if (!this.canvas) {
            this.canvas = canvas = document.createElement("canvas");
        }
        this.canvas.width = 2000;
        this.canvas.height = 2000;
 
        //左上角地理坐标转换屏幕坐标,为了获取canvas绘制图片的起点
        var mapPoint = {
            x: this.extent.xmin,
            y: this.extent.ymax,
            spatialReference: {
                wkid: 4326
            }
        };
        var screenPoint = view.toScreen(mapPoint);
        //根据extent范围计算canvas绘制图片的宽度以及高度
        //左下角
        var leftbottom = {
            x: this.extent.xmin,
            y: this.extent.ymin,
            spatialReference: {
                wkid: 4326
            }
        };
        var screen_leftbottom = view.toScreen(leftbottom);
        //右上角
        var righttop = {
            x: this.extent.xmax,
            y: this.extent.ymax,
            spatialReference: {
                wkid: 4326
            }
        };
        var screen_righttop = view.toScreen(righttop);
        this.canvas.getContext("2d").drawImage(this.image, screenPoint.x, screenPoint.y, Math.abs(screen_righttop.x - screen_leftbottom.x), Math.abs(screen_righttop.y - screen_leftbottom.y));
        return this.canvas.toDataURL("image/png");
 
    }
});

实例化这个类的代码如下:

var ImageOverlayLayer = new CustomImageOverlayLayer({
    picUrl: "1.jpg",
    extent: { 
                        xmin: 91.0761406150, 
                        ymin: 29.5803130630, 
                        xmax: 92.0761406150, 
                        ymax: 30.5803130630 
                    }
});
map.add(ImageOverlayLayer);

最终得到的图片叠加效果如下:

通过这种方法得到的效果是我们所需要的,操作实现简单,并且最终叠加到地图上的图片也会随着地图缩放进行大小调整,所以本文最后采用这种方式来实现。

附:

第四种方法的全部源码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>地图叠加图片</title>
    <link rel="stylesheet" href="http://localhost/4.14/esri/themes/light/main.css" />
    <style>
        html,
        body,
        #viewDiv {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
        }
    </style>
    <script src="http://localhost/4.14/init.js"></script>
</head>
 
<body>
    <div id="viewDiv"></div>
    <script>
        require([
            "esri/Map",
            "esri/views/MapView",
            "esri/layers/BaseDynamicLayer"
        ], function (Map, MapView, BaseDynamicLayer) {
 
            //自定义叠加图片图层
            var CustomImageOverlayLayer = BaseDynamicLayer.createSubclass({
                properties: {
                    picUrl: null,
                    extent: null,
                    image: null,
                    canvas: null,
                },
 
                // Override the getImageUrl() method to generate URL
                // to an image for a given extent, width, and height.
                getImageUrl: function (extent, width, height) {
                    //新Image对象,可以理解为DOM 
                    if (!this.image) {
                        this.image = new Image();
                    }
                    this.image.src = this.picUrl;
 
                    // 创建canvas DOM元素,并设置其宽高和图片一样   
                    if (!this.canvas) {
                        this.canvas = canvas = document.createElement("canvas");
                    }
                    this.canvas.width = 2000;
                    this.canvas.height = 2000;
 
                    //左上角地理坐标转换屏幕坐标,为了获取canvas绘制图片的起点
                    var mapPoint = {
                        x: this.extent.xmin,
                        y: this.extent.ymax,
                        spatialReference: {
                            wkid: 4326
                        }
                    };
                    var screenPoint = view.toScreen(mapPoint);
                    //根据extent范围计算canvas绘制图片的宽度以及高度
                    //左下角
                    var leftbottom = {
                        x: this.extent.xmin,
                        y: this.extent.ymin,
                        spatialReference: {
                            wkid: 4326
                        }
                    };
                    var screen_leftbottom = view.toScreen(leftbottom);
                    //右上角
                    var righttop = {
                        x: this.extent.xmax,
                        y: this.extent.ymax,
                        spatialReference: {
                            wkid: 4326
                        }
                    };
                    var screen_righttop = view.toScreen(righttop);
                    this.canvas.getContext("2d").drawImage(this.image, screenPoint.x, screenPoint.y, Math.abs(screen_righttop.x - screen_leftbottom.x), Math.abs(screen_righttop.y - screen_leftbottom.y));
                    return this.canvas.toDataURL("image/png");
 
                }
            });
 
            var map = new Map({
                basemap: 'osm',
            });
 
            var view = new MapView({
                container: "viewDiv",
                map: map,
                center: [107.246152,34.414465],
                zoom: 7
            });
 
            view.when(function () {
                var ImageOverlayLayer = new CustomImageOverlayLayer({
                    picUrl: "1.jpg",
                    extent: { 
                        xmin: 91.0761406150, 
                        ymin: 29.5803130630, 
                        xmax: 92.0761406150, 
                        ymax: 30.5803130630 
                    }
                });
                map.add(ImageOverlayLayer);
                //地图移动刷新,防止地图初始化时候,图片叠加图层加载刷新不过来
                setTimeout(function () {
                    var center = view.center.clone();
                    center.x -= 0.001;//底图是经纬度
                    view.center = center;
                    view.goTo(view.center,
                        {
                            speedFactor: 0.1,
                            easing: "linear" //linear, in-cubic, out-cubic, in-out-cubic, in-expo, out-expo, in-out-expo
                        });
                }, 500);
            }, function (error) {
                console.log("图片叠加失败: ", error);
            });
 
 
        });
    </script>
</body>
 
</html>
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:http://www.xbeichenbei.com/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • ArcGIS JS API 4.15实现地图加载图片(优化版)

    主要介绍如何用ArcGIS JS API 4.15实现在二维地图中添加图片的操作。上一篇文章已经介绍了如何添加图片的四种方法,但是添加到地图上的图片在拖动时有些...

    X北辰北
  • ArcGIS JS API 加载 TMS 地图瓦片

    TMS(Tile Map Service) 是 OSGeo (开源地理基金会) 提出的一种地图瓦片服务。额外补充一句,WMTS、WMS、WFS这些是 OGC(开...

    前端小tips
  • ArcGIS JS API 4.14离线部署

    本文主要介绍ArcGIS JS API 4.14的离线部署和测试离线部署是否成功,JS API离线部署是ArcGIS JS API开发的首要前提,也是基本技能,...

    X北辰北
  • Arcgis for js加载天地图

    天地图的切片地图可以通过esri.layers.TiledMapServiceLayer来加载,在此将之进行了一定的封装,如下:

    lzugis
  • 【番外】 Vue中使用ArcGIS JS API 4.14开发

    本文主要介绍如何在Vue项目中使用ArcGIS JS API进行开发,文中使用的JS API是目前最新版本的ArcGIS JS API 4.14,主要是在Vue...

    X北辰北
  • Arcgis for js加载百度地图

    在前面的文章里提到了Arcgis for js加载天地图,在本节,继续讲解如何在Arcgis for js中加载百度地图。

    lzugis
  • 【番外】 React中使用ArcGIS JS API 4.14开发

    本文主要介绍如何在React项目中使用ArcGIS JS API进行开发,文中使用的JS API是目前最新版本的ArcGIS JS API 4.14,主要是在R...

    X北辰北
  • ArcGIS REST API 实现服务端地图图层叠加和地图图片导出

    (1)ArcGIS Desktop 10.5 路径下自带的 影像文件 wsiearth.tif,影像文件存放路径: E:\Program Files (x86...

    魏晓蕾
  • 实例化二维地图

    主要介绍如何用ArcGIS JS API 4.14去实例化一张二维地图,并简单介绍了和3.X版本实现方式的异同。

    X北辰北
  • ArcGIS JS API 4.16控制地图的缩放大小

    在3.X的ArcGIS JS API版本中我们可以轻松的调用相应的API来实现地图的缩放大小的控制,让实例化后的地图在我们设置的范围中进行缩放,但是在4.X的版...

    X北辰北
  • ArcGIS API for JavaScript 4.18基于ES Modules的新开发方式@arcgis/core

    ArcGIS API for JavaScript 4.18中新增加了一种基于ES Modules的新开发方式@arcgis/core,这篇文章就来介绍一下如何...

    X北辰北
  • 【ArcGIS JS API + eCharts系列】实现地图上二维图表的绘制

    本文主要介绍使用ArcGIS JS API 4.14和eCharts 4.7.0来实现在地图上绘制二维图表中的柱状图的实现步骤。

    X北辰北
  • JS实现图片懒加载

    由于网页中占用资源较多的一般是图片,所以我们一般实施懒加载都是对图片资源而言的,所以这里的实现原理主要是针对图片。

    愤怒的小鸟
  • 【番外】 使用@arcgis/cli脚手架进行ArcGIS JS API开发

    本文主要介绍如何在Vue和React项目中使用ArcGIS JS API进行开发,与以往的esri-loader开发方式不同的是,本文使用的是@arcgis/c...

    X北辰北
  • 原生JS实现图片懒加载

  • ArcGIS JS API 4.15实现萤火虫效果

    看到网上的萤火虫效果后,也想在前端通过ArcGIS JS API来实现一下,所以感兴趣的话就跟我一起来看看吧。

    X北辰北
  • Html图片懒加载动画,js实现图片懒加载效果

    window.onload = window.onscroll = function(){

    全栈程序员站长

扫码关注腾讯云开发者

领取腾讯云代金券