esri-leaflet入门教程(5)- 动态要素加载

by 李远祥

在上一章节中已经说明了esr-leaflet是如何加载ArcGIS Server提供的各种服务,这些都是服务本身来决定的,API脚本只是非常简单的调用。但如果要做一列的地图交互操作或者动态渲染等,那就必须使用地图区域跳转、查询结果渲染、动态添加图形等多种交互手段。而这些交互手段基本上离不开一些非服务类型的数据加载,我们可以将其成为动态要素。动态要素一般是在页面端进行动态绘制的。

动态要素这一说法并不是ArcGIS 或者leaflet的说法,而是笔者想了很久之后才编出来的一个名词,为的就是要使ArcGIS JavaScript API 体系与esri-leaflet能找到一个比较好的对应关系,便于ArcGIS的开发人员能够快速的切换过来。esri-leaflet 本身就是基于leaflet去做的扩展,因此,很多情况下都不能摆脱leaflet的限制,文雅点来说就是必须遵循leaflet的定义的接口规范。对于多年ArcGIS开发人员来说,刚开始的时候是有点不习惯的,因为很多时候某些功能和接口不能很好的映射回ArcGIS JavaScript API中。所以,要搞清楚esri-leaflet的使用,那就必须从leaflet本身入手。

在传统的ArcGIS JavaScript API中,要加载一些动态的要素(非直接引用服务的数据),必须使用graphic或者是graphicLayer(其实也是graphic的数组)。graphic在ArcGIS JS里面是由四个部分组成的,分别是geometry(图形)、symbol(符号)、attributes(属性)、infoTemplate(弹窗),如下图

虽然是四个参数,但并不一定要全部使用才能构建,一般来说最基本是需要一个geometry参数就行了,系统会自动给与graphic一个默认的symbol,这样构成一个最简单的graphic,就可以加载到地图上去了。总的来看,ArcGIS的API中是遵循ArcGIS数据的理念,图元(暂且这么说吧,真不知道中文怎么区分graphic和geometry)的显示是读取了要素的图形和属性,并且可以修改其符号(symbol),在地图交互时还可以绑定一些特定的弹出信息。

但在esri-leaflet中,从它提供的基础类来看,根本没有graphic和symbol,就连geometry都没有!这很让人抓狂。为了搞清楚这个关键问题,笔者特意去翻一遍leaflet的接口。终于在leaflet的基础类型中找到了关于图形的一系列接口 http://leafletjs.com/reference-1.0.3.html#layer,其分组也是相当的奇怪,名叫 Vector Layers ,乍一看还以为是一种矢量地图服务,如下图

这个里面就包含了所有的图形定义格式,传统的线、面、圆等都有了,但没有点,因为点是单独的在UI Layers 里面的Marker 。当然了没有arcgis定义的多,但基本也能满足了。再来看其定义是怎样的,点击polyline去查看其构建例子:

// create a red polyline from an array of LatLng points
var latlngs = [
    [45.51, -122.68],
    [37.77, -122.43],
    [34.04, -118.2]
];
var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
// zoom the map to the polyline
map.fitBounds(polyline.getBounds());

通过上述代码可以发现几个问题,polyline的构建方式是需要传入点的数组,在构建的时候在其option参数中可以设置其样式,例子中就是使用了color参数,最后图形是可以直接作为Layer加载到地图中,可以与这个图形交互,通过fitBounds 方法跳转到polyline的图形位置。

也有人奇怪,点进polyline和polygon等的这些接口,居然没看到有类似的options可以实现color或者是宽度等设置。那是因为这些接口都在Path中实现,其他的如polyline、polygon、circle等都是实现了path的接口。可以查看下Path的options,如下图

可以看到传统的颜色、线宽、透明度等都可以在options里面设置。

从上面的资料基本上可以看到leaflet与ArcGIS的对应关系了。leaflet中的没有所谓的单独的图元一说,在动态要素来说,全部都是属于Layer,都实现了Layer的接口,其相当于ArcGIS体系中将graphicLayer和graphic 融合在一起,leaflet所谓的图元就是ArcGIS的 graphicLayer+graphics。这感觉还是有点乱,但基本上可以了解leaflet和ArcGIS JS体系的差异。

那么问题来了,由于esri-leaflet 中是没有实现类似ArcGIS JavaScript API中的graphic、geometry、symbol等接口,所有的操作都必须在leaflet的基础体系下完成,那这个代码该怎么编写?

我们可以通过查看esri-leaflet的例子,看看它是如何实现前端加载的。笔者查看的例子在 http://esri.github.io/esri-leaflet/examples/styling-feature-layer-polylines.html 可以看看其截图效果,对道路进行了动态的渲染。

再来看关键代码实现部分

  var map = L.map('map').setView([45.5275, -122.6717], 14);
  L.esri.basemapLayer('Streets').addTo(map);
  var bikePaths = L.esri.featureLayer({
    url: 'https://services.arcgis.com/uCXeTVveQzP4IIcx/ArcGIS/rest/services/Bike_Routes/FeatureServer/0',
    style: function (feature) {
      var c,o = 0.75;
      switch (feature.properties.BIKEMODE) {
        case 'Low traffic through street':
          c = '#007D7D';
          break;
        case 'Bike boulevard':
          c = '#00FF3C';
          break;
        case 'Caution area':
          c = '#FF0000';
          break;
        case 'Local multi-use path':
          c = '#00BEFF';
          break;
        case 'Regional multi-use path':
          c = '#b1a9d0';
          break;
        case 'Moderate traffic through street':
          c = '#FFEB00';
          break;
        case 'Planned multi-use path':
          c = '#000000';
          break;
        case 'Bike lane':
          c = '#328000';
          o = '0.70';
          break;
        case 'High traffic through street':
          c = '#FFA500';
          break;
        case 'Planned bike lane':
          c = '#000000';
          o = '1.0';
          break;
        default:
          c = '#C0C0C0';
      }
      return {color: c, opacity: o, weight: 5};
    }
  }).addTo(map);

可以关键部分是获取了ArcGIS 的Featureservice 的要素,针对要素进行了style的设置,做了一个分等级和颜色的渲染。关键部分是使用了L.esri.featureLayer 接口。从这里可以看到端倪,对于动态的要素的加载,esri也是遵循了leaflet的规则,特意搞什么graphiclayer之类的特殊图层,而是想leaflet一样,直接在Layer中实现,当然这个Layer就是自家的featurelayer了。那传统的graphic前端绘制怎么办?esri-leaflet没有给出答案,笔者看到所有的基于后台服务查询的结果最终都是以featurelayer的形式展示出来,esri-leaflet中根本就没有在客户端绘制图形的接口。其实这部分答案很明确,就是使用leaflet本身的Vector Layers ,开发人员可以直接将其等同于graphiclayer就行了。

为了搞清楚真相,笔者又专门查找了 L.esri.featureLayer 的相关说明,发现其options 的属性里面的style一项居然是实现了leaflet本身的ILayer接口,如下图所示

前面我们看到的Vector Layers 部分其实也是实现了ILayer,也就是说,不管是采用什么样的方式去做动态要素的加载和交互操作,最终还是会落到ILayer和Vector Layers 中去,从ArcGIS的实现方法来看,可见一斑了。

原理清楚之后,所以的问题都可以迎刃而解了,关于代码实现部分就变得异常的简单了。接下来就可以用自己的数据按照例子进行制作一遍。例如笔者现在要按照道路的等级对道路进行实际的渲染,分别采用不同的颜色和粗细来渲染。代码很简单,跟例子差不多,不过就是替换成自己的数据。

function addFeatures() {
 var featlayer =  L.esri.featureLayer({
   url: 'http://localhost:6080/arcgis/rest/services/dongguan/FeatureServer/2',
   style: function(feature) {
    var c, w, o = 0.75;
    switch(feature.properties.type) {
     case '44000':
      c = '#007D7D';
      w = 3;
      break;
     case '45000':
      c = '#00FF3C';
      w = 2;
      break;
     case '51000':
      c = '#FFA500';
      w = 3;
      break;
     default:
      c = '#C0C0C0';
      w = 1;
    }
    return {
     color: c,
     opacity: o
    };
   }
  });

  map.addLayer(featlayer);
 }

这里要注意几点,首先要留意的是esri-leaflet是如何获取要素的属性的,代码中可以看到参数中使用的是 feature.properties.type  其中feature.properties是获取属性的方式,后面加点,接着是字段名(笔者的服务里面使用的是type字段分类)。可以通过arcgis server 的服务路径查看服务的字段情况,例如 http://localhost:6080/arcgis/rest/services/dongguan/FeatureServer/2 道路的要素图层信息如下图所示:

这里还要注意一点就是,arcgis server发布的服务,无论原始数据的字段是否大小写,都会一律转为小写字母,所以要特别注意。还有数据中千万别使用中文作为字段名称,这个大家懂的^_^ 。那么可以看看接下来的效果了,如下图所示

接下来就是自定义的图形加载地图上了。前面已经提及过,这类型的加载方式必须使用leaflet的方式进行。例如我们可以通过以下代码去加载一个面图形,并将其加到地图上面。

function addPolygon(){
 var latlngs = [[23.17, 113.45],[23.15, 113.46],[23.19, 113.46],[23.19, 113.45]];
 var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
}

其实际效果如下:

这里值得注意的有几点,一是面图形的构建是一个二维数组,数组元素记录的是xy坐标,但不是[经度,纬度],而是恰好反过来,[纬度,经度]。另外,如果要清除掉动态绘制的图形,那就必须使用ILayer的接口,就是在构建面的时候,其实声明的就是一个Layer,可以利用Layer的remove方法进行清除,在该代码实例中要清除前端的图形,那就是使用 polygon.remove();

由于leaflet对点的绘制做了特别的关照,将它放在UI Layer 而非Vector Layers  中,属于非常个性的部分,如果要加载点的数据,也是可以使用leaflet的方法来构建。但是arcgis server 中的点要素服务,如果要直接动态加载,那L.esri.featureLayer 接口中也有特殊的做法。例如如下核心代码

L.esri.basemapLayer('Streets').addTo(map);
  L.esri.featureLayer({
    url: 'https://services.arcgis.com/rOo16HdIMeOBI4Mb/arcgis/rest/services/Trimet_Transit_Stops/FeatureServer/0',
    pointToLayer: function (geojson, latlng) {
      return L.marker(latlng, {
        icon: icons[geojson.properties.direction.toLowerCase()]
      });
    },
  }).addTo(map);

可以看到,在featurelayer中使用了一个方法是pointToLayer ,强行将esri格式转为L.marker 。说到底还是使用了L.marker接口,只不过是再封装了一层转换而已。具体的代码可在 http://esri.github.io/esri-leaflet/examples/styling-feature-layer-points.html  进行查看。

上面的很多代码都是基于arcgis 的featureservice 是制作的,但也有人会说,featureservice的发布需要后台有ArcSDE支撑,如果没有ArcSDE的数据源,那岂不是连基本的动态要素绘制都不能做?其实不然,这个问题Esri的工程师应该早就想到。看接口名称叫 L.esri.featureLayer  ,不代表它只能支持featureservice,在上述的动态要素加载的代码中,将featureservice改为使用mapservice,同样可以使可运行的,这个只是命名的名称让大家产生误会而已。笔者也特意尝试了一把,将原来的路径替换为 http://localhost:6080/arcgis/rest/services/dongguan/MapServer/2  ,还是能出来原来的结果,因此无需虚惊一场。

总结:总的来说,esri-leaflet在动态要素加载方面还是坚持的不错,尽管大部分的操作都是使用leaflet原有的接口实现。对于从来没有过ArcGIS开发经验的人来说,毫无历史负担,即学即用。对于ArcGIS老鸟来说,还是需要一些时间去琢磨,毕竟一些使用习惯和叫法发生了一定改变。

esri-leaflet入门教程(5)- 动态要素加载的更多相关文章

  1. esri-leaflet入门教程(4)-加载各类图层

    esri-leaflet入门教程(4)-加载各类图层 by 李远祥 在leaflet中图层一般分为底图(Basemap)和叠加图层(Overlay).前面章节已经介绍过底图其实也是实现了TileLay ...

  2. Entity Framework入门教程(8)---预先加载、延迟加载、显示加载

    1.预先加载 预先加载:在对一种类型的实体进行查询时,将相关的实体作为查询的一部分一起加载.预先加载可以使用Include()方法实现. 1.加载一个相关实体类型 栗子:使用Include()方法从数 ...

  3. 【PHP面向对象(OOP)编程入门教程】23.自动加载类 __autoload()函数

    很多开发者写面向对象的应用程序时,对每个类的定义建立一个 PHP 源文件.一个很大的烦恼是不得不在每个脚本(每个类一个文件)开头写一个长长的包含文件的列表. 在软件开发的系统中,不可能把所有的类都写在 ...

  4. Canvas制作动态进度加载水球

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Java_动态重新加载Class总结

    在此记载Java动态重新加载Class的点点滴滴,实现之前也在网上看了很多文章,但发现不是很清晰,后来发现总结,看源码实现还是最靠谱. 直接上代码: package com.lkb.autoCode. ...

  6. Java_动态重新加载Class机制

    Java动态重新加载Class 项目中使用到了动态重新加载Class的机制,作用是让一些代码上线之前可以在线上环境测试一下,当然,这是非常不好的测试机制,我刚来的时候也为这种机制感到惊讶—怎么可以在线 ...

  7. Echarts通过Ajax实现动态数据加载

    Echarts(3.x版)官网实例的数据都是静态的,实际使用中往往会要求从服务器端取数据进行动态显示,官网教程里给出的异步数据加载很粗略,下面就以官网最简单的实例为例子,详细演示如下过程:1.客户端通 ...

  8. Android系统下的动态Dex加载

    1 问题在Android系统中,一个App的所有代码都在一个Dex文件里面.Dex是一个类似Jar的存储了多有Java编译字节码的归档文件.因为Android系统使用Dalvik虚拟机,所以需要把使用 ...

  9. Android系统下的动态Dex加载与app速度优化

    1 问题 在Android系统中,一个App的所有代码都在一个Dex文件里面.Dex是一个类似Jar的存储了多有Java编译字节码的归档文件.因为Android系统使用Dalvik虚拟机,所以需要把 ...

随机推荐

  1. 【转】图片缓存之内存缓存技术LruCache、软引用 比较

    每当碰到一些大图片的时候,我们如果不对图片进行处理就会报OOM异常,这个问题曾经让我觉得很烦恼,后来终于得到了解决,那么现在就让我和大家一起分享一下吧.这篇博文要讲的图片缓存机制,我接触到的有两钟,一 ...

  2. UVa 11172 - Relational Operator

    题目大意:给两个数,比较大小... #include <cstdio> int main() { int T; scanf("%d", &T); int a, ...

  3. UVa 11456 - Trainsorting

    题目大意:给一个车辆到达车站的序列(按时间先后),可以对车辆进行以下处理:插在队首.插在队尾或者拒绝进站.车站内的车辆必须按照重量大小从大到小排列,问车站内最多能有多少辆车辆? 假设车i是第一个进站, ...

  4. 基于Python,scrapy,redis的分布式爬虫实现框架

    原文  http://www.xgezhang.com/python_scrapy_redis_crawler.html 爬虫技术,无论是在学术领域,还是在工程领域,都扮演者非常重要的角色.相比于其他 ...

  5. 基于arm开发板四个按键控制四个灯亮

    基于s5pv2410,cortex a8的四个按键每一个按键点了对应的灯 对于用汇编来编程的话不难,重点在于数据手册,电路图,管脚的看懂 直接上代码 .globl _start_start: ldr ...

  6. 基于回调的事件处理——重写onTouchEvent方法响应触摸屏事件

    对于Android提供的事件处理模型,不难发现基于监听的事件处理模型具有更大的优势: 基于监听的事件模型分工更加明确,事件源.事件监听有两个类分开实现,因此具有更好的维护性. Android的事件处理 ...

  7. abstract、override、new、virtual、sealed使用和示例

    abstract修饰类名为抽象类,修饰方法为抽象方法.如果一个类为抽象类,则这个类智能是其他某个类的基类.抽象方法在抽象类中没有函数体.抽象类中的抽象方法是没有方法体的,继承其的子类必须实现抽象类的抽 ...

  8. DNS没有生效的几个原因

    1.记录没有正确添加 请确认你的域名记录是否完全正确的添加.线路类型正确,记录类型正确 2.域名还没有生效 这个情况还会有另外一个现象,就是域名有时候可以ping,有时候不能ping. 这是因为你当地 ...

  9. Canvas drawImage API

    drawImage <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  10. JavaWeb知识回顾二

    动态web资源相关 1.tomcat相关 tomcat的目录结构 bin -- tomcat服务器的批处理文件的存放目录 conf -- tomcat服务器配置文件的存放目录 lib -- tomca ...