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.
 
 
 
 
 
 

479 lines
12 KiB

<template>
<div id="map-wrap">
<!-- <pre style="z-index: 500; position: fixed; right: 0;">
airportsOrig: {{ airportsOrig.length }}
airportsDest: {{ airportsDest.length }}
selectedOrig: {{ selectedOrig.key }}
selectedDest: {{ selectedDest.key }}
</pre> -->
<client-only>
<l-map ref="flMap" v-bind="map">
<v-marker-cluster v-if="!selectedOrigNorm.key">
<l-marker
v-for="airport in airportsOrig"
:key="airport.iata"
:lat-lng="[airport.lat, airport.long]"
>
<l-icon
:icon-anchor="[16, 37]"
class-name="airport-icon orig-icon unselected"
>
<div class="map__icon-muni">
{{ airport.municipality }} ►
</div>
</l-icon>
<l-popup class="map__popup">
<div class="map__popup-iata">
{{ airport.iata }}
</div>
<div class="map__popup-muni">
{{ airport.municipality }}
</div>
<div class="map__popup-buttons">
<button class="btn btn--primary btn--map" @click="$emit('make-origin', airport)">
start here ►
</button>
</div>
</l-popup>
</l-marker>
</v-marker-cluster>
<v-marker-cluster v-if="selectedOrigNorm.key && !selectedDestNorm.key">
<l-marker
v-for="airport in airportsDest"
:key="airport.iata"
:lat-lng="[airport.lat, airport.long]"
>
<l-icon
:icon-anchor="[16, 37]"
class-name="airport-icon dest-icon unselected"
>
<div class="map__icon-muni">
{{ airport.municipality }} ■
</div>
</l-icon>
<l-popup class="map__popup">
<div class="map__popup-iata">
{{ airport.iata }}
</div>
<div class="map__popup-muni">
{{ airport.municipality }}
</div>
<div class="map__popup-buttons">
<button
class="btn btn--primary btn--map"
@click="$emit('make-destination', airport)"
>
land here ■
</button>
<button class="btn btn--secondary btn--map" @click="$emit('make-origin', airport)">
start here ►
</button>
</div>
</l-popup>
</l-marker>
</v-marker-cluster>
<l-marker v-if="selectedOrigNorm.key" v-bind="selectedOrigNorm">
<l-icon
:icon-anchor="[16, 37]"
class-name="airport-icon orig-icon selected"
>
<div class="map__icon-muni">
{{ selectedOrig.municipality }}
</div>
</l-icon>
<l-popup class="map__popup">
<div class="map__popup-iata">
{{ selectedOrig.iata }}
</div>
<div class="map__popup-muni">
{{ selectedOrig.municipality }}
</div>
<div class="map__popup-buttons">
<button class="btn btn--cancel btn--map" @click="$emit('clear-origin')">
remove
</button>
</div>
</l-popup>
</l-marker>
<l-marker v-if="selectedDestNorm.key" v-bind="selectedDestNorm">
<l-icon
:icon-anchor="[16, 37]"
class-name="airport-icon dest-icon selected"
>
<div class="map__icon-muni">
{{ selectedDest.municipality }}
</div>
</l-icon>
<l-popup class="map__popup">
<div class="map__popup-iata">
{{ selectedDest.iata }}
</div>
<div class="map__popup-muni">
{{ selectedDest.municipality }}
</div>
<div class="map__popup-buttons">
<button class="btn btn--cancel btn--map" @click="$emit('clear-destination')">
remove
</button>
</div>
</l-popup>
</l-marker>
<l-polyline v-if="selectedOrigNorm.key && selectedDestNorm.key" :lat-lngs="[selectedOrigNorm['lat-lng'],selectedDestNorm['lat-lng']]" color="#007fff" />
<!-- <l-tile-layer v-bind="tileLayer" :url="mapBoxUrl" /> -->
<!-- <l-tile-layer :url="osmUrl" :attribution="osmAttribution" /> -->
<l-tile-layer v-bind="tileLayer" />
</l-map>
</client-only>
</div>
</template>
<script>
export default {
props: {
airportsOrig: {
type: [Array, Object],
default () {
return []
}
},
airportsDest: {
type: [Array, Object],
default () {
return []
}
},
selectedOrig: {
type: [Object],
default () {
return {}
}
},
selectedDest: {
type: [Object],
default () {
return {}
}
}
},
data () {
return {
map: {
zoom: 3,
minZoom: 3,
maxZoom: 13,
zoomSnap: 0.1,
center: {
lat: 63.8333,
lng: -152
},
bounds: [
[
71.0394374664382,
-178.4706617597655
],
[
50.44556513009691,
-127.25240004101528
]
]
},
mapBoxAccessToken: 'pk.eyJ1IjoiZmx5bG9jYWwiLCJhIjoiY2t0OHUxZXB6MTVueTJ4cGVwOHRuc2s2NyJ9.YF9frLvISHfOuT7nqs3TNg',
mapBoxAttribution: 'FlyLocal | Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
osmUrl: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
osmAttribution:
'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
mapBoxUrl: 'https://api.mapbox.com/styles/v1/flylocal/ckt9x8aho0igb18mmm9ks2fsv/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoiZmx5bG9jYWwiLCJhIjoiY2t0OHUxZXB6MTVueTJ4cGVwOHRuc2s2NyJ9.YF9frLvISHfOuT7nqs3TNg',
mapBinder: {
}
}
},
computed: {
selectedOrigNorm () {
return {
key: this.selectedOrig.iata,
iata: this.selectedOrig.iata,
'lat-lng': [this.selectedOrig.lat, this.selectedOrig.long],
municipality: this.selectedOrig.municipality
}
},
selectedDestNorm () {
return {
key: this.selectedDest.iata,
iata: this.selectedDest.iata,
'lat-lng': [this.selectedDest.lat, this.selectedDest.long],
municipality: this.selectedDest.municipality
}
},
mapBoxApiUrl () {
return `https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=${this.mapBoxAccessToken}`
},
tileLayer () {
return {
url: 'https://api.mapbox.com/styles/v1/flylocal/ckt9x8aho0igb18mmm9ks2fsv/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoiZmx5bG9jYWwiLCJhIjoiY2t0OHUxZXB6MTVueTJ4cGVwOHRuc2s2NyJ9.YF9frLvISHfOuT7nqs3TNg',
maxZoom: 18,
id: 'flylocal/ckt9x8aho0igb18mmm9ks2fsv',
tileSize: 256,
zoomOffset: -1,
token: 'pk.eyJ1IjoiZmx5bG9jYWwiLCJhIjoiY2t0OHUxZXB6MTVueTJ4cGVwOHRuc2s2NyJ9.YF9frLvISHfOuT7nqs3TNg',
attribution: this.mapBoxAttribution
}
}
},
updated () {
this.setBounds()
// const o = this.airportsOrig.map((m) => { return [m.lat, m.long] })
// const d = this.airportsDest.map((m) => { return [m.lat, m.long] })
// const so = [this.selectedOrig.lat, this.selectedOrig.long]
// const sd = [this.selectedDest.lat, this.selectedDest.long]
// this.$refs.flMap.mapObject.fitBounds([...o, ...d, ...so, ...sd])
// this.$refs.flMap.mapObject.fitBounds([[40, -73], [5, -55]])
},
methods: {
resize () {
this.$refs.flMap.mapObject.invalidateSize()
},
setBounds () {
if (this.$refs.flMap && this.$refs.flMap.mapObject) {
/* selected none */
if (!this.selectedOrigNorm.key && !this.selectedDestNorm.key && this.airportsOrig.length > 0) {
const o = this.airportsOrig.map((m) => { return [m.lat, m.long] })
// console.log('origins:')
// console.log(o)
if (o) {
this.$refs.flMap.mapObject.fitBounds([...o], { padding: [50, 70] })
}
}
/* selected org */
if (this.selectedOrigNorm.key && !this.selectedDestNorm.key) {
const so = [this.selectedOrig.lat, this.selectedOrig.long]
const d = this.airportsDest.map((m) => { return [m.lat, m.long] })
// console.log('so:')
// console.log(so)
// console.log('dests:')
// console.log(d)
if (so && d) {
this.$refs.flMap.mapObject.fitBounds([so, ...d], { padding: [50, 70] })
}
}
/* selected both */
if (this.selectedOrigNorm.key && this.selectedDestNorm.key) {
const so = [this.selectedOrig.lat, this.selectedOrig.long]
const sd = [this.selectedDest.lat, this.selectedDest.long]
// console.log('so:')
// console.log(so)
// console.log('sd:')
// console.log(sd)
if (so && sd) {
this.$refs.flMap.mapObject.fitBounds([so, sd], { padding: [50, 70] })
}
}
}
}
}
}
</script>
<style lang="css" >
:root {
--map-shadow: 5px 3px 10px rgb(0 0 0 / 20%);
}
#map-wrap {
height: 0;
flex: 1 0 auto;
}
.map__icon-muni {
white-space: nowrap;
}
.map__popup {
font-size: 0.9rem;
}
.map__popup-iata {
white-space: nowrap;
font-size: 3em;
line-height: 1;
}
.map__popup-muni {
white-space: nowrap;
margin-bottom: 0.5rem;
}
.map__popup-buttons {
display: flex;
flex-direction: row;
gap: 0.5em;
}
.leaflet-container {
font-size: 1rem;
}
.leaflet-popup {
top: 0 !important;
left: 0 !important;
}
.leaflet-popup-content-wrapper {
border-radius: 0.2rem 1.5rem 1.5rem 1.5rem;
box-shadow: var(--map-shadow);
}
.leaflet-popup-tip-container,
.leaflet-popup-close-button {
display: none;
}
.leaflet-popup-content {
margin: 0.75rem;
}
/* TODO: change these? */
.leaflet-top {
top: auto;
bottom: 0;
}
.leaflet-top .leaflet-control {
margin-top: auto;
margin-bottom: 10px;
}
.airport-icon {
padding: 0.55rem;
border-radius: 0.2rem 1.5rem 1.5rem 1.5rem;
box-shadow: var(--map-shadow);
text-align: center;
width: auto !important;
height: auto !important;
margin: 0 !important;
border: 0.2rem solid transparent;
transition: all 0.2s;
}
.airport-icon.orig-icon {
background-color: white;
color: black;
}
.airport-icon.orig-icon:hover {
border-color: #007fff;
}
.airport-icon.dest-icon {
background-color: #ddd;
color: black;
}
.airport-icon.orig-icon.selected {
border-color: #007fff;
}
.airport-icon.dest-icon:hover {
background-color: #007fff;
color: white;
}
.airport-icon.dest-icon.selected {
background-color: #007fff;
color: white;
}
/* .marker-cluster {
background-clip: padding-box;
border-radius: 20px;
} */
.marker-cluster div {
/* width: 30px;
height: 30px;
margin-left: 5px;
margin-top: 5px;
text-align: center;
border-radius: 15px;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif; */
}
/* .marker-cluster span {
line-height: 30px;
} */
#map-wrap .marker-cluster-small {
background-color: rgba(47, 151, 255, 0.15);
}
#map-wrap .marker-cluster-small div {
background-color: rgba(0, 127, 255, 0.15);
}
#map-wrap .marker-cluster-medium {
background-color: rgba(47, 151, 255, 0.5);
}
#map-wrap .marker-cluster-medium div {
background-color: rgba(0, 127, 255, 0.5);
}
#map-wrap .marker-cluster-large {
background-color: rgba(47, 151, 255, 0.9);
}
#map-wrap .marker-cluster-large div {
background-color: rgba(0, 127, 255, 0.9);
}
.btn {
border-radius: 999px;
padding: 0.5em 0.8em;
font-size: min(1rem, 18px);
white-space: nowrap;
transition: 0.2s all;
display: flex;
align-items: center;
justify-content: space-around;
gap: 0.25rem;
line-height: 1;
min-height: 1em;
}
.btn svg {
height: min(1rem, 18px);
display: inline-block;
aspect-ratio: 1;
}
.btn.btn--primary {
background: #007fff;
color: #fff;
}
.btn.btn--primary:hover {
background: #00ca00;
color: #fff;
}
.btn.btn--cancel {
background: red;
color: #fff;
}
.btn.btn--cancel:hover {
background: rgb(255, 83, 83);
color: #fff;
}
.btn.btn--secondary {
background: #ccc;
color: #000;
}
.btn.btn--secondary:hover {
background: rgb(151, 151, 151);
color: #000;
}
</style>