441 lines
17 KiB
Vue
441 lines
17 KiB
Vue
<!--
|
|
Copyright 2020 Keith Paterson
|
|
|
|
This file is part of FG Airports.
|
|
|
|
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
|
|
-->
|
|
<template>
|
|
<l-map
|
|
:zoom="zoom"
|
|
:center="center"
|
|
:options="options"
|
|
@ready="ready"
|
|
@update:zoom="zoomUpdated"
|
|
@update:center="centerUpdated"
|
|
@update:bounds="boundsUpdated"
|
|
ref="map"
|
|
>
|
|
<!--The backgroundmap-->
|
|
<l-tile-layer :url="url" :attribution="attribution" :options="{maxZoom: 22, maxNativeZoom: 17}"></l-tile-layer>
|
|
<!--
|
|
<l-control position="topright" >
|
|
<el-button @click="editAirport()">{{ icao }}</el-button>
|
|
</l-control>
|
|
-->
|
|
<!--<l-marker :lat-lng="marker"></l-marker>-->
|
|
<LeafletSidebar ref="sidebar" @editParking="onEditSidebar" @edit="onEdit($event)"></LeafletSidebar>
|
|
<AiLayer ref="aiLayer"></AiLayer>
|
|
<l-layer-group layerType="overlay" name="airports" ref="airportLayer">
|
|
<l-circle
|
|
v-for="(item, index) in this.$store.state.Airports.airports"
|
|
:key="index"
|
|
:lat-lng="[item.geometry.coordinates[1], item.geometry.coordinates[0]]"
|
|
:radius="((item.properties.flights+5)*20)"
|
|
:color="item.color"
|
|
@add="addEvent($event, item)"
|
|
@click="onClick($event, item)"
|
|
></l-circle>
|
|
</l-layer-group>
|
|
<EditLayer ref="editLayer"></EditLayer>
|
|
<PavementLayer ref="pavementLayer"></PavementLayer>
|
|
<ThresholdLayer ref="thresholdLayer"></ThresholdLayer>
|
|
<TowerLayer ref="towerLayer"></TowerLayer>
|
|
<ToolLayer ref="toolLayer" @select-poly="onSelectedPolygon"></ToolLayer>
|
|
<EditBar ref="editBar" @edit="onEdit($event)"></EditBar>
|
|
<ToolBar ref="toolBar"></ToolBar>
|
|
</l-map>
|
|
</template>
|
|
|
|
<script lang="js">
|
|
import 'leaflet/dist/leaflet.css'
|
|
import 'leaflet-search/dist/leaflet-search.src.css'
|
|
import '@/assets/button.css'
|
|
import { LMap, LTileLayer, LMarker, LCircle, LLayerGroup, LControl, LTooltip } from 'vue2-leaflet'
|
|
import LeafletSidebar from './LeafletSidebar'
|
|
import AiLayer from './AiLayer'
|
|
import EditBar from './EditBar'
|
|
import ToolBar from './ToolBar'
|
|
import EditLayer from './EditLayer'
|
|
import ToolLayer from './ToolLayer'
|
|
import TowerLayer from './TowerLayer'
|
|
import PavementLayer from './PavementLayer'
|
|
import ThresholdLayer from './ThresholdLayer'
|
|
|
|
import { Loading } from 'element-ui'
|
|
import L from 'leaflet'
|
|
import { LeafletSearch } from 'leaflet-search'
|
|
|
|
// https://github.com/KoRiGaN/Vue2Leaflet/issues/103
|
|
delete L.Icon.Default.prototype._getIconUrl
|
|
L.Icon.Default.mergeOptions({
|
|
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
|
|
iconUrl: require('leaflet/dist/images/marker-icon.png'),
|
|
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
|
|
})
|
|
export default {
|
|
name: 'flightgear-map',
|
|
components: { LMap, LTileLayer, LMarker, LCircle, LTooltip, LeafletSidebar, AiLayer, EditBar, ToolBar, EditLayer, TowerLayer, PavementLayer, LLayerGroup, LControl, ThresholdLayer, ToolLayer, LeafletSearch },
|
|
props: [],
|
|
created () {
|
|
this.loadingInstance = null
|
|
this.$store.watch(
|
|
function (state) {
|
|
return state.Loading.icao
|
|
},
|
|
(newValue, oldValue) => {
|
|
// debugger
|
|
console.log('setIcaoLoading ' + oldValue + ' => ' + newValue + ' groundnetLoaded ' + this.groundnetLoaded + ' pavementLoaded ' + this.pavementLoaded + ' ' + this.loadingInstance)
|
|
if (newValue !== oldValue && newValue !== '') {
|
|
this.groundnetLoaded = newValue
|
|
if ((this.loadingInstance === null || this.loadingInstance === undefined)) {
|
|
this.loadingInstance = Loading.service({ fullscreen: false })
|
|
}
|
|
}
|
|
}
|
|
,
|
|
{
|
|
deep: false,
|
|
immediate: true
|
|
}
|
|
)
|
|
this.$store.watch(
|
|
function (state) {
|
|
return state.Loading.groundnetLoaded
|
|
},
|
|
(newValue, oldValue) => {
|
|
// debugger
|
|
console.log('groundnetLoaded ' + oldValue + ' => ' + newValue + ' groundnetLoaded ' + this.groundnetLoaded + ' pavementLoaded ' + this.pavementLoaded + ' ' + this.loadingInstance)
|
|
if (newValue !== oldValue) {
|
|
this.groundnetLoaded = newValue
|
|
if (this.groundnetLoaded &&
|
|
this.pavementLoaded &&
|
|
this.loadingInstance !== null) {
|
|
this.loadingInstance.close()
|
|
this.loadingInstance = null
|
|
}
|
|
}
|
|
}
|
|
,
|
|
{
|
|
deep: false,
|
|
immediate: true
|
|
}
|
|
)
|
|
this.$store.watch(
|
|
function (state) {
|
|
return state.Loading.pavementLoaded
|
|
},
|
|
(newValue, oldValue) => {
|
|
console.log('pavementLoaded ' + oldValue + ' => ' + newValue + ' ' + this.groundnetLoaded + ' ' + this.pavementLoaded + ' ' + this.loadingInstance)
|
|
if (newValue !== oldValue) {
|
|
this.pavementLoaded = newValue
|
|
if (this.groundnetLoaded &&
|
|
this.pavementLoaded &&
|
|
this.loadingInstance !== null) {
|
|
this.loadingInstance.close()
|
|
this.loadingInstance = null
|
|
}
|
|
}
|
|
}
|
|
,
|
|
{
|
|
deep: false,
|
|
immediate: true
|
|
}
|
|
)
|
|
},
|
|
mounted () {
|
|
this.$store.dispatch('getAirports')
|
|
this.$store.subscribe((mutation, state) => {
|
|
if (mutation.type === 'CENTER' || mutation.type === 'SET_AIRPORTS' || mutation.type === 'ZOOM') {
|
|
// console.log(this.$parent)
|
|
// console.log(this.$store.state.Settings.bounds)
|
|
let airportsToLoad = this.$store.state.Airports.airports
|
|
.filter(feature => this.visible(feature))
|
|
.map(feature => feature.properties.icao)
|
|
if (airportsToLoad.length > 0 && airportsToLoad[0] !== this.editingAirport && this.zoom > 12) {
|
|
this.$store.dispatch('setIcaoLoading', airportsToLoad[0])
|
|
this.$nextTick(() => { // Loading should be closed asynchronously
|
|
this.$refs.pavementLayer.load(airportsToLoad[0])
|
|
this.$refs.editLayer.load(airportsToLoad[0])
|
|
this.$refs.thresholdLayer.load(airportsToLoad[0])
|
|
if (this.$refs.towerLayer) {
|
|
this.$refs.towerLayer.load(airportsToLoad[0])
|
|
}
|
|
this.editingAirport = airportsToLoad[0]
|
|
})
|
|
}
|
|
if (this.$refs.editLayer) {
|
|
this.$refs.editLayer.setVisible(this.zoom >= 12)
|
|
}
|
|
if (this.$refs.airportLayer) {
|
|
this.$refs.airportLayer.setVisible(this.zoom < 12)
|
|
}
|
|
// console.log(this.groundnet)
|
|
}
|
|
})
|
|
},
|
|
data () {
|
|
return {
|
|
loadingInstance: Object,
|
|
groundnetLoaded: false,
|
|
pavementLoaded: false,
|
|
url: 'https://a.tile.openstreetmap.de/{z}/{x}/{y}.png',
|
|
attribution: '<A href="https://github.com/Portree-Kid/flightgear-airports" target="_blank">Flightgear Airports ' + require('electron').remote.app.getVersion() +
|
|
'</A> <A href="https://www.electronjs.org/" target="_blank">Electron</A> ' +
|
|
' <A href="https://element.eleme.io/#/en-US/" target="_blank">element.io</A> ' +
|
|
' © <a href="http://osm.org/copyright" target="_blank">OpenStreetMap</a> contributors',
|
|
airports: this.$store.state.Airports.airports,
|
|
options: {editable: true},
|
|
layersControl: null,
|
|
icao: 'TEST'
|
|
}
|
|
},
|
|
methods: {
|
|
ready (e) {
|
|
e.on('layeradd', this.onLayerAdd)
|
|
},
|
|
onLayerAdd (e) {
|
|
if (this.layersControl === null) {
|
|
this.layersControl = L.control.layers({}, {}, {position: 'topleft'})
|
|
this.layersControl.addTo(this.$refs.map.mapObject)
|
|
var icon = this.layersControl._container.ownerDocument.createElement('I')
|
|
icon.className = 'fas fa-layer-group'
|
|
icon.style = 'padding-top: 9px; height: 30px; width: 30px; text-align: center; vertical-align: sub;'
|
|
this.layersControl._container.children[0].appendChild(icon)
|
|
// this.layersControl.addOverlay(this.$refs.thresholdLayer, 'Threshold Layer')
|
|
}
|
|
if (this.$refs.pavementLayer.getLayer() === e.layer) {
|
|
// debugger
|
|
var l = this.layersControl._layers.filter(l => l.name === 'APT Layer')
|
|
if (l.length > 0 && l[0].layer !== this.$refs.pavementLayer.getLayer()) {
|
|
this.layersControl.removeLayer(l[0].layer)
|
|
this.layersControl.addOverlay(this.$refs.pavementLayer.getLayer(), 'APT Layer')
|
|
}
|
|
if (l.length === 0) {
|
|
this.layersControl.addOverlay(this.$refs.pavementLayer.getLayer(), 'APT Layer')
|
|
}
|
|
}
|
|
if (this.$refs.thresholdLayer !== undefined && this.$refs.thresholdLayer.getLayer() === e.layer) {
|
|
l = this.layersControl._layers.filter(l => l.name === 'Threshold Layer')
|
|
if (l.length > 0 && l[0].layer !== this.$refs.thresholdLayer.getLayer()) {
|
|
this.layersControl.removeLayer(l[0].layer)
|
|
this.layersControl.addOverlay(this.$refs.thresholdLayer.getLayer(), 'Threshold Layer')
|
|
}
|
|
if (l.length === 0) {
|
|
this.layersControl.addOverlay(this.$refs.thresholdLayer.getLayer(), 'Threshold Layer')
|
|
}
|
|
this.$refs.thresholdLayer.zoomUpdated()
|
|
}
|
|
if (this.$refs.towerLayer !== undefined && this.$refs.towerLayer.getLayer() === e.layer) {
|
|
l = this.layersControl._layers.filter(l => l.name === 'Tower Layer')
|
|
if (l.length > 0 && l[0].layer !== this.$refs.towerLayer.getLayer()) {
|
|
this.layersControl.removeLayer(l[0].layer)
|
|
this.layersControl.addOverlay(this.$refs.towerLayer.getLayer(), 'Tower Layer')
|
|
}
|
|
if (l.length === 0) {
|
|
this.layersControl.addOverlay(this.$refs.towerLayer.getLayer(), 'Tower Layer')
|
|
}
|
|
this.$refs.towerLayer.zoomUpdated()
|
|
}
|
|
if (this.$refs.editLayer !== undefined && this.searchControl === undefined && this.$refs.editLayer.getLayer() === e.layer) {
|
|
this.searchControl = new L.Control.Search({
|
|
layer: this.$refs.editLayer.getLayer(),
|
|
position: 'topleft',
|
|
propertyName: 'searchTerm',
|
|
marker: {animate: false},
|
|
initial: false
|
|
})
|
|
this.searchControl.addTo(this.$refs.map.mapObject)
|
|
}
|
|
},
|
|
onSelectedPolygon (ring) {
|
|
var parkings = this.$refs.editLayer.getParkings(ring)
|
|
console.debug(ring)
|
|
console.debug(parkings)
|
|
|
|
this.$store.commit('SET_EDIT_TYPE', 'parking-group')
|
|
this.$refs.sidebar.setData(parkings)
|
|
},
|
|
onEdit (event) {
|
|
if (event) {
|
|
this.$refs.map.mapObject.options.minZoom = 13
|
|
} else {
|
|
this.$refs.map.mapObject.options.minZoom = 1
|
|
}
|
|
this.$refs.editLayer.enableEdit()
|
|
this.$refs.towerLayer.enableEdit()
|
|
this.$refs.thresholdLayer.enableEdit()
|
|
this.$refs.editBar.setEditing(event)
|
|
this.$refs.toolBar.setEditing(event)
|
|
this.$refs.sidebar.setEditing(event)
|
|
},
|
|
onEditSidebar (event) {
|
|
this.$refs.editLayer.onEdit(event)
|
|
},
|
|
editAirport () {
|
|
if (this.editingAirport) {
|
|
let airportsToLoad = this.$store.state.Airports.airports
|
|
.filter(feature => feature.properties.icao === this.icao)
|
|
let properties = Object.assign({}, airportsToLoad[0].properties)
|
|
this.$store.commit('SET_EDIT_AIRPORT', properties)
|
|
}
|
|
},
|
|
setIcao (icao) {
|
|
this.icao = icao
|
|
this.$store.dispatch('setCurrentAirport', icao)
|
|
},
|
|
visible (feature) {
|
|
let bounds
|
|
if (this.$refs.map) {
|
|
bounds = this.$refs.map.mapObject.getBounds()
|
|
} else {
|
|
return false
|
|
}
|
|
let width = L.latLng(bounds._northEast).distanceTo(L.latLng(bounds._southWest))
|
|
// Load all airports in a minimum 5 km box
|
|
if (width < 5000) {
|
|
let rest = 5000 - width
|
|
let padFactor = rest / width
|
|
bounds = bounds.pad(padFactor)
|
|
}
|
|
|
|
let coordinates = feature.geometry.coordinates
|
|
let ret = bounds._northEast.lat > Number(coordinates[1]) &&
|
|
bounds._northEast.lng > Number(coordinates[0])
|
|
let ret2 = bounds._southWest.lat < Number(coordinates[1]) &&
|
|
bounds._southWest.lng < Number(coordinates[0])
|
|
return ret && ret2
|
|
},
|
|
normalStyle (target) {
|
|
if (target.airport.properties.groundnet === 0) {
|
|
target.setStyle({color: 'red', fillColor: 'red', weight: '2'})
|
|
} else if ((target.airport.properties.flights / target.airport.properties.parking) > 40) {
|
|
target.setStyle({color: 'yellow', fillColor: 'yellow', weight: '2'})
|
|
} else {
|
|
target.setStyle({color: '#3388ff', fillColor: '#3388ff', weight: '2'})
|
|
}
|
|
},
|
|
highlightStyle (target) {
|
|
if (target.airport.properties.groundnet === 0) {
|
|
target.setStyle({color: 'red', fillColor: 'red', weight: '5'})
|
|
} else if ((target.airport.properties.flights / target.airport.properties.parking) > 40) {
|
|
target.setStyle({color: 'yellow', fillColor: 'yellow', weight: '5'})
|
|
} else {
|
|
target.setStyle({color: '#3388ff', fillColor: '#3388ff', weight: '5'})
|
|
}
|
|
},
|
|
// Triggered by adding airports to the map
|
|
addEvent (event, item) {
|
|
this.selected = null
|
|
event.target.airport = item
|
|
// console.log(event, item)
|
|
this.normalStyle(event.target)
|
|
event.target.bindTooltip(event.target.airport.properties.icao + ' ' + event.target.airport.properties.name)
|
|
},
|
|
onClick (event, item) {
|
|
console.log(item)
|
|
if (this.selected !== null) {
|
|
this.normalStyle(this.selected)
|
|
}
|
|
this.selected = event.target
|
|
this.highlightStyle(this.selected)
|
|
event.target.bringToBack()
|
|
this.setIcao(item.properties.icao)
|
|
this.$store.commit('SET_EDIT_AIRPORT', item.properties)
|
|
let newCenter = L.latLng(item.geometry.coordinates[1], item.geometry.coordinates[0])
|
|
this.$refs.map.setCenter(newCenter)
|
|
},
|
|
zoomUpdated (zoom) {
|
|
if (zoom !== this.$store.state.Settings.zoom) {
|
|
this.$store.dispatch('setZoom', zoom)
|
|
this.$refs.airportLayer.setVisible(zoom < 12)
|
|
if (this.$refs.towerLayer) {
|
|
this.$refs.towerLayer.setVisible(this.zoom >= 12)
|
|
}
|
|
if (this.$refs.thresholdLayer) {
|
|
this.$refs.thresholdLayer.setVisible(this.zoom >= 12)
|
|
}
|
|
this.$refs.pavementLayer.setVisible(zoom >= 12)
|
|
}
|
|
if (this.$refs.editLayer.groundnetLayerGroup) {
|
|
this.$refs.editLayer.groundnetLayerGroup.eachLayer(function (layer) {
|
|
if (layer.updateArrows !== undefined) {
|
|
layer.updateArrows(zoom)
|
|
}
|
|
})
|
|
}
|
|
if (this.$refs.thresholdLayer) {
|
|
this.$refs.thresholdLayer.zoomUpdated()
|
|
}
|
|
if (this.$refs.towerLayer) {
|
|
this.$refs.towerLayer.zoomUpdated()
|
|
}
|
|
},
|
|
async centerUpdated (center) {
|
|
if (center !== this.$store.state.Settings.center) {
|
|
this.$store.dispatch('setCenter', {lat: Number(center.lat), lng: Number(center.lng)})
|
|
this.$refs.airportLayer.setVisible(this.zoom < 12)
|
|
if (this.$refs.thresholdLayer) {
|
|
this.$refs.thresholdLayer.setVisible(this.zoom >= 12)
|
|
}
|
|
if (this.$refs.towerLayer) {
|
|
this.$refs.towerLayer.setVisible(this.zoom >= 12)
|
|
}
|
|
this.$refs.pavementLayer.setVisible(this.zoom >= 12)
|
|
}
|
|
},
|
|
async boundsUpdated (bounds) {
|
|
/*
|
|
if (bounds !== this.$store.state.Settings.bounds) {
|
|
this.$store.dispatch('setBounds', bounds)
|
|
this.$refs.airportLayer.setVisible(this.zoom < 12)
|
|
this.$refs.pavementLayer.setVisible(this.zoom < 12)
|
|
}
|
|
*/
|
|
}
|
|
},
|
|
computed: {
|
|
zoom: function () {
|
|
return this.$store.state.Settings.zoom
|
|
},
|
|
isEditing: function () {
|
|
return this.$refs.editLayer !== undefined && this.$refs.editLayer.editing
|
|
},
|
|
center: function () {
|
|
return this.$store.state.Settings.center
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.vue2leaflet-map {
|
|
height: 100%;
|
|
}
|
|
.l-map {
|
|
height: 100%;
|
|
}
|
|
.flightgear-map {
|
|
color: aqua;
|
|
}
|
|
.item {
|
|
padding: 18px 0;
|
|
}
|
|
.l-control.el-card {
|
|
padding-left: 2px;
|
|
padding-top: 0px;
|
|
padding-right: 2px;
|
|
padding-bottom: 0px;
|
|
}
|
|
.leaflet-touch .leaflet-control-layers-toggle {
|
|
width: 30px;
|
|
height: 30px;
|
|
}
|
|
</style>
|