Source

components/shared/SimplicitiMap/draw-positions-markers-mixin.js

import Vue from 'vue'
import colors from '@/styles/colors.js'

const torCodes = [
  'TOR1',
  'TOR2',
  'TOR3',
  'TOR4',
  'TOR5',
  'TOR6',
  'TOR7',
  'TOR8',
  'TOR+',
]

export default {
  inject: {
    isTripHistoryTableMap: {
      default: false,
    },
    enableRenderVehiclePositionsMarkers: {
      default: false,
    },
    isCircuitMap: {
      default: false,
    },
  },
  computed: {
    /**
     * @todo Remove isTripHistoryTableMap
     */
    showPositionMarkersByDefault() {
      return (
        this.isCircuitMap ||
        this.isTripHistoryTableMap ||
        this.enableRenderVehiclePositionsMarkers
      )
    },
  },
  watch: {
    /**
     * Used by:
     * Location - Real-time by any - Last trip history
     * Location - History by Vehicle/Driver - Main search (details)
     */
    vehiclePositionMarkers: {
      handler() {
        this.onPositionsChange(this.mapOptions.positions)
      },
      deep: true,
      immediate: true,
    },
  },
  methods: {
    /**
     * Will be trigered any time positions data change or positions toggle button is pressed
     */
    onPositionsChange(isPositionsLayerToggleButtonChecked) {
      if (!this.isLocationMainSearch) {
        this.drawPositionsMarkers({
          visible: isPositionsLayerToggleButtonChecked, //Ignored?
        })

        this.waitForMapRef(this.drawSensorMarkers)
      }
    },
    /**
     * Draw sensor markers when user toggle layers
     */
    onSensorLayerToggleChange() {
      if (!(this.sensorsConfig && this.sensorsConfig.sensors)) {
        return
      }
      this.waitForMapRef(() => {
        //TOR 1..8 layers (bind to each button: i.g Tor1 enables Tor1 layers)
        this.sensorsConfig.sensors
          .filter((item) => item.code !== 'TOR+')
          .forEach((configItem) => {
            this.toggleMapLayers({
              layerName: `positions_${configItem.code}`, //i.g positions_TOR5
              enabled: configItem.showMarkers,
            })
            this.toggleMapLayers({
              layerName: `positions_multiple_tors_${configItem.code.toLowerCase()}`,
              enabled: configItem.showMarkers,
            })
          })

        //TOR+ layers
        const enableMultipleTors = (
          this.sensorsConfig.sensors.find((item) => item.code === 'TOR+') || {
            showMarkers: false,
          }
        ).showMarkers
        torCodes
          .filter((c) => c !== 'TOR+')
          .forEach((code) => {
            this.toggleMapLayers({
              layerName: `positions_multiple_tors_${code.toLowerCase()}`,
              enabled: enableMultipleTors,
            })
          })

        /*
        Re initializing leaflet layers is hardware intensive
        this.drawSensorMarkers({
          ignoreVisibilityByDefault: true,
        })
        */
      })
    },
    /**
     * Draw position markers on the map
     * - Waits until map ref is available
     * - Filters only common positions (without sensor data)
     */
    drawPositionsMarkers(options = {}) {
      this.$nextTick(() => {
        this.waitForMapRef(() => {
          drawPositionsMarkersFromData.apply(this, [
            this.vehiclePositionMarkers.filter(
              (i) => !i.code || i.code === 'POS'
            ),
            {
              ...options,
              //Hot-fix: Removed due to invalid position in some clients
              //after: ({ map, geometries }) =>
              //map.fitBounds(geometries.map((g) => g.getLatLng())),
            },
          ])
        })
      })
    },
    /**
     * Draw positions with sensor data using dataset from store
     *
     * Markers will be visible by default if each marker type has showMarkers equals true
     * Markers will not be visible if computed showPositionMarkersByDefault equals false
     * showPositionMakrersByDefault has no effect if ignoreVisibilityByDefault equals true
     */
    drawSensorMarkers(options = {}) {
      const getSensorConfig = (code) =>
        (this.sensorsConfig.sensors || []).find((i) => i.code === code) || null

      const shouldRenderMarkersFromType = (config) =>
        options.ignoreVisibilityByDefault
          ? config.showMarkers
          : this.showPositionMarkersByDefault
          ? config.showMarkers
          : false

      let configs = {}

      torCodes.forEach((code) => (configs[code] = getSensorConfig(code)))
      torCodes.forEach((code) => {
        let sensorConfig = configs[code]

        //Clear previous colored gps positions

        drawSensorMarkersFromData.apply(this, [
          [],
          {
            name: `multiple_tors_${code}`.toLowerCase(),
          },
        ])
        drawSensorMarkersFromData.apply(this, [
          [],
          {
            name: code,
          },
        ])

        if (!sensorConfig) {
          return
        }

        let filteredItems = this.vehiclePositionMarkers.filter(
          (i) => i.code === code
        )

        //Tor 1..8 also contains Tor+ positions containing Tor 1..8 sensors
        filteredItems = filteredItems.concat(
          this.vehiclePositionMarkers.filter(
            (p) =>
              p.code === 'TOR+' &&
              !!p.code_capteurs.find((torCode) => torCode == code)
          )
        )

        if (code === 'TOR+') {
          //Multiple activations of TORs will create a marker per valid TOR
          let multipleSensors = {}
          filteredItems.forEach((item) => {
            item.code_capteurs.forEach((torCode) => {
              if (configs[torCode]) {
                multipleSensors[torCode] = multipleSensors[torCode] || []
                multipleSensors[torCode].push(item)
              }
            })
          })
          Object.keys(multipleSensors).forEach((torCode) => {
            drawSensorMarkersFromData.apply(this, [
              multipleSensors[torCode],
              {
                name: `multiple_tors_${torCode}`.toLowerCase(),
                //Color will be the color from TOR+ (Black)
                color: configs[code].color,
                //i.g: TOR1 will be visible if TOR1 layer is enabled or if TOR+ layer is enabled
                visible: shouldRenderMarkersFromType(configs[torCode]), //||  shouldRenderMarkersFromType(configs["TOR+"]),
              },
            ])
          })
        } else {
          //Normal TORs (1,2,3,4,5,6,7,8) goes here
          drawSensorMarkersFromData.apply(this, [
            filteredItems,
            {
              name: code,
              color: sensorConfig.color,
              visible: shouldRenderMarkersFromType(sensorConfig),
            },
          ])
        }
      })
    },
  },
}

function getDrawGeometriesPopupOptions(options = {}) {
  return {
    popupOptions: {
      style: 'min-width:267px;',
    },
    popup:
      options.popup ||
      (() =>
        /* webpackChunkName "shared_components" */
        import('./PositionMarkerPopup.vue')),
  }
}

function getPositionsClusterIcon(cluster, options = {}) {
  let style = `${
    options.backgroundColor
      ? 'background: ' + options.backgroundColor + ';'
      : ''
  }${options.color ? 'color: ' + options.color + ';' : ''}${
    options.borderColor ? 'border: 2px solid ' + options.borderColor + ';' : ''
  }`
  return L.divIcon({
    className: 'cluster_marker',
    html: `<div class="cluster_position_marker" style="${style}">${cluster.getChildCount()}</div>`,
  })
}

/**
 * Border color: same as trip history polyline if available
 *
 */
function getPositionBorderColor() {
  let color = colors.color_main

  let singlePolyline =
    (this.$store.getters['location_module/selectedItem'] || {})
      ?.tripHistoryPolyline || {}
  if (singlePolyline instanceof Array) {
    singlePolyline = singlePolyline[0]
  }

  if (singlePolyline.color) {
    color = singlePolyline.color
  }

  return color
}

/**
 * Draw common position markers (without sensor data)
 * Border: Same as trip history
 * Fill: #cccccc (Same as V2)
 */
function drawPositionsMarkersFromData(positions = [], options = {}) {
  let color = getPositionBorderColor.apply(this)

  positions = positions.filter(validPositionsFilter).map((pp) => {
    let p = { ...pp }
    p.lat = p.lat || p.latitude || p.latitude_mapmatch
    p.lng = p.lng || p.lon || p.longitude || p.longitude_mapmatch
    p.radius = 3
    p.color = color
    p.fillColor = '#cccccc'
    p.weight = 2
    return p
  })

  let visible = this.showPositionMarkersByDefault

  //Update visible if layer is toggle off in map options
  if (!this.mapOptions.positions) {
    visible = false
  }

  this.$refs.map.drawCircles(positions, {
    ...options,
    /*clusterOptions: {
      iconCreateFunction: function (cluster) {
        return getPositionsClusterIcon(cluster, {
          borderColor: color,
        });
      },
    },*/
    layer: options.layer || 'positions',
    zIndex: options.zIndex || 5,
    type: 'positions',
    visible,
    ...getDrawGeometriesPopupOptions(options),
    after() {
      options.after && options.after.apply(this, arguments)
    },
  })
}

/**
 * There might be data with invalid lat/lng, which should be ignored
 * @todo Move to services/utils
 * @memberof SimplicitiMapComponent
 * @returns {Boolean}
 */
function validPositionsFilter(p) {
  return (
    (!!p.lat || !!p.latitude_mapmatch) &&
    (!!p.lng || !!p.lon || !!p.longitude_mapmatch)
  )
}

/**
 * Draw sensor markers (positions with sensor data) using LeafletMap::drawGeometries
 * Border: Trip history polyline color
 * Fill: Sensor color
 */
function drawSensorMarkersFromData(data = [], options = {}) {
  let self = this
  /*
  if (data.length === 0) {
    this.$refs.map.clearLayers &&
      this.$refs.map.clearLayers(`positions_${options.name}`)
    return
  }*/

  data = data.map((item) => {
    item.lng = item.lng || item.lon
    return item
  })

  const drawOptions = {
    ...options,
    leafletType: 'marker',
    visible: options.visible || false,
    type: `positions_${options.name}`,
    layer: `positions_${options.name}`,
    zIndex: options.zIndex || 5,
    /*clusterOptions: {
      iconCreateFunction: function (cluster) {
        return getPositionsClusterIcon(cluster, {
          borderColor: options.color,
        });
      },
    },*/
    data,
    ...getDrawGeometriesPopupOptions(options),
    generate(item) {
      try {
        const circle = L.circle([item.lat, item.lng], {
          color: getPositionBorderColor.apply(self),
          fillColor: options.color,
          fillOpacity: 1,
          radius: 3,
        })
        return circle
      } catch (err) {
        console.error({
          err,
          item,
        })
        return null
      }
    },
  }

  this.$refs.map.drawGeometries(drawOptions)
}