@@ -5,13 +5,16 @@ import { environment } from '../../../environments/environment';
55import { catchError , map , take } from 'rxjs/operators' ;
66import { hasValue } from '../../shared/empty.util' ;
77
8- export interface LocationCoordinates {
9- latitude : number ,
10- longitude : number ,
8+ /**
9+ * Geographic coordinates in Decimal Degree notation
10+ */
11+ export interface LocationDDCoordinates {
12+ latitude : number | string ,
13+ longitude : number | string ,
1114}
1215
1316export interface LocationPlace {
14- coordinates ?: LocationCoordinates ,
17+ coordinates ?: LocationDDCoordinates ,
1518 displayName ?: string ,
1619}
1720
@@ -22,7 +25,8 @@ export enum LocationErrorCodes {
2225 API_ERROR = 'api-error' ,
2326}
2427
25- const IS_COORDINATE_PAIR_REGEXP = / ^ \d + \. ? \d * , \d + \. ? \d * $ / ;
28+ const IS_DD_COORDINATE_PAIR_REGEXP = / ^ \d + \. ? \d * , \d + \. ? \d * $ / ;
29+ const IS_SG_COORDINATE_PAIR_REGEXP = / ^ [ N S ] * \d + ° * \d + [ ' ′ ] * \d + (?: " | ″ | \. \d + ) , ? * [ E W ] * \d + ° * \d + [ ' ′ ] * \d + (?: " | ″ | \. \d + ) | \d + ° * \d + [ ' ′ ] * \d + (?: " | ″ | \. \d + ) * [ N S ] , ? * \d + ° * \d + [ ' ′ ] * \d + (?: " | ″ | \. \d + ) * [ E W ] $ / ;
2630
2731const NOMINATIM_RESPONSE_FORMAT = 'jsonv2' ;
2832
@@ -39,11 +43,11 @@ export class LocationService {
3943 ) { }
4044
4145 /**
42- * Search a place (address or POI) with Nominatim, and return the coordinates and the display name of the most relevant search result
46+ * Search Nominatim for a place (address or POI), then find the coordinates and the display name of the most relevant search result
4347 * @param address the place to be searched
4448 * @returns {LocationPlace } the information related to the searched place
4549 */
46- public searchPlace ( address : string ) : Observable < LocationPlace > {
50+ public findPlaceCoordinates ( address : string ) : Observable < LocationPlace > {
4751 let params = new HttpParams ( ) . append ( 'q' , address ) . append ( 'format' , NOMINATIM_RESPONSE_FORMAT ) ;
4852
4953 return this . http . get < Record < string , any > [ ] > ( this . searchEndpoint , { params : params } ) . pipe (
@@ -58,15 +62,15 @@ export class LocationService {
5862 }
5963 if ( searchResults . length > 0 ) {
6064 const firstMatch = searchResults [ 0 ] ;
61- const coordinates : LocationCoordinates = {
65+ const coordinates : LocationDDCoordinates = {
6266 latitude : parseFloat ( firstMatch . lat ) ,
6367 longitude : parseFloat ( firstMatch . lon ) ,
6468 } ;
65- const info : LocationPlace = {
69+ const place : LocationPlace = {
6670 coordinates : coordinates ,
6771 displayName : firstMatch . display_name ,
6872 } ;
69- return info ;
73+ return place ;
7074 } else {
7175 console . warn ( 'Location service' , `Location "${ address } " not found` ) ;
7276 throw Error ( LocationErrorCodes . LOCATION_NOT_FOUND ) ;
@@ -76,18 +80,53 @@ export class LocationService {
7680 }
7781
7882 /**
79- * Search coordinates with Nominatim and return the display name
83+ * Search Nominatim for coordinates, then return the decimal coordinates and the display name
84+ * @param coordinates the coordinates, in any supported format
85+ * @returns {LocationPlace } the information related to the searched coordinates
86+ */
87+ public findPlaceAndDecimalCoordinates ( coordinates : string ) : Observable < LocationPlace > {
88+ let params = new HttpParams ( ) . append ( 'q' , coordinates ) . append ( 'format' , NOMINATIM_RESPONSE_FORMAT ) ;
89+
90+ return this . http . get < Record < string , any > [ ] > ( this . searchEndpoint , { params : params } ) . pipe (
91+ catchError ( ( err ) => {
92+ console . error ( 'Location service' , err ) ;
93+ throw Error ( LocationErrorCodes . API_ERROR ) ;
94+ } ) ,
95+ take ( 1 ) ,
96+ map ( ( searchResults ) => {
97+ if ( searchResults . length > 0 ) {
98+ const firstMatch = searchResults [ 0 ] ;
99+ const decimalCoordinates : LocationDDCoordinates = {
100+ latitude : parseFloat ( firstMatch . lat ) ,
101+ longitude : parseFloat ( firstMatch . lon ) ,
102+ } ;
103+ const place : LocationPlace = {
104+ coordinates : decimalCoordinates ,
105+ displayName : firstMatch . display_name ,
106+ } ;
107+ return place ;
108+ } else {
109+ console . warn ( 'Location service' , `Invalid coordinates ${ coordinates } ` ) ;
110+ throw Error ( LocationErrorCodes . INVALID_COORDINATES ) ;
111+ }
112+ } ) ,
113+ ) ;
114+ }
115+
116+ /**
117+ * Search Nominatim for a place by coordinates, and return the display name
80118 * @param coordinates
81119 * @returns {Observable<string> } the display name for the coordinates
82120 */
83- public searchCoordinates ( coordinates : LocationCoordinates ) : Observable < string > {
121+ public searchByCoordinates ( coordinates : LocationDDCoordinates ) : Observable < string > {
84122 let params = new HttpParams ( )
85123 . append ( 'lat' , coordinates . latitude )
86124 . append ( 'lon' , coordinates . longitude )
87125 . append ( 'format' , NOMINATIM_RESPONSE_FORMAT ) ;
88126
89127 return this . http . get < Record < string , any > > ( this . reverseSearchEndpoint , { params : params } ) . pipe (
90128 catchError ( ( err ) => {
129+ console . error ( 'Location service' , err ) ;
91130 throw Error ( LocationErrorCodes . API_ERROR ) ;
92131 } ) ,
93132 take ( 1 ) ,
@@ -107,7 +146,7 @@ export class LocationService {
107146 * @param longitude the longitude as float number
108147 * @returns {boolean } whether the coordinates are valid
109148 */
110- public isValidCoordinatePair ( latitude : number , longitude : number ) : boolean {
149+ public isValidDecimalCoordinatePair ( latitude : number , longitude : number ) : boolean {
111150 return ! ( isNaN ( latitude ) || isNaN ( longitude ) || latitude > 90 || latitude < - 90 || longitude > 180 || longitude < - 180 ) ;
112151 }
113152
@@ -116,21 +155,30 @@ export class LocationService {
116155 * @param coordinateString the string to be checked
117156 * @returns {boolean } whether the string has a valid format
118157 */
119- public isCoordinateString ( coordinateString : string ) : boolean {
120- return IS_COORDINATE_PAIR_REGEXP . test ( coordinateString ) && coordinateString . split ( ',' ) . length === 2 ;
158+ public isDecimalCoordinateString ( coordinateString : string ) : boolean {
159+ return IS_DD_COORDINATE_PAIR_REGEXP . test ( coordinateString ) && coordinateString . split ( ',' ) . length === 2 ;
160+ }
161+
162+ /**
163+ * Check if a string is in the format `latitude,longitude`
164+ * @param coordinateString the string to be checked
165+ * @returns {boolean } whether the string has a valid format
166+ */
167+ public isSexagesimalCoordinateString ( coordinateString : string ) : boolean {
168+ return IS_SG_COORDINATE_PAIR_REGEXP . test ( coordinateString ) ;
121169 }
122170
123171 /**
124172 * Check if a string contains valid coordinateString in the format `latitude,longitude`
125173 * @param coordinateString the string to be checked
126- * @returns {boolean } whether the string is valid, and it contains valid coordinateString
174+ * @returns {boolean } whether the string is valid and it contains valid coordinateString
127175 */
128176 public isValidCoordinateString ( coordinateString : string ) : boolean {
129- if ( this . isCoordinateString ( coordinateString ) ) {
177+ if ( this . isDecimalCoordinateString ( coordinateString ) ) {
130178 const coordinateArray = coordinateString . split ( ',' ) ;
131- const latitude = parseFloat ( coordinateArray [ 0 ] ?. trim ( ) ) ;
132- const longitude = parseFloat ( coordinateArray [ 1 ] ?. trim ( ) ) ;
133- return ! isNaN ( latitude ) && ! isNaN ( longitude ) && this . isValidCoordinatePair ( latitude , longitude ) ;
179+ const latitude = parseFloat ( coordinateArray [ 0 ] ) ;
180+ const longitude = parseFloat ( coordinateArray [ 1 ] ) ;
181+ return ! isNaN ( latitude ) && ! isNaN ( longitude ) && this . isValidDecimalCoordinatePair ( latitude , longitude ) ;
134182 } else {
135183 return false ;
136184 }
@@ -139,13 +187,13 @@ export class LocationService {
139187 /**
140188 * Parse a string containing location coordinates, and return an object containing latitude and longitude as numbers
141189 * @param coordinates a string in the format `latitude,longitude`
142- * @returns {LocationCoordinates } the parsed coordinates
190+ * @returns {LocationDDCoordinates } the parsed coordinates
143191 */
144- public parseCoordinates ( coordinates : string ) : LocationCoordinates {
192+ public parseCoordinates ( coordinates : string ) : LocationDDCoordinates {
145193 const coordinateArr = coordinates . split ( ',' ) ;
146194 if ( this . isValidCoordinateString ( coordinates ) ) {
147- const latitude = parseFloat ( coordinateArr [ 0 ] ?. trim ( ) ) ;
148- const longitude = parseFloat ( coordinateArr [ 1 ] ?. trim ( ) ) ;
195+ const latitude = parseFloat ( coordinateArr [ 0 ] ) ;
196+ const longitude = parseFloat ( coordinateArr [ 1 ] ) ;
149197 return { latitude, longitude } ;
150198 } else {
151199 console . warn ( 'Location service' , `Invalid coordinates "${ coordinates } "` ) ;
0 commit comments