From f12a1a1280bbe50aa514b305d9bacd82e5d6311c Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 19 Sep 2023 14:41:19 -0800 Subject: [PATCH 01/32] adds placeholder dataset entry for OPERA RTC --- src/app/models/dataset.model.ts | 2 ++ src/app/models/datasets/index.ts | 1 + src/app/models/datasets/opera_s1.ts | 50 +++++++++++++++++++++++++++++ src/assets/i18n/en.json | 1 + 4 files changed, 54 insertions(+) create mode 100644 src/app/models/datasets/opera_s1.ts diff --git a/src/app/models/dataset.model.ts b/src/app/models/dataset.model.ts index c2262c304..33f4b0888 100644 --- a/src/app/models/dataset.model.ts +++ b/src/app/models/dataset.model.ts @@ -52,6 +52,7 @@ export type DatasetSubtypes = DatasetSubtype[]; export const sentinel_1 = fromDatasets.sentinel_1; export const sentinel_1_bursts = fromDatasets.sentinel_1_bursts; +export const opera_s1 = fromDatasets.opera_s1; export const alos = fromDatasets.alos; export const avnir = fromDatasets.avnir; export const sirc = fromDatasets.sirc; @@ -67,6 +68,7 @@ export const seasat = fromDatasets.seasat; export const datasetList: Dataset[] = [ fromDatasets.sentinel_1, fromDatasets.sentinel_1_bursts, + fromDatasets.opera_s1, fromDatasets.alos, fromDatasets.avnir, fromDatasets.sirc, diff --git a/src/app/models/datasets/index.ts b/src/app/models/datasets/index.ts index 5424ffb33..2fca65ae8 100644 --- a/src/app/models/datasets/index.ts +++ b/src/app/models/datasets/index.ts @@ -11,3 +11,4 @@ export * from './beta'; export * from './sirc'; export * from './avnir'; export * from './sentinel-1-burst'; +export * from './opera_s1'; diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts new file mode 100644 index 000000000..2dd2088a1 --- /dev/null +++ b/src/app/models/datasets/opera_s1.ts @@ -0,0 +1,50 @@ +import { Props } from '../filters.model'; + +export const opera_s1 = { + id: 'OPERA-S1', + name: 'OPERA-S1', + subName: '', + beta: true, + properties: [ + Props.DATE, + Props.BEAM_MODE, + Props.FLIGHT_DIRECTION, + Props.POLARIZATION, + Props.ABSOLUTE_ORBIT, + Props.BASELINE_TOOL, + Props.SUBTYPE, + ], + apiValue: { platform: 'SENTINEL-1', instrument: 'C-SAR', processingLevel: 'RTC' }, + date: { start: new Date('2014/06/15 03:44:43 UTC') }, + infoUrl: 'https://sentinels.copernicus.eu/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath', + citationUrl: 'https://asf.alaska.edu/data-sets/sar-data-sets/sentinel-1/sentinel-1-how-to-cite/', + frequency: 'C-Band', + source: { + name: 'ESA', + url: 'https://www.esa.int/ESA' + }, + productTypes: [ + { + apiValue: 'RTC', + displayName: 'OPERA RTC (RTC)' + } + ], + beamModes: [ + 'IW', 'EW' + ], + polarizations: [ + 'VV', + 'HH', + 'HV', + 'VH' + ], + subtypes: [{ + displayName: 'Sentinel-1A', + apiValue: 'SA', + }, { + displayName: 'Sentinel-1B', + apiValue: 'SB', + }], + platformDesc: 'OPERA_S1_DESC', + platformIcon: '/assets/icons/satellite_alt_black_48dp.svg', +}; diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 3d3fb597b..6ee5efa28 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -505,6 +505,7 @@ "OPEN_LINK_TO_DOCUMENTATION": "Open link to documentation.", "OPEN_MANUAL": "Open Manual", "OPEN_STREET_MAP_CONTRIBUTORS": "© OpenStreetMap contributors", + "OPERA_S1_DESC": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "OPTION_OR": "option or", "OPTION_OR ": "option or", "OPTIONS": "Options", From 9585934968b0bf70a81bf0d8d0ffe5ce5d396593 Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 19 Sep 2023 16:45:23 -0800 Subject: [PATCH 02/32] adds opera s1 products, adds cmr metadata entry for opera burst ID --- src/app/models/cmr-product.model.ts | 7 +++++++ src/app/models/datasets/opera_s1.ts | 11 ++++++++++- src/app/services/product.service.ts | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/app/models/cmr-product.model.ts b/src/app/models/cmr-product.model.ts index 220e5e425..03735666c 100644 --- a/src/app/models/cmr-product.model.ts +++ b/src/app/models/cmr-product.model.ts @@ -57,6 +57,9 @@ export interface CMRProductMetadata { // SLC BURST burst: SLCBurstMetadata | null; + // OPERA-S1 + opera: OperaS1Metadata | null; + fileName: string | null; job: Hyp3Job | null; @@ -75,6 +78,10 @@ export interface SLCBurstMetadata { subswath: string; } +export interface OperaS1Metadata { + operaBurstID: string; +} + export enum FlightDirection { ASCENDING = 'ASCENDING', DESCENDING = 'DESCENDING', diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index 2dd2088a1..e172b66b2 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -26,7 +26,16 @@ export const opera_s1 = { productTypes: [ { apiValue: 'RTC', - displayName: 'OPERA RTC (RTC)' + displayName: 'L2 Radiometric Terrain Corrected (RTC)' + }, { + apiValue: 'CSLC', + displayName: 'L2 Co-registered Single Look Complex (CSLC)' + }, { + apiValue: 'RTC-STATIC', + displayName: 'L2 Radiometric Terrain Corrected Static Layer (RTC-STATIC)' + }, { + apiValue: 'CSLC-STATIC', + displayName: 'L2 Co-registered Single Look Complex Static Layer (CSLC-STATIC)' } ], beamModes: [ diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts index 4344bb36a..9dfe0a92f 100644 --- a/src/app/services/product.service.ts +++ b/src/app/services/product.service.ts @@ -86,6 +86,7 @@ export class ProductService { job: null, fileName: null, burst: g.s1b ? g.s1b : null, + opera: g.s1o ? g.s10 : null, pgeVersion: g.pge !== null ? parseFloat(g.pge) : null }) From 723500c4e43c260b705980c0cec9882b51c0d12b Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 20 Sep 2023 09:54:51 -0800 Subject: [PATCH 03/32] adds collections keyword to opera_s1 apiValue, keeping platform value until SearchAPI update --- src/app/models/datasets/opera_s1.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index e172b66b2..77249bf43 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -14,7 +14,10 @@ export const opera_s1 = { Props.BASELINE_TOOL, Props.SUBTYPE, ], - apiValue: { platform: 'SENTINEL-1', instrument: 'C-SAR', processingLevel: 'RTC' }, + apiValue: { + platform: 'SENTINEL-1', + collections: + "OPERA_L2_CSLC-S1-STATIC_PROVISIONAL_V0,OPERA_L2_CSLC-S1_PROVISIONAL_V0,OPERA_L2_RTC-S1-STATIC_PROVISIONAL_V0,OPERA_L2_RTC-S1_PROVISIONAL_V0,OPERA_L2_RTC-S1_V1,OPERA_L2_RTC-S1-STATIC_V1,OPERA_L2_CSLC-S1_V1,OPERA_L2_CSLC-S1-STATIC_V1" }, date: { start: new Date('2014/06/15 03:44:43 UTC') }, infoUrl: 'https://sentinels.copernicus.eu/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath', citationUrl: 'https://asf.alaska.edu/data-sets/sar-data-sets/sentinel-1/sentinel-1-how-to-cite/', From 7ded8d505e647de3b00013c3d7a5865dcdf88757 Mon Sep 17 00:00:00 2001 From: codefactor-io Date: Wed, 20 Sep 2023 18:13:50 +0000 Subject: [PATCH 04/32] [CodeFactor] Apply fixes --- src/app/models/datasets/opera_s1.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index 77249bf43..06070741b 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -14,9 +14,9 @@ export const opera_s1 = { Props.BASELINE_TOOL, Props.SUBTYPE, ], - apiValue: { + apiValue: { platform: 'SENTINEL-1', - collections: + collections: "OPERA_L2_CSLC-S1-STATIC_PROVISIONAL_V0,OPERA_L2_CSLC-S1_PROVISIONAL_V0,OPERA_L2_RTC-S1-STATIC_PROVISIONAL_V0,OPERA_L2_RTC-S1_PROVISIONAL_V0,OPERA_L2_RTC-S1_V1,OPERA_L2_RTC-S1-STATIC_V1,OPERA_L2_CSLC-S1_V1,OPERA_L2_CSLC-S1-STATIC_V1" }, date: { start: new Date('2014/06/15 03:44:43 UTC') }, infoUrl: 'https://sentinels.copernicus.eu/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath', From 4d31c642fe9f4d4000c6381d5fd531c0c014fb58 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 20 Sep 2023 11:29:32 -0800 Subject: [PATCH 05/32] adds operaBurstID param. --- .../dataset-filters.component.html | 16 ++++++ .../dataset-filters/dataset-filters.module.ts | 2 + .../selectors/opera-s1-selector/index.ts | 2 + .../opera-burst-id-selector.component.html | 5 ++ .../opera-burst-id-selector.component.scss | 0 .../opera-burst-id-selector.component.ts | 53 +++++++++++++++++++ .../opera-s1-selector.component.html | 1 + .../opera-s1-selector.component.scss | 0 .../opera-s1-selector.component.ts | 10 ++++ .../opera-s1-selector.module.ts | 32 +++++++++++ src/app/models/search.model.ts | 1 + src/app/services/url-state.service.ts | 12 +++++ src/app/store/filters/filters.action.ts | 12 ++++- src/app/store/filters/filters.reducer.ts | 27 ++++++++-- src/assets/i18n/en.json | 1 + 15 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 src/app/components/shared/selectors/opera-s1-selector/index.ts create mode 100644 src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.html create mode 100644 src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.scss create mode 100644 src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.ts create mode 100644 src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.html create mode 100644 src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.scss create mode 100644 src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.ts create mode 100644 src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.module.ts diff --git a/src/app/components/filters-dropdown/dataset-filters/dataset-filters.component.html b/src/app/components/filters-dropdown/dataset-filters/dataset-filters.component.html index 073354819..e9c6d76d7 100644 --- a/src/app/components/filters-dropdown/dataset-filters/dataset-filters.component.html +++ b/src/app/components/filters-dropdown/dataset-filters/dataset-filters.component.html @@ -130,4 +130,20 @@ + + + + + + OPERA S1 Filters + + +
+ +
+
diff --git a/src/app/components/filters-dropdown/dataset-filters/dataset-filters.module.ts b/src/app/components/filters-dropdown/dataset-filters/dataset-filters.module.ts index 407d93157..5c8c537c0 100644 --- a/src/app/components/filters-dropdown/dataset-filters/dataset-filters.module.ts +++ b/src/app/components/filters-dropdown/dataset-filters/dataset-filters.module.ts @@ -18,6 +18,7 @@ import { DatasetSelectorModule } from '@components/shared/selectors/dataset-sele import { AoiOptionsModule } from '@components/shared/aoi-options'; import { DocsModalModule } from '@components/shared/docs-modal'; import { BurstSelectorModule } from '@components/shared/selectors/burst-selector'; +import { OperaS1SelectorModule } from '@components/shared/selectors/opera-s1-selector'; // import { TranslateModule } from "@ngx-translate/core"; import { SharedModule } from "@shared"; @@ -42,6 +43,7 @@ import { SharedModule } from "@shared"; AoiOptionsModule, SearchTypeSelectorModule, BurstSelectorModule, + OperaS1SelectorModule, SharedModule, // TranslateModule ], diff --git a/src/app/components/shared/selectors/opera-s1-selector/index.ts b/src/app/components/shared/selectors/opera-s1-selector/index.ts new file mode 100644 index 000000000..00f03d8e9 --- /dev/null +++ b/src/app/components/shared/selectors/opera-s1-selector/index.ts @@ -0,0 +1,2 @@ +export * from "./opera-s1-selector.module"; +export * from "./opera-s1-selector.component"; \ No newline at end of file diff --git a/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.html b/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.html new file mode 100644 index 000000000..487af654b --- /dev/null +++ b/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.html @@ -0,0 +1,5 @@ + + {{'OPERA_BURST_ID' | translate}} + + + \ No newline at end of file diff --git a/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.scss b/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.ts b/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.ts new file mode 100644 index 000000000..1911f8a18 --- /dev/null +++ b/src/app/components/shared/selectors/opera-s1-selector/opera-burst-id-selector/opera-burst-id-selector.component.ts @@ -0,0 +1,53 @@ +import { Component, OnInit, EventEmitter, OnDestroy } from '@angular/core'; +import { debounceTime, filter, map } from 'rxjs'; +import { SubSink } from 'subsink'; + +import * as filtersStore from '@store/filters'; +import { Store } from '@ngrx/store'; +import { AppState } from '@store'; +@Component({ + selector: 'app-opera-burst-id-selector', + templateUrl: './opera-burst-id-selector.component.html', + styleUrls: ['./opera-burst-id-selector.component.scss'] +}) +export class OperaBurstIdSelectorComponent implements OnInit, OnDestroy { + public operaBurstIDs: string[] = [] + private IDsInputUpdated: EventEmitter = new EventEmitter(); + private subs: SubSink = new SubSink(); + + constructor(private store$: Store) { } + + ngOnInit(): void { + this.subs.add( + this.IDsInputUpdated.pipe( + debounceTime(3.0), + filter(ids => ids !== null), + map(ids => { + const idsArray = ids.split(',').map(id => id.trim()); + return idsArray.filter(entry => entry.length > 0); + }), + filter(ids => ids !== this.operaBurstIDs) + ).subscribe(ids => this.updateIDs(ids)) + ); + + this.subs.add( + this.store$.select(filtersStore.getOperaBurstIDs) + .subscribe( + ids => this.operaBurstIDs = ids + ) + ); + } + + ngOnDestroy(): void { + this.subs.unsubscribe() + } + + public onChange(event: Event) { + const text = (event.target as HTMLInputElement).value + this.IDsInputUpdated.emit(text) + } + + private updateIDs(ids: string[]) { + this.store$.dispatch(new filtersStore.setOperaBurstID(ids)) + } +} diff --git a/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.html b/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.html new file mode 100644 index 000000000..b3a118d78 --- /dev/null +++ b/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.scss b/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.ts b/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.ts new file mode 100644 index 000000000..b9fe34391 --- /dev/null +++ b/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-opera-s1-selector', + templateUrl: './opera-s1-selector.component.html', + styleUrls: ['./opera-s1-selector.component.scss'] +}) +export class OperaS1SelectorComponent { + +} diff --git a/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.module.ts b/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.module.ts new file mode 100644 index 000000000..e47d51402 --- /dev/null +++ b/src/app/components/shared/selectors/opera-s1-selector/opera-s1-selector.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { OperaBurstIdSelectorComponent } from './opera-burst-id-selector/opera-burst-id-selector.component'; +import { OperaS1SelectorComponent } from './opera-s1-selector.component'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { FormsModule } from '@angular/forms'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatInputModule } from '@angular/material/input'; +import { MatSharedModule, SharedModule } from '@shared'; + + + +@NgModule({ + declarations: [ + OperaS1SelectorComponent, + OperaBurstIdSelectorComponent, + ], + imports: [ + CommonModule, + MatFormFieldModule, + MatExpansionModule, + MatInputModule, + FormsModule, + MatSharedModule, + SharedModule + ], + exports: [ + OperaS1SelectorComponent, + OperaBurstIdSelectorComponent + ] +}) +export class OperaS1SelectorModule { } diff --git a/src/app/models/search.model.ts b/src/app/models/search.model.ts index 6cf0cc9f2..a4c33811e 100644 --- a/src/app/models/search.model.ts +++ b/src/app/models/search.model.ts @@ -80,6 +80,7 @@ export interface GeographicFiltersType { selectedMission: null | string; fullBurstIDs: string[]; + operaBurstIDs: string[]; } export interface SarviewsFiltersType { diff --git a/src/app/services/url-state.service.ts b/src/app/services/url-state.service.ts index 711f72ddb..036b07afe 100644 --- a/src/app/services/url-state.service.ts +++ b/src/app/services/url-state.service.ts @@ -486,6 +486,13 @@ export class UrlStateService { map(list => ({ fullBurstIDs: list?.map(num => num.toString()).join(',') })) ), loader: this.loadFullBurstIDs + }, + { + name: 'operaBurstIDs', + source: this.store$.select(filterStore.getOperaBurstIDs).pipe( + map(list => ({ operaBurstID: list?.map(num => num.toString()).join(',') })) + ), + loader: this.loadOperaBurstIDs }]; } @@ -804,4 +811,9 @@ export class UrlStateService { const list = ids.split(','); return new filterStore.setFullBurst(list); }; + + private loadOperaBurstIDs = (ids: string): Action => { + const list = ids.split(','); + return new filterStore.setOperaBurstID(list); + }; } diff --git a/src/app/store/filters/filters.action.ts b/src/app/store/filters/filters.action.ts index 5ad1d8ad2..3ede28600 100644 --- a/src/app/store/filters/filters.action.ts +++ b/src/app/store/filters/filters.action.ts @@ -91,6 +91,8 @@ export enum FiltersActionType { SET_GEOCODE = '[Filters] Set geocode area name', SET_FULL_BURST = '[Filters] Set Full Burst IDs', + + SET_OPERA_BURST_ID = '[Filters] Set Full OPERA S1 Burst IDs' } export class SetSelectedDataset implements Action { @@ -432,6 +434,13 @@ export class setFullBurst implements Action { constructor(public payload: string[]) {} } +export class setOperaBurstID implements Action { + public readonly type = FiltersActionType.SET_OPERA_BURST_ID; + + constructor(public payload: string[]) {} +} + + export type FiltersActions = | SetSelectedDataset @@ -493,4 +502,5 @@ export type FiltersActions = | SetEventProductSorting | ClearEventFilters | ClearHyp3ProductTypes - | setFullBurst; + | setFullBurst + | setOperaBurstID; diff --git a/src/app/store/filters/filters.reducer.ts b/src/app/store/filters/filters.reducer.ts index 82ab59784..87ecfe118 100644 --- a/src/app/store/filters/filters.reducer.ts +++ b/src/app/store/filters/filters.reducer.ts @@ -50,6 +50,8 @@ export interface FiltersState { geocode: null | string; fullBurstIDs: null | string[]; + + operaBurstIDs: null | string[]; } @@ -120,7 +122,9 @@ export const initState: FiltersState = { geocode: null, - fullBurstIDs: [] + fullBurstIDs: [], + + operaBurstIDs: [] }; @@ -142,6 +146,7 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte polarizations: [], subtypes: [], fullBurstIDs: [], + operaBurstIDs: [], selectedMission: null, }; } @@ -394,7 +399,8 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte flightDirections: new Set([]), selectedMission: null, geocode: null, - fullBurstIDs: [] + fullBurstIDs: [], + operaBurstIDs: [] }; } @@ -610,7 +616,8 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte flightDirections: new Set(filters.flightDirections), subtypes, selectedMission: filters.selectedMission, - fullBurstIDs: filters.fullBurstIDs + fullBurstIDs: filters.fullBurstIDs, + operaBurstIDs: filters.operaBurstIDs }; } } @@ -726,6 +733,12 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte fullBurstIDs: action.payload } } + case FiltersActionType.SET_OPERA_BURST_ID: { + return { + ...state, + operaBurstIDs: action.payload + } + } default: { return state; } @@ -897,7 +910,8 @@ export const getGeographicSearch = createSelector( flightDirections: state.flightDirections, subtypes: state.subtypes, selectedMission: state.selectedMission, - fullBurstIDs: state.fullBurstIDs + fullBurstIDs: state.fullBurstIDs, + operaBurstIDs: state.operaBurstIDs }) ); @@ -1008,3 +1022,8 @@ export const getFullBurstIDs = createSelector( getFiltersState, (state: FiltersState) => state.fullBurstIDs ) + +export const getOperaBurstIDs = createSelector( + getFiltersState, + (state: FiltersState) => state.operaBurstIDs +) \ No newline at end of file diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 6ee5efa28..0d3308d40 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -505,6 +505,7 @@ "OPEN_LINK_TO_DOCUMENTATION": "Open link to documentation.", "OPEN_MANUAL": "Open Manual", "OPEN_STREET_MAP_CONTRIBUTORS": "© OpenStreetMap contributors", + "OPERA_BURST_ID": "Opera Burst ID", "OPERA_S1_DESC": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "OPTION_OR": "option or", "OPTION_OR ": "option or", From 9b2b757e13f78b5e68b500da3c90f32c4f2738a9 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 20 Sep 2023 13:29:47 -0800 Subject: [PATCH 06/32] fix typo in opera burst id label field --- src/assets/i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 0d3308d40..40ad762e2 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -505,7 +505,7 @@ "OPEN_LINK_TO_DOCUMENTATION": "Open link to documentation.", "OPEN_MANUAL": "Open Manual", "OPEN_STREET_MAP_CONTRIBUTORS": "© OpenStreetMap contributors", - "OPERA_BURST_ID": "Opera Burst ID", + "OPERA_BURST_ID": "Opera Burst IDs", "OPERA_S1_DESC": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "OPTION_OR": "option or", "OPTION_OR ": "option or", From 7586598aea93a8873fbcfcb1e179b2d26b8a09da Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 21 Sep 2023 10:24:11 -0800 Subject: [PATCH 07/32] uses actual concept ids for opera products --- src/app/models/datasets/opera_s1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index 77249bf43..bb54dc71f 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -17,7 +17,7 @@ export const opera_s1 = { apiValue: { platform: 'SENTINEL-1', collections: - "OPERA_L2_CSLC-S1-STATIC_PROVISIONAL_V0,OPERA_L2_CSLC-S1_PROVISIONAL_V0,OPERA_L2_RTC-S1-STATIC_PROVISIONAL_V0,OPERA_L2_RTC-S1_PROVISIONAL_V0,OPERA_L2_RTC-S1_V1,OPERA_L2_RTC-S1-STATIC_V1,OPERA_L2_CSLC-S1_V1,OPERA_L2_CSLC-S1-STATIC_V1" }, + "C1258354200-ASF,C1258354201-ASF,C1258121071-ASFDEV,C1258121066-ASFDEV" }, date: { start: new Date('2014/06/15 03:44:43 UTC') }, infoUrl: 'https://sentinels.copernicus.eu/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath', citationUrl: 'https://asf.alaska.edu/data-sets/sar-data-sets/sentinel-1/sentinel-1-how-to-cite/', From f41aca46d4a27a1d1dee6088727c5cf20b42578b Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 21 Sep 2023 11:10:48 -0800 Subject: [PATCH 08/32] removes platform keyword from opera_s1 --- src/app/models/datasets/opera_s1.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index 5c8816f70..525b88928 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -15,7 +15,6 @@ export const opera_s1 = { Props.SUBTYPE, ], apiValue: { - platform: 'SENTINEL-1', collections: "C1258354200-ASF,C1258354201-ASF,C1258121071-ASFDEV,C1258121066-ASFDEV" }, date: { start: new Date('2014/06/15 03:44:43 UTC') }, From 7e656388b04d367528c7534a2e7784c8ca4b5f3c Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 26 Sep 2023 11:16:30 -0800 Subject: [PATCH 09/32] current v0.4 products now displaying subproducts in files list --- .../scene-detail/scene-detail.component.html | 2 +- .../scenes-list-header.component.ts | 9 +- src/app/models/cmr-product.model.ts | 8 ++ src/app/services/product.service.ts | 93 ++++++++++++++++++- src/app/store/scenes/scenes.reducer.ts | 75 +++++++-------- 5 files changed, 142 insertions(+), 45 deletions(-) diff --git a/src/app/components/results-menu/scene-detail/scene-detail.component.html b/src/app/components/results-menu/scene-detail/scene-detail.component.html index 07158cb76..1eb58b242 100644 --- a/src/app/components/results-menu/scene-detail/scene-detail.component.html +++ b/src/app/components/results-menu/scene-detail/scene-detail.component.html @@ -145,7 +145,7 @@ diff --git a/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts b/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts index fda0be885..4f8519521 100644 --- a/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts +++ b/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts @@ -59,9 +59,12 @@ export class ScenesListHeaderComponent implements OnInit, OnDestroy { public numBaselineScenes$ = this.scenesService.scenes$().pipe( map(scenes => scenes.length), ); + + private products$ = this.scenesService.products$(); + public isBurstStack$ = combineLatest([ - this.scenesService.products$(), + this.products$, this.pairService.pairs$, this.store$.select(searchStore.getSearchType), ] @@ -88,7 +91,7 @@ export class ScenesListHeaderComponent implements OnInit, OnDestroy { this.sarviewsEventProducts.filter(prod => browseIds.includes(prod.product_id))) ); - private currentBurstProducts$ = this.scenesService.products$().pipe( + private currentBurstProducts$ = this.products$.pipe( map(products => products.filter(p => p.metadata.productType === 'BURST' || p.metadata.productType === 'BURST_XML') ) @@ -180,7 +183,7 @@ export class ScenesListHeaderComponent implements OnInit, OnDestroy { this.subs.add( combineLatest([ - this.scenesService.products$(), + this.products$, this.pairs$ ] ).subscribe( diff --git a/src/app/models/cmr-product.model.ts b/src/app/models/cmr-product.model.ts index 03735666c..8ebe5b104 100644 --- a/src/app/models/cmr-product.model.ts +++ b/src/app/models/cmr-product.model.ts @@ -65,6 +65,9 @@ export interface CMRProductMetadata { // versioning pgeVersion: number | null; + + // BURST XML, OPERA-S1 + subproducts: any[]; } export interface SLCBurstMetadata { @@ -80,6 +83,7 @@ export interface SLCBurstMetadata { export interface OperaS1Metadata { operaBurstID: string; + additionalUrls: string[]; } export enum FlightDirection { @@ -96,3 +100,7 @@ export enum ColumnSortDirection { DECREASING = 'DECREASING', NONE = 'NONE', } + +export interface CMRSubProduct extends CMRProduct{ + parentID: string; +} \ No newline at end of file diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts index 9dfe0a92f..fed050ab4 100644 --- a/src/app/services/product.service.ts +++ b/src/app/services/product.service.ts @@ -35,7 +35,7 @@ export class ProductService { if ( !filename.includes(g.gn)) { filename = `${g.gn}-${filename}`; } - return ({ + let product = { name: g.gn, productTypeDisplay: g.ptd || g.gn, file: filename, @@ -48,7 +48,10 @@ export class ProductService { groupId: g.gid.replace('{gn}', g.gn), isUnzippedFile: false, metadata: this.getMetadataFrom(g) - }); + }; + + product.metadata.subproducts = this.getSubproducts(product) + return product; } ); @@ -86,8 +89,9 @@ export class ProductService { job: null, fileName: null, burst: g.s1b ? g.s1b : null, - opera: g.s1o ? g.s10 : null, - pgeVersion: g.pge !== null ? parseFloat(g.pge) : null + opera: g.s1o ? g.s1o : null, + pgeVersion: g.pge !== null ? parseFloat(g.pge) : null, + subproducts: [] }) private isNumber = n => !isNaN(n) && isFinite(n); @@ -95,4 +99,85 @@ export class ProductService { (dateString: string): moment.Moment => { return moment.utc(dateString); } + + private getSubproducts(product: models.CMRProduct): models.CMRSubProduct[] { + if (product.metadata.productType === 'BURST') { + return [this.burstXMLFromScene(product)] + } + if (!!product.metadata.opera) { + return this.operaSubproductsFromScene(product) + } + return [] + } + + + private burstXMLFromScene(product: models.CMRProduct) { + let p = { + ...product, + downloadUrl: product.downloadUrl.replace('tiff', 'xml'), + productTypeDisplay: 'XML Metadata (BURST)', + file: product.file.replace('tiff', 'xml'), + id: product.id + '-XML', + bytes: 0, + metadata: { + ...product.metadata, + productType: product.metadata.productType + '_XML' + }, + parentID: product.id + } as models.CMRSubProduct; + + return p; + } + + private operaSubproductsFromScene(product: models.CMRProduct) { + let products = [] + // incidence_angle + // local_incidence_angle + // mask + // number_of_looks + // rtc_anf_gamma0_to_beta0 + // rtc_anf_gamma0_to_sigma0 + + // product_types = [ + // 'incidence_angle', + // 'local_incidence_angle', + // 'mask', + // 'number_of_looks', + // 'rtc_anf_gamma0_to_beta0', + // 'rtc_anf_gamma0_to_sigma0', + // ] + const display = { + 'incidence_angle': 'Incidence Angle (TIF)', + 'local_incidence_angle': 'Local Incidence Angle (TIF)', + 'mask': 'Mask (TIF)', + 'number_of_looks': 'Number of Looks (TIF)', + 'rtc_anf_gamma0_to_beta0': 'RTC Anf Gamma0 to Beta0 (TIF)', + 'rtc_anf_gamma0_to_sigma0': 'RTC Anf Gamma0 to Sigma0 (TIF)', + } + for (const p of product.metadata.opera.additionalUrls) { + const file_suffix = p.split('v0.')[1] + const fileID = 'v0.' + file_suffix + const file_name = file_suffix.slice(2, file_suffix.length - 4) + console.log(file_name) + let subproduct = { + ...product, + downloadUrl: p, + productTypeDisplay: display[file_name] || 'Opera Subproduct', + file: fileID, + id: product.id + '-' + file_name, + bytes: 0, + metadata: { + ...product.metadata, + productType: product.metadata.productType + '_TIF' + }, + parentID: product.id + } as models.CMRSubProduct; + + if(subproduct.productTypeDisplay !== 'Opera Subproduct') { + products.push(subproduct) + } + } + + return products + } } diff --git a/src/app/store/scenes/scenes.reducer.ts b/src/app/store/scenes/scenes.reducer.ts index 9d4f405db..e09b3fff0 100644 --- a/src/app/store/scenes/scenes.reducer.ts +++ b/src/app/store/scenes/scenes.reducer.ts @@ -2,7 +2,7 @@ import { createFeatureSelector, createSelector } from '@ngrx/store'; import { ScenesActionType, ScenesActions } from './scenes.action'; -import { CMRProduct, UnzippedFolder, ColumnSortDirection, SarviewsEvent, SarviewsProduct } from '@models'; +import { CMRProduct, UnzippedFolder, ColumnSortDirection, SarviewsEvent, SarviewsProduct, CMRSubProduct } from '@models'; import { PinnedProduct } from '@services/browse-map.service'; import { createSelectorFactory, defaultMemoize } from '@ngrx/store'; @@ -66,19 +66,21 @@ export const initState: ScenesState = { export function scenesReducer(state = initState, action: ScenesActions): ScenesState { switch (action.type) { case ScenesActionType.SET_SCENES: { - let bursts: CMRProduct[] = [] + let subproducts: CMRProduct[] = [] let searchResults = action.payload.products.map(p => p.metadata.productType === 'BURST' ? ({...p, productTypeDisplay: 'Single Look Complex (BURST)'}) as CMRProduct : p) for (let product of searchResults) { - if(product.metadata.productType === 'BURST') { - const p = burstXMLFromScene(product) - bursts.push(p) + if(product.metadata.subproducts.length > 0) { + // const p = burstXMLFromScene(product) + for (let subproduct of product.metadata.subproducts) { + subproducts.push(subproduct) + } } } - searchResults = searchResults.concat(bursts) + searchResults = searchResults.concat(subproducts) const products = searchResults .reduce((total, product) => { @@ -87,30 +89,39 @@ export function scenesReducer(state = initState, action: ScenesActions): ScenesS return total; }, {}); - const productIDs = searchResults.reduce((total, product) => { - total[product.metadata.productType] = product; + // const productIDs = searchResults.reduce((total, product) => { + // total[product.metadata.productType] = product; - return total; - }, {}); + // return total; + // }, {}); let productGroups: {[id: string]: string[]} = {} let scenes: {[id: string]: string[]} = {} - if (Object.keys(productIDs).length <= 2 && Object.keys(productIDs)[0].toUpperCase() === 'BURST') { - productGroups = searchResults.reduce((total, product) => { - const scene = total[product.name] || []; + // if (Object.keys(productIDs).length <= 2 && Object.keys(productIDs)[0].toUpperCase() === 'BURST') { + // productGroups = searchResults.reduce((total, product) => { + // const scene = total[product.name] || []; - total[product.name] = [...scene, product.id]; - return total; - }, {}) - } else { + // total[product.name] = [...scene, product.id]; + // return total; + // }, {}) + // } else { productGroups = searchResults.reduce((total, product) => { - const scene = total[product.groupId] || []; - - total[product.groupId] = [...scene, product.id]; + // if isSubProduct(product) { + + // } + let groupCriteria = product.groupId; + if (product.metadata.subproducts.length > 0) { + groupCriteria = product.id; + } else if(isSubProduct(product)) { + groupCriteria = (product as CMRSubProduct).parentID; + } + const scene = total[groupCriteria] || []; + + total[groupCriteria] = [...scene, product.id]; return total; }, {}); - } + // } for (const [groupId, productNames] of Object.entries(productGroups)) { @@ -482,7 +493,10 @@ const productsForScene = (selected, state) => { if (Object.keys(productTypes).length <= 2 && Object.keys(productTypes)[0] === 'BURST') { products = state.scenes[selected.name] || []; - } else { + } else if(selected.metadata.subproducts.length > 0) { + products = state.scenes[selected.id] || []; + } + else { products = state.scenes[selected.groupId] || [] } @@ -739,19 +753,6 @@ function eqSet(aSet, bSet): boolean { return true; } -function burstXMLFromScene(product: CMRProduct) { - let p = { - ...product, - downloadUrl: product.downloadUrl.replace('tiff', 'xml'), - productTypeDisplay: 'XML Metadata (BURST)', - file: product.file.replace('tiff', 'xml'), - id: product.id + '-XML', - bytes: 0, - metadata: { - ...product.metadata, - productType: product.metadata.productType + '_XML' - } - } as CMRProduct; - - return p; +function isSubProduct(product): boolean { + return 'parentID' in product; } \ No newline at end of file From 8687bdb2c8e6688170e4b6c320f593c1348e4048 Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 26 Sep 2023 16:16:32 -0800 Subject: [PATCH 10/32] adds extension to end of files list display --- src/app/services/product.service.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts index fed050ab4..b84c12b6f 100644 --- a/src/app/services/product.service.ts +++ b/src/app/services/product.service.ts @@ -154,15 +154,20 @@ export class ProductService { 'rtc_anf_gamma0_to_beta0': 'RTC Anf Gamma0 to Beta0 (TIF)', 'rtc_anf_gamma0_to_sigma0': 'RTC Anf Gamma0 to Sigma0 (TIF)', } - for (const p of product.metadata.opera.additionalUrls) { + for (const p of product.metadata.opera.additionalUrls.slice(1)) { const file_suffix = p.split('v0.')[1] const fileID = 'v0.' + file_suffix const file_name = file_suffix.slice(2, file_suffix.length - 4) console.log(file_name) + + const extension = p.split('.').slice(-1)[0] + const fileDisplay = file_name.replace('_', ' ').toLowerCase() + ` (${extension.toUpperCase()})` + + let subproduct = { ...product, downloadUrl: p, - productTypeDisplay: display[file_name] || 'Opera Subproduct', + productTypeDisplay: display[file_name] || fileDisplay, file: fileID, id: product.id + '-' + file_name, bytes: 0, From 30919d61d7e72a3675d741aa6d8be66c774f2bde Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 27 Sep 2023 12:24:55 -0800 Subject: [PATCH 11/32] adds collections with 1.0 products to opera_s1 definition --- src/app/models/datasets/opera_s1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index 525b88928..d1a5fbc93 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -16,7 +16,7 @@ export const opera_s1 = { ], apiValue: { collections: - "C1258354200-ASF,C1258354201-ASF,C1258121071-ASFDEV,C1258121066-ASFDEV" }, + "C1258354200-ASF,C1258354201-ASF,C1258121071-ASFDEV,C1258121066-ASFDEV,C1259974840-ASF,C1259976861-ASF,C1259981910-ASF,C1259982010-ASF,C1259975087-ASFDEV,C1259976862-ASFDEV,C1259983643-ASFDEV,C1259983645-ASFDEV" }, date: { start: new Date('2014/06/15 03:44:43 UTC') }, infoUrl: 'https://sentinels.copernicus.eu/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath', citationUrl: 'https://asf.alaska.edu/data-sets/sar-data-sets/sentinel-1/sentinel-1-how-to-cite/', From a6eafe03b8abec225aee61a5b9b7ff95457c7cb7 Mon Sep 17 00:00:00 2001 From: William Horn Date: Thu, 28 Sep 2023 08:29:59 -0800 Subject: [PATCH 12/32] fix: switch env to use api_maturity -> test by default --- src/app/services/env.ts | 2 +- src/app/services/envs/env-devel.ts | 2 +- src/app/services/envs/env-test.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/services/env.ts b/src/app/services/env.ts index f48499233..b4c3b5152 100644 --- a/src/app/services/env.ts +++ b/src/app/services/env.ts @@ -11,7 +11,7 @@ export const env = { }, test: { api: 'https://api-test.asf.alaska.edu', - api_maturity: 'prod', + api_maturity: 'test', auth: 'https://auth.asf.alaska.edu', urs: 'https://urs.earthdata.nasa.gov', urs_client_id: 'BO_n7nTIlMljdvU6kRRB3g', diff --git a/src/app/services/envs/env-devel.ts b/src/app/services/envs/env-devel.ts index f48499233..b4c3b5152 100644 --- a/src/app/services/envs/env-devel.ts +++ b/src/app/services/envs/env-devel.ts @@ -11,7 +11,7 @@ export const env = { }, test: { api: 'https://api-test.asf.alaska.edu', - api_maturity: 'prod', + api_maturity: 'test', auth: 'https://auth.asf.alaska.edu', urs: 'https://urs.earthdata.nasa.gov', urs_client_id: 'BO_n7nTIlMljdvU6kRRB3g', diff --git a/src/app/services/envs/env-test.ts b/src/app/services/envs/env-test.ts index da358b457..b4c3b5152 100644 --- a/src/app/services/envs/env-test.ts +++ b/src/app/services/envs/env-test.ts @@ -11,7 +11,7 @@ export const env = { }, test: { api: 'https://api-test.asf.alaska.edu', - api_maturity: 'prod', + api_maturity: 'test', auth: 'https://auth.asf.alaska.edu', urs: 'https://urs.earthdata.nasa.gov', urs_client_id: 'BO_n7nTIlMljdvU6kRRB3g', @@ -22,4 +22,3 @@ export const env = { }, defaultEnv: 'test' }; - From a75a301cbbf846e21735dd51bc49e34b937272cd Mon Sep 17 00:00:00 2001 From: William Horn Date: Thu, 28 Sep 2023 11:50:32 -0800 Subject: [PATCH 13/32] chore: rip out unused hyp3 code --- .../create-subscription.component.html | 331 ------------ .../create-subscription.component.scss | 284 ---------- .../create-subscription.component.ts | 500 ------------------ .../create-subscription.module.ts | 58 -- .../header/create-subscription/index.ts | 2 - .../subscription-date-range.component.html | 10 - .../subscription-date-range.component.scss | 0 .../subscription-date-range.component.ts | 56 -- .../header-buttons.component.html | 12 - .../header-buttons.component.ts | 4 - src/app/components/header/header.module.ts | 2 - .../processing-queue.component.html | 9 +- .../processing-queue.component.ts | 10 - .../processing-queue.module.ts | 2 - .../baseline-results-menu.component.html | 2 +- .../scenes-list-header.component.html | 6 +- .../on-demand-add-menu.component.html | 8 - .../on-demand-add-menu.component.ts | 23 - .../on-demand-add-menu.module.ts | 2 - .../sidebar/on-demand-subscriptions/index.ts | 2 - .../on-demand-subscription.component.html | 103 ---- .../on-demand-subscription.component.scss | 52 -- .../on-demand-subscription.component.ts | 135 ----- .../subscription-filters.component.html | 54 -- .../subscription-filters.component.scss | 6 - .../subscription-filters.component.ts | 24 - .../subscription-job-options.component.html | 9 - .../subscription-job-options.component.scss | 0 .../subscription-job-options.component.ts | 21 - .../on-demand-subscriptions.component.html | 60 --- .../on-demand-subscriptions.component.scss | 42 -- .../on-demand-subscriptions.component.ts | 200 ------- .../on-demand-subscriptions.module.ts | 55 -- .../components/sidebar/sidebar.component.html | 3 - src/app/components/sidebar/sidebar.module.ts | 2 - src/app/models/sidebar.model.ts | 1 - 36 files changed, 5 insertions(+), 2085 deletions(-) delete mode 100644 src/app/components/header/create-subscription/create-subscription.component.html delete mode 100644 src/app/components/header/create-subscription/create-subscription.component.scss delete mode 100644 src/app/components/header/create-subscription/create-subscription.component.ts delete mode 100644 src/app/components/header/create-subscription/create-subscription.module.ts delete mode 100644 src/app/components/header/create-subscription/index.ts delete mode 100644 src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.html delete mode 100644 src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.scss delete mode 100644 src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.ts delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/index.ts delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.html delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.scss delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.ts delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.html delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.scss delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.ts delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.html delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.scss delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.ts delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.html delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.scss delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.ts delete mode 100644 src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.module.ts diff --git a/src/app/components/header/create-subscription/create-subscription.component.html b/src/app/components/header/create-subscription/create-subscription.component.html deleted file mode 100644 index c8653d050..000000000 --- a/src/app/components/header/create-subscription/create-subscription.component.html +++ /dev/null @@ -1,331 +0,0 @@ - - - -
-
- - - - {{ 'SEARCH_OPTIONS' | translate }} - - -
-
- {{ 'SUBSCRIPTION_DATE' | translate }} -
*{{ errors.dateError }}
-
- - -
- -
-
- {{ 'AOI_OPTIONS' | translate }} -
*{{ errors.polygonError }}
-
- -
- -
- - {{ 'PATH' | translate }} - - {{ path !== null ? '' : ('NO_PATH_INPUT' | translate)}} - - - - {{ 'FRAME' | translate }} - - {{ frame !== null ? '' : ('NO_FRAME_INPUT' | translate) }} - -
-
- - - {{ 'SUBSCRIPTION_OPTIONS' | translate }} - -
-
- {{ 'JOB_TYPE' | translate }} -
- - - - - {{jobType.name}} - - - -
- -
-
- {{ 'DATASET_OPTIONS' | translate }} -
- - - - - {{types[type].displayName}} - - - - - {{ !productType ? ( 'SELECT_FILE_TYPE' | translate) : productType + ' ' + ('SELECTED' | translate | lowercase ) }} - - - - - - - {{type.displayName}} - - - - - {{ !productType ? ('SELECT_TYPE' | translate ) : subtype + ' ' + ('SELECTED' | translate | lowercase ) }} - - - - - - - {{direction.toUpperCase() | translate }} - - - - - {{ !flightDirection ? ('NO_FLIGHT_DIRECTION_SELECTED' | translate ) : ('FLIGHT_DIRECTION_SELECTED' | translate ) }} - - - - - - - {{pol}} - - - - - {{ polarization.length === 0 ? ('NO_POLARIZATION_SELECTED' | translate ) : ('POLARIZATION_SELECTED' | translate ) }} - - -
- - - -
- - - {{ 'REVIEW' | translate }} - -
- {{ 'BASED_ON_YOUR_SEARCH_PARAMETERS_THIS_SUBSCRIPTION_WILL_RUN' | translate }} - {{ currentProducts }} {{'JOBS_NOW' | translate }} {{ 'AND' | translate }} - {{ 'APPROXIMATELY' | translate }} {{ subEstimate }} {{ 'JOBS_PER_MONTH' | translate }}. -
- -
- {{ 'NAME' | translate }}: -
*{{ errors.projectNameError }}
- -
- -
- {{ 'JOB_TYPE' | translate }}: {{ jobTypeId }} -
-

{{ 'SEARCH_OPTIONS' | translate }}:

- -
-
- {{ 'DATASET' | translate }}: {{ subtype }} -
-
- {{ 'SEARCH_AREA' | translate }}: {{ showSearchAreaType(polygon) }} -
- -
- {{ 'STAR' | translate }}t: {{ dateRange.start | shortDate }} -
-
- {{ 'END' | translate }}: {{ dateRange.end | shortDate }} -
- -
- {{ 'FILE_TYPE' | translate }}: {{ productType }} -
- -
- {{ 'POLARIZATION' | translate }}: {{ polarization }} -
- -
- {{ 'FLIGHT_DIR' | translate }}: {{ flightDirection }} -
-
- -

{{ 'PROCESSING_OPTIONS' | translate }}:

- -
-
- {{ param.name }}: {{ param.val }} -
-
- -
-
-
- - -
-
- - - - diff --git a/src/app/components/header/create-subscription/create-subscription.component.scss b/src/app/components/header/create-subscription/create-subscription.component.scss deleted file mode 100644 index 8283d70e5..000000000 --- a/src/app/components/header/create-subscription/create-subscription.component.scss +++ /dev/null @@ -1,284 +0,0 @@ -@import "asf-theme"; - -$header-height: 95px; -$footer-height: 60px; -$bottom-offset: 10px; - -[mat-dialog-title] { - background: #369; // TODO: check this - color: $light-primary-text; - cursor: move; - text-align: center; -} - -.queue { - @include themify($themes) { - color: themed('dark-primary-text'); - background: themed('primary-light'); - } - - min-width: 350px; - height: 100%; - min-height: 500px; - margin: 0; - padding: 0; - position: relative; - display: flex; - align-items: flex-start; - justify-content: flex-start; - font-size: 25px; - font-weight: bold; -} - -.mat-dialog-content { - @include themify($themes) { - background-color: themed('background-white'); - } - - position: absolute; - display: block; - top: $header-height; - height: calc(100% - #{$header-height} - #{$footer-height} - #{$bottom-offset}); - max-height: calc(100% - #{$header-height} - #{$footer-height} - #{$bottom-offset}); - width: 100%; - overflow-y: hidden; - overflow-x: hidden; - padding: 0; - margin: 0; - - .mat-dialog-content-2 { - top: initial; - height: calc(100% - #{$footer-height}); - overflow: initial; - } -} - -.content-top-area { - display: flex; - flex-flow: row wrap; - padding: 0 0 8px 15px; - border-bottom: solid 2px grey; - height: 100%; -} - -.content-area { - padding: 0 0 0 15px; - height: 100%; -} - -.content-bottom-area { - display: flex; - flex-direction: row; - height: 100%; - overflow-y: auto; -} - -.pq-card-title-group { - @include themify($themes) { - background: themed('primary-light'); - color: themed('dark-primary-text'); - border-bottom-color: themed('primary-dark'); - } - - padding: 10px 10px 0 0; - border-bottom-style: solid; - border-bottom-width: 1px; - width: 100%; - height: calc(#{$header-height} - 16px); - position: absolute; - top: 5px; - flex: 1 1 auto; - text-align: left; -} - -.header-link { - font-size: 18px; - - a { - @include themify($themes) { - color: themed('blue-link'); - } - - text-decoration: underline; - } -} - -.doc-feedback-div-wrapper { - flex: 1 1 auto; -} - -.doc-feedback-div { - display: flex; - justify-content: flex-end; - margin-top: 29px; - margin-right: 20px; -} - -.docs-div { - cursor: pointer; - flex: 0 1 auto; - margin-right: 20px; - margin-top: 2px; -} - -.feedback-div { - cursor: pointer; - flex: 0 1 auto; -} - -.content-block { - display: flex; - flex-direction: column; - width: 100%; - text-align: left; -} - -.footer { - @include themify($themes) { - background: themed('primary-light'); - border-top-color: themed('primary-dark'); - } - - display: flex; - flex-direction: row; - justify-content: space-between; - height: 100%; - border-top: 1px; - border-top-style: solid; -} - -.mat-dialog-actions { - @include themify($themes) { - background-color: themed('primary-light'); - } - - position: absolute; - display: flex; - width: 100%; - height: $footer-height; - bottom: $bottom-offset; - margin-bottom: 0; - padding: 0; -} - -.subscription-footer { - @include themify($themes) { - background-color: themed('primary-light'); - border-top-color: themed('primary-dark'); - } - - display: flex; - flex-direction: row; - justify-content: flex-end; - height: 100%; - border-top: 1px; - border-top-style: solid; -} - -.subscription-footer-item { - margin: 15px 25px 15px 15px; -} - -.validate-toggle { - @include themify($themes) { - color: themed('dark-secondary-text'); - } - - margin-top: 22px; -} - -.validate-only { - @include themify($themes) { - color: themed('dark-primary-text'); - } -} - -.on-demand-branding { - display: inline-block; - vertical-align: middle; - margin-left: 10px; - margin-bottom: -5px; -} - -.on-demand-icon { - @include themify($themes) { - color: themed('dark-primary-text'); - } - @include md-icon-size(72px); -} - -.on-demand-text { - vertical-align: middle; - margin-top: -16px; -} - -.on-demand-subtitle { - @include themify($themes) { - color: themed('dark-secondary-text'); - } - - font-style: italic; - font-size: 14px; - margin-top: -15px; -} - -.sub-option { - margin-right: 8px; -} - -.step-error { - @include themify($themes) { - color: themed('err'); - } -} - -.close-x { - @include themify($themes) { - color: themed('dark-primary-text'); - } - - position: absolute; - top: 10px; - right: 24px; - text-align: right; - float: right; - cursor: pointer; - z-index: 100; -} - -.navigation-row { - display: flex; - flex-direction: row; - justify-content: center; - margin-top: 25px; - margin-bottom: 25px; -} - -.navigation-item { - flex: none; -} - -.mobile-navigation-item { - display: flex; - justify-content: space-between; - width: 100%; -} - -.hyp3-url { - @include themify($themes) { - color: themed('dark-secondary-text'); - } - - height: 32px; - font-size: 12px; - margin: -4px 0 6px 0; -} - -.hyp3-url-mobile { - margin: 17px 0 0 0; -} - -.branding-block { - display: flex; - flex-direction: column; -} diff --git a/src/app/components/header/create-subscription/create-subscription.component.ts b/src/app/components/header/create-subscription/create-subscription.component.ts deleted file mode 100644 index 7afb3e89a..000000000 --- a/src/app/components/header/create-subscription/create-subscription.component.ts +++ /dev/null @@ -1,500 +0,0 @@ -import { Component, OnInit, OnDestroy, ViewChild, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { MatStepper } from '@angular/material/stepper'; -import { SubSink } from 'subsink'; -import * as moment from 'moment'; - -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper'; - -import { Store } from '@ngrx/store'; -import { AppState } from '@store'; -import * as hyp3Store from '@store/hyp3'; - -import { - ScreenSizeService, MapService, Hyp3Service, EnvironmentService, - AsfApiService, NotificationService, WktService -} from '@services'; -import * as models from '@models'; - -enum CreateSubscriptionSteps { - SEARCH_OPTIONS = 0, - PROCESSING_OPTIONS = 1, - REVIEW = 2 -} - -@Component({ - selector: 'app-create-subscription', - templateUrl: './create-subscription.component.html', - styleUrls: ['./create-subscription.component.scss'], - providers: [{ - provide: STEPPER_GLOBAL_OPTIONS, useValue: {showError: true} - }] -}) -export class CreateSubscriptionComponent implements OnInit, OnDestroy { - @ViewChild('stepper', { static: false }) public stepper: MatStepper; - - public steps = CreateSubscriptionSteps; - - public jobTypeId = models.hyp3JobTypes.RTC_GAMMA.id; - public errors = { - dateError: null, - polygonError: null, - projectNameError: null, - }; - - public hyp3JobTypes = models.hyp3JobTypes; - public hyp3JobTypesList = models.hyp3JobTypesList; - public selectedJobTypeId: string | null = models.hyp3JobTypes.RTC_GAMMA.id; - - public types = { - 'GRD_HD': { - apiValue: 'GRD_HD', - displayName: 'L1 Detected High-Res Dual-Pol (GRD-HD)' - }, - 'SLC': { - apiValue: 'SLC', - displayName: 'L1 Single Look Complex (SLC)' - } - }; - public productTypes = ['SLC', 'GRD_HD']; - public productType = 'SLC'; - - public flightDirectionTypes = [ - 'All', - ...models.flightDirections - ]; - public flightDirection = 'All'; - - public polarizations = [ - 'VV+VH', - 'HH+HV', - 'VV', - 'HH', - ]; - public polarization = []; - public path: number = null; - public frame: number = null; - - public s1Subtypes = [{ - displayName: 'Sentinel-1', - apiValue: 'S1', - }, - ...models.sentinel_1.subtypes - ]; - public subtype = 'S1'; - - public processingOptionsList = []; - public processingOptions; - - public current: Date; - public dateRange: models.Range; - public projectName: string; - public searchFilters; - public polygon: string; - public subEstimate: number | null = null; - public currentProducts: number | null = null; - - public breakpoint: models.Breakpoints; - public breakpoints = models.Breakpoints; - public validateOnly = false; - public searchOptionErrorsFound = true; - public subscriptionOptionErrorsFound = true; - public reviewErrorsFound = false; - public errorsFound = true; - - private subs = new SubSink(); - - public searchOptionsFormGroup: UntypedFormGroup; - public reviewFormGroup: UntypedFormGroup; - - constructor( - @Inject(MAT_DIALOG_DATA) public data: any, - public dialogRef: MatDialogRef, - private screenSize: ScreenSizeService, - private mapService: MapService, - private wktService: WktService, - private hyp3: Hyp3Service, - public env: EnvironmentService, - private asfApi: AsfApiService, - private store$: Store, - private _formBuilder: UntypedFormBuilder, - private notificationService: NotificationService, - ) { } - - ngOnInit(): void { - if (this.data !== null && this.data.referenceScene) { - const reference = this.data.referenceScene; - this.loadOptionsFromReferenceScene(reference); - this.selectedJobTypeId = models.hyp3JobTypes.INSAR_GAMMA.id; - this.setJobType(models.hyp3JobTypes.INSAR_GAMMA.id); - } - - // @ts-ignore - this.dialogRef.afterClosed().subscribe(x => { - this.store$.dispatch(new hyp3Store.ClearProcessingOptions()); - this.store$.dispatch(new hyp3Store.SetProcessingProjectName('')); - this.selectedJobTypeId = null; - this.subs.unsubscribe(); - this.dialogRef = null; - }); - this.current = new Date(); - - const end = new Date(); - this.dateRange = { - start: new Date(this.current.getTime()), - end: new Date(end.setDate(end.getDate() + 179)), - }; - - this.searchOptionsFormGroup = this._formBuilder.group({ - dateRange: [1, Validators.max(1)], - aoi: [1, Validators.max(1)] - }); - - this.reviewFormGroup = this._formBuilder.group({ - projectName: [1, Validators.max(1)] - }); - - this.subs.add( - this.screenSize.breakpoint$.subscribe( - breakpoint => this.breakpoint = breakpoint - ) - ); - - this.subs.add( - this.mapService.searchPolygon$.subscribe( - polygon => this.polygon = polygon - ) - ); - - this.subs.add( - this.store$.select(hyp3Store.getProcessingOptions).subscribe( - options => { - this.processingOptionsList = Object.entries(options).filter(([_, v]) => v); - this.processingOptions = options; - }) - ); - - this.subs.add( - this.store$.select(hyp3Store.getProcessingProjectName).subscribe( - name => this.projectName = name - ) - ); - } - - private loadOptionsFromReferenceScene(reference: models.CMRProduct): void { - const features = this.wktService.wktToFeature( - reference.metadata.polygon, - this.mapService.epsg() - ); - - this.mapService.setDrawFeature(features); - this.polarization = [reference.metadata.polarization]; - this.path = reference.metadata.path; - - this.flightDirectionTypes.forEach(flightDir => { - if (flightDir.toLowerCase() === reference.metadata.flightDirection.toLowerCase()) { - this.flightDirection = flightDir; - } - }); - } - - public onSelectionChange(): void { - this.checkErrors(); - } - - public onNewStartDate(d: Date): void { - this.dateRange.start = d; - - if (this.dateRange.end < this.dateRange.start && !!this.dateRange.end) { - this.dateRange.end = d; - } - } - - public onNewEndDate(d: Date): void { - this.dateRange.end = d; - - if (this.dateRange.start > this.dateRange.end && !!this.dateRange.start) { - this.dateRange.start = d; - } - } - - public onNext(): void { - const hasErrors = this.checkErrors(); - if (hasErrors) { - return; - } - - if (this.isLastStep() && !hasErrors) { - if (this.checkAllErrors()) { - return; - } - - this.submitSubscription(); - } else { - this.stepper.next(); - } - } - - public checkErrors(): boolean { - - if (this.stepper.selectedIndex === CreateSubscriptionSteps.SEARCH_OPTIONS) { - this.errors.dateError = null; - this.errors.polygonError = null; - this.checkSearchOptions(); - - return this.searchOptionErrorsFound; - } else if (this.stepper.selectedIndex === CreateSubscriptionSteps.PROCESSING_OPTIONS) { - this.checkSubscriptionOptions(); - return this.subscriptionOptionErrorsFound; - - } else if (this.stepper.selectedIndex === CreateSubscriptionSteps.REVIEW) { - this.checkReviewOptions(); - return this.reviewErrorsFound; - } - } - - public checkAllErrors(): boolean { - - this.searchOptionErrorsFound = true; - this.subscriptionOptionErrorsFound = true; - this.reviewErrorsFound = false; - this.errorsFound = true; - - this.errors.dateError = null; - this.errors.polygonError = null; - - this.checkSearchOptions(); - this.checkSubscriptionOptions(); - this.checkReviewOptions(); - - this.errorsFound = this.searchOptionErrorsFound || this.subscriptionOptionErrorsFound || this.reviewErrorsFound; - - return this.errorsFound; - } - - public checkSearchOptions() { - this.searchOptionErrorsFound = true; - - if (!this.dateRange.start && !this.dateRange.end) { - this.errors.dateError = 'Start and end date required'; - } else if (!this.dateRange.start) { - this.errors.dateError = 'Start date required'; - } else if (!this.dateRange.end) { - this.errors.dateError = 'End date required'; - } else if (this.dateRange.end < this.dateRange.start) { - this.errors.dateError = 'End date is before start date'; - } - - if (!this.polygon && this.frame === null && this.path === null) { - this.errors.polygonError = 'Area of interest, path or frame required (will use polygon from current search)'; - } - - if (this.errors.dateError) { - this.searchOptionsFormGroup.controls['dateRange'].setValue(2); - } else if (this.errors.polygonError) { - this.searchOptionsFormGroup.controls['aoi'].setValue(2); - } else { - this.searchOptionErrorsFound = false; - this.searchOptionsFormGroup.controls['aoi'].setValue(1); - this.searchOptionsFormGroup.controls['dateRange'].setValue(1); - } - } - - public checkSubscriptionOptions() { - this.onEstimateSubscription(); - this.subscriptionOptionErrorsFound = false; - } - - public checkReviewOptions() { - if (!this.projectName) { - this.errors.projectNameError = 'Project Name is required'; - this.reviewFormGroup.controls['projectName'].setValue(2); - this.reviewErrorsFound = true; - } else { - this.reviewFormGroup.controls['projectName'].setValue(1); - this.reviewErrorsFound = false; - } - } - - public isLastStep(): boolean { - if (!this.stepper) { - return false; - } - - return this.stepper.selectedIndex === this.stepper.steps.length - 1; - } - - public submitSubscription(): void { - const searchParams = this.getSearchParams(); - const searchOptions = this.filterOptions(this.processingOptionsList, this.jobTypeId).reduce( - (ps, p) => { - ps[p.apiName] = p.val; - return ps; - }, {} - ); - - const sub = { - job_specification: { - job_parameters: { - ...searchOptions - }, - job_type: this.jobTypeId, - name: this.projectName - }, - search_parameters: { - ...searchParams, - } - }; - - this.hyp3.submitSubscription$({ - subscription: sub, validate_only: this.validateOnly - }).subscribe(_ => { - this.notificationService.info(this.projectName + ' subscription submitted'); - this.dialogRef.close(); - }); - - } - - public onNewProductType(e): void { - this.productType = e.value; - } - - public onNewFlightDirection(e): void { - this.flightDirection = e.value; - } - - public onNewPolarization(e): void { - this.polarization = e.value; - } - - public onNewSubType(e): void { - this.subtype = e.value; - } - - public onNewJobType(e): void { - this.setJobType(e.value); - } - - private setJobType(jobTypeId: string): void { - this.jobTypeId = jobTypeId; - const dataset = models.hyp3JobTypes[jobTypeId].productTypes[0]; - this.productTypes = dataset.productTypes; - this.productType = this.productTypes[0]; - - this.polarizations = dataset.polarizations; - this.polarization = []; - - } - - public onBack(): void { - this.stepper.previous(); - } - - public showSearchAreaType(polygon: string): string { - return polygon.split('(')[0]; - } - - public onValidateOnlyToggle(val: boolean): void { - this.validateOnly = val; - } - - public filterOptions(optionList, jobTypeId) { - - const jobType = models.hyp3JobTypes[jobTypeId]; - const allOptions = jobType ? jobType.options : models.hyp3JobOptionsOrdered; - - const options = optionList.reduce((total, op) => { - total[op[0]] = op[1]; - return total; - }, {}); - - const filtered = allOptions - .filter(option => options[option.apiName]) - .map(option => { - return {name: option.name, val: options[option.apiName], apiName: option.apiName}; - }); - - return filtered; - } - - public onEstimateSubscription(): void { - const start = new Date(this.current.getTime()); - const dateRange = { - start: new Date(start.setDate(start.getDate() - 179)), - end: new Date(this.current.getTime()), - }; - - const flightDir = this.flightDirection === 'All' ? null : this.flightDirection; - const pol = this.polarization.join(','); - - const params = this.filterNullKeys({ - start: moment.utc(dateRange.start).format(), - end: moment.utc(dateRange.end).format(), - intersectsWith: this.polygon, - relativeOrbit: this.path, - frame: this.frame, - platform: this.subtype, - flightDirection: !!flightDir ? flightDir.toUpperCase() : null, - polarization: pol, - processinglevel: this.productType, - output: 'COUNT', - }); - - if (this.dateRange.start < this.current) { - const curr = { - ...params, - start: moment.utc(this.dateRange.start).format(), - }; - - this.subs.add( - this.asfApi.query(curr).subscribe( - estimate => this.currentProducts = estimate - ) - ); - } - - this.subs.add( - this.asfApi.query(params).subscribe( - estimate => this.subEstimate = Math.round(estimate / 6) - ) - ); - } - - private getSearchParams() { - const flightDir = this.flightDirection === 'All' ? null : this.flightDirection; - const pol = this.polarization.length > 0 ? this.polarization : null; - - const params = { - start: moment.utc(this.dateRange.start).format(), - end: moment.utc(this.dateRange.end).format(), - intersectsWith: this.polygon, - relativeOrbit: !!this.path ? [this.path] : null, - frame: !!this.frame ? [ this.frame ] : null, - platform: this.subtype, - flightDirection: !!flightDir ? flightDir.toUpperCase() : null, - polarization: pol, - processingLevel: this.productType, - }; - - return this.filterNullKeys(params); - } - - public isDevMode(): boolean { - return !this.env.isProd; - } - - private filterNullKeys(params) { - return Object.keys(params) - .filter((k) => params[k] != null) - .reduce((a, k) => ({ ...a, [k]: params[k] }), {}); - } - - public onCloseDialog() { - this.dialogRef.close(); - } - - ngOnDestroy(): void { - this.subs.unsubscribe(); - } -} diff --git a/src/app/components/header/create-subscription/create-subscription.module.ts b/src/app/components/header/create-subscription/create-subscription.module.ts deleted file mode 100644 index 395d90bc8..000000000 --- a/src/app/components/header/create-subscription/create-subscription.module.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; - -import { MatSelectModule } from '@angular/material/select'; -import { MatStepperModule } from '@angular/material/stepper'; -import { MatSlideToggleModule } from '@angular/material/slide-toggle'; -import { MatInputModule } from '@angular/material/input'; -import { MatSharedModule } from '@shared'; - -import { SearchFiltersModule } from '@components/sidebar/saved-searches/saved-search/search-filters'; -import { DateSelectorModule } from '@components/shared/selectors/date-selector'; -import { ProjectNameSelectorModule } from '@components/shared/selectors/project-name-selector'; -import { ProductTypeSelectorModule } from '@components/shared/selectors/product-type-selector'; -import { AoiOptionsModule } from '@components/shared/aoi-options'; - -import { CreateSubscriptionComponent } from './create-subscription.component'; -import { ProcessingOptionsModule } from '../processing-queue/processing-options'; -import { DateRangeModule } from '@components/shared/selectors/date-range/date-range.module'; - -import { MatDialogModule } from '@angular/material/dialog'; -import { PipesModule } from '@pipes'; -import { SubscriptionDateRangeComponent } from './subscription-date-range/subscription-date-range.component'; -import {DocsModalModule} from '@components/shared/docs-modal'; -import {Hyp3UrlModule} from '@components/shared/hyp3-url/hyp3-url.module'; -import { SharedModule } from '@shared'; - -@NgModule({ - declarations: [ - CreateSubscriptionComponent, - SubscriptionDateRangeComponent - ], - imports: [ - CommonModule, - FormsModule, - MatDialogModule, - MatSelectModule, - MatStepperModule, - MatSlideToggleModule, - MatInputModule, - MatSharedModule, - ProcessingOptionsModule, - SearchFiltersModule, - DateSelectorModule, - ProjectNameSelectorModule, - ProductTypeSelectorModule, - AoiOptionsModule, - DateRangeModule, - PipesModule, - DocsModalModule, - Hyp3UrlModule, - SharedModule - ], - exports: [ - CreateSubscriptionComponent - ] -}) -export class CreateSubscriptionModule { } diff --git a/src/app/components/header/create-subscription/index.ts b/src/app/components/header/create-subscription/index.ts deleted file mode 100644 index f3ed18e25..000000000 --- a/src/app/components/header/create-subscription/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './create-subscription.component'; -export * from './create-subscription.module'; diff --git a/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.html b/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.html deleted file mode 100644 index de2358034..000000000 --- a/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.html +++ /dev/null @@ -1,10 +0,0 @@ - - diff --git a/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.scss b/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.ts b/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.ts deleted file mode 100644 index c70acf465..000000000 --- a/src/app/components/header/create-subscription/subscription-date-range/subscription-date-range.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { NotificationService } from '@services'; - -import * as models from '@models'; -import * as moment from 'moment'; - -@Component({ - selector: 'app-subscription-date-range', - templateUrl: './subscription-date-range.component.html', - styleUrls: ['./subscription-date-range.component.scss'] -}) -export class SubscriptionDateRangeComponent implements OnInit { - public minDate: Date; - public maxDate: Date; - - @Input() public startDate: Date; - @Input() public endDate: Date; - - @Output() public newEnd = new EventEmitter(); - @Output() public newStart = new EventEmitter(); - - constructor(private notificationService: NotificationService) { } - - ngOnInit(): void { - this.minDate = models.sentinel_1.date.start; - this.maxDate = this.addDays(new Date(), 179); - } - - public onStartDateChange(date): void { - this.newStart.emit(date); - } - - public onEndDateChange(date): void { - this.newEnd.emit(date); - } - - public onStartDateError(): void { - const min = new Date(this.minDate); - const dayBefore = moment(min.setDate(min.getDate() - 1)).format('MMMM Do YYYY'); - - this.notificationService.error( - `subscription start date must be after ${dayBefore}`, - 'Invalid Start Date'); - } - - public onEndDateError(): void { - this.notificationService.error( - 'subscription end date must be within 6 months of current date', - 'Invalid End Date'); - } - - private addDays(date: Date, numDays: number): Date { - const d = new Date(date.valueOf()); - return new Date(d.setDate(d.getDate() + numDays)); - } -} diff --git a/src/app/components/header/header-buttons/header-buttons.component.html b/src/app/components/header/header-buttons/header-buttons.component.html index 6dec72a51..6c8ab35c3 100644 --- a/src/app/components/header/header-buttons/header-buttons.component.html +++ b/src/app/components/header/header-buttons/header-buttons.component.html @@ -22,11 +22,6 @@ (click)="onOpenProcessingQueue()" mat-menu-item>{{ 'ON_DEMAND_QUEUE' | translate }} - - - - @@ -364,5 +354,3 @@ - - diff --git a/src/app/components/header/header-buttons/header-buttons.component.ts b/src/app/components/header/header-buttons/header-buttons.component.ts index 1e3cc8019..8fa34bd03 100644 --- a/src/app/components/header/header-buttons/header-buttons.component.ts +++ b/src/app/components/header/header-buttons/header-buttons.component.ts @@ -262,10 +262,6 @@ export class HeaderButtonsComponent implements OnInit, OnDestroy { this.store$.dispatch(new uiStore.SetIsOnDemandQueueOpen(true)); } - public onOpenSubscriptions() { - this.store$.dispatch(new uiStore.OpenSidebar(SidebarType.ON_DEMAND_SUBSCRIPTIONS)); - } - public listWebsiteLinks() { const links = new Set(); diff --git a/src/app/components/header/header.module.ts b/src/app/components/header/header.module.ts index a3558ff52..a256fd182 100644 --- a/src/app/components/header/header.module.ts +++ b/src/app/components/header/header.module.ts @@ -44,7 +44,6 @@ import { AoiFilterComponent } from './dataset-header/aoi-filter/aoi-filter.compo import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MasterSceneSelectorModule } from '@components/shared/selectors/master-scene-selector'; -import { CreateSubscriptionModule } from './create-subscription/create-subscription.module'; import { CiSearchModule } from './info-bar/ci-search/ci-search.module'; import { SarviewsEventTypeSelectorModule } from '@components/shared/selectors/sarviews-event-type-selector'; import { Hyp3UrlModule } from '@components/shared/hyp3-url/hyp3-url.module'; @@ -92,7 +91,6 @@ import { LanguageSelectorModule } from "@components/shared/selectors/language-se ProjectNameSelectorModule, JobStatusSelectorModule, JobProductNameSelectorModule, - CreateSubscriptionModule, CiSearchModule, SarviewsEventSearchSelectorModule, SarviewsEventTypeSelectorModule, diff --git a/src/app/components/header/processing-queue/processing-queue.component.html b/src/app/components/header/processing-queue/processing-queue.component.html index 76120ae4b..55cddf480 100644 --- a/src/app/components/header/processing-queue/processing-queue.component.html +++ b/src/app/components/header/processing-queue/processing-queue.component.html @@ -199,7 +199,7 @@ [value]="progress"> - diff --git a/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.html b/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.html index 68749b45d..5d322e831 100644 --- a/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.html +++ b/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.html @@ -158,7 +158,7 @@ - + @@ -283,7 +283,7 @@ - --> diff --git a/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.component.ts b/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.component.ts index 03bd38bd8..752bbca9c 100644 --- a/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.component.ts +++ b/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.component.ts @@ -1,11 +1,8 @@ import { Component, OnInit, Input, ViewChild } from '@angular/core'; import { MatMenu } from '@angular/material/menu'; -import { MatDialog } from '@angular/material/dialog'; - import { Store } from '@ngrx/store'; import { AppState } from '@store'; import * as queueStore from '@store/queue'; -import * as hyp3Store from '@store/hyp3'; import * as userStore from '@store/user'; import * as models from '@models'; @@ -14,7 +11,6 @@ import { getMasterName, getScenes } from '@store/scenes'; import { getSearchType } from '@store/search'; import { CMRProduct, Hyp3ableByProductType, SearchType } from '@models'; import { withLatestFrom } from 'rxjs/operators'; -import { CreateSubscriptionComponent } from '../../header/create-subscription'; import { EnvironmentService } from '@services'; @Component({ @@ -26,7 +22,6 @@ export class OnDemandAddMenuComponent implements OnInit { @Input() hyp3ableProducts: models.Hyp3ableProductByJobType; @Input() isExpired = false; @Input() expiredJobs: models.Hyp3Job; - @Input() showSubscriptions = false; @ViewChild('addMenu', {static: true}) addMenu: MatMenu; @@ -44,7 +39,6 @@ export class OnDemandAddMenuComponent implements OnInit { constructor( private store$: Store, - private dialog: MatDialog, public env: EnvironmentService, ) { } @@ -128,23 +122,6 @@ export class OnDemandAddMenuComponent implements OnInit { this.store$.dispatch(new queueStore.AddJobs(jobs)); } - public onOpenCreateSubscription() { - const ref = this.dialog.open(CreateSubscriptionComponent, { - id: 'subscriptionQueueDialog', - maxWidth: '100vw', - maxHeight: '100vh', - data: { - referenceScene: this.referenceScene - } - }); - - ref.afterClosed().subscribe( - _ => { - this.store$.dispatch(new hyp3Store.LoadSubscriptions()); - } - ); - } - public onOpenHelp(infoUrl) { window.open(infoUrl); } diff --git a/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.module.ts b/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.module.ts index 221703f78..c09a01ba1 100644 --- a/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.module.ts +++ b/src/app/components/shared/on-demand-add-menu/on-demand-add-menu.module.ts @@ -9,7 +9,6 @@ import { MatSharedModule } from '@shared'; import { OnDemandAddMenuComponent } from './on-demand-add-menu.component'; import { ClosestPairComponent } from './closest-pair/closest-pair.component'; -import { CreateSubscriptionModule } from '@components/header/create-subscription'; import { SharedModule } from '@shared'; @NgModule({ @@ -20,7 +19,6 @@ import { SharedModule } from '@shared'; MatSharedModule, MatInputModule, FormsModule, - CreateSubscriptionModule, SharedModule ], exports: [ OnDemandAddMenuComponent ] diff --git a/src/app/components/sidebar/on-demand-subscriptions/index.ts b/src/app/components/sidebar/on-demand-subscriptions/index.ts deleted file mode 100644 index 3296d25ca..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './on-demand-subscriptions.module'; -export * from './on-demand-subscriptions.component'; diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.html b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.html deleted file mode 100644 index 7f7d8954c..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.html +++ /dev/null @@ -1,103 +0,0 @@ -
-
-
-
- keyboard_arrow_right - keyboard_arrow_down -
- -
-
- {{ subscription.name }} · - {{ subscription.jobType.name }} -
-
- {{ subscription.filters.start | shortDate }} to - - - {{ subscription.filters.end | shortDate }} - edit - -
-
-
- -
- - - - - - - -
- - {{ 'LOADING' | translate }}... - - - {{ subscription.enabled ? 'Disable' : 'Enable' }} - -
-
-
-
- -
-
- -
- - - {{ 'NEW_END_DATE' | translate }} - - - - - - - -
-
-
- - -
- -
- - -
-
-
diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.scss b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.scss deleted file mode 100644 index ee1e27501..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.scss +++ /dev/null @@ -1,52 +0,0 @@ -.subscription { - padding: 10px; -} - -.subscription-disabled { - color: grey !important; -} - -.edit-icon { - font-size: 18px; -} - -.subscription-header { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; -} - -.subscription-header-info { - display: flex; -} - -.subscription-preview { - display: flex; - flex-direction: column; -} - -.preview-top { - font-weight: bold; -} - -.subscription-detail { - margin: 10px 10px 10px 45px; -} - -.subscription-action { - background-color: unset !important; -} - -.expand-arrow { - margin-right: 20px; - margin-top: -2px; -} - -.expand-arrow-icon { - font-size: 38px; -} - -.t-label { - width: 70px; -} diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.ts b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.ts deleted file mode 100644 index 42cf4be1c..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/on-demand-subscription.component.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Component, OnInit, Input, Output, EventEmitter, ViewChild, OnDestroy } from '@angular/core'; -import { NgForm } from '@angular/forms'; - -import { MatDatepickerInputEvent } from '@angular/material/datepicker'; -import { Subject } from 'rxjs'; -import { tap, delay } from 'rxjs/operators'; -import { SubSink } from 'subsink'; -import * as moment from 'moment'; - -import * as models from '@models'; - -@Component({ - selector: 'app-on-demand-subscription', - templateUrl: './on-demand-subscription.component.html', - styleUrls: ['./on-demand-subscription.component.scss'] -}) -export class OnDemandSubscriptionComponent implements OnInit, OnDestroy { - @Input() subscription: models.OnDemandSubscription; - @Input() isExpanded: boolean; - @Input() isToggling: boolean; - - @Output() toggleEnabled = new EventEmitter(); - @Output() toggleExpand = new EventEmitter(); - @Output() loadSearch = new EventEmitter(); - @Output() viewProducts = new EventEmitter(); - @Output() newEnd = new EventEmitter(); - @Output() renew = new EventEmitter(); - - @ViewChild('endDateForm', { static: false }) public endDateForm: NgForm; - - public endDateErrors$ = new Subject(); - - public isEditingEndDate = false; - public newEndDate: Date; - public isEndError = false; - public isSubOutOfDate: boolean; - public expiresThisMonth: boolean; - - public subs = new SubSink(); - - constructor() { } - - ngOnInit(): void { - this.subs.add( - this.endDateErrors$.pipe( - tap(_ => { - this.isEndError = true; - this.subEndControl.reset(); - this.subEndControl.setErrors({'incorrect': true}); - }), - delay(820), - ).subscribe(_ => { - this.isEndError = false; - this.subEndControl.setErrors(null); - }) - ); - - const today = new Date(); - const subEnd = new Date(this.subscription.filters.end); - this.isSubOutOfDate = today > subEnd; - const daysLeft = Math.max(moment(subEnd).diff(moment(today), 'days'), 0); - this.expiresThisMonth = daysLeft < 29; - } - - public getMinDate(): Date { - return this.subscription.filters.start; - } - - public getMaxDate(): Date { - const current = new Date(); - current.setDate(current.getDate() + 179); - - return current; - } - - public onEndDateChange(e: MatDatepickerInputEvent): void { - let date: null | Date; - - if (!this.subEndControl.valid || !e.value) { - date = null; - this.endDateErrors$.next(); - } else { - const momentDate = e.value.set({h: 0}); - date = momentDate.toDate(); - } - - this.newEndDate = date; - } - - public onEditDate(): void { - if (this.isExpanded === false) { - this.toggleExpand.emit(this.subscription.id); - } - - this.newEndDate = this.subscription.filters.end; - this.isEditingEndDate = true; - } - - public onDoneEditing(): void { - this.isEditingEndDate = false; - - if (this.newEndDate) { - this.newEnd.emit(this.newEndDate); - } - } - - public onToggleEnabled(): void { - this.toggleEnabled.emit(this.subscription); - } - - public onToggleExpand(): void { - this.toggleExpand.emit(this.subscription.id); - } - - public loadOnDemandSearch(): void { - this.viewProducts.emit(this.subscription.name); - } - - public onRenewSubscription(): void { - this.renew.emit(this.subscription); - } - - public onLoadSearch(): void { - this.loadSearch.emit(this.subscription); - } - - private get subEndControl() { - return this.endDateForm.form - .controls['endInput']; - } - - ngOnDestroy() { - this.subs.unsubscribe(); - } -} diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.html b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.html deleted file mode 100644 index a519fde1f..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.html +++ /dev/null @@ -1,54 +0,0 @@ -
-
-
{{ 'FILTERS' | translate }}
- - -
- -
-
-
- {{ 'DATASET' | translate }}: {{ filters.platform }} -
- -
- Search Area: {{ showSearchAreaType(filters.intersectsWith) }} - - -
- -
- {{ 'PATH' | translate }}: {{ filters.relativeOrbit }} -
- -
- {{ 'FRAME' | translate}}: {{ filters.frame }} -
- -
- {{ 'PRODUCT_TYPE' | translate }}: {{ filters.processingLevel }} -
- -
- {{ 'POLARIZATIONS' | translate }}: {{ filters.polarization }} -
- -
- {{ 'FLIGHT_DIRECTION' | translate }}: {{ filters.flightDirection }} -
- -
- {{ 'BEAM_MODE' | translate }}: {{ filters.beamMode }} -
-
-
diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.scss b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.scss deleted file mode 100644 index 480e13dcc..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -.header { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; -} diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.ts b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.ts deleted file mode 100644 index d650dd6c4..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-filters/subscription-filters.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; - -@Component({ - selector: 'app-subscription-filters', - templateUrl: './subscription-filters.component.html', - styleUrls: ['./subscription-filters.component.scss'] -}) -export class SubscriptionFiltersComponent implements OnInit { - @Input() filters; - @Output() loadSearch = new EventEmitter(); - - constructor() { } - - ngOnInit(): void { - } - - public showSearchAreaType(polygon: string): string { - return polygon.split('(')[0]; - } - - public onLoadSearch() { - this.loadSearch.emit(); - } -} diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.html b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.html deleted file mode 100644 index 12be5d553..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
{{ 'PROCESSING_OPTIONS' | translate }}
-
-
-
- {{ param.name }}: {{ param.val }} -
-
-
diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.scss b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.ts b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.ts deleted file mode 100644 index 545ea9a4f..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscription/subscription-job-options/subscription-job-options.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, OnInit, Input } from '@angular/core'; - -@Component({ - selector: 'app-subscription-job-options', - templateUrl: './subscription-job-options.component.html', - styleUrls: ['./subscription-job-options.component.scss'] -}) -export class SubscriptionJobOptionsComponent implements OnInit { - @Input() options = {}; - - constructor() { } - - ngOnInit(): void { - } - - public listFrom(options): any[] { - return Object.entries(options).map( - ([name, val]) => ({ name, val }) - ).filter(param => !!param.val); - } -} diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.html b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.html deleted file mode 100644 index 59901e1e8..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.html +++ /dev/null @@ -1,60 +0,0 @@ -
-
-
- {{ 'ON_DEMAND_SUBSCRIPTIONS' | translate }} -
-
- -
-
- -
- -
- -
- -
-
-
- - -
-
-
- - -
-

{{ 'YOU_HAVE_NO_SUBSCRIPTIONS' | translate }}

-
-
-
- -
-
- -
-
-
diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.scss b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.scss deleted file mode 100644 index 9aa2edc96..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.scss +++ /dev/null @@ -1,42 +0,0 @@ -@import "asf-theme"; - - -.subscriptions-content-wrapper { - display: flex; - flex-direction: row; - width: 100%; - max-height: calc(100vh - 200px); -} - -.no-saved-filters-presets h2 { - color: #c9cac4; - padding-left: 15px; -} - -.add-button-container { - display: flex; - justify-content: center; - width: 100px; - float: right; - margin-top: -32px; -} - -.fab-button { - border-radius: 40px !important; -} - -.subscription-disabled { - color: grey !important; -} - -.header-overrides { - flex-direction: column !important; - align-items: flex-start !important; - height: fit-content !important; - padding: 15px !important; -} - -.api-url { - display: flex; - font-size: 12px; -} diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.ts b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.ts deleted file mode 100644 index 5e36bc901..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.component.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { MatDialog } from '@angular/material/dialog'; -import { SubSink } from 'subsink'; -import * as moment from 'moment'; - -import { Store } from '@ngrx/store'; -import { AppState } from '@store'; -import * as filtersStore from '@store/filters'; -import * as uiStore from '@store/ui'; -import * as hyp3Store from '@store/hyp3'; -import * as searchStore from '@store/search'; - -import { CreateSubscriptionComponent } from '@components/header/create-subscription'; -import { ScreenSizeService, Hyp3Service, WktService, MapService } from '@services'; -import * as models from '@models'; - -@Component({ - selector: 'app-on-demand-subscriptions', - templateUrl: './on-demand-subscriptions.component.html', - styleUrls: [ - './on-demand-subscriptions.component.scss', - '../save-user-filters/save-user-filters.component.scss', - '../saved-searches/saved-searches.component.scss', - ] -}) -export class OnDemandSubscriptionsComponent implements OnInit, OnDestroy { - public subscriptions = []; - public selectedSubId: string = null; - - public breakpoint: models.Breakpoints; - public breakpoints = models.Breakpoints; - public loadingSubs = new Set(); - - public searchType$ = this.store$.select(searchStore.getSearchType); - public searchType: models.SearchType; - public SearchType = models.SearchType; - - private subs = new SubSink(); - - constructor( - private store$: Store, - private dialog: MatDialog, - private screenSize: ScreenSizeService, - private wktService: WktService, - private mapService: MapService, - private hyp3: Hyp3Service, - ) { } - - ngOnInit(): void { - this.subs.add( - this.screenSize.breakpoint$.subscribe( - breakpoint => this.breakpoint = breakpoint - ) - ); - - this.subs.add( - this.store$.select(hyp3Store.getOnDemandSubscriptions).subscribe( - subs => this.subscriptions = subs - ) - ); - - this.subs.add( - this.searchType$.subscribe(searchType => { - this.searchType = searchType; - }) - ); - } - - public onCreateSubscription() { - const ref = this.dialog.open(CreateSubscriptionComponent, { - id: 'subscriptionQueueDialog', - maxWidth: '100vw', - maxHeight: '100vh', - }); - - ref.afterClosed().subscribe( - _ => { - this.store$.dispatch(new hyp3Store.LoadSubscriptions()); - } - ); - } - - public onRenewSubscription(sub: models.OnDemandSubscription): void { - let end; - - if (this.expiresThisMonth(sub)) { - const subEnd = new Date(sub.filters.end); - const newEnd = new Date(subEnd.setDate(subEnd.getDate() + 30)); - end = moment.utc(newEnd).format(); - } else { - const today = new Date(); - const endDate = new Date(today.setDate(today.getDate() + 30)); - end = moment.utc(endDate).format(); - } - - this.hyp3.editSubscription(sub.id, {end}).subscribe( - _ => { - this.store$.dispatch(new hyp3Store.LoadSubscriptions()); - } - ); - } - - private expiresThisMonth(sub: models.OnDemandSubscription): boolean { - const today = new Date(); - const subEnd = new Date(sub.filters.end); - const daysLeft = Math.max(moment(subEnd).diff(moment(today), 'days'), 0); - - return daysLeft < 29; - } - - public onLoadSearch(sub: models.OnDemandSubscription): void { - this.store$.dispatch(new searchStore.SetSearchType(models.SearchType.DATASET)); - this.store$.dispatch(new searchStore.ClearSearch()); - - const filters = sub.filters; - const features = this.wktService.wktToFeature( - filters.intersectsWith, - this.mapService.epsg() - ); - - this.mapService.setDrawFeature(features); - - if (filters.processingLevel !== undefined) { - models.sentinel_1.productTypes.forEach(productType => { - if (productType.apiValue === filters.processingLevel) { - this.store$.dispatch(new filtersStore.SetProductTypes([productType])); - } - }); - } - - if (filters.flightDirection !== undefined) { - this.store$.dispatch(new filtersStore.SetFlightDirections([filters.flightDirection])); - } - - if (filters.polarization !== undefined) { - this.store$.dispatch(new filtersStore.SetPolarizations(filters.polarization)); - } - - if (filters.beamMode !== undefined) { - this.store$.dispatch(new filtersStore.SetBeamModes(filters.beamMode)); - } - - if (filters.relativeOrbit !== undefined) { - this.store$.dispatch(new filtersStore.SetPathStart(filters.relativeOrbit)); - this.store$.dispatch(new filtersStore.SetPathEnd(filters.relativeOrbit)); - } - - if (filters.frame !== undefined) { - this.store$.dispatch(new filtersStore.SetFrameStart(filters.frame)); - this.store$.dispatch(new filtersStore.SetFrameEnd(filters.frame)); - } - - this.store$.dispatch(new uiStore.CloseSidebar()); - this.store$.dispatch(new searchStore.MakeSearch()); - } - - public onClose() { - this.store$.dispatch(new uiStore.CloseSidebar()); - } - - public onNewEndDate(subId, date: Date): void { - const end = moment.utc(date).format(); - - this.hyp3.editSubscription(subId, {end}).subscribe( - _ => { - this.store$.dispatch(new hyp3Store.LoadSubscriptions()); - } - ); - } - - public onToggleSelected(subId: string): void { - if (this.selectedSubId === subId) { - this.selectedSubId = null; - } else { - this.selectedSubId = subId; - } - } - - public onToggleEnabled(sub: models.OnDemandSubscription): void { - this.loadingSubs.add(sub.id); - - this.hyp3.editSubscription(sub.id, {enabled: !sub.enabled}).subscribe( - _ => { - this.store$.dispatch(new hyp3Store.LoadSubscriptions()); - this.loadingSubs.delete(sub.id); - } - ); - } - - public onLoadProductsFor(subName: string): void { - this.store$.dispatch(new uiStore.CloseSidebar()); - this.store$.dispatch(new searchStore.SetSearchType(models.SearchType.CUSTOM_PRODUCTS)); - this.store$.dispatch(new filtersStore.SetProjectName(subName)); - } - - ngOnDestroy() { - this.subs.unsubscribe(); - } -} - diff --git a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.module.ts b/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.module.ts deleted file mode 100644 index fb7a53673..000000000 --- a/src/app/components/sidebar/on-demand-subscriptions/on-demand-subscriptions.module.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; - -import { CreateSubscriptionModule } from '@components/header/create-subscription'; -import { CopyToClipboardModule } from '@components/shared/copy-to-clipboard'; -import { MatDatepickerModule } from '@angular/material/datepicker'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MAT_MOMENT_DATE_ADAPTER_OPTIONS, MatMomentDateModule } from '@angular/material-moment-adapter'; - -import { PipesModule } from '@pipes'; -import { MatSharedModule } from '@shared'; - -import { OnDemandSubscriptionsComponent } from './on-demand-subscriptions.component'; -import { OnDemandSubscriptionComponent } from './on-demand-subscription/on-demand-subscription.component'; -import { SubscriptionFiltersComponent } from './on-demand-subscription/subscription-filters/subscription-filters.component'; -import { SubscriptionJobOptionsComponent } from './on-demand-subscription/subscription-job-options/subscription-job-options.component'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; -import {Hyp3UrlModule} from '@components/shared/hyp3-url/hyp3-url.module'; -import { SharedModule } from '@shared'; - - -@NgModule({ - declarations: [ - OnDemandSubscriptionsComponent, - OnDemandSubscriptionComponent, - SubscriptionFiltersComponent, - SubscriptionJobOptionsComponent, - ], - imports: [ - CommonModule, - FormsModule, - PipesModule, - MatSharedModule, - MatDatepickerModule, - MatFormFieldModule, - MatInputModule, - MatSharedModule, - MatMomentDateModule, - CreateSubscriptionModule, - MatSlideToggleModule, - CopyToClipboardModule, - Hyp3UrlModule, - SharedModule - ], - exports: [ - OnDemandSubscriptionsComponent, - OnDemandSubscriptionComponent - ], - providers: [ - { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } } - ], -}) -export class OnDemandSubscriptionsModule { } diff --git a/src/app/components/sidebar/sidebar.component.html b/src/app/components/sidebar/sidebar.component.html index 45e8a4a68..260a3f243 100644 --- a/src/app/components/sidebar/sidebar.component.html +++ b/src/app/components/sidebar/sidebar.component.html @@ -6,7 +6,4 @@ - - diff --git a/src/app/components/sidebar/sidebar.module.ts b/src/app/components/sidebar/sidebar.module.ts index d6f14c0f0..a72e7e396 100644 --- a/src/app/components/sidebar/sidebar.module.ts +++ b/src/app/components/sidebar/sidebar.module.ts @@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common'; import { SavedSearchesModule } from './saved-searches'; import { SaveUserFiltersModule } from './save-user-filters'; -import { OnDemandSubscriptionsModule } from './on-demand-subscriptions'; import { SidebarComponent } from './sidebar.component'; @@ -15,7 +14,6 @@ import { SidebarComponent } from './sidebar.component'; CommonModule, SavedSearchesModule, SaveUserFiltersModule, - OnDemandSubscriptionsModule, ], exports: [ SidebarComponent diff --git a/src/app/models/sidebar.model.ts b/src/app/models/sidebar.model.ts index b803bd681..14f0307ea 100644 --- a/src/app/models/sidebar.model.ts +++ b/src/app/models/sidebar.model.ts @@ -2,6 +2,5 @@ export enum SidebarType { SAVED_SEARCHES = 'Saved Searches', SEARCH_HISTORY = 'Search History', USER_FILTERS = 'User Filters', - ON_DEMAND_SUBSCRIPTIONS = 'On Demand Subscriptions', NONE = 'None', } From cba10fc468a4068cc19079e336b298b1a3366045 Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 28 Sep 2023 11:50:42 -0800 Subject: [PATCH 14/32] cleans up opera_s1 collections list to use latest only, subproducts from 1.0 products working --- src/app/models/datasets/opera_s1.ts | 2 +- .../services/dataset-for-product.service.ts | 3 ++ src/app/services/product.service.ts | 51 ++++++++----------- src/app/store/filters/filters.reducer.ts | 5 ++ src/app/store/scenes/scenes.reducer.ts | 30 +++-------- 5 files changed, 36 insertions(+), 55 deletions(-) diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index d1a5fbc93..06b35d93c 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -16,7 +16,7 @@ export const opera_s1 = { ], apiValue: { collections: - "C1258354200-ASF,C1258354201-ASF,C1258121071-ASFDEV,C1258121066-ASFDEV,C1259974840-ASF,C1259976861-ASF,C1259981910-ASF,C1259982010-ASF,C1259975087-ASFDEV,C1259976862-ASFDEV,C1259983643-ASFDEV,C1259983645-ASFDEV" }, + "C1259974840-ASF,C1259976861-ASF,C1259981910-ASF,C1259982010-ASF,C1259975087-ASFDEV,C1259976862-ASFDEV,C1259983643-ASFDEV,C1259983645-ASFDEV" }, date: { start: new Date('2014/06/15 03:44:43 UTC') }, infoUrl: 'https://sentinels.copernicus.eu/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath', citationUrl: 'https://asf.alaska.edu/data-sets/sar-data-sets/sentinel-1/sentinel-1-how-to-cite/', diff --git a/src/app/services/dataset-for-product.service.ts b/src/app/services/dataset-for-product.service.ts index d13333471..e85e0c8b8 100644 --- a/src/app/services/dataset-for-product.service.ts +++ b/src/app/services/dataset-for-product.service.ts @@ -17,6 +17,9 @@ export class DatasetForProductService { if(scene.metadata.productType === 'BURST') { return models.sentinel_1_bursts; } + if(scene.id.startsWith('OPERA')) { + return models.opera_s1; + } const exact = (datasetID, sceneDataset) => ( datasetID === sceneDataset diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts index b84c12b6f..fbbaf8ff8 100644 --- a/src/app/services/product.service.ts +++ b/src/app/services/product.service.ts @@ -105,6 +105,7 @@ export class ProductService { return [this.burstXMLFromScene(product)] } if (!!product.metadata.opera) { + product.productTypeDisplay = this.operaProductTypeDisplay(product.downloadUrl) return this.operaSubproductsFromScene(product) } return [] @@ -131,43 +132,22 @@ export class ProductService { private operaSubproductsFromScene(product: models.CMRProduct) { let products = [] - // incidence_angle - // local_incidence_angle - // mask - // number_of_looks - // rtc_anf_gamma0_to_beta0 - // rtc_anf_gamma0_to_sigma0 - - // product_types = [ - // 'incidence_angle', - // 'local_incidence_angle', - // 'mask', - // 'number_of_looks', - // 'rtc_anf_gamma0_to_beta0', - // 'rtc_anf_gamma0_to_sigma0', - // ] - const display = { - 'incidence_angle': 'Incidence Angle (TIF)', - 'local_incidence_angle': 'Local Incidence Angle (TIF)', - 'mask': 'Mask (TIF)', - 'number_of_looks': 'Number of Looks (TIF)', - 'rtc_anf_gamma0_to_beta0': 'RTC Anf Gamma0 to Beta0 (TIF)', - 'rtc_anf_gamma0_to_sigma0': 'RTC Anf Gamma0 to Sigma0 (TIF)', - } - for (const p of product.metadata.opera.additionalUrls.slice(1)) { - const file_suffix = p.split('v0.')[1] - const fileID = 'v0.' + file_suffix - const file_name = file_suffix.slice(2, file_suffix.length - 4) - console.log(file_name) + + let productType = models.opera_s1.productTypes.find(productType => productType.apiValue === product.metadata.productType) + let file_suffix = product.downloadUrl.split(/(_v)\w+.*_/).slice(-1)[0] + let file_name = file_suffix.endsWith('h5') ? 'H5' : file_suffix.split('.').slice(0, -1).join('.') + product.productTypeDisplay = productType.displayName + `- (${file_name})` - const extension = p.split('.').slice(-1)[0] - const fileDisplay = file_name.replace('_', ' ').toLowerCase() + ` (${extension.toUpperCase()})` + for (const p of product.metadata.opera.additionalUrls.slice(1)) { + file_suffix = p.split(/(_v)\w+.*_/).slice(-1)[0] + file_name = file_suffix.endsWith('h5') ? 'H5' : file_suffix.split('.').slice(0, -1).join('.') + const fileID = p.split('/').slice(-1)[0] let subproduct = { ...product, downloadUrl: p, - productTypeDisplay: display[file_name] || fileDisplay, + productTypeDisplay: productType.displayName + `- (${file_name})`, file: fileID, id: product.id + '-' + file_name, bytes: 0, @@ -185,4 +165,13 @@ export class ProductService { return products } + + operaProductTypeDisplay(p: string) { + const file_suffix = p.split(/(_v)\w+.*_/).slice(-1)[0] + const file_name = file_suffix.slice(0, file_suffix.length - 4) + const extension = p.split('.').slice(-1)[0] + const fileDisplay = file_name.replace('_', ' ').toLowerCase() + ` (${extension.toUpperCase()})` + + return fileDisplay + } } diff --git a/src/app/store/filters/filters.reducer.ts b/src/app/store/filters/filters.reducer.ts index 87ecfe118..74f01ff9c 100644 --- a/src/app/store/filters/filters.reducer.ts +++ b/src/app/store/filters/filters.reducer.ts @@ -355,6 +355,11 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte fullBurstIDs: [metadata.burst.fullBurstID], } } + if(action.payload.dataset.id === models.opera_s1.id) { + filters = { + groupId: action.payload.product.groupId + } + } return { ...state, ...filters diff --git a/src/app/store/scenes/scenes.reducer.ts b/src/app/store/scenes/scenes.reducer.ts index e09b3fff0..e7ea8daa0 100644 --- a/src/app/store/scenes/scenes.reducer.ts +++ b/src/app/store/scenes/scenes.reducer.ts @@ -2,7 +2,7 @@ import { createFeatureSelector, createSelector } from '@ngrx/store'; import { ScenesActionType, ScenesActions } from './scenes.action'; -import { CMRProduct, UnzippedFolder, ColumnSortDirection, SarviewsEvent, SarviewsProduct, CMRSubProduct } from '@models'; +import { CMRProduct, UnzippedFolder, ColumnSortDirection, SarviewsEvent, SarviewsProduct, CMRSubProduct, opera_s1 } from '@models'; import { PinnedProduct } from '@services/browse-map.service'; import { createSelectorFactory, defaultMemoize } from '@ngrx/store'; @@ -71,9 +71,10 @@ export function scenesReducer(state = initState, action: ScenesActions): ScenesS let searchResults = action.payload.products.map(p => p.metadata.productType === 'BURST' ? ({...p, productTypeDisplay: 'Single Look Complex (BURST)'}) as CMRProduct : p) + + const ungrouped_product_types = opera_s1.productTypes.map(m => m.apiValue) for (let product of searchResults) { - if(product.metadata.subproducts.length > 0) { - // const p = burstXMLFromScene(product) + if(product.metadata.subproducts.length > 0 || ungrouped_product_types.includes(product.metadata.productType)) { for (let subproduct of product.metadata.subproducts) { subproducts.push(subproduct) } @@ -88,30 +89,13 @@ export function scenesReducer(state = initState, action: ScenesActions): ScenesS return total; }, {}); - - // const productIDs = searchResults.reduce((total, product) => { - // total[product.metadata.productType] = product; - - // return total; - // }, {}); let productGroups: {[id: string]: string[]} = {} let scenes: {[id: string]: string[]} = {} - // if (Object.keys(productIDs).length <= 2 && Object.keys(productIDs)[0].toUpperCase() === 'BURST') { - // productGroups = searchResults.reduce((total, product) => { - // const scene = total[product.name] || []; - - // total[product.name] = [...scene, product.id]; - // return total; - // }, {}) - // } else { productGroups = searchResults.reduce((total, product) => { - // if isSubProduct(product) { - - // } let groupCriteria = product.groupId; - if (product.metadata.subproducts.length > 0) { + if (product.metadata.subproducts.length > 0 || ungrouped_product_types.includes(product.metadata.productType)) { groupCriteria = product.id; } else if(isSubProduct(product)) { groupCriteria = (product as CMRSubProduct).parentID; @@ -121,7 +105,6 @@ export function scenesReducer(state = initState, action: ScenesActions): ScenesS total[groupCriteria] = [...scene, product.id]; return total; }, {}); - // } for (const [groupId, productNames] of Object.entries(productGroups)) { @@ -491,9 +474,10 @@ const productsForScene = (selected, state) => { let products = [] + const ungrouped_product_types = opera_s1.productTypes.map(m => m.apiValue) if (Object.keys(productTypes).length <= 2 && Object.keys(productTypes)[0] === 'BURST') { products = state.scenes[selected.name] || []; - } else if(selected.metadata.subproducts.length > 0) { + } else if(selected.metadata.subproducts.length > 0 || ungrouped_product_types.includes(selected.metadata.productType)) { products = state.scenes[selected.id] || []; } else { From 4c8c12335008abfcb0dcc73b2ed056f4b58fc8d9 Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 28 Sep 2023 16:30:25 -0800 Subject: [PATCH 15/32] subproducts cleaned up --- .../header/queue/queue.component.html | 4 +++- .../components/header/queue/queue.component.ts | 2 +- .../scene-file/scene-file.component.html | 4 +++- src/app/models/cmr-product.model.ts | 5 +---- src/app/services/product.service.ts | 18 +++++++++--------- src/app/store/scenes/scenes.reducer.ts | 6 +++--- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/app/components/header/queue/queue.component.html b/src/app/components/header/queue/queue.component.html index 3eb3c406c..fbadb4e73 100644 --- a/src/app/components/header/queue/queue.component.html +++ b/src/app/components/header/queue/queue.component.html @@ -89,7 +89,9 @@

*ngIf="!product.isUnzippedFile && !product.metadata.job && product.groupId !== 'SARViews'" style="margin-right: 15px;" prompt="{{ 'COPY_FILE_ID' | translate}}" - [value]="product.metadata.productType === 'BURST_XML' ? product.id?.split('-XML')[0] : product.id" + [value]="product.metadata.productType === 'BURST_XML' ? product.id?.split('-XML')[0] : ( + product.metadata.parentID || product.id + )" > diff --git a/src/app/components/header/queue/queue.component.ts b/src/app/components/header/queue/queue.component.ts index b24a39369..3725d5a2d 100644 --- a/src/app/components/header/queue/queue.component.ts +++ b/src/app/components/header/queue/queue.component.ts @@ -169,7 +169,7 @@ export class QueueComponent implements OnInit, OnDestroy { if (product.metadata.productType === 'BURST_XML') { return product.id?.split('-XML')[0] } - return product.id; + return product.metadata.parentID || product.id; }) .join('\n'); this.clipboardService.copyFromContent(productListStr); diff --git a/src/app/components/results-menu/scene-files/scene-file/scene-file.component.html b/src/app/components/results-menu/scene-files/scene-file/scene-file.component.html index c01a8822a..ed4d45d58 100644 --- a/src/app/components/results-menu/scene-files/scene-file/scene-file.component.html +++ b/src/app/components/results-menu/scene-files/scene-file/scene-file.component.html @@ -60,7 +60,9 @@ diff --git a/src/app/models/cmr-product.model.ts b/src/app/models/cmr-product.model.ts index 8ebe5b104..46bb2aad7 100644 --- a/src/app/models/cmr-product.model.ts +++ b/src/app/models/cmr-product.model.ts @@ -68,6 +68,7 @@ export interface CMRProductMetadata { // BURST XML, OPERA-S1 subproducts: any[]; + parentID: string; } export interface SLCBurstMetadata { @@ -100,7 +101,3 @@ export enum ColumnSortDirection { DECREASING = 'DECREASING', NONE = 'NONE', } - -export interface CMRSubProduct extends CMRProduct{ - parentID: string; -} \ No newline at end of file diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts index fbbaf8ff8..f090e4465 100644 --- a/src/app/services/product.service.ts +++ b/src/app/services/product.service.ts @@ -91,7 +91,8 @@ export class ProductService { burst: g.s1b ? g.s1b : null, opera: g.s1o ? g.s1o : null, pgeVersion: g.pge !== null ? parseFloat(g.pge) : null, - subproducts: [] + subproducts: [], + parentID: null }) private isNumber = n => !isNaN(n) && isFinite(n); @@ -100,7 +101,7 @@ export class ProductService { return moment.utc(dateString); } - private getSubproducts(product: models.CMRProduct): models.CMRSubProduct[] { + private getSubproducts(product: models.CMRProduct): models.CMRProduct[] { if (product.metadata.productType === 'BURST') { return [this.burstXMLFromScene(product)] } @@ -125,7 +126,7 @@ export class ProductService { productType: product.metadata.productType + '_XML' }, parentID: product.id - } as models.CMRSubProduct; + } as models.CMRProduct; return p; } @@ -133,10 +134,9 @@ export class ProductService { private operaSubproductsFromScene(product: models.CMRProduct) { let products = [] - let productType = models.opera_s1.productTypes.find(productType => productType.apiValue === product.metadata.productType) let file_suffix = product.downloadUrl.split(/(_v)\w+.*_/).slice(-1)[0] let file_name = file_suffix.endsWith('h5') ? 'H5' : file_suffix.split('.').slice(0, -1).join('.') - product.productTypeDisplay = productType.displayName + `- (${file_name})` + product.productTypeDisplay = file_name for (const p of product.metadata.opera.additionalUrls.slice(1)) { file_suffix = p.split(/(_v)\w+.*_/).slice(-1)[0] @@ -147,16 +147,16 @@ export class ProductService { let subproduct = { ...product, downloadUrl: p, - productTypeDisplay: productType.displayName + `- (${file_name})`, + productTypeDisplay: file_name, file: fileID, id: product.id + '-' + file_name, bytes: 0, metadata: { ...product.metadata, - productType: product.metadata.productType + '_TIF' + productType: product.metadata.productType + '_TIF', + parentID: product.id }, - parentID: product.id - } as models.CMRSubProduct; + } as models.CMRProduct; if(subproduct.productTypeDisplay !== 'Opera Subproduct') { products.push(subproduct) diff --git a/src/app/store/scenes/scenes.reducer.ts b/src/app/store/scenes/scenes.reducer.ts index e7ea8daa0..52da7d0f8 100644 --- a/src/app/store/scenes/scenes.reducer.ts +++ b/src/app/store/scenes/scenes.reducer.ts @@ -2,7 +2,7 @@ import { createFeatureSelector, createSelector } from '@ngrx/store'; import { ScenesActionType, ScenesActions } from './scenes.action'; -import { CMRProduct, UnzippedFolder, ColumnSortDirection, SarviewsEvent, SarviewsProduct, CMRSubProduct, opera_s1 } from '@models'; +import { CMRProduct, UnzippedFolder, ColumnSortDirection, SarviewsEvent, SarviewsProduct, opera_s1 } from '@models'; import { PinnedProduct } from '@services/browse-map.service'; import { createSelectorFactory, defaultMemoize } from '@ngrx/store'; @@ -98,7 +98,7 @@ export function scenesReducer(state = initState, action: ScenesActions): ScenesS if (product.metadata.subproducts.length > 0 || ungrouped_product_types.includes(product.metadata.productType)) { groupCriteria = product.id; } else if(isSubProduct(product)) { - groupCriteria = (product as CMRSubProduct).parentID; + groupCriteria = product.metadata.parentID; } const scene = total[groupCriteria] || []; @@ -738,5 +738,5 @@ function eqSet(aSet, bSet): boolean { } function isSubProduct(product): boolean { - return 'parentID' in product; + return !!product.metadata.parentID; } \ No newline at end of file From a96f183470762f9db89817e44396eb221c989d30 Mon Sep 17 00:00:00 2001 From: kim Date: Mon, 2 Oct 2023 15:31:44 -0800 Subject: [PATCH 16/32] subproducts appear for all product types, fixes results not appearing when switching product types --- .../baseline-chart.component.ts | 2 +- src/app/components/map/map.component.ts | 2 +- .../baseline-results-menu.component.ts | 2 +- .../desktop-results-menu.component.ts | 2 +- .../browse-list/browse-list.component.ts | 2 +- .../scenes-list-header.component.ts | 14 ++++++----- .../scenes-list/scenes-list.component.html | 4 +-- .../scenes-list/scenes-list.component.ts | 25 +++++++++++++------ .../sbas-chart/sbas-chart.component.ts | 2 +- .../hyp3-job-status-badge.component.ts | 2 +- .../max-results-selector.component.ts | 2 +- .../job-product-name-selector.component.ts | 2 +- src/app/services/keyboard.service.ts | 2 +- src/app/services/product.service.ts | 12 +++++---- src/app/services/scenes.service.ts | 9 +++---- src/app/store/scenes/scenes.effect.ts | 5 ++-- src/app/store/scenes/scenes.reducer.ts | 16 +++++++----- 17 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/app/components/baseline-chart/baseline-chart.component.ts b/src/app/components/baseline-chart/baseline-chart.component.ts index 3ca4236fe..59748098d 100644 --- a/src/app/components/baseline-chart/baseline-chart.component.ts +++ b/src/app/components/baseline-chart/baseline-chart.component.ts @@ -79,7 +79,7 @@ export class BaselineChartComponent implements OnInit, OnDestroy { ngOnInit(): void { this.createSVG(); - const products$ = this.scenesService.scenes$().pipe( + const products$ = this.scenesService.scenes$.pipe( tap(products => products.map( product => this.criticalBaseline = criticalBaselineFor(product) )), diff --git a/src/app/components/map/map.component.ts b/src/app/components/map/map.component.ts index 118c75dc8..7e272732b 100644 --- a/src/app/components/map/map.component.ts +++ b/src/app/components/map/map.component.ts @@ -430,7 +430,7 @@ export class MapComponent implements OnInit, OnDestroy { } private scenesToFeatures(projection: string): Observable[]> { - return this.scenesService.scenes$().pipe( + return this.scenesService.scenes$.pipe( map(scenes => scenes.filter(scene => scene.id !== this.selectedScene?.id)), map(scenes => this.scenesToFeature(scenes, projection))); } diff --git a/src/app/components/results-menu/baseline-results-menu/baseline-results-menu.component.ts b/src/app/components/results-menu/baseline-results-menu/baseline-results-menu.component.ts index 6605455d2..0b293d402 100644 --- a/src/app/components/results-menu/baseline-results-menu/baseline-results-menu.component.ts +++ b/src/app/components/results-menu/baseline-results-menu/baseline-results-menu.component.ts @@ -30,7 +30,7 @@ enum CardViews { export class BaselineResultsMenuComponent implements OnInit, OnDestroy { @Input() resize$: Observable; - public numBaselineScenes$ = this.scenesService.scenes$().pipe( + public numBaselineScenes$ = this.scenesService.scenes$.pipe( map(scenes => scenes.length), ); diff --git a/src/app/components/results-menu/desktop-results-menu/desktop-results-menu.component.ts b/src/app/components/results-menu/desktop-results-menu/desktop-results-menu.component.ts index 968be2fa0..e70176f98 100644 --- a/src/app/components/results-menu/desktop-results-menu/desktop-results-menu.component.ts +++ b/src/app/components/results-menu/desktop-results-menu/desktop-results-menu.component.ts @@ -40,7 +40,7 @@ export class DesktopResultsMenuComponent implements OnInit, OnDestroy { ) ); this.subs.add( - this.scenesService.scenes$().subscribe( + this.scenesService.scenes$.subscribe( scenes => this.scenesLength = scenes.length ) ); diff --git a/src/app/components/results-menu/scene-detail/image-dialog/browse-list/browse-list.component.ts b/src/app/components/results-menu/scene-detail/image-dialog/browse-list/browse-list.component.ts index dda3d6a46..1dc13e710 100644 --- a/src/app/components/results-menu/scene-detail/image-dialog/browse-list/browse-list.component.ts +++ b/src/app/components/results-menu/scene-detail/image-dialog/browse-list/browse-list.component.ts @@ -25,7 +25,7 @@ export class BrowseListComponent implements OnInit, AfterViewInit, OnDestroy { @ViewChild(CdkVirtualScrollViewport) scroll: CdkVirtualScrollViewport; public scenesSorted$ = this.scenesService.sortScenes$( - this.scenesService.scenes$() + this.scenesService.scenes$ ); public scenes$: Observable; private scene: models.CMRProduct; diff --git a/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts b/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts index 4f8519521..dedd0b3cb 100644 --- a/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts +++ b/src/app/components/results-menu/scenes-list-header/scenes-list-header.component.ts @@ -37,7 +37,7 @@ export class ScenesListHeaderComponent implements OnInit, OnDestroy { public totalResultCount$ = combineLatest([ this.store$.select(searchStore.getTotalResultCount), - this.scenesService.scenes$()] + this.scenesService.scenes$] ).pipe( map(([count, scenes]) => count + scenes?.filter(scene => scene.metadata.productType === 'BURST').length) ) @@ -56,7 +56,7 @@ export class ScenesListHeaderComponent implements OnInit, OnDestroy { public sarviewsEventProducts: SarviewsProduct[] = []; public pinnedEventIDs: string[]; - public numBaselineScenes$ = this.scenesService.scenes$().pipe( + public numBaselineScenes$ = this.scenesService.scenes$.pipe( map(scenes => scenes.length), ); @@ -202,16 +202,18 @@ export class ScenesListHeaderComponent implements OnInit, OnDestroy { this.subs.add( combineLatest([ - this.scenesService.scenes$(), + this.scenesService.scenes$, this.store$.select(filtersStore.getProductTypes), - this.store$.select(searchStore.getSearchType),] + this.store$.select(searchStore.getSearchType), + this.store$.select(filtersStore.getSelectedDataset)] ).pipe( debounceTime(250) - ).subscribe(([scenes, productTypes, searchType]) => { + ).subscribe(([scenes, productTypes, searchType, selectedDataset]) => { this.canHideRawData = searchType === models.SearchType.DATASET && scenes.every(scene => scene.dataset === 'Sentinel-1B' || scene.dataset === 'Sentinel-1A') && - productTypes.length <= 0; + productTypes.length <= 0 + && selectedDataset.id !== models.opera_s1.id; }) ); diff --git a/src/app/components/results-menu/scenes-list/scenes-list.component.html b/src/app/components/results-menu/scenes-list/scenes-list.component.html index 98aba1fec..46a12e8b5 100644 --- a/src/app/components/results-menu/scenes-list/scenes-list.component.html +++ b/src/app/components/results-menu/scenes-list/scenes-list.component.html @@ -20,11 +20,11 @@
= this.scenesService.sortScenes$(this.scenesService.scenes$()); + const scenes$ = this.scenesService.scenes$; + const sortedScenes$: Observable = this.scenesService.sortScenes$(scenes$); this.subs.add( this.store$.select(scenesStore.getSelectedScene).pipe( @@ -210,9 +211,10 @@ export class ScenesListComponent implements OnInit, OnDestroy, AfterContentInit map(([queueProducts, searchScenes]) => { const queuedProductGroups: { [id: string]: string[] } = queueProducts.reduce((total, product) => { - const scene = total[product.metadata.productType !== 'BURST' && product.metadata.productType !== 'BURST_XML' ? product.groupId : product.name] || []; + const groupCriteria = this.getGroupCriteria(product) + const scene = total[groupCriteria] || []; - total[product.metadata.productType !== 'BURST' && product.metadata.productType !== 'BURST_XML' ? product.groupId : product.name] = [...scene, product.id]; + total[groupCriteria] = [...scene, product.id]; return total; }, {}); @@ -349,14 +351,23 @@ export class ScenesListComponent implements OnInit, OnDestroy, AfterContentInit this.store$.dispatch(new scenesStore.SetSelectedPair(pair)); } - public onToggleScene(groupId: string): void { - if (!this.allQueued[groupId]) { - this.store$.dispatch(new queueStore.QueueScene(groupId)); + public onToggleScene(groupCriteria: string): void { + if (!this.allQueued[groupCriteria]) { + this.store$.dispatch(new queueStore.QueueScene(groupCriteria)); } else { - this.store$.dispatch(new queueStore.RemoveSceneFromQueue(groupId)); + this.store$.dispatch(new queueStore.RemoveSceneFromQueue(groupCriteria)); } } + public getGroupCriteria(scene: CMRProduct): string { + const ungrouped_product_types = models.opera_s1.productTypes.map(m => m.apiValue) + if(['BURST', 'BURST_XML'].includes(scene.metadata.productType)) { + return scene.name; + } else if(ungrouped_product_types.includes(scene.metadata.productType)) { + return scene.metadata.parentID || scene.id; + } + return scene.groupId; + } ngOnDestroy() { this.subs.unsubscribe(); } diff --git a/src/app/components/sbas-chart/sbas-chart.component.ts b/src/app/components/sbas-chart/sbas-chart.component.ts index ee50e4be1..975392537 100644 --- a/src/app/components/sbas-chart/sbas-chart.component.ts +++ b/src/app/components/sbas-chart/sbas-chart.component.ts @@ -73,7 +73,7 @@ export class SBASChartComponent implements OnInit, OnDestroy { ) { } ngOnInit(): void { - const scenes$ = this.scenesService.scenes$(); + const scenes$ = this.scenesService.scenes$; this.store$.select(scenesStore.getSelectedPair).pipe( map(pair => !!pair?.[0] && !!pair?.[1] ? pair : null) diff --git a/src/app/components/shared/hyp3-job-status-badge/hyp3-job-status-badge.component.ts b/src/app/components/shared/hyp3-job-status-badge/hyp3-job-status-badge.component.ts index 645f55bfb..1c1b6da90 100644 --- a/src/app/components/shared/hyp3-job-status-badge/hyp3-job-status-badge.component.ts +++ b/src/app/components/shared/hyp3-job-status-badge/hyp3-job-status-badge.component.ts @@ -48,7 +48,7 @@ export class Hyp3JobStatusBadgeComponent implements OnInit { } ); - this.scenesService.scenes$().subscribe( + this.scenesService.scenes$.subscribe( scenes => { this.jobs = scenes.map(scene => scene.metadata.job); } diff --git a/src/app/components/shared/max-results-selector/max-results-selector.component.ts b/src/app/components/shared/max-results-selector/max-results-selector.component.ts index 5f95dd464..269705967 100644 --- a/src/app/components/shared/max-results-selector/max-results-selector.component.ts +++ b/src/app/components/shared/max-results-selector/max-results-selector.component.ts @@ -69,7 +69,7 @@ export class MaxResultsSelectorComponent implements OnInit, OnDestroy { ); this.subs.add( - this.sceneService.scenes$().subscribe( + this.sceneService.scenes$.subscribe( scenes => { this.numberOfScenes = scenes.length; this.burstXMLFileCount = scenes.filter(p => p.metadata.productType === 'BURST').length diff --git a/src/app/components/shared/selectors/job-product-name-selector/job-product-name-selector.component.ts b/src/app/components/shared/selectors/job-product-name-selector/job-product-name-selector.component.ts index 28e7e4711..df137f13c 100644 --- a/src/app/components/shared/selectors/job-product-name-selector/job-product-name-selector.component.ts +++ b/src/app/components/shared/selectors/job-product-name-selector/job-product-name-selector.component.ts @@ -57,7 +57,7 @@ export class JobProductNameSelectorComponent implements OnInit, OnDestroy { ) ); - const fileNames = this.scenesService.scenes$().pipe( + const fileNames = this.scenesService.scenes$.pipe( map(scenes => scenes.map(scene => scene.metadata.fileName.toLowerCase().split('.')[0]) ) diff --git a/src/app/services/keyboard.service.ts b/src/app/services/keyboard.service.ts index 84956a008..b90e8d64a 100644 --- a/src/app/services/keyboard.service.ts +++ b/src/app/services/keyboard.service.ts @@ -23,7 +23,7 @@ export class KeyboardService { init() { const scenesSorted$ = this.scenesService.sortScenes$( - this.scenesService.scenes$() + this.scenesService.scenes$ ); fromEvent(document, 'keydown').pipe( diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts index f090e4465..41cb1acb9 100644 --- a/src/app/services/product.service.ts +++ b/src/app/services/product.service.ts @@ -123,7 +123,8 @@ export class ProductService { bytes: 0, metadata: { ...product.metadata, - productType: product.metadata.productType + '_XML' + productType: product.metadata.productType + '_XML', + subproducts: [] }, parentID: product.id } as models.CMRProduct; @@ -151,16 +152,17 @@ export class ProductService { file: fileID, id: product.id + '-' + file_name, bytes: 0, + browses: [], + thumbnail: null, metadata: { ...product.metadata, - productType: product.metadata.productType + '_TIF', - parentID: product.id + productType: product.metadata.productType, + parentID: product.id, + subproducts: [] }, } as models.CMRProduct; - if(subproduct.productTypeDisplay !== 'Opera Subproduct') { products.push(subproduct) - } } return products diff --git a/src/app/services/scenes.service.ts b/src/app/services/scenes.service.ts index f817048b3..32f86c65e 100644 --- a/src/app/services/scenes.service.ts +++ b/src/app/services/scenes.service.ts @@ -46,9 +46,7 @@ export class ScenesService { ); } - public scenes$(): Observable { - return ( - this.filterByProductName$( + public scenes$: Observable = this.filterByProductName$( this.projectNameFilter$( this.hideExpired$( this.jobStatusFilter$( @@ -56,8 +54,7 @@ export class ScenesService { this.filterBaselineValues$( this.filterByDate$( this.store$.select(getScenes) - )))))))); - } + ))))))); public withBrowses$(scenes$: Observable): Observable { @@ -72,7 +69,7 @@ export class ScenesService { this.store$.select(getTemporalSortDirection), this.store$.select(getPerpendicularSortDirection)] ).pipe( - debounceTime(0), + debounceTime(50), map( ([scenes, tempSort, perpSort]) => { if (tempSort === ColumnSortDirection.NONE && perpSort === ColumnSortDirection.NONE) { diff --git a/src/app/store/scenes/scenes.effect.ts b/src/app/store/scenes/scenes.effect.ts index 272c355ff..c20aab1ef 100644 --- a/src/app/store/scenes/scenes.effect.ts +++ b/src/app/store/scenes/scenes.effect.ts @@ -71,8 +71,7 @@ export class ScenesEffects { ofType(ScenesActionType.SET_SCENES), filter(scenes => !!scenes.payload.products), filter(scenes => scenes.payload.products.length > 0), - debounceTime(1000), - withLatestFrom(this.sceneService.scenes$()), + withLatestFrom(this.sceneService.scenes$), map(([action, filtered]) => ({ products: filtered, searchType: action.payload.searchType })), distinctUntilChanged(), withLatestFrom(this.store$.select(getSelectedScene)), @@ -126,7 +125,7 @@ export class ScenesEffects { public onHideRawData = createEffect(() => this.actions$.pipe( ofType(UIActionType.HIDE_S1_RAW_DATA), debounceTime(1000), - withLatestFrom(this.sceneService.scenes$()), + withLatestFrom(this.sceneService.scenes$), withLatestFrom(this.store$.select(getSelectedScene)), map(([[_, scenes], selected]) => { if (!scenes.map(scene => scene.id).includes(selected.id)) { diff --git a/src/app/store/scenes/scenes.reducer.ts b/src/app/store/scenes/scenes.reducer.ts index 52da7d0f8..6971ce496 100644 --- a/src/app/store/scenes/scenes.reducer.ts +++ b/src/app/store/scenes/scenes.reducer.ts @@ -74,7 +74,7 @@ export function scenesReducer(state = initState, action: ScenesActions): ScenesS const ungrouped_product_types = opera_s1.productTypes.map(m => m.apiValue) for (let product of searchResults) { - if(product.metadata.subproducts.length > 0 || ungrouped_product_types.includes(product.metadata.productType)) { + if(product.metadata.subproducts.length > 0) { for (let subproduct of product.metadata.subproducts) { subproducts.push(subproduct) } @@ -95,10 +95,14 @@ export function scenesReducer(state = initState, action: ScenesActions): ScenesS productGroups = searchResults.reduce((total, product) => { let groupCriteria = product.groupId; - if (product.metadata.subproducts.length > 0 || ungrouped_product_types.includes(product.metadata.productType)) { + if (product.metadata.subproducts.length > 0) { groupCriteria = product.id; - } else if(isSubProduct(product)) { - groupCriteria = product.metadata.parentID; + } else if(ungrouped_product_types.includes(product.metadata.productType)) { + if(isSubProduct(product)) { + groupCriteria = product.metadata.parentID; + } else { + groupCriteria = product.id; + } } const scene = total[groupCriteria] || []; @@ -372,7 +376,7 @@ export const createArraySelector = ) ); -export const getScenes = createArraySelector( +export const getScenes = createSelector( getScenesState, (state: ScenesState) => allScenesFrom(state.scenes, state.products) ); @@ -477,7 +481,7 @@ const productsForScene = (selected, state) => { const ungrouped_product_types = opera_s1.productTypes.map(m => m.apiValue) if (Object.keys(productTypes).length <= 2 && Object.keys(productTypes)[0] === 'BURST') { products = state.scenes[selected.name] || []; - } else if(selected.metadata.subproducts.length > 0 || ungrouped_product_types.includes(selected.metadata.productType)) { + } else if(ungrouped_product_types.includes(selected.metadata.productType)) { products = state.scenes[selected.id] || []; } else { From 0e708571b0e945ed6b905a443986a14dbde89861 Mon Sep 17 00:00:00 2001 From: kim Date: Mon, 2 Oct 2023 16:29:05 -0800 Subject: [PATCH 17/32] completes opera burst id integration --- .../header/info-bar/info-bar.component.html | 5 +++++ .../components/header/info-bar/info-bar.component.ts | 9 ++++++++- .../scene-metadata/scene-metadata.component.html | 11 +++++++++++ .../scene-metadata/scene-metadata.component.ts | 4 ++++ .../geographic-search-filters.component.html | 3 +++ src/app/services/search-params.service.ts | 12 ++++++++++-- 6 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/app/components/header/info-bar/info-bar.component.html b/src/app/components/header/info-bar/info-bar.component.html index 2b88e3c50..ff9a3960c 100644 --- a/src/app/components/header/info-bar/info-bar.component.html +++ b/src/app/components/header/info-bar/info-bar.component.html @@ -55,6 +55,11 @@ {{'FULL_BURST_ID' | translate}}: {{fullBurstIDs.length <= 4 ? fullBurstIDs.join(', ') : fullBurstIDs.length + ' IDs'}} + + + {{'OPERA_BURST_ID' | translate}}: {{operaBurstIDs.length <= 4 ? operaBurstIDs.join(', ') : operaBurstIDs.length + ' IDs'}} + +
diff --git a/src/app/components/header/info-bar/info-bar.component.ts b/src/app/components/header/info-bar/info-bar.component.ts index 34e69317b..76767ae43 100644 --- a/src/app/components/header/info-bar/info-bar.component.ts +++ b/src/app/components/header/info-bar/info-bar.component.ts @@ -46,6 +46,7 @@ export class InfoBarComponent implements OnInit, OnDestroy { public perpRange: models.Range; public tempRange: models.Range; public fullBurstIDs: string[] = []; + public operaBurstIDs: string[] = []; private subs = new SubSink(); @@ -126,6 +127,11 @@ export class InfoBarComponent implements OnInit, OnDestroy { burstIDs => this.fullBurstIDs = burstIDs ); + const operaBurstIDSub = this.store$.select(filtersStore.getOperaBurstIDs).subscribe( + burstIDs => this.operaBurstIDs = burstIDs + ); + + [ startSub, endSub, pathSub, frameSub, @@ -140,7 +146,8 @@ export class InfoBarComponent implements OnInit, OnDestroy { missionSub, tempSub, perpSub, eventProductType, - fullBurstIDSub + fullBurstIDSub, + operaBurstIDSub ].forEach(sub => this.subs.add(sub)); this.subs.add( diff --git a/src/app/components/shared/scene-metadata/scene-metadata.component.html b/src/app/components/shared/scene-metadata/scene-metadata.component.html index 5aefc4652..c14d7a89b 100644 --- a/src/app/components/shared/scene-metadata/scene-metadata.component.html +++ b/src/app/components/shared/scene-metadata/scene-metadata.component.html @@ -157,6 +157,17 @@ {{ 'PGE_VERSION' | translate }} • {{ scene.metadata.pgeVersion }} +
  • + {{'OPERA_BURST_ID' | translate }} • {{scene.metadata.opera.operaBurstID}} + + settings + + + + + +
  • +
  • {{'BURST_ID_FULL' | translate }} • {{scene.metadata.burst.fullBurstID}} diff --git a/src/app/components/shared/scene-metadata/scene-metadata.component.ts b/src/app/components/shared/scene-metadata/scene-metadata.component.ts index 2b95c1937..c1f4ae8b9 100644 --- a/src/app/components/shared/scene-metadata/scene-metadata.component.ts +++ b/src/app/components/shared/scene-metadata/scene-metadata.component.ts @@ -136,6 +136,10 @@ export class SceneMetadataComponent implements OnInit, OnDestroy { this.store$.dispatch(new filtersStore.setFullBurst([this.scene.metadata.burst.fullBurstID])) } + public setOperaBurst(): void { + this.store$.dispatch(new filtersStore.setOperaBurstID([this.scene.metadata.opera.operaBurstID])) + } + private capitalizeFirstLetter(str) { return str.charAt(0).toUpperCase() + str.slice(1); } diff --git a/src/app/components/sidebar/saved-searches/saved-search/search-filters/geographic-search-filters/geographic-search-filters.component.html b/src/app/components/sidebar/saved-searches/saved-search/search-filters/geographic-search-filters/geographic-search-filters.component.html index b9b217618..7da14d833 100644 --- a/src/app/components/sidebar/saved-searches/saved-search/search-filters/geographic-search-filters/geographic-search-filters.component.html +++ b/src/app/components/sidebar/saved-searches/saved-search/search-filters/geographic-search-filters/geographic-search-filters.component.html @@ -44,6 +44,9 @@
    {{ 'FULL_BURST_ID' | translate}}: {{ filters.fullBurstIDs }}
    +
    + {{ 'OPERA_BURST_ID' | translate}}: {{ filters.operaBurstIDs }} +
  • diff --git a/src/app/services/search-params.service.ts b/src/app/services/search-params.service.ts index fb89acf3f..ff7d61666 100644 --- a/src/app/services/search-params.service.ts +++ b/src/app/services/search-params.service.ts @@ -112,7 +112,8 @@ export class SearchParamsService { this.polarizations$(), this.maxResults$(), this.missionParam$(), - this.burstParams$(),] + this.burstParams$(), + this.operaBurstParams$()] ).pipe( map((params: any[]) => params .reduce( @@ -150,7 +151,14 @@ export class SearchParamsService { ) ); } - + + private operaBurstParams$() { + return this.store$.select(filterStore.getOperaBurstIDs).pipe( + map(operaIDs => ({ + operaburstid: operaIDs?.join(',')}) + ) + ); +} private searchPolygon$() { return combineLatest([ this.mapService.searchPolygon$.pipe(startWith(null)), From a78981c4c4c9259bb0e06ce3913d381c35982e7c Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 3 Oct 2023 10:46:19 -0800 Subject: [PATCH 18/32] adds RTC-Static static layer button for opera products --- .../scene-detail/scene-detail.component.html | 14 +++++++++++++- .../scene-detail/scene-detail.component.ts | 8 ++++++++ src/app/store/scenes/scenes.reducer.ts | 12 ++++++++++-- src/assets/i18n/en.json | 1 + 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/app/components/results-menu/scene-detail/scene-detail.component.html b/src/app/components/results-menu/scene-detail/scene-detail.component.html index 1eb58b242..eede7e1b0 100644 --- a/src/app/components/results-menu/scene-detail/scene-detail.component.html +++ b/src/app/components/results-menu/scene-detail/scene-detail.component.html @@ -329,7 +329,8 @@ (prop.isRelevant(p.PATH, dataset) && prop.isRelevant(p.FRAME, dataset)) || dataset?.id === 'UAVSAR'|| - dataset?.id === 'SENTINEL-1 BURSTS' + dataset?.id === 'SENTINEL-1 BURSTS' || + dataset?.id === 'OPERA-S1' ) " (click)="moreLikeThis()" @@ -338,6 +339,17 @@ {{searchType !== searchTypes.SARVIEWS_EVENTS ? ('MORE_LIKE_THIS' | translate ) : ('GEOGRAPHIC' | translate )}} + +
    diff --git a/src/app/components/results-menu/scene-detail/scene-detail.component.ts b/src/app/components/results-menu/scene-detail/scene-detail.component.ts index eda67c37c..0c92baddb 100644 --- a/src/app/components/results-menu/scene-detail/scene-detail.component.ts +++ b/src/app/components/results-menu/scene-detail/scene-detail.component.ts @@ -382,6 +382,14 @@ export class SceneDetailComponent implements OnInit, OnDestroy { } } + public staticLayer(){ + this.store$.dispatch(new searchStore.ClearSearch()); + this.store$.dispatch(new filtersStore.SetSelectedDataset('OPERA-S1')) + this.store$.dispatch(new filtersStore.setOperaBurstID([this.scene.metadata.opera.operaBurstID])); + this.store$.dispatch(new filtersStore.SetProductTypes([models.opera_s1.productTypes.find(t => t.apiValue === 'RTC-STATIC')])); + this.store$.dispatch(new filtersStore.SetEndDate(new Date(this.scene.metadata.date.toDate()))); + this.store$.dispatch(new searchStore.MakeSearch()); + } public makeBaselineSearch(): void { const sceneName = this.baselineSceneName(); const dateRange = this.dateRange; diff --git a/src/app/store/scenes/scenes.reducer.ts b/src/app/store/scenes/scenes.reducer.ts index 6971ce496..1a861a8e7 100644 --- a/src/app/store/scenes/scenes.reducer.ts +++ b/src/app/store/scenes/scenes.reducer.ts @@ -360,11 +360,19 @@ export const allScenesWithBrowse = (scenes: {[id: string]: string[]}, products) }; function arrayEquals(a, b) { + return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.toString() === b.toString() && - a.every((value, index) => value.toString() === b[index].toString()) + a.every((value, index) => { + if(Array.isArray(value) && Array.isArray(b[index])) { + return arrayEquals(value, b[index]) + } else { + value.id === b[index].id + } + } + ) } export const createArraySelector = createSelectorFactory( @@ -376,7 +384,7 @@ export const createArraySelector = ) ); -export const getScenes = createSelector( +export const getScenes = createArraySelector( getScenesState, (state: ScenesState) => allScenesFrom(state.scenes, state.products) ); diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 40ad762e2..0dbd2d0e7 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -732,6 +732,7 @@ "START_DATE_TIME_BEAM_MODE_PATH_FRAME_FLIGHT_DIRECTION_POLARIZATION_ABSOLUTE_ORBIT_AND_A": "Start Date/Time, Beam Mode, Path, Frame, Flight Direction, Polarization, Absolute Orbit, and a", "START_TIME": "Start Time", "STARTING_CORNER_MOVE_THE_MOUSE_THEN_CLICK_AGAIN_TO_FINISH_THE_BOX": "starting corner, move the mouse, then click again to finish the box.", + "STATIC_LAYER": "Static Layer", "STATISTICS_AND_GITHUB_REPOSITORY": "Statistics and Github Repository", "STOP_ADDING_CUSTOM_PAIR": "Stop adding custom pair", "STOP_DOWNLOAD": "Stop download", From b4f9c1cd98b3bf643c06527ae043dce8b605e43c Mon Sep 17 00:00:00 2001 From: kim Date: Tue, 3 Oct 2023 10:55:23 -0800 Subject: [PATCH 19/32] fix bug with staticLayer button --- .../results-menu/scene-detail/scene-detail.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/components/results-menu/scene-detail/scene-detail.component.ts b/src/app/components/results-menu/scene-detail/scene-detail.component.ts index 0c92baddb..3106f9a46 100644 --- a/src/app/components/results-menu/scene-detail/scene-detail.component.ts +++ b/src/app/components/results-menu/scene-detail/scene-detail.component.ts @@ -383,11 +383,13 @@ export class SceneDetailComponent implements OnInit, OnDestroy { } public staticLayer(){ + const operaBurstID = this.scene.metadata.opera.operaBurstID; + const sensorDate = new Date(this.scene.metadata.date.toDate()); this.store$.dispatch(new searchStore.ClearSearch()); this.store$.dispatch(new filtersStore.SetSelectedDataset('OPERA-S1')) - this.store$.dispatch(new filtersStore.setOperaBurstID([this.scene.metadata.opera.operaBurstID])); + this.store$.dispatch(new filtersStore.setOperaBurstID([operaBurstID])); this.store$.dispatch(new filtersStore.SetProductTypes([models.opera_s1.productTypes.find(t => t.apiValue === 'RTC-STATIC')])); - this.store$.dispatch(new filtersStore.SetEndDate(new Date(this.scene.metadata.date.toDate()))); + this.store$.dispatch(new filtersStore.SetEndDate(sensorDate)); this.store$.dispatch(new searchStore.MakeSearch()); } public makeBaselineSearch(): void { From 0d56ce691050909020e4d81bd9dddbde45fe4d45 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 4 Oct 2023 08:58:22 -0800 Subject: [PATCH 20/32] disables staticLayer button on rtc-static, enable on CSLC and RTC products --- .../results-menu/scene-detail/scene-detail.component.html | 2 +- .../results-menu/scene-detail/scene-detail.component.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/components/results-menu/scene-detail/scene-detail.component.html b/src/app/components/results-menu/scene-detail/scene-detail.component.html index eede7e1b0..07db970b6 100644 --- a/src/app/components/results-menu/scene-detail/scene-detail.component.html +++ b/src/app/components/results-menu/scene-detail/scene-detail.component.html @@ -343,7 +343,7 @@
    - - + + +
    diff --git a/src/app/components/results-menu/scene-files/scene-files.component.ts b/src/app/components/results-menu/scene-files/scene-files.component.ts index 4ddd6abaf..03387f8d6 100644 --- a/src/app/components/results-menu/scene-files/scene-files.component.ts +++ b/src/app/components/results-menu/scene-files/scene-files.component.ts @@ -1,8 +1,8 @@ import {Component, OnInit, OnDestroy, AfterContentInit, Input, ViewChild} from '@angular/core'; import { SubSink } from 'subsink'; -import { combineLatest } from 'rxjs'; -import { debounceTime, filter, map, take, withLatestFrom } from 'rxjs/operators'; +import { combineLatest, of } from 'rxjs'; +import { debounceTime, distinctUntilChanged, filter, map, switchMap, take, withLatestFrom } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@store'; @@ -12,7 +12,7 @@ import * as userStore from '@store/user'; import * as hyp3Store from '@store/hyp3'; import * as uiStore from '@store/ui'; -import { Hyp3Service, NotificationService, SarviewsEventsService } from '@services'; +import { AsfApiService, Hyp3Service, NotificationService, ProductService, SarviewsEventsService } from '@services'; import * as models from '@models'; import { CMRProductMetadata, hyp3JobTypes, SarviewProductGranule, SarviewsProduct } from '@models'; import { ClipboardService } from 'ngx-clipboard'; @@ -111,6 +111,8 @@ export class SceneFilesComponent implements OnInit, OnDestroy, AfterContentInit private eventMonitoringService: SarviewsEventsService, public dialog: MatDialog, private screenSize: ScreenSizeService, + private asfApiService: AsfApiService, + private productService: ProductService ) { } ngOnInit() { @@ -118,9 +120,10 @@ export class SceneFilesComponent implements OnInit, OnDestroy, AfterContentInit combineLatest([ this.store$.select(scenesStore.getSelectedSceneProducts), this.store$.select(scenesStore.getOpenUnzippedProduct), - this.store$.select(scenesStore.getUnzippedProducts)] - ).pipe(debounceTime(0)) - .subscribe( + this.store$.select(scenesStore.getUnzippedProducts), + ] + ).pipe(debounceTime(0) + ).subscribe( ([products, unzipped, unzippedFiles]) => { this.unzippedProducts = unzippedFiles; this.products = products; @@ -417,6 +420,40 @@ export class SceneFilesComponent implements OnInit, OnDestroy, AfterContentInit }; return toCMRProduct; } + + public StaticLayerProduct$ = this.store$.select(scenesStore.getSelectedScene).pipe( + debounceTime(100), + distinctUntilChanged((prev, curr) => prev.id === curr.id), + switchMap(scene => { + const queryParams = { + processinglevel : scene.metadata.productType + '-STATIC', + start: scene.metadata.stopDate === null ? '' : moment.utc( scene.metadata.stopDate ).format(), + operaburstid: scene.metadata?.opera?.operaBurstID, + collections: models.opera_s1.apiValue.collections, + }; + if(!!scene && ['RTC', 'CSLC'].includes(scene?.metadata?.productType) && scene?.id.startsWith('OPERA')) { + return this.asfApiService.query(queryParams).pipe( + map(products => products.length > 0 ? this.productService.fromResponse(products).slice(0, 1) : []) + ); + } else { + return of([]); + + } + } + + ) + ); + + // const queryParams = { + // processinglevel : selectedScene.metadata.productType + '-STATIC', + // start: selectedScene.metadata.stopDate === null ? '' : moment.utc( selectedScene.metadata.stopDate ).format(), + // operaburstid: selectedScene.metadata?.opera?.operaBurstID + // } + + // const staticLayer = this.asfApiService.query(queryParams).pipe( + // map(products => this.productService.fromResponse(products).slice(0,1)) + // ); + // } public getProductSceneCount(products: SarviewsProduct[]) { const outputList = products.reduce((prev, product) => { const temp = product.granules.map(granule => granule.granule_name); From 36f5989e4f86ad06a1adb2b85d9613908e54e60c Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 4 Oct 2023 16:38:52 -0800 Subject: [PATCH 22/32] Fixes groupID query, fixes bug with staticLayer query when selected scene is undefined --- .../scene-files/scene-files.component.ts | 14 +++++++------- src/app/services/search-params.service.ts | 16 +++++++++++++--- src/app/store/filters/filters.reducer.ts | 13 +++++++++++-- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/app/components/results-menu/scene-files/scene-files.component.ts b/src/app/components/results-menu/scene-files/scene-files.component.ts index 03387f8d6..6579e860c 100644 --- a/src/app/components/results-menu/scene-files/scene-files.component.ts +++ b/src/app/components/results-menu/scene-files/scene-files.component.ts @@ -423,15 +423,15 @@ export class SceneFilesComponent implements OnInit, OnDestroy, AfterContentInit public StaticLayerProduct$ = this.store$.select(scenesStore.getSelectedScene).pipe( debounceTime(100), - distinctUntilChanged((prev, curr) => prev.id === curr.id), + distinctUntilChanged((prev, curr) => prev?.id === curr?.id), switchMap(scene => { - const queryParams = { - processinglevel : scene.metadata.productType + '-STATIC', - start: scene.metadata.stopDate === null ? '' : moment.utc( scene.metadata.stopDate ).format(), - operaburstid: scene.metadata?.opera?.operaBurstID, - collections: models.opera_s1.apiValue.collections, - }; if(!!scene && ['RTC', 'CSLC'].includes(scene?.metadata?.productType) && scene?.id.startsWith('OPERA')) { + const queryParams = { + processinglevel : scene.metadata.productType + '-STATIC', + start: scene.metadata.stopDate === null ? '' : moment.utc( scene.metadata.stopDate ).format(), + operaburstid: scene.metadata?.opera?.operaBurstID, + collections: models.opera_s1.apiValue.collections, + }; return this.asfApiService.query(queryParams).pipe( map(products => products.length > 0 ? this.productService.fromResponse(products).slice(0, 1) : []) ); diff --git a/src/app/services/search-params.service.ts b/src/app/services/search-params.service.ts index ff7d61666..406a2043a 100644 --- a/src/app/services/search-params.service.ts +++ b/src/app/services/search-params.service.ts @@ -113,7 +113,8 @@ export class SearchParamsService { this.maxResults$(), this.missionParam$(), this.burstParams$(), - this.operaBurstParams$()] + this.operaBurstParams$(), + this.groupID$()] ).pipe( map((params: any[]) => params .reduce( @@ -157,8 +158,17 @@ export class SearchParamsService { map(operaIDs => ({ operaburstid: operaIDs?.join(',')}) ) - ); -} + ); + } + + private groupID$() { + return this.store$.select(filterStore.getGroupID).pipe( + map(groupid => ({ + groupid + })) + ) + } + private searchPolygon$() { return combineLatest([ this.mapService.searchPolygon$.pipe(startWith(null)), diff --git a/src/app/store/filters/filters.reducer.ts b/src/app/store/filters/filters.reducer.ts index 74f01ff9c..71776aefd 100644 --- a/src/app/store/filters/filters.reducer.ts +++ b/src/app/store/filters/filters.reducer.ts @@ -52,6 +52,8 @@ export interface FiltersState { fullBurstIDs: null | string[]; operaBurstIDs: null | string[]; + + groupID: null | string; } @@ -124,7 +126,9 @@ export const initState: FiltersState = { fullBurstIDs: [], - operaBurstIDs: [] + operaBurstIDs: [], + + groupID: null }; @@ -357,7 +361,7 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte } if(action.payload.dataset.id === models.opera_s1.id) { filters = { - groupId: action.payload.product.groupId + groupID: action.payload.product.groupId } } return { @@ -1031,4 +1035,9 @@ export const getFullBurstIDs = createSelector( export const getOperaBurstIDs = createSelector( getFiltersState, (state: FiltersState) => state.operaBurstIDs +) + +export const getGroupID = createSelector( + getFiltersState, + (state: FiltersState) => state.groupID ) \ No newline at end of file From 858e7e28b1e99dd343692424d9c6b5b7a7bc5cd9 Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 5 Oct 2023 15:28:22 -0800 Subject: [PATCH 23/32] updates urls, descriptions, more like to button, etc --- .../scene-detail/scene-detail.component.html | 8 ++++---- .../scene-metadata/scene-metadata.component.html | 2 +- src/app/models/datasets/opera_s1.ts | 10 +++++----- src/app/store/filters/filters.reducer.ts | 3 ++- src/assets/i18n/en.json | 3 ++- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/app/components/results-menu/scene-detail/scene-detail.component.html b/src/app/components/results-menu/scene-detail/scene-detail.component.html index 07db970b6..c7e9d8b0e 100644 --- a/src/app/components/results-menu/scene-detail/scene-detail.component.html +++ b/src/app/components/results-menu/scene-detail/scene-detail.component.html @@ -336,11 +336,11 @@ (click)="moreLikeThis()" class="mini-toggle-button search-button"> {{ 'MORE' | translate }} - {{searchType !== searchTypes.SARVIEWS_EVENTS ? ('MORE_LIKE_THIS' | translate ) : ('GEOGRAPHIC' | translate )}} - + {{searchType !== searchTypes.SARVIEWS_EVENTS ? dataset?.id === 'OPERA-S1' ? ('SOURCE_DATA' | translate) : ('MORE_LIKE_THIS' | translate ) : ('GEOGRAPHIC' | translate )}} + Source Data - + -->
    diff --git a/src/app/components/shared/scene-metadata/scene-metadata.component.html b/src/app/components/shared/scene-metadata/scene-metadata.component.html index c14d7a89b..4cd5e36b8 100644 --- a/src/app/components/shared/scene-metadata/scene-metadata.component.html +++ b/src/app/components/shared/scene-metadata/scene-metadata.component.html @@ -37,7 +37,7 @@
  • {{ 'BEAM_MODE' |translate }} • {{ scene.metadata.beamMode }} - settings diff --git a/src/app/models/datasets/opera_s1.ts b/src/app/models/datasets/opera_s1.ts index 06b35d93c..5601dc0ef 100644 --- a/src/app/models/datasets/opera_s1.ts +++ b/src/app/models/datasets/opera_s1.ts @@ -16,14 +16,14 @@ export const opera_s1 = { ], apiValue: { collections: - "C1259974840-ASF,C1259976861-ASF,C1259981910-ASF,C1259982010-ASF,C1259975087-ASFDEV,C1259976862-ASFDEV,C1259983643-ASFDEV,C1259983645-ASFDEV" }, + "C1259974840-ASF,C1259976861-ASF,C1259981910-ASF,C1259982010-ASF,C1259975087-ASFDEV,C1259976862-ASFDEV,C1259983643-ASFDEV,C1259983645-ASFDEV,C2777443834-ASF,C2777436413-ASF" }, date: { start: new Date('2014/06/15 03:44:43 UTC') }, - infoUrl: 'https://sentinels.copernicus.eu/web/sentinel/user-guides/sentinel-1-sar/acquisition-modes/interferometric-wide-swath', - citationUrl: 'https://asf.alaska.edu/data-sets/sar-data-sets/sentinel-1/sentinel-1-how-to-cite/', + infoUrl: 'https://asf.alaska.edu/datasets/daac/opera/', + citationUrl: 'https://asf.alaska.edu/datasets/daac/opera/', frequency: 'C-Band', source: { - name: 'ESA', - url: 'https://www.esa.int/ESA' + name: 'OPERA-JPL', + url: 'https://www.jpl.nasa.gov/go/opera' }, productTypes: [ { diff --git a/src/app/store/filters/filters.reducer.ts b/src/app/store/filters/filters.reducer.ts index 71776aefd..4ffbb1a01 100644 --- a/src/app/store/filters/filters.reducer.ts +++ b/src/app/store/filters/filters.reducer.ts @@ -361,7 +361,8 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte } if(action.payload.dataset.id === models.opera_s1.id) { filters = { - groupID: action.payload.product.groupId + groupID: action.payload.product.groupId, + selectedDatasetId: 'SENTINEL-1' } } return { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 0dbd2d0e7..db05263f2 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -506,7 +506,7 @@ "OPEN_MANUAL": "Open Manual", "OPEN_STREET_MAP_CONTRIBUTORS": "© OpenStreetMap contributors", "OPERA_BURST_ID": "Opera Burst IDs", - "OPERA_S1_DESC": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "OPERA_S1_DESC": "Sentinel-1 RTC backscatter products providing near-global coverage, as well as Sentinel-1 CSLC products covering North America.", "OPTION_OR": "option or", "OPTION_OR ": "option or", "OPTIONS": "Options", @@ -724,6 +724,7 @@ "SORT_BY": "Sort By", "SORT_CRITERIA": "Sort Criteria", "SORT_ORDER": "Sort Order", + "SOURCE_DATA": "Source Data", "START": "Start", "START DATE": "Start Date", "START_ADDING_CUSTOM_PAIR": "Start adding custom pair", From 3ec72bfbfa78dd50043e1f2784434e090f76ba6a Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 5 Oct 2023 16:27:11 -0800 Subject: [PATCH 24/32] group ID now filterable param for S1 datasets --- .../scene-detail/scene-detail.component.html | 1 - .../other-selector/other-selector.component.html | 16 ++++++++++++++++ .../other-selector/other-selector.component.ts | 9 +++++++++ .../other-selector/other-selector.module.ts | 4 +++- src/app/services/url-state.service.ts | 11 +++++++++++ src/app/store/filters/filters.action.ts | 13 +++++++++++-- src/app/store/filters/filters.reducer.ts | 6 ++++++ 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/app/components/results-menu/scene-detail/scene-detail.component.html b/src/app/components/results-menu/scene-detail/scene-detail.component.html index c7e9d8b0e..907362654 100644 --- a/src/app/components/results-menu/scene-detail/scene-detail.component.html +++ b/src/app/components/results-menu/scene-detail/scene-detail.component.html @@ -337,7 +337,6 @@ class="mini-toggle-button search-button"> {{ 'MORE' | translate }} {{searchType !== searchTypes.SARVIEWS_EVENTS ? dataset?.id === 'OPERA-S1' ? ('SOURCE_DATA' | translate) : ('MORE_LIKE_THIS' | translate ) : ('GEOGRAPHIC' | translate )}} - Source Data +
  • diff --git a/src/app/components/shared/selectors/other-selector/other-selector.component.ts b/src/app/components/shared/selectors/other-selector/other-selector.component.ts index f5ca15004..79fc54759 100644 --- a/src/app/components/shared/selectors/other-selector/other-selector.component.ts +++ b/src/app/components/shared/selectors/other-selector/other-selector.component.ts @@ -21,6 +21,7 @@ export class OtherSelectorComponent implements OnInit, OnDestroy { beamModes: models.DatasetBeamModes; polarizations: models.DatasetPolarizations; subtypes: models.DatasetSubtypes; + groupID: string; public datasetProductTypes$ = this.store$.select(filtersStore.getProductTypes); public flightDirections$ = this.store$.select(filtersStore.getFlightDirections); @@ -28,6 +29,7 @@ export class OtherSelectorComponent implements OnInit, OnDestroy { public polarizations$ = this.store$.select(filtersStore.getPolarizations); public selectedDataset$ = this.store$.select(filtersStore.getSelectedDataset); public subtypes$ = this.store$.select(filtersStore.getSubtypes); + public groupID$ = this.store$.select(filtersStore.getGroupID); public flightDirectionTypes = models.flightDirections; public p = models.Props; @@ -62,6 +64,9 @@ export class OtherSelectorComponent implements OnInit, OnDestroy { this.subs.add( this.subtypes$.subscribe(subtypes => this.subtypes = subtypes) ); + this.subs.add( + this.groupID$.subscribe(groupID => this.groupID = groupID) + ); } public onNewDatasetBeamModes(beamModes: string[]): void { @@ -88,6 +93,10 @@ export class OtherSelectorComponent implements OnInit, OnDestroy { this.store$.dispatch(new filtersStore.SetSubtypes(subtypes)); } + public onNewGroupID(): void { + this.store$.dispatch(new filtersStore.setGroupID(this.groupID)); + } + ngOnDestroy() { this.subs.unsubscribe(); } diff --git a/src/app/components/shared/selectors/other-selector/other-selector.module.ts b/src/app/components/shared/selectors/other-selector/other-selector.module.ts index 4c2b6cb68..c756f1a62 100644 --- a/src/app/components/shared/selectors/other-selector/other-selector.module.ts +++ b/src/app/components/shared/selectors/other-selector/other-selector.module.ts @@ -10,6 +10,7 @@ import { ProductTypeSelectorModule } from '@components/shared/selectors/product- import { OtherSelectorComponent } from './other-selector.component'; import { BurstSelectorModule } from '../burst-selector'; import { SharedModule } from "@shared"; +import { MatInputModule } from '@angular/material/input'; @NgModule({ declarations: [ OtherSelectorComponent ], @@ -21,7 +22,8 @@ import { SharedModule } from "@shared"; MatSharedModule, ProductTypeSelectorModule, BurstSelectorModule, - SharedModule + SharedModule, + MatInputModule ], exports: [ OtherSelectorComponent ], }) diff --git a/src/app/services/url-state.service.ts b/src/app/services/url-state.service.ts index 036b07afe..bd1a81ee2 100644 --- a/src/app/services/url-state.service.ts +++ b/src/app/services/url-state.service.ts @@ -493,6 +493,13 @@ export class UrlStateService { map(list => ({ operaBurstID: list?.map(num => num.toString()).join(',') })) ), loader: this.loadOperaBurstIDs + }, + { + name: 'groupID', + source: this.store$.select(filterStore.getGroupID).pipe( + map(groupId => ({ groupId }) + )), + loader: this.loadGroupId }]; } @@ -816,4 +823,8 @@ export class UrlStateService { const list = ids.split(','); return new filterStore.setOperaBurstID(list); }; + + private loadGroupId = (id: string): Action => { + return new filterStore.setGroupID(id); + }; } diff --git a/src/app/store/filters/filters.action.ts b/src/app/store/filters/filters.action.ts index 3ede28600..a889d0f87 100644 --- a/src/app/store/filters/filters.action.ts +++ b/src/app/store/filters/filters.action.ts @@ -92,7 +92,9 @@ export enum FiltersActionType { SET_FULL_BURST = '[Filters] Set Full Burst IDs', - SET_OPERA_BURST_ID = '[Filters] Set Full OPERA S1 Burst IDs' + SET_OPERA_BURST_ID = '[Filters] Set Full OPERA S1 Burst IDs', + + SET_GROUP_ID = '[Filters] Set Sentinel-1 Group ID' } export class SetSelectedDataset implements Action { @@ -440,6 +442,12 @@ export class setOperaBurstID implements Action { constructor(public payload: string[]) {} } +export class setGroupID implements Action { + public readonly type = FiltersActionType.SET_GROUP_ID; + + constructor(public payload: string) {} +} + export type FiltersActions = @@ -503,4 +511,5 @@ export type FiltersActions = | ClearEventFilters | ClearHyp3ProductTypes | setFullBurst - | setOperaBurstID; + | setOperaBurstID + | setGroupID; diff --git a/src/app/store/filters/filters.reducer.ts b/src/app/store/filters/filters.reducer.ts index 4ffbb1a01..6b2328cc1 100644 --- a/src/app/store/filters/filters.reducer.ts +++ b/src/app/store/filters/filters.reducer.ts @@ -749,6 +749,12 @@ export function filtersReducer(state = initState, action: FiltersActions): Filte operaBurstIDs: action.payload } } + case FiltersActionType.SET_GROUP_ID: { + return { + ...state, + groupID: action.payload + } + } default: { return state; } From 9d04a9cee241f2357955ee4eb474fa202240aa66 Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 5 Oct 2023 16:55:07 -0800 Subject: [PATCH 25/32] makes group id clearable, appears in info bar --- src/app/components/header/info-bar/info-bar.component.html | 5 +++++ src/app/components/header/info-bar/info-bar.component.ts | 7 ++++++- .../selectors/other-selector/other-selector.component.html | 4 +++- .../selectors/other-selector/other-selector.component.ts | 3 +++ src/app/store/filters/filters.reducer.ts | 3 ++- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/app/components/header/info-bar/info-bar.component.html b/src/app/components/header/info-bar/info-bar.component.html index ff9a3960c..ed7d9842b 100644 --- a/src/app/components/header/info-bar/info-bar.component.html +++ b/src/app/components/header/info-bar/info-bar.component.html @@ -60,6 +60,11 @@ {{'OPERA_BURST_ID' | translate}}: {{operaBurstIDs.length <= 4 ? operaBurstIDs.join(', ') : operaBurstIDs.length + ' IDs'}} + + + Group ID: {{groupID}} + +
    diff --git a/src/app/components/header/info-bar/info-bar.component.ts b/src/app/components/header/info-bar/info-bar.component.ts index 76767ae43..4b73ba3b4 100644 --- a/src/app/components/header/info-bar/info-bar.component.ts +++ b/src/app/components/header/info-bar/info-bar.component.ts @@ -47,6 +47,7 @@ export class InfoBarComponent implements OnInit, OnDestroy { public tempRange: models.Range; public fullBurstIDs: string[] = []; public operaBurstIDs: string[] = []; + public groupID: string; private subs = new SubSink(); @@ -131,6 +132,9 @@ export class InfoBarComponent implements OnInit, OnDestroy { burstIDs => this.operaBurstIDs = burstIDs ); + const groupIDSub = this.store$.select(filtersStore.getGroupID).subscribe( + groupID => this.groupID = groupID + ); [ startSub, endSub, @@ -147,7 +151,8 @@ export class InfoBarComponent implements OnInit, OnDestroy { tempSub, perpSub, eventProductType, fullBurstIDSub, - operaBurstIDSub + operaBurstIDSub, + groupIDSub ].forEach(sub => this.subs.add(sub)); this.subs.add( diff --git a/src/app/components/shared/selectors/other-selector/other-selector.component.html b/src/app/components/shared/selectors/other-selector/other-selector.component.html index 681001fd4..8373608bd 100644 --- a/src/app/components/shared/selectors/other-selector/other-selector.component.html +++ b/src/app/components/shared/selectors/other-selector/other-selector.component.html @@ -91,7 +91,9 @@ (input)="onNewGroupID()" [(ngModel)]="groupID" - placeholder="Sentinel-1 Group ID"> + [maxlength]="29" + type="text" + placeholder="Group ID">