<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          大屏地圖:從瓦片到引擎,到手把手實(shí)戰(zhàn)

          共 15870字,需瀏覽 32分鐘

           ·

          2023-01-09 14:01

          術(shù)  堅(jiān)  

          ??閱讀本文,你將

          • 了解 mapbox-gl 和 maplibre-gl 這兩款地圖引擎的長短
          • 了解 天地圖 這一權(quán)威地圖平臺(tái)的使用
          • 進(jìn)行一個(gè) 瓦片風(fēng) 地圖的開發(fā)實(shí)戰(zhàn)

          關(guān)于錘子的隱喻

          有人說:

          “手里捏著錘子的人,看什么都像釘子。”

          雖然有點(diǎn)挖苦的意思,但其實(shí)也可以理解為一種解決問題的方法和思路:“先把各種難題轉(zhuǎn)換為自己熟悉的問題,然后就可以用自己熟悉的方式解決問題了?!?/p>

          當(dāng)然,這思路可能有 緣木求魚 的挖苦意味在里面。

          但是,有沒有一種可能:

          “我手里是一把多功能錘子!”

          噢,對于我而言,mapbox-gl/maplibre-gl 就是我面對各種大屏地圖開發(fā)需求的那把 多功能錘子。

          讓我們看看這把錘子,究竟如何。

          一、地圖引擎的選擇

          談到 GIS,就很難繞開 Mapbox 這家公司,畢竟目前世界上最廣泛使用的 矢量瓦片標(biāo)準(zhǔn) MAPBOX CECTOR TILE SPECIFICATION 正是這家公司發(fā)布制定的。

          除此之外, mapbox 還提供了非常完全的地理信息服務(wù)、非常多的地圖開發(fā)工具,其中就包括一款在前端開發(fā)者圈中非常熱門的地圖渲染引擎:

          mapbox-gl。

          這也是我日常進(jìn)行地圖開發(fā),所選擇的地圖引擎。

          1.1 認(rèn)識(shí) mapbox-gl

          mapbox-gl 是一款開源地圖引擎。

          它的 npmjs 地址:https://www.npmjs.com/package/mapbox-gl

          它的 github 官網(wǎng):https://github.com/mapbox/mapbox-gl-js

          它的使用文檔:https://docs.mapbox.com/mapbox-gl-js/

          首先,我們要認(rèn)識(shí)這個(gè)庫,就要認(rèn)識(shí)它的能力和邊界,以下是我的個(gè)人使用總結(jié):

          mapbox是一款地圖引擎,它能做什么?

          • 能通過各種投影系進(jìn)行地圖瓦片的投影。
          • 支持在地圖瓦片上疊加各種圖層,支持 geojson、圖片、文本 等多種信息在圖層上進(jìn)行加載顯示。
          • 支持自定義 Style (矢量瓦片)
          • 支持 2.5D 視角旋轉(zhuǎn)及顯示
          • 支持加載 3D 模型
          • 支持通過 DOM 的方式添加 HTML 元素
          • 支持 web-gl 能力進(jìn)行圖形渲染
          • 支持進(jìn)行 3D 形式的球星地理渲染和星空背景渲染

          尤其是其 "2.5D 視角旋轉(zhuǎn)及顯示"、"加載 3D 模型" 這兩點(diǎn),是非常亮眼的,相比于 OpenLayers 和 Leaflet 這兩款競品,這也是它最為吸引人的地方所在。

          但也不能盲目樂觀,我也總結(jié)了使用中感受到不足的點(diǎn):

          • 無法支持 地下管網(wǎng)開挖 這種形式的頁面展示(相比于 Cesium
          • 3D 支持上能力比較弱(相比于 Cesium
          • 不夠 open

          不夠 open ?” 想必你也有這樣的困惑吧,為什么我會(huì)這樣說?

          mapbox-gl 開源,但很可惜,它也不是純粹的 開源作品,雖然它確實(shí) 開源。

          這得從它的 accessToken 和賬號(hào)注冊 說起。

          1.2 使用 mapbox?可能沒那么容易

          不久前,我曾在掘金發(fā)過一篇文章介紹 mapbox-gl: 《【一庫】mapbox-gl!一款開箱即用的地圖引擎》

          但文章發(fā)布后,卻收到很多小伙伴的反饋:"注冊 mapbox 賬號(hào)居然需要國際信用卡..."

          我去試了試:還真是!

          這是 mapbox 在 2022年6月 新出的規(guī)定,注冊賬號(hào)必須綁定一張國際信用卡。這個(gè)要求,就讓很多國內(nèi)小伙伴想試用的成本大大提升了。

          那么,可能有人就會(huì)問了:“mapbox 不是開源產(chǎn)品嗎?不注冊它們官方的賬號(hào),難道用不了嗎?”問的很好,也很合理。

          但是:

          抱歉,真的用不了。

          納尼?引用一段 stackoverflow.com 上小伙伴對其的評價(jià)吧:

          Mapbox have now changed mapbox-gl-js in version 2 to no longer be Open, you will have to have a key going forward.

          翻譯一下:

          Mapbox 在 [email protected] 版本開始,已經(jīng)不再開放。你必須有它家的 accessToken 才能進(jìn)行下一步。

          沒錯(cuò),沒有國際信用卡,不能注冊 mapbox,沒有 mapbox 賬號(hào)用不了 mapbox-gl的 v2 版本。

          好家伙,它是懂資本的。

          那么?我的意思是:別用 mapbox-gl 了嗎?

          并不是,我只是要推薦一下它的孿生弟弟:

          maplibre-gl

          1.3 maplibre-gl:我比哥哥更開放

          如果你想嘗試 mapbox-gl 的各種炫酷能力,但你不想(能)注冊 Mapbox 官網(wǎng)賬號(hào),現(xiàn)在,有了一個(gè)更好的選擇:

          maplibre-gl

          它的 npmjs 地址:https://www.npmjs.com/package/maplibre-gl

          它的 github 官網(wǎng):https://github.com/maplibre/maplibre-gl-js

          簡單介紹一下:它就是 mapbox-gl 倉庫 fork 出來的開放版本,無需 accessToken 就能品嘗 mapbox-gl 的強(qiáng)大能力。

          其他介紹?不用了,參照本文關(guān)于 mapbox-gl 的相關(guān)介紹即可。

          1.4 一個(gè)簡單的選擇原則

          到底是用 mapbox-gl 還是使用 maplibre-gl? 我提供一個(gè)我自己的簡單原則:

          • 如果你希望使用 Mapbox 官方提供的瓦片服務(wù),那選 mapbox-gl 就完事了。
          • 如果你只是希望使用其地圖引擎的相關(guān)能力,并不打算使用 Mapbox 官方的瓦片服務(wù),很好,你可以選擇maplibre-gl 這款更加 Open 的開源引擎。

          按照這個(gè)原則,本系列涉及到的各類 Demo 都會(huì)以 maplibre-gl 作為地圖引擎進(jìn)行開發(fā)。

          二、 大屏的地圖一般怎么玩?

          在各種各樣場景的大屏開發(fā)中,關(guān)于地圖的展示,一般存在兩種常見的玩法:

          • 線框風(fēng)格 地圖

          • 瓦片風(fēng)格 地圖

          一款大屏到底選取哪種風(fēng)格作為地圖樣式,通常是由 業(yè)務(wù)特點(diǎn) 決定的:

          • 如果業(yè)務(wù)方并不在意具體的業(yè)務(wù)地理位置,只在乎自己在每個(gè)省的營收關(guān)系、投資情況等粗粒度的數(shù)據(jù)展示及分析,那天然適合 線框風(fēng) 地圖。沒有瓦片帶來的地理信息細(xì)節(jié)干擾,展示上也更加清爽明白。

          • 但如果業(yè)務(wù)方非常在意實(shí)際的地理業(yè)務(wù)數(shù)據(jù),關(guān)心自己的轄區(qū)在 XX街道XX區(qū)域,區(qū)域與區(qū)域之間的關(guān)聯(lián),事件在地理位置上的準(zhǔn)確顯示,那則適合選用 瓦片風(fēng) 地圖,提供精準(zhǔn)的參考和地理信息。

          maplibre-gl 最擅長的便是 瓦片風(fēng)格 的地圖,但不必?fù)?dān)心,作為一款 多功能錘子,它也能輕松駕馭 線框風(fēng) 的地圖場景。

          三、通過 "天地圖" 獲取在線瓦片服務(wù)

          "天地圖" 是由 "國家基礎(chǔ)地理信息中心" 提供的一個(gè)地理信息服務(wù)平臺(tái)。

          通過 "天地圖",我們能夠獲得免費(fèi)、權(quán)威的地理信息數(shù)據(jù),也是很多人獲取地圖瓦片的首選方案。

          官網(wǎng):https://www.tianditu.gov.cn/

          注冊完成后,訪問控制臺(tái)(https://console.tianditu.gov.cn/api/key),申請 稱為個(gè)人開發(fā)者,然后注冊一個(gè)應(yīng)用。

          這樣,你就能夠獲得一個(gè)自動(dòng)生成的 key(密鑰)。

          這個(gè) key 就是你后期請求瓦片的一個(gè)重要憑證。

          // 在文本后續(xù)的代碼引用中,我都會(huì)用全局變量 MY_KEY 來代替我申請到的這個(gè) `key`,這是為了避免你圖方便把它用到了項(xiàng)目中。那對你而言是一件危險(xiǎn)的事情。
          window.MY_KEY = '88******************2030'

          有了這個(gè)密鑰后,訪問 地圖服務(wù)清單(http://lbs.tianditu.gov.cn/server/MapService.html),查看天地圖提供的各類地圖服務(wù):

          各類地圖瓦片、標(biāo)注瓦片,應(yīng)有盡有。

          通過這些提供的瓦片,你將可以快速搭建一個(gè)完全免費(fèi)、且完全權(quán)威的地圖頁面,并且把業(yè)務(wù)數(shù)據(jù)展示其上。

          四、用引擎顯示地圖

          3.1 安裝地圖引擎

          按照本文第 1.4 節(jié)【一個(gè)簡單的選擇原則】中所說,我們要使用 天地圖 的瓦片,因此我們選用 maplibre-gl:

          yarn add maplibre-gl@latest

          或者通過 cdn 的形式完成代碼引入。

          <script src='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js'></script>
          <link href='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css' rel='stylesheet' />

          3.1 渲染天地圖瓦片的地圖

          在 mapbox 的設(shè)計(jì)思路中,“地圖” 是一個(gè)對象,你可以通過使用如下 API 快速初始化一個(gè)地圖實(shí)例:

          <template>
            <div ref="mapEl" class="map"></div>
          </template>
          <script setup>
          import mapboxgl from 'maplibre-gl';
          import 'maplibre-gl/
          dist/maplibre-gl.css';
          import { onMounted, ref } from '
          vue'
          const mapEl = ref(null)

          const initOption = {
            style: {
              "version": 8,
              "id": "43f36e14-e3f5-43c1-84c0-50a9c80dc5c7",
              "sources": {
                "tdt-vec": {
                  "type": "raster",
                  "tiles": [`https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=${MY_KEY}`],
                  "tileSize": 256
                }
              },
              "layers": [{
                "id": "tdt-tiles-layer",
                "type": "raster",
                "source": "tdt-vec",
              }]
            },
          }

          onMounted(() => {
            const map = new mapboxgl.Map({
              container: mapEl.value,
              ...initOption,
            });
          })
          </script>
          <style lang="scss" scoped>
          .map {
            width: 600px;
            height: 300px;
          }
          </style>

          通過以上代碼,就能快速渲染一個(gè)基于 墨卡托投影、天地圖瓦片 的平面 瓦片風(fēng) 二維地圖。

          發(fā)現(xiàn)沒,不僅可以正確加載天地圖的瓦片服務(wù),還可以完成 2.5D 的視角傾斜。

          上面代碼中,所做的,正是簡單生成了一個(gè)地圖實(shí)例,其中最核心的代碼在這里:

            "tiles": [`https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=${MY_KEY}`],

          聲明了天地圖瓦片資源的請求方式。

          在碼上掘金中你也可以試試:

          代碼片段

          效果實(shí)現(xiàn)了,代碼有了,但你想必還是一臉懵逼:

          為什么要這么寫呢?

          這要說到 mapbox 系框架的基本 API 思路了:圖層與資源。

          • 圖層(Layers): 我們所能看到的絕大部分內(nèi)容都屬于圖層,這和 PhotoShop 里的圖層概念很相似,圖層間有層級(jí)關(guān)系;圖層上可以設(shè)置各種布局(layout)屬性和繪制(paint)屬性,用來規(guī)定自己的顯示特點(diǎn)。但歸根結(jié)底,一張圖層上顯示什么,還是取決于它所引用的 **資源(source)**。

          • 資源(Sources): 瓦片是資源,GeoJSON是資源,圖片也是資源。資源是影響顯示的第一要素。

          所以,我們可以理解,如果在 mapbox 系中,要顯示一個(gè)內(nèi)容,起碼需要兩步:

          // step 1:添加資源
          map.addSource(...)
          // step 2:添加圖層
          map.addLayer(...)

          當(dāng)然,上面生效的這段代碼,是通過在初始化階段把 資源 和 圖層 注入到了地圖實(shí)例當(dāng)中,我們完全可以換一種寫法,同樣能實(shí)現(xiàn)相關(guān)功能:

          map.on('load', () => {
            map.addSource('tdt-vec', {
              "type""raster",
              "tiles": [`https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=${MY_KEY}`],
              "tileSize"256
            })
            map.addLayer({
              "id"'tdt-tiles-layer'
              "type""raster",
              "source""tdt-vec",
            })
          })

          思路上是一致,只是添加資源及圖層的時(shí)機(jī)不同罷了。

          3.2 添加標(biāo)注層

          只有地理瓦片,對于很多人而言依然不足以表達(dá)出足夠的地理信息,比如:

          當(dāng)前看到的是什么省、什么市、什么街道?

          因此,在一張健全的地圖上,地圖標(biāo)注 也是必要而關(guān)鍵的。

          在 3.1 節(jié)示例代碼的基礎(chǔ)上,我們按照解釋說明的思路,再添加 一個(gè)標(biāo)注資源 和 一個(gè)標(biāo)注圖層

          "sources": {
            // ... 上一節(jié)內(nèi)容省略
            "tdt-cva": {
              "type""raster",
              "tiles": [`https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=${MY_KEY}`],
              "tileSize"256
            }
          },
          "layers": [
            //... 上一節(jié)內(nèi)容省略
            {
              "id""tdt-cva-layer",
              "type""raster",
              "source""tdt-cva",
            },
          ]

          這樣一來,我們的地圖就不再單調(diào)了:

          在碼上掘金里親手嘗試吧:

          代碼片段

          3.3 對地圖顏色進(jìn)行微調(diào)

          通常來說,大屏是以深色作為主色調(diào)的,目前市面上最常見的大屏主題,前三排名為:

          1. 科技藍(lán)
          2. 科技藍(lán)
          3. 還TM是科技藍(lán)

          因此,如果地圖底色過于鮮亮,可能會(huì)和 科技藍(lán) 風(fēng)格不搭,此時(shí),你可以選擇通過 layers.raster.paint 提供的一些配置,進(jìn)行色相轉(zhuǎn)換,滿足自己的審美訴求。

          比如,修改底圖 layer 為:

            {
              "id""tdt-tiles-layer",
              "type""raster",
              "source""tdt-vec",
              "paint": {
                "raster-brightness-max"0.7// 最大亮度
                "raster-brightness-min"0.3// 最小亮度
                "raster-hue-rotate"20// 色相變換的角度
                "raster-saturation"0.7 // 飽和度
              }
            },

          如果這種風(fēng)格還不能滿足你的訴求,你可以選擇 "天地圖 影像底圖" 作為背景進(jìn)行展示,修改底圖和標(biāo)注的來源為:

          "tdt-vec": {
            "type""raster",
            "tiles": [`https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=${MY_KEY}`],
            "tileSize"256
          },
          "tdt-cva": {
            "type""raster",
            "tiles": [`https://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=${MY_KEY}`],
            "tileSize"256
          }

          兩相對比:

          很顯然,影像底圖會(huì)具備更好的在大屏上展示的效果。

          四、加載業(yè)務(wù)信息

          甲方要的不是世界地圖,而是業(yè)務(wù)地圖。

          沒有業(yè)務(wù)屬性的地圖,對于甲方而言,并無價(jià)值。

          4.1 加載多邊形塊

          假設(shè)我在地圖上繪制了兩個(gè)多邊形,形成了一個(gè) FeatureCollection 的 GeoJSON 數(shù)據(jù)。

          你問我什么是 GeoJSON ? 你是不是還沒看過上一篇基礎(chǔ)知識(shí)篇?看緊去補(bǔ)補(bǔ):《前端開發(fā)大屏地圖?必知必會(huì)的基本知識(shí)》

          那么,我應(yīng)該如何把它們在地圖上繪制出來,表現(xiàn)出兩塊區(qū)域的形狀呢?


            map.on('style.load', () => {
              map.addSource('geojson-area-source', {
                type'geojson',
                data: geojsonArea // 你得到的geojson
              })

              map.addLayer({
                id'geojson-area-layer',
                type'fill',
                source'geojson-area-source',
                layout: {},
                paint: {
                  'fill-color''red',
                  'fill-opacity'0.5,
                },
              })
            })

          沒錯(cuò),就是這么容易,還是我們之前總結(jié)的兩步走:

          1. 添加資源
          2. 添加圖層

          4.2 加載圖標(biāo)及文本

          假設(shè),我們現(xiàn)在又 3 位靚仔正在地圖上玩躲貓貓,我們希望標(biāo)注出他們的位置,以及名稱,我們應(yīng)該怎么做?

          記住兩步走的法則:先加資源,再加圖層。

          • 資源1:頭像

          分別創(chuàng)建了三個(gè)人的頭像:

          {
          zhuren'https://pic.zhangshichun.top/pic/20221129-12.png'
          bao'https://pic.zhangshichun.top/pic/20221129-10.png'
          nan'https://pic.zhangshichun.top/pic/20221129-11.png'
          }
          • 資源2:三位靚仔的坐標(biāo)和信息
          {
          "type""FeatureCollection",
          "features": [
            {
              "type""Feature",
              "properties": {
                "name""德育處主任",
                "icon""zhuren"
              },
              "geometry": {
                "coordinates": [
                  114.34495622042738,
                  30.51879704948628
                ],
                "type""Point"
              }
            },
            {
              "type""Feature",
              "properties": {
                "name""戰(zhàn)場小包",
                "icon""bao"
              },
              "geometry": {
                "coordinates": [
                  114.46248908403493,
                  30.52385942598788
                ],
                "type""Point"
              }
            },
            {
              "type""Feature",
              "properties": {
                "name""南方者",
                "icon""nan"
              },
              "geometry": {
                "coordinates": [
                  114.4188340204089,
                  30.481906063384173
                ],
                "type""Point"
              }
            }
          ]
          }

          開始編碼!

          首先,先定義一個(gè)方法,簡化 maplibre 的掛在圖片的邏輯:

          // 注冊圖片的方法
          const loadImages = async (imgs) => {
            await Promise.all(
              Object.entries(imgs).map(
                ([key, url]) =>
                  new Promise((resolve) => {
                    map.loadImage(url, (error, res) => {
                      if (error) throw error;
                      map.addImage(key, res);
                      resolve(res);
                    });
                }),
              ),
            );
          };

          然后,兩步走(先加資源,再加圖層):

          // 加載圖片
          await loadImages(images)
          // 添加位置資源
          map.addSource('boys-source', {
            type'geojson',
            data: boys
          })
          // 添加ICON圖層
          map.addLayer({
            id'boys-icon-layer',
            type'symbol',
            source'boys-source',
            layout: {
              'icon-image''{icon}',
              'icon-size'0.2,
              'icon-anchor''center',
              'icon-rotation-alignment''viewport',
              'icon-allow-overlap'true
            }
          })
          // 添加名字圖層
          map.addLayer({
            id'boys-name-layer',
            "type""symbol",
            source'boys-source',
            "layout": {
              "text-field"'{name}',
              "text-size"14,
              'text-offset': [02.4], // 名字要設(shè)置便宜,避免被頭像擋住
              'text-allow-overlap'true
            },
            "paint": {
              "text-color""white",
            },
          })

          效果達(dá)成:

          可以在碼上掘金里親自嘗試:

          代碼片段

          總體上來說,業(yè)務(wù)信息的加載,都是同樣的邏輯,只要記住兩步走的基本方針,就能完成絕大多數(shù)的業(yè)務(wù)需求。

          五、總結(jié)

          在本篇文章,我們系統(tǒng)性地了解了:

          • mapbox-gl 和 maplibre-gl 兩個(gè)庫的使用范疇。
          • 學(xué)習(xí)了天地圖的使用方法
          • 并且實(shí)戰(zhàn)了幾個(gè)簡單的業(yè)務(wù)場景

          碰到 瓦片風(fēng) 的大屏地圖開發(fā),想必不會(huì)再難倒你了。


          以上便是本次分享的全部內(nèi)容,希望對你有所幫助^_^

          喜歡的話別忘了 分享、點(diǎn)贊、收藏 三連哦~。


          從零搭建全棧可視化大屏制作平臺(tái)V6.Dooring

          從零設(shè)計(jì)可視化大屏搭建引擎

          Dooring可視化搭建平臺(tái)數(shù)據(jù)源設(shè)計(jì)剖析

          可視化搭建的一些思考和實(shí)踐

          基于Koa + React + TS從零開發(fā)全棧文檔編輯器(進(jìn)階實(shí)戰(zhàn)




          點(diǎn)個(gè)在看你最好看


          瀏覽 183
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  爱情岛论坛av | 日本黄色免费网站 | 做爱呻吟视频 | 亚洲欧美日本视频 | 无码视频在线看 |