记一次前端"揭开绘制地图的神秘面纱"分享会

记一次前端"揭开绘制地图的神秘面纱"分享会

记录了我在组内的技术分享, 有一样需求的同窗能够参考一下
分享全程下来时间大约 70分钟

image

一. 为何要分享前端相关的"地图"知识

  1. (大屏展现)不少公司都会有相应的大屏幕展现系统, 例如中国或者全世界的客户与资产分布图.
  2. (生动描绘)用地图的角度来展现地理方面的关系, 让人看着比单纯的文字更直观
  3. (场景多)好比今年的各种疫情严重状况的分布图.
  4. 总的来讲仍是看起来比较炫酷, 能够提高一点点b格, 而且这个只是也是属于前端的范畴, 那么咱们就有必要弄懂它.

二. 作地图相关技术简介

这里我只介绍几款我经常使用的

** 百度地图
这个名气太大了, 功能不少而且如今对3d的支持也很不错, 注意GL版v1.0 与以前 v2.0版本地图的api有点不同别掉坑里.
缺点也比较明显, 好比你想要一份干干净净的地图, 上面没有店铺没有任何标识的时候我就建议你用echarts来玩了, 由于百度地图带的东西比较多.
想要使用百度地图的同窗能够看这里, 超级简单就能够完成注册用玩耍.
使用很是简单php

** hcharts
很是牛很是好用, 可是它部分功能是要收费的, 使用以前要让公司帮你买好相应的功能才能用于商用哦.
因为咱们公司地图库是本身研发的最后也就没有这种网上付费的.
详情地址html

** echarts
这个库前端无人不知了, 在需求很简单的状况下建议用这个技术来作, 大部分时候项目中须要绘制柱状图或折线图的时候已经引入了echarts此时不用重复引用来节省空间.
echarts画的地图前端

** 咱们公司本身的2d, 3d地图组件库
这个在这里就不作过多详细介绍了, 一些公司也会有自主研发的地图组件, 设计的思想上可能与上面三个不太相同, 接下来我也会聊到.vue

三.echarts实现基础地图

以echarts为例是由于这个最好弄...

这里我新建了一个vue工程c++

<template>
  <div class="home">
    <div id="map"></div>
  </div>
</template>

<script>
import echarts from "echarts";
import mapData from "./geo";

export default {
  name: "Home",
  data() {
    return {
      myChart: null,
    };
  },
  methods: {
    initMap() {
      this.myChart = echarts.init(document.getElementById("map"));
      echarts.registerMap("world", mapData); // 定义名称下面要用, 这样作的好处就是能够很方便的实现切换地图的效果
      this.myChart.setOption({
        series: [
          {
            type: "map",
            mapType: "world", // 自定义扩展图表类型
            label: {
              show: false,
            },
          },
        ],
      });
    },
  },
  mounted() {
    this.initMap();
  }
};
</script>
  1. 像咱们平时使用echarts同样先初始化
  2. 接下来有点不一样须要echarts.registerMap("world", mapData); 能够理解为把这个数据命名为'world', 方便之后的切换(这里的数据我下面会讲).
  3. 在option的配置里面设置类型是地图, 使用上面定义好的'world'类型.

效果图
imagegit

咱们能够看得出来, 地图的绘制也没什么'特殊'的, 最主要的就是那个 mapData数据, 这个数据通常叫它geojson数据, 那么接下来咱们认识一下它.github

四.geojson数据究竟是什么

  1. geojson是用json的语法表达和存储地理数据,能够说是json的子集, 它不是专门js使用的这点要清楚.
  2. 地图上有山川, 河流, 海洋等等的地理信息, 那么如何描述一条河? 这个时候就要使用geojson格式的文件来描绘.
  3. 并非必须用geojson, geojson只是一套规范, 各大解析器用这套规范来解析生成对应的景色, 咱们彻底能够制定本身的规范来实现这些, 无非是兼容性很差须要本身写绘制的解析器.

五.geojson详细介绍

英语好的能够先撸网站
1. 基本结构web

{ // 能够包括点线面, 一个大的集合
  "type": "FeatureCollection", // 定义这个是个geojson文件, 这里还能够是其余值下面会说
  "features": [] // 这里放要绘制的数据
}

之后咱们看到"type": "FeatureCollection"这样一行就说明这个文件是geojson规范的文件算法

2. 描述一个点(Feature)
地图上的打点数据apache

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",  // 表示这个对象是一个要素
      "properties": {}, // 这里放样式, 后面会专门说
      "geometry": { // 这里面放具体的数据
        "type": "Point",  // 专指画点
        "coordinates": [105.380859375, 31.57853542647338] // 默认是经度与纬度, 三维的话就是xyz三个值, 固然这里也不必定是经纬度(不一样的坐标体系)中会讲为何
      }
    },
  ]
}

3. 描述多个点(FeatureCollection)
**优势

  1. 写法简洁
  2. 这些点样式能够共用
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "MultiPoint", // 多点, 也就是连续画多个一样的点
        "coordinates": [[105.380859375, 31.57853542647338],
        [105.580859375, 31.52853542647338]
        ]
      }
    },
  ]
}

4. 描述一条线(LineString)

  1. 这里仍是描绘每个点, 但这些点会链接在一块儿造成线
  2. 地图上的连线数据
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "LineString", // 这里全部的点会链接在一块儿造成线
        "coordinates": [[105.6005859375, 30.65681556429287],
        [107.95166015624999, 31.98944183792288],
        [109.3798828125, 30.031055426540206],
        [107.7978515625, 29.935895213372444]]
      }
    },
  ]
}

5. 描述多条线(MultiLineString)

  1. 这里第二组与第一组的线, 能够分隔开不会首尾相连.
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "MultiLineString",
        "coordinates":
          [
            [
              [105.6005859375, 30.65681556429287],
              [107.95166015624999, 31.98944183792288],
              [109.3798828125, 30.031055426540206],
              [107.7978515625, 29.935895213372444]
            ],
            [
              [109.3798828125, 30.031055426540206],
              [107.1978515625, 31.235895213372444]
            ]
          ]
      }
    },
  ]
}

6. 描述一个面(Polygon, 也叫多边形)

  1. 第一个点与最后一个点要相同, 这样才能完成闭环!!
  2. 三维数组的格式须要注意
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon", // 注意这里是三维数组
        "coordinates": [
          [
            [106.10595703125, 33.33970700424026],
            [106.32568359375, 32.41706632846282],
            [108.03955078125, 32.2313896627376],
            [108.25927734375, 33.15594830078649],
            [106.10595703125, 33.33970700424026]
          ]
        ]
      }
    },
  ]
}

7. 一个面里面有多个面(Polygon)

  1. 这种单一的'Polygon'里面出现多个形状, 会出现中空的状况, 相似布尔运算, 这样就能够在地图中描述那种圈型的国家
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -39.7265625,
              -3.162455530237848
            ],
            [
              127.96875,
              -3.162455530237848
            ],
            [
              127.96875,
              74.1160468394894
            ],
            [
              -39.7265625,
              74.1160468394894
            ],
            [
              -39.7265625,
              -3.162455530237848
            ]
          ],
          [
            [
              -22.5,
              15.961329081596647
            ],
            [
              110.74218749999999,
              15.961329081596647
            ],
            [
              110.74218749999999,
              70.8446726342528
            ],
            [
              -22.5,
              70.8446726342528
            ],
            [
              -22.5,
              15.961329081596647
            ]
          ]
        ]
      }
    }
  ]
}

效果以下:
image

8. 描述多个面(MultiPolygon)
优点:

  1. 写法简洁
  2. 这些点样式能够共用
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
          [
          [
            [
              -39.7265625,
              -3.162455530237848
            ],
            [
              127.96875,
              -3.162455530237848
            ],
            [
              127.96875,
              74.1160468394894
            ],
            [
              -39.7265625,
              74.1160468394894
            ],
            [
              -39.7265625,
              -3.162455530237848
            ]
          ]
        ],
        [
          [
            [
              -22.5,
              15.961329081596647
            ],
            [
              110.74218749999999,
              15.961329081596647
            ],
            [
              110.74218749999999,
              70.8446726342528
            ],
            [
              -22.5,
              70.8446726342528
            ],
            [
              -22.5,
              15.961329081596647
            ]
          ]
        ]
        ]
      }
    }
  ]
}

这里若是重叠了就是颜色的叠加了如图所示:
image

9. 描述一个组(geometries)

  1. 好比咱们为了表示一种特定的地貌那么咱们能够把这个地貌数据独立起来
{
  "type": "FeatureCollection",
  "features": [
    { // 能够包括点线面, 一个独立的集合
      "type": "GeometryCollection",
      "geometries": [
        {
          "type": "Point",
          "coordinates": [108.62, 31.02819]
        }, {
          "type": "LineString",
          "coordinates": [[108.896484375, 30.1071178870],
          [108.2184375, 30.91717870],
          [109.5184375, 31.2175780]]
        }
      ]
    }
  ]
 }

10. 不一样的样式(properties)

{
  "type": "FeatureCollection",
  "features": [
     {
      "type": "Feature",
      "properties": { // 专门放属性
        "stroke": "#fa9661", // 外边颜色
        "stroke-width": 4.1, // 外边宽
        "stroke-opacity": 0.7, // 外边透明度
        "fill": "#9e290c",  // 填充色
        "fill-opacity": 0.7 // 填充色透明度
      },
      "geometry": {
        "type": "Point",  // 画点
        "coordinates": [105.380859375, 31.57853542647338]
      }
    },
  ]
}

六. geojson的相关网站与工具的使用

** 展现干巴巴的数据你们看着不起劲, 这里我推荐一个绘制geojson的超棒网站地址

那么我来介绍一下如何使用这个网站高效的生成, 以及调试geojson
image

  1. 也就是最后生成的geojson, 这里的变化能够实时影响图像, 而且会有错误提示很方便编写.
  2. 绘制直线
  3. 绘制多边形也就是面, 这里注意要首位相连.
  4. 绘制矩形, 这里应该是专门封装的方法绘制矩形.
  5. 绘制点, 这里会为咱们在地图上mark一下, 具体的图片须要咱们本身在项目中引用.

image

  1. 点击以后进入编辑模式, 鼠标在图形上会出现小手标识, 此时能够拖动图形移动, 操做能够选择是否保留.
  2. 删除模式, 点击能够删除指定图形,操做能够选择是否保留.

image
单击图形能够出现如图所示的操做框.

  1. 添加样式属性, 上方展现的是当前样式属性
  2. 保存你的更改
  3. 删除这个图形

image

  1. 点击open可使用本地的geojson文件进行导入绘制.
  2. save下面点击geojson能够把生成的代码文件下载到本地.

七.自制geojson解析绘制工具的思路

  1. 咱们能够只作一个转换器, 也就是你随便写认为不错的格式, 最后转换成geojson的格式.
  2. 直接用你喜欢的格式来绘制图形
  3. 若是用canvas来实现就是绘制对应的图形就行了, 就是图形叠加那里须要特殊处理一下, 样式直接读取properties里面的数据进行设置.
  4. 绘制经纬度也是个问题, 毕竟在平面上很差计算经纬度(接下地图绘制章节会讲相关知识).
  5. 因此综上看来是否是绘制一张平面版的地图也没那么困难, 只要数据对了就成功一小半了.

八.地图的基本概念 (瓦片地图, 矢量地图)

** 有没有发现我们使用的地图在放大的时候,区域都是一个方块一个方块的被加载成图像的.
** 若是你打开控制台的network还能够看到有好多png的请求.
** 地图这种超大的数据, 超多细节是如何作到快速渲染的?
** 下面是如今比较主流的两种地图的绘制模式.

栅格瓦片地图

顾名思义图片像是瓦片同样堆叠起来的格子状成为地图, 有点像拼图, 是否是感受一点也不高大上....
但这里也是有不少问题要解决的, 好比你在俯视世界的视角看地图, 那么出现的就是世界的瓦片图片, 当高度小于必定的数值时就采用另外一套相应的瓦片, 在某个高度范围内是采用放大瓦片图片的方式模拟视野的降低, 每次请求瓦片图片都须要传递: 1: 当前视口所在坐标(经纬度) 2: 当前视口宽高 3: 当前视角高度.

栅格瓦片以 256 256 或 512 512 大小的图片为介质,这种技术一般是在服务端预先将图片渲染好,前端根据地图的缩放等级,按需加载图片加以拼接,目前依旧在大规模使用,但这种方式存在一些劣势:

受到网络带宽开销和存储空间限制的影响大,离线化部署成本高,单套主题将近 500 多 G(中国)。
样式编辑完后端渲染须要时间长。
无三维的建筑数据,在 3D 场景中无高度信息。
数据保密性差。

矢量地图

顾名思义就是矢量绘制出图形, 只要不是照片确定会小不少, 对于矢量为何轻量而且不失真能够参考的上篇文章svg的分享svg实战

矢量瓦片采用和栅格瓦片相同的分级切割方案,不一样的是,瓦片数据传输的是地理数据,包括道路、土地、建筑等,经过在前端作地图的渲染,具备以下优点:

极少占用服务器空间,下降网络开销,本地化部署只需5G空间(中国)。
地图的底图样式更换简单.
由于具备了地理数据自己,可在数据基础上作三维空间的延伸,例如 3D 建筑。
数据保密性强。

九.不一样的坐标系

** 地球自己是个椭球体, 要把它以平面的方式绘制在一个矩形上也真的很差办, 如今有很多绘制的方式可是都有各自的优缺点, 感兴趣的朋友能够查查看具体的细节, 我这里就简单介绍下比较常见的方式.

  1. 经纬度EPSG:4326 也就是地图的默认坐标
    如今球体上定义好经纬度, 而后在正方形纸上画出刻度, 对应的绘制
  2. 墨卡托投影(EPSG:3785 )
    把地球放在一个圆筒里面, 假设地球内部有个光源, 那么地球在圆柱上的投影就是地图
  3. 火星坐标系

火星坐标是国家测绘局为了国家安全在原始坐标的基础上进行偏移获得的坐标,基本国内的电子地图、导航设备都是采用的这一坐标系或在这一坐标的基础上进行二次加密获得的。
火星坐标的真实名称应该是 GCJ-02 坐标,基本上全部的国内的电子地图采用的都是火星坐标系甚至 Google 地图中国部分都特地为中国政府作了偏移。

  1. 百度坐标系

火星坐标是在国际标准坐标 WGS-84 上进行的一次加密,因为国内的电子地图都要至少使用火星坐标进行一次加密,百度直接就职性一些,直接本身又研究了一套加密算法,来了个 二次加密,这就是咱们所熟知的百度坐标 BD-09,固然只有百度地图使用的是百度坐标

  1. WGS-84 坐标系

GS-84 坐标是一个国际的标准,通常卫星导航,原始的 GPS 设备中的数据都是采用这一坐标系。国外的 GoogleMap、OpenStreetMap、MapBox、OpenLayer 等采用的都是这一坐标。

geojson设置坐标系
因为坐标系的不一样, 那么就算绘制一个点的坐标也都不会彻底相同了, 那么就须要咱们来告诉使用geojson的人按哪一种坐标系进行解析

{
  "type": "FeatureCollection",
  "crs": { // 定义坐标系 (若是不写就是使用经纬度的坐标系)   默认为EPSG:4326。
    "type": "name", // "type" 和 "properties"。为强制拥有
    "properties": {
      "name": "urn: ogc: def: crs: EPSG: 54013" // 这里定义具体的规则
    }
  },
  "features": [
     {},
  ]
}

使用上线的规则

{
  "type": "FeatureCollection",
  "crs": {
    "type": "link", // 这里变成了link
    "properties": {
      "href": "http://example.com/crs/42", // 这里是你设置的资源连接
      "type": "proj4" // "proj4","ogcwkt",esriwkt" 只能这三种格式
    }
  },
  "features": [
     {},
  ]
}

十.更快的前端数据 -> WebAssembly

**WebAssembly是一种新的编码方式,文件体积更小,启动速度更快,运行速度也更快,与使用JavaScript构建的Web应用相比,性能提高明显。它是多种编程语言的编译器目标,包括C++、C、Rust等。
WebAssembly 是由主流浏览器厂商组成的 W3C 社区团体 制定的一个新的规范。**

WebAssembly 能够明显的提高计算的速率, 还挺适合用在地图库里面的

  1. WebAssembly 和 JavaScript 结合使用, 短期并不会替代js
  2. .wasm文件结尾的文件来标识.
  3. WebAssembly 有一套完整的语义,实际上 wasm 是体积小且加载快的二进制格式, 其目标就是充分发挥硬件能力以达到原生执行效率
    WebAssembly 运行在一个沙箱化的执行环境中,甚至能够在现有的 JavaScript 虚拟机中实现。在web环境中,WebAssembly将会严格遵照同源策略以及浏览器安全策略。
    WebAssembly 设计了一个很是规整的文本格式用来、调试、测试、实验、优化、学习、教学或者编写程序。能够以这种文本格式在web页面上查看wasm模块的源码。
    WebAssembly 在 web 中被设计成无版本、特性可测试、向后兼容的。WebAssembly 能够被 JavaScript 调用,进入 JavaScript 上下文,也能够像 Web API 同样调用浏览器的功能。固然,WebAssembly 不只能够运行在浏览器上,也能够运行在非web环境下。
  4. 解析 - 解码 WebAssembly 比解析 JavaScript 要快

编译和优化 - 编译和优化所需的时间较少,由于在将文件推送到服务器以前已经进行了更多优化,JavaScript 须要为动态类型屡次编译代码
从新优化 - WebAssembly 代码不须要从新优化,由于编译器有足够的信息能够在第一次运行时得到正确的代码
执行 - 执行能够更快,WebAssembly 指令更接近机器码
垃圾回收 - 目前 WebAssembly 不直接支持垃圾回收,垃圾回收都是手动控制的,因此比自动垃圾回收效率更高。目前浏览器中的 MVP(最小化可行产品) 已经很快了。在接下来的几年里,随着浏览器的发展和新功能的增长,它将在将来几年内变得更快。

说了这些都是概念, 接下来咱们就一块儿实战一下go

十一. hello 级别的WebAssembly

中文官网
官网的实现还须要配置环境啥的搞得很正式, 入门级别其实咱们更想的是尝尝鲜, 只要你会点c++就能用我接下来的方法实现.

在线生成
在线生成

image

  1. 点击转换c++代码为WebAssembly格式
  2. 点击下载转换好的文件
  3. 下载到的是个二进制文件

引用文件

fetch("/test.wasm")
      .then((res) => res.arrayBuffer()) // 拿到Buffer格式
      .then((bytes) => WebAssembly.compile(bytes)) // 转字节码
      .then((mod) => {
        const instance = new WebAssembly.Instance(mod);
        const exp = instance.exports;
        console.log(exp._Z7showNumv())
      });
  1. exp._Z7showNumv 而不是 exp.showNum, 这个咱们能够在Wat那一栏修改一下, 可是代码多了修改起来也不容易应该有禁止转换时修更名称的选项这里就不过多展开了.
  2. 注意这里会跨域, 由于属于文件协议, 你能够本地启个服务.

开发成本

  1. 须要的不仅是前端技术了.
  2. bug稍微有点多, 好比很差调试, 还有的同窗遇到了每次编译结果不一样等问题.
  3. 社区不完善
  4. 建议这门技术先使用在封装度较高, 计算量很大的模块上.

十二.(组内篇)我写的2d与3d工程的代码介绍

这里我在组内展现一下我编写的两个项目的代码结构与遇到的问题, 就不在这里展开了毕竟涉及保密问题, 但大致思路就是把地图分红世界, 国家, 省, 市, 区 几个等级(省市区是中国的分法), 至关于一个状态机, 而后在每一个状态下作相应的事好比打点与连线, 每次变换图层状态都会隐藏其余图层展现相应视野的图层.

end.

地图方面也属于前端比较有用的一环, 我今年刚接触地图相关项目也是一脸蒙, 可是详细学习了geojson等知识以后再用地图相关组件库就很是顺畅了.此次就是这样, 但愿和你一块儿进步.

相关文章
相关标签/搜索