Skip to content
Open
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
63 changes: 54 additions & 9 deletions modules/react-maplibre/src/maplibre/maplibre.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {transformToViewState, applyViewStateToTransform} from '../utils/transform';
import {transformToViewState, applyViewStateToTransform, updateZoomConstraint} from '../utils/transform';
import {normalizeStyle} from '../utils/style-utils';
import {deepEqual} from '../utils/deep-equal';

Expand Down Expand Up @@ -76,7 +76,32 @@ export type MaplibreProps = Partial<ViewState> &
interactiveLayerIds?: string[];
/** CSS cursor */
cursor?: string;
};

/** Minimum zoom available to the map.
* @default 0
*/
minZoom?: number
/** Maximum zoom available to the map.
* @default 22
*/
maxZoom?: number
/** Minimum pitch available to the map.
* @default 0
*/
minPitch?: number
/** Maximum pitch available to the map.
* @default 85
*/
maxPitch?: number
/** Bounds of the map.
* @default [-180, -85.051129, 180, 85.051129]
*/
maxBounds?: [number, number, number, number]
/** Whether to render copies of the world or not.
* @default true
*/
renderWorldCopies?: boolean
}

const DEFAULT_STYLE = {version: 8, sources: {}, layers: []} as StyleSpecification;

Expand Down Expand Up @@ -146,7 +171,7 @@ const settingNames = [
'maxBounds',
'projection',
'renderWorldCopies'
];
] as const;
const handlerNames = [
'scrollZoom',
'boxZoom',
Expand Down Expand Up @@ -414,25 +439,45 @@ export default class Maplibre {
return false;
}

private _updateZoomConstraint(nextProps: MaplibreProps, currProps: MaplibreProps): boolean {
if (!('minZoom' in nextProps) && !('maxZoom' in nextProps)) {
return false
}

updateZoomConstraint(this._map, {
min: nextProps.minZoom ?? DEFAULT_SETTINGS.minZoom,
max: nextProps.maxZoom ?? DEFAULT_SETTINGS.maxZoom
}, {
min: currProps.minZoom ?? DEFAULT_SETTINGS.minZoom,
max: currProps.maxZoom ?? DEFAULT_SETTINGS.maxZoom,
})

return true
}

/* Update camera constraints and projection settings to match props
@param {object} nextProps
@param {object} currProps
@returns {bool} true if anything is changed
*/
private _updateSettings(nextProps: MaplibreProps, currProps: MaplibreProps): boolean {
const map = this._map;
let changed = false;
let settingsChanged = false;
for (const propName of settingNames) {
const propPresent = propName in nextProps || propName in currProps;
if (propName === 'minZoom' || propName === 'maxZoom') {
// eslint-disable-next-line no-continue
continue
}

if (propPresent && !deepEqual(nextProps[propName], currProps[propName])) {
changed = true;
const nextValue = propName in nextProps ? nextProps[propName] : DEFAULT_SETTINGS[propName];
if (propName in nextProps && !deepEqual(nextProps[propName], currProps[propName])) {
settingsChanged = true;
const nextValue = propName in nextProps ? nextProps[propName] : DEFAULT_SETTINGS[propName]
const setter = map[`set${propName[0].toUpperCase()}${propName.slice(1)}`];
setter?.call(map, nextValue);
}
}
return changed;
const zoomChanged = this._updateZoomConstraint(nextProps, currProps)
return settingsChanged || zoomChanged;
}

/* Update map style to match props */
Expand Down
33 changes: 33 additions & 0 deletions modules/react-maplibre/src/utils/transform.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {MaplibreProps} from '../maplibre/maplibre';
import type {ViewState} from '../types/common';
import type {TransformLike} from '../types/internal';
import { MapInstance } from '../types/lib';
import {deepEqual} from './deep-equal';

/**
Expand Down Expand Up @@ -56,3 +57,35 @@ export function applyViewStateToTransform(
}
return changes;
}

/**
* Update zoom constraints to match props by calling
* `setMinZoom` and `setMaxZoom` in the right order
* @param {object} nextRange
* @param {object} currRange
**/
export function updateZoomConstraint(map: MapInstance, nextRange: { min: number; max: number}, currentRange: { min: number; max: number }): void {
if (nextRange.min === currentRange.min && nextRange.max === currentRange.max) {
return
}

// if moving up ie. 1 - 3 -> 5 - 10
if (nextRange.min >= currentRange.min) {
if (nextRange.max !== currentRange.max) {
map.setMaxZoom(nextRange.max)
}
if (nextRange.min !== currentRange.min) {
map.setMinZoom(nextRange.min)
}
}

// if moving down ie. 5 - 10 -> 1 - 3
if (nextRange.min < currentRange.min) {
if (nextRange.min !== currentRange.min) {
map.setMinZoom(nextRange.min)
}
if (nextRange.max !== currentRange.max) {
map.setMaxZoom(nextRange.max)
}
}
}
75 changes: 73 additions & 2 deletions modules/react-maplibre/test/utils/transform.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import test from 'tape-promise/tape';
import test from 'tape';
import {
transformToViewState,
applyViewStateToTransform
applyViewStateToTransform,
updateZoomConstraint
} from '@vis.gl/react-maplibre/utils/transform';
import maplibregl from 'maplibre-gl';

Expand Down Expand Up @@ -64,3 +65,73 @@ test('applyViewStateToTransform', t => {

t.end();
});

test('updateZoomConstraint', t => {
let first = null
let currentMinZoom = 0
let currentMaxZoom = 0
const map = {
setMinZoom: (nextMinZoom) => {
if (nextMinZoom > currentMaxZoom) {
throw new Error('Setting minZoom > maxZoom')
}
currentMinZoom = nextMinZoom
if (!first) {
first = 'min'
}
},
setMaxZoom: (nextMaxZoom) => {
if (nextMaxZoom < currentMinZoom) {
throw new Error('Setting maxZoom < minZoom')
}
currentMaxZoom = nextMaxZoom
if (!first) {
first = 'max'
}
}
}

// moving down. 5 - 10 -> 1 - 3
currentMinZoom = 5
currentMaxZoom = 10
updateZoomConstraint(map, { min: 1, max: 3 }, { min: 5, max: 10 });
t.equal(first, 'min', 'min first')
first = null

// moving up. 1 - 3 -> 5 - 10
currentMinZoom = 1
currentMaxZoom = 3
updateZoomConstraint(map, { min: 5, max: 10 }, { min: 1, max: 3 });
t.equal(first, 'max', 'max first')
first = null

// expanding. 5 - 18 -> 3 - 22
currentMinZoom = 5
currentMaxZoom = 18
updateZoomConstraint(map, { min: 3, max: 22 }, { min: 5, max: 18 });
t.equal(first, 'min', 'min first')
first = null

// expanding down. 5 - 18 -> 3 - 18
currentMinZoom = 5
currentMaxZoom = 18
updateZoomConstraint(map, { min: 3, max: 18 }, { min: 5, max: 18 });
t.equal(first, 'min', 'min first')
first = null

// contracting. 3 - 22 -> 5 - 18
currentMinZoom = 5
currentMaxZoom = 18
updateZoomConstraint(map, { min: 5, max: 18 }, { min: 3, max: 22 });
t.equal(first, 'max', 'max first')
first = null

// contracting down. 12 - 22 -> 5 - 10
currentMinZoom = 12
currentMaxZoom = 22
updateZoomConstraint(map, { min: 5, max: 10 }, { min: 12, max: 22 });
t.equal(first, 'min', 'min first')
first = null

t.end();
});