博客 / 詳情

返回

JSAPIThree 地圖視野控制學習筆記:讓地圖動起來

作為一個剛開始學習 mapvthree 的小白,今天要學習地圖視野控制了!聽説這個模塊可以控制地圖的視角、縮放、旋轉等,還能轉換座標!想想就激動!

第一次聽説地圖視野控制

今天在文檔裏看到了 engine.map 這個詞,一開始我還以為是地圖本身,結果查了一下才知道,原來這是用來控制地圖視野的模塊!

文檔説地圖視野控制可以:

  • 控制地圖的中心點位置
  • 控制地圖的縮放級別
  • 控制地圖的旋轉角度
  • 控制地圖的俯仰角
  • 轉換座標系統
  • 切換視野動畫

我的理解:簡單説就是控制"怎麼看地圖",比如看哪裏、看多遠、從什麼角度看!就像控制相機一樣!

第一步:發現引擎的地圖屬性

作為一個初學者,我習慣先看看引擎有哪些屬性。文檔説 engine.map 就是地圖管理器!

我的發現:原來引擎創建後,就自動有了一個地圖管理器對象!不需要手動創建,直接用就行!

import * as mapvthree from '@baidumap/mapv-three';

const container = document.getElementById('container');
const engine = new mapvthree.Engine(container);

// 地圖管理器已經自動創建了!
console.log(engine.map); // 可以訪問地圖管理器

我的理解engine.map 就是地圖視野控制的入口,所有視野相關的操作都通過它來完成。

第二步:設置地圖中心點

文檔説可以通過 engine.map.setCenter() 來設置地圖的中心點。我試了試:

// 設置中心點為北京天安門
engine.map.setCenter([116.404, 39.915]);

我的發現:設置中心點後,地圖會立即移動到指定位置!

我的嘗試:我寫了個簡單的測試:

import * as mapvthree from '@baidumap/mapv-three';

const container = document.getElementById('container');

const engine = new mapvthree.Engine(container, {
    map: {
        center: [116.404, 39.915], // 初始中心點
        range: 1000,
        pitch: 60,
    },
});

// 切換到上海
engine.map.setCenter([121.473, 31.230]);

我的觀察:地圖從北京跳到了上海!雖然有點突然,但是位置確實變了!

我的理解setCenter() 接受一個經緯度數組 [經度, 緯度],地圖會立即移動到該位置。

第三步:獲取當前中心點

看到可以設置中心點後,我開始好奇:能不能獲取當前的中心點?

文檔説可以用 engine.map.getCenter() 來獲取!

// 獲取當前中心點
const center = engine.map.getCenter();
console.log(center); // [116.404, 39.915]

我的發現:可以獲取當前的經緯度座標!

我的想法:如果做位置記錄功能,可以用這個方法來保存當前位置!

第四步:控制地圖縮放

看到中心點控制後,我想:能不能控制地圖的縮放?

文檔説可以通過 engine.map.setRange() 來控制視野距離地面的距離!

// 設置視野距離為 1000 米(比較近)
engine.map.setRange(1000);

// 設置視野距離為 10000 米(比較遠)
engine.map.setRange(10000);

我的理解range 是相機距離地面的高度(米),值越小越近,值越大越遠。

我的嘗試

// 拉近視野(1000 米)
engine.map.setRange(1000);

// 拉遠視野(10000 米)
engine.map.setRange(10000);

我的發現

  • range = 1000:視野很近,能看到細節
  • range = 10000:視野很遠,能看到更大的範圍

我的想法:如果做縮放功能,可以用這個方法來控制視野距離!

使用 setZoom 控制縮放

文檔還説可以用 setZoom() 來控制縮放級別!

// 設置縮放級別
engine.map.setZoom(14);

我的理解zoom 是縮放級別,數字越大越近,數字越小越遠。

我的嘗試

// 放大(級別 18)
engine.map.setZoom(18);

// 縮小(級別 10)
engine.map.setZoom(10);

我的發現setZoom()setRange() 都能控制縮放,但是 setZoom() 更直觀!

我的想法:如果做縮放按鈕,可以用 zoomIn()zoomOut() 方法!

// 放大
engine.map.zoomIn();

// 縮小
engine.map.zoomOut();

我的發現:這兩個方法更方便,不需要知道具體的縮放級別!

第五步:控制地圖旋轉

看到縮放控制後,我想:能不能控制地圖的旋轉?

文檔説可以通過 engine.map.setHeading() 來設置旋轉角度!

// 設置旋轉角度(0 度是正北)
engine.map.setHeading(0);

// 設置旋轉角度(90 度是正東)
engine.map.setHeading(90);

我的理解heading 是旋轉角度,以正北為 0 度,逆時針方向遞增。

我的嘗試

// 正北方向
engine.map.setHeading(0);

// 正東方向
engine.map.setHeading(90);

// 正南方向
engine.map.setHeading(180);

// 正西方向
engine.map.setHeading(270);

我的發現:地圖會旋轉到指定方向!

我的想法:如果做指南針功能,可以用這個方法來控制方向!

獲取當前旋轉角度

文檔説可以用 getHeading() 來獲取當前的旋轉角度!

// 獲取當前旋轉角度
const heading = engine.map.getHeading();
console.log(heading); // 例如:45

我的發現:可以獲取當前的旋轉角度,這樣就能知道地圖朝向哪個方向了!

第六步:控制地圖俯仰角

看到旋轉控制後,我想:能不能控制地圖的俯仰角?

文檔説可以通過 engine.map.setPitch() 來設置俯仰角!

// 設置俯仰角(0 度是垂直向下看)
engine.map.setPitch(0);

// 設置俯仰角(90 度是水平看)
engine.map.setPitch(90);

我的理解pitch 是俯仰角,0 度是垂直向下看,90 度是水平看。

我的嘗試

// 垂直向下看(俯視)
engine.map.setPitch(0);

// 45 度俯視
engine.map.setPitch(45);

// 水平看(平視)
engine.map.setPitch(90);

我的發現

  • pitch = 0:完全俯視,像從正上方看
  • pitch = 45:斜向下看,能看到側面
  • pitch = 90:水平看,像站在地面上看

我的想法:如果做視角切換功能,可以用這個方法來控制俯仰角!

獲取當前俯仰角

文檔説可以用 getPitch() 來獲取當前的俯仰角!

// 獲取當前俯仰角
const pitch = engine.map.getPitch();
console.log(pitch); // 例如:60

我的發現:可以獲取當前的俯仰角,這樣就能知道地圖的視角了!

第七步:使用 lookAt 設置視野

看到可以分別設置中心點、縮放、旋轉、俯仰角後,我想:能不能一次性設置所有參數?

文檔説可以用 engine.map.lookAt() 來一次性設置視野!

engine.map.lookAt(
    [116.404, 39.915], // 中心點
    {
        heading: 0,    // 旋轉角度
        pitch: 60,      // 俯仰角
        range: 1000,    // 視野距離
    }
);

我的理解lookAt() 可以一次性設置中心點、旋轉角度、俯仰角和視野距離,更方便!

我的嘗試

// 飛到天安門,正北方向,60 度俯視,1000 米高度
engine.map.lookAt(
    [116.404, 39.915],
    {
        heading: 0,
        pitch: 60,
        range: 1000,
    }
);

我的發現:地圖會立即切換到指定視野,所有參數一次性設置!

我的想法:如果做場景切換功能,可以用這個方法來快速切換視野!

第八步:使用 flyTo 實現平滑過渡

看到 lookAt() 後,我想:能不能讓視野切換有動畫效果?

文檔説可以用 engine.map.flyTo() 來實現平滑的動畫過渡!

engine.map.flyTo(
    [116.404, 39.915], // 目標位置
    {
        heading: 0,
        pitch: 60,
        range: 1000,
        duration: 2000, // 動畫持續時間(毫秒)
    }
);

我的理解flyTo()lookAt() 功能類似,但是 flyTo() 有平滑的動畫效果!

我的嘗試

// 平滑飛到天安門
engine.map.flyTo(
    [116.404, 39.915],
    {
        heading: 0,
        pitch: 60,
        range: 1000,
        duration: 2000, // 2 秒動畫
    }
);

我的發現:地圖會平滑地飛到目標位置,有動畫效果,看起來很舒服!

我的感受flyTo()lookAt() 更友好,用户體驗更好!

我的想法:如果做場景切換,應該用 flyTo() 而不是 lookAt()

flyTo 的回調函數

文檔説 flyTo() 還支持回調函數!

engine.map.flyTo(
    [116.404, 39.915],
    {
        duration: 2000,
        complete: () => {
            console.log('動畫完成!');
        },
        cancel: () => {
            console.log('動畫取消!');
        },
    }
);

我的發現:可以在動畫完成或取消時執行回調函數,這樣就能做更多操作了!

我的想法:如果做場景切換,可以在動畫完成後加載數據或顯示信息!

第九步:座標轉換

看到視野控制後,我開始好奇:什麼是座標轉換?

文檔説可以通過 projectCoordinate()unprojectCoordinate() 來轉換座標!

我的理解:地理座標(經緯度)和投影座標(米)之間可以互相轉換!

地理座標轉投影座標

import * as THREE from 'three';

// 地理座標(經緯度)
const geoCoord = new THREE.Vector3(116.404, 39.915, 0);

// 投影座標(米)
const projCoord = new THREE.Vector3();
engine.map.projectCoordinate(geoCoord, projCoord);

console.log(projCoord); // 投影座標

我的理解projectCoordinate() 把地理座標轉換為投影座標。

我的發現:投影座標是三維座標(x, y, z),單位是米,可以用來放置 3D 物體!

投影座標轉地理座標

import * as THREE from 'three';

// 投影座標(米)
const projCoord = new THREE.Vector3(12960000, 4850000, 0);

// 地理座標(經緯度)
const geoCoord = new THREE.Vector3();
engine.map.unprojectCoordinate(projCoord, geoCoord);

console.log(geoCoord); // 地理座標

我的理解unprojectCoordinate() 把投影座標轉換為地理座標。

我的發現:可以從投影座標轉換回經緯度,這樣就能知道 3D 物體對應的地理位置了!

數組座標轉換

文檔還説可以用 projectArrayCoordinate() 來轉換數組格式的座標!

// 地理座標數組
const geoArray = [116.404, 39.915];

// 投影座標數組
const projArray = engine.map.projectArrayCoordinate(geoArray, []);

console.log(projArray); // [x, y, z]

我的發現:數組格式更方便,不需要創建 Vector3 對象!

我的想法:如果做批量座標轉換,可以用數組格式!

第十步:瞭解投影方式

看到座標轉換後,我開始好奇:什麼是投影?

文檔説可以通過 engine.map.projection 來獲取投影方式!

我的理解:投影是把地球的球面座標轉換為平面座標的方法。

投影類型

文檔説支持三種投影方式:

  1. EPSG:4326:WGS84 座標系,經緯度形式
  2. EPSG:3857:Web 墨卡託投影,單位為米(默認)
  3. EPSG:4978:ECEF 座標系,單位為米(3D 地球)

我的嘗試

const engine = new mapvthree.Engine(container, {
    map: {
        projection: 'EPSG:3857', // Web 墨卡託投影(默認)
        center: [116.404, 39.915],
        range: 1000,
    },
});

我的發現:可以在初始化時設置投影方式!

我的理解

  • EPSG:4326:適合顯示經緯度座標
  • EPSG:3857:適合顯示平面地圖(默認)
  • EPSG:4978:適合顯示 3D 地球

我的想法:如果做 3D 地球效果,應該用 EPSG:4978

第十一步:設置視野範圍

看到座標轉換後,我想:能不能限制地圖的可拖動範圍?

文檔説可以用 engine.map.setBounds() 來設置視野範圍!

// 設置視野範圍(左下角和右上角座標)
engine.map.setBounds([
    [116.0, 39.0], // 左下角
    [117.0, 40.0], // 右上角
]);

我的理解setBounds() 限制地圖只能在指定區域內拖動。

我的嘗試

// 限制在北京範圍內
engine.map.setBounds([
    [116.0, 39.5],  // 左下角
    [117.0, 40.5],  // 右上角
]);

我的發現:設置後,地圖只能在指定區域內拖動,超出範圍會自動限制!

我的想法:如果做區域展示,可以用這個方法來限制視野範圍!

獲取當前視野範圍

文檔説可以用 getBounds() 來獲取當前的視野範圍!

// 獲取當前視野範圍
const bounds = engine.map.getBounds();
console.log(bounds); // Box3 對象

我的發現:可以獲取當前可視區域的邊界,這樣就能知道地圖顯示的範圍了!

第十二步:根據座標數組設置視野

看到視野範圍後,我想:能不能根據一組座標自動調整視野?

文檔説可以用 engine.map.setViewport() 來根據座標數組設置視野!

// 根據座標數組設置視野
engine.map.setViewport(
    [
        [116.404, 39.915],
        [116.414, 39.925],
        [116.424, 39.935],
    ],
    {
        range: 5000, // 視野距離
    }
);

我的理解setViewport() 會根據座標數組自動計算合適的視野,讓所有座標都在視野內。

我的嘗試

// 顯示多個點的視野
const points = [
    [116.404, 39.915], // 點 1
    [116.414, 39.925], // 點 2
    [116.424, 39.935], // 點 3
];

engine.map.setViewport(points, {
    range: 5000,
});

我的發現:地圖會自動調整視野,讓所有點都在視野內!

我的想法:如果做數據展示,可以用這個方法來自動調整視野,顯示所有數據點!

第十三步:縮放到對象範圍

看到 setViewport() 後,我想:能不能縮放到某個 3D 對象的範圍?

文檔説可以用 engine.map.zoomTo() 來縮放到對象範圍!

import * as THREE from 'three';

// 創建一個 3D 對象
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
engine.add(mesh);

// 縮放到對象範圍
engine.map.zoomTo(mesh, {
    range: 1000,
});

我的理解zoomTo() 會自動調整視野,讓指定的 3D 對象在視野內。

我的發現:地圖會自動調整視野,讓對象在視野中心!

我的想法:如果做模型查看功能,可以用這個方法來自動調整視野!

第十四步:設置視野限制

看到視野控制後,我想:能不能限制視野的最大最小距離?

文檔説可以用 setMaxRange()setMinRange() 來設置視野限制!

// 設置最大視野距離(不能拉得太遠)
engine.map.setMaxRange(100000);

// 設置最小視野距離(不能拉得太近)
engine.map.setMinRange(100);

我的理解setMaxRange()setMinRange() 限制視野的距離範圍。

我的嘗試

// 限制視野在 100 米到 10000 米之間
engine.map.setMinRange(100);
engine.map.setMaxRange(10000);

我的發現:設置後,用户無法將視野拉得太近或太遠,會被限制在指定範圍內!

我的想法:如果做特定場景,可以用這個方法來限制視野範圍!

第十五步:獲取相機位置

看到視野控制後,我想:能不能獲取當前相機的位置?

文檔説可以用 engine.map.getCameraLocation() 來獲取相機位置!

import * as THREE from 'three';

// 獲取相機位置
const cameraPos = new THREE.Object3D();
const location = engine.map.getCameraLocation(cameraPos);

console.log(location); // 相機位置的經緯度和高度

我的發現:可以獲取當前相機的經緯度座標和高度!

我的想法:如果做相機位置記錄,可以用這個方法來保存相機位置!

第十六步:做一個完整的示例

我想寫一個完整的示例,把學到的都用上:

import * as mapvthree from '@baidumap/mapv-three';

const container = document.getElementById('container');

const engine = new mapvthree.Engine(container, {
    map: {
        center: [116.404, 39.915],
        range: 1000,
        pitch: 60,
        heading: 0,
    },
});

// 創建按鈕控制視野
document.getElementById('flyToBtn').addEventListener('click', () => {
    // 平滑飛到天安門
    engine.map.flyTo(
        [116.404, 39.915],
        {
            heading: 0,
            pitch: 60,
            range: 1000,
            duration: 2000,
            complete: () => {
                console.log('到達天安門!');
            },
        }
    );
});

document.getElementById('lookAtBtn').addEventListener('click', () => {
    // 立即切換到上海
    engine.map.lookAt(
        [121.473, 31.230],
        {
            heading: 0,
            pitch: 60,
            range: 2000,
        }
    );
});

document.getElementById('zoomInBtn').addEventListener('click', () => {
    // 放大
    engine.map.zoomIn();
});

document.getElementById('zoomOutBtn').addEventListener('click', () => {
    // 縮小
    engine.map.zoomOut();
});

// 獲取當前視野信息
const center = engine.map.getCenter();
const range = engine.map.getRange();
const heading = engine.map.getHeading();
const pitch = engine.map.getPitch();

console.log('中心點:', center);
console.log('視野距離:', range);
console.log('旋轉角度:', heading);
console.log('俯仰角:', pitch);

我的感受:寫一個完整的示例,把學到的都用上,感覺很有成就感!

我的發現

  • 可以控制地圖視野
  • 可以實現平滑動畫
  • 可以獲取視野信息
  • 可以轉換座標

雖然代碼還很簡單,但是已經能做出一個基本的地圖視野控制系統了!

第十七步:踩過的坑

作為一個初學者,我踩了不少坑,記錄下來避免再犯:

坑 1:座標格式寫錯了

原因:經緯度順序寫反了,或者格式不對。

解決:座標格式是 [經度, 緯度],經度在前,緯度在後。

坑 2:flyTo 和 lookAt 的區別不清楚

原因:不知道什麼時候用 flyTo,什麼時候用 lookAt

解決

  • lookAt:立即切換視野,沒有動畫
  • flyTo:平滑切換視野,有動畫效果

坑 3:range 和 zoom 的區別不清楚

原因:不知道 rangezoom 的區別。

解決

  • range:視野距離地面的高度(米),值越小越近
  • zoom:縮放級別,數字越大越近

坑 4:投影方式設置不對

原因:投影方式設置錯誤,導致座標轉換失敗。

解決:根據需求選擇合適的投影方式,默認是 EPSG:3857

坑 5:座標轉換時沒有創建輸出對象

原因:座標轉換時沒有創建輸出對象。

解決:座標轉換需要提供輸出對象,比如 new THREE.Vector3()

我的學習總結

經過這一天的學習,我掌握了:

  1. 地圖視野控制的作用:控制地圖的視角、縮放、旋轉等
  2. 如何設置中心點:通過 setCenter() 設置
  3. 如何控制縮放:通過 setRange()setZoom() 控制
  4. 如何控制旋轉:通過 setHeading() 設置旋轉角度
  5. 如何控制俯仰角:通過 setPitch() 設置俯仰角
  6. 如何切換視野:通過 lookAt()flyTo() 切換
  7. 如何轉換座標:通過 projectCoordinate()unprojectCoordinate() 轉換
  8. 如何設置視野範圍:通過 setBounds() 限制視野範圍
  9. 如何自動調整視野:通過 setViewport() 根據座標數組自動調整

我的感受:地圖視野控制真的很強大!雖然功能很多,但是用起來其實不難。關鍵是要理解每個方法的作用,然後根據需求選擇合適的方法!

下一步計劃

  1. 學習更多高級的視野控制功能
  2. 嘗試創建複雜的視野切換動畫
  3. 做一個完整的地圖導航項目

學習筆記就到這裏啦!作為一個初學者,我覺得地圖視野控制雖然功能很多,但是用起來其實不難。關鍵是要理解每個方法的作用,然後根據需求選擇合適的方法!希望我的筆記能幫到其他初學者!大家一起加油!
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.