Skip to content

Commit 1609659

Browse files
[UXP-120] Refactoring
1 parent 57ffe38 commit 1609659

25 files changed

Lines changed: 545 additions & 345 deletions

src/app/core/geo-location/geo-location.service.ts

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { LocationService } from './location.service';
4+
import { HttpClient } from '@angular/common/http';
5+
6+
describe('LocationService', () => {
7+
let service: LocationService;
8+
9+
beforeEach(() => {
10+
TestBed.configureTestingModule({
11+
providers: [
12+
{ provide: HttpClient, useValue: {} },
13+
],
14+
});
15+
service = TestBed.inject(LocationService);
16+
});
17+
18+
it('should be created', () => {
19+
expect(service).toBeTruthy();
20+
});
21+
22+
describe('Test utility methods', () => {
23+
it('isCoordinateString() should validate coordinate strings correctly', () => {
24+
expect(service.isCoordinateString(null)).toBeFalse(); // invalid pattern
25+
expect(service.isCoordinateString(undefined)).toBeFalse(); // invalid pattern
26+
expect(service.isCoordinateString('qwerty')).toBeFalse(); // invalid pattern
27+
expect(service.isCoordinateString('45')).toBeFalse(); // invalid pattern, wrong array size
28+
expect(service.isCoordinateString('45,')).toBeFalse(); // invalid pattern, wrong array size
29+
expect(service.isCoordinateString(',45')).toBeFalse(); // invalid pattern, wrong array size
30+
expect(service.isCoordinateString('45,45')).toBeTrue();
31+
expect(service.isCoordinateString('45,45,')).toBeFalse(); // invalid pattern, wrong array size
32+
expect(service.isCoordinateString('45,45,45')).toBeFalse(); // invalid pattern, wrong array size
33+
expect(service.isCoordinateString('.0,.0')).toBeFalse(); // valid numbers, but invalid pattern
34+
expect(service.isCoordinateString('45.000,45.000')).toBeTrue();
35+
expect(service.isCoordinateString('45.000, 45.000')).toBeFalse(); // it contains a space
36+
expect(service.isCoordinateString('200,200')).toBeTrue(); // invalid numbers, but valid pattern
37+
});
38+
39+
it('isValidCoordinateString() should validate coordinate strings and check for their values correctly', () => {
40+
expect(service.isValidCoordinateString('45,45')).toBeTrue();
41+
expect(service.isValidCoordinateString('200,200')).toBeFalse(); // valid pattern, invalid numbers
42+
});
43+
44+
it('should validate coordinate pairs correctly', () => {
45+
expect(service.isValidCoordinatePair(0, 0)).toBeTrue();
46+
expect(service.isValidCoordinatePair(45, 45)).toBeTrue();
47+
expect(service.isValidCoordinatePair(-45, -45)).toBeTrue();
48+
expect(service.isValidCoordinatePair(200, 0)).toBeFalse();
49+
expect(service.isValidCoordinatePair(0, 200)).toBeFalse();
50+
expect(service.isValidCoordinatePair(-200, 0)).toBeFalse();
51+
expect(service.isValidCoordinatePair(0, -200)).toBeFalse();
52+
expect(service.isValidCoordinatePair(NaN, 0)).toBeFalse();
53+
expect(service.isValidCoordinatePair(0, NaN)).toBeFalse();
54+
});
55+
});
56+
});
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpClient, HttpParams } from '@angular/common/http';
3+
import { Observable } from 'rxjs';
4+
import { environment } from '../../../environments/environment';
5+
import { map, take } from 'rxjs/operators';
6+
import { hasValue } from '../../shared/empty.util';
7+
8+
export interface LocationCoordinates {
9+
latitude: number,
10+
longitude: number,
11+
}
12+
13+
export interface LocationPlace {
14+
coordinates?: LocationCoordinates,
15+
displayName?: string,
16+
}
17+
18+
export enum LocationErrorCodes {
19+
// define a `location.error.*` i18n label for each error code
20+
INVALID_COORDINATES = 'invalid-coordinates',
21+
LOCATION_NOT_FOUND = 'location-not-found',
22+
}
23+
24+
const IS_COORDINATE_PAIR_REGEXP = /^\d+\.?\d*,\d+\.?\d*$/;
25+
26+
const NOMINATIM_RESPONSE_FORMAT = 'jsonv2';
27+
28+
@Injectable({
29+
providedIn: 'root'
30+
})
31+
export class LocationService {
32+
33+
searchEndpoint = environment.location.nominatimApi.searchEndpoint;
34+
reverseSearchEndpoint = environment.location.nominatimApi.reverseSearchEndpoint;
35+
36+
constructor(
37+
protected http: HttpClient,
38+
) { }
39+
40+
/**
41+
* Search a place (address or POI) with Nominatim, and return the coordinates and the display name of the most relevant search result
42+
* @param address the place to be searched
43+
* @returns {LocationPlace} the information related to the searched place
44+
*/
45+
public searchPlace(address: string): Observable<LocationPlace> {
46+
let params = new HttpParams().append('q', address).append('format', NOMINATIM_RESPONSE_FORMAT);
47+
48+
return this.http.get<Record<string,any>[]>(this.searchEndpoint, { params: params }).pipe(
49+
take(1),
50+
map((searchResults) => {
51+
if (searchResults.length > 1) {
52+
console.warn(`Multiple locations found for address "${address}", showing top matches`, searchResults.slice(0,5));
53+
}
54+
if (searchResults.length > 0) {
55+
const firstMatch = searchResults[0];
56+
const coordinates: LocationCoordinates = {
57+
latitude: parseFloat(firstMatch.lat),
58+
longitude: parseFloat(firstMatch.lon),
59+
};
60+
const info: LocationPlace = {
61+
coordinates: coordinates,
62+
displayName: firstMatch.display_name,
63+
};
64+
return info;
65+
} else {
66+
console.warn(`Location service: location "${address}" not found`);
67+
throw new Error(LocationErrorCodes.LOCATION_NOT_FOUND);
68+
}
69+
}),
70+
);
71+
}
72+
73+
/**
74+
* Search coordinates with Nominatim and return the display name
75+
* @param coordinates
76+
* @returns {Observable<string>} the display name for the coordinates
77+
*/
78+
public searchCoordinates(coordinates: LocationCoordinates): Observable<string> {
79+
let params = new HttpParams()
80+
.append('lat', coordinates.latitude)
81+
.append('lon', coordinates.longitude)
82+
.append('format', NOMINATIM_RESPONSE_FORMAT);
83+
84+
return this.http.get<Record<string,any>>(this.reverseSearchEndpoint, { params: params }).pipe(
85+
take(1),
86+
map((searchResults) => {
87+
if (hasValue(searchResults.error)) {
88+
throw new Error(searchResults.error);
89+
} else {
90+
return searchResults.display_name;
91+
}
92+
}),
93+
);
94+
}
95+
96+
/**
97+
* Check if the provided pair of coordinates is valid
98+
* @param latitude the latitude as float number
99+
* @param longitude the longitude as float number
100+
* @returns {boolean} whether the coordinates are valid
101+
*/
102+
public isValidCoordinatePair(latitude: number, longitude: number): boolean {
103+
return !(isNaN(latitude) || isNaN(longitude) || latitude > 90 || latitude < -90 || longitude > 180 || longitude < -180);
104+
}
105+
106+
/**
107+
* Check if a string is in the format `latitude,longitude`
108+
* @param coordinateString the string to be checked
109+
* @returns {boolean} whether the string has a valid format
110+
*/
111+
public isCoordinateString(coordinateString: string): boolean {
112+
return IS_COORDINATE_PAIR_REGEXP.test(coordinateString) && coordinateString.split(',').length === 2;
113+
}
114+
115+
/**
116+
* Check if a string contains valid coordinateString in the format `latitude,longitude`
117+
* @param coordinateString the string to be checked
118+
* @returns {boolean} whether the string is valid and it contains valid coordinateString
119+
*/
120+
public isValidCoordinateString(coordinateString: string): boolean {
121+
if (this.isCoordinateString(coordinateString)) {
122+
const coordinateArray = coordinateString.split(',');
123+
const latitude = parseFloat(coordinateArray[0]);
124+
const longitude = parseFloat(coordinateArray[1]);
125+
return !isNaN(latitude) && !isNaN(longitude) && this.isValidCoordinatePair(latitude, longitude);
126+
} else {
127+
return false;
128+
}
129+
}
130+
131+
/**
132+
* Parse a string containing location coordinates, and return an object containing latitude and longitude as numbers
133+
* @param coordinates a string in the format `latitude,longitude`
134+
* @returns {LocationCoordinates} the parsed coordinates
135+
*/
136+
public parseCoordinates(coordinates: string): LocationCoordinates {
137+
const coordinateArr = coordinates.split(',');
138+
if (this.isValidCoordinateString(coordinates)) {
139+
const latitude = parseFloat(coordinateArr[0]);
140+
const longitude = parseFloat(coordinateArr[1]);
141+
return { latitude, longitude };
142+
} else {
143+
console.warn(`Location service: invalid coordinates "${coordinates}"`);
144+
throw new Error(LocationErrorCodes.INVALID_COORDINATES);
145+
}
146+
}
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<div *ngIf="(place | async )">
2+
<ds-open-street-map
3+
[coordinates]="(coordinates$ | async)"
4+
[pointers]="(pointers$ | async)"
5+
[showControlsZoom]="true"
6+
></ds-open-street-map>
7+
<div class="my-1 text-muted">{{ displayName$ | async }}</div>
8+
</div>
9+
10+
<div *ngIf="(invalidLocationErrorCode | async) as errorCode" class="text-danger">{{ 'location.error.' + errorCode | translate }}</div>

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/osmap/osmap.component.scss renamed to src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/open-street-map/open-street-map-rendering.component.scss

File renamed without changes.

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/osmap/osmap.component.spec.ts renamed to src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/open-street-map/open-street-map-rendering.component.spec.ts

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { LayoutField } from '../../../../../../../core/layout/models/box.model';
55
import { Item } from '../../../../../../../core/shared/item.model';
66
import { MetadataValue } from '../../../../../../../core/shared/metadata.models';
77
import { TranslateLoaderMock } from '../../../../../../../shared/mocks/translate-loader.mock';
8-
import { OsmapComponent } from './osmap.component';
8+
import { OpenStreetMapRenderingComponent } from './open-street-map-rendering.component';
9+
import { HttpClientTestingModule } from '@angular/common/http/testing';
910

10-
describe('OsmapComponent', () => {
11-
let component: OsmapComponent;
12-
let fixture: ComponentFixture<OsmapComponent>;
11+
describe('OpenStreetMapRenderingComponent', () => {
12+
let component: OpenStreetMapRenderingComponent;
13+
let fixture: ComponentFixture<OpenStreetMapRenderingComponent>;
1314

1415
const metadataValue = Object.assign(new MetadataValue(), {
1516
'value': '@42.1334,56.7654',
@@ -43,25 +44,30 @@ describe('OsmapComponent', () => {
4344

4445
beforeEach(async () => {
4546
await TestBed.configureTestingModule({
46-
imports: [TranslateModule.forRoot({
47-
loader: {
48-
provide: TranslateLoader,
49-
useClass: TranslateLoaderMock
50-
}
51-
}), BrowserAnimationsModule],
47+
imports: [
48+
TranslateModule.forRoot({
49+
loader: {
50+
provide: TranslateLoader,
51+
useClass: TranslateLoaderMock
52+
}
53+
}),
54+
BrowserAnimationsModule,
55+
HttpClientTestingModule,
56+
],
5257
providers: [
5358
{ provide: 'fieldProvider', useValue: mockField },
5459
{ provide: 'itemProvider', useValue: testItem },
5560
{ provide: 'metadataValueProvider', useValue: metadataValue },
5661
{ provide: 'renderingSubTypeProvider', useValue: '' },
62+
// { provide: HttpClient, useValue: {} },
5763
],
58-
declarations: [ OsmapComponent ]
64+
declarations: [ OpenStreetMapRenderingComponent ]
5965
})
6066
.compileComponents();
6167
});
6268

6369
beforeEach(() => {
64-
fixture = TestBed.createComponent(OsmapComponent);
70+
fixture = TestBed.createComponent(OpenStreetMapRenderingComponent);
6571
component = fixture.componentInstance;
6672
fixture.detectChanges();
6773
});
@@ -70,18 +76,4 @@ describe('OsmapComponent', () => {
7076
expect(component).toBeTruthy();
7177
});
7278

73-
// it('should rendered google map.',() => {
74-
// component.coordinates = '@42.1334,56.7654';
75-
// fixture.detectChanges();
76-
// const container = fixture.debugElement.query(By.css('#googlemap'));
77-
// expect(container).toBeTruthy();
78-
// });
79-
80-
// it('should not rendered google map.',() => {
81-
// component.coordinates = undefined;
82-
// fixture.detectChanges();
83-
// const container = fixture.debugElement.query(By.css('#googlemap'));
84-
// expect(container).toBeFalsy();
85-
// });
86-
8779
});

0 commit comments

Comments
 (0)