Structures.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <LayoutContainer>
  3. <v-responsive :aspect-ratio="1" width="100%">
  4. <div id="map" />
  5. </v-responsive>
  6. <div class="advice">
  7. {{ $t("click_on_land_to_go_there") }}
  8. </div>
  9. <v-row class="map-shortcuts">
  10. <v-col v-for="shortcut in shortcuts" :key="shortcut.src" cols="2">
  11. <div @click="setMapBounds(shortcut.bounds)">
  12. <nuxt-img
  13. :src="shortcut.src"
  14. :alt="shortcut.alt"
  15. />
  16. </div>
  17. </v-col>
  18. </v-row>
  19. </LayoutContainer>
  20. </template>
  21. <script>
  22. import 'leaflet/dist/leaflet.css'
  23. let L
  24. if (typeof window !== 'undefined') {
  25. // only require leaflet on client-side
  26. L = require('leaflet')
  27. }
  28. export default {
  29. props: {
  30. structures: {
  31. type: Array,
  32. required: false,
  33. default: () => []
  34. }
  35. },
  36. data () {
  37. return {
  38. map: null,
  39. defaultBounds: [[51.03, -5.78], [41.2, 9.70]],
  40. shortcuts: [
  41. { src: '/images/metropole.png', alt: 'Metropole', bounds: [[51.03, -5.78], [41.2, 9.70]] },
  42. { src: '/images/guadeloupe.png', alt: 'Guadeloupe', bounds: [[16.62, -62.03], [15.74, -60.97]] },
  43. { src: '/images/martinique.png', alt: 'Martinique', bounds: [[14.95, -61.43], [14.28, -60.60]] },
  44. { src: '/images/mayotte.png', alt: 'Mayotte', bounds: [[-12.51, 44.86], [-13.19, 45.45]] },
  45. { src: '/images/la_reunion.png', alt: 'La Réunion', bounds: [[-20.65, 54.92], [-21.65, 56.15]] },
  46. { src: '/images/guyane.png', alt: 'Guyane', bounds: [[6.24, -54.62], [1.87, -50.59]] }
  47. ],
  48. zoomRequired: false
  49. }
  50. },
  51. watch: {
  52. structures (oldval, newval) {
  53. if (oldval !== newval) {
  54. this.populateMarkers()
  55. }
  56. if (this.zoomRequired) {
  57. this._fitBoundsToMarkers()
  58. this.zoomRequired = false
  59. }
  60. }
  61. },
  62. mounted () {
  63. const options = { scrollWheelZoom: false, zoomSnap: 0.25 }
  64. const defaultCenter = [46.71, 1.94]
  65. const defaultZoom = 5.75
  66. const layerSource = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png'
  67. const attribution = '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
  68. this.map = L.map('map', options)
  69. this.map.setView(defaultCenter, defaultZoom)
  70. L.tileLayer(layerSource, { attribution }).addTo(this.map)
  71. this.populateMarkers()
  72. this.map.on('zoomend moveend', () => {
  73. this.$emit('boundsUpdated', this.map.getBounds())
  74. })
  75. },
  76. beforeDestroy () {
  77. if (this.map) {
  78. this.map.remove()
  79. }
  80. },
  81. methods: {
  82. populateMarkers () {
  83. // remove any existant marker layers
  84. this.map.eachLayer((layer) => {
  85. if (layer instanceof L.MarkerClusterGroup) {
  86. this.map.removeLayer(layer)
  87. }
  88. })
  89. // populate new layer
  90. const clusters = L.markerClusterGroup()
  91. for (const s of this.structures) {
  92. if (!s.latitude || !s.longitude) { continue }
  93. const marker = L.marker([s.latitude, s.longitude])
  94. marker.bindPopup(`<b>${s.name}</b><br/>${s.postalCode} ${s.addressCity}<br/><a href="${s.website}" target="_blank">${s.website}</a>`)
  95. clusters.addLayer(marker)
  96. }
  97. this.map.addLayer(clusters)
  98. },
  99. setMapBounds (bounds) {
  100. this.map.fitBounds(bounds) // << without this, the new bounds may not be properly set when the current view overlaps the new bounds.
  101. },
  102. resetBounds () {
  103. this.setMapBounds(this.defaultBounds)
  104. },
  105. zoomOnResults () {
  106. this.zoomRequired = true
  107. },
  108. _fitBoundsToMarkers () {
  109. const coords = this.structures.filter(
  110. (s) => { return s.latitude && s.longitude }
  111. ).map(
  112. (s) => { return [s.latitude, s.longitude] })
  113. if (!coords.length > 0) {
  114. return
  115. }
  116. this.setMapBounds(coords)
  117. }
  118. }
  119. }
  120. </script>
  121. <style scoped>
  122. #map {
  123. height: 100%;
  124. width: 100%;
  125. }
  126. .advice {
  127. margin: 1rem 0;
  128. color: #262626;
  129. font-weight: 750;
  130. text-align: center;
  131. font-size: 0.9rem;
  132. width: 100%;
  133. }
  134. .map-shortcuts .col {
  135. padding: 12px 6px;
  136. }
  137. .map-shortcuts img {
  138. border: solid 1px #000;
  139. width: 75px;
  140. height: 75px;
  141. }
  142. </style>