shuilong

shuilong的博客

Path Recording Demo

Background#

Recently, I used an application called anydistance on my iPhone, which can record paths for activities like running or biking. What's even more amazing is that it also records the altitude and supports generating 3D paths.

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

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. It is best to set it to true. I tested it by running a lap in my neighborhood and comparing the results. When high accuracy is not set, the path appears very messy.

three.js#

First, we need to convert the data. Using the example of running a lap around the neighborhood mentioned above, the longitude range is from 120.11420594721491 to 120.11537834321953, which is very small. If we use this data to display the path, it is difficult to see the shape on the canvas. Therefore, we need to perform 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();

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

We 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 considerations to take into account. 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.