Source

plugins/vue-map.js

import Vue from 'vue'
import mitt from '@/plugins/mitt.js'
import colors from '@/styles/colors.js'
const defaultHighlightColor = '#00ff00'
const defaultNormalColor = colors.color_main

/**
 * Map singleton that holds references to active maps.
 */
const wrapper = {
  defaultHighlightColor,
  /**
   * Refs to current loaded maps (Component instances = VM)
   */
  vms: {},
  /**
   * @rule Should not overwrite existing refs.
   * @rule Skip if no object (vm)
   * @param {String} name Unique name
   * @param {Object} vm Component instance (VM)
   * @return {Boolean} Success
   */
  registerVM(name = '', vm) {
    if (['string', 'number'].includes(typeof vm)) {
      console.warn('registerVM: Object required')
      return false
    }
    //Unregister existing mainMap if destroyed
    if (!!this.vms.mainMap && this.vms.mainMap._isDestroyed) {
      delete this.vms.mainMap
    }
    if (!!this.vms.mainMap && name === 'mainMap') {
      name = '' //Use case: Location - Main search - Table,Table+Map mode: Assign a differentn ame to avoid lossing ref to mainMap.
    }
    if (!name && !this.vms.mainMap) {
      name = 'mainMap' //First map without name will be "mainMap" (i.g AlertModule's map)
    }
    name = name || `map${Object.keys(this.vms).length + 1}`
    this.vms[name] = vm
    vm.__mapName = name
    return true
  },
  unregisterVM(vm) {
    if (vm.__mapName) {
      delete this.vms[vm.__mapName]
    }
  },
  fitBounds() {
    return this.getLeafletInstance().fitBounds.apply(
      this.getLeafletInstance(),
      Array.prototype.slice.call(arguments)
    )
  },
  /**
   * @param {String} name map name (Defaults to current map)
   * @returns
   */
  getLeafletInstance(name = '') {
    name = name || this.getCurrentMapName()
    return this.vms[name] && this.vms[name].getLeafletMapWrapper().map
  },
  /**
   * @param {String} name map name (Defaults to current map)
   * @returns
   */
  getLeafletWrapperVM(name = '') {
    name = name || this.getCurrentMapName()
    return this.vms[name] && this.vms[name].getLeafletMapWrapper()
  },
  /**
   * @param {String} name map name (Defaults to current map)
   * @returns
   */
  getSimplicitiMapBottomMenuVM(name = '') {
    name = name || this.getCurrentMapName()
    return this.vms[name] && this.vms[name].$refs.bottomMenu
  },
  /**
   * @param {String} name map name (Defaults to current map)
   * @returns
   */
  getSimplicitiMapVM(name = '') {
    name = name || this.getCurrentMapName()
    return this.vms[name] && this.vms[name]
  },
  /*
   * The current map name is mainMap or mapN if present (i.g map4 if mainMap, map1, map2, map3)
   */
  getCurrentMapName(vms) {
    vms = vms || this.vms
    if (vms.mainMap && Object.keys(vms).length === 1) {
      return 'mainMap'
    } else {
      let mapNumberMax = -1
      Object.keys(vms)
        .filter((k) => k !== 'mainMap' && k.includes('map') && k.length <= 4) //i.g map1, map2, map3
        .map((k) => parseInt(k.split('map').join('')))
        .forEach((mapNumber) => {
          if (mapNumber > mapNumberMax) {
            mapNumberMax = mapNumber
          }
        })

      if (mapNumberMax === -1 && !!vms.mainMap) {
        return 'mainMap'
      }

      return `map${mapNumberMax}`
    }
  },
  /**
   * @warn unused
   * @param {*} groupName
   * @param {*} newColor
   * @param {*} options
   */
  setLayersColor(groupName, newColor = defaultHighlightColor, options = {}) {
    let defaultColor = options.defaultColor || defaultNormalColor
    let filterHandler = options.filter || false
    let layers =
      this.getLeafletWrapperVM().layerGroups[groupName]?.getLayers() || []
    layers = layers.map((layer) => {
      if (filterHandler) {
        return { layer, filtered: filterHandler(layer) }
      }
      return { layer, filtered: true }
    })

    layers.forEach(({ layer, filtered }) => {
      layer.setStyle({
        color: filtered ? newColor : defaultColor,
      })
      if (layer.arrowheads) {
        layer.arrowheads({
          color: filtered ? newColor : defaultColor,
        })
      }
    })
    this.getLeafletWrapperVM().map.invalidateSize()
    options.after && options.after(layers)
  },
  /**
   * Used by Location - History (List mode) (Table mode)
   *
   * Will add reactive property "highlightedColor" to each item related polyline.
   * @param {Object} locationItem Location main search item
   * @param {*} results
   */
  highlightLocationItemPolylines(locationItem) {
    if (!locationItem) {
      throw new Error('INVALID_LOCATION_ITEM')
    }
    if (Object.keys(wrapper.getSimplicitiMapVM() || {}).length === 0) {
      console.warn('vue-map', 'No SimplicitiMapVM found!')
      return false
    }
    if (!locationItem.relatedPolylines) {
      throw new Error('NO_RELATED_POLYLINES')
    }
    wrapper
      .getSimplicitiMapVM()
      .withLeafletMapLayerGroup('linestrings', (layerGroup) => {
        //unhighlight existing
        layerGroup.getLayers().forEach((layer) => {
          layer.closePopup && layer.closePopup()
          if (layer) {
            //console.log("unhighlight", layer);
            layer.options.originalColor =
              layer?.options?.originalColor ||
              layer?.options?.color ||
              layer?.style?.color ||
              layer.color ||
              'darkgrey'
            layer.setStyle({
              color:
                layer.originalColor ||
                layer?.options?.originalColor ||
                layer?.properties?.originalColor ||
                'darkgray',
            })
          }
        })
        //highlight match
        let lastMatch = null
        layerGroup
          .getLayers()
          .filter(
            (layer) =>
              !!locationItem.relatedPolylines.find(
                (p) => p.stepNumber == layer.options.stepNumber
              )
          )
          .forEach((match) => {
            //console.log("highlight", match);
            lastMatch = match
            match.closePopup && match.closePopup()
            match.setStyle({
              color: defaultHighlightColor,
            })
          })
        lastMatch.openPopup && lastMatch.openPopup()
      })
  },
  /**
   * Used by Location module - Selectable polylines feature
   *
   * @param {String} stepNumber Used to filter geometries
   */
  highlightPolylines(stepNumber) {
    if (!stepNumber) {
      throw new Error('INVALID_STEP_NUMBER')
    }
    if (Object.keys(wrapper.getSimplicitiMapVM() || {}).length === 0) {
      console.warn('vue-map', 'No SimplicitiMapVM found!')
      return false
    }
    wrapper
      .getSimplicitiMapVM()
      .withLeafletMapLayerGroup('linestrings', (layerGroup) => {
        //unhighlight existing
        layerGroup.getLayers().forEach((layer) => {
          //console.log("unhighlight", layer);
          layer.setStyle({
            color:
              layer.originalColor ||
              layer?.options?.originalColor ||
              layer?.properties?.originalColor ||
              'darkgray',
          })
        })
        //highlight match
        layerGroup
          .getLayers()
          .filter((layer) => layer.options.stepNumber == stepNumber)
          .forEach((match) => {
            //console.log("highlight", match);
            match.setStyle({
              color: '#00ff00',
            })
          })
      })
  },
  /**
   * Used by Location module
   * Location - Realtime - List/Table (main search) : Zoom on lat/lng
   * Location - History - ListTable (main search): Fit Bound to related polylines
   **/
  zoomOnLocationItem(item) {
    if (!item || !item.searchType) {
      return false
    }
    if (item.searchType === 'realtime') {
      if (!(!!item.lat && !!item.lng)) {
        return false
      }
      mitt.emit('SIMPLICITI_MAP_FIT_TO_BOUNDS', [[item.lat, item.lng]])
    } else {
      if (!item.relatedPolylines) {
        throw new Error('NO_RELATED_POLYLINES')
      }
      let bounds = item.relatedPolylines.reduce((acum, value) => {
        return [...acum, value.polyline]
      }, [])
      if (bounds.length === 0) {
        return false
      }
      mitt.emit('SIMPLICITI_MAP_FIT_TO_BOUNDS', bounds)
    }
    return true
  },
}

/**
 * Map singleton to that holds current maps references for access.
 */
Vue.use({
  install() {
    Vue.$map = Vue.prototype.$map = wrapper
  },
})

export default wrapper