Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
012ec41
feat: add dropdown menu for new roads thematic accuracy indicator
Gigaszi Jan 14, 2026
63491b5
refactor: improve variable names and fix tests
Gigaszi Jan 14, 2026
f32dfef
refactor: rename thematical-accuracy-indicator to land-cover-thematic…
Gigaszi Jan 14, 2026
a5fa4f8
fix: typo
Gigaszi Jan 14, 2026
576c1b5
i18n: translate new dropdown text
Gigaszi Jan 14, 2026
303b50d
i18n: translate attributes for dropdown options
Gigaszi Jan 14, 2026
555cce4
i18n: translate corine classes to german
Gigaszi Jan 14, 2026
54dd414
fix: dropdownmenu returns an empty string on clearing
Gigaszi Jan 15, 2026
90add8f
feat: show selected thematic attribute in result card
Gigaszi Jan 19, 2026
e87f310
feat: show "All Classes" if no class selected in result card von Land…
Gigaszi Jan 19, 2026
4891418
fix: use correct indicator name for tests
Gigaszi Jan 19, 2026
6025ce9
refactor: use one thematic accuracy indicator instead of two
Gigaszi Jan 20, 2026
76df573
fix: use correct label for each thematic accuracy indicator
Gigaszi Jan 20, 2026
37ea4d7
i18n: add internationalisation and german translation
Gigaszi Jan 20, 2026
caf9b5e
refactor: use @switch instead of ngswitch
Gigaszi Jan 20, 2026
2fb8586
fix: typo
Gigaszi Jan 22, 2026
7d3ba4e
feat: add link to indicator description page to oqapi indicators
Gigaszi Jan 22, 2026
d1027f1
chore: add changelog
Gigaszi Jan 22, 2026
293b94a
fix: revert change to environment.ts
Gigaszi Jan 22, 2026
abf98c2
fix: do not parse locale from JSON
Gigaszi Feb 12, 2026
3301239
Merge branch 'roads-thematic-accuracy' into more-indicator-info
Gigaszi Feb 12, 2026
5501f56
Merge pull request #82 from GIScience/more-indicator-info
Gigaszi Feb 12, 2026
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: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ Changelog

## current

* feat: add link to indicator description page to oqapi indicators
* feat: add option for new indicator roads-thematic-accuracy

## 1.8.1

* test: update ohsomeapi- and oqapi-metadata response mocks
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const category = $localize`high`;

To actually do the translations run `npm run extract-i18n`.

This will analyze the application and generate a source file with ids and original text snippets from html and typescipt code in one place.
This will analyze the application and generate a source file with ids and original text snippets from html and typescript code in one place.

`src/locale/messages-source.en.json`

Expand Down
6 changes: 3 additions & 3 deletions src/app/oqapi/oqt.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
import {PrismEditorComponent} from '../shared/components/prism-editor/prism-editor.component';
import {SuiMultiSelectSearchDropdownComponent} from '../shared/components/sui-dropdown/sui-multi-select-search-dropdown.component';
import {
ThematicalAccuracyIndicatorComponent
} from "./query-form/oqt-api-query-form/thematical-accuracy-indicator/thematical-accuracy-indicator.component";
ThematicAccuracyIndicatorComponent
} from "./query-form/oqt-api-query-form/thematic-accuracy-indicator/thematic-accuracy-indicator.component";


@NgModule({
Expand All @@ -23,9 +23,9 @@ import {
OqtApiQueryFormComponent,
SimpleIndicatorComponent,
AttributeCompletenessAttributesComponent,
ThematicalAccuracyIndicatorComponent,
OqtResultComponent,
IndicatorResultComponent,
ThematicAccuracyIndicatorComponent
],
providers: [
OqtApiMetadataProviderService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,18 @@ <h4 class="ui header" i18n><i class="chart line icon"></i>Quality Indicators</h4
}
@case ("land-cover-thematic-accuracy") {
@if (indicator.checked) {
<app-thematical-accuracy-indicator
<app-thematic-accuracy-indicator
[indicatorKey]="indicator.key"
[hashParams]="hashParams"
></app-thematical-accuracy-indicator>
></app-thematic-accuracy-indicator>
}
}
@case ("roads-thematic-accuracy") {
@if (indicator.checked) {
<app-thematic-accuracy-indicator
[indicatorKey]="indicator.key"
[hashParams]="hashParams"
></app-thematic-accuracy-indicator>
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { SuiMultiSelectSearchDropdownComponent } from '../../../shared/component
import { PrismEditorComponent } from '../../../shared/components/prism-editor/prism-editor.component';
import { SimpleIndicatorComponent } from './simple-indicator/simple-indicator.component';
import { AttributeCompletenessAttributesComponent } from './attribute-completeness-attributes/attribute-completeness-attributes.component';
import { ThematicalAccuracyIndicatorComponent } from './thematical-accuracy-indicator/thematical-accuracy-indicator.component';
import { ThematicAccuracyIndicatorComponent } from './thematic-accuracy-indicator/thematic-accuracy-indicator.component';
import { KeyValuePipe } from '@angular/common';

@Component({
selector: 'app-oqt-api-query-form',
templateUrl: './oqt-api-query-form.component.html',
styleUrls: ['./oqt-api-query-form.component.css'],
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
imports: [FormsModule, SuiMultiSelectSearchDropdownComponent, PrismEditorComponent, SimpleIndicatorComponent, AttributeCompletenessAttributesComponent, ThematicalAccuracyIndicatorComponent, KeyValuePipe]
imports: [FormsModule, SuiMultiSelectSearchDropdownComponent, PrismEditorComponent, SimpleIndicatorComponent, AttributeCompletenessAttributesComponent, KeyValuePipe, ThematicAccuracyIndicatorComponent]
})
export class OqtApiQueryFormComponent implements OnInit, OnDestroy {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<label for="{{indicator.key}}">
<div class="content">
<a class="header">{{indicator.name}}</a>
<div [innerHTML]="indicator.description" class="description"></div>
<div [innerHTML]=getDescriptionWithLink() class="description"></div>
</div>
</label>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ export class SimpleIndicatorComponent {
@Input() indicator!: Checkbox<Indicator>;
@Input() qualityDimension!: string;
@Output() indicatorToggle: EventEmitter<{indicator: Indicator, state: boolean}> = new EventEmitter<{indicator: Indicator, state: boolean}>();

getDescriptionWithLink(): string {
const locale = localStorage.getItem("locale") || "en";
const link_text = $localize`Click here for more info.`
return `${this.indicator.description} <br> <a target="_blank" href="https://github.com/GIScience/ohsome-quality-api/blob/main/docs/indicators_${locale}.md">${link_text}</a>`;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@switch (indicatorKey) {
@case ("land-cover-thematic-accuracy") {
<h5 style="margin-top: 0.8rem" i18n>Select a specific land cover class to check. Leave empty for all classes:</h5>
}
@case ("roads-thematic-accuracy") {
<h5 style="margin-top: 0.8rem" i18n>Select a attribute to check. Leave empty for all attributes:</h5>
}
}

<div class="field">
<app-sui-multi-select-search-dropdown
id="search-select-thematic-accuracy"
name="{{indicatorKey}}--{{categoryType}}"
[(ngModel)]="selectedCategoryIds"
[options]="thematicDropdownOptions"
[selectOptions]="this.categories | keyvalue">
</app-sui-multi-select-search-dropdown>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {ThematicAccuracyIndicatorComponent} from './thematic-accuracy-indicator.component';
import {OqtModule} from '../../../oqt.module';
import {OqtApiMetadataProviderService} from '../../../oqt-api-metadata-provider.service';
import OqtApiMetadataProviderServiceMock from '../../../oqt-api-metadata-provider.service.mock';
import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {NgForm} from '@angular/forms';
import { provideAppInitializer } from '@angular/core';
import {preparePrismToRenderOhsomeFilterLangauge} from '../../../../../main';
import {PrismEditorComponent} from '../../../../shared/components/prism-editor/prism-editor.component';


describe('ThematicAccuracyIndicatorComponent', () => {
let component: ThematicAccuracyIndicatorComponent;
let fixture: ComponentFixture<ThematicAccuracyIndicatorComponent>;

describe('with land-cover-thematic-accuracy indicator', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [OqtModule, PrismEditorComponent],
providers: [
NgForm,
{provide: OqtApiMetadataProviderService, useValue: OqtApiMetadataProviderServiceMock},
provideAppInitializer(() => {
const initializerFn = (preparePrismToRenderOhsomeFilterLangauge)();
return initializerFn();
}),
provideHttpClient(withInterceptorsFromDi())
]
})
.compileComponents();

fixture = TestBed.createComponent(ThematicAccuracyIndicatorComponent);
component = fixture.componentInstance;
component.indicatorKey = "land-cover-thematic-accuracy";
component.hashParams = new URLSearchParams("land-cover-thematic-accuracy--corine_land_cover_class=11");
component.selectedCategoryIds = '11';
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});


describe('getThematicAccuracyFromUrlHashParams', () => {
const hashParamsCases = [
{
description: 'Valid corine land cover class',
topicKey: 'land-cover',
hashParams: new URLSearchParams('land-cover-thematic-accuracy--corine_land_cover_class=11'),
expected: '11'
},
{
description: 'Invalid corine land cover class',
topicKey: 'land-cover',
hashParams: new URLSearchParams('land-cover-thematic-accuracy--corine_land_cover_class=19'),
expected: ''
},
{
description: 'No corine land cover class selected',
topicKey: 'land-cover',
hashParams: new URLSearchParams('land-cover-thematic-accuracy--corine_land_cover_class='),
expected: ''
}
];

hashParamsCases.forEach((hashParamsCase) => {
it(hashParamsCase.description, () => {
const result = component.getThematicCategoryFromUrlHashParams(hashParamsCase.hashParams);
expect(result).toEqual(hashParamsCase.expected);
})
})
});
});


describe('with roads-thematic-accuracy indicator', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [OqtModule, PrismEditorComponent],
providers: [
NgForm,
{provide: OqtApiMetadataProviderService, useValue: OqtApiMetadataProviderServiceMock},
provideAppInitializer(() => {
const initializerFn = (preparePrismToRenderOhsomeFilterLangauge)();
return initializerFn();
}),
provideHttpClient(withInterceptorsFromDi())
]
})
.compileComponents();

fixture = TestBed.createComponent(ThematicAccuracyIndicatorComponent);
component = fixture.componentInstance;
component.indicatorKey = "roads-thematic-accuracy";
component.hashParams = new URLSearchParams("roads-thematic-accuracy--attribute=surface");
component.selectedCategoryIds = 'surface';
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});


describe('getCorineLandCoverClassFromUrlHashParams', () => {
const hashParamsCases = [
{
description: 'Valid thematic attribute',
topicKey: 'roads',
hashParams: new URLSearchParams('roads-thematic-accuracy--attribute=surface'),
expected: 'surface'
},
{
description: 'Invalid thematic attribute',
topicKey: 'roads',
hashParams: new URLSearchParams('roads-thematic-accuracy--attribute=foo'),
expected: ''
},
{
description: 'No thematic attribute selected',
topicKey: 'roads',
hashParams: new URLSearchParams('roads-thematic-accuracy--attribute='),
expected: ''
}
];

hashParamsCases.forEach((hashParamsCase) => {
it(hashParamsCase.description, () => {
const result = component.getThematicCategoryFromUrlHashParams(hashParamsCase.hashParams);
expect(result).toEqual(hashParamsCase.expected);
})
})
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
Component,
Input,
OnInit,
} from '@angular/core';
import { categoryRegistry, thematicCategoryType } from './thematic-accuracy-indicator.constants';
import { ControlContainer, NgForm, FormsModule } from '@angular/forms';
import { SuiMultiSelectSearchDropdownComponent } from '../../../../shared/components/sui-dropdown/sui-multi-select-search-dropdown.component';
import {KeyValuePipe} from '@angular/common';


@Component({
selector: 'app-thematic-accuracy-indicator',
templateUrl: './thematic-accuracy-indicator.component.html',
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
imports: [
SuiMultiSelectSearchDropdownComponent,
FormsModule,
KeyValuePipe,
],
})
export class ThematicAccuracyIndicatorComponent implements OnInit {
@Input({required: true}) indicatorKey!: string;
@Input() selectOptions!: { label: string; value: number }[];
@Input() hashParams!: URLSearchParams;

selectedCategoryIds: string = "";

categories: Record<
string,
never
>;

categoryType: string;

thematicDropdownOptions: object;

getThematicCategoryFromUrlHashParams(hashParams: URLSearchParams): string {

console.log("hashParams", hashParams);

// 1. extract the category from URL
const thematicCategoryFromUrl: string | null = hashParams.get(this.indicatorKey + '--' + this.categoryType);

// 2.undefined should return an empty string
if (thematicCategoryFromUrl == null) {
return "";
}
// 3. if the category is not in the map, return an empty string
if (!Object.keys(this.categories).includes(thematicCategoryFromUrl)) {
return "";
}

return thematicCategoryFromUrl;
}
ngOnInit() {
this.selectedCategoryIds = this.getThematicCategoryFromUrlHashParams(this.hashParams);
this.categories = categoryRegistry[this.indicatorKey]
this.categoryType = thematicCategoryType[this.indicatorKey];

this.thematicDropdownOptions = {
fullTextSearch: 'exact',
clearable: true,
};
console.log(this.indicatorKey + '--' + this.categoryType);
console.log(this.categoryType);
}

protected readonly thematicCategoryType = thematicCategoryType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const corineLandCoverClassMapLevel2: Record<
string,
{ name: string; class: number }
> = {
"11": { name: $localize`Urban fabric`, class: 1 },
"12": { name: $localize`Industrial, commercial and transport units`, class: 1 },
"13": { name: $localize`Mine, dump and construction sites`, class: 1 },
"14": { name: $localize`Artificial non-agricultural vegetated areas`, class: 1 },
"21": { name: $localize`Arable land`, class: 2 },
"22": { name: $localize`Permanent crops`, class: 2 },
"23": { name: $localize`Pastures`, class: 2 },
"24": { name: $localize`Heterogeneous agricultural areas`, class: 2 },
"31": { name: $localize`Forest`, class: 3 },
"32": { name: $localize`Shrubs and/or herbaceous vegetation associations`, class: 3 },
"33": { name: $localize`Open spaces with little or no vegetation`, class: 3 },
"41": { name: $localize`Inland wetlands`, class: 4 },
"42": { name: $localize`Coastal wetlands`, class: 4 },
"51": { name: $localize`Inland waters`, class: 5 },
"52": { name: $localize`Marine waters`, class: 5 },
};

export const thematicAttributeMap: Record<
string,
{ name: string}
> = {
"surface": { name: $localize`Surface`},
"oneway": { name: $localize`Oneway`},
"lanes": { name: $localize`Lanes`},
"name": { name: $localize`Name`},
"width": { name: $localize`Width`},
};

export const categoryRegistry = {
"land-cover-thematic-accuracy": corineLandCoverClassMapLevel2,
"roads-thematic-accuracy" : thematicAttributeMap
} as const;

export const thematicCategoryType = {
"land-cover-thematic-accuracy" : "corine_land_cover_class",
"roads-thematic-accuracy" : "attribute"
}

export const thematicAccuracyCategoryNamesForBlank = {
"land-cover-thematic-accuracy" : $localize`All Classes`,
"roads-thematic-accuracy" : $localize`All Attributes`
}

This file was deleted.

Loading