diff --git a/src/DataView/APPDataview.vue b/src/DataView/APPDataview.vue index fc77f6b..365f6a4 100644 --- a/src/DataView/APPDataview.vue +++ b/src/DataView/APPDataview.vue @@ -5,7 +5,9 @@ - + 0) { @@ -185,6 +226,7 @@ export default { this.filesToSave = [] }, async openCurveSaveDialogForSelectedFiles() { + this.closeContextMenu() const paths = (this.selectedFilePaths && this.selectedFilePaths.length > 0) ? this.selectedFilePaths : (this.selectedFilePath ? [this.selectedFilePath] : []); @@ -213,4 +255,4 @@ h1 { height: 100%; width: 100%; } - \ No newline at end of file + diff --git a/src/DataView/utils/mapLocation.js b/src/DataView/utils/mapLocation.js new file mode 100644 index 0000000..978fafb --- /dev/null +++ b/src/DataView/utils/mapLocation.js @@ -0,0 +1,71 @@ +function toFixedNumber(value, digits = 6) { + return Number(Number(value).toFixed(digits)); +} + +function buildSeedFromText(text) { + const input = String(text || 'default'); + let hash = 0; + + for (let i = 0; i < input.length; i++) { + hash = ((hash << 5) - hash) + input.charCodeAt(i); + hash |= 0; + } + + return Math.abs(hash); +} + +function buildFallbackCoordinate(seedText) { + const seed = buildSeedFromText(seedText); + const lonRatio = (seed % 100000) / 100000; + const latRatio = (Math.floor(seed / 100000) % 100000) / 100000; + + return { + longitude: toFixedNumber(115.7 + (117.4 - 115.7) * lonRatio), + latitude: toFixedNumber(40.2 + (40.5 - 40.2) * latRatio) + }; +} + +function normalizeGpsCoordinate(gps) { + if (!gps) return null; + + let latitude = Number(gps.latitude); + let longitude = Number(gps.longitude); + + if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { + return null; + } + + // 某些数据源纬经度字段可能写反,优先纠正到合法范围 + if (Math.abs(latitude) > 90 && Math.abs(longitude) <= 90) { + [latitude, longitude] = [longitude, latitude]; + } + + if (Math.abs(latitude) > 90 || Math.abs(longitude) > 180) { + return null; + } + + return { + latitude: toFixedNumber(latitude), + longitude: toFixedNumber(longitude), + altitude: Number.isFinite(Number(gps.altitude)) ? Number(gps.altitude) : null + }; +} + +function resolveDisplayCoordinate(item) { + const environmentData = item?.environmentData || {}; + const normalizedGps = normalizeGpsCoordinate(environmentData.gps); + + if (normalizedGps) { + return normalizedGps; + } + + return { + ...buildFallbackCoordinate(environmentData.fileName || item?.name || ''), + altitude: null + }; +} + +export { + normalizeGpsCoordinate, + resolveDisplayCoordinate +}; diff --git a/src/DataView/vuecomponents/GuiForDataShow.vue b/src/DataView/vuecomponents/GuiForDataShow.vue index e64fb0a..1297114 100644 --- a/src/DataView/vuecomponents/GuiForDataShow.vue +++ b/src/DataView/vuecomponents/GuiForDataShow.vue @@ -5,7 +5,7 @@ justify-content: space-between;"> + @processOptionsChanged="onProcessOptionsChanged" @useDarkDnChanged="onUseDarkDnChanged" ref="ASDPlotShow" class="plotcontainer"> @@ -40,9 +40,20 @@ defineOptions({ name: "GuiForDataShow" }); +function createDefaultProcessConfig() { + return { + movingAverage: { + window: 5, + startMethod: 'shrink', + endMethod: 'shrink' + } + }; +} + const fromData = ref({ comBox1: spectralTypeList[0].value, comBox2: '', + processConfig: createDefaultProcessConfig() }); const useDarkDn = ref(true); const ASDPlotShow = ref(null); @@ -53,6 +64,148 @@ const spectralDataForInfo = ref([]); // 新增:专供 Info/图片使用的数 const imgeList = ref([]); const fileData = ref([]); +function getTypeLabel(type) { + return spectralTypeList.find(item => item.value === type)?.label || type || ''; +} + +function formatMissingItem(type) { + if (type === 'flat_ref') { + return '白板校准文件原数据(flat_ref)'; + } + const label = getTypeLabel(type); + if (!label) return ''; + if (!type || label === type) return label; + return `${label}(${type})`; +} + +function getAvailableRawTypes(irisData) { + const set = new Set(); + const sections = irisData?.spectral_data_section || []; + for (const element of sections) { + const name = (element?.name || '').toString().toLowerCase(); + if (!name) continue; + if (name.includes('ground_dn')) set.add('ground_dn'); + if (name.includes('flat_dn')) set.add('flat_dn'); + if (name.includes('dark_dn')) set.add('dark_dn'); + if (name.includes('flat_ref')) set.add('flat_ref'); + if (name.includes('gain')) set.add('gain'); + } + return set; +} + +function getRequiredRawTypesForSpectralType(spectralType, useDarkDn) { + const needDark = !!useDarkDn; + const addDark = (arr) => (needDark ? [...arr, 'dark_dn'] : arr); + + switch ((spectralType || '').toString()) { + case 'ground_dn': + return ['ground_dn']; + case 'flat_dn': + return ['flat_dn']; + case 'dark_dn': + return ['dark_dn']; + case 'gain': + return ['gain']; + case 'bbjzwj': + return ['flat_ref']; + case 'flat_ref': + return addDark(['ground_dn', 'flat_dn']); + case 'radiance_ground': + return addDark(['gain', 'ground_dn']); + case 'radiance_flat': + return addDark(['gain', 'flat_dn']); + case 'refrad': + return addDark(['gain', 'ground_dn', 'flat_dn']); + case 'ref_abs': + return addDark(['ground_dn', 'flat_dn', 'flat_ref']); + case 'fszd': + return addDark(['gain', 'ground_dn']); + default: + return []; + } +} + +function parseFiberValue(value) { + if (value === undefined || value === null) return undefined; + if (typeof value === 'number' && Number.isFinite(value)) return value; + if (typeof value === 'string') { + const match = value.match(/-?\d+(\.\d+)?/); + if (match) { + const n = Number(match[0]); + if (Number.isFinite(n)) return n; + } + } + return undefined; +} + +function parseFiberFromCaliFilePath(value) { + if (typeof value !== 'string' || !value) return undefined; + const match = value.match(/_(\d+(?:\.\d+)?)d_/i); + if (!match) return undefined; + const n = Number(match[1]); + return Number.isFinite(n) ? n : undefined; +} + +function isIrradianceMode(irisData) { + for (const info of irisData?.spectral_info_section || []) { + const explicit = parseFiberValue(info?.ForeOptics); + const parsed = explicit ?? parseFiberFromCaliFilePath(info?.cailifilePath) ?? parseFiberFromCaliFilePath(info?.califilePath); + if (parsed === undefined) continue; + const rounded = Math.round(Number(parsed)); + return rounded === 180 || rounded === 360; + } + return false; +} + +function buildNoDataDetailMessage(files, spectralType, useDarkDn) { + const usableFiles = (files || []).filter(file => file?.data); + const fileCount = usableFiles.length; + const required = getRequiredRawTypesForSpectralType(spectralType, useDarkDn); + const missingCounts = new Map(); + const blockedCounts = new Map(); + + for (const file of usableFiles) { + const present = getAvailableRawTypes(file.data); + for (const dep of required) { + if (!present.has(dep)) { + missingCounts.set(dep, (missingCounts.get(dep) || 0) + 1); + } + } + + if (spectralType === 'radiance_ground' && isIrradianceMode(file.data)) { + blockedCounts.set('辐射照度模式不生成地物辐射亮度(radiance_ground)', (blockedCounts.get('辐射照度模式不生成地物辐射亮度(radiance_ground)') || 0) + 1); + } + if (spectralType === 'fszd' && !isIrradianceMode(file.data)) { + blockedCounts.set('非辐射照度模式不生成辐射照度(fszd)', (blockedCounts.get('非辐射照度模式不生成辐射照度(fszd)') || 0) + 1); + } + } + + const missingList = Array.from(missingCounts.entries()) + .sort((a, b) => b[1] - a[1]) + .map(([dep, count]) => { + const formatted = formatMissingItem(dep); + if (!formatted) return ''; + return count === fileCount ? formatted : `${formatted}(部分文件)`; + }) + .filter(Boolean); + + const blockedList = Array.from(blockedCounts.entries()) + .sort((a, b) => b[1] - a[1]) + .map(([reason, count]) => (count === fileCount ? reason : `${reason}(部分文件)`)); + + const parts = []; + if (missingList.length) parts.push(`缺少:${missingList.join('、')}`); + if (blockedList.length) parts.push(`原因:${blockedList.join('、')}`); + return parts.join(';'); +} + +function warnNoData() { + const spectralType = fromData.value.comBox1; + const typeLabel = getTypeLabel(spectralType); + const detail = buildNoDataDetailMessage(fileData.value, spectralType, useDarkDn.value); + ElMessage.warning(detail ? `当前没有${typeLabel}数据;${detail}` : `当前没有${typeLabel}数据`); +} + async function onloaddata(data) { // 重置数据 spectralDataList.value = []; @@ -67,13 +220,11 @@ async function onloaddata(data) { try { // 加载文件数据 fileData.value = await SpectralDataService.loadFileData(data); - // 处理数据 await processSpectralData(); if (!spectralDataList.value || spectralDataList.value.length === 0) { - const typeLabel = spectralTypeList.find(i => i.value === fromData.value.comBox1)?.label || fromData.value.comBox1; - ElMessage.warning(`当前数据没有${typeLabel}数据`); + warnNoData(); } } catch (error) { console.error('加载数据失败:', error); @@ -87,7 +238,8 @@ async function processSpectralData() { fileData.value, fromData.value.comBox1, fromData.value.comBox2, - useDarkDn.value + useDarkDn.value, + fromData.value.processConfig ); @@ -143,8 +295,7 @@ const onComBox1 = async (e) => { await processSpectralData(); if (!spectralDataList.value || spectralDataList.value.length === 0) { - const typeLabel = spectralTypeList.find(i => i.value === fromData.value.comBox1)?.label || fromData.value.comBox1; - ElMessage.warning(`当前数据没有${typeLabel}数据`); + warnNoData(); } }; @@ -155,8 +306,26 @@ const onComBox2 = async (val) => { await processSpectralData(); if (!spectralDataList.value || spectralDataList.value.length === 0) { - const typeLabel = spectralTypeList.find(i => i.value === fromData.value.comBox1)?.label || fromData.value.comBox1; - ElMessage.warning(`当前数据没有${typeLabel}数据`); + warnNoData(); + } + } +}; + +const onProcessOptionsChanged = async (config) => { + const nextConfig = createDefaultProcessConfig(); + if (config?.movingAverage) { + nextConfig.movingAverage = { + ...nextConfig.movingAverage, + ...config.movingAverage + }; + } + fromData.value.processConfig = nextConfig; + + if (fromData.value.comBox2 === 'MA' && fromData.value.comBox1) { + await processSpectralData(); + + if (!spectralDataList.value || spectralDataList.value.length === 0) { + warnNoData(); } } }; @@ -257,4 +426,4 @@ const onUseDarkDnChanged = async (val) => { border-radius: 4px; margin-left: 24px; } - \ No newline at end of file + diff --git a/src/DataView/vuecomponents/GuiForPlotShow.vue b/src/DataView/vuecomponents/GuiForPlotShow.vue index d3b1210..461dc2a 100644 --- a/src/DataView/vuecomponents/GuiForPlotShow.vue +++ b/src/DataView/vuecomponents/GuiForPlotShow.vue @@ -1,13 +1,77 @@