Spaces:
Sleeping
Sleeping
| // @flow | |
| import DOM from '../../util/dom'; | |
| import {extend, bindAll} from '../../util/util'; | |
| import type Map from '../map'; | |
| type Unit = 'imperial' | 'metric' | 'nautical'; | |
| type Options = { | |
| maxWidth?: number, | |
| unit?: Unit; | |
| }; | |
| const defaultOptions: Options = { | |
| maxWidth: 100, | |
| unit: 'metric' | |
| }; | |
| /** | |
| * A `ScaleControl` control displays the ratio of a distance on the map to the corresponding distance on the ground. | |
| * | |
| * @implements {IControl} | |
| * @param {Object} [options] | |
| * @param {number} [options.maxWidth='100'] The maximum length of the scale control in pixels. | |
| * @param {string} [options.unit='metric'] Unit of the distance (`'imperial'`, `'metric'` or `'nautical'`). | |
| * @example | |
| * var scale = new mapboxgl.ScaleControl({ | |
| * maxWidth: 80, | |
| * unit: 'imperial' | |
| * }); | |
| * map.addControl(scale); | |
| * | |
| * scale.setUnit('metric'); | |
| */ | |
| class ScaleControl { | |
| _map: Map; | |
| _container: HTMLElement; | |
| options: Options; | |
| constructor(options: Options) { | |
| this.options = extend({}, defaultOptions, options); | |
| bindAll([ | |
| '_onMove', | |
| 'setUnit' | |
| ], this); | |
| } | |
| getDefaultPosition() { | |
| return 'bottom-left'; | |
| } | |
| _onMove() { | |
| updateScale(this._map, this._container, this.options); | |
| } | |
| onAdd(map: Map) { | |
| this._map = map; | |
| this._container = DOM.create('div', 'mapboxgl-ctrl mapboxgl-ctrl-scale', map.getContainer()); | |
| this._map.on('move', this._onMove); | |
| this._onMove(); | |
| return this._container; | |
| } | |
| onRemove() { | |
| DOM.remove(this._container); | |
| this._map.off('move', this._onMove); | |
| this._map = (undefined: any); | |
| } | |
| /** | |
| * Set the scale's unit of the distance | |
| * | |
| * @param unit Unit of the distance (`'imperial'`, `'metric'` or `'nautical'`). | |
| */ | |
| setUnit(unit: Unit) { | |
| this.options.unit = unit; | |
| updateScale(this._map, this._container, this.options); | |
| } | |
| } | |
| export default ScaleControl; | |
| function updateScale(map, container, options) { | |
| // A horizontal scale is imagined to be present at center of the map | |
| // container with maximum length (Default) as 100px. | |
| // Using spherical law of cosines approximation, the real distance is | |
| // found between the two coordinates. | |
| const maxWidth = options && options.maxWidth || 100; | |
| const y = map._container.clientHeight / 2; | |
| const left = map.unproject([0, y]); | |
| const right = map.unproject([maxWidth, y]); | |
| const maxMeters = left.distanceTo(right); | |
| // The real distance corresponding to 100px scale length is rounded off to | |
| // near pretty number and the scale length for the same is found out. | |
| // Default unit of the scale is based on User's locale. | |
| if (options && options.unit === 'imperial') { | |
| const maxFeet = 3.2808 * maxMeters; | |
| if (maxFeet > 5280) { | |
| const maxMiles = maxFeet / 5280; | |
| setScale(container, maxWidth, maxMiles, map._getUIString('ScaleControl.Miles')); | |
| } else { | |
| setScale(container, maxWidth, maxFeet, map._getUIString('ScaleControl.Feet')); | |
| } | |
| } else if (options && options.unit === 'nautical') { | |
| const maxNauticals = maxMeters / 1852; | |
| setScale(container, maxWidth, maxNauticals, map._getUIString('ScaleControl.NauticalMiles')); | |
| } else if (maxMeters >= 1000) { | |
| setScale(container, maxWidth, maxMeters / 1000, map._getUIString('ScaleControl.Kilometers')); | |
| } else { | |
| setScale(container, maxWidth, maxMeters, map._getUIString('ScaleControl.Meters')); | |
| } | |
| } | |
| function setScale(container, maxWidth, maxDistance, unit) { | |
| const distance = getRoundNum(maxDistance); | |
| const ratio = distance / maxDistance; | |
| container.style.width = `${maxWidth * ratio}px`; | |
| container.innerHTML = `${distance} ${unit}`; | |
| } | |
| function getDecimalRoundNum(d) { | |
| const multiplier = Math.pow(10, Math.ceil(-Math.log(d) / Math.LN10)); | |
| return Math.round(d * multiplier) / multiplier; | |
| } | |
| function getRoundNum(num) { | |
| const pow10 = Math.pow(10, (`${Math.floor(num)}`).length - 1); | |
| let d = num / pow10; | |
| d = d >= 10 ? 10 : | |
| d >= 5 ? 5 : | |
| d >= 3 ? 3 : | |
| d >= 2 ? 2 : | |
| d >= 1 ? 1 : getDecimalRoundNum(d); | |
| return pow10 * d; | |
| } | |