diff --git a/CHANGELOG.md b/CHANGELOG.md index 2646a4d8ef..fdf5404559 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### ✨ Features and improvements - Catches network fetching errors such as CORS, DNS or malformed URL as actual `AJAXError` to expose HTTP request details to the `"error"` event (https://github.com/maplibre/maplibre-gl-js/pull/4822) +- Add setVerticalFieldOfView() to public API ([#4717](https://github.com/maplibre/maplibre-gl-js/issues/4717)) - _...Add new stuff here..._ ### 🐞 Bug fixes diff --git a/src/geo/transform_helper.ts b/src/geo/transform_helper.ts index bf5d0df754..f7b9418787 100644 --- a/src/geo/transform_helper.ts +++ b/src/geo/transform_helper.ts @@ -1,7 +1,7 @@ import {LngLat} from './lng_lat'; import {LngLatBounds} from './lng_lat_bounds'; import Point from '@mapbox/point-geometry'; -import {wrap, clamp} from '../util/util'; +import {wrap, clamp, degreesToRadians, radiansToDegrees} from '../util/util'; import {mat4, mat2} from 'gl-matrix'; import {EdgeInsets} from './edge_insets'; import type {PaddingOptions} from './edge_insets'; @@ -317,13 +317,13 @@ export class TransformHelper implements ITransformGetters { return this._fovInRadians; } get fov(): number { - return this._fovInRadians / Math.PI * 180; + return radiansToDegrees(this._fovInRadians); } setFov(fov: number) { - fov = Math.max(0.01, Math.min(60, fov)); - if (this._fovInRadians === fov) return; + fov = clamp(fov, 0.1, 150); + if (this.fov === fov) return; this._unmodified = false; - this._fovInRadians = fov / 180 * Math.PI; + this._fovInRadians = degreesToRadians(fov); this._calcMatrices(); } diff --git a/src/ui/camera.test.ts b/src/ui/camera.test.ts index d288ff0189..3ae21ad980 100644 --- a/src/ui/camera.test.ts +++ b/src/ui/camera.test.ts @@ -275,6 +275,11 @@ describe('#jumpTo', () => { expect(camera.getRoll()).toBe(45); }); + test('sets field of view', () => { + camera.setVerticalFieldOfView(29); + expect(camera.getVerticalFieldOfView()).toBeCloseTo(29, 10); + }); + test('sets multiple properties', () => { camera.jumpTo({ center: [10, 20], @@ -318,6 +323,21 @@ describe('#jumpTo', () => { expect(ended).toBe('ok'); }); + test('emits move events when FOV changes, preserving eventData', () => { + let started, moved, ended; + const eventData = {data: 'ok'}; + + camera + .on('movestart', (d) => { started = d.data; }) + .on('move', (d) => { moved = d.data; }) + .on('moveend', (d) => { ended = d.data; }); + + camera.setVerticalFieldOfView(44, eventData); + expect(started).toBe('ok'); + expect(moved).toBe('ok'); + expect(ended).toBe('ok'); + }); + test('emits zoom events, preserving eventData', () => { let started, zoomed, ended; const eventData = {data: 'ok'}; diff --git a/src/ui/camera.ts b/src/ui/camera.ts index ec60a28444..844bfac81c 100644 --- a/src/ui/camera.ts +++ b/src/ui/camera.ts @@ -550,6 +550,42 @@ export abstract class Camera extends Evented { return this; } + /** + * Returns the map's current vertical field of view, in degrees. + * + * @returns The map's current vertical field of view. + * @defaultValue 36.87 + * @example + * ```ts + * const verticalFieldOfView = map.getVerticalFieldOfView(); + * ``` + */ + getVerticalFieldOfView(): number { return this.transform.fov; } + + /** + * Sets the map's vertical field of view, in degrees. + * + * Triggers the following events: `movestart`, `move`, and `moveend`. + * + * @param fov - The vertical field of view to set, in degrees (0-180). + * @param eventData - Additional properties to be added to event objects of events triggered by this method. + * @defaultValue 36.87 + * @example + * Change vertical field of view to 30 degrees + * ```ts + * map.setVerticalFieldOfView(30); + * ``` + */ + setVerticalFieldOfView(fov: number, eventData?: any): this { + if (fov != this.transform.fov) { + this.transform.setFov(fov); + this.fire(new Event('movestart', eventData)) + .fire(new Event('move', eventData)) + .fire(new Event('moveend', eventData)); + } + return this; + } + /** * Returns the map's current bearing. The bearing is the compass direction that is "up"; for example, a bearing * of 90° orients the map so that east is up. diff --git a/test/build/min.test.ts b/test/build/min.test.ts index 712295c7c4..ee5bbc5544 100644 --- a/test/build/min.test.ts +++ b/test/build/min.test.ts @@ -37,7 +37,7 @@ describe('test min build', () => { const decreaseQuota = 4096; // feel free to update this value after you've checked that it has changed on purpose :-) - const expectedBytes = 889777; + const expectedBytes = 890732; expect(actualBytes).toBeLessThan(expectedBytes + increaseQuota); expect(actualBytes).toBeGreaterThan(expectedBytes - decreaseQuota); diff --git a/test/integration/render/tests/field-of-view/default/expected.png b/test/integration/render/tests/field-of-view/default/expected.png new file mode 100644 index 0000000000..3a6e114a2b Binary files /dev/null and b/test/integration/render/tests/field-of-view/default/expected.png differ diff --git a/test/integration/render/tests/field-of-view/default/style.json b/test/integration/render/tests/field-of-view/default/style.json new file mode 100644 index 0000000000..97f91a0b6c --- /dev/null +++ b/test/integration/render/tests/field-of-view/default/style.json @@ -0,0 +1,25 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 512, + "width": 512 + } + }, + "center": [35.372566, 31.556437], + "zoom": 16.25, + "pitch": 30, + "bearing": 22.5, + "sources": { + "repeat": { + "type": "raster", + "tiles": ["local://tiles/white-with-x.png"], + "minzoom": 0, + "maxzoom": 22, + "tileSize": 256 + } + }, + "layers": [ + {"id": "repeat", "type": "raster", "source": "repeat"} + ] +} \ No newline at end of file diff --git a/test/integration/render/tests/field-of-view/field-of-view-10/expected.png b/test/integration/render/tests/field-of-view/field-of-view-10/expected.png new file mode 100644 index 0000000000..8503a8dccb Binary files /dev/null and b/test/integration/render/tests/field-of-view/field-of-view-10/expected.png differ diff --git a/test/integration/render/tests/field-of-view/field-of-view-10/style.json b/test/integration/render/tests/field-of-view/field-of-view-10/style.json new file mode 100644 index 0000000000..00463ae38f --- /dev/null +++ b/test/integration/render/tests/field-of-view/field-of-view-10/style.json @@ -0,0 +1,29 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 512, + "width": 512, + "operations": [ + ["setVerticalFieldOfView", 10], + ["wait"] + ] + } + }, + "center": [35.372566, 31.556437], + "zoom": 16.25, + "pitch": 30, + "bearing": 22.5, + "sources": { + "repeat": { + "type": "raster", + "tiles": ["local://tiles/white-with-x.png"], + "minzoom": 0, + "maxzoom": 22, + "tileSize": 256 + } + }, + "layers": [ + {"id": "repeat", "type": "raster", "source": "repeat"} + ] +} \ No newline at end of file diff --git a/test/integration/render/tests/field-of-view/field-of-view-150/expected.png b/test/integration/render/tests/field-of-view/field-of-view-150/expected.png new file mode 100644 index 0000000000..88d56a6d9a Binary files /dev/null and b/test/integration/render/tests/field-of-view/field-of-view-150/expected.png differ diff --git a/test/integration/render/tests/field-of-view/field-of-view-150/style.json b/test/integration/render/tests/field-of-view/field-of-view-150/style.json new file mode 100644 index 0000000000..071b994bae --- /dev/null +++ b/test/integration/render/tests/field-of-view/field-of-view-150/style.json @@ -0,0 +1,29 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 512, + "width": 512, + "operations": [ + ["setVerticalFieldOfView", 150], + ["wait"] + ] + } + }, + "center": [35.372566, 31.556437], + "zoom": 16.25, + "pitch": 30, + "bearing": 22.5, + "sources": { + "repeat": { + "type": "raster", + "tiles": ["local://tiles/white-with-x.png"], + "minzoom": 0, + "maxzoom": 22, + "tileSize": 256 + } + }, + "layers": [ + {"id": "repeat", "type": "raster", "source": "repeat"} + ] +} \ No newline at end of file diff --git a/test/integration/render/tests/field-of-view/field-of-view-90/expected.png b/test/integration/render/tests/field-of-view/field-of-view-90/expected.png new file mode 100644 index 0000000000..97bd4204f0 Binary files /dev/null and b/test/integration/render/tests/field-of-view/field-of-view-90/expected.png differ diff --git a/test/integration/render/tests/field-of-view/field-of-view-90/style.json b/test/integration/render/tests/field-of-view/field-of-view-90/style.json new file mode 100644 index 0000000000..45e7590abf --- /dev/null +++ b/test/integration/render/tests/field-of-view/field-of-view-90/style.json @@ -0,0 +1,29 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 512, + "width": 512, + "operations": [ + ["setVerticalFieldOfView", 90], + ["wait"] + ] + } + }, + "center": [35.372566, 31.556437], + "zoom": 16.25, + "pitch": 30, + "bearing": 22.5, + "sources": { + "repeat": { + "type": "raster", + "tiles": ["local://tiles/white-with-x.png"], + "minzoom": 0, + "maxzoom": 22, + "tileSize": 256 + } + }, + "layers": [ + {"id": "repeat", "type": "raster", "source": "repeat"} + ] +} \ No newline at end of file