shuilong

shuilong的博客

路徑記錄示範

背景#

最近使用了一個 iPhone 上的應用 anydistance,可以記錄比如跑步或者騎車的路徑,更驚喜的是它會記錄海拔高度,並且支持生成 3d 的路徑。

這時上次去湖州一段路上所記錄,看起來挺酷的。

所以想著自己做個 demo。

體驗連結是 http://geo-path.pages.dev/

技術細節#

技術上,navigator.geolocation支持獲取用戶當前的地理信息,然後我們用three.js畫出路徑。

geoloation#

getGeoData() {
    return new Promise((resolve, reject) => {
        navigator.geolocation.getCurrentPosition((position) => {
            const data = {
                altitude: position.coords.altitude ?? 0,
                latitude: position.coords.latitude,
                longitude: position.coords.longitude
            };
            resolve(data);
        }, () => {

        }, {
            enableHighAccuracy: true
        });
    })
}

這裡有兩點需要注意的。

首先是有些瀏覽器不支持 altitude 屬性也就是海拔高度,比如安卓上的 chrome。

其次是 enableHighAccuracy 這個屬性,設為 true 表示支持高準確性,最好設置成 true。我在小區跑了一圈測試比對了下,沒有設置高準確性時,路徑會顯得很沒有雜亂。

three.js#

首先是需要數據的轉換,還是以上述提到的繞小區跑一圈為例子,經度的範圍是 120.11420594721491 到 120.11537834321953,相差非常非常小,如果用這個數據顯示的話,在畫布上很難看清形狀,所以需要做相應的數據轉換

const latitude = [Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER];
const longitude = [Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER];

rawData.forEach((data) => {
    latitude[0] = Math.min(latitude[0], data.latitude)
    latitude[1] = Math.max(latitude[1], data.latitude)
    longitude[0] = Math.min(longitude[0], data.longitude)
    longitude[1] = Math.max(longitude[1], data.longitude)
});

const points = rawData.map((data) => {
    const latitudeValue = latitude[1] === latitude[0] ? 300 : (data.latitude - latitude[0]) / (latitude[1] - latitude[0]) * 300;
    const longitudeValue = longitude[1] === longitude[0] ? 300 : (data.longitude - longitude[0]) / (longitude[1] - longitude[0]) * 300;
    return new THREE.Vector3( longitudeValue, latitudeValue, data.altitude ) 
});

路徑#

const geometry = new THREE.BufferGeometry().setFromPoints( points );
const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
const line = new THREE.Line( geometry, material );
this.scene.add( line );

line.geometry.center();

使用 Line 來表示路徑,並且我們把這個 line 的中心放到原點。

表示當前位置的點#

const dotGeometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3( 0, 0, 0)]);
const dotMaterial = new THREE.PointsMaterial( { size: 5, sizeAttenuation: false, color: 0x888888 } );
this.dot = new THREE.Points( dotGeometry, dotMaterial );
this.scene.add( this.dot );

使用 Points 來表示當前位置的點。

動畫#

// 旋轉路徑
this.line.rotateOnAxis(new THREE.Vector3(0, 0, 1), 1 * Math.PI * 2 / frames);

// 更新當前位置
this.dotIndex++;
const length = this.line.geometry.attributes.position.array.length / 3;
this.dotIndex = this.dotIndex % length;
const vector = this.line.geometry.attributes.position.array.slice(this.dotIndex * 3, this.dotIndex * 3 + 3)
this.dot.position.set(vector[0], vector[1], vector[2]);

效果#

http://geo-path.pages.dev/

其他#

上面只是 demo,要做好這個產品其實還有些情況需要考慮,現在是 1 秒記錄 20 次,如果用戶的路徑很長,則數據就會很多,數據的存儲,還有展示時選擇合適的數據顆粒度,都是需要考慮的。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。