shuilong

shuilong的博客

Path Recording Demo

Background#

Recently, I used an application called anydistance on my iPhone, which can record paths such as running or cycling, and what's even more amazing is that it can record altitude and support the generation of 3D paths.

Here is a cool-looking path that was recorded on a trip to Huzhou last time.

So I thought about making a demo myself.

The demo link is http://geo-path.pages.dev/

Technical Details#

Technically, navigator.geolocation supports obtaining the user's current geographical information, and then we use three.js to draw the path.

geolocation#

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
        });
    })
}

There are two points to note here.

First, some browsers do not support the altitude attribute, such as Chrome on Android.

Second, the enableHighAccuracy property, when set to true, indicates support for high accuracy, which is best set to true. I tested and compared it by running a lap in the neighborhood. When high accuracy is not set, the path appears very messy.

three.js#

First, we need to convert the data. Taking the example of running a lap around the neighborhood mentioned above, the longitude range is from 120.11420594721491 to 120.11537834321953, which is very, very small. If we use this data to display, it is difficult to see the shape on the canvas, so we need to do the corresponding data conversion.

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 ) 
});

Path#

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();

Use Line to represent the path, and we place the center of this line at the origin.

Current Position Dot#

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 );

Use Points to represent the current position dot.

Animation#

// Rotate the path
this.line.rotateOnAxis(new THREE.Vector3(0, 0, 1), 1 * Math.PI * 2 / frames);

// Update current position
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]);

Effect#

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

Others#

The above is just a demo. To make this product better, there are still some situations to consider. Currently, it records 20 times per second. If the user's path is very long, there will be a lot of data. The storage of data and the selection of appropriate data granularity for display are all things to consider.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.