src/controls/ArrowButtons.js
import $ from 'jquery'
import { merge } from '../utilitiesObject'
import { Control } from './Control'
import { addTooltip } from '../html/html'
import '../../less/arrowbuttons.less'
import { rotate } from 'ol/coordinate'
/**
* @typedef {g4uControlOptions} ArrowButtonOptions
* @property {number} [pixelDelta=128] how many pixels should one click move the map
* @property {ol.Extent|undefined} [initExtent=undefined] the initial Extent to move the map back to (center button)
* @property {number} [animationDuration=100] duration of the animation
* @property {boolean} [animated=true] if the move should be done with an animation or without
* @property {{up: string, right: string, down: string, left: string, center: string}|{}} [labels={}]
* unicode labels for the buttons (only shown if pictures are not loaded)
*/
/**
* ArrowButtons shows Buttons on the map which let you move in all 4 directions and a button to return to the initial
* position of the map. It provides an Interface class with an produceOlControl returning an
* {ol.control.Control} - Object. This can be added as normal controls to the map
* (e.g. ``map.addControl(arrowButtons.produceOlControl)`` ).
*
* The Options are passed as an Object (e.g. ``{ initCenter : map.getView().getCenter(),
* initZoom : map.getView().getZoom() }``.
*/
export class ArrowButtons extends Control {
/**
* @param {ArrowButtonOptions} [options={}]
*/
constructor (options = {}) {
options.className = options.className || 'g4u-arrowbuttons'
options.element = $('<div class="ol-unselectable ol-control"></div>')[0]
options.singleButton = false
super(options)
const description = {
left: this.getLocaliser().localiseUsingDictionary('ArrowButtons leftward'),
right: this.getLocaliser().localiseUsingDictionary('ArrowButtons rightward'),
up: this.getLocaliser().localiseUsingDictionary('ArrowButtons upward'),
down: this.getLocaliser().localiseUsingDictionary('ArrowButtons downward'),
center: this.getLocaliser().localiseUsingDictionary('ArrowButtons centerward')
}
/**
* @type {number}
* @private
*/
this.pixelDelta_ = options.pixelDelta || 128
/**
* @type {ol.Extent|undefined}
* @private
*/
this.initExtent_ = options.initExtent || undefined
/**
* @type {number}
* @private
*/
this.animationDuration_ = options.animationDuration || 100
/**
* @type {boolean}
* @private
*/
this.animated_ = options.animated || true
/**
* @type {string[]}
* @private
*/
this.directions_ = ['center', 'left', 'up', 'right', 'down']
/**
* @type {{left: number[], up: number[], right: number[], down: number[]}}
* @private
*/
this.vectors_ = { left: [-1, 0], up: [0, 1], right: [1, 0], down: [0, -1] }
/**
* @type {{up: string, right: string, down: string, left: string, center: string}}
* @private
*/
this.labels_ = merge(options.labels || {}, {
left: '◁',
up: '△',
right: '▷',
down: '▽',
center: '○'
})
// creating the HTML-Elements and registering Event-Handlers
for (const direction of this.directions_) {
// HTML
const $button = $('<button>')
.addClass(this.className_ + '-' + direction)
.html(this.labels_[direction])
addTooltip($button, description[direction])
// Handler
$button.on('click', () => {
this.onClick_(direction)
$button.blur()
})
// adding button
this.get$Element().append($button)
}
}
/**
* @param {G4UMap} map
*/
setMap (map) {
super.setMap(map)
if (map) {
if (!this.initExtent_) {
this.initExtent_ = map.getView().calculateExtent(map.getSize())
}
}
}
/**
* This method is called when a button is clicked and makes a move on the map corresponding to the direction.
* @param {string} direction
* @private
*/
onClick_ (direction) {
const map = this.getMap()
const view = map.getView()
if (direction === 'center') {
map.get('move').toExtent(this.initExtent_, { animated: this.animated_ })
} else {
const resolution = view.getResolution()
const rotation = view.getRotation()
// a vector that points in the direction the move should be going
const dirVec = this.vectors_[direction]
// this calculates the 'move'-vector out of the direction and pixelDelta (-> length, how many pixels per move)
const delta = [dirVec[0] * resolution * this.pixelDelta_, dirVec[1] * resolution * this.pixelDelta_]
// this rotates if needed
rotate(delta, rotation)
const oldPosition = view.getCenter()
// adding the delta to the actual center
const newPosition = [oldPosition[0] + delta[0], oldPosition[1] + delta[1]]
if (this.animated_) {
// creating a pan-animation
view.animate({
center: newPosition,
duration: this.animationDuration_
})
} else {
view.setCenter(newPosition)
}
}
$(map.getViewport()).focus()
}
}