deck.gl渲染ArcLayer弧线图层

deck.gl渲染ArcLayer弧线图层

效果图

deck.gl渲染ArcLayer弧线图层
鼠标移动点击效果

在线地址

deck.gl渲染ArcLayer弧线图层
https://tool.giserdqy.com/deckgl/arc-layer.html

功能说明

本实例实现了deck.gl加载底图,基于ArcLayer渲染海量弧线。实现了点击要素动态,动态更新渲染数据。可对比上一篇LineLayer https://www.giserdqy.com/#:~:text=deck.gl渲染LineLayer直线图层

数据结构

数据结构

数据结构分为两部分,

  1. FeatureCollection是geojson数据,直接渲染为面图层,
  2. 另一部分是每个Feature附带的属性flows,centroid;centroid代表弧线的起点,flows存储的是多个终点要素的唯一id,可以在FeatureCollection中查询到
{"type":"Feature","properties":{"name":"Los Angeles, CA","flows":{"3":13,"8":-30,"22":-34,"23":259,"26":-336,"30":-20,"47":-17,"48":19,"53":-15,"54":-12,"67":-47,"69":-336,"70":-12,"71":-58,"72":-109,"73":-593,"74":-28,"77":12,"79":-14,"83":-13,"88":-32,"98":-67,"114":-20,"117":70,"119":-11,"123":131,"124":-12,"135":-100,"141":-17,"142":26,"148":-22,"149":17,"152":13,"160":-23,"172":45,"175":24,"176":-22,"178":-13,"182":-15,"186":26,"191":-13,"198":26,"202":12,"204":42,"212":-11,"214":-90,"224":-189,"234":-12,"247":13,"248":21,"253":25,"255":-32,"256":-13,"260":-40,"263":-58,"272":-166,"276":-23,"281":20,"285":153,"289":-39,"299":-17,"306":-76,"313":200,"325":313,"330":84,"333":-31,"334":-94,"336":-11,"338":-19,"340":20,"346":122,"355":-23,"362":-332,"364":-18,"370":-370,"382":-15,"391":-81,"394":-240,"405":-34,"420":-31,"447":-22,"449":14,"457":91,"460":-11,"465":-23,"466":-13,"467":-90,"478":111,"480":-123,"487":-38,"498":-59},"centroid":[-118.34921704225347,33.83014929577467,0]},"geometry":{"type":"MultiPolygon","coordinates":[[[[-118.59397,33.4672],[-118.48479,33.48748],[-118.37033,33.40928],[-118.28627,33.35146],[-118.32525,33.29907],[-118.37477,33.32006],[-118.46537,33.32605],[-118.48261,33.36991],[-118.56344,33.43438],[-118.59397,33.4672]]],[[[-118.59403,33.03595],[-118.54007,32.98093],[-118.44677,32.89542],[-118.3535,32.82196],[-118.42563,32.80059],[-118.4879,32.84458],[-118.5815,32.93166],[-118.64156,33.01712],[-118.59402,33.03594]]],[[[-118.9408,34.07497],[-118.85647,34.12677],[-118.78889,34.16822],[-118.69383,34.16856],[-118.66794,34.19917],[-118.6677,34.2367],[-118.63345,34.26953],[-118.65227,34.3234],[-118.7386,34.49898],[-118.88135,34.79064],[-118.89446,34.81798],[-118.32627,34.81973],[-117.77436,34.8233],[-117.66728,34.82253],[-117.65522,34.39723],[-117.64636,34.28918],[-117.67772,34.16506],[-117.69353,34.12163],[-117.70428,34.09506],[-117.71106,34.07954],[-117.74406,34.01981],[-117.76775,34.01943],[-117.80254,33.97555],[-117.78329,33.94641],[-117.91973,33.94767],[-117.9667,33.94606],[-117.9766,33.90281],[-118.02872,33.86624],[-118.05866,33.84613],[-118.06327,33.82422],[-118.0931,33.78615],[-118.11508,33.7438],[-118.1327,33.75321],[-118.1837,33.73611],[-118.23193,33.71529],[-118.25869,33.70374],[-118.31721,33.71282],[-118.3333,33.72119],[-118.35471,33.73232],[-118.39661,33.73592],[-118.42841,33.77472],[-118.39431,33.80432],[-118.41271,33.88392],[-118.46061,33.96912],[-118.51951,34.02752],[-118.60357,34.03906],[-118.67936,34.03327],[-118.74495,34.03212],[-118.80511,34.00126],[-118.85465,34.03424],[-118.94448,34.04676],[-118.9408,34.07499]]]]}}

代码解析

  1. 此部分是引入在线deck.gl包,d3js包用于csv数据读取格式处理,设置了基本的css样式
<head>
    <title>deck.gl ArcLayer Example</title>

    <script src="https://unpkg.com/deck.gl@^8.8.0/dist.min.js"></script>
    <script src="https://d3js.org/d3.v5.min.js"></script>

    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.13.0/mapbox-gl.js"></script>

    <style type="text/css">
      body {
        width: 100vw;
        height: 100vh;
        margin: 0;
        overflow: hidden;
      }
      .deck-tooltip {
        font-family: Helvetica, Arial, sans-serif;
        padding: 6px !important;
        margin: 8px;
        max-width: 300px;
        font-size: 10px;
      }
    </style>
  </head>
  1. 这部分是es6写法,引入需要的模块DeckGL、GeoJsonLayer、ArcLayer,并初始化DeckGL(GeoJsonLayer已经有过解释可参照deck.gl渲染GeoJsonLayer
    https://www.giserdqy.com/secdev/deckgl/39849/
    const {DeckGL, GeoJsonLayer, ArcLayer} = deck;
    
    const deckgl = new DeckGL({
      mapStyle: 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
      initialViewState: {
        longitude: -100,
        latitude: 40.7,
        zoom: 3,
        maxZoom: 15,
        pitch: 30,
        bearing: 30
      },
      controller: true,
      layers: [],
      getTooltip: ({object}) => object && object.properties.name // 设置地图弹出框数据格式
    });
  </script>
  1. d3js读取数据,读取到数据后转为json,调用renderLayers方法渲染
fetch('https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/arc/counties.json')
    .then(res => res.json())
    .then(data => renderLayers(data))
  1. 渲染数据renderLayers方法封装,详情见注释
function renderLayers(data, selectedFeature) {
            // 没有选中,默认设置选中Los Angeles, CA
      selectedFeature = selectedFeature || data.features.find(f => f.properties.name === 'Los Angeles, CA');
      const arcLayer = getArcLayer(data, selectedFeature);
      const countyLayer = new GeoJsonLayer({ // 把geojson 多面数据直接渲染为GeoJSON图层
        id: 'geojson',
        data,
        stroked: false,
        filled: true,
        autoHighlight: true,
        pickable: true,
        getFillColor: () => [0, 0, 0, 0],
        onClick: info => renderLayers(data, info.object) // 点击面时,捕捉面对象属性,重新渲染该面存储的数据,渲染解释见5
      });

      deckgl.setProps({ layers: [countyLayer, arcLayer] });
    }
  1. 封装初始化ArcLayer方法,具体配置见代码注释
// 定义颜色常量
const inFlowColors = [
      [255, 255, 204],
      [199, 233, 180],
      [127, 205, 187],
      [65, 182, 196],
      [29, 145, 192],
      [34, 94, 168],
      [12, 44, 132]
    ];

    const outFlowColors = [
      [255, 255, 178],
      [254, 217, 118],
      [254, 178, 76],
      [253, 141, 60],
      [252, 78, 42],
      [227, 26, 28],
      [177, 0, 38]
    ];


// data 所有的数据
// selectedFeature 当前点击的面要素

function getArcLayer(data, selectedFeature) {
      const {flows, centroid} = selectedFeature.properties; //读取选中面的中心点和目标位置id
      // 遍历所有的终点id,并返回统一的数据结构
      const arcs = Object.keys(flows).map(toId => {
      const f = data.features[toId]; // 根据id获取到终点的geojson格式数据
      return {// 调整成统一含有起点、终点、权重值结构的数据
        source: centroid,
        target: f.properties.centroid,
        value: flows[toId]
      };
      });
            // 通过d3js方法基于inFlowColors颜色值域排序并赋值gain、quantile属性,主要是为了确定渐变色值
      const scale = d3.scaleQuantile()
        .domain(arcs.map(a => Math.abs(a.value)))
        .range(inFlowColors.map((c, i) => i));

        arcs.forEach(a => {
        a.gain = Math.sign(a.value);
        a.quantile = scale(Math.abs(a.value));
      });
            // 初始化ArcLayer
      return new ArcLayer({
        id: 'arc', // 图层唯一id
        data: arcs,// 绑定数据
        getSourcePosition: d => d.source, // 动态获取起点geojson
        getTargetPosition: d => d.target, // 动态获取终点geojson
        getSourceColor: d => (d.gain > 0 ? inFlowColors : outFlowColors)[d.quantile],// 根据属性值设置起点颜色
        getTargetColor: d => (d.gain > 0 ? outFlowColors : inFlowColors)[d.quantile],// 根据属性值设置终点颜色
        strokeWidth: 4 // 边线颜色
      });
    }

源代码

拷贝到html文件中可直接用浏览器打开

<html>
  <head>
    <title>deck.gl ArcLayer Example</title>

    <script src="https://unpkg.com/deck.gl@^8.8.0/dist.min.js"></script>
    <script src="https://d3js.org/d3.v5.min.js"></script>

    <script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.13.0/mapbox-gl.js"></script>

    <style type="text/css">
      body {
        width: 100vw;
        height: 100vh;
        margin: 0;
        overflow: hidden;
      }
      .deck-tooltip {
        font-family: Helvetica, Arial, sans-serif;
        padding: 6px !important;
        margin: 8px;
        max-width: 300px;
        font-size: 10px;
      }
    </style>
  </head>

  <body>
  </body>

  <script type="text/javascript">
    
    const {DeckGL, GeoJsonLayer, ArcLayer} = deck;
    
    const inFlowColors = [
      [255, 255, 204],
      [199, 233, 180],
      [127, 205, 187],
      [65, 182, 196],
      [29, 145, 192],
      [34, 94, 168],
      [12, 44, 132]
    ];

    const outFlowColors = [
      [255, 255, 178],
      [254, 217, 118],
      [254, 178, 76],
      [253, 141, 60],
      [252, 78, 42],
      [227, 26, 28],
      [177, 0, 38]
    ];
    
    const deckgl = new DeckGL({
      mapStyle: 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
      initialViewState: {
        longitude: -100,
        latitude: 40.7,
        zoom: 3,
        maxZoom: 15,
        pitch: 30,
        bearing: 30
      },
      controller: true,
      layers: [],
      getTooltip: ({object}) => object && object.properties.name
    });
    
    function getArcLayer(data, selectedFeature) {

      const {flows, centroid} = selectedFeature.properties;
      const arcs = Object.keys(flows).map(toId => {
      const f = data.features[toId];
      return {
        source: centroid,
        target: f.properties.centroid,
        value: flows[toId]
      };
      });

      const scale = d3.scaleQuantile()
        .domain(arcs.map(a => Math.abs(a.value)))
        .range(inFlowColors.map((c, i) => i));

        arcs.forEach(a => {
        a.gain = Math.sign(a.value);
        a.quantile = scale(Math.abs(a.value));
      });

      return new ArcLayer({
        id: 'arc',
        data: arcs,
        getSourcePosition: d => d.source,
        getTargetPosition: d => d.target,
        getSourceColor: d => (d.gain > 0 ? inFlowColors : outFlowColors)[d.quantile],
        getTargetColor: d => (d.gain > 0 ? outFlowColors : inFlowColors)[d.quantile],
        strokeWidth: 4
      });
    }

    function renderLayers(data, selectedFeature) {

      selectedFeature = selectedFeature || data.features.find(f => f.properties.name === 'Los Angeles, CA');
      const arcLayer = getArcLayer(data, selectedFeature);
      const countyLayer = new GeoJsonLayer({
        id: 'geojson',
        data,
        stroked: false,
        filled: true,
        autoHighlight: true,
        pickable: true,
        getFillColor: () => [0, 0, 0, 0],
        onClick: info => renderLayers(data, info.object)
      });

      deckgl.setProps({ layers: [countyLayer, arcLayer] });
    }

    fetch('https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/arc/counties.json')
    .then(res => res.json())
    .then(data => renderLayers(data))

  </script>
</html>

You may also like...