From 96065b1237f376e3ef1765773c3ed77fbcad30f6 Mon Sep 17 00:00:00 2001 From: Roman Priesol Date: Mon, 7 Sep 2026 22:30:56 +0200 Subject: [PATCH] Home button fits all of Slovakia; add an overview map, top-right Home now fitBounds()s a country-wide bbox instead of resetting to CENTER/ZOOM's Bratislava point, since Home resetting into the same city you'd already be looking at wasn't useful. The overview map is NPS's own overview plugin (npmap5-plugins, still on an unmerged branch), vendored and converted from its UMD dist build to a plain ES module. Patched one MapLibre API break found while verifying it: the plugin reads map.transform (public in the MapLibre ~2.4 it targets) to relax the inset's polar pan limit; MapLibre 6 (this app's vendored version) no longer exposes .transform at all, so it threw on load. Guarded to skip that cosmetic adjustment when absent instead. --- docs/architecture.adoc | 44 ++- docs/nps-resources.adoc | 21 +- docs/third-party-notices.adoc | 13 + public/css/style.css | 6 + public/js/map.js | 37 +- public/vendor/npmap-overview/LICENSE.md | 5 + public/vendor/npmap-overview/overview.esm.js | 346 +++++++++++++++++++ 7 files changed, 441 insertions(+), 31 deletions(-) create mode 100644 public/vendor/npmap-overview/LICENSE.md create mode 100644 public/vendor/npmap-overview/overview.esm.js diff --git a/docs/architecture.adoc b/docs/architecture.adoc index ccc3061..b8558b1 100644 --- a/docs/architecture.adoc +++ b/docs/architecture.adoc @@ -137,26 +137,40 @@ Now that any view can be the starting one (via that permalink), a way back to the app's actual default is worth having: a "Home" button (`public/vendor/npmap-home-button/`, the National Park Service's own `home-button` MapLibre control — see `docs/third-party-notices.adoc`) -sits top-left on the map, pinned to `CENTER`/`ZOOM` explicitly rather -than its own fallback (the view at construction time, which would just -be wherever a loaded permalink happened to point). It resets to the -app's default view, not the visitor's own location — that's -`GeolocateControl`, a separate button in the same stack (below). +sits top-left on the map. It fits to `SLOVAKIA_BOUNDS` (a country-wide +bounding box) rather than the plain `CENTER`/`ZOOM` point the map +itself starts at — "home" is meant as a full-country overview, not a +re-centering on Bratislava. It resets to that fixed default view, not +the visitor's own location — that's `GeolocateControl`, a separate +button in the same stack (below). That Home button is the top of a small top-left stack — Home, zoom in/out (`NavigationControl`, `showCompass: false` since this map never rotates), a passive zoom-level readout (`ZoomLevelControl`, a plain div updated on the map's `zoom` event — not a button, since it has no -click behavior of its own), locate-me (`GeolocateControl`), and a -"Base map" button — matching the National Park Service's own map -control layout (grouped -together on the map itself, rather than split across corners or -between the map and a side panel). The last of those, -`MapLayersButton` (`public/js/map.js`), is a second entry point into -the exact same Online/Offline panel `LayerToggleControl` already -manages in the page's own sidebar — both call the same -`LayerToggleControl#toggle()`, so there's one source of truth for the -panel's open/closed state either way. +click behavior of its own), a distance/area measuring tool +(`MeasureControl`, `public/js/measure.js`), locate-me +(`GeolocateControl`), and a "Base map" button — matching the National +Park Service's own map control layout (grouped together on the map +itself, rather than split across corners or between the map and a side +panel). The last of those, `MapLayersButton` (`public/js/map.js`), is +a second entry point into the exact same Online/Offline panel +`LayerToggleControl` already manages in the page's own sidebar — both +call the same `LayerToggleControl#toggle()`, so there's one source of +truth for the panel's open/closed state either way. + +A separate control sits top-right: a small inset overview map +(`public/vendor/npmap-overview/`, NPS's `overview` plugin — see +`docs/nps-resources.adoc`) showing the current view's location in a +wider, zoomed-out context, with the visible area drawn as a box on top +of it; dragging that box pans the main map. It clones whichever base +style the main map is currently showing (`style: ''`, its own default — +the plugin reads `map.style.serialize()`) rather than a separate style +of its own, so it stays visually consistent with the Online/Offline +toggle without this app maintaining a second style for it. Vendored +from an unmerged upstream branch and patched for a MapLibre API break +(it targeted MapLibre ~2.4's public `map.transform`, since removed) — +see `docs/third-party-notices.adoc` for the exact patch. === Debug inspector (Alt+click) diff --git a/docs/nps-resources.adoc b/docs/nps-resources.adoc index 6609195..1e39774 100644 --- a/docs/nps-resources.adoc +++ b/docs/nps-resources.adoc @@ -27,6 +27,10 @@ checking here before searching from zero next time. | Park Tiles color palette (offline basemap theme) | `public/js/map.js`'s `OFFLINE_THEME` | Colors only — see <> for why the actual tile service isn't used + +| `overview` (top-right minimap/inset control) +| `public/vendor/npmap-overview/` +| MIT (per that branch's `package.json`); same public-domain repo as above |=== See `docs/third-party-notices.adoc` for the formal vendored-library @@ -99,18 +103,19 @@ https://github.com/nationalparkservice/npmap5-plugins/issues/7[#7]. === On unmerged feature branches (less vetted — not on `main` yet) +`overview` (branch `maplibre-gl-overview`) has since been adopted — +see "What this app actually uses" above and +`docs/architecture.adoc`'s control-stack section. Live demo (as +upstream ships it, unpatched): +https://raw.githack.com/nationalparkservice/npmap5-plugins/maplibre-gl-overview/examples/index.html. +Note: MapLibre GL JS has no first-party minimap of its own — this +isn't more "official" than any other third-party minimap plugin, just +NPS-flavored. + [cols="1,3"] |=== | Plugin | What it is -| `overview` (branch `maplibre-gl-overview`) -| A minimap/inset control, any corner, respects pitch/bearing. Live - demo: https://raw.githack.com/nationalparkservice/npmap5-plugins/maplibre-gl-overview/examples/index.html. - Note: MapLibre GL JS has no first-party minimap of its own — this - wouldn't be more "official" than any other third-party minimap - plugin, just NPS-flavored. No minimap has been requested for this - app; would be a nice-to-have, not a fix for anything. - | `carto` (branch `maplibre-gl-carto-source`) | Adds a vector-tile source backed by Carto.com-hosted data. Only useful if we actually have a Carto-hosted dataset to point at — we diff --git a/docs/third-party-notices.adoc b/docs/third-party-notices.adoc index 9f16cc5..f64cd75 100644 --- a/docs/third-party-notices.adoc +++ b/docs/third-party-notices.adoc @@ -36,8 +36,21 @@ required): | NPS Home Button (`home-button`) | 0.0.1-d | https://github.com/nationalparkservice/npmap5-plugins/tree/main/home-button + +| NPS Overview Map (`maplibre-gl-overview`) +| 5.0.1 +| https://github.com/nationalparkservice/npmap5-plugins/tree/maplibre-gl-overview |=== +The overview map is vendored from an unmerged branch, not `main` (see +`docs/nps-resources.adoc`), and converted from its UMD dist build to a +plain ES module — no functional change except the module wrapper. It +was also patched for a MapLibre API break: it reads `map.transform` +(public in the MapLibre ~2.4 this plugin targets) to relax the +overview's polar pan limit, but MapLibre 6's `Map` no longer exposes +`.transform` at all — guarded to skip that (cosmetic) adjustment +instead of throwing when it's absent. + Offline map text labels need font glyphs too (see `docs/architecture.adoc`), vendored under `public/vendor/fonts/` — SIL Open Font License, not BSD-3-Clause like the above: diff --git a/public/css/style.css b/public/css/style.css index c85b1bf..34fa26a 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -472,6 +472,12 @@ html, body { word-break: break-word; } +.maplibregl-ctrl-overview-map { + border-radius: 4px; + overflow: hidden; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); +} + .measure-ctrl { position: relative; } diff --git a/public/js/map.js b/public/js/map.js index 658fd5a..6b8f215 100644 --- a/public/js/map.js +++ b/public/js/map.js @@ -14,6 +14,8 @@ import { MeasureControl } from "/js/measure.js"; import { LAYERS_ICON } from "/js/icons.js"; import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js"; import HomeButton from "/vendor/npmap-home-button/home-button.esm.js"; +import OverviewMap from "/vendor/npmap-overview/overview.esm.js"; +import * as maplibregl from "/vendor/maplibre-gl/maplibre-gl.mjs"; const CENTER = [17.1077, 48.1486]; const ZOOM = 13; @@ -235,14 +237,18 @@ const map = new MaplibreMap({ // all grouped together rather than split across corners. Added in this // order since MapLibre stacks same-position controls in add order. // -// homePosition/zoom pinned to the app's own default rather than left to the -// button's own fallback (current center/zoom at construction) — now that -// the URL hash can restore any view on load (see the Hash option above), -// "current view" and "the default view" are no longer the same thing, and -// a home button that resets to wherever you happened to load the page -// wouldn't be a home button. This is *not* "go to my location" — that's -// GeolocateControl, below. -map.addControl(new HomeButton({ homePosition: CENTER, zoom: ZOOM }), "top-left"); +// bounds (rather than homePosition/zoom) so Home resets to an overview of +// the whole country, not back to CENTER/ZOOM's initial Bratislava view — +// now that the URL hash can restore any view on load (see the Hash option +// above), "current view" and "the default view" are no longer the same +// thing, and a home button that resets to wherever you happened to load +// the page wouldn't be a home button. This is *not* "go to my location" — +// that's GeolocateControl, below. +const SLOVAKIA_BOUNDS = [ + [16.83, 47.73], + [22.57, 49.61], +]; +map.addControl(new HomeButton({ bounds: SLOVAKIA_BOUNDS }), "top-left"); // showCompass: false — this map never rotates, so a bearing-reset button // (NavigationControl's default third button) would just be dead weight. map.addControl(new NavigationControl({ showCompass: false }), "top-left"); @@ -257,6 +263,21 @@ map.addControl( "top-left", ); +// A small inset map showing where the current view sits in context — NPS's +// own overview plugin (docs/nps-resources.adoc's "overview" entry, still on +// an unmerged branch upstream). It draws its own embedded MapLibre map, so +// it needs the maplibregl module itself (not just the Map class) — style +// '' (the default) means it clones whatever the main map's current style +// is (`this._mainMap.style.serialize()`), so it stays in sync with the +// Online/Offline toggle without a second style to maintain here. Built only +// once the main map's own style has loaded, matching the plugin's own +// example — its internal map is constructed eagerly in the constructor, +// before that clone happens in onAdd. +map.once("style.load", () => { + const OverviewMapControl = OverviewMap(maplibregl); + map.addControl(new OverviewMapControl({}), "top-right"); +}); + const sidebar = document.getElementById("sidebar"); const layerToggle = new LayerToggleControl(applyStyle); diff --git a/public/vendor/npmap-overview/LICENSE.md b/public/vendor/npmap-overview/LICENSE.md new file mode 100644 index 0000000..823946b --- /dev/null +++ b/public/vendor/npmap-overview/LICENSE.md @@ -0,0 +1,5 @@ +### LICENSE.md + +This project constitutes a work of the United States Government and is not subject to domestic copyright protection under [17 USC § 105](https://www.govinfo.gov/app/details/USCODE-2010-title17/USCODE-2010-title17-chap1-sec105). + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/public/vendor/npmap-overview/overview.esm.js b/public/vendor/npmap-overview/overview.esm.js new file mode 100644 index 0000000..d369f26 --- /dev/null +++ b/public/vendor/npmap-overview/overview.esm.js @@ -0,0 +1,346 @@ +// Vendored from nationalparkservice/npmap5-plugins, branch +// maplibre-gl-overview (not yet merged to main — see +// docs/nps-resources.adoc). MIT-licensed per that branch's package.json; +// see LICENSE.md alongside this file for the repo's own notice. +// +// Converted from its UMD dist build (dist/maplibre-gl-overview.min.js) to a +// plain ES module export — the only change from upstream besides that +// wrapper swap. +const blankGeoJsonFeature = () => ({ + "type": "FeatureCollection", + "features": [] +}); +const between = (min, value, max) => Math.min(max, Math.max(min, value)); +function OverviewMap(mapLibrary) { + return class OverviewMapControl { + constructor(options) { + this._extent = blankGeoJsonFeature(); + this._moving = false; + this._easingMain = false; + this._keyboardEvents = (e) => { + if (e.altKey || e.ctrlKey || e.metaKey) + return; + // The +1 directions are in Pixel space, not LngLat, in pixel space 0,0 it the top left + const events = { + "ArrowLeft": () => { + e.preventDefault(); + return { xDir: 1 }; + }, + "ArrowRight": () => { + e.preventDefault(); + return { xDir: -1 }; + }, + "ArrowUp": () => { + e.preventDefault(); + return { yDir: 1 }; + }, + "ArrowDown": () => { + e.preventDefault(); + return { yDir: -1 }; + }, + "+": () => ({ zoomDir: +1 }), + "-": () => ({ zoomDir: -1 }), + }; + if (events[e.key]) { + // Update the variables + let { xDir, yDir, zoomDir } = events[e.key](); + xDir = xDir || 0; + yDir = yDir || 0; + zoomDir = zoomDir || 0; + if (this._mainMap) { + const zoomMain = this._mainMap.getZoom(); + // Convert the current center of the overview map to pixel space + const overviewMapCenterPixels = this._overviewMap.project(this._overviewMap.getCenter()); + // Get the point that would be panned that center + const newCenter = this._overviewMap.unproject(new this._mapLibrary.Point(overviewMapCenterPixels.x + (xDir * this._overviewMap.keyboard._panStep), overviewMapCenterPixels.y + (yDir * this._overviewMap.keyboard._panStep))); + // Get the current center of the main map + const mainMapCenterPixels = this._mainMap.project(this._mainMap.getCenter()); + // Get the new point on the main map in pixels + const mainMapNewCenterPixels = this._mainMap.project(newCenter); + // Define the offset to pan to + const xyOffset = { + x: mainMapNewCenterPixels.x - mainMapCenterPixels.x, + y: mainMapNewCenterPixels.y - mainMapCenterPixels.y + }; + this._mainMap.easeTo({ + duration: 300, + easeId: 'keyboardHandler', + easing: (t) => t * (2 - t), + zoom: zoomDir ? Math.round(zoomMain) + zoomDir * (e.shiftKey ? 2 : 1) : zoomMain, + offset: [xyOffset.x, xyOffset.y], + center: this._mainMap.getCenter() + }, { originalEvent: e }); + } + } + }; + // Create a container for the overview map + this._container = document.createElement("div"); + this._container.className = "maplibregl-ctrl-overview-map maplibregl-ctrl"; + this._container.addEventListener("contextmenu", (e) => e.preventDefault()); + // options + const defaultOptions = { + zoomLevelOffset: -5, + watchEvents: ['move', 'rotate', 'pitch'], + overlayPaint: { + 'fill-color': '#d29700', + 'fill-opacity': 0.75 + }, + selectionPaint: { + 'fill-color': '#d29700', + 'fill-opacity': 0.25, + 'fill-outline-color': '#000000' + }, + width: 150, + height: 150, + style: '', + _keyboardPanStep: 15 + }; + this.options = { ...defaultOptions, ...options }; + this._mapLibrary = mapLibrary; + this._container.setAttribute('style', `width: ${this.options.width}px; height: ${this.options.height}px;`); + // Create the map for the overview + this._overviewMap = new mapLibrary.Map({ + container: this._container, + style: this.options.style, + interactive: true, + pitchWithRotate: false, + attributionControl: false, + boxZoom: false, + dragRotate: false, + touchZoomRotate: false, + touchPitch: false, + minZoom: -2 + }); + this._overviewMap.on('load', () => { + // `.transform` existed as a public Map property when this + // plugin was built (~MapLibre 2.4.0); this app vendors a + // newer MapLibre where it's gone entirely, so this relaxes + // nothing and the overview keeps MapLibre's default ~85° + // Web Mercator pan limit instead — harmless, just means the + // inset can't scroll arbitrarily close to the poles. + if (this._overviewMap.transform) { + const maxLat = this._overviewMap.transform.maxValidLatitude || 85.051129; + this._overviewMap.transform.latRange = [maxLat * -2, maxLat * 2]; + } + this._overviewMap.resize(); + this._overviewMap.addSource('bboxSource', { + 'type': 'geojson', + 'data': this._extent + }); + this._overviewMap.addSource('selectionSource', { + 'type': 'geojson', + 'data': blankGeoJsonFeature() + }); + // Add a new layer to visualize the polygon. + this._overviewMap.addLayer({ + 'id': 'bboxLayer', + 'type': 'fill', + 'source': 'bboxSource', + 'layout': {}, + 'paint': this.options.overlayPaint + }); + this._overviewMap.addLayer({ + 'id': 'selectionFillLayer', + 'type': 'fill', + 'source': 'selectionSource', + 'layout': {}, + 'paint': this.options.selectionPaint + }); + this._overviewMap.addLayer({ + 'id': 'selectionLineLayer', + 'type': 'line', + 'source': 'selectionSource', + 'layout': {}, + 'paint': { + 'line-color': this.options.selectionPaint['fill-outline-color'] || '#000000', + 'line-width': 2 + } + }); + }); + } + onAdd(map) { + this._mainMap = map; + if (this.options.style === '') { + this._overviewMap.setStyle(this._mainMap.style.serialize()); + } + // Disable the keyboard events, but use our own + this._overviewMap.keyboard.disable(); + // we still use the _panstep from the keyboard events in out custom function + this._overviewMap.keyboard._panStep = this.options._keyboardPanStep; + //this._overviewMap.getCanvas().addEventListener('keydown', this._keyboardEvents, true); + // Update the overview map when the main map moves + this.options.watchEvents.forEach(event => map.on(event, () => this._updateOverview(map))); + ///////////////////////////// + // Update Overview Map triggers + // Once the style is loaded, start listening + map.once('style.load', () => this._updateOverview(map)); + // Start listening when it's added + this._updateOverview(map); + // Update the main map when the overview moves if the map is loaded (otherwise the style.load will pick up the change) + this._overviewMap.on('movestart', () => map.loaded() && this._updateMain(map)); + // Make the overview map take control whenever the viewview map gets clicked + const takeControl = () => { + map.stop(); + this._moving = false; + this._updateMain(map); + }; + this._overviewMap.on('mousedown', takeControl); + this._overviewMap.on('touchstart', takeControl); + // Forward keydown events to the main map + this._overviewMap.getCanvas().addEventListener('keydown', (e) => { + //e.preventDefault(); + map.stop(); + this._keyboardEvents(e); + }); + ///////////////////////////// + return this._container; + } + onRemove() { + if (this._mainMap) { + //this._overviewMap.off('movestart', () => this._mainMap && this._moveOverview(this._mainMap)); + this.options.watchEvents + .forEach(event => this._mainMap && this._mainMap + .off(event, () => this._updateOverview(this._mainMap))); + this._overviewMap.off('movestart', () => this._updateMain(this._mainMap)); + } + if (this.options.style === '') { + this._overviewMap.setStyle(''); + } + this._mainMap = undefined; + } + _updateOverview(map) { + var _a; + // Don't update the overview map if it's already moving + if (this._moving || !this._overviewMap) + return; + const pixelRatio = map.getPixelRatio(); + const geojson = blankGeoJsonFeature(); + if (map) { + const center = map.unproject([(map.getCanvas().width / pixelRatio) / 2, (map.getCanvas().height / pixelRatio) / 2]); + const bounds = map.getBounds(); + let extentCoordinates = [ + [-179.99, bounds.getNorth()], + [179.99, bounds.getNorth()], + [179.99, bounds.getSouth()], + [-179.99, bounds.getSouth()], + [-179.99, bounds.getNorth()], + ]; + // If the extent isn't larger that the earth, create a better one + if ((bounds.getEast() - bounds.getWest()) < 360 + (Math.abs(90 - Math.abs(90 - Math.abs(map.getBearing())))) * (map.getPitch() / 60)) { + extentCoordinates = [ + map.unproject([0, 0]).toArray(), + map.unproject([map.getCanvas().width / pixelRatio, 0]).toArray(), + map.unproject([map.getCanvas().width / pixelRatio, map.getCanvas().height / pixelRatio]).toArray(), + map.unproject([0, map.getCanvas().height / pixelRatio]).toArray(), + map.unproject([0, 0]).toArray() + ]; + } + geojson.features.push({ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [extentCoordinates] + } + }); + const newOverviewZoom = between(this._overviewMap.getMinZoom(), map.getZoom() + this.options.zoomLevelOffset, this._overviewMap.getMaxZoom()); + if (!this._easingMain) { + this._moving = true; + this._overviewMap.setCenter(center); + this._overviewMap.setZoom(newOverviewZoom); + this._moving = false; + } + (_a = this._overviewMap.getSource('bboxSource')) === null || _a === void 0 ? void 0 : _a.setData(geojson); + this._extent = geojson; + } + } + _updateMain(map) { + var _a; + // Don't update the main map if it's already moving or if it doesn't exist + if (this._moving || !this._mainMap) + return; + const moveStartCenter = this._overviewMap.getCenter(); + // Take control of the main map + this._mainMap.stop(); + // Get the current extent, and copy it + const selectionExtent = JSON.parse(JSON.stringify(this._extent)); + // Get the coordinates on the screen for the current map extent + const screenCoords = (selectionExtent.features[0]).geometry.coordinates[0].map(pos => this._overviewMap.project(pos)); + // Update the GeoJSON object in the overview map to match the extent + (_a = this._overviewMap.getSource('selectionSource')) === null || _a === void 0 ? void 0 : _a.setData(selectionExtent); + // Used when overzoomed + const overZoomOffset = Math.max(this._overviewMap.getZoom() - this._mainMap.getZoom(), this.options.zoomLevelOffset); + // Called when the overview map is moving + const moving = () => { + var _a; + // Gets the current position + const currentSelectionExtentCoordinates = screenCoords + .map(pos => this._overviewMap.unproject([pos.x, pos.y])) + .map(xy => [xy.lng, xy.lat]); + // Updates the GeoJSON to match the new position + const geojson = blankGeoJsonFeature(); + geojson.features.push({ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [currentSelectionExtentCoordinates] + } + }); + // Updates the box on the overview map + (_a = this._overviewMap.getSource('selectionSource')) === null || _a === void 0 ? void 0 : _a.setData(geojson); + }; + const doneEvents = () => { + var _a; + // Draw the current box on the overview map + (_a = this._overviewMap.getSource('selectionSource')) === null || _a === void 0 ? void 0 : _a.setData(blankGeoJsonFeature()); + this._moving = false; + this._easingMain = false; + // Start up the event listeners again + this._updateOverview(map); + }; + // Called when the overview map is done moving + const moveend = () => { + // Stop listening for events + this._overviewMap.off('move', moving); + this._overviewMap.off('moveend', moveend); + this._overviewMap.off('mouseup', moveend); + this._overviewMap.off('touchend', moveend); + if (this._easingMain) + return; + // If the map and the oveview map are ready, then we can update the map's center + if (this._mainMap) { + // Deal with the overview map being outside of its minZoom + let newCenter = this._overviewMap.getCenter(); + const newMainZoom = between(this._mainMap.getMinZoom(), this._overviewMap.getZoom() - overZoomOffset, this._mainMap.getMaxZoom()); + // Check if the overview map has moved, if so, ease the main map, otherwise finish events + if (newCenter.lng !== moveStartCenter.lng || newCenter.lat !== moveStartCenter.lat) { + // Execute the "doneEvents" code when the map stops moving + this._mainMap.once('moveend', (event) => event.doneEvents && event.doneEvents()); + this._moving = true; + this._easingMain = true; + this._mainMap.easeTo({ + center: newCenter, + zoom: newMainZoom + }, { + doneEvents + }); + } + else { + doneEvents(); + } + } + }; + this._overviewMap.on('move', moving); + this._overviewMap.once('moveend', moveend); + this._overviewMap.once('mouseup', moveend); + this._overviewMap.once('touchend', moveend); + } + _getCenterAtPitch(map, pitch, rotation) { + const pixelRatio = map.getPixelRatio(); + const center = map.unproject([(map.getCanvas().width / pixelRatio) / 2, (map.getCanvas().height / pixelRatio) / 2]); + return center; + } + }; +} +export default OverviewMap;