Skip to content

Commit 7547c79

Browse files
committed
geocat: add location filter in search & location input in home page
1 parent ff3dae2 commit 7547c79

32 files changed

+935
-78
lines changed

apps/datahub/src/app/app.module.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
EXTERNAL_VIEWER_URL_TEMPLATE,
1414
FeatureRecordModule,
1515
GN_UI_VERSION,
16-
WEB_COMPONENT_EMBEDDER_URL,
1716
RecordMetaComponent,
17+
WEB_COMPONENT_EMBEDDER_URL,
1818
} from '@geonetwork-ui/feature/record'
1919
import {
2020
DefaultRouterModule,
@@ -27,6 +27,7 @@ import {
2727
import {
2828
FeatureSearchModule,
2929
FILTER_GEOMETRY,
30+
LocationSearchComponent,
3031
RECORD_URL_TOKEN,
3132
} from '@geonetwork-ui/feature/search'
3233
import {
@@ -102,6 +103,7 @@ import {
102103
matStarOutline,
103104
} from '@ng-icons/material-icons/outline'
104105
import { NgIconsModule, provideNgIconsConfig } from '@ng-icons/core'
106+
import { ORGANIZATIONS_STRATEGY } from '@geonetwork-ui/api/repository/gn4'
105107

106108
export const metaReducers: MetaReducer[] = !environment.production ? [] : []
107109

@@ -173,6 +175,7 @@ export const metaReducers: MetaReducer[] = !environment.production ? [] : []
173175
}),
174176
OrganisationsComponent,
175177
LanguageSwitcherComponent,
178+
LocationSearchComponent,
176179
],
177180
providers: [
178181
provideNgIconsConfig({
@@ -237,6 +240,10 @@ export const metaReducers: MetaReducer[] = !environment.production ? [] : []
237240
provide: ORGANIZATION_URL_TOKEN,
238241
useValue: `${ROUTER_ROUTE_SEARCH}?${ROUTE_PARAMS.PUBLISHER}=\${name}`,
239242
},
243+
{
244+
provide: ORGANIZATIONS_STRATEGY,
245+
useValue: 'groups',
246+
},
240247
{
241248
provide: DO_NOT_USE_DEFAULT_BASEMAP,
242249
useFactory: () => getOptionalMapConfig()?.DO_NOT_USE_DEFAULT_BASEMAP,

apps/datahub/src/app/home/home-header/home-header.component.html

+21-13
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,27 @@
1414
[style.opacity]="expandRatio"
1515
[innerHTML]="'datahub.header.title.html' | translate"
1616
></div>
17-
<gn-ui-fuzzy-search
18-
class="text-[18px] pointer-events-auto"
19-
style="
20-
--gn-ui-text-input-padding: 1.1em;
21-
--gn-ui-text-input-border-size: 0px;
22-
"
23-
(itemSelected)="onFuzzySearchSelection($event)"
24-
[autoFocus]="true"
25-
></gn-ui-fuzzy-search>
26-
<div
27-
class="flex flex-wrap h-0 py-5 gap-3"
28-
[style.opacity]="-0.6 + expandRatio * 2"
29-
>
17+
<div class="flex flex-wrap gap-4">
18+
<gn-ui-fuzzy-search
19+
class="text-[18px] grow pointer-events-auto"
20+
style="
21+
--gn-ui-text-input-padding: 1.1em;
22+
--gn-ui-text-input-border-size: 0px;
23+
"
24+
(itemSelected)="onFuzzySearchSelection($event)"
25+
(inputSubmitted)="updateLocationFilter()"
26+
[autoFocus]="true"
27+
></gn-ui-fuzzy-search>
28+
<gn-ui-location-search
29+
class="text-[18px] grow"
30+
style="
31+
--gn-ui-text-input-padding: 1.1em;
32+
--gn-ui-text-input-border-size: 0px;
33+
"
34+
(inputSubmitted)="updateTextFilter()"
35+
></gn-ui-location-search>
36+
</div>
37+
<div class="flex h-0 py-5 gap-3" [style.opacity]="-0.6 + expandRatio * 2">
3038
<datahub-header-badge-button
3139
[routerLink]="ROUTE_SEARCH"
3240
*ngIf="isAuthenticated$ | async"

apps/datahub/src/app/home/home-header/home-header.component.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
Input,
5+
ViewChild,
6+
} from '@angular/core'
27
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
38
import {
49
ROUTER_ROUTE_SEARCH,
510
RouterFacade,
611
} from '@geonetwork-ui/feature/router'
712
import {
813
FieldsService,
14+
FuzzySearchComponent,
15+
LocationSearchComponent,
916
SearchFacade,
1017
SearchService,
1118
} from '@geonetwork-ui/feature/search'
@@ -40,6 +47,11 @@ marker('datahub.header.popularRecords')
4047
export class HomeHeaderComponent {
4148
@Input() expandRatio: number
4249

50+
// specific geocat: used to trigger the other field when one is triggered
51+
@ViewChild(FuzzySearchComponent)
52+
textSearch: FuzzySearchComponent
53+
@ViewChild(LocationSearchComponent) locationSearch: LocationSearchComponent
54+
4355
backgroundCss =
4456
getThemeConfig().HEADER_BACKGROUND ||
4557
`center /cover url('assets/img/header_bg.webp')`
@@ -95,4 +107,12 @@ export class HomeHeaderComponent {
95107
this.searchService.setFilters(searchFilters)
96108
}
97109
}
110+
111+
// specific geocat
112+
updateLocationFilter() {
113+
this.locationSearch.trigger()
114+
}
115+
updateTextFilter() {
116+
this.textSearch.trigger()
117+
}
98118
}

libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.spec.ts

+18-9
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,24 @@ describe('ElasticsearchService', () => {
426426
query: 'Org:(world)',
427427
},
428428
},
429+
{
430+
geo_shape: {
431+
geom: {
432+
relation: 'intersects',
433+
shape: {
434+
coordinates: [
435+
[
436+
[3.017921158755172, 50.65759907920972],
437+
[3.017921158755172, 50.613483610573155],
438+
[3.1098886148436122, 50.613483610573155],
439+
[3.017921158755172, 50.65759907920972],
440+
],
441+
],
442+
type: 'Polygon',
443+
},
444+
},
445+
},
446+
},
429447
],
430448
must: [
431449
{
@@ -458,15 +476,6 @@ describe('ElasticsearchService', () => {
458476
boost: 10.0,
459477
},
460478
},
461-
{
462-
geo_shape: {
463-
geom: {
464-
shape: geojsonPolygon,
465-
relation: 'intersects',
466-
},
467-
boost: 7.0,
468-
},
469-
},
470479
],
471480
},
472481
})

libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts

+15-18
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ export class ElasticsearchService {
256256
string,
257257
unknown
258258
>[]
259-
260259
if (any) {
261260
must.push({
262261
query_string: {
@@ -282,26 +281,24 @@ export class ElasticsearchService {
282281
})
283282
}
284283
if (geometry) {
285-
should.push(
286-
{
287-
geo_shape: {
288-
geom: {
289-
shape: geometry,
290-
relation: 'within',
291-
},
292-
boost: 10.0,
284+
// geocat specific: exclude records outside of geometry
285+
should.push({
286+
geo_shape: {
287+
geom: {
288+
shape: geometry,
289+
relation: 'within',
293290
},
291+
boost: 10.0,
294292
},
295-
{
296-
geo_shape: {
297-
geom: {
298-
shape: geometry,
299-
relation: 'intersects',
300-
},
301-
boost: 7.0,
293+
})
294+
filter.push({
295+
geo_shape: {
296+
geom: {
297+
shape: geometry,
298+
relation: 'intersects',
302299
},
303-
}
304-
)
300+
},
301+
})
305302
}
306303

307304
return {

libs/feature/router/src/lib/default/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ export enum ROUTE_PARAMS {
88
SORT = '_sort',
99
PUBLISHER = 'publisher', // FIXME: this shouldn't be here as it is a search field
1010
PAGE = '_page',
11+
LOCATION = 'location',
12+
BBOX = 'bbox',
1113
}
1214
export type SearchRouteParams = Record<string, string | string[] | number>

libs/feature/router/src/lib/default/services/router-search.service.spec.ts

+55-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import { FieldsService, SearchFacade } from '@geonetwork-ui/feature/search'
1+
import {
2+
FieldsService,
3+
LocationBbox,
4+
SearchFacade,
5+
} from '@geonetwork-ui/feature/search'
26
import {
37
SortByEnum,
48
SortByField,
59
} from '@geonetwork-ui/common/domain/model/search'
610
import { BehaviorSubject, of } from 'rxjs'
711
import { RouterFacade } from '../state'
812
import { RouterSearchService } from './router-search.service'
13+
import { RouterService } from '../router.service'
914

1015
let state = {}
1116
class SearchFacadeMock {
@@ -16,6 +21,7 @@ class SearchFacadeMock {
1621
class RouterFacadeMock {
1722
setSearch = jest.fn()
1823
updateSearch = jest.fn()
24+
go = jest.fn()
1925
}
2026

2127
class FieldsServiceMock {
@@ -43,18 +49,29 @@ class FieldsServiceMock {
4349
)
4450
}
4551

52+
class RouterServiceMock {
53+
getSearchRoute = jest.fn().mockReturnValue('/test/path')
54+
}
55+
4656
describe('RouterSearchService', () => {
4757
let service: RouterSearchService
4858
let routerFacade: RouterFacade
4959
let searchFacade: SearchFacade
5060
let fieldsService: FieldsService
61+
let routerService: RouterService
5162

5263
beforeEach(() => {
5364
state = { OrgForResource: { mel: true } }
5465
routerFacade = new RouterFacadeMock() as any
5566
searchFacade = new SearchFacadeMock() as any
5667
fieldsService = new FieldsServiceMock() as any
57-
service = new RouterSearchService(searchFacade, routerFacade, fieldsService)
68+
routerService = new RouterServiceMock() as any
69+
service = new RouterSearchService(
70+
searchFacade,
71+
routerFacade,
72+
fieldsService,
73+
routerService
74+
)
5875
})
5976

6077
it('should be created', () => {
@@ -118,4 +135,40 @@ describe('RouterSearchService', () => {
118135
})
119136
})
120137
})
138+
139+
describe('#setLocationFilter', () => {
140+
beforeEach(() => {
141+
const location: LocationBbox = {
142+
label: 'New location',
143+
bbox: [4, 5, 6, 7],
144+
}
145+
service.setLocationFilter(location)
146+
})
147+
it('dispatch setLocationFilter with merged mapped params', () => {
148+
expect(routerFacade.go).toHaveBeenCalledWith({
149+
path: '/test/path',
150+
query: {
151+
location: 'New location',
152+
bbox: '4,5,6,7',
153+
},
154+
queryParamsHandling: 'merge',
155+
})
156+
})
157+
})
158+
159+
describe('#clearLocationFilter', () => {
160+
beforeEach(() => {
161+
service.clearLocationFilter()
162+
})
163+
it('dispatch clearLocationFilter with merged mapped params', () => {
164+
expect(routerFacade.go).toHaveBeenCalledWith({
165+
path: '/test/path',
166+
query: {
167+
location: undefined,
168+
bbox: undefined,
169+
},
170+
queryParamsHandling: 'merge',
171+
})
172+
})
173+
})
121174
})

libs/feature/router/src/lib/default/services/router-search.service.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Injectable } from '@angular/core'
22
import {
33
FieldsService,
4+
LocationBbox,
45
SearchFacade,
56
SearchServiceI,
67
} from '@geonetwork-ui/feature/search'
@@ -11,14 +12,16 @@ import {
1112
import { ROUTE_PARAMS, SearchRouteParams } from '../constants'
1213
import { RouterFacade } from '../state/router.facade'
1314
import { firstValueFrom } from 'rxjs'
15+
import { RouterService } from '../router.service'
1416
import { sortByToString } from '@geonetwork-ui/util/shared'
1517

1618
@Injectable()
1719
export class RouterSearchService implements SearchServiceI {
1820
constructor(
1921
private searchFacade: SearchFacade,
2022
private facade: RouterFacade,
21-
private fieldsService: FieldsService
23+
private fieldsService: FieldsService,
24+
private routerService: RouterService
2225
) {}
2326

2427
setSortAndFilters(filters: FieldFilters, sortBy: SortByField) {
@@ -65,4 +68,20 @@ export class RouterSearchService implements SearchServiceI {
6568
[ROUTE_PARAMS.PAGE]: page,
6669
})
6770
}
71+
72+
setLocationFilter(location: LocationBbox) {
73+
this.facade.go({
74+
path: this.routerService.getSearchRoute(),
75+
query: { location: location.label, bbox: location.bbox.join() },
76+
queryParamsHandling: 'merge',
77+
})
78+
}
79+
80+
clearLocationFilter() {
81+
this.facade.go({
82+
path: this.routerService.getSearchRoute(),
83+
query: { location: undefined, bbox: undefined },
84+
queryParamsHandling: 'merge',
85+
})
86+
}
6887
}

0 commit comments

Comments
 (0)