mirror of
https://github.com/iNavFlight/inav-configurator.git
synced 2025-07-16 21:05:28 +03:00
Geozones
This commit is contained in:
parent
4dfd16dbd8
commit
6e7a04a817
15 changed files with 3063 additions and 694 deletions
BIN
images/icons/cf_icon_geozone_excl.png
Executable file
BIN
images/icons/cf_icon_geozone_excl.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
images/icons/cf_icon_geozone_incl.png
Executable file
BIN
images/icons/cf_icon_geozone_incl.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
21
images/icons/cf_icon_geozone_white.svg
Executable file
21
images/icons/cf_icon_geozone_white.svg
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="Capa_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 141.7 141.7"
|
||||||
|
enable-background="new 0 0 141.7 141.7"
|
||||||
|
xml:space="preserve"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
|
||||||
|
id="metadata955"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs953" /> <path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:3.5;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 58.476193,18.095785 120.30346,54.454907 81.598584,125.66517 33.678266,100.19703 15.079821,42.558605 Z"
|
||||||
|
id="path1" /></svg>
|
After Width: | Height: | Size: 934 B |
8
js/fc.js
8
js/fc.js
|
@ -10,7 +10,8 @@ const ProgrammingPidStatus = require('./programmingPidStatus');
|
||||||
const WaypointCollection = require('./waypointCollection');
|
const WaypointCollection = require('./waypointCollection');
|
||||||
const OutputMappingCollection = require('./outputMapping');
|
const OutputMappingCollection = require('./outputMapping');
|
||||||
const SafehomeCollection = require('./safehomeCollection');
|
const SafehomeCollection = require('./safehomeCollection');
|
||||||
const FwApproachCollection = require('./fwApproachCollection')
|
const FwApproachCollection = require('./fwApproachCollection');
|
||||||
|
const GeozoneCollection = require('./geozoneCollection');
|
||||||
const { PLATFORM } = require('./model')
|
const { PLATFORM } = require('./model')
|
||||||
const VTX = require('./vtx');
|
const VTX = require('./vtx');
|
||||||
const BitHelper = require('./bitHelper');
|
const BitHelper = require('./bitHelper');
|
||||||
|
@ -86,6 +87,7 @@ var FC = {
|
||||||
RATE_DYNAMICS: null,
|
RATE_DYNAMICS: null,
|
||||||
EZ_TUNE: null,
|
EZ_TUNE: null,
|
||||||
FLIGHT_MODES: null,
|
FLIGHT_MODES: null,
|
||||||
|
GEOZONES: null,
|
||||||
|
|
||||||
restartRequired: false,
|
restartRequired: false,
|
||||||
MAX_SERVO_RATE: 125,
|
MAX_SERVO_RATE: 125,
|
||||||
|
@ -597,6 +599,7 @@ var FC = {
|
||||||
|
|
||||||
|
|
||||||
this.FW_APPROACH = new FwApproachCollection();
|
this.FW_APPROACH = new FwApproachCollection();
|
||||||
|
this.GEOZONES = new GeozoneCollection();
|
||||||
|
|
||||||
this.OSD_CUSTOM_ELEMENTS = {
|
this.OSD_CUSTOM_ELEMENTS = {
|
||||||
settings: {customElementsCount: 0, customElementTextSize: 0},
|
settings: {customElementsCount: 0, customElementTextSize: 0},
|
||||||
|
@ -634,7 +637,8 @@ var FC = {
|
||||||
{bit: 2, group: 'other', name: 'TX_PROF_SEL', haveTip: false, showNameInTip: false},
|
{bit: 2, group: 'other', name: 'TX_PROF_SEL', haveTip: false, showNameInTip: false},
|
||||||
{bit: 0, group: 'other', name: 'THR_VBAT_COMP', haveTip: true, showNameInTip: true},
|
{bit: 0, group: 'other', name: 'THR_VBAT_COMP', haveTip: true, showNameInTip: true},
|
||||||
{bit: 3, group: 'other', name: 'BAT_PROFILE_AUTOSWITCH', haveTip: true, showNameInTip: true},
|
{bit: 3, group: 'other', name: 'BAT_PROFILE_AUTOSWITCH', haveTip: true, showNameInTip: true},
|
||||||
{bit: 31, group: 'other', name: "FW_AUTOTRIM", haveTip: true, showNameInTip: true}
|
{bit: 31, group: 'other', name: "FW_AUTOTRIM", haveTip: true, showNameInTip: true},
|
||||||
|
{bit: 4, group: 'other', name: "GEOZONE", haveTip: true, showNameInTip: true}
|
||||||
];
|
];
|
||||||
|
|
||||||
return features.reverse();
|
return features.reverse();
|
||||||
|
|
174
js/geozone.js
Normal file
174
js/geozone.js
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const GeozoneType = Object.freeze({
|
||||||
|
EXCULSIVE: 0,
|
||||||
|
INCLUSIVE: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const GeozoneShapes = Object.freeze({
|
||||||
|
CIRCULAR: 0,
|
||||||
|
POLYGON: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const GeozoneFenceAction = Object.freeze({
|
||||||
|
NONE: 0,
|
||||||
|
AVOID: 1,
|
||||||
|
POSHOLD: 2,
|
||||||
|
RTH: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
let GeozoneVertex = function (number, lat, lon) {
|
||||||
|
self = {};
|
||||||
|
|
||||||
|
self.setNumber = (data) => {
|
||||||
|
number = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getNumber = () => {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setLat = (data) => {
|
||||||
|
lat = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getLat = () => {
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setLon = (data) => {
|
||||||
|
lon = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getLon = () => {
|
||||||
|
return lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getLatMap = () => {
|
||||||
|
return lat / 1e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getLonMap = () => {
|
||||||
|
return lon / 1e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Geozone = function (type, shape, minAltitude, maxAltitude, radius, fenceAction, vertices, number = 0) {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
self.setNumber = (data) => {
|
||||||
|
number = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getNumber = () => {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setType = (data) => {
|
||||||
|
type = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getType = () => {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setShape = (data) => {
|
||||||
|
shape = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getShape = () => {
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setMinAltitude = (data) => {
|
||||||
|
minAltitude = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getMinAltitude = () => {
|
||||||
|
return minAltitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setMaxAltitude = (data) => {
|
||||||
|
maxAltitude = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getMaxAltitude = () => {
|
||||||
|
return maxAltitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setRadius = (data) => {
|
||||||
|
radius = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getRadius = () => {
|
||||||
|
return radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setFenceAction = (data) => {
|
||||||
|
fenceAction = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getFenceAction = () => {
|
||||||
|
return fenceAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getFirstVertex = () => {
|
||||||
|
return vertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getLastVertex = () => {
|
||||||
|
return vertices[vertices.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getVerticesCount = () => {
|
||||||
|
return vertices.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getVertex = (idx) => {
|
||||||
|
return vertices[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setVertex = (idx, vertex) => {
|
||||||
|
vertices[idx] = vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getVertices = () => {
|
||||||
|
return vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setVertices = (data) => {
|
||||||
|
vertices = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getVerticesCount = () => {
|
||||||
|
return vertices.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.insertVertex = (idx, newVertex) => {
|
||||||
|
vertices.forEach(vertex => {
|
||||||
|
if (vertex.getNumber() >= idx) {
|
||||||
|
vertex.setNumber(vertex.getNumber() + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
vertices.splice(idx, 0, newVertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dropVertex = (idx) => {
|
||||||
|
vertices.forEach(vertex => {
|
||||||
|
if (vertex.getNumber() >= idx) {
|
||||||
|
vertex.setNumber(vertex.getNumber() - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
vertices.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.resetVertices = () => {
|
||||||
|
vertices = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { Geozone, GeozoneVertex, GeozoneType, GeozoneShapes, GeozoneFenceAction };
|
151
js/geozoneCollection.js
Normal file
151
js/geozoneCollection.js
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const BitHelper = require('./bitHelper');
|
||||||
|
const { GeozoneShapes } = require('./geozone');
|
||||||
|
|
||||||
|
let GeozoneCollection = function() {
|
||||||
|
let self = {},
|
||||||
|
data = [],
|
||||||
|
maxVertices = 126,
|
||||||
|
maxZones = 62;
|
||||||
|
|
||||||
|
self.getMaxVertices = () => {
|
||||||
|
return maxVertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getMaxZones = () => {
|
||||||
|
return maxZones;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.put = (element) => {
|
||||||
|
element.setNumber(data.length);
|
||||||
|
data.push(element);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.get = () => {
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.at = (idx) => {
|
||||||
|
return data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isEmpty = () => {
|
||||||
|
return data.length == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.flush = () => {
|
||||||
|
data = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
self.first = () => {
|
||||||
|
return !self.isEmpty() ? data[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last = () => {
|
||||||
|
return !self.isEmpty() ? data[data.length - 1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateGeozone = (newGeozone) => {
|
||||||
|
data[newGeozone.getNumber()] = newGeozone;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.geozoneCount = () => {
|
||||||
|
return data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getUsedVerticesCount = () => {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
data.forEach(zone => {
|
||||||
|
if (zone.getShape() == GeozoneShapes.POLYGON) {
|
||||||
|
count += zone.getVerticesCount();
|
||||||
|
} else {
|
||||||
|
count += 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.drop = (idx) => {
|
||||||
|
data.forEach(zone => {
|
||||||
|
if (zone.getNumber() >= idx) {
|
||||||
|
zone.setNumber(zone.getNumber() - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.insert = (geoZone, idx) => {
|
||||||
|
data.forEach(zone => {
|
||||||
|
if (zone.getNumber() >= idx) {
|
||||||
|
zone.setNumber(zone.getNumber() + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.splice(idx, 0, geoZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.extractBufferVertices = (zoneId, vertexId) => {
|
||||||
|
let buffer = [];
|
||||||
|
if (zoneId < self.geozoneCount()) {
|
||||||
|
let zone = data[zoneId];
|
||||||
|
if (vertexId < zone.getVerticesCount()) {
|
||||||
|
buffer.push(zoneId);
|
||||||
|
let vertex = zone.getVertex(vertexId);
|
||||||
|
buffer.push(vertex.getNumber());
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLat(), 0));
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLat(), 1));
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLat(), 2));
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLat(), 3));
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLon(), 0));
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLon(), 1));
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLon(), 2));
|
||||||
|
buffer.push(BitHelper.specificByte(vertex.getLon(), 3));
|
||||||
|
if (zone.getShape() == GeozoneShapes.CIRCULAR) {
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getRadius(), 0));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getRadius(), 1));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getRadius(), 2));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getRadius(), 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer.length == 0) {
|
||||||
|
buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.extractBufferZone = (id) => {
|
||||||
|
let buffer = [];
|
||||||
|
if (id < self.geozoneCount()) {
|
||||||
|
let zone = data[id];
|
||||||
|
buffer.push(zone.getNumber());
|
||||||
|
buffer.push(zone.getType());
|
||||||
|
buffer.push(zone.getShape());
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 0));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 1));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 2));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMinAltitude(), 3));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 0));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 1));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 2));
|
||||||
|
buffer.push(BitHelper.specificByte(zone.getMaxAltitude(), 3));
|
||||||
|
buffer.push(zone.getFenceAction());
|
||||||
|
if (zone.getShape() == GeozoneShapes.CIRCULAR) {
|
||||||
|
buffer.push(2);
|
||||||
|
} else {
|
||||||
|
buffer.push(zone.getVerticesCount());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer = [id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = GeozoneCollection;
|
|
@ -243,6 +243,11 @@ var MSPCodes = {
|
||||||
MSP2_INAV_SERVO_CONFIG: 0x2200,
|
MSP2_INAV_SERVO_CONFIG: 0x2200,
|
||||||
MSP2_INAV_SET_SERVO_CONFIG: 0x2201,
|
MSP2_INAV_SET_SERVO_CONFIG: 0x2201,
|
||||||
|
|
||||||
|
MSP2_INAV_GEOZONE: 0x2210,
|
||||||
|
MSP2_INAV_SET_GEOZONE: 0x2211,
|
||||||
|
MSP2_INAV_GEOZONE_VERTEX: 0x2212,
|
||||||
|
MSP2_INAV_SET_GEOZONE_VERTICE: 0x2213
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = MSPCodes;
|
module.exports = MSPCodes;
|
|
@ -21,6 +21,7 @@ const Waypoint = require('./../waypoint');
|
||||||
const mspDeduplicationQueue = require('./mspDeduplicationQueue');
|
const mspDeduplicationQueue = require('./mspDeduplicationQueue');
|
||||||
const mspStatistics = require('./mspStatistics');
|
const mspStatistics = require('./mspStatistics');
|
||||||
const settingsCache = require('./../settingsCache');
|
const settingsCache = require('./../settingsCache');
|
||||||
|
const {Geozone, GeozoneVertex, GeozoneShapes } = require('./../geozone');
|
||||||
|
|
||||||
var mspHelper = (function () {
|
var mspHelper = (function () {
|
||||||
var self = {};
|
var self = {};
|
||||||
|
@ -1590,10 +1591,63 @@ var mspHelper = (function () {
|
||||||
FC.OSD_CUSTOM_ELEMENTS .items.push(customElement)
|
FC.OSD_CUSTOM_ELEMENTS .items.push(customElement)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MSPCodes.MSP2_INAV_GPS_UBLOX_COMMAND:
|
case MSPCodes.MSP2_INAV_GPS_UBLOX_COMMAND:
|
||||||
// Just and ACK from the fc.
|
// Just and ACK from the fc.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MSPCodes.MSP2_INAV_GEOZONE:
|
||||||
|
var geozone = new Geozone(
|
||||||
|
data.getUint8(0),
|
||||||
|
data.getUint8(1),
|
||||||
|
data.getInt32(2, true),
|
||||||
|
data.getInt32(6, true),
|
||||||
|
0,
|
||||||
|
data.getInt8(10, true),
|
||||||
|
null,
|
||||||
|
data.getUint8(12, true),
|
||||||
|
);
|
||||||
|
let verticesCount = data.getUint8(11, true);
|
||||||
|
if (verticesCount == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (geozone.getShape() == GeozoneShapes.POLYGON) {
|
||||||
|
geozone.setVertices(new Array(verticesCount));
|
||||||
|
} else {
|
||||||
|
geozone.setVertices(new Array(1));
|
||||||
|
}
|
||||||
|
FC.GEOZONES.put(geozone);
|
||||||
|
break;
|
||||||
|
case MSPCodes.MSP2_INAV_GEOZONE_VERTEX:
|
||||||
|
if (data.buffer.byteLength == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var zoneID = data.getUint8(0);
|
||||||
|
var vertexId = data.getUint8(1);
|
||||||
|
var geozone = FC.GEOZONES.at(zoneID);
|
||||||
|
if (zoneID < FC.GEOZONES.geozoneCount()) {
|
||||||
|
geozone.setVertex(
|
||||||
|
vertexId,
|
||||||
|
new GeozoneVertex(
|
||||||
|
vertexId,
|
||||||
|
data.getUint32(2, true),
|
||||||
|
data.getUint32(6, true),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (geozone.getShape() == GeozoneShapes.CIRCULAR) {
|
||||||
|
geozone.setRadius(data.getUint32(10, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSPCodes.MSP2_INAV_SET_GEOZONE_VERTICE:
|
||||||
|
console.log("Geozone vertex saved")
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSPCodes.MSP2_INAV_SET_GEOZONE:
|
||||||
|
console.log("Geozone saved")
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log('Unknown code detected: 0x' + dataHandler.code.toString(16));
|
console.log('Unknown code detected: 0x' + dataHandler.code.toString(16));
|
||||||
} else {
|
} else {
|
||||||
|
@ -3071,6 +3125,68 @@ var mspHelper = (function () {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.loadGeozones = function (callback) {
|
||||||
|
FC.GEOZONES.flush();
|
||||||
|
let geozoneID = -1;
|
||||||
|
let vertexID = -1;
|
||||||
|
nextGeozone();
|
||||||
|
|
||||||
|
function nextVertex() {
|
||||||
|
vertexID++;
|
||||||
|
let zone = FC.GEOZONES.at(geozoneID);
|
||||||
|
if (!zone || zone.getVerticesCount() == 0) {
|
||||||
|
nextGeozone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vertexID < FC.GEOZONES.at(geozoneID).getVerticesCount() - 1 && zone.getShape() == GeozoneShapes.POLYGON) {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE_VERTEX, [geozoneID, vertexID], false, nextVertex);
|
||||||
|
} else {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE_VERTEX, [geozoneID, vertexID], false, nextGeozone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextGeozone() {
|
||||||
|
geozoneID++;
|
||||||
|
vertexID = -1;
|
||||||
|
if (geozoneID < FC.GEOZONES.getMaxZones() - 1) {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE, [geozoneID], false, nextVertex);
|
||||||
|
} else {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_GEOZONE, [geozoneID], false, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.saveGeozones = function (callback) {
|
||||||
|
let geozoneID = -1;
|
||||||
|
let vertexID = -1;
|
||||||
|
nextGeozone()
|
||||||
|
|
||||||
|
function nextVertex() {
|
||||||
|
vertexID++;
|
||||||
|
|
||||||
|
let zone = FC.GEOZONES.at(geozoneID);
|
||||||
|
if (!zone || zone.getVerticesCount() == 0) {
|
||||||
|
nextGeozone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vertexID < FC.GEOZONES.at(geozoneID).getVerticesCount() - 1) {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE_VERTICE, FC.GEOZONES.extractBufferVertices(geozoneID, vertexID), false, nextVertex);
|
||||||
|
} else {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE_VERTICE, FC.GEOZONES.extractBufferVertices(geozoneID, vertexID), false, nextGeozone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextGeozone() {
|
||||||
|
geozoneID++;
|
||||||
|
vertexID = -1;
|
||||||
|
if (geozoneID < FC.GEOZONES.getMaxZones() - 1) {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE, FC.GEOZONES.extractBufferZone(geozoneID), false, nextVertex);
|
||||||
|
} else {
|
||||||
|
MSP.send_message(MSPCodes.MSP2_INAV_SET_GEOZONE, FC.GEOZONES.extractBufferZone(geozoneID), false, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self._getSetting = function (name) {
|
self._getSetting = function (name) {
|
||||||
|
|
||||||
const storedSetting = settingsCache.get(name);
|
const storedSetting = settingsCache.get(name);
|
||||||
|
|
|
@ -4288,6 +4288,15 @@
|
||||||
"osdElement_OSD_RANGEFINDER": {
|
"osdElement_OSD_RANGEFINDER": {
|
||||||
"message": "Rangefinder distance"
|
"message": "Rangefinder distance"
|
||||||
},
|
},
|
||||||
|
"osdElement_COURSE_NEXT_GEOZONE": {
|
||||||
|
"message": "Course to next Geozone"
|
||||||
|
},
|
||||||
|
"osdElement_HOR_DIST_TO_NEXT_GEOZONE": {
|
||||||
|
"message": "Horizontal distance to the next geozone"
|
||||||
|
},
|
||||||
|
"osdElement_VERT_DIST_TO_NEXT_GEOZONE": {
|
||||||
|
"message": "Vertical distance to the next geozone"
|
||||||
|
},
|
||||||
"osdSettingPLUS_CODE_DIGITS_HELP": {
|
"osdSettingPLUS_CODE_DIGITS_HELP": {
|
||||||
"message": "Precision at the equator: 10=13.9x13.9m; 11=2.8x3.5m; 12=56x87cm; 13=11x22cm."
|
"message": "Precision at the equator: 10=13.9x13.9m; 11=2.8x3.5m; 12=56x87cm; 13=11x22cm."
|
||||||
},
|
},
|
||||||
|
@ -4645,6 +4654,147 @@
|
||||||
"missionSafehomeMaxSafehomesReached": {
|
"missionSafehomeMaxSafehomesReached": {
|
||||||
"message": "Maximum number of safehomes reached."
|
"message": "Maximum number of safehomes reached."
|
||||||
},
|
},
|
||||||
|
"missionGeozoneHead": {
|
||||||
|
"message": "Geozones"
|
||||||
|
},
|
||||||
|
"missionGeozoneEdit": {
|
||||||
|
"message": "Edit Geozone $1"
|
||||||
|
},
|
||||||
|
"missionGeozoneSaveAndReboot": {
|
||||||
|
"message": "Save Eeprom Geozones and reboot"
|
||||||
|
},
|
||||||
|
"missionGeozoneLoad": {
|
||||||
|
"message": "Load Eeprom Geozones"
|
||||||
|
},
|
||||||
|
"missionGeozone": {
|
||||||
|
"message": "-Geozone "
|
||||||
|
},
|
||||||
|
"missionGezoneType": {
|
||||||
|
"message": "Type"
|
||||||
|
},
|
||||||
|
"missionGezoneShape": {
|
||||||
|
"message": "Shape"
|
||||||
|
},
|
||||||
|
"missionGeozoneTypePolygon": {
|
||||||
|
"message": "Polygon"
|
||||||
|
},
|
||||||
|
"missionGeozoneTypeCircular": {
|
||||||
|
"message": "Circular"
|
||||||
|
},
|
||||||
|
"missionGeozoneMaxZonesReached": {
|
||||||
|
"message": "Maximum number of geozones reached."
|
||||||
|
},
|
||||||
|
"missionGeozoneMaxVerticesReached": {
|
||||||
|
"message": "Maximum number of geozone vertices reached."
|
||||||
|
},
|
||||||
|
"missionGeozoneWarning": {
|
||||||
|
"message": "At least one mission and one geozone are configured, please ensure that the mission does not violate the geozone."
|
||||||
|
},
|
||||||
|
"geozoneEdit": {
|
||||||
|
"message": "Edit Geozone "
|
||||||
|
},
|
||||||
|
"geozoneShape": {
|
||||||
|
"message": "Shape"
|
||||||
|
},
|
||||||
|
"geozoneInclusive": {
|
||||||
|
"message": "Inclusive"
|
||||||
|
},
|
||||||
|
"geozoneExcusive": {
|
||||||
|
"message": "Exclusive"
|
||||||
|
},
|
||||||
|
"geozoneMinAlt": {
|
||||||
|
"message": "Min. Alt (cm):"
|
||||||
|
},
|
||||||
|
"geozoneMaxAlt": {
|
||||||
|
"message": "Max. Alt (cm):"
|
||||||
|
},
|
||||||
|
"geozoneInfiniteAlt": {
|
||||||
|
"message": "0 = Infinite altitude"
|
||||||
|
},
|
||||||
|
"geozoneAction": {
|
||||||
|
"message": "Action:"
|
||||||
|
},
|
||||||
|
"geozoneActionNone": {
|
||||||
|
"message": "None"
|
||||||
|
},
|
||||||
|
"geozoneActionAvoid": {
|
||||||
|
"message": "Avoid"
|
||||||
|
},
|
||||||
|
"geozoneActionPosHold": {
|
||||||
|
"message": "Pos. Hold"
|
||||||
|
},
|
||||||
|
"geozoneActionRTH": {
|
||||||
|
"message": "RTH"
|
||||||
|
},
|
||||||
|
"geozoneRadius": {
|
||||||
|
"message": "Radius (cm):"
|
||||||
|
},
|
||||||
|
"geozoneVerices": {
|
||||||
|
"message": "Vertices:"
|
||||||
|
},
|
||||||
|
"": {
|
||||||
|
"message": ""
|
||||||
|
},
|
||||||
|
"featureGEOZONE": {
|
||||||
|
"message": "Geozone"
|
||||||
|
},
|
||||||
|
"featureGEOZONETip": {
|
||||||
|
"message": "Virtual perimeters for geographical areas (also called geofence) with automatically triggered actions when the perimeters are violated."
|
||||||
|
},
|
||||||
|
"GeozoneSettings": {
|
||||||
|
"message": "Geozone Settings"
|
||||||
|
},
|
||||||
|
"geozoneDetectionDistance": {
|
||||||
|
"message": "Detection distance"
|
||||||
|
},
|
||||||
|
"geozoneDetectionDistanceHelp": {
|
||||||
|
"message": "Distance from which a geozone is detected"
|
||||||
|
},
|
||||||
|
"geozoneAvoidAltitudeRange": {
|
||||||
|
"message": "Avoid altitude range"
|
||||||
|
},
|
||||||
|
"geozoneAvoidAltitudeRangeHelp": {
|
||||||
|
"message": "Altitude range in which an attempt is made to avoid a geozone upwards"
|
||||||
|
},
|
||||||
|
"geozoneSafeAltitudeDistance": {
|
||||||
|
"message": "Safe altitude distance"
|
||||||
|
},
|
||||||
|
"geozoneSafeAltitudeDistanceHelp": {
|
||||||
|
"message": "Vertical distance that must be maintained to the upper and lower limits of the zone."
|
||||||
|
},
|
||||||
|
"geozoneSafehomeAsInclusive": {
|
||||||
|
"message": "Safehome as inclusive"
|
||||||
|
},
|
||||||
|
"geozoneSafehomeAsInclusiveHelp": {
|
||||||
|
"message": "Treat nearest safehome as inclusive geozone"
|
||||||
|
},
|
||||||
|
"geozoneSafehomeZoneAction": {
|
||||||
|
"message": "Safehome zone action"
|
||||||
|
},
|
||||||
|
"geozoneSafehomeZoneActionHelp": {
|
||||||
|
"message": "Fence action for safehome zone"
|
||||||
|
},
|
||||||
|
"geozoneMrStopDistance": {
|
||||||
|
"message" : "Multirotor stop distance"
|
||||||
|
},
|
||||||
|
"geozoneMrStopDistanceHelp": {
|
||||||
|
"message" : "Distance in which multirotors stops before the border"
|
||||||
|
},
|
||||||
|
"geozoneNoWayHomeAction": {
|
||||||
|
"message": "No way home action"
|
||||||
|
},
|
||||||
|
"geozoneNoWayHomeActionHelp": {
|
||||||
|
"message": "Action if RTH with active geozones is unable to calculate a course to home. RTH: Return to home and ignore any geozones."
|
||||||
|
},
|
||||||
|
"missionGeozoneReboot": {
|
||||||
|
"message": "Do you want to save and reboot?"
|
||||||
|
},
|
||||||
|
"missionGeozoneAvailableZones": {
|
||||||
|
"message": "Available Geozones:"
|
||||||
|
},
|
||||||
|
"missionGeozoneAvailableVertices": {
|
||||||
|
"message": "Available Vertices:"
|
||||||
|
},
|
||||||
"missionMultiMissionHead": {
|
"missionMultiMissionHead": {
|
||||||
"message": "Multi Missions"
|
"message": "Multi Missions"
|
||||||
},
|
},
|
||||||
|
|
2368
package-lock.json
generated
2368
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -317,6 +317,58 @@
|
||||||
top: 140px;
|
top: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.geozone-settings {
|
||||||
|
top: 165px;
|
||||||
|
left: .5em;
|
||||||
|
}
|
||||||
|
.ol-touch .geozone-settings {
|
||||||
|
top: 165px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-mission-control .geozoneVerticesTable {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-mission-control .geozoneVerticesTable input[type="text"] {
|
||||||
|
font-size: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-mission-control .geozoneVerticesTable input[type="number"] {
|
||||||
|
font-size: 10px;
|
||||||
|
width: 100%;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chrome, Safari, Edge, Opera */
|
||||||
|
.tab-mission-control .geozoneVerticesTable input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-mission-control .geozoneVerticesTable thead {
|
||||||
|
display: table-header-group !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-mission-control .geozoneVerticesTable thead tr {
|
||||||
|
border-left: 1px solid #e4e4e4;
|
||||||
|
border-right: 1px solid #e4e4e4;
|
||||||
|
background-color: #828885;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
.tab-mission-control .geozoneVerticesTable td,
|
||||||
|
.tab-mission-control .geozoneVerticesTable th {
|
||||||
|
padding: 2px;
|
||||||
|
/* height: 2.5em; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-mission-control .geozoneVerticesTable tr:nth-child(even) td,
|
||||||
|
.tab-mission-control .geozoneVerticesTable tr:nth-child(even) th {
|
||||||
|
background-color: #ebe7e7;
|
||||||
|
}
|
||||||
|
|
||||||
.mission-control-template {
|
.mission-control-template {
|
||||||
top: 140px;
|
top: 140px;
|
||||||
left: .5em;
|
left: .5em;
|
||||||
|
|
|
@ -513,6 +513,48 @@
|
||||||
-->
|
-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="config-section gui_box grey">
|
||||||
|
<div class="gui_box_titlebar">
|
||||||
|
<div class="spacer_box_title" data-i18n="GeozoneSettings"></div>
|
||||||
|
</div>
|
||||||
|
<div class="spacer_box">
|
||||||
|
<div class="number">
|
||||||
|
<input type="number" id="geozoneDetectionDistance" data-unit="cm" data-setting="geozone_detection_distance" data-setting-multiplier="1" step="1" min="0" max="1000000" />
|
||||||
|
<label for="geozoneDetectionDistance"><span data-i18n="geozoneDetectionDistance"></span></label>
|
||||||
|
<div for="geozoneDetectionDistance" class="helpicon cf_tip" data-i18n_title="geozoneDetectionDistanceHelp"></div>
|
||||||
|
</div>
|
||||||
|
<div class="number">
|
||||||
|
<input type="number" id="geozoneAvoidAltitudeRange" data-unit="cm" data-setting="geozone_avoid_altitude_range" data-setting-multiplier="1" step="1" min="0" max="1000000" />
|
||||||
|
<label for="geozoneAvoidAltitudeRange"><span data-i18n="geozoneAvoidAltitudeRange"></span></label>
|
||||||
|
<div for="geozoneAvoidAltitudeRange" class="helpicon cf_tip" data-i18n_title="geozoneAvoidAltitudeRangeHelp"></div>
|
||||||
|
</div>
|
||||||
|
<div class="number">
|
||||||
|
<input type="number" id="geozoneSafeAltitudeDistance" data-unit="cm" data-setting="geozone_safe_altitude_distance" data-setting-multiplier="1" step="1" min="0" max="10000" />
|
||||||
|
<label for="geozoneSafeAltitudeDistance"><span data-i18n="geozoneSafeAltitudeDistance"></span></label>
|
||||||
|
<div for="geozoneSafeAltitudeDistance" class="helpicon cf_tip" data-i18n_title="geozoneSafeAltitudeDistanceHelp"></div>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" class="toggle update_preview" id="geozoneSafehomeAsInclusive" data-setting="geozone_safehome_as_inclusive" data-live="true" />
|
||||||
|
<label for="geozoneSafehomeAsInclusive"><span data-i18n="geozoneSafehomeAsInclusive"></span></label>
|
||||||
|
<div for="geozoneSafehomeAsInclusive" class="helpicon cf_tip" data-i18n_title="geozoneSafehomeAsInclusiveHelp"></div>
|
||||||
|
</div>
|
||||||
|
<div class="select">
|
||||||
|
<select id="geozoneSafehomeZoneAction" data-setting="geozone_safehome_zone_action"></select>
|
||||||
|
<label for="geozoneSafehomeZoneAction"><span data-i18n="geozoneSafehomeZoneAction"></span></label>
|
||||||
|
<div for="geozoneSafehomeZoneAction" class="helpicon cf_tip" data-i18n_title="geozoneSafehomeZoneActionHelp"></div>
|
||||||
|
</div>
|
||||||
|
<div class="number">
|
||||||
|
<input type="number" id="geozoneMrStopDistance" data-unit="cm" data-setting="geozone_mr_stop_distance" data-setting-multiplier="1" step="1" min="0" max="1000000" />
|
||||||
|
<label for="geozoneMrStopDistance"><span data-i18n="geozoneMrStopDistance"></span></label>
|
||||||
|
<div for="geozoneMrStopDistance" class="helpicon cf_tip" data-i18n_title="geozoneMrStopDistanceHelp"></div>
|
||||||
|
</div>
|
||||||
|
<div class="select">
|
||||||
|
<select id="geozoneNoWayHomeAction" data-setting="geozone_no_way_home_action"></select>
|
||||||
|
<label for="geozoneNoWayHomeAction"><span data-i18n="geozoneNoWayHomeAction"></span></label>
|
||||||
|
<div for="geozoneNoWayHomeAction" class="helpicon cf_tip" data-i18n_title="geozoneNoWayHomeActionHelp"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Left wrapper -->
|
<!-- Left wrapper -->
|
||||||
|
|
|
@ -62,6 +62,10 @@
|
||||||
<span data-i18n="missionTotalInfoMissionValid"></span>
|
<span data-i18n="missionTotalInfoMissionValid"></span>
|
||||||
<div id="missionValid" style="display: inline-block"></div>
|
<div id="missionValid" style="display: inline-block"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="infoGeozoneMissionWarning" style="padding-bottom: 2px;">
|
||||||
|
<span style="color: red" i18n-replaced data-i18n="warning"></span>
|
||||||
|
<div id="geozoneMissionWarning" style="display: inline-block" data-i18n="missionGeozoneWarning"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -281,6 +285,96 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="missionPlannerGeozones" class="gui_box grey" style="display: none">
|
||||||
|
<div class="gui_box_titlebar">
|
||||||
|
<div class="spacer_box_title" data-i18n="missionGeozoneHead"></div>
|
||||||
|
<div class="btnMenu btnMenuIcon save_btn">
|
||||||
|
<div id="showHideGeozonesButton">
|
||||||
|
<a class="ic_hide" href="#" title="Hide"></a>
|
||||||
|
</div>
|
||||||
|
<a id="cancelGeozone" class="ic_cancel" href="#" title="Cancel"></a>
|
||||||
|
<a id="addGeozone" class="ic_add" href="#" title="Add Geozone"></a>
|
||||||
|
<a id="saveEepromGeozoneButton" class="ic_save2Eprom" href="#" i18n_title="missionGeozoneSaveAndReboot"></a>
|
||||||
|
<a id="loadEepromGeozoneButton" class="ic_loadFromEprom" href="#" i18n_title="missionGeozoneLoad"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="spacer" id="geozoneContent">
|
||||||
|
<div style="padding-bottom: 2px;">
|
||||||
|
<span data-i18n="missionGeozoneAvailableZones"></span>
|
||||||
|
<span id="availableGeozones"></span>
|
||||||
|
</div>
|
||||||
|
<div id="infoAvailableVeritces" style="padding-bottom: 2px;">
|
||||||
|
<span data-i18n="missionGeozoneAvailableVertices"></span>
|
||||||
|
<span id="availableVertices"></span>
|
||||||
|
</div>
|
||||||
|
<div id="geozoneContentBox" style="display: none;" >
|
||||||
|
<div class="gui_box grey missionPlannerGeozone">
|
||||||
|
<div class="gui_box_titlebar">
|
||||||
|
<div class="spacer_box_title" data-i18n="geozoneEdit"></div>
|
||||||
|
<div class="btnMenu btnMenuIcon">
|
||||||
|
<div class="btnMenu-danger">
|
||||||
|
<a id="deleteGeozone" class="ic_removeAll" href="#" title="Delete"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="spacer" id="geozoneSingelContent">
|
||||||
|
<div class="point">
|
||||||
|
<label class="point-label" for="zoneType" data-i18n="geozoneShape"></label>
|
||||||
|
<select name="zoneShape" id="geozoneShape">
|
||||||
|
<option value="1" data-i18n="missionGeozoneTypePolygon"></option>
|
||||||
|
<option value="0" data-i18n="missionGeozoneTypeCircular"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="point">
|
||||||
|
<label class="point-label" for="zoneType" data-i18n="missionGezoneType"></label>
|
||||||
|
<select name="zoneType" id="geozoneType">
|
||||||
|
<option value="1" data-i18n="geozoneInclusive"></option>
|
||||||
|
<option value="0" data-i18n="geozoneExcusive"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="point">
|
||||||
|
<label class="point-label" for="zoneMinAlt" data-i18n="geozoneMinAlt"></label>
|
||||||
|
<input type="number" id="geozoneMinAlt"></input>
|
||||||
|
</div>
|
||||||
|
<div class="point">
|
||||||
|
<label class="point-label" for="zoneMaxAlt" data-i18n="geozoneMaxAlt"></label>
|
||||||
|
<input type="number" id="geozoneMaxAlt"></input>
|
||||||
|
<span data-i18n="geozoneInfiniteAlt"></span>
|
||||||
|
</div>
|
||||||
|
<div class="point">
|
||||||
|
<label class="point-label" for="zoneAction" data-i18n="geozoneAction">Action:</label>
|
||||||
|
<select name="zoneAction" id="geozoneAction">
|
||||||
|
<option value="0" data-i18n="geozoneActionNone"></option>
|
||||||
|
<option value="1" data-i18n="geozoneActionAvoid"></option>
|
||||||
|
<option value="2" data-i18n="geozoneActionPosHold"></option>
|
||||||
|
<option value="3" data-i18n="geozoneActionRTH"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="point">
|
||||||
|
<label class="point-label" for="raduis" data-i18n="geozoneRadius"></label>
|
||||||
|
<input type="number" id="geozoneRadius"></input>
|
||||||
|
</div>
|
||||||
|
<div class="point">
|
||||||
|
<label class="point-label" for="verticesTable" data-i18n="geozoneVerices"></label>
|
||||||
|
<table class="geozoneVerticesTable" id="verticesTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 20px"> </th>
|
||||||
|
<th style="width: 40px">#</th>
|
||||||
|
<th style="width: 120px" data-i18n="gpsLat"></th>
|
||||||
|
<th style="width: 120px" data-i18n="gpsLon"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="geozoneVerticesTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="gui_box grey" id="MPeditPoint" style="display: none">
|
<div class="gui_box grey" id="MPeditPoint" style="display: none">
|
||||||
<div class="gui_box_titlebar">
|
<div class="gui_box_titlebar">
|
||||||
<div class="spacer_box_title" data-i18n="editPointHead" id="EditPointNumber"></div>
|
<div class="spacer_box_title" data-i18n="editPointHead" id="EditPointNumber"></div>
|
||||||
|
|
|
@ -29,6 +29,8 @@ const SerialBackend = require('./../js/serial_backend');
|
||||||
const { distanceOnLine, wrap_360, calculate_new_cooridatnes } = require('./../js/helpers');
|
const { distanceOnLine, wrap_360, calculate_new_cooridatnes } = require('./../js/helpers');
|
||||||
const Plotly = require('./../js/libraries/plotly-latest.min');
|
const Plotly = require('./../js/libraries/plotly-latest.min');
|
||||||
const interval = require('./../js/intervals');
|
const interval = require('./../js/intervals');
|
||||||
|
const { Geozone, GeozoneVertex, GeozoneType, GeozoneShapes, GeozoneFenceAction } = require('./../js/geozone');
|
||||||
|
const GeozoneCollection = require('./../js/geozoneCollection');
|
||||||
|
|
||||||
var MAX_NEG_FW_LAND_ALT = -2000; // cm
|
var MAX_NEG_FW_LAND_ALT = -2000; // cm
|
||||||
|
|
||||||
|
@ -72,12 +74,19 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
let selectedSafehome;
|
let selectedSafehome;
|
||||||
let $safehomeContentBox;
|
let $safehomeContentBox;
|
||||||
let $waypointOptionsTableBody;
|
let $waypointOptionsTableBody;
|
||||||
|
let selectedGeozone;
|
||||||
|
let $geozoneContent;
|
||||||
|
let isGeozoneEnabeld = false;
|
||||||
let settings = {speed: 0, alt: 5000, safeRadiusSH: 50, fwApproachAlt: 60, fwLandAlt: 5, maxDistSH: 0, fwApproachLength: 0, fwLoiterRadius: 0, bingDemModel: false};
|
let settings = {speed: 0, alt: 5000, safeRadiusSH: 50, fwApproachAlt: 60, fwLandAlt: 5, maxDistSH: 0, fwApproachLength: 0, fwLoiterRadius: 0, bingDemModel: false};
|
||||||
|
|
||||||
if (GUI.active_tab != 'mission_control') {
|
if (GUI.active_tab != 'mission_control') {
|
||||||
GUI.active_tab = 'mission_control';
|
GUI.active_tab = 'mission_control';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FC.isFeatureEnabled('GEOZONE')) {
|
||||||
|
isGeozoneEnabeld = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (CONFIGURATOR.connectionValid) {
|
if (CONFIGURATOR.connectionValid) {
|
||||||
var loadChainer = new MSPChainerClass();
|
var loadChainer = new MSPChainerClass();
|
||||||
loadChainer.setChain([
|
loadChainer.setChain([
|
||||||
|
@ -85,6 +94,13 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
//mspHelper.loadWaypoints,
|
//mspHelper.loadWaypoints,
|
||||||
mspHelper.loadSafehomes,
|
mspHelper.loadSafehomes,
|
||||||
mspHelper.loadFwApproach,
|
mspHelper.loadFwApproach,
|
||||||
|
function (callback) {
|
||||||
|
if (isGeozoneEnabeld) {
|
||||||
|
mspHelper.loadGeozones(callback);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
function (callback) {
|
function (callback) {
|
||||||
mspHelper.getSetting("nav_fw_land_approach_length").then((data) => {
|
mspHelper.getSetting("nav_fw_land_approach_length").then((data) => {
|
||||||
settings.fwApproachLength = parseInt(data.value);
|
settings.fwApproachLength = parseInt(data.value);
|
||||||
|
@ -135,8 +151,10 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
isOffline = true;
|
isOffline = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#infoGeozoneMissionWarning').hide();
|
||||||
$safehomeContentBox = $('#SafehomeContentBox');
|
$safehomeContentBox = $('#SafehomeContentBox');
|
||||||
$waypointOptionsTableBody = $('#waypointOptionsTableBody');
|
$waypointOptionsTableBody = $('#waypointOptionsTableBody');
|
||||||
|
$geozoneContent = $('#geozoneContent');
|
||||||
|
|
||||||
if (typeof require !== "undefined") {
|
if (typeof require !== "undefined") {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
@ -153,6 +171,11 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
renderSafehomesOnMap();
|
renderSafehomesOnMap();
|
||||||
updateSafehomeInfo();
|
updateSafehomeInfo();
|
||||||
}, 500);
|
}, 500);
|
||||||
|
setTimeout(() => {
|
||||||
|
selectedGeozone = FC.GEOZONES.last();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
updateGeozoneInfo();
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$('#missionMap, #missionControls').hide();
|
$('#missionMap, #missionControls').hide();
|
||||||
|
@ -366,6 +389,8 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
var approachLayers = [] // Layers for FW approach
|
var approachLayers = [] // Layers for FW approach
|
||||||
var safehomeMarkers = []; // layer for Safehome points
|
var safehomeMarkers = []; // layer for Safehome points
|
||||||
var approachLayers = [] // Layers for FW approach
|
var approachLayers = [] // Layers for FW approach
|
||||||
|
var geozoneMarkers = []; // Layer for Geozonemarkers
|
||||||
|
var geozoneLines = []; // Layer for Lines between geozone vertices
|
||||||
|
|
||||||
var map;
|
var map;
|
||||||
|
|
||||||
|
@ -741,6 +766,226 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
return radiusProjected;
|
return radiusProjected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Manage Geozones
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
function getGeozoneIcon(geozone, number) {
|
||||||
|
return new ol.style.Style({
|
||||||
|
image: new ol.style.Icon(({
|
||||||
|
anchor: [0.5, 1],
|
||||||
|
opacity: 1,
|
||||||
|
scale: 0.5,
|
||||||
|
src: geozone.getType() == GeozoneType.EXCULSIVE ? './images/icons/cf_icon_geozone_excl.png' : './images/icons/cf_icon_geozone_incl.png'
|
||||||
|
})),
|
||||||
|
text: new ol.style.Text(({
|
||||||
|
text: String(number + 1),
|
||||||
|
font: '12px sans-serif',
|
||||||
|
offsetY: -15,
|
||||||
|
offsetX: -2,
|
||||||
|
fill: new ol.style.Fill({
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}),
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addZoneVertex(zone, vertex) {
|
||||||
|
|
||||||
|
let coord = ol.proj.fromLonLat([vertex.getLonMap(), vertex.getLatMap()]);
|
||||||
|
var iconFeature = new ol.Feature({
|
||||||
|
geometry: new ol.geom.Point(coord),
|
||||||
|
name: 'geozone'
|
||||||
|
});
|
||||||
|
|
||||||
|
var vectorLayer = new ol.layer.Vector({
|
||||||
|
source: new ol.source.Vector({
|
||||||
|
features: [iconFeature]
|
||||||
|
}),
|
||||||
|
style : function(iconFeature) {
|
||||||
|
return [getGeozoneIcon(zone, zone.getShape() == GeozoneShapes.POLYGON ? vertex.getNumber() : zone.getNumber())];
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
vectorLayer.kind = "geozone";
|
||||||
|
vectorLayer.number = vertex.getNumber();
|
||||||
|
vectorLayer.layerNumber = zone.getNumber();
|
||||||
|
vectorLayer.selection = true;
|
||||||
|
|
||||||
|
geozoneMarkers.push(vectorLayer);
|
||||||
|
|
||||||
|
return vectorLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function paintGeozoneLine(pos1, pos2, color, number, zoneNum)
|
||||||
|
{
|
||||||
|
var line = new ol.geom.LineString([pos1, pos2]);
|
||||||
|
|
||||||
|
var feature = new ol.Feature({
|
||||||
|
geometry: line
|
||||||
|
});
|
||||||
|
|
||||||
|
feature.setStyle(
|
||||||
|
new ol.style.Style({
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: color,
|
||||||
|
width: 3,
|
||||||
|
}),
|
||||||
|
text: new ol.style.Text({
|
||||||
|
text: String(zoneNum + 1),
|
||||||
|
font: '14px sans-serif',
|
||||||
|
placement : 'line',
|
||||||
|
textBaseline: 'ideographic',
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: color
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
var vectorSource = new ol.source.Vector({
|
||||||
|
features: [feature]
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var vectorLayer = new ol.layer.Vector({
|
||||||
|
source: vectorSource
|
||||||
|
});
|
||||||
|
|
||||||
|
vectorLayer.kind = "geozoneline";
|
||||||
|
vectorLayer.selection = true;
|
||||||
|
vectorLayer.number = number;
|
||||||
|
vectorLayer.layerNumber = zoneNum;
|
||||||
|
|
||||||
|
geozoneLines.push(vectorLayer);
|
||||||
|
map.addLayer(vectorLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function repaintGeozoneLines() {
|
||||||
|
cleanGeozoneLines();
|
||||||
|
|
||||||
|
FC.GEOZONES.get().forEach(zone => {
|
||||||
|
if (zone.getVerticesCount() != 0) {
|
||||||
|
if (zone.getShape() == GeozoneShapes.CIRCULAR) {
|
||||||
|
var circleFeature = new ol.Feature({
|
||||||
|
geometry: new ol.geom.Circle(ol.proj.fromLonLat([zone.getFirstVertex().getLonMap(), zone.getFirstVertex().getLatMap()]), getProjectedRadius(zone.getRadius() / 100)),
|
||||||
|
name: "geozoneCircle",
|
||||||
|
});
|
||||||
|
|
||||||
|
var vectorSource = new ol.source.Vector();
|
||||||
|
vectorSource.addFeatures([circleFeature]);
|
||||||
|
|
||||||
|
var vectorLayer = new ol.layer.Vector({
|
||||||
|
source: vectorSource,
|
||||||
|
style : [
|
||||||
|
new ol.style.Style({
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: zone.getType() == GeozoneType.EXCULSIVE ? '#E62121' : '#1DBE0A',
|
||||||
|
width: 3,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
vectorLayer.kind = "geozoneline";
|
||||||
|
vectorLayer.selection = false;
|
||||||
|
|
||||||
|
geozoneLines.push(vectorLayer);
|
||||||
|
map.addLayer(vectorLayer);
|
||||||
|
} else if (zone.getShape() == GeozoneShapes.POLYGON) {
|
||||||
|
var verticesCount = zone.getVerticesCount();
|
||||||
|
var prev = zone.getLastVertex();
|
||||||
|
var current;
|
||||||
|
for (let i = 0; i < verticesCount; i++) {
|
||||||
|
current = zone.getVertex(i);
|
||||||
|
let pos1 = ol.proj.fromLonLat([prev.getLonMap(), prev.getLatMap()]);
|
||||||
|
let pos2 = ol.proj.fromLonLat([current.getLonMap(), current.getLatMap()]);
|
||||||
|
paintGeozoneLine(pos1, pos2, zone.getType() == GeozoneType.EXCULSIVE ? '#E62121' : '#1DBE0A', prev.getNumber(), zone.getNumber());
|
||||||
|
prev = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGeozonesOnMap()
|
||||||
|
{
|
||||||
|
cleanGeozoneLayers();
|
||||||
|
if (!selectedGeozone) {
|
||||||
|
cleanGeozoneLines();
|
||||||
|
geozoneMissionWarning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
repaintGeozoneLines();
|
||||||
|
FC.GEOZONES.get().forEach(zone => {
|
||||||
|
if (zone.getVerticesCount() > 0) {
|
||||||
|
zone.getVertices().forEach(vertex => {
|
||||||
|
map.addLayer(addZoneVertex(zone, vertex));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
geozoneMissionWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanGeozoneLines() {
|
||||||
|
geozoneLines.forEach(line => {
|
||||||
|
map.removeLayer(line);
|
||||||
|
});
|
||||||
|
geozoneLines = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanGeozoneLayers() {
|
||||||
|
geozoneMarkers.forEach(marker => {
|
||||||
|
map.removeLayer(marker);
|
||||||
|
});
|
||||||
|
|
||||||
|
geozoneMarkers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function geozoneMissionWarning() {
|
||||||
|
|
||||||
|
if (markers.length >= 1 && geozoneMarkers.length >= 1) {
|
||||||
|
$('#infoGeozoneMissionWarning').show();
|
||||||
|
} else {
|
||||||
|
$('#infoGeozoneMissionWarning').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGeozoneInfo() {
|
||||||
|
$('#availableGeozones').text((FC.GEOZONES.getMaxZones() - FC.GEOZONES.geozoneCount()) + '/' + FC.GEOZONES.getMaxZones());
|
||||||
|
$('#availableVertices').text((FC.GEOZONES.getMaxVertices() - FC.GEOZONES.getUsedVerticesCount()) + '/' + FC.GEOZONES.getMaxVertices());
|
||||||
|
}
|
||||||
|
|
||||||
|
function addGeozone() {
|
||||||
|
|
||||||
|
if (FC.GEOZONES.geozoneCount() + 1 > FC.GEOZONES.getMaxZones()) {
|
||||||
|
GUI.alert(i18n.getMessage('missionGeozoneMaxZonesReached'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FC.GEOZONES.getUsedVerticesCount() + 2 > FC.GEOZONES.getMaxVertices()) {FC.
|
||||||
|
GUI.alert(i18n.getMessage('missionGeozoneMaxVerticesReached'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapCenter = map.getView().getCenter();
|
||||||
|
let midLon = Math.round(ol.proj.toLonLat(mapCenter)[0] * 1e7);
|
||||||
|
let midLat = Math.round(ol.proj.toLonLat(mapCenter)[1] * 1e7);
|
||||||
|
FC.GEOZONES.put(new Geozone(GeozoneType.INCLUSIVE, GeozoneShapes.CIRCULAR, 0, 10000, 20000, GeozoneFenceAction.NONE, [ new GeozoneVertex(0, midLat, midLon) ]));
|
||||||
|
|
||||||
|
selectedGeozone = FC.GEOZONES.last();
|
||||||
|
renderGeozoneOptions();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
updateGeozoneInfo();
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Manage Take Off Home
|
// Manage Take Off Home
|
||||||
|
@ -1376,6 +1621,7 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
geozoneMissionWarning();
|
||||||
}
|
}
|
||||||
|
|
||||||
function redrawLayer() {
|
function redrawLayer() {
|
||||||
|
@ -1429,6 +1675,68 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderGeozoneOptions() {
|
||||||
|
if (selectedGeozone) {
|
||||||
|
if (!$('#missionPlannerGeozones').is(':visible')) {
|
||||||
|
$('#missionPlannerGeozones').fadeIn(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#geozoneContentBox').show();
|
||||||
|
const $geozonContent = $geozoneContent.find('.missionPlannerGeozone:last-child');
|
||||||
|
$geozonContent.find('.spacer_box_title').text(i18n.getMessage('missionGeozoneEdit', selectedGeozone.getNumber() + 1));
|
||||||
|
|
||||||
|
$('#geozoneShape').val(selectedGeozone.getShape());
|
||||||
|
$('#geozoneType').val(selectedGeozone.getType());
|
||||||
|
$('#geozoneMinAlt').val(selectedGeozone.getMinAltitude());
|
||||||
|
$('#geozoneMaxAlt').val(selectedGeozone.getMaxAltitude());
|
||||||
|
$('#geozoneAction').val(selectedGeozone.getFenceAction());
|
||||||
|
$('#geozoneRadius').val(selectedGeozone.getRadius);
|
||||||
|
if (selectedGeozone.getShape() == GeozoneShapes.CIRCULAR) {
|
||||||
|
$('#geozoneRadius').prop('disabled', false);
|
||||||
|
} else {
|
||||||
|
$('#geozoneRadius').prop('disabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let $verticesTable = $('#geozoneVerticesTableBody');
|
||||||
|
$verticesTable.empty();
|
||||||
|
selectedGeozone.getVertices().forEach(vertex => {
|
||||||
|
$verticesTable.append('\
|
||||||
|
<tr> \
|
||||||
|
<td> \
|
||||||
|
<div class="btnTable btnTableIcon"> \
|
||||||
|
<a class="ic_removeAll" id="removeVertex" href="#" title="Remove"></a> \
|
||||||
|
</div>\
|
||||||
|
</td> \
|
||||||
|
<td> \
|
||||||
|
<span class="vertexNumber"></span> \
|
||||||
|
</td> \
|
||||||
|
<td> \
|
||||||
|
<input type="number" class="vertexLat" readonly/> \
|
||||||
|
</td> \
|
||||||
|
<td> \
|
||||||
|
<input type="number" class="vertexLon" readonly/> \
|
||||||
|
</td> \
|
||||||
|
</tr> \
|
||||||
|
');
|
||||||
|
const $row = $verticesTable.find('tr:last');
|
||||||
|
$row.find('.vertexNumber').text(vertex.getNumber() + 1);
|
||||||
|
$row.find('.vertexLat').val((vertex.getLatMap()).toLocaleString(['en-US'], {minimumFractionDigits: 7}));
|
||||||
|
$row.find('.vertexLon').val((vertex.getLonMap()).toLocaleString(['en-US'], {minimumFractionDigits: 7}));
|
||||||
|
$row.find('#removeVertex').on('click', event => {
|
||||||
|
if (selectedGeozone.getVerticesCount() > 3) {
|
||||||
|
selectedGeozone.dropVertex(vertex.getNumber());
|
||||||
|
renderGeozoneOptions();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
updateGeozoneInfo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$('#geozoneContentBox').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderWaypointOptionsTable(waypoint) {
|
function renderWaypointOptionsTable(waypoint) {
|
||||||
/*
|
/*
|
||||||
* Process Waypoint Options table UI
|
* Process Waypoint Options table UI
|
||||||
|
@ -1667,6 +1975,40 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
};
|
};
|
||||||
ol.inherits(app.PlannerSafehomeControl, ol.control.Control);
|
ol.inherits(app.PlannerSafehomeControl, ol.control.Control);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @extends {ol.control.Control}
|
||||||
|
* @param {Object=} opt_options Control options.
|
||||||
|
*/
|
||||||
|
app.GeozonesControl = function(opt_options) {
|
||||||
|
var options = opt_options || {};
|
||||||
|
var button = document.createElement('button');
|
||||||
|
|
||||||
|
button.innerHTML = ' ';
|
||||||
|
button.style = 'background: url(\'./images/icons/cf_icon_geozone_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);';
|
||||||
|
|
||||||
|
var handleShowGeozoneSettings = function () {
|
||||||
|
$('#missionPlannerGeozones').fadeIn(300);
|
||||||
|
renderGeozoneOptions();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
};
|
||||||
|
|
||||||
|
button.addEventListener('click', handleShowGeozoneSettings, false);
|
||||||
|
button.addEventListener('touchstart', handleShowGeozoneSettings, false);
|
||||||
|
|
||||||
|
var element = document.createElement('div');
|
||||||
|
element.className = 'geozone-settings ol-unselectable ol-control';
|
||||||
|
element.appendChild(button);
|
||||||
|
element.title = 'Geozone';
|
||||||
|
|
||||||
|
ol.control.Control.call(this, {
|
||||||
|
element: element,
|
||||||
|
target: options.target
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
ol.inherits(app.GeozonesControl, ol.control.Control);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @extends {ol.control.Control}
|
* @extends {ol.control.Control}
|
||||||
|
@ -1767,6 +2109,7 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
* @param {ol.MapBrowserEvent} evt Map browser event.
|
* @param {ol.MapBrowserEvent} evt Map browser event.
|
||||||
*/
|
*/
|
||||||
app.Drag.prototype.handleDragEvent = function (evt) {
|
app.Drag.prototype.handleDragEvent = function (evt) {
|
||||||
|
|
||||||
if (tempMarker.kind == "safehomecircle") {
|
if (tempMarker.kind == "safehomecircle") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1783,7 +2126,7 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
|
|
||||||
var geometry = /** @type {ol.geom.SimpleGeometry} */
|
var geometry = /** @type {ol.geom.SimpleGeometry} */
|
||||||
(this.feature_.getGeometry());
|
(this.feature_.getGeometry());
|
||||||
if (tempMarker.kind == "waypoint" || tempMarker.kind == "safehome" || tempMarker.kind == "home") {
|
if (tempMarker.kind == "waypoint" || tempMarker.kind == "safehome" || tempMarker.kind == "home" || tempMarker.kind == "geozone") {
|
||||||
geometry.translate(deltaX, deltaY);
|
geometry.translate(deltaX, deltaY);
|
||||||
this.coordinate_[0] = evt.coordinate[0];
|
this.coordinate_[0] = evt.coordinate[0];
|
||||||
this.coordinate_[1] = evt.coordinate[1];
|
this.coordinate_[1] = evt.coordinate[1];
|
||||||
|
@ -1818,6 +2161,18 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
HOME.setLat(Math.round(coord[1] * 10000000));
|
HOME.setLat(Math.round(coord[1] * 10000000));
|
||||||
$('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000);
|
$('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000);
|
||||||
$('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000);
|
$('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000);
|
||||||
|
} else if (tempMarker.kind == "geozone") {
|
||||||
|
let tmpVertex = FC.GEOZONES.at(tempMarker.layerNumber).getVertex(tempMarker.number);
|
||||||
|
tmpVertex.setLon(Math.round(coord[0] * 1e7));
|
||||||
|
tmpVertex.setLat(Math.round(coord[1] * 1e7));
|
||||||
|
//GEOZONES.updateGeozone(tmpVertex);
|
||||||
|
let tableBody = $($geozoneContent.find('.missionPlannerGeozone').get(tempMarker.layerNumber)).find('#geozoneVerticesTableBody');
|
||||||
|
tableBody.find('tr:nth-child(' + String(tempMarker.number + 1) + ') > td > .vertexLon').val(Math.round(coord[0] * 1e7) / 1e7);
|
||||||
|
tableBody.find('tr:nth-child(' + String(tempMarker.number + 1) + ') > td > .vertexLat').val(Math.round(coord[1] * 1e7) / 1e7);
|
||||||
|
selectedGeozone = FC.GEOZONES.at(tempMarker.layerNumber);
|
||||||
|
renderGeozoneOptions();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
updateGeozoneInfo();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1933,6 +2288,10 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
new app.PlannerSafehomeControl(),
|
new app.PlannerSafehomeControl(),
|
||||||
new app.PlannerElevationControl(),
|
new app.PlannerElevationControl(),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (isGeozoneEnabeld) {
|
||||||
|
control_list.push(new app.GeozonesControl());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
control_list = [
|
control_list = [
|
||||||
|
@ -2150,6 +2509,25 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
$('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000);
|
$('.home-lon').val(Math.round(coord[0] * 10000000) / 10000000);
|
||||||
$('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000);
|
$('.home-lat').val(Math.round(coord[1] * 10000000) / 10000000);
|
||||||
}
|
}
|
||||||
|
else if (selectedFeature && tempMarker.kind == "geozone" && tempMarker.selection) {
|
||||||
|
selectedGeozone = FC.GEOZONES.at(tempMarker.layerNumber);
|
||||||
|
renderGeozoneOptions();
|
||||||
|
}
|
||||||
|
else if (selectedFeature && tempMarker.kind == "geozoneline" && tempMarker.selection) {
|
||||||
|
|
||||||
|
if (FC.GEOZONES.getUsedVerticesCount() + 1 >= FC.GEOZONES.getMaxVertices()) {
|
||||||
|
GUI.alert(i18n.getMessage('missionGeozoneMaxVerticesReached'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tempCoord = ol.proj.toLonLat(evt.coordinate);
|
||||||
|
let tmpVertex = new GeozoneVertex(tempMarker.number + 1, Math.round(tempCoord[1] * 1e7), Math.round(tempCoord[0] * 1e7));
|
||||||
|
FC.GEOZONES.at(tempMarker.layerNumber).insertVertex(tempMarker.number + 1, tmpVertex);
|
||||||
|
selectedGeozone = FC.GEOZONES.at(tempMarker.layerNumber);
|
||||||
|
renderGeozoneOptions();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
updateGeozoneInfo();
|
||||||
|
}
|
||||||
else if (!disableMarkerEdit) {
|
else if (!disableMarkerEdit) {
|
||||||
let tempWpCoord = ol.proj.toLonLat(evt.coordinate);
|
let tempWpCoord = ol.proj.toLonLat(evt.coordinate);
|
||||||
let tempWp = new Waypoint(mission.get().length, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), Number(settings.alt), Number(settings.speed));
|
let tempWp = new Waypoint(mission.get().length, MWNP.WPTYPE.WAYPOINT, Math.round(tempWpCoord[1] * 10000000), Math.round(tempWpCoord[0] * 10000000), Number(settings.alt), Number(settings.speed));
|
||||||
|
@ -2194,7 +2572,7 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (hit && name != "safehomeDist" && name != "safehomeSafe") {
|
if (hit && name != "safehomeDist" && name != "safehomeSafe" && name != "geozoneCircle") {
|
||||||
map.getTarget().style.cursor = 'pointer';
|
map.getTarget().style.cursor = 'pointer';
|
||||||
} else {
|
} else {
|
||||||
map.getTarget().style.cursor = '';
|
map.getTarget().style.cursor = '';
|
||||||
|
@ -2299,6 +2677,19 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#showHideGeozonesButton').on('click', function () {
|
||||||
|
var src = ($(this).children().attr('class') === 'ic_hide')
|
||||||
|
? 'ic_show'
|
||||||
|
: 'ic_hide';
|
||||||
|
$(this).children().attr('class', src);
|
||||||
|
if ($(this).children().attr('class') === 'ic_hide') {
|
||||||
|
$('#geozoneContent').fadeIn(300);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#geozoneContent').fadeOut(300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// Callback for Waypoint edition
|
// Callback for Waypoint edition
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
|
@ -2913,6 +3304,141 @@ TABS.mission_control.initialize = function (callback) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
// Callback for Geozones
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
|
||||||
|
function reboot() {
|
||||||
|
//noinspection JSUnresolvedVariable
|
||||||
|
GUI.log(i18n.getMessage('configurationEepromSaved'));
|
||||||
|
GUI.tab_switch_cleanup(function () {
|
||||||
|
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function reinitialize() {
|
||||||
|
//noinspection JSUnresolvedVariable
|
||||||
|
GUI.log(i18n.getMessage('deviceRebooting'));
|
||||||
|
GUI.handleReconnect($('.tab_mission_control a'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#cancelGeozone').on('click', function() {
|
||||||
|
$('#missionPlannerGeozones').hide();
|
||||||
|
cleanGeozoneLayers();
|
||||||
|
cleanGeozoneLines();
|
||||||
|
selectedGeozone = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#addGeozone').on('click', function() {
|
||||||
|
addGeozone();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#deleteGeozone').on('click', event => {
|
||||||
|
FC.GEOZONES.drop(selectedGeozone.getNumber());
|
||||||
|
selectedGeozone = FC.GEOZONES.last();
|
||||||
|
renderGeozoneOptions();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
updateGeozoneInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#loadEepromGeozoneButton').on('click', event => {
|
||||||
|
$(event.currentTarget).addClass('disabled');
|
||||||
|
GUI.log('Start of getting Geozones');
|
||||||
|
mspHelper.loadGeozones();
|
||||||
|
setTimeout( () => {
|
||||||
|
if (FC.GEOZONES.geozoneCount() >= 1) {
|
||||||
|
selectedGeozone = FC.GEOZONES.first();
|
||||||
|
} else {
|
||||||
|
selectedGeozone = null;
|
||||||
|
}
|
||||||
|
renderGeozoneOptions();
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
updateGeozoneInfo();
|
||||||
|
GUI.log('End of getting Geozones');
|
||||||
|
$(event.currentTarget).removeClass('disabled');
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#saveEepromGeozoneButton').on('click', event => {
|
||||||
|
if (confirm(i18n.getMessage("missionGeozoneReboot"))) {
|
||||||
|
$(event.currentTarget).addClass('disabled');
|
||||||
|
GUI.log('Start of sending Geozones');
|
||||||
|
mspHelper.saveGeozones();
|
||||||
|
setTimeout(() => {
|
||||||
|
mspHelper.saveToEeprom();
|
||||||
|
GUI.log('End of sending Geozones');
|
||||||
|
reboot();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#geozoneShape').on('change', event => {
|
||||||
|
if (selectedGeozone) {
|
||||||
|
selectedGeozone.setShape($(event.currentTarget).val());
|
||||||
|
|
||||||
|
if ($(event.currentTarget).val() == GeozoneShapes.CIRCULAR) {
|
||||||
|
$('#geozoneRadius').prop('disabled', false);
|
||||||
|
let tmpVertex = selectedGeozone.getFirstVertex();
|
||||||
|
selectedGeozone.resetVertices();
|
||||||
|
selectedGeozone.setVertices([tmpVertex]);
|
||||||
|
} else {
|
||||||
|
if (FC.GEOZONES.getUsedVerticesCount() + 3 > FC.GEOZONES.getMaxVertices()) {
|
||||||
|
GUI.alert(i18n.getMessage('missionGeozoneMaxVerticesReached'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#geozoneRadius').prop('disabled', true);
|
||||||
|
if (selectedGeozone.getVerticesCount() < 3) {
|
||||||
|
let lat = selectedGeozone.getFirstVertex().getLat();
|
||||||
|
let lon = selectedGeozone.getFirstVertex().getLon();
|
||||||
|
let vertices = [
|
||||||
|
new GeozoneVertex(0, lat - 25000, lon - 25000),
|
||||||
|
new GeozoneVertex(1, lat - 25000, lon + 25000),
|
||||||
|
new GeozoneVertex(2, lat + 25000, lon + 25000),
|
||||||
|
new GeozoneVertex(3, lat + 25000, lon - 25000)
|
||||||
|
];
|
||||||
|
selectedGeozone.setVertices(vertices);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#geozoneType').on('change', event => {
|
||||||
|
if (selectedGeozone) {
|
||||||
|
selectedGeozone.setType($(event.currentTarget).val());
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#geozoneMinAlt').on('change', event => {
|
||||||
|
if (selectedGeozone) {
|
||||||
|
selectedGeozone.setMinAltitude($(event.currentTarget).val());
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#geozoneMaxAlt').on('change', event => {
|
||||||
|
if (selectedGeozone) {
|
||||||
|
selectedGeozone.setMaxAltitude($(event.currentTarget).val());
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#geozoneAction').on('change', event => {
|
||||||
|
if (selectedGeozone) {
|
||||||
|
selectedGeozone.setFenceAction($(event.currentTarget).val());
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#geozoneRadius').on('change', event => {
|
||||||
|
if (selectedGeozone) {
|
||||||
|
selectedGeozone.setRadius($(event.currentTarget).val());
|
||||||
|
renderGeozonesOnMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// Callback for HOME Table
|
// Callback for HOME Table
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
|
|
44
tabs/osd.js
44
tabs/osd.js
|
@ -1731,7 +1731,51 @@ OSD.constants = {
|
||||||
return FONT.symbol(SYM.CROSS_TRACK_ERROR) + FONT.embed_dot('1.57') + FONT.symbol(SYM.DIST_KM);
|
return FONT.symbol(SYM.CROSS_TRACK_ERROR) + FONT.embed_dot('1.57') + FONT.symbol(SYM.DIST_KM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},{
|
||||||
|
name: 'COURSE_NEXT_GEOZONE',
|
||||||
|
id: 154,
|
||||||
|
min_version: '8.0.0',
|
||||||
|
enabled: function() {
|
||||||
|
return FC.isFeatureEnabled('GEOZONE');
|
||||||
},
|
},
|
||||||
|
preview: FONT.symbol(SYM.DIR_TO_HOME)
|
||||||
|
}, {
|
||||||
|
name: 'HOR_DIST_TO_NEXT_GEOZONE',
|
||||||
|
id: 155,
|
||||||
|
min_version: '8.0.0',
|
||||||
|
enabled: function() {
|
||||||
|
return FC.isFeatureEnabled('GEOZONE');
|
||||||
|
},
|
||||||
|
preview: function(osd_data) {
|
||||||
|
switch (OSD.data.preferences.units) {
|
||||||
|
case 0: // Imperial
|
||||||
|
case 3: // UK
|
||||||
|
return 'FD ' + FONT.embed_dot('0.88') + FONT.symbol(SYM.DIST_MI);
|
||||||
|
case 4: // GA
|
||||||
|
return 'FD ' + FONT.embed_dot('0.78') + FONT.symbol(SYM.DIST_NM);
|
||||||
|
default: // Metric
|
||||||
|
return 'FD ' + FONT.embed_dot('1.42') + FONT.symbol(SYM.DIST_KM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VERT_DIST_TO_NEXT_GEOZONE',
|
||||||
|
id: 156,
|
||||||
|
min_version: '8.0.0',
|
||||||
|
enabled: function() {
|
||||||
|
return FC.isFeatureEnabled('GEOZONE');
|
||||||
|
},
|
||||||
|
preview: function(osd_data) {
|
||||||
|
switch (OSD.data.preferences.units) {
|
||||||
|
case 0: // Imperial
|
||||||
|
case 3: // UK
|
||||||
|
case 4: // GA
|
||||||
|
return 'FD 466' + FONT.symbol(SYM.ALT_FT) + FONT.symbol(SYM.DIR_TO_HOME);
|
||||||
|
default: // Metric
|
||||||
|
return 'FD 142' + FONT.symbol(SYM.ALT_M) + FONT.symbol(SYM.DIR_TO_HOME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue