@math.gl/web-mercator
Advanced tools
Comparing version 3.2.0-alpha.2 to 3.2.0-alpha.3
@@ -9,3 +9,3 @@ /** | ||
*/ | ||
type Padding = { | ||
export type Padding = { | ||
top: number; | ||
@@ -30,6 +30,6 @@ bottom: number; | ||
*/ | ||
type FitBoundsOptions = { | ||
export type FitBoundsOptions = { | ||
width: number; | ||
height: number; | ||
bounds: number; | ||
bounds: [[number, number], [number, number]]; | ||
minExtent?: number; // 0.01 would be about 1000 meters (degree is ~110KM) | ||
@@ -42,3 +42,3 @@ maxZoom?: number; // ~x4,000,000 => About 10 meter extents | ||
type Bounds = { | ||
export type Bounds = { | ||
longitude: number; | ||
@@ -45,0 +45,0 @@ latitude: number; |
@@ -5,2 +5,3 @@ // Classic web-mercator-project | ||
export {default as getBounds} from './get-bounds'; | ||
export {default as fitBounds} from './fit-bounds'; | ||
@@ -7,0 +8,0 @@ export {default as normalizeViewportProps} from './normalize-viewport-props'; |
@@ -10,2 +10,8 @@ "use strict"; | ||
}); | ||
Object.defineProperty(exports, "default", { | ||
enumerable: true, | ||
get: function get() { | ||
return _webMercatorViewport["default"]; | ||
} | ||
}); | ||
Object.defineProperty(exports, "WebMercatorViewport", { | ||
@@ -17,2 +23,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "getBounds", { | ||
enumerable: true, | ||
get: function get() { | ||
return _getBounds["default"]; | ||
} | ||
}); | ||
Object.defineProperty(exports, "fitBounds", { | ||
@@ -117,2 +129,4 @@ enumerable: true, | ||
var _getBounds = _interopRequireDefault(require("./get-bounds")); | ||
var _fitBounds = _interopRequireDefault(require("./fit-bounds")); | ||
@@ -119,0 +133,0 @@ |
@@ -173,4 +173,4 @@ "use strict"; | ||
var halfFov = Math.atan(0.5 / altitude); | ||
var topHalfSurfaceDistance = Math.sin(halfFov) * altitude / Math.sin(Math.PI / 2 - pitchRadians - halfFov); | ||
var farZ = Math.cos(Math.PI / 2 - pitchRadians) * topHalfSurfaceDistance + altitude; | ||
var topHalfSurfaceDistance = Math.sin(halfFov) * altitude / Math.sin(Math.min(Math.max(Math.PI / 2 - pitchRadians - halfFov, 0.01), Math.PI - 0.01)); | ||
var farZ = Math.sin(pitchRadians) * topHalfSurfaceDistance + altitude; | ||
return { | ||
@@ -177,0 +177,0 @@ fov: 2 * halfFov, |
@@ -1,2 +0,2 @@ | ||
import Viewport from './viewport'; | ||
import { Bounds, FitBoundsOptions } from "./fit-bounds"; | ||
@@ -23,3 +23,3 @@ type WebMercatorViewportOptions = { | ||
*/ | ||
export default class WebMercatorViewport extends Viewport { | ||
export default class WebMercatorViewport { | ||
latitude: number; | ||
@@ -34,2 +34,16 @@ longitude: number; | ||
width: number; | ||
height: number; | ||
scale: number; | ||
distanceScales: { | ||
unitsPerMeter: number[] | ||
}; | ||
viewMatrix: number[]; | ||
projectionMatrix: number[]; | ||
viewProjectionMatrix: number[]; | ||
pixelProjectionMatrix: number[]; | ||
pixelUnprojectionMatrix: number[]; | ||
/** | ||
@@ -66,3 +80,37 @@ * @classdesc | ||
/** Two viewports are equal if width and height are identical, and if | ||
* their view and projection matrices are (approximately) equal. | ||
*/ | ||
equals(viewport: WebMercatorViewport | null): boolean; | ||
/** | ||
* Projects xyz (possibly latitude and longitude) to pixel coordinates in window | ||
* using viewport projection parameters | ||
* - [longitude, latitude] to [x, y] | ||
* - [longitude, latitude, Z] => [x, y, z] | ||
* Note: By default, returns top-left coordinates for canvas/SVG type render | ||
* | ||
* @param lngLatZ - [lng, lat] or [lng, lat, Z] | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @return - screen coordinates [x, y] or [x, y, z], z as pixel depth | ||
*/ | ||
project(lngLatZ: number[], options?: {topLeft?: boolean}): number[]; | ||
/** | ||
* Unproject pixel coordinates on screen onto world coordinates, possibly `[lon, lat]` on map. | ||
* | ||
* - [x, y] => [lng, lat] | ||
* - [x, y, z] => [lng, lat, Z] | ||
* | ||
* @param xyz - screen coordinates, z as pixel depth | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @param options.targetZ=0 - If pixel depth is unknown, targetZ is used as | ||
* the elevation plane to unproject onto | ||
* @return - [lng, lat, Z] or [X, Y, Z] | ||
*/ | ||
unproject(xyz: number[], options?: {topLeft?: boolean; targetZ?: number}): number[]; | ||
/** | ||
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile. | ||
@@ -101,3 +149,3 @@ * Performs the nonlinear part of the web mercator projection. | ||
*/ | ||
getMapCenterByLngLatPosition({lngLat, pos}): number[]; | ||
getMapCenterByLngLatPosition({lngLat, pos}: {lngLat: number[], pos: number[]}): number[]; | ||
@@ -117,3 +165,22 @@ /** @deprecated Legacy method name */ | ||
*/ | ||
fitBounds(bounds, options?: {padding?: number; offset?: [number, number]}): WebMercatorViewport; | ||
fitBounds( | ||
bounds: [[number, number], [number, number]], | ||
options?: Omit<FitBoundsOptions, 'width' | 'height' | 'bounds'>, | ||
): WebMercatorViewport; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} bounds - [[lon, lat], [lon, lat]] | ||
*/ | ||
getBounds(options?: {z?: number}) : Array<number[]>; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} an array of 4 points that define the visible region | ||
*/ | ||
getBoundingRegion(options?: {z?: number}) : Array<number[]>; | ||
} |
@@ -12,2 +12,6 @@ "use strict"; | ||
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); | ||
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
@@ -17,24 +21,16 @@ | ||
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); | ||
var _mathUtils = require("./math-utils"); | ||
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); | ||
var _webMercatorUtils = require("./web-mercator-utils"); | ||
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); | ||
var _fitBounds3 = _interopRequireDefault(require("./fit-bounds")); | ||
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); | ||
var _getBounds = _interopRequireDefault(require("./get-bounds")); | ||
var _viewport = _interopRequireDefault(require("./viewport")); | ||
var mat4 = _interopRequireWildcard(require("gl-matrix/mat4")); | ||
var _webMercatorUtils = require("./web-mercator-utils"); | ||
var _fitBounds3 = _interopRequireDefault(require("./fit-bounds")); | ||
var vec2 = _interopRequireWildcard(require("gl-matrix/vec2")); | ||
var WebMercatorViewport = function (_Viewport) { | ||
(0, _inherits2["default"])(WebMercatorViewport, _Viewport); | ||
var WebMercatorViewport = function () { | ||
function WebMercatorViewport() { | ||
var _this; | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { | ||
@@ -70,3 +66,3 @@ width: 1, | ||
center[2] = 0; | ||
var projectionMatrix = (0, _webMercatorUtils.getProjectionMatrix)({ | ||
this.projectionMatrix = (0, _webMercatorUtils.getProjectionMatrix)({ | ||
width: width, | ||
@@ -79,3 +75,3 @@ height: height, | ||
}); | ||
var viewMatrix = (0, _webMercatorUtils.getViewMatrix)({ | ||
this.viewMatrix = (0, _webMercatorUtils.getViewMatrix)({ | ||
height: height, | ||
@@ -88,22 +84,127 @@ scale: scale, | ||
}); | ||
_this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(WebMercatorViewport).call(this, { | ||
width: width, | ||
height: height, | ||
scale: scale, | ||
viewMatrix: viewMatrix, | ||
projectionMatrix: projectionMatrix | ||
})); | ||
_this.latitude = latitude; | ||
_this.longitude = longitude; | ||
_this.zoom = zoom; | ||
_this.pitch = pitch; | ||
_this.bearing = bearing; | ||
_this.altitude = altitude; | ||
_this.center = center; | ||
_this.unitsPerMeter = (0, _webMercatorUtils.getDistanceScales)((0, _assertThisInitialized2["default"])(_this)).unitsPerMeter[2]; | ||
Object.freeze((0, _assertThisInitialized2["default"])(_this)); | ||
return _this; | ||
this.width = width; | ||
this.height = height; | ||
this.scale = scale; | ||
this.latitude = latitude; | ||
this.longitude = longitude; | ||
this.zoom = zoom; | ||
this.pitch = pitch; | ||
this.bearing = bearing; | ||
this.altitude = altitude; | ||
this.center = center; | ||
this.distanceScales = (0, _webMercatorUtils.getDistanceScales)(this); | ||
this._initMatrices(); | ||
this.equals = this.equals.bind(this); | ||
this.project = this.project.bind(this); | ||
this.unproject = this.unproject.bind(this); | ||
this.projectPosition = this.projectPosition.bind(this); | ||
this.unprojectPosition = this.unprojectPosition.bind(this); | ||
Object.freeze(this); | ||
} | ||
(0, _createClass2["default"])(WebMercatorViewport, [{ | ||
key: "_initMatrices", | ||
value: function _initMatrices() { | ||
var width = this.width, | ||
height = this.height, | ||
projectionMatrix = this.projectionMatrix, | ||
viewMatrix = this.viewMatrix; | ||
var vpm = (0, _mathUtils.createMat4)(); | ||
mat4.multiply(vpm, vpm, projectionMatrix); | ||
mat4.multiply(vpm, vpm, viewMatrix); | ||
this.viewProjectionMatrix = vpm; | ||
var m = (0, _mathUtils.createMat4)(); | ||
mat4.scale(m, m, [width / 2, -height / 2, 1]); | ||
mat4.translate(m, m, [1, -1, 0]); | ||
mat4.multiply(m, m, vpm); | ||
var mInverse = mat4.invert((0, _mathUtils.createMat4)(), m); | ||
if (!mInverse) { | ||
throw new Error('Pixel project matrix not invertible'); | ||
} | ||
this.pixelProjectionMatrix = m; | ||
this.pixelUnprojectionMatrix = mInverse; | ||
} | ||
}, { | ||
key: "equals", | ||
value: function equals(viewport) { | ||
if (!(viewport instanceof WebMercatorViewport)) { | ||
return false; | ||
} | ||
return viewport.width === this.width && viewport.height === this.height && mat4.equals(viewport.projectionMatrix, this.projectionMatrix) && mat4.equals(viewport.viewMatrix, this.viewMatrix); | ||
} | ||
}, { | ||
key: "project", | ||
value: function project(xyz) { | ||
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref2$topLeft = _ref2.topLeft, | ||
topLeft = _ref2$topLeft === void 0 ? true : _ref2$topLeft; | ||
var worldPosition = this.projectPosition(xyz); | ||
var coord = (0, _webMercatorUtils.worldToPixels)(worldPosition, this.pixelProjectionMatrix); | ||
var _coord = (0, _slicedToArray2["default"])(coord, 2), | ||
x = _coord[0], | ||
y = _coord[1]; | ||
var y2 = topLeft ? y : this.height - y; | ||
return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]]; | ||
} | ||
}, { | ||
key: "unproject", | ||
value: function unproject(xyz) { | ||
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref3$topLeft = _ref3.topLeft, | ||
topLeft = _ref3$topLeft === void 0 ? true : _ref3$topLeft, | ||
_ref3$targetZ = _ref3.targetZ, | ||
targetZ = _ref3$targetZ === void 0 ? undefined : _ref3$targetZ; | ||
var _xyz = (0, _slicedToArray2["default"])(xyz, 3), | ||
x = _xyz[0], | ||
y = _xyz[1], | ||
z = _xyz[2]; | ||
var y2 = topLeft ? y : this.height - y; | ||
var targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2]; | ||
var coord = (0, _webMercatorUtils.pixelsToWorld)([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld); | ||
var _this$unprojectPositi = this.unprojectPosition(coord), | ||
_this$unprojectPositi2 = (0, _slicedToArray2["default"])(_this$unprojectPositi, 3), | ||
X = _this$unprojectPositi2[0], | ||
Y = _this$unprojectPositi2[1], | ||
Z = _this$unprojectPositi2[2]; | ||
if (Number.isFinite(z)) { | ||
return [X, Y, Z]; | ||
} | ||
return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y]; | ||
} | ||
}, { | ||
key: "projectPosition", | ||
value: function projectPosition(xyz) { | ||
var _lngLatToWorld = (0, _webMercatorUtils.lngLatToWorld)(xyz), | ||
_lngLatToWorld2 = (0, _slicedToArray2["default"])(_lngLatToWorld, 2), | ||
X = _lngLatToWorld2[0], | ||
Y = _lngLatToWorld2[1]; | ||
var Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2]; | ||
return [X, Y, Z]; | ||
} | ||
}, { | ||
key: "unprojectPosition", | ||
value: function unprojectPosition(xyz) { | ||
var _worldToLngLat = (0, _webMercatorUtils.worldToLngLat)(xyz), | ||
_worldToLngLat2 = (0, _slicedToArray2["default"])(_worldToLngLat, 2), | ||
X = _worldToLngLat2[0], | ||
Y = _worldToLngLat2[1]; | ||
var Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2]; | ||
return [X, Y, Z]; | ||
} | ||
}, { | ||
key: "projectFlat", | ||
@@ -120,5 +221,5 @@ value: function projectFlat(lngLat) { | ||
key: "getMapCenterByLngLatPosition", | ||
value: function getMapCenterByLngLatPosition(_ref2) { | ||
var lngLat = _ref2.lngLat, | ||
pos = _ref2.pos; | ||
value: function getMapCenterByLngLatPosition(_ref4) { | ||
var lngLat = _ref4.lngLat, | ||
pos = _ref4.pos; | ||
var fromLocation = (0, _webMercatorUtils.pixelsToWorld)(pos, this.pixelUnprojectionMatrix); | ||
@@ -132,5 +233,5 @@ var toLocation = (0, _webMercatorUtils.lngLatToWorld)(lngLat); | ||
key: "getLocationAtPoint", | ||
value: function getLocationAtPoint(_ref3) { | ||
var lngLat = _ref3.lngLat, | ||
pos = _ref3.pos; | ||
value: function getLocationAtPoint(_ref5) { | ||
var lngLat = _ref5.lngLat, | ||
pos = _ref5.pos; | ||
return this.getMapCenterByLngLatPosition({ | ||
@@ -165,7 +266,31 @@ lngLat: lngLat, | ||
} | ||
}, { | ||
key: "getBounds", | ||
value: function getBounds(options) { | ||
var corners = this.getBoundingRegion(options); | ||
var west = Math.min.apply(Math, (0, _toConsumableArray2["default"])(corners.map(function (p) { | ||
return p[0]; | ||
}))); | ||
var east = Math.max.apply(Math, (0, _toConsumableArray2["default"])(corners.map(function (p) { | ||
return p[0]; | ||
}))); | ||
var south = Math.min.apply(Math, (0, _toConsumableArray2["default"])(corners.map(function (p) { | ||
return p[1]; | ||
}))); | ||
var north = Math.max.apply(Math, (0, _toConsumableArray2["default"])(corners.map(function (p) { | ||
return p[1]; | ||
}))); | ||
return [[west, south], [east, north]]; | ||
} | ||
}, { | ||
key: "getBoundingRegion", | ||
value: function getBoundingRegion() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return (0, _getBounds["default"])(this, options.z || 0); | ||
} | ||
}]); | ||
return WebMercatorViewport; | ||
}(_viewport["default"]); | ||
}(); | ||
exports["default"] = WebMercatorViewport; | ||
//# sourceMappingURL=web-mercator-viewport.js.map |
@@ -9,3 +9,3 @@ /** | ||
*/ | ||
type Padding = { | ||
export type Padding = { | ||
top: number; | ||
@@ -30,6 +30,6 @@ bottom: number; | ||
*/ | ||
type FitBoundsOptions = { | ||
export type FitBoundsOptions = { | ||
width: number; | ||
height: number; | ||
bounds: number; | ||
bounds: [[number, number], [number, number]]; | ||
minExtent?: number; // 0.01 would be about 1000 meters (degree is ~110KM) | ||
@@ -42,3 +42,3 @@ maxZoom?: number; // ~x4,000,000 => About 10 meter extents | ||
type Bounds = { | ||
export type Bounds = { | ||
longitude: number; | ||
@@ -45,0 +45,0 @@ latitude: number; |
@@ -5,2 +5,3 @@ // Classic web-mercator-project | ||
export {default as getBounds} from './get-bounds'; | ||
export {default as fitBounds} from './fit-bounds'; | ||
@@ -7,0 +8,0 @@ export {default as normalizeViewportProps} from './normalize-viewport-props'; |
@@ -0,2 +1,4 @@ | ||
export { default } from './web-mercator-viewport'; | ||
export { default as WebMercatorViewport } from './web-mercator-viewport'; | ||
export { default as getBounds } from './get-bounds'; | ||
export { default as fitBounds } from './fit-bounds'; | ||
@@ -3,0 +5,0 @@ export { default as normalizeViewportProps } from './normalize-viewport-props'; |
@@ -117,4 +117,4 @@ import { createMat4, transformVector } from './math-utils'; | ||
const halfFov = Math.atan(0.5 / altitude); | ||
const topHalfSurfaceDistance = Math.sin(halfFov) * altitude / Math.sin(Math.PI / 2 - pitchRadians - halfFov); | ||
const farZ = Math.cos(Math.PI / 2 - pitchRadians) * topHalfSurfaceDistance + altitude; | ||
const topHalfSurfaceDistance = Math.sin(halfFov) * altitude / Math.sin(Math.min(Math.max(Math.PI / 2 - pitchRadians - halfFov, 0.01), Math.PI - 0.01)); | ||
const farZ = Math.sin(pitchRadians) * topHalfSurfaceDistance + altitude; | ||
return { | ||
@@ -121,0 +121,0 @@ fov: 2 * halfFov, |
@@ -1,2 +0,2 @@ | ||
import Viewport from './viewport'; | ||
import { Bounds, FitBoundsOptions } from "./fit-bounds"; | ||
@@ -23,3 +23,3 @@ type WebMercatorViewportOptions = { | ||
*/ | ||
export default class WebMercatorViewport extends Viewport { | ||
export default class WebMercatorViewport { | ||
latitude: number; | ||
@@ -34,2 +34,16 @@ longitude: number; | ||
width: number; | ||
height: number; | ||
scale: number; | ||
distanceScales: { | ||
unitsPerMeter: number[] | ||
}; | ||
viewMatrix: number[]; | ||
projectionMatrix: number[]; | ||
viewProjectionMatrix: number[]; | ||
pixelProjectionMatrix: number[]; | ||
pixelUnprojectionMatrix: number[]; | ||
/** | ||
@@ -66,3 +80,37 @@ * @classdesc | ||
/** Two viewports are equal if width and height are identical, and if | ||
* their view and projection matrices are (approximately) equal. | ||
*/ | ||
equals(viewport: WebMercatorViewport | null): boolean; | ||
/** | ||
* Projects xyz (possibly latitude and longitude) to pixel coordinates in window | ||
* using viewport projection parameters | ||
* - [longitude, latitude] to [x, y] | ||
* - [longitude, latitude, Z] => [x, y, z] | ||
* Note: By default, returns top-left coordinates for canvas/SVG type render | ||
* | ||
* @param lngLatZ - [lng, lat] or [lng, lat, Z] | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @return - screen coordinates [x, y] or [x, y, z], z as pixel depth | ||
*/ | ||
project(lngLatZ: number[], options?: {topLeft?: boolean}): number[]; | ||
/** | ||
* Unproject pixel coordinates on screen onto world coordinates, possibly `[lon, lat]` on map. | ||
* | ||
* - [x, y] => [lng, lat] | ||
* - [x, y, z] => [lng, lat, Z] | ||
* | ||
* @param xyz - screen coordinates, z as pixel depth | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @param options.targetZ=0 - If pixel depth is unknown, targetZ is used as | ||
* the elevation plane to unproject onto | ||
* @return - [lng, lat, Z] or [X, Y, Z] | ||
*/ | ||
unproject(xyz: number[], options?: {topLeft?: boolean; targetZ?: number}): number[]; | ||
/** | ||
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile. | ||
@@ -101,3 +149,3 @@ * Performs the nonlinear part of the web mercator projection. | ||
*/ | ||
getMapCenterByLngLatPosition({lngLat, pos}): number[]; | ||
getMapCenterByLngLatPosition({lngLat, pos}: {lngLat: number[], pos: number[]}): number[]; | ||
@@ -117,3 +165,22 @@ /** @deprecated Legacy method name */ | ||
*/ | ||
fitBounds(bounds, options?: {padding?: number; offset?: [number, number]}): WebMercatorViewport; | ||
fitBounds( | ||
bounds: [[number, number], [number, number]], | ||
options?: Omit<FitBoundsOptions, 'width' | 'height' | 'bounds'>, | ||
): WebMercatorViewport; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} bounds - [[lon, lat], [lon, lat]] | ||
*/ | ||
getBounds(options?: {z?: number}) : Array<number[]>; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} an array of 4 points that define the visible region | ||
*/ | ||
getBoundingRegion(options?: {z?: number}) : Array<number[]>; | ||
} |
@@ -1,6 +0,8 @@ | ||
import Viewport from './viewport'; | ||
import { zoomToScale, pixelsToWorld, lngLatToWorld, worldToLngLat, getProjectionMatrix, getDistanceScales, getViewMatrix } from './web-mercator-utils'; | ||
import { createMat4 } from './math-utils'; | ||
import { zoomToScale, pixelsToWorld, lngLatToWorld, worldToLngLat, worldToPixels, getProjectionMatrix, getDistanceScales, getViewMatrix } from './web-mercator-utils'; | ||
import fitBounds from './fit-bounds'; | ||
import getBounds from './get-bounds'; | ||
import * as mat4 from 'gl-matrix/mat4'; | ||
import * as vec2 from 'gl-matrix/vec2'; | ||
export default class WebMercatorViewport extends Viewport { | ||
export default class WebMercatorViewport { | ||
constructor({ | ||
@@ -27,3 +29,3 @@ width, | ||
center[2] = 0; | ||
const projectionMatrix = getProjectionMatrix({ | ||
this.projectionMatrix = getProjectionMatrix({ | ||
width, | ||
@@ -36,3 +38,3 @@ height, | ||
}); | ||
const viewMatrix = getViewMatrix({ | ||
this.viewMatrix = getViewMatrix({ | ||
height, | ||
@@ -45,9 +47,5 @@ scale, | ||
}); | ||
super({ | ||
width, | ||
height, | ||
scale, | ||
viewMatrix, | ||
projectionMatrix | ||
}); | ||
this.width = width; | ||
this.height = height; | ||
this.scale = scale; | ||
this.latitude = latitude; | ||
@@ -60,6 +58,86 @@ this.longitude = longitude; | ||
this.center = center; | ||
this.unitsPerMeter = getDistanceScales(this).unitsPerMeter[2]; | ||
this.distanceScales = getDistanceScales(this); | ||
this._initMatrices(); | ||
this.equals = this.equals.bind(this); | ||
this.project = this.project.bind(this); | ||
this.unproject = this.unproject.bind(this); | ||
this.projectPosition = this.projectPosition.bind(this); | ||
this.unprojectPosition = this.unprojectPosition.bind(this); | ||
Object.freeze(this); | ||
} | ||
_initMatrices() { | ||
const { | ||
width, | ||
height, | ||
projectionMatrix, | ||
viewMatrix | ||
} = this; | ||
const vpm = createMat4(); | ||
mat4.multiply(vpm, vpm, projectionMatrix); | ||
mat4.multiply(vpm, vpm, viewMatrix); | ||
this.viewProjectionMatrix = vpm; | ||
const m = createMat4(); | ||
mat4.scale(m, m, [width / 2, -height / 2, 1]); | ||
mat4.translate(m, m, [1, -1, 0]); | ||
mat4.multiply(m, m, vpm); | ||
const mInverse = mat4.invert(createMat4(), m); | ||
if (!mInverse) { | ||
throw new Error('Pixel project matrix not invertible'); | ||
} | ||
this.pixelProjectionMatrix = m; | ||
this.pixelUnprojectionMatrix = mInverse; | ||
} | ||
equals(viewport) { | ||
if (!(viewport instanceof WebMercatorViewport)) { | ||
return false; | ||
} | ||
return viewport.width === this.width && viewport.height === this.height && mat4.equals(viewport.projectionMatrix, this.projectionMatrix) && mat4.equals(viewport.viewMatrix, this.viewMatrix); | ||
} | ||
project(xyz, { | ||
topLeft = true | ||
} = {}) { | ||
const worldPosition = this.projectPosition(xyz); | ||
const coord = worldToPixels(worldPosition, this.pixelProjectionMatrix); | ||
const [x, y] = coord; | ||
const y2 = topLeft ? y : this.height - y; | ||
return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]]; | ||
} | ||
unproject(xyz, { | ||
topLeft = true, | ||
targetZ = undefined | ||
} = {}) { | ||
const [x, y, z] = xyz; | ||
const y2 = topLeft ? y : this.height - y; | ||
const targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2]; | ||
const coord = pixelsToWorld([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld); | ||
const [X, Y, Z] = this.unprojectPosition(coord); | ||
if (Number.isFinite(z)) { | ||
return [X, Y, Z]; | ||
} | ||
return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y]; | ||
} | ||
projectPosition(xyz) { | ||
const [X, Y] = lngLatToWorld(xyz); | ||
const Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2]; | ||
return [X, Y, Z]; | ||
} | ||
unprojectPosition(xyz) { | ||
const [X, Y] = worldToLngLat(xyz); | ||
const Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2]; | ||
return [X, Y, Z]; | ||
} | ||
projectFlat(lngLat) { | ||
@@ -117,3 +195,16 @@ return lngLatToWorld(lngLat); | ||
getBounds(options) { | ||
const corners = this.getBoundingRegion(options); | ||
const west = Math.min(...corners.map(p => p[0])); | ||
const east = Math.max(...corners.map(p => p[0])); | ||
const south = Math.min(...corners.map(p => p[1])); | ||
const north = Math.max(...corners.map(p => p[1])); | ||
return [[west, south], [east, north]]; | ||
} | ||
getBoundingRegion(options = {}) { | ||
return getBounds(this, options.z || 0); | ||
} | ||
} | ||
//# sourceMappingURL=web-mercator-viewport.js.map |
@@ -9,3 +9,3 @@ /** | ||
*/ | ||
type Padding = { | ||
export type Padding = { | ||
top: number; | ||
@@ -30,6 +30,6 @@ bottom: number; | ||
*/ | ||
type FitBoundsOptions = { | ||
export type FitBoundsOptions = { | ||
width: number; | ||
height: number; | ||
bounds: number; | ||
bounds: [[number, number], [number, number]]; | ||
minExtent?: number; // 0.01 would be about 1000 meters (degree is ~110KM) | ||
@@ -42,3 +42,3 @@ maxZoom?: number; // ~x4,000,000 => About 10 meter extents | ||
type Bounds = { | ||
export type Bounds = { | ||
longitude: number; | ||
@@ -45,0 +45,0 @@ latitude: number; |
@@ -5,2 +5,3 @@ // Classic web-mercator-project | ||
export {default as getBounds} from './get-bounds'; | ||
export {default as fitBounds} from './fit-bounds'; | ||
@@ -7,0 +8,0 @@ export {default as normalizeViewportProps} from './normalize-viewport-props'; |
@@ -0,2 +1,4 @@ | ||
export { default } from './web-mercator-viewport'; | ||
export { default as WebMercatorViewport } from './web-mercator-viewport'; | ||
export { default as getBounds } from './get-bounds'; | ||
export { default as fitBounds } from './fit-bounds'; | ||
@@ -3,0 +5,0 @@ export { default as normalizeViewportProps } from './normalize-viewport-props'; |
@@ -136,4 +136,4 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; | ||
var halfFov = Math.atan(0.5 / altitude); | ||
var topHalfSurfaceDistance = Math.sin(halfFov) * altitude / Math.sin(Math.PI / 2 - pitchRadians - halfFov); | ||
var farZ = Math.cos(Math.PI / 2 - pitchRadians) * topHalfSurfaceDistance + altitude; | ||
var topHalfSurfaceDistance = Math.sin(halfFov) * altitude / Math.sin(Math.min(Math.max(Math.PI / 2 - pitchRadians - halfFov, 0.01), Math.PI - 0.01)); | ||
var farZ = Math.sin(pitchRadians) * topHalfSurfaceDistance + altitude; | ||
return { | ||
@@ -140,0 +140,0 @@ fov: 2 * halfFov, |
@@ -1,2 +0,2 @@ | ||
import Viewport from './viewport'; | ||
import { Bounds, FitBoundsOptions } from "./fit-bounds"; | ||
@@ -23,3 +23,3 @@ type WebMercatorViewportOptions = { | ||
*/ | ||
export default class WebMercatorViewport extends Viewport { | ||
export default class WebMercatorViewport { | ||
latitude: number; | ||
@@ -34,2 +34,16 @@ longitude: number; | ||
width: number; | ||
height: number; | ||
scale: number; | ||
distanceScales: { | ||
unitsPerMeter: number[] | ||
}; | ||
viewMatrix: number[]; | ||
projectionMatrix: number[]; | ||
viewProjectionMatrix: number[]; | ||
pixelProjectionMatrix: number[]; | ||
pixelUnprojectionMatrix: number[]; | ||
/** | ||
@@ -66,3 +80,37 @@ * @classdesc | ||
/** Two viewports are equal if width and height are identical, and if | ||
* their view and projection matrices are (approximately) equal. | ||
*/ | ||
equals(viewport: WebMercatorViewport | null): boolean; | ||
/** | ||
* Projects xyz (possibly latitude and longitude) to pixel coordinates in window | ||
* using viewport projection parameters | ||
* - [longitude, latitude] to [x, y] | ||
* - [longitude, latitude, Z] => [x, y, z] | ||
* Note: By default, returns top-left coordinates for canvas/SVG type render | ||
* | ||
* @param lngLatZ - [lng, lat] or [lng, lat, Z] | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @return - screen coordinates [x, y] or [x, y, z], z as pixel depth | ||
*/ | ||
project(lngLatZ: number[], options?: {topLeft?: boolean}): number[]; | ||
/** | ||
* Unproject pixel coordinates on screen onto world coordinates, possibly `[lon, lat]` on map. | ||
* | ||
* - [x, y] => [lng, lat] | ||
* - [x, y, z] => [lng, lat, Z] | ||
* | ||
* @param xyz - screen coordinates, z as pixel depth | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @param options.targetZ=0 - If pixel depth is unknown, targetZ is used as | ||
* the elevation plane to unproject onto | ||
* @return - [lng, lat, Z] or [X, Y, Z] | ||
*/ | ||
unproject(xyz: number[], options?: {topLeft?: boolean; targetZ?: number}): number[]; | ||
/** | ||
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile. | ||
@@ -101,3 +149,3 @@ * Performs the nonlinear part of the web mercator projection. | ||
*/ | ||
getMapCenterByLngLatPosition({lngLat, pos}): number[]; | ||
getMapCenterByLngLatPosition({lngLat, pos}: {lngLat: number[], pos: number[]}): number[]; | ||
@@ -117,3 +165,22 @@ /** @deprecated Legacy method name */ | ||
*/ | ||
fitBounds(bounds, options?: {padding?: number; offset?: [number, number]}): WebMercatorViewport; | ||
fitBounds( | ||
bounds: [[number, number], [number, number]], | ||
options?: Omit<FitBoundsOptions, 'width' | 'height' | 'bounds'>, | ||
): WebMercatorViewport; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} bounds - [[lon, lat], [lon, lat]] | ||
*/ | ||
getBounds(options?: {z?: number}) : Array<number[]>; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} an array of 4 points that define the visible region | ||
*/ | ||
getBoundingRegion(options?: {z?: number}) : Array<number[]>; | ||
} |
@@ -0,18 +1,14 @@ | ||
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; | ||
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; | ||
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/esm/createClass"; | ||
import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn"; | ||
import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf"; | ||
import _assertThisInitialized from "@babel/runtime/helpers/esm/assertThisInitialized"; | ||
import _inherits from "@babel/runtime/helpers/esm/inherits"; | ||
import Viewport from './viewport'; | ||
import { zoomToScale, pixelsToWorld, lngLatToWorld, worldToLngLat, getProjectionMatrix, getDistanceScales, getViewMatrix } from './web-mercator-utils'; | ||
import { createMat4 } from './math-utils'; | ||
import { zoomToScale, pixelsToWorld, lngLatToWorld, worldToLngLat, worldToPixels, getProjectionMatrix, getDistanceScales, getViewMatrix } from './web-mercator-utils'; | ||
import _fitBounds from './fit-bounds'; | ||
import getBounds from './get-bounds'; | ||
import * as mat4 from 'gl-matrix/mat4'; | ||
import * as vec2 from 'gl-matrix/vec2'; | ||
var WebMercatorViewport = function (_Viewport) { | ||
_inherits(WebMercatorViewport, _Viewport); | ||
var WebMercatorViewport = function () { | ||
function WebMercatorViewport() { | ||
var _this; | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { | ||
@@ -49,3 +45,3 @@ width: 1, | ||
center[2] = 0; | ||
var projectionMatrix = getProjectionMatrix({ | ||
this.projectionMatrix = getProjectionMatrix({ | ||
width: width, | ||
@@ -58,3 +54,3 @@ height: height, | ||
}); | ||
var viewMatrix = getViewMatrix({ | ||
this.viewMatrix = getViewMatrix({ | ||
height: height, | ||
@@ -67,22 +63,127 @@ scale: scale, | ||
}); | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(WebMercatorViewport).call(this, { | ||
width: width, | ||
height: height, | ||
scale: scale, | ||
viewMatrix: viewMatrix, | ||
projectionMatrix: projectionMatrix | ||
})); | ||
_this.latitude = latitude; | ||
_this.longitude = longitude; | ||
_this.zoom = zoom; | ||
_this.pitch = pitch; | ||
_this.bearing = bearing; | ||
_this.altitude = altitude; | ||
_this.center = center; | ||
_this.unitsPerMeter = getDistanceScales(_assertThisInitialized(_this)).unitsPerMeter[2]; | ||
Object.freeze(_assertThisInitialized(_this)); | ||
return _this; | ||
this.width = width; | ||
this.height = height; | ||
this.scale = scale; | ||
this.latitude = latitude; | ||
this.longitude = longitude; | ||
this.zoom = zoom; | ||
this.pitch = pitch; | ||
this.bearing = bearing; | ||
this.altitude = altitude; | ||
this.center = center; | ||
this.distanceScales = getDistanceScales(this); | ||
this._initMatrices(); | ||
this.equals = this.equals.bind(this); | ||
this.project = this.project.bind(this); | ||
this.unproject = this.unproject.bind(this); | ||
this.projectPosition = this.projectPosition.bind(this); | ||
this.unprojectPosition = this.unprojectPosition.bind(this); | ||
Object.freeze(this); | ||
} | ||
_createClass(WebMercatorViewport, [{ | ||
key: "_initMatrices", | ||
value: function _initMatrices() { | ||
var width = this.width, | ||
height = this.height, | ||
projectionMatrix = this.projectionMatrix, | ||
viewMatrix = this.viewMatrix; | ||
var vpm = createMat4(); | ||
mat4.multiply(vpm, vpm, projectionMatrix); | ||
mat4.multiply(vpm, vpm, viewMatrix); | ||
this.viewProjectionMatrix = vpm; | ||
var m = createMat4(); | ||
mat4.scale(m, m, [width / 2, -height / 2, 1]); | ||
mat4.translate(m, m, [1, -1, 0]); | ||
mat4.multiply(m, m, vpm); | ||
var mInverse = mat4.invert(createMat4(), m); | ||
if (!mInverse) { | ||
throw new Error('Pixel project matrix not invertible'); | ||
} | ||
this.pixelProjectionMatrix = m; | ||
this.pixelUnprojectionMatrix = mInverse; | ||
} | ||
}, { | ||
key: "equals", | ||
value: function equals(viewport) { | ||
if (!(viewport instanceof WebMercatorViewport)) { | ||
return false; | ||
} | ||
return viewport.width === this.width && viewport.height === this.height && mat4.equals(viewport.projectionMatrix, this.projectionMatrix) && mat4.equals(viewport.viewMatrix, this.viewMatrix); | ||
} | ||
}, { | ||
key: "project", | ||
value: function project(xyz) { | ||
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref2$topLeft = _ref2.topLeft, | ||
topLeft = _ref2$topLeft === void 0 ? true : _ref2$topLeft; | ||
var worldPosition = this.projectPosition(xyz); | ||
var coord = worldToPixels(worldPosition, this.pixelProjectionMatrix); | ||
var _coord = _slicedToArray(coord, 2), | ||
x = _coord[0], | ||
y = _coord[1]; | ||
var y2 = topLeft ? y : this.height - y; | ||
return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]]; | ||
} | ||
}, { | ||
key: "unproject", | ||
value: function unproject(xyz) { | ||
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref3$topLeft = _ref3.topLeft, | ||
topLeft = _ref3$topLeft === void 0 ? true : _ref3$topLeft, | ||
_ref3$targetZ = _ref3.targetZ, | ||
targetZ = _ref3$targetZ === void 0 ? undefined : _ref3$targetZ; | ||
var _xyz = _slicedToArray(xyz, 3), | ||
x = _xyz[0], | ||
y = _xyz[1], | ||
z = _xyz[2]; | ||
var y2 = topLeft ? y : this.height - y; | ||
var targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2]; | ||
var coord = pixelsToWorld([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld); | ||
var _this$unprojectPositi = this.unprojectPosition(coord), | ||
_this$unprojectPositi2 = _slicedToArray(_this$unprojectPositi, 3), | ||
X = _this$unprojectPositi2[0], | ||
Y = _this$unprojectPositi2[1], | ||
Z = _this$unprojectPositi2[2]; | ||
if (Number.isFinite(z)) { | ||
return [X, Y, Z]; | ||
} | ||
return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y]; | ||
} | ||
}, { | ||
key: "projectPosition", | ||
value: function projectPosition(xyz) { | ||
var _lngLatToWorld = lngLatToWorld(xyz), | ||
_lngLatToWorld2 = _slicedToArray(_lngLatToWorld, 2), | ||
X = _lngLatToWorld2[0], | ||
Y = _lngLatToWorld2[1]; | ||
var Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2]; | ||
return [X, Y, Z]; | ||
} | ||
}, { | ||
key: "unprojectPosition", | ||
value: function unprojectPosition(xyz) { | ||
var _worldToLngLat = worldToLngLat(xyz), | ||
_worldToLngLat2 = _slicedToArray(_worldToLngLat, 2), | ||
X = _worldToLngLat2[0], | ||
Y = _worldToLngLat2[1]; | ||
var Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2]; | ||
return [X, Y, Z]; | ||
} | ||
}, { | ||
key: "projectFlat", | ||
@@ -99,5 +200,5 @@ value: function projectFlat(lngLat) { | ||
key: "getMapCenterByLngLatPosition", | ||
value: function getMapCenterByLngLatPosition(_ref2) { | ||
var lngLat = _ref2.lngLat, | ||
pos = _ref2.pos; | ||
value: function getMapCenterByLngLatPosition(_ref4) { | ||
var lngLat = _ref4.lngLat, | ||
pos = _ref4.pos; | ||
var fromLocation = pixelsToWorld(pos, this.pixelUnprojectionMatrix); | ||
@@ -111,5 +212,5 @@ var toLocation = lngLatToWorld(lngLat); | ||
key: "getLocationAtPoint", | ||
value: function getLocationAtPoint(_ref3) { | ||
var lngLat = _ref3.lngLat, | ||
pos = _ref3.pos; | ||
value: function getLocationAtPoint(_ref5) { | ||
var lngLat = _ref5.lngLat, | ||
pos = _ref5.pos; | ||
return this.getMapCenterByLngLatPosition({ | ||
@@ -144,8 +245,32 @@ lngLat: lngLat, | ||
} | ||
}, { | ||
key: "getBounds", | ||
value: function getBounds(options) { | ||
var corners = this.getBoundingRegion(options); | ||
var west = Math.min.apply(Math, _toConsumableArray(corners.map(function (p) { | ||
return p[0]; | ||
}))); | ||
var east = Math.max.apply(Math, _toConsumableArray(corners.map(function (p) { | ||
return p[0]; | ||
}))); | ||
var south = Math.min.apply(Math, _toConsumableArray(corners.map(function (p) { | ||
return p[1]; | ||
}))); | ||
var north = Math.max.apply(Math, _toConsumableArray(corners.map(function (p) { | ||
return p[1]; | ||
}))); | ||
return [[west, south], [east, north]]; | ||
} | ||
}, { | ||
key: "getBoundingRegion", | ||
value: function getBoundingRegion() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return getBounds(this, options.z || 0); | ||
} | ||
}]); | ||
return WebMercatorViewport; | ||
}(Viewport); | ||
}(); | ||
export { WebMercatorViewport as default }; | ||
//# sourceMappingURL=web-mercator-viewport.js.map |
@@ -6,3 +6,3 @@ { | ||
"license": "MIT", | ||
"version": "3.2.0-alpha.2", | ||
"version": "3.2.0-alpha.3", | ||
"keywords": [ | ||
@@ -38,3 +38,3 @@ "webgl", | ||
}, | ||
"gitHead": "127b2196b775281db912472e43f579a201000c88" | ||
"gitHead": "d4dbf43e20c7d51108bcdb52a3bd2ee6846d05a7" | ||
} |
@@ -9,3 +9,3 @@ /** | ||
*/ | ||
type Padding = { | ||
export type Padding = { | ||
top: number; | ||
@@ -30,6 +30,6 @@ bottom: number; | ||
*/ | ||
type FitBoundsOptions = { | ||
export type FitBoundsOptions = { | ||
width: number; | ||
height: number; | ||
bounds: number; | ||
bounds: [[number, number], [number, number]]; | ||
minExtent?: number; // 0.01 would be about 1000 meters (degree is ~110KM) | ||
@@ -42,3 +42,3 @@ maxZoom?: number; // ~x4,000,000 => About 10 meter extents | ||
type Bounds = { | ||
export type Bounds = { | ||
longitude: number; | ||
@@ -45,0 +45,0 @@ latitude: number; |
@@ -5,2 +5,3 @@ // Classic web-mercator-project | ||
export {default as getBounds} from './get-bounds'; | ||
export {default as fitBounds} from './fit-bounds'; | ||
@@ -7,0 +8,0 @@ export {default as normalizeViewportProps} from './normalize-viewport-props'; |
// Classic web-mercator-project | ||
export {default} from './web-mercator-viewport'; | ||
export {default as WebMercatorViewport} from './web-mercator-viewport'; | ||
export {default as getBounds} from './get-bounds'; | ||
export {default as fitBounds} from './fit-bounds'; | ||
@@ -5,0 +7,0 @@ export {default as normalizeViewportProps} from './normalize-viewport-props'; |
@@ -207,6 +207,7 @@ // TODO - THE UTILITIES IN THIS FILE SHOULD BE IMPORTED FROM WEB-MERCATOR-VIEWPORT MODULE | ||
const topHalfSurfaceDistance = | ||
(Math.sin(halfFov) * altitude) / Math.sin(Math.PI / 2 - pitchRadians - halfFov); | ||
(Math.sin(halfFov) * altitude) / | ||
Math.sin(Math.min(Math.max(Math.PI / 2 - pitchRadians - halfFov, 0.01), Math.PI - 0.01)); | ||
// Calculate z value of the farthest fragment that should be rendered. | ||
const farZ = Math.cos(Math.PI / 2 - pitchRadians) * topHalfSurfaceDistance + altitude; | ||
const farZ = Math.sin(pitchRadians) * topHalfSurfaceDistance + altitude; | ||
@@ -213,0 +214,0 @@ return { |
@@ -1,2 +0,2 @@ | ||
import Viewport from './viewport'; | ||
import { Bounds, FitBoundsOptions } from "./fit-bounds"; | ||
@@ -23,3 +23,3 @@ type WebMercatorViewportOptions = { | ||
*/ | ||
export default class WebMercatorViewport extends Viewport { | ||
export default class WebMercatorViewport { | ||
latitude: number; | ||
@@ -34,2 +34,16 @@ longitude: number; | ||
width: number; | ||
height: number; | ||
scale: number; | ||
distanceScales: { | ||
unitsPerMeter: number[] | ||
}; | ||
viewMatrix: number[]; | ||
projectionMatrix: number[]; | ||
viewProjectionMatrix: number[]; | ||
pixelProjectionMatrix: number[]; | ||
pixelUnprojectionMatrix: number[]; | ||
/** | ||
@@ -66,3 +80,37 @@ * @classdesc | ||
/** Two viewports are equal if width and height are identical, and if | ||
* their view and projection matrices are (approximately) equal. | ||
*/ | ||
equals(viewport: WebMercatorViewport | null): boolean; | ||
/** | ||
* Projects xyz (possibly latitude and longitude) to pixel coordinates in window | ||
* using viewport projection parameters | ||
* - [longitude, latitude] to [x, y] | ||
* - [longitude, latitude, Z] => [x, y, z] | ||
* Note: By default, returns top-left coordinates for canvas/SVG type render | ||
* | ||
* @param lngLatZ - [lng, lat] or [lng, lat, Z] | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @return - screen coordinates [x, y] or [x, y, z], z as pixel depth | ||
*/ | ||
project(lngLatZ: number[], options?: {topLeft?: boolean}): number[]; | ||
/** | ||
* Unproject pixel coordinates on screen onto world coordinates, possibly `[lon, lat]` on map. | ||
* | ||
* - [x, y] => [lng, lat] | ||
* - [x, y, z] => [lng, lat, Z] | ||
* | ||
* @param xyz - screen coordinates, z as pixel depth | ||
* @param options - options | ||
* @param options.topLeft=true - Whether projected coords are top left | ||
* @param options.targetZ=0 - If pixel depth is unknown, targetZ is used as | ||
* the elevation plane to unproject onto | ||
* @return - [lng, lat, Z] or [X, Y, Z] | ||
*/ | ||
unproject(xyz: number[], options?: {topLeft?: boolean; targetZ?: number}): number[]; | ||
/** | ||
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile. | ||
@@ -101,3 +149,3 @@ * Performs the nonlinear part of the web mercator projection. | ||
*/ | ||
getMapCenterByLngLatPosition({lngLat, pos}): number[]; | ||
getMapCenterByLngLatPosition({lngLat, pos}: {lngLat: number[], pos: number[]}): number[]; | ||
@@ -117,3 +165,22 @@ /** @deprecated Legacy method name */ | ||
*/ | ||
fitBounds(bounds, options?: {padding?: number; offset?: [number, number]}): WebMercatorViewport; | ||
fitBounds( | ||
bounds: [[number, number], [number, number]], | ||
options?: Omit<FitBoundsOptions, 'width' | 'height' | 'bounds'>, | ||
): WebMercatorViewport; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} bounds - [[lon, lat], [lon, lat]] | ||
*/ | ||
getBounds(options?: {z?: number}) : Array<number[]>; | ||
/** | ||
* Returns the bounding box of the viewport. | ||
* @param {Object} [options] | ||
* @param {Number} [options.z] - The altitude at which the bounds should be calculated. | ||
* @returns {Array} an array of 4 points that define the visible region | ||
*/ | ||
getBoundingRegion(options?: {z?: number}) : Array<number[]>; | ||
} |
// View and Projection Matrix calculations for mapbox-js style map view properties | ||
import Viewport from './viewport'; | ||
import {createMat4} from './math-utils'; | ||
@@ -9,2 +9,3 @@ import { | ||
worldToLngLat, | ||
worldToPixels, | ||
getProjectionMatrix, | ||
@@ -15,6 +16,9 @@ getDistanceScales, | ||
import fitBounds from './fit-bounds'; | ||
import getBounds from './get-bounds'; | ||
import * as mat4 from 'gl-matrix/mat4'; | ||
import * as vec2 from 'gl-matrix/vec2'; | ||
export default class WebMercatorViewport extends Viewport { | ||
export default class WebMercatorViewport { | ||
// eslint-disable-next-line max-statements | ||
constructor( | ||
@@ -47,3 +51,3 @@ { | ||
const projectionMatrix = getProjectionMatrix({ | ||
this.projectionMatrix = getProjectionMatrix({ | ||
width, | ||
@@ -57,3 +61,3 @@ height, | ||
const viewMatrix = getViewMatrix({ | ||
this.viewMatrix = getViewMatrix({ | ||
height, | ||
@@ -67,5 +71,7 @@ scale, | ||
super({width, height, scale, viewMatrix, projectionMatrix}); | ||
// Save parameters | ||
this.width = width; | ||
this.height = height; | ||
this.scale = scale; | ||
// Save parameters | ||
this.latitude = latitude; | ||
@@ -77,9 +83,111 @@ this.longitude = longitude; | ||
this.altitude = altitude; | ||
this.center = center; | ||
this.unitsPerMeter = getDistanceScales(this).unitsPerMeter[2]; | ||
this.distanceScales = getDistanceScales(this); | ||
this._initMatrices(); | ||
// Bind methods for easy access | ||
this.equals = this.equals.bind(this); | ||
this.project = this.project.bind(this); | ||
this.unproject = this.unproject.bind(this); | ||
this.projectPosition = this.projectPosition.bind(this); | ||
this.unprojectPosition = this.unprojectPosition.bind(this); | ||
Object.freeze(this); | ||
} | ||
_initMatrices() { | ||
const {width, height, projectionMatrix, viewMatrix} = this; | ||
// Note: As usual, matrix operations should be applied in "reverse" order | ||
// since vectors will be multiplied in from the right during transformation | ||
const vpm = createMat4(); | ||
mat4.multiply(vpm, vpm, projectionMatrix); | ||
mat4.multiply(vpm, vpm, viewMatrix); | ||
this.viewProjectionMatrix = vpm; | ||
// Calculate matrices and scales needed for projection | ||
/** | ||
* Builds matrices that converts preprojected lngLats to screen pixels | ||
* and vice versa. | ||
* Note: Currently returns bottom-left coordinates! | ||
* Note: Starts with the GL projection matrix and adds steps to the | ||
* scale and translate that matrix onto the window. | ||
* Note: WebGL controls clip space to screen projection with gl.viewport | ||
* and does not need this step. | ||
*/ | ||
const m = createMat4(); | ||
// matrix for conversion from location to screen coordinates | ||
mat4.scale(m, m, [width / 2, -height / 2, 1]); | ||
mat4.translate(m, m, [1, -1, 0]); | ||
mat4.multiply(m, m, vpm); | ||
const mInverse = mat4.invert(createMat4(), m); | ||
if (!mInverse) { | ||
throw new Error('Pixel project matrix not invertible'); | ||
} | ||
this.pixelProjectionMatrix = m; | ||
this.pixelUnprojectionMatrix = mInverse; | ||
} | ||
// Two viewports are equal if width and height are identical, and if | ||
// their view and projection matrices are (approximately) equal. | ||
equals(viewport) { | ||
if (!(viewport instanceof WebMercatorViewport)) { | ||
return false; | ||
} | ||
return ( | ||
viewport.width === this.width && | ||
viewport.height === this.height && | ||
mat4.equals(viewport.projectionMatrix, this.projectionMatrix) && | ||
mat4.equals(viewport.viewMatrix, this.viewMatrix) | ||
); | ||
} | ||
// Projects xyz (possibly latitude and longitude) to pixel coordinates in window | ||
// using viewport projection parameters | ||
project(xyz, {topLeft = true} = {}) { | ||
const worldPosition = this.projectPosition(xyz); | ||
const coord = worldToPixels(worldPosition, this.pixelProjectionMatrix); | ||
const [x, y] = coord; | ||
const y2 = topLeft ? y : this.height - y; | ||
return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]]; | ||
} | ||
// Unproject pixel coordinates on screen onto world coordinates, | ||
// (possibly [lon, lat]) on map. | ||
unproject(xyz, {topLeft = true, targetZ = undefined} = {}) { | ||
const [x, y, z] = xyz; | ||
const y2 = topLeft ? y : this.height - y; | ||
const targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2]; | ||
const coord = pixelsToWorld([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld); | ||
const [X, Y, Z] = this.unprojectPosition(coord); | ||
if (Number.isFinite(z)) { | ||
return [X, Y, Z]; | ||
} | ||
return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y]; | ||
} | ||
// NON_LINEAR PROJECTION HOOKS | ||
// Used for web meractor projection | ||
projectPosition(xyz) { | ||
const [X, Y] = lngLatToWorld(xyz); | ||
const Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2]; | ||
return [X, Y, Z]; | ||
} | ||
unprojectPosition(xyz) { | ||
const [X, Y] = worldToLngLat(xyz); | ||
const Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2]; | ||
return [X, Y, Z]; | ||
} | ||
// Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile. | ||
@@ -117,2 +225,16 @@ projectFlat(lngLat) { | ||
} | ||
getBounds(options) { | ||
const corners = this.getBoundingRegion(options); | ||
const west = Math.min(...corners.map(p => p[0])); | ||
const east = Math.max(...corners.map(p => p[0])); | ||
const south = Math.min(...corners.map(p => p[1])); | ||
const north = Math.max(...corners.map(p => p[1])); | ||
return [[west, south], [east, north]]; | ||
} | ||
getBoundingRegion(options = {}) { | ||
return getBounds(this, options.z || 0); | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
361915
4754