You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
13 KiB

  1. <template>
  2. <div id="map-wrap">
  3. <client-only>
  4. <l-map ref="myMap" v-bind="map">
  5. <v-marker-cluster v-if="!selectedOrigin.key">
  6. <l-marker
  7. v-for="airport in airports_orig"
  8. :key="airport.iata"
  9. :lat-lng="[airport.lat, airport.long]"
  10. >
  11. <l-icon
  12. :icon-anchor="[16, 37]"
  13. class-name="airport-icon orig-icon unselected"
  14. >
  15. <div class="map__icon-muni">
  16. {{ airport.municipality }}
  17. </div>
  18. </l-icon>
  19. <l-popup class="map__popup">
  20. <div class="map__popup-iata">
  21. {{ airport.iata }}
  22. </div>
  23. <div class="map__popup-muni">
  24. {{ airport.municipality }}
  25. </div>
  26. <div class="map__popup-buttons">
  27. <button class="btn btn--primary btn--map" @click="makeOrigin(airport)">
  28. start here
  29. </button>
  30. </div>
  31. </l-popup>
  32. </l-marker>
  33. </v-marker-cluster>
  34. <v-marker-cluster v-if="selectedOrigin.key && !selectedDestination.key">
  35. <l-marker
  36. v-for="airport in airports_dest"
  37. :key="airport.iata"
  38. :lat-lng="[airport.lat, airport.long]"
  39. >
  40. <l-icon
  41. :icon-anchor="[16, 37]"
  42. class-name="airport-icon dest-icon unselected"
  43. >
  44. <div class="map__icon-muni">
  45. {{ airport.municipality }}
  46. </div>
  47. </l-icon>
  48. <l-popup class="map__popup">
  49. <div class="map__popup-iata">
  50. {{ airport.iata }}
  51. </div>
  52. <div class="map__popup-muni">
  53. {{ airport.municipality }}
  54. </div>
  55. <div class="map__popup-buttons">
  56. <button
  57. class="btn btn--primary btn--map"
  58. @click="makeDestination(airport)"
  59. >
  60. land here
  61. </button>
  62. <button class="btn btn--secondary btn--map" @click="makeOrigin(airport)">
  63. start here
  64. </button>
  65. </div>
  66. </l-popup>
  67. </l-marker>
  68. </v-marker-cluster>
  69. <l-marker v-if="selectedOrigin.key" v-bind="selectedOrigin">
  70. <l-icon
  71. :icon-anchor="[16, 37]"
  72. class-name="airport-icon orig-icon selected"
  73. >
  74. <div class="map__icon-muni">
  75. {{ selectedOrigin.municipality }}
  76. </div>
  77. </l-icon>
  78. <l-popup class="map__popup">
  79. <div class="map__popup-iata">
  80. {{ selectedOrigin.iata }}
  81. </div>
  82. <div class="map__popup-muni">
  83. {{ selectedOrigin.municipality }}
  84. </div>
  85. <div class="map__popup-buttons">
  86. <button class="btn btn--cancel btn--map" @click="clearOrigin()">
  87. remove
  88. </button>
  89. </div>
  90. </l-popup>
  91. </l-marker>
  92. <l-marker v-if="selectedDestination.key" v-bind="selectedDestination">
  93. <l-icon
  94. :icon-anchor="[16, 37]"
  95. class-name="airport-icon dest-icon selected"
  96. >
  97. <div class="map__icon-muni">
  98. {{ selectedDestination.municipality }}
  99. </div>
  100. </l-icon>
  101. <l-popup class="map__popup">
  102. <div class="map__popup-iata">
  103. {{ selectedDestination.iata }}
  104. </div>
  105. <div class="map__popup-muni">
  106. {{ selectedDestination.municipality }}
  107. </div>
  108. <div class="map__popup-buttons">
  109. <button class="btn btn--cancel btn--map" @click="clearDestination()">
  110. remove
  111. </button>
  112. </div>
  113. </l-popup>
  114. </l-marker>
  115. <!-- <l-polyline :lat-lngs="polyline.latlngs" :color="polyline.color" /> -->
  116. <!-- <l-tile-layer v-bind="tileLayer" :url="mapBoxUrl" /> -->
  117. <l-tile-layer :url="osmUrl" :attribution="osmAttribution" />
  118. </l-map>
  119. </client-only>
  120. </div>
  121. </template>
  122. <script>
  123. export default {
  124. data () {
  125. return {
  126. map: {
  127. zoom: 3,
  128. minZoom: 3,
  129. maxZoom: 13,
  130. center: {
  131. lat: 63.8333,
  132. lng: -152
  133. },
  134. bounds: [
  135. [
  136. 71.0394374664382,
  137. -178.4706617597655
  138. ],
  139. [
  140. 50.44556513009691,
  141. -127.25240004101528
  142. ]
  143. ]
  144. },
  145. mapBoxAccessToken: 'pk.eyJ1IjoiZmx5bG9jYWwiLCJhIjoiY2t0OHUxZXB6MTVueTJ4cGVwOHRuc2s2NyJ9.YF9frLvISHfOuT7nqs3TNg',
  146. mapBoxAttribution: 'FlyLocal | Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
  147. osmUrl: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
  148. osmAttribution:
  149. '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
  150. mapBoxUrl: 'https://api.mapbox.com/styles/v1/flylocal/ckt9x8aho0igb18mmm9ks2fsv/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoiZmx5bG9jYWwiLCJhIjoiY2t0OHUxZXB6MTVueTJ4cGVwOHRuc2s2NyJ9.YF9frLvISHfOuT7nqs3TNg',
  151. // address: {
  152. // long: '',
  153. // display: ''
  154. // },
  155. // polyline: {
  156. // color: 'red',
  157. // latlngs: []
  158. // },
  159. airports_orig: [],
  160. airports_dest: [],
  161. airportFetch_filterFormula: 'AND(Is_Origin=1,{IsCurrent-AsOrigin}=\'Yes\')',
  162. airportFetch_fields: [
  163. 'Airport_IATA',
  164. 'Icon_Url',
  165. 'Latitude_Deg',
  166. 'Longitude_Deg',
  167. 'Municipality',
  168. 'Type'
  169. ],
  170. airportFetch_sort: '&sort[0][field]=Airport_IATA&sort[0][direction]=asc',
  171. selectedOrigin: {
  172. key: '',
  173. iata: '',
  174. 'lat-lng': [],
  175. municipality: ''
  176. },
  177. selectedDestination: {
  178. key: '',
  179. iata: '',
  180. 'lat-lng': [],
  181. municipality: ''
  182. }
  183. }
  184. },
  185. async fetch () {
  186. let response2 = {}
  187. let mapData2 = {}
  188. const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${this.airportFetch_filterFormula}${this.airportFetch_fields_string}${this.airportFetch_sort}`, {
  189. method: 'GET',
  190. headers: {
  191. 'Content-Type': 'application/x-www-form-urlencoded',
  192. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  193. }
  194. })
  195. if (await response.offset) {
  196. response2 = fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${this.airportFetch_filterFormula}&fields[]=Airport_IATA&fields[]=Icon_Url&fields[]=Latitude_Deg&fields[]=Longitude_Deg&sort[0][field]=Airport_IATA&sort[0][direction]=asc&offset=${response.offset}`, {
  197. method: 'GET',
  198. headers: {
  199. 'Content-Type': 'application/x-www-form-urlencoded',
  200. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  201. }
  202. })
  203. mapData2 = await response2.json()
  204. }
  205. const mapData = await response.json()
  206. const r1 = mapData.records
  207. const r2 = mapData2.records
  208. console.log(r2)
  209. // const mapDataTotal = [...r1, ...r2]
  210. const mapDataTotal = [...r1]
  211. this.airports_orig = mapDataTotal.map((record) => {
  212. return {
  213. iata: record.fields.Airport_IATA,
  214. lat: record.fields.Latitude_Deg,
  215. long: record.fields.Longitude_Deg,
  216. icon: record.fields.Icon_Url,
  217. municipality: record.fields.Municipality,
  218. type: record.fields.Type
  219. }
  220. })
  221. },
  222. computed: {
  223. airportFetch_fields_string () {
  224. const vm = this
  225. const array = vm.airportFetch_fields.map((field) => {
  226. return '&fields[]=' + field
  227. })
  228. return array.join('')
  229. },
  230. mapBoxApiUrl () {
  231. return `https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=${this.mapBoxAccessToken}`
  232. },
  233. tileLayer () {
  234. return {
  235. // url: this.mapBoxApiUrl,
  236. maxZoom: 18,
  237. // id: 'flylocal/ckt9x8aho0igb18mmm9ks2fsv',
  238. tileSize: 512,
  239. zoomOffset: -1,
  240. accessToken: 'your.mapbox.access.token',
  241. attribution: this.mapBoxAttribution
  242. }
  243. }
  244. },
  245. methods: {
  246. makeOrigin (airport) {
  247. this.selectedOrigin = {
  248. key: airport.iata,
  249. iata: airport.iata,
  250. 'lat-lng': [airport.lat, airport.long],
  251. municipality: airport.municipality
  252. }
  253. this.fetchDestinations(airport.iata)
  254. },
  255. makeDestination (airport) {
  256. this.selectedDestination = {
  257. key: airport.iata,
  258. iata: airport.iata,
  259. 'lat-lng': [airport.lat, airport.long],
  260. municipality: airport.municipality
  261. }
  262. },
  263. clearOrigin () {
  264. this.selectedOrigin = {
  265. key: '',
  266. iata: '',
  267. 'lat-lng': [],
  268. municipality: ''
  269. }
  270. this.selectedDestination = {
  271. key: '',
  272. iata: '',
  273. 'lat-lng': [],
  274. municipality: ''
  275. }
  276. },
  277. clearDestination () {
  278. this.selectedDestination = {
  279. key: '',
  280. iata: '',
  281. 'lat-lng': [],
  282. municipality: ''
  283. }
  284. },
  285. async fetchDestinations (iata) {
  286. const airportFetchDestfilterFormula = `AND(Is_Destination=1,{IsCurrent-AsDest}="Yes",(FIND("${iata}", Associated_Origin_Search)>0))`
  287. const test = `https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchDestfilterFormula}${this.airportFetch_fields_string}${this.airportFetch_sort}`
  288. console.log(test)
  289. const response = await fetch(`https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchDestfilterFormula}${this.airportFetch_fields_string}${this.airportFetch_sort}`, {
  290. method: 'GET',
  291. headers: {
  292. 'Content-Type': 'application/x-www-form-urlencoded',
  293. Authorization: 'Bearer keyJ2ht64ZSN57AG1'
  294. }
  295. })
  296. const mapData = await response.json()
  297. this.airports_dest = mapData.records.map((record) => {
  298. return {
  299. iata: record.fields.Airport_IATA,
  300. lat: record.fields.Latitude_Deg,
  301. long: record.fields.Longitude_Deg,
  302. icon: record.fields.Icon_Url,
  303. municipality: record.fields.Municipality,
  304. type: record.fields.Type
  305. }
  306. })
  307. }
  308. }
  309. }
  310. </script>
  311. <style lang="css" >
  312. :root {
  313. --map-shadow: 5px 3px 10px rgb(0 0 0 / 20%);
  314. }
  315. #map-wrap {
  316. height: 100vh;
  317. }
  318. .map__icon-muni {
  319. white-space: nowrap;
  320. }
  321. .map__popup {
  322. font-size: 0.9rem;
  323. }
  324. .map__popup-iata {
  325. white-space: nowrap;
  326. font-size: 3em;
  327. line-height: 1;
  328. }
  329. .map__popup-muni {
  330. white-space: nowrap;
  331. margin-bottom: 0.5rem;
  332. }
  333. .map__popup-buttons {
  334. display: flex;
  335. flex-direction: row;
  336. gap: 0.5em;
  337. }
  338. .leaflet-popup {
  339. top: 0 !important;
  340. left: 0 !important;
  341. }
  342. .leaflet-popup-content-wrapper {
  343. border-radius: 0.2rem 1.5rem 1.5rem 1.5rem;
  344. box-shadow: var(--map-shadow);
  345. }
  346. .leaflet-popup-tip-container,
  347. .leaflet-popup-close-button {
  348. display: none;
  349. }
  350. .leaflet-popup-content {
  351. margin: 0.75rem;
  352. }
  353. .airport-icon {
  354. padding: 0.55rem;
  355. border-radius: 0.2rem 1.5rem 1.5rem 1.5rem;
  356. box-shadow: var(--map-shadow);
  357. text-align: center;
  358. width: auto !important;
  359. height: auto !important;
  360. margin: 0 !important;
  361. border: 0.2rem solid transparent;
  362. transition: all 0.2s;
  363. }
  364. .airport-icon.orig-icon {
  365. background-color: white;
  366. color: black;
  367. }
  368. .airport-icon.orig-icon:hover {
  369. border-color: #007fff;
  370. }
  371. .airport-icon.dest-icon {
  372. background-color: #ddd;
  373. color: black;
  374. }
  375. .airport-icon.orig-icon.selected {
  376. border-color: #007fff;
  377. }
  378. .airport-icon.dest-icon:hover {
  379. background-color: #007fff;
  380. color: white;
  381. }
  382. .airport-icon.dest-icon.selected {
  383. background-color: #007fff;
  384. color: white;
  385. }
  386. #map-wrap .marker-cluster-small {
  387. background-color: rgba(47, 151, 255, 0.15);
  388. }
  389. #map-wrap .marker-cluster-small div {
  390. background-color: rgba(0, 127, 255, 0.15);
  391. }
  392. #map-wrap .marker-cluster-medium {
  393. background-color: rgba(47, 151, 255, 0.5);
  394. }
  395. #map-wrap .marker-cluster-medium div {
  396. background-color: rgba(0, 127, 255, 0.5);
  397. }
  398. #map-wrap .marker-cluster-large {
  399. background-color: rgba(47, 151, 255, 0.9);
  400. }
  401. #map-wrap .marker-cluster-large div {
  402. background-color: rgba(0, 127, 255, 0.9);
  403. }
  404. /* .marker-cluster {
  405. background-clip: padding-box;
  406. border-radius: 20px;
  407. }
  408. .marker-cluster div {
  409. width: 30px;
  410. height: 30px;
  411. margin-left: 5px;
  412. margin-top: 5px;
  413. text-align: center;
  414. border-radius: 15px;
  415. font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
  416. }
  417. .marker-cluster span {
  418. line-height: 30px;
  419. } */
  420. .btn {
  421. border-radius: 999px;
  422. padding: 0.4em 0.8em;
  423. font-size: 1rem;
  424. white-space: nowrap;
  425. transition: 0.2s all;
  426. }
  427. .btn.btn--primary {
  428. background: #007fff;
  429. color: #fff;
  430. }
  431. .btn.btn--primary:hover {
  432. background: #72b9ff;
  433. color: #fff;
  434. }
  435. .btn.btn--cancel {
  436. background: red;
  437. color: #fff;
  438. }
  439. .btn.btn--cancel:hover {
  440. background: rgb(255, 83, 83);
  441. color: #fff;
  442. }
  443. .btn.btn--secondary {
  444. background: #ccc;
  445. color: #000;
  446. }
  447. .btn.btn--secondary:hover {
  448. background: rgb(151, 151, 151);
  449. color: #000;
  450. }
  451. /* .btn.btn--map {
  452. //..
  453. } */
  454. </style>