Skip to content

Commit

Permalink
add MercatorCoordinate meterInMercatorCoordinateUnits method (#8524)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewharvey authored Aug 14, 2019
1 parent b4fd6ee commit f1f2229
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
35 changes: 33 additions & 2 deletions src/geo/mercator_coordinate.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
import LngLat from '../geo/lng_lat';
import type {LngLatLike} from '../geo/lng_lat';

/*
* The circumference of the world in meters at the equator.
*/
const circumferenceAtEquator = 2 * Math.PI * 6378137;

/*
* The circumference of the world in meters at the given latitude.
*/
function circumferenceAtLatitude(latitude: number) {
const circumference = 2 * Math.PI * 6378137;
return circumference * Math.cos(latitude * Math.PI / 180);
return circumferenceAtEquator * Math.cos(latitude * Math.PI / 180);
}

export function mercatorXfromLng(lng: number) {
Expand Down Expand Up @@ -36,6 +40,19 @@ export function altitudeFromMercatorZ(z: number, y: number) {
return z * circumferenceAtLatitude(latFromMercatorY(y));
}

/**
* Determine the Mercator scale factor for a given latitude, see
* https://en.wikipedia.org/wiki/Mercator_projection#Scale_factor
*
* At the equator the scale factor will be 1, which increases at higher latitudes.
*
* @param {number} lat Latitude
* @returns {number} scale factor
*/
export function mercatorScale(lat: number) {
return 1 / Math.cos(lat * Math.PI / 180);
}

/**
* A `MercatorCoordinate` object represents a projected three dimensional position.
*
Expand Down Expand Up @@ -113,6 +130,20 @@ class MercatorCoordinate {
toAltitude() {
return altitudeFromMercatorZ(this.z, this.y);
}

/**
* Returns the distance of 1 meter in `MercatorCoordinate` units at this latitude.
*
* For coordinates in real world units using meters, this naturally provides the scale
* to transform into `MercatorCoordinate`s.
*
* @returns {number} Distance of 1 meter in `MercatorCoordinate` units.
*/
meterInMercatorCoordinateUnits() {
// 1 meter / circumference at equator in meters * Mercator projection scale factor at this latitude
return 1 / circumferenceAtEquator * mercatorScale(latFromMercatorY(this.y));
}

}

export default MercatorCoordinate;
14 changes: 13 additions & 1 deletion test/unit/geo/mercator_coordinate.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test } from '../../util/test';
import LngLat from '../../../src/geo/lng_lat';
import MercatorCoordinate from '../../../src/geo/mercator_coordinate';
import MercatorCoordinate, { mercatorScale } from '../../../src/geo/mercator_coordinate';

test('LngLat', (t) => {
t.test('#constructor', (t) => {
Expand All @@ -27,5 +27,17 @@ test('LngLat', (t) => {
t.end();
});

t.test('#mercatorScale', (t) => {
t.equal(mercatorScale(0), 1, 'mercator scale at the equator');
t.equal(mercatorScale(45), 1.414213562373095, 'mercator scale at 45 degrees latitude');
t.end();
});

t.test('#meterInMercatorCoordinateUnits', (t) => {
const nullIsland = new LngLat(0, 0);
t.equal(MercatorCoordinate.fromLngLat(nullIsland).meterInMercatorCoordinateUnits(), 2.495320233665337e-8, 'length of 1 meter in MercatorCoordinate units at the equator');
t.end();
});

t.end();
});

0 comments on commit f1f2229

Please sign in to comment.