@igo2/geo 21.0.0-next.2 → 21.0.0-next.21

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.
@@ -0,0 +1,710 @@
1
+ import { Compression, ObjectUtils } from '@igo2/utils';
2
+ import { from, of, switchMap, first, concatMap, map, mergeMap, zip, forkJoin, defer, EMPTY, fromEvent, merge, combineLatest } from 'rxjs';
3
+ import { openDB } from 'idb';
4
+ import { HttpClient } from '@angular/common/http';
5
+ import * as i0 from '@angular/core';
6
+ import { inject, Injectable, Injector, makeEnvironmentProviders, provideAppInitializer } from '@angular/core';
7
+ import { MessageService } from '@igo2/core/message';
8
+ import JSZip from 'jszip';
9
+ import { catchError, concatMap as concatMap$1, first as first$1, delay, tap, switchMap as switchMap$1, debounceTime, map as map$1 } from 'rxjs/operators';
10
+ import { NetworkService } from '@igo2/core/network';
11
+ import { isOlFlatStyleLike, LayerService, LAYER_PERSISTENCE, OFFLINE_LAYER_RESTORE, VECTOR_LAYER_EXTENSIONS } from '@igo2/geo';
12
+ import * as olformat from 'ol/format';
13
+ import { ConfigService } from '@igo2/core/config';
14
+
15
+ var InsertSourceInsertDBEnum;
16
+ (function (InsertSourceInsertDBEnum) {
17
+ InsertSourceInsertDBEnum["System"] = "system";
18
+ InsertSourceInsertDBEnum["User"] = "user";
19
+ })(InsertSourceInsertDBEnum || (InsertSourceInsertDBEnum = {}));
20
+
21
+ function createIndexedDb() {
22
+ return openDB('igo2DB', undefined, {
23
+ upgrade(db) {
24
+ const geoDataStore = db.createObjectStore('geoData', {
25
+ keyPath: 'url',
26
+ autoIncrement: false
27
+ });
28
+ geoDataStore.createIndex('regionID-idx', 'regionID', {
29
+ unique: false
30
+ });
31
+ db.createObjectStore('layerData', {
32
+ keyPath: 'layerId',
33
+ autoIncrement: false
34
+ });
35
+ }
36
+ });
37
+ }
38
+
39
+ class GeoDB {
40
+ db$;
41
+ compression = new Compression();
42
+ collisionsMap = new Map();
43
+ _newData = 0;
44
+ revertObservablesSubscription;
45
+ constructor() {
46
+ this.db$ = from(createIndexedDb());
47
+ }
48
+ /**
49
+ * Only blob can be compressed
50
+ * @param url
51
+ * @param regionID
52
+ * @param object object to handle
53
+ * @param insertSource type of event user or system
54
+ * @param insertEvent Name of the event where the insert has been triggered
55
+ * @returns
56
+ */
57
+ update(url, regionID, object, insertSource, insertEvent) {
58
+ const object$ = (object instanceof Blob
59
+ ? this.compression.compressBlob(object)
60
+ : of(object));
61
+ const compress = object instanceof Blob ? true : false;
62
+ let geoDBData;
63
+ const a = this.db$.pipe(switchMap((db) => {
64
+ return object$.pipe(first(), switchMap((object) => {
65
+ geoDBData = {
66
+ url,
67
+ regionID,
68
+ object: object,
69
+ compressed: compress,
70
+ insertSource,
71
+ insertEvent
72
+ };
73
+ return this.getGeoDBData(url);
74
+ }), concatMap((res) => {
75
+ const dbObject = res;
76
+ if (!dbObject) {
77
+ this._newData++;
78
+ return from(db.add('geoData', geoDBData)).pipe(map(() => geoDBData));
79
+ }
80
+ else {
81
+ const currentRegionID = dbObject.regionID;
82
+ if (currentRegionID !== regionID) {
83
+ const collisions = this.collisionsMap.get(currentRegionID);
84
+ if (collisions !== undefined) {
85
+ collisions.push(dbObject.url);
86
+ this.collisionsMap.set(currentRegionID, collisions);
87
+ }
88
+ else {
89
+ this.collisionsMap.set(currentRegionID, [dbObject.url]);
90
+ }
91
+ }
92
+ return this.customUpdate(geoDBData);
93
+ }
94
+ }));
95
+ }));
96
+ return a;
97
+ }
98
+ customUpdate(geoDBData) {
99
+ const b = this.delete(geoDBData.url).pipe(mergeMap(() => this.add(geoDBData)));
100
+ return b;
101
+ }
102
+ add(geoDBData) {
103
+ return this.db$.pipe(switchMap((db) => from(db?.add('geoData', geoDBData)).pipe(map(() => geoDBData))));
104
+ }
105
+ put(geoDBData) {
106
+ return this.db$.pipe(switchMap((db) => from(db?.put('geoData', geoDBData)).pipe(map(() => geoDBData))));
107
+ }
108
+ getGeoDBData(url) {
109
+ return this.db$.pipe(switchMap((db) => from(db?.get('geoData', url))));
110
+ }
111
+ get(url) {
112
+ return this.getGeoDBData(url).pipe(mergeMap((data) => {
113
+ const rObj = !data?.compressed
114
+ ? data?.object
115
+ : this.compression.decompressBlob(data?.object);
116
+ return of(rObj);
117
+ }));
118
+ }
119
+ delete(key) {
120
+ return this.db$.pipe(switchMap((db) => from(db?.delete('geoData', key))), map(() => {
121
+ {
122
+ return { key };
123
+ }
124
+ }));
125
+ }
126
+ getRegionCountByID(id) {
127
+ return this.getRegionByID(id).pipe(switchMap((datas) => {
128
+ return of(datas.length);
129
+ }));
130
+ }
131
+ getRegionByID(id) {
132
+ if (!id) {
133
+ return of([]);
134
+ }
135
+ const IDBKey = IDBKeyRange.only(id);
136
+ return this.db$.pipe(switchMap((db) => from(db?.getAllFromIndex('geoData', 'regionID-idx', IDBKey))));
137
+ }
138
+ deleteByRegionID(id) {
139
+ if (!id) {
140
+ return of([]);
141
+ }
142
+ return this.db$.pipe(switchMap((db) => {
143
+ const tx = db.transaction('geoData', 'readwrite');
144
+ return of(tx);
145
+ }), mergeMap((tx) => this.getRegionByID(id).pipe(concatMap((datas) => {
146
+ const promises = datas.map((data) => tx.store.delete(data.url));
147
+ promises.push(tx.done);
148
+ return from(Promise.all(promises));
149
+ }))));
150
+ }
151
+ resetCounters() {
152
+ this.resetCollisionsMap();
153
+ this._newData = 0;
154
+ }
155
+ resetCollisionsMap() {
156
+ this.collisionsMap = new Map();
157
+ }
158
+ revertCollisions() {
159
+ if (this.revertObservablesSubscription) {
160
+ this.revertObservablesSubscription.unsubscribe();
161
+ }
162
+ const revertObservables = [];
163
+ for (const [regionID, collisions] of this.collisionsMap) {
164
+ for (const url of collisions) {
165
+ revertObservables.push(this.getGeoDBData(url).pipe(first(), concatMap((dbObject) => {
166
+ const updatedObject = dbObject;
167
+ updatedObject.regionID = regionID;
168
+ return this.customUpdate(updatedObject);
169
+ })));
170
+ }
171
+ }
172
+ this.revertObservablesSubscription = zip(...revertObservables).subscribe();
173
+ }
174
+ get newData() {
175
+ return this._newData;
176
+ }
177
+ }
178
+
179
+ /** Delay before dismissing the "download completed" toast, so it stays legible for a moment. */
180
+ const DOWNLOAD_COMPLETED_DELAY_MS = 2500;
181
+ const TOAST_TIMEOUT_MS = 40000;
182
+ class GeoDataSyncService {
183
+ http = inject(HttpClient);
184
+ messageService = inject(MessageService);
185
+ load(urlFile) {
186
+ const geoDB = new GeoDB();
187
+ const downloadState = {};
188
+ this.http
189
+ .get(urlFile)
190
+ .pipe(catchError((error) => this.handleConfigFileError(urlFile, error)), concatMap$1((datasToIDB) => this.processGeoDatas(geoDB, datasToIDB?.geoDatas ?? [], downloadState)))
191
+ .subscribe(() => this.notifyDownloadCompleted(downloadState));
192
+ }
193
+ handleConfigFileError(urlFile, error) {
194
+ this.messageService.error(`GeoData file ${urlFile} could not be read`);
195
+ error.error.caught = true;
196
+ throw error;
197
+ }
198
+ processGeoDatas(geoDB, geoDatas, downloadState) {
199
+ const currentDate = new Date();
200
+ const operations = [];
201
+ for (const geoData of geoDatas) {
202
+ if (typeof geoData.triggerDate === 'string') {
203
+ geoData.triggerDate = new Date(Date.parse(geoData.triggerDate));
204
+ }
205
+ if (currentDate < geoData.triggerDate) {
206
+ continue;
207
+ }
208
+ if (geoData.action === 'update') {
209
+ operations.push(...this.updateGeoData(geoDB, geoData, downloadState));
210
+ }
211
+ else if (geoData.action === 'delete') {
212
+ operations.push(...geoData.urls.map((url) => geoDB.delete(url)));
213
+ }
214
+ }
215
+ return forkJoin(operations);
216
+ }
217
+ updateGeoData(geoDB, geoData, downloadState) {
218
+ const insertEvent = `${geoData.source || InsertSourceInsertDBEnum.System} (${geoData.triggerDate})`;
219
+ return geoData.urls.map((url) => geoDB
220
+ .getGeoDBData(url)
221
+ .pipe(concatMap$1((existing) => existing?.insertEvent === insertEvent
222
+ ? of(false)
223
+ : this.downloadAndStore(geoDB, url, geoData, insertEvent, downloadState))));
224
+ }
225
+ downloadAndStore(geoDB, url, geoData, insertEvent, downloadState) {
226
+ this.showDownloadStartMessage(downloadState);
227
+ const isZip = this.isZip(url);
228
+ const download$ = isZip
229
+ ? this.http.get(url, { responseType: 'arraybuffer' })
230
+ : this.http.get(url, { responseType: 'json' });
231
+ return download$.pipe(catchError((error) => this.handleDownloadError(downloadState, error)), concatMap$1((response) => isZip
232
+ ? this.storeZippedGeoData(geoDB, response, url, geoData, insertEvent)
233
+ : geoDB.update(url, url, response, InsertSourceInsertDBEnum.System, insertEvent)));
234
+ }
235
+ handleDownloadError(downloadState, error) {
236
+ if (downloadState.toast) {
237
+ this.messageService.remove(downloadState.toast.toastId);
238
+ }
239
+ this.messageService.error('igo.geo.indexedDb.data-download-failed', undefined, { timeOut: TOAST_TIMEOUT_MS });
240
+ error.error.caught = true;
241
+ throw error;
242
+ }
243
+ storeZippedGeoData(geoDB, archive, url, geoData, insertEvent) {
244
+ return from(this.extractZipGeojsonEntries(archive, geoData.zippedBaseUrl)).pipe(concatMap$1((entries) => forkJoin([
245
+ // the archive itself is tracked so future loads can detect it was already processed
246
+ geoDB.update(url, url, {}, InsertSourceInsertDBEnum.System, insertEvent),
247
+ ...entries.map(({ zippedUrl, geojson }) => geoDB.update(zippedUrl, url, geojson, InsertSourceInsertDBEnum.System, insertEvent))
248
+ ])));
249
+ }
250
+ async extractZipGeojsonEntries(archive, zippedBaseUrl = '') {
251
+ const zipped = await JSZip.loadAsync(archive);
252
+ const baseUrl = zippedBaseUrl.endsWith('/')
253
+ ? zippedBaseUrl
254
+ : `${zippedBaseUrl}/`;
255
+ const geojsonPaths = Object.keys(zipped.files).filter((relativePath) => relativePath.toLowerCase().endsWith('.geojson'));
256
+ return Promise.all(geojsonPaths.map(async (relativePath) => ({
257
+ zippedUrl: `${baseUrl}${relativePath}`,
258
+ geojson: JSON.parse(await zipped.file(relativePath).async('text'))
259
+ })));
260
+ }
261
+ showDownloadStartMessage(downloadState) {
262
+ if (downloadState.toast) {
263
+ return;
264
+ }
265
+ downloadState.toast = this.messageService.info('igo.geo.indexedDb.data-download-start', undefined, {
266
+ disableTimeOut: true,
267
+ progressBar: false,
268
+ closeButton: true,
269
+ tapToDismiss: false
270
+ });
271
+ }
272
+ notifyDownloadCompleted(downloadState) {
273
+ if (!downloadState.toast) {
274
+ return;
275
+ }
276
+ const toast = downloadState.toast;
277
+ setTimeout(() => {
278
+ this.messageService.remove(toast.toastId);
279
+ this.messageService.success('igo.geo.indexedDb.data-download-completed', undefined, { timeOut: TOAST_TIMEOUT_MS });
280
+ }, DOWNLOAD_COMPLETED_DELAY_MS);
281
+ }
282
+ isZip(value) {
283
+ const regex = /(zip)$/;
284
+ return typeof value === 'string' && regex.test(value.toLowerCase());
285
+ }
286
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GeoDataSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
287
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GeoDataSyncService });
288
+ }
289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GeoDataSyncService, decorators: [{
290
+ type: Injectable
291
+ }] });
292
+
293
+ class LayerDB {
294
+ dbPromise;
295
+ constructor() {
296
+ this.dbPromise = createIndexedDb();
297
+ }
298
+ /**
299
+ * This method allow to update the stored layer into the indexeddb (layerData)
300
+ * @param layerDBData
301
+ * @returns
302
+ */
303
+ update(layerDBData) {
304
+ return from(this.updateAsync(layerDBData));
305
+ }
306
+ async updateAsync(layerDBData) {
307
+ const db = await this.dbPromise;
308
+ const existing = await db.get('layerData', layerDBData.layerId);
309
+ if (existing) {
310
+ await db.delete('layerData', layerDBData.layerId);
311
+ }
312
+ await db.add('layerData', layerDBData);
313
+ return layerDBData;
314
+ }
315
+ add(layerDBData) {
316
+ return from(this.addAsync(layerDBData));
317
+ }
318
+ async addAsync(layerDBData) {
319
+ const db = await this.dbPromise;
320
+ await db.add('layerData', layerDBData);
321
+ return layerDBData;
322
+ }
323
+ /**
324
+ * This method retrieve an idb layer definition
325
+ * @param layerId
326
+ * @returns
327
+ */
328
+ getByID(layerId) {
329
+ return from(this.getByIDAsync(layerId));
330
+ }
331
+ async getByIDAsync(layerId) {
332
+ const db = await this.dbPromise;
333
+ return db.get('layerData', layerId.toString());
334
+ }
335
+ /**
336
+ * This method delete an idb layer definition
337
+ * @param key
338
+ * @returns
339
+ */
340
+ delete(key) {
341
+ return from(this.deleteAsync(key));
342
+ }
343
+ async deleteAsync(key) {
344
+ const db = await this.dbPromise;
345
+ await db.delete('layerData', key);
346
+ return { key };
347
+ }
348
+ /**
349
+ * This method retrive all idb layer definition
350
+ * @param layerId
351
+ * @returns
352
+ */
353
+ getAll() {
354
+ return from(this.getAllAsync());
355
+ }
356
+ async getAllAsync() {
357
+ const db = await this.dbPromise;
358
+ return db.getAll('layerData');
359
+ }
360
+ }
361
+
362
+ class GeoNetworkService {
363
+ http = inject(HttpClient);
364
+ networkService = inject(NetworkService);
365
+ networkOnline = true;
366
+ constructor() {
367
+ this.networkService.currentState().subscribe((state) => {
368
+ this.networkOnline = state.connection;
369
+ });
370
+ }
371
+ get(url, simpleGetOptions) {
372
+ if (window.navigator.onLine && this.networkOnline) {
373
+ return this.getOnline(url, simpleGetOptions);
374
+ }
375
+ return this.getOffline(url);
376
+ }
377
+ getOnline(url, simpleGetOptions) {
378
+ let request;
379
+ switch (simpleGetOptions.responseType) {
380
+ case 'arraybuffer':
381
+ request = this.http.get(url, {
382
+ responseType: 'arraybuffer',
383
+ withCredentials: simpleGetOptions.withCredentials
384
+ });
385
+ break;
386
+ case 'text':
387
+ request = this.http.get(url, {
388
+ responseType: 'text',
389
+ withCredentials: simpleGetOptions.withCredentials
390
+ });
391
+ break;
392
+ case 'json':
393
+ request = this.http.get(url, {
394
+ responseType: 'json',
395
+ withCredentials: simpleGetOptions.withCredentials
396
+ });
397
+ break;
398
+ default:
399
+ request = this.http.get(url, {
400
+ responseType: 'blob',
401
+ withCredentials: simpleGetOptions.withCredentials
402
+ });
403
+ break;
404
+ }
405
+ return request;
406
+ }
407
+ getOffline(url) {
408
+ const geoDB = new GeoDB();
409
+ return geoDB?.get(url);
410
+ }
411
+ isOnline() {
412
+ return this.networkOnline && window.navigator.onLine;
413
+ }
414
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GeoNetworkService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
415
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GeoNetworkService });
416
+ }
417
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: GeoNetworkService, decorators: [{
418
+ type: Injectable
419
+ }], ctorParameters: () => [] });
420
+
421
+ class IndexedDbLayerPersistenceService {
422
+ geoDB = inject(GeoDB);
423
+ layerDB = inject(LayerDB);
424
+ isPersistent(layer) {
425
+ if (layer.type !== 'vector') {
426
+ return false;
427
+ }
428
+ const options = layer.options;
429
+ return options.offline?.enabled === true;
430
+ }
431
+ removePersistedData(layer) {
432
+ if (!this.isPersistent(layer) || layer.id === undefined) {
433
+ return;
434
+ }
435
+ const options = layer.options;
436
+ const layerId = layer.id.toString();
437
+ const sourceUrl = options.sourceOptions?.url;
438
+ const featureStorageKey = typeof sourceUrl === 'string' && sourceUrl.length > 0
439
+ ? sourceUrl
440
+ : layerId;
441
+ forkJoin([
442
+ this.geoDB.delete(featureStorageKey),
443
+ this.layerDB.delete(layerId)
444
+ ]).subscribe();
445
+ }
446
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IndexedDbLayerPersistenceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
447
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IndexedDbLayerPersistenceService });
448
+ }
449
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IndexedDbLayerPersistenceService, decorators: [{
450
+ type: Injectable
451
+ }] });
452
+
453
+ class IndexedDbVectorLayerExtensionInstance {
454
+ context;
455
+ geoDB;
456
+ layerDB;
457
+ geoNetworkService;
458
+ subscriptions = [];
459
+ constructor(context, geoDB, layerDB, geoNetworkService) {
460
+ this.context = context;
461
+ this.geoDB = geoDB;
462
+ this.layerDB = layerDB;
463
+ this.geoNetworkService = geoNetworkService;
464
+ this.registerPersistence();
465
+ }
466
+ interceptLoad(request, next) {
467
+ if (typeof request.url === 'function') {
468
+ next(request);
469
+ return;
470
+ }
471
+ const requestUrl = request.url;
472
+ const storageKey = this.context.options.sourceOptions?.url ||
473
+ this.context.id?.toString() ||
474
+ requestUrl;
475
+ const responseType = this.context.source
476
+ .getFormat()
477
+ .getType();
478
+ const getVectorObs$ = defer(() => this.geoNetworkService.get(requestUrl, { responseType })).pipe(first$1());
479
+ const idbGetVectorObs$ = this.geoDB.get(storageKey).pipe(catchError(() => of(undefined)), delay(750), concatMap$1((response) => (response ? of(response) : getVectorObs$)), tap((content) => this.handleOnLoad(request, content)), catchError(() => {
480
+ request.source.removeLoadedExtent(request.extent);
481
+ request.failure();
482
+ return EMPTY;
483
+ }));
484
+ this.subscriptions.push(idbGetVectorObs$.subscribe());
485
+ }
486
+ destroy() {
487
+ this.subscriptions.forEach((subscription) => subscription.unsubscribe());
488
+ this.subscriptions = [];
489
+ }
490
+ registerPersistence() {
491
+ const sourceReady$ = fromEvent(this.context.layer, 'sourceready');
492
+ const featurePersistSubscription = sourceReady$
493
+ .pipe(tap(() => {
494
+ if (this.context.source.getFeatures().length > 0) {
495
+ this.maintainFeaturesInIdb();
496
+ }
497
+ this.maintainOptionsInIdb();
498
+ }), switchMap$1(() => merge(fromEvent(this.context.source, 'featuresloadend'), fromEvent(this.context.source, 'addfeature'), fromEvent(this.context.source, 'changefeature'), fromEvent(this.context.source, 'clear'), fromEvent(this.context.source, 'removefeature'))))
499
+ .pipe(debounceTime(750))
500
+ .subscribe(() => this.maintainFeaturesInIdb());
501
+ const optionPersistSubscription = sourceReady$
502
+ .pipe(switchMap$1(() => merge(fromEvent(this.context.layer, 'change'), fromEvent(this.context.layer, 'change:visible'), fromEvent(this.context.layer, 'change:opacity'), fromEvent(this.context.layer, 'change:zIndex'))))
503
+ .pipe(debounceTime(750))
504
+ .subscribe(() => this.maintainOptionsInIdb());
505
+ this.subscriptions.push(featurePersistSubscription, optionPersistSubscription);
506
+ }
507
+ maintainOptionsInIdb() {
508
+ if (this.context.id === undefined) {
509
+ return;
510
+ }
511
+ const id = this.context.id;
512
+ const options = this.context.options;
513
+ const offline = this.getOfflineState(options);
514
+ const layerData = ObjectUtils.removeUndefined({
515
+ layerId: id,
516
+ detailedContextUri: offline.contextUri,
517
+ sourceOptions: {
518
+ id,
519
+ type: 'vector',
520
+ queryable: true,
521
+ url: options.sourceOptions?.url
522
+ },
523
+ layerOptions: {
524
+ workspace: options.workspace,
525
+ zIndex: this.context.layer.getZIndex() ?? 1000000,
526
+ id,
527
+ isIgoInternalLayer: options.isIgoInternalLayer,
528
+ title: options.title,
529
+ visible: this.context.layer.getVisible(),
530
+ opacity: this.context.layer.getOpacity(),
531
+ style: isOlFlatStyleLike(options.style) ? options.style : undefined,
532
+ offline: Object.assign({}, options.offline, {
533
+ enabled: true,
534
+ contextUri: offline.contextUri
535
+ })
536
+ },
537
+ insertEvent: `${options.title}-${id}-${new Date()}`
538
+ });
539
+ this.layerDB.update(layerData).pipe(first$1()).subscribe();
540
+ }
541
+ maintainFeaturesInIdb() {
542
+ if (this.context.id === undefined) {
543
+ return;
544
+ }
545
+ const sourceFeatures = this.context.source.getFeatures().map((feature) => {
546
+ const persistedFeature = feature.clone();
547
+ persistedFeature.setId(feature.getId());
548
+ persistedFeature.unset('_featureStore', true);
549
+ return persistedFeature;
550
+ });
551
+ const geojsonObject = JSON.parse(new olformat.GeoJSON().writeFeatures(sourceFeatures, {
552
+ dataProjection: 'EPSG:4326',
553
+ featureProjection: this.context.source.getProjection() || 'EPSG:3857'
554
+ }));
555
+ this.geoDB
556
+ .update(this.context.options.sourceOptions?.url || this.context.id.toString(), this.context.id, geojsonObject, InsertSourceInsertDBEnum.User, `${this.context.options.title}-${this.context.id}-${new Date()}`)
557
+ .pipe(first$1())
558
+ .subscribe();
559
+ }
560
+ handleOnLoad(request, content) {
561
+ const format = request.source.getFormat();
562
+ const type = format.getType();
563
+ let source;
564
+ switch (type) {
565
+ case 'xml':
566
+ source = new DOMParser().parseFromString(content.toString(), 'application/xml');
567
+ break;
568
+ case 'json':
569
+ case 'text':
570
+ case 'arraybuffer':
571
+ default:
572
+ source = content;
573
+ break;
574
+ }
575
+ const readOptions = {
576
+ extent: request.extent,
577
+ featureProjection: request.projection
578
+ };
579
+ const features = format.readFeatures(source, readOptions);
580
+ if (features) {
581
+ request.source.addFeatures(features);
582
+ request.success(features);
583
+ return;
584
+ }
585
+ request.success([]);
586
+ }
587
+ getOfflineState(options) {
588
+ return {
589
+ enabled: options.offline?.enabled === true,
590
+ contextUri: options.offline?.contextUri
591
+ };
592
+ }
593
+ }
594
+ class IndexedDbVectorLayerExtension {
595
+ id = 'indexed-db';
596
+ priority = 100;
597
+ geoDB = inject(GeoDB);
598
+ layerDB = inject(LayerDB);
599
+ geoNetworkService = inject(GeoNetworkService);
600
+ supports(options) {
601
+ return options.offline?.enabled === true;
602
+ }
603
+ attach(context) {
604
+ return new IndexedDbVectorLayerExtensionInstance(context, this.geoDB, this.layerDB, this.geoNetworkService);
605
+ }
606
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IndexedDbVectorLayerExtension, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
607
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IndexedDbVectorLayerExtension });
608
+ }
609
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IndexedDbVectorLayerExtension, decorators: [{
610
+ type: Injectable
611
+ }] });
612
+
613
+ const OfflineFeatureKind = ['IndexedDb'];
614
+
615
+ class OfflineLayerRestoreService {
616
+ layerDB = inject(LayerDB);
617
+ injector = inject(Injector);
618
+ createAsyncLayers(contextUri = '*') {
619
+ const layerService = this.injector.get(LayerService);
620
+ return this.layerDB.getAll().pipe(concatMap$1((persistedLayers) => {
621
+ const filteredPersistedLayers = contextUri === '*'
622
+ ? persistedLayers
623
+ : persistedLayers.filter((layer) => layer.detailedContextUri === contextUri);
624
+ if (!filteredPersistedLayers.length) {
625
+ return of([]);
626
+ }
627
+ const layerOptions = filteredPersistedLayers.map((persistedLayer) => ({
628
+ ...persistedLayer.layerOptions,
629
+ offline: {
630
+ enabled: true,
631
+ contextUri: persistedLayer.detailedContextUri
632
+ },
633
+ sourceOptions: {
634
+ ...persistedLayer.sourceOptions,
635
+ url: persistedLayer.sourceOptions.url ?? persistedLayer.layerId
636
+ }
637
+ }));
638
+ return combineLatest(layerOptions.map((options) => layerService.createAsyncLayer(options))).pipe(map$1((layers) => layers.filter(Boolean)));
639
+ }));
640
+ }
641
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: OfflineLayerRestoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
642
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: OfflineLayerRestoreService });
643
+ }
644
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: OfflineLayerRestoreService, decorators: [{
645
+ type: Injectable
646
+ }] });
647
+
648
+ function provideOffline(...features) {
649
+ const providers = [];
650
+ for (const feature of features) {
651
+ providers.push(...feature.providers);
652
+ }
653
+ return makeEnvironmentProviders(providers);
654
+ }
655
+ function withIndexedDb() {
656
+ return {
657
+ kind: 'IndexedDb',
658
+ providers: [
659
+ provideAppInitializer(indexedDbInitializerFactory),
660
+ provideAppInitializer(configFileToGeoDBInitializerFactory),
661
+ GeoDB,
662
+ LayerDB,
663
+ GeoNetworkService,
664
+ GeoDataSyncService,
665
+ IndexedDbLayerPersistenceService,
666
+ {
667
+ provide: LAYER_PERSISTENCE,
668
+ useExisting: IndexedDbLayerPersistenceService
669
+ },
670
+ OfflineLayerRestoreService,
671
+ {
672
+ provide: OFFLINE_LAYER_RESTORE,
673
+ useExisting: OfflineLayerRestoreService
674
+ },
675
+ IndexedDbVectorLayerExtension,
676
+ {
677
+ provide: VECTOR_LAYER_EXTENSIONS,
678
+ useExisting: IndexedDbVectorLayerExtension,
679
+ multi: true
680
+ }
681
+ ]
682
+ };
683
+ }
684
+ function indexedDbInitializerFactory() {
685
+ inject(GeoDB);
686
+ inject(LayerDB);
687
+ return createIndexedDb();
688
+ }
689
+ async function configFileToGeoDBInitializerFactory() {
690
+ const configService = inject(ConfigService);
691
+ const configFileToGeoDBService = inject(GeoDataSyncService);
692
+ await new Promise((resolve) => {
693
+ configService.isLoaded$.subscribe((loaded) => {
694
+ if (loaded) {
695
+ resolve();
696
+ }
697
+ });
698
+ });
699
+ const url = configService.getConfig('importExport.configFileToGeoDBService');
700
+ if (url) {
701
+ configFileToGeoDBService.load(url);
702
+ }
703
+ }
704
+
705
+ /**
706
+ * Generated bundle index. Do not edit.
707
+ */
708
+
709
+ export { GeoDB, GeoDataSyncService, GeoNetworkService, IndexedDbLayerPersistenceService, IndexedDbVectorLayerExtension, InsertSourceInsertDBEnum, LayerDB, OfflineFeatureKind, createIndexedDb, provideOffline, withIndexedDb };
710
+ //# sourceMappingURL=igo2-geo-offline.mjs.map