@geosdi/ngx-geocoding 1.2.1 → 2.0.0
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/README.md +40 -12
- package/fesm2022/geosdi-ngx-geocoding.mjs +324 -564
- package/fesm2022/geosdi-ngx-geocoding.mjs.map +1 -1
- package/package.json +3 -4
- package/types/geosdi-ngx-geocoding.d.ts +167 -175
|
@@ -1,584 +1,344 @@
|
|
|
1
|
-
import * as i0 from
|
|
2
|
-
import {
|
|
3
|
-
import { HttpClient } from
|
|
4
|
-
import {
|
|
5
|
-
import { catchError,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { Injectable, InjectionToken, NgModule, inject } from "@angular/core";
|
|
3
|
+
import { HttpClient } from "@angular/common/http";
|
|
4
|
+
import { Observable, Subject, from, of, throwError } from "rxjs";
|
|
5
|
+
import { catchError, map, switchMap, tap } from "rxjs/operators";
|
|
6
|
+
var NgxGeocodingModule = class NgxGeocodingModule {
|
|
7
|
+
static {
|
|
8
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
9
|
+
minVersion: "12.0.0",
|
|
10
|
+
version: "22.2.4",
|
|
11
|
+
ngImport: i0,
|
|
12
|
+
type: NgxGeocodingModule,
|
|
13
|
+
deps: [],
|
|
14
|
+
target: i0.ɵɵFactoryTarget.NgModule
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
static {
|
|
18
|
+
this.ɵmod = i0.ɵɵngDeclareNgModule({
|
|
19
|
+
minVersion: "14.0.0",
|
|
20
|
+
version: "22.2.4",
|
|
21
|
+
ngImport: i0,
|
|
22
|
+
type: NgxGeocodingModule
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
static {
|
|
26
|
+
this.ɵinj = i0.ɵɵngDeclareInjector({
|
|
27
|
+
minVersion: "12.0.0",
|
|
28
|
+
version: "22.2.4",
|
|
29
|
+
ngImport: i0,
|
|
30
|
+
type: NgxGeocodingModule
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
35
|
+
minVersion: "12.0.0",
|
|
36
|
+
version: "22.2.4",
|
|
37
|
+
ngImport: i0,
|
|
38
|
+
type: NgxGeocodingModule,
|
|
39
|
+
decorators: [{
|
|
40
|
+
type: NgModule,
|
|
41
|
+
args: [{
|
|
42
|
+
declarations: [],
|
|
43
|
+
imports: [],
|
|
44
|
+
exports: []
|
|
45
|
+
}]
|
|
46
|
+
}]
|
|
47
|
+
});
|
|
33
48
|
const NGX_GEOCODING_CONFIG = new InjectionToken("NGX_GEOCODING_CONFIG");
|
|
34
|
-
/**
|
|
35
|
-
* Dichiara da dove arrivano gli indirizzi, nei `providers` dell'applicazione :
|
|
36
|
-
*
|
|
37
|
-
* ```ts
|
|
38
|
-
* provideNgxGeocoding({
|
|
39
|
-
* sources: ["google", "nominatim"],
|
|
40
|
-
* country: "it",
|
|
41
|
-
* bias: { lat: 40.83, lon: 14.11, radiusM: 25_000 },
|
|
42
|
-
* })
|
|
43
|
-
* ```
|
|
44
|
-
*
|
|
45
|
-
* L'ordine è quello in cui le sorgenti si provano, e una che non è utilizzabile
|
|
46
|
-
* si salta : la stessa riga vale quindi per il deploy che ha la chiave Google e
|
|
47
|
-
* per quello che non ce l'ha.
|
|
48
|
-
*
|
|
49
|
-
* Chi non la chiama non cambia niente.
|
|
50
|
-
*/
|
|
51
49
|
function provideNgxGeocoding(config) {
|
|
52
|
-
|
|
50
|
+
return {
|
|
51
|
+
provide: NGX_GEOCODING_CONFIG,
|
|
52
|
+
useValue: config
|
|
53
|
+
};
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* L'SDK Maps JS dell'applicazione ospite, e l'attesa perché sia pronto.
|
|
57
|
-
*
|
|
58
|
-
* **Questa libreria non lo carica** : il tag lo mette in pagina chi la usa, con
|
|
59
|
-
* la propria chiave —
|
|
60
|
-
*
|
|
61
|
-
* ```html
|
|
62
|
-
* <script defer src="https://maps.googleapis.com/maps/api/js?key=…&libraries=places&language=it"></script>
|
|
63
|
-
* ```
|
|
64
|
-
*
|
|
65
|
-
* — e le ragioni perché resti così sono due : la chiave è dell'applicazione e
|
|
66
|
-
* non deve passare di qui, e caricarlo una seconda volta vorrebbe dire due
|
|
67
|
-
* copie dell'SDK nella stessa pagina.
|
|
68
|
-
*
|
|
69
|
-
* Serve però **aspettarlo** : quel tag è di norma `defer`, quindi chi cerca un
|
|
70
|
-
* indirizzo subito dopo l'avvio può arrivare prima di lui.
|
|
71
|
-
*
|
|
72
|
-
* Della query contano due parametri : `libraries=places`, senza cui non c'è
|
|
73
|
-
* niente da aspettare, e `language`, che decide la lingua delle proposte —
|
|
74
|
-
* `getPlacePredictions` non ha un parametro per chiederla.
|
|
75
|
-
*
|
|
76
|
-
* @author Vito Salvia - CNR IMAA geoSDI Group
|
|
77
|
-
* @email vito.salvia@gmail.com
|
|
78
|
-
*/
|
|
79
|
-
/** Quanto si aspetta l'SDK prima di rinunciare, e ogni quanto lo si guarda. */
|
|
80
55
|
const ATTESA = {
|
|
81
|
-
|
|
82
|
-
|
|
56
|
+
tetto_ms: 1e4,
|
|
57
|
+
passo_ms: 100
|
|
83
58
|
};
|
|
84
|
-
/** Gli stati dell'SDK su cui si decide qualcosa. */
|
|
85
59
|
const STATO_PLACES = {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
60
|
+
ok: "OK",
|
|
61
|
+
nessuno: "ZERO_RESULTS",
|
|
62
|
+
negato: "REQUEST_DENIED",
|
|
63
|
+
quota: "OVER_QUERY_LIMIT"
|
|
90
64
|
};
|
|
91
|
-
/** La libreria `places` se è già in pagina, `undefined` altrimenti. */
|
|
92
65
|
function places() {
|
|
93
|
-
|
|
66
|
+
return window.google?.maps?.places;
|
|
94
67
|
}
|
|
95
|
-
/** Il costruttore del `Geocoder`, che sta nell'SDK di base. */
|
|
96
68
|
function geocoder() {
|
|
97
|
-
|
|
69
|
+
return window.google?.maps?.Geocoder;
|
|
98
70
|
}
|
|
99
|
-
/**
|
|
100
|
-
* La libreria `places`, aspettando che l'SDK finisca di caricarsi.
|
|
101
|
-
*
|
|
102
|
-
* Rifiuta dopo {@link ATTESA} : il chiamante ripiega sulla strada successiva,
|
|
103
|
-
* perché una ricerca che non risponde mai è peggio di una che risponde meno
|
|
104
|
-
* bene. Il motivo tipico di un'attesa che scade è che al tag dello script manchi
|
|
105
|
-
* `libraries=places` — l'SDK c'è, ma quella libreria no.
|
|
106
|
-
*/
|
|
107
71
|
function attendiPlaces() {
|
|
108
|
-
|
|
72
|
+
return attendi(places, "la libreria 'places' (manca `libraries=places` ?)");
|
|
109
73
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Il `Geocoder`, aspettando l'SDK.
|
|
112
|
-
*
|
|
113
|
-
* **Non vuole `libraries=places`** : sta nell'SDK di base, ed è la ragione per
|
|
114
|
-
* cui regge da ultima spiaggia anche dove Places non è disponibile.
|
|
115
|
-
*/
|
|
116
74
|
function attendiGeocoder() {
|
|
117
|
-
|
|
75
|
+
return attendi(geocoder, "il Geocoder").then((costruttore) => new costruttore());
|
|
118
76
|
}
|
|
119
|
-
/** L'attesa vera, uguale per le due parti dell'SDK. */
|
|
120
77
|
function attendi(cerca, cosa) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
78
|
+
const gia = cerca();
|
|
79
|
+
if (gia) return Promise.resolve(gia);
|
|
80
|
+
return new Promise((risolvi, rifiuta) => {
|
|
81
|
+
const scadenza = Date.now() + ATTESA.tetto_ms;
|
|
82
|
+
const attesa = setInterval(() => {
|
|
83
|
+
const pronto = cerca();
|
|
84
|
+
if (pronto) {
|
|
85
|
+
clearInterval(attesa);
|
|
86
|
+
risolvi(pronto);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (Date.now() > scadenza) {
|
|
90
|
+
clearInterval(attesa);
|
|
91
|
+
rifiuta(/* @__PURE__ */ new Error(`SDK Maps senza ${cosa}`));
|
|
92
|
+
}
|
|
93
|
+
}, ATTESA.passo_ms);
|
|
94
|
+
});
|
|
139
95
|
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* @author Vito Salvia - CNR IMAA geoSDI Group
|
|
143
|
-
* @email vito.salvia@gmail.com
|
|
144
|
-
*/
|
|
145
|
-
/** Il servizio pubblico di OpenStreetMap, quando non se ne dichiara un altro. */
|
|
146
96
|
const NOMINATIM = "https://nominatim.openstreetmap.org/search.php";
|
|
147
|
-
/** Le sorgenti da provare quando l'applicazione non dice niente. */
|
|
148
97
|
const SOLO_NOMINATIM = ["nominatim"];
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
* errore**, è una ricerca senza esiti, quindi non fa scendere di gradino.
|
|
394
|
-
*
|
|
395
|
-
* Se la classe non c'è nell'SDK — versione più vecchia — si solleva subito,
|
|
396
|
-
* che è il modo di scendere senza aspettare una risposta che non arriverà.
|
|
397
|
-
*/
|
|
398
|
-
daSuggestion(address) {
|
|
399
|
-
const paese = this.config?.country;
|
|
400
|
-
const cerchio = this.cerchio();
|
|
401
|
-
return from(attendiPlaces()).pipe(switchMap((places) => {
|
|
402
|
-
const nuova = places.AutocompleteSuggestion;
|
|
403
|
-
if (!nuova) {
|
|
404
|
-
return throwError(() => new Error("AutocompleteSuggestion non c'è in questo SDK"));
|
|
405
|
-
}
|
|
406
|
-
return from(nuova.fetchAutocompleteSuggestions({
|
|
407
|
-
input: address,
|
|
408
|
-
...(paese
|
|
409
|
-
? { includedRegionCodes: [paese], region: paese }
|
|
410
|
-
: {}),
|
|
411
|
-
...(cerchio ? { locationBias: cerchio } : {}),
|
|
412
|
-
})).pipe(map((risposta) => (risposta?.suggestions ?? [])
|
|
413
|
-
.map((s) => s.placePrediction)
|
|
414
|
-
.filter((p) => !!p?.placeId)
|
|
415
|
-
.map((p) => ({
|
|
416
|
-
place_id: p.placeId,
|
|
417
|
-
display_name: p.text?.toString() ?? "",
|
|
418
|
-
}))));
|
|
419
|
-
}));
|
|
420
|
-
}
|
|
421
|
-
/** Le proposte incrementali di `AutocompleteService`. */
|
|
422
|
-
daPlaces(address) {
|
|
423
|
-
const paese = this.config?.country;
|
|
424
|
-
const cerchio = this.cerchio();
|
|
425
|
-
return from(attendiPlaces()).pipe(switchMap((places) => new Observable((chi) => {
|
|
426
|
-
new places.AutocompleteService().getPlacePredictions({
|
|
427
|
-
input: address,
|
|
428
|
-
...(paese
|
|
429
|
-
? {
|
|
430
|
-
componentRestrictions: {
|
|
431
|
-
country: paese,
|
|
432
|
-
},
|
|
433
|
-
}
|
|
434
|
-
: {}),
|
|
435
|
-
...(cerchio ? { locationBias: cerchio } : {}),
|
|
436
|
-
}, (proposte, stato) => {
|
|
437
|
-
if (stato === STATO_PLACES.nessuno) {
|
|
438
|
-
chi.next([]);
|
|
439
|
-
chi.complete();
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
if (stato !== STATO_PLACES.ok) {
|
|
443
|
-
chi.error(new Error(stato));
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
chi.next((proposte ?? [])
|
|
447
|
-
.filter((p) => !!p.place_id)
|
|
448
|
-
.map((p) => ({
|
|
449
|
-
place_id: p.place_id,
|
|
450
|
-
display_name: p.description ?? "",
|
|
451
|
-
})));
|
|
452
|
-
chi.complete();
|
|
453
|
-
});
|
|
454
|
-
})));
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Il `Geocoder`, l'ultima strada di Google.
|
|
458
|
-
*
|
|
459
|
-
* I suoi risultati **portano già le coordinate**, quindi su questi
|
|
460
|
-
* {@link resolve} non fa nessuna richiesta in più.
|
|
461
|
-
*/
|
|
462
|
-
daGeocoder(address) {
|
|
463
|
-
const paese = this.config?.country;
|
|
464
|
-
const riquadro = this.riquadro();
|
|
465
|
-
return from(attendiGeocoder()).pipe(switchMap((geocoder) => new Observable((chi) => {
|
|
466
|
-
geocoder.geocode({
|
|
467
|
-
address,
|
|
468
|
-
...(paese
|
|
469
|
-
? {
|
|
470
|
-
componentRestrictions: {
|
|
471
|
-
country: paese,
|
|
472
|
-
},
|
|
473
|
-
region: paese,
|
|
474
|
-
}
|
|
475
|
-
: {}),
|
|
476
|
-
...(riquadro ? { bounds: riquadro } : {}),
|
|
477
|
-
}, (risultati, stato) => {
|
|
478
|
-
if (stato === STATO_PLACES.nessuno) {
|
|
479
|
-
chi.next([]);
|
|
480
|
-
chi.complete();
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
if (stato !== STATO_PLACES.ok) {
|
|
484
|
-
chi.error(new Error(stato));
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
chi.next((risultati ?? []).map((r) => ({
|
|
488
|
-
place_id: r.place_id,
|
|
489
|
-
display_name: r.formatted_address ?? "",
|
|
490
|
-
lat: this.testo(r.geometry?.location?.lat()),
|
|
491
|
-
lon: this.testo(r.geometry?.location?.lng()),
|
|
492
|
-
})));
|
|
493
|
-
chi.complete();
|
|
494
|
-
});
|
|
495
|
-
})));
|
|
496
|
-
}
|
|
497
|
-
/** Il bias come lo vuole `AutocompleteService`. */
|
|
498
|
-
cerchio() {
|
|
499
|
-
const bias = this.config?.bias;
|
|
500
|
-
return bias
|
|
501
|
-
? {
|
|
502
|
-
center: { lat: bias.lat, lng: bias.lon },
|
|
503
|
-
radius: bias.radiusM,
|
|
504
|
-
}
|
|
505
|
-
: undefined;
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
|
-
* Il bias come lo vuole il `Geocoder`, che prende un **riquadro** e non un
|
|
509
|
-
* cerchio : lo si iscrive attorno al punto. La longitudine si stringe con
|
|
510
|
-
* la latitudine (`cos`), o alle nostre latitudini il riquadro verrebbe
|
|
511
|
-
* largo un terzo in più del voluto.
|
|
512
|
-
*/
|
|
513
|
-
riquadro() {
|
|
514
|
-
const bias = this.config?.bias;
|
|
515
|
-
if (!bias)
|
|
516
|
-
return undefined;
|
|
517
|
-
const dLat = bias.radiusM / METRI_PER_GRADO;
|
|
518
|
-
const dLon = dLat / Math.max(Math.cos((bias.lat * Math.PI) / 180), 0.01);
|
|
519
|
-
return {
|
|
520
|
-
north: bias.lat + dLat,
|
|
521
|
-
south: bias.lat - dLat,
|
|
522
|
-
east: bias.lon + dLon,
|
|
523
|
-
west: bias.lon - dLon,
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
/** Le sorgenti dichiarate, o quella storica. */
|
|
527
|
-
sorgenti() {
|
|
528
|
-
const dichiarate = this.config?.sources ?? [];
|
|
529
|
-
return dichiarate.length ? dichiarate : SOLO_NOMINATIM;
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* Perché una strada ha detto di no, in una riga : dall'SDK arriva uno stato
|
|
533
|
-
* (`REQUEST_DENIED` per la chiave senza il prodotto abilitato o per il
|
|
534
|
-
* referrer, `OVER_QUERY_LIMIT` per la quota), da una http il corpo
|
|
535
|
-
* d'errore. Senza questo il motivo resta dentro un oggetto che in console
|
|
536
|
-
* va aperto, e si legge solo «si ripiega».
|
|
537
|
-
*/
|
|
538
|
-
perche(err) {
|
|
539
|
-
if (err instanceof Error && err.message)
|
|
540
|
-
return err.message;
|
|
541
|
-
const dettaglio = err?.error?.error;
|
|
542
|
-
if (!dettaglio)
|
|
543
|
-
return "motivo non dichiarato";
|
|
544
|
-
return [dettaglio.status, dettaglio.message]
|
|
545
|
-
.filter((pezzo) => !!pezzo)
|
|
546
|
-
.join(" : ");
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Una coordinata come stringa, o `undefined` se non c'è.
|
|
550
|
-
*
|
|
551
|
-
* `String(undefined)` darebbe `"undefined"`, che passerebbe indenne per
|
|
552
|
-
* ogni controllo di presenza e morirebbe in `Number()` — cioè una mappa
|
|
553
|
-
* portata su `NaN, NaN` senza che niente lo dica.
|
|
554
|
-
*/
|
|
555
|
-
testo(valore) {
|
|
556
|
-
return valore === null || valore === undefined
|
|
557
|
-
? undefined
|
|
558
|
-
: String(valore);
|
|
559
|
-
}
|
|
560
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgxGeocodingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
561
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgxGeocodingService, providedIn: "root" }); }
|
|
562
|
-
}
|
|
563
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgxGeocodingService, decorators: [{
|
|
564
|
-
type: Injectable,
|
|
565
|
-
args: [{
|
|
566
|
-
providedIn: "root",
|
|
567
|
-
}]
|
|
568
|
-
}] });
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* @author Vito Salvia - CNR IMAA geoSDI Group
|
|
572
|
-
* @email vito.salvia@gmail.com
|
|
573
|
-
*/
|
|
574
|
-
|
|
575
|
-
/*
|
|
576
|
-
* Public API Surface of ngx-geocoding
|
|
577
|
-
*/
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* Generated bundle index. Do not edit.
|
|
581
|
-
*/
|
|
582
|
-
|
|
98
|
+
const METRI_PER_GRADO = 111320;
|
|
99
|
+
var NgxGeocodingService = class NgxGeocodingService {
|
|
100
|
+
constructor() {
|
|
101
|
+
this.subjectLocation = new Subject();
|
|
102
|
+
this.subjectNoResults = new Subject();
|
|
103
|
+
this.subjectRemove = new Subject();
|
|
104
|
+
this.http = inject(HttpClient);
|
|
105
|
+
this.config = inject(NGX_GEOCODING_CONFIG, { optional: true });
|
|
106
|
+
this.spente = /* @__PURE__ */ new Set();
|
|
107
|
+
this.suggestionSpenta = false;
|
|
108
|
+
this.placesSpenta = false;
|
|
109
|
+
}
|
|
110
|
+
geocode(address) {
|
|
111
|
+
return this.daSorgenti(address, this.sorgenti());
|
|
112
|
+
}
|
|
113
|
+
resolve(place) {
|
|
114
|
+
if (!place?.place_id || place.lat && place.lon) return of(place);
|
|
115
|
+
if (this.spente.has("google")) return of(place);
|
|
116
|
+
if (!this.sorgenti().includes("google")) return of(place);
|
|
117
|
+
if (!this.suggestionSpenta) return this.luogoDaSuggestion(place).pipe(catchError((err) => {
|
|
118
|
+
this.suggestionSpenta = true;
|
|
119
|
+
console.warn(`[ngx-geocoding] dettaglio da Places (New) non riuscito, si prova la legacy — ${this.perche(err)}`);
|
|
120
|
+
return this.luogoDaLegacy(place);
|
|
121
|
+
}));
|
|
122
|
+
if (this.placesSpenta) return of(place);
|
|
123
|
+
return this.luogoDaLegacy(place);
|
|
124
|
+
}
|
|
125
|
+
luogoDaSuggestion(place) {
|
|
126
|
+
return from(attendiPlaces()).pipe(switchMap((places) => {
|
|
127
|
+
const Luogo = places.Place;
|
|
128
|
+
if (!Luogo) return throwError(() => /* @__PURE__ */ new Error("Place non c'è in questo SDK"));
|
|
129
|
+
const luogo = new Luogo({ id: place.place_id });
|
|
130
|
+
return from(luogo.fetchFields({ fields: ["location", "formattedAddress"] })).pipe(map(() => ({
|
|
131
|
+
...place,
|
|
132
|
+
display_name: place.display_name ?? luogo.formattedAddress ?? "",
|
|
133
|
+
lat: this.testo(luogo.location?.lat()),
|
|
134
|
+
lon: this.testo(luogo.location?.lng())
|
|
135
|
+
})));
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
luogoDaLegacy(place) {
|
|
139
|
+
return from(attendiPlaces()).pipe(switchMap((places) => new Observable((chi) => {
|
|
140
|
+
new places.PlacesService(document.createElement("div")).getDetails({
|
|
141
|
+
placeId: place.place_id,
|
|
142
|
+
fields: ["geometry", "formatted_address"]
|
|
143
|
+
}, (luogo, stato) => {
|
|
144
|
+
if (stato !== STATO_PLACES.ok || !luogo) {
|
|
145
|
+
chi.error(new Error(stato));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const punto = luogo.geometry?.location;
|
|
149
|
+
chi.next({
|
|
150
|
+
...place,
|
|
151
|
+
display_name: place.display_name ?? luogo.formatted_address ?? "",
|
|
152
|
+
lat: this.testo(punto?.lat()),
|
|
153
|
+
lon: this.testo(punto?.lng())
|
|
154
|
+
});
|
|
155
|
+
chi.complete();
|
|
156
|
+
});
|
|
157
|
+
})), catchError((err) => {
|
|
158
|
+
console.warn("[ngx-geocoding] coordinate del luogo non risolte — " + this.perche(err));
|
|
159
|
+
return of(place);
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
daSorgenti(address, restanti) {
|
|
163
|
+
const sorgente = restanti[0];
|
|
164
|
+
if (!sorgente) return throwError(() => /* @__PURE__ */ new Error("Nessuna sorgente di geocoding disponibile"));
|
|
165
|
+
const dopo = restanti.slice(1);
|
|
166
|
+
if (this.spente.has(sorgente)) return this.daSorgenti(address, dopo);
|
|
167
|
+
return (sorgente === "google" ? this.daGoogle(address) : this.daNominatim(address)).pipe(tap((luoghi) => {
|
|
168
|
+
if (luoghi.length <= 0) this.subjectNoResults.next(null);
|
|
169
|
+
}), catchError((err) => {
|
|
170
|
+
this.spente.add(sorgente);
|
|
171
|
+
console.warn(`[ngx-geocoding] sorgente "${sorgente}" non utilizzabile, non la si richiede più — ${this.perche(err)}`);
|
|
172
|
+
return this.daSorgenti(address, dopo);
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
daNominatim(address) {
|
|
176
|
+
const encoded = encodeURIComponent(address);
|
|
177
|
+
const base = this.config?.nominatimUrl ?? NOMINATIM;
|
|
178
|
+
return this.http.get(`${base}?q=${encoded}&format=jsonv2`).pipe(map((res) => res.map((item) => ({
|
|
179
|
+
display_name: item.display_name,
|
|
180
|
+
lat: item.lat,
|
|
181
|
+
lon: item.lon,
|
|
182
|
+
place_id: item.place_id
|
|
183
|
+
}))));
|
|
184
|
+
}
|
|
185
|
+
daGoogle(address) {
|
|
186
|
+
if (this.suggestionSpenta) return this.dallaLegacy(address);
|
|
187
|
+
return this.daSuggestion(address).pipe(catchError((err) => {
|
|
188
|
+
this.suggestionSpenta = true;
|
|
189
|
+
console.warn(`[ngx-geocoding] Places (New) non utilizzabile, si prova quella legacy — ${this.perche(err)}`);
|
|
190
|
+
return this.dallaLegacy(address);
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
dallaLegacy(address) {
|
|
194
|
+
if (this.placesSpenta) return this.daGeocoder(address);
|
|
195
|
+
return this.daPlaces(address).pipe(catchError((err) => {
|
|
196
|
+
this.placesSpenta = true;
|
|
197
|
+
console.warn(`[ngx-geocoding] Places legacy non utilizzabile, si prova il Geocoder — ${this.perche(err)}`);
|
|
198
|
+
return this.daGeocoder(address);
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
daSuggestion(address) {
|
|
202
|
+
const paese = this.config?.country;
|
|
203
|
+
const cerchio = this.cerchio();
|
|
204
|
+
return from(attendiPlaces()).pipe(switchMap((places) => {
|
|
205
|
+
const nuova = places.AutocompleteSuggestion;
|
|
206
|
+
if (!nuova) return throwError(() => /* @__PURE__ */ new Error("AutocompleteSuggestion non c'è in questo SDK"));
|
|
207
|
+
return from(nuova.fetchAutocompleteSuggestions({
|
|
208
|
+
input: address,
|
|
209
|
+
...paese ? {
|
|
210
|
+
includedRegionCodes: [paese],
|
|
211
|
+
region: paese
|
|
212
|
+
} : {},
|
|
213
|
+
...cerchio ? { locationBias: cerchio } : {}
|
|
214
|
+
})).pipe(map((risposta) => (risposta?.suggestions ?? []).map((s) => s.placePrediction).filter((p) => !!p?.placeId).map((p) => ({
|
|
215
|
+
place_id: p.placeId,
|
|
216
|
+
display_name: p.text?.toString() ?? ""
|
|
217
|
+
}))));
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
daPlaces(address) {
|
|
221
|
+
const paese = this.config?.country;
|
|
222
|
+
const cerchio = this.cerchio();
|
|
223
|
+
return from(attendiPlaces()).pipe(switchMap((places) => new Observable((chi) => {
|
|
224
|
+
new places.AutocompleteService().getPlacePredictions({
|
|
225
|
+
input: address,
|
|
226
|
+
...paese ? { componentRestrictions: { country: paese } } : {},
|
|
227
|
+
...cerchio ? { locationBias: cerchio } : {}
|
|
228
|
+
}, (proposte, stato) => {
|
|
229
|
+
if (stato === STATO_PLACES.nessuno) {
|
|
230
|
+
chi.next([]);
|
|
231
|
+
chi.complete();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (stato !== STATO_PLACES.ok) {
|
|
235
|
+
chi.error(new Error(stato));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
chi.next((proposte ?? []).filter((p) => !!p.place_id).map((p) => ({
|
|
239
|
+
place_id: p.place_id,
|
|
240
|
+
display_name: p.description ?? ""
|
|
241
|
+
})));
|
|
242
|
+
chi.complete();
|
|
243
|
+
});
|
|
244
|
+
})));
|
|
245
|
+
}
|
|
246
|
+
daGeocoder(address) {
|
|
247
|
+
const paese = this.config?.country;
|
|
248
|
+
const riquadro = this.riquadro();
|
|
249
|
+
return from(attendiGeocoder()).pipe(switchMap((geocoder) => new Observable((chi) => {
|
|
250
|
+
geocoder.geocode({
|
|
251
|
+
address,
|
|
252
|
+
...paese ? {
|
|
253
|
+
componentRestrictions: { country: paese },
|
|
254
|
+
region: paese
|
|
255
|
+
} : {},
|
|
256
|
+
...riquadro ? { bounds: riquadro } : {}
|
|
257
|
+
}, (risultati, stato) => {
|
|
258
|
+
if (stato === STATO_PLACES.nessuno) {
|
|
259
|
+
chi.next([]);
|
|
260
|
+
chi.complete();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (stato !== STATO_PLACES.ok) {
|
|
264
|
+
chi.error(new Error(stato));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
chi.next((risultati ?? []).map((r) => ({
|
|
268
|
+
place_id: r.place_id,
|
|
269
|
+
display_name: r.formatted_address ?? "",
|
|
270
|
+
lat: this.testo(r.geometry?.location?.lat()),
|
|
271
|
+
lon: this.testo(r.geometry?.location?.lng())
|
|
272
|
+
})));
|
|
273
|
+
chi.complete();
|
|
274
|
+
});
|
|
275
|
+
})));
|
|
276
|
+
}
|
|
277
|
+
cerchio() {
|
|
278
|
+
const bias = this.config?.bias;
|
|
279
|
+
return bias ? {
|
|
280
|
+
center: {
|
|
281
|
+
lat: bias.lat,
|
|
282
|
+
lng: bias.lon
|
|
283
|
+
},
|
|
284
|
+
radius: bias.radiusM
|
|
285
|
+
} : void 0;
|
|
286
|
+
}
|
|
287
|
+
riquadro() {
|
|
288
|
+
const bias = this.config?.bias;
|
|
289
|
+
if (!bias) return void 0;
|
|
290
|
+
const dLat = bias.radiusM / METRI_PER_GRADO;
|
|
291
|
+
const dLon = dLat / Math.max(Math.cos(bias.lat * Math.PI / 180), .01);
|
|
292
|
+
return {
|
|
293
|
+
north: bias.lat + dLat,
|
|
294
|
+
south: bias.lat - dLat,
|
|
295
|
+
east: bias.lon + dLon,
|
|
296
|
+
west: bias.lon - dLon
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
sorgenti() {
|
|
300
|
+
const dichiarate = this.config?.sources ?? [];
|
|
301
|
+
return dichiarate.length ? dichiarate : SOLO_NOMINATIM;
|
|
302
|
+
}
|
|
303
|
+
perche(err) {
|
|
304
|
+
if (err instanceof Error && err.message) return err.message;
|
|
305
|
+
const dettaglio = err?.error?.error;
|
|
306
|
+
if (!dettaglio) return "motivo non dichiarato";
|
|
307
|
+
return [dettaglio.status, dettaglio.message].filter((pezzo) => !!pezzo).join(" : ");
|
|
308
|
+
}
|
|
309
|
+
testo(valore) {
|
|
310
|
+
return valore === null || valore === void 0 ? void 0 : String(valore);
|
|
311
|
+
}
|
|
312
|
+
static {
|
|
313
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
314
|
+
minVersion: "12.0.0",
|
|
315
|
+
version: "22.2.4",
|
|
316
|
+
ngImport: i0,
|
|
317
|
+
type: NgxGeocodingService,
|
|
318
|
+
deps: [],
|
|
319
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
static {
|
|
323
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
324
|
+
minVersion: "12.0.0",
|
|
325
|
+
version: "22.2.4",
|
|
326
|
+
ngImport: i0,
|
|
327
|
+
type: NgxGeocodingService,
|
|
328
|
+
providedIn: "root"
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
333
|
+
minVersion: "12.0.0",
|
|
334
|
+
version: "22.2.4",
|
|
335
|
+
ngImport: i0,
|
|
336
|
+
type: NgxGeocodingService,
|
|
337
|
+
decorators: [{
|
|
338
|
+
type: Injectable,
|
|
339
|
+
args: [{ providedIn: "root" }]
|
|
340
|
+
}]
|
|
341
|
+
});
|
|
583
342
|
export { NGX_GEOCODING_CONFIG, NgxGeocodingModule, NgxGeocodingService, provideNgxGeocoding };
|
|
584
|
-
|
|
343
|
+
|
|
344
|
+
//# sourceMappingURL=geosdi-ngx-geocoding.mjs.map
|