Home Reference Source

src/layers/GroupLayer.js

import Group from 'ol/layer/Group'
import { unByKey } from 'ol/Observable'
import { mixin } from '../utilities'

import { ProvideMapMixin } from './ProvideMapMixin'

/**
 * This Class is a Wrap around {ol.layer.Group} providing some extra functionality. This class is normally used for a
 * category of layers containing them.
 */
export class GroupLayer extends mixin(Group, ProvideMapMixin) {
  /**
   * @param {object} [options={}]
   */
  constructor (options = {}) {
    super(options)

    const listenerKeys = new WeakMap()

    this.getLayers().on('add', /** CollectionEvent */ e => {
      const layer = e.element
      if (layer.provideMap) {
        layer.provideMap(this.getProvidedMap())
      }
      listenerKeys.set(layer, layer.on(['change:visible', 'change:childVisible'], () => {
        this.dispatchEvent({
          type: 'change:childVisible',
          child: layer
        })
      }))
    })

    this.getLayers().on('remove', /** CollectionEvent */ e => {
      const layer = e.element
      if (layer.provideMap) {
        layer.provideMap(null)
      }
      unByKey(listenerKeys.get(layer))
      listenerKeys.delete(layer)
    })
  }

  /**
   * The provideMap methods of all contained children are called recursively
   * @param {G4UMap} map
   */
  provideMap (map) {
    super.provideMap(map)

    this.getLayers().forEach(layer => {
      if (layer.provideMap) { layer.provideMap(map) }
    })
  }

  /**
   * calls callback for every terminal, non-group Layer
   * @param {Function} callback
   */
  recursiveForEach (callback) {
    this.getLayers().forEach(layer => {
      callback(layer, this)
      if (layer.recursiveForEach) {
        layer.recursiveForEach(callback)
      }
    })
  }

  /**
   * Attachs a listener to each layer and to each newly added listener and removes the listener if the layer gets
   * removed.
   * @param eventType
   * @param listener
   */
  forEachOn (eventType, listener) {
    this.recursiveForEach(layer => {
      layer.on(eventType, listener)
      if (layer instanceof GroupLayer) {
        this.getLayers().on('add', (e) => {
          e.element.on(eventType, listener)
        })
        this.getLayers().on('remove', (e) => {
          e.element.un(eventType, listener)
        })
      }
    })
  }

  /**
   * Checks how many children are visible. Doesn't check visibility of the group layer
   * @returns {number}
   */
  countChildrenVisible () {
    const array = this.getLayersArray()
    let count = 0

    for (let i = 0, ii = array.length; i < ii; i++) {
      if (array[i] instanceof GroupLayer) {
        count += array[i].countChildrenVisible()
      } else if (array[i].getVisible()) {
        count += 1
      }
    }
    return count
  }

  countChildren () {
    const array = this.getLayersArray()
    let count = 0

    for (let i = 0, ii = array.length; i < ii; i++) {
      if (array[i] instanceof GroupLayer) {
        count += array[i].countChildren()
      } else {
        count += 1
      }
    }
    return count
  }

  /**
   * Returns the layers inside as an array.
   * @returns {ol.layer.Base[]}
   */
  getLayersArray () {
    return this.getLayers().getArray()
  }

  /**
   * Returns the number of direct child layers
   * @returns {number}
   */
  getLength () {
    return this.getLayers().getLength()
  }

  /**
   * checks if the given layer is a child of the grouplayer
   * @param {ol.layer.Base} layer
   * @returns {boolean}
   */
  isParentOf (layer) {
    let found = false
    this.recursiveForEach(function (childLayer) {
      if (layer === childLayer) {
        found = true
      }
    })
    return found
  }

  /**
   * Returns all ids of all contained layers and its own.
   * @returns {number[]}
   */
  getIds () {
    let ids = this.get('id') !== undefined ? [this.get('id')] : []

    const array = this.getLayersArray()

    for (let i = 0, ii = array.length; i < ii; i++) {
      if (array[i].getIds) {
        ids = ids.concat(array[i].getIds())
      } else {
        ids.push(array[i].get('id'))
      }
    }

    return ids
  }

  /**
   * Returns all attributions of all visible layers.
   * @returns {number[]}
   */
  getAttributions () {
    let attributions = []

    this.recursiveForEach(function (layer) {
      if (layer.getVisible()) {
        if (layer.getSource) {
          const atts = layer.getSource().getAttributions()
          if (atts) {
            attributions = attributions.concat(atts)
          }
        }
      }
    })

    return attributions
  }

  /**
   * Remove layer by id
   * @param {string|number} id
   */
  removeLayerById (id) {
    const layers = this.getLayers()
    for (let i = 0; i < layers.getLength(); i++) {
      if (layers.item(i).get('id') === id) {
        return layers.removeAt(i)
      } else if (layers.item(i).removeLayerById) {
        const res = layers.item(i).removeLayerById(id)
        if (res) {
          return res
        }
      }
    }
  }

  /**
   * Find layer with callback
   * @param {Function} cb
   */
  findLayer (cb) {
    for (const layer of this.getLayersArray()) {
      if (cb(layer)) {
        return layer
      } else if (layer.findLayer) {
        const res = layer.findLayer(cb)
        if (res) {
          return res
        }
      }
    }
  }

  /**
   * Get layer by id
   * @param {string|number} id
   */
  getLayerById (id) {
    for (const layer of this.getLayersArray()) {
      if (layer.get('id') === id) {
        return layer
      } else if (layer.getLayerById) {
        const res = layer.getLayerById(id)
        if (res) {
          return res
        }
      }
    }
  }

  /**
   * removes layer
   * @param {ol.layer.Base} layer
   */
  removeLayer (layer) {
    let found = this.getLayers().remove(layer)
    if (!found) {
      this.getLayers().forEach(subLayer => {
        if (!found && subLayer.removeLayer) {
          found = subLayer.removeLayer(layer)
        }
      })
    }
    return found
  }

  /**
   * gives an object with ids as keys and visibility as value
   * @returns {Object.<string|number,boolean>}
   */
  getIdsVisibilities () {
    const visibilities = {}
    this.recursiveForEach(layer => {
      if (layer.get('id')) {
        visibilities[layer.get('id')] = layer.getVisible()
      }
    })
    return visibilities
  }

  /**
   * Sets the visibility of the layers to the given visibility
   * @param {Object.<string|number,boolean>} visibilities
   */
  setIdsVisibilities (visibilities) {
    this.recursiveForEach(layer => {
      if (layer.get('id')) {
        layer.setVisible(visibilities[layer.get('id')])
      }
    })
  }

  /**
   * Checks if the group contains the layer
   * @param {ol.layer.Layer} layer
   * @returns {boolean}
   */
  containsLayer (layer) {
    return this.getLayers().getArray().some(subLayer => {
      if (subLayer === layer) {
        return true
      }
      if (subLayer.containsLayer) {
        return subLayer.containsLayer(layer)
      }
    })
  }
}