src/controls/DiscreteLegend.js
import L from 'leaflet'
import {inject, fromTemplate, $$} from './utils.js'
import {getLanguageTag, getLanguageString} from 'covutils'
const DEFAULT_TEMPLATE_ID = 'template-coverage-parameter-discrete-legend'
const DEFAULT_TEMPLATE = `<template id="${DEFAULT_TEMPLATE_ID}">
<div class="leaflet-coverage-control legend discrete-legend">
<div class="legend-title-container"><strong class="legend-title"></strong></div>
<div class="legend-palette discrete-legend-palette"></div>
</div>
</template>`
/**
* Displays a discrete palette legend for the parameter displayed by the given
* Coverage layer. Supports category parameters only at the moment.
*
* @example <caption>Coverage data layer</caption>
* new C.DiscreteLegend(covLayer).addTo(map)
* // changing the palette of the layer automatically updates the legend
* covLayer.palette = C.discretePalette(['red', 'blue'])
*
* @example <caption>Fake layer</caption>
* var fakeLayer = {
* parameter: {
* observedProperty: {
* label: { en: 'Land cover' },
* categories: [{
* label: { en: 'Land' }
* }, {
* label: { en: 'Water' }
* }]
* }
* },
* palette: C.directPalette(['gray', 'blue']) // CSS colors in category order
* }
* var legend = new C.DiscreteLegend(fakeLayer).addTo(map)
*
* // change the palette and trigger a manual update
* fakeLayer.palette = C.discretePalette(['red', 'blue'])
* legend.update()
*
* @extends {L.Control}
*/
export class DiscreteLegend extends L.Control {
/**
* Creates a discrete legend control.
*
* @param {object} covLayer
* The coverage data layer, or any object with <code>palette</code>
* and <code>parameter</code> properties.
* If the object has <code>on</code>/<code>off</code> methods, then the legend will
* listen for <code>"paletteChange"</code> events and update itself automatically.
* If the layer fires a <code>"remove"</code> event, then the legend will remove itself
* from the map.
* @param {object} [options] Legend options.
* @param {string} [options.position='bottomright'] The initial position of the control (see Leaflet docs).
* @param {string} [options.language] A language tag, indicating the preferred language to use for labels.
* @param {string} [options.templateId] Uses the HTML element with the given id as template.
*/
constructor (covLayer, options = {}) {
super({position: options.position || 'bottomright'})
this._covLayer = covLayer
this._templateId = options.templateId || DEFAULT_TEMPLATE_ID
this._language = options.language
if (!options.templateId && document.getElementById(DEFAULT_TEMPLATE_ID) === null) {
inject(DEFAULT_TEMPLATE)
}
if (covLayer.on) {
this._remove = () => this.remove()
this._update = () => this._doUpdate(false)
covLayer.on('remove', this._remove)
}
}
/**
* Triggers a manual update of the legend.
*
* Useful if the supplied coverage data layer is not a real layer
* and won't fire the necessary events for automatic updates.
*/
update () {
this._doUpdate(true)
}
_doUpdate (fullUpdate) {
let el = this._el
if (fullUpdate) {
let param = this._covLayer.parameter
// if requested language doesn't exist, use the returned one for all other labels
this._language = getLanguageTag(param.observedProperty.label, this._language)
let title = getLanguageString(param.observedProperty.label, this._language)
$$('.legend-title', el).innerHTML = title
}
let palette = this._covLayer.palette
let param = this._covLayer.parameter
let html = ''
for (let i=0; i < palette.steps; i++) {
let cat = getLanguageString(param.observedProperty.categories[i].label, this._language)
html += `
<i style="background:rgb(${palette.red[i]}, ${palette.green[i]}, ${palette.blue[i]})"></i>
${cat}
<br>`
}
$$('.legend-palette', el).innerHTML = html
}
/**
* @override
* @ignore
*/
onAdd (map) {
this._map = map
if (this._covLayer.on) {
this._covLayer.on('paletteChange', this._update)
}
this._el = fromTemplate(this._templateId)
this.update()
return this._el
}
/**
* @override
* @ignore
*/
onRemove () {
if (this._covLayer.off) {
this._covLayer.off('remove', this._remove)
this._covLayer.off('paletteChange', this._update)
}
}
}