Add webcams to map

pull/13/head v1.13.1
Manuel Kasper 2021-03-31 14:05:49 +02:00
rodzic 4c8ea60dce
commit e87e76180f
8 zmienionych plików z 232 dodań i 12 usunięć

Wyświetl plik

@ -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>

Wyświetl plik

@ -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
}
}
}

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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: {

Wyświetl plik

@ -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;

Wyświetl plik

@ -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)

Wyświetl plik

@ -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