mirror of
https://github.com/iNavFlight/inav-configurator.git
synced 2025-07-16 04:45:18 +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 OutputMappingCollection = require('./outputMapping');
|
||||
const SafehomeCollection = require('./safehomeCollection');
|
||||
const FwApproachCollection = require('./fwApproachCollection')
|
||||
const FwApproachCollection = require('./fwApproachCollection');
|
||||
const GeozoneCollection = require('./geozoneCollection');
|
||||
const { PLATFORM } = require('./model')
|
||||
const VTX = require('./vtx');
|
||||
const BitHelper = require('./bitHelper');
|
||||
|
@ -86,6 +87,7 @@ var FC = {
|
|||
RATE_DYNAMICS: null,
|
||||
EZ_TUNE: null,
|
||||
FLIGHT_MODES: null,
|
||||
GEOZONES: null,
|
||||
|
||||
restartRequired: false,
|
||||
MAX_SERVO_RATE: 125,
|
||||
|
@ -597,6 +599,7 @@ var FC = {
|
|||
|
||||
|
||||
this.FW_APPROACH = new FwApproachCollection();
|
||||
this.GEOZONES = new GeozoneCollection();
|
||||
|
||||
this.OSD_CUSTOM_ELEMENTS = {
|
||||
settings: {customElementsCount: 0, customElementTextSize: 0},
|
||||
|
@ -634,7 +637,8 @@ var FC = {
|
|||
{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: 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();
|
||||
|
|
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_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;
|
|
@ -21,6 +21,7 @@ const Waypoint = require('./../waypoint');
|
|||
const mspDeduplicationQueue = require('./mspDeduplicationQueue');
|
||||
const mspStatistics = require('./mspStatistics');
|
||||
const settingsCache = require('./../settingsCache');
|
||||
const {Geozone, GeozoneVertex, GeozoneShapes } = require('./../geozone');
|
||||
|
||||
var mspHelper = (function () {
|
||||
var self = {};
|
||||
|
@ -1590,10 +1591,63 @@ var mspHelper = (function () {
|
|||
FC.OSD_CUSTOM_ELEMENTS .items.push(customElement)
|
||||
}
|
||||
break;
|
||||
|
||||
case MSPCodes.MSP2_INAV_GPS_UBLOX_COMMAND:
|
||||
// Just and ACK from the fc.
|
||||
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:
|
||||
console.log('Unknown code detected: 0x' + dataHandler.code.toString(16));
|
||||
} 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) {
|
||||
|
||||
const storedSetting = settingsCache.get(name);
|
||||
|
|
|
@ -4288,6 +4288,15 @@
|
|||
"osdElement_OSD_RANGEFINDER": {
|
||||
"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": {
|
||||
"message": "Precision at the equator: 10=13.9x13.9m; 11=2.8x3.5m; 12=56x87cm; 13=11x22cm."
|
||||
},
|
||||
|
@ -4645,6 +4654,147 @@
|
|||
"missionSafehomeMaxSafehomesReached": {
|
||||
"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": {
|
||||
"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;
|
||||
}
|
||||
|
||||
.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 {
|
||||
top: 140px;
|
||||
left: .5em;
|
||||
|
|
|
@ -513,6 +513,48 @@
|
|||
-->
|
||||
</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>
|
||||
<!-- Left wrapper -->
|
||||
|
|
|
@ -62,6 +62,10 @@
|
|||
<span data-i18n="missionTotalInfoMissionValid"></span>
|
||||
<div id="missionValid" style="display: inline-block"></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>
|
||||
|
||||
|
@ -281,6 +285,96 @@
|
|||
</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_titlebar">
|
||||
<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 Plotly = require('./../js/libraries/plotly-latest.min');
|
||||
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
|
||||
|
||||
|
@ -72,12 +74,19 @@ TABS.mission_control.initialize = function (callback) {
|
|||
let selectedSafehome;
|
||||
let $safehomeContentBox;
|
||||
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};
|
||||
|
||||
if (GUI.active_tab != 'mission_control') {
|
||||
GUI.active_tab = 'mission_control';
|
||||
}
|
||||
|
||||
if (FC.isFeatureEnabled('GEOZONE')) {
|
||||
isGeozoneEnabeld = true;
|
||||
}
|
||||
|
||||
if (CONFIGURATOR.connectionValid) {
|
||||
var loadChainer = new MSPChainerClass();
|
||||
loadChainer.setChain([
|
||||
|
@ -85,6 +94,13 @@ TABS.mission_control.initialize = function (callback) {
|
|||
//mspHelper.loadWaypoints,
|
||||
mspHelper.loadSafehomes,
|
||||
mspHelper.loadFwApproach,
|
||||
function (callback) {
|
||||
if (isGeozoneEnabeld) {
|
||||
mspHelper.loadGeozones(callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
function (callback) {
|
||||
mspHelper.getSetting("nav_fw_land_approach_length").then((data) => {
|
||||
settings.fwApproachLength = parseInt(data.value);
|
||||
|
@ -135,8 +151,10 @@ TABS.mission_control.initialize = function (callback) {
|
|||
isOffline = true;
|
||||
}
|
||||
|
||||
$('#infoGeozoneMissionWarning').hide();
|
||||
$safehomeContentBox = $('#SafehomeContentBox');
|
||||
$waypointOptionsTableBody = $('#waypointOptionsTableBody');
|
||||
$geozoneContent = $('#geozoneContent');
|
||||
|
||||
if (typeof require !== "undefined") {
|
||||
loadSettings();
|
||||
|
@ -153,6 +171,11 @@ TABS.mission_control.initialize = function (callback) {
|
|||
renderSafehomesOnMap();
|
||||
updateSafehomeInfo();
|
||||
}, 500);
|
||||
setTimeout(() => {
|
||||
selectedGeozone = FC.GEOZONES.last();
|
||||
renderGeozonesOnMap();
|
||||
updateGeozoneInfo();
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
$('#missionMap, #missionControls').hide();
|
||||
|
@ -366,6 +389,8 @@ TABS.mission_control.initialize = function (callback) {
|
|||
var approachLayers = [] // Layers for FW approach
|
||||
var safehomeMarkers = []; // layer for Safehome points
|
||||
var approachLayers = [] // Layers for FW approach
|
||||
var geozoneMarkers = []; // Layer for Geozonemarkers
|
||||
var geozoneLines = []; // Layer for Lines between geozone vertices
|
||||
|
||||
var map;
|
||||
|
||||
|
@ -741,6 +766,226 @@ TABS.mission_control.initialize = function (callback) {
|
|||
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
|
||||
|
@ -1376,6 +1621,7 @@ TABS.mission_control.initialize = function (callback) {
|
|||
});
|
||||
|
||||
}
|
||||
geozoneMissionWarning();
|
||||
}
|
||||
|
||||
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) {
|
||||
/*
|
||||
* Process Waypoint Options table UI
|
||||
|
@ -1667,6 +1975,40 @@ TABS.mission_control.initialize = function (callback) {
|
|||
};
|
||||
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
|
||||
* @extends {ol.control.Control}
|
||||
|
@ -1767,6 +2109,7 @@ TABS.mission_control.initialize = function (callback) {
|
|||
* @param {ol.MapBrowserEvent} evt Map browser event.
|
||||
*/
|
||||
app.Drag.prototype.handleDragEvent = function (evt) {
|
||||
|
||||
if (tempMarker.kind == "safehomecircle") {
|
||||
return;
|
||||
}
|
||||
|
@ -1783,7 +2126,7 @@ TABS.mission_control.initialize = function (callback) {
|
|||
|
||||
var geometry = /** @type {ol.geom.SimpleGeometry} */
|
||||
(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);
|
||||
this.coordinate_[0] = evt.coordinate[0];
|
||||
this.coordinate_[1] = evt.coordinate[1];
|
||||
|
@ -1818,6 +2161,18 @@ TABS.mission_control.initialize = function (callback) {
|
|||
HOME.setLat(Math.round(coord[1] * 10000000));
|
||||
$('.home-lon').val(Math.round(coord[0] * 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.PlannerElevationControl(),
|
||||
]
|
||||
|
||||
if (isGeozoneEnabeld) {
|
||||
control_list.push(new app.GeozonesControl());
|
||||
}
|
||||
}
|
||||
else {
|
||||
control_list = [
|
||||
|
@ -2150,6 +2509,25 @@ TABS.mission_control.initialize = function (callback) {
|
|||
$('.home-lon').val(Math.round(coord[0] * 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) {
|
||||
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));
|
||||
|
@ -2194,7 +2572,7 @@ TABS.mission_control.initialize = function (callback) {
|
|||
}
|
||||
return true;
|
||||
});
|
||||
if (hit && name != "safehomeDist" && name != "safehomeSafe") {
|
||||
if (hit && name != "safehomeDist" && name != "safehomeSafe" && name != "geozoneCircle") {
|
||||
map.getTarget().style.cursor = 'pointer';
|
||||
} else {
|
||||
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
|
||||
/////////////////////////////////////////////
|
||||
|
@ -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
|
||||
/////////////////////////////////////////////
|
||||
|
|
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);
|
||||
}
|
||||
}
|
||||
},{
|
||||
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