diff options
author | Alexander Neonxp Kiryukhin <i@neonxp.ru> | 2024-08-18 13:29:54 +0300 |
---|---|---|
committer | Alexander Neonxp Kiryukhin <i@neonxp.ru> | 2024-08-18 13:29:54 +0300 |
commit | fd70f95224374d23157ee7c0357733102cd0df53 (patch) | |
tree | e490c12e021cedaf211b292d5d623baa32a673fc /node_modules/pigeon-maps |
Diffstat (limited to 'node_modules/pigeon-maps')
28 files changed, 7036 insertions, 0 deletions
diff --git a/node_modules/pigeon-maps/LICENSE.md b/node_modules/pigeon-maps/LICENSE.md new file mode 100644 index 0000000..d2847f4 --- /dev/null +++ b/node_modules/pigeon-maps/LICENSE.md @@ -0,0 +1,8 @@ +# The MIT License (MIT) +Copyright (c) 2016 Marius Andra <marius.andra@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +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/node_modules/pigeon-maps/README.md b/node_modules/pigeon-maps/README.md new file mode 100644 index 0000000..3de35a8 --- /dev/null +++ b/node_modules/pigeon-maps/README.md @@ -0,0 +1,59 @@ +# Pigeon Maps - ReactJS maps without external dependencies + +[![npm version](https://img.shields.io/npm/v/pigeon-maps.svg)](https://www.npmjs.com/package/pigeon-maps) +[![minified](https://badgen.net/bundlephobia/min/pigeon-maps)](https://bundlephobia.com/result?p=pigeon-maps) +[![minified + gzipped](https://badgen.net/bundlephobia/minzip/pigeon-maps)](https://bundlephobia.com/result?p=pigeon-maps) + +Demo: https://pigeon-maps.js.org/ (using maps from MapTiler, OSM and Stamen) + +## What is it? + +Are you tired of waiting 3 seconds to parse 200kb of Google Maps JavaScript just to display a few tiles and a marker? 140kb of minified Leaflet too much? + +Welcome to the club! + +This project aims to provide a performance-first React-centric extendable map engine. + +We're currently at: +- [![minified](https://badgen.net/bundlephobia/min/pigeon-maps)](https://bundlephobia.com/result?p=pigeon-maps) +- [![minified + gzipped](https://badgen.net/bundlephobia/minzip/pigeon-maps)](https://bundlephobia.com/result?p=pigeon-maps) + +Implemented: + +- Show tiles +- Arbitrary overlays (markers, etc) +- Move the map by dragging +- Move the map by touch on mobile +- Zooming with the scroll wheel +- Zooming by touch +- Fractional zooming (e.g. to level 12.2) +- Zoom without flickering (keep old tiles until new ones load) +- Smooth animated zooming +- Slide when dragging and letting go +- Event handling (clicks, etc) +- Double click and double tap zooming +- Option to block dragging with one finger and mouse wheel scrolling without holding meta key +- Enable/disable touch and mouse events as needed - you could make a 100% static server rendered react map +- Support for 100% width/height containers +- Markers +- Overlays +- Draggable Overlays +- Zoom Controls + +Missing: +- Double tap and then swipe touch zooming +- Many other components + +## Install + +[Read the docs here!](https://pigeon-maps.js.org/docs/installation) + +## Yeah, but why "pigeon"?? + +Pigeons are experts in [magnetoreception](https://en.wikipedia.org/wiki/magnetoreception). Good pigeons can find their way home from anywhere. + +Magnets were essential in making the first maps. With a good map you can find your way home from anywhere. + +Thus, `pigeon`. + +Source: https://en.wikipedia.org/wiki/Homing_pigeon diff --git a/node_modules/pigeon-maps/lib/controls/ZoomControl.d.ts b/node_modules/pigeon-maps/lib/controls/ZoomControl.d.ts new file mode 100644 index 0000000..30aeff8 --- /dev/null +++ b/node_modules/pigeon-maps/lib/controls/ZoomControl.d.ts @@ -0,0 +1,8 @@ +import React from 'react';
+import { PigeonProps } from '../types';
+interface ZoomProps extends PigeonProps {
+ style?: React.CSSProperties;
+ buttonStyle?: React.CSSProperties;
+}
+export declare function ZoomControl({ style, buttonStyle, setCenterZoom, mapState, mapProps }: ZoomProps): JSX.Element;
+export {};
diff --git a/node_modules/pigeon-maps/lib/index.cjs.js b/node_modules/pigeon-maps/lib/index.cjs.js new file mode 100644 index 0000000..68c2721 --- /dev/null +++ b/node_modules/pigeon-maps/lib/index.cjs.js @@ -0,0 +1,2098 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var React = require('react'); + +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + +var React__default = /*#__PURE__*/_interopDefaultLegacy(React); + +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + enumerableOnly && (symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + })), keys.push.apply(keys, symbols); + } + + return keys; +} + +function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = null != arguments[i] ? arguments[i] : {}; + i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { + _defineProperty(target, key, source[key]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + + return target; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + + _setPrototypeOf(subClass, superClass); +} + +function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); +} + +function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; +} + +function debounce(func, wait) { + var timeout; + return function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var context = this; + clearTimeout(timeout); + timeout = setTimeout(function () { + return func.apply(context, args); + }, wait); + }; +} +function parentHasClass(element, className) { + while (element) { + if (element.classList && element.classList.contains(className)) { + return true; + } + + element = element.parentElement; + } + + return false; +} +function parentPosition(element) { + var rect = element.getBoundingClientRect(); + return { + x: rect.left, + y: rect.top + }; +} + +function osm(x, y, z) { + return "https://tile.openstreetmap.org/" + z + "/" + x + "/" + y + ".png"; +} + +var ANIMATION_TIME = 300; +var DIAGONAL_THROW_TIME = 1500; +var SCROLL_PIXELS_FOR_ZOOM_LEVEL = 150; +var MIN_DRAG_FOR_THROW = 40; +var CLICK_TOLERANCE = 2; +var DOUBLE_CLICK_DELAY = 300; +var DEBOUNCE_DELAY = 60; +var PINCH_RELEASE_THROW_DELAY = 300; +var WARNING_DISPLAY_TIMEOUT = 300; + +var NOOP = function NOOP() { + return true; +}; + +var lng2tile = function lng2tile(lon, zoom) { + return (lon + 180) / 360 * Math.pow(2, zoom); +}; + +var lat2tile = function lat2tile(lat, zoom) { + return (1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom); +}; + +function tile2lng(x, z) { + return x / Math.pow(2, z) * 360 - 180; +} + +function tile2lat(y, z) { + var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); + return 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); +} + +function getMousePixel(dom, event) { + var parent = parentPosition(dom); + return [event.clientX - parent.x, event.clientY - parent.y]; +} + +function easeOutQuad(t) { + return t * (2 - t); +} + +var absoluteMinMax = [tile2lat(Math.pow(2, 10), 10), tile2lat(0, 10), tile2lng(0, 10), tile2lng(Math.pow(2, 10), 10)]; +var hasWindow = typeof window !== 'undefined'; +var performanceNow = hasWindow && window.performance && window.performance.now ? function () { + return window.performance.now(); +} : function () { + var timeStart = new Date().getTime(); + return function () { + return new Date().getTime() - timeStart; + }; +}(); + +var requestAnimationFrame = function requestAnimationFrame(callback) { + if (hasWindow) { + return (window.requestAnimationFrame || window.setTimeout)(callback); + } else { + callback(new Date().getTime()); + return null; + } +}; + +var cancelAnimationFrame = function cancelAnimationFrame(animFrame) { + return hasWindow && animFrame ? (window.cancelAnimationFrame || window.clearTimeout)(animFrame) : false; +}; + +function srcSet(dprs, url, x, y, z) { + if (!dprs || dprs.length === 0) { + return ''; + } + + return dprs.map(function (dpr) { + return url(x, y, z, dpr) + (dpr === 1 ? '' : " " + dpr + "x"); + }).join(', '); +} + +var ImgTile = function ImgTile(_ref) { + var tile = _ref.tile, + tileLoaded = _ref.tileLoaded; + return /*#__PURE__*/React__default["default"].createElement("img", { + src: tile.url, + srcSet: tile.srcSet, + width: tile.width, + height: tile.height, + loading: 'lazy', + onLoad: tileLoaded, + alt: '', + style: { + position: 'absolute', + left: tile.left, + top: tile.top, + willChange: 'transform', + transformOrigin: 'top left', + opacity: 1 + } + }); +}; + +var Map = /*#__PURE__*/function (_Component) { + _inheritsLoose(Map, _Component); + + function Map(props) { + var _ref2, _props$defaultZoom, _ref3, _props$defaultCenter, _ref4, _props$width, _ref5, _props$height; + + var _this; + + _this = _Component.call(this, props) || this; + + _defineProperty(_assertThisInitialized(_this), "_containerRef", void 0); + + _defineProperty(_assertThisInitialized(_this), "_mousePosition", void 0); + + _defineProperty(_assertThisInitialized(_this), "_loadTracker", void 0); + + _defineProperty(_assertThisInitialized(_this), "_dragStart", null); + + _defineProperty(_assertThisInitialized(_this), "_mouseDown", false); + + _defineProperty(_assertThisInitialized(_this), "_moveEvents", []); + + _defineProperty(_assertThisInitialized(_this), "_lastClick", null); + + _defineProperty(_assertThisInitialized(_this), "_lastTap", null); + + _defineProperty(_assertThisInitialized(_this), "_lastWheel", null); + + _defineProperty(_assertThisInitialized(_this), "_touchStartPixel", null); + + _defineProperty(_assertThisInitialized(_this), "_touchStartMidPoint", null); + + _defineProperty(_assertThisInitialized(_this), "_touchStartDistance", null); + + _defineProperty(_assertThisInitialized(_this), "_secondTouchEnd", null); + + _defineProperty(_assertThisInitialized(_this), "_warningClearTimeout", null); + + _defineProperty(_assertThisInitialized(_this), "_isAnimating", false); + + _defineProperty(_assertThisInitialized(_this), "_animationStart", null); + + _defineProperty(_assertThisInitialized(_this), "_animationEnd", null); + + _defineProperty(_assertThisInitialized(_this), "_zoomStart", null); + + _defineProperty(_assertThisInitialized(_this), "_centerTarget", null); + + _defineProperty(_assertThisInitialized(_this), "_zoomTarget", null); + + _defineProperty(_assertThisInitialized(_this), "_zoomAround", null); + + _defineProperty(_assertThisInitialized(_this), "_animFrame", null); + + _defineProperty(_assertThisInitialized(_this), "_boundsSynced", false); + + _defineProperty(_assertThisInitialized(_this), "_minMaxCache", null); + + _defineProperty(_assertThisInitialized(_this), "_lastZoom", void 0); + + _defineProperty(_assertThisInitialized(_this), "_lastCenter", void 0); + + _defineProperty(_assertThisInitialized(_this), "_centerStart", void 0); + + _defineProperty(_assertThisInitialized(_this), "_resizeObserver", null); + + _defineProperty(_assertThisInitialized(_this), "updateWidthHeight", function () { + if (_this._containerRef) { + var rect = _this._containerRef.getBoundingClientRect(); + + if (rect && rect.width > 0 && rect.height > 0) { + _this.setState({ + width: rect.width, + height: rect.height + }); + + return true; + } + } + + return false; + }); + + _defineProperty(_assertThisInitialized(_this), "wa", function () { + var _window; + + return (_window = window).addEventListener.apply(_window, arguments); + }); + + _defineProperty(_assertThisInitialized(_this), "wr", function () { + var _window2; + + return (_window2 = window).removeEventListener.apply(_window2, arguments); + }); + + _defineProperty(_assertThisInitialized(_this), "bindMouseEvents", function () { + _this.wa('mousedown', _this.handleMouseDown); + + _this.wa('mouseup', _this.handleMouseUp); + + _this.wa('mousemove', _this.handleMouseMove); + }); + + _defineProperty(_assertThisInitialized(_this), "bindTouchEvents", function () { + _this.wa('touchstart', _this.handleTouchStart, { + passive: false + }); + + _this.wa('touchmove', _this.handleTouchMove, { + passive: false + }); + + _this.wa('touchend', _this.handleTouchEnd, { + passive: false + }); + }); + + _defineProperty(_assertThisInitialized(_this), "unbindMouseEvents", function () { + _this.wr('mousedown', _this.handleMouseDown); + + _this.wr('mouseup', _this.handleMouseUp); + + _this.wr('mousemove', _this.handleMouseMove); + }); + + _defineProperty(_assertThisInitialized(_this), "unbindTouchEvents", function () { + _this.wr('touchstart', _this.handleTouchStart); + + _this.wr('touchmove', _this.handleTouchMove); + + _this.wr('touchend', _this.handleTouchEnd); + }); + + _defineProperty(_assertThisInitialized(_this), "bindResizeEvent", function () { + _this.wa('resize', _this.updateWidthHeight); + }); + + _defineProperty(_assertThisInitialized(_this), "unbindResizeEvent", function () { + _this.wr('resize', _this.updateWidthHeight); + }); + + _defineProperty(_assertThisInitialized(_this), "bindWheelEvent", function () { + if (_this._containerRef) { + _this._containerRef.addEventListener('wheel', _this.handleWheel, { + passive: false + }); + } + }); + + _defineProperty(_assertThisInitialized(_this), "unbindWheelEvent", function () { + if (_this._containerRef) { + _this._containerRef.removeEventListener('wheel', _this.handleWheel); + } + }); + + _defineProperty(_assertThisInitialized(_this), "setCenterZoomTarget", function (center, zoom, fromProps, zoomAround, animationDuration) { + if (fromProps === void 0) { + fromProps = false; + } + + if (zoomAround === void 0) { + zoomAround = null; + } + + if (animationDuration === void 0) { + animationDuration = ANIMATION_TIME; + } + + if (_this.props.animate && (!fromProps || _this.distanceInScreens(center, zoom, _this.state.center, _this.state.zoom) <= _this.props.animateMaxScreens)) { + if (_this._isAnimating) { + cancelAnimationFrame(_this._animFrame); + + var _this$animationStep = _this.animationStep(performanceNow()), + centerStep = _this$animationStep.centerStep, + zoomStep = _this$animationStep.zoomStep; + + _this._centerStart = centerStep; + _this._zoomStart = zoomStep; + } else { + _this._isAnimating = true; + _this._centerStart = _this.limitCenterAtZoom([_this._lastCenter[0], _this._lastCenter[1]], _this._lastZoom); + _this._zoomStart = _this._lastZoom; + + _this.onAnimationStart(); + } + + _this._animationStart = performanceNow(); + _this._animationEnd = _this._animationStart + animationDuration; + + if (zoomAround) { + _this._zoomAround = zoomAround; + _this._centerTarget = _this.calculateZoomCenter(_this._lastCenter, zoomAround, _this._lastZoom, zoom); + } else { + _this._zoomAround = null; + _this._centerTarget = center; + } + + _this._zoomTarget = zoom; + _this._animFrame = requestAnimationFrame(_this.animate); + } else { + _this.stopAnimating(); + + if (zoomAround) { + var _center = _this.calculateZoomCenter(_this._lastCenter, zoomAround, _this._lastZoom, zoom); + + _this.setCenterZoom(_center, zoom, fromProps); + } else { + _this.setCenterZoom(center || _this.state.center, zoom, fromProps); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "setCenterZoomForChildren", function (center, zoom) { + _this.setCenterZoomTarget(center || _this.state.center, zoom || _this.state.zoom, true); + }); + + _defineProperty(_assertThisInitialized(_this), "distanceInScreens", function (centerTarget, zoomTarget, center, zoom) { + var _this$state = _this.state, + width = _this$state.width, + height = _this$state.height; + + var l1 = _this.latLngToPixel(center, center, zoom); + + var l2 = _this.latLngToPixel(centerTarget, center, zoom); + + var z1 = _this.latLngToPixel(center, center, zoomTarget); + + var z2 = _this.latLngToPixel(centerTarget, center, zoomTarget); + + var w = (Math.abs(l1[0] - l2[0]) + Math.abs(z1[0] - z2[0])) / 2 / width; + var h = (Math.abs(l1[1] - l2[1]) + Math.abs(z1[1] - z2[1])) / 2 / height; + return Math.sqrt(w * w + h * h); + }); + + _defineProperty(_assertThisInitialized(_this), "animationStep", function (timestamp) { + if (!_this._animationEnd || !_this._animationStart || !_this._zoomTarget || !_this._zoomStart || !_this._centerStart || !_this._centerTarget) { + return { + centerStep: _this.state.center, + zoomStep: _this.state.zoom + }; + } + + var length = _this._animationEnd - _this._animationStart; + var progress = Math.max(timestamp - _this._animationStart, 0); + var percentage = easeOutQuad(progress / length); + var zoomDiff = (_this._zoomTarget - _this._zoomStart) * percentage; + var zoomStep = _this._zoomStart + zoomDiff; + + if (_this._zoomAround) { + var centerStep = _this.calculateZoomCenter(_this._centerStart, _this._zoomAround, _this._zoomStart, zoomStep); + + return { + centerStep: centerStep, + zoomStep: zoomStep + }; + } else { + var _centerStep = [_this._centerStart[0] + (_this._centerTarget[0] - _this._centerStart[0]) * percentage, _this._centerStart[1] + (_this._centerTarget[1] - _this._centerStart[1]) * percentage]; + return { + centerStep: _centerStep, + zoomStep: zoomStep + }; + } + }); + + _defineProperty(_assertThisInitialized(_this), "animate", function (timestamp) { + if (!_this._animationEnd || timestamp >= _this._animationEnd) { + _this._isAnimating = false; + + _this.setCenterZoom(_this._centerTarget, _this._zoomTarget, true); + + _this.onAnimationStop(); + } else { + var _this$animationStep2 = _this.animationStep(timestamp), + centerStep = _this$animationStep2.centerStep, + zoomStep = _this$animationStep2.zoomStep; + + _this.setCenterZoom(centerStep, zoomStep); + + _this._animFrame = requestAnimationFrame(_this.animate); + } + }); + + _defineProperty(_assertThisInitialized(_this), "stopAnimating", function () { + if (_this._isAnimating) { + _this._isAnimating = false; + + _this.onAnimationStop(); + + cancelAnimationFrame(_this._animFrame); + } + }); + + _defineProperty(_assertThisInitialized(_this), "limitCenterAtZoom", function (center, zoom) { + var minMax = _this.getBoundsMinMax(zoom || _this.state.zoom); + + return [Math.max(Math.min(!center || isNaN(center[0]) ? _this.state.center[0] : center[0], minMax[1]), minMax[0]), Math.max(Math.min(!center || isNaN(center[1]) ? _this.state.center[1] : center[1], minMax[3]), minMax[2])]; + }); + + _defineProperty(_assertThisInitialized(_this), "onAnimationStart", function () { + _this.props.onAnimationStart && _this.props.onAnimationStart(); + }); + + _defineProperty(_assertThisInitialized(_this), "onAnimationStop", function () { + _this.props.onAnimationStop && _this.props.onAnimationStop(); + }); + + _defineProperty(_assertThisInitialized(_this), "setCenterZoom", function (center, zoom, animationEnded) { + if (animationEnded === void 0) { + animationEnded = false; + } + + var limitedCenter = _this.limitCenterAtZoom(center, zoom); + + if (zoom && Math.round(_this.state.zoom) !== Math.round(zoom)) { + var tileValues = _this.tileValues(_this.state); + + var nextValues = _this.tileValues({ + center: limitedCenter, + zoom: zoom, + width: _this.state.width, + height: _this.state.height + }); + + var oldTiles = _this.state.oldTiles; + + _this.setState({ + oldTiles: oldTiles.filter(function (o) { + return o.roundedZoom !== tileValues.roundedZoom; + }).concat(tileValues) + }, NOOP); + + var loadTracker = {}; + + for (var x = nextValues.tileMinX; x <= nextValues.tileMaxX; x++) { + for (var y = nextValues.tileMinY; y <= nextValues.tileMaxY; y++) { + var key = x + "-" + y + "-" + nextValues.roundedZoom; + loadTracker[key] = false; + } + } + + _this._loadTracker = loadTracker; + } + + _this.setState({ + center: limitedCenter, + zoom: zoom || _this.state.zoom + }, NOOP); + + var maybeZoom = _this.props.zoom ? _this.props.zoom : _this._lastZoom; + var maybeCenter = _this.props.center ? _this.props.center : _this._lastCenter; + + if (zoom && (animationEnded || Math.abs(maybeZoom - zoom) > 0.001 || Math.abs(maybeCenter[0] - limitedCenter[0]) > 0.00001 || Math.abs(maybeCenter[1] - limitedCenter[1]) > 0.00001)) { + _this._lastZoom = zoom; + _this._lastCenter = [].concat(limitedCenter); + + _this.syncToProps(limitedCenter, zoom); + } + }); + + _defineProperty(_assertThisInitialized(_this), "getBoundsMinMax", function (zoom) { + if (_this.props.limitBounds === 'center') { + return absoluteMinMax; + } + + var _this$state2 = _this.state, + width = _this$state2.width, + height = _this$state2.height; + + if (_this._minMaxCache && _this._minMaxCache[0] === zoom && _this._minMaxCache[1] === width && _this._minMaxCache[2] === height) { + return _this._minMaxCache[3]; + } + + var pixelsAtZoom = Math.pow(2, zoom) * 256; + var minLng = width > pixelsAtZoom ? 0 : tile2lng(width / 512, zoom); + var minLat = height > pixelsAtZoom ? 0 : tile2lat(Math.pow(2, zoom) - height / 512, zoom); + var maxLng = width > pixelsAtZoom ? 0 : tile2lng(Math.pow(2, zoom) - width / 512, zoom); + var maxLat = height > pixelsAtZoom ? 0 : tile2lat(height / 512, zoom); + var minMax = [minLat, maxLat, minLng, maxLng]; + _this._minMaxCache = [zoom, width, height, minMax]; + return minMax; + }); + + _defineProperty(_assertThisInitialized(_this), "tileLoaded", function (key) { + if (_this._loadTracker && key in _this._loadTracker) { + _this._loadTracker[key] = true; + var unloadedCount = Object.values(_this._loadTracker).filter(function (v) { + return !v; + }).length; + + if (unloadedCount === 0) { + _this.setState({ + oldTiles: [] + }, NOOP); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleTouchStart", function (event) { + if (!_this._containerRef) { + return; + } + + if (event.target && parentHasClass(event.target, 'pigeon-drag-block')) { + return; + } + + if (event.touches.length === 1) { + var touch = event.touches[0]; + var pixel = getMousePixel(_this._containerRef, touch); + + if (_this.coordsInside(pixel)) { + _this._touchStartPixel = [pixel]; + + if (!_this.props.twoFingerDrag) { + _this.stopAnimating(); + + if (_this._lastTap && performanceNow() - _this._lastTap < DOUBLE_CLICK_DELAY) { + event.preventDefault(); + + var latLngNow = _this.pixelToLatLng(_this._touchStartPixel[0]); + + _this.setCenterZoomTarget(null, Math.max(_this.props.minZoom, Math.min(_this.state.zoom + 1, _this.props.maxZoom)), false, latLngNow); + } else { + _this._lastTap = performanceNow(); + + _this.trackMoveEvents(pixel); + } + } + } + } else if (event.touches.length === 2 && _this._touchStartPixel) { + event.preventDefault(); + + _this.stopTrackingMoveEvents(); + + if (_this.state.pixelDelta || _this.state.zoomDelta) { + _this.sendDeltaChange(); + } + + var t1 = getMousePixel(_this._containerRef, event.touches[0]); + var t2 = getMousePixel(_this._containerRef, event.touches[1]); + _this._touchStartPixel = [t1, t2]; + _this._touchStartMidPoint = [(t1[0] + t2[0]) / 2, (t1[1] + t2[1]) / 2]; + _this._touchStartDistance = Math.sqrt(Math.pow(t1[0] - t2[0], 2) + Math.pow(t1[1] - t2[1], 2)); + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleTouchMove", function (event) { + if (!_this._containerRef) { + _this._touchStartPixel = null; + return; + } + + if (event.touches.length === 1 && _this._touchStartPixel) { + var touch = event.touches[0]; + var pixel = getMousePixel(_this._containerRef, touch); + + if (_this.props.twoFingerDrag) { + if (_this.coordsInside(pixel)) { + _this.showWarning('fingers'); + } + } else { + event.preventDefault(); + + _this.trackMoveEvents(pixel); + + _this.setState({ + pixelDelta: [pixel[0] - _this._touchStartPixel[0][0], pixel[1] - _this._touchStartPixel[0][1]] + }, NOOP); + } + } else if (event.touches.length === 2 && _this._touchStartPixel && _this._touchStartMidPoint && _this._touchStartDistance) { + var _this$state3 = _this.state, + width = _this$state3.width, + height = _this$state3.height, + zoom = _this$state3.zoom; + event.preventDefault(); + var t1 = getMousePixel(_this._containerRef, event.touches[0]); + var t2 = getMousePixel(_this._containerRef, event.touches[1]); + var midPoint = [(t1[0] + t2[0]) / 2, (t1[1] + t2[1]) / 2]; + var midPointDiff = [midPoint[0] - _this._touchStartMidPoint[0], midPoint[1] - _this._touchStartMidPoint[1]]; + var distance = Math.sqrt(Math.pow(t1[0] - t2[0], 2) + Math.pow(t1[1] - t2[1], 2)); + var zoomDelta = Math.max(_this.props.minZoom, Math.min(_this.props.maxZoom, zoom + Math.log2(distance / _this._touchStartDistance))) - zoom; + var scale = Math.pow(2, zoomDelta); + var centerDiffDiff = [(width / 2 - midPoint[0]) * (scale - 1), (height / 2 - midPoint[1]) * (scale - 1)]; + + _this.setState({ + zoomDelta: zoomDelta, + pixelDelta: [centerDiffDiff[0] + midPointDiff[0] * scale, centerDiffDiff[1] + midPointDiff[1] * scale] + }, NOOP); + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleTouchEnd", function (event) { + if (!_this._containerRef) { + _this._touchStartPixel = null; + return; + } + + if (_this._touchStartPixel) { + var _this$props = _this.props, + zoomSnap = _this$props.zoomSnap, + twoFingerDrag = _this$props.twoFingerDrag, + minZoom = _this$props.minZoom, + maxZoom = _this$props.maxZoom; + var zoomDelta = _this.state.zoomDelta; + + var _this$sendDeltaChange = _this.sendDeltaChange(), + center = _this$sendDeltaChange.center, + zoom = _this$sendDeltaChange.zoom; + + if (event.touches.length === 0) { + if (twoFingerDrag) { + _this.clearWarning(); + } else { + var oldTouchPixel = _this._touchStartPixel[0]; + var newTouchPixel = getMousePixel(_this._containerRef, event.changedTouches[0]); + + if (Math.abs(oldTouchPixel[0] - newTouchPixel[0]) > CLICK_TOLERANCE || Math.abs(oldTouchPixel[1] - newTouchPixel[1]) > CLICK_TOLERANCE) { + if (!_this._secondTouchEnd || performanceNow() - _this._secondTouchEnd > PINCH_RELEASE_THROW_DELAY) { + event.preventDefault(); + + _this.throwAfterMoving(newTouchPixel, center, zoom); + } + } + + _this._touchStartPixel = null; + _this._secondTouchEnd = null; + } + } else if (event.touches.length === 1) { + event.preventDefault(); + var touch = getMousePixel(_this._containerRef, event.touches[0]); + _this._secondTouchEnd = performanceNow(); + _this._touchStartPixel = [touch]; + + _this.trackMoveEvents(touch); + + if (zoomSnap) { + var latLng = _this._touchStartMidPoint ? _this.pixelToLatLng(_this._touchStartMidPoint) : _this.state.center; + var zoomTarget; + + if (twoFingerDrag && Math.round(_this.state.zoom) === Math.round(_this.state.zoom + zoomDelta)) { + zoomTarget = Math.round(_this.state.zoom); + } else { + zoomTarget = zoomDelta > 0 ? Math.ceil(_this.state.zoom) : Math.floor(_this.state.zoom); + } + + var _zoom = Math.max(minZoom, Math.min(zoomTarget, maxZoom)); + + _this.setCenterZoomTarget(latLng, _zoom, false, latLng); + } + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleMouseDown", function (event) { + if (!_this._containerRef) { + return; + } + + var pixel = getMousePixel(_this._containerRef, event); + + if (event.button === 0 && (!event.target || !parentHasClass(event.target, 'pigeon-drag-block')) && _this.coordsInside(pixel)) { + _this.stopAnimating(); + + event.preventDefault(); + + if (_this._lastClick && performanceNow() - _this._lastClick < DOUBLE_CLICK_DELAY) { + if (!parentHasClass(event.target, 'pigeon-click-block')) { + var latLngNow = _this.pixelToLatLng(_this._mousePosition || pixel); + + _this.setCenterZoomTarget(null, Math.max(_this.props.minZoom, Math.min(_this.state.zoom + 1, _this.props.maxZoom)), false, latLngNow); + } + } else { + _this._lastClick = performanceNow(); + _this._mouseDown = true; + _this._dragStart = pixel; + + _this.trackMoveEvents(pixel); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleMouseMove", function (event) { + if (!_this._containerRef) { + return; + } + + _this._mousePosition = getMousePixel(_this._containerRef, event); + + if (_this._mouseDown && _this._dragStart) { + _this.trackMoveEvents(_this._mousePosition); + + _this.setState({ + pixelDelta: [_this._mousePosition[0] - _this._dragStart[0], _this._mousePosition[1] - _this._dragStart[1]] + }, NOOP); + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleMouseUp", function (event) { + if (!_this._containerRef) { + _this._mouseDown = false; + return; + } + + var pixelDelta = _this.state.pixelDelta; + + if (_this._mouseDown) { + _this._mouseDown = false; + var pixel = getMousePixel(_this._containerRef, event); + + if (_this.props.onClick && (!event.target || !parentHasClass(event.target, 'pigeon-click-block')) && (!pixelDelta || Math.abs(pixelDelta[0]) + Math.abs(pixelDelta[1]) <= CLICK_TOLERANCE)) { + var latLng = _this.pixelToLatLng(pixel); + + _this.props.onClick({ + event: event, + latLng: latLng, + pixel: pixel + }); + + _this.setState({ + pixelDelta: undefined + }, NOOP); + } else { + var _this$sendDeltaChange2 = _this.sendDeltaChange(), + center = _this$sendDeltaChange2.center, + zoom = _this$sendDeltaChange2.zoom; + + _this.throwAfterMoving(pixel, center, zoom); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "stopTrackingMoveEvents", function () { + _this._moveEvents = []; + }); + + _defineProperty(_assertThisInitialized(_this), "trackMoveEvents", function (coords) { + var timestamp = performanceNow(); + + if (_this._moveEvents.length === 0 || timestamp - _this._moveEvents[_this._moveEvents.length - 1].timestamp > 40) { + _this._moveEvents.push({ + timestamp: timestamp, + coords: coords + }); + + if (_this._moveEvents.length > 2) { + _this._moveEvents.shift(); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "throwAfterMoving", function (coords, center, zoom) { + var _this$state4 = _this.state, + width = _this$state4.width, + height = _this$state4.height; + var animate = _this.props.animate; + var timestamp = performanceNow(); + + var lastEvent = _this._moveEvents.shift(); + + if (lastEvent && animate) { + var deltaMs = Math.max(timestamp - lastEvent.timestamp, 1); + var delta = [(coords[0] - lastEvent.coords[0]) / deltaMs * 120, (coords[1] - lastEvent.coords[1]) / deltaMs * 120]; + var distance = Math.sqrt(delta[0] * delta[0] + delta[1] * delta[1]); + + if (distance > MIN_DRAG_FOR_THROW) { + var diagonal = Math.sqrt(width * width + height * height); + var throwTime = DIAGONAL_THROW_TIME * distance / diagonal; + var lng = tile2lng(lng2tile(center[1], zoom) - delta[0] / 256.0, zoom); + var lat = tile2lat(lat2tile(center[0], zoom) - delta[1] / 256.0, zoom); + + _this.setCenterZoomTarget([lat, lng], zoom, false, null, throwTime); + } + } + + _this.stopTrackingMoveEvents(); + }); + + _defineProperty(_assertThisInitialized(_this), "sendDeltaChange", function () { + var _this$state5 = _this.state, + center = _this$state5.center, + zoom = _this$state5.zoom, + pixelDelta = _this$state5.pixelDelta, + zoomDelta = _this$state5.zoomDelta; + var lat = center[0]; + var lng = center[1]; + + if (pixelDelta || zoomDelta !== 0) { + lng = tile2lng(lng2tile(center[1], zoom + zoomDelta) - (pixelDelta ? pixelDelta[0] / 256.0 : 0), zoom + zoomDelta); + lat = tile2lat(lat2tile(center[0], zoom + zoomDelta) - (pixelDelta ? pixelDelta[1] / 256.0 : 0), zoom + zoomDelta); + + _this.setCenterZoom([lat, lng], zoom + zoomDelta); + } + + _this.setState({ + pixelDelta: undefined, + zoomDelta: 0 + }, NOOP); + + return { + center: _this.limitCenterAtZoom([lat, lng], zoom + zoomDelta), + zoom: zoom + zoomDelta + }; + }); + + _defineProperty(_assertThisInitialized(_this), "getBounds", function (center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.zoomPlusDelta(); + } + + var _this$state6 = _this.state, + width = _this$state6.width, + height = _this$state6.height; + return { + ne: _this.pixelToLatLng([width - 1, 0], center, zoom), + sw: _this.pixelToLatLng([0, height - 1], center, zoom) + }; + }); + + _defineProperty(_assertThisInitialized(_this), "syncToProps", function (center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.state.zoom; + } + + var onBoundsChanged = _this.props.onBoundsChanged; + + if (onBoundsChanged) { + var bounds = _this.getBounds(center, zoom); + + onBoundsChanged({ + center: center, + zoom: zoom, + bounds: bounds, + initial: !_this._boundsSynced + }); + _this._boundsSynced = true; + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleWheel", function (event) { + var _this$props2 = _this.props, + mouseEvents = _this$props2.mouseEvents, + metaWheelZoom = _this$props2.metaWheelZoom, + zoomSnap = _this$props2.zoomSnap, + animate = _this$props2.animate; + + if (!mouseEvents) { + return; + } + + if (!metaWheelZoom || event.metaKey || event.ctrlKey) { + event.preventDefault(); + var addToZoom = -event.deltaY / SCROLL_PIXELS_FOR_ZOOM_LEVEL; + + if (!zoomSnap && _this._zoomTarget) { + var stillToAdd = _this._zoomTarget - _this.state.zoom; + + _this.zoomAroundMouse(addToZoom + stillToAdd, event); + } else { + if (animate) { + _this.zoomAroundMouse(addToZoom, event); + } else { + if (!_this._lastWheel || performanceNow() - _this._lastWheel > ANIMATION_TIME) { + _this._lastWheel = performanceNow(); + + _this.zoomAroundMouse(addToZoom, event); + } + } + } + } else { + _this.showWarning('wheel'); + } + }); + + _defineProperty(_assertThisInitialized(_this), "showWarning", function (warningType) { + if (!_this.state.showWarning || _this.state.warningType !== warningType) { + _this.setState({ + showWarning: true, + warningType: warningType + }); + } + + if (_this._warningClearTimeout) { + window.clearTimeout(_this._warningClearTimeout); + } + + _this._warningClearTimeout = window.setTimeout(_this.clearWarning, WARNING_DISPLAY_TIMEOUT); + }); + + _defineProperty(_assertThisInitialized(_this), "clearWarning", function () { + if (_this.state.showWarning) { + _this.setState({ + showWarning: false + }); + } + }); + + _defineProperty(_assertThisInitialized(_this), "zoomAroundMouse", function (zoomDiff, event) { + if (!_this._containerRef) { + return; + } + + var zoom = _this.state.zoom; + var _this$props3 = _this.props, + minZoom = _this$props3.minZoom, + maxZoom = _this$props3.maxZoom, + zoomSnap = _this$props3.zoomSnap; + _this._mousePosition = getMousePixel(_this._containerRef, event); + + if (!_this._mousePosition || zoom === minZoom && zoomDiff < 0 || zoom === maxZoom && zoomDiff > 0) { + return; + } + + var latLngNow = _this.pixelToLatLng(_this._mousePosition); + + var zoomTarget = zoom + zoomDiff; + + if (zoomSnap) { + zoomTarget = zoomDiff < 0 ? Math.floor(zoomTarget) : Math.ceil(zoomTarget); + } + + zoomTarget = Math.max(minZoom, Math.min(zoomTarget, maxZoom)); + + _this.setCenterZoomTarget(null, zoomTarget, false, latLngNow); + }); + + _defineProperty(_assertThisInitialized(_this), "zoomPlusDelta", function () { + return _this.state.zoom + _this.state.zoomDelta; + }); + + _defineProperty(_assertThisInitialized(_this), "pixelToLatLng", function (pixel, center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.zoomPlusDelta(); + } + + var _this$state7 = _this.state, + width = _this$state7.width, + height = _this$state7.height, + pixelDelta = _this$state7.pixelDelta; + var pointDiff = [(pixel[0] - width / 2 - (pixelDelta ? pixelDelta[0] : 0)) / 256.0, (pixel[1] - height / 2 - (pixelDelta ? pixelDelta[1] : 0)) / 256.0]; + var tileX = lng2tile(center[1], zoom) + pointDiff[0]; + var tileY = lat2tile(center[0], zoom) + pointDiff[1]; + return [Math.max(absoluteMinMax[0], Math.min(absoluteMinMax[1], tile2lat(tileY, zoom))), Math.max(absoluteMinMax[2], Math.min(absoluteMinMax[3], tile2lng(tileX, zoom)))]; + }); + + _defineProperty(_assertThisInitialized(_this), "latLngToPixel", function (latLng, center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.zoomPlusDelta(); + } + + var _this$state8 = _this.state, + width = _this$state8.width, + height = _this$state8.height, + pixelDelta = _this$state8.pixelDelta; + var tileCenterX = lng2tile(center[1], zoom); + var tileCenterY = lat2tile(center[0], zoom); + var tileX = lng2tile(latLng[1], zoom); + var tileY = lat2tile(latLng[0], zoom); + return [(tileX - tileCenterX) * 256.0 + width / 2 + (pixelDelta ? pixelDelta[0] : 0), (tileY - tileCenterY) * 256.0 + height / 2 + (pixelDelta ? pixelDelta[1] : 0)]; + }); + + _defineProperty(_assertThisInitialized(_this), "calculateZoomCenter", function (center, coords, oldZoom, newZoom) { + var _this$state9 = _this.state, + width = _this$state9.width, + height = _this$state9.height; + + var pixelBefore = _this.latLngToPixel(coords, center, oldZoom); + + var pixelAfter = _this.latLngToPixel(coords, center, newZoom); + + var newCenter = _this.pixelToLatLng([width / 2 + pixelAfter[0] - pixelBefore[0], height / 2 + pixelAfter[1] - pixelBefore[1]], center, newZoom); + + return _this.limitCenterAtZoom(newCenter, newZoom); + }); + + _defineProperty(_assertThisInitialized(_this), "setRef", function (dom) { + _this._containerRef = dom; + }); + + _this.syncToProps = debounce(_this.syncToProps, DEBOUNCE_DELAY); + _this._lastZoom = (_ref2 = (_props$defaultZoom = props.defaultZoom) !== null && _props$defaultZoom !== void 0 ? _props$defaultZoom : props.zoom) !== null && _ref2 !== void 0 ? _ref2 : 14; + _this._lastCenter = (_ref3 = (_props$defaultCenter = props.defaultCenter) !== null && _props$defaultCenter !== void 0 ? _props$defaultCenter : props.center) !== null && _ref3 !== void 0 ? _ref3 : [0, 0]; + _this.state = { + zoom: _this._lastZoom, + center: _this._lastCenter, + width: (_ref4 = (_props$width = props.width) !== null && _props$width !== void 0 ? _props$width : props.defaultWidth) !== null && _ref4 !== void 0 ? _ref4 : -1, + height: (_ref5 = (_props$height = props.height) !== null && _props$height !== void 0 ? _props$height : props.defaultHeight) !== null && _ref5 !== void 0 ? _ref5 : -1, + zoomDelta: 0, + pixelDelta: undefined, + oldTiles: [], + showWarning: false, + warningType: undefined + }; + return _this; + } + + var _proto = Map.prototype; + + _proto.componentDidMount = function componentDidMount() { + var _this2 = this; + + this.props.mouseEvents && this.bindMouseEvents(); + this.props.touchEvents && this.bindTouchEvents(); + + if (!this.props.width || !this.props.height) { + if (!this.updateWidthHeight()) { + requestAnimationFrame(this.updateWidthHeight); + } + + this.bindResizeEvent(); + } + + this.bindWheelEvent(); + this.syncToProps(); + + if (typeof window.ResizeObserver !== 'undefined') { + this._resizeObserver = new window.ResizeObserver(function () { + _this2.updateWidthHeight(); + }); + + this._resizeObserver.observe(this._containerRef); + } + }; + + _proto.componentWillUnmount = function componentWillUnmount() { + this.props.mouseEvents && this.unbindMouseEvents(); + this.props.touchEvents && this.unbindTouchEvents(); + this.unbindWheelEvent(); + + if (!this.props.width || !this.props.height) { + this.unbindResizeEvent(); + } + + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + } + }; + + _proto.componentDidUpdate = function componentDidUpdate(prevProps) { + var _prevProps$center; + + if (this.props.mouseEvents !== prevProps.mouseEvents) { + this.props.mouseEvents ? this.bindMouseEvents() : this.unbindMouseEvents(); + } + + if (this.props.touchEvents !== prevProps.touchEvents) { + this.props.touchEvents ? this.bindTouchEvents() : this.unbindTouchEvents(); + } + + if (this.props.width && this.props.width !== prevProps.width) { + this.setState({ + width: this.props.width + }); + } + + if (this.props.height && this.props.height !== prevProps.height) { + this.setState({ + height: this.props.height + }); + } + + if (!this.props.center && !this.props.zoom) { + return; + } + + if ((!this.props.center || this.props.center[0] === (prevProps === null || prevProps === void 0 ? void 0 : (_prevProps$center = prevProps.center) === null || _prevProps$center === void 0 ? void 0 : _prevProps$center[0]) && this.props.center[1] === prevProps.center[1]) && this.props.zoom === prevProps.zoom) { + return; + } + + var currentCenter = this._isAnimating ? this._centerTarget : this.state.center; + var currentZoom = this._isAnimating ? this._zoomTarget : this.state.zoom; + + if (currentCenter && currentZoom) { + var _this$props$center, _this$props$zoom; + + var nextCenter = (_this$props$center = this.props.center) !== null && _this$props$center !== void 0 ? _this$props$center : currentCenter; + var nextZoom = (_this$props$zoom = this.props.zoom) !== null && _this$props$zoom !== void 0 ? _this$props$zoom : currentZoom; + + if (Math.abs(nextZoom - currentZoom) > 0.001 || Math.abs(nextCenter[0] - currentCenter[0]) > 0.0001 || Math.abs(nextCenter[1] - currentCenter[1]) > 0.0001) { + this.setCenterZoomTarget(nextCenter, nextZoom, true); + } + } + }; + + _proto.coordsInside = function coordsInside(pixel) { + var _this$state10 = this.state, + width = _this$state10.width, + height = _this$state10.height; + + if (pixel[0] < 0 || pixel[1] < 0 || pixel[0] >= width || pixel[1] >= height) { + return false; + } + + var parent = this._containerRef; + + if (parent) { + var pos = parentPosition(parent); + var element = document.elementFromPoint(pixel[0] + pos.x, pixel[1] + pos.y); + return parent === element || parent.contains(element); + } else { + return false; + } + }; + + _proto.tileValues = function tileValues(_ref6) { + var center = _ref6.center, + zoom = _ref6.zoom, + pixelDelta = _ref6.pixelDelta, + zoomDelta = _ref6.zoomDelta, + width = _ref6.width, + height = _ref6.height; + var roundedZoom = Math.round(zoom + (zoomDelta || 0)); + var zoomDiff = zoom + (zoomDelta || 0) - roundedZoom; + var scale = Math.pow(2, zoomDiff); + var scaleWidth = width / scale; + var scaleHeight = height / scale; + var tileCenterX = lng2tile(center[1], roundedZoom) - (pixelDelta ? pixelDelta[0] / 256.0 / scale : 0); + var tileCenterY = lat2tile(center[0], roundedZoom) - (pixelDelta ? pixelDelta[1] / 256.0 / scale : 0); + var halfWidth = scaleWidth / 2 / 256.0; + var halfHeight = scaleHeight / 2 / 256.0; + var tileMinX = Math.floor(tileCenterX - halfWidth); + var tileMaxX = Math.floor(tileCenterX + halfWidth); + var tileMinY = Math.floor(tileCenterY - halfHeight); + var tileMaxY = Math.floor(tileCenterY + halfHeight); + return { + tileMinX: tileMinX, + tileMaxX: tileMaxX, + tileMinY: tileMinY, + tileMaxY: tileMaxY, + tileCenterX: tileCenterX, + tileCenterY: tileCenterY, + roundedZoom: roundedZoom, + zoomDelta: zoomDelta || 0, + scaleWidth: scaleWidth, + scaleHeight: scaleHeight, + scale: scale + }; + }; + + _proto.renderTiles = function renderTiles() { + var _this3 = this; + + var _this$state11 = this.state, + oldTiles = _this$state11.oldTiles, + width = _this$state11.width, + height = _this$state11.height; + var dprs = this.props.dprs; + var mapUrl = this.props.provider || osm; + + var _this$tileValues = this.tileValues(this.state), + tileMinX = _this$tileValues.tileMinX, + tileMaxX = _this$tileValues.tileMaxX, + tileMinY = _this$tileValues.tileMinY, + tileMaxY = _this$tileValues.tileMaxY, + tileCenterX = _this$tileValues.tileCenterX, + tileCenterY = _this$tileValues.tileCenterY, + roundedZoom = _this$tileValues.roundedZoom, + scaleWidth = _this$tileValues.scaleWidth, + scaleHeight = _this$tileValues.scaleHeight, + scale = _this$tileValues.scale; + + var tiles = []; + + for (var i = 0; i < oldTiles.length; i++) { + var old = oldTiles[i]; + var zoomDiff = old.roundedZoom - roundedZoom; + + if (Math.abs(zoomDiff) > 4 || zoomDiff === 0) { + continue; + } + + var pow = 1 / Math.pow(2, zoomDiff); + var xDiff = -(tileMinX - old.tileMinX * pow) * 256; + var yDiff = -(tileMinY - old.tileMinY * pow) * 256; + + var _xMin = Math.max(old.tileMinX, 0); + + var _yMin = Math.max(old.tileMinY, 0); + + var _xMax = Math.min(old.tileMaxX, Math.pow(2, old.roundedZoom) - 1); + + var _yMax = Math.min(old.tileMaxY, Math.pow(2, old.roundedZoom) - 1); + + for (var x = _xMin; x <= _xMax; x++) { + for (var y = _yMin; y <= _yMax; y++) { + tiles.push({ + key: x + "-" + y + "-" + old.roundedZoom, + url: mapUrl(x, y, old.roundedZoom), + srcSet: srcSet(dprs, mapUrl, x, y, old.roundedZoom), + left: xDiff + (x - old.tileMinX) * 256 * pow, + top: yDiff + (y - old.tileMinY) * 256 * pow, + width: 256 * pow, + height: 256 * pow, + active: false + }); + } + } + } + + var xMin = Math.max(tileMinX, 0); + var yMin = Math.max(tileMinY, 0); + var xMax = Math.min(tileMaxX, Math.pow(2, roundedZoom) - 1); + var yMax = Math.min(tileMaxY, Math.pow(2, roundedZoom) - 1); + + for (var _x = xMin; _x <= xMax; _x++) { + for (var _y = yMin; _y <= yMax; _y++) { + tiles.push({ + key: _x + "-" + _y + "-" + roundedZoom, + url: mapUrl(_x, _y, roundedZoom), + srcSet: srcSet(dprs, mapUrl, _x, _y, roundedZoom), + left: (_x - tileMinX) * 256, + top: (_y - tileMinY) * 256, + width: 256, + height: 256, + active: true + }); + } + } + + var boxStyle = { + width: scaleWidth, + height: scaleHeight, + position: 'absolute', + top: "calc((100% - " + height + "px) / 2)", + left: "calc((100% - " + width + "px) / 2)", + overflow: 'hidden', + willChange: 'transform', + transform: "scale(" + scale + ", " + scale + ")", + transformOrigin: 'top left' + }; + var boxClassname = this.props.boxClassname || 'pigeon-tiles-box'; + var left = -((tileCenterX - tileMinX) * 256 - scaleWidth / 2); + var top = -((tileCenterY - tileMinY) * 256 - scaleHeight / 2); + var tilesStyle = { + position: 'absolute', + width: (tileMaxX - tileMinX + 1) * 256, + height: (tileMaxY - tileMinY + 1) * 256, + willChange: 'transform', + transform: "translate(" + left + "px, " + top + "px)" + }; + var Tile = this.props.tileComponent; + return /*#__PURE__*/React__default["default"].createElement("div", { + style: boxStyle, + className: boxClassname + }, /*#__PURE__*/React__default["default"].createElement("div", { + className: "pigeon-tiles", + style: tilesStyle + }, tiles.map(function (tile) { + return /*#__PURE__*/React__default["default"].createElement(Tile, { + key: tile.key, + tile: tile, + tileLoaded: function tileLoaded() { + return _this3.tileLoaded(tile.key); + } + }); + }))); + }; + + _proto.renderOverlays = function renderOverlays() { + var _this4 = this; + + var _this$state12 = this.state, + width = _this$state12.width, + height = _this$state12.height, + center = _this$state12.center; + var mapState = { + bounds: this.getBounds(), + zoom: this.zoomPlusDelta(), + center: center, + width: width, + height: height + }; + var childrenWithProps = React__default["default"].Children.map(this.props.children, function (child) { + if (!child) { + return null; + } + + if (! /*#__PURE__*/React__default["default"].isValidElement(child)) { + return child; + } + + var _child$props = child.props, + anchor = _child$props.anchor, + position = _child$props.position, + offset = _child$props.offset; + + var c = _this4.latLngToPixel(anchor || position || center); + + return /*#__PURE__*/React__default["default"].cloneElement(child, { + left: c[0] - (offset ? offset[0] : 0), + top: c[1] - (offset ? offset[1] : 0), + latLngToPixel: _this4.latLngToPixel, + pixelToLatLng: _this4.pixelToLatLng, + setCenterZoom: _this4.setCenterZoomForChildren, + mapProps: _this4.props, + mapState: mapState + }); + }); + var childrenStyle = { + position: 'absolute', + width: width, + height: height, + top: "calc((100% - " + height + "px) / 2)", + left: "calc((100% - " + width + "px) / 2)" + }; + return /*#__PURE__*/React__default["default"].createElement("div", { + className: "pigeon-overlays", + style: childrenStyle + }, childrenWithProps); + }; + + _proto.renderAttribution = function renderAttribution() { + var _this$props4 = this.props, + attribution = _this$props4.attribution, + attributionPrefix = _this$props4.attributionPrefix; + + if (attribution === false) { + return null; + } + + var style = { + position: 'absolute', + bottom: 0, + right: 0, + fontSize: '11px', + padding: '2px 5px', + background: 'rgba(255, 255, 255, 0.7)', + fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif", + color: '#333' + }; + var linkStyle = { + color: '#0078A8', + textDecoration: 'none' + }; + return /*#__PURE__*/React__default["default"].createElement("div", { + key: "attr", + className: "pigeon-attribution", + style: style + }, attributionPrefix === false ? null : /*#__PURE__*/React__default["default"].createElement("span", null, attributionPrefix || /*#__PURE__*/React__default["default"].createElement("a", { + href: "https://pigeon-maps.js.org/", + style: linkStyle, + target: "_blank", + rel: "noreferrer noopener" + }, "Pigeon"), ' | '), attribution || /*#__PURE__*/React__default["default"].createElement("span", null, ' © ', /*#__PURE__*/React__default["default"].createElement("a", { + href: "https://www.openstreetmap.org/copyright", + style: linkStyle, + target: "_blank", + rel: "noreferrer noopener" + }, "OpenStreetMap"), ' contributors')); + }; + + _proto.renderWarning = function renderWarning() { + var _this$props5 = this.props, + metaWheelZoom = _this$props5.metaWheelZoom, + metaWheelZoomWarning = _this$props5.metaWheelZoomWarning, + twoFingerDrag = _this$props5.twoFingerDrag, + twoFingerDragWarning = _this$props5.twoFingerDragWarning, + warningZIndex = _this$props5.warningZIndex; + var _this$state13 = this.state, + showWarning = _this$state13.showWarning, + warningType = _this$state13.warningType, + width = _this$state13.width, + height = _this$state13.height; + + if (metaWheelZoom && metaWheelZoomWarning || twoFingerDrag && twoFingerDragWarning) { + var style = { + position: 'absolute', + top: 0, + left: 0, + width: width, + height: height, + overflow: 'hidden', + pointerEvents: 'none', + opacity: showWarning ? 100 : 0, + transition: 'opacity 300ms', + background: 'rgba(0,0,0,0.5)', + color: '#fff', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + fontSize: 22, + fontFamily: '"Arial", sans-serif', + textAlign: 'center', + zIndex: warningZIndex + }; + var meta = typeof window !== 'undefined' && window.navigator && window.navigator.platform.toUpperCase().indexOf('MAC') >= 0 ? '⌘' : 'ctrl'; + var warningText = warningType === 'fingers' ? twoFingerDragWarning : metaWheelZoomWarning; + return /*#__PURE__*/React__default["default"].createElement("div", { + className: "pigeon-overlay-warning", + style: style + }, warningText.replace('META', meta)); + } else { + return null; + } + }; + + _proto.render = function render() { + var _this$props6 = this.props, + touchEvents = _this$props6.touchEvents, + twoFingerDrag = _this$props6.twoFingerDrag; + var _this$state14 = this.state, + width = _this$state14.width, + height = _this$state14.height; + var containerStyle = { + width: this.props.width ? width : '100%', + height: this.props.height ? height : '100%', + position: 'relative', + display: 'inline-block', + overflow: 'hidden', + background: '#dddddd', + touchAction: touchEvents ? twoFingerDrag ? 'pan-x pan-y' : 'none' : 'auto' + }; + var hasSize = !!(width && height); + return /*#__PURE__*/React__default["default"].createElement("div", { + style: containerStyle, + ref: this.setRef, + dir: "ltr" + }, hasSize && this.renderTiles(), hasSize && this.renderOverlays(), hasSize && this.renderAttribution(), hasSize && this.renderWarning()); + }; + + return Map; +}(React.Component); + +_defineProperty(Map, "defaultProps", { + animate: true, + metaWheelZoom: false, + metaWheelZoomWarning: 'Use META + wheel to zoom!', + twoFingerDrag: false, + twoFingerDragWarning: 'Use two fingers to move the map', + zoomSnap: true, + mouseEvents: true, + touchEvents: true, + warningZIndex: 100, + animateMaxScreens: 5, + minZoom: 1, + maxZoom: 18, + limitBounds: 'center', + dprs: [], + tileComponent: ImgTile +}); + +function Marker(props) { + var width = typeof props.width !== 'undefined' ? props.width : typeof props.height !== 'undefined' ? props.height * 29 / 34 : 29; + var height = typeof props.height !== 'undefined' ? props.height : typeof props.width !== 'undefined' ? props.width * 34 / 29 : 34; + + var _useState = React.useState(props.hover || false), + internalHover = _useState[0], + setInternalHover = _useState[1]; + + var hover = typeof props.hover === 'undefined' ? internalHover : props.hover; + var color = props.color || '#93C0D0'; + + var eventParameters = function eventParameters(event) { + return { + event: event, + anchor: props.anchor, + payload: props.payload + }; + }; + + return /*#__PURE__*/React__default["default"].createElement("div", { + style: _objectSpread2({ + position: 'absolute', + transform: "translate(" + (props.left - width / 2) + "px, " + (props.top - (height - 1)) + "px)", + filter: hover ? 'drop-shadow(0 0 4px rgba(0, 0, 0, .3))' : '', + pointerEvents: 'none', + cursor: 'pointer' + }, props.style || {}), + className: props.className ? props.className + " pigeon-click-block" : 'pigeon-click-block', + onClick: props.onClick ? function (event) { + return props.onClick(eventParameters(event)); + } : null, + onContextMenu: props.onContextMenu ? function (event) { + return props.onContextMenu(eventParameters(event)); + } : null, + onMouseOver: function onMouseOver(event) { + props.onMouseOver && props.onMouseOver(eventParameters(event)); + setInternalHover(true); + }, + onMouseOut: function onMouseOut(event) { + props.onMouseOut && props.onMouseOut(eventParameters(event)); + setInternalHover(false); + } + }, props.children || /*#__PURE__*/React__default["default"].createElement("svg", { + width: width, + height: height, + viewBox: "0 0 61 71", + fill: "none", + xmlns: "http://www.w3.org/2000/svg" + }, /*#__PURE__*/React__default["default"].createElement("g", { + style: { + pointerEvents: 'auto' + } + }, /*#__PURE__*/React__default["default"].createElement("path", { + d: "M52 31.5C52 36.8395 49.18 42.314 45.0107 47.6094C40.8672 52.872 35.619 57.678 31.1763 61.6922C30.7916 62.0398 30.2084 62.0398 29.8237 61.6922C25.381 57.678 20.1328 52.872 15.9893 47.6094C11.82 42.314 9 36.8395 9 31.5C9 18.5709 18.6801 9 30.5 9C42.3199 9 52 18.5709 52 31.5Z", + fill: color, + stroke: "white", + strokeWidth: "4" + }), /*#__PURE__*/React__default["default"].createElement("circle", { + cx: "30.5", + cy: "30.5", + r: "8.5", + fill: "white", + opacity: hover ? 0.98 : 0.6 + })))); +} + +function Overlay(props) { + return /*#__PURE__*/React__default["default"].createElement("div", { + style: _objectSpread2({ + position: 'absolute', + transform: "translate(" + props.left + "px, " + props.top + "px)" + }, props.style || {}), + className: props.className ? props.className + " pigeon-click-block" : 'pigeon-click-block' + }, props.children); +} + +var defaultSvgAttributes = { + fill: '#93c0d099', + strokeWidth: '2', + stroke: 'white', + r: '30' +}; +function PointComponent(props) { + var _props$svgAttributes; + + var latLngToPixel = props.latLngToPixel; + var _props$coordinates = props.coordinates, + y = _props$coordinates[0], + x = _props$coordinates[1]; + + var _latLngToPixel = latLngToPixel([x, y]), + cx = _latLngToPixel[0], + cy = _latLngToPixel[1]; + + if ((_props$svgAttributes = props.svgAttributes) !== null && _props$svgAttributes !== void 0 && _props$svgAttributes.path) { + var path = "M" + cx + "," + cy + "c" + props.svgAttributes.path.split(/[c|C|L|l|v|V|h|H]([\s\S]*)/)[1]; + return /*#__PURE__*/React__default["default"].createElement("path", _objectSpread2({ + d: path + }, props.svgAttributes)); + } + + return /*#__PURE__*/React__default["default"].createElement("circle", _objectSpread2({ + cx: cx, + cy: cy + }, props.svgAttributes)); +} +function MultiPoint(props) { + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, props.coordinates.map(function (point, i) { + return /*#__PURE__*/React__default["default"].createElement(PointComponent, _objectSpread2(_objectSpread2({}, props), {}, { + coordinates: point, + key: i + })); + })); +} +function LineString(props) { + var latLngToPixel = props.latLngToPixel; + var p = 'M' + props.coordinates.reduce(function (a, _ref) { + var y = _ref[0], + x = _ref[1]; + + var _latLngToPixel2 = latLngToPixel([x, y]), + v = _latLngToPixel2[0], + w = _latLngToPixel2[1]; + + return a + ' ' + v + ' ' + w; + }, ''); + return /*#__PURE__*/React__default["default"].createElement("path", _objectSpread2({ + d: p + }, props.svgAttributes)); +} +function MultiLineString(props) { + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, props.coordinates.map(function (line, i) { + return /*#__PURE__*/React__default["default"].createElement(LineString, _objectSpread2(_objectSpread2({}, props), {}, { + coordinates: line, + key: i + })); + })); +} +function Polygon(props) { + var latLngToPixel = props.latLngToPixel; + var p = props.coordinates.reduce(function (a, part) { + return a + ' M' + part.reduce(function (a, _ref2) { + var y = _ref2[0], + x = _ref2[1]; + + var _latLngToPixel3 = latLngToPixel([x, y]), + v = _latLngToPixel3[0], + w = _latLngToPixel3[1]; + + return a + ' ' + v + ' ' + w; + }, '') + 'Z'; + }, ''); + return /*#__PURE__*/React__default["default"].createElement("path", _objectSpread2({ + d: p + }, props.svgAttributes)); +} +function MultiPolygon(props) { + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, props.coordinates.map(function (polygon, i) { + return /*#__PURE__*/React__default["default"].createElement(Polygon, _objectSpread2(_objectSpread2({}, props), {}, { + coordinates: polygon, + key: i + })); + })); +} +function GeometryCollection(props) { + var renderer = { + Point: PointComponent, + MultiPoint: MultiPoint, + LineString: LineString, + MultiLineString: MultiLineString, + Polygon: Polygon, + MultiPolygon: MultiPolygon + }; + var _props$geometry = props.geometry, + type = _props$geometry.type, + coordinates = _props$geometry.coordinates, + geometries = _props$geometry.geometries; + + if (type === 'GeometryCollection') { + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, geometries.map(function (geometry, i) { + return /*#__PURE__*/React__default["default"].createElement(GeometryCollection, _objectSpread2(_objectSpread2({ + key: i + }, props), {}, { + geometry: geometry + })); + })); + } + + var Component = renderer[type]; + + if (Component === undefined) { + console.warn("The GeoJson Type " + type + " is not known"); + return null; + } + + return /*#__PURE__*/React__default["default"].createElement(Component, { + latLngToPixel: props.latLngToPixel, + geometry: props.geometry, + coordinates: coordinates, + svgAttributes: props.svgAttributes + }); +} +function GeoJsonFeature(props) { + var _useState = React.useState(props.hover || false), + internalHover = _useState[0], + setInternalHover = _useState[1]; + + var hover = props.hover !== undefined ? props.hover : internalHover; + var callbackSvgAttributes = props.styleCallback && props.styleCallback(props.feature, hover); + var svgAttributes = callbackSvgAttributes ? props.svgAttributes ? _objectSpread2(_objectSpread2({}, props.svgAttributes), callbackSvgAttributes) : callbackSvgAttributes : props.svgAttributes ? props.svgAttributes : defaultSvgAttributes; + + var eventParameters = function eventParameters(event) { + return { + event: event, + anchor: props.anchor, + payload: props.feature + }; + }; + + return /*#__PURE__*/React__default["default"].createElement("g", { + clipRule: "evenodd", + style: { + pointerEvents: 'auto' + }, + onClick: props.onClick ? function (event) { + return props.onClick(eventParameters(event)); + } : null, + onContextMenu: props.onContextMenu ? function (event) { + return props.onContextMenu(eventParameters(event)); + } : null, + onMouseOver: function onMouseOver(event) { + props.onMouseOver && props.onMouseOver(eventParameters(event)); + setInternalHover(true); + }, + onMouseOut: function onMouseOut(event) { + props.onMouseOut && props.onMouseOut(eventParameters(event)); + setInternalHover(false); + } + }, /*#__PURE__*/React__default["default"].createElement(GeometryCollection, _objectSpread2(_objectSpread2(_objectSpread2({}, props), props.feature), {}, { + svgAttributes: svgAttributes + }))); +} +function GeoJson(props) { + var _props$mapState = props.mapState, + width = _props$mapState.width, + height = _props$mapState.height; + return /*#__PURE__*/React__default["default"].createElement("div", { + style: _objectSpread2({ + position: 'absolute', + left: '0', + top: '0', + pointerEvents: 'none', + cursor: 'pointer' + }, props.style || {}), + className: props.className ? props.className + " pigeon-click-block" : 'pigeon-click-block' + }, /*#__PURE__*/React__default["default"].createElement("svg", { + width: width, + height: height, + viewBox: "0 0 " + width + " " + height, + fill: "none", + xmlns: "http://www.w3.org/2000/svg" + }, props.data && props.data.features.map(function (feature, i) { + return /*#__PURE__*/React__default["default"].createElement(GeoJsonFeature, _objectSpread2(_objectSpread2({ + key: i + }, props), {}, { + feature: feature + })); + }), React__default["default"].Children.map(props.children, function (child) { + if (!child) { + return null; + } + + if (! /*#__PURE__*/React__default["default"].isValidElement(child)) { + return child; + } + + return /*#__PURE__*/React__default["default"].cloneElement(child, props); + }))); +} +function GeoJsonLoader(props) { + var _useState2 = React.useState(props.data ? props.data : null), + data = _useState2[0], + setData = _useState2[1]; + + React.useEffect(function () { + fetch(props.link).then(function (response) { + return response.json(); + }).then(function (data) { + return setData(data); + }); + }, [props.link]); + return data ? /*#__PURE__*/React__default["default"].createElement(GeoJson, _objectSpread2({ + data: data + }, props)) : null; +} + +function isDescendentOf(element, ancestor) { + while (element) { + if (element === ancestor) { + return true; + } + + element = element.parentElement; + } + + return false; +} + +var defaultState = { + isDragging: false, + startX: undefined, + startY: undefined, + startLeft: undefined, + startTop: undefined, + deltaX: 0, + deltaY: 0 +}; +function Draggable(props) { + var dragRef = React.useRef(); + var propsRef = React.useRef(props); + var stateRef = React.useRef(_objectSpread2({}, defaultState)); + + var _useState = React.useState(defaultState), + _state = _useState[0], + _setState = _useState[1]; + + propsRef.current = props; + + var setState = function setState(stateUpdate) { + var newState = _objectSpread2(_objectSpread2({}, stateRef.current), stateUpdate); + + stateRef.current = newState; + + _setState(newState); + }; + + var _props$mapProps = props.mapProps, + mouseEvents = _props$mapProps.mouseEvents, + touchEvents = _props$mapProps.touchEvents; + React.useEffect(function () { + var handleDragStart = function handleDragStart(event) { + if (isDescendentOf(event.target, dragRef.current)) { + event.preventDefault(); + setState({ + isDragging: true, + startX: ('touches' in event ? event.touches[0] : event).clientX, + startY: ('touches' in event ? event.touches[0] : event).clientY, + startLeft: propsRef.current.left, + startTop: propsRef.current.top, + deltaX: 0, + deltaY: 0 + }); + + if (propsRef.current.onDragStart) { + var _propsRef$current = propsRef.current, + _left = _propsRef$current.left, + _top = _propsRef$current.top, + offset = _propsRef$current.offset, + pixelToLatLng = _propsRef$current.pixelToLatLng; + propsRef.current.onDragMove(pixelToLatLng([_left + (offset ? offset[0] : 0), _top + (offset ? offset[1] : 0)])); + } + } + }; + + var handleDragMove = function handleDragMove(event) { + if (!stateRef.current.isDragging) { + return; + } + + event.preventDefault(); + var x = ('touches' in event ? event.touches[0] : event).clientX; + var y = ('touches' in event ? event.touches[0] : event).clientY; + var deltaX = x - stateRef.current.startX; + var deltaY = y - stateRef.current.startY; + setState({ + deltaX: deltaX, + deltaY: deltaY + }); + + if (propsRef.current.onDragMove) { + var _propsRef$current2 = propsRef.current, + offset = _propsRef$current2.offset, + pixelToLatLng = _propsRef$current2.pixelToLatLng; + var _stateRef$current = stateRef.current, + _startLeft = _stateRef$current.startLeft, + _startTop = _stateRef$current.startTop; + propsRef.current.onDragMove(pixelToLatLng([_startLeft + deltaX + (offset ? offset[0] : 0), _startTop + deltaY + (offset ? offset[1] : 0)])); + } + }; + + var handleDragEnd = function handleDragEnd(event) { + var _propsRef$current$onD, _propsRef$current4; + + if (!stateRef.current.isDragging) { + return; + } + + event.preventDefault(); + var _propsRef$current3 = propsRef.current, + offset = _propsRef$current3.offset, + pixelToLatLng = _propsRef$current3.pixelToLatLng; + var _stateRef$current2 = stateRef.current, + deltaX = _stateRef$current2.deltaX, + deltaY = _stateRef$current2.deltaY, + startLeft = _stateRef$current2.startLeft, + startTop = _stateRef$current2.startTop; + (_propsRef$current$onD = (_propsRef$current4 = propsRef.current).onDragEnd) === null || _propsRef$current$onD === void 0 ? void 0 : _propsRef$current$onD.call(_propsRef$current4, pixelToLatLng([startLeft + deltaX + (offset ? offset[0] : 0), startTop + deltaY + (offset ? offset[1] : 0)])); + setState({ + isDragging: false, + startX: undefined, + startY: undefined, + startLeft: undefined, + startTop: undefined, + deltaX: 0, + deltaY: 0 + }); + }; + + var wa = function wa(e, t, o) { + return window.addEventListener(e, t, o); + }; + + var wr = function wr(e, t) { + return window.removeEventListener(e, t); + }; + + if (mouseEvents) { + wa('mousedown', handleDragStart); + wa('mousemove', handleDragMove); + wa('mouseup', handleDragEnd); + } + + if (touchEvents) { + wa('touchstart', handleDragStart, { + passive: false + }); + wa('touchmove', handleDragMove, { + passive: false + }); + wa('touchend', handleDragEnd, { + passive: false + }); + } + + return function () { + if (mouseEvents) { + wr('mousedown', handleDragStart); + wr('mousemove', handleDragMove); + wr('mouseup', handleDragEnd); + } + + if (touchEvents) { + wr('touchstart', handleDragStart); + wr('touchmove', handleDragMove); + wr('touchend', handleDragEnd); + } + }; + }, [mouseEvents, touchEvents]); + var left = props.left, + top = props.top, + className = props.className, + style = props.style; + var deltaX = _state.deltaX, + deltaY = _state.deltaY, + startLeft = _state.startLeft, + startTop = _state.startTop, + isDragging = _state.isDragging; + return /*#__PURE__*/React__default["default"].createElement("div", { + style: _objectSpread2(_objectSpread2({ + cursor: isDragging ? 'grabbing' : 'grab' + }, style || {}), {}, { + position: 'absolute', + transform: "translate(" + (isDragging ? startLeft + deltaX : left) + "px, " + (isDragging ? startTop + deltaY : top) + "px)" + }), + ref: dragRef, + className: "pigeon-drag-block" + (className ? " " + className : '') + }, props.children); +} + +var commonStyle = { + position: 'absolute', + top: 10, + left: 10 +}; +var commonButtonStyle = { + width: 28, + height: 28, + borderRadius: 2, + boxShadow: '0 1px 4px -1px rgba(0,0,0,.3)', + background: 'white', + lineHeight: '26px', + fontSize: '20px', + fontWeight: 700, + color: '#666', + marginBottom: 1, + cursor: 'pointer', + border: 'none', + display: 'block', + outline: 'none' +}; +function ZoomControl(_ref) { + var style = _ref.style, + buttonStyle = _ref.buttonStyle, + setCenterZoom = _ref.setCenterZoom, + mapState = _ref.mapState, + mapProps = _ref.mapProps; + return /*#__PURE__*/React__default["default"].createElement("div", { + className: "pigeon-zoom-buttons pigeon-drag-block", + style: style ? _objectSpread2(_objectSpread2({}, commonStyle), style) : commonStyle + }, /*#__PURE__*/React__default["default"].createElement("button", { + className: "pigeon-zoom-in", + type: "button", + style: buttonStyle ? _objectSpread2(_objectSpread2({}, commonButtonStyle), buttonStyle) : commonButtonStyle, + onClick: function onClick() { + return setCenterZoom(mapState.center, Math.min(mapState.zoom + 1, mapProps.maxZoom)); + } + }, "+"), /*#__PURE__*/React__default["default"].createElement("button", { + className: "pigeon-zoom-out", + type: "button", + style: buttonStyle ? _objectSpread2(_objectSpread2({}, commonButtonStyle), buttonStyle) : commonButtonStyle, + onClick: function onClick() { + return setCenterZoom(mapState.center, Math.max(mapState.zoom - 1, mapProps.minZoom)); + } + }, "\u2013")); +} + +exports.Draggable = Draggable; +exports.GeoJson = GeoJson; +exports.GeoJsonFeature = GeoJsonFeature; +exports.GeoJsonLoader = GeoJsonLoader; +exports.Map = Map; +exports.Marker = Marker; +exports.Overlay = Overlay; +exports.ZoomControl = ZoomControl; diff --git a/node_modules/pigeon-maps/lib/index.d.ts b/node_modules/pigeon-maps/lib/index.d.ts new file mode 100644 index 0000000..939d685 --- /dev/null +++ b/node_modules/pigeon-maps/lib/index.d.ts @@ -0,0 +1,7 @@ +export * from './types';
+export { Map } from './map/Map';
+export { Marker } from './overlays/Marker';
+export { Overlay } from './overlays/Overlay';
+export { GeoJson, GeoJsonLoader, GeoJsonFeature } from './overlays/GeoJson';
+export { Draggable } from './overlays/Draggable';
+export { ZoomControl } from './controls/ZoomControl';
diff --git a/node_modules/pigeon-maps/lib/index.esm.js b/node_modules/pigeon-maps/lib/index.esm.js new file mode 100644 index 0000000..2b85a6d --- /dev/null +++ b/node_modules/pigeon-maps/lib/index.esm.js @@ -0,0 +1,2083 @@ +import React, { Component, useState, useEffect, useRef } from 'react'; + +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + enumerableOnly && (symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + })), keys.push.apply(keys, symbols); + } + + return keys; +} + +function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = null != arguments[i] ? arguments[i] : {}; + i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { + _defineProperty(target, key, source[key]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + + return target; +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + + _setPrototypeOf(subClass, superClass); +} + +function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); +} + +function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; +} + +function debounce(func, wait) { + var timeout; + return function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var context = this; + clearTimeout(timeout); + timeout = setTimeout(function () { + return func.apply(context, args); + }, wait); + }; +} +function parentHasClass(element, className) { + while (element) { + if (element.classList && element.classList.contains(className)) { + return true; + } + + element = element.parentElement; + } + + return false; +} +function parentPosition(element) { + var rect = element.getBoundingClientRect(); + return { + x: rect.left, + y: rect.top + }; +} + +function osm(x, y, z) { + return "https://tile.openstreetmap.org/" + z + "/" + x + "/" + y + ".png"; +} + +var ANIMATION_TIME = 300; +var DIAGONAL_THROW_TIME = 1500; +var SCROLL_PIXELS_FOR_ZOOM_LEVEL = 150; +var MIN_DRAG_FOR_THROW = 40; +var CLICK_TOLERANCE = 2; +var DOUBLE_CLICK_DELAY = 300; +var DEBOUNCE_DELAY = 60; +var PINCH_RELEASE_THROW_DELAY = 300; +var WARNING_DISPLAY_TIMEOUT = 300; + +var NOOP = function NOOP() { + return true; +}; + +var lng2tile = function lng2tile(lon, zoom) { + return (lon + 180) / 360 * Math.pow(2, zoom); +}; + +var lat2tile = function lat2tile(lat, zoom) { + return (1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom); +}; + +function tile2lng(x, z) { + return x / Math.pow(2, z) * 360 - 180; +} + +function tile2lat(y, z) { + var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); + return 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); +} + +function getMousePixel(dom, event) { + var parent = parentPosition(dom); + return [event.clientX - parent.x, event.clientY - parent.y]; +} + +function easeOutQuad(t) { + return t * (2 - t); +} + +var absoluteMinMax = [tile2lat(Math.pow(2, 10), 10), tile2lat(0, 10), tile2lng(0, 10), tile2lng(Math.pow(2, 10), 10)]; +var hasWindow = typeof window !== 'undefined'; +var performanceNow = hasWindow && window.performance && window.performance.now ? function () { + return window.performance.now(); +} : function () { + var timeStart = new Date().getTime(); + return function () { + return new Date().getTime() - timeStart; + }; +}(); + +var requestAnimationFrame = function requestAnimationFrame(callback) { + if (hasWindow) { + return (window.requestAnimationFrame || window.setTimeout)(callback); + } else { + callback(new Date().getTime()); + return null; + } +}; + +var cancelAnimationFrame = function cancelAnimationFrame(animFrame) { + return hasWindow && animFrame ? (window.cancelAnimationFrame || window.clearTimeout)(animFrame) : false; +}; + +function srcSet(dprs, url, x, y, z) { + if (!dprs || dprs.length === 0) { + return ''; + } + + return dprs.map(function (dpr) { + return url(x, y, z, dpr) + (dpr === 1 ? '' : " " + dpr + "x"); + }).join(', '); +} + +var ImgTile = function ImgTile(_ref) { + var tile = _ref.tile, + tileLoaded = _ref.tileLoaded; + return /*#__PURE__*/React.createElement("img", { + src: tile.url, + srcSet: tile.srcSet, + width: tile.width, + height: tile.height, + loading: 'lazy', + onLoad: tileLoaded, + alt: '', + style: { + position: 'absolute', + left: tile.left, + top: tile.top, + willChange: 'transform', + transformOrigin: 'top left', + opacity: 1 + } + }); +}; + +var Map = /*#__PURE__*/function (_Component) { + _inheritsLoose(Map, _Component); + + function Map(props) { + var _ref2, _props$defaultZoom, _ref3, _props$defaultCenter, _ref4, _props$width, _ref5, _props$height; + + var _this; + + _this = _Component.call(this, props) || this; + + _defineProperty(_assertThisInitialized(_this), "_containerRef", void 0); + + _defineProperty(_assertThisInitialized(_this), "_mousePosition", void 0); + + _defineProperty(_assertThisInitialized(_this), "_loadTracker", void 0); + + _defineProperty(_assertThisInitialized(_this), "_dragStart", null); + + _defineProperty(_assertThisInitialized(_this), "_mouseDown", false); + + _defineProperty(_assertThisInitialized(_this), "_moveEvents", []); + + _defineProperty(_assertThisInitialized(_this), "_lastClick", null); + + _defineProperty(_assertThisInitialized(_this), "_lastTap", null); + + _defineProperty(_assertThisInitialized(_this), "_lastWheel", null); + + _defineProperty(_assertThisInitialized(_this), "_touchStartPixel", null); + + _defineProperty(_assertThisInitialized(_this), "_touchStartMidPoint", null); + + _defineProperty(_assertThisInitialized(_this), "_touchStartDistance", null); + + _defineProperty(_assertThisInitialized(_this), "_secondTouchEnd", null); + + _defineProperty(_assertThisInitialized(_this), "_warningClearTimeout", null); + + _defineProperty(_assertThisInitialized(_this), "_isAnimating", false); + + _defineProperty(_assertThisInitialized(_this), "_animationStart", null); + + _defineProperty(_assertThisInitialized(_this), "_animationEnd", null); + + _defineProperty(_assertThisInitialized(_this), "_zoomStart", null); + + _defineProperty(_assertThisInitialized(_this), "_centerTarget", null); + + _defineProperty(_assertThisInitialized(_this), "_zoomTarget", null); + + _defineProperty(_assertThisInitialized(_this), "_zoomAround", null); + + _defineProperty(_assertThisInitialized(_this), "_animFrame", null); + + _defineProperty(_assertThisInitialized(_this), "_boundsSynced", false); + + _defineProperty(_assertThisInitialized(_this), "_minMaxCache", null); + + _defineProperty(_assertThisInitialized(_this), "_lastZoom", void 0); + + _defineProperty(_assertThisInitialized(_this), "_lastCenter", void 0); + + _defineProperty(_assertThisInitialized(_this), "_centerStart", void 0); + + _defineProperty(_assertThisInitialized(_this), "_resizeObserver", null); + + _defineProperty(_assertThisInitialized(_this), "updateWidthHeight", function () { + if (_this._containerRef) { + var rect = _this._containerRef.getBoundingClientRect(); + + if (rect && rect.width > 0 && rect.height > 0) { + _this.setState({ + width: rect.width, + height: rect.height + }); + + return true; + } + } + + return false; + }); + + _defineProperty(_assertThisInitialized(_this), "wa", function () { + var _window; + + return (_window = window).addEventListener.apply(_window, arguments); + }); + + _defineProperty(_assertThisInitialized(_this), "wr", function () { + var _window2; + + return (_window2 = window).removeEventListener.apply(_window2, arguments); + }); + + _defineProperty(_assertThisInitialized(_this), "bindMouseEvents", function () { + _this.wa('mousedown', _this.handleMouseDown); + + _this.wa('mouseup', _this.handleMouseUp); + + _this.wa('mousemove', _this.handleMouseMove); + }); + + _defineProperty(_assertThisInitialized(_this), "bindTouchEvents", function () { + _this.wa('touchstart', _this.handleTouchStart, { + passive: false + }); + + _this.wa('touchmove', _this.handleTouchMove, { + passive: false + }); + + _this.wa('touchend', _this.handleTouchEnd, { + passive: false + }); + }); + + _defineProperty(_assertThisInitialized(_this), "unbindMouseEvents", function () { + _this.wr('mousedown', _this.handleMouseDown); + + _this.wr('mouseup', _this.handleMouseUp); + + _this.wr('mousemove', _this.handleMouseMove); + }); + + _defineProperty(_assertThisInitialized(_this), "unbindTouchEvents", function () { + _this.wr('touchstart', _this.handleTouchStart); + + _this.wr('touchmove', _this.handleTouchMove); + + _this.wr('touchend', _this.handleTouchEnd); + }); + + _defineProperty(_assertThisInitialized(_this), "bindResizeEvent", function () { + _this.wa('resize', _this.updateWidthHeight); + }); + + _defineProperty(_assertThisInitialized(_this), "unbindResizeEvent", function () { + _this.wr('resize', _this.updateWidthHeight); + }); + + _defineProperty(_assertThisInitialized(_this), "bindWheelEvent", function () { + if (_this._containerRef) { + _this._containerRef.addEventListener('wheel', _this.handleWheel, { + passive: false + }); + } + }); + + _defineProperty(_assertThisInitialized(_this), "unbindWheelEvent", function () { + if (_this._containerRef) { + _this._containerRef.removeEventListener('wheel', _this.handleWheel); + } + }); + + _defineProperty(_assertThisInitialized(_this), "setCenterZoomTarget", function (center, zoom, fromProps, zoomAround, animationDuration) { + if (fromProps === void 0) { + fromProps = false; + } + + if (zoomAround === void 0) { + zoomAround = null; + } + + if (animationDuration === void 0) { + animationDuration = ANIMATION_TIME; + } + + if (_this.props.animate && (!fromProps || _this.distanceInScreens(center, zoom, _this.state.center, _this.state.zoom) <= _this.props.animateMaxScreens)) { + if (_this._isAnimating) { + cancelAnimationFrame(_this._animFrame); + + var _this$animationStep = _this.animationStep(performanceNow()), + centerStep = _this$animationStep.centerStep, + zoomStep = _this$animationStep.zoomStep; + + _this._centerStart = centerStep; + _this._zoomStart = zoomStep; + } else { + _this._isAnimating = true; + _this._centerStart = _this.limitCenterAtZoom([_this._lastCenter[0], _this._lastCenter[1]], _this._lastZoom); + _this._zoomStart = _this._lastZoom; + + _this.onAnimationStart(); + } + + _this._animationStart = performanceNow(); + _this._animationEnd = _this._animationStart + animationDuration; + + if (zoomAround) { + _this._zoomAround = zoomAround; + _this._centerTarget = _this.calculateZoomCenter(_this._lastCenter, zoomAround, _this._lastZoom, zoom); + } else { + _this._zoomAround = null; + _this._centerTarget = center; + } + + _this._zoomTarget = zoom; + _this._animFrame = requestAnimationFrame(_this.animate); + } else { + _this.stopAnimating(); + + if (zoomAround) { + var _center = _this.calculateZoomCenter(_this._lastCenter, zoomAround, _this._lastZoom, zoom); + + _this.setCenterZoom(_center, zoom, fromProps); + } else { + _this.setCenterZoom(center || _this.state.center, zoom, fromProps); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "setCenterZoomForChildren", function (center, zoom) { + _this.setCenterZoomTarget(center || _this.state.center, zoom || _this.state.zoom, true); + }); + + _defineProperty(_assertThisInitialized(_this), "distanceInScreens", function (centerTarget, zoomTarget, center, zoom) { + var _this$state = _this.state, + width = _this$state.width, + height = _this$state.height; + + var l1 = _this.latLngToPixel(center, center, zoom); + + var l2 = _this.latLngToPixel(centerTarget, center, zoom); + + var z1 = _this.latLngToPixel(center, center, zoomTarget); + + var z2 = _this.latLngToPixel(centerTarget, center, zoomTarget); + + var w = (Math.abs(l1[0] - l2[0]) + Math.abs(z1[0] - z2[0])) / 2 / width; + var h = (Math.abs(l1[1] - l2[1]) + Math.abs(z1[1] - z2[1])) / 2 / height; + return Math.sqrt(w * w + h * h); + }); + + _defineProperty(_assertThisInitialized(_this), "animationStep", function (timestamp) { + if (!_this._animationEnd || !_this._animationStart || !_this._zoomTarget || !_this._zoomStart || !_this._centerStart || !_this._centerTarget) { + return { + centerStep: _this.state.center, + zoomStep: _this.state.zoom + }; + } + + var length = _this._animationEnd - _this._animationStart; + var progress = Math.max(timestamp - _this._animationStart, 0); + var percentage = easeOutQuad(progress / length); + var zoomDiff = (_this._zoomTarget - _this._zoomStart) * percentage; + var zoomStep = _this._zoomStart + zoomDiff; + + if (_this._zoomAround) { + var centerStep = _this.calculateZoomCenter(_this._centerStart, _this._zoomAround, _this._zoomStart, zoomStep); + + return { + centerStep: centerStep, + zoomStep: zoomStep + }; + } else { + var _centerStep = [_this._centerStart[0] + (_this._centerTarget[0] - _this._centerStart[0]) * percentage, _this._centerStart[1] + (_this._centerTarget[1] - _this._centerStart[1]) * percentage]; + return { + centerStep: _centerStep, + zoomStep: zoomStep + }; + } + }); + + _defineProperty(_assertThisInitialized(_this), "animate", function (timestamp) { + if (!_this._animationEnd || timestamp >= _this._animationEnd) { + _this._isAnimating = false; + + _this.setCenterZoom(_this._centerTarget, _this._zoomTarget, true); + + _this.onAnimationStop(); + } else { + var _this$animationStep2 = _this.animationStep(timestamp), + centerStep = _this$animationStep2.centerStep, + zoomStep = _this$animationStep2.zoomStep; + + _this.setCenterZoom(centerStep, zoomStep); + + _this._animFrame = requestAnimationFrame(_this.animate); + } + }); + + _defineProperty(_assertThisInitialized(_this), "stopAnimating", function () { + if (_this._isAnimating) { + _this._isAnimating = false; + + _this.onAnimationStop(); + + cancelAnimationFrame(_this._animFrame); + } + }); + + _defineProperty(_assertThisInitialized(_this), "limitCenterAtZoom", function (center, zoom) { + var minMax = _this.getBoundsMinMax(zoom || _this.state.zoom); + + return [Math.max(Math.min(!center || isNaN(center[0]) ? _this.state.center[0] : center[0], minMax[1]), minMax[0]), Math.max(Math.min(!center || isNaN(center[1]) ? _this.state.center[1] : center[1], minMax[3]), minMax[2])]; + }); + + _defineProperty(_assertThisInitialized(_this), "onAnimationStart", function () { + _this.props.onAnimationStart && _this.props.onAnimationStart(); + }); + + _defineProperty(_assertThisInitialized(_this), "onAnimationStop", function () { + _this.props.onAnimationStop && _this.props.onAnimationStop(); + }); + + _defineProperty(_assertThisInitialized(_this), "setCenterZoom", function (center, zoom, animationEnded) { + if (animationEnded === void 0) { + animationEnded = false; + } + + var limitedCenter = _this.limitCenterAtZoom(center, zoom); + + if (zoom && Math.round(_this.state.zoom) !== Math.round(zoom)) { + var tileValues = _this.tileValues(_this.state); + + var nextValues = _this.tileValues({ + center: limitedCenter, + zoom: zoom, + width: _this.state.width, + height: _this.state.height + }); + + var oldTiles = _this.state.oldTiles; + + _this.setState({ + oldTiles: oldTiles.filter(function (o) { + return o.roundedZoom !== tileValues.roundedZoom; + }).concat(tileValues) + }, NOOP); + + var loadTracker = {}; + + for (var x = nextValues.tileMinX; x <= nextValues.tileMaxX; x++) { + for (var y = nextValues.tileMinY; y <= nextValues.tileMaxY; y++) { + var key = x + "-" + y + "-" + nextValues.roundedZoom; + loadTracker[key] = false; + } + } + + _this._loadTracker = loadTracker; + } + + _this.setState({ + center: limitedCenter, + zoom: zoom || _this.state.zoom + }, NOOP); + + var maybeZoom = _this.props.zoom ? _this.props.zoom : _this._lastZoom; + var maybeCenter = _this.props.center ? _this.props.center : _this._lastCenter; + + if (zoom && (animationEnded || Math.abs(maybeZoom - zoom) > 0.001 || Math.abs(maybeCenter[0] - limitedCenter[0]) > 0.00001 || Math.abs(maybeCenter[1] - limitedCenter[1]) > 0.00001)) { + _this._lastZoom = zoom; + _this._lastCenter = [].concat(limitedCenter); + + _this.syncToProps(limitedCenter, zoom); + } + }); + + _defineProperty(_assertThisInitialized(_this), "getBoundsMinMax", function (zoom) { + if (_this.props.limitBounds === 'center') { + return absoluteMinMax; + } + + var _this$state2 = _this.state, + width = _this$state2.width, + height = _this$state2.height; + + if (_this._minMaxCache && _this._minMaxCache[0] === zoom && _this._minMaxCache[1] === width && _this._minMaxCache[2] === height) { + return _this._minMaxCache[3]; + } + + var pixelsAtZoom = Math.pow(2, zoom) * 256; + var minLng = width > pixelsAtZoom ? 0 : tile2lng(width / 512, zoom); + var minLat = height > pixelsAtZoom ? 0 : tile2lat(Math.pow(2, zoom) - height / 512, zoom); + var maxLng = width > pixelsAtZoom ? 0 : tile2lng(Math.pow(2, zoom) - width / 512, zoom); + var maxLat = height > pixelsAtZoom ? 0 : tile2lat(height / 512, zoom); + var minMax = [minLat, maxLat, minLng, maxLng]; + _this._minMaxCache = [zoom, width, height, minMax]; + return minMax; + }); + + _defineProperty(_assertThisInitialized(_this), "tileLoaded", function (key) { + if (_this._loadTracker && key in _this._loadTracker) { + _this._loadTracker[key] = true; + var unloadedCount = Object.values(_this._loadTracker).filter(function (v) { + return !v; + }).length; + + if (unloadedCount === 0) { + _this.setState({ + oldTiles: [] + }, NOOP); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleTouchStart", function (event) { + if (!_this._containerRef) { + return; + } + + if (event.target && parentHasClass(event.target, 'pigeon-drag-block')) { + return; + } + + if (event.touches.length === 1) { + var touch = event.touches[0]; + var pixel = getMousePixel(_this._containerRef, touch); + + if (_this.coordsInside(pixel)) { + _this._touchStartPixel = [pixel]; + + if (!_this.props.twoFingerDrag) { + _this.stopAnimating(); + + if (_this._lastTap && performanceNow() - _this._lastTap < DOUBLE_CLICK_DELAY) { + event.preventDefault(); + + var latLngNow = _this.pixelToLatLng(_this._touchStartPixel[0]); + + _this.setCenterZoomTarget(null, Math.max(_this.props.minZoom, Math.min(_this.state.zoom + 1, _this.props.maxZoom)), false, latLngNow); + } else { + _this._lastTap = performanceNow(); + + _this.trackMoveEvents(pixel); + } + } + } + } else if (event.touches.length === 2 && _this._touchStartPixel) { + event.preventDefault(); + + _this.stopTrackingMoveEvents(); + + if (_this.state.pixelDelta || _this.state.zoomDelta) { + _this.sendDeltaChange(); + } + + var t1 = getMousePixel(_this._containerRef, event.touches[0]); + var t2 = getMousePixel(_this._containerRef, event.touches[1]); + _this._touchStartPixel = [t1, t2]; + _this._touchStartMidPoint = [(t1[0] + t2[0]) / 2, (t1[1] + t2[1]) / 2]; + _this._touchStartDistance = Math.sqrt(Math.pow(t1[0] - t2[0], 2) + Math.pow(t1[1] - t2[1], 2)); + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleTouchMove", function (event) { + if (!_this._containerRef) { + _this._touchStartPixel = null; + return; + } + + if (event.touches.length === 1 && _this._touchStartPixel) { + var touch = event.touches[0]; + var pixel = getMousePixel(_this._containerRef, touch); + + if (_this.props.twoFingerDrag) { + if (_this.coordsInside(pixel)) { + _this.showWarning('fingers'); + } + } else { + event.preventDefault(); + + _this.trackMoveEvents(pixel); + + _this.setState({ + pixelDelta: [pixel[0] - _this._touchStartPixel[0][0], pixel[1] - _this._touchStartPixel[0][1]] + }, NOOP); + } + } else if (event.touches.length === 2 && _this._touchStartPixel && _this._touchStartMidPoint && _this._touchStartDistance) { + var _this$state3 = _this.state, + width = _this$state3.width, + height = _this$state3.height, + zoom = _this$state3.zoom; + event.preventDefault(); + var t1 = getMousePixel(_this._containerRef, event.touches[0]); + var t2 = getMousePixel(_this._containerRef, event.touches[1]); + var midPoint = [(t1[0] + t2[0]) / 2, (t1[1] + t2[1]) / 2]; + var midPointDiff = [midPoint[0] - _this._touchStartMidPoint[0], midPoint[1] - _this._touchStartMidPoint[1]]; + var distance = Math.sqrt(Math.pow(t1[0] - t2[0], 2) + Math.pow(t1[1] - t2[1], 2)); + var zoomDelta = Math.max(_this.props.minZoom, Math.min(_this.props.maxZoom, zoom + Math.log2(distance / _this._touchStartDistance))) - zoom; + var scale = Math.pow(2, zoomDelta); + var centerDiffDiff = [(width / 2 - midPoint[0]) * (scale - 1), (height / 2 - midPoint[1]) * (scale - 1)]; + + _this.setState({ + zoomDelta: zoomDelta, + pixelDelta: [centerDiffDiff[0] + midPointDiff[0] * scale, centerDiffDiff[1] + midPointDiff[1] * scale] + }, NOOP); + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleTouchEnd", function (event) { + if (!_this._containerRef) { + _this._touchStartPixel = null; + return; + } + + if (_this._touchStartPixel) { + var _this$props = _this.props, + zoomSnap = _this$props.zoomSnap, + twoFingerDrag = _this$props.twoFingerDrag, + minZoom = _this$props.minZoom, + maxZoom = _this$props.maxZoom; + var zoomDelta = _this.state.zoomDelta; + + var _this$sendDeltaChange = _this.sendDeltaChange(), + center = _this$sendDeltaChange.center, + zoom = _this$sendDeltaChange.zoom; + + if (event.touches.length === 0) { + if (twoFingerDrag) { + _this.clearWarning(); + } else { + var oldTouchPixel = _this._touchStartPixel[0]; + var newTouchPixel = getMousePixel(_this._containerRef, event.changedTouches[0]); + + if (Math.abs(oldTouchPixel[0] - newTouchPixel[0]) > CLICK_TOLERANCE || Math.abs(oldTouchPixel[1] - newTouchPixel[1]) > CLICK_TOLERANCE) { + if (!_this._secondTouchEnd || performanceNow() - _this._secondTouchEnd > PINCH_RELEASE_THROW_DELAY) { + event.preventDefault(); + + _this.throwAfterMoving(newTouchPixel, center, zoom); + } + } + + _this._touchStartPixel = null; + _this._secondTouchEnd = null; + } + } else if (event.touches.length === 1) { + event.preventDefault(); + var touch = getMousePixel(_this._containerRef, event.touches[0]); + _this._secondTouchEnd = performanceNow(); + _this._touchStartPixel = [touch]; + + _this.trackMoveEvents(touch); + + if (zoomSnap) { + var latLng = _this._touchStartMidPoint ? _this.pixelToLatLng(_this._touchStartMidPoint) : _this.state.center; + var zoomTarget; + + if (twoFingerDrag && Math.round(_this.state.zoom) === Math.round(_this.state.zoom + zoomDelta)) { + zoomTarget = Math.round(_this.state.zoom); + } else { + zoomTarget = zoomDelta > 0 ? Math.ceil(_this.state.zoom) : Math.floor(_this.state.zoom); + } + + var _zoom = Math.max(minZoom, Math.min(zoomTarget, maxZoom)); + + _this.setCenterZoomTarget(latLng, _zoom, false, latLng); + } + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleMouseDown", function (event) { + if (!_this._containerRef) { + return; + } + + var pixel = getMousePixel(_this._containerRef, event); + + if (event.button === 0 && (!event.target || !parentHasClass(event.target, 'pigeon-drag-block')) && _this.coordsInside(pixel)) { + _this.stopAnimating(); + + event.preventDefault(); + + if (_this._lastClick && performanceNow() - _this._lastClick < DOUBLE_CLICK_DELAY) { + if (!parentHasClass(event.target, 'pigeon-click-block')) { + var latLngNow = _this.pixelToLatLng(_this._mousePosition || pixel); + + _this.setCenterZoomTarget(null, Math.max(_this.props.minZoom, Math.min(_this.state.zoom + 1, _this.props.maxZoom)), false, latLngNow); + } + } else { + _this._lastClick = performanceNow(); + _this._mouseDown = true; + _this._dragStart = pixel; + + _this.trackMoveEvents(pixel); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleMouseMove", function (event) { + if (!_this._containerRef) { + return; + } + + _this._mousePosition = getMousePixel(_this._containerRef, event); + + if (_this._mouseDown && _this._dragStart) { + _this.trackMoveEvents(_this._mousePosition); + + _this.setState({ + pixelDelta: [_this._mousePosition[0] - _this._dragStart[0], _this._mousePosition[1] - _this._dragStart[1]] + }, NOOP); + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleMouseUp", function (event) { + if (!_this._containerRef) { + _this._mouseDown = false; + return; + } + + var pixelDelta = _this.state.pixelDelta; + + if (_this._mouseDown) { + _this._mouseDown = false; + var pixel = getMousePixel(_this._containerRef, event); + + if (_this.props.onClick && (!event.target || !parentHasClass(event.target, 'pigeon-click-block')) && (!pixelDelta || Math.abs(pixelDelta[0]) + Math.abs(pixelDelta[1]) <= CLICK_TOLERANCE)) { + var latLng = _this.pixelToLatLng(pixel); + + _this.props.onClick({ + event: event, + latLng: latLng, + pixel: pixel + }); + + _this.setState({ + pixelDelta: undefined + }, NOOP); + } else { + var _this$sendDeltaChange2 = _this.sendDeltaChange(), + center = _this$sendDeltaChange2.center, + zoom = _this$sendDeltaChange2.zoom; + + _this.throwAfterMoving(pixel, center, zoom); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "stopTrackingMoveEvents", function () { + _this._moveEvents = []; + }); + + _defineProperty(_assertThisInitialized(_this), "trackMoveEvents", function (coords) { + var timestamp = performanceNow(); + + if (_this._moveEvents.length === 0 || timestamp - _this._moveEvents[_this._moveEvents.length - 1].timestamp > 40) { + _this._moveEvents.push({ + timestamp: timestamp, + coords: coords + }); + + if (_this._moveEvents.length > 2) { + _this._moveEvents.shift(); + } + } + }); + + _defineProperty(_assertThisInitialized(_this), "throwAfterMoving", function (coords, center, zoom) { + var _this$state4 = _this.state, + width = _this$state4.width, + height = _this$state4.height; + var animate = _this.props.animate; + var timestamp = performanceNow(); + + var lastEvent = _this._moveEvents.shift(); + + if (lastEvent && animate) { + var deltaMs = Math.max(timestamp - lastEvent.timestamp, 1); + var delta = [(coords[0] - lastEvent.coords[0]) / deltaMs * 120, (coords[1] - lastEvent.coords[1]) / deltaMs * 120]; + var distance = Math.sqrt(delta[0] * delta[0] + delta[1] * delta[1]); + + if (distance > MIN_DRAG_FOR_THROW) { + var diagonal = Math.sqrt(width * width + height * height); + var throwTime = DIAGONAL_THROW_TIME * distance / diagonal; + var lng = tile2lng(lng2tile(center[1], zoom) - delta[0] / 256.0, zoom); + var lat = tile2lat(lat2tile(center[0], zoom) - delta[1] / 256.0, zoom); + + _this.setCenterZoomTarget([lat, lng], zoom, false, null, throwTime); + } + } + + _this.stopTrackingMoveEvents(); + }); + + _defineProperty(_assertThisInitialized(_this), "sendDeltaChange", function () { + var _this$state5 = _this.state, + center = _this$state5.center, + zoom = _this$state5.zoom, + pixelDelta = _this$state5.pixelDelta, + zoomDelta = _this$state5.zoomDelta; + var lat = center[0]; + var lng = center[1]; + + if (pixelDelta || zoomDelta !== 0) { + lng = tile2lng(lng2tile(center[1], zoom + zoomDelta) - (pixelDelta ? pixelDelta[0] / 256.0 : 0), zoom + zoomDelta); + lat = tile2lat(lat2tile(center[0], zoom + zoomDelta) - (pixelDelta ? pixelDelta[1] / 256.0 : 0), zoom + zoomDelta); + + _this.setCenterZoom([lat, lng], zoom + zoomDelta); + } + + _this.setState({ + pixelDelta: undefined, + zoomDelta: 0 + }, NOOP); + + return { + center: _this.limitCenterAtZoom([lat, lng], zoom + zoomDelta), + zoom: zoom + zoomDelta + }; + }); + + _defineProperty(_assertThisInitialized(_this), "getBounds", function (center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.zoomPlusDelta(); + } + + var _this$state6 = _this.state, + width = _this$state6.width, + height = _this$state6.height; + return { + ne: _this.pixelToLatLng([width - 1, 0], center, zoom), + sw: _this.pixelToLatLng([0, height - 1], center, zoom) + }; + }); + + _defineProperty(_assertThisInitialized(_this), "syncToProps", function (center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.state.zoom; + } + + var onBoundsChanged = _this.props.onBoundsChanged; + + if (onBoundsChanged) { + var bounds = _this.getBounds(center, zoom); + + onBoundsChanged({ + center: center, + zoom: zoom, + bounds: bounds, + initial: !_this._boundsSynced + }); + _this._boundsSynced = true; + } + }); + + _defineProperty(_assertThisInitialized(_this), "handleWheel", function (event) { + var _this$props2 = _this.props, + mouseEvents = _this$props2.mouseEvents, + metaWheelZoom = _this$props2.metaWheelZoom, + zoomSnap = _this$props2.zoomSnap, + animate = _this$props2.animate; + + if (!mouseEvents) { + return; + } + + if (!metaWheelZoom || event.metaKey || event.ctrlKey) { + event.preventDefault(); + var addToZoom = -event.deltaY / SCROLL_PIXELS_FOR_ZOOM_LEVEL; + + if (!zoomSnap && _this._zoomTarget) { + var stillToAdd = _this._zoomTarget - _this.state.zoom; + + _this.zoomAroundMouse(addToZoom + stillToAdd, event); + } else { + if (animate) { + _this.zoomAroundMouse(addToZoom, event); + } else { + if (!_this._lastWheel || performanceNow() - _this._lastWheel > ANIMATION_TIME) { + _this._lastWheel = performanceNow(); + + _this.zoomAroundMouse(addToZoom, event); + } + } + } + } else { + _this.showWarning('wheel'); + } + }); + + _defineProperty(_assertThisInitialized(_this), "showWarning", function (warningType) { + if (!_this.state.showWarning || _this.state.warningType !== warningType) { + _this.setState({ + showWarning: true, + warningType: warningType + }); + } + + if (_this._warningClearTimeout) { + window.clearTimeout(_this._warningClearTimeout); + } + + _this._warningClearTimeout = window.setTimeout(_this.clearWarning, WARNING_DISPLAY_TIMEOUT); + }); + + _defineProperty(_assertThisInitialized(_this), "clearWarning", function () { + if (_this.state.showWarning) { + _this.setState({ + showWarning: false + }); + } + }); + + _defineProperty(_assertThisInitialized(_this), "zoomAroundMouse", function (zoomDiff, event) { + if (!_this._containerRef) { + return; + } + + var zoom = _this.state.zoom; + var _this$props3 = _this.props, + minZoom = _this$props3.minZoom, + maxZoom = _this$props3.maxZoom, + zoomSnap = _this$props3.zoomSnap; + _this._mousePosition = getMousePixel(_this._containerRef, event); + + if (!_this._mousePosition || zoom === minZoom && zoomDiff < 0 || zoom === maxZoom && zoomDiff > 0) { + return; + } + + var latLngNow = _this.pixelToLatLng(_this._mousePosition); + + var zoomTarget = zoom + zoomDiff; + + if (zoomSnap) { + zoomTarget = zoomDiff < 0 ? Math.floor(zoomTarget) : Math.ceil(zoomTarget); + } + + zoomTarget = Math.max(minZoom, Math.min(zoomTarget, maxZoom)); + + _this.setCenterZoomTarget(null, zoomTarget, false, latLngNow); + }); + + _defineProperty(_assertThisInitialized(_this), "zoomPlusDelta", function () { + return _this.state.zoom + _this.state.zoomDelta; + }); + + _defineProperty(_assertThisInitialized(_this), "pixelToLatLng", function (pixel, center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.zoomPlusDelta(); + } + + var _this$state7 = _this.state, + width = _this$state7.width, + height = _this$state7.height, + pixelDelta = _this$state7.pixelDelta; + var pointDiff = [(pixel[0] - width / 2 - (pixelDelta ? pixelDelta[0] : 0)) / 256.0, (pixel[1] - height / 2 - (pixelDelta ? pixelDelta[1] : 0)) / 256.0]; + var tileX = lng2tile(center[1], zoom) + pointDiff[0]; + var tileY = lat2tile(center[0], zoom) + pointDiff[1]; + return [Math.max(absoluteMinMax[0], Math.min(absoluteMinMax[1], tile2lat(tileY, zoom))), Math.max(absoluteMinMax[2], Math.min(absoluteMinMax[3], tile2lng(tileX, zoom)))]; + }); + + _defineProperty(_assertThisInitialized(_this), "latLngToPixel", function (latLng, center, zoom) { + if (center === void 0) { + center = _this.state.center; + } + + if (zoom === void 0) { + zoom = _this.zoomPlusDelta(); + } + + var _this$state8 = _this.state, + width = _this$state8.width, + height = _this$state8.height, + pixelDelta = _this$state8.pixelDelta; + var tileCenterX = lng2tile(center[1], zoom); + var tileCenterY = lat2tile(center[0], zoom); + var tileX = lng2tile(latLng[1], zoom); + var tileY = lat2tile(latLng[0], zoom); + return [(tileX - tileCenterX) * 256.0 + width / 2 + (pixelDelta ? pixelDelta[0] : 0), (tileY - tileCenterY) * 256.0 + height / 2 + (pixelDelta ? pixelDelta[1] : 0)]; + }); + + _defineProperty(_assertThisInitialized(_this), "calculateZoomCenter", function (center, coords, oldZoom, newZoom) { + var _this$state9 = _this.state, + width = _this$state9.width, + height = _this$state9.height; + + var pixelBefore = _this.latLngToPixel(coords, center, oldZoom); + + var pixelAfter = _this.latLngToPixel(coords, center, newZoom); + + var newCenter = _this.pixelToLatLng([width / 2 + pixelAfter[0] - pixelBefore[0], height / 2 + pixelAfter[1] - pixelBefore[1]], center, newZoom); + + return _this.limitCenterAtZoom(newCenter, newZoom); + }); + + _defineProperty(_assertThisInitialized(_this), "setRef", function (dom) { + _this._containerRef = dom; + }); + + _this.syncToProps = debounce(_this.syncToProps, DEBOUNCE_DELAY); + _this._lastZoom = (_ref2 = (_props$defaultZoom = props.defaultZoom) !== null && _props$defaultZoom !== void 0 ? _props$defaultZoom : props.zoom) !== null && _ref2 !== void 0 ? _ref2 : 14; + _this._lastCenter = (_ref3 = (_props$defaultCenter = props.defaultCenter) !== null && _props$defaultCenter !== void 0 ? _props$defaultCenter : props.center) !== null && _ref3 !== void 0 ? _ref3 : [0, 0]; + _this.state = { + zoom: _this._lastZoom, + center: _this._lastCenter, + width: (_ref4 = (_props$width = props.width) !== null && _props$width !== void 0 ? _props$width : props.defaultWidth) !== null && _ref4 !== void 0 ? _ref4 : -1, + height: (_ref5 = (_props$height = props.height) !== null && _props$height !== void 0 ? _props$height : props.defaultHeight) !== null && _ref5 !== void 0 ? _ref5 : -1, + zoomDelta: 0, + pixelDelta: undefined, + oldTiles: [], + showWarning: false, + warningType: undefined + }; + return _this; + } + + var _proto = Map.prototype; + + _proto.componentDidMount = function componentDidMount() { + var _this2 = this; + + this.props.mouseEvents && this.bindMouseEvents(); + this.props.touchEvents && this.bindTouchEvents(); + + if (!this.props.width || !this.props.height) { + if (!this.updateWidthHeight()) { + requestAnimationFrame(this.updateWidthHeight); + } + + this.bindResizeEvent(); + } + + this.bindWheelEvent(); + this.syncToProps(); + + if (typeof window.ResizeObserver !== 'undefined') { + this._resizeObserver = new window.ResizeObserver(function () { + _this2.updateWidthHeight(); + }); + + this._resizeObserver.observe(this._containerRef); + } + }; + + _proto.componentWillUnmount = function componentWillUnmount() { + this.props.mouseEvents && this.unbindMouseEvents(); + this.props.touchEvents && this.unbindTouchEvents(); + this.unbindWheelEvent(); + + if (!this.props.width || !this.props.height) { + this.unbindResizeEvent(); + } + + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + } + }; + + _proto.componentDidUpdate = function componentDidUpdate(prevProps) { + var _prevProps$center; + + if (this.props.mouseEvents !== prevProps.mouseEvents) { + this.props.mouseEvents ? this.bindMouseEvents() : this.unbindMouseEvents(); + } + + if (this.props.touchEvents !== prevProps.touchEvents) { + this.props.touchEvents ? this.bindTouchEvents() : this.unbindTouchEvents(); + } + + if (this.props.width && this.props.width !== prevProps.width) { + this.setState({ + width: this.props.width + }); + } + + if (this.props.height && this.props.height !== prevProps.height) { + this.setState({ + height: this.props.height + }); + } + + if (!this.props.center && !this.props.zoom) { + return; + } + + if ((!this.props.center || this.props.center[0] === (prevProps === null || prevProps === void 0 ? void 0 : (_prevProps$center = prevProps.center) === null || _prevProps$center === void 0 ? void 0 : _prevProps$center[0]) && this.props.center[1] === prevProps.center[1]) && this.props.zoom === prevProps.zoom) { + return; + } + + var currentCenter = this._isAnimating ? this._centerTarget : this.state.center; + var currentZoom = this._isAnimating ? this._zoomTarget : this.state.zoom; + + if (currentCenter && currentZoom) { + var _this$props$center, _this$props$zoom; + + var nextCenter = (_this$props$center = this.props.center) !== null && _this$props$center !== void 0 ? _this$props$center : currentCenter; + var nextZoom = (_this$props$zoom = this.props.zoom) !== null && _this$props$zoom !== void 0 ? _this$props$zoom : currentZoom; + + if (Math.abs(nextZoom - currentZoom) > 0.001 || Math.abs(nextCenter[0] - currentCenter[0]) > 0.0001 || Math.abs(nextCenter[1] - currentCenter[1]) > 0.0001) { + this.setCenterZoomTarget(nextCenter, nextZoom, true); + } + } + }; + + _proto.coordsInside = function coordsInside(pixel) { + var _this$state10 = this.state, + width = _this$state10.width, + height = _this$state10.height; + + if (pixel[0] < 0 || pixel[1] < 0 || pixel[0] >= width || pixel[1] >= height) { + return false; + } + + var parent = this._containerRef; + + if (parent) { + var pos = parentPosition(parent); + var element = document.elementFromPoint(pixel[0] + pos.x, pixel[1] + pos.y); + return parent === element || parent.contains(element); + } else { + return false; + } + }; + + _proto.tileValues = function tileValues(_ref6) { + var center = _ref6.center, + zoom = _ref6.zoom, + pixelDelta = _ref6.pixelDelta, + zoomDelta = _ref6.zoomDelta, + width = _ref6.width, + height = _ref6.height; + var roundedZoom = Math.round(zoom + (zoomDelta || 0)); + var zoomDiff = zoom + (zoomDelta || 0) - roundedZoom; + var scale = Math.pow(2, zoomDiff); + var scaleWidth = width / scale; + var scaleHeight = height / scale; + var tileCenterX = lng2tile(center[1], roundedZoom) - (pixelDelta ? pixelDelta[0] / 256.0 / scale : 0); + var tileCenterY = lat2tile(center[0], roundedZoom) - (pixelDelta ? pixelDelta[1] / 256.0 / scale : 0); + var halfWidth = scaleWidth / 2 / 256.0; + var halfHeight = scaleHeight / 2 / 256.0; + var tileMinX = Math.floor(tileCenterX - halfWidth); + var tileMaxX = Math.floor(tileCenterX + halfWidth); + var tileMinY = Math.floor(tileCenterY - halfHeight); + var tileMaxY = Math.floor(tileCenterY + halfHeight); + return { + tileMinX: tileMinX, + tileMaxX: tileMaxX, + tileMinY: tileMinY, + tileMaxY: tileMaxY, + tileCenterX: tileCenterX, + tileCenterY: tileCenterY, + roundedZoom: roundedZoom, + zoomDelta: zoomDelta || 0, + scaleWidth: scaleWidth, + scaleHeight: scaleHeight, + scale: scale + }; + }; + + _proto.renderTiles = function renderTiles() { + var _this3 = this; + + var _this$state11 = this.state, + oldTiles = _this$state11.oldTiles, + width = _this$state11.width, + height = _this$state11.height; + var dprs = this.props.dprs; + var mapUrl = this.props.provider || osm; + + var _this$tileValues = this.tileValues(this.state), + tileMinX = _this$tileValues.tileMinX, + tileMaxX = _this$tileValues.tileMaxX, + tileMinY = _this$tileValues.tileMinY, + tileMaxY = _this$tileValues.tileMaxY, + tileCenterX = _this$tileValues.tileCenterX, + tileCenterY = _this$tileValues.tileCenterY, + roundedZoom = _this$tileValues.roundedZoom, + scaleWidth = _this$tileValues.scaleWidth, + scaleHeight = _this$tileValues.scaleHeight, + scale = _this$tileValues.scale; + + var tiles = []; + + for (var i = 0; i < oldTiles.length; i++) { + var old = oldTiles[i]; + var zoomDiff = old.roundedZoom - roundedZoom; + + if (Math.abs(zoomDiff) > 4 || zoomDiff === 0) { + continue; + } + + var pow = 1 / Math.pow(2, zoomDiff); + var xDiff = -(tileMinX - old.tileMinX * pow) * 256; + var yDiff = -(tileMinY - old.tileMinY * pow) * 256; + + var _xMin = Math.max(old.tileMinX, 0); + + var _yMin = Math.max(old.tileMinY, 0); + + var _xMax = Math.min(old.tileMaxX, Math.pow(2, old.roundedZoom) - 1); + + var _yMax = Math.min(old.tileMaxY, Math.pow(2, old.roundedZoom) - 1); + + for (var x = _xMin; x <= _xMax; x++) { + for (var y = _yMin; y <= _yMax; y++) { + tiles.push({ + key: x + "-" + y + "-" + old.roundedZoom, + url: mapUrl(x, y, old.roundedZoom), + srcSet: srcSet(dprs, mapUrl, x, y, old.roundedZoom), + left: xDiff + (x - old.tileMinX) * 256 * pow, + top: yDiff + (y - old.tileMinY) * 256 * pow, + width: 256 * pow, + height: 256 * pow, + active: false + }); + } + } + } + + var xMin = Math.max(tileMinX, 0); + var yMin = Math.max(tileMinY, 0); + var xMax = Math.min(tileMaxX, Math.pow(2, roundedZoom) - 1); + var yMax = Math.min(tileMaxY, Math.pow(2, roundedZoom) - 1); + + for (var _x = xMin; _x <= xMax; _x++) { + for (var _y = yMin; _y <= yMax; _y++) { + tiles.push({ + key: _x + "-" + _y + "-" + roundedZoom, + url: mapUrl(_x, _y, roundedZoom), + srcSet: srcSet(dprs, mapUrl, _x, _y, roundedZoom), + left: (_x - tileMinX) * 256, + top: (_y - tileMinY) * 256, + width: 256, + height: 256, + active: true + }); + } + } + + var boxStyle = { + width: scaleWidth, + height: scaleHeight, + position: 'absolute', + top: "calc((100% - " + height + "px) / 2)", + left: "calc((100% - " + width + "px) / 2)", + overflow: 'hidden', + willChange: 'transform', + transform: "scale(" + scale + ", " + scale + ")", + transformOrigin: 'top left' + }; + var boxClassname = this.props.boxClassname || 'pigeon-tiles-box'; + var left = -((tileCenterX - tileMinX) * 256 - scaleWidth / 2); + var top = -((tileCenterY - tileMinY) * 256 - scaleHeight / 2); + var tilesStyle = { + position: 'absolute', + width: (tileMaxX - tileMinX + 1) * 256, + height: (tileMaxY - tileMinY + 1) * 256, + willChange: 'transform', + transform: "translate(" + left + "px, " + top + "px)" + }; + var Tile = this.props.tileComponent; + return /*#__PURE__*/React.createElement("div", { + style: boxStyle, + className: boxClassname + }, /*#__PURE__*/React.createElement("div", { + className: "pigeon-tiles", + style: tilesStyle + }, tiles.map(function (tile) { + return /*#__PURE__*/React.createElement(Tile, { + key: tile.key, + tile: tile, + tileLoaded: function tileLoaded() { + return _this3.tileLoaded(tile.key); + } + }); + }))); + }; + + _proto.renderOverlays = function renderOverlays() { + var _this4 = this; + + var _this$state12 = this.state, + width = _this$state12.width, + height = _this$state12.height, + center = _this$state12.center; + var mapState = { + bounds: this.getBounds(), + zoom: this.zoomPlusDelta(), + center: center, + width: width, + height: height + }; + var childrenWithProps = React.Children.map(this.props.children, function (child) { + if (!child) { + return null; + } + + if (! /*#__PURE__*/React.isValidElement(child)) { + return child; + } + + var _child$props = child.props, + anchor = _child$props.anchor, + position = _child$props.position, + offset = _child$props.offset; + + var c = _this4.latLngToPixel(anchor || position || center); + + return /*#__PURE__*/React.cloneElement(child, { + left: c[0] - (offset ? offset[0] : 0), + top: c[1] - (offset ? offset[1] : 0), + latLngToPixel: _this4.latLngToPixel, + pixelToLatLng: _this4.pixelToLatLng, + setCenterZoom: _this4.setCenterZoomForChildren, + mapProps: _this4.props, + mapState: mapState + }); + }); + var childrenStyle = { + position: 'absolute', + width: width, + height: height, + top: "calc((100% - " + height + "px) / 2)", + left: "calc((100% - " + width + "px) / 2)" + }; + return /*#__PURE__*/React.createElement("div", { + className: "pigeon-overlays", + style: childrenStyle + }, childrenWithProps); + }; + + _proto.renderAttribution = function renderAttribution() { + var _this$props4 = this.props, + attribution = _this$props4.attribution, + attributionPrefix = _this$props4.attributionPrefix; + + if (attribution === false) { + return null; + } + + var style = { + position: 'absolute', + bottom: 0, + right: 0, + fontSize: '11px', + padding: '2px 5px', + background: 'rgba(255, 255, 255, 0.7)', + fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif", + color: '#333' + }; + var linkStyle = { + color: '#0078A8', + textDecoration: 'none' + }; + return /*#__PURE__*/React.createElement("div", { + key: "attr", + className: "pigeon-attribution", + style: style + }, attributionPrefix === false ? null : /*#__PURE__*/React.createElement("span", null, attributionPrefix || /*#__PURE__*/React.createElement("a", { + href: "https://pigeon-maps.js.org/", + style: linkStyle, + target: "_blank", + rel: "noreferrer noopener" + }, "Pigeon"), ' | '), attribution || /*#__PURE__*/React.createElement("span", null, ' © ', /*#__PURE__*/React.createElement("a", { + href: "https://www.openstreetmap.org/copyright", + style: linkStyle, + target: "_blank", + rel: "noreferrer noopener" + }, "OpenStreetMap"), ' contributors')); + }; + + _proto.renderWarning = function renderWarning() { + var _this$props5 = this.props, + metaWheelZoom = _this$props5.metaWheelZoom, + metaWheelZoomWarning = _this$props5.metaWheelZoomWarning, + twoFingerDrag = _this$props5.twoFingerDrag, + twoFingerDragWarning = _this$props5.twoFingerDragWarning, + warningZIndex = _this$props5.warningZIndex; + var _this$state13 = this.state, + showWarning = _this$state13.showWarning, + warningType = _this$state13.warningType, + width = _this$state13.width, + height = _this$state13.height; + + if (metaWheelZoom && metaWheelZoomWarning || twoFingerDrag && twoFingerDragWarning) { + var style = { + position: 'absolute', + top: 0, + left: 0, + width: width, + height: height, + overflow: 'hidden', + pointerEvents: 'none', + opacity: showWarning ? 100 : 0, + transition: 'opacity 300ms', + background: 'rgba(0,0,0,0.5)', + color: '#fff', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + fontSize: 22, + fontFamily: '"Arial", sans-serif', + textAlign: 'center', + zIndex: warningZIndex + }; + var meta = typeof window !== 'undefined' && window.navigator && window.navigator.platform.toUpperCase().indexOf('MAC') >= 0 ? '⌘' : 'ctrl'; + var warningText = warningType === 'fingers' ? twoFingerDragWarning : metaWheelZoomWarning; + return /*#__PURE__*/React.createElement("div", { + className: "pigeon-overlay-warning", + style: style + }, warningText.replace('META', meta)); + } else { + return null; + } + }; + + _proto.render = function render() { + var _this$props6 = this.props, + touchEvents = _this$props6.touchEvents, + twoFingerDrag = _this$props6.twoFingerDrag; + var _this$state14 = this.state, + width = _this$state14.width, + height = _this$state14.height; + var containerStyle = { + width: this.props.width ? width : '100%', + height: this.props.height ? height : '100%', + position: 'relative', + display: 'inline-block', + overflow: 'hidden', + background: '#dddddd', + touchAction: touchEvents ? twoFingerDrag ? 'pan-x pan-y' : 'none' : 'auto' + }; + var hasSize = !!(width && height); + return /*#__PURE__*/React.createElement("div", { + style: containerStyle, + ref: this.setRef, + dir: "ltr" + }, hasSize && this.renderTiles(), hasSize && this.renderOverlays(), hasSize && this.renderAttribution(), hasSize && this.renderWarning()); + }; + + return Map; +}(Component); + +_defineProperty(Map, "defaultProps", { + animate: true, + metaWheelZoom: false, + metaWheelZoomWarning: 'Use META + wheel to zoom!', + twoFingerDrag: false, + twoFingerDragWarning: 'Use two fingers to move the map', + zoomSnap: true, + mouseEvents: true, + touchEvents: true, + warningZIndex: 100, + animateMaxScreens: 5, + minZoom: 1, + maxZoom: 18, + limitBounds: 'center', + dprs: [], + tileComponent: ImgTile +}); + +function Marker(props) { + var width = typeof props.width !== 'undefined' ? props.width : typeof props.height !== 'undefined' ? props.height * 29 / 34 : 29; + var height = typeof props.height !== 'undefined' ? props.height : typeof props.width !== 'undefined' ? props.width * 34 / 29 : 34; + + var _useState = useState(props.hover || false), + internalHover = _useState[0], + setInternalHover = _useState[1]; + + var hover = typeof props.hover === 'undefined' ? internalHover : props.hover; + var color = props.color || '#93C0D0'; + + var eventParameters = function eventParameters(event) { + return { + event: event, + anchor: props.anchor, + payload: props.payload + }; + }; + + return /*#__PURE__*/React.createElement("div", { + style: _objectSpread2({ + position: 'absolute', + transform: "translate(" + (props.left - width / 2) + "px, " + (props.top - (height - 1)) + "px)", + filter: hover ? 'drop-shadow(0 0 4px rgba(0, 0, 0, .3))' : '', + pointerEvents: 'none', + cursor: 'pointer' + }, props.style || {}), + className: props.className ? props.className + " pigeon-click-block" : 'pigeon-click-block', + onClick: props.onClick ? function (event) { + return props.onClick(eventParameters(event)); + } : null, + onContextMenu: props.onContextMenu ? function (event) { + return props.onContextMenu(eventParameters(event)); + } : null, + onMouseOver: function onMouseOver(event) { + props.onMouseOver && props.onMouseOver(eventParameters(event)); + setInternalHover(true); + }, + onMouseOut: function onMouseOut(event) { + props.onMouseOut && props.onMouseOut(eventParameters(event)); + setInternalHover(false); + } + }, props.children || /*#__PURE__*/React.createElement("svg", { + width: width, + height: height, + viewBox: "0 0 61 71", + fill: "none", + xmlns: "http://www.w3.org/2000/svg" + }, /*#__PURE__*/React.createElement("g", { + style: { + pointerEvents: 'auto' + } + }, /*#__PURE__*/React.createElement("path", { + d: "M52 31.5C52 36.8395 49.18 42.314 45.0107 47.6094C40.8672 52.872 35.619 57.678 31.1763 61.6922C30.7916 62.0398 30.2084 62.0398 29.8237 61.6922C25.381 57.678 20.1328 52.872 15.9893 47.6094C11.82 42.314 9 36.8395 9 31.5C9 18.5709 18.6801 9 30.5 9C42.3199 9 52 18.5709 52 31.5Z", + fill: color, + stroke: "white", + strokeWidth: "4" + }), /*#__PURE__*/React.createElement("circle", { + cx: "30.5", + cy: "30.5", + r: "8.5", + fill: "white", + opacity: hover ? 0.98 : 0.6 + })))); +} + +function Overlay(props) { + return /*#__PURE__*/React.createElement("div", { + style: _objectSpread2({ + position: 'absolute', + transform: "translate(" + props.left + "px, " + props.top + "px)" + }, props.style || {}), + className: props.className ? props.className + " pigeon-click-block" : 'pigeon-click-block' + }, props.children); +} + +var defaultSvgAttributes = { + fill: '#93c0d099', + strokeWidth: '2', + stroke: 'white', + r: '30' +}; +function PointComponent(props) { + var _props$svgAttributes; + + var latLngToPixel = props.latLngToPixel; + var _props$coordinates = props.coordinates, + y = _props$coordinates[0], + x = _props$coordinates[1]; + + var _latLngToPixel = latLngToPixel([x, y]), + cx = _latLngToPixel[0], + cy = _latLngToPixel[1]; + + if ((_props$svgAttributes = props.svgAttributes) !== null && _props$svgAttributes !== void 0 && _props$svgAttributes.path) { + var path = "M" + cx + "," + cy + "c" + props.svgAttributes.path.split(/[c|C|L|l|v|V|h|H]([\s\S]*)/)[1]; + return /*#__PURE__*/React.createElement("path", _objectSpread2({ + d: path + }, props.svgAttributes)); + } + + return /*#__PURE__*/React.createElement("circle", _objectSpread2({ + cx: cx, + cy: cy + }, props.svgAttributes)); +} +function MultiPoint(props) { + return /*#__PURE__*/React.createElement(React.Fragment, null, props.coordinates.map(function (point, i) { + return /*#__PURE__*/React.createElement(PointComponent, _objectSpread2(_objectSpread2({}, props), {}, { + coordinates: point, + key: i + })); + })); +} +function LineString(props) { + var latLngToPixel = props.latLngToPixel; + var p = 'M' + props.coordinates.reduce(function (a, _ref) { + var y = _ref[0], + x = _ref[1]; + + var _latLngToPixel2 = latLngToPixel([x, y]), + v = _latLngToPixel2[0], + w = _latLngToPixel2[1]; + + return a + ' ' + v + ' ' + w; + }, ''); + return /*#__PURE__*/React.createElement("path", _objectSpread2({ + d: p + }, props.svgAttributes)); +} +function MultiLineString(props) { + return /*#__PURE__*/React.createElement(React.Fragment, null, props.coordinates.map(function (line, i) { + return /*#__PURE__*/React.createElement(LineString, _objectSpread2(_objectSpread2({}, props), {}, { + coordinates: line, + key: i + })); + })); +} +function Polygon(props) { + var latLngToPixel = props.latLngToPixel; + var p = props.coordinates.reduce(function (a, part) { + return a + ' M' + part.reduce(function (a, _ref2) { + var y = _ref2[0], + x = _ref2[1]; + + var _latLngToPixel3 = latLngToPixel([x, y]), + v = _latLngToPixel3[0], + w = _latLngToPixel3[1]; + + return a + ' ' + v + ' ' + w; + }, '') + 'Z'; + }, ''); + return /*#__PURE__*/React.createElement("path", _objectSpread2({ + d: p + }, props.svgAttributes)); +} +function MultiPolygon(props) { + return /*#__PURE__*/React.createElement(React.Fragment, null, props.coordinates.map(function (polygon, i) { + return /*#__PURE__*/React.createElement(Polygon, _objectSpread2(_objectSpread2({}, props), {}, { + coordinates: polygon, + key: i + })); + })); +} +function GeometryCollection(props) { + var renderer = { + Point: PointComponent, + MultiPoint: MultiPoint, + LineString: LineString, + MultiLineString: MultiLineString, + Polygon: Polygon, + MultiPolygon: MultiPolygon + }; + var _props$geometry = props.geometry, + type = _props$geometry.type, + coordinates = _props$geometry.coordinates, + geometries = _props$geometry.geometries; + + if (type === 'GeometryCollection') { + return /*#__PURE__*/React.createElement(React.Fragment, null, geometries.map(function (geometry, i) { + return /*#__PURE__*/React.createElement(GeometryCollection, _objectSpread2(_objectSpread2({ + key: i + }, props), {}, { + geometry: geometry + })); + })); + } + + var Component = renderer[type]; + + if (Component === undefined) { + console.warn("The GeoJson Type " + type + " is not known"); + return null; + } + + return /*#__PURE__*/React.createElement(Component, { + latLngToPixel: props.latLngToPixel, + geometry: props.geometry, + coordinates: coordinates, + svgAttributes: props.svgAttributes + }); +} +function GeoJsonFeature(props) { + var _useState = useState(props.hover || false), + internalHover = _useState[0], + setInternalHover = _useState[1]; + + var hover = props.hover !== undefined ? props.hover : internalHover; + var callbackSvgAttributes = props.styleCallback && props.styleCallback(props.feature, hover); + var svgAttributes = callbackSvgAttributes ? props.svgAttributes ? _objectSpread2(_objectSpread2({}, props.svgAttributes), callbackSvgAttributes) : callbackSvgAttributes : props.svgAttributes ? props.svgAttributes : defaultSvgAttributes; + + var eventParameters = function eventParameters(event) { + return { + event: event, + anchor: props.anchor, + payload: props.feature + }; + }; + + return /*#__PURE__*/React.createElement("g", { + clipRule: "evenodd", + style: { + pointerEvents: 'auto' + }, + onClick: props.onClick ? function (event) { + return props.onClick(eventParameters(event)); + } : null, + onContextMenu: props.onContextMenu ? function (event) { + return props.onContextMenu(eventParameters(event)); + } : null, + onMouseOver: function onMouseOver(event) { + props.onMouseOver && props.onMouseOver(eventParameters(event)); + setInternalHover(true); + }, + onMouseOut: function onMouseOut(event) { + props.onMouseOut && props.onMouseOut(eventParameters(event)); + setInternalHover(false); + } + }, /*#__PURE__*/React.createElement(GeometryCollection, _objectSpread2(_objectSpread2(_objectSpread2({}, props), props.feature), {}, { + svgAttributes: svgAttributes + }))); +} +function GeoJson(props) { + var _props$mapState = props.mapState, + width = _props$mapState.width, + height = _props$mapState.height; + return /*#__PURE__*/React.createElement("div", { + style: _objectSpread2({ + position: 'absolute', + left: '0', + top: '0', + pointerEvents: 'none', + cursor: 'pointer' + }, props.style || {}), + className: props.className ? props.className + " pigeon-click-block" : 'pigeon-click-block' + }, /*#__PURE__*/React.createElement("svg", { + width: width, + height: height, + viewBox: "0 0 " + width + " " + height, + fill: "none", + xmlns: "http://www.w3.org/2000/svg" + }, props.data && props.data.features.map(function (feature, i) { + return /*#__PURE__*/React.createElement(GeoJsonFeature, _objectSpread2(_objectSpread2({ + key: i + }, props), {}, { + feature: feature + })); + }), React.Children.map(props.children, function (child) { + if (!child) { + return null; + } + + if (! /*#__PURE__*/React.isValidElement(child)) { + return child; + } + + return /*#__PURE__*/React.cloneElement(child, props); + }))); +} +function GeoJsonLoader(props) { + var _useState2 = useState(props.data ? props.data : null), + data = _useState2[0], + setData = _useState2[1]; + + useEffect(function () { + fetch(props.link).then(function (response) { + return response.json(); + }).then(function (data) { + return setData(data); + }); + }, [props.link]); + return data ? /*#__PURE__*/React.createElement(GeoJson, _objectSpread2({ + data: data + }, props)) : null; +} + +function isDescendentOf(element, ancestor) { + while (element) { + if (element === ancestor) { + return true; + } + + element = element.parentElement; + } + + return false; +} + +var defaultState = { + isDragging: false, + startX: undefined, + startY: undefined, + startLeft: undefined, + startTop: undefined, + deltaX: 0, + deltaY: 0 +}; +function Draggable(props) { + var dragRef = useRef(); + var propsRef = useRef(props); + var stateRef = useRef(_objectSpread2({}, defaultState)); + + var _useState = useState(defaultState), + _state = _useState[0], + _setState = _useState[1]; + + propsRef.current = props; + + var setState = function setState(stateUpdate) { + var newState = _objectSpread2(_objectSpread2({}, stateRef.current), stateUpdate); + + stateRef.current = newState; + + _setState(newState); + }; + + var _props$mapProps = props.mapProps, + mouseEvents = _props$mapProps.mouseEvents, + touchEvents = _props$mapProps.touchEvents; + useEffect(function () { + var handleDragStart = function handleDragStart(event) { + if (isDescendentOf(event.target, dragRef.current)) { + event.preventDefault(); + setState({ + isDragging: true, + startX: ('touches' in event ? event.touches[0] : event).clientX, + startY: ('touches' in event ? event.touches[0] : event).clientY, + startLeft: propsRef.current.left, + startTop: propsRef.current.top, + deltaX: 0, + deltaY: 0 + }); + + if (propsRef.current.onDragStart) { + var _propsRef$current = propsRef.current, + _left = _propsRef$current.left, + _top = _propsRef$current.top, + offset = _propsRef$current.offset, + pixelToLatLng = _propsRef$current.pixelToLatLng; + propsRef.current.onDragMove(pixelToLatLng([_left + (offset ? offset[0] : 0), _top + (offset ? offset[1] : 0)])); + } + } + }; + + var handleDragMove = function handleDragMove(event) { + if (!stateRef.current.isDragging) { + return; + } + + event.preventDefault(); + var x = ('touches' in event ? event.touches[0] : event).clientX; + var y = ('touches' in event ? event.touches[0] : event).clientY; + var deltaX = x - stateRef.current.startX; + var deltaY = y - stateRef.current.startY; + setState({ + deltaX: deltaX, + deltaY: deltaY + }); + + if (propsRef.current.onDragMove) { + var _propsRef$current2 = propsRef.current, + offset = _propsRef$current2.offset, + pixelToLatLng = _propsRef$current2.pixelToLatLng; + var _stateRef$current = stateRef.current, + _startLeft = _stateRef$current.startLeft, + _startTop = _stateRef$current.startTop; + propsRef.current.onDragMove(pixelToLatLng([_startLeft + deltaX + (offset ? offset[0] : 0), _startTop + deltaY + (offset ? offset[1] : 0)])); + } + }; + + var handleDragEnd = function handleDragEnd(event) { + var _propsRef$current$onD, _propsRef$current4; + + if (!stateRef.current.isDragging) { + return; + } + + event.preventDefault(); + var _propsRef$current3 = propsRef.current, + offset = _propsRef$current3.offset, + pixelToLatLng = _propsRef$current3.pixelToLatLng; + var _stateRef$current2 = stateRef.current, + deltaX = _stateRef$current2.deltaX, + deltaY = _stateRef$current2.deltaY, + startLeft = _stateRef$current2.startLeft, + startTop = _stateRef$current2.startTop; + (_propsRef$current$onD = (_propsRef$current4 = propsRef.current).onDragEnd) === null || _propsRef$current$onD === void 0 ? void 0 : _propsRef$current$onD.call(_propsRef$current4, pixelToLatLng([startLeft + deltaX + (offset ? offset[0] : 0), startTop + deltaY + (offset ? offset[1] : 0)])); + setState({ + isDragging: false, + startX: undefined, + startY: undefined, + startLeft: undefined, + startTop: undefined, + deltaX: 0, + deltaY: 0 + }); + }; + + var wa = function wa(e, t, o) { + return window.addEventListener(e, t, o); + }; + + var wr = function wr(e, t) { + return window.removeEventListener(e, t); + }; + + if (mouseEvents) { + wa('mousedown', handleDragStart); + wa('mousemove', handleDragMove); + wa('mouseup', handleDragEnd); + } + + if (touchEvents) { + wa('touchstart', handleDragStart, { + passive: false + }); + wa('touchmove', handleDragMove, { + passive: false + }); + wa('touchend', handleDragEnd, { + passive: false + }); + } + + return function () { + if (mouseEvents) { + wr('mousedown', handleDragStart); + wr('mousemove', handleDragMove); + wr('mouseup', handleDragEnd); + } + + if (touchEvents) { + wr('touchstart', handleDragStart); + wr('touchmove', handleDragMove); + wr('touchend', handleDragEnd); + } + }; + }, [mouseEvents, touchEvents]); + var left = props.left, + top = props.top, + className = props.className, + style = props.style; + var deltaX = _state.deltaX, + deltaY = _state.deltaY, + startLeft = _state.startLeft, + startTop = _state.startTop, + isDragging = _state.isDragging; + return /*#__PURE__*/React.createElement("div", { + style: _objectSpread2(_objectSpread2({ + cursor: isDragging ? 'grabbing' : 'grab' + }, style || {}), {}, { + position: 'absolute', + transform: "translate(" + (isDragging ? startLeft + deltaX : left) + "px, " + (isDragging ? startTop + deltaY : top) + "px)" + }), + ref: dragRef, + className: "pigeon-drag-block" + (className ? " " + className : '') + }, props.children); +} + +var commonStyle = { + position: 'absolute', + top: 10, + left: 10 +}; +var commonButtonStyle = { + width: 28, + height: 28, + borderRadius: 2, + boxShadow: '0 1px 4px -1px rgba(0,0,0,.3)', + background: 'white', + lineHeight: '26px', + fontSize: '20px', + fontWeight: 700, + color: '#666', + marginBottom: 1, + cursor: 'pointer', + border: 'none', + display: 'block', + outline: 'none' +}; +function ZoomControl(_ref) { + var style = _ref.style, + buttonStyle = _ref.buttonStyle, + setCenterZoom = _ref.setCenterZoom, + mapState = _ref.mapState, + mapProps = _ref.mapProps; + return /*#__PURE__*/React.createElement("div", { + className: "pigeon-zoom-buttons pigeon-drag-block", + style: style ? _objectSpread2(_objectSpread2({}, commonStyle), style) : commonStyle + }, /*#__PURE__*/React.createElement("button", { + className: "pigeon-zoom-in", + type: "button", + style: buttonStyle ? _objectSpread2(_objectSpread2({}, commonButtonStyle), buttonStyle) : commonButtonStyle, + onClick: function onClick() { + return setCenterZoom(mapState.center, Math.min(mapState.zoom + 1, mapProps.maxZoom)); + } + }, "+"), /*#__PURE__*/React.createElement("button", { + className: "pigeon-zoom-out", + type: "button", + style: buttonStyle ? _objectSpread2(_objectSpread2({}, commonButtonStyle), buttonStyle) : commonButtonStyle, + onClick: function onClick() { + return setCenterZoom(mapState.center, Math.max(mapState.zoom - 1, mapProps.minZoom)); + } + }, "\u2013")); +} + +export { Draggable, GeoJson, GeoJsonFeature, GeoJsonLoader, Map, Marker, Overlay, ZoomControl }; diff --git a/node_modules/pigeon-maps/lib/map/Map.d.ts b/node_modules/pigeon-maps/lib/map/Map.d.ts new file mode 100644 index 0000000..b0588aa --- /dev/null +++ b/node_modules/pigeon-maps/lib/map/Map.d.ts @@ -0,0 +1,119 @@ +import { Component } from 'react';
+import { Bounds, MapProps, MapReactState, MinMaxBounds, MoveEvent, Point, TileComponent, TileValues, WAdd, WarningType, WRem } from '../types';
+export declare class Map extends Component<MapProps, MapReactState> {
+ static defaultProps: {
+ animate: boolean;
+ metaWheelZoom: boolean;
+ metaWheelZoomWarning: string;
+ twoFingerDrag: boolean;
+ twoFingerDragWarning: string;
+ zoomSnap: boolean;
+ mouseEvents: boolean;
+ touchEvents: boolean;
+ warningZIndex: number;
+ animateMaxScreens: number;
+ minZoom: number;
+ maxZoom: number;
+ limitBounds: string;
+ dprs: any[];
+ tileComponent: TileComponent;
+ };
+ _containerRef?: HTMLDivElement;
+ _mousePosition?: Point;
+ _loadTracker?: {
+ [key: string]: boolean;
+ };
+ _dragStart: Point | null;
+ _mouseDown: boolean;
+ _moveEvents: MoveEvent[];
+ _lastClick: number | null;
+ _lastTap: number | null;
+ _lastWheel: number | null;
+ _touchStartPixel: Point[] | null;
+ _touchStartMidPoint: Point | null;
+ _touchStartDistance: number | null;
+ _secondTouchEnd: number | null;
+ _warningClearTimeout: number | null;
+ _isAnimating: boolean;
+ _animationStart: number | null;
+ _animationEnd: number | null;
+ _zoomStart: number | null;
+ _centerTarget: Point | null;
+ _zoomTarget: number | null;
+ _zoomAround: Point | null;
+ _animFrame: number | null;
+ _boundsSynced: boolean;
+ _minMaxCache: [number, number, number, MinMaxBounds] | null;
+ _lastZoom: number;
+ _lastCenter: Point;
+ _centerStart?: Point;
+ _resizeObserver: any;
+ constructor(props: MapProps);
+ componentDidMount(): void;
+ componentWillUnmount(): void;
+ updateWidthHeight: () => boolean;
+ wa: WAdd;
+ wr: WRem;
+ bindMouseEvents: () => void;
+ bindTouchEvents: () => void;
+ unbindMouseEvents: () => void;
+ unbindTouchEvents: () => void;
+ bindResizeEvent: () => void;
+ unbindResizeEvent: () => void;
+ bindWheelEvent: () => void;
+ unbindWheelEvent: () => void;
+ componentDidUpdate(prevProps: MapProps): void;
+ setCenterZoomTarget: (center: Point | null, zoom: number, fromProps?: boolean, zoomAround?: Point | null, animationDuration?: number) => void;
+ setCenterZoomForChildren: (center: Point | null, zoom: number) => void;
+ distanceInScreens: (centerTarget: Point, zoomTarget: number, center: Point, zoom: number) => number;
+ animationStep: (timestamp: number) => {
+ centerStep: Point;
+ zoomStep: number;
+ };
+ animate: (timestamp: number) => void;
+ stopAnimating: () => void;
+ limitCenterAtZoom: (center?: Point | null, zoom?: number | null) => Point;
+ onAnimationStart: () => void;
+ onAnimationStop: () => void;
+ setCenterZoom: (center?: Point | null, zoom?: number | null, animationEnded?: boolean) => void;
+ getBoundsMinMax: (zoom: number) => MinMaxBounds;
+ tileLoaded: (key: string) => void;
+ coordsInside(pixel: Point): boolean;
+ handleTouchStart: (event: TouchEvent) => void;
+ handleTouchMove: (event: TouchEvent) => void;
+ handleTouchEnd: (event: TouchEvent) => void;
+ handleMouseDown: (event: MouseEvent) => void;
+ handleMouseMove: (event: MouseEvent) => void;
+ handleMouseUp: (event: MouseEvent) => void;
+ stopTrackingMoveEvents: () => void;
+ trackMoveEvents: (coords: Point) => void;
+ throwAfterMoving: (coords: Point, center: Point, zoom: number) => void;
+ sendDeltaChange: () => {
+ center: Point;
+ zoom: number;
+ };
+ getBounds: (center?: Point, zoom?: number) => Bounds;
+ syncToProps: (center?: Point, zoom?: number) => void;
+ handleWheel: (event: WheelEvent) => void;
+ showWarning: (warningType: WarningType) => void;
+ clearWarning: () => void;
+ zoomAroundMouse: (zoomDiff: number, event: MouseEvent) => void;
+ zoomPlusDelta: () => number;
+ pixelToLatLng: (pixel: Point, center?: Point, zoom?: number) => Point;
+ latLngToPixel: (latLng: Point, center?: Point, zoom?: number) => Point;
+ calculateZoomCenter: (center: Point, coords: Point, oldZoom: number, newZoom: number) => Point;
+ setRef: (dom: HTMLDivElement) => void;
+ tileValues({ center, zoom, pixelDelta, zoomDelta, width, height, }: {
+ center: Point;
+ zoom: number;
+ pixelDelta?: Point;
+ zoomDelta?: number;
+ width: number;
+ height: number;
+ }): TileValues;
+ renderTiles(): JSX.Element;
+ renderOverlays(): JSX.Element;
+ renderAttribution(): JSX.Element | null;
+ renderWarning(): JSX.Element | null;
+ render(): JSX.Element;
+}
diff --git a/node_modules/pigeon-maps/lib/overlays/Draggable.d.ts b/node_modules/pigeon-maps/lib/overlays/Draggable.d.ts new file mode 100644 index 0000000..16a0623 --- /dev/null +++ b/node_modules/pigeon-maps/lib/overlays/Draggable.d.ts @@ -0,0 +1,12 @@ +import React from 'react';
+import { PigeonProps, Point } from '../types';
+interface DraggableProps extends PigeonProps {
+ className?: string;
+ style?: React.CSSProperties;
+ children?: React.ReactNode;
+ onDragStart?: (anchor: Point) => void;
+ onDragMove?: (anchor: Point) => void;
+ onDragEnd?: (anchor: Point) => void;
+}
+export declare function Draggable(props: DraggableProps): JSX.Element;
+export {};
diff --git a/node_modules/pigeon-maps/lib/overlays/GeoJson.d.ts b/node_modules/pigeon-maps/lib/overlays/GeoJson.d.ts new file mode 100644 index 0000000..88a5ff4 --- /dev/null +++ b/node_modules/pigeon-maps/lib/overlays/GeoJson.d.ts @@ -0,0 +1,57 @@ +import React, { CSSProperties, SVGProps } from 'react';
+import { PigeonProps, Point } from '../types';
+interface GeoJsonProps extends PigeonProps {
+ className?: string;
+ data?: any;
+ svgAttributes?: any;
+ styleCallback?: any;
+ hover?: any;
+ feature?: any;
+ style?: CSSProperties;
+ children?: React.ReactNode;
+ onClick?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+ onContextMenu?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+ onMouseOver?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+ onMouseOut?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+}
+interface GeoJsonLoaderProps extends GeoJsonProps {
+ link?: string;
+}
+interface GeoJsonGeometry {
+ type: string;
+ coordinates?: [number, number] | Array<[number, number]> | Array<Array<[number, number]>> | Array<Array<Array<[number, number]>>>;
+ geometries?: Array<GeoJsonGeometry>;
+}
+interface GeometryProps {
+ coordinates?: [number, number] | Array<[number, number]> | Array<Array<[number, number]>> | Array<Array<Array<[number, number]>>>;
+ latLngToPixel?: (latLng: Point, center?: Point, zoom?: number) => Point;
+ svgAttributes?: SVGProps<SVGElement>;
+ geometry?: GeoJsonGeometry;
+}
+export declare function PointComponent(props: GeometryProps): JSX.Element;
+export declare function MultiPoint(props: GeometryProps): JSX.Element;
+export declare function LineString(props: GeometryProps): JSX.Element;
+export declare function MultiLineString(props: GeometryProps): JSX.Element;
+export declare function Polygon(props: GeometryProps): JSX.Element;
+export declare function MultiPolygon(props: GeometryProps): JSX.Element;
+export declare function GeometryCollection(props: GeometryProps): JSX.Element;
+export declare function GeoJsonFeature(props: GeoJsonProps): JSX.Element;
+export declare function GeoJson(props: GeoJsonProps): JSX.Element;
+export declare function GeoJsonLoader(props: GeoJsonLoaderProps): JSX.Element;
+export {};
diff --git a/node_modules/pigeon-maps/lib/overlays/Marker.d.ts b/node_modules/pigeon-maps/lib/overlays/Marker.d.ts new file mode 100644 index 0000000..8cb7d5d --- /dev/null +++ b/node_modules/pigeon-maps/lib/overlays/Marker.d.ts @@ -0,0 +1,34 @@ +import React from 'react';
+import { PigeonProps } from '../types';
+interface MarkerProps extends PigeonProps {
+ color?: string;
+ payload?: any;
+ width?: number;
+ height?: number;
+ hover?: boolean;
+ style?: React.CSSProperties;
+ className?: string;
+ children?: JSX.Element;
+ onClick?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+ onContextMenu?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+ onMouseOver?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+ onMouseOut?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }: {
+ event: any;
+ anchor: any;
+ payload: any;
+ }) => void;
+}
+export declare function Marker(props: MarkerProps): JSX.Element;
+export {};
diff --git a/node_modules/pigeon-maps/lib/overlays/Overlay.d.ts b/node_modules/pigeon-maps/lib/overlays/Overlay.d.ts new file mode 100644 index 0000000..3949d2f --- /dev/null +++ b/node_modules/pigeon-maps/lib/overlays/Overlay.d.ts @@ -0,0 +1,9 @@ +import React from 'react';
+import { PigeonProps } from '../types';
+interface OverlayProps extends PigeonProps {
+ style?: React.CSSProperties;
+ className?: string;
+ children?: React.ReactNode;
+}
+export declare function Overlay(props: OverlayProps): JSX.Element;
+export {};
diff --git a/node_modules/pigeon-maps/lib/providers.cjs.js b/node_modules/pigeon-maps/lib/providers.cjs.js new file mode 100644 index 0000000..58e063f --- /dev/null +++ b/node_modules/pigeon-maps/lib/providers.cjs.js @@ -0,0 +1,53 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +function osm(x, y, z) { + return "https://tile.openstreetmap.org/" + z + "/" + x + "/" + y + ".png"; +} +function stamenToner(x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://stamen-tiles.a.ssl.fastly.net/toner/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".png"; +} +function stamenTerrain(x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://stamen-tiles.a.ssl.fastly.net/terrain/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".jpg"; +} +var maptiler = function maptiler(apiKey, map) { + if (map === void 0) { + map = 'streets'; + } + + return function (x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://api.maptiler.com/maps/" + map + "/256/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".png?key=" + apiKey; + }; +}; +var stadiamaps = function stadiamaps(style) { + if (style === void 0) { + style = 'alidade_smooth'; + } + + return function (x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://tiles.stadiamaps.com/styles/" + style + "/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".png"; + }; +}; + +exports.maptiler = maptiler; +exports.osm = osm; +exports.stadiamaps = stadiamaps; +exports.stamenTerrain = stamenTerrain; +exports.stamenToner = stamenToner; diff --git a/node_modules/pigeon-maps/lib/providers.d.ts b/node_modules/pigeon-maps/lib/providers.d.ts new file mode 100644 index 0000000..3744cfc --- /dev/null +++ b/node_modules/pigeon-maps/lib/providers.d.ts @@ -0,0 +1,5 @@ +export declare function osm(x: number, y: number, z: number): string;
+export declare function stamenToner(x: number, y: number, z: number, dpr?: number): string;
+export declare function stamenTerrain(x: number, y: number, z: number, dpr?: number): string;
+export declare const maptiler: (apiKey: string, map?: string) => (x: number, y: number, z: number, dpr?: number) => string;
+export declare const stadiamaps: (style?: string) => (x: number, y: number, z: number, dpr?: number) => string;
diff --git a/node_modules/pigeon-maps/lib/providers.esm.js b/node_modules/pigeon-maps/lib/providers.esm.js new file mode 100644 index 0000000..de5299e --- /dev/null +++ b/node_modules/pigeon-maps/lib/providers.esm.js @@ -0,0 +1,45 @@ +function osm(x, y, z) { + return "https://tile.openstreetmap.org/" + z + "/" + x + "/" + y + ".png"; +} +function stamenToner(x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://stamen-tiles.a.ssl.fastly.net/toner/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".png"; +} +function stamenTerrain(x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://stamen-tiles.a.ssl.fastly.net/terrain/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".jpg"; +} +var maptiler = function maptiler(apiKey, map) { + if (map === void 0) { + map = 'streets'; + } + + return function (x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://api.maptiler.com/maps/" + map + "/256/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".png?key=" + apiKey; + }; +}; +var stadiamaps = function stadiamaps(style) { + if (style === void 0) { + style = 'alidade_smooth'; + } + + return function (x, y, z, dpr) { + if (dpr === void 0) { + dpr = 1; + } + + return "https://tiles.stadiamaps.com/styles/" + style + "/" + z + "/" + x + "/" + y + (dpr >= 2 ? '@2x' : '') + ".png"; + }; +}; + +export { maptiler, osm, stadiamaps, stamenTerrain, stamenToner }; diff --git a/node_modules/pigeon-maps/lib/types.d.ts b/node_modules/pigeon-maps/lib/types.d.ts new file mode 100644 index 0000000..a9f1de7 --- /dev/null +++ b/node_modules/pigeon-maps/lib/types.d.ts @@ -0,0 +1,119 @@ +import React from 'react';
+export declare type Point = [number, number];
+export interface Bounds {
+ ne: [number, number];
+ sw: [number, number];
+}
+export interface MapProps {
+ center?: Point;
+ defaultCenter?: Point;
+ zoom?: number;
+ defaultZoom?: number;
+ width?: number;
+ defaultWidth?: number;
+ height?: number;
+ defaultHeight?: number;
+ provider?: (x: number, y: number, z: number, dpr?: number) => string;
+ dprs?: number[];
+ children?: React.ReactNode;
+ animate?: boolean;
+ animateMaxScreens?: number;
+ minZoom?: number;
+ maxZoom?: number;
+ metaWheelZoom?: boolean;
+ metaWheelZoomWarning?: string;
+ twoFingerDrag?: boolean;
+ twoFingerDragWarning?: string;
+ warningZIndex?: number;
+ attribution?: JSX.Element | false;
+ attributionPrefix?: JSX.Element | false;
+ zoomSnap?: boolean;
+ mouseEvents?: boolean;
+ touchEvents?: boolean;
+ onClick?: ({ event, latLng, pixel }: {
+ event: MouseEvent;
+ latLng: [number, number];
+ pixel: [number, number];
+ }) => void;
+ onBoundsChanged?: ({ center, zoom, bounds, initial, }: {
+ center: [number, number];
+ bounds: Bounds;
+ zoom: number;
+ initial: boolean;
+ }) => void;
+ onAnimationStart?: () => void;
+ onAnimationStop?: () => void;
+ limitBounds?: 'center' | 'edge';
+ boxClassname?: string;
+ tileComponent?: TileComponent;
+}
+export declare type TileComponent = (props: TileComponentProps) => JSX.Element;
+export interface TileComponentProps {
+ tile: Tile;
+ tileLoaded: () => void;
+}
+export interface Tile {
+ key: string;
+ url: string;
+ srcSet: string;
+ left: number;
+ top: number;
+ width: number;
+ height: number;
+ active: boolean;
+}
+export interface TileValues {
+ tileMinX: number;
+ tileMaxX: number;
+ tileMinY: number;
+ tileMaxY: number;
+ tileCenterX: number;
+ tileCenterY: number;
+ roundedZoom: number;
+ zoomDelta: number;
+ scaleWidth: number;
+ scaleHeight: number;
+ scale: number;
+}
+export declare type WarningType = 'fingers' | 'wheel';
+export declare type WAdd = typeof window.addEventListener;
+export declare type WRem = typeof window.removeEventListener;
+export interface MoveEvent {
+ timestamp: number;
+ coords: Point;
+}
+declare type MinLat = number;
+declare type MaxLat = number;
+declare type MinLng = number;
+declare type MaxLng = number;
+export declare type MinMaxBounds = [MinLat, MaxLat, MinLng, MaxLng];
+export interface MapReactState {
+ zoom: number;
+ center: Point;
+ width: number;
+ height: number;
+ zoomDelta: number;
+ pixelDelta?: [number, number];
+ oldTiles: TileValues[];
+ showWarning: boolean;
+ warningType?: WarningType;
+}
+export interface MapState {
+ bounds: Bounds;
+ zoom: number;
+ center: Point;
+ width: number;
+ height: number;
+}
+export interface PigeonProps {
+ anchor?: Point;
+ offset?: Point;
+ left?: number;
+ top?: number;
+ mapState?: MapState;
+ mapProps?: MapProps;
+ latLngToPixel?: (latLng: Point, center?: Point, zoom?: number) => Point;
+ pixelToLatLng?: (pixel: Point, center?: Point, zoom?: number) => Point;
+ setCenterZoom?: (center: Point | null, zoom: number, zoomAround?: Point | null, animationDuration?: number) => void;
+}
+export {};
diff --git a/node_modules/pigeon-maps/lib/utils.d.ts b/node_modules/pigeon-maps/lib/utils.d.ts new file mode 100644 index 0000000..c4a6d46 --- /dev/null +++ b/node_modules/pigeon-maps/lib/utils.d.ts @@ -0,0 +1,6 @@ +export declare function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (this: any, ...args: Parameters<T>) => void;
+export declare function parentHasClass(element: HTMLElement, className: string): boolean;
+export declare function parentPosition(element: HTMLElement): {
+ x: number;
+ y: number;
+};
diff --git a/node_modules/pigeon-maps/package.json b/node_modules/pigeon-maps/package.json new file mode 100644 index 0000000..8aa6b56 --- /dev/null +++ b/node_modules/pigeon-maps/package.json @@ -0,0 +1,103 @@ +{ + "name": "pigeon-maps", + "version": "0.21.6", + "description": "ReactJS maps without external dependencies", + "author": "Marius Andra", + "license": "MIT", + "types": "lib/index.d.ts", + "main": "lib/index.cjs.js", + "module": "lib/index.esm.js", + "repository": { + "type": "git", + "url": "git+https://github.com/mariusandra/pigeon-maps.git" + }, + "bugs": { + "url": "https://github.com/mariusandra/pigeon-maps/issues" + }, + "homepage": "https://github.com/mariusandra/pigeon-maps#readme", + "scripts": { + "start": "webpack-dev-server --history-api-fallback --hot --host 0.0.0.0 --port 4040", + "build": "rimraf lib && rollup -c && rimraf lib/demo && mv lib/src/* lib/ && rimraf lib/src", + "prepublishOnly": "yarn build", + "prepare": "npm run build", + "prettier": "prettier --write \"src/**/*.tsx\" \"demo/**/*.tsx\" ", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "files": [ + "lib", + "src", + "LICENSE.md", + "README.md", + "providers" + ], + "dependencies": {}, + "peerDependencies": { + "react": "*" + }, + "devDependencies": { + "@babel/cli": "^7.16.8", + "@babel/core": "^7.16.12", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-decorators": "^7.16.7", + "@babel/plugin-proposal-do-expressions": "^7.16.7", + "@babel/plugin-proposal-export-default-from": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-function-bind": "^7.16.7", + "@babel/plugin-proposal-function-sent": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-pipeline-operator": "^7.16.7", + "@babel/plugin-proposal-throw-expressions": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/preset-env": "^7.16.11", + "@babel/preset-react": "^7.16.7", + "@babel/preset-typescript": "^7.16.7", + "@types/react": "17.0.38", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.2.3", + "babel-plugin-add-module-exports": "1.0.4", + "babel-plugin-minify-dead-code-elimination": "^0.5.1", + "babel-plugin-react-transform": "^3.0.0", + "babel-plugin-transform-babel-env-inline": "^0.0.1", + "eslint": "^7.6.0", + "eslint-config-prettier": "^6.11.0", + "eslint-config-standard": "14.1.1", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-promise": "4.2.1", + "eslint-plugin-react": "^7.20.5", + "eslint-plugin-standard": "4.0.1", + "file-loader": "^6.0.0", + "husky": "^4", + "lint-staged": ">=10.5.1", + "prettier": "^2.0.5", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "rimraf": "^3.0.2", + "rollup": "^2.66.1", + "rollup-plugin-babel": "^4.4.0", + "rollup-plugin-dts": "^4.1.0", + "rollup-plugin-typescript2": "^0.31.1", + "tslib": "^2.0.1", + "typescript": "^4.2.3", + "webpack": "^4.44.1", + "webpack-cli": "^3.3.12", + "webpack-dev-server": "^3.11.0" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{js,css,scss}": "prettier --write" + } +} diff --git a/node_modules/pigeon-maps/providers/package.json b/node_modules/pigeon-maps/providers/package.json new file mode 100644 index 0000000..3573075 --- /dev/null +++ b/node_modules/pigeon-maps/providers/package.json @@ -0,0 +1,7 @@ +{ + "name": "pigeon-maps/providers", + "private": true, + "types": "../lib/providers.d.ts", + "main": "../lib/providers.cjs.js", + "module": "../lib/providers.esm.js" +}
\ No newline at end of file diff --git a/node_modules/pigeon-maps/src/controls/ZoomControl.tsx b/node_modules/pigeon-maps/src/controls/ZoomControl.tsx new file mode 100644 index 0000000..a98ba39 --- /dev/null +++ b/node_modules/pigeon-maps/src/controls/ZoomControl.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import { PigeonProps } from '../types' + +interface ZoomProps extends PigeonProps { + style?: React.CSSProperties + buttonStyle?: React.CSSProperties +} + +const commonStyle: React.CSSProperties = { + position: 'absolute', + top: 10, + left: 10, +} + +const commonButtonStyle: React.CSSProperties = { + width: 28, + height: 28, + borderRadius: 2, + boxShadow: '0 1px 4px -1px rgba(0,0,0,.3)', + background: 'white', + lineHeight: '26px', + fontSize: '20px', + fontWeight: 700, + color: '#666', + marginBottom: 1, + cursor: 'pointer', + border: 'none', + display: 'block', + outline: 'none', +} + +export function ZoomControl({ style, buttonStyle, setCenterZoom, mapState, mapProps }: ZoomProps): JSX.Element { + return ( + <div className="pigeon-zoom-buttons pigeon-drag-block" style={style ? { ...commonStyle, ...style } : commonStyle}> + <button + className="pigeon-zoom-in" + type="button" + style={buttonStyle ? { ...commonButtonStyle, ...buttonStyle } : commonButtonStyle} + onClick={() => setCenterZoom(mapState.center, Math.min(mapState.zoom + 1, mapProps.maxZoom))} + > + + + </button> + <button + className="pigeon-zoom-out" + type="button" + style={buttonStyle ? { ...commonButtonStyle, ...buttonStyle } : commonButtonStyle} + onClick={() => setCenterZoom(mapState.center, Math.max(mapState.zoom - 1, mapProps.minZoom))} + > + – + </button> + </div> + ) +} diff --git a/node_modules/pigeon-maps/src/index.tsx b/node_modules/pigeon-maps/src/index.tsx new file mode 100644 index 0000000..78617f9 --- /dev/null +++ b/node_modules/pigeon-maps/src/index.tsx @@ -0,0 +1,7 @@ +export * from './types' +export { Map } from './map/Map' +export { Marker } from './overlays/Marker' +export { Overlay } from './overlays/Overlay' +export { GeoJson, GeoJsonLoader, GeoJsonFeature } from './overlays/GeoJson' +export { Draggable } from './overlays/Draggable' +export { ZoomControl } from './controls/ZoomControl' diff --git a/node_modules/pigeon-maps/src/map/Map.tsx b/node_modules/pigeon-maps/src/map/Map.tsx new file mode 100644 index 0000000..38a9c26 --- /dev/null +++ b/node_modules/pigeon-maps/src/map/Map.tsx @@ -0,0 +1,1411 @@ +import React, { Component } from 'react' + +import { debounce, parentPosition, parentHasClass } from '../utils' +import { + Bounds, + MapProps, + MapReactState, + MinMaxBounds, + MoveEvent, + Point, + Tile, + TileComponent, + TileValues, + WAdd, + WarningType, + WRem, +} from '../types' +import { osm } from '../providers' + +const ANIMATION_TIME = 300 +const DIAGONAL_THROW_TIME = 1500 +const SCROLL_PIXELS_FOR_ZOOM_LEVEL = 150 +const MIN_DRAG_FOR_THROW = 40 +const CLICK_TOLERANCE = 2 +const DOUBLE_CLICK_DELAY = 300 +const DEBOUNCE_DELAY = 60 +const PINCH_RELEASE_THROW_DELAY = 300 +const WARNING_DISPLAY_TIMEOUT = 300 + +const NOOP = () => true + +// https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames +const lng2tile = (lon: number, zoom: number): number => ((lon + 180) / 360) * Math.pow(2, zoom) +const lat2tile = (lat: number, zoom: number): number => + ((1 - Math.log(Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180)) / Math.PI) / 2) * + Math.pow(2, zoom) + +function tile2lng(x: number, z: number): number { + return (x / Math.pow(2, z)) * 360 - 180 +} + +function tile2lat(y: number, z: number): number { + const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z) + return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))) +} + +function getMousePixel(dom: HTMLElement, event: Pick<MouseEvent, 'clientX' | 'clientY'>): Point { + const parent = parentPosition(dom) + return [event.clientX - parent.x, event.clientY - parent.y] +} + +function easeOutQuad(t: number): number { + return t * (2 - t) +} + +// minLat, maxLat, minLng, maxLng +const absoluteMinMax = [ + tile2lat(Math.pow(2, 10), 10), + tile2lat(0, 10), + tile2lng(0, 10), + tile2lng(Math.pow(2, 10), 10), +] as MinMaxBounds + +const hasWindow = typeof window !== 'undefined' + +const performanceNow = + hasWindow && window.performance && window.performance.now + ? () => window.performance.now() + : (() => { + const timeStart = new Date().getTime() + return () => new Date().getTime() - timeStart + })() + +const requestAnimationFrame = (callback: (timestamp: number) => void): number | null => { + if (hasWindow) { + return (window.requestAnimationFrame || window.setTimeout)(callback) + } else { + callback(new Date().getTime()) + return null + } +} +const cancelAnimationFrame = (animFrame: number | null) => + hasWindow && animFrame ? (window.cancelAnimationFrame || window.clearTimeout)(animFrame) : false + +function srcSet( + dprs: number[], + url: (x: number, y: number, z: number, dpr?: number) => string, + x: number, + y: number, + z: number +): string { + if (!dprs || dprs.length === 0) { + return '' + } + return dprs.map((dpr) => url(x, y, z, dpr) + (dpr === 1 ? '' : ` ${dpr}x`)).join(', ') +} + +const ImgTile: TileComponent = ({ tile, tileLoaded }) => ( + <img + src={tile.url} + srcSet={tile.srcSet} + width={tile.width} + height={tile.height} + loading={'lazy'} + onLoad={tileLoaded} + alt={''} + style={{ + position: 'absolute', + left: tile.left, + top: tile.top, + willChange: 'transform', + transformOrigin: 'top left', + opacity: 1, + }} + /> +) + +export class Map extends Component<MapProps, MapReactState> { + static defaultProps = { + animate: true, + metaWheelZoom: false, + metaWheelZoomWarning: 'Use META + wheel to zoom!', + twoFingerDrag: false, + twoFingerDragWarning: 'Use two fingers to move the map', + zoomSnap: true, + mouseEvents: true, + touchEvents: true, + warningZIndex: 100, + animateMaxScreens: 5, + minZoom: 1, + maxZoom: 18, + limitBounds: 'center', + dprs: [], + tileComponent: ImgTile, + } + + _containerRef?: HTMLDivElement + _mousePosition?: Point + _loadTracker?: { [key: string]: boolean } + _dragStart: Point | null = null + _mouseDown = false + _moveEvents: MoveEvent[] = [] + _lastClick: number | null = null + _lastTap: number | null = null + _lastWheel: number | null = null + _touchStartPixel: Point[] | null = null + _touchStartMidPoint: Point | null = null + _touchStartDistance: number | null = null + _secondTouchEnd: number | null = null + _warningClearTimeout: number | null = null + + _isAnimating = false + _animationStart: number | null = null + _animationEnd: number | null = null + _zoomStart: number | null = null + _centerTarget: Point | null = null + _zoomTarget: number | null = null + _zoomAround: Point | null = null + _animFrame: number | null = null + + _boundsSynced = false + _minMaxCache: [number, number, number, MinMaxBounds] | null = null + + _lastZoom: number + _lastCenter: Point + _centerStart?: Point + + _resizeObserver = null + + constructor(props: MapProps) { + super(props) + + this.syncToProps = debounce(this.syncToProps, DEBOUNCE_DELAY) + + // When users are using uncontrolled components we have to keep this + // so we can know if we should call onBoundsChanged + this._lastZoom = props.defaultZoom ?? props.zoom ?? 14 + this._lastCenter = props.defaultCenter ?? props.center ?? [0, 0] + + this.state = { + zoom: this._lastZoom, + center: this._lastCenter, + width: props.width ?? props.defaultWidth ?? -1, + height: props.height ?? props.defaultHeight ?? -1, + zoomDelta: 0, + pixelDelta: undefined, + oldTiles: [], + showWarning: false, + warningType: undefined, + } + } + + componentDidMount(): void { + this.props.mouseEvents && this.bindMouseEvents() + this.props.touchEvents && this.bindTouchEvents() + + if (!this.props.width || !this.props.height) { + // A height:100% container div often results in height=0 being returned on mount. + // So ask again once everything is painted. + if (!this.updateWidthHeight()) { + requestAnimationFrame(this.updateWidthHeight) + } + this.bindResizeEvent() + } + + this.bindWheelEvent() + this.syncToProps() + + if (typeof (window as any).ResizeObserver !== 'undefined') { + this._resizeObserver = new (window as any).ResizeObserver(() => { + this.updateWidthHeight() + }) + + this._resizeObserver.observe(this._containerRef) + } + } + + componentWillUnmount(): void { + this.props.mouseEvents && this.unbindMouseEvents() + this.props.touchEvents && this.unbindTouchEvents() + + this.unbindWheelEvent() + + if (!this.props.width || !this.props.height) { + this.unbindResizeEvent() + } + + if (this._resizeObserver) { + this._resizeObserver.disconnect() + } + } + + updateWidthHeight = (): boolean => { + if (this._containerRef) { + const rect = this._containerRef.getBoundingClientRect() + + if (rect && rect.width > 0 && rect.height > 0) { + this.setState({ + width: rect.width, + height: rect.height, + }) + return true + } + } + return false + } + + wa: WAdd = (...args: Parameters<WAdd>) => window.addEventListener(...args) + wr: WRem = (...args: Parameters<WRem>) => window.removeEventListener(...args) + + bindMouseEvents = (): void => { + this.wa('mousedown', this.handleMouseDown) + this.wa('mouseup', this.handleMouseUp) + this.wa('mousemove', this.handleMouseMove) + } + + bindTouchEvents = (): void => { + this.wa('touchstart', this.handleTouchStart, { passive: false }) + this.wa('touchmove', this.handleTouchMove, { passive: false }) + this.wa('touchend', this.handleTouchEnd, { passive: false }) + } + + unbindMouseEvents = (): void => { + this.wr('mousedown', this.handleMouseDown) + this.wr('mouseup', this.handleMouseUp) + this.wr('mousemove', this.handleMouseMove) + } + + unbindTouchEvents = (): void => { + this.wr('touchstart', this.handleTouchStart) + this.wr('touchmove', this.handleTouchMove) + this.wr('touchend', this.handleTouchEnd) + } + + bindResizeEvent = (): void => { + this.wa('resize', this.updateWidthHeight) + } + + unbindResizeEvent = (): void => { + this.wr('resize', this.updateWidthHeight) + } + + bindWheelEvent = (): void => { + if (this._containerRef) { + this._containerRef.addEventListener('wheel', this.handleWheel, { passive: false }) + } + } + + unbindWheelEvent = (): void => { + if (this._containerRef) { + this._containerRef.removeEventListener('wheel', this.handleWheel) + } + } + + componentDidUpdate(prevProps: MapProps): void { + if (this.props.mouseEvents !== prevProps.mouseEvents) { + this.props.mouseEvents ? this.bindMouseEvents() : this.unbindMouseEvents() + } + + if (this.props.touchEvents !== prevProps.touchEvents) { + this.props.touchEvents ? this.bindTouchEvents() : this.unbindTouchEvents() + } + + if (this.props.width && this.props.width !== prevProps.width) { + this.setState({ width: this.props.width }) + } + + if (this.props.height && this.props.height !== prevProps.height) { + this.setState({ height: this.props.height }) + } + + if (!this.props.center && !this.props.zoom) { + // if the user isn't controlling neither zoom nor center we don't have to update. + return + } + if ( + (!this.props.center || + (this.props.center[0] === prevProps?.center?.[0] && this.props.center[1] === prevProps.center[1])) && + this.props.zoom === prevProps.zoom + ) { + // if the user is controlling either zoom or center but nothing changed + // we don't have to update aswell + return + } + + const currentCenter = this._isAnimating ? this._centerTarget : this.state.center + const currentZoom = this._isAnimating ? this._zoomTarget : this.state.zoom + + if (currentCenter && currentZoom) { + const nextCenter = this.props.center ?? currentCenter // prevent the rare null errors + const nextZoom = this.props.zoom ?? currentZoom + + if ( + Math.abs(nextZoom - currentZoom) > 0.001 || + Math.abs(nextCenter[0] - currentCenter[0]) > 0.0001 || + Math.abs(nextCenter[1] - currentCenter[1]) > 0.0001 + ) { + this.setCenterZoomTarget(nextCenter, nextZoom, true) + } + } + } + + setCenterZoomTarget = ( + center: Point | null, + zoom: number, + fromProps = false, + zoomAround: Point | null = null, + animationDuration = ANIMATION_TIME + ): void => { + if ( + this.props.animate && + (!fromProps || + this.distanceInScreens(center, zoom, this.state.center, this.state.zoom) <= this.props.animateMaxScreens) + ) { + if (this._isAnimating) { + cancelAnimationFrame(this._animFrame) + const { centerStep, zoomStep } = this.animationStep(performanceNow()) + this._centerStart = centerStep + this._zoomStart = zoomStep + } else { + this._isAnimating = true + this._centerStart = this.limitCenterAtZoom([this._lastCenter[0], this._lastCenter[1]], this._lastZoom) + this._zoomStart = this._lastZoom + this.onAnimationStart() + } + + this._animationStart = performanceNow() + this._animationEnd = this._animationStart + animationDuration + + if (zoomAround) { + this._zoomAround = zoomAround + this._centerTarget = this.calculateZoomCenter(this._lastCenter, zoomAround, this._lastZoom, zoom) + } else { + this._zoomAround = null + this._centerTarget = center + } + this._zoomTarget = zoom + + this._animFrame = requestAnimationFrame(this.animate) + } else { + this.stopAnimating() + + if (zoomAround) { + const center = this.calculateZoomCenter(this._lastCenter, zoomAround, this._lastZoom, zoom) + this.setCenterZoom(center, zoom, fromProps) + } else { + this.setCenterZoom(center || this.state.center, zoom, fromProps) + } + } + } + + setCenterZoomForChildren = (center: Point | null, zoom: number): void => { + this.setCenterZoomTarget(center || this.state.center, zoom || this.state.zoom, true) + } + + distanceInScreens = (centerTarget: Point, zoomTarget: number, center: Point, zoom: number): number => { + const { width, height } = this.state + + // distance in pixels at the current zoom level + const l1 = this.latLngToPixel(center, center, zoom) + const l2 = this.latLngToPixel(centerTarget, center, zoom) + + // distance in pixels at the target zoom level (could be the same) + const z1 = this.latLngToPixel(center, center, zoomTarget) + const z2 = this.latLngToPixel(centerTarget, center, zoomTarget) + + // take the average between the two and divide by width or height to get the distance multiplier in screens + const w = (Math.abs(l1[0] - l2[0]) + Math.abs(z1[0] - z2[0])) / 2 / width + const h = (Math.abs(l1[1] - l2[1]) + Math.abs(z1[1] - z2[1])) / 2 / height + + // return the distance + return Math.sqrt(w * w + h * h) + } + + animationStep = (timestamp: number): { centerStep: Point; zoomStep: number } => { + if ( + !this._animationEnd || + !this._animationStart || + !this._zoomTarget || + !this._zoomStart || + !this._centerStart || + !this._centerTarget + ) { + return { + centerStep: this.state.center, + zoomStep: this.state.zoom, + } + } + const length = this._animationEnd - this._animationStart + const progress = Math.max(timestamp - this._animationStart, 0) + const percentage = easeOutQuad(progress / length) + + const zoomDiff = (this._zoomTarget - this._zoomStart) * percentage + const zoomStep = this._zoomStart + zoomDiff + + if (this._zoomAround) { + const centerStep = this.calculateZoomCenter(this._centerStart, this._zoomAround, this._zoomStart, zoomStep) + + return { centerStep, zoomStep } + } else { + const centerStep = [ + this._centerStart[0] + (this._centerTarget[0] - this._centerStart[0]) * percentage, + this._centerStart[1] + (this._centerTarget[1] - this._centerStart[1]) * percentage, + ] as Point + + return { centerStep, zoomStep } + } + } + + animate = (timestamp: number): void => { + if (!this._animationEnd || timestamp >= this._animationEnd) { + this._isAnimating = false + this.setCenterZoom(this._centerTarget, this._zoomTarget, true) + this.onAnimationStop() + } else { + const { centerStep, zoomStep } = this.animationStep(timestamp) + this.setCenterZoom(centerStep, zoomStep) + this._animFrame = requestAnimationFrame(this.animate) + } + } + + stopAnimating = (): void => { + if (this._isAnimating) { + this._isAnimating = false + this.onAnimationStop() + cancelAnimationFrame(this._animFrame) + } + } + + limitCenterAtZoom = (center?: Point | null, zoom?: number | null): Point => { + // [minLat, maxLat, minLng, maxLng] + const minMax = this.getBoundsMinMax(zoom || this.state.zoom) + + return [ + Math.max(Math.min(!center || isNaN(center[0]) ? this.state.center[0] : center[0], minMax[1]), minMax[0]), + Math.max(Math.min(!center || isNaN(center[1]) ? this.state.center[1] : center[1], minMax[3]), minMax[2]), + ] as Point + } + + onAnimationStart = (): void => { + this.props.onAnimationStart && this.props.onAnimationStart() + } + + onAnimationStop = (): void => { + this.props.onAnimationStop && this.props.onAnimationStop() + } + + // main logic when changing coordinates + setCenterZoom = (center?: Point | null, zoom?: number | null, animationEnded = false): void => { + const limitedCenter = this.limitCenterAtZoom(center, zoom) + + if (zoom && Math.round(this.state.zoom) !== Math.round(zoom)) { + const tileValues = this.tileValues(this.state) + const nextValues = this.tileValues({ + center: limitedCenter, + zoom, + width: this.state.width, + height: this.state.height, + }) + const oldTiles = this.state.oldTiles + + this.setState( + { + oldTiles: oldTiles.filter((o) => o.roundedZoom !== tileValues.roundedZoom).concat(tileValues), + }, + NOOP + ) + + const loadTracker: { [key: string]: boolean } = {} + + for (let x = nextValues.tileMinX; x <= nextValues.tileMaxX; x++) { + for (let y = nextValues.tileMinY; y <= nextValues.tileMaxY; y++) { + const key = `${x}-${y}-${nextValues.roundedZoom}` + loadTracker[key] = false + } + } + + this._loadTracker = loadTracker + } + + this.setState({ center: limitedCenter, zoom: zoom || this.state.zoom }, NOOP) + + const maybeZoom = this.props.zoom ? this.props.zoom : this._lastZoom + const maybeCenter = this.props.center ? this.props.center : this._lastCenter + if ( + zoom && + (animationEnded || + Math.abs(maybeZoom - zoom) > 0.001 || + Math.abs(maybeCenter[0] - limitedCenter[0]) > 0.00001 || + Math.abs(maybeCenter[1] - limitedCenter[1]) > 0.00001) + ) { + this._lastZoom = zoom + this._lastCenter = [...limitedCenter] + this.syncToProps(limitedCenter, zoom) + } + } + + getBoundsMinMax = (zoom: number): MinMaxBounds => { + if (this.props.limitBounds === 'center') { + return absoluteMinMax + } + + const { width, height } = this.state + + if ( + this._minMaxCache && + this._minMaxCache[0] === zoom && + this._minMaxCache[1] === width && + this._minMaxCache[2] === height + ) { + return this._minMaxCache[3] + } + + const pixelsAtZoom = Math.pow(2, zoom) * 256 + + const minLng = width > pixelsAtZoom ? 0 : tile2lng(width / 512, zoom) // x + const minLat = height > pixelsAtZoom ? 0 : tile2lat(Math.pow(2, zoom) - height / 512, zoom) // y + + const maxLng = width > pixelsAtZoom ? 0 : tile2lng(Math.pow(2, zoom) - width / 512, zoom) // x + const maxLat = height > pixelsAtZoom ? 0 : tile2lat(height / 512, zoom) // y + + const minMax = [minLat, maxLat, minLng, maxLng] as MinMaxBounds + + this._minMaxCache = [zoom, width, height, minMax] + + return minMax + } + + tileLoaded = (key: string): void => { + if (this._loadTracker && key in this._loadTracker) { + this._loadTracker[key] = true + + const unloadedCount = Object.values(this._loadTracker).filter((v) => !v).length + + if (unloadedCount === 0) { + this.setState({ oldTiles: [] }, NOOP) + } + } + } + + coordsInside(pixel: Point): boolean { + const { width, height } = this.state + + if (pixel[0] < 0 || pixel[1] < 0 || pixel[0] >= width || pixel[1] >= height) { + return false + } + + const parent = this._containerRef + if (parent) { + const pos = parentPosition(parent) + const element = document.elementFromPoint(pixel[0] + pos.x, pixel[1] + pos.y) + + return parent === element || parent.contains(element) + } else { + return false + } + } + + handleTouchStart = (event: TouchEvent): void => { + if (!this._containerRef) { + return + } + if (event.target && parentHasClass(event.target as HTMLElement, 'pigeon-drag-block')) { + return + } + if (event.touches.length === 1) { + const touch = event.touches[0] + const pixel = getMousePixel(this._containerRef, touch) + + if (this.coordsInside(pixel)) { + this._touchStartPixel = [pixel] + + if (!this.props.twoFingerDrag) { + this.stopAnimating() + + if (this._lastTap && performanceNow() - this._lastTap < DOUBLE_CLICK_DELAY) { + event.preventDefault() + const latLngNow = this.pixelToLatLng(this._touchStartPixel[0]) + this.setCenterZoomTarget( + null, + Math.max(this.props.minZoom, Math.min(this.state.zoom + 1, this.props.maxZoom)), + false, + latLngNow + ) + } else { + this._lastTap = performanceNow() + this.trackMoveEvents(pixel) + } + } + } + // added second finger and first one was in the area + } else if (event.touches.length === 2 && this._touchStartPixel) { + event.preventDefault() + + this.stopTrackingMoveEvents() + + if (this.state.pixelDelta || this.state.zoomDelta) { + this.sendDeltaChange() + } + + const t1 = getMousePixel(this._containerRef, event.touches[0]) + const t2 = getMousePixel(this._containerRef, event.touches[1]) + + this._touchStartPixel = [t1, t2] + this._touchStartMidPoint = [(t1[0] + t2[0]) / 2, (t1[1] + t2[1]) / 2] + this._touchStartDistance = Math.sqrt(Math.pow(t1[0] - t2[0], 2) + Math.pow(t1[1] - t2[1], 2)) + } + } + + handleTouchMove = (event: TouchEvent): void => { + if (!this._containerRef) { + this._touchStartPixel = null + return + } + if (event.touches.length === 1 && this._touchStartPixel) { + const touch = event.touches[0] + const pixel = getMousePixel(this._containerRef, touch) + + if (this.props.twoFingerDrag) { + if (this.coordsInside(pixel)) { + this.showWarning('fingers') + } + } else { + event.preventDefault() + this.trackMoveEvents(pixel) + + this.setState( + { + pixelDelta: [pixel[0] - this._touchStartPixel[0][0], pixel[1] - this._touchStartPixel[0][1]], + }, + NOOP + ) + } + } else if ( + event.touches.length === 2 && + this._touchStartPixel && + this._touchStartMidPoint && + this._touchStartDistance + ) { + const { width, height, zoom } = this.state + + event.preventDefault() + + const t1 = getMousePixel(this._containerRef, event.touches[0]) + const t2 = getMousePixel(this._containerRef, event.touches[1]) + + const midPoint = [(t1[0] + t2[0]) / 2, (t1[1] + t2[1]) / 2] + const midPointDiff = [midPoint[0] - this._touchStartMidPoint[0], midPoint[1] - this._touchStartMidPoint[1]] + + const distance = Math.sqrt(Math.pow(t1[0] - t2[0], 2) + Math.pow(t1[1] - t2[1], 2)) + + const zoomDelta = + Math.max( + this.props.minZoom, + Math.min(this.props.maxZoom, zoom + Math.log2(distance / this._touchStartDistance)) + ) - zoom + const scale = Math.pow(2, zoomDelta) + + const centerDiffDiff = [(width / 2 - midPoint[0]) * (scale - 1), (height / 2 - midPoint[1]) * (scale - 1)] + + this.setState( + { + zoomDelta: zoomDelta, + pixelDelta: [centerDiffDiff[0] + midPointDiff[0] * scale, centerDiffDiff[1] + midPointDiff[1] * scale], + }, + NOOP + ) + } + } + + handleTouchEnd = (event: TouchEvent): void => { + if (!this._containerRef) { + this._touchStartPixel = null + return + } + if (this._touchStartPixel) { + const { zoomSnap, twoFingerDrag, minZoom, maxZoom } = this.props + const { zoomDelta } = this.state + const { center, zoom } = this.sendDeltaChange() + + if (event.touches.length === 0) { + if (twoFingerDrag) { + this.clearWarning() + } else { + // if the click started and ended at about + // the same place we can view it as a click + // and not prevent default behavior. + const oldTouchPixel = this._touchStartPixel[0] + const newTouchPixel = getMousePixel(this._containerRef, event.changedTouches[0]) + + if ( + Math.abs(oldTouchPixel[0] - newTouchPixel[0]) > CLICK_TOLERANCE || + Math.abs(oldTouchPixel[1] - newTouchPixel[1]) > CLICK_TOLERANCE + ) { + // don't throw immediately after releasing the second finger + if (!this._secondTouchEnd || performanceNow() - this._secondTouchEnd > PINCH_RELEASE_THROW_DELAY) { + event.preventDefault() + this.throwAfterMoving(newTouchPixel, center, zoom) + } + } + + this._touchStartPixel = null + this._secondTouchEnd = null + } + } else if (event.touches.length === 1) { + event.preventDefault() + const touch = getMousePixel(this._containerRef, event.touches[0]) + + this._secondTouchEnd = performanceNow() + this._touchStartPixel = [touch] + this.trackMoveEvents(touch) + + if (zoomSnap) { + // if somehow we have no midpoint for the two finger touch, just take the center of the map + const latLng = this._touchStartMidPoint ? this.pixelToLatLng(this._touchStartMidPoint) : this.state.center + + let zoomTarget + + // do not zoom up/down if we must drag with 2 fingers and didn't change the zoom level + if (twoFingerDrag && Math.round(this.state.zoom) === Math.round(this.state.zoom + zoomDelta)) { + zoomTarget = Math.round(this.state.zoom) + } else { + zoomTarget = zoomDelta > 0 ? Math.ceil(this.state.zoom) : Math.floor(this.state.zoom) + } + const zoom = Math.max(minZoom, Math.min(zoomTarget, maxZoom)) + + this.setCenterZoomTarget(latLng, zoom, false, latLng) + } + } + } + } + + handleMouseDown = (event: MouseEvent): void => { + if (!this._containerRef) { + return + } + const pixel = getMousePixel(this._containerRef, event) + + if ( + event.button === 0 && + (!event.target || !parentHasClass(event.target as HTMLElement, 'pigeon-drag-block')) && + this.coordsInside(pixel) + ) { + this.stopAnimating() + event.preventDefault() + + if (this._lastClick && performanceNow() - this._lastClick < DOUBLE_CLICK_DELAY) { + if (!parentHasClass(event.target as HTMLElement, 'pigeon-click-block')) { + const latLngNow = this.pixelToLatLng(this._mousePosition || pixel) + this.setCenterZoomTarget( + null, + Math.max(this.props.minZoom, Math.min(this.state.zoom + 1, this.props.maxZoom)), + false, + latLngNow + ) + } + } else { + this._lastClick = performanceNow() + + this._mouseDown = true + this._dragStart = pixel + this.trackMoveEvents(pixel) + } + } + } + + handleMouseMove = (event: MouseEvent): void => { + if (!this._containerRef) { + return + } + this._mousePosition = getMousePixel(this._containerRef, event) + + if (this._mouseDown && this._dragStart) { + this.trackMoveEvents(this._mousePosition) + this.setState( + { + pixelDelta: [this._mousePosition[0] - this._dragStart[0], this._mousePosition[1] - this._dragStart[1]], + }, + NOOP + ) + } + } + + handleMouseUp = (event: MouseEvent): void => { + if (!this._containerRef) { + this._mouseDown = false + return + } + const { pixelDelta } = this.state + + if (this._mouseDown) { + this._mouseDown = false + + const pixel = getMousePixel(this._containerRef, event) + + if ( + this.props.onClick && + (!event.target || !parentHasClass(event.target as HTMLElement, 'pigeon-click-block')) && + (!pixelDelta || Math.abs(pixelDelta[0]) + Math.abs(pixelDelta[1]) <= CLICK_TOLERANCE) + ) { + const latLng = this.pixelToLatLng(pixel) + this.props.onClick({ event, latLng, pixel }) + this.setState({ pixelDelta: undefined }, NOOP) + } else { + const { center, zoom } = this.sendDeltaChange() + + this.throwAfterMoving(pixel, center, zoom) + } + } + } + + // https://www.bennadel.com/blog/1856-using-jquery-s-animate-step-callback-function-to-create-custom-animations.htm + stopTrackingMoveEvents = (): void => { + this._moveEvents = [] + } + + trackMoveEvents = (coords: Point): void => { + const timestamp = performanceNow() + + if (this._moveEvents.length === 0 || timestamp - this._moveEvents[this._moveEvents.length - 1].timestamp > 40) { + this._moveEvents.push({ timestamp, coords }) + if (this._moveEvents.length > 2) { + this._moveEvents.shift() + } + } + } + + throwAfterMoving = (coords: Point, center: Point, zoom: number): void => { + const { width, height } = this.state + const { animate } = this.props + + const timestamp = performanceNow() + const lastEvent = this._moveEvents.shift() + + if (lastEvent && animate) { + const deltaMs = Math.max(timestamp - lastEvent.timestamp, 1) + + const delta = [ + ((coords[0] - lastEvent.coords[0]) / deltaMs) * 120, + ((coords[1] - lastEvent.coords[1]) / deltaMs) * 120, + ] + + const distance = Math.sqrt(delta[0] * delta[0] + delta[1] * delta[1]) + + if (distance > MIN_DRAG_FOR_THROW) { + const diagonal = Math.sqrt(width * width + height * height) + + const throwTime = (DIAGONAL_THROW_TIME * distance) / diagonal + + const lng = tile2lng(lng2tile(center[1], zoom) - delta[0] / 256.0, zoom) + const lat = tile2lat(lat2tile(center[0], zoom) - delta[1] / 256.0, zoom) + + this.setCenterZoomTarget([lat, lng], zoom, false, null, throwTime) + } + } + + this.stopTrackingMoveEvents() + } + + sendDeltaChange = () => { + const { center, zoom, pixelDelta, zoomDelta } = this.state + + let lat = center[0] + let lng = center[1] + + if (pixelDelta || zoomDelta !== 0) { + lng = tile2lng(lng2tile(center[1], zoom + zoomDelta) - (pixelDelta ? pixelDelta[0] / 256.0 : 0), zoom + zoomDelta) + lat = tile2lat(lat2tile(center[0], zoom + zoomDelta) - (pixelDelta ? pixelDelta[1] / 256.0 : 0), zoom + zoomDelta) + this.setCenterZoom([lat, lng], zoom + zoomDelta) + } + + this.setState( + { + pixelDelta: undefined, + zoomDelta: 0, + }, + NOOP + ) + + return { + center: this.limitCenterAtZoom([lat, lng], zoom + zoomDelta), + zoom: zoom + zoomDelta, + } + } + + getBounds = (center = this.state.center, zoom = this.zoomPlusDelta()): Bounds => { + const { width, height } = this.state + + return { + ne: this.pixelToLatLng([width - 1, 0], center, zoom), + sw: this.pixelToLatLng([0, height - 1], center, zoom), + } + } + + syncToProps = (center = this.state.center, zoom = this.state.zoom): void => { + const { onBoundsChanged } = this.props + + if (onBoundsChanged) { + const bounds = this.getBounds(center, zoom) + + onBoundsChanged({ center, zoom, bounds, initial: !this._boundsSynced }) + + this._boundsSynced = true + } + } + + handleWheel = (event: WheelEvent): void => { + const { mouseEvents, metaWheelZoom, zoomSnap, animate } = this.props + + if (!mouseEvents) { + return + } + + if (!metaWheelZoom || event.metaKey || event.ctrlKey) { + event.preventDefault() + + const addToZoom = -event.deltaY / SCROLL_PIXELS_FOR_ZOOM_LEVEL + + if (!zoomSnap && this._zoomTarget) { + const stillToAdd = this._zoomTarget - this.state.zoom + this.zoomAroundMouse(addToZoom + stillToAdd, event) + } else { + if (animate) { + this.zoomAroundMouse(addToZoom, event) + } else { + if (!this._lastWheel || performanceNow() - this._lastWheel > ANIMATION_TIME) { + this._lastWheel = performanceNow() + this.zoomAroundMouse(addToZoom, event) + } + } + } + } else { + this.showWarning('wheel') + } + } + + showWarning = (warningType: WarningType): void => { + if (!this.state.showWarning || this.state.warningType !== warningType) { + this.setState({ showWarning: true, warningType }) + } + + if (this._warningClearTimeout) { + window.clearTimeout(this._warningClearTimeout) + } + this._warningClearTimeout = window.setTimeout(this.clearWarning, WARNING_DISPLAY_TIMEOUT) + } + + clearWarning = (): void => { + if (this.state.showWarning) { + this.setState({ showWarning: false }) + } + } + + zoomAroundMouse = (zoomDiff: number, event: MouseEvent): void => { + if (!this._containerRef) { + return + } + const { zoom } = this.state + const { minZoom, maxZoom, zoomSnap } = this.props + + this._mousePosition = getMousePixel(this._containerRef, event) + + if (!this._mousePosition || (zoom === minZoom && zoomDiff < 0) || (zoom === maxZoom && zoomDiff > 0)) { + return + } + + const latLngNow = this.pixelToLatLng(this._mousePosition) + + let zoomTarget = zoom + zoomDiff + if (zoomSnap) { + zoomTarget = zoomDiff < 0 ? Math.floor(zoomTarget) : Math.ceil(zoomTarget) + } + zoomTarget = Math.max(minZoom, Math.min(zoomTarget, maxZoom)) + + this.setCenterZoomTarget(null, zoomTarget, false, latLngNow) + } + + // tools + + zoomPlusDelta = (): number => { + return this.state.zoom + this.state.zoomDelta + } + + pixelToLatLng = (pixel: Point, center = this.state.center, zoom = this.zoomPlusDelta()): Point => { + const { width, height, pixelDelta } = this.state + + const pointDiff = [ + (pixel[0] - width / 2 - (pixelDelta ? pixelDelta[0] : 0)) / 256.0, + (pixel[1] - height / 2 - (pixelDelta ? pixelDelta[1] : 0)) / 256.0, + ] + + const tileX = lng2tile(center[1], zoom) + pointDiff[0] + const tileY = lat2tile(center[0], zoom) + pointDiff[1] + + return [ + Math.max(absoluteMinMax[0], Math.min(absoluteMinMax[1], tile2lat(tileY, zoom))), + Math.max(absoluteMinMax[2], Math.min(absoluteMinMax[3], tile2lng(tileX, zoom))), + ] as Point + } + + latLngToPixel = (latLng: Point, center = this.state.center, zoom = this.zoomPlusDelta()): Point => { + const { width, height, pixelDelta } = this.state + + const tileCenterX = lng2tile(center[1], zoom) + const tileCenterY = lat2tile(center[0], zoom) + + const tileX = lng2tile(latLng[1], zoom) + const tileY = lat2tile(latLng[0], zoom) + + return [ + (tileX - tileCenterX) * 256.0 + width / 2 + (pixelDelta ? pixelDelta[0] : 0), + (tileY - tileCenterY) * 256.0 + height / 2 + (pixelDelta ? pixelDelta[1] : 0), + ] as Point + } + + calculateZoomCenter = (center: Point, coords: Point, oldZoom: number, newZoom: number): Point => { + const { width, height } = this.state + + const pixelBefore = this.latLngToPixel(coords, center, oldZoom) + const pixelAfter = this.latLngToPixel(coords, center, newZoom) + + const newCenter = this.pixelToLatLng( + [width / 2 + pixelAfter[0] - pixelBefore[0], height / 2 + pixelAfter[1] - pixelBefore[1]], + center, + newZoom + ) + + return this.limitCenterAtZoom(newCenter, newZoom) + } + + // ref + + setRef = (dom: HTMLDivElement) => { + this._containerRef = dom + } + + // data to display the tiles + + tileValues({ + center, + zoom, + pixelDelta, + zoomDelta, + width, + height, + }: { + center: Point + zoom: number + pixelDelta?: Point + zoomDelta?: number + width: number + height: number + }): TileValues { + const roundedZoom = Math.round(zoom + (zoomDelta || 0)) + const zoomDiff = zoom + (zoomDelta || 0) - roundedZoom + + const scale = Math.pow(2, zoomDiff) + const scaleWidth = width / scale + const scaleHeight = height / scale + + const tileCenterX = lng2tile(center[1], roundedZoom) - (pixelDelta ? pixelDelta[0] / 256.0 / scale : 0) + const tileCenterY = lat2tile(center[0], roundedZoom) - (pixelDelta ? pixelDelta[1] / 256.0 / scale : 0) + + const halfWidth = scaleWidth / 2 / 256.0 + const halfHeight = scaleHeight / 2 / 256.0 + + const tileMinX = Math.floor(tileCenterX - halfWidth) + const tileMaxX = Math.floor(tileCenterX + halfWidth) + + const tileMinY = Math.floor(tileCenterY - halfHeight) + const tileMaxY = Math.floor(tileCenterY + halfHeight) + + return { + tileMinX, + tileMaxX, + tileMinY, + tileMaxY, + tileCenterX, + tileCenterY, + roundedZoom, + zoomDelta: zoomDelta || 0, + scaleWidth, + scaleHeight, + scale, + } + } + + // display the tiles + + renderTiles(): JSX.Element { + const { oldTiles, width, height } = this.state + const { dprs } = this.props + const mapUrl = this.props.provider || osm + + const { + tileMinX, + tileMaxX, + tileMinY, + tileMaxY, + tileCenterX, + tileCenterY, + roundedZoom, + scaleWidth, + scaleHeight, + scale, + } = this.tileValues(this.state) + + const tiles: Tile[] = [] + + for (let i = 0; i < oldTiles.length; i++) { + const old = oldTiles[i] + const zoomDiff = old.roundedZoom - roundedZoom + + if (Math.abs(zoomDiff) > 4 || zoomDiff === 0) { + continue + } + + const pow = 1 / Math.pow(2, zoomDiff) + const xDiff = -(tileMinX - old.tileMinX * pow) * 256 + const yDiff = -(tileMinY - old.tileMinY * pow) * 256 + + const xMin = Math.max(old.tileMinX, 0) + const yMin = Math.max(old.tileMinY, 0) + const xMax = Math.min(old.tileMaxX, Math.pow(2, old.roundedZoom) - 1) + const yMax = Math.min(old.tileMaxY, Math.pow(2, old.roundedZoom) - 1) + + for (let x = xMin; x <= xMax; x++) { + for (let y = yMin; y <= yMax; y++) { + tiles.push({ + key: `${x}-${y}-${old.roundedZoom}`, + url: mapUrl(x, y, old.roundedZoom), + srcSet: srcSet(dprs, mapUrl, x, y, old.roundedZoom), + left: xDiff + (x - old.tileMinX) * 256 * pow, + top: yDiff + (y - old.tileMinY) * 256 * pow, + width: 256 * pow, + height: 256 * pow, + active: false, + }) + } + } + } + + const xMin = Math.max(tileMinX, 0) + const yMin = Math.max(tileMinY, 0) + const xMax = Math.min(tileMaxX, Math.pow(2, roundedZoom) - 1) + const yMax = Math.min(tileMaxY, Math.pow(2, roundedZoom) - 1) + + for (let x = xMin; x <= xMax; x++) { + for (let y = yMin; y <= yMax; y++) { + tiles.push({ + key: `${x}-${y}-${roundedZoom}`, + url: mapUrl(x, y, roundedZoom), + srcSet: srcSet(dprs, mapUrl, x, y, roundedZoom), + left: (x - tileMinX) * 256, + top: (y - tileMinY) * 256, + width: 256, + height: 256, + active: true, + }) + } + } + + const boxStyle: React.CSSProperties = { + width: scaleWidth, + height: scaleHeight, + position: 'absolute', + top: `calc((100% - ${height}px) / 2)`, + left: `calc((100% - ${width}px) / 2)`, + overflow: 'hidden', + willChange: 'transform', + transform: `scale(${scale}, ${scale})`, + transformOrigin: 'top left', + } + const boxClassname = this.props.boxClassname || 'pigeon-tiles-box' + + const left = -((tileCenterX - tileMinX) * 256 - scaleWidth / 2) + const top = -((tileCenterY - tileMinY) * 256 - scaleHeight / 2) + + const tilesStyle: React.CSSProperties = { + position: 'absolute', + width: (tileMaxX - tileMinX + 1) * 256, + height: (tileMaxY - tileMinY + 1) * 256, + willChange: 'transform', + transform: `translate(${left}px, ${top}px)`, + } + + const Tile = this.props.tileComponent + + return ( + <div style={boxStyle} className={boxClassname}> + <div className="pigeon-tiles" style={tilesStyle}> + {tiles.map((tile) => ( + <Tile key={tile.key} tile={tile} tileLoaded={() => this.tileLoaded(tile.key)} /> + ))} + </div> + </div> + ) + } + + renderOverlays(): JSX.Element { + const { width, height, center } = this.state + + const mapState = { + bounds: this.getBounds(), + zoom: this.zoomPlusDelta(), + center: center, + width, + height, + } + + const childrenWithProps = React.Children.map(this.props.children, (child) => { + if (!child) { + return null + } + + if (!React.isValidElement(child)) { + return child + } + + const { anchor, position, offset } = child.props + + const c = this.latLngToPixel(anchor || position || center) + + return React.cloneElement(child, { + left: c[0] - (offset ? offset[0] : 0), + top: c[1] - (offset ? offset[1] : 0), + latLngToPixel: this.latLngToPixel, + pixelToLatLng: this.pixelToLatLng, + setCenterZoom: this.setCenterZoomForChildren, + mapProps: this.props, + mapState, + }) + }) + + const childrenStyle: React.CSSProperties = { + position: 'absolute', + width: width, + height: height, + top: `calc((100% - ${height}px) / 2)`, + left: `calc((100% - ${width}px) / 2)`, + } + + return ( + <div className="pigeon-overlays" style={childrenStyle}> + {childrenWithProps} + </div> + ) + } + + renderAttribution(): JSX.Element | null { + const { attribution, attributionPrefix } = this.props + + if (attribution === false) { + return null + } + + const style: React.CSSProperties = { + position: 'absolute', + bottom: 0, + right: 0, + fontSize: '11px', + padding: '2px 5px', + background: 'rgba(255, 255, 255, 0.7)', + fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif", + color: '#333', + } + + const linkStyle: React.CSSProperties = { + color: '#0078A8', + textDecoration: 'none', + } + + return ( + <div key="attr" className="pigeon-attribution" style={style}> + {attributionPrefix === false ? null : ( + <span> + {attributionPrefix || ( + <a href="https://pigeon-maps.js.org/" style={linkStyle} target="_blank" rel="noreferrer noopener"> + Pigeon + </a> + )} + {' | '} + </span> + )} + {attribution || ( + <span> + {' © '} + <a + href="https://www.openstreetmap.org/copyright" + style={linkStyle} + target="_blank" + rel="noreferrer noopener" + > + OpenStreetMap + </a> + {' contributors'} + </span> + )} + </div> + ) + } + + renderWarning(): JSX.Element | null { + const { metaWheelZoom, metaWheelZoomWarning, twoFingerDrag, twoFingerDragWarning, warningZIndex } = this.props + const { showWarning, warningType, width, height } = this.state + + if ((metaWheelZoom && metaWheelZoomWarning) || (twoFingerDrag && twoFingerDragWarning)) { + const style: React.CSSProperties = { + position: 'absolute', + top: 0, + left: 0, + width: width, + height: height, + overflow: 'hidden', + pointerEvents: 'none', + opacity: showWarning ? 100 : 0, + transition: 'opacity 300ms', + background: 'rgba(0,0,0,0.5)', + color: '#fff', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + fontSize: 22, + fontFamily: '"Arial", sans-serif', + textAlign: 'center', + zIndex: warningZIndex, + } + + const meta = + typeof window !== 'undefined' && window.navigator && window.navigator.platform.toUpperCase().indexOf('MAC') >= 0 + ? '⌘' + : 'ctrl' + + const warningText = warningType === 'fingers' ? twoFingerDragWarning : metaWheelZoomWarning + + return ( + <div className="pigeon-overlay-warning" style={style}> + {warningText.replace('META', meta)} + </div> + ) + } else { + return null + } + } + + render(): JSX.Element { + const { touchEvents, twoFingerDrag } = this.props + const { width, height } = this.state + + const containerStyle: React.CSSProperties = { + width: this.props.width ? width : '100%', + height: this.props.height ? height : '100%', + position: 'relative', + display: 'inline-block', + overflow: 'hidden', + background: '#dddddd', + touchAction: touchEvents ? (twoFingerDrag ? 'pan-x pan-y' : 'none') : 'auto', + } + + const hasSize = !!(width && height) + + return ( + <div style={containerStyle} ref={this.setRef} dir="ltr"> + {hasSize && this.renderTiles()} + {hasSize && this.renderOverlays()} + {hasSize && this.renderAttribution()} + {hasSize && this.renderWarning()} + </div> + ) + } +} diff --git a/node_modules/pigeon-maps/src/overlays/Draggable.tsx b/node_modules/pigeon-maps/src/overlays/Draggable.tsx new file mode 100644 index 0000000..4cac3c6 --- /dev/null +++ b/node_modules/pigeon-maps/src/overlays/Draggable.tsx @@ -0,0 +1,181 @@ +import React, { useEffect, useRef, useState } from 'react' +import { PigeonProps, Point } from '../types' + +function isDescendentOf(element, ancestor) { + while (element) { + if (element === ancestor) { + return true + } + element = element.parentElement + } + + return false +} + +interface DraggableProps extends PigeonProps { + className?: string + style?: React.CSSProperties + + children?: React.ReactNode + + onDragStart?: (anchor: Point) => void + onDragMove?: (anchor: Point) => void + onDragEnd?: (anchor: Point) => void +} + +interface DraggableState { + isDragging: boolean + startX?: number + startY?: number + startLeft?: number + startTop?: number + deltaX: number + deltaY: number +} + +const defaultState: DraggableState = { + isDragging: false, + startX: undefined, + startY: undefined, + startLeft: undefined, + startTop: undefined, + deltaX: 0, + deltaY: 0, +} + +export function Draggable(props: DraggableProps): JSX.Element { + const dragRef = useRef<HTMLDivElement>() + const propsRef = useRef<DraggableProps>(props) + const stateRef = useRef({ ...defaultState }) + const [_state, _setState] = useState(defaultState) + + propsRef.current = props + + const setState = (stateUpdate: Partial<DraggableState>): void => { + const newState = { ...stateRef.current, ...stateUpdate } + stateRef.current = newState + _setState(newState) + } + + const { mouseEvents, touchEvents } = props.mapProps + + useEffect(() => { + const handleDragStart = (event: MouseEvent | TouchEvent) => { + if (isDescendentOf(event.target, dragRef.current)) { + event.preventDefault() + + setState({ + isDragging: true, + startX: ('touches' in event ? event.touches[0] : event).clientX, + startY: ('touches' in event ? event.touches[0] : event).clientY, + startLeft: propsRef.current.left, + startTop: propsRef.current.top, + deltaX: 0, + deltaY: 0, + }) + + if (propsRef.current.onDragStart) { + const { left, top, offset, pixelToLatLng } = propsRef.current + propsRef.current.onDragMove(pixelToLatLng([left + (offset ? offset[0] : 0), top + (offset ? offset[1] : 0)])) + } + } + } + + const handleDragMove = (event: MouseEvent | TouchEvent) => { + if (!stateRef.current.isDragging) { + return + } + + event.preventDefault() + + const x = ('touches' in event ? event.touches[0] : event).clientX + const y = ('touches' in event ? event.touches[0] : event).clientY + + const deltaX = x - stateRef.current.startX + const deltaY = y - stateRef.current.startY + + setState({ deltaX, deltaY }) + + if (propsRef.current.onDragMove) { + const { offset, pixelToLatLng } = propsRef.current + const { startLeft, startTop } = stateRef.current + + propsRef.current.onDragMove( + pixelToLatLng([startLeft + deltaX + (offset ? offset[0] : 0), startTop + deltaY + (offset ? offset[1] : 0)]) + ) + } + } + + const handleDragEnd = (event: MouseEvent | TouchEvent) => { + if (!stateRef.current.isDragging) { + return + } + + event.preventDefault() + + const { offset, pixelToLatLng } = propsRef.current + const { deltaX, deltaY, startLeft, startTop } = stateRef.current + + propsRef.current.onDragEnd?.( + pixelToLatLng([startLeft + deltaX + (offset ? offset[0] : 0), startTop + deltaY + (offset ? offset[1] : 0)]) + ) + + setState({ + isDragging: false, + startX: undefined, + startY: undefined, + startLeft: undefined, + startTop: undefined, + deltaX: 0, + deltaY: 0, + }) + } + + const wa = (e: string, t: EventListener, o?: AddEventListenerOptions) => window.addEventListener(e, t, o) + const wr = (e: string, t: EventListener) => window.removeEventListener(e, t) + + if (mouseEvents) { + wa('mousedown', handleDragStart) + wa('mousemove', handleDragMove) + wa('mouseup', handleDragEnd) + } + + if (touchEvents) { + wa('touchstart', handleDragStart, { passive: false }) + wa('touchmove', handleDragMove, { passive: false }) + wa('touchend', handleDragEnd, { passive: false }) + } + + return () => { + if (mouseEvents) { + wr('mousedown', handleDragStart) + wr('mousemove', handleDragMove) + wr('mouseup', handleDragEnd) + } + + if (touchEvents) { + wr('touchstart', handleDragStart) + wr('touchmove', handleDragMove) + wr('touchend', handleDragEnd) + } + } + }, [mouseEvents, touchEvents]) + + const { left, top, className, style } = props + const { deltaX, deltaY, startLeft, startTop, isDragging } = _state + + return ( + <div + style={{ + cursor: isDragging ? 'grabbing' : 'grab', + ...(style || {}), + position: 'absolute', + transform: `translate(${isDragging ? startLeft + deltaX : left}px, ${isDragging ? startTop + deltaY : top}px)`, + }} + ref={dragRef} + className={`pigeon-drag-block${className ? ` ${className}` : ''}`} + > + {props.children} + </div> + ) +} diff --git a/node_modules/pigeon-maps/src/overlays/GeoJson.tsx b/node_modules/pigeon-maps/src/overlays/GeoJson.tsx new file mode 100644 index 0000000..d25cfe6 --- /dev/null +++ b/node_modules/pigeon-maps/src/overlays/GeoJson.tsx @@ -0,0 +1,244 @@ +import React, { CSSProperties, SVGProps, useMemo, useEffect, useState } from 'react' +import { PigeonProps, Point } from '../types' + +interface GeoJsonProps extends PigeonProps { + className?: string + data?: any + svgAttributes?: any + styleCallback?: any + hover?: any + feature?: any + style?: CSSProperties + children?: React.ReactNode + + // callbacks + onClick?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void + onContextMenu?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void + onMouseOver?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void + onMouseOut?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void +} + +interface GeoJsonLoaderProps extends GeoJsonProps { + link?: string +} + +interface GeoJsonGeometry { + type: string + coordinates?: + | [number, number] + | Array<[number, number]> + | Array<Array<[number, number]>> + | Array<Array<Array<[number, number]>>> + geometries?: Array<GeoJsonGeometry> +} + +interface GeometryProps { + coordinates?: + | [number, number] + | Array<[number, number]> + | Array<Array<[number, number]>> + | Array<Array<Array<[number, number]>>> + latLngToPixel?: (latLng: Point, center?: Point, zoom?: number) => Point + svgAttributes?: SVGProps<SVGElement> + geometry?: GeoJsonGeometry +} + +const defaultSvgAttributes = { fill: '#93c0d099', strokeWidth: '2', stroke: 'white', r: '30' } + +export function PointComponent(props: GeometryProps): JSX.Element { + const { latLngToPixel } = props + const [y, x] = props.coordinates as [number, number] + const [cx, cy] = latLngToPixel([x, y]) + if (props.svgAttributes?.path) { + const path = `M${cx},${cy}c${props.svgAttributes.path.split(/[c|C|L|l|v|V|h|H](.*)/s)[1]}` + return <path d={path} {...(props.svgAttributes as SVGProps<SVGCircleElement>)} /> + } + return <circle cx={cx} cy={cy} {...(props.svgAttributes as SVGProps<SVGCircleElement>)} /> +} + +export function MultiPoint(props: GeometryProps): JSX.Element { + return ( + <> + {props.coordinates.map((point, i) => ( + <PointComponent {...props} coordinates={point} key={i} /> + ))} + </> + ) +} + +export function LineString(props: GeometryProps): JSX.Element { + const { latLngToPixel } = props + const p = + 'M' + + (props.coordinates as Array<[number, number]>).reduce((a, [y, x]) => { + const [v, w] = latLngToPixel([x, y]) + return a + ' ' + v + ' ' + w + }, '') + + return <path d={p} {...(props.svgAttributes as SVGProps<SVGPathElement>)} /> +} + +export function MultiLineString(props: GeometryProps): JSX.Element { + return ( + <> + {props.coordinates.map((line, i) => ( + <LineString {...props} coordinates={line} key={i} /> + ))} + </> + ) +} + +export function Polygon(props: GeometryProps): JSX.Element { + const { latLngToPixel } = props + // GeoJson polygons is a collection of linear rings + const p = (props.coordinates as Array<Array<[number, number]>>).reduce( + (a, part) => + a + + ' M' + + part.reduce((a, [y, x]) => { + const [v, w] = latLngToPixel([x, y]) + return a + ' ' + v + ' ' + w + }, '') + + 'Z', + '' + ) + return <path d={p} {...(props.svgAttributes as SVGProps<SVGPathElement>)} /> +} + +export function MultiPolygon(props: GeometryProps): JSX.Element { + return ( + <> + {props.coordinates.map((polygon, i) => ( + <Polygon {...props} coordinates={polygon} key={i} /> + ))} + </> + ) +} + +export function GeometryCollection(props: GeometryProps): JSX.Element { + const renderer = { + Point: PointComponent, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon, + } + + const { type, coordinates, geometries } = props.geometry + + if (type === 'GeometryCollection') { + return ( + <> + {geometries.map((geometry, i) => ( + <GeometryCollection key={i} {...props} geometry={geometry} /> + ))} + </> + ) + } + + const Component = renderer[type] + + if (Component === undefined) { + console.warn(`The GeoJson Type ${type} is not known`) + return null + } + return ( + <Component + latLngToPixel={props.latLngToPixel} + geometry={props.geometry} + coordinates={coordinates} + svgAttributes={props.svgAttributes} + /> + ) +} + +export function GeoJsonFeature(props: GeoJsonProps): JSX.Element { + const [internalHover, setInternalHover] = useState(props.hover || false) + const hover = props.hover !== undefined ? props.hover : internalHover + const callbackSvgAttributes = props.styleCallback && props.styleCallback(props.feature, hover) + const svgAttributes = callbackSvgAttributes + ? props.svgAttributes + ? { ...props.svgAttributes, ...callbackSvgAttributes } + : callbackSvgAttributes + : props.svgAttributes + ? props.svgAttributes + : defaultSvgAttributes + + const eventParameters = (event: React.MouseEvent<SVGElement>) => ({ + event, + anchor: props.anchor, + payload: props.feature, + }) + + return ( + <g + clipRule="evenodd" + style={{ pointerEvents: 'auto' }} + onClick={props.onClick ? (event) => props.onClick(eventParameters(event)) : null} + onContextMenu={props.onContextMenu ? (event) => props.onContextMenu(eventParameters(event)) : null} + onMouseOver={(event) => { + props.onMouseOver && props.onMouseOver(eventParameters(event)) + setInternalHover(true) + }} + onMouseOut={(event) => { + props.onMouseOut && props.onMouseOut(eventParameters(event)) + setInternalHover(false) + }} + > + <GeometryCollection {...props} {...props.feature} svgAttributes={svgAttributes} /> + </g> + ) +} + +export function GeoJson(props: GeoJsonProps): JSX.Element { + const { width, height } = props.mapState + + return ( + <div + style={{ + position: 'absolute', + left: '0', + top: '0', + pointerEvents: 'none', + cursor: 'pointer', + ...(props.style || {}), + }} + className={props.className ? `${props.className} pigeon-click-block` : 'pigeon-click-block'} + > + <svg + width={width} + height={height} + viewBox={`0 0 ${width} ${height}`} + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + {props.data && props.data.features.map((feature, i) => <GeoJsonFeature key={i} {...props} feature={feature} />)} + + {React.Children.map(props.children, (child) => { + if (!child) { + return null + } + + if (!React.isValidElement(child)) { + return child + } + + return React.cloneElement(child, props) + })} + </svg> + </div> + ) +} + +export function GeoJsonLoader(props: GeoJsonLoaderProps): JSX.Element { + const [data, setData] = useState(props.data ? props.data : null) + + useEffect(() => { + fetch(props.link) + .then((response) => response.json()) + .then((data) => setData(data)) + }, [props.link]) + + return data ? <GeoJson data={data} {...props} /> : null +} diff --git a/node_modules/pigeon-maps/src/overlays/Marker.tsx b/node_modules/pigeon-maps/src/overlays/Marker.tsx new file mode 100644 index 0000000..f1970c2 --- /dev/null +++ b/node_modules/pigeon-maps/src/overlays/Marker.tsx @@ -0,0 +1,86 @@ +import React, { useState } from 'react' +import { PigeonProps } from '../types' + +interface MarkerProps extends PigeonProps { + color?: string + payload?: any + + width?: number + height?: number + + // optional modifiers + hover?: boolean + style?: React.CSSProperties + className?: string + + children?: JSX.Element + + // callbacks + onClick?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void + onContextMenu?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void + onMouseOver?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void + onMouseOut?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void +} + +export function Marker(props: MarkerProps): JSX.Element { + const width = + typeof props.width !== 'undefined' + ? props.width + : typeof props.height !== 'undefined' + ? (props.height * 29) / 34 + : 29 + const height = + typeof props.height !== 'undefined' + ? props.height + : typeof props.width !== 'undefined' + ? (props.width * 34) / 29 + : 34 + const [internalHover, setInternalHover] = useState(props.hover || false) + const hover = typeof props.hover === 'undefined' ? internalHover : props.hover + const color = props.color || '#93C0D0' + + // what do you expect to get back with the event + const eventParameters = (event: React.MouseEvent) => ({ + event, + anchor: props.anchor, + payload: props.payload, + }) + + return ( + <div + style={{ + position: 'absolute', + transform: `translate(${props.left - width / 2}px, ${props.top - (height - 1)}px)`, + filter: hover ? 'drop-shadow(0 0 4px rgba(0, 0, 0, .3))' : '', + pointerEvents: 'none', + cursor: 'pointer', + ...(props.style || {}), + }} + className={props.className ? `${props.className} pigeon-click-block` : 'pigeon-click-block'} + onClick={props.onClick ? (event) => props.onClick(eventParameters(event)) : null} + onContextMenu={props.onContextMenu ? (event) => props.onContextMenu(eventParameters(event)) : null} + onMouseOver={(event) => { + props.onMouseOver && props.onMouseOver(eventParameters(event)) + setInternalHover(true) + }} + onMouseOut={(event) => { + props.onMouseOut && props.onMouseOut(eventParameters(event)) + setInternalHover(false) + }} + > + {props.children || ( + <svg width={width} height={height} viewBox="0 0 61 71" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g style={{ pointerEvents: 'auto' }}> + <path + d="M52 31.5C52 36.8395 49.18 42.314 45.0107 47.6094C40.8672 52.872 35.619 57.678 31.1763 61.6922C30.7916 62.0398 30.2084 62.0398 29.8237 61.6922C25.381 57.678 20.1328 52.872 15.9893 47.6094C11.82 42.314 9 36.8395 9 31.5C9 18.5709 18.6801 9 30.5 9C42.3199 9 52 18.5709 52 31.5Z" + fill={color} + stroke="white" + strokeWidth="4" + /> + <circle cx="30.5" cy="30.5" r="8.5" fill="white" opacity={hover ? 0.98 : 0.6} /> + </g> + </svg> + )} + </div> + ) +} diff --git a/node_modules/pigeon-maps/src/overlays/Overlay.tsx b/node_modules/pigeon-maps/src/overlays/Overlay.tsx new file mode 100644 index 0000000..0bc8ecd --- /dev/null +++ b/node_modules/pigeon-maps/src/overlays/Overlay.tsx @@ -0,0 +1,23 @@ +import React from 'react' +import { PigeonProps } from '../types' + +interface OverlayProps extends PigeonProps { + style?: React.CSSProperties + className?: string + children?: React.ReactNode +} + +export function Overlay(props: OverlayProps) { + return ( + <div + style={{ + position: 'absolute', + transform: `translate(${props.left}px, ${props.top}px)`, + ...(props.style || {}), + }} + className={props.className ? `${props.className} pigeon-click-block` : 'pigeon-click-block'} + > + {props.children} + </div> + ) +} diff --git a/node_modules/pigeon-maps/src/providers.ts b/node_modules/pigeon-maps/src/providers.ts new file mode 100644 index 0000000..5a11046 --- /dev/null +++ b/node_modules/pigeon-maps/src/providers.ts @@ -0,0 +1,24 @@ +export function osm(x: number, y: number, z: number): string { + const s = String.fromCharCode(97 + ((x + y + z) % 3)) + return `https://tile.openstreetmap.org/${z}/${x}/${y}.png` +} + +export function stamenToner(x: number, y: number, z: number, dpr = 1): string { + return `https://stamen-tiles.a.ssl.fastly.net/toner/${z}/${x}/${y}${dpr >= 2 ? '@2x' : ''}.png` +} + +export function stamenTerrain(x: number, y: number, z: number, dpr = 1): string { + return `https://stamen-tiles.a.ssl.fastly.net/terrain/${z}/${x}/${y}${dpr >= 2 ? '@2x' : ''}.jpg` +} + +export const maptiler = + (apiKey: string, map = 'streets') => + (x: number, y: number, z: number, dpr = 1): string => { + return `https://api.maptiler.com/maps/${map}/256/${z}/${x}/${y}${dpr >= 2 ? '@2x' : ''}.png?key=${apiKey}` + } + +export const stadiamaps = + (style = 'alidade_smooth') => + (x: number, y: number, z: number, dpr = 1): string => { + return `https://tiles.stadiamaps.com/styles/${style}/${z}/${x}/${y}${dpr >= 2 ? '@2x' : ''}.png` + } diff --git a/node_modules/pigeon-maps/src/types.ts b/node_modules/pigeon-maps/src/types.ts new file mode 100644 index 0000000..2bee710 --- /dev/null +++ b/node_modules/pigeon-maps/src/types.ts @@ -0,0 +1,150 @@ +import React from 'react' + +/** @description `[latitude, longitude]` */ +export type Point = [number, number] + +/** @description `{ ne: [latitude, longitude], sw: [latitude, longitude] }` */ +export interface Bounds { + ne: [number, number] + sw: [number, number] +} + +export interface MapProps { + center?: Point + defaultCenter?: Point + + zoom?: number + defaultZoom?: number + + width?: number + defaultWidth?: number + + height?: number + defaultHeight?: number + + provider?: (x: number, y: number, z: number, dpr?: number) => string + dprs?: number[] + children?: React.ReactNode + + animate?: boolean + animateMaxScreens?: number + + minZoom?: number + maxZoom?: number + + metaWheelZoom?: boolean + metaWheelZoomWarning?: string + twoFingerDrag?: boolean + twoFingerDragWarning?: string + warningZIndex?: number + + attribution?: JSX.Element | false + attributionPrefix?: JSX.Element | false + + zoomSnap?: boolean + mouseEvents?: boolean + touchEvents?: boolean + + onClick?: ({ event, latLng, pixel }: { event: MouseEvent; latLng: [number, number]; pixel: [number, number] }) => void + onBoundsChanged?: ({ + center, + zoom, + bounds, + initial, + }: { + center: [number, number] + bounds: Bounds + zoom: number + initial: boolean + }) => void + onAnimationStart?: () => void + onAnimationStop?: () => void + + // will be set to "edge" from v0.12 onward, defaulted to "center" before + limitBounds?: 'center' | 'edge' + + boxClassname?: string + tileComponent?: TileComponent +} + +export type TileComponent = (props: TileComponentProps) => JSX.Element + +export interface TileComponentProps { + tile: Tile + tileLoaded: () => void +} + +export interface Tile { + key: string + url: string + srcSet: string + left: number + top: number + width: number + height: number + active: boolean +} + +export interface TileValues { + tileMinX: number + tileMaxX: number + tileMinY: number + tileMaxY: number + tileCenterX: number + tileCenterY: number + roundedZoom: number + zoomDelta: number + scaleWidth: number + scaleHeight: number + scale: number +} + +export type WarningType = 'fingers' | 'wheel' + +export type WAdd = typeof window.addEventListener +export type WRem = typeof window.removeEventListener + +export interface MoveEvent { + timestamp: number + coords: Point +} + +type MinLat = number +type MaxLat = number +type MinLng = number +type MaxLng = number +export type MinMaxBounds = [MinLat, MaxLat, MinLng, MaxLng] + +export interface MapReactState { + zoom: number + center: Point + width: number + height: number + zoomDelta: number + pixelDelta?: [number, number] + oldTiles: TileValues[] + showWarning: boolean + warningType?: WarningType +} + +export interface MapState { + bounds: Bounds + zoom: number + center: Point + width: number + height: number +} + +export interface PigeonProps { + anchor?: Point + offset?: Point + left?: number + top?: number + mapState?: MapState + mapProps?: MapProps + + // pigeon functions + latLngToPixel?: (latLng: Point, center?: Point, zoom?: number) => Point + pixelToLatLng?: (pixel: Point, center?: Point, zoom?: number) => Point + setCenterZoom?: (center: Point | null, zoom: number, zoomAround?: Point | null, animationDuration?: number) => void +} diff --git a/node_modules/pigeon-maps/src/utils.ts b/node_modules/pigeon-maps/src/utils.ts new file mode 100644 index 0000000..ae80350 --- /dev/null +++ b/node_modules/pigeon-maps/src/utils.ts @@ -0,0 +1,25 @@ +export function debounce<T extends (...args: any[]) => any>(func: T, wait: number) { + let timeout: ReturnType<typeof setTimeout> + return function (this: any, ...args: Parameters<T>) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const context = this + clearTimeout(timeout) + timeout = setTimeout(() => func.apply(context, args), wait) + } +} + +export function parentHasClass(element: HTMLElement, className: string) { + while (element) { + if (element.classList && element.classList.contains(className)) { + return true + } + element = element.parentElement + } + + return false +} + +export function parentPosition(element: HTMLElement) { + const rect = element.getBoundingClientRect() + return { x: rect.left, y: rect.top } +} |