@kispace-io/gs-lib 0.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.
Files changed (59) hide show
  1. package/README.md +58 -0
  2. package/bin/map-builder.js +132 -0
  3. package/dist/base-map-builder.d.ts +102 -0
  4. package/dist/base-map-builder.d.ts.map +1 -0
  5. package/dist/gs-gs2ol.d.ts +41 -0
  6. package/dist/gs-gs2ol.d.ts.map +1 -0
  7. package/dist/gs-lib.css +3724 -0
  8. package/dist/gs-lib.d.ts +16 -0
  9. package/dist/gs-lib.d.ts.map +1 -0
  10. package/dist/gs-litns.d.ts +32 -0
  11. package/dist/gs-litns.d.ts.map +1 -0
  12. package/dist/gs-model.d.ts +186 -0
  13. package/dist/gs-model.d.ts.map +1 -0
  14. package/dist/gs-ol-adapters.d.ts +23 -0
  15. package/dist/gs-ol-adapters.d.ts.map +1 -0
  16. package/dist/gs-ol2gs.d.ts +9 -0
  17. package/dist/gs-ol2gs.d.ts.map +1 -0
  18. package/dist/gs-olns.d.ts +22 -0
  19. package/dist/gs-olns.d.ts.map +1 -0
  20. package/dist/index.d.ts +11 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.html +69 -0
  23. package/dist/index.js +104888 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/pwa/assets/icons/192x192.png +0 -0
  26. package/dist/pwa/assets/icons/24x24.png +0 -0
  27. package/dist/pwa/assets/icons/48x48.png +0 -0
  28. package/dist/pwa/assets/icons/512x512.png +0 -0
  29. package/dist/pwa/assets/icons/icon_192.png +0 -0
  30. package/dist/pwa/assets/icons/icon_24.png +0 -0
  31. package/dist/pwa/assets/icons/icon_48.png +0 -0
  32. package/dist/pwa/assets/icons/icon_512.png +0 -0
  33. package/dist/pwa/manifest.json +54 -0
  34. package/dist/pwa/staticwebapp.config.json +6 -0
  35. package/dist/pwa/sw.js +109 -0
  36. package/lib/node-map-builder.ts +200 -0
  37. package/package.json +51 -0
  38. package/public/index.html +69 -0
  39. package/public/pwa/assets/icons/192x192.png +0 -0
  40. package/public/pwa/assets/icons/24x24.png +0 -0
  41. package/public/pwa/assets/icons/48x48.png +0 -0
  42. package/public/pwa/assets/icons/512x512.png +0 -0
  43. package/public/pwa/assets/icons/icon_192.png +0 -0
  44. package/public/pwa/assets/icons/icon_24.png +0 -0
  45. package/public/pwa/assets/icons/icon_48.png +0 -0
  46. package/public/pwa/assets/icons/icon_512.png +0 -0
  47. package/public/pwa/manifest.json +54 -0
  48. package/public/pwa/staticwebapp.config.json +6 -0
  49. package/public/pwa/sw.js +109 -0
  50. package/src/base-map-builder.ts +414 -0
  51. package/src/gs-gs2ol.ts +626 -0
  52. package/src/gs-lib.ts +54 -0
  53. package/src/gs-litns.ts +213 -0
  54. package/src/gs-model.ts +393 -0
  55. package/src/gs-ol-adapters.ts +89 -0
  56. package/src/gs-ol2gs.ts +86 -0
  57. package/src/gs-olns.ts +30 -0
  58. package/src/index.ts +15 -0
  59. package/tsconfig.json +23 -0
@@ -0,0 +1,626 @@
1
+ import {
2
+ DEFAULT_GSMAP,
3
+ GsControl,
4
+ GsFeature,
5
+ GsFillStyle,
6
+ GsGeometry,
7
+ GsIcon,
8
+ GsImageStyle,
9
+ GsLayer,
10
+ GsLayerType,
11
+ GsMap,
12
+ GsOverlay,
13
+ GsSource,
14
+ GsSourceType,
15
+ GsState,
16
+ GsStrokeStyle,
17
+ GsStyle,
18
+ GsTextStyle,
19
+ KEY_ENV,
20
+ KEY_EVENT_SUBSCRIPTIONS,
21
+ KEY_FORMAT,
22
+ KEY_GS_MANAGED,
23
+ KEY_LABEL,
24
+ KEY_NAME,
25
+ KEY_SOURCETYPE,
26
+ KEY_SRC,
27
+ KEY_STATE,
28
+ KEY_UUID,
29
+ KEY_URL
30
+ } from "./gs-model";
31
+ import {Feature, Map, Overlay, View} from "ol";
32
+ import {MapOptions} from "ol/Map";
33
+ import BaseObject from "ol/Object";
34
+ import * as olGeom from "ol/geom";
35
+ import {Circle as CircleStyle, Fill, Icon, RegularShape, Stroke, Style, Text} from "ol/style";
36
+ import {GeoTIFF, OSM, Source, TileWMS, WMTS, XYZ} from "ol/source";
37
+ import {optionsFromCapabilities} from "ol/source/WMTS";
38
+ import FeatureFormat from "ol/format/Feature";
39
+ import WMTSCapabilities from "ol/format/WMTSCapabilities";
40
+ import VectorSource from "ol/source/Vector";
41
+ import {GeoJSON, GPX} from "ol/format";
42
+ import TileLayer from "ol/layer/Tile";
43
+ import TileSource from "ol/source/Tile";
44
+ import VectorLayer from "ol/layer/Vector";
45
+ import BaseLayer from "ol/layer/Base";
46
+ import {apply as applyMapboxStyle} from "ol-mapbox-style";
47
+ import LayerGroup from "ol/layer/Group";
48
+ import {Control} from "ol/control";
49
+ import * as ol from "./gs-olns"
50
+ import {lit} from "./gs-litns";
51
+ import {v4 as uuidv4} from '@kispace-io/appspace/externals/third-party'
52
+ import PubSub from '@kispace-io/appspace/externals/third-party'
53
+ import {GsControlAdapter, GsOverlayAdapter} from "./gs-ol-adapters";
54
+ import {rtUtils} from "./index";
55
+ import Layer from "ol/layer/Layer";
56
+
57
+ const withState = <T extends BaseObject>(state: GsState, olObject: T): T => {
58
+ if (state.uuid) {
59
+ olObject.set(KEY_UUID, state.uuid)
60
+ }
61
+ if (state.state) {
62
+ // add as properties for easy access via get(...)
63
+ for (let stateKey in state.state) {
64
+ olObject.set(stateKey, state.state[stateKey])
65
+ }
66
+ // add states as object as it is not possible to differentiate own state keys from OL keys
67
+ olObject.set(KEY_STATE, state.state)
68
+ }
69
+ return olObject
70
+ }
71
+
72
+ export const toOLGeometry = (geometry: GsGeometry) => {
73
+ return withState(geometry, new olGeom[geometry.type](geometry.coordinates));
74
+ }
75
+
76
+ export const toOlResource = (resource: GsIcon) => {
77
+ return new Icon({
78
+ src: resource.src
79
+ })
80
+ }
81
+
82
+ export const toOlStroke = (gsStroke: GsStrokeStyle): Stroke => {
83
+ return new Stroke({
84
+ color: gsStroke.color,
85
+ width: gsStroke.width,
86
+ lineDash: gsStroke.lineDash,
87
+ lineCap: gsStroke.lineCap,
88
+ lineJoin: gsStroke.lineJoin,
89
+ miterLimit: gsStroke.miterLimit
90
+ })
91
+ }
92
+
93
+ export const toOlFill = (gsFill: GsFillStyle): Fill => {
94
+ return new Fill({
95
+ color: gsFill.color
96
+ })
97
+ }
98
+
99
+ export const toOlCircleImage = (gsImage: GsImageStyle): CircleStyle => {
100
+ return new CircleStyle({
101
+ radius: gsImage.radius || 5,
102
+ fill: gsImage.fill ? toOlFill(gsImage.fill) : undefined,
103
+ stroke: gsImage.stroke ? toOlStroke(gsImage.stroke) : undefined,
104
+ displacement: gsImage.displacement,
105
+ scale: gsImage.scale,
106
+ rotation: gsImage.rotation,
107
+ rotateWithView: gsImage.rotateWithView,
108
+ declutterMode: undefined
109
+ })
110
+ }
111
+
112
+ export const toOlIconImage = (gsImage: GsImageStyle): Icon => {
113
+ if (!gsImage.src) {
114
+ throw new Error('Icon image requires src property')
115
+ }
116
+ return new Icon({
117
+ src: gsImage.src,
118
+ anchor: gsImage.anchor || [0.5, 1],
119
+ anchorXUnits: gsImage.anchorXUnits || 'fraction',
120
+ anchorYUnits: gsImage.anchorYUnits || 'fraction',
121
+ anchorOrigin: gsImage.anchorOrigin,
122
+ scale: gsImage.scale || 1,
123
+ opacity: gsImage.opacity,
124
+ rotation: gsImage.rotation,
125
+ rotateWithView: gsImage.rotateWithView,
126
+ displacement: gsImage.displacement,
127
+ offset: gsImage.offset,
128
+ offsetOrigin: gsImage.offsetOrigin,
129
+ size: gsImage.size,
130
+ color: gsImage.color,
131
+ crossOrigin: gsImage.crossOrigin,
132
+ declutterMode: undefined
133
+ })
134
+ }
135
+
136
+ export const toOlRegularShapeImage = (gsImage: GsImageStyle): RegularShape => {
137
+ return new RegularShape({
138
+ points: gsImage.points || 3,
139
+ radius: gsImage.radius || gsImage.radius1 || 5,
140
+ radius2: gsImage.radius2,
141
+ angle: gsImage.angle || 0,
142
+ displacement: gsImage.displacement,
143
+ fill: gsImage.fill ? toOlFill(gsImage.fill) : undefined,
144
+ stroke: gsImage.stroke ? toOlStroke(gsImage.stroke) : undefined,
145
+ rotation: gsImage.rotation,
146
+ rotateWithView: gsImage.rotateWithView,
147
+ scale: gsImage.scale,
148
+ declutterMode: undefined
149
+ })
150
+ }
151
+
152
+ export const toOlImage = (gsImage: GsImageStyle): CircleStyle | Icon | RegularShape => {
153
+ switch (gsImage.type) {
154
+ case 'circle':
155
+ return toOlCircleImage(gsImage)
156
+ case 'icon':
157
+ return toOlIconImage(gsImage)
158
+ case 'regular-shape':
159
+ return toOlRegularShapeImage(gsImage)
160
+ default:
161
+ throw new Error(`Unknown image type: ${gsImage.type}`)
162
+ }
163
+ }
164
+
165
+ export const toOlText = (gsText: GsTextStyle): Text => {
166
+ return new Text({
167
+ text: gsText.text,
168
+ font: gsText.font,
169
+ maxAngle: gsText.maxAngle,
170
+ offsetX: gsText.offsetX,
171
+ offsetY: gsText.offsetY,
172
+ overflow: gsText.overflow,
173
+ placement: gsText.placement,
174
+ repeat: gsText.repeat,
175
+ scale: gsText.scale,
176
+ rotateWithView: gsText.rotateWithView,
177
+ rotation: gsText.rotation,
178
+ textAlign: gsText.textAlign,
179
+ justify: gsText.justify,
180
+ textBaseline: gsText.textBaseline,
181
+ fill: gsText.fill ? toOlFill(gsText.fill) : undefined,
182
+ stroke: gsText.stroke ? toOlStroke(gsText.stroke) : undefined,
183
+ backgroundFill: gsText.backgroundFill ? toOlFill(gsText.backgroundFill) : undefined,
184
+ backgroundStroke: gsText.backgroundStroke ? toOlStroke(gsText.backgroundStroke) : undefined,
185
+ padding: gsText.padding,
186
+ declutterMode: undefined
187
+ })
188
+ }
189
+
190
+ export const toOlStyle = (gsStyle: GsStyle): Style => {
191
+ const styleOptions: any = {}
192
+
193
+ if (gsStyle.stroke) {
194
+ styleOptions.stroke = toOlStroke(gsStyle.stroke)
195
+ }
196
+
197
+ if (gsStyle.fill) {
198
+ styleOptions.fill = toOlFill(gsStyle.fill)
199
+ }
200
+
201
+ if (gsStyle.image) {
202
+ styleOptions.image = toOlImage(gsStyle.image)
203
+ }
204
+
205
+ if (gsStyle.text) {
206
+ styleOptions.text = toOlText(gsStyle.text)
207
+ }
208
+
209
+ if (gsStyle.zIndex !== undefined) {
210
+ styleOptions.zIndex = gsStyle.zIndex
211
+ }
212
+
213
+ return new Style(styleOptions)
214
+ }
215
+
216
+ export const toOlFeature = (feature: GsFeature): Feature => {
217
+ return withState(feature, new Feature({
218
+ geometry: toOLGeometry(feature.geometry),
219
+ }))
220
+ }
221
+
222
+ export const OL_SOURCES: any = {}
223
+ OL_SOURCES[GsSourceType.OSM] = (source: GsSource) => {
224
+ const olSource = new OSM();
225
+ olSource.set(KEY_LABEL, source.type)
226
+ return olSource
227
+ }
228
+ OL_SOURCES[GsSourceType.XYZ] = (source: GsSource) => {
229
+ const olSource = new XYZ({
230
+ url: source.url!,
231
+ crossOrigin: 'anonymous'
232
+ });
233
+ olSource.set(KEY_URL, source.url!)
234
+ olSource.set(KEY_LABEL, `${source.type}@${source.url}`)
235
+ return olSource
236
+ }
237
+ OL_SOURCES[GsSourceType.WMS] = (source: GsSource) => {
238
+ const olSource = new TileWMS({
239
+ url: source.url!,
240
+ params: {
241
+ 'LAYERS': '', // Will be overridden by source.state
242
+ ...source.state || {}
243
+ },
244
+ crossOrigin: 'anonymous' // Required for COEP, same as OSM default
245
+ })
246
+ olSource.set(KEY_URL, source.url!)
247
+ olSource.set(KEY_LABEL, `${source.type}@${source.url}`)
248
+ return olSource
249
+ }
250
+ OL_SOURCES[GsSourceType.WMTS] = (source: GsSource, olLayer?: Layer) => {
251
+ const state = source.state || {}
252
+ const parser = new WMTSCapabilities()
253
+
254
+ // Create a minimal placeholder source
255
+ const olSource = new WMTS({
256
+ url: source.url!,
257
+ layer: state['LAYER'] || '',
258
+ matrixSet: state['MATRIXSET'] || 'GLOBAL_WEBMERCATOR',
259
+ style: state['STYLE'] || 'default',
260
+ crossOrigin: 'anonymous'
261
+ } as any)
262
+
263
+ // Fetch and parse capabilities, then replace the source on the layer
264
+ fetch(source.url!)
265
+ .then(response => response.text())
266
+ .then(text => {
267
+ const result = parser.read(text)
268
+ const options = optionsFromCapabilities(result, {
269
+ layer: state['LAYER'] || '',
270
+ matrixSet: state['MATRIXSET'] || 'GLOBAL_WEBMERCATOR',
271
+ })
272
+
273
+ if (options) {
274
+ // Create a new WMTS source with proper options
275
+ const newSource = new WMTS({
276
+ ...options,
277
+ crossOrigin: 'anonymous'
278
+ })
279
+ newSource.set(KEY_URL, source.url!)
280
+ newSource.set(KEY_LABEL, `${source.type}@${source.url}`)
281
+
282
+ // Replace the source on the layer
283
+ if (olLayer && olLayer.setSource) {
284
+ olLayer.setSource(newSource)
285
+ }
286
+ }
287
+ })
288
+ .catch(error => {
289
+ console.error('Failed to fetch WMTS capabilities:', error)
290
+ })
291
+
292
+ olSource.set(KEY_URL, source.url!)
293
+ olSource.set(KEY_LABEL, `${source.type}@${source.url}`)
294
+ return olSource
295
+ }
296
+
297
+ const formatSource = (source: GsSource, format: FeatureFormat): Source => {
298
+ const olSource = new VectorSource({
299
+ format: format,
300
+ url: source.url!
301
+ })
302
+
303
+ olSource.set(KEY_FORMAT, source.type)
304
+ olSource.set(KEY_URL, source.url!)
305
+ olSource.set(KEY_LABEL, `${source.type}@${source.url}`)
306
+ return olSource
307
+ }
308
+
309
+ OL_SOURCES[GsSourceType.GeoJSON] = (source: GsSource) => {
310
+ return formatSource(source, new GeoJSON())
311
+ }
312
+ OL_SOURCES[GsSourceType.GPX] = (source: GsSource) => {
313
+ return formatSource(source, new GPX())
314
+ }
315
+ OL_SOURCES[GsSourceType.GeoTIFF] = (source: GsSource) => {
316
+ const olSource = new GeoTIFF({
317
+ sources: [
318
+ {
319
+ url: source.url!
320
+ }
321
+ ]
322
+ })
323
+
324
+ olSource.set(KEY_URL, source.url!)
325
+ olSource.set(KEY_LABEL, `${source.type}@${source.url}`)
326
+ return olSource
327
+ }
328
+ OL_SOURCES[GsSourceType.Features] = (source: GsSource) => {
329
+ const features = (source.features || [])
330
+ .map(feature => toOlFeature(feature))!
331
+ const olSource = new VectorSource({
332
+ features: features
333
+ })
334
+ olSource.set(KEY_LABEL, source.type)
335
+ return olSource
336
+ }
337
+
338
+ export const toOlSource = (source: GsSource, olLayer?: TileLayer<TileSource>) => {
339
+ return withState(source, OL_SOURCES[source.type](source, olLayer))
340
+ }
341
+
342
+ export const OL_LAYERS: any = {}
343
+ OL_LAYERS[GsLayerType.TILE] = (layer: GsLayer) => {
344
+ const tileLayer = new TileLayer()
345
+ const source = toOlSource(layer.source, tileLayer) as TileSource
346
+ tileLayer.setSource(source)
347
+ return tileLayer
348
+ }
349
+ OL_LAYERS[GsLayerType.VECTOR] = (layer: GsLayer) => {
350
+ return new VectorLayer({
351
+ source: toOlSource(layer.source) as VectorSource,
352
+ });
353
+ }
354
+ OL_LAYERS[GsLayerType.GROUP] = (layer: GsLayer) => {
355
+ const group = new LayerGroup();
356
+ group.set(KEY_URL, layer.source.url)
357
+ group.set(KEY_SOURCETYPE, layer.source.type)
358
+ applyMapboxStyle(group, layer.source.url).then()
359
+ return group
360
+ }
361
+ export const toOlLayer = (layer: GsLayer) => {
362
+ const olLayer: BaseLayer = withState(layer, OL_LAYERS[layer.type](layer))
363
+ olLayer.set(KEY_LABEL, layer.type)
364
+ olLayer.set(KEY_NAME, layer.name)
365
+ olLayer.set(KEY_GS_MANAGED, true)
366
+ olLayer.setVisible(layer.visible ?? true)
367
+ return olLayer
368
+ }
369
+
370
+ export class GsOlControl extends Control {
371
+ constructor(options?: any) {
372
+ const element = document.createElement('div');
373
+ element.style.margin = "0"
374
+ element.style.padding = "0"
375
+ super({
376
+ ...options,
377
+ element: element,
378
+ });
379
+ }
380
+
381
+ public getElement() {
382
+ return this.element;
383
+ }
384
+ }
385
+
386
+ export class GsOlOverlay extends Overlay {
387
+ constructor(options?: any) {
388
+ const element = document.createElement('div');
389
+ element.style.margin = "0"
390
+ element.style.padding = "0"
391
+ super({
392
+ ...options
393
+ });
394
+ }
395
+
396
+ public getElement() {
397
+ return this.element;
398
+ }
399
+ }
400
+
401
+ export const toOlOverlay = (overlay: GsOverlay) => {
402
+ const olOverlay = withState(overlay, new GsOlOverlay({
403
+ positioning: overlay.position,
404
+ stopEvent: true
405
+ }))
406
+ olOverlay.set(KEY_SRC, overlay.src)
407
+ return olOverlay
408
+ }
409
+
410
+ export const toOlControl = (control: GsControl) => {
411
+ const olControl = withState(control, new GsOlControl())
412
+ olControl.set(KEY_SRC, control.src)
413
+ return olControl
414
+ }
415
+
416
+ const importSrc = async (adapter: GsControlAdapter | GsOverlayAdapter, src: string, importer?: Importer) => {
417
+ const olMap = adapter.getMap()!;
418
+ return (importer || DefaultImporter)(src).then((mod) => {
419
+ const init = () => {
420
+ olMap.removeEventListener("rendercomplete", init)
421
+ const vars: any = {
422
+ // backward compatibility
423
+ ...lit,
424
+ // forward compatibility
425
+ lit: lit,
426
+ style: adapter.style.bind(adapter),
427
+ render: adapter.render.bind(adapter),
428
+ map: olMap,
429
+ element: adapter.getElement(),
430
+ querySelector: adapter.getElement().querySelector.bind(adapter.getElement()),
431
+ querySelectorAll: adapter.getElement().querySelector.bind(adapter.getElement()),
432
+ ol: ol,
433
+ env: olMap.get(KEY_ENV) || {},
434
+ utils: {
435
+ uuid: uuidv4
436
+ },
437
+ asset: (path: string) => {
438
+ return rtUtils.resolveUrl(`assets/${path}`)
439
+ },
440
+ signal: (_name: string) => {
441
+ },
442
+ events: (topic: string, callback: Function | any) => {
443
+ if (callback instanceof Function) {
444
+ const token = PubSub.subscribe(topic, (_message: string, data: any) => callback(data));
445
+ let subscriptions = olMap.get(KEY_EVENT_SUBSCRIPTIONS) as string[] | undefined;
446
+ if (!subscriptions) {
447
+ subscriptions = [];
448
+ olMap.set(KEY_EVENT_SUBSCRIPTIONS, subscriptions);
449
+ }
450
+ subscriptions.push(token);
451
+ return token;
452
+ } else {
453
+ return PubSub.publish(topic, callback);
454
+ }
455
+ },
456
+ settings: (key: string, callback?: Function | any) => {
457
+ const mapUuid = olMap.get(KEY_UUID) as string | undefined;
458
+ const storageKey = mapUuid ? `gs-settings-${mapUuid}` : 'gs-settings';
459
+
460
+ const loadSettings = (): any => {
461
+ try {
462
+ const stored = localStorage.getItem(storageKey);
463
+ return stored ? JSON.parse(stored) : {};
464
+ } catch {
465
+ return {};
466
+ }
467
+ };
468
+
469
+ const saveSettings = (settings: any): void => {
470
+ try {
471
+ localStorage.setItem(storageKey, JSON.stringify(settings));
472
+ } catch (error) {
473
+ console.error('Failed to save settings to localStorage:', error);
474
+ }
475
+ };
476
+
477
+ const settings = loadSettings();
478
+
479
+ // if no callback, assume caller wants the key value
480
+ if (callback === undefined) {
481
+ return settings[key];
482
+ }
483
+ // if a function, register as event topic, call it with current value, and return value
484
+ if (callback instanceof Function) {
485
+ vars.events(key, callback);
486
+ callback(settings[key]);
487
+ return settings[key];
488
+ }
489
+ settings[key] = callback;
490
+ saveSettings(settings);
491
+ // publish as event to inform settings listeners
492
+ return PubSub.publish(key, callback);
493
+ }
494
+ }
495
+ const objectType = adapter instanceof GsControlAdapter ? "control" : "overlay"
496
+ vars[objectType] = adapter
497
+
498
+ const templateFunction = mod instanceof Function ? mod : mod.default
499
+ if (templateFunction) {
500
+ vars.state = <T>(initialValue: T) => {
501
+ const createReactiveProperty = (target: any, key: string, getValue: () => any, setValue: (v: any) => void) => {
502
+ Object.defineProperty(target, key, {
503
+ get() {
504
+ return getValue();
505
+ },
506
+ set(newValue: any) {
507
+ const oldValue = getValue();
508
+ if (oldValue !== newValue) {
509
+ setValue(newValue);
510
+ adapter.render();
511
+ }
512
+ },
513
+ enumerable: true,
514
+ configurable: true
515
+ });
516
+ };
517
+
518
+ if (typeof initialValue === 'object' && initialValue !== null && !Array.isArray(initialValue)) {
519
+ const values: any = { ...initialValue };
520
+
521
+ return new Proxy({} as any, {
522
+ get(_target, prop: string) {
523
+ return values[prop];
524
+ },
525
+ set(_target, prop: string, newValue: any) {
526
+ if (values[prop] !== newValue) {
527
+ values[prop] = newValue;
528
+ adapter.render();
529
+ }
530
+ return true;
531
+ },
532
+ has(_target, prop: string) {
533
+ return prop in values;
534
+ },
535
+ ownKeys(_target) {
536
+ return Object.keys(values);
537
+ },
538
+ getOwnPropertyDescriptor(_target, prop: string) {
539
+ if (prop in values) {
540
+ return {
541
+ enumerable: true,
542
+ configurable: true,
543
+ value: values[prop]
544
+ };
545
+ }
546
+ return undefined;
547
+ }
548
+ });
549
+ } else {
550
+ const stateObj: any = {};
551
+ let value = initialValue;
552
+ createReactiveProperty(
553
+ stateObj,
554
+ 'value',
555
+ () => value,
556
+ (v) => { value = v; }
557
+ );
558
+ return stateObj;
559
+ }
560
+ };
561
+
562
+ const component = templateFunction(vars)
563
+
564
+ if (component instanceof Function) {
565
+ adapter.render(component);
566
+ } else {
567
+ adapter.render(component)
568
+ }
569
+ }
570
+ if (adapter instanceof GsControlAdapter) {
571
+ adapter.rendered()
572
+ }
573
+ olMap.render()
574
+ }
575
+ olMap.on("rendercomplete", init)
576
+ })
577
+ }
578
+
579
+ export const importOverlaySource = async (olOverlay: GsOlOverlay, src: string, importer?: Importer) => {
580
+ const overlayAdapter = new GsOverlayAdapter(olOverlay)
581
+ return importSrc(overlayAdapter, src, importer)
582
+ }
583
+
584
+ export const importControlSource = async (olControl: GsOlControl, src: string, importer?: Importer) => {
585
+ const controlAdapter = new GsControlAdapter(olControl)
586
+ return importSrc(controlAdapter, src, importer)
587
+ }
588
+
589
+ export type Importer = (src: string) => Promise<any>
590
+ export const DefaultImporter: Importer = (src: string) => import(src)
591
+
592
+ export const cleanupEventSubscriptions = (olMap: Map): void => {
593
+ const subscriptions = olMap.get(KEY_EVENT_SUBSCRIPTIONS) as string[] | undefined;
594
+ if (subscriptions) {
595
+ subscriptions.forEach(token => PubSub.unsubscribe(token));
596
+ olMap.set(KEY_EVENT_SUBSCRIPTIONS, []);
597
+ }
598
+ }
599
+
600
+ export const toOlMap = async (gsMap: GsMap, options?: MapOptions, env?: any, importer?: Importer) => {
601
+ const olMap = withState(gsMap, new Map(options));
602
+ olMap.set(KEY_ENV, env)
603
+ olMap.setView(new View({
604
+ center: gsMap.view.center && gsMap.view.center.length == 2 ? gsMap.view.center : DEFAULT_GSMAP.view.center,
605
+ zoom: gsMap.view.zoom || DEFAULT_GSMAP.view.zoom,
606
+ projection: gsMap.view.projection || DEFAULT_GSMAP.view.projection
607
+ }))
608
+ for (const layer of gsMap.layers || []) {
609
+ const olLayer = toOlLayer(layer)
610
+ olMap.addLayer(olLayer)
611
+ }
612
+
613
+ for (const overlay of gsMap.overlays || []) {
614
+ const olOverlay = toOlOverlay(overlay)
615
+ olMap.addOverlay(olOverlay)
616
+ await importOverlaySource(olOverlay, overlay.src, importer)
617
+ }
618
+
619
+ for (const control of gsMap.controls || []) {
620
+ const olControl = toOlControl(control)
621
+ olMap.addControl(olControl)
622
+ await importControlSource(olControl, control.src, importer)
623
+ }
624
+
625
+ return olMap
626
+ }
package/src/gs-lib.ts ADDED
@@ -0,0 +1,54 @@
1
+ import {toOlMap, Importer} from "./gs-gs2ol";
2
+ import {GsMap} from "./gs-model";
3
+ import "ol/ol.css";
4
+ import {defaultControls, defaultInteractions} from "./gs-olns";
5
+ // Note: WebAwesome is imported via gs-litns (which is imported by gs-gs2ol), so it's available to user modules
6
+
7
+ export * from "./gs-model";
8
+ export * from "./gs-gs2ol";
9
+
10
+ export interface GsAppOptions {
11
+ containerSelector: string | HTMLElement,
12
+ gsMap: GsMap,
13
+ env?: any,
14
+ modules?: any,
15
+ importer?: Importer,
16
+ mapOptions?: {
17
+ controls: any
18
+ }
19
+ }
20
+
21
+ export const gsLib = async (options: GsAppOptions) => {
22
+ const mapOptions = {
23
+ interactions: defaultInteractions({keyboard: false}),
24
+ controls: defaultControls(options.mapOptions?.controls)
25
+ }
26
+
27
+ let importer: Importer | undefined = options.importer;
28
+
29
+ if (!importer && options.modules) {
30
+ importer = async (src: string) => {
31
+ const module = options.modules[src];
32
+ if (module) {
33
+ // If module is a string, it's a URL/identifier - import it
34
+ if (typeof module === 'string') {
35
+ return import(module);
36
+ }
37
+ // If module is already an object, it's the imported module - return it directly
38
+ return module;
39
+ }
40
+ throw new Error(`Module not found: ${src}`);
41
+ };
42
+ }
43
+
44
+ const olMap = await toOlMap(options.gsMap, mapOptions, options.env, importer)
45
+
46
+ // Handle both string selector and DOM element
47
+ const target = typeof options.containerSelector === 'string'
48
+ ? document.querySelector(options.containerSelector)! as HTMLElement
49
+ : options.containerSelector;
50
+
51
+ olMap.setTarget(target)
52
+ return olMap
53
+ }
54
+