geonetwork-ui 2.6.0-dev.3a78a5eaa → 2.6.0-dev.4fa0a0afc
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/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
- package/esm2022/libs/feature/dataviz/src/lib/chart-view/chart-view.component.mjs +3 -3
- package/esm2022/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.mjs +2 -2
- package/esm2022/libs/feature/dataviz/src/lib/service/data.service.mjs +2 -1
- package/esm2022/libs/feature/dataviz/src/lib/table-view/table-view.component.mjs +3 -3
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-map-container/form-field-map-container.component.mjs +1 -1
- package/esm2022/libs/feature/map/src/lib/map-state-container/map-state-container.component.mjs +2 -2
- package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +2 -4
- package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +40 -14
- package/esm2022/libs/ui/elements/src/lib/api-card/api-card.component.mjs +27 -6
- package/esm2022/libs/ui/elements/src/lib/download-item/download-item.component.mjs +22 -6
- package/esm2022/libs/ui/elements/src/lib/downloads-list/downloads-list.component.mjs +3 -3
- package/esm2022/libs/ui/elements/src/lib/link-card/link-card.component.mjs +29 -7
- package/esm2022/libs/ui/elements/src/lib/record-api-form/record-api-form.component.mjs +3 -2
- package/esm2022/libs/ui/layout/src/lib/carousel/carousel.component.mjs +3 -3
- package/esm2022/libs/ui/map/src/lib/components/map-container/map-container.component.mjs +14 -3
- package/esm2022/libs/ui/map/src/lib/components/map-legend/map-legend.component.mjs +5 -1
- package/esm2022/libs/util/data-fetcher/src/lib/data-fetcher.mjs +6 -2
- package/esm2022/libs/util/data-fetcher/src/lib/model.mjs +4 -1
- package/esm2022/libs/util/data-fetcher/src/lib/readers/wfs.mjs +30 -2
- package/esm2022/libs/util/shared/src/lib/links/link-utils.mjs +21 -19
- package/esm2022/translations/de.json +1 -0
- package/esm2022/translations/en.json +2 -1
- package/esm2022/translations/es.json +1 -0
- package/esm2022/translations/fr.json +1 -0
- package/esm2022/translations/it.json +1 -0
- package/esm2022/translations/nl.json +1 -0
- package/esm2022/translations/pt.json +1 -0
- package/fesm2022/geonetwork-ui.mjs +204 -65
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +1 -0
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
- package/libs/feature/dataviz/src/lib/service/data.service.d.ts.map +1 -1
- package/libs/feature/dataviz/src/lib/table-view/table-view.component.d.ts +1 -1
- package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
- package/libs/feature/record/src/lib/map-view/map-view.component.d.ts +7 -2
- package/libs/feature/record/src/lib/map-view/map-view.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/api-card/api-card.component.d.ts +9 -1
- package/libs/ui/elements/src/lib/api-card/api-card.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/download-item/download-item.component.d.ts +8 -1
- package/libs/ui/elements/src/lib/download-item/download-item.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/link-card/link-card.component.d.ts +10 -2
- package/libs/ui/elements/src/lib/link-card/link-card.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts.map +1 -1
- package/libs/ui/map/src/lib/components/map-container/map-container.component.d.ts +4 -2
- package/libs/ui/map/src/lib/components/map-container/map-container.component.d.ts.map +1 -1
- package/libs/ui/map/src/lib/components/map-legend/map-legend.component.d.ts.map +1 -1
- package/libs/util/data-fetcher/src/lib/data-fetcher.d.ts.map +1 -1
- package/libs/util/data-fetcher/src/lib/model.d.ts +3 -2
- package/libs/util/data-fetcher/src/lib/model.d.ts.map +1 -1
- package/libs/util/data-fetcher/src/lib/readers/wfs.d.ts +1 -0
- package/libs/util/data-fetcher/src/lib/readers/wfs.d.ts.map +1 -1
- package/libs/util/shared/src/lib/links/link-utils.d.ts +16 -16
- package/libs/util/shared/src/lib/links/link-utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +1 -0
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.html +1 -0
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +1 -0
- package/src/libs/feature/dataviz/src/lib/table-view/table-view.component.html +10 -8
- package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +1 -3
- package/src/libs/feature/record/src/lib/map-view/map-view.component.html +1 -0
- package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +36 -7
- package/src/libs/ui/elements/src/lib/api-card/api-card.component.html +64 -38
- package/src/libs/ui/elements/src/lib/api-card/api-card.component.ts +26 -2
- package/src/libs/ui/elements/src/lib/download-item/download-item.component.html +17 -17
- package/src/libs/ui/elements/src/lib/download-item/download-item.component.ts +20 -2
- package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.html +1 -0
- package/src/libs/ui/elements/src/lib/link-card/link-card.component.html +27 -29
- package/src/libs/ui/elements/src/lib/link-card/link-card.component.ts +33 -3
- package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +2 -1
- package/src/libs/ui/layout/src/lib/carousel/carousel.component.css +0 -4
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +14 -0
- package/src/libs/ui/map/src/lib/components/map-legend/map-legend.component.ts +3 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +3 -1
- package/src/libs/util/data-fetcher/src/lib/model.ts +11 -1
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +29 -3
- package/src/libs/util/shared/src/lib/links/link-utils.ts +20 -18
- package/tailwind.base.css +29 -1
- package/translations/de.json +1 -0
- package/translations/en.json +2 -1
- package/translations/es.json +1 -0
- package/translations/fr.json +1 -0
- package/translations/it.json +1 -0
- package/translations/nl.json +1 -0
- package/translations/pt.json +1 -0
- package/translations/sk.json +1 -0
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
createViewFromLayer,
|
|
35
35
|
MapContext,
|
|
36
36
|
MapContextLayer,
|
|
37
|
+
SourceLoadErrorEvent,
|
|
37
38
|
} from '@geospatial-sdk/core'
|
|
38
39
|
import {
|
|
39
40
|
FeatureDetailComponent,
|
|
@@ -49,13 +50,14 @@ import {
|
|
|
49
50
|
ButtonComponent,
|
|
50
51
|
DropdownSelectorComponent,
|
|
51
52
|
} from '../../../../../../libs/ui/inputs/src'
|
|
52
|
-
import { TranslateModule } from '@ngx-translate/core'
|
|
53
|
+
import { TranslateModule, TranslateService } from '@ngx-translate/core'
|
|
53
54
|
import { ExternalViewerButtonComponent } from '../external-viewer-button/external-viewer-button.component'
|
|
54
55
|
import {
|
|
55
56
|
LoadingMaskComponent,
|
|
56
57
|
PopupAlertComponent,
|
|
57
58
|
} from '../../../../../../libs/ui/widgets/src'
|
|
58
59
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
60
|
+
import { FetchError } from '../../../../../../libs/util/data-fetcher/src'
|
|
59
61
|
|
|
60
62
|
marker('map.dropdown.placeholder')
|
|
61
63
|
marker('wfs.feature.limit')
|
|
@@ -100,9 +102,6 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
100
102
|
|
|
101
103
|
onLegendStatusChange(status: boolean) {
|
|
102
104
|
this.legendExists = status
|
|
103
|
-
if (!status) {
|
|
104
|
-
this.showLegend = false
|
|
105
|
-
}
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
compatibleMapLinks$ = combineLatest([
|
|
@@ -149,8 +148,7 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
149
148
|
return this.getLayerFromLink(link).pipe(
|
|
150
149
|
map((layer) => [layer]),
|
|
151
150
|
catchError((e) => {
|
|
152
|
-
this.
|
|
153
|
-
console.warn(e.stack || e.message)
|
|
151
|
+
this.handleError(e)
|
|
154
152
|
return of([])
|
|
155
153
|
}),
|
|
156
154
|
finalize(() => (this.loading = false))
|
|
@@ -192,7 +190,8 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
192
190
|
private mdViewFacade: MdViewFacade,
|
|
193
191
|
private mapUtils: MapUtilsService,
|
|
194
192
|
private dataService: DataService,
|
|
195
|
-
private changeRef: ChangeDetectorRef
|
|
193
|
+
private changeRef: ChangeDetectorRef,
|
|
194
|
+
private translateService: TranslateService
|
|
196
195
|
) {}
|
|
197
196
|
|
|
198
197
|
async ngAfterViewInit() {
|
|
@@ -210,6 +209,16 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
210
209
|
this.changeRef.detectChanges()
|
|
211
210
|
}
|
|
212
211
|
|
|
212
|
+
onSourceLoadError(error: SourceLoadErrorEvent) {
|
|
213
|
+
if (error.httpStatus === 403 || error.httpStatus === 401) {
|
|
214
|
+
this.error = this.translateService.instant(`dataset.error.forbidden`)
|
|
215
|
+
} else {
|
|
216
|
+
this.error = this.translateService.instant(`dataset.error.http`, {
|
|
217
|
+
info: error.httpStatus,
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
213
222
|
resetSelection(): void {
|
|
214
223
|
if (this.selection) {
|
|
215
224
|
// FIXME: restore styling of selected feature
|
|
@@ -255,4 +264,24 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
255
264
|
selectLinkToDisplay(link: number) {
|
|
256
265
|
this.selectedLinkIndex$.next(link)
|
|
257
266
|
}
|
|
267
|
+
|
|
268
|
+
handleError(error: FetchError | Error | string) {
|
|
269
|
+
if (error instanceof FetchError) {
|
|
270
|
+
this.error = this.translateService.instant(
|
|
271
|
+
`dataset.error.${error.type}`,
|
|
272
|
+
{
|
|
273
|
+
info: error.info,
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
console.warn(error.message)
|
|
277
|
+
} else if (error instanceof Error) {
|
|
278
|
+
this.error = this.translateService.instant(error.message)
|
|
279
|
+
console.warn(error.stack || error)
|
|
280
|
+
} else {
|
|
281
|
+
this.error = this.translateService.instant(error)
|
|
282
|
+
console.warn(error)
|
|
283
|
+
}
|
|
284
|
+
this.loading = false
|
|
285
|
+
this.changeRef.detectChanges()
|
|
286
|
+
}
|
|
258
287
|
}
|
|
@@ -1,13 +1,66 @@
|
|
|
1
1
|
<div
|
|
2
|
-
class="group flex
|
|
2
|
+
class="group flex justify-between rounded filter overflow-hidden"
|
|
3
|
+
[ngClass]="cardClass"
|
|
3
4
|
>
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
<ng-container *ngIf="size !== 'S'">
|
|
6
|
+
<div>
|
|
7
|
+
<ng-container *ngTemplateOutlet="content"></ng-container>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="flex items-center">
|
|
10
|
+
<ng-container *ngTemplateOutlet="buttons"></ng-container>
|
|
11
|
+
</div>
|
|
12
|
+
</ng-container>
|
|
13
|
+
<ng-container *ngIf="size === 'S'">
|
|
14
|
+
<ng-container *ngTemplateOutlet="content"></ng-container>
|
|
15
|
+
</ng-container>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<ng-template #buttons>
|
|
19
|
+
<div class="flex flex-row gap-2">
|
|
20
|
+
<gn-ui-copy-text-button
|
|
21
|
+
[text]="link.url"
|
|
22
|
+
[tooltipText]="'tooltip.url.copy' | translate"
|
|
23
|
+
[displayText]="false"
|
|
24
|
+
class="border border-gray-300 rounded-lg pt-1 px-2 h-[34px]"
|
|
25
|
+
></gn-ui-copy-text-button>
|
|
26
|
+
<button
|
|
27
|
+
*ngIf="displayApiFormButton"
|
|
28
|
+
type="button"
|
|
29
|
+
class="gn-ui-card-icon"
|
|
30
|
+
[ngClass]="{
|
|
31
|
+
'py-2 px-4 rounded-r-md bg-gray-400 hover:bg-gray-600 focus:bg-gray-800 text-white':
|
|
32
|
+
displayText,
|
|
33
|
+
}"
|
|
34
|
+
[matTooltip]="
|
|
35
|
+
!currentlyActive
|
|
36
|
+
? ('record.metadata.api.form.openForm' | translate)
|
|
37
|
+
: ('record.metadata.api.form.closeForm' | translate)
|
|
38
|
+
"
|
|
39
|
+
matTooltipPosition="above"
|
|
40
|
+
(click)="openRecordApiFormPanel()"
|
|
41
|
+
>
|
|
42
|
+
<ng-icon
|
|
43
|
+
class="pointer-events-none align-middle card-icon"
|
|
44
|
+
name="iconoirSettings"
|
|
45
|
+
[ngClass]="{
|
|
46
|
+
'text-secondary opacity-100': currentlyActive,
|
|
47
|
+
}"
|
|
48
|
+
></ng-icon>
|
|
49
|
+
</button>
|
|
8
50
|
</div>
|
|
9
|
-
|
|
10
|
-
|
|
51
|
+
</ng-template>
|
|
52
|
+
|
|
53
|
+
<ng-template #content>
|
|
54
|
+
<div>
|
|
55
|
+
<div class="gn-ui-card-title">
|
|
56
|
+
{{ link.description || link.name }}
|
|
57
|
+
</div>
|
|
58
|
+
<div class="gn-ui-card-detail">
|
|
59
|
+
{{ link.name }}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="flex flex-row justify-between">
|
|
63
|
+
<div class="flex flex-row gap-2.5 items-center pt-1">
|
|
11
64
|
<span
|
|
12
65
|
*ngIf="link.accessServiceProtocol !== 'GPFDL'"
|
|
13
66
|
class="bg-primary-opacity-50 uppercase inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded text-primary-lightest group-hover:bg-primary transition-colors"
|
|
@@ -26,36 +79,9 @@
|
|
|
26
79
|
>
|
|
27
80
|
record.metadata.api.gpfdl</span
|
|
28
81
|
>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
[tooltipText]="'tooltip.url.copy' | translate"
|
|
33
|
-
[displayText]="false"
|
|
34
|
-
></gn-ui-copy-text-button>
|
|
35
|
-
<button
|
|
36
|
-
*ngIf="displayApiFormButton"
|
|
37
|
-
type="button"
|
|
38
|
-
[ngClass]="{
|
|
39
|
-
'py-2 px-4 rounded-r-md bg-gray-400 hover:bg-gray-600 focus:bg-gray-800 text-white':
|
|
40
|
-
displayText,
|
|
41
|
-
}"
|
|
42
|
-
[matTooltip]="
|
|
43
|
-
!currentlyActive
|
|
44
|
-
? ('record.metadata.api.form.openForm' | translate)
|
|
45
|
-
: ('record.metadata.api.form.closeForm' | translate)
|
|
46
|
-
"
|
|
47
|
-
matTooltipPosition="above"
|
|
48
|
-
(click)="openRecordApiFormPanel()"
|
|
49
|
-
>
|
|
50
|
-
<ng-icon
|
|
51
|
-
class="pointer-events-none align-middle card-icon"
|
|
52
|
-
name="matMoreHoriz"
|
|
53
|
-
[ngClass]="{
|
|
54
|
-
'text-secondary opacity-100': currentlyActive,
|
|
55
|
-
}"
|
|
56
|
-
></ng-icon>
|
|
57
|
-
</button>
|
|
58
|
-
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<div *ngIf="size === 'S'">
|
|
84
|
+
<ng-container *ngTemplateOutlet="buttons"></ng-container>
|
|
59
85
|
</div>
|
|
60
86
|
</div>
|
|
61
|
-
</
|
|
87
|
+
</ng-template>
|
|
@@ -14,7 +14,9 @@ import { CopyTextButtonComponent } from '../../../../../../libs/ui/inputs/src'
|
|
|
14
14
|
import { TranslateModule } from '@ngx-translate/core'
|
|
15
15
|
import { MatTooltipModule } from '@angular/material/tooltip'
|
|
16
16
|
import { NgIcon, provideIcons } from '@ng-icons/core'
|
|
17
|
-
import {
|
|
17
|
+
import { iconoirSettings } from '@ng-icons/iconoir'
|
|
18
|
+
|
|
19
|
+
type CardSize = 'L' | 'M' | 'S' | 'XS'
|
|
18
20
|
|
|
19
21
|
@Component({
|
|
20
22
|
selector: 'gn-ui-api-card',
|
|
@@ -31,18 +33,40 @@ import { matMoreHoriz } from '@ng-icons/material-icons/baseline'
|
|
|
31
33
|
],
|
|
32
34
|
viewProviders: [
|
|
33
35
|
provideIcons({
|
|
34
|
-
|
|
36
|
+
iconoirSettings,
|
|
35
37
|
}),
|
|
36
38
|
],
|
|
37
39
|
})
|
|
38
40
|
export class ApiCardComponent implements OnInit, OnChanges {
|
|
41
|
+
private _size: 'L' | 'M' | 'S' | 'XS'
|
|
39
42
|
@Input() link: DatasetServiceDistribution
|
|
40
43
|
@Input() currentLink: DatasetServiceDistribution
|
|
44
|
+
private readonly sizeClassMap: Record<CardSize, string> = {
|
|
45
|
+
L: 'gn-ui-card-l py-2 px-5 flex-row',
|
|
46
|
+
M: 'gn-ui-card-m py-2 px-5 flex-row',
|
|
47
|
+
S: 'gn-ui-card-s p-4 flex-col',
|
|
48
|
+
XS: 'gn-ui-card-xs py-2 px-5 flex-row',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Input() set size(value: CardSize) {
|
|
52
|
+
this._size = value
|
|
53
|
+
this.cardClass = this.sizeClassMap[value]
|
|
54
|
+
}
|
|
55
|
+
get size(): 'L' | 'M' | 'S' | 'XS' {
|
|
56
|
+
return this._size
|
|
57
|
+
}
|
|
58
|
+
cardClass = ''
|
|
41
59
|
displayApiFormButton: boolean
|
|
42
60
|
currentlyActive = false
|
|
43
61
|
@Output() openRecordApiForm: EventEmitter<DatasetServiceDistribution> =
|
|
44
62
|
new EventEmitter<DatasetServiceDistribution>()
|
|
45
63
|
|
|
64
|
+
get generatedText() {
|
|
65
|
+
return this.link.accessServiceProtocol === 'wfs'
|
|
66
|
+
? 'datahub.search.filter.generatedByWfs'
|
|
67
|
+
: 'datahub.search.filter.generatedByAPI'
|
|
68
|
+
}
|
|
69
|
+
|
|
46
70
|
ngOnInit() {
|
|
47
71
|
this.displayApiFormButton =
|
|
48
72
|
this.link.accessServiceProtocol === 'ogcFeatures' ||
|
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
<a
|
|
2
2
|
href="{{ link.url }}"
|
|
3
3
|
target="_blank"
|
|
4
|
-
class="group flex justify-between card-shadow
|
|
4
|
+
class="group flex flex-row justify-between card-shadow cursor-pointer rounded overflow-hidden"
|
|
5
5
|
rel="noopener"
|
|
6
|
+
[ngClass]="cardClass"
|
|
6
7
|
>
|
|
7
|
-
<div class="
|
|
8
|
-
<div
|
|
9
|
-
class="text-21 text-black truncate font-title w-11/12"
|
|
10
|
-
[title]="link.description || link.name"
|
|
11
|
-
>
|
|
8
|
+
<div class="flex flex-col justify-between">
|
|
9
|
+
<div class="gn-ui-card-title" [title]="link.description || link.name">
|
|
12
10
|
{{ link.description || link.name }}
|
|
13
11
|
</div>
|
|
14
|
-
<div class="
|
|
12
|
+
<div class="gn-ui-card-detail">
|
|
13
|
+
{{ link.name }}
|
|
14
|
+
</div>
|
|
15
|
+
<div class="flex flex-row gap-2 items-center pt-1">
|
|
15
16
|
<span
|
|
16
17
|
class="inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded transition-opacity opacity-70 group-hover:opacity-100"
|
|
17
18
|
[style.background-color]="color"
|
|
18
19
|
data-cy="download-format"
|
|
19
20
|
>{{ format || ('downloads.format.unknown' | translate) }}</span
|
|
20
21
|
>
|
|
21
|
-
<span
|
|
22
|
-
class="pl-2 inline-flex items-center text-gray-800 text-sm"
|
|
23
|
-
*ngIf="isFromApi"
|
|
24
|
-
translate=""
|
|
22
|
+
<span class="text-gray-800 text-xs" *ngIf="isFromApi" translate=""
|
|
25
23
|
>datahub.search.filter.generatedByAPI</span
|
|
26
24
|
>
|
|
27
25
|
</div>
|
|
28
26
|
</div>
|
|
29
|
-
<div class="
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
<div class="flex" [ngClass]="size === 'S' ? 'items-end' : 'items-center'">
|
|
28
|
+
<div class="gn-ui-card-icon">
|
|
29
|
+
<ng-icon
|
|
30
|
+
class="inline-block card-icon align-middle"
|
|
31
|
+
name="iconoirDownload"
|
|
32
|
+
>
|
|
33
|
+
</ng-icon>
|
|
34
|
+
</div>
|
|
35
35
|
</div>
|
|
36
36
|
</a>
|
|
@@ -9,7 +9,9 @@ import { DatasetOnlineResource } from '../../../../../../libs/common/domain/src/
|
|
|
9
9
|
import { TranslateModule } from '@ngx-translate/core'
|
|
10
10
|
import { CommonModule } from '@angular/common'
|
|
11
11
|
import { NgIcon, provideIcons } from '@ng-icons/core'
|
|
12
|
-
import {
|
|
12
|
+
import { iconoirDownload } from '@ng-icons/iconoir'
|
|
13
|
+
|
|
14
|
+
type CardSize = 'L' | 'M' | 'S' | 'XS'
|
|
13
15
|
|
|
14
16
|
@Component({
|
|
15
17
|
selector: 'gn-ui-download-item',
|
|
@@ -20,15 +22,31 @@ import { matCloudDownloadOutline } from '@ng-icons/material-icons/outline'
|
|
|
20
22
|
standalone: true,
|
|
21
23
|
viewProviders: [
|
|
22
24
|
provideIcons({
|
|
23
|
-
|
|
25
|
+
iconoirDownload,
|
|
24
26
|
}),
|
|
25
27
|
],
|
|
26
28
|
})
|
|
27
29
|
export class DownloadItemComponent {
|
|
30
|
+
private _size: 'L' | 'M' | 'S' | 'XS'
|
|
28
31
|
@Input() link: DatasetOnlineResource
|
|
29
32
|
@Input() color: string
|
|
30
33
|
@Input() format: string
|
|
31
34
|
@Input() isFromApi: boolean
|
|
35
|
+
private readonly sizeClassMap: Record<CardSize, string> = {
|
|
36
|
+
L: 'gn-ui-card-l py-2 px-5',
|
|
37
|
+
M: 'gn-ui-card-m py-2 px-5',
|
|
38
|
+
S: 'gn-ui-card-s p-4',
|
|
39
|
+
XS: 'gn-ui-card-xs py-2 px-5',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Input() set size(value: CardSize) {
|
|
43
|
+
this._size = value
|
|
44
|
+
this.cardClass = this.sizeClassMap[value]
|
|
45
|
+
}
|
|
46
|
+
get size(): 'L' | 'M' | 'S' | 'XS' {
|
|
47
|
+
return this._size
|
|
48
|
+
}
|
|
49
|
+
cardClass = ''
|
|
32
50
|
@Output() exportUrl = new EventEmitter<string>()
|
|
33
51
|
|
|
34
52
|
openUrl() {
|
|
@@ -1,39 +1,37 @@
|
|
|
1
1
|
<a
|
|
2
2
|
[href]="link.url"
|
|
3
3
|
target="_blank"
|
|
4
|
-
class="flex flex-
|
|
5
|
-
[ngClass]="
|
|
4
|
+
class="group flex flex-row justify-between card-shadow cursor-pointer rounded overflow-hidden"
|
|
5
|
+
[ngClass]="cardClass"
|
|
6
6
|
[title]="title"
|
|
7
7
|
>
|
|
8
|
-
<
|
|
9
|
-
<div class="
|
|
10
|
-
|
|
11
|
-
class="font-title font-medium text-21 text-black break-words mb-1 line-clamp-2"
|
|
12
|
-
>
|
|
13
|
-
{{ link.name }}
|
|
14
|
-
</p>
|
|
15
|
-
<p class="font-medium text-sm break-words">
|
|
16
|
-
{{ link.description }}
|
|
17
|
-
</p>
|
|
18
|
-
<p
|
|
19
|
-
*ngIf="!link.name && !link.description"
|
|
20
|
-
class="font-medium text-sm break-words truncate"
|
|
21
|
-
>
|
|
22
|
-
{{ link.url }}
|
|
23
|
-
</p>
|
|
8
|
+
<div class="flex flex-col justify-between">
|
|
9
|
+
<div class="gn-ui-card-title">
|
|
10
|
+
{{ link.description || link.name }}
|
|
24
11
|
</div>
|
|
25
|
-
<div>
|
|
26
|
-
|
|
12
|
+
<div class="gn-ui-card-detail">
|
|
13
|
+
{{ link.name }}
|
|
27
14
|
</div>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
<p *ngIf="!link.name && !link.description" class="gn-ui-card-title">
|
|
16
|
+
{{ link.url }}
|
|
17
|
+
</p>
|
|
18
|
+
<div class="pt-1">
|
|
19
|
+
<span
|
|
20
|
+
class="inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded transition-opacity opacity-70 group-hover:opacity-100"
|
|
21
|
+
[style.background-color]="getLinkColor(link)"
|
|
22
|
+
data-cy="download-format"
|
|
23
|
+
>{{
|
|
24
|
+
getLinkFormat(link) || ('downloads.format.unknown' | translate)
|
|
25
|
+
}}</span
|
|
33
26
|
>
|
|
34
|
-
{{ link.name || link.description }}
|
|
35
|
-
</p>
|
|
36
|
-
<ng-icon class="card-icon flex-shrink-0" name="matOpenInNew"></ng-icon>
|
|
37
27
|
</div>
|
|
38
|
-
</
|
|
28
|
+
</div>
|
|
29
|
+
<div class="flex" [ngClass]="size === 'S' ? 'items-end' : 'items-center'">
|
|
30
|
+
<div class="gn-ui-card-icon">
|
|
31
|
+
<ng-icon
|
|
32
|
+
class="inline-block card-icon align-middle"
|
|
33
|
+
name="matOpenInNew"
|
|
34
|
+
></ng-icon>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
39
37
|
</a>
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DatasetDownloadDistribution,
|
|
4
|
+
DatasetOnlineResource,
|
|
5
|
+
DatasetServiceDistribution,
|
|
6
|
+
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
3
7
|
import { CommonModule } from '@angular/common'
|
|
4
8
|
import {
|
|
5
9
|
NgIconComponent,
|
|
@@ -7,14 +11,17 @@ import {
|
|
|
7
11
|
provideNgIconsConfig,
|
|
8
12
|
} from '@ng-icons/core'
|
|
9
13
|
import { matOpenInNew } from '@ng-icons/material-icons/baseline'
|
|
14
|
+
import { getBadgeColor, getFileFormat } from '../../../../../../libs/util/shared/src'
|
|
15
|
+
import { TranslateModule } from '@ngx-translate/core'
|
|
10
16
|
|
|
17
|
+
type CardSize = 'L' | 'M' | 'S' | 'XS'
|
|
11
18
|
@Component({
|
|
12
19
|
selector: 'gn-ui-link-card',
|
|
13
20
|
templateUrl: './link-card.component.html',
|
|
14
21
|
styleUrls: ['./link-card.component.css'],
|
|
15
22
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
16
23
|
standalone: true,
|
|
17
|
-
imports: [CommonModule, NgIconComponent],
|
|
24
|
+
imports: [CommonModule, NgIconComponent, TranslateModule],
|
|
18
25
|
providers: [
|
|
19
26
|
provideIcons({
|
|
20
27
|
matOpenInNew,
|
|
@@ -23,8 +30,23 @@ import { matOpenInNew } from '@ng-icons/material-icons/baseline'
|
|
|
23
30
|
],
|
|
24
31
|
})
|
|
25
32
|
export class LinkCardComponent {
|
|
33
|
+
private _size: 'L' | 'M' | 'S' | 'XS'
|
|
26
34
|
@Input() link: DatasetOnlineResource
|
|
27
|
-
|
|
35
|
+
private readonly sizeClassMap: Record<CardSize, string> = {
|
|
36
|
+
L: 'gn-ui-card-l py-2 px-5',
|
|
37
|
+
M: 'gn-ui-card-m py-2 px-5',
|
|
38
|
+
S: 'gn-ui-card-s p-4',
|
|
39
|
+
XS: 'gn-ui-card-xs py-2 px-5',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Input() set size(value: CardSize) {
|
|
43
|
+
this._size = value
|
|
44
|
+
this.cardClass = this.sizeClassMap[value]
|
|
45
|
+
}
|
|
46
|
+
get size(): 'L' | 'M' | 'S' | 'XS' {
|
|
47
|
+
return this._size
|
|
48
|
+
}
|
|
49
|
+
cardClass = ''
|
|
28
50
|
|
|
29
51
|
get title() {
|
|
30
52
|
if (this.link.name && this.link.description) {
|
|
@@ -32,4 +54,12 @@ export class LinkCardComponent {
|
|
|
32
54
|
}
|
|
33
55
|
return this.link.name || this.link.description || ''
|
|
34
56
|
}
|
|
57
|
+
|
|
58
|
+
getLinkFormat(link: any) {
|
|
59
|
+
return getFileFormat(link)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getLinkColor(link: any) {
|
|
63
|
+
return getBadgeColor(getFileFormat(link))
|
|
64
|
+
}
|
|
35
65
|
}
|
|
@@ -162,7 +162,7 @@ export class RecordApiFormComponent {
|
|
|
162
162
|
limit: limit !== '-1' ? Number(limit) : -1,
|
|
163
163
|
offset: offset !== '' ? Number(offset) : undefined,
|
|
164
164
|
outputCrs:
|
|
165
|
-
format
|
|
165
|
+
format.toLowerCase().indexOf('json') > -1 ? 'EPSG:4326' : undefined,
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
if (this.endpoint instanceof WfsEndpoint) {
|
|
@@ -170,6 +170,7 @@ export class RecordApiFormComponent {
|
|
|
170
170
|
options.maxFeatures = limit !== '-1' ? Number(limit) : undefined
|
|
171
171
|
return this.endpoint.getFeatureUrl(this.apiFeatureType, options)
|
|
172
172
|
} else {
|
|
173
|
+
delete options.outputCrs
|
|
173
174
|
return await this.endpoint.getCollectionItemsUrl(
|
|
174
175
|
this.apiFeatureType,
|
|
175
176
|
options
|
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
MapContextLayer,
|
|
29
29
|
MapContextLayerXyz,
|
|
30
30
|
MapContextView,
|
|
31
|
+
SourceLoadErrorEvent,
|
|
32
|
+
SourceLoadErrorType,
|
|
31
33
|
} from '@geospatial-sdk/core'
|
|
32
34
|
import {
|
|
33
35
|
applyContextDiffToMap,
|
|
@@ -119,6 +121,18 @@ export class MapContainerComponent implements AfterViewInit, OnChanges {
|
|
|
119
121
|
}
|
|
120
122
|
return this._mapClick
|
|
121
123
|
}
|
|
124
|
+
_sourceLoadError: EventEmitter<SourceLoadErrorEvent>
|
|
125
|
+
@Output() get sourceLoadError() {
|
|
126
|
+
if (!this._sourceLoadError) {
|
|
127
|
+
this.openlayersMap.then((olMap) => {
|
|
128
|
+
listen(olMap, SourceLoadErrorType, (error: SourceLoadErrorEvent) =>
|
|
129
|
+
this._sourceLoadError.emit(error)
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
this._sourceLoadError = new EventEmitter<SourceLoadErrorEvent>()
|
|
133
|
+
}
|
|
134
|
+
return this._sourceLoadError
|
|
135
|
+
}
|
|
122
136
|
|
|
123
137
|
@ViewChild('map') container: ElementRef
|
|
124
138
|
displayMessage$: Observable<boolean>
|
|
@@ -53,7 +53,9 @@ export async function openDataset(
|
|
|
53
53
|
reader.load()
|
|
54
54
|
return reader
|
|
55
55
|
} catch (e: any) {
|
|
56
|
-
|
|
56
|
+
//WfsReader may already raise a FetchError
|
|
57
|
+
if (e instanceof FetchError) throw e
|
|
58
|
+
else throw FetchError.parsingFailed(e.message)
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -7,7 +7,14 @@ export class FetchError {
|
|
|
7
7
|
stack = null
|
|
8
8
|
|
|
9
9
|
constructor(
|
|
10
|
-
public type:
|
|
10
|
+
public type:
|
|
11
|
+
| 'http'
|
|
12
|
+
| 'forbidden'
|
|
13
|
+
| 'network'
|
|
14
|
+
| 'parse'
|
|
15
|
+
| 'unsupportedType'
|
|
16
|
+
| 'unknown',
|
|
17
|
+
|
|
11
18
|
public info: string,
|
|
12
19
|
public httpStatus = 0
|
|
13
20
|
) {
|
|
@@ -20,6 +27,9 @@ ${body}`
|
|
|
20
27
|
: `${code}`
|
|
21
28
|
return new FetchError('http', info, code)
|
|
22
29
|
}
|
|
30
|
+
static forbidden(code: number) {
|
|
31
|
+
return new FetchError('forbidden', '', code)
|
|
32
|
+
}
|
|
23
33
|
static corsOrNetwork(message: string) {
|
|
24
34
|
return new FetchError('network', message, 0)
|
|
25
35
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { WfsEndpoint, WfsVersion } from '@camptocamp/ogc-client'
|
|
2
|
-
import { DataItem, DatasetInfo, PropertyInfo } from '../model'
|
|
1
|
+
import { EndpointError, WfsEndpoint, WfsVersion } from '@camptocamp/ogc-client'
|
|
2
|
+
import { DataItem, DatasetInfo, FetchError, PropertyInfo } from '../model'
|
|
3
3
|
import { fetchDataAsText } from '../utils'
|
|
4
4
|
import { GmlReader, parseGml } from './gml'
|
|
5
5
|
import { GeojsonReader, parseGeojson } from './geojson'
|
|
@@ -7,6 +7,32 @@ import { BaseCacheReader } from './base-cache'
|
|
|
7
7
|
import { getJsonDataItemsProxy, jsonToGeojsonFeature } from '../utils'
|
|
8
8
|
import { generateSqlQuery } from '../sql-utils'
|
|
9
9
|
|
|
10
|
+
export async function getWfsEndpoint(wfsUrl: string): Promise<WfsEndpoint> {
|
|
11
|
+
try {
|
|
12
|
+
return await new WfsEndpoint(wfsUrl).isReady()
|
|
13
|
+
} catch (e) {
|
|
14
|
+
if (
|
|
15
|
+
e instanceof Error &&
|
|
16
|
+
'isCrossOriginRelated' in e &&
|
|
17
|
+
'httpStatus' in e
|
|
18
|
+
) {
|
|
19
|
+
const error = e as EndpointError
|
|
20
|
+
if (error.isCrossOriginRelated === true) {
|
|
21
|
+
throw new Error(`wfs.unreachable.cors`)
|
|
22
|
+
}
|
|
23
|
+
if (error.httpStatus === 401 || error.httpStatus === 403) {
|
|
24
|
+
throw FetchError.forbidden(error.httpStatus)
|
|
25
|
+
} else if (error.httpStatus === 400 || error.httpStatus > 403) {
|
|
26
|
+
throw FetchError.http(error.httpStatus)
|
|
27
|
+
} else {
|
|
28
|
+
throw FetchError.unknownType()
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
throw FetchError.unknownType()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
10
36
|
export class WfsReader extends BaseCacheReader {
|
|
11
37
|
endpoint: WfsEndpoint
|
|
12
38
|
featureTypeName: string
|
|
@@ -53,7 +79,7 @@ export class WfsReader extends BaseCacheReader {
|
|
|
53
79
|
}
|
|
54
80
|
|
|
55
81
|
static async createReader(wfsUrlEndpoint: string, featureTypeName?: string) {
|
|
56
|
-
const wfsEndpoint = await
|
|
82
|
+
const wfsEndpoint = await getWfsEndpoint(wfsUrlEndpoint)
|
|
57
83
|
const featureTypes = wfsEndpoint.getFeatureTypes()
|
|
58
84
|
const featureType = wfsEndpoint.getFeatureTypeSummary(
|
|
59
85
|
featureTypes.length === 1 && !featureTypeName
|