From ab97a9104315221c0bd7031f927c7ca8f133c9cd Mon Sep 17 00:00:00 2001 From: Salwa Jeries Date: Wed, 24 Jun 2026 14:16:25 -0500 Subject: [PATCH 01/29] Updated lob and sigint to handle no icon for lobs --- src/modules/map/mapVisualizations.ts | 13 ----- .../components/MapVisualizationWrapper.vue | 11 +---- src/modules/visualization/types/layers.ts | 6 +-- .../visualizations/lob/Builder.ts | 6 +-- .../visualizations/lob/Customize.vue | 47 ------------------- .../visualizations/lob/Descriptor.ts | 7 --- .../visualizations/sigint/Customize.vue | 39 --------------- 7 files changed, 4 insertions(+), 125 deletions(-) diff --git a/src/modules/map/mapVisualizations.ts b/src/modules/map/mapVisualizations.ts index a45925f7..f8d945e2 100644 --- a/src/modules/map/mapVisualizations.ts +++ b/src/modules/map/mapVisualizations.ts @@ -148,7 +148,6 @@ export function createLoBLayer( let getOrigin: any; let getBearing: any; let getLobId: any; - let getIconColor: any; let getColor: any; for (const dsProps of dsArray) { @@ -191,16 +190,6 @@ export function createLoBLayer( }, }; } - // Check for iconColor property - if (dsProps.properties.lobIconColor) { - getIconColor = { - dataSourceIds: [dsInstance.id], - handler: (rec: any) => { - return colorHash(getLayerId(rec, dsProps.properties.lobIconColor.property)) - .rgba; - }, - }; - } // Check for line color property if (dsProps.properties.lobLineColor) { getColor = { @@ -220,12 +209,10 @@ export function createLoBLayer( ...viz.visualizationComponents.dataLayer, name: viz.name, id: viz.id, - icon: `${ICON_BASE}${viz.visualizationComponents.dataLayer.icon}`, dataSourceIds: dsInstances.map((ds) => ds.id), ...(getOrigin ? { getOrigin } : {}), ...(getBearing ? { getBearing } : {}), ...(getLobId ? { getLobId } : {}), - ...(getIconColor ? { getIconColor } : {}), ...(getColor ? { getColor } : {}), }); diff --git a/src/modules/visualization/sidebar/components/MapVisualizationWrapper.vue b/src/modules/visualization/sidebar/components/MapVisualizationWrapper.vue index 447ae044..65f56da6 100644 --- a/src/modules/visualization/sidebar/components/MapVisualizationWrapper.vue +++ b/src/modules/visualization/sidebar/components/MapVisualizationWrapper.vue @@ -40,16 +40,9 @@ function getIcon(viz: OSHVisualization) { } // Use iconName from dataLayer const iconName = viz.visualizationComponents.dataLayer.iconName; - if (iconName) { - if (viz.visualizationComponents.dataLayer.iconOpacity === 0) { - return VisualizationRegistry[viz.type].icon ?? 'mdi-shape'; - } - return `mdi-${iconName}`; - } + if (iconName) return `mdi-${iconName}`; // Otherwise, use viz type icon - else { - return VisualizationRegistry[viz.type].icon ?? 'mdi-shape'; - } + else return VisualizationRegistry[viz.type].icon ?? 'mdi-shape'; } function getIconColor(viz: OSHVisualization) { if (viz.isParentVisualization()) { diff --git a/src/modules/visualization/types/layers.ts b/src/modules/visualization/types/layers.ts index 717d09eb..41b655df 100644 --- a/src/modules/visualization/types/layers.ts +++ b/src/modules/visualization/types/layers.ts @@ -37,11 +37,7 @@ export interface ILineOfBearingLayerProperties extends DataLayerProperties { weight: number; opacity: number; length: number; - icon: string | null; // Allow null for optional icon - iconColor: string; - iconName: string; - iconSize: number[]; - iconOpacity: number; + iconName: string; // Used for display in map visualizations list label: string; } diff --git a/src/modules/visualization/visualizations/lob/Builder.ts b/src/modules/visualization/visualizations/lob/Builder.ts index 3027cfd8..f83fc527 100644 --- a/src/modules/visualization/visualizations/lob/Builder.ts +++ b/src/modules/visualization/visualizations/lob/Builder.ts @@ -61,11 +61,7 @@ export function CreateLobVizProps( weight: visOptions.lobWeight, opacity: visOptions.lobOpacity, length: visOptions.lobDistanceKm * 1000, // Convert km to m - icon: visOptions.showIcon ? visOptions.icon : null, - iconColor: visOptions.iconColor, - iconName: visOptions.iconName, - iconOpacity: visOptions.showIcon ? 1 : 0, // Set opacity to 0 if no icon, otherwise use default opacity - iconSize: [32, 32], + iconName: 'ray-start', label: visOptions.name, name: visOptions.name, }; diff --git a/src/modules/visualization/visualizations/lob/Customize.vue b/src/modules/visualization/visualizations/lob/Customize.vue index d8c078c8..ee09a79c 100644 --- a/src/modules/visualization/visualizations/lob/Customize.vue +++ b/src/modules/visualization/visualizations/lob/Customize.vue @@ -1,18 +1,13 @@ diff --git a/src/modules/visualization/visualizations/pointmarker/Builder.ts b/src/modules/visualization/visualizations/pointmarker/Builder.ts index 0fc7f3f4..48314602 100644 --- a/src/modules/visualization/visualizations/pointmarker/Builder.ts +++ b/src/modules/visualization/visualizations/pointmarker/Builder.ts @@ -94,6 +94,12 @@ export async function CreatePointMarkerVizProps( }, }; vizDatasources.push(currentDataSource); + + // If milSymbol, change icon name + if (properties.milSymbol) { + pointMarkerLayer.iconName = 'mil-symbol'; + pointMarkerLayer.iconColor = ''; // Reset icon color if using milsymbol + } } return { diff --git a/src/modules/visualization/visualizations/pointmarker/Customize.vue b/src/modules/visualization/visualizations/pointmarker/Customize.vue index eb4e4766..3f07a5a1 100644 --- a/src/modules/visualization/visualizations/pointmarker/Customize.vue +++ b/src/modules/visualization/visualizations/pointmarker/Customize.vue @@ -8,10 +8,12 @@ import { useComponentValidation } from '../../wizard/composables/useComponentVal import { useVizWizStore } from '@/stores/vizwizstore'; const vizwizStore = useVizWizStore(); -// If milSymbol is selected in Config, add alert to icon customize -const showFullIcon = computed(() => (vizwizStore.dsConfig.milSymbol ? false : true)); -// If pmIconColor selected in Config, don't show icon color select -const showIconColor = computed(() => (vizwizStore.dsConfig.pmIconColor ? false : true)); +// If milSymbol is selected in Config, don't show icon select +const showIcon = computed(() => (vizwizStore.dsConfig.milSymbol ? false : true)); +// If pmIconColor or milSymbol selected in Config, don't show icon color select +const showIconColor = computed(() => + vizwizStore.dsConfig.pmIconColor || vizwizStore.dsConfig.milSymbol ? false : true +); const openPanels = ref(['general', 'pointmarker']); @@ -62,23 +64,17 @@ useComponentValidation(valid, emit); > -
+
- Icon will be dynamically generated with the respective military symbol based on the selected properties from the previous step. - Select an icon to represent in the visualization panel. -
diff --git a/src/modules/visualization/visualizations/sigint/Customize.vue b/src/modules/visualization/visualizations/sigint/Customize.vue index d9c97d95..22da3988 100644 --- a/src/modules/visualization/visualizations/sigint/Customize.vue +++ b/src/modules/visualization/visualizations/sigint/Customize.vue @@ -13,8 +13,10 @@ import { EllipseConfigRoles } from '../ellipse/Descriptor'; const vizwizStore = useVizWizStore(); // POINTMARKER // If milSymbol is selected in PM Config, add alert to icon customize -const showFullPmIcon = computed(() => (vizwizStore.dsConfig.milSymbol ? false : true)); -const showPmIconColor = computed(() => (vizwizStore.dsConfig.pmIconColor ? false : true)); +const showPmIcon = computed(() => (vizwizStore.dsConfig.milSymbol ? false : true)); +const showPmIconColor = computed(() => + vizwizStore.dsConfig.pmIconColor || vizwizStore.dsConfig.milSymbol ? false : true +); // LOB const showLobLineColor = computed(() => (vizwizStore.dsConfig.lobLineColor ? false : true)); // ELLIPSE @@ -69,23 +71,17 @@ useComponentValidation(valid, emit); > -
+
- Icon will be dynamically generated with the respective military symbol based on the selected properties from the previous step. - Select an icon to represent in the visualization panel. -
From 25d419bb5d623d1efb508297c7b46c20ba157a7a Mon Sep 17 00:00:00 2001 From: kalynstricklin Date: Tue, 16 Jun 2026 08:46:08 -0500 Subject: [PATCH 20/29] reorder waypoints # Conflicts: # src/modules/visualization/visualizations/mission/MissionBuilder.vue # Conflicts: # package.json --- .../visualizations/mission/MissionBuilder.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/visualization/visualizations/mission/MissionBuilder.vue b/src/modules/visualization/visualizations/mission/MissionBuilder.vue index 511ad0ef..f5d24bf8 100644 --- a/src/modules/visualization/visualizations/mission/MissionBuilder.vue +++ b/src/modules/visualization/visualizations/mission/MissionBuilder.vue @@ -103,9 +103,9 @@ const fileInputRef = ref(null); const selectedFile = ref(null); const exportFilename = ref('mission'); -const droneDatasourceLLA = ref(null); -const droneHomeDatasource = ref(null); -let dsInstances = ref([]); +const droneDatasourceLLA = ref(null); +const droneHomeDatasource = ref(null); +let dsInstances = ref([]); let homeLocation = ref<{ lat: number; lon: number; alt: number }>({ lat: 0, lon: 0, alt: 0 }); @@ -518,7 +518,7 @@ function generateMissionControlPlan() { }; } -function onLLAListener(dsInstance: ConSysApi) { +function onLLAListener(dsInstance: typeof ConSysApi) { const dataBroadcastChannel = new BroadcastChannel(DATASOURCE_DATA_TOPIC + dsInstance.id); dataBroadcastChannel.onmessage = (message) => { From 314b177f470e54f9610ea12eb7918b404b34d444 Mon Sep 17 00:00:00 2001 From: kalynstricklin Date: Thu, 18 Jun 2026 09:22:07 -0500 Subject: [PATCH 21/29] refactor missionbuilder to controller pattern like geoptz + add support for legacy qGroundControlPlan schema # Conflicts: # src/modules/visualization/visualizations/mission/MissionBuilder.vue --- .../visualization/visualizations/mission/MissionBuilder.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/visualization/visualizations/mission/MissionBuilder.vue b/src/modules/visualization/visualizations/mission/MissionBuilder.vue index f5d24bf8..073a106f 100644 --- a/src/modules/visualization/visualizations/mission/MissionBuilder.vue +++ b/src/modules/visualization/visualizations/mission/MissionBuilder.vue @@ -1,6 +1,6 @@ @@ -60,6 +60,17 @@ onMounted(() => { :datasource="dataSource[0]" v-if="viz.type === 'text'" > + +
diff --git a/src/modules/visualization/visualizations/geoptz/Descriptor.ts b/src/modules/visualization/visualizations/geoptz/Descriptor.ts index 525ccd3a..d0870f4e 100644 --- a/src/modules/visualization/visualizations/geoptz/Descriptor.ts +++ b/src/modules/visualization/visualizations/geoptz/Descriptor.ts @@ -38,7 +38,7 @@ export const CustomizeComponent: VisualizationFormComponent = { export const GeoPtzDescriptor: VisualizationDescriptor = { label: 'GeoPTZ', id: 'geoPtz', - icon: 'mdi-map', + icon: 'mdi-camera-marker', viewLocation: 'multi', layers: ['PointMarkerLayer'], description: 'Task supported sensors with LLA coordinates.', diff --git a/src/modules/visualization/visualizations/minimap/Builder.ts b/src/modules/visualization/visualizations/minimap/Builder.ts new file mode 100644 index 00000000..92e0b028 --- /dev/null +++ b/src/modules/visualization/visualizations/minimap/Builder.ts @@ -0,0 +1,80 @@ +import { OSHVisualization } from '@/lib/OSHConnectDataStructs'; +import { useVisualizationStore } from '@/stores/visualizationstore'; +import { useVizWizStore } from '@/stores/vizwizstore'; +//@ts-ignore +import { Mode } from 'osh-js/source/core/datasource/Mode'; +// @ts-ignore +import { useDataStreamStore } from '@/stores/datastreamstore'; +import { MiniMapDescriptor } from './Descriptor'; +import { + AggregateDatastreams, + BuildRoleProperty, + getUsedDatastreams, +} from '../../services/aggregation.service'; +import { VisualizationComponents } from '../../types/visualization'; +import { ISweApiDataSourceProperties } from '../../types/datasource'; + + +export default function build() { + console.log('Building Mini Map Visualization...'); + const vizwizStore = useVizWizStore(); + const visualizationStore = useVisualizationStore(); + + const datastreams = AggregateDatastreams(vizwizStore.dsConfig); + + const minimapResult = CreateMiniMapVizProps(datastreams); + + const visualizationComponents: VisualizationComponents = { + dataSource: minimapResult.vizDatasources, + dataLayer: [], + controlstream: [], + }; + + const newViz: OSHVisualization = new OSHVisualization( + vizwizStore.id, + vizwizStore.visualizationCustomizationOptions.name, + 'minimap', + MiniMapDescriptor.viewLocation, + getUsedDatastreams(vizwizStore.datastreams, vizwizStore.dsConfig), + [] + ); + newViz.setVisualizationComponents(visualizationComponents); + newViz.setWizardConfig(vizwizStore.getWizardConfig()); + visualizationStore.addVisualization(newViz); + console.log('Created Mini Map Visualization:', newViz); +} + +export function CreateMiniMapVizProps(datastreams: { [key: string]: any } +) { + const datastreamStore = useDataStreamStore(); + + const vizDatastreams: ISweApiDataSourceProperties[] = []; + + for (const [dsId, entry] of Object.entries(datastreams)) { + const properties = BuildRoleProperty(entry); + + const currentOSHDatastream = datastreamStore.getDataStreamsById([dsId]); + + const currentDatastream: ISweApiDataSourceProperties = { + endpointUrl: currentOSHDatastream[0].datastream.networkProperties.endpointUrl, + resource: `/datastreams/${dsId}/observations`, + tls: currentOSHDatastream[0].datastream.networkProperties.tls, + protocol: 'ws', + mode: Mode.REAL_TIME, + responseFormat: 'application/swe+json', + id: currentOSHDatastream[0].id, + properties: properties, + connectorOpts: { + username: + currentOSHDatastream[0].datastream.networkProperties.connectorOpts.username, + password: + currentOSHDatastream[0].datastream.networkProperties.connectorOpts.password, + }, + }; + vizDatastreams.push(currentDatastream); + } + + return { + vizDatasources: vizDatastreams + }; +} diff --git a/src/modules/visualization/visualizations/minimap/Config.vue b/src/modules/visualization/visualizations/minimap/Config.vue new file mode 100644 index 00000000..d008138a --- /dev/null +++ b/src/modules/visualization/visualizations/minimap/Config.vue @@ -0,0 +1,100 @@ + + + + diff --git a/src/modules/visualization/visualizations/minimap/Customize.vue b/src/modules/visualization/visualizations/minimap/Customize.vue new file mode 100644 index 00000000..ab48d214 --- /dev/null +++ b/src/modules/visualization/visualizations/minimap/Customize.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/modules/visualization/visualizations/minimap/Descriptor.ts b/src/modules/visualization/visualizations/minimap/Descriptor.ts new file mode 100644 index 00000000..c8790c91 --- /dev/null +++ b/src/modules/visualization/visualizations/minimap/Descriptor.ts @@ -0,0 +1,37 @@ +import { defineAsyncComponent } from 'vue'; +import { VisualizationDescriptor, VisualizationFormComponent } from '../../registry/types'; + +export const ConfigComponent: VisualizationFormComponent = { + id: 'minimap-config', + label: 'Configure Mini Map Properties', + short: 'Configure', + component: defineAsyncComponent( + () => import('@/modules/visualization/visualizations/minimap/Config.vue') + ), +}; + +export const CustomizeComponent: VisualizationFormComponent = { + id: 'minimap-customize', + label: 'Customize Mini Map', + short: 'Customize', + component: defineAsyncComponent( + () => import('@/modules/visualization/visualizations/minimap/Customize.vue') + ), +}; + +export const MiniMapDescriptor: VisualizationDescriptor = { + label: 'Mini Map', + id: 'minimap', + icon: 'mdi-map', + viewLocation: 'multi', + layers: [], + description: 'A mini Cesium map view that can toggle between platform, follow and overhead views.', + formComponents: [ + ConfigComponent, + CustomizeComponent + ], + builder: () => import('@/modules/visualization/visualizations/minimap/Builder'), + supportsCs: false, + requireCs: false, + supportedMaps: ['cesium'], +}; diff --git a/src/modules/visualization/visualizations/minimap/MiniMapView.vue b/src/modules/visualization/visualizations/minimap/MiniMapView.vue new file mode 100644 index 00000000..cda6e6cb --- /dev/null +++ b/src/modules/visualization/visualizations/minimap/MiniMapView.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/src/modules/visualization/visualizations/mission/Builder.ts b/src/modules/visualization/visualizations/mission/Builder.ts index 5ec345fe..25172935 100644 --- a/src/modules/visualization/visualizations/mission/Builder.ts +++ b/src/modules/visualization/visualizations/mission/Builder.ts @@ -3,9 +3,12 @@ import { useVisualizationStore } from '@/stores/visualizationstore'; import { useVizWizStore } from '@/stores/vizwizstore'; //@ts-ignore import { Mode } from 'osh-js/source/core/datasource/Mode'; +// @ts-ignore +import { randomUUID } from 'osh-js/source/core/utils/Utils.js'; import { useControlStreamStore } from '@/stores/controlstreamstore'; import { useDataStreamStore } from '@/stores/datastreamstore'; import { MissionDescriptor } from './Descriptor'; +import { MiniMapDescriptor } from '../minimap/Descriptor'; import { AggregateControlstreams, AggregateDatastreams, @@ -18,6 +21,7 @@ import { IConSysApiControlStreamProperties, IConSysApiDataSourceProperties, } from '../../types/datasource'; +import { CreateMiniMapVizProps } from '../minimap/Builder'; export default function build() { console.log('Building Mission Visualization...'); @@ -29,12 +33,28 @@ export default function build() { const missionResult = CreateMissionVizProps(datastreams, controlstreams); - const visualizationComponents: VisualizationComponents = { + const missionVisualizationComponents: VisualizationComponents = { dataSource: missionResult.vizDatasources, dataLayer: [], controlstream: missionResult.vizControlstreams, }; + const minimapResult = CreateMiniMapVizProps(datastreams); + const minimapVisualizationComponents: VisualizationComponents = { + dataSource: minimapResult.vizDatasources, + dataLayer: [], + controlstream: [], + }; + const minimapViz: OSHVisualization = new OSHVisualization( + `${vizwizStore.id}-${randomUUID()}`, + `${vizwizStore.visualizationCustomizationOptions.name} - Mini Map`, + 'minimap', + MiniMapDescriptor.viewLocation, + getUsedDatastreams(vizwizStore.datastreams, vizwizStore.dsConfig), + [] + ); + minimapViz.setVisualizationComponents(minimapVisualizationComponents); + const newViz: OSHVisualization = new OSHVisualization( vizwizStore.id, vizwizStore.visualizationCustomizationOptions.name, @@ -43,8 +63,9 @@ export default function build() { getUsedDatastreams(vizwizStore.datastreams, vizwizStore.dsConfig), getUsedControlstreams(vizwizStore.controlstreams, vizwizStore.csConfig) ); - newViz.setVisualizationComponents(visualizationComponents); - newViz.setWizardConfig(vizwizStore.getWizardConfig()); // Save wizard state in visualization + newViz.setVisualizationComponents(missionVisualizationComponents); + newViz.setWizardConfig(vizwizStore.getWizardConfig()); + newViz.addChildVisualization([minimapViz]); visualizationStore.addVisualization(newViz); console.log('Created Mission Visualization:', newViz); } diff --git a/src/modules/visualization/visualizations/mission/Descriptor.ts b/src/modules/visualization/visualizations/mission/Descriptor.ts index 8178b154..c3ecda60 100644 --- a/src/modules/visualization/visualizations/mission/Descriptor.ts +++ b/src/modules/visualization/visualizations/mission/Descriptor.ts @@ -77,13 +77,22 @@ export const MissionConfigRoles: VisualizationConfigRole[] = [ export const ConfigComponent: VisualizationFormComponent = { id: 'mission-config', label: 'Configure Mission Builder Properties', - short: 'Configure', + short: 'Mission', component: defineAsyncComponent( () => import('@/modules/visualization/visualizations/mission/Config.vue') ), roles: MissionConfigRoles, }; +export const ConfigComponentMiniMap: VisualizationFormComponent = { + id: 'mission-config-minimap', + label: 'Configure Mission Builder Properties: Mini Map', + short: 'MiniMap', + component: defineAsyncComponent( + () => import('@/modules/visualization/visualizations/minimap/Config.vue') + ), +}; + export const CustomizeComponent: VisualizationFormComponent = { id: 'mission-customize', label: 'Customize Mission Builder', @@ -100,7 +109,11 @@ export const MissionDescriptor: VisualizationDescriptor = { viewLocation: 'multi', layers: ['PointMarkerLayer', 'PolylineLayer'], description: 'Create and manage missions for a drone.', - formComponents: [ConfigComponent, CustomizeComponent], + formComponents: [ + ConfigComponent, + ConfigComponentMiniMap, + CustomizeComponent + ], builder: () => import('@/modules/visualization/visualizations/mission/Builder'), supportsCs: true, // This visualization requires a controlstream to function requireCs: true, // This visualization requires a controlstream to function diff --git a/src/modules/visualization/visualizations/mission/MissionBuilder.vue b/src/modules/visualization/visualizations/mission/MissionBuilder.vue index 073a106f..d7f3d331 100644 --- a/src/modules/visualization/visualizations/mission/MissionBuilder.vue +++ b/src/modules/visualization/visualizations/mission/MissionBuilder.vue @@ -7,6 +7,7 @@ import { useMapStore } from '@/stores/mapstore'; import { showToast } from '@/composables/useToast'; import ConSysApi from 'osh-js/source/core/datasource/consysapi/ConSysApi.datasource.js'; import MissionCommandPad from './MissionCommandPad.vue'; +import PanelVisualizationWrapper from '../../sidebar/components/PanelVisualizationWrapper.vue'; import { createDatasource, disconnectDatasources, @@ -74,6 +75,7 @@ const missionControlStream = computed(() => const noController = computed(() => props.visualizations.length === 0); + interface Waypoint { id: string; lat: number; @@ -87,6 +89,10 @@ interface LLAData { alt: number; } +const minimapViz = computed(() => + props.visualization?.children?.find((c: OSHVisualization) => c.type === 'minimap') ?? null +); + const missionSource = ref<'waypoints' | 'file'>('waypoints'); const receivedLLA = ref({ lat: 0, lon: 0, alt: 0 }); @@ -94,7 +100,7 @@ const waypoints = ref([]); const latInput = ref(0.0); const lonInput = ref(0.0); -const altInput = ref(25); +const altInput = ref(25.0); const waypointForm = ref(null); const mapStore = useMapStore(); @@ -111,7 +117,7 @@ let homeLocation = ref<{ lat: number; lon: number; alt: number }>({ lat: 0, lon: const cruiseSpeed = ref(15); const hoverSpeed = ref(5); -const waypointAltitude = ref(25); +const waypointAltitude = ref(25.0); const altitudeMode = ref(1); const autoContinue = ref(true); const amslAltAboveTerrain = ref(null); @@ -195,6 +201,7 @@ function removeWaypoint(id: string) { const showClearConfirm = ref(false); const showMissionSummary = ref(false); +const minimapViewActive = ref(false); function confirmSendMission() { showMissionSummary.value = true; @@ -604,22 +611,53 @@ useVisualizationCleanup(dsInstances); class="pa-0 d-flex flex-column" v-if="!noController" > + +
+ Mini Map +
+ +
- Live Telemetry - +
+ + Live Telemetry + + Mini Map + + {{ minimapViewActive ? 'Hide mini map' : 'Show mini map' }} + + +
+ - Latitude - {{ receivedLLA.lat.toFixed(6) }} + + Latitude + + + {{ receivedLLA.lat.toFixed(6) }} + - Longitude - {{ receivedLLA.lon.toFixed(6) }} + + Longitude + + + {{ receivedLLA.lon.toFixed(6) }} + From d4746acbb0d7aa469f26e533f53a898481f0f9a5 Mon Sep 17 00:00:00 2001 From: kalynstricklin Date: Thu, 18 Jun 2026 12:22:42 -0500 Subject: [PATCH 23/29] rebased off of the refactored mission builder --- .../visualizations/minimap/Config.vue | 106 ++++-------------- .../visualizations/minimap/Descriptor.ts | 37 +++++- .../visualizations/minimap/MiniMapView.vue | 39 ++----- .../visualizations/mission/Descriptor.ts | 2 + .../visualizations/mission/MissionBuilder.vue | 2 +- 5 files changed, 71 insertions(+), 115 deletions(-) diff --git a/src/modules/visualization/visualizations/minimap/Config.vue b/src/modules/visualization/visualizations/minimap/Config.vue index d008138a..e5af3db2 100644 --- a/src/modules/visualization/visualizations/minimap/Config.vue +++ b/src/modules/visualization/visualizations/minimap/Config.vue @@ -4,96 +4,38 @@ import { computed, reactive, watch, onMounted, ref } from 'vue'; import DataSourcePicker from '../../wizard/components/DataSourcePicker.vue'; import { VisualizationComponentEmits } from '../../registry/VisualizationRegistry'; import { useComponentValidation } from '../../wizard/composables/useComponentValidation'; - -const vizwizStore = useVizWizStore(); - -// Checked status for each role -const checkedRoles = reactive({ - location: computed({ - get: () => vizwizStore.dsConfig.location?.selected ?? true, - set: (val: boolean) => vizwizStore.updateDsConfig('location', { selected: val }), - }), - orientation: computed({ - get: () => vizwizStore.dsConfig.orientation?.selected ?? false, - set: (val: boolean) => vizwizStore.updateDsConfig('orientation', { selected: val }), - }), - video: computed({ - get: () => vizwizStore.dsConfig.video?.selected ?? false, - set: (val: boolean) => vizwizStore.updateDsConfig('video', { selected: val }), - }), -}); - -// Initialize dsConfig with selected by default when mounted -onMounted(() => { - if (!vizwizStore.dsConfig.location) { - vizwizStore.updateDsConfig('location', { selected: true }); - } +import { MiniMapConfigRoles } from '@/modules/visualization/visualizations/minimap/Descriptor'; +import { VisualizationConfigRole } from '@/modules/visualization/registry/types'; +import { useConfig } from '@/modules/visualization/wizard/composables/useConfig'; +import RoleCheckbox from '@/modules/visualization/wizard/components/RoleCheckbox.vue'; +import ControlStreamPicker from '@/modules/visualization/wizard/components/ControlStreamPicker.vue'; + +const props = withDefaults(defineProps<{ configRoles: VisualizationConfigRole[] }>(), { + configRoles: () => MiniMapConfigRoles, }); -// If dsConfig is reset, ensure location is selected by default -watch( - () => vizwizStore.dsConfig, - (newVal) => { - if (!newVal.location) { - vizwizStore.updateDsConfig('location', { selected: true }); - } - }, - { deep: true } -); +const { checkedRoles, validRoles, valid } = useConfig(props.configRoles); // Validation const emit = defineEmits(); -const locationValid = ref(false); -const orientationValid = ref(false); -const roleVideoValid = ref(false); - -const valid = computed(() => { - const locationValidChecked = checkedRoles.location ? locationValid.value : true; - const orientationValidChecked = checkedRoles.orientation ? orientationValid.value : true; - const videoValid = checkedRoles.video ? roleVideoValid.value : true; - - return videoValid && locationValidChecked && orientationValidChecked; -}); useComponentValidation(valid, emit); diff --git a/src/modules/visualization/visualizations/minimap/Descriptor.ts b/src/modules/visualization/visualizations/minimap/Descriptor.ts index c8790c91..8fcb8b70 100644 --- a/src/modules/visualization/visualizations/minimap/Descriptor.ts +++ b/src/modules/visualization/visualizations/minimap/Descriptor.ts @@ -1,5 +1,37 @@ import { defineAsyncComponent } from 'vue'; -import { VisualizationDescriptor, VisualizationFormComponent } from '../../registry/types'; +import { + VisualizationConfigRole, + VisualizationDescriptor, + VisualizationFormComponent, +} from '../../registry/types'; +import { MissionConfigRoles } from '@/modules/visualization/visualizations/mission/Descriptor'; + +export const MiniMapConfigRoles: VisualizationConfigRole[] = [ + { + role: 'location', + label: 'Location', + description: 'Select the data stream for the vehicle\'s live position.', + type: 'ds', + required: true, + showPropertySelector: false, + }, + { + role: 'orientation', + label: 'Orientation/ Attitude', + description: 'Select the data stream for the direction of the vehicle\'s facing', + type: 'ds', + required: false, + showPropertySelector: false, + }, + { + role: 'video', + label: 'Video', + description: 'Select the data stream for the video overlay', + type: 'ds', + required: false, + showPropertySelector: false, + }, +]; export const ConfigComponent: VisualizationFormComponent = { id: 'minimap-config', @@ -8,6 +40,7 @@ export const ConfigComponent: VisualizationFormComponent = { component: defineAsyncComponent( () => import('@/modules/visualization/visualizations/minimap/Config.vue') ), + roles: MiniMapConfigRoles, }; export const CustomizeComponent: VisualizationFormComponent = { @@ -24,7 +57,7 @@ export const MiniMapDescriptor: VisualizationDescriptor = { id: 'minimap', icon: 'mdi-map', viewLocation: 'multi', - layers: [], + layers: ['VideoDataLayer', 'PointMarkerLayer', 'PolylineLayer'], description: 'A mini Cesium map view that can toggle between platform, follow and overhead views.', formComponents: [ ConfigComponent, diff --git a/src/modules/visualization/visualizations/minimap/MiniMapView.vue b/src/modules/visualization/visualizations/minimap/MiniMapView.vue index cda6e6cb..252bbc37 100644 --- a/src/modules/visualization/visualizations/minimap/MiniMapView.vue +++ b/src/modules/visualization/visualizations/minimap/MiniMapView.vue @@ -3,16 +3,14 @@ import { OSHVisualization } from '@/lib/OSHConnectDataStructs'; import { onMounted, onBeforeUnmount, ref, watch } from 'vue'; import * as Cesium from 'cesium'; import CesiumView from 'osh-js/source/core/ui/view/map/CesiumView'; -import SweApi from 'osh-js/source/core/datasource/sweapi/SweApi.datasource.js'; import { DATASOURCE_DATA_TOPIC } from 'osh-js/source/core/Constants.js'; import { createDatasource, } from '@/modules/visualization/services/datasource.service'; import { useVisualizationCleanup } from '../../sidebar/composables/useVisualizationCleanup'; -import { ISweApiDataSourceProperties } from '../../types/datasource'; +import { IConSysApiDataSourceProperties } from '../../types/datasource'; import { getGroundAltitude } from '@/modules/map/services/altitude.service'; - -// @ts-ignore +import ConSysApi from 'osh-js/source/core/datasource/consysapi/ConSysApi.datasource.js'; const props = defineProps({ visualization: { @@ -21,7 +19,7 @@ const props = defineProps({ default: null, }, datasource: { - type: Array as () => ISweApiDataSourceProperties[], + type: Array as () => IConSysApiDataSourceProperties[], required: true, default: () => [], }, @@ -42,9 +40,9 @@ interface OrientationData { const receivedLLA = ref({ lat: 0, lon: 0, alt: 0 }); const receivedOrientation = ref({ yaw: 0, pitch: 0, roll: 0 }); -let dsInstances = ref([]); +let dsInstances = ref([]); -let mapView: CesiumView | null = null; +let mapView: typeof CesiumView | null = null; const minimapContainerId = `minimap-${Date.now()}`; const viewMode = ref<'platform' | 'follow' | 'overhead'>('follow'); @@ -55,7 +53,6 @@ let lastSampledLat = 0; let lastSampledLon = 0; async function updateTerrainHeight(lon: number, lat: number) { - // Only re-sample when the drone moves > ~50 m const dLat = lat - lastSampledLat; const dLon = lon - lastSampledLon; if (Math.abs(dLat) < 0.0005 && Math.abs(dLon) < 0.0005) return; @@ -67,7 +64,7 @@ async function updateTerrainHeight(lon: number, lat: number) { if (height !== null) terrainHeight = height; } -function onLLAListener(dsInstance: SweApi) { +function onLLAListener(dsInstance: typeof ConSysApi) { const dataBroadcastChannel = new BroadcastChannel(DATASOURCE_DATA_TOPIC + dsInstance.id); dataBroadcastChannel.onmessage = (message) => { @@ -85,7 +82,7 @@ function onLLAListener(dsInstance: SweApi) { }; } -function onOrientationListener(dsInstance: SweApi) { +function onOrientationListener(dsInstance: typeof ConSysApi) { const dataBroadcastChannel = new BroadcastChannel(DATASOURCE_DATA_TOPIC + dsInstance.id); dataBroadcastChannel.onmessage = (message) => { @@ -105,9 +102,7 @@ function onOrientationListener(dsInstance: SweApi) { }; } -//yaw = turn left/right -// pitch = nose up/down -// roll = bank left/right + function updateCamera() { if (!mapView?.viewer) return; @@ -128,7 +123,7 @@ function updateCamera() { ); const heading = Cesium.Math.toRadians( - 90 - receivedOrientation.value.yaw + receivedOrientation.value.yaw ); const pitch = Cesium.Math.toRadians( @@ -140,13 +135,7 @@ function updateCamera() { ); switch (viewMode.value) { - - // - // PLATFORM VIEW — true first-person, full 6-DOF - // Camera sits at the drone and looks where it looks. - // case 'platform': { - // Release any previous lookAt lock viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); viewer.camera.setView({ @@ -159,10 +148,6 @@ function updateCamera() { }); break; } - - // - // FOLLOW VIEW — 3rd-person chase camera - // case 'follow': { const transform = Cesium.Transforms.headingPitchRollToFixedFrame( @@ -176,11 +161,6 @@ function updateCamera() { ); break; } - - // - // OVERHEAD VIEW — top-down, rotated to match heading - // so the drone always appears to face "up" on screen. - // case 'overhead': { const transform = Cesium.Transforms.headingPitchRollToFixedFrame( @@ -188,7 +168,6 @@ function updateCamera() { new Cesium.HeadingPitchRoll(heading, 0, 0) ); - // Offset straight up (+Z) in the heading-aligned frame viewer.camera.lookAtTransform( transform, new Cesium.Cartesian3(0, 0, 250) diff --git a/src/modules/visualization/visualizations/mission/Descriptor.ts b/src/modules/visualization/visualizations/mission/Descriptor.ts index c3ecda60..4b42975f 100644 --- a/src/modules/visualization/visualizations/mission/Descriptor.ts +++ b/src/modules/visualization/visualizations/mission/Descriptor.ts @@ -4,6 +4,7 @@ import { VisualizationDescriptor, VisualizationFormComponent, } from '../../registry/types'; +import { MiniMapConfigRoles } from '@/modules/visualization/visualizations/minimap/Descriptor'; export const MissionConfigRoles: VisualizationConfigRole[] = [ { @@ -91,6 +92,7 @@ export const ConfigComponentMiniMap: VisualizationFormComponent = { component: defineAsyncComponent( () => import('@/modules/visualization/visualizations/minimap/Config.vue') ), + roles: MiniMapConfigRoles }; export const CustomizeComponent: VisualizationFormComponent = { diff --git a/src/modules/visualization/visualizations/mission/MissionBuilder.vue b/src/modules/visualization/visualizations/mission/MissionBuilder.vue index d7f3d331..f609e404 100644 --- a/src/modules/visualization/visualizations/mission/MissionBuilder.vue +++ b/src/modules/visualization/visualizations/mission/MissionBuilder.vue @@ -90,7 +90,7 @@ interface LLAData { } const minimapViz = computed(() => - props.visualization?.children?.find((c: OSHVisualization) => c.type === 'minimap') ?? null + activeVisualization.value?.children?.find((c: OSHVisualization) => c.type === 'minimap') ?? null ); const missionSource = ref<'waypoints' | 'file'>('waypoints'); From d4e98bc49e5fca274aeb160aed551c5072b46607 Mon Sep 17 00:00:00 2001 From: kalynstricklin Date: Fri, 19 Jun 2026 08:21:46 -0500 Subject: [PATCH 24/29] added pml to minimap --- .../visualizations/minimap/MiniMapView.vue | 39 +++++++++++++++++++ .../visualizations/mission/MissionBuilder.vue | 3 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/modules/visualization/visualizations/minimap/MiniMapView.vue b/src/modules/visualization/visualizations/minimap/MiniMapView.vue index 252bbc37..9870b78f 100644 --- a/src/modules/visualization/visualizations/minimap/MiniMapView.vue +++ b/src/modules/visualization/visualizations/minimap/MiniMapView.vue @@ -11,6 +11,8 @@ import { useVisualizationCleanup } from '../../sidebar/composables/useVisualizat import { IConSysApiDataSourceProperties } from '../../types/datasource'; import { getGroundAltitude } from '@/modules/map/services/altitude.service'; import ConSysApi from 'osh-js/source/core/datasource/consysapi/ConSysApi.datasource.js'; +import { useVisualizationStore } from '@/stores/visualizationstore'; +import { createPointMarkerLayer } from '@/modules/map/mapVisualizations'; const props = defineProps({ visualization: { @@ -48,6 +50,10 @@ const minimapContainerId = `minimap-${Date.now()}`; const viewMode = ref<'platform' | 'follow' | 'overhead'>('follow'); const hasOrientation = ref(false); +const visualizationStore = useVisualizationStore(); +const duplicatedLayerIds = new Set(); +const duplicatedDsInstances: (typeof ConSysApi)[] = []; + let terrainHeight = 0; let lastSampledLat = 0; let lastSampledLon = 0; @@ -205,6 +211,8 @@ onMounted(async () => { dsInstance.connect(); dsInstances.value.push(dsInstance); } + + addPointMarkerLayers(); }); watch([receivedLLA, receivedOrientation], () => { @@ -218,6 +226,34 @@ watch(viewMode, () => { updateCamera(); }); +function addPointMarkerLayers() { + if (!mapView) return; + + const pmVizs = visualizationStore.getVisualizationsByType('pointmarker'); + for (const viz of pmVizs) { + if (duplicatedLayerIds.has(viz.id)) continue; + if (Array.isArray(viz.visualizationComponents)) continue; + + const dsArray = viz.visualizationComponents.dataSource; + if (!dsArray?.length) continue; + + const result = createPointMarkerLayer(viz, dsArray); + mapView.addLayer(result.vizLayer); + duplicatedLayerIds.add(viz.id); + + for (const ds of result.dsInstances) { + duplicatedDsInstances.push(ds); + } + } +} + +watch( + () => visualizationStore.mapVisualizations.length, + () => { + addPointMarkerLayers(); + } +); + onBeforeUnmount(() => { if (mapView) { mapView.destroy(); @@ -226,6 +262,9 @@ onBeforeUnmount(() => { for (const ds of dsInstances.value) { ds.disconnect(); } + for (const ds of duplicatedDsInstances) { + ds.disconnect(); + } }); useVisualizationCleanup(dsInstances); diff --git a/src/modules/visualization/visualizations/mission/MissionBuilder.vue b/src/modules/visualization/visualizations/mission/MissionBuilder.vue index f609e404..78451aee 100644 --- a/src/modules/visualization/visualizations/mission/MissionBuilder.vue +++ b/src/modules/visualization/visualizations/mission/MissionBuilder.vue @@ -317,7 +317,7 @@ async function isLegacyPlanSchema(): Promise { try { const { cs: storeCs } = mineControlObsPropsFromCS(cs.id); const schema = await fetchCsSchema(storeCs.controlstream); - console.log('schema', schema); + console.log('schema', schema) if (!schema?.parametersSchema) return false; const items = schema.parametersSchema.fields ?? schema.parametersSchema; @@ -549,6 +549,7 @@ function cleanupDatasources() { dsInstances.value = []; } + async function connectDatasources() { for (const ds of datasources.value) { let dsInstance = createDatasource(ds); From cd82d57a6907e1f90681dcede999bd68608b2504 Mon Sep 17 00:00:00 2001 From: kalynstricklin Date: Fri, 19 Jun 2026 15:06:39 -0500 Subject: [PATCH 25/29] add HUD overlay simplified, and add a free movement button option --- .../visualizations/minimap/MiniMapView.vue | 168 ++++++++++++++++-- 1 file changed, 157 insertions(+), 11 deletions(-) diff --git a/src/modules/visualization/visualizations/minimap/MiniMapView.vue b/src/modules/visualization/visualizations/minimap/MiniMapView.vue index 9870b78f..a8bc0289 100644 --- a/src/modules/visualization/visualizations/minimap/MiniMapView.vue +++ b/src/modules/visualization/visualizations/minimap/MiniMapView.vue @@ -1,6 +1,6 @@