Skip to content

Commit

Permalink
add MercatorCoordinate type, fix #7485
Browse files Browse the repository at this point in the history
  • Loading branch information
ansis committed Oct 26, 2018
1 parent ab7d06a commit 33d6089
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 11 deletions.
1 change: 1 addition & 0 deletions docs/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ toc:
- LngLatBoundsLike
- Point
- PointLike
- MercatorCoordinate
- name: User Interface
description: |
Controls, markers, and popups add new user interface elements to the map.
Expand Down
19 changes: 11 additions & 8 deletions docs/pages/example/custom-style-layer.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<script>
var map = window.map = new mapboxgl.Map({
container: 'map',
zoom: 4,
center: [0, 0],
zoom: 3,
center: [7.5, 58],
style: 'mapbox://styles/mapbox/light-v9'
});

Expand All @@ -17,7 +17,7 @@
"uniform mat4 u_matrix;" +
"attribute vec2 a_pos;" +
"void main() {" +
" gl_Position = vec4(a_pos, 0.0, 1.0);" +
" gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);" +
"}";

var fragmentSource = "" +
Expand All @@ -39,13 +39,16 @@

this.aPos = gl.getAttribLocation(this.program, "a_pos");

var helsinki = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 25.004, lat: 60.239 });
var berlin = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 13.403, lat: 52.562 });
var kyiv = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 30.498, lat: 50.541 });

this.buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, 0.5
helsinki.x, helsinki.y,
berlin.x, berlin.y,
kyiv.x, kyiv.y,
]), gl.STATIC_DRAW);
},

Expand All @@ -57,7 +60,7 @@
gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, 0, 0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3);
}
};

Expand Down
88 changes: 88 additions & 0 deletions src/geo/mercator_coordinate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// @flow

import LngLat from '../geo/lng_lat';
import type {LngLatLike} from '../geo/lng_lat';

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

/**
* A `MercatorCoordinate` object represents a 3 dimensional position in projected web mercator coordinates.
*
* The "world size" used by `MercatorCoordinate` is 1, meaning `MercatorCoordinate(0, 0, 0)` is the north-west
* corner of the mercator world and `MercatorCoordinate(1, 1, 0)` is the south-east corner of the mercator world.
*
* @param {number} x The x component of the position.
* @param {number} y The y component of the position.
* @param {number} z The z component of the position.
* @example
* var nullIsland = new mapboxgl.MercatorCoordinate(0.5, 0.5, 0);
*
* @see [Add a custom style layer](https://www.mapbox.com/mapbox-gl-js/example/custom-style-layer/)
*/
class MercatorCoordinate {
x: number;
y: number;
z: number;

constructor(x: number, y: number, z?: number) {
this.x = +x;
this.y = +y;
this.z = z === undefined ? 0 : +z;
}

/**
* Project a `LngLat` to a `MercatorCoordinate`.
*
* @param {LngLatLike} lngLatLike The location to project.
* @param {number} altitude The altitude in meters of the position.
* @returns {MercatorCoordinate} The projected mercator coordinate.
* @example
* var coord = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 0, lat: 0}, 0);
* coord; // MercatorCoordinate(0.5, 0.5, 0)
*/
static fromLngLat(lngLatLike: LngLatLike, altitude?: number) {
const lngLat = LngLat.convert(lngLatLike);

const x = (180 + lngLat.lng) / 360;
const y = (180 - (180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lngLat.lat * Math.PI / 360)))) / 360;
const z = altitude === undefined ? 0 : (altitude / circumferenceAtLatitude(lngLat.lat));
return new MercatorCoordinate(x, y, z);

}

/**
* Returns the `LatLng` for the coordinate.
*
* @returns {LngLat} The `LngLat` object.
* @example
* var coord = new mapboxglMercatorCoordinate(0.5, 0.5, 0);
* var latLng = coord.toLatLng(); // LngLat(0, 0)
*/
toLngLat(){
const lng = this.x * 360 - 180;
const y2 = 180 - this.y * 360;
const lat = 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
return new LngLat(lng, lat);
}

/**
* Returns the altitude in meters of the coordinate.
*
* @returns {number} The altitude in meters.
* @example
* var coord = new mapboxgl.MercatorCoordinate(0, 0, 0.02);
* coord.toAltitude(); // 6914.281956295339
*/
toAltitude() {
const lat = this.toLngLat().lat;
return this.z * circumferenceAtLatitude(lat);
}
}

export default MercatorCoordinate;
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Style from './style/style';
import LngLat from './geo/lng_lat';
import LngLatBounds from './geo/lng_lat_bounds';
import Point from '@mapbox/point-geometry';
import MercatorCoordinate from './geo/mercator_coordinate';
import {Evented} from './util/evented';
import config from './util/config';
import {setRTLTextPlugin} from './source/rtl_text_plugin';
Expand All @@ -37,6 +38,7 @@ const exported = {
LngLat,
LngLatBounds,
Point,
MercatorCoordinate,
Evented,
config,

Expand Down
8 changes: 5 additions & 3 deletions src/style/style_layer/custom_style_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array<number>) =>
* @name prerender
* @param {WebGLRenderingContext} gl The map's gl context.
* @param {Array<number>} matrix The map's camera matrix. It projects spherical mercator
* coordinates to gl coordinates. The spherical mercator coordinate `[0, 0]` represents the
* coordinates to gl coordinates. The mercator coordinate `[0, 0]` represents the
* top left corner of the mercator world and `[1, 1]` represents the bottom right corner. When
* the `renderingMode` is `"3d"`, the z coordinate is conformal. A box with identical x, y, and z
* lengths in mercator units would be rendered as a cube.
* lengths in mercator units would be rendered as a cube. {@link MercatorCoordinate}.fromLatLng
* can be used to project a `LngLat` to a mercator coordinate.
*/

/**
Expand All @@ -131,7 +132,8 @@ type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array<number>) =>
* coordinates to gl coordinates. The spherical mercator coordinate `[0, 0]` represents the
* top left corner of the mercator world and `[1, 1]` represents the bottom right corner. When
* the `renderingMode` is `"3d"`, the z coordinate is conformal. A box with identical x, y, and z
* lengths in mercator units would be rendered as a cube.
* lengths in mercator units would be rendered as a cube. {@link MercatorCoordinate}.fromLatLng
* can be used to project a `LngLat` to a mercator coordinate.
*/
export type CustomLayerInterface = {
id: string,
Expand Down
31 changes: 31 additions & 0 deletions test/unit/geo/mercator_coordinate.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { test } from 'mapbox-gl-js-test';
import LngLat from '../../../src/geo/lng_lat';
import MercatorCoordinate from '../../../src/geo/mercator_coordinate';

test('LngLat', (t) => {
t.test('#constructor', (t) => {
t.ok(new MercatorCoordinate(0, 0) instanceof MercatorCoordinate, 'creates an object');
t.ok(new MercatorCoordinate(0, 0, 0) instanceof MercatorCoordinate, 'creates an object with altitude');
t.end();
});

t.test('#fromLngLat', (t) => {
const nullIsland = new LngLat(0, 0);
t.deepEqual(MercatorCoordinate.fromLngLat(nullIsland), { x: 0.5, y: 0.5, z: 0 });
t.end();
});

t.test('#toLngLat', (t) => {
const dc = new LngLat(-77, 39);
t.deepEqual(MercatorCoordinate.fromLngLat(dc, 500).toLngLat(), { lng: -77, lat: 39 });
t.end();
});

t.test('#toAltitude', (t) => {
const dc = new LngLat(-77, 39);
t.equal(MercatorCoordinate.fromLngLat(dc, 500).toAltitude(), 500);
t.end();
});

t.end();
});

0 comments on commit 33d6089

Please sign in to comment.