ol-ext
Version:
A set of cool extensions for OpenLayers (ol) in node modules structure
330 lines (304 loc) • 11.4 kB
JavaScript
/* Copyright (c) 2016 Jean-Marc VIGLINO,
released under the CeCILL-B license (French BSD license)
(http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/
import ol_interaction_DragOverlay from './DragOverlay'
import {unByKey as ol_Observable_unByKey} from 'ol/Observable'
import ol_interaction_Interaction from 'ol/interaction/Interaction'
import ol_ext_inherits from '../util/ext'
import ol_ext_element from '../util/element'
import ol_Overlay_Fixed from '../overlay/Fixed'
/** Handle a touch cursor to defer event position on overlay position
* It can be used as abstract base class used for creating subclasses.
* The TouchCursor interaction modifies map browser event coordinate and pixel properties to force pointer on the graphic cursor on the screen to any interaction that them.
* @constructor
* @extends {ol_interaction_DragOverlay}
* @param {olx.interaction.InteractionOptions} options Options
* @param {string} options.className cursor class name
* @param {ol.coordinate} options.coordinate position of the cursor
* @param {Array<*>} options.buttons an array of buttons
* @param {number} options.maxButtons maximum number of buttons (default 5)
*/
var ol_interaction_TouchCursor = function(options) {
options = options || {};
// List of listerner on the object
this._listeners = {};
// Interaction to defer position on top of the interaction
// this is done to enable other coordinates manipulation inserted after the interaction (snapping)
var offset = [-35,-35];
this.ctouch = new ol_interaction_Interaction({
handleEvent: function(e) {
if (!/drag/.test(e.type) && this.getMap()) {
e.coordinate = this.overlay.getPosition();
e.pixel = this.getMap().getPixelFromCoordinate(e.coordinate);
this._lastEvent = e;
} else {
var res = e.frameState.viewState.resolution
var cosa = Math.cos(e.frameState.viewState.rotation);
var sina = Math.sin(e.frameState.viewState.rotation);
e.coordinate = [
e.coordinate[0] + cosa*offset[0]*res + sina*offset[1]*res,
e.coordinate[1] + sina*offset[0]*res - cosa*offset[1]*res
];
e.pixel = this.getMap().getPixelFromCoordinate(e.coordinate);
}
return true;
}.bind(this)
});
// Force interaction on top
this.ctouch.set('onTop', true);
// Add Overlay
this.overlay = new ol_Overlay_Fixed({
className: ('ol-touch-cursor '+(options.className||'')).trim(),
positioning: 'top-left',
element: ol_ext_element.create('DIV', {}),
stopEvent: false,
});
ol_interaction_DragOverlay.call(this, {
centerOnClick: false,
//offset: [-20,-20],
overlays: this.overlay
});
this.setPosition(options.coordinate, true);
this.set('maxButtons', options.maxButtons || 5);
if (options.buttons) {
if (options.buttons.length > this.get('maxButtons')) this.set('maxButtons', options.buttons.length);
var elt = this.overlay.element;
var begin = options.buttons.length > 4 ? 0 : 1;
options.buttons.forEach((function (b, i) {
if (i<5) {
ol_ext_element.create('DIV', {
className: ((b.className||'')+' ol-button ol-button-' + (i+begin)).trim(),
html: ol_ext_element.create('DIV', { html: b.html }),
click: b.click,
on: b.on,
parent: elt
})
}
}))
}
// Replace events to handle click
var dragging = false;
var start = false;
this.on('dragstart', function (e) {
this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
start = e;
return !e.overlay;
})
this.on('dragend', function (e) {
this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
if (!e.overlay) return true;
if (dragging) {
this.dispatchEvent({
type: 'dragend',
dragging: dragging,
originalEvent: e.originalEvent,
frameState: e.frameState,
pixel: this._pixel,
coordinate: this.overlay.getPosition()
});
dragging = false;
} else {
if (e.originalEvent.target === this.overlay.element) {
this.dispatchEvent({
type: 'click',
dragging: dragging,
originalEvent: e.originalEvent,
frameState: e.frameState,
pixel: this._pixel,
coordinate: this.overlay.getPosition()
});
}
}
return false;
}.bind(this))
this.on('dragging', function (e) {
this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition());
if (!e.overlay) return true;
dragging = true;
if (start) {
this.dispatchEvent({
type: 'dragstart',
dragging: dragging,
originalEvent: start.originalEvent,
frameState: e.frameState,
pixel: this._pixel,
coordinate: start.coordinate
});
start = false;
}
this.dispatchEvent({
type: 'dragging',
dragging: dragging,
originalEvent: e.originalEvent,
frameState: e.frameState,
pixel: this._pixel,
coordinate: this.overlay.getPosition()
});
return false;
}.bind(this))
};
ol_ext_inherits(ol_interaction_TouchCursor, ol_interaction_DragOverlay);
/**
* Remove the interaction from its current map, if any, and attach it to a new
* map, if any. Pass `null` to just remove the interaction from the current map.
* @param {_ol_Map_} map Map.
* @api stable
*/
ol_interaction_TouchCursor.prototype.setMap = function(map) {
// Reset
if (this.getMap()) {
this.getMap().removeInteraction(this.ctouch);
if (this.getActive()) this.getMap().removeOverlay(this.overlay);
}
for (let l in this._listeners) {
ol_Observable_unByKey(this._listeners[l]);
}
this._listeners = {};
ol_interaction_DragOverlay.prototype.setMap.call (this, map);
// Set listeners
if (map) {
if (this.getActive()) {
map.addOverlay(this.overlay);
setTimeout( function() {
this.setPosition(this.getPosition() || map.getView().getCenter());
}.bind(this));
}
map.addInteraction(this.ctouch);
this._listeners.addInteraction = map.getInteractions().on('add', function(e) {
// Move on top
if (!e.element.get('onTop')) {
map.removeInteraction(this.ctouch);
map.addInteraction(this.ctouch);
}
}.bind(this));
}
};
/**
* Activate or deactivate the interaction.
* @param {boolean} active Active.
* @param {ol.coordinate|null} position position of the cursor (when activating), default viewport center.
* @observable
* @api
*/
ol_interaction_TouchCursor.prototype.setActive = function(b, position) {
if (b!==this.getActive()) {
this.ctouch.setActive(b);
if (!b) {
this.setPosition();
this.overlay.element.classList.remove('active');
if (this._activate) clearTimeout(this._activate);
if (this.getMap()) this.getMap().removeOverlay(this.overlay);
} else {
if (this.getMap()) {
this.getMap().addOverlay(this.overlay);
}
if (position) {
this.setPosition(position);
} else if (this.getMap()) {
this.setPosition(this.getMap().getView().getCenter());
}
this._activate = setTimeout(function() {
this.overlay.element.classList.add('active');
}.bind(this), 100);
}
ol_interaction_DragOverlay.prototype.setActive.call (this, b);
} else if (position) {
this.setPosition(position);
} else if (this.getMap()) {
this.setPosition(this.getMap().getView().getCenter());
}
};
/** Set the position of the target
* @param {ol.coordinate} coord
*/
ol_interaction_TouchCursor.prototype.setPosition = function (coord) {
this.overlay.setPosition(coord, true);
if (this.getMap() && coord) {
this._pixel = this.getMap().getPixelFromCoordinate(coord);
}
};
/** Offset the target position
* @param {ol.coordinate} coord
*/
ol_interaction_TouchCursor.prototype.offsetPosition = function (coord) {
var pos = this.overlay.getPosition();
if (pos) this.overlay.setPosition([pos[0]+coord[0], pos[1]+coord[1]]);
};
/** Get the position of the target
* @return {ol.coordinate}
*/
ol_interaction_TouchCursor.prototype.getPosition = function () {
return this.overlay.getPosition();
};
/** Get pixel position
* @return {ol.pixel}
*/
ol_interaction_TouchCursor.prototype.getPixel = function () {
if (this.getMap()) return this.getMap().getPixelFromCoordinate(this.getPosition());
};
/** Get cursor overlay
* @return {ol.Overlay}
*/
ol_interaction_TouchCursor.prototype.getOverlay = function () {
return this.overlay;
};
/** Get cursor overlay element
* @return {Element}
*/
ol_interaction_TouchCursor.prototype.getOverlayElement = function () {
return this.overlay.element;
};
/** Get cursor button element
* @param {string|number} button the button className or the button index
* @return {Element}
*/
ol_interaction_TouchCursor.prototype.getButtonElement = function (button) {
if (typeof(button) === 'number') return this.getOverlayElement().getElementsByClassName('ol-button')[button];
return this.getOverlayElement().getElementsByClassName(button)[0];
};
/** Remove a button element
* @param {string|number|undefined} button the button className or the button index, if undefined remove all buttons, default remove all
* @return {Element}
*/
ol_interaction_TouchCursor.prototype.removeButton = function (button) {
if (button===undefined) {
var buttons = this.getOverlayElement().getElementsByClassName('ol-button');
for (var i=buttons.length-1; i>=0; i--) {
this.getOverlayElement().removeChild(buttons[i]);
}
} else {
var elt = this.getButtonElement(button);
if (elt) this.getOverlayElement().removeChild(elt);
}
};
/** Add a button element
* @param {*} button
* @param {string} options.className button class name
* @param {DOMElement|string} options.html button content
* @param {function} options.click onclick function
* @param {*} options.on an object with
* @param {boolean} options.before
*/
ol_interaction_TouchCursor.prototype.addButton = function (b) {
var buttons = this.getOverlayElement().getElementsByClassName('ol-button');
var max = (this.get('maxButtons') || 5);
if (buttons.length >= max) {
console.error('[ol/interaction/TouchCursor~addButton] too many button on the cursor (max='+max+')...')
return;
}
var button = ol_ext_element.create('DIV', {
className: ((b.className||'')+' ol-button').trim(),
html: ol_ext_element.create('DIV', { html: b.html }),
click: b.click,
on: b.on
});
if (!b.before || buttons.length===0) this.getOverlayElement().appendChild(button);
else this.getOverlayElement().insertBefore(button, buttons[0]);
// Reorder buttons
var start = buttons.length >= max ? 0 : 1;
for (let i=0; i<buttons.length; i++) {
buttons[i].className = buttons[i].className.replace(/ol-button-\d/g, '').trim() + ' ol-button-' + (i+start);
}
};
export default ol_interaction_TouchCursor