Skip to content

Commit

Permalink
Add GlobeControl (maplibre#4960)
Browse files Browse the repository at this point in the history
  • Loading branch information
birkskyum authored Nov 1, 2024
1 parent ec8398a commit ef6e9ee
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Add setVerticalFieldOfView() to public API ([#4717](https://github.com/maplibre/maplibre-gl-js/issues/4717))
- ⚠️ Return actual altitude from queryTerrainElevation + Pass non-translated matrices to custom layer on mercator map ([#3854](https://github.com/maplibre/maplibre-gl-js/pull/3854))
- Disable sky when using globe and blend it in when changing to mercator ([#4853](https://github.com/maplibre/maplibre-gl-js/issues/4853))
- New GlobeControl ([#4960](https://github.com/maplibre/maplibre-gl-js/pull/4960))
- _...Add new stuff here..._

### 🐞 Bug fixes
Expand Down
20 changes: 18 additions & 2 deletions src/css/maplibre-gl.css
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,30 @@
}
}

@svg-load ctrl-globe url("svg/maplibregl-ctrl-globe.svg") {
stroke: #333;
}

@svg-load ctrl-globe-enabled url("svg/maplibregl-ctrl-globe.svg") {
stroke: #33b5e5;
}

.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon {
background-image: svg-inline(ctrl-globe);
}

.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon {
background-image: svg-inline(ctrl-globe-enabled);
}

@svg-load ctrl-terrain url("svg/maplibregl-ctrl-terrain.svg") {
fill: #333;
#stroke { display: none; }

}

@svg-load ctrl-terrain-enabled url("svg/maplibregl-ctrl-terrain.svg") {
fill: #33b5e5;
#stroke { display: none; }

}

.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon {
Expand Down
6 changes: 6 additions & 0 deletions src/css/svg/maplibregl-ctrl-globe.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {LogoControl} from './ui/control/logo_control';
import {ScaleControl} from './ui/control/scale_control';
import {FullscreenControl} from './ui/control/fullscreen_control';
import {TerrainControl} from './ui/control/terrain_control';
import {GlobeControl} from './ui/control/globe_control';
import {Popup} from './ui/popup';
import {Marker} from './ui/marker';
import {Style} from './style/style';
Expand Down Expand Up @@ -180,6 +181,7 @@ export {
ScaleControl,
FullscreenControl,
TerrainControl,
GlobeControl,
Hash,
Popup,
Marker,
Expand Down
2 changes: 1 addition & 1 deletion src/style/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1513,7 +1513,7 @@ export class Style extends Evented {
}

getProjection(): ProjectionSpecification {
return this.stylesheet.projection;
return this.stylesheet?.projection;
}

setProjection(projection: ProjectionSpecification) {
Expand Down
65 changes: 65 additions & 0 deletions src/ui/control/globe_control.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {GlobeControl} from './globe_control';
import {createMap as globalCreateMap, beforeMapTest} from '../../util/test/util';

function createMap() {
return globalCreateMap({
attributionControl: false,
style: {
version: 8,
sources: {},
layers: [],
owner: 'maplibre',
id: 'basic'
},
hash: true
}, undefined);
}

let map;

beforeEach(() => {
beforeMapTest();
map = createMap();
});

afterEach(() => {
map.remove();
});

describe('GlobeControl', () => {
test('appears in top-right by default', () => {
map.addControl(new GlobeControl());

expect(
map.getContainer().querySelectorAll('.maplibregl-ctrl-top-right .maplibregl-ctrl-globe')
).toHaveLength(1);
});

test('appears in the position specified by the position option', () => {
map.addControl(new GlobeControl(), 'bottom-right');

expect(
map.getContainer().querySelectorAll('.maplibregl-ctrl-bottom-right .maplibregl-ctrl-globe')
).toHaveLength(1);

expect(
map.getContainer().querySelectorAll('.maplibregl-ctrl-top-right .maplibregl-ctrl-globe')
).toHaveLength(0);
});

test('toggles projection when clicked', async () => {
await new Promise(resolve => map.on('load', resolve));

map.addControl(new GlobeControl());
expect(map.style.projection.name).toBe('mercator');
const button = map.getContainer().querySelector('.maplibregl-ctrl-globe');

button.click();
await new Promise(resolve => setTimeout(resolve, 0));
expect(map.style.projection.name).toBe('globe');

button.click();
await new Promise(resolve => setTimeout(resolve, 0));
expect(map.style.projection.name).toBe('mercator');
});
});
67 changes: 67 additions & 0 deletions src/ui/control/globe_control.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {DOM} from '../../util/dom';

import type {Map} from '../map';
import type {IControl} from './control';

/**
* A `GlobeControl` control contains a button for toggling the map projection between "mercator" and "globe".
*
* @group Markers and Controls
*
* @example
* ```ts
* let map = new Map()
* .addControl(new GlobeControl());
* ```
*
* @see [Display a globe with a fill extrusion layer](https://maplibre.org/maplibre-gl-js/docs/examples/globe-fill-extrusion/)
*/
export class GlobeControl implements IControl {
_map: Map;
_container: HTMLElement;
_globeButton: HTMLButtonElement;

/** {@inheritDoc IControl.onAdd} */
onAdd(map: Map) {
this._map = map;
this._container = DOM.create('div', 'maplibregl-ctrl maplibregl-ctrl-group');
this._globeButton = DOM.create('button', 'maplibregl-ctrl-globe', this._container);
DOM.create('span', 'maplibregl-ctrl-icon', this._globeButton).setAttribute('aria-hidden', 'true');
this._globeButton.type = 'button';
this._globeButton.addEventListener('click', this._toggleProjection);

this._updateGlobeIcon();
this._map.on('styledata', this._updateGlobeIcon);
return this._container;
}

/** {@inheritDoc IControl.onRemove} */
onRemove() {
DOM.remove(this._container);
this._map.off('styledata', this._updateGlobeIcon);
this._globeButton.removeEventListener('click', this._toggleProjection);
this._map = undefined;
}

_toggleProjection = () => {
const currentProjection = this._map.getProjection()?.type;
if (currentProjection === 'mercator' || !currentProjection) {
this._map.setProjection({type: 'globe'});
} else {
this._map.setProjection({type: 'mercator'});
}
this._updateGlobeIcon();
};

_updateGlobeIcon = () => {
this._globeButton.classList.remove('maplibregl-ctrl-globe');
this._globeButton.classList.remove('maplibregl-ctrl-globe-enabled');
if (this._map.getProjection()?.type === 'globe') {
this._globeButton.classList.add('maplibregl-ctrl-globe-enabled');
this._globeButton.title = this._map._getUIString('GlobeControl.Disable');
} else {
this._globeButton.classList.add('maplibregl-ctrl-globe');
this._globeButton.title = this._map._getUIString('GlobeControl.Enable');
}
};
}
2 changes: 2 additions & 0 deletions src/ui/default_locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const defaultLocale = {
'ScaleControl.Kilometers': 'km',
'ScaleControl.Miles': 'mi',
'ScaleControl.NauticalMiles': 'nm',
'GlobeControl.Enable': 'Enable globe',
'GlobeControl.Disable': 'Disable globe',
'TerrainControl.Enable': 'Enable terrain',
'TerrainControl.Disable': 'Disable terrain',
'CooperativeGesturesHandler.WindowsHelpText': 'Use Ctrl + scroll to zoom the map',
Expand Down
2 changes: 1 addition & 1 deletion test/build/min.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 890732;
const expectedBytes = 892558;

expect(actualBytes).toBeLessThan(expectedBytes + increaseQuota);
expect(actualBytes).toBeGreaterThan(expectedBytes - decreaseQuota);
Expand Down
2 changes: 2 additions & 0 deletions test/examples/globe-fill-extrusion.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
center: [30.0, 40.0],
});

map.addControl(new maplibregl.GlobeControl(), 'top-right');

map.on('style.load', () => {
map.setProjection({
type: 'globe', // Set projection to globe
Expand Down

0 comments on commit ef6e9ee

Please sign in to comment.