1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-08-14 15:33:34 -04:00

Map: Add save to KML. Support MUF/foF2 varying with time. Support VLF transmitters being read from .csv.

This commit is contained in:
srcejon
2024-04-05 10:41:24 +01:00
parent c137faf012
commit 4955e6ab08
20 changed files with 449 additions and 350 deletions
+98 -26
View File
@@ -364,6 +364,52 @@
["railways", railwaysLayer]
]);
function downloadBlob(filename, blob) {
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
const elem = window.document.createElement("a");
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
function downloadText(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
var dataDir = ""; // Directory where 3D models are stored
function modelCallback(modelGraphics, time, externalFiles) {
const resource = modelGraphics.uri.getValue(time);
console.log("modelcallback " + resource);
const regex = /http:\/\/127.0.0.1:\d+/;
var file = resource.url.replace(regex, dataDir);
// KML only supports Collada files. User will have to convert the models if required
file = file.replace(/glb$/, "dae");
file = file.replace(/gltf$/, "dae");
if (navigator.platform.indexOf('Win') > -1) {
file = file.replace(/\//g, "\\");
}
return file;
}
// Use WebSockets for handling commands from MapPlugin
// (CZML doesn't support camera control, for example)
// and sending events back to it
@@ -469,30 +515,40 @@
viewer.scene.postProcessStages.fxaa.enabled = false;
}
} else if (command.command == "showMUF") {
if (mufGeoJSONStream != null) {
viewer.dataSources.remove(mufGeoJSONStream, true);
mufGeoJSONStream = null;
}
if (command.show == true) {
viewer.dataSources.add(
Cesium.GeoJsonDataSource.load(
"muf.geojson",
{ describe: describeMUF }
)
).then(function (dataSource) { mufGeoJSONStream = dataSource; });
).then(function (dataSource) {
if (mufGeoJSONStream != null) {
viewer.dataSources.remove(mufGeoJSONStream, true);
mufGeoJSONStream = null;
}
mufGeoJSONStream = dataSource;
});
} else {
viewer.dataSources.remove(mufGeoJSONStream, true);
mufGeoJSONStream = null;
}
} else if (command.command == "showfoF2") {
if (foF2GeoJSONStream != null) {
viewer.dataSources.remove(foF2GeoJSONStream, true);
foF2GeoJSONStream = null;
}
if (command.show == true) {
viewer.dataSources.add(
Cesium.GeoJsonDataSource.load(
"fof2.geojson",
{ describe: describefoF2 }
)
).then(function (dataSource) { foF2GeoJSONStream = dataSource; });
).then(function (dataSource) {
if (foF2GeoJSONStream != null) {
viewer.dataSources.remove(foF2GeoJSONStream, true);
foF2GeoJSONStream = null;
}
foF2GeoJSONStream = dataSource;
});
} else {
viewer.dataSources.remove(foF2GeoJSONStream, true);
foF2GeoJSONStream = null;
}
} else if (command.command == "showLayer") {
layers.get(command.layer).show = command.show;
@@ -639,7 +695,7 @@
czmlStream.process(command);
} else {
var promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [position]);
Cesium.when(promise, function(updatedPositions) {
Cesium.when(promise, function (updatedPositions) {
if (height < updatedPositions[0].height) {
if (size == 3) {
command.position.cartographicDegrees[2] = updatedPositions[0].height;
@@ -648,7 +704,7 @@
}
}
czmlStream.process(command);
}, function() {
}, function () {
console.log(`Terrain doesn't support sampleTerrainMostDetailed`);
czmlStream.process(command);
});
@@ -657,47 +713,47 @@
console.log(`Can't currently use altitudeReference when more than one position`);
czmlStream.process(command);
}
} else if ( (command.hasOwnProperty('polygon') && command.polygon.hasOwnProperty('altitudeReference'))
|| (command.hasOwnProperty('polyline') && command.polyline.hasOwnProperty('altitudeReference'))) {
} else if ((command.hasOwnProperty('polygon') && command.polygon.hasOwnProperty('altitudeReference'))
|| (command.hasOwnProperty('polyline') && command.polyline.hasOwnProperty('altitudeReference'))) {
// Support per vertex height reference in polygons and CLIP_TO_GROUND in polylines
var prim = command.hasOwnProperty('polygon') ? command.polygon : command.polyline;
var clipToGround = prim.altitudeReference == "CLIP_TO_GROUND";
var clampToGround = prim.altitudeReference == "CLAMP_TO_GROUND";
var size = prim.positions.cartographicDegrees.length;
var positionCount = size/3;
var positionCount = size / 3;
var positions = new Array(positionCount);
if (viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) {
if (clampToGround) {
for (let i = 0; i < positionCount; i++) {
prim.positions.cartographicDegrees[i*3+2] = 0;
prim.positions.cartographicDegrees[i * 3 + 2] = 0;
}
} else if (clipToGround) {
for (let i = 0; i < positionCount; i++) {
if (prim.positions.cartographicDegrees[i*3+2] < 0) {
prim.positions.cartographicDegrees[i*3+2] = 0;
if (prim.positions.cartographicDegrees[i * 3 + 2] < 0) {
prim.positions.cartographicDegrees[i * 3 + 2] = 0;
}
}
}
czmlStream.process(command);
} else {
for (let i = 0; i < positionCount; i++) {
positions[i] = Cesium.Cartographic.fromDegrees(prim.positions.cartographicDegrees[i*3+0], prim.positions.cartographicDegrees[i*3+1]);
positions[i] = Cesium.Cartographic.fromDegrees(prim.positions.cartographicDegrees[i * 3 + 0], prim.positions.cartographicDegrees[i * 3 + 1]);
}
var promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions);
Cesium.when(promise, function(updatedPositions) {
Cesium.when(promise, function (updatedPositions) {
if (clampToGround) {
for (let i = 0; i < positionCount; i++) {
prim.positions.cartographicDegrees[i*3+2] = updatedPositions[i].height;
prim.positions.cartographicDegrees[i * 3 + 2] = updatedPositions[i].height;
}
} else if (clipToGround) {
for (let i = 0; i < positionCount; i++) {
if (prim.positions.cartographicDegrees[i*3+2] < updatedPositions[i].height) {
prim.positions.cartographicDegrees[i*3+2] = updatedPositions[i].height;
if (prim.positions.cartographicDegrees[i * 3 + 2] < updatedPositions[i].height) {
prim.positions.cartographicDegrees[i * 3 + 2] = updatedPositions[i].height;
}
}
}
czmlStream.process(command);
}, function() {
}, function () {
console.log(`Terrain doesn't support sampleTerrainMostDetailed`);
czmlStream.process(command);
});
@@ -705,7 +761,20 @@
} else {
czmlStream.process(command);
}
} else if (command.command == "save") {
// Export to kml/kmz
dataDir = command.dataDir;
Cesium.exportKml({
entities: czmlStream.entities,
kmz: command.filename.endsWith("kmz"),
modelCallback: modelCallback
}).then(function (result) {
if (command.filename.endsWith("kmz")) {
downloadBlob(command.filename, result.kmz);
} else {
downloadText(command.filename, result.kml);
}
});
} else {
console.log(`Unknown command ${command.command}`);
}
@@ -759,10 +828,13 @@
Cesium.knockout.getObservable(viewer.clockViewModel, 'multiplier').subscribe(function(multiplier) {
reportClock();
});
// This is called every frame
// This is called every frame, which is too fast, so instead use setInterval with 1 second period
//Cesium.knockout.getObservable(viewer.clockViewModel, 'currentTime').subscribe(function(currentTime) {
//reportClock();
//});
setInterval(function () {
reportClock();
}, 1000);
viewer.timeline.addEventListener('settime', reportClock, false);
socket.onopen = () => {