Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project module: support pre-projected positions #4140

Merged
merged 2 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions modules/core/src/shaderlib/project/project.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,18 @@ vec2 project_mercator_(vec2 lnglat) {
// Projects positions (defined by project_uCoordinateSystem) to common space (defined by project_uProjectionMode)
//
vec4 project_position(vec4 position, vec3 position64Low) {
vec4 position_world = project_uModelMatrix * position;

if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT && project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR) {
return project_uModelMatrix * vec4(
project_mercator_(position.xy) * WORLD_SCALE,
project_size(position.z),
position.w
return vec4(
project_mercator_(position_world.xy) * WORLD_SCALE,
project_size(position_world.z),
position_world.w
);
}

vec4 position_world = project_uModelMatrix * position;

if (project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT && project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET) {
if (project_uProjectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET &&
(project_uCoordinateSystem == COORDINATE_SYSTEM_LNGLAT ||
project_uCoordinateSystem == COORDINATE_SYSTEM_CARTESIAN)) {
// Subtract high part of 64 bit value. Convert remainder to float32, preserving precision.
position_world.xyz -= project_uCoordinateOrigin;
position_world.xyz += position64Low;
Expand Down
110 changes: 79 additions & 31 deletions modules/core/src/shaderlib/project/viewport-uniforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/* eslint-disable complexity */

import * as mat4 from 'gl-matrix/mat4';
import * as vec4 from 'gl-matrix/vec4';
Expand All @@ -42,23 +43,58 @@ const getMemoizedViewportUniforms = memoize(calculateViewportUniforms);
// The code that utilizes Matrix4 does the same calculation as their mat4 counterparts,
// has lower performance but provides error checking.
// Uncomment when debugging
function calculateMatrixAndOffset({viewport, coordinateSystem, coordinateOrigin}) {
function calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin) {
const {viewMatrixUncentered, projectionMatrix, projectionMode} = viewport;
let {viewMatrix, viewProjectionMatrix} = viewport;

let projectionCenter = ZERO_VECTOR;
let cameraPosCommon = viewport.cameraPosition;
let shaderCoordinateOrigin = coordinateOrigin;
let geospatialOrigin;
let offsetMode = true;

if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT) {
if (projectionMode === PROJECTION_MODE.WEB_MERCATOR) {
if (
coordinateSystem === COORDINATE_SYSTEM.LNGLAT_OFFSETS ||
coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS
) {
geospatialOrigin = coordinateOrigin;
} else {
geospatialOrigin = viewport.isGeospatial
? [Math.fround(viewport.longitude), Math.fround(viewport.latitude), 0]
: null;
}

switch (projectionMode) {
case PROJECTION_MODE.WEB_MERCATOR:
if (
coordinateSystem === COORDINATE_SYSTEM.LNGLAT ||
coordinateSystem === COORDINATE_SYSTEM.CARTESIAN
) {
offsetMode = false;
}
break;

case PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET:
if (coordinateSystem === COORDINATE_SYSTEM.LNGLAT) {
// viewport center in world space
shaderCoordinateOrigin = geospatialOrigin;
} else if (coordinateSystem === COORDINATE_SYSTEM.CARTESIAN) {
// viewport center in common space
shaderCoordinateOrigin = [
Math.fround(viewport.center[0]),
Math.fround(viewport.center[1]),
0
];
}
break;

case PROJECTION_MODE.IDENTITY:
shaderCoordinateOrigin = viewport.position.map(Math.fround);
break;

default:
// Unknown projection mode
offsetMode = false;
} else {
shaderCoordinateOrigin = [Math.fround(viewport.longitude), Math.fround(viewport.latitude), 0];
}
} else if (projectionMode === PROJECTION_MODE.IDENTITY) {
shaderCoordinateOrigin = viewport.position.map(Math.fround);
}

shaderCoordinateOrigin[2] = shaderCoordinateOrigin[2] || 0;
Expand All @@ -67,7 +103,9 @@ function calculateMatrixAndOffset({viewport, coordinateSystem, coordinateOrigin}
// Calculate transformed projectionCenter (using 64 bit precision JS)
// This is the key to offset mode precision
// (avoids doing this addition in 32 bit precision in GLSL)
const positionCommonSpace = viewport.projectPosition(shaderCoordinateOrigin);
const positionCommonSpace = viewport.projectPosition(
geospatialOrigin || shaderCoordinateOrigin
);

cameraPosCommon = [
cameraPosCommon[0] - positionCommonSpace[0],
Expand Down Expand Up @@ -96,7 +134,8 @@ function calculateMatrixAndOffset({viewport, coordinateSystem, coordinateOrigin}
viewProjectionMatrix,
projectionCenter,
cameraPosCommon,
shaderCoordinateOrigin
shaderCoordinateOrigin,
geospatialOrigin
};
}

Expand Down Expand Up @@ -152,12 +191,9 @@ function calculateViewportUniforms({
projectionCenter,
viewProjectionMatrix,
cameraPosCommon,
shaderCoordinateOrigin
} = calculateMatrixAndOffset({
coordinateSystem,
coordinateOrigin,
viewport
});
shaderCoordinateOrigin,
geospatialOrigin
} = calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin);

// Calculate projection pixels per unit
const distanceScales = viewport.getDistanceScales();
Expand Down Expand Up @@ -189,21 +225,33 @@ function calculateViewportUniforms({
project_uCameraPosition: cameraPosCommon
};

const distanceScalesAtOrigin = viewport.getDistanceScales(shaderCoordinateOrigin);
switch (coordinateSystem) {
case COORDINATE_SYSTEM.METER_OFFSETS:
uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerMeter;
uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerMeter2;
break;

case COORDINATE_SYSTEM.LNGLAT:
case COORDINATE_SYSTEM.LNGLAT_OFFSETS:
uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerDegree;
uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerDegree2;
break;

default:
break;
if (geospatialOrigin) {
const distanceScalesAtOrigin = viewport.getDistanceScales(geospatialOrigin);
switch (coordinateSystem) {
case COORDINATE_SYSTEM.METER_OFFSETS:
uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerMeter;
uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerMeter2;
break;

case COORDINATE_SYSTEM.LNGLAT:
case COORDINATE_SYSTEM.LNGLAT_OFFSETS:
uniforms.project_uCommonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerDegree;
uniforms.project_uCommonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerDegree2;
break;

// a.k.a "preprojected" positions
case COORDINATE_SYSTEM.CARTESIAN:
uniforms.project_uCommonUnitsPerWorldUnit = [1, 1, distanceScalesAtOrigin.unitsPerMeter[2]];
uniforms.project_uCommonUnitsPerWorldUnit2 = [
0,
0,
distanceScalesAtOrigin.unitsPerMeter2[2]
];
break;

default:
break;
}
}

return uniforms;
Expand Down
2 changes: 2 additions & 0 deletions test/apps/mapbox-tile/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, {PureComponent} from 'react';
import {render} from 'react-dom';
import DeckGL from '@deck.gl/react';
import {TileLayer} from '@deck.gl/geo-layers';
import {COORDINATE_SYSTEM} from '@deck.gl/core';

import {decodeTile} from './utils/decode';

Expand Down Expand Up @@ -95,6 +96,7 @@ class Root extends PureComponent {
layers={[
new TileLayer({
...MAP_LAYER_STYLES,
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
getPolygonOffset: null,
pickable: true,
getTileData
Expand Down
19 changes: 7 additions & 12 deletions test/apps/mapbox-tile/utils/decode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ import Protobuf from 'pbf';
import {VectorTile} from '@mapbox/vector-tile';
import {vectorTileFeatureToGeoJSON} from './feature';

const PI = Math.PI;
const PI_4 = PI / 4;
const RADIANS_TO_DEGREES_2 = 360 / PI;
const TILE_SIZE = 512;

export function decodeTile(x, y, z, arrayBuffer) {
const tile = new VectorTile(new Protobuf(arrayBuffer));

const result = [];

const scale = Math.pow(2, z);
const projX = x / scale;
const projY = y / scale;
const projX = (x * TILE_SIZE) / scale;
const projY = (y * TILE_SIZE) / scale;

const projectFunc = project.bind(null, projX, projY, scale);

Expand All @@ -32,15 +30,12 @@ export function decodeTile(x, y, z, arrayBuffer) {
}

function project(x, y, scale, line, extent) {
const pixelToCommon = 1 / extent / scale;
const pixelToCommon = TILE_SIZE / extent / scale;

for (let i = 0; i < line.length; i++) {
const p = line[i];
// common space
const cx = x + p[0] * pixelToCommon;
const cy = y + p[1] * pixelToCommon;
// LNGLAT
p[0] = cx * 360 - 180;
p[1] = (Math.atan(Math.exp(PI - cy * 2 * PI)) - PI_4) * RADIANS_TO_DEGREES_2;
// convert to deck.gl common space
p[0] = x + p[0] * pixelToCommon;
p[1] = TILE_SIZE - y - p[1] * pixelToCommon;
}
}
32 changes: 32 additions & 0 deletions test/modules/core/shaderlib/project/viewport-uniforms.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ const UNIFORMS_64 = {
project64_uScale: Number
};

const EPSILON = 1e-4;

function getUniformsError(uniforms, formats) {
for (const name in UNIFORMS) {
const value = uniforms[name];
Expand All @@ -106,8 +108,21 @@ test('project#getUniforms', t => {
t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated');
t.deepEqual(uniforms.project_uCenter, [0, 0, 0, 0], 'Returned zero projection center');

uniforms = project.getUniforms({
viewport: TEST_VIEWPORTS.map,
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN
});
t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated');
t.deepEqual(uniforms.project_uCenter, [0, 0, 0, 0], 'Returned zero projection center');

uniforms = project.getUniforms({viewport: TEST_VIEWPORTS.mapHighZoom});
t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated');
t.ok(uniforms.project_uCenter.some(x => x), 'Returned non-trivial projection center');
t.ok(
Math.abs(uniforms.project_uCenter[0]) < EPSILON &&
Math.abs(uniforms.project_uCenter[1]) < EPSILON,
'project center at center of clipspace'
);
t.deepEqual(
uniforms.project_uCoordinateOrigin,
[-122.42694091796875, 37.75153732299805, 0],
Expand All @@ -123,6 +138,23 @@ test('project#getUniforms', t => {
t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated');
t.ok(uniforms.project_uCenter.some(x => x), 'Returned non-trivial projection center');

uniforms = project.getUniforms({
viewport: TEST_VIEWPORTS.mapHighZoom,
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN
});
t.notOk(getUniformsError(uniforms, UNIFORMS), 'Uniforms validated');
t.ok(uniforms.project_uCenter.some(x => x), 'Returned non-trivial projection center');
t.ok(
Math.abs(uniforms.project_uCenter[0]) < EPSILON &&
Math.abs(uniforms.project_uCenter[1]) < EPSILON,
'project center at center of clipspace'
);
t.ok(
uniforms.project_uCommonUnitsPerWorldUnit[0] === 1 &&
uniforms.project_uCommonUnitsPerWorldUnit[1] === 1,
'Returned correct distanceScales'
);

uniforms = project.getUniforms({
viewport: TEST_VIEWPORTS.infoVis,
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN
Expand Down