Frequency Editing and Upload

This commit is contained in:
portree_kid 2020-03-18 16:48:18 +01:00
parent 84989714e5
commit 1f6fc221f9
11 changed files with 391 additions and 176 deletions

View File

@ -35,7 +35,8 @@
"filter": [ "filter": [
"**/*" "**/*"
] ]
}], }
],
"files": [ "files": [
"dist/electron/**/*" "dist/electron/**/*"
], ],
@ -66,7 +67,7 @@
}, },
"dependencies": { "dependencies": {
"@turf/turf": "^5.1.6", "@turf/turf": "^5.1.6",
"axios": "^0.18.0", "axios": "^0.18.1",
"dijkstrajs": "^1.0.1", "dijkstrajs": "^1.0.1",
"electron-debug": "^3.0.1", "electron-debug": "^3.0.1",
"element-ui": "^2.12.0", "element-ui": "^2.12.0",

View File

@ -26,75 +26,36 @@
<el-col :span="15">{{ parking }}</el-col> <el-col :span="15">{{ parking }}</el-col>
</el-row> </el-row>
</div> </div>
<el-divider></el-divider> <el-divider v-if="airport"></el-divider>
<h3 v-if="airport">Frequencies</h3>
<div v-if="airport"> <div v-if="airport">
<el-row> <el-row v-for="f in Frequencies" :key="f.index">
<el-col :span="7">AWOS :</el-col> <Frequency :frequency="f"></Frequency>
<el-col :span="15"><el-input placeholder="Please input AWOS frequency" v-model="AWOS"
:disabled="!editing"
v-bind:class="{ invalid: !awosOk && editing}">></el-input></el-col>
</el-row>
<el-row>
<el-col :span="7">Ground :</el-col>
<el-col :span="15"><el-input placeholder="Please input GROUND frequency" v-model="GROUND"
:disabled="!editing"
v-bind:class="{ invalid: !groundOk && editing}"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="7">Tower :</el-col>
<el-col :span="15"><el-input placeholder="Please input TOWER frequency" v-model="TOWER"
:disabled="!editing"
v-bind:class="{ invalid: !towerOk && editing}"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="7">Approach :</el-col>
<el-col :span="15"><el-input placeholder="Please input APPROACH frequency" v-model="APPROACH"
:disabled="!editing"
v-bind:class="{ invalid: !approachOk && editing}"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="7">Departure :</el-col>
<el-col :span="15"><el-input placeholder="Please input DEPARTURE frequency" v-model="DEPARTURE"
:disabled="!editing"
v-bind:class="{ invalid: !departureOk && editing}"></el-input></el-col>
</el-row> </el-row>
</div> </div>
<el-button @click="addFrequency" v-if="editing" >Add</el-button>
</div> </div>
</template> </template>
<script lang="js"> <script lang="js">
import Frequency from './Frequency'
export default { export default {
data () { data () {
return { awosOk: true, groundOk: true, towerOk: true, approachOk: true, departureOk: true } return { }
},
components: {
Frequency
}, },
methods: { methods: {
isValid (frequency) { addFrequency () {
let ok = frequency >= 118 && frequency <= 137 this.$store.dispatch('addFrequency', {type: 'AWOS', value: 0})
if (!ok) {
return false
}
let fractions = (frequency - Math.trunc(frequency)) * 1000
let subFraction = Math.round(fractions % 25)
switch (subFraction) {
case 0:
break
case 5:
break
case 10:
break
case 15:
break
case 25:
break
default:
return false
}
return true
} }
}, },
computed: { computed: {
editing: function () { editing: function () {
return this.$parent.$parent.$parent.$refs.editLayer.editing return this.$parent.$parent.$parent.$refs.editLayer !== undefined &&
this.$parent.$parent.$parent.$refs.editLayer.editing &&
this.$store.state.Editable.type === 'airport'
}, },
icao: function () { icao: function () {
return this.$store.state.Editable.data.airport.icao return this.$store.state.Editable.data.airport.icao
@ -117,69 +78,10 @@
airport: function () { airport: function () {
return this.$store.state.Editable.type === 'airport' return this.$store.state.Editable.type === 'airport'
}, },
AWOS: { Frequencies: {
// getter // getter
get: function () { get: function () {
let newValue = (this.$store.state.Frequencies.AWOS / 100).toFixed(3) return this.$store.state.Frequencies.items
this.awosOk = this.isValid(newValue)
return newValue
},
// setter
set: function (newValue) {
this.awosOk = this.isValid(newValue)
this.$store.dispatch('setAwos', newValue * 100)
}
},
GROUND: {
// getter
get: function () {
let newValue = (this.$store.state.Frequencies.GROUND / 100).toFixed(3)
this.groundOk = this.isValid(newValue)
return newValue
},
// setter
set: function (newValue) {
this.groundOk = this.isValid(newValue)
this.$store.dispatch('setGround', newValue * 100)
}
},
TOWER: {
// getter
get: function () {
let newValue = (this.$store.state.Frequencies.TOWER / 100).toFixed(3)
this.towerOk = this.isValid(newValue)
return newValue
},
// setter
set: function (newValue) {
this.towerOk = this.isValid(newValue)
this.$store.dispatch('setTower', newValue * 100)
}
},
APPROACH: {
// getter
get: function () {
let newValue = (this.$store.state.Frequencies.APPROACH / 100).toFixed(3)
this.approachOk = this.isValid(newValue)
return newValue
},
// setter
set: function (newValue) {
this.approachOk = this.isValid(newValue)
this.$store.dispatch('setApproach', newValue * 100)
}
},
DEPARTURE: {
// getter
get: function () {
let newValue = (this.$store.state.Frequencies.DEPARTURE / 100).toFixed(3)
this.departureOk = this.isValid(newValue)
return newValue
},
// setter
set: function (newValue) {
this.departureOk = this.isValid(newValue)
this.$store.dispatch('setDeparture', newValue * 100)
} }
} }
} }
@ -190,8 +92,4 @@
.el-row { .el-row {
padding: 0em padding: 0em
} }
.invalid {
padding: 1px;
background-color: red;
}
</style> </style>

View File

@ -1,7 +1,9 @@
<template> <template>
<div id="EditBar"> <div id="EditBar">
<Upload :visible.sync="uploadVisible" ref="upload"></Upload>
<EditButton icon="fas fa-th" v-on:click="zoomin" :show="true" tooltip="Zoomin"></EditButton> <EditButton icon="fas fa-th" v-on:click="zoomin" :show="true" tooltip="Zoomin"></EditButton>
<EditButton icon="fas fa-th-large" v-on:click="zoomout" :show="true" tooltip="Zoomout"></EditButton> <EditButton icon="fas fa-th-large" v-on:click="zoomout" :show="true" tooltip="Zoomout"></EditButton>
<EditButton icon="fas fa-upload" v-on:click="upload" :show="!editing" tooltip="Upload"></EditButton>
<EditButton icon="fas fa-edit" v-on:click="edit" :show="!editing" tooltip="Edit"></EditButton> <EditButton icon="fas fa-edit" v-on:click="edit" :show="!editing" tooltip="Edit"></EditButton>
<EditButton <EditButton
icon="fas fa-undo" icon="fas fa-undo"
@ -46,19 +48,23 @@
<script lang="js"> <script lang="js">
/* eslint-disable */ /* eslint-disable */
import EditButton from './EditButton' import EditButton from './EditButton'
import Upload from './Upload'
import Vue from 'vue' import Vue from 'vue'
import fileUrl from 'file-url' import fileUrl from 'file-url'
const path = require('path') const path = require('path')
export default { export default {
components: { EditButton }, components: { EditButton, Upload },
data () { data () {
return {isEditing: false, centerDialogVisible: false, saveDialogVisible: false, checkDialogVisible: false, checking: false, progress: 0, max: 0} return {isEditing: false, uploadVisible: false, centerDialogVisible: false, saveDialogVisible: false, checkDialogVisible: false, checking: false, progress: 0, max: 0}
}, },
created () { created () {
}, },
methods: { methods: {
upload() {
this.uploadVisible = true
},
zoomout() { zoomout() {
this.$parent.$parent.zoomUpdated(9) this.$parent.$parent.zoomUpdated(9)
}, },

View File

@ -0,0 +1,109 @@
<template>
<div>
<span>
<el-row>
<el-col :span="7">
<el-select v-model="type" placeholder="Select">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
></el-option>
</el-select>
</el-col>
<el-col :span="15">
<el-input
placeholder="Please input frequency"
v-model="value"
:disabled="!editing"
v-bind:class="{ invalid: !ok && editing}"
></el-input>
</el-col>
</el-row>
</span>
</div>
</template>
<script lang="js">
/* eslint-disable */
export default {
name: 'frequency',
props: {frequency: Object},
mounted () {
},
data () {
return {
ok: true
}
},
methods: {
isValid (frequency) {
let ok = frequency >= 118 && frequency <= 137
if (!ok) {
return false
}
let fractions = (frequency - Math.trunc(frequency)) * 1000
let subFraction = Math.round(fractions % 25)
switch (subFraction) {
case 0:
break
case 5:
break
case 10:
break
case 15:
break
case 25:
break
default:
return false
}
return true
}
},
computed: {
editing: function () {
return this.$parent.$parent.$parent.$parent.$parent.$refs.editLayer.editing
},
options: function () {
return [
{value: 'AWOS', label: 'AWOS'},
{value: 'GROUND', label: 'GROUND'},
{value: 'TOWER', label: 'TOWER'},
{value: 'APPROACH', label: 'APPROACH'},
{value: 'DEPARTURE', label: 'DEPARTURE'}
]
},
type: {
// getter
get: function () {
return this.frequency.type;
},
// setter
set: function (newValue) {
this.frequency.type = newValue;
}
},
value: {
// getter
get: function () {
return this.frequency.value;
},
// setter
set: function (newValue) {
this.frequency.value = newValue;
ok = this.isValid(newValue);
}
}
}
}
</script>
<style scoped lang="scss">
.invalid {
padding: 1px;
background-color: red;
}
</style>

View File

@ -42,7 +42,7 @@
<el-row> <el-row>
<el-col :span="7" class="label">Author : </el-col> <el-col :span="7" class="label">Author : </el-col>
<el-col :span="17"> <el-col :span="17">
<el-input placeholder="Please input your name" v-model="name"></el-input> <el-input placeholder="Please input your email" v-model="email"></el-input>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -74,14 +74,14 @@
} }
}, },
computed: { computed: {
name: { email: {
// getter // getter
get: function () { get: function () {
return this.$store.state.Settings.settings.name return this.$store.state.Settings.settings.email
}, },
// setter // setter
set: function (newValue) { set: function (newValue) {
this.$store.commit('SET_USERNAME', newValue) this.$store.commit('SET_EMAIL', newValue)
} }
}, },
flightgear_directory: function () { flightgear_directory: function () {

View File

@ -0,0 +1,80 @@
<template>
<el-dialog title="Upload" :visible.sync="visible" width="30%" center>
<form ref="request_form" method="post" enctype="multipart/form-data"
action="http://groundweb.azurewebsites.net/groundnets/upload"
target="result">
<label>Email (not checked but used for commit)</label>
<input name="user_email" type="text"/><BR/>
<label>I agree to release my content under the GPL v2 </label> <input name="gpl" type="checkbox"/>
<br/>
<label>Your groundnet file (groundnet/rwyuse)
<input name="groundnet" type="file" size="50" accept="text/*">
</label>
<el-button @click="upload">Ok</el-button>
</form>
</el-dialog>
</template>
<script lang="js">
/* eslint-disable */
import Vue from 'vue'
export default {
name: 'upload',
props: [],
mounted () {
},
data () {
return {
gplv2: false
}
},
methods: {
upload () {
this.$refs.request_form.$el.submit()
Vue.set(this.$parent, 'uploadVisible', false)
},
upload2 () {
var blob = new Blob([JSON.stringify(data)]);
var url = URL.createObjectURL(blob);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'myForm.php', true);
// define new form
var formData = new FormData();
formData.append('someUploadIdentifier', blob, 'someFileName.json');
// action after uploading happens
xhr.onload = function(e) {
console.log("File uploading completed!");
};
// do the uploading
console.log("File uploading started!");
xhr.send(formData);
}
},
computed: {
visible: {
// getter
get: function () {
return this.$attrs.visible
},
// setter
set: function (newValue) {
Vue.set(this.$parent, 'uploadVisible', newValue)
}
},
icao: {
get: function () {
return this.$parent.$parent.$parent.icao
},
set: function (newValue) {
}
}
}
}
</script>
<style scoped lang="scss">
.center { text-align: center;}
</style>

View File

@ -0,0 +1,104 @@
<template>
<el-dialog title="Upload" :visible.sync="visible" width="30%" center>
<span style="center">Upload {{icao}} to groundweb.</span><br/>
<span style="center">E-Mail : {{this.$store.state.Settings.settings.email}}</span><br/>
<span style="center"><el-checkbox v-model="gplv2">I agree to release the groundnet under GPL v2</el-checkbox></span><br/>
<span style="center" v-if="message.length>0">{{message}}</span><br/>
<span slot="footer" class="dialog-footer">
<el-button @click="upload" :disabled="!gplv2">Ok</el-button>
</span>
</el-dialog>
</template>
<script lang="js">
/* eslint-disable */
import Vue from 'vue'
const fs = require('fs')
const path = require('path')
export default {
name: 'upload',
props: [],
mounted () {
},
data () {
return {
gplv2: false, message: ''
}
},
methods: {
upload () {
var f = path.join(this.$store.state.Settings.settings.airportsDirectory,
this.icao[0],
this.icao[1],
this.icao[2],
this.icao + '.groundnet.new.xml');
if (f == null || !fs.existsSync(f)) {
this.message = 'File doesn\'t exist';
return;
}
var data = fs.readFileSync(f, 'utf8').toString();
var blob = new Blob([data]);
var url = URL.createObjectURL(blob);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://groundweb.azurewebsites.net/groundnets/upload', true);
// define new form
var formData = new FormData();
formData.append("gpl", this.gplv2 )
formData.append("user_email", this.$store.state.Settings.settings.email)
formData.append('groundnet', blob, this.icao + '.groundnet.xml');
var parent = this.$parent;
var messageField = this.message;
// action after uploading happens
xhr.onload = function(e) {
console.log("File uploading completed! ");
console.log(e);
var response = JSON.parse(e.srcElement.response);
if(response.message === 'OK') {
Vue.set(parent, 'uploadVisible', false)
} else if(response.message === 'XML Errors') {
if (response.validationErrors) {
response.validationErrors.forEach(element => {
parent.$refs.upload.message += element.message + '\r\n';
});
}
} else {
parent.$refs.upload.message = response.message;
}
};
// do the uploading
console.log("File uploading started!");
xhr.send(formData);
}
},
computed: {
visible: {
// getter
get: function () {
return this.$attrs.visible
},
// setter
set: function (newValue) {
Vue.set(this.$parent, 'uploadVisible', newValue)
}
},
icao: {
get: function () {
return this.$parent.$parent.$parent.icao
},
set: function (newValue) {
}
}
}
}
</script>
<style scoped lang="scss">
.center { text-align: center;}
</style>

View File

@ -17,6 +17,17 @@ var $ = require('jquery');
var featureLookup = {}; var featureLookup = {};
var frequencies = [];
function addFrequencies (type, value) {
value.split(' ').forEach(frequencyValue => {
if( value.length > 0) {
var frequency = {type: type, value: frequencyValue};
frequencies.push(frequency);
}
})
}
exports.addFeature = function (feature) { exports.addFeature = function (feature) {
featureLookup[feature.id] = new Array(); featureLookup[feature.id] = new Array();
} }
@ -54,14 +65,22 @@ exports.readGroundnetXML = function (fDir, icao, force) {
// <APPROACH>12120</APPROACH> // <APPROACH>12120</APPROACH>
// </frequencies> // </frequencies>
var awos = xml.find('groundnet/frequencies/AWOS/text()').text();
store.default.dispatch('setAwos', awos);
var ground = xml.find('groundnet/frequencies/GROUND/text()').text();
store.default.dispatch('setGround', ground);
var tower = xml.find('groundnet/frequencies/TOWER/text()').text();
store.default.dispatch('setTower', tower);
var approach = xml.find('groundnet/frequencies/APPROACH/text()').text(); var approach = xml.find('groundnet/frequencies/APPROACH/text()').text();
store.default.dispatch('setApproach', approach); addFrequencies('APPROACH', approach);
var awos = xml.find('groundnet/frequencies/AWOS/text()').text();
addFrequencies('AWOS', awos);
var clearance = xml.find('groundnet/frequencies/CLEARANCE/text()').text();
addFrequencies('CLEARANCE', clearance);
var departure = xml.find('groundnet/frequencies/DEPARTURE/text()').text();
addFrequencies('DEPARTURE', departure);
var ground = xml.find('groundnet/frequencies/GROUND/text()').text();
addFrequencies('GROUND', ground);
var tower = xml.find('groundnet/frequencies/TOWER/text()').text();
addFrequencies('TOWER', tower);
var unicom = xml.find('groundnet/frequencies/UNICOM/text()').text();
addFrequencies('UNICOM', unicom);
store.default.dispatch('setFrequencies', frequencies);
var parkingNodes = xml.find('groundnet/parkingList/Parking'); var parkingNodes = xml.find('groundnet/parkingList/Parking');
console.log("Parking Nodes" + parkingNodes.length); console.log("Parking Nodes" + parkingNodes.length);

View File

@ -30,18 +30,12 @@ exports.writeGroundnetXML = function (fDir, icao, featureList) {
var nodes = []; var nodes = [];
var arcList = []; var arcList = [];
var frequencies = [];
var version = new Date().toUTCString() + ' by FlightgearAirports'; var version = new Date().toUTCString() + ' by FlightgearAirports';
var name = store.default.state.Settings.settings.name; var email = store.default.state.Settings.settings.email;
//Frequencies
var frequencies = {
'AWOS': store.default.state.Frequencies.AWOS,
'GROUND': store.default.state.Frequencies.GROUND,
'TOWER': store.default.state.Frequencies.TOWER,
'APPROACH': store.default.state.Frequencies.APPROACH,
'DEPARTURE': store.default.state.Frequencies.DEPARTURE
};
var featureLookup = []; var featureLookup = [];
// Loaded segments // Loaded segments
@ -102,7 +96,23 @@ exports.writeGroundnetXML = function (fDir, icao, featureList) {
var maxId = uniqueNodes.slice(-1)[0]['@index']; var maxId = uniqueNodes.slice(-1)[0]['@index'];
var xmlObj = { groundnet: { version: version, name: name, frequencies, parkingList: { Parking: parkings }, TaxiNodes: { node: uniqueNodes }, TaxiWaySegments: { arc: arcList } } }; var approachList = store.default.state.Frequencies.items.filter(f => f.type === 'APPROACH').map(mapFrequency);
var awosList = store.default.state.Frequencies.items.filter(f => f.type === 'AWOS').map(mapFrequency);
var clearanceList = store.default.state.Frequencies.items.filter(f => f.type === 'CLEARANCE').map(mapFrequency);
var departureList = store.default.state.Frequencies.items.filter(f => f.type === 'DEPARTURE').map(mapFrequency);
var groundList = store.default.state.Frequencies.items.filter(f => f.type === 'GROUND').map(mapFrequency);
var towerList = store.default.state.Frequencies.items.filter(f => f.type === 'TOWER').map(mapFrequency);
var unicomList = store.default.state.Frequencies.items.filter(f => f.type === 'UNICOM').map(mapFrequency);
var xmlObj = { groundnet: { version: version, email: email,
'frequencies': { APPROACH: approachList, DEPARTURE: departureList, AWOS: awosList, CLEARANCE: clearanceList, GROUND: groundList, TOWER: towerList, UNICOM: unicomList },
parkingList: { Parking: parkings }, TaxiNodes: { node: uniqueNodes }, TaxiWaySegments: { arc: arcList } } };
xmlString = builder.create(xmlObj).end({ pretty: true }); xmlString = builder.create(xmlObj).end({ pretty: true });
fs.writeFileSync(f, xmlString); fs.writeFileSync(f, xmlString);
@ -113,6 +123,10 @@ exports.writeGroundnetXML = function (fDir, icao, featureList) {
return layerGroup; return layerGroup;
} }
var mapFrequency = function (o) {
return o.value;
}
var mapParkings = function (o) { var mapParkings = function (o) {
console.log(o); console.log(o);
if (o instanceof L.ParkingSpot) { if (o instanceof L.ParkingSpot) {

View File

@ -1,45 +1,29 @@
import Vue from 'vue' import Vue from 'vue'
const state = { const state = { items: [] }
'AWOS': 0,
'GROUND': 0,
'TOWER': 0,
'APPROACH': 0
}
const mutations = { const mutations = {
'SET_AWOS' (state, frequency) { ADD_FREQUENCY: (state, item) => {
Vue.set(state, 'AWOS', frequency) state.items.push(item)
}, },
'SET_GROUND' (state, frequency) { UPDATE_FREQUENCY: (state, item) => {
Vue.set(state, 'GROUND', frequency) const existingItem = state.items.find((i) => i.id === item.id)
Object.assign(existingItem, item)
}, },
'SET_TOWER' (state, frequency) { SET_FREQUENCIES (state, frequencies) {
Vue.set(state, 'TOWER', frequency) Vue.set(state, 'items', frequencies)
},
'SET_APPROACH' (state, frequency) {
Vue.set(state, 'APPROACH', frequency)
},
'SET_DEPARTURE' (state, frequency) {
Vue.set(state, 'DEPARTURE', frequency)
} }
} }
const actions = { const actions = {
async setAwos (context, frequency) { async addFrequency (context, frequency) {
context.commit('SET_AWOS', frequency) context.commit('ADD_FREQUENCY', frequency)
}, },
async setGround (context, frequency) { async updateFrequency (context, frequency) {
context.commit('SET_GROUND', frequency) context.commit('SET_GROUND', frequency)
}, },
async setTower (context, frequency) { async setFrequencies (context, frequencies) {
context.commit('SET_TOWER', frequency) context.commit('SET_FREQUENCIES', frequencies)
},
async setApproach (context, frequency) {
context.commit('SET_APPROACH', frequency)
},
async setDeparture (context, frequency) {
context.commit('SET_DEPARTURE', frequency)
} }
} }

View File

@ -1,5 +1,5 @@
const state = { const state = {
settings: {flightgearDirectory: '.', name: ''}, settings: {flightgearDirectory: '.', email: 'x'},
zoom: 14, zoom: 14,
center: [47.413220, -1.219482], center: [47.413220, -1.219482],
bounds: undefined bounds: undefined
@ -25,8 +25,8 @@ const mutations = {
'BOUNDS' (state, bounds) { 'BOUNDS' (state, bounds) {
state.bounds = bounds state.bounds = bounds
}, },
'SET_USERNAME' (state, name) { 'SET_EMAIL' (state, email) {
state.settings.name = name state.settings.email = email
} }
} }