src/Attributions.js
import { groupBy, map } from 'lodash/collection'
import { uniq } from 'lodash/array'
import { debounce } from 'lodash/function'
import Observable from 'ol/Observable'
import { ListenerOrganizerMixin } from './ListenerOrganizerMixin'
import { GroupLayer } from './layers/GroupLayer'
import { mixin } from './utilities'
function groupByChain (col) {
return {
groupBy: (cb) => {
return groupByChain(groupBy(col, cb))
},
forEach: (cb) => {
return groupByChain(col.forEach(cb))
},
map: (cb) => {
return groupByChain(map(col, cb))
},
asArray: () => {
return col
}
}
}
export class Attributions extends mixin(Observable, ListenerOrganizerMixin) {
constructor () {
super()
/**
* @type {string[]}
* @private
*/
this.attributions_ = []
this.debouncedUpdate = debounce(() => this.update())
}
/**
* @param {G4UMap} map
*/
setMap (map) {
if (this.map_) {
this.attributions_ = []
this.detachAllListeners()
}
this.map_ = map
if (map) {
map.asSoonAs('ready:layers', true, () => {
setTimeout(() => {
this.attachListeners(this.map_.getLayerGroup())
this.update()
}, 0)
})
}
}
getArray () {
return this.attributions_
}
scanLayers () {
return this.scanLayer(this.map_.getLayerGroup())
}
scanLayer (layer, layerTitle = null) {
if (layer && layer.getVisible()) {
if (layer.getLayers) {
const silentGroup = !(layer instanceof GroupLayer)
return layer.getLayers().getArray().reduce((arr, nextLayer) => {
layerTitle = silentGroup ? layer.get('title') : null
return arr.concat(this.scanLayer(nextLayer, layerTitle))
}, [])
} else {
if (layer.getVisible()) {
const label = layer.isBaseLayer
? this.map_.get('localiser').localiseUsingDictionary('Attribution baseLayerLabel')
: (layerTitle || layer.get('title'))
if (label) {
return this.extractAttributions(layer, this.labelize(label))
}
}
}
}
return []
}
extractAttributions (layer, label) {
if (layer.getSource && layer.getSource()) {
const attributions = layer.getSource().getAttributions()
if (attributions) {
return attributions().map(a => [label, a])
}
return []
}
}
labelize (title) {
return title.replace(/<[^>]*>/, ' ').replace(/\s{2,}/, ' ')
}
update () {
this.attributions_ = groupByChain(this.scanLayers())
.groupBy(([layerTitle, attribution]) => {
return layerTitle
}) // -> { layerTitle: [[layerTitle, attribution]*] }
.map((val, key) => {
return [key, val.map(p => p[1])]
}) // -> [[layerTitle, attribution*]*]
.groupBy(([layerTitle, attributions]) => {
attributions = uniq(attributions)
if (attributions.length === 1) {
return attributions[0]
} else {
return attributions.join(' & ')
}
})
.map((val, key) => {
return [val.map(p => p[0]), key]
})
.map(([layerTitles, attributionText]) => {
let text
if (layerTitles.length === 1) {
text = layerTitles[0]
} else {
text = layerTitles.slice(0, -1).join(', ') + ' & ' + layerTitles[layerTitles.length - 1]
}
text += ': ' + attributionText
return text
})
.asArray()
this.dispatchEvent('change', this.attributions_)
}
attachListeners (layer) {
if (layer.getLayers) {
layer.getLayers().forEach(l => this.attachListeners(l))
this.listenAt(layer.getLayers())
.on('add', e => {
this.attachListeners(e.element)
this.debouncedUpdate()
})
.on('remove', e => {
this.detachFrom(e.element)
this.debouncedUpdate()
})
}
if (!(layer instanceof GroupLayer)) {
this.listenAt(layer)
.on('change:visible', () => {
this.debouncedUpdate()
})
.on('change:source', () => {
if (layer.getVisible()) {
this.debouncedUpdate()
}
})
}
}
}