Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion projects/common/src/lib/common.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ const COMMON_COMPONENTS = [
LineIconComponent,
LineIconComponent,
LoginComponent,
MeasurementComponent,
NumberParamEditorComponent,
PercentileBreakpointSelectorComponent,
PercentileBreakpointSelectorComponent,
Expand Down Expand Up @@ -166,6 +165,7 @@ const FXFLEX_LEGACY_DIRECTIVES = [FxFlexDirective, FxLayoutDirective, FxLayoutGa
AngularCommonModule,
ScrollingModule,
OgrDatasetComponent,
MeasurementComponent,
RouterOutlet,
RouterLink,
RouterLinkActive,
Expand All @@ -183,6 +183,7 @@ const FXFLEX_LEGACY_DIRECTIVES = [FxFlexDirective, FxLayoutDirective, FxLayoutGa
ScrollingModule,
NgxMatSelectSearchModule,
OgrDatasetComponent,
MeasurementComponent,
],
})
export class CommonModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,28 @@
<mat-label>Spatial Reference</mat-label>
<input matInput type="text" formControlName="spatialReference" />
</mat-form-field>

<mat-form-field class="column">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you include some sort of indication in the UI that this section is meant for setting the measurement? Alternatively, would it maybe make sense to have the "geoengine-measurement" appear directly below the "Integer Columns" etc. fields when one clicks on a column name/enters a new one there? It might be more intuitive to have it directly there, associated with the column definition and would be neat to have it only appear when actively editing the column names this way and hidden otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit

I could change it s.t. the measurement appears underneath the selected column. However I would need to track which column is selected etc. ..a little bit more code.

<mat-label>Column</mat-label>
<mat-select
[ngModel]="selectedColumn()"
(ngModelChange)="setSelectedColumn($event)"
[ngModelOptions]="{standalone: true}"
(selectionChange)="onColumnMatSelectChange($event)"
>
<!--
this causes an infinite loop in.
I think that's because we create a new column vector every time we call this method?
-->
<!-- <mat-option *ngFor="let col of _columns" [value]="col">{{ col }}</mat-option>-->
@for (col of sortedColumns(); track col.name) {
<mat-option [value]="col.name">{{ col.name }}</mat-option>
}
</mat-select>
</mat-form-field>
<geoengine-measurement
[measurement]="newMeasurementForColumn()"
(measurementChange)="onMeasurementChange($event)"
(onInputChange)="markDirty()"
></geoengine-measurement>
</form>
152 changes: 113 additions & 39 deletions projects/common/src/lib/datasets/ogr-dataset/ogr-dataset.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {ChangeDetectorRef, Component, Input, OnChanges, SimpleChanges} from '@angular/core';
import {ChangeDetectorRef, Component, Input, OnChanges, OnInit, signal, SimpleChanges, ViewChild} from '@angular/core';
import {FormsModule, ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';
import {DatasetsService} from '../datasets.service';
import {UploadsService} from '../../uploads/uploads.service';
import {UserService} from '../../user/user.service';
import {MatChipInputEvent, MatChipsModule} from '@angular/material/chips';
import {
Measurement,
MetaDataDefinition,
MetaDataSuggestion,
OgrMetaData,
Expand All @@ -18,16 +19,21 @@ import {UUID} from '../dataset.model';
import {CommonModule as AngularCommonModule} from '@angular/common';
import {MatInputModule} from '@angular/material/input';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatSelectModule} from '@angular/material/select';
import {MatSelectChange, MatSelectModule} from '@angular/material/select';
import {timeStepGranularityOptions} from '../../time/time.model';
import {MatButtonModule} from '@angular/material/button';
import {MatIconModule} from '@angular/material/icon';
import {
FxFlexDirective,
FxLayoutAlignDirective,
FxLayoutDirective,
FxLayoutGapDirective,
} from '../../util/directives/flexbox-legacy.directive';
import {FxLayoutDirective} from '../../util/directives/flexbox-legacy.directive';
import {MeasurementComponent} from '../../measurement/measurement.component';

interface Column {
name: string;
vectorColumnInfo: VectorColumnInfo;
newMeasurement: Measurement;
dataType: string;
}

type ColumnInfo = {[name: string]: Column};

@Component({
selector: 'geoengine-ogr-dataset',
Expand All @@ -41,15 +47,13 @@ import {
MatChipsModule,
MatButtonModule,
MatIconModule,
FxFlexDirective,
FxLayoutDirective,
FxLayoutGapDirective,
FxLayoutAlignDirective,
MeasurementComponent,
],
templateUrl: './ogr-dataset.component.html',
styleUrl: './ogr-dataset.component.css',
})
export class OgrDatasetComponent implements OnChanges {
export class OgrDatasetComponent implements OnChanges, OnInit {
vectorDataTypes = ['Data', 'MultiPoint', 'MultiLineString', 'MultiPolygon'];
timeDurationValueTypes = ['infinite', 'value', 'zero'];
timeTypes = ['None', 'Start', 'Start/End', 'Start/Duration'];
Expand All @@ -62,13 +66,17 @@ export class OgrDatasetComponent implements OnChanges {
@Input() volumeName?: string;
@Input() metaData?: OgrMetaData;

@ViewChild(MeasurementComponent) measurementComponent?: MeasurementComponent;

formMetaData: UntypedFormGroup;

uploadFiles?: Array<string>;
uploadFileLayers: Array<string> = [];

readonly defaultTimeGranularity: TimeGranularity = 'seconds';

columns: ColumnInfo = {};

constructor(
protected datasetsService: DatasetsService,
protected uploadsService: UploadsService,
Expand Down Expand Up @@ -101,6 +109,7 @@ export class OgrDatasetComponent implements OnChanges {
spatialReference: new UntypedFormControl('EPSG:4326', Validators.required), // TODO: validate sref string
});
}

ngOnChanges(changes: SimpleChanges): void {
if (changes.uploadId && changes.uploadId.currentValue) {
this.setUpMetadataSpecification(changes.uploadId.currentValue);
Expand All @@ -118,6 +127,13 @@ export class OgrDatasetComponent implements OnChanges {
}
}

ngOnInit() {
this.columns = this.getColumnsAsMap();
if (this.columns) {
this.setSelectedColumn(this.sortedColumns()[0].name);
}
}

changeTimeType(): void {
const form = this.formMetaData.controls;
const timeType = form.timeType.value;
Expand Down Expand Up @@ -427,42 +443,40 @@ export class OgrDatasetComponent implements OnChanges {
return undefined;
}

private getColumnsAsMap(): {[key: string]: VectorColumnInfo} {
private getColumnsAsMap(): ColumnInfo {
const formMeta = this.formMetaData.controls;
const columns: {[key: string]: VectorColumnInfo} = {};

for (const column of formMeta.columnsText.value as Array<string>) {
columns[column] = {
dataType: 'text',
measurement: {
// TODO: incorporate in selection
type: 'unitless',
},
};
const columns: ColumnInfo = {};

if (!this.metaData) {
return {};
}
for (const name of formMeta.columnsText.value as Array<string>) {
this.setColumnInfo(columns, name, 'text');
}

for (const column of formMeta.columnsInt.value as Array<string>) {
columns[column] = {
dataType: 'int',
measurement: {
// TODO: incorporate in selection
type: 'unitless',
},
};
this.setColumnInfo(columns, column, 'int');
}

for (const column of formMeta.columnsFloat.value as Array<string>) {
columns[column] = {
dataType: 'float',
measurement: {
// TODO: incorporate in selection
type: 'unitless',
},
};
this.setColumnInfo(columns, column, 'float');
}
return columns;
}

private setColumnInfo(columns: ColumnInfo, name: string, type_: 'float' | 'int' | 'text') {
const vectorInfo = this.metaData?.resultDescriptor.columns[name];
if (!vectorInfo) {
return;
}
columns[name] = {
name: name,
vectorColumnInfo: vectorInfo,
newMeasurement: vectorInfo.measurement,
dataType: type_,
};
}

private getDuration(): OgrSourceDurationSpec {
const formMeta = this.formMetaData.controls;

Expand Down Expand Up @@ -577,9 +591,21 @@ export class OgrDatasetComponent implements OnChanges {
return 'None';
}

columnInfoToVectorInfo() {
const keys = Object.keys(this.columns);
const ret: {[key: string]: VectorColumnInfo} = {};
for (const column of keys) {
// copy to avoid modification
const info = {...this.columns[column].vectorColumnInfo};
info['measurement'] = this.columns[column].newMeasurement;
ret[column] = info;
}
return ret;
}

getMetaData(): MetaDataDefinition {
const formMeta = this.formMetaData.controls;

const columns = this.columnInfoToVectorInfo();
return {
type: 'OgrMetaData',
loadingInfo: {
Expand All @@ -600,8 +626,56 @@ export class OgrDatasetComponent implements OnChanges {
resultDescriptor: {
dataType: formMeta.dataType.value,
spatialReference: formMeta.spatialReference.value,
columns: this.getColumnsAsMap(),
columns: columns,
},
};
}

_selectedColumn: string | undefined = undefined;
previousColumn = signal<string | undefined>(undefined);

selectedColumn() {
return this._selectedColumn;
}

sortedColumns() {
return Object.values(this.columns).sort((a, b) => a.name.localeCompare(b.name));
}

setSelectedColumn(name: string) {
this.previousColumn.set(this._selectedColumn);
this._selectedColumn = name;
}

newMeasurementForColumn(): Measurement {
const key = this.selectedColumn();
if (key) {
const searchResult = this.columns[key];
if (searchResult) {
return searchResult.newMeasurement;
}
}
return {type: 'unitless'};
}

onMeasurementChange(measurement: Measurement) {
const key = this.selectedColumn();
if (key) {
this.columns[key].newMeasurement = measurement;
}
this.markDirty();
}

markDirty() {
this.formMetaData.markAsDirty();
}

onColumnMatSelectChange(event: MatSelectChange) {
const measurement = this.measurementComponent?.measurement || {type: 'unitless'};
const prev = this.previousColumn();
if (prev) {
this.columns[prev].newMeasurement = measurement;
}
this.measurementComponent?.reset(); // reset cached components for new column
}
}
15 changes: 7 additions & 8 deletions projects/common/src/lib/measurement/measurement.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<div>
Output Measurement:
<mat-button-toggle-group
id="measurement-type"
[multiple]="false"
Expand All @@ -22,11 +21,11 @@
<div class="flex-container" *ngFor="let class of classificationMeasurement.classes | keyvalue">
<mat-form-field appearance="fill">
<mat-label>Class value</mat-label>
<input matInput type="number" required="true" [(ngModel)]="class.key" />
<input matInput type="number" required="true" [(ngModel)]="class.key" (input)="inputChange($event)" />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Class label</mat-label>
<input matInput type="text" required="true" [(ngModel)]="class.value" />
<input matInput type="text" required="true" [(ngModel)]="class.value" (input)="inputChange($event)" />
</mat-form-field>

<button mat-icon-button (click)="removeClass(class.key)">
Expand All @@ -38,11 +37,11 @@
<div class="flex-container">
<mat-form-field appearance="fill">
<mat-label>New class value</mat-label>
<input matInput type="number" formControlName="key" />
<input matInput type="number" formControlName="key" (input)="inputChange($event)" />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>New class label</mat-label>
<input matInput type="text" formControlName="value" />
<input matInput type="text" formControlName="value" (input)="inputChange($event)" />
</mat-form-field>

<button mat-icon-button [disabled]="!addClassForm.valid" (click)="addClass()">
Expand All @@ -52,15 +51,15 @@
</form>
}

@if (measurement.type === MeasurementType.Continuous && continousMeasurement) {
@if (measurement.type === MeasurementType.Continuous && continuousMeasurement) {
<div class="flex-container">
<mat-form-field appearance="fill">
<mat-label>Measurement</mat-label>
<input matInput type="text" required="true" [(ngModel)]="continousMeasurement.measurement" />
<input matInput type="text" required="true" [(ngModel)]="continuousMeasurement.measurement" (input)="inputChange($event)" />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Unit</mat-label>
<input matInput type="text" [(ngModel)]="continousMeasurement.unit" />
<input matInput type="text" [(ngModel)]="continuousMeasurement.unit" (input)="inputChange($event)" />
</mat-form-field>
</div>
}
Expand Down
Loading
Loading