web GIS初探

988 阅读4分钟

前言:

我接触前端已经两年了,有一年的实习经历,对于前端方面的html、css、js、vue、react都比较熟悉,做过些后台管理系统的业务。但是对于GIS就不太了解了,最近在面试webgis的前端岗位,所以想去了解从事webgis开发需要掌握哪些技术。

这一块的知识很多且杂,我也不能逮着一个技术就深入去学,那样太耗费时间。所以我的目标是对webgis的概念、应用场景、技术栈有个了解,并解决学习过程中遇到的问题。

我努力想将本文写成“综述”一类的东西,希望能给和我有相同境遇的人提供些参考。

正文

一、我需要学些什么?

GIS即地理信息系统, webgis即将前端可视化技术与 GIS 技术结合,提供更好的信息展示和用户交互。所以对于前端开发人员就是做”获取数据、展示数据“两件事,这点和以往做得项目是共通的。

前端可视化技术涉及有canvas、webGL、计算机图形学,这一块我觉得是可以深入学习的,因为它较为底层,有益于自己在这一行深耕;而且就算不能从事webgis,还可以转到数据可视化。

GIS开发框架如下,这些框架都是用js语言开发的,所以比较容易上手的,而且都是调用API,经过一段时间的使用应该就能掌握了。

  • 二维GIS(openlayers MapBox leaflet)
  • 三维GIS(cesium)

当然gis相关的知识也是很重要的,毕竟是一个独立的专业嘛!

这里将web开发和webgis开发做一个比较,可以看到webgis开发时多了GIS数据库和GIS服务器。这也好理解,毕竟地理信息很丰富,而且一些数据资源应该是很机密的,所以被垄断在专门的GIS数据库和服务器商手里。但是也不乏有些开放的平台信息,如高德地图API。一般的小公司出于成本考虑,应该是用第三方的数据。 image.png

image.png

GIS数据库 image.png GIS服务器:Web GIS架构中最重要的部分,决定了能提供的服务 image.png

二、Web GIS 的应用:

那么webgis开发的产品形式是什么样的呢?我知道后台管理系统是怎么回事,对于这个,还不太懂呢?就像你要去做一辆车,最好得先知道车是什么样的呀?

  1. Web GIS + 应急 image.png
  2. Web GIS + 环保 image.png
  3. Web GIS + 智慧城市 image.png

从上图可以看到,就是开发一些应用,当然载体有web浏览器,移动端,或是桌面软件。

但是正如第一部分提到的,webgis的生态位上还涉及到各种框架、提供第三方服务的机构,所以可做的事情不局限于调用API去开发系统。但是就我来说,我还是喜欢贴近底层用户,做些高性能、炫酷的页面,开发出可用的系统来,这也是我选择前端开发的原因。当然,随着学习的深入,我也会慢慢向生态位的上游靠拢。

三、Web GIS 的发展趋势以及对应的难点

  • 实时数据吞吐能力、大数据分析、实时分析
  • 从二维地图转向三维地图,而且三维展示更形象。这一点要得益于webGL、缓存技术的发展,使得浏览器可以平滑地展示三维数据
  • VR与AR展示更加逼真地展示地物

数据多了问题就来了,由此而来的GIS服务器访问压力、网络传输带宽压力、客户端渲染能力的问题,其实哪怕简单的增删改查,只要数据量一多都要考虑到这些问题,从而要求做出优化,如web缓存、算法优化、合理分配服务器和客户端的工作量

四、基于高德地图的webgis案例——《智慧校园》 体验链接

这个案例很简单,仅涉及到js+高德地图提供的API+GeoJSON。

简单介绍下GeoJSON:其实质就是我们熟悉的json,使用 json 数据格式来保存地理信息,因为要配合相应的 api 使用——如高德地图中的 AMap.GeoJSON ,所以有些字段是限定的。将数据存为 GeoJSON 格式,再保存到数据库或文件中,实现数据的持久化

功能:可以用鼠标在地图上标记点,自动规划出路程最短的校园游览路线;点击标记点,可以打卡并记录次数

案例代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>智慧校园</title>
  <!-- 引入css资源 -->
  <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" />
  <style>
    body,
    html,
    #container {
      width: 100%;
      height: 100%;
    }
  </style>

  <!-- 引入js资源,这里需要预先去高德平台申请key -->
  <script type="text/javascript">
    window._AMapSecurityConfig = {
      securityJsCode: '014b3a524fc1fdb97c989671801bae00',
    }
  </script>
  <script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=688698070791bd5d966f8dc8656803f0"></script>

</head>

<body>
  <div id="container"></div>
  <div class="info">标记校园景点,规划游玩路线</div>
  <div class="input-card" style="width:10rem; height:10rem">
    <h4>推荐浏览路线</h4>
    <div class="input-item">
      <button class="btn" onclick="startAnimation()" style="margin-bottom: 15px">显示路线</button>
      <button class="btn" onclick="deleteData()">清除标记</button>
    </div>
  </div>
  <script>
    // 创建地图对象
    let map = new AMap.Map('container', {
      center: [118.715422, 32.203426],
      zoom: 16,
      viewMode: '3D',
      pitch: 45
    })

    // 使用控件
    // 同时引入工具条插件,比例尺插件和鹰眼插件
    AMap.plugin([
      'AMap.ToolBar',
      'AMap.Scale',
      'AMap.ControlBar',
      'AMap.GeoJSON',
      'AMap.MoveAnimation'
    ], function () {
      map.addControl(new AMap.ToolBar({
        position: {
          top: '80px',
          right: '40px'
        }
      }))
      map.addControl(new AMap.Scale())
      map.addControl(new AMap.ControlBar())
    })
    // 从localStorage中读取数据
    function getData () {
      if (!localStorage.getItem('geojson')) {
        saveData([])
      }
      return localStorage.getItem('geojson')
    }

    // 保存数据到localStorage中
    function saveData (data) {
      localStorage.setItem('geojson', JSON.stringify(data))
    }

    let driving
    function deleteData () {
      localStorage.removeItem('geojson')
      map.clearMap()
      driving.clear()
    }

    // 定义一个全局变量,保存geojson
    let geojson = new AMap.GeoJSON({
      geojson: null
    })
    if (getData() !== '[]') {
      // 导入数据
      geojson.importData(JSON.parse(getData()))
      // 恢复旧数据的点击事件
      geojson.eachOverlay((item) => {
        item.on('click', (e) => {
          let ext = item.getExtData()
          //click用于统计点击次数
          let click = ++ext._geoJsonProperties.click
          // 使用信息提示框显示
          let infowindow = new AMap.InfoWindow({
            anchor: 'top-center',
            content: `<div>打卡了${click}次</div>`
          })
          infowindow.open(map, item.getPosition())
          // 保存数据
          saveData(geojson.toGeoJSON())
        })
      })
    }

    map.add(geojson)

    // 监听点击事件
    map.on('click', (e) => {
      let marker = new AMap.Marker({
        position: e.lnglat,
        extData: {
          _geoJsonProperties: {
            gid: geojson.getOverlays().length + 1,
            click: 0
          }
        }
      })

      // 使用覆盖物的点击事件
      marker.on('click', function (e) {
        let ext = marker.getExtData()
        //click用于统计点击次数
        let click = ++ext._geoJsonProperties.click

        // 使用信息提示框显示
        let infowindow = new AMap.InfoWindow({
          anchor: 'top-center',
          content: `打卡了${click}次`
        })
        infowindow.open(map, marker.getPosition())
        // 保存数据
        saveData(geojson.toGeoJSON())
      })

      // 通过geojson对象来管理覆盖物
      geojson.addOverlay(marker)
      // 保存数据
      saveData(geojson.toGeoJSON())
    })
    function startAnimation () {
      // 实现路径规划
      AMap.plugin('AMap.Driving', function () {
        driving = new AMap.Driving({
          // 驾车策略,用时最少
          policy: AMap.DrivingPolicy.REAL_TRAFFIC,
          map: map
        })
        // 设置起止点
        let start = new AMap.LngLat(118.724521, 32.202509)
        let end = new AMap.LngLat(118.708021, 32.205772)
        // 得到途经点的经纬度
        let opts = {
          waypoints: [],
        }
        geojson.eachOverlay(function (item) {
          opts.waypoints.push(item.getPosition())
        })

        driving.search(start, end, opts, function (status, result) {
          if (status === 'complete') {
            // 实现轨迹模拟
            console.log(result)
            let marker = new AMap.Marker({
              map: map,
              position: start,
              icon: "https://webapi.amap.com/images/car.png",
              offset: new AMap.Pixel(-26, -13),
              autoRotation: true,
              angle: 180,
            })
            let passedPolyline = new AMap.Polyline({
              map: map,
              strokeColor: '#af5',
              strokeWeight: 6,
            })
            marker.on('moving', function (e) {
              passedPolyline.setPath(e.passedPath)
            })
            map.setFitView()

            // 获取经过的路径
            let lineArr = []
            result.routes[0].steps.forEach((item) => {
              lineArr.push(...item.path)
            })
            marker.moveAlong(lineArr, {
              duration: 100,
              autoRotation: true
            })
          } else {
            console.log('error')
          }
        })

      })
    }

  </script>
</body>

</html>

参考资料