# 示例:对数据进行提取中心点计算

设想我们正在开发一个需要分析计算的应用,在这个应用中我们已经能够查看用户的数据,现在我们需要将这个数据进行中心点提取,并查看提取后的结果。

示例使用 Vue 作为开发框架

# 1. 搭建页面框架

首先我们创建一个名为 data-transform.html 的页面,然后将需要处理的数据显示在地图上:

<!DOCTYPE html>
<html>
<meta charset="utf-8">

<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>

<body>
  <div id="app">
    <div id="map" style="width: 600px; height: 600px"></div>
  </div>

  <script>
  var app = new Vue({
    el: '#app',
    data: {
      map: null,
      renderLayer: null,

      accountId: '3897a3d3-3d32-4b6d-a138-489657278b70',
      vdatasetId: 'a444f5c3-c384-4ea2-8543-22295fbe49cb',
    },

    mounted() {
      this.map = new ol.Map({
        target: 'map',
        view: new ol.View({
          center: ol.proj.fromLonLat([140, 40]),
          zoom: 5,
        }),
      });

      fetch('http://localhost:9000/heycloud/api/render/layer', {
          method: 'POST',
          mode: 'cors',
          headers: {
            'content-type': 'application/json',
            'x-heycloud-account-id': this.accountId,
          },
          body: JSON.stringify({
            'datasource': {
              'type': 'vdataset',
              'source': this.vdatasetId,
              'attrFields': ['nl_name'],
            },
            'style': {
              'type': 'polygon-simple',
              'fillColor': '#000',
              'outlineColor': '#fff',
              'outlineWidth': 1,
              'labelField': 'nl_name',
              'labelPlacement': 'interior',
              'labelSize': 14,
              'labelHaloRadius': 1,
            },
          }),
        })
        .then(resp => resp.json())
        .then(resp => {
          const { uid } = resp.result;

          this.renderLayer = new ol.layer.Tile({
            source: new ol.source.XYZ({
              url: `http://localhost:9000/heycloud/api/render/layer/${uid}/tile/{z}/{x}/{y}/${ol.has.DEVICE_PIXEL_RATIO||1}/image.png?x-heycloud-account-id=${this.accountId}`,
              crossOrigin: '*',
            }),
          });

          this.map.addLayer(this.renderLayer);
        });
    }
  });
  </script>
</body>

</html>

这时我们会看到这样的效果:

# 2. 添加执行提取中心点计算的按钮

下面,我们在页面上添加一个按钮,当点击这个按钮的时候,调用analytics/job/create接口来创建一个分析任务。我们希望这个分析任务可以执行提取中心点计算,但暂时将具体执行计算的内容留空,留待下面详细解释。

<div id="app">
  <div id="map" style="width: 600px; height: 600px"></div>

  <div>
    <button @click="process">提取中心点</button>
  </div>
</div>
methods: {

  process: function() {
    const job = {
      'title': '示例-提取中心点',
      'type': 'flow',
      'definition': {
        // 暂时忽略
      },
    };

    fetch('http://localhost:9000/heycloud/api/analytics/job/create', {
        method: 'POST',
        mode: 'cors',
        headers: {
          'content-type': 'application/json',
          'x-heycloud-account-id': this.accountId,
        },
        body: JSON.stringify(job),
      })
      .then(resp => resp.json())
      .then(resp => {
        const { jobId } = resp.result;
        console.log(jobId)
      });
  },

},

# 3. 定义具体的计算任务

在分析计算 API 的接口中,definition属性定义了这个计算任务的具体内容。这是一个用 JSON 描述的工作流,工作流中主要有三种对象需要定义:

  • node 节点,一个最小的计算单元
  • link 连接,将节点和节点连接起来构成完整的工作流
  • result 结果,将计算结果输出

在这里,我们需要将一个矢量数据集进行提取中心点计算,那么总共需要三个节点来构成这个工作流:

  • io/ExtraceVdataset 提取矢量数据集
  • transform/Centroid 提取中心点计算
  • io/SaveVdataset 保存结果

具体用definition属性定义如下:

{
  "nodes": {
    "node-1": {
      "name": "io/ExtractVdataset",
      "settings": {
        "vdatasetId": "172484b1-2444-4fff-be18-eaa8b7ed8172"
      }
    },
    "node-2": {
      "name": "transform/Centroid",
    },
    "node-3": {
      "name": "io/SaveVdataset",
    }
  },
  "links": [
    {
      "from": {
        "nodeId": "node-1",
        "outPort": 0
      },
      "to": {
        "nodeId": "node-2",
        "inPort": 0
      }
    },
    {
      "from": {
        "nodeId": "node-2",
        "outPort": 0
      },
      "to": {
        "nodeId": "node-3",
        "inPort": 0
      }
    }
  ],
  "results": [
    {
      "title": "结果",
      "nodeId": "node-3",
      "outPort": 0,
      "outType": "kvs"
    }
  ]
}

# 4. 检查任务的完成情况

所有的分析计算任务都是异步执行的,因此我们需要定时去检查这个任务是否已经完成,如果任务已经完成,那么通过接口获取新生成的结果数据的 ID,然后就可以通过这个 ID 将结果数据也显示到地图上:

check: function(jobId) {
  fetch(`http://localhost:9000/heycloud/api/analytics/job/${jobId}`, {
      method: 'GET',
      mode: 'cors',
      headers: {
        'x-heycloud-account-id': this.accountId,
      },
    })
    .then(resp => resp.json())
    .then(resp => {
      const { status } = resp.result;
      if (status === 'success') {
        clearInterval(this.tid);

        // 获取结果
        fetch(`http://localhost:9000/heycloud/api/analytics/job/${jobId}/result/0`, {
            method: 'GET',
            mode: 'cors',
            headers: {
              'x-heycloud-account-id': this.accountId,
            },
          })
          .then(resp => resp.json())
          .then(resp => {
            const { vdatasetId } = resp;
            this.showResult(vdatasetId);
          });
      }
    });
},

# 5. 最终的效果和代码

页面效果如下图:

最终的页面代码:

<!DOCTYPE html>
<html>
<meta charset="utf-8">

<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>

<body>
  <div id="app">
    <div id="map" style="width: 600px; height: 600px"></div>

    <div>
      <button @click="process" :disabled="isProcessing">{{ isProcessing?'计算中...':'提取中心点' }}</button>
    </div>
  </div>

  <script>
  var app = new Vue({
    el: '#app',
    data: {
      map: null,
      renderLayer: null,
      resultLayer: null,

      accountId: '3897a3d3-3d32-4b6d-a138-489657278b70',
      vdatasetId: 'a444f5c3-c384-4ea2-8543-22295fbe49cb',

      isProcessing: false,
      tid: null,
    },

    mounted() {
      this.map = new ol.Map({
        target: 'map',
        view: new ol.View({
          center: ol.proj.fromLonLat([140, 40]),
          zoom: 5,
        }),
      });

      fetch('http://localhost:9000/heycloud/api/render/layer', {
          method: 'POST',
          mode: 'cors',
          headers: {
            'content-type': 'application/json',
            'x-heycloud-account-id': this.accountId,
          },
          body: JSON.stringify({
            'datasource': {
              'type': 'vdataset',
              'source': this.vdatasetId,
              'attrFields': ['nl_name'],
            },
            'style': {
              'type': 'polygon-simple',
              'fillColor': '#000',
              'outlineColor': '#fff',
              'outlineWidth': 1,
              'labelField': 'nl_name',
              'labelPlacement': 'interior',
              'labelSize': 14,
              'labelHaloRadius': 1,
            },
          }),
        })
        .then(resp => resp.json())
        .then(resp => {
          const { uid } = resp.result;

          this.renderLayer = new ol.layer.Tile({
            source: new ol.source.XYZ({
              url: `http://localhost:9000/heycloud/api/render/layer/${uid}/tile/{z}/{x}/{y}/${ol.has.DEVICE_PIXEL_RATIO||1}/image.png?x-heycloud-account-id=${this.accountId}`,
              crossOrigin: '*',
            }),
          });

          this.map.addLayer(this.renderLayer);
        });
    },

    methods: {

      process: function() {
        this.resultLayer && this.map.removeLayer(this.resultLayer);

        this.isProcessing = true;

        const job = {
          'title': '示例-提取中心点',
          'type': 'flow',
          'definition': {
            "nodes": {
              "node-1": {
                "name": "io/ExtractVdataset",
                "settings": {
                  "vdatasetId": this.vdatasetId,
                }
              },
              "node-2": {
                "name": "transform/Centroid",
              },
              "node-3": {
                "name": "io/SaveVdataset",
                "settings": {
                  "title": '示例-提取中心点结果',
                }
              }
            },
            "links": [{
                "from": {
                  "nodeId": "node-1",
                  "outPort": 0
                },
                "to": {
                  "nodeId": "node-2",
                  "inPort": 0
                }
              },
              {
                "from": {
                  "nodeId": "node-2",
                  "outPort": 0
                },
                "to": {
                  "nodeId": "node-3",
                  "inPort": 0
                }
              }
            ],
            "results": [{
              "title": "结果",
              "nodeId": "node-3",
              "outPort": 0,
              "outType": "kvs"
            }]
          },
        };

        fetch('http://localhost:9000/heycloud/api/analytics/job/create', {
            method: 'POST',
            mode: 'cors',
            headers: {
              'content-type': 'application/json',
              'x-heycloud-account-id': this.accountId,
            },
            body: JSON.stringify(job),
          })
          .then(resp => resp.json())
          .then(resp => {
            const { jobId } = resp.result;

            clearInterval(this.tid);
            this.tid = setInterval(() => {
              this.check(jobId);
            }, 1000);
          });
      },

      check: function(jobId) {
        fetch(`http://localhost:9000/heycloud/api/analytics/job/${jobId}`, {
            method: 'GET',
            mode: 'cors',
            headers: {
              'x-heycloud-account-id': this.accountId,
            },
          })
          .then(resp => resp.json())
          .then(resp => {
            const { status } = resp.result;
            if (status === 'success') {
              clearInterval(this.tid);

              // 获取结果
              fetch(`http://localhost:9000/heycloud/api/analytics/job/${jobId}/result/0`, {
                  method: 'GET',
                  mode: 'cors',
                  headers: {
                    'x-heycloud-account-id': this.accountId,
                  },
                })
                .then(resp => resp.json())
                .then(resp => {
                  const { vdatasetId } = resp;
                  this.showResult(vdatasetId);
                });
            }
          });
      },

      showResult: function(resultVdatasetId) {
        fetch('http://localhost:9000/heycloud/api/render/layer', {
            method: 'POST',
            mode: 'cors',
            headers: {
              'content-type': 'application/json',
              'x-heycloud-account-id': this.accountId,
            },
            body: JSON.stringify({
              'datasource': {
                'type': 'vdataset',
                'source': resultVdatasetId,
              },
              'style': {
                'type': 'marker-simple',
                "markerWidth": 8,
                "markerHeight": 8,
                "markerColor": '#f00',
                'outlineColor': '#fff',
                'outlineWidth': 1,
              },
            }),
          })
          .then(resp => resp.json())
          .then(resp => {
            const { uid } = resp.result;

            this.resultLayer = new ol.layer.Tile({
              source: new ol.source.XYZ({
                url: `http://localhost:9000/heycloud/api/render/layer/${uid}/tile/{z}/{x}/{y}/${ol.has.DEVICE_PIXEL_RATIO||1}/image.png?x-heycloud-account-id=${this.accountId}`,
                crossOrigin: '*',
              }),
            });

            this.map.addLayer(this.resultLayer);
          });

        this.isProcessing = false;
      },

    },
  });
  </script>
</body>

</html>