geonetwork-ui 2.11.0-dev.bc1a6d4e2 → 2.11.0-dev.bc1acb627
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/geonetwork-ui.mjs +1446 -1300
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +170 -100
- package/index.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/mel.records.dataset+featurecatalog.ts +317 -0
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +35 -8
- package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +19 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +98 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +7 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +2 -1
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +90 -31
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +5 -34
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +6 -13
- package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
- package/src/libs/common/domain/src/lib/model/search/filter.model.ts +12 -2
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +1 -1
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +32 -19
- package/src/libs/feature/map/src/index.ts +0 -2
- package/src/libs/feature/map/src/lib/feature-map.module.ts +1 -8
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +3 -1
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
- package/src/libs/feature/search/src/index.ts +2 -0
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +8 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +26 -4
- package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +105 -0
- package/src/libs/feature/search/src/lib/location-search/location-search.component.html +18 -0
- package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +114 -0
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +2 -0
- package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +62 -4
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +67 -9
- package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +2 -2
- package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
- package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.ts +2 -1
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +12 -3
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +11 -3
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +7 -4
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +12 -9
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.model.ts +2 -2
- package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +8 -2
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +34 -5
- package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
- package/src/libs/util/app-config/src/lib/app-config.ts +34 -0
- package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
- package/src/libs/util/app-config/src/lib/model.ts +24 -1
- package/src/libs/util/shared/src/lib/links/link-utils.ts +19 -10
- package/src/libs/util/shared/src/lib/utils/geojson.ts +16 -0
- package/translations/de.json +10 -2
- package/translations/en.json +12 -4
- package/translations/es.json +10 -2
- package/translations/fr.json +17 -9
- package/translations/it.json +10 -2
- package/translations/nl.json +10 -2
- package/translations/pt.json +10 -2
- package/translations/sk.json +10 -2
- package/src/libs/feature/map/src/lib/geocoding/geocoding.component.css +0 -0
- package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +0 -41
- package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +0 -112
- package/src/libs/feature/map/src/lib/geocoding.service.ts +0 -57
- package/src/libs/feature/map/src/lib/style/index.ts +0 -2
- package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
- package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
OnInit,
|
|
12
12
|
Output,
|
|
13
13
|
SimpleChanges,
|
|
14
|
+
TemplateRef,
|
|
14
15
|
ViewChild,
|
|
15
16
|
inject,
|
|
16
17
|
} from '@angular/core'
|
|
@@ -97,6 +98,13 @@ export class AutocompleteComponent
|
|
|
97
98
|
@ViewChild(MatAutocompleteTrigger) triggerRef: MatAutocompleteTrigger
|
|
98
99
|
@ViewChild(MatAutocomplete) autocomplete: MatAutocomplete
|
|
99
100
|
@ViewChild('searchInput') inputRef: ElementRef<HTMLInputElement>
|
|
101
|
+
/**
|
|
102
|
+
* Custom template to render each suggestion. Takes precedence over
|
|
103
|
+
* `displayWithFn` when set.
|
|
104
|
+
*/
|
|
105
|
+
@Input() displayWithTemplate?: TemplateRef<{
|
|
106
|
+
$implicit: AutocompleteItem
|
|
107
|
+
}>
|
|
100
108
|
|
|
101
109
|
searching: boolean
|
|
102
110
|
control = new UntypedFormControl()
|
|
@@ -126,13 +134,13 @@ export class AutocompleteComponent
|
|
|
126
134
|
getExtraClass(): string {
|
|
127
135
|
if (this.allowSubmit) {
|
|
128
136
|
if (this.enterButton) {
|
|
129
|
-
return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+var(--icon-padding))]
|
|
137
|
+
return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+var(--icon-padding))] top-1/2 -translate-y-1/2'
|
|
130
138
|
} else {
|
|
131
|
-
return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+0.25*var(--icon-width))]
|
|
139
|
+
return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+0.25*var(--icon-width))] top-1/2 -translate-y-1/2'
|
|
132
140
|
}
|
|
133
141
|
} else {
|
|
134
142
|
if (!this.enterButton) {
|
|
135
|
-
return 'border rounded-lg absolute w-8 h-8 right-2
|
|
143
|
+
return 'border rounded-lg absolute w-8 h-8 right-2 top-1/2 -translate-y-1/2'
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
return 'border rounded-lg absolute w-8 h-8'
|
|
@@ -20,10 +20,13 @@
|
|
|
20
20
|
</div>
|
|
21
21
|
}
|
|
22
22
|
</div>
|
|
23
|
-
<button
|
|
23
|
+
<button
|
|
24
|
+
class="h-6 w-6 flex items-center justify-center"
|
|
25
|
+
data-test="dropdown-clear"
|
|
26
|
+
>
|
|
24
27
|
@if (hasSelectedChoices && !overlayOpen) {
|
|
25
28
|
<ng-icon
|
|
26
|
-
class="shrink-0 opacity-40
|
|
29
|
+
class="shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn"
|
|
27
30
|
(click)="clearSelection($event)"
|
|
28
31
|
name="matClose"
|
|
29
32
|
></ng-icon>
|
|
@@ -61,13 +64,13 @@
|
|
|
61
64
|
#overlayContainer
|
|
62
65
|
>
|
|
63
66
|
<div
|
|
64
|
-
class="border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-
|
|
67
|
+
class="border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-1 flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary"
|
|
65
68
|
>
|
|
66
69
|
@for (selected of selectedChoices; track selected) {
|
|
67
70
|
<button
|
|
68
71
|
type="button"
|
|
69
72
|
[title]="selected.label"
|
|
70
|
-
class="max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity
|
|
73
|
+
class="max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity"
|
|
71
74
|
(click)="select(selected, false)"
|
|
72
75
|
>
|
|
73
76
|
<div class="text-sm truncate leading-[26px] px-2">
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
propagateToDocumentOnly,
|
|
25
25
|
} from '../../../../../../libs/util/shared/src'
|
|
26
26
|
import { ButtonComponent } from '../button/button.component'
|
|
27
|
-
import { NgIcon, provideIcons } from '@ng-icons/core'
|
|
27
|
+
import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
|
|
28
28
|
import { FormsModule } from '@angular/forms'
|
|
29
29
|
import { TranslatePipe } from '@ngx-translate/core'
|
|
30
30
|
|
|
@@ -46,19 +46,22 @@ import {
|
|
|
46
46
|
matExpandMore,
|
|
47
47
|
matExpandLess,
|
|
48
48
|
}),
|
|
49
|
+
provideNgIconsConfig({
|
|
50
|
+
size: '1.5rem',
|
|
51
|
+
}),
|
|
49
52
|
],
|
|
50
53
|
standalone: true,
|
|
51
54
|
})
|
|
52
|
-
export class DropdownMultiselectComponent {
|
|
55
|
+
export class DropdownMultiselectComponent<T = unknown> {
|
|
53
56
|
private scrollStrategies = inject(ScrollStrategyOptions)
|
|
54
57
|
|
|
55
58
|
@Input() title: string
|
|
56
|
-
@Input() choices: Choice[]
|
|
57
|
-
@Input() selected:
|
|
59
|
+
@Input() choices: Choice<T>[]
|
|
60
|
+
@Input() selected: T[] = []
|
|
58
61
|
@Input() allowSearch = true
|
|
59
62
|
@Input() maxRows: number
|
|
60
63
|
@Input() searchInputValue = ''
|
|
61
|
-
@Output() selectValues = new EventEmitter<
|
|
64
|
+
@Output() selectValues = new EventEmitter<T[]>()
|
|
62
65
|
@ViewChild('overlayOrigin') overlayOrigin: CdkOverlayOrigin
|
|
63
66
|
@ViewChild(CdkConnectedOverlay) overlay: CdkConnectedOverlay
|
|
64
67
|
@ViewChild('overlayContainer', { read: ElementRef })
|
|
@@ -115,7 +118,7 @@ export class DropdownMultiselectComponent {
|
|
|
115
118
|
|
|
116
119
|
private setFocus() {
|
|
117
120
|
setTimeout(() => {
|
|
118
|
-
this.searchFieldInput
|
|
121
|
+
this.searchFieldInput?.nativeElement.focus()
|
|
119
122
|
}, 0)
|
|
120
123
|
}
|
|
121
124
|
|
|
@@ -194,18 +197,18 @@ export class DropdownMultiselectComponent {
|
|
|
194
197
|
this.checkboxes.get(newIndex).nativeElement.focus()
|
|
195
198
|
}
|
|
196
199
|
|
|
197
|
-
isSelected(choice: Choice) {
|
|
200
|
+
isSelected(choice: Choice<T>) {
|
|
198
201
|
return this.selected.indexOf(choice.value) > -1
|
|
199
202
|
}
|
|
200
203
|
|
|
201
|
-
select(choice: Choice
|
|
204
|
+
select(choice: Choice<T>, selected: boolean) {
|
|
202
205
|
this.selected = selected
|
|
203
206
|
? [...this.selected.filter((v) => v !== choice.value), choice.value]
|
|
204
207
|
: this.selected.filter((v) => v !== choice.value)
|
|
205
208
|
this.selectValues.emit(this.selected)
|
|
206
209
|
}
|
|
207
210
|
|
|
208
|
-
toggle(choice: Choice) {
|
|
211
|
+
toggle(choice: Choice<T>) {
|
|
209
212
|
this.select(choice, !this.isSelected(choice))
|
|
210
213
|
}
|
|
211
214
|
|
package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts
CHANGED
|
@@ -56,7 +56,8 @@ export class SearchFeatureCatalogComponent {
|
|
|
56
56
|
const fuzzyFilter = createFuzzyFilter(searchTerm)
|
|
57
57
|
return (
|
|
58
58
|
fuzzyFilter(featureType.name) ||
|
|
59
|
-
(featureType.
|
|
59
|
+
(featureType.description &&
|
|
60
|
+
fuzzyFilter(featureType.description))
|
|
60
61
|
)
|
|
61
62
|
}
|
|
62
63
|
)
|
package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html
CHANGED
|
@@ -58,7 +58,12 @@
|
|
|
58
58
|
[class]="
|
|
59
59
|
(hasSelection
|
|
60
60
|
? 'border-b-[1px] border-gray-400 pb-[20px]'
|
|
61
|
-
: 'pb-[
|
|
61
|
+
: 'pb-[12px]') + ' text-black flex flex-col gap-[8px]'
|
|
62
|
+
"
|
|
63
|
+
style="
|
|
64
|
+
--gn-ui-text-input-rounded: 8px;
|
|
65
|
+
--gn-ui-text-input-border-size: 1px;
|
|
66
|
+
--gn-ui-text-input-padding: 6px;
|
|
62
67
|
"
|
|
63
68
|
>
|
|
64
69
|
<gn-ui-drag-and-drop-file-input
|
|
@@ -68,12 +73,13 @@
|
|
|
68
73
|
[showFileName]="false"
|
|
69
74
|
textClass="text-black truncate"
|
|
70
75
|
dropzoneBackgroundColor="transparent"
|
|
71
|
-
icon="
|
|
76
|
+
icon="iconoirUpload"
|
|
72
77
|
extraClass="gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between"
|
|
73
78
|
(fileChange)="handleFileSelected($event)"
|
|
74
79
|
(errorChange)="handleFileError($event)"
|
|
75
80
|
data-cy="importGeojson"
|
|
76
81
|
></gn-ui-drag-and-drop-file-input>
|
|
82
|
+
<ng-content></ng-content>
|
|
77
83
|
</div>
|
|
78
84
|
|
|
79
85
|
@if (hasSelection) {
|
package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts
CHANGED
|
@@ -20,9 +20,9 @@ import { ButtonComponent } from '../button/button.component'
|
|
|
20
20
|
import { NgIcon, provideIcons } from '@ng-icons/core'
|
|
21
21
|
import {
|
|
22
22
|
iconoirCheckCircle,
|
|
23
|
-
iconoirImport,
|
|
24
23
|
iconoirSquareDashed,
|
|
25
24
|
iconoirTrash,
|
|
25
|
+
iconoirUpload,
|
|
26
26
|
} from '@ng-icons/iconoir'
|
|
27
27
|
import {
|
|
28
28
|
matClose,
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
BoundingBox,
|
|
36
36
|
getGeometryBoundingBox,
|
|
37
37
|
getGeometryFromGeoJSON,
|
|
38
|
+
isBoundingBoxWithinWorldExtent,
|
|
38
39
|
propagateToDocumentOnly,
|
|
39
40
|
readFileAsText,
|
|
40
41
|
} from '../../../../../../libs/util/shared/src'
|
|
@@ -46,11 +47,18 @@ import {
|
|
|
46
47
|
marker('search.filters.spatialExtent.import')
|
|
47
48
|
marker('search.filters.spatialExtent.helpText')
|
|
48
49
|
marker('search.filters.spatialExtent.error.title')
|
|
50
|
+
marker('search.filters.spatialExtent.error.outOfBounds')
|
|
49
51
|
|
|
50
52
|
const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile')
|
|
51
53
|
const LABEL_FROM_FILE_DELETE = marker(
|
|
52
54
|
'search.filters.spatialExtent.bboxFromFileDelete'
|
|
53
55
|
)
|
|
56
|
+
const LABEL_FROM_LOCATION = marker(
|
|
57
|
+
'search.filters.spatialExtent.bboxFromLocation'
|
|
58
|
+
)
|
|
59
|
+
const LABEL_FROM_LOCATION_DELETE = marker(
|
|
60
|
+
'search.filters.spatialExtent.bboxFromLocationDelete'
|
|
61
|
+
)
|
|
54
62
|
const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial')
|
|
55
63
|
const LABEL_INITIAL_DELETE = marker(
|
|
56
64
|
'search.filters.spatialExtent.bboxInitialDelete'
|
|
@@ -74,9 +82,9 @@ export interface SpatialExtentDropdownError {
|
|
|
74
82
|
providers: [
|
|
75
83
|
provideIcons({
|
|
76
84
|
iconoirCheckCircle,
|
|
77
|
-
iconoirImport,
|
|
78
85
|
iconoirSquareDashed,
|
|
79
86
|
iconoirTrash,
|
|
87
|
+
iconoirUpload,
|
|
80
88
|
matClose,
|
|
81
89
|
matExpandLess,
|
|
82
90
|
matExpandMore,
|
|
@@ -103,6 +111,7 @@ export class SpatialExtentDropdownComponent {
|
|
|
103
111
|
|
|
104
112
|
bbox: BoundingBox | null = null
|
|
105
113
|
fileName = ''
|
|
114
|
+
locationName = ''
|
|
106
115
|
|
|
107
116
|
@ViewChild('overlayOrigin') overlayOrigin: CdkOverlayOrigin
|
|
108
117
|
@ViewChild(CdkConnectedOverlay) overlay: CdkConnectedOverlay
|
|
@@ -136,15 +145,19 @@ export class SpatialExtentDropdownComponent {
|
|
|
136
145
|
}
|
|
137
146
|
|
|
138
147
|
get selectionLabelKey() {
|
|
139
|
-
|
|
148
|
+
if (this.fileName) return LABEL_FROM_FILE
|
|
149
|
+
if (this.locationName) return LABEL_FROM_LOCATION
|
|
150
|
+
return LABEL_INITIAL
|
|
140
151
|
}
|
|
141
152
|
|
|
142
153
|
get selectionDeleteLabelKey() {
|
|
143
|
-
|
|
154
|
+
if (this.fileName) return LABEL_FROM_FILE_DELETE
|
|
155
|
+
if (this.locationName) return LABEL_FROM_LOCATION_DELETE
|
|
156
|
+
return LABEL_INITIAL_DELETE
|
|
144
157
|
}
|
|
145
158
|
|
|
146
159
|
get selectionLabelParams() {
|
|
147
|
-
return { fileName: this.fileName }
|
|
160
|
+
return { fileName: this.fileName, locationName: this.locationName }
|
|
148
161
|
}
|
|
149
162
|
|
|
150
163
|
openOverlay() {
|
|
@@ -178,8 +191,13 @@ export class SpatialExtentDropdownComponent {
|
|
|
178
191
|
return
|
|
179
192
|
}
|
|
180
193
|
const bbox = getGeometryBoundingBox(geometry)
|
|
194
|
+
if (!isBoundingBoxWithinWorldExtent(bbox)) {
|
|
195
|
+
this.setError(marker('search.filters.spatialExtent.error.outOfBounds'))
|
|
196
|
+
return
|
|
197
|
+
}
|
|
181
198
|
this.bbox = bbox
|
|
182
199
|
this.fileName = file.name
|
|
200
|
+
this.locationName = ''
|
|
183
201
|
this.bboxChange.emit(bbox)
|
|
184
202
|
this.cd.markForCheck()
|
|
185
203
|
} catch {
|
|
@@ -188,6 +206,16 @@ export class SpatialExtentDropdownComponent {
|
|
|
188
206
|
}
|
|
189
207
|
}
|
|
190
208
|
|
|
209
|
+
setLocationBbox(bbox: BoundingBox, name: string) {
|
|
210
|
+
this.errorKey = null
|
|
211
|
+
this.bbox = bbox
|
|
212
|
+
this.locationName = name
|
|
213
|
+
this.fileName = ''
|
|
214
|
+
this.fileInput?.clear()
|
|
215
|
+
this.bboxChange.emit(bbox)
|
|
216
|
+
this.cd.markForCheck()
|
|
217
|
+
}
|
|
218
|
+
|
|
191
219
|
handleFileError(error: DragAndDropFileInputError) {
|
|
192
220
|
if (error === 'file-too-large') {
|
|
193
221
|
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
@@ -207,6 +235,7 @@ export class SpatialExtentDropdownComponent {
|
|
|
207
235
|
removeSelection(event: Event) {
|
|
208
236
|
this.bbox = null
|
|
209
237
|
this.fileName = ''
|
|
238
|
+
this.locationName = ''
|
|
210
239
|
this.errorKey = null
|
|
211
240
|
this.fileInput?.clear()
|
|
212
241
|
this.bboxChange.emit(null)
|
|
@@ -253,6 +253,9 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
253
253
|
'advanced_filters',
|
|
254
254
|
'limit',
|
|
255
255
|
'spatial_extent_max_file_size',
|
|
256
|
+
'geocoding_provider',
|
|
257
|
+
'geocoding_provider_options',
|
|
258
|
+
'geocoding_provider_labels',
|
|
256
259
|
],
|
|
257
260
|
warnings,
|
|
258
261
|
errors
|
|
@@ -265,6 +268,18 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
265
268
|
warnings,
|
|
266
269
|
errors
|
|
267
270
|
)
|
|
271
|
+
const parsedCustomFilters = parseMultiConfigSection(
|
|
272
|
+
parsed,
|
|
273
|
+
'custom_filter',
|
|
274
|
+
['name', 'base_filter'],
|
|
275
|
+
['exclude_values', 'include_values', 'label_key'],
|
|
276
|
+
warnings,
|
|
277
|
+
errors
|
|
278
|
+
)
|
|
279
|
+
const parsedGeocodingProviderLabels =
|
|
280
|
+
parsedSearchSection?.geocoding_provider_labels as
|
|
281
|
+
| Record<string, string>
|
|
282
|
+
| undefined
|
|
268
283
|
searchConfig =
|
|
269
284
|
parsedSearchSection === null
|
|
270
285
|
? null
|
|
@@ -281,9 +296,28 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
281
296
|
filters: param.filters,
|
|
282
297
|
})),
|
|
283
298
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
299
|
+
CUSTOM_FILTERS: (parsedCustomFilters ?? []).map(
|
|
300
|
+
(customFilter) => ({
|
|
301
|
+
name: customFilter.name,
|
|
302
|
+
baseFilter: customFilter.base_filter,
|
|
303
|
+
excludeValues: customFilter.exclude_values,
|
|
304
|
+
includeValues: customFilter.include_values,
|
|
305
|
+
labelKey: customFilter.label_key,
|
|
306
|
+
})
|
|
307
|
+
),
|
|
284
308
|
LIMIT: parsedSearchSection.limit,
|
|
285
309
|
SPATIAL_EXTENT_MAX_FILE_SIZE:
|
|
286
310
|
parsedSearchSection.spatial_extent_max_file_size,
|
|
311
|
+
GEOCODING_PROVIDER: parsedSearchSection.geocoding_provider,
|
|
312
|
+
GEOCODING_PROVIDER_OPTIONS:
|
|
313
|
+
parsedSearchSection.geocoding_provider_options,
|
|
314
|
+
GEOCODING_PROVIDER_LABELS: parsedGeocodingProviderLabels
|
|
315
|
+
? {
|
|
316
|
+
MAIN: parsedGeocodingProviderLabels.main,
|
|
317
|
+
SECONDARY: parsedGeocodingProviderLabels.secondary,
|
|
318
|
+
TERTIARY: parsedGeocodingProviderLabels.tertiary,
|
|
319
|
+
}
|
|
320
|
+
: undefined,
|
|
287
321
|
} as SearchConfig)
|
|
288
322
|
|
|
289
323
|
const parsedMetadataQualitySection = parseConfigSection(
|
|
@@ -41,7 +41,26 @@ fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
|
|
|
41
41
|
record_kind_quick_filter = false
|
|
42
42
|
filter_geometry_url = 'https://my.domain.org/geom.json'
|
|
43
43
|
do_not_use_default_search_preset = false
|
|
44
|
-
|
|
44
|
+
geocoding_provider = "geoplateforme"
|
|
45
|
+
geocoding_provider_options.index = "poi"
|
|
46
|
+
geocoding_provider_options.category = "administratif"
|
|
47
|
+
geocoding_provider_options.limit = 5
|
|
48
|
+
geocoding_provider_labels.main = '/properties/name/0'
|
|
49
|
+
geocoding_provider_labels.secondary = '/properties/citycode/0'
|
|
50
|
+
geocoding_provider_labels.tertiary = '/properties/category/1'
|
|
51
|
+
advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license', 'myOrg:firstCustomFilter', 'myOrg:secondCustomFilter']
|
|
52
|
+
|
|
53
|
+
[[custom_filter]]
|
|
54
|
+
name = 'myOrg:firstCustomFilter'
|
|
55
|
+
base_filter = 'keyword'
|
|
56
|
+
exclude_values = ['my keyword 1', 'my keyword 2']
|
|
57
|
+
label_key = 'myOrg.firstCustomFilter'
|
|
58
|
+
|
|
59
|
+
[[custom_filter]]
|
|
60
|
+
name = 'myOrg:secondCustomFilter'
|
|
61
|
+
base_filter = 'keyword'
|
|
62
|
+
include_values = ['my keyword 3', 'my keyword 4']
|
|
63
|
+
label_key = 'myOrg.secondCustomFilter'
|
|
45
64
|
|
|
46
65
|
[[search_preset]]
|
|
47
66
|
sort = "-createDate"
|
|
@@ -81,6 +100,23 @@ main_color = "#212029" # All-purpose text color
|
|
|
81
100
|
background_color = "#fdfbff"
|
|
82
101
|
`
|
|
83
102
|
|
|
103
|
+
export const customFilterMissingMandatoryConfigFixture = () => `
|
|
104
|
+
[global]
|
|
105
|
+
geonetwork4_api_url = "/geonetwork/srv/api"
|
|
106
|
+
|
|
107
|
+
[theme]
|
|
108
|
+
primary_color = "#093564"
|
|
109
|
+
secondary_color = "#c2e9dc"
|
|
110
|
+
main_color = "#212029" # All-purpose text color
|
|
111
|
+
background_color = "#fdfbff"
|
|
112
|
+
|
|
113
|
+
[search]
|
|
114
|
+
advanced_filters = ['myOrg:firstCustomFilter']
|
|
115
|
+
|
|
116
|
+
[[custom_filter]]
|
|
117
|
+
name = 'myOrg:firstCustomFilter'
|
|
118
|
+
`
|
|
119
|
+
|
|
84
120
|
export const mapConfigFixture = (): MapConfig => ({
|
|
85
121
|
MAX_ZOOM: 10,
|
|
86
122
|
MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
|
|
@@ -55,15 +55,38 @@ export interface SearchPreset {
|
|
|
55
55
|
filters: Record<string, string[] | string>
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export type GeocodingProviderName = 'geoadmin' | 'geonames' | 'geoplateforme'
|
|
59
|
+
|
|
60
|
+
export interface CustomSearchFieldConfig {
|
|
61
|
+
name: string
|
|
62
|
+
baseFilter: string
|
|
63
|
+
excludeValues?: string[]
|
|
64
|
+
includeValues?: string[]
|
|
65
|
+
labelKey?: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface GeocodingProviderLabelsConfig {
|
|
69
|
+
MAIN?: string
|
|
70
|
+
SECONDARY?: string
|
|
71
|
+
TERTIARY?: string
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
export interface SearchConfig {
|
|
59
75
|
RECORD_KIND_QUICK_FILTER?: boolean
|
|
60
76
|
FILTER_GEOMETRY_URL?: string
|
|
61
77
|
FILTER_GEOMETRY_DATA?: string
|
|
62
78
|
DO_NOT_USE_DEFAULT_SEARCH_PRESET?: boolean
|
|
63
79
|
SEARCH_PRESET?: SearchPreset[]
|
|
64
|
-
ADVANCED_FILTERS?: []
|
|
80
|
+
ADVANCED_FILTERS?: string[]
|
|
81
|
+
CUSTOM_FILTERS?: CustomSearchFieldConfig[]
|
|
65
82
|
LIMIT?: number
|
|
66
83
|
SPATIAL_EXTENT_MAX_FILE_SIZE?: number
|
|
84
|
+
GEOCODING_PROVIDER?: GeocodingProviderName
|
|
85
|
+
GEOCODING_PROVIDER_OPTIONS?: Record<
|
|
86
|
+
string,
|
|
87
|
+
string | number | boolean | string[]
|
|
88
|
+
>
|
|
89
|
+
GEOCODING_PROVIDER_LABELS?: GeocodingProviderLabelsConfig
|
|
67
90
|
}
|
|
68
91
|
|
|
69
92
|
export interface MetadataQualityConfig {
|
|
@@ -344,7 +344,11 @@ export function getLinkLabel(
|
|
|
344
344
|
return format ? `${label} (${format})` : label
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
export async function getLayers(
|
|
347
|
+
export async function getLayers(
|
|
348
|
+
url: string,
|
|
349
|
+
serviceProtocol: ServiceProtocol,
|
|
350
|
+
deep = false
|
|
351
|
+
) {
|
|
348
352
|
switch (serviceProtocol) {
|
|
349
353
|
case 'ogcFeatures': {
|
|
350
354
|
const layers = await new OgcApiEndpoint(url).allCollections
|
|
@@ -352,7 +356,10 @@ export async function getLayers(url: string, serviceProtocol: ServiceProtocol) {
|
|
|
352
356
|
}
|
|
353
357
|
case 'wfs': {
|
|
354
358
|
const endpointWfs = await new WfsEndpoint(url).isReady()
|
|
355
|
-
const featureTypes =
|
|
359
|
+
const featureTypes = endpointWfs.getFeatureTypes()
|
|
360
|
+
if (!deep) {
|
|
361
|
+
return featureTypes
|
|
362
|
+
}
|
|
356
363
|
const layers = (
|
|
357
364
|
await Promise.allSettled(
|
|
358
365
|
featureTypes.map((collection) => {
|
|
@@ -366,14 +373,16 @@ export async function getLayers(url: string, serviceProtocol: ServiceProtocol) {
|
|
|
366
373
|
}
|
|
367
374
|
case 'wms': {
|
|
368
375
|
const endpointWms = await new WmsEndpoint(url).isReady()
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
376
|
+
const layersSummary = endpointWms
|
|
377
|
+
.getLayers()
|
|
378
|
+
.flatMap(wmsLayerFlatten)
|
|
379
|
+
.filter((l) => l.name)
|
|
380
|
+
if (!deep) {
|
|
381
|
+
return layersSummary
|
|
382
|
+
}
|
|
383
|
+
const layers = layersSummary.map((collection) =>
|
|
384
|
+
endpointWms.getLayerByName(collection.name)
|
|
385
|
+
)
|
|
377
386
|
return layers
|
|
378
387
|
}
|
|
379
388
|
case 'wmts': {
|
|
@@ -46,6 +46,22 @@ export function isBoundingBox(value: unknown): value is BoundingBox {
|
|
|
46
46
|
)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Checks that a bounding box's coordinates stay within the valid
|
|
51
|
+
* longitude (-180/180) and latitude (-90/90) ranges.
|
|
52
|
+
*/
|
|
53
|
+
export function isBoundingBoxWithinWorldExtent(bbox: BoundingBox): boolean {
|
|
54
|
+
const [minX, minY, maxX, maxY] = bbox
|
|
55
|
+
return (
|
|
56
|
+
minX >= -180 &&
|
|
57
|
+
maxX <= 180 &&
|
|
58
|
+
minY >= -90 &&
|
|
59
|
+
maxY <= 90 &&
|
|
60
|
+
minX <= maxX &&
|
|
61
|
+
minY <= maxY
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
export function getGeometryBoundingBox(geometry: Geometry): BoundingBox {
|
|
50
66
|
// use the bounding box if specified in the GeoJSON object
|
|
51
67
|
if (geometry.bbox) {
|
package/translations/de.json
CHANGED
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
"favorite.starToggle.add": "Zu Favoriten hinzufügen",
|
|
373
373
|
"favorite.starToggle.remove": "Aus Favoriten entfernen",
|
|
374
374
|
"feature.catalog.attribute.code": "Code",
|
|
375
|
-
"feature.catalog.attribute.
|
|
375
|
+
"feature.catalog.attribute.description": "Beschreibung",
|
|
376
376
|
"feature.catalog.attribute.name": "Name",
|
|
377
377
|
"feature.catalog.attribute.type": "Typ",
|
|
378
378
|
"feature.catalog.attribute.values": "Werte",
|
|
@@ -425,7 +425,6 @@
|
|
|
425
425
|
"map.add.layer.wms": "Aus WMS",
|
|
426
426
|
"map.addFromFile.placeholder": "Klicke hier oder ziehe eine Datei herein",
|
|
427
427
|
"map.dropdown.placeholder": "Kein Vorschaulayer verfügbar",
|
|
428
|
-
"map.geocoding.placeholder": "Nach einem Ort suchen",
|
|
429
428
|
"map.help.addFromFile": "Klicke oder ziehe eine Datei herein um eine Karte hinzuzufügen (momentan wird nur das GeoJSON-Format unterstützt).",
|
|
430
429
|
"map.layer.add": "Hinzufügen",
|
|
431
430
|
"map.layers.available": "Verfügbare Layer",
|
|
@@ -659,6 +658,7 @@
|
|
|
659
658
|
"search.filters.changeDate": "Letzte Aktualisierung",
|
|
660
659
|
"search.filters.clear": "Zurücksetzen",
|
|
661
660
|
"search.filters.contact": "Kontakte",
|
|
661
|
+
"search.filters.documentStandard": "Dokumentenstandard",
|
|
662
662
|
"search.filters.format": "Formate",
|
|
663
663
|
"search.filters.inspireKeyword": "INSPIRE-Schlüsselwort",
|
|
664
664
|
"search.filters.isSpatial": "Ist räumliche Daten",
|
|
@@ -690,21 +690,29 @@
|
|
|
690
690
|
"search.filters.recordKind.reuse": "Wiederverwendungen",
|
|
691
691
|
"search.filters.recordKind.service": "Dienste",
|
|
692
692
|
"search.filters.representationType": "Repräsentationstyp",
|
|
693
|
+
"search.filters.resourceCreationRevisionDate": "Ressourcendatum",
|
|
693
694
|
"search.filters.resourceType": "Ressourcentyp",
|
|
694
695
|
"search.filters.spatialExtent": "",
|
|
695
696
|
"search.filters.spatialExtent.bboxFromFile": "",
|
|
696
697
|
"search.filters.spatialExtent.bboxFromFileDelete": "",
|
|
698
|
+
"search.filters.spatialExtent.bboxFromLocation": "",
|
|
699
|
+
"search.filters.spatialExtent.bboxFromLocationDelete": "",
|
|
697
700
|
"search.filters.spatialExtent.bboxInitial": "",
|
|
698
701
|
"search.filters.spatialExtent.bboxInitialDelete": "",
|
|
699
702
|
"search.filters.spatialExtent.error.fileTooLarge": "",
|
|
700
703
|
"search.filters.spatialExtent.error.invalidFormat": "",
|
|
701
704
|
"search.filters.spatialExtent.error.noGeometry": "",
|
|
705
|
+
"search.filters.spatialExtent.error.outOfBounds": "",
|
|
702
706
|
"search.filters.spatialExtent.error.title": "",
|
|
703
707
|
"search.filters.spatialExtent.helpText": "",
|
|
704
708
|
"search.filters.spatialExtent.import": "",
|
|
709
|
+
"search.filters.spatialExtent.searchLocation": "",
|
|
705
710
|
"search.filters.standard": "Standard",
|
|
706
711
|
"search.filters.summaryLabel.changeDate": "Geändert am: ",
|
|
712
|
+
"search.filters.summaryLabel.resourceCreationRevisionDate": "Ressourcendatum: ",
|
|
713
|
+
"search.filters.summaryLabel.temporalExtent": "Zeitlicher Umfang: ",
|
|
707
714
|
"search.filters.summaryLabel.user": "Geändert von: ",
|
|
715
|
+
"search.filters.temporalExtent": "Zeitlicher Umfang",
|
|
708
716
|
"search.filters.title": "Ergebnisse filtern",
|
|
709
717
|
"search.filters.topic": "Themen",
|
|
710
718
|
"search.filters.useSpatialFilter": "Zuerst Ressourcen im Interessenbereich anzeigen",
|
package/translations/en.json
CHANGED
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
"favorite.starToggle.add": "Add to favorites",
|
|
373
373
|
"favorite.starToggle.remove": "Remove from favorites",
|
|
374
374
|
"feature.catalog.attribute.code": "Code",
|
|
375
|
-
"feature.catalog.attribute.
|
|
375
|
+
"feature.catalog.attribute.description": "Description",
|
|
376
376
|
"feature.catalog.attribute.name": "Name",
|
|
377
377
|
"feature.catalog.attribute.type": "Type",
|
|
378
378
|
"feature.catalog.attribute.values": "Values",
|
|
@@ -425,7 +425,6 @@
|
|
|
425
425
|
"map.add.layer.wms": "From WMS",
|
|
426
426
|
"map.addFromFile.placeholder": "Click or drop a file here",
|
|
427
427
|
"map.dropdown.placeholder": "No preview layer available",
|
|
428
|
-
"map.geocoding.placeholder": "Search for a place",
|
|
429
428
|
"map.help.addFromFile": "Click or drag and drop a file to add to the map (currently supports GeoJSON format only).",
|
|
430
429
|
"map.layer.add": "Add",
|
|
431
430
|
"map.layers.available": "Available Layers",
|
|
@@ -659,6 +658,7 @@
|
|
|
659
658
|
"search.filters.changeDate": "Updated",
|
|
660
659
|
"search.filters.clear": "Reset",
|
|
661
660
|
"search.filters.contact": "Contacts",
|
|
661
|
+
"search.filters.documentStandard": "Document standard",
|
|
662
662
|
"search.filters.format": "Formats",
|
|
663
663
|
"search.filters.inspireKeyword": "INSPIRE keyword",
|
|
664
664
|
"search.filters.isSpatial": "Is spatial data",
|
|
@@ -690,21 +690,29 @@
|
|
|
690
690
|
"search.filters.recordKind.reuse": "Reuses",
|
|
691
691
|
"search.filters.recordKind.service": "Services",
|
|
692
692
|
"search.filters.representationType": "Representation type",
|
|
693
|
+
"search.filters.resourceCreationRevisionDate": "Resource date",
|
|
693
694
|
"search.filters.resourceType": "Resource type",
|
|
694
695
|
"search.filters.spatialExtent": "Spatial extent",
|
|
695
|
-
"search.filters.spatialExtent.bboxFromFile": "
|
|
696
|
+
"search.filters.spatialExtent.bboxFromFile": "Extent from {fileName}",
|
|
696
697
|
"search.filters.spatialExtent.bboxFromFileDelete": "Delete {fileName}",
|
|
698
|
+
"search.filters.spatialExtent.bboxFromLocation": "Extent from {locationName}",
|
|
699
|
+
"search.filters.spatialExtent.bboxFromLocationDelete": "Delete {locationName}",
|
|
697
700
|
"search.filters.spatialExtent.bboxInitial": "Pre-selected extent",
|
|
698
701
|
"search.filters.spatialExtent.bboxInitialDelete": "Delete selection",
|
|
699
702
|
"search.filters.spatialExtent.error.fileTooLarge": "The file exceeds the maximum allowed size ({maxSize} MB)",
|
|
700
703
|
"search.filters.spatialExtent.error.invalidFormat": "The file could not be read as a valid GeoJSON",
|
|
701
704
|
"search.filters.spatialExtent.error.noGeometry": "No geometry could be found in the file",
|
|
705
|
+
"search.filters.spatialExtent.error.outOfBounds": "The geometry of the GeoJSON file is not compliant; either some coordinates are out of bounds ([-180, 180] for longitude and [-90, 90] for latitude), or the file uses projected coordinates instead of longitude and latitude",
|
|
702
706
|
"search.filters.spatialExtent.error.title": "GeoJSON import error",
|
|
703
|
-
"search.filters.spatialExtent.helpText": "The
|
|
707
|
+
"search.filters.spatialExtent.helpText": "The extent of the selected geometry will be used",
|
|
704
708
|
"search.filters.spatialExtent.import": "Import a GeoJSON",
|
|
709
|
+
"search.filters.spatialExtent.searchLocation": "Search in the list",
|
|
705
710
|
"search.filters.standard": "Standard",
|
|
706
711
|
"search.filters.summaryLabel.changeDate": "Updated: ",
|
|
712
|
+
"search.filters.summaryLabel.resourceCreationRevisionDate": "Resource date: ",
|
|
713
|
+
"search.filters.summaryLabel.temporalExtent": "Temporal extent: ",
|
|
707
714
|
"search.filters.summaryLabel.user": "Editor: ",
|
|
715
|
+
"search.filters.temporalExtent": "Temporal extent",
|
|
708
716
|
"search.filters.title": "Filter your results",
|
|
709
717
|
"search.filters.topic": "Topics",
|
|
710
718
|
"search.filters.useSpatialFilter": "Show records in the area of interest first",
|