1. Edit Layer

This commit is contained in:
portree_kid 2020-01-01 19:46:36 +01:00
parent 3134e34c33
commit 7f07026fb7
11 changed files with 524 additions and 66 deletions

View File

@ -56,13 +56,16 @@
}
},
"dependencies": {
"@turf/turf": "^5.1.6",
"axios": "^0.18.0",
"electron-debug": "^3.0.1",
"element-ui": "^2.12.0",
"file-url": "^3.0.0",
"font-awesome": "^4.7.0",
"fs": "0.0.1-security",
"geo-coordinates-parser": "^1.2.3",
"idb": "^4.0.5",
"jquery": "^3.4.1",
"leaflet": "^1.5.1",
"leaflet-editable": "^1.2.0",
"leaflet-sidebar-v2": "^3.2.1",

View File

@ -7,26 +7,34 @@
<script lang="js">
import { LMap, LMarker } from 'vue2-leaflet'
import L from 'leaflet'
import LEdit from 'leaflet-editable/src/Leaflet.Editable.js'
import {readGroundnetXML} from '../loaders/groundnet_loader'
export default {
name: 'edit-layer',
props: [],
created () {
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'BOUNDS') {
// 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[0] !== this.editingAirport) {
this.groundnet = readGroundnetXML(this.$store.state.Settings.settings.airportsDirectory, airportsToLoad[0])
this.groundnet.addTo(this.$parent.mapObject)
this.editingAirport = airportsToLoad[0]
}
// console.log(this.groundnet)
}
})
},
mounted () {
console.log(LMap)
console.log(LMarker)
console.log(L)
this.$store.dispatch('getAirports')
console.log(this.$store.state.Airports.airports)
LMap.on('dragend', function onDragEnd () {
var width = L.Map.getBounds().getEast() - L.Map.getBounds().getWest()
var height = L.Map.getBounds().getNorth() - L.Map.getBounds().getSouth()
console.log(
'center:' + L.Map.getCenter() + '\n' +
'width:' + width + '\n' +
'height:' + height + '\n' +
'size in pixels:' + L.Map.getSize()
)
})
console.log(LEdit)
},
beforeDestroy () {
this.remove()
@ -36,13 +44,21 @@
}
},
methods: {
visible (feature) {
let bounds = this.$store.state.Settings.bounds
let coordinates = feature.geometry.coordinates
let ret = bounds.getNorthEast().lat > Number(coordinates[1]) &&
bounds.getNorthEast().lng > Number(coordinates[0])
let ret2 = bounds.getSouthWest().lat < Number(coordinates[1]) &&
bounds.getSouthWest().lng < Number(coordinates[0])
return ret && ret2
},
deferredMountedTo (parent) {
console.log(parent)
console.log(L)
this.layerGroup = L.layerGroup()
},
remove () {
if (this.sidebar) {
this.$parent.removeLayer(this.sidebar)
if (this.layerGroup) {
this.$parent.removeLayer(this.layerGroup)
}
},
add () {
@ -52,7 +68,12 @@
}
},
computed: {
edit: function () {
console.log('Zoom : ' + this.$store.state.Settings.zoom)
if (this.$store.state.Settings.zoom > 12) {
console.log()
}
}
}
}
</script>

View File

@ -2,6 +2,7 @@
<l-map
:zoom="zoom"
:center="center"
:options="options"
@update:zoom="zoomUpdated"
@update:center="centerUpdated"
@update:bounds="boundsUpdated"
@ -10,7 +11,7 @@
<!--<l-marker :lat-lng="marker"></l-marker>-->
<LeafletSidebar></LeafletSidebar>
<EditLayer></EditLayer>
<l-layer-group layerType="overlay" name="Sources">
<l-layer-group layerType="overlay" name="airports" ref="airportLayer">
<l-circle
v-for="(item, index) in this.$store.state.Airports.airports"
:key="index"
@ -43,14 +44,15 @@
components: { LMap, LTileLayer, LMarker, LCircle, LeafletSidebar, EditLayer, LLayerGroup },
props: [],
mounted () {
this.$store.dispatch('getAirports')
},
data () {
return {
url: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
marker: L.latLng(47.413220, -1.219482),
airports: this.$store.state.Airports.airports
airports: this.$store.state.Airports.airports,
options: {editable: true}
}
},
methods: {
@ -60,12 +62,13 @@
},
zoomUpdated (zoom) {
this.$store.commit('ZOOM', zoom)
console.log(this.$refs.airportLayer.setVisible(zoom < 12))
},
centerUpdated (center) {
this.$store.commit('CENTER', center)
},
boundsUpdated (bounds) {
this.bounds = bounds
this.$store.commit('BOUNDS', bounds)
}
},
computed: {

View File

@ -26,10 +26,6 @@
<AirportEdit></AirportEdit>
</div>
<div class="leaflet-sidebar-pane" id="scan">
<h1 class="leaflet-sidebar-header">
Scanning
<div class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></div>
</h1>
<RunScan></RunScan>
</div>
<div class="leaflet-sidebar-pane" id="settings">

View File

@ -1,32 +1,55 @@
<template>
<div>
<h1 class="leaflet-sidebar-header">
Scanning
<div class="leaflet-sidebar-close">
<i class="fa fa-caret-left"></i>
</div>
</h1>
<el-container direction="vertical">
<el-button @click="scanAPT()">Scan APT File</el-button>
<el-button @click="scanGroundnets()">Scan Groundnet Files</el-button>
<el-button @click="scanTraffic()">Scan Traffic Files</el-button>
<el-input placeholder="Search" v-model="searchterm" class="input-with-select">
</el-input>
</el-container>
<li v-for="item in searched" v-bind:key="item.icao"><el-link type="primary" @click="goto(item.icao)">{{ item.icao }} {{ item.name }}</el-link></li>
</div>
</template>
<script lang="js">
// import scanner from '../utils/scan'
import fileUrl from 'file-url'
import {Table, TableColumn} from 'element-ui'
const path = require('path')
export default {
name: 'run-scan',
components: {},
components: { [Table.name]: Table,
[TableColumn.name]: TableColumn},
props: [],
mounted () {
},
beforeDestroy () {
},
data () {
return {
}
// this.$store.dispatch('getAirportsUnfiltered')
return {searchterm: this.searchterm}
},
methods: {
goto (icao) {
console.log(icao)
let airports = this.$store.state.Airports.airports
.filter(a => a.properties.icao.match(icao))
if (airports.length > 0) {
this.$store.commit('CENTER', [airports[0].geometry.coordinates[1], airports[0].geometry.coordinates[0]])
}
},
formatter (row, column) {
console.log('Row ' + row)
return row
},
scanAPT () {
try {
const winURL = process.env.NODE_ENV === 'development'
@ -105,6 +128,12 @@
}
},
computed: {
searched: function () {
return this.$store.state.Airports.airports
.filter(a => a.properties.icao.match(this.searchterm) || a.properties.name.match(this.searchterm))
// .map(a => console.log(a.properties))
.map(a => ({ icao: a.properties.icao, name: a.properties.name }))
}
}
}
</script>

View File

@ -0,0 +1,109 @@
/* eslint-disable */
const convert = require('geo-coordinates-parser');
const leaflet = require('leaflet');
const turf = require('@turf/turf');
const util = require('util');
var $ = require('jquery');
L.ParkingSpot = L.Circle.extend({
id: String,
attributes: Object,
createDirection: function () {
if (this.direction === undefined ) {
var start = this._latlng;
var options = { units: 'kilometers' };
var end = turf.destination([start.lat, start.lng], this.attributes.radius / 1000, this.attributes.heading - 180, options);
// Resize, since leaflet is wrong
var rad2 = start.distanceTo(end.geometry.coordinates);
this.setRadius(rad2);
this.editor._resizeLatLng.__vertex.setLatLng(end.geometry.coordinates);
this.direction = L.polyline([start, end.geometry.coordinates]);
this.direction.addTo(map);
this.on('editable:drawing:move', function (event) {
this.updateDirectionFromVertex();
});
}
},
//
updateVertexFromDirection: function () {
if (this.editEnabled()) {
var start = this._latlng;
var options = { units: 'kilometers' };
var end = turf.destination([start.lat, start.lng], this.attributes.radius / 1000, this.attributes.heading - 180, options);
// Resize, since leaflet is wrong
var rad2 = start.distanceTo(end.geometry.coordinates);
this.setRadius(rad2);
if(this.editor._resizeLatLng.__vertex!== undefined){
this.editor._resizeLatLng.__vertex.setLatLng(end.geometry.coordinates);
}
this.direction.setLatLngs([start, end.geometry.coordinates]);
}
},
updateDirectionFromVertex: function () {
if (this.editEnabled()) {
var start = this._latlng;
var end = this.editor._resizeLatLng.__vertex.getLatLng();
var heading = turf.bearing([start.lat, start.lng], [end.lat, end.lng]);
this.attributes.heading = heading + 180;
this.direction.setLatLngs([start, end]);
}
},
_getLatRadius: function () {
return this._mRadius;
},
_getLngRadius: function () {
return this._mRadius;
},
});
var parkingSpot = function (n, layerGroup) {
//console.log(n.attr('lat') + " " + n.attr('lon'));
var latlon = convert(n.attr('lat') + " " + n.attr('lon'));
//console.log(latlon.decimalLatitude);
//console.log(convert(n.attr('lat') + " " + n.attr('lon')).decimalLongitude);
const circle = new L.ParkingSpot([latlon.decimalLatitude, latlon.decimalLongitude], { radius: n.attr('radius') });
circle.on('add', function (event) {
console.log(event);
});
circle.id = n.attr('index');
/*
<Parking index="2"
type="gate"
name="A6"
number=""
lat="N44 52.799"
lon="W93 11.947"
heading="-147.51"
radius="18"
pushBackRoute="541"
airlineCodes="VIR,KAL,DAL,KLM" />
*/
//circle.attributes = { type: n.attr('type'), name: n.attr('name'), radius: Number(n.attr('radius')), airlineCodes: n.attr('airlineCodes'), heading: Number(n.attr('heading')) };
$.each( n.attrs, function( key, value ) {
console.log( key + "\t" + value);
if(isNaN(value))
circle.attributes[ key ] = value;
else
circle.attributes[ key ] = Number( value);
});
circle.addTo(layerGroup);
// circle.enableEdit();
circle.on('dblclick', function (event) { L.DomEvent.stop(); circle.toggleEdit(); });
circle.on('click', function (event) {
console.log("Click : " + event.target);
});
// circle.createDirection();
return circle;
}
module.exports = parkingSpot;

View File

@ -0,0 +1,214 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
var xamel = require('xamel');
const convert = require('geo-coordinates-parser');
const markers = require('./markers');
const parkingSpot = require('./ParkingSpot.js');
const util = require('util');
console.log(convert.formats);
var featureLookup = {};
exports.readGroundnetXML = function (fDir, icao) {
try {
layerGroup = L.layerGroup();
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.xml');
if (f == null || !fs.existsSync(f))
return;
var features = new Array();
// map.on("editable:vertex:new", function (event) {
// log.console("Add vertex " + event);
// });
var xmlGroundnet = fs.readFileSync(f, 'utf8').toString();
xamel.parse(xmlGroundnet, function (err, xml) {
console.debug("parsed " + path.basename(f));
if (err !== null) {
console.error("Error in " + airline);
throw err;
}
var parkingNodes = xml.find('groundnet/parkingList/Parking');
console.log("Parking Nodes" + parkingNodes.length);
var merged = new Array();
var nodesLookup = {};
parkingNodes.map(n => {
var circle = parkingSpot(n, layerGroup);
circle.on('editable:drawing:move', function (event) {
// console.log("Follow 1: " + event.sourceTarget );
// console.log("Follow 2: " + event.sourceTarget._dragStartTarget );
// console.log("Follow 3: " + event.target.editor._resizeLatLng.__vertex );
// console.log("Follow 4: " + event.target.editor._resizeLatLng.__vertex._icon );
// console.log("Follow 5: " + (event.target.editor._resizeLatLng.__vertex === event.sourceTarget) );
// console.log("Follow 6: " + (event.target.editor._resizeLatLng.__vertex._icon === event.sourceTarget._element) );
// console.log("Follow 7: " + (event.target.editor._resizeLatLng.__vertex._icon === event.sourceTarget._dragStartTarget) );
// console.log("Follow 8: " + (event.target.editor._resizeLatLng.__vertex._icon._leaflet_id === event.sourceTarget._dragStartTarget._leaflet_id) );
// Is it the edit vertex moving?
if(event.target.editor._resizeLatLng.__vertex._icon !== event.sourceTarget._element){
follow(event.target.id, event);
}
});
nodesLookup[n.attr('index')] = n;
featureLookup[n.attr('index')] = new Array();
featureLookup[n.attr('index')].push(circle);
features.push(circle);
}).sort();
// Get all nodes into the lookup
var taxiNodes = xml.find('groundnet/TaxiNodes/node');
taxiNodes.map(n => {
//attrs.lat
//console.log(n.attr('lat') + " " + n.attr('lon'));
var latlon = convert(n.attr('lat') + " " + n.attr('lon'));
//console.log(latlon.decimalLatitude);
nodesLookup[n.attr('index')] = n;
}).sort();
var taxiArcs = xml.find('groundnet/TaxiWaySegments/arc');
taxiArcs.map(n => {
var begin = nodesLookup[n.attr('begin')];
var end = nodesLookup[n.attr('end')];
if (typeof begin !== 'undefined' && typeof end !== 'undefined') {
var bidirectional = false;
if (typeof featureLookup[n.attr('begin')] !== 'undefined') {
featureLookup[n.attr('begin')].forEach(element => {
if (element instanceof L.TaxiwaySegment && element.end === n.attr('begin') && element.begin === n.attr('end')) {
element.bidirectional = true;
bidirectional = true;
}
});
}
if (typeof featureLookup[n.attr('end')] !== 'undefined') {
featureLookup[n.attr('end')].forEach(element => {
if (element instanceof L.TaxiwaySegment && element.end === n.attr('begin') && element.begin === n.attr('end')) {
element.bidirectional = true;
bidirectional = true;
}
});
}
if (!bidirectional) {
var beginlatlon = convert(begin.attr('lat') + " " + begin.attr('lon'));
var endlatlon = convert(end.attr('lat') + " " + end.attr('lon'));
const polyline = new L.TaxiwaySegment([[beginlatlon.decimalLatitude, beginlatlon.decimalLongitude], [endlatlon.decimalLatitude, endlatlon.decimalLongitude]], { radius: n.attr('radius') }).addTo(layerGroup);
polyline.begin = begin.attr('index');
polyline.end = end.attr('index');
// polyline.enableEdit();
polyline.on('dblclick', function (event) { L.DomEvent.stop; polyline.toggleEdit; });
polyline.on('click', function (event) {
console.log(event);
});
polyline.on('editable:drawing:move', function (event) {
console.log(event.target);
if (dragIndex >= 0) {
follow(dragIndex, event);
}
});
var dragIndex = -1;
polyline.on('editable:vertex:dragstart', function (event) {
console.log(event.vertex);
console.log(event.target);
if (typeof featureLookup[event.target.begin] !== 'undefined') {
featureLookup[event.target.begin].forEach(element => {
if (element instanceof L.ParkingSpot) {
dragIndex = event.target.begin;
}
else if (element instanceof L.TaxiwaySegment) {
if (element.getLatLngs()[0].equals(event.vertex.getLatLng())) {
dragIndex = element.begin;
}
if (element.getLatLngs()[element.getLatLngs().length - 1].equals(event.vertex.getLatLng())) {
dragIndex = element.end;
}
}
});
}
if (typeof featureLookup[event.target.end] !== 'undefined') {
featureLookup[event.target.end].forEach(element => {
if (element instanceof L.ParkingSpot) {
dragIndex = event.target.end;
}
});
}
});
polyline.on('editable:vertex:dragend', function (event) {
console.log(event.vertex);
if (dragIndex > 0) {
featureLookup[dragIndex].forEach(element => {
if (element instanceof L.ParkingSpot) {
//element.setLatLng(event);
console.log(element);
}
});
}
dragIndex = -1;
});
polyline.addTo(layerGroup);
if (typeof featureLookup[polyline.begin] === 'undefined') {
featureLookup[polyline.begin] = new Array();
}
if (typeof featureLookup[polyline.end] === 'undefined') {
featureLookup[polyline.end] = new Array();
}
featureLookup[polyline.begin].push(polyline);
featureLookup[polyline.end].push(polyline);
}
}
}).sort();
return layerGroup;
});
// var jsonAirports = JSON.parse(geoJSON);
// return jsonAirports;
} catch (error) {
console.error(error);
}
return layerGroup;
}
follow = function (dragIndex, event) {
featureLookup[dragIndex].forEach(element => {
if(element !== event.target){
if (element instanceof L.ParkingSpot) {
element.disableEdit();
element.setLatLng(event.latlng);
// element.enableEdit();
element.updateVertexFromDirection();
}
else if (element instanceof L.TaxiwaySegment) {
if (element.begin === dragIndex) {
element.getLatLngs()[0].update(event.latlng);
element.setLatLngs(element.getLatLngs());
element.updateBeginVertex(event.latlng);
element.updateMiddle();
}
if (element.end === dragIndex) {
element.getLatLngs()[element.getLatLngs().length - 1].update(event.latlng);
element.setLatLngs(element.getLatLngs());
element.updateEndVertex(event.latlng);
element.updateMiddle();
}
}
}
});
}

View File

@ -0,0 +1,33 @@
/* eslint-disable */
var L = require('leaflet');
L.TaxiwaySegment = L.Polyline.extend({
begin: String,
end: String,
bidirectional: Boolean,
updateBeginVertex : function (latlng) {
this._latlngs[0].__vertex.setLatLng(latlng);
},
updateEndVertex : function (latlng) {
this._latlngs[1].__vertex.setLatLng(latlng);
},
updateMiddle: function () {
this._latlngs.forEach(element => {
if(element.__vertex.middleMarker){
element.__vertex.middleMarker.updateLatLng();
}
});
}
});
L.MagneticVertex = L.Editable.VertexMarker.extend({
updateMiddle: function () {
if (this.middleMarker) this.middleMarker.updateLatLng();
var next = this.getNext();
if (next && next.middleMarker) next.middleMarker.updateLatLng();
}
});

View File

@ -3,10 +3,11 @@ import idb from '../api/airports';
const ADD_AIRPORT = 'ADD_AIRPORT';
const SET_AIRPORTS = 'SET_AIRPORTS';
const SET_UNFILTERED_AIRPORTS = 'SET_UNFILTERED_AIRPORTS';
const RESET_AIRPORTS = 'RESET_AIRPORTS';
const state = {
airports: []
airports: [], unfilteredairports:[]
}
const mutations = {
@ -17,6 +18,9 @@ const mutations = {
SET_AIRPORTS (state, airports) {
state.airports = airports;
},
SET_UNFILTERED_AIRPORTS (state, airports) {
state.unfilteredairports = airports;
},
RESET_AIRPORTS (state) {
state.airports = [];
},
@ -30,14 +34,17 @@ const actions = {
async getAirports(context) {
context.commit(RESET_AIRPORTS);
let airports = await idb.getAirports();
// let newAirports = [];
// airports.forEach(c => {
// newAirports.push(c);
// });
context.commit(SET_AIRPORTS, airports
.filter(point => typeof point.geometry.coordinates !== "undefined" )
.filter(point => point.properties.flights > 0 ));
},
async getAirportsUnfiltered(context) {
context.commit(RESET_AIRPORTS);
let airports = await idb.getAirports();
context.commit(SET_UNFILTERED_AIRPORTS, airports
.filter(point => typeof point.geometry.coordinates !== "undefined" )
.filter(point => point.properties.flights > 0 ));
},
async saveAirport(context, airport) {
await idb.saveAirport(airport);
}}

View File

@ -1,7 +1,8 @@
const state = {
settings: { flightgearDirectory: '.' },
zoom: 14,
center: [47.413220, -1.219482]
center: [47.413220, -1.219482],
bounds: undefined
}
const mutations = {
@ -17,8 +18,10 @@ const mutations = {
},
'CENTER' (state, center) {
state.center = center
},
'BOUNDS' (state, bounds) {
state.bounds = bounds
}
}
const plugins = []

View File

@ -8,6 +8,12 @@
// const homedir = require('os').homedir();
// const apt = require('apt.js');
/**
* Iterates over an array with a async function and await
* @param {*} array The array being iterated over
* @param {*} callback
*/
async function asyncForEach(array, callback) {
console.log("AsyncForEach Len " + array.length);
for (let index = 0; index < array.length; index++) {
@ -69,10 +75,39 @@ async function scanGroundnetFiles(p, features) {
});
}
async function resetAI() {
var promise = new Promise(function (resolve, reject) {
try {
var objectStore = features.transaction("airports", 'readwrite').objectStore("airports");
objectStore.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
cursor.value.properties.flights = 0
cursor.value.properties.airlines = [];
console.log("Name for SSN " + cursor.key + " is " + cursor.value.properties.name);
cursor.update(cursor.value);
cursor.continue();
}
else {
console.log("No more entries!");
resolve();
}
};
} catch (error) {
console.log(error);
reject(error);
}
});
return promise;
}
async function scanTrafficFiles(p, features) {
var promise = new Promise(function (resolve, reject) {
try {
console.log('Start Groundnets ' + p);
console.log('Start Traffic ' + p);
resetAI().then(f => {
var files = traverseDir(p);
console.log(files);
@ -97,6 +132,9 @@ async function scanTrafficFiles(p, features) {
features.close()
this.postMessage('DONE');
});
}
);
//walkDir(p, f => { readGroundnet(f, features) });
@ -131,7 +169,7 @@ function scanTrafficIntoDB(p, features) {
}
};
walkDir(p, f => { readAI(f, features) });
//walkDir(p, f => { readAI(f, features) });
console.log("Closing");
features.close();
this.postMessage('DONE');
@ -182,7 +220,7 @@ function readAI(f, apts) {
console.log(path.basename(f));
// Reset
var airline = path.basename(f).match('([^.]+)\.xml');
if (airline == null){
if (airline == null) {
resolve();
return;
}
@ -195,7 +233,7 @@ function readAI(f, apts) {
// console.log(flights);
var dat = tXml(xmlSource);
// console.log(dat);
if( !dat || !dat[0] || !dat[0].children || !dat[0].children[0] ) {
if (!dat || !dat[0] || !dat[0].children || !dat[0].children[0]) {
resolve();
return;
}
@ -227,7 +265,7 @@ function readAI(f, apts) {
asyncForEach(Object.keys(counts), async key => {
console.log(key);
await store(key, airline[0], counts[key]);
await store(key, airline[1], counts[key]);
}).then(t => {
console.log("Finished");
resolve();
@ -277,8 +315,10 @@ function store(icao, airline, value) {
}
feature.properties.flights += value;
console.log("Airline " + airline);
if (!feature.properties.airlines.includes(airline)) {
feature.properties.airlines.push(airline);
feature.properties.airlines.sort();
}
console.log("ICAO : " + feature.properties.icao + " Flights : " + feature.properties.flights);
// Create another request that inserts the item back into the database
var updateAirportRequest = objectStore.put(feature);