Source

components/shared/geocoding/RoutingFeature/RoutingFeature.vue

<template lang="pug">
.routing-form-wrapper
  .routing-form-search(v-show="instructions.length===0")
    RoutingCoordsForm(v-model="addresses")
    FormBuilder(
      v-model="formData"
      :sections="sections"
      has-valid-btn
      :loading="isLoading"
      :validBtnDisabled="!isSearchFormValid"
      class="mt-2 form-builder-wrapper"
      @valid="performRouting"
      theme="theme-simpliciti"
    )
      template(v-slot:component_vehicle_feature="slotProps")
        VehicleFeatureForm(v-model="slotProps.formData.vehicle")

      template(v-slot:component_speed_ponderation="slotProps")
        SpeedPonderationForm(v-model="slotProps.formData.speedPonderation") 

  RoutingList(v-show="instructions.length>0" :items="instructions"
    :headerInformation="headerInformation"
    @mode="switchViewMode"
    @selectItem="item=>highlightRoutingInstructionPolyline(item.number)"
    @deselectItem="item=>unhightlightRoutingInstructionPolyline(item.number)"
  )

</template>

<script>
import FormBuilder from '@c/shared/Builders/FormBuilder.vue'
import VehicleFeatureForm from './VehicleFeatureForm.vue'
import SpeedPonderationForm from './SpeedPonderationForm.vue'
import RoutingCoordsForm from '@c/shared/geocoding/RoutingCoordsForm/RoutingCoordsForm.vue'
import geocodingMixin from '@c/shared/geocoding/geocoding-mixin.js'
import mapMixins from '@/mixins/simpliciti-map-mixin.js'
import RoutingList from './RoutingList.vue'
import { transportTypeOptions } from '@/services/geocoding-service.js'

/**
 * @description Routing feature (Calcul d'itinéraire)
 * Note: The Routing feature doesn't have a custom map but instead, it should use the map the user see at that moment.
 * @requires SimplicitiMap Component should be present and alive in the DOM (For table results and markers)
 * @example
 * Test API:
 * vue.$geocoding.routingGeocoding({coordinates:[[3.9060772,43.6028552],[4.1398221,43.5189621
]]}).then(console.warn)
 * API response format: see routing-example.json
  * @namespace components
 * @category components
 * @subcategory shared/geocoding/routing
 * @module RoutingFeature
 */
export default {
  name: 'RoutingFeature',
  components: {
    SpeedPonderationForm,
    VehicleFeatureForm,
    FormBuilder,
    RoutingCoordsForm,
    RoutingList,
  },
  mixins: [geocodingMixin, mapMixins],
  provide() {
    let self = this
    return {
      handleListBackButtonClick() {
        self.markers = []
        self.instructions = []
        self.headerInformation = {}
        self.instructionsCoordinates = []
        self.addRoutingSearchMarkersToMap()
        self.setSimplicitiMapBottomMenuVMComponent(null)
      },
    }
  },
  inject: {
    mapToolboxContext: {
      default: () => ({ setContentPanelVisible: () => {} }),
    },
  },
  data() {
    return {
      viewMode: 'list',
      isLoading: false,
      addresses: [
        {
          key: 'start',
          value: {},
        },
        {
          key: 'end',
          value: {},
        },
      ],
      instructionsCoordinates: [],
      instructions: [],
      markers: [],
      headerInformation: {},
      formData: {
        no_ferries_routing: false,
        routing_locomotion: false,
        no_highways_routing: false,
        short_routing: false,
        no_tolls_routing: false,
        transportType: 'car',
        fastest: true,
        vehicle: {},
        speedPonderation: {},
      },
      sections: [
        {
          name: 'routing_start_section',
          header: false,
          collapsable: false,
          rowsClass: 'mb-1',
          rows: [
            {
              columns: [
                {
                  label: 'transportType',
                  title: 'geocoding.routing.transportType',
                  type: 'select',
                  value: 'car',
                  options: transportTypeOptions,
                },
              ],
            },
            {
              columns: [
                {
                  label: 'shortest',
                  title: 'geocoding.routing.shortest',
                  type: 'checkbox',
                  value: false,
                },
                {
                  label: 'fastest',
                  title: 'geocoding.routing.fastest',
                  type: 'checkbox',
                  value: true,
                },
              ],
            },
            {
              id: 'routing_checkbox_row_2',
              columns: [
                {
                  label: 'avoidMotorways',
                  title: 'geocoding.routing.avoidMotorways',
                  type: 'checkbox',
                  value: false,
                },
                {
                  label: 'avoidTolls',
                  title: 'geocoding.routing.avoidTolls',
                  type: 'checkbox',
                  value: false,
                },
              ],
            },
            {
              columns: [
                {
                  label: 'avoidFerries',
                  title: 'geocoding.routing.avoidFerries',
                  type: 'checkbox',
                  value: false,
                },
              ],
            },
            {
              columns: [
                {
                  label: 'vehicle_feature',
                  type: 'component',
                  disableFlyingLabel: true,
                },
              ],
            },
            {
              id: 'speed_ponderation_row',
              columns: [
                {
                  label: 'speed_ponderation',
                  type: 'component',
                  disableFlyingLabel: true,
                },
              ],
            },
          ],
        },
      ],
    }
  },
  computed: {
    isSearchFormValid() {
      return (
        this.addresses.filter((a) => a.value && !!a.value.lat && !!a.value.lng)
          .length >= 2
      )
    },
    computedSections() {
      return [
        /* ...[{
          name:"routing_from_section", header:false, collapsable:false,
          rows: this.fromArray.map(item=>{
            return {}
          })
        }],*/
        ...this.sections,
      ]
    },
  },
  watch: {
    addresses: {
      handler() {
        this.addRoutingSearchMarkersToMap()
      },
      deep: true,
      immediate: true,
    },
    instructions: {
      handler() {
        this.addRoutingInstructionsPolylines([
          //A base polyline will cover the entire trip (API response.linestring attribute)
          ...[
            {
              id: 'base',
              coordinates: this.instructionsCoordinates,
            },
          ],
          ...this.instructions,
        ])
      },
    },
    markers: {
      handler() {
        this.addRoutingResultMarkers(this.markers)
      },
    },
  },
  mounted() {
    this.mapClearMarkersFromLayer('routingSearchMarkers')
    this.mapClearMarkersFromLayer('routingInstructionsPolylines')
    this.mapClearMarkersFromLayer('routingResultsMarkers')
    this.setSimplicitiMapBottomMenuVMComponent(null)
  },
  destroyed() {
    this.mapClearMarkersFromLayer('routingSearchMarkers')
    this.mapClearMarkersFromLayer('routingInstructionsPolylines')
    this.mapClearMarkersFromLayer('routingResultsMarkers')
    this.setSimplicitiMapBottomMenuVMComponent(null)
  },
  methods: {
    setSimplicitiMapBottomMenuVMComponent(component) {
      let vm = this.$map.getSimplicitiMapBottomMenuVM()
      if (vm) {
        vm.component = component
      }
    },
    setSimplicitiMapBottomMenuVMFullScreen(isFullscreen) {
      let vm = this.$map.getSimplicitiMapBottomMenuVM()
      if (vm) {
        vm.isFullscreen = isFullscreen
      }
    },
    switchViewMode(mode) {
      let self = this
      self.viewMode = mode

      if (['table', 'table_map'].includes(mode)) {
        this.setSimplicitiMapBottomMenuVMComponent({
          name: 'RoutingTableWrapper',
          template: `<RoutingTable :items="items" 
            :headerText="headerText"
            :mode="mode" @mode="switchViewMode"/>`,
          components: {
            RoutingTable: () =>
              import('@c/shared/geocoding/RoutingFeature/RoutingTable.vue'),
          },
          computed: {
            mode() {
              return self.viewMode
            },
            items() {
              return self.instructions
            },
            headerText() {
              return `Durée : ${
                self.headerInformation.duration || '...'
              } Distance : ${
                self.headerInformation.length || '...'
              } Vitesse : ${self.headerInformation.speed || '...'}`
            },
          },
          methods: {
            switchViewMode(mode) {
              self.switchViewMode(mode)
            },
          },
        })
        if (mode === 'table') {
          this.setSimplicitiMapBottomMenuVMFullScreen(true)
        } else {
          this.setSimplicitiMapBottomMenuVMFullScreen(false)
        }
        this.mapToolboxContext.setContentPanelVisible(false)
      } else {
        this.mapToolboxContext.setContentPanelVisible(true)
        this.setSimplicitiMapBottomMenuVMComponent(null)
      }
    },
    async addRoutingSearchMarkersToMap() {
      let markers = this.addresses
        .filter(
          (item) =>
            Object.keys(item.value).length > 0 &&
            !!item.value.lat &&
            !!item.value.lng
        )
        .map((item) => item.value)
      if (markers.length > 0) {
        this.addRoutingSearchMarkers(markers)
      } else {
        this.mapClearMarkersFromLayer('routingSearchMarkers')
      }
    },
    async performRouting(event) {
      this.$loader.show()
      let res = await this.$geocoding.routingGeocoding({
        ...this.formData,
        coordinates: this.addresses
          .filter((i) => i.value && !!i.value.lat && !!i.value.lng)
          .map((i) => [i.value.lng, i.value.lat]),
      })
      this.instructionsCoordinates = res.instructionsCoordinates
      this.instructions = res.instructions || []
      this.markers = res.markers || []
      this.headerInformation = {
        length: res.lengthFormatted,
        duration: res.durationFormatted,
        speed: res.averageSpeedFormatted,
      }
      this.$loader.hide()
    },
  },
}
</script>

<style lang="scss" scoped>
.form-builder-wrapper {
  font-size: 12px;
}
</style>