kopia lustrzana https://github.com/manuelkasper/sotlas-frontend
rodzic
4c8ea60dce
commit
e87e76180f
|
@ -1,6 +1,8 @@
|
|||
<template>
|
||||
<div :class="{ 'mapboxgl-ctrl-group': true, 'mapboxgl-ctrl': true, 'mapbox-gl-map-options-container': true }">
|
||||
<button :class="{ 'mapboxgl-ctrl-icon': true, 'mapbox-gl-map-options': true }" type="button" title="Map options" @click="openCloseMapOptions" />
|
||||
<b-tooltip class="info-tooltip" type="is-info" position="is-right" :active="!infoTooltipShown" always animated multilined label="Webcams and more available – open map options to see!">
|
||||
<button :class="{ 'mapboxgl-ctrl-icon': true, 'mapbox-gl-map-options': true }" type="button" title="Map options" @click="openCloseMapOptions" />
|
||||
</b-tooltip>
|
||||
<div v-if="open" class="map-options-container">
|
||||
<div class="map-option">
|
||||
<b-field>
|
||||
|
@ -52,6 +54,11 @@
|
|||
<b-checkbox v-model="mapOptions.inactive" size="is-small" @input="setMapOption('inactive', $event)">Inactive summits</b-checkbox>
|
||||
</b-field>
|
||||
</div>
|
||||
<div class="map-option">
|
||||
<b-field>
|
||||
<b-checkbox v-model="mapOptions.webcams" size="is-small" @input="setMapOption('webcams', $event)"><b-icon pack="fas" icon="cctv" size="is-small" /> Webcams</b-checkbox>
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -59,16 +66,22 @@
|
|||
<script>
|
||||
import moment from 'moment'
|
||||
import mapstyle from '../mixins/mapstyle.js'
|
||||
import prefs from '../mixins/prefs.js'
|
||||
|
||||
const RECENT_SPOT_AGE = 30 * 60 * 1000
|
||||
|
||||
export default {
|
||||
name: 'MapOptionsControl',
|
||||
inject: ['map'],
|
||||
mixins: [mapstyle],
|
||||
mixins: [mapstyle, prefs],
|
||||
prefs: {
|
||||
key: 'mapOptionsControl',
|
||||
props: ['infoTooltipShown']
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
open: false
|
||||
open: false,
|
||||
infoTooltipShown: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
@ -110,6 +123,11 @@ export default {
|
|||
this.updateRecentSpots()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
open () {
|
||||
if (this.open) {
|
||||
this.infoTooltipShown = true
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -160,4 +178,10 @@ export default {
|
|||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.icon {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.b-tooltip {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<font-awesome-icon :icon="['fas', 'camera']" transform="shrink-7" :style="{ color: 'white' }" />
|
||||
</font-awesome-layers>
|
||||
</div>
|
||||
<MglPopup :closeButton="false">
|
||||
<MglPopup :closeButton="false" @added="popupAdded">
|
||||
<div class="thumbwrapper">
|
||||
<img class="thumb" :src="photoSrc(photo, 'thumb')" @click="$emit('photoClicked', photo)" />
|
||||
<div v-if="photo.title" class="caption">{{ photo.title }}</div>
|
||||
|
@ -40,6 +40,9 @@ export default {
|
|||
methods: {
|
||||
markerClicked (e) {
|
||||
e.hitMarker = true
|
||||
},
|
||||
popupAdded (popup) {
|
||||
popup.popup.options.focusAfterOpen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<div>
|
||||
<MglMarker :coordinates="coordinates">
|
||||
<div slot="marker" class="marker-icon" @click="markerClicked">
|
||||
<font-awesome-layers slot="marker" class="fa-2x fa-fw">
|
||||
<font-awesome-icon icon="circle" />
|
||||
<font-awesome-icon :icon="['fas', 'cctv']" transform="shrink-7" :style="{ color: 'white' }" />
|
||||
</font-awesome-layers>
|
||||
<div v-if="webcam.map.clustersize > 1" class="clustersize">+{{ webcam.map.clustersize - 1 }}</div>
|
||||
</div>
|
||||
<MglPopup :closeButton="false" @added="popupAdded">
|
||||
<div :class="['thumbwrapper', size]">
|
||||
<a :href="thumbnailHref" target="_blank"><img class="thumb" :src="thumbnailSrc" /></a>
|
||||
<div class="caption">{{ title }}</div>
|
||||
<template v-if="webcam.map.clustersize > 1 && size != 'is-small'">
|
||||
<div class="clustercaption">+{{ webcam.map.clustersize - 1 }} more webcam{{ webcam.map.clustersize > 2 ? 's' : '' }}</div>
|
||||
<div class="clusterinfo">zoom in to view</div>
|
||||
</template>
|
||||
<div class="attribution">Webcams provided by <a href="https://www.windy.com/" target="_blank">windy.com</a></div>
|
||||
</div>
|
||||
</MglPopup>
|
||||
</MglMarker>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { MglMarker, MglPopup } from 'vue-mapbox'
|
||||
|
||||
export default {
|
||||
name: 'MapWebcam',
|
||||
props: {
|
||||
webcam: Object,
|
||||
size: String
|
||||
},
|
||||
components: {
|
||||
MglMarker, MglPopup
|
||||
},
|
||||
computed: {
|
||||
coordinates () {
|
||||
return [this.webcam.location.longitude, this.webcam.location.latitude]
|
||||
},
|
||||
title () {
|
||||
return this.webcam.title
|
||||
},
|
||||
thumbnailSrc () {
|
||||
return this.webcam.image.daylight.preview
|
||||
},
|
||||
thumbnailHref () {
|
||||
return this.$mq.mobile ? this.webcam.url.current.mobile : this.webcam.url.current.desktop
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
markerClicked (e) {
|
||||
e.hitMarker = true
|
||||
},
|
||||
popupAdded (popup) {
|
||||
popup.popup.options.focusAfterOpen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.marker-icon {
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.clustersize {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 60%;
|
||||
top: 60%;
|
||||
font-size: 0.75rem;
|
||||
background-color: #1759bd;
|
||||
padding: 0 0.3em;
|
||||
border-radius: 1em;
|
||||
color: white;
|
||||
}
|
||||
.marker-icon:hover .clustersize {
|
||||
display: block;
|
||||
}
|
||||
.clustercaption {
|
||||
display: inline-block;
|
||||
font-size: 0.75rem;
|
||||
background-color: #1759bd;
|
||||
padding: 0 0.5em;
|
||||
border-radius: 1em;
|
||||
color: white;
|
||||
margin-top: 0.3em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
.clusterinfo {
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
color: #777;
|
||||
margin-left: 0.5em;
|
||||
font-style: italic;
|
||||
}
|
||||
.thumbwrapper {
|
||||
max-width: min(50vw, 300px);
|
||||
}
|
||||
.thumbwrapper.is-small {
|
||||
max-width: 172px;
|
||||
}
|
||||
.thumb {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.caption {
|
||||
font-size: 0.75rem;
|
||||
margin-top: 0.3rem;
|
||||
line-height: 1.4;
|
||||
color: #555;
|
||||
}
|
||||
.attribution {
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.4;
|
||||
font-style: italic;
|
||||
color: #777;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div>
|
||||
<MapWebcam v-for="webcam in webcams" :key="webcam.id" :webcam="webcam" :size="size" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MapWebcam from '../components/MapWebcam.vue'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'MapWebcams',
|
||||
components: { MapWebcam },
|
||||
inject: ['map'],
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
default: 'is-normal'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setupMap () {
|
||||
if (!this.map || this.setup) {
|
||||
return
|
||||
}
|
||||
|
||||
this.map.on('idle', e => {
|
||||
this.loadWebcams()
|
||||
})
|
||||
this.loadWebcams()
|
||||
this.setup = true
|
||||
},
|
||||
loadWebcams () {
|
||||
// Convert MapBox zoom level to Google Maps like zoom level
|
||||
let mapZoom = Math.floor(Math.min(this.map.getZoom() + 2, 22))
|
||||
let mapBounds = this.map.getBounds().getNorthEast().lat + ',' + this.map.getBounds().getNorthEast().lng + ',' + this.map.getBounds().getSouthWest().lat + ',' + this.map.getBounds().getSouthWest().lng + ',' + mapZoom
|
||||
|
||||
axios.get('https://api.windy.com/api/webcams/v2/map/' + mapBounds, { params: { key: this.windyApiKey, show: 'webcams:location,image,url,map' } })
|
||||
.then(response => {
|
||||
this.webcams = response.data.result.webcams.filter(webcam => { return webcam.status === 'active' })
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
map: {
|
||||
handler () {
|
||||
this.setupMap()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
windyApiKey: 'FIHFGWMrA0iF5Wz4fnBIR8Sb0GRUUeQY',
|
||||
webcams: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -11,6 +11,7 @@
|
|||
<MapRoute v-for="route in routes" :key="route.id" :route="route" />
|
||||
<MapPhoto v-for="photo in mapPhotos" :key="photo.filename" :summit="summit" :photo="photo" @photoClicked="photo => $emit('photoClicked', photo)" />
|
||||
<MapInfoPopup v-if="infoCoordinates !== null" :coordinates="infoCoordinates" @close="infoCoordinates = null" />
|
||||
<MapWebcams v-if="mapOptions.webcams" size="is-small" />
|
||||
<div v-if="zoomWarningVisible" class="zoom-warning">Zoom in to see all activations</div>
|
||||
</MglMap>
|
||||
</template>
|
||||
|
@ -21,6 +22,7 @@ import MapRoute from './MapRoute.vue'
|
|||
import MapPhoto from './MapPhoto.vue'
|
||||
import MapInfoPopup from './MapInfoPopup.vue'
|
||||
import MapEnlargeControl from './MapEnlargeControl.vue'
|
||||
import MapWebcams from './MapWebcams.vue'
|
||||
import mapstyle from '../mixins/mapstyle.js'
|
||||
import utils from '../mixins/utils.js'
|
||||
import longtouch from '../mixins/longtouch.js'
|
||||
|
@ -42,7 +44,7 @@ export default {
|
|||
overviewMap: Boolean
|
||||
},
|
||||
components: {
|
||||
MglMap, MglGeolocateControl, MglNavigationControl, MapEnlargeControl, MglScaleControl, MglAttributionControl, MapRoute, MapPhoto, MapInfoPopup
|
||||
MglMap, MglGeolocateControl, MglNavigationControl, MapEnlargeControl, MglScaleControl, MglAttributionControl, MapRoute, MapPhoto, MapInfoPopup, MapWebcams
|
||||
},
|
||||
mixins: [utils, mapstyle, longtouch],
|
||||
watch: {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<MglPopup v-if="summit" key="summitinfo" :coordinates="[summit.coordinates.longitude, summit.coordinates.latitude]" :showed="true" anchor="bottom" :closeButton="false" @close="$emit('close')">
|
||||
<MglPopup v-if="summit" key="summitinfo" :coordinates="[summit.coordinates.longitude, summit.coordinates.latitude]" :showed="true" anchor="bottom" :closeButton="false" @close="$emit('close')" @added="popupAdded">
|
||||
<div :class="{ summitPopup: true, minimize: minimizePopup }">
|
||||
<div v-if="coverPhoto" class="photo">
|
||||
<div style="text-align: center"><a :href="coverPhoto.mediaLink" target="_blank"><img :src="coverPhoto.src" /></a></div>
|
||||
|
@ -42,6 +42,11 @@ export default {
|
|||
components: {
|
||||
MglPopup, ModeLabel, AltitudeLabel, SummitPointsLabel
|
||||
},
|
||||
methods: {
|
||||
popupAdded (popup) {
|
||||
popup.popup.options.focusAfterOpen = false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
minimizePopup: false
|
||||
|
@ -113,7 +118,7 @@ export default {
|
|||
color: #3f5da7;
|
||||
}
|
||||
.photo .attribution {
|
||||
font-size: 8pt;
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.4;
|
||||
font-style: italic;
|
||||
color: #777;
|
||||
|
|
|
@ -15,7 +15,7 @@ import { faCheck, faCheckCircle, faInfoCircle, faExclamationTriangle, faExclamat
|
|||
faSnowflake, faWindowMinimize, faWindowMaximize, faWindowClose, faExpandArrows, faLocation, faCalendarCheck, faComment, faSpinner,
|
||||
faBookUser } from '@fortawesome/pro-regular-svg-icons'
|
||||
import { faMap, faCheckCircle as fasCheckCircle, faChevronCircleDown as fasChevronCircleDown, faChevronCircleUp as fasChevronCircleUp,
|
||||
faParking, faSquare, faBus, faHiking, faCircle, faCamera, faVolume, faVolumeMute, faCog, faCaretDown as fasCaretDown, faLocationArrow as fasLocationArrow } from '@fortawesome/pro-solid-svg-icons'
|
||||
faParking, faSquare, faBus, faHiking, faCircle, faCamera, faCctv, faVolume, faVolumeMute, faCog, faCaretDown as fasCaretDown, faLocationArrow as fasLocationArrow } from '@fortawesome/pro-solid-svg-icons'
|
||||
import { faWikipediaW, faGoogle, faGithub } from '@fortawesome/free-brands-svg-icons'
|
||||
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
|
||||
import '@/assets/global.css'
|
||||
|
@ -29,7 +29,7 @@ library.add(faCheck, faCheckCircle, faInfoCircle, faExclamationTriangle, faExcla
|
|||
faExchange, faGlobe, faCalendarDay, faTrashAlt, faEdit, faClone, farCheckCircle, faArrowsH, faArrowsAlt,
|
||||
faSnowflake, faWindowMinimize, faWindowMaximize, faWindowClose, faExpandArrows, faLocation, faCalendarCheck, faComment, faSpinner,
|
||||
faBookUser)
|
||||
library.add(faMap, fasCheckCircle, fasChevronCircleDown, fasChevronCircleUp, faParking, faSquare, faBus, faHiking, faCircle, faCamera, faVolume, faVolumeMute, faCog, fasCaretDown, fasLocationArrow)
|
||||
library.add(faMap, fasCheckCircle, fasChevronCircleDown, fasChevronCircleUp, faParking, faSquare, faBus, faHiking, faCircle, faCamera, faCctv, faVolume, faVolumeMute, faCog, fasCaretDown, fasLocationArrow)
|
||||
library.add(faWikipediaW, faGoogle, faGithub)
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
||||
Vue.component('font-awesome-layers', FontAwesomeLayers)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<MapDownloadControl position="top-left" />
|
||||
</div>
|
||||
|
||||
<MglPopup v-if="loadingPopupCoordinates" key="loading" :coordinates="loadingPopupCoordinates" :showed="true" anchor="bottom">
|
||||
<MglPopup v-if="loadingPopupCoordinates" key="loading" :coordinates="loadingPopupCoordinates" :showed="true" anchor="bottom" @added="onPopupAdded">
|
||||
<div class="loading-ring-wrapper">
|
||||
<LoadingRing />
|
||||
</div>
|
||||
|
@ -27,6 +27,8 @@
|
|||
<MapInfoPopup v-if="infoCoordinates !== null" :coordinates="infoCoordinates" @close="infoCoordinates = null" />
|
||||
|
||||
<MapDraw ref="draw" />
|
||||
|
||||
<MapWebcams v-if="mapOptions.webcams" />
|
||||
</MglMap>
|
||||
<div v-if="browserNotSupported" class="browser-not-supported">Your browser does not support WebGL, which is required to render this map.</div>
|
||||
<div v-if="zoomWarning" class="zoom-warning">Zoom in to see all filtered/spotted summits</div>
|
||||
|
@ -52,12 +54,13 @@ import SummitPopup from '../components/SummitPopup.vue'
|
|||
import MapRoute from '../components/MapRoute.vue'
|
||||
import MapInfoPopup from '../components/MapInfoPopup.vue'
|
||||
import MapDraw from '../components/MapDraw.vue'
|
||||
import MapWebcams from '../components/MapWebcams.vue'
|
||||
import SwisstopoInfo from '../components/SwisstopoInfo.vue'
|
||||
|
||||
export default {
|
||||
name: 'Map',
|
||||
components: {
|
||||
MglMap, MglPopup, MglNavigationControl, MglGeolocateControl, MglScaleControl, MglAttributionControl, MapFilterControl, MapOptionsControl, MapDownloadControl, LoadingRing, SummitPopup, MapRoute, MapInfoPopup, MapDraw, SwisstopoInfo
|
||||
MglMap, MglPopup, MglNavigationControl, MglGeolocateControl, MglScaleControl, MglAttributionControl, MapFilterControl, MapOptionsControl, MapDownloadControl, LoadingRing, SummitPopup, MapRoute, MapInfoPopup, MapDraw, MapWebcams, SwisstopoInfo
|
||||
},
|
||||
mixins: [utils, smptracks, mapstyle, longtouch],
|
||||
created () {
|
||||
|
@ -221,7 +224,7 @@ export default {
|
|||
this.updateRoute()
|
||||
},
|
||||
onMapClicked (event) {
|
||||
if (this.$refs.draw.isDrawing()) {
|
||||
if (this.$refs.draw.isDrawing() || event.mapboxEvent.originalEvent.hitMarker) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -307,6 +310,9 @@ export default {
|
|||
this.summit = null
|
||||
this.updateMapURL()
|
||||
},
|
||||
onPopupAdded (popup) {
|
||||
popup.popup.options.focusAfterOpen = false
|
||||
},
|
||||
handleSummitClick (feature) {
|
||||
this.loadingPopupCoordinates = feature.geometry.coordinates
|
||||
this.summit = null
|
||||
|
|
Ładowanie…
Reference in New Issue