geokernel-electron 1.1.36 → 1.1.38

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.
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geokernel-electron",
3
- "version": "1.1.36",
3
+ "version": "1.1.38",
4
4
  "description": "Electron bindings for the GeoKernel GIS SDK.",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -274,13 +274,33 @@ const SIGNATURES = Object.freeze({
274
274
  "GeoKernelViewer_ZoomToLayer": ["bool", ["void_p", "int"]],
275
275
  "GeoKernelViewer_ZoomToScreenRect": ["void", ["void_p", "int", "int", "int", "int"]],
276
276
  "GeoKernelViewer_ZoomToSelectedFeatures": ["bool", ["void_p"]],
277
+ "GeoKernelWindow_AddCancellableLayerLoadToolbar": ["bool", ["void_p", "void_p", "wchar_p"]],
278
+ "GeoKernelWindow_AddCommandToolbar": ["bool", ["void_p", "wchar_p", "command_callback", "void_p"]],
279
+ "GeoKernelWindow_AddControlPanel": ["bool", ["void_p", "wchar_p", "control_callback", "void_p"]],
277
280
  "GeoKernelWindow_AddLayerManagementToolbar": ["bool", ["void_p", "void_p", "wchar_p"]],
281
+ "GeoKernelWindow_AddLayerPanel": ["bool", ["void_p", "void_p", "int"]],
282
+ "GeoKernelWindow_AddLayerSelector": ["bool", ["void_p", "void_p", "int"]],
283
+ "GeoKernelWindow_AddLayerStyleToolbar": ["bool", ["void_p", "void_p", "wchar_p"]],
284
+ "GeoKernelWindow_AddLegendPanel": ["bool", ["void_p", "wchar_p"]],
285
+ "GeoKernelWindow_AddLogPanel": ["bool", ["void_p", "wchar_p"]],
278
286
  "GeoKernelWindow_AddMeasureToolbar": ["void", ["void_p", "void_p"]],
279
287
  "GeoKernelWindow_AddNavigationToolbar": ["void", ["void_p", "void_p"]],
288
+ "GeoKernelWindow_AddSpatialIndexBenchmarkToolbar": ["bool", ["void_p", "void_p", "wchar_p"]],
289
+ "GeoKernelWindow_AddViewerPane": ["void_p", ["void_p", "wchar_p"]],
290
+ "GeoKernelWindow_AppendLog": ["void", ["void_p", "wchar_p"]],
291
+ "GeoKernelWindow_ClearLog": ["void", ["void_p"]],
280
292
  "GeoKernelWindow_Create": ["void_p", ["int", "int", "wchar_p"]],
281
293
  "GeoKernelWindow_Destroy": ["void", ["void_p"]],
294
+ "GeoKernelWindow_GetSelectedLayerIndex": ["int", ["void_p"]],
282
295
  "GeoKernelWindow_IsVisible": ["bool", ["void_p"]],
283
296
  "GeoKernelWindow_SetCentralViewer": ["void_p", ["void_p"]],
297
+ "GeoKernelWindow_SetCentralTextPanel": ["bool", ["void_p", "wchar_p"]],
298
+ "GeoKernelWindow_SetCentralText": ["bool", ["void_p", "wchar_p"]],
299
+ "GeoKernelWindow_SetControlValue": ["bool", ["void_p", "int", "double", "wchar_p"]],
300
+ "GeoKernelWindow_SetLegendItemsJson": ["bool", ["void_p", "wchar_p"]],
301
+ "GeoKernelWindow_SetLegendTitle": ["bool", ["void_p", "wchar_p"]],
302
+ "GeoKernelWindow_SetLegendWidth": ["bool", ["void_p", "int"]],
303
+ "GeoKernelWindow_SetStatusText": ["void", ["void_p", "wchar_p"]],
284
304
  "GeoKernelWindow_Show": ["void", ["void_p"]],
285
305
  });
286
306
 
@@ -59,6 +59,18 @@ const GeoKernelViewerSpatialIndexStateCallback = koffi.proto(
59
59
  ["int", "void *"],
60
60
  );
61
61
 
62
+ const GeoKernelWindowCommandCallback = koffi.proto(
63
+ "GeoKernelWindowCommandCallback",
64
+ "void",
65
+ ["int", "void *"],
66
+ );
67
+
68
+ const GeoKernelWindowControlCallback = koffi.proto(
69
+ "GeoKernelWindowControlCallback",
70
+ "void",
71
+ ["int", "double", "str16", "void *"],
72
+ );
73
+
62
74
  const TYPES = Object.freeze({
63
75
  void: "void",
64
76
  void_p: "void *",
@@ -86,6 +98,8 @@ const TYPES = Object.freeze({
86
98
  progress_callback: koffi.pointer(GeoKernelViewerLayerLoadProgressCallback),
87
99
  cancel_callback: koffi.pointer(GeoKernelViewerLayerLoadCancellationCallback),
88
100
  index_state_callback: koffi.pointer(GeoKernelViewerSpatialIndexStateCallback),
101
+ command_callback: koffi.pointer(GeoKernelWindowCommandCallback),
102
+ control_callback: koffi.pointer(GeoKernelWindowControlCallback),
89
103
  });
90
104
 
91
105
  module.exports = {
@@ -97,5 +111,7 @@ module.exports = {
97
111
  GeoKernelViewerRouteSummary,
98
112
  GeoKernelViewerScreenRectangle,
99
113
  GeoKernelViewerSpatialIndexStateCallback,
114
+ GeoKernelWindowCommandCallback,
115
+ GeoKernelWindowControlCallback,
100
116
  TYPES,
101
117
  };
package/src/types.js CHANGED
@@ -13,6 +13,37 @@ const ViewerTool = Object.freeze({
13
13
  EDIT_VERTICES: 9,
14
14
  });
15
15
 
16
+ const ViewerEventType = Object.freeze({
17
+ ACTIVE_TOOL_CHANGED: 1,
18
+ LAYERS_CHANGED: 2,
19
+ LAYER_ADDED: 3,
20
+ LAYER_REMOVED: 4,
21
+ LAYER_VISIBILITY_CHANGED: 5,
22
+ LAYER_EDIT_STATE_CHANGED: 6,
23
+ LAYER_EDIT_SESSION_STARTED: 7,
24
+ LAYER_EDIT_SESSION_COMMITTED: 8,
25
+ LAYER_EDIT_SESSION_ROLLED_BACK: 9,
26
+ LAYER_ORDER_CHANGED: 10,
27
+ SELECTION_CHANGED: 11,
28
+ VISIBLE_EXTENT_CHANGED: 12,
29
+ ZOOM_CHANGED: 13,
30
+ VIEW_CHANGED: 14,
31
+ BUSY_CHANGED: 15,
32
+ PROJECT_OPENED: 16,
33
+ PROJECT_CLOSED: 17,
34
+ MAP_SELECTION_BOX_FINISHED: 18,
35
+ MOUSE_COORDINATES_CHANGED: 19,
36
+ MAP_MOUSE_DOWN: 20,
37
+ MAP_MOUSE_MOVE: 21,
38
+ MAP_MOUSE_UP: 22,
39
+ LAYER_ADDING: 23,
40
+ LAYER_REMOVING: 24,
41
+ INDEX_CREATING: 25,
42
+ INDEX_CREATED: 26,
43
+ INDEX_LOADED: 27,
44
+ RENDER_BACKEND_CHANGED: 28,
45
+ });
46
+
16
47
  const ShapeType = Object.freeze({
17
48
  UNKNOWN: 0,
18
49
  POINT: 1,
@@ -25,6 +56,14 @@ const ShapeType = Object.freeze({
25
56
  MULTI_POINT_Z: 18,
26
57
  });
27
58
 
59
+ const AttributeType = Object.freeze({
60
+ STRING: 0,
61
+ INTEGER: 1,
62
+ DOUBLE: 2,
63
+ BOOLEAN: 3,
64
+ DATE_TIME: 4,
65
+ });
66
+
28
67
  const SpatialIndexType = Object.freeze({
29
68
  LINEAR: 0,
30
69
  RTREE: 1,
@@ -104,6 +143,7 @@ function routeSummary(value) {
104
143
  }
105
144
 
106
145
  module.exports = {
146
+ AttributeType,
107
147
  ClassificationMethod,
108
148
  ColorRampMode,
109
149
  MeasureMode,
@@ -114,6 +154,7 @@ module.exports = {
114
154
  SpatialIndexType,
115
155
  SymbolStyleTarget,
116
156
  ViewerTool,
157
+ ViewerEventType,
117
158
  extent,
118
159
  point,
119
160
  routeSummary,
package/src/viewer.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
 
3
3
  const fs = require("fs");
4
+ const koffi = require("koffi");
5
+ const { GeoKernelViewerEventData, TYPES } = require("./native-types");
4
6
 
5
7
  const { createNativeLibrary } = require("./native");
6
8
  const { MAP_STYLES, argb } = require("./styles");
@@ -63,10 +65,20 @@ class ViewerWindow {
63
65
  throw new Error("GeoKernelWindow_Create failed.");
64
66
  }
65
67
 
66
- this.viewerHandle = this.library.windowSetCentralViewer(this.windowHandle);
67
- if (!this.viewerHandle) {
68
+ const paneTitles = Array.isArray(options.viewerPanes) ? options.viewerPanes : null;
69
+ const paneHandles = paneTitles?.map((paneTitle) =>
70
+ this.library.function("GeoKernelWindow_AddViewerPane")(this.windowHandle, paneTitle));
71
+ this.viewerHandle = paneHandles?.[0] ?? this.library.windowSetCentralViewer(this.windowHandle);
72
+ if (!this.viewerHandle || paneHandles?.some((handle) => !handle)) {
68
73
  this.library.windowDestroy(this.windowHandle);
69
- throw new Error("GeoKernelWindow_SetCentralViewer failed.");
74
+ throw new Error(paneTitles
75
+ ? "GeoKernelWindow_AddViewerPane failed."
76
+ : "GeoKernelWindow_SetCentralViewer failed.");
77
+ }
78
+
79
+ this.paneFacades = [];
80
+ if (paneHandles) {
81
+ this.paneFacades = paneHandles.slice(1).map((handle) => this._createPaneFacade(handle));
70
82
  }
71
83
 
72
84
  if (options.navigationToolbar !== false) {
@@ -81,15 +93,49 @@ class ViewerWindow {
81
93
  this.addLayerManagementToolbar(options.layerManagementToolbar);
82
94
  }
83
95
 
96
+ if (options.layerPanel) {
97
+ this.addLayerPanel(options.layerPanel);
98
+ }
99
+ if (options.layerSelector) {
100
+ this.addLayerSelector(options.layerSelector);
101
+ }
102
+ if (options.layerStyleToolbar) {
103
+ this.addLayerStyleToolbar(options.layerStyleToolbar);
104
+ }
105
+
84
106
  this.interval = null;
85
107
  this.visibleOnce = false;
86
108
  this.startedAt = 0;
109
+ this.commandCallbacks = [];
110
+ this.controlCallbacks = [];
111
+ this.eventCallbacks = [];
87
112
  }
88
113
 
89
114
  setActivationType(value) {
90
115
  this.library.setActivationType(this.viewerHandle, value);
91
116
  }
92
117
 
118
+ pane(index) {
119
+ if (index === 0) return this;
120
+ return this.paneFacades[index - 1] ?? null;
121
+ }
122
+
123
+ _createPaneFacade(viewerHandle) {
124
+ const pane = Object.create(ViewerWindow.prototype);
125
+ pane.library = this.library;
126
+ pane.windowHandle = this.windowHandle;
127
+ pane.viewerHandle = viewerHandle;
128
+ pane.interval = null;
129
+ pane.visibleOnce = false;
130
+ pane.startedAt = 0;
131
+ pane.commandCallbacks = [];
132
+ pane.controlCallbacks = [];
133
+ pane.eventCallbacks = [];
134
+ pane.paneFacades = [];
135
+ pane.isPaneFacade = true;
136
+ return pane;
137
+ }
138
+
93
139
  setLicenseManagerUrl(value) {
94
140
  this.library.setLicenseManagerUrl(this.viewerHandle, value ?? "");
95
141
  }
@@ -113,6 +159,164 @@ class ViewerWindow {
113
159
  }
114
160
  }
115
161
 
162
+ addLayerPanel(options = {}) {
163
+ const features =
164
+ (options.allowReorder === true ? 1 : 0) |
165
+ (options.allowVisibility === true ? 2 : 0);
166
+ const ok = this.library.function("GeoKernelWindow_AddLayerPanel")(
167
+ this.windowHandle,
168
+ this.viewerHandle,
169
+ features,
170
+ );
171
+ if (!ok) {
172
+ throw new Error("GeoKernelWindow_AddLayerPanel failed.");
173
+ }
174
+ }
175
+
176
+ addLayerSelector(options = {}) {
177
+ const actions = { zoom: 1 };
178
+ const action = actions[options.action] ?? 0;
179
+ const ok = this.library.function("GeoKernelWindow_AddLayerSelector")(
180
+ this.windowHandle,
181
+ this.viewerHandle,
182
+ action,
183
+ );
184
+ if (!ok) {
185
+ throw new Error("GeoKernelWindow_AddLayerSelector failed.");
186
+ }
187
+ }
188
+
189
+ addLayerStyleToolbar(options = {}) {
190
+ const ok = this.library.function("GeoKernelWindow_AddLayerStyleToolbar")(
191
+ this.windowHandle,
192
+ this.viewerHandle,
193
+ jsonText(options),
194
+ );
195
+ if (!ok) {
196
+ throw new Error("GeoKernelWindow_AddLayerStyleToolbar failed.");
197
+ }
198
+ }
199
+
200
+ addSpatialIndexBenchmarkToolbar(options = {}) {
201
+ const ok = this.library.function("GeoKernelWindow_AddSpatialIndexBenchmarkToolbar")(
202
+ this.windowHandle,
203
+ this.viewerHandle,
204
+ jsonText(options),
205
+ );
206
+ if (!ok) throw new Error("GeoKernelWindow_AddSpatialIndexBenchmarkToolbar failed.");
207
+ }
208
+
209
+ addCancellableLayerLoadToolbar(options = {}) {
210
+ const ok = this.library.function("GeoKernelWindow_AddCancellableLayerLoadToolbar")(
211
+ this.windowHandle, this.viewerHandle, jsonText(options),
212
+ );
213
+ if (!ok) throw new Error("GeoKernelWindow_AddCancellableLayerLoadToolbar failed.");
214
+ }
215
+
216
+ addCommandToolbar(commands, onCommand) {
217
+ const callback = koffi.register((commandId) => onCommand(commandId), TYPES.command_callback);
218
+ this.commandCallbacks.push(callback);
219
+ const ok = this.library.function("GeoKernelWindow_AddCommandToolbar")(
220
+ this.windowHandle, jsonText({ commands }), callback, null,
221
+ );
222
+ if (!ok) throw new Error("GeoKernelWindow_AddCommandToolbar failed.");
223
+ }
224
+
225
+ addControlPanel(configuration, onChange) {
226
+ const callback = koffi.register(
227
+ (controlId, numericValue, textValue) => onChange(controlId, numericValue, textValue ?? ""),
228
+ TYPES.control_callback,
229
+ );
230
+ this.controlCallbacks.push(callback);
231
+ const ok = this.library.function("GeoKernelWindow_AddControlPanel")(
232
+ this.windowHandle, jsonText(configuration), callback, null,
233
+ );
234
+ if (!ok) throw new Error("GeoKernelWindow_AddControlPanel failed.");
235
+ }
236
+
237
+ setControlValue(controlId, value) {
238
+ const numericValue = typeof value === "number" ? value : 0;
239
+ const textValue = typeof value === "string" ? value : null;
240
+ return this.library.function("GeoKernelWindow_SetControlValue")(
241
+ this.windowHandle, Number(controlId), numericValue, textValue,
242
+ );
243
+ }
244
+
245
+ setStatusText(text) {
246
+ this.library.function("GeoKernelWindow_SetStatusText")(this.windowHandle, text ?? "");
247
+ }
248
+
249
+ setCentralTextPanel(text = "") {
250
+ const ok = this.library.function("GeoKernelWindow_SetCentralTextPanel")(
251
+ this.windowHandle,
252
+ text,
253
+ );
254
+ if (!ok) throw new Error("GeoKernelWindow_SetCentralTextPanel failed.");
255
+ }
256
+
257
+ setCentralText(text) {
258
+ const ok = this.library.function("GeoKernelWindow_SetCentralText")(
259
+ this.windowHandle,
260
+ text,
261
+ );
262
+ if (!ok) throw new Error("GeoKernelWindow_SetCentralText failed.");
263
+ }
264
+
265
+ addLogPanel(title = "Event log") {
266
+ const ok = this.library.function("GeoKernelWindow_AddLogPanel")(this.windowHandle, title);
267
+ if (!ok) throw new Error("GeoKernelWindow_AddLogPanel failed.");
268
+ }
269
+
270
+ addLegendPanel(title = "Legend") {
271
+ const ok = this.library.function("GeoKernelWindow_AddLegendPanel")(this.windowHandle, title);
272
+ if (!ok) throw new Error("GeoKernelWindow_AddLegendPanel failed.");
273
+ }
274
+
275
+ setLegendTitle(title) {
276
+ return this.library.function("GeoKernelWindow_SetLegendTitle")(this.windowHandle, title);
277
+ }
278
+
279
+ setLegendWidth(width) {
280
+ return this.library.function("GeoKernelWindow_SetLegendWidth")(this.windowHandle, Number(width));
281
+ }
282
+
283
+ setLegendItems(items) {
284
+ return this.library.function("GeoKernelWindow_SetLegendItemsJson")(
285
+ this.windowHandle, jsonText(items),
286
+ );
287
+ }
288
+
289
+ appendLog(text) {
290
+ this.library.function("GeoKernelWindow_AppendLog")(this.windowHandle, text ?? "");
291
+ }
292
+
293
+ clearLog() {
294
+ this.library.function("GeoKernelWindow_ClearLog")(this.windowHandle);
295
+ }
296
+
297
+ selectedLayerIndex() {
298
+ return this.library.function("GeoKernelWindow_GetSelectedLayerIndex")(this.windowHandle);
299
+ }
300
+
301
+ setEventCallback(onEvent) {
302
+ this.library.function("GeoKernelViewer_SetEventCallback")(this.viewerHandle, null, null);
303
+ for (const callback of this.eventCallbacks) koffi.unregister(callback);
304
+ this.eventCallbacks = [];
305
+ if (typeof onEvent !== "function") return;
306
+
307
+ const callback = koffi.register((dataPointer, text) => {
308
+ const data = koffi.decode(dataPointer, GeoKernelViewerEventData);
309
+ onEvent({
310
+ ...data,
311
+ extent: { ...data.extent },
312
+ screenRectangle: { ...data.screenRectangle },
313
+ text: text ?? "",
314
+ });
315
+ }, TYPES.event_callback);
316
+ this.eventCallbacks.push(callback);
317
+ this.library.function("GeoKernelViewer_SetEventCallback")(this.viewerHandle, callback, null);
318
+ }
319
+
116
320
  getTool() {
117
321
  return this._call("GeoKernelViewer_GetTool");
118
322
  }
@@ -208,11 +412,154 @@ class ViewerWindow {
208
412
  return this._call("GeoKernelViewer_AddEmptyVectorLayer", name, Number(shapeType), jsonText(style));
209
413
  }
210
414
 
415
+ beginEditLayer(index) { return this._call("GeoKernelViewer_BeginEditLayer", Number(index)); }
416
+ commitEditLayer(index) { return this._call("GeoKernelViewer_CommitEditLayer", Number(index)); }
417
+ rollbackEditLayer(index) { return this._call("GeoKernelViewer_RollbackEditLayer", Number(index)); }
418
+ isLayerEditing(index) { return this._call("GeoKernelViewer_IsLayerEditing", Number(index)); }
419
+ canEditLayer(index) { return this._call("GeoKernelViewer_CanEditLayer", Number(index)); }
420
+ setActiveEditLayerIndex(index) { return this._call("GeoKernelViewer_SetActiveEditLayerIndex", Number(index)); }
421
+ addLayerAttributeDefinition(index, name, type, length = 0, decimalCount = 0) {
422
+ return this._call(
423
+ "GeoKernelViewer_AddLayerAttributeDefinition",
424
+ Number(index),
425
+ name,
426
+ Number(type),
427
+ Number(length),
428
+ Number(decimalCount),
429
+ );
430
+ }
431
+ addPointToEditLayer(index, x, y, attributes) {
432
+ if (attributes === undefined) {
433
+ return this._call("GeoKernelViewer_AddPointToEditLayer", Number(index), Number(x), Number(y));
434
+ }
435
+ return this._call(
436
+ "GeoKernelViewer_AddPointToEditLayerWithAttributes",
437
+ Number(index),
438
+ Number(x),
439
+ Number(y),
440
+ jsonText(attributes),
441
+ );
442
+ }
443
+ addPolylineToEditLayer(index, points, attributes) {
444
+ const xy = xyArray(points);
445
+ if (attributes !== undefined) {
446
+ return this._call(
447
+ "GeoKernelViewer_AddPolylineToEditLayerWithAttributes",
448
+ Number(index),
449
+ xy,
450
+ xy.length / 2,
451
+ jsonText(attributes),
452
+ );
453
+ }
454
+ return this._call("GeoKernelViewer_AddPolylineToEditLayer", Number(index), xy, xy.length / 2);
455
+ }
456
+ addPolygonToEditLayer(index, points, attributes) {
457
+ const xy = xyArray(points);
458
+ if (attributes !== undefined) {
459
+ return this._call(
460
+ "GeoKernelViewer_AddPolygonToEditLayerWithAttributes",
461
+ Number(index),
462
+ xy,
463
+ xy.length / 2,
464
+ jsonText(attributes),
465
+ );
466
+ }
467
+ return this._call("GeoKernelViewer_AddPolygonToEditLayer", Number(index), xy, xy.length / 2);
468
+ }
469
+ deleteShapeFromEditLayer(index, shapeId) {
470
+ return this._call("GeoKernelViewer_DeleteShapeFromEditLayer", Number(index), Number(shapeId));
471
+ }
472
+ deleteSelectedFeaturesFromEditLayer() {
473
+ return this._call("GeoKernelViewer_DeleteSelectedFeaturesFromEditLayer");
474
+ }
475
+ canMoveSelectedFeatures() {
476
+ return this._call("GeoKernelViewer_CanMoveSelectedFeatures");
477
+ }
478
+ canEditSelectedFeatures() {
479
+ return this._call("GeoKernelViewer_CanEditSelectedFeatures");
480
+ }
481
+ canEditSelectedVertices() {
482
+ return this._call("GeoKernelViewer_CanEditSelectedVertices");
483
+ }
484
+ deleteSelectedVertexFromEditLayer() {
485
+ return this._call("GeoKernelViewer_DeleteSelectedVertexFromEditLayer");
486
+ }
487
+ insertSelectedFeatureVertexInEditLayer(partIndex, insertIndex, worldX, worldY) {
488
+ return this._call(
489
+ "GeoKernelViewer_InsertSelectedFeatureVertexInEditLayer",
490
+ Number(partIndex),
491
+ Number(insertIndex),
492
+ Number(worldX),
493
+ Number(worldY),
494
+ );
495
+ }
496
+ deleteSelectedFeatureVertexInEditLayer(partIndex, vertexIndex) {
497
+ return this._call(
498
+ "GeoKernelViewer_DeleteSelectedFeatureVertexInEditLayer",
499
+ Number(partIndex),
500
+ Number(vertexIndex),
501
+ );
502
+ }
503
+ canUndoEditLayer(index) {
504
+ return this._call("GeoKernelViewer_CanUndoEditLayer", Number(index));
505
+ }
506
+ canRedoEditLayer(index) {
507
+ return this._call("GeoKernelViewer_CanRedoEditLayer", Number(index));
508
+ }
509
+ undoEditLayer(index) {
510
+ return this._call("GeoKernelViewer_UndoEditLayer", Number(index));
511
+ }
512
+ redoEditLayer(index) {
513
+ return this._call("GeoKernelViewer_RedoEditLayer", Number(index));
514
+ }
515
+ setShapeAttributesInEditLayer(index, shapeId, attributes) {
516
+ return this._call(
517
+ "GeoKernelViewer_SetShapeAttributesInEditLayerJson",
518
+ Number(index),
519
+ Number(shapeId),
520
+ jsonText(attributes),
521
+ );
522
+ }
523
+ editSnappingEnabled() {
524
+ return this._call("GeoKernelViewer_GetEditSnappingEnabled");
525
+ }
526
+ setEditSnappingEnabled(enabled) {
527
+ this._call("GeoKernelViewer_SetEditSnappingEnabled", Boolean(enabled));
528
+ }
529
+ editSnappingTolerancePixels() {
530
+ return this._call("GeoKernelViewer_GetEditSnappingTolerancePixels");
531
+ }
532
+ setEditSnappingTolerancePixels(tolerance) {
533
+ this._call("GeoKernelViewer_SetEditSnappingTolerancePixels", Number(tolerance));
534
+ }
535
+ isLayerDirty(index) {
536
+ return this._call("GeoKernelViewer_IsLayerDirty", Number(index));
537
+ }
538
+ moveSelectedFeaturesInEditLayer(deltaX, deltaY) {
539
+ return this._call("GeoKernelViewer_MoveSelectedFeaturesInEditLayer", Number(deltaX), Number(deltaY));
540
+ }
541
+
211
542
  addPointLayer(name, points, style) {
212
543
  const xy = xyArray(points);
213
544
  return this._call("GeoKernelViewer_AddPointLayer", name, xy, xy.length / 2, jsonText(style));
214
545
  }
215
546
 
547
+ addAttributedPointLayer(name, points, attributes, style, sourceEpsg = 4326) {
548
+ if (!Array.isArray(attributes) || attributes.length !== points.length) {
549
+ throw new Error("Attributed point layers require one attribute object per point.");
550
+ }
551
+ const xy = xyArray(points);
552
+ return this._call(
553
+ "GeoKernelViewer_AddAttributedPointLayer",
554
+ name,
555
+ xy,
556
+ xy.length / 2,
557
+ jsonText(attributes),
558
+ jsonText(style),
559
+ Number(sourceEpsg),
560
+ );
561
+ }
562
+
216
563
  addPolylineLayer(name, parts, style) {
217
564
  const arrays = partsArrays(parts);
218
565
  return this._call("GeoKernelViewer_AddPolylineLayer", name, arrays.xy, arrays.counts, arrays.counts.length, jsonText(style));
@@ -227,6 +574,10 @@ class ViewerWindow {
227
574
  this.library.setMapBackgroundColor(this.viewerHandle, argb(color));
228
575
  }
229
576
 
577
+ invalidateRenderCache(clearTileCache = false, clearLayerCache = false) {
578
+ this._call("GeoKernelViewer_InvalidateRenderCache", Boolean(clearTileCache), Boolean(clearLayerCache));
579
+ }
580
+
230
581
  getBackground() {
231
582
  return this._call("GeoKernelViewer_GetMapBackgroundColor");
232
583
  }
@@ -345,7 +696,7 @@ class ViewerWindow {
345
696
  options.colorRampName ?? "Unique",
346
697
  options.categoryLimit ?? 64,
347
698
  options.reverseColorRamp ?? false,
348
- options.sampleLimit ?? 0,
699
+ options.sampleLimit ?? 5000,
349
700
  options.styleTarget ?? 0,
350
701
  options.startSize ?? 4.0,
351
702
  options.endSize ?? 14.0,
@@ -366,7 +717,7 @@ class ViewerWindow {
366
717
  breaks.length,
367
718
  options.colorRampMode ?? 0,
368
719
  options.reverseColorRamp ?? false,
369
- options.sampleLimit ?? 0,
720
+ options.sampleLimit ?? 5000,
370
721
  options.styleTarget ?? 0,
371
722
  options.startSize ?? 4.0,
372
723
  options.endSize ?? 18.0,
@@ -400,6 +751,49 @@ class ViewerWindow {
400
751
  this._call("GeoKernelViewer_SetViewExtent", extentObject(value));
401
752
  }
402
753
 
754
+ setCoordinateSystemPreset(preset) {
755
+ return this._call("GeoKernelViewer_SetCoordinateSystemPreset", String(preset));
756
+ }
757
+
758
+ setLayerCoordinateSystemPreset(index, preset) {
759
+ return this._call(
760
+ "GeoKernelViewer_SetLayerCoordinateSystemPreset",
761
+ Number(index),
762
+ String(preset),
763
+ );
764
+ }
765
+
766
+ transformPoint(sourceEpsg, targetEpsg, x, y) {
767
+ const transformedX = [0];
768
+ const transformedY = [0];
769
+ const ok = this.library.function("GeoKernelViewer_TransformPoint")(
770
+ Number(sourceEpsg),
771
+ Number(targetEpsg),
772
+ Number(x),
773
+ Number(y),
774
+ transformedX,
775
+ transformedY,
776
+ );
777
+ return ok ? point(transformedX[0], transformedY[0]) : null;
778
+ }
779
+
780
+ coordinateSystemFromAuthority(authority, code) {
781
+ return this._readStaticJson(
782
+ "GeoKernelViewer_CoordinateSystemFromAuthorityJson",
783
+ {},
784
+ String(authority),
785
+ Number(code),
786
+ );
787
+ }
788
+
789
+ coordinateSystemFromEpsg(code) {
790
+ return this._readStaticJson(
791
+ "GeoKernelViewer_CoordinateSystemFromEpsgJson",
792
+ {},
793
+ Number(code),
794
+ );
795
+ }
796
+
403
797
  worldToScreen(x, y) {
404
798
  const screenX = [0];
405
799
  const screenY = [0];
@@ -474,6 +868,68 @@ class ViewerWindow {
474
868
  return this._readJson("GeoKernelViewer_GetLayerFeatureAttributesJson", {}, Number(index), Number(rowIndex));
475
869
  }
476
870
 
871
+ hitTestTopFeatureAt(screenX, screenY, pixelTolerance = 8) {
872
+ return this._readJson(
873
+ "GeoKernelViewer_HitTestTopFeatureAtJson",
874
+ null,
875
+ Number(screenX),
876
+ Number(screenY),
877
+ Number(pixelTolerance),
878
+ );
879
+ }
880
+
881
+ hitTestFeaturesAt(screenX, screenY, pixelTolerance = 8) {
882
+ return this._readJson(
883
+ "GeoKernelViewer_HitTestFeaturesAtJson",
884
+ [],
885
+ Number(screenX),
886
+ Number(screenY),
887
+ Number(pixelTolerance),
888
+ );
889
+ }
890
+
891
+ hitTestFeatures(worldX, worldY, worldTolerance) {
892
+ return this._readJson(
893
+ "GeoKernelViewer_HitTestFeaturesJson",
894
+ [],
895
+ Number(worldX),
896
+ Number(worldY),
897
+ Number(worldTolerance),
898
+ );
899
+ }
900
+
901
+ hitTestFeaturesInScreenRect(left, top, right, bottom) {
902
+ return this._readJson(
903
+ "GeoKernelViewer_HitTestFeaturesInScreenRectJson",
904
+ [],
905
+ Number(left),
906
+ Number(top),
907
+ Number(right),
908
+ Number(bottom),
909
+ );
910
+ }
911
+
912
+ selectFeaturesInScreenRect(left, top, right, bottom, selectionMode = 0) {
913
+ const x = Math.min(Number(left), Number(right));
914
+ const y = Math.min(Number(top), Number(bottom));
915
+ const width = Math.abs(Number(right) - Number(left)) + 1;
916
+ const height = Math.abs(Number(bottom) - Number(top)) + 1;
917
+ return this._call("GeoKernelViewer_SelectFeaturesInScreenRect", x, y, width, height, Number(selectionMode));
918
+ }
919
+
920
+ selectFeatureHit(hit, worldTolerance) {
921
+ if (!hit?.worldPoint) return false;
922
+ return this._call(
923
+ "GeoKernelViewer_SelectFeatureHit",
924
+ Number(hit.worldPoint.x),
925
+ Number(hit.worldPoint.y),
926
+ Number(worldTolerance),
927
+ Number(hit.layerIndex),
928
+ Number(hit.shapeId),
929
+ Number(hit.featureId),
930
+ );
931
+ }
932
+
477
933
  layerProjectedExtent(index) {
478
934
  const value = {};
479
935
  const ok = this._call("GeoKernelViewer_GetLayerProjectedExtent", Number(index), value);
@@ -521,6 +977,34 @@ class ViewerWindow {
521
977
  this._call("GeoKernelViewer_ClearShapes");
522
978
  }
523
979
 
980
+ clearSelectedFeatures() {
981
+ this._call("GeoKernelViewer_ClearSelectedFeatures");
982
+ }
983
+
984
+ selectTopFeatureAt(screenX, screenY, pixelTolerance = 8) {
985
+ return this._call("GeoKernelViewer_SelectTopFeatureAt", Number(screenX), Number(screenY), Number(pixelTolerance));
986
+ }
987
+
988
+ addTopFeatureToSelectionAt(screenX, screenY, pixelTolerance = 8) {
989
+ return this._call("GeoKernelViewer_AddTopFeatureToSelectionAt", Number(screenX), Number(screenY), Number(pixelTolerance));
990
+ }
991
+
992
+ toggleTopFeatureSelectionAt(screenX, screenY, pixelTolerance = 8) {
993
+ return this._call("GeoKernelViewer_ToggleTopFeatureSelectionAt", Number(screenX), Number(screenY), Number(pixelTolerance));
994
+ }
995
+
996
+ selectedFeatures() {
997
+ return this._readJson("GeoKernelViewer_GetSelectedFeaturesJson", []);
998
+ }
999
+
1000
+ selectedFeatureCount() {
1001
+ return this._call("GeoKernelViewer_GetSelectedFeatureCount");
1002
+ }
1003
+
1004
+ zoomToSelectedFeatures() {
1005
+ return this._call("GeoKernelViewer_ZoomToSelectedFeatures");
1006
+ }
1007
+
524
1008
  clearLayers() {
525
1009
  this._call("GeoKernelViewer_ClearLayers");
526
1010
  }
@@ -566,6 +1050,27 @@ class ViewerWindow {
566
1050
  this.interval = null;
567
1051
  }
568
1052
 
1053
+ for (const pane of this.paneFacades) {
1054
+ if (pane.viewerHandle) {
1055
+ pane.library.function("GeoKernelViewer_SetEventCallback")(pane.viewerHandle, null, null);
1056
+ for (const callback of pane.eventCallbacks) koffi.unregister(callback);
1057
+ pane.eventCallbacks = [];
1058
+ pane.clearAll();
1059
+ pane.viewerHandle = null;
1060
+ }
1061
+ }
1062
+ this.paneFacades = [];
1063
+
1064
+ if (this.viewerHandle) {
1065
+ this.library.function("GeoKernelViewer_SetEventCallback")(this.viewerHandle, null, null);
1066
+ }
1067
+ for (const callback of this.eventCallbacks) koffi.unregister(callback);
1068
+ this.eventCallbacks = [];
1069
+ for (const callback of this.commandCallbacks) koffi.unregister(callback);
1070
+ this.commandCallbacks = [];
1071
+ for (const callback of this.controlCallbacks) koffi.unregister(callback);
1072
+ this.controlCallbacks = [];
1073
+
569
1074
  if (this.viewerHandle) {
570
1075
  this.clearAll();
571
1076
  this.viewerHandle = null;
@@ -592,6 +1097,21 @@ class ViewerWindow {
592
1097
  return parseJson(readWideJson(this.library.function(name), this.viewerHandle, ...args), fallback);
593
1098
  }
594
1099
 
1100
+ _readStaticJson(name, fallback, ...args) {
1101
+ const fn = this.library.function(name);
1102
+ let bufferLength = 4096;
1103
+ for (let attempt = 0; attempt < 4; attempt += 1) {
1104
+ const out = ["\0".repeat(bufferLength)];
1105
+ const length = fn(...args, out, bufferLength);
1106
+ if (length < 0) throw new Error(`${name} failed with result ${length}.`);
1107
+ const nullIndex = out[0].indexOf("\0");
1108
+ const text = nullIndex >= 0 ? out[0].slice(0, nullIndex) : out[0];
1109
+ if (length < bufferLength - 1) return parseJson(text, fallback);
1110
+ bufferLength = Math.max(bufferLength * 2, length + 1);
1111
+ }
1112
+ throw new Error(`${name} output exceeded the supported buffer size.`);
1113
+ }
1114
+
595
1115
  _requireViewer() {
596
1116
  if (!this.viewerHandle) {
597
1117
  throw new Error("GeoKernel viewer is closed.");