maplibre-gl
Advanced tools
Comparing version 4.5.1 to 4.5.2
{ | ||
"name": "maplibre-gl", | ||
"description": "BSD licensed community fork of mapbox-gl, a WebGL interactive maps library", | ||
"version": "4.5.1", | ||
"version": "4.5.2", | ||
"main": "dist/maplibre-gl.js", | ||
@@ -48,3 +48,3 @@ "style": "dist/maplibre-gl.css", | ||
"devDependencies": { | ||
"autoprefixer": "^10.4.19", | ||
"autoprefixer": "^10.4.20", | ||
"@mapbox/mapbox-gl-rtl-text": "^0.3.0", | ||
@@ -72,3 +72,3 @@ "@mapbox/mvt-fixtures": "^3.10.0", | ||
"@types/nise": "^1.4.5", | ||
"@types/node": "^22.0.2", | ||
"@types/node": "^22.1.0", | ||
"@types/offscreencanvas": "^2019.7.3", | ||
@@ -90,3 +90,3 @@ "@types/pixelmatch": "^5.2.6", | ||
"d3-queue": "^3.0.7", | ||
"devtools-protocol": "^0.0.1335233", | ||
"devtools-protocol": "^0.0.1339468", | ||
"diff": "^5.2.0", | ||
@@ -98,3 +98,3 @@ "dts-bundle-generator": "^9.5.1", | ||
"eslint-plugin-import": "^2.29.1", | ||
"eslint-plugin-jest": "^28.6.0", | ||
"eslint-plugin-jest": "^28.8.0", | ||
"eslint-plugin-react": "^7.35.0", | ||
@@ -111,6 +111,6 @@ "eslint-plugin-tsdoc": "0.3.0", | ||
"jsdom": "^24.1.1", | ||
"junit-report-builder": "^4.0.1", | ||
"junit-report-builder": "^5.0.0", | ||
"minimist": "^1.2.8", | ||
"mock-geolocation": "^1.0.11", | ||
"monocart-coverage-reports": "^2.10.0", | ||
"monocart-coverage-reports": "^2.10.2", | ||
"nise": "^6.0.0", | ||
@@ -122,10 +122,10 @@ "npm-font-open-sans": "^1.1.0", | ||
"pngjs": "^7.0.0", | ||
"postcss": "^8.4.40", | ||
"postcss": "^8.4.41", | ||
"postcss-cli": "^11.0.0", | ||
"postcss-inline-svg": "^6.0.0", | ||
"pretty-bytes": "^6.1.1", | ||
"puppeteer": "^22.15.0", | ||
"puppeteer": "^23.0.2", | ||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1", | ||
"rollup": "^4.19.1", | ||
"rollup": "^4.20.0", | ||
"rollup-plugin-sourcemaps": "^0.6.3", | ||
@@ -139,3 +139,3 @@ "rw": "^1.3.3", | ||
"stylelint-config-standard": "^36.0.1", | ||
"ts-jest": "^29.2.3", | ||
"ts-jest": "^29.2.4", | ||
"ts-node": "^10.9.2", | ||
@@ -142,0 +142,0 @@ "tslib": "^2.6.3", |
@@ -67,2 +67,9 @@ import {clamp} from '../util/util'; | ||
// Enable anisotropic filtering only when the pitch is greater than 20 degrees | ||
// to preserve image sharpness on flat or slightly tilted maps. | ||
if (tile.texture.useMipmap && context.extTextureFilterAnisotropic && painter.transform.pitch > 20) { | ||
gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, | ||
context.extTextureFilterAnisotropicMax); | ||
} | ||
const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord); | ||
@@ -69,0 +76,0 @@ const terrainCoord = terrainData ? coord : null; |
@@ -197,6 +197,2 @@ import {extend, pick} from '../util/util'; | ||
tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST); | ||
if (context.extTextureFilterAnisotropic) { | ||
gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax); | ||
} | ||
} | ||
@@ -203,0 +199,0 @@ tile.state = 'loaded'; |
@@ -961,6 +961,6 @@ import {extend, warnOnce, clamp, wrap, defaultEasing, pick, degreesToRadians} from '../util/util'; | ||
const tr = this._getTransformForUpdate(), | ||
startZoom = this.getZoom(), | ||
startBearing = this.getBearing(), | ||
startPitch = this.getPitch(), | ||
startPadding = this.getPadding(), | ||
startZoom = tr.zoom, | ||
startBearing = tr.bearing, | ||
startPitch = tr.pitch, | ||
startPadding = tr.padding, | ||
@@ -979,3 +979,3 @@ bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing, | ||
); | ||
this._normalizeCenter(center); | ||
this._normalizeCenter(center, tr); | ||
@@ -1096,3 +1096,4 @@ const from = tr.project(locationAtOffset); | ||
* Called when the camera is about to be manipulated. | ||
* If `transformCameraUpdate` is specified, a copy of the current transform is created to track the accumulated changes. | ||
* If `transformCameraUpdate` is specified or terrain is enabled, a copy of | ||
* the current transform is created to track the accumulated changes. | ||
* This underlying transform represents the "desired state" proposed by input handlers / animations / UI controls. | ||
@@ -1103,3 +1104,3 @@ * It may differ from the state used for rendering (`this.transform`). | ||
_getTransformForUpdate(): Transform { | ||
if (!this.transformCameraUpdate) return this.transform; | ||
if (!this.transformCameraUpdate && !this.terrain) return this.transform; | ||
@@ -1114,23 +1115,61 @@ if (!this._requestedCameraState) { | ||
* @internal | ||
* Checks the given transform for the camera being below terrain surface and | ||
* returns new pitch and zoom to fix that. | ||
* | ||
* With the new pitch and zoom, the camera will be at the same ground | ||
* position but at higher altitude. It will still point to the same spot on | ||
* the map. | ||
* | ||
* @param tr - The transform to check. | ||
*/ | ||
_elevateCameraIfInsideTerrain(tr: Transform) : { pitch?: number; zoom?: number } { | ||
const camera = tr.getCameraPosition(); | ||
const minAltitude = this.terrain.getElevationForLngLatZoom(camera.lngLat, tr.zoom); | ||
if (camera.altitude < minAltitude) { | ||
const newCamera = this.calculateCameraOptionsFromTo( | ||
camera.lngLat, minAltitude, tr.center, tr.elevation); | ||
return { | ||
pitch: newCamera.pitch, | ||
zoom: newCamera.zoom, | ||
}; | ||
} | ||
return {}; | ||
} | ||
/** | ||
* @internal | ||
* Called after the camera is done being manipulated. | ||
* @param tr - the requested camera end state | ||
* If the camera is inside terrain, it gets elevated. | ||
* Call `transformCameraUpdate` if present, and then apply the "approved" changes. | ||
*/ | ||
_applyUpdatedTransform(tr: Transform) { | ||
if (!this.transformCameraUpdate) return; | ||
const nextTransform = tr.clone(); | ||
const { | ||
center, | ||
zoom, | ||
pitch, | ||
bearing, | ||
elevation | ||
} = this.transformCameraUpdate(nextTransform); | ||
if (center) nextTransform.center = center; | ||
if (zoom !== undefined) nextTransform.zoom = zoom; | ||
if (pitch !== undefined) nextTransform.pitch = pitch; | ||
if (bearing !== undefined) nextTransform.bearing = bearing; | ||
if (elevation !== undefined) nextTransform.elevation = elevation; | ||
this.transform.apply(nextTransform); | ||
const modifiers : ((tr: Transform) => ReturnType<CameraUpdateTransformFunction>)[] = []; | ||
if (this.terrain) { | ||
modifiers.push(tr => this._elevateCameraIfInsideTerrain(tr)); | ||
} | ||
if (this.transformCameraUpdate) { | ||
modifiers.push(tr => this.transformCameraUpdate(tr)); | ||
} | ||
if (!modifiers.length) { | ||
return; | ||
} | ||
const finalTransform = tr.clone(); | ||
for (const modifier of modifiers) { | ||
const nextTransform = finalTransform.clone(); | ||
const { | ||
center, | ||
zoom, | ||
pitch, | ||
bearing, | ||
elevation | ||
} = modifier(nextTransform); | ||
if (center) nextTransform.center = center; | ||
if (zoom !== undefined) nextTransform.zoom = zoom; | ||
if (pitch !== undefined) nextTransform.pitch = pitch; | ||
if (bearing !== undefined) nextTransform.bearing = bearing; | ||
if (elevation !== undefined) nextTransform.elevation = elevation; | ||
finalTransform.apply(nextTransform); | ||
} | ||
this.transform.apply(finalTransform); | ||
} | ||
@@ -1240,6 +1279,6 @@ | ||
const tr = this._getTransformForUpdate(), | ||
startZoom = this.getZoom(), | ||
startBearing = this.getBearing(), | ||
startPitch = this.getPitch(), | ||
startPadding = this.getPadding(); | ||
startZoom = tr.zoom, | ||
startBearing = tr.bearing, | ||
startPitch = tr.pitch, | ||
startPadding = tr.padding; | ||
@@ -1258,3 +1297,3 @@ const bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing; | ||
); | ||
this._normalizeCenter(center); | ||
this._normalizeCenter(center, tr); | ||
const scale = tr.zoomScale(zoom - startZoom); | ||
@@ -1460,4 +1499,3 @@ | ||
// interpolating between the two endpoints will cross it. | ||
_normalizeCenter(center: LngLat) { | ||
const tr = this.transform; | ||
_normalizeCenter(center: LngLat, tr: Transform) { | ||
if (!tr.renderWorldCopies || tr.lngRange) return; | ||
@@ -1464,0 +1502,0 @@ |
@@ -414,2 +414,9 @@ import {Event} from '../util/evented'; | ||
terrain: MapTerrainEvent; | ||
/** | ||
* Fired whenever the cooperativeGestures option prevents a gesture from being handled by the map. | ||
* This is useful for showing your own UI when this happens. | ||
*/ | ||
cooperativegestureprevented: MapLibreEvent<WheelEvent | TouchEvent> & { | ||
gestureType: 'wheel_zoom' | 'touch_pan'; | ||
}; | ||
}; | ||
@@ -416,0 +423,0 @@ |
@@ -33,8 +33,20 @@ import {browser} from '../../util/browser'; | ||
const cooperativegestureprevented = jest.fn(); | ||
map.on('cooperativegestureprevented', cooperativegestureprevented); | ||
const startZoom = map.getZoom(); | ||
// simulate a single 'wheel' event | ||
simulate.wheel(map.getCanvas(), {type: 'wheel', deltaY: -simulate.magicWheelZoomDelta}); | ||
const wheelEvent = {type: 'wheel', deltaY: -simulate.magicWheelZoomDelta}; | ||
simulate.wheel(map.getCanvas(), wheelEvent); | ||
map._renderTaskQueue.run(); | ||
expect(map.getContainer().querySelector('.maplibregl-cooperative-gesture-screen.maplibregl-show')).toBeInstanceOf(HTMLDivElement); | ||
// ensure events are emitted when cooperative gesture is prevented | ||
expect(cooperativegestureprevented).toHaveBeenCalledTimes(1); | ||
expect(cooperativegestureprevented).toHaveBeenCalledWith(expect.objectContaining({ | ||
type: 'cooperativegestureprevented', | ||
gestureType: 'wheel_zoom', | ||
})); | ||
expect(cooperativegestureprevented.mock.calls[0][0].originalEvent).toMatchObject(wheelEvent); | ||
now += 400; | ||
@@ -141,2 +153,6 @@ browserNow.mockReturnValue(now); | ||
const map = createMap(true); | ||
const cooperativegestureprevented = jest.fn(); | ||
map.on('cooperativegestureprevented', cooperativegestureprevented); | ||
map.scrollZoom.disable(); | ||
@@ -150,2 +166,5 @@ map._renderTaskQueue.run(); | ||
// ensure events are not emitted when cooperative gesture is prevented | ||
expect(cooperativegestureprevented).toHaveBeenCalledTimes(0); | ||
map.remove(); | ||
@@ -162,7 +181,9 @@ }); | ||
const drag = jest.fn(); | ||
const dragend = jest.fn(); | ||
const dragend = jest.fn(); | ||
const cooperativegestureprevented = jest.fn(); | ||
map.on('dragstart', dragstart); | ||
map.on('drag', drag); | ||
map.on('dragend', dragend); | ||
map.on('dragend', dragend); | ||
map.on('cooperativegestureprevented', cooperativegestureprevented); | ||
@@ -177,2 +198,10 @@ simulate.touchstart(target, {touches: [{target, clientX: 0, clientY: 0}]}); | ||
// ensure events are emitted when cooperative gesture is prevented | ||
expect(cooperativegestureprevented).toHaveBeenCalledTimes(1); | ||
expect(cooperativegestureprevented).toHaveBeenCalledWith(expect.objectContaining({ | ||
type: 'cooperativegestureprevented', | ||
gestureType: 'touch_pan', | ||
})); | ||
expect(cooperativegestureprevented.mock.calls[0][0].originalEvent.touches[0].clientX).toBe(10); | ||
simulate.touchend(target); | ||
@@ -190,2 +219,5 @@ map._renderTaskQueue.run(); | ||
const map = createMap(true); | ||
const cooperativegestureprevented = jest.fn(); | ||
map.on('cooperativegestureprevented', cooperativegestureprevented); | ||
map.cooperativeGestures.disable(); | ||
@@ -203,2 +235,3 @@ const target = map.getCanvasContainer(); | ||
expect(map.getContainer().querySelector('.maplibregl-cooperative-gesture-screen.maplibregl-show')).toBeNull(); | ||
expect(cooperativegestureprevented).toHaveBeenCalledTimes(0); | ||
@@ -217,2 +250,6 @@ simulate.touchend(target); | ||
const map = createMap(true); | ||
const cooperativegestureprevented = jest.fn(); | ||
map.on('cooperativegestureprevented', cooperativegestureprevented); | ||
const target = map.getCanvas(); | ||
@@ -230,2 +267,3 @@ const startCenter = map.getCenter(); | ||
expect(map.getContainer().querySelector('.maplibregl-cooperative-gesture-screen.maplibregl-show')).toBeNull(); | ||
expect(cooperativegestureprevented).toHaveBeenCalledTimes(0); | ||
@@ -246,2 +284,6 @@ simulate.touchend(target); | ||
const map = createMap(true); | ||
const cooperativegestureprevented = jest.fn(); | ||
map.on('cooperativegestureprevented', cooperativegestureprevented); | ||
const target = map.getCanvas(); | ||
@@ -258,2 +300,3 @@ const startPitch = map.getPitch(); | ||
expect(map.getContainer().querySelector('.maplibregl-cooperative-gesture-screen.maplibregl-show')).toBeNull(); | ||
expect(cooperativegestureprevented).toHaveBeenCalledTimes(0); | ||
@@ -260,0 +303,0 @@ simulate.touchend(target); |
import {DOM} from '../../util/dom'; | ||
import {Event} from '../../util/evented'; | ||
import {Handler} from '../handler_manager'; | ||
@@ -14,2 +15,4 @@ | ||
* | ||
* When the CooperativeGestureHandler blocks a gesture, it will emit a `cooperativegestureprevented` event. | ||
* | ||
* @group Handlers | ||
@@ -70,3 +73,3 @@ * | ||
_destoryUI() { | ||
_destroyUI() { | ||
if (this._container) { | ||
@@ -87,3 +90,3 @@ DOM.remove(this._container); | ||
this._enabled = false; | ||
this._destoryUI(); | ||
this._destroyUI(); | ||
} | ||
@@ -95,28 +98,12 @@ | ||
touchmove(e: TouchEvent) { | ||
this._onCooperativeGesture(e.touches.length === 1); | ||
isBypassed(event: MouseEvent | WheelEvent | PointerEvent) { | ||
return event[this._bypassKey]; | ||
} | ||
wheel(e: WheelEvent) { | ||
if (!this._map.scrollZoom.isEnabled()) { | ||
return; | ||
} | ||
notifyGestureBlocked(gestureType: 'wheel_zoom' | 'touch_pan', originalEvent: Event) { | ||
if (!this._enabled) return; | ||
const isPrevented = this.shouldPreventWheelEvent(e); | ||
this._onCooperativeGesture(isPrevented); | ||
} | ||
// notify subscribers that a cooperative gesture was prevented | ||
this._map.fire(new Event('cooperativegestureprevented', {gestureType, originalEvent})); | ||
shouldPreventWheelEvent(e: WheelEvent) { | ||
if (!this.isEnabled()) { | ||
return false; | ||
} | ||
const isTrackpadPinch = e.ctrlKey; | ||
const isBypassed = e[this._bypassKey] || isTrackpadPinch; | ||
return !isBypassed; | ||
} | ||
_onCooperativeGesture(showNotification: boolean) { | ||
if (!this._enabled || !showNotification) return; | ||
// Alert user how to scroll/pan | ||
@@ -123,0 +110,0 @@ this._container.classList.add('maplibregl-show'); |
@@ -306,2 +306,3 @@ import {browser} from '../../util/browser'; | ||
const map = createMap(); | ||
map._elevateCameraIfInsideTerrain = (_tr : any) => ({}); | ||
map._renderTaskQueue.run(); | ||
@@ -333,2 +334,3 @@ map.terrain = { | ||
let map = createMap(); | ||
map._elevateCameraIfInsideTerrain = (_tr : any) => ({}); | ||
map._renderTaskQueue.run(); | ||
@@ -358,2 +360,3 @@ map.terrain = { | ||
map = createMap(); | ||
map._elevateCameraIfInsideTerrain = (_tr : any) => ({}); | ||
map._renderTaskQueue.run(); | ||
@@ -360,0 +363,0 @@ map.terrain = { |
@@ -152,5 +152,20 @@ import {DOM} from '../../util/dom'; | ||
/** | ||
* Determines whether or not the gesture is blocked due to cooperativeGestures. | ||
*/ | ||
_shouldBePrevented(e: WheelEvent) { | ||
if (!this._map.cooperativeGestures.isEnabled()) { | ||
return false; | ||
} | ||
const isTrackpadPinch = e.ctrlKey; | ||
const isBypassed = isTrackpadPinch || this._map.cooperativeGestures.isBypassed(e); | ||
return !isBypassed; | ||
} | ||
wheel(e: WheelEvent) { | ||
if (!this.isEnabled()) return; | ||
if (this._map.cooperativeGestures.shouldPreventWheelEvent(e)) { | ||
if (this._shouldBePrevented(e)) { | ||
this._map.cooperativeGestures.notifyGestureBlocked('wheel_zoom', e); | ||
return; | ||
@@ -157,0 +172,0 @@ } |
@@ -32,4 +32,5 @@ import Point from '@mapbox/point-geometry'; | ||
minTouchs() { | ||
return this._map.cooperativeGestures.isEnabled() ? 2 : 1; | ||
_shouldBePrevented(touchesCount: number) { | ||
const minTouches = this._map.cooperativeGestures.isEnabled() ? 2 : 1; | ||
return touchesCount < minTouches; | ||
} | ||
@@ -42,3 +43,7 @@ | ||
touchmove(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) { | ||
if (!this._active || mapTouches.length < this.minTouchs()) return; | ||
if (!this._active) return; | ||
if (this._shouldBePrevented(mapTouches.length)) { | ||
this._map.cooperativeGestures.notifyGestureBlocked('touch_pan', e); | ||
return; | ||
} | ||
e.preventDefault(); | ||
@@ -51,3 +56,3 @@ return this._calculateTransform(e, points, mapTouches); | ||
if (this._active && mapTouches.length < this.minTouchs()) { | ||
if (this._active && this._shouldBePrevented(mapTouches.length)) { | ||
this.reset(); | ||
@@ -83,3 +88,3 @@ } | ||
if (touchDeltaCount < this.minTouchs() || !touchDeltaSum.mag()) return; | ||
if (this._shouldBePrevented(touchDeltaCount) || !touchDeltaSum.mag()) return; | ||
@@ -86,0 +91,0 @@ const panDelta = touchDeltaSum.div(touchDeltaCount); |
@@ -10,4 +10,5 @@ import type {Map} from '../map'; | ||
* Shared utilities for the Handler classes to access the correct camera state. | ||
* If Camera.transformCameraUpdate is specified, the "desired state" of camera may differ from the state used for rendering. | ||
* The handlers need the "desired state" to track accumulated changes. | ||
* If Camera.transformCameraUpdate is specified or terrain is enabled, the | ||
* "desired state" of camera may differ from the state used for rendering. The | ||
* handlers need the "desired state" to track accumulated changes. | ||
*/ | ||
@@ -14,0 +15,0 @@ export class TransformProvider { |
@@ -72,1 +72,34 @@ import {createMap, beforeMapTest} from '../../util/test/util'; | ||
}); | ||
describe('Keep camera outside terrain', () => { | ||
test('Try to move camera into terrain', () => { | ||
const map = createMap(); | ||
let terrainElevation = 10; | ||
const terrainStub = {} as Terrain; | ||
terrainStub.getElevationForLngLatZoom = jest.fn( | ||
(_lngLat: LngLat, _zoom: number) => terrainElevation | ||
); | ||
map.terrain = terrainStub; | ||
// Terrain elevation is 10 everywhere, we are above it at zoom level 15 | ||
// with pitch 45 deg. | ||
map.jumpTo({center: [0.0, 0.0], bearing: 0, pitch: 45, zoom: 15}); | ||
const initialCamPosition = map.transform.getCameraPosition(); | ||
expect(initialCamPosition.altitude).toBeCloseTo(506, 0); | ||
// Now we set the elevation to 5000 everywhere and try to jump to the | ||
// same position. This would lead to a jump into the terrain, which | ||
// must not be possible. | ||
// Camera should be above the terrain, but at the same location as | ||
// before and with decreased pitch. | ||
terrainElevation = 5000; | ||
map.jumpTo({center: [0.0, 0.0], pitch: 45, zoom: 15}); | ||
expect(map.transform.getCameraPosition().lngLat.lng).toBeCloseTo(initialCamPosition.lngLat.lng); | ||
expect(map.transform.getCameraPosition().lngLat.lat).toBeCloseTo(initialCamPosition.lngLat.lat); | ||
expect(map.transform.pitch).toBeLessThan(45); | ||
expect(map.transform.getCameraPosition().altitude).toBeGreaterThan(initialCamPosition.altitude); | ||
expect(map.transform.getCameraPosition().altitude).toBeGreaterThan(terrainElevation); | ||
}); | ||
}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
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
40619093
295934