import Interaction from 'ol/interaction/Interaction'
// code inspiré de ol-ext https://github.com/Viglino/ol-ext/blob/master/src/interaction/Hover.js
// on va dispatch les event directement sur core
/** Interaction qui permet de lever un event lorsque l'on passe sur une feature
* @constructor
* @extends {ol_interaction_Interaction}
* @fires hover, hover-enter, hover-leave (directement sur le viewer)
* @param {olx.interaction.HoverOptions}
* @param { string | undefined } options.cursor css cursor a appliquer lorsque l'on survole une feature
* @param {function | undefined} options.featureFilter fonction filtre avec deux arguments, la feature et son layer . Return true pour valider le hover de la feature
* @param {number | undefined} options.hitTolerance Hit-detection tolerance en pixels.
* @param { function | undefined } options.handleCancelHover fonction a appeler pour empecher globalement le survol
* @param { function | undefined } options.handleCancelPropagation fonction a appeler pour empêcher la propagation des events apres cette interactions
*/
class Hover extends Interaction {
constructor (options) {
options = options || { }
// on utilise self car on ne peut pas utiliser this avant la méthode super()
let self = null
const optOptions = {
...options,
handleEvent: (evt) => {
if (!self.getActive() || evt.dragging) { return true }
// si certaines fonctions internes sont actives, on quitte les feature "hover" et on ne fait rien
// il faudra surement éttofer au fur et a mesurre du temps
if (self.viewer.dataLayer.hasNonHoverableInteractionInProgress()) {
self.unHoverLastFeature()
return true
}
if (options.handleCancelHover && options.handleCancelHover(evt)) { return true }
if (evt.type === 'pointermove') {
self.handleMove_(evt)
}
// l'utilisateur de cette api peut empecher de propager les event
if (options.handleCancelPropagation) { return options.handleCancelPropagation(evt) }
return true
},
}
super(optOptions)
self = this
if (!options.viewer) {
console.error('[CustomHover] options.viewer non présent')
return
}
this.viewer = options.viewer
// dernière feature survolée
this.lastFeature_ = null
// et son calque
this.lastLayer_ = null
this.layerFilter_ = (layer) => {
// seulement si on a défini le calque comme hoverable
return this.viewer.dataLayer.isHoverableLayer(layer)
}
this.setFeatureFilter(options.featureFilter)
this.set('hitTolerance', options.hitTolerance)
this.setCursor(options.cursor)
}
/**
* 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
*/
setMap (map) {
if (this.previousCursor_ !== undefined && this.getMap()) {
this.getMap().getTargetElement().style.cursor = this.previousCursor_
this.previousCursor_ = undefined
}
super.setMap(map)
}
/** Activate / deactivate interaction
* @param {boolean} b
*/
setActive (b) {
super.setActive(b)
if (this.cursor_ && this.getMap() && this.getMap().getTargetElement()) {
const style = this.getMap().getTargetElement().style
if (this.previousCursor_ !== undefined) {
style.cursor = this.previousCursor_
this.previousCursor_ = undefined
}
}
}
/**
* Set cursor on hover
* @param { string } cursor css cursor propertie or a function that gets a feature, default: none
* @api stable
*/
setCursor (cursor) {
if (!cursor && this.previousCursor_ !== undefined && this.getMap()) {
this.getMap().getTargetElement().style.cursor = this.previousCursor_
this.previousCursor_ = undefined
}
this.cursor_ = cursor
}
/** Feature filter to get only one feature
* @param {function} filter a function with two arguments, the feature and the layer of the feature. Return true to select the feature
*/
setFeatureFilter (filter) {
if (typeof (filter) === 'function') { this.featureFilter_ = filter } else { this.featureFilter_ = function () { return true } }
}
/** Feature filter to get only one feature
* @param {function} filter a function with one argument, the layer to test. Return true to test the layer
*/
/* setLayerFilter (filter) {
if (typeof (filter) === 'function') { this.layerFilter_ = filter } else { this.layerFilter_ = function () { return true } }
} */
/** Get features whenmove
* @param {ol.event} e "move" event
*/
handleMove_ (e) {
const map = this.getMap()
if (map) {
let feature, layer
const b = map.forEachFeatureAtPixel(
e.pixel,
(f, l) => {
if (this.featureFilter_.call(null, f, l)) {
feature = f
layer = l
return true
} else {
feature = layer = null
return false
}
}, {
hitTolerance: this.get('hitTolerance'),
layerFilter: this.layerFilter_,
}
)
if (b) {
this.hoverFeature(e, feature, layer)
}
if (this.lastFeature_ === feature && this.lastLayer_ === layer) {
// on est sur la même feature, on ne génère par d'event
} else {
// on quitte la dernière feature avant de faire un enter
if (this.lastFeature_) {
this.leaveFeature(e, this.lastFeature_, this.lastLayer_)
}
if (feature) {
this.enterFeature(e, feature, layer)
}
}
if (this.cursor_) {
const style = map.getTargetElement().style
if (b) {
if (style.cursor !== this.cursor_) {
this.previousCursor_ = style.cursor
style.cursor = this.cursor_
}
} else if (this.previousCursor_ !== undefined) {
style.cursor = this.previousCursor_
this.previousCursor_ = undefined
}
}
}
}
hoverFeature (e, feature, layer) {
// on fait du hover sur une feature, on dispatch un event
this.viewer.dispatchEvent(
'hover',
{
feature,
layer,
coordinate: e.coordinate,
pixel: e.pixel,
map: e.map,
originalEvent: e.originalEvent,
dragging: e.dragging,
})
}
leaveFeature (e, feature, layer) {
if (feature) {
this.viewer.dataLayer.removeTagToOlFeature(feature, this.viewer.dataLayer.systemTagName.HOVERED_FEATURE)
}
this.viewer.dispatchEvent(
'hover-leave',
{
feature,
layer,
coordinate: e.coordinate,
pixel: e.pixel,
map: e.map,
originalEvent: e.originalEvent,
dragging: e.dragging,
})
this.lastFeature_ = null
this.lastLayer_ = null
}
enterFeature (e, feature, layer) {
this.viewer.dataLayer.addTagToOlFeature(feature, this.viewer.dataLayer.systemTagName.HOVERED_FEATURE)
this.lastFeature_ = feature
this.lastLayer_ = layer
this.viewer.dispatchEvent(
'hover-enter',
{
feature,
layer,
coordinate: e.coordinate,
pixel: e.pixel,
map: e.map,
originalEvent: e.originalEvent,
dragging: e.dragging,
})
}
unHoverLastFeature () {
if (this.lastFeature_) {
this.leaveFeature({}, this.lastFeature_, this.lastLayer_)
}
}
}
export default Hover