260 lines
7.5 KiB
JavaScript
260 lines
7.5 KiB
JavaScript
/*
|
|
Copyright (c) 2023-forever Douglas Malnati. All rights reserved.
|
|
|
|
See the /faq/tos page for details.
|
|
|
|
(If this generated header is stamped on a file which is a 3rd party file or under a different license or copyright, then ignore this copyright statement and use that file's terms.)
|
|
*/
|
|
|
|
import * as utl from '/js/Utl.js';
|
|
|
|
import { Base } from './Base.js';
|
|
import { SpotMapAsyncLoader } from './SpotMapAsyncLoader.js';
|
|
import { TabularData } from '../../../../js/TabularData.js';
|
|
import { WSPREncoded } from '/js/WSPREncoded.js';
|
|
import { WsprSearchUiDataTableVisibility } from './WsprSearchUiDataTableVisibility.js';
|
|
import { WsprSearchUiDataTableColumnOrder } from './WsprSearchUiDataTableColumnOrder.js';
|
|
|
|
|
|
export class WsprSearchUiMapController
|
|
extends Base
|
|
{
|
|
constructor(cfg)
|
|
{
|
|
super();
|
|
|
|
this.cfg = cfg;
|
|
this.td = null;
|
|
this.mapDataToken = 0;
|
|
this.showEmptyMapRequested = false;
|
|
this.showControlHelpRequested = false;
|
|
|
|
this.ok = this.cfg.container;
|
|
|
|
// map gets async loaded
|
|
this.mapModule = null;
|
|
this.map = null;
|
|
SpotMapAsyncLoader.SetOnLoadCallback((module) => {
|
|
this.mapModule = module;
|
|
|
|
this.map = new this.mapModule.SpotMap({
|
|
container: this.ui,
|
|
});
|
|
|
|
this.map.SetDebug(this.debug);
|
|
|
|
if (this.showEmptyMapRequested)
|
|
{
|
|
this.map.SetSpotList([]);
|
|
this.showEmptyMapRequested = false;
|
|
}
|
|
else if (this.td)
|
|
{
|
|
this.ScheduleMapData();
|
|
}
|
|
|
|
if (this.showControlHelpRequested)
|
|
{
|
|
this.map.ShowControlHelpDialog();
|
|
this.showControlHelpRequested = false;
|
|
}
|
|
});
|
|
|
|
if (this.ok)
|
|
{
|
|
this.ui = this.MakeUI();
|
|
this.cfg.container.appendChild(this.ui);
|
|
}
|
|
}
|
|
|
|
SetDebug(tf)
|
|
{
|
|
super.SetDebug(tf);
|
|
|
|
this.t.SetCcGlobal(tf);
|
|
}
|
|
|
|
OnEvent(evt)
|
|
{
|
|
if (this.ok)
|
|
{
|
|
switch (evt.type) {
|
|
case "DATA_TABLE_RAW_READY": this.OnDataTableRawReady(evt); break;
|
|
case "DATA_TABLE_VISIBILITY_CHANGED": this.OnDataTableVisibilityChanged(evt); break;
|
|
case "SHOW_EMPTY_MAP": this.OnShowEmptyMap(evt); break;
|
|
case "SHOW_MAP_CONTROL_HELP": this.OnShowMapControlHelp(evt); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
OnDataTableRawReady(evt)
|
|
{
|
|
// cache data
|
|
this.td = evt.tabularDataReadOnly;
|
|
|
|
// check if we can map immediately
|
|
if (this.mapModule != null)
|
|
{
|
|
this.ScheduleMapData();
|
|
}
|
|
}
|
|
|
|
OnDataTableVisibilityChanged(evt)
|
|
{
|
|
if (this.td && this.mapModule != null)
|
|
{
|
|
this.ScheduleMapData();
|
|
}
|
|
}
|
|
|
|
OnShowEmptyMap(evt)
|
|
{
|
|
this.td = null;
|
|
|
|
if (this.map)
|
|
{
|
|
this.map.SetSpotList([]);
|
|
}
|
|
else
|
|
{
|
|
this.showEmptyMapRequested = true;
|
|
}
|
|
}
|
|
|
|
OnShowMapControlHelp(evt)
|
|
{
|
|
if (this.map)
|
|
{
|
|
this.map.ShowControlHelpDialog();
|
|
}
|
|
else
|
|
{
|
|
this.showControlHelpRequested = true;
|
|
}
|
|
}
|
|
|
|
ScheduleMapData()
|
|
{
|
|
let token = ++this.mapDataToken;
|
|
|
|
let run = () => {
|
|
if (token != this.mapDataToken)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.MapData();
|
|
};
|
|
|
|
if (window.requestIdleCallback)
|
|
{
|
|
window.requestIdleCallback(() => {
|
|
window.requestAnimationFrame(run);
|
|
}, { timeout: 250 });
|
|
}
|
|
else
|
|
{
|
|
window.setTimeout(() => {
|
|
window.requestAnimationFrame(run);
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
MapData()
|
|
{
|
|
this.t.Reset();
|
|
this.t.Event(`WsprSearchUiMapController::MapData Start`);
|
|
|
|
let spotList = [];
|
|
if (this.td.Idx("Lat") != undefined && this.td.Idx("Lng") != undefined)
|
|
{
|
|
this.td.ForEach(row => {
|
|
let metaData = this.td.GetRowMetaData(row);
|
|
let locationSource = metaData?.overlap?.resolved?.sourceByFamily?.location ?? null;
|
|
let latResolved = this.td.Idx("Lat") != undefined ? this.td.Get(row, "Lat") : null;
|
|
let lngResolved = this.td.Idx("Lng") != undefined ? this.td.Get(row, "Lng") : null;
|
|
|
|
let lat = null;
|
|
let lng = null;
|
|
|
|
if (latResolved != null && lngResolved != null)
|
|
{
|
|
lat = latResolved;
|
|
lng = lngResolved;
|
|
}
|
|
|
|
if (lat != null && lng != null)
|
|
{
|
|
// get a list of all the reporting stations
|
|
let seenDataList = [];
|
|
for (let msg of metaData.slotMsgList)
|
|
{
|
|
if (msg)
|
|
{
|
|
for (let rxRecord of msg.rxRecordList)
|
|
{
|
|
let [lat, lng] = WSPREncoded.DecodeMaidenheadToDeg(rxRecord.rxGrid);
|
|
|
|
let seenData = {
|
|
sign: rxRecord.callsign,
|
|
lat,
|
|
lng,
|
|
grid: rxRecord.rxGrid,
|
|
};
|
|
|
|
seenDataList.push(seenData);
|
|
}
|
|
}
|
|
}
|
|
|
|
// send along a cut-down version of the data available
|
|
let tdSpot = new TabularData(this.td.MakeDataTableFromRow(row));
|
|
let popupVisibleColSet = new Set([
|
|
...WsprSearchUiDataTableVisibility.GetVisibleColumnsForStorageKey("dateTimeVisible"),
|
|
...WsprSearchUiDataTableVisibility.GetVisibleColumnsForStorageKey("resolvedVisible"),
|
|
]);
|
|
let popupColList = tdSpot.GetHeaderList().filter(col => popupVisibleColSet.has(col));
|
|
tdSpot.SetColumnOrder(popupColList);
|
|
WsprSearchUiDataTableColumnOrder.Apply(tdSpot);
|
|
tdSpot.DeleteEmptyColumns();
|
|
|
|
let spot = new this.mapModule.Spot({
|
|
lat: lat,
|
|
lng: lng,
|
|
grid: null,
|
|
accuracy:
|
|
(locationSource == "HRL" || locationSource == "EBT") ? "veryHigh" :
|
|
(locationSource == "BT") ? "high" :
|
|
"low",
|
|
dtLocal: tdSpot.Get(0, "DateTimeLocal"),
|
|
td: tdSpot,
|
|
seenDataList: seenDataList,
|
|
});
|
|
|
|
spotList.push(spot);
|
|
}
|
|
}, true);
|
|
}
|
|
|
|
// hand off even an empty spot list
|
|
this.map.SetSpotList(spotList);
|
|
|
|
this.t.Event(`WsprSearchUiMapController::MapData End`);
|
|
}
|
|
|
|
MakeUI()
|
|
{
|
|
let ui = document.createElement("div");
|
|
|
|
ui.style.boxSizing = "border-box";
|
|
ui.style.border = "1px solid black";
|
|
ui.style.width = "1210px";
|
|
ui.style.height = "550px";
|
|
|
|
ui.style.resize = "both";
|
|
ui.style.overflow = "hidden";
|
|
|
|
return ui;
|
|
}
|
|
}
|