cesiumjs-anywidget 0.4.0__py3-none-any.whl → 0.6.0__py3-none-any.whl

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.
@@ -1,18 +1,49 @@
1
1
  // Generated bundle - DO NOT EDIT DIRECTLY. Edit files in src/cesiumjs_anywidget/js/ instead.
2
2
 
3
3
 
4
+ // src/cesiumjs_anywidget/js/logger.js
5
+ var debugEnabled = false;
6
+ function setDebugMode(enabled) {
7
+ debugEnabled = enabled;
8
+ if (enabled) {
9
+ console.log("[CesiumWidget] Debug mode enabled");
10
+ }
11
+ }
12
+ function log(prefix, ...args) {
13
+ if (debugEnabled) {
14
+ console.log(`[CesiumWidget:${prefix}]`, ...args);
15
+ }
16
+ }
17
+ function warn(prefix, ...args) {
18
+ console.warn(`[CesiumWidget:${prefix}]`, ...args);
19
+ }
20
+ function error(prefix, ...args) {
21
+ console.error(`[CesiumWidget:${prefix}]`, ...args);
22
+ }
23
+
4
24
  // src/cesiumjs_anywidget/js/viewer-init.js
25
+ var PREFIX = "ViewerInit";
5
26
  async function loadCesiumJS() {
27
+ log(PREFIX, "Loading CesiumJS...");
6
28
  if (window.Cesium) {
29
+ log(PREFIX, "CesiumJS already loaded, reusing existing instance");
7
30
  return window.Cesium;
8
31
  }
9
32
  const script = document.createElement("script");
10
33
  script.src = "https://cesium.com/downloads/cesiumjs/releases/1.135/Build/Cesium/Cesium.js";
34
+ log(PREFIX, "Loading CesiumJS from CDN...");
11
35
  await new Promise((resolve, reject) => {
12
- script.onload = resolve;
13
- script.onerror = reject;
36
+ script.onload = () => {
37
+ log(PREFIX, "CesiumJS script loaded successfully");
38
+ resolve();
39
+ };
40
+ script.onerror = (err) => {
41
+ error(PREFIX, "Failed to load CesiumJS script:", err);
42
+ reject(err);
43
+ };
14
44
  document.head.appendChild(script);
15
45
  });
46
+ log(PREFIX, "CesiumJS initialized");
16
47
  return window.Cesium;
17
48
  }
18
49
  function createLoadingIndicator(container, hasToken) {
@@ -34,6 +65,7 @@ function createLoadingIndicator(container, hasToken) {
34
65
  return loadingDiv;
35
66
  }
36
67
  function createViewer(container, model, Cesium) {
68
+ log(PREFIX, "Creating viewer with options...");
37
69
  const viewerOptions = {
38
70
  timeline: model.get("show_timeline"),
39
71
  animation: model.get("show_animation"),
@@ -47,17 +79,28 @@ function createViewer(container, model, Cesium) {
47
79
  shadows: false,
48
80
  shouldAnimate: false
49
81
  };
82
+ log(PREFIX, "Viewer options:", viewerOptions);
50
83
  if (model.get("enable_terrain")) {
51
84
  viewerOptions.terrain = Cesium.Terrain.fromWorldTerrain();
85
+ log(PREFIX, "Terrain enabled");
52
86
  }
53
87
  const viewer = new Cesium.Viewer(container, viewerOptions);
54
88
  viewer.scene.globe.enableLighting = model.get("enable_lighting");
89
+ log(PREFIX, "Viewer created, lighting:", model.get("enable_lighting"));
55
90
  return viewer;
56
91
  }
57
92
  function setupViewerListeners(viewer, model, container, Cesium) {
93
+ log(PREFIX, "Setting up viewer listeners");
94
+ let isDestroyed = false;
95
+ let scrubTimeout = null;
58
96
  model.on("change:enable_terrain", () => {
97
+ if (isDestroyed) {
98
+ log(PREFIX, "Skipping enable_terrain change - destroyed");
99
+ return;
100
+ }
59
101
  if (!viewer)
60
102
  return;
103
+ log(PREFIX, "Terrain setting changed:", model.get("enable_terrain"));
61
104
  if (model.get("enable_terrain")) {
62
105
  viewer.scene.setTerrain(Cesium.Terrain.fromWorldTerrain());
63
106
  } else {
@@ -65,32 +108,47 @@ function setupViewerListeners(viewer, model, container, Cesium) {
65
108
  }
66
109
  });
67
110
  model.on("change:enable_lighting", () => {
111
+ if (isDestroyed)
112
+ return;
68
113
  if (!viewer)
69
114
  return;
115
+ log(PREFIX, "Lighting setting changed:", model.get("enable_lighting"));
70
116
  viewer.scene.globe.enableLighting = model.get("enable_lighting");
71
117
  });
72
118
  model.on("change:height", () => {
119
+ if (isDestroyed)
120
+ return;
73
121
  if (!viewer)
74
122
  return;
123
+ log(PREFIX, "Height changed:", model.get("height"));
75
124
  container.style.height = model.get("height");
76
125
  viewer.resize();
77
126
  });
78
127
  model.on("change:show_timeline", () => {
128
+ if (isDestroyed)
129
+ return;
79
130
  if (!viewer || !viewer.timeline)
80
131
  return;
132
+ log(PREFIX, "Timeline visibility changed:", model.get("show_timeline"));
81
133
  viewer.timeline.container.style.visibility = model.get("show_timeline") ? "visible" : "hidden";
82
134
  });
83
135
  model.on("change:show_animation", () => {
136
+ if (isDestroyed)
137
+ return;
84
138
  if (!viewer || !viewer.animation)
85
139
  return;
140
+ log(PREFIX, "Animation visibility changed:", model.get("show_animation"));
86
141
  viewer.animation.container.style.visibility = model.get("show_animation") ? "visible" : "hidden";
87
142
  });
88
143
  model.on("change:atmosphere_settings", () => {
144
+ if (isDestroyed)
145
+ return;
89
146
  if (!viewer || !viewer.scene || !viewer.scene.atmosphere)
90
147
  return;
91
148
  const settings = model.get("atmosphere_settings");
92
149
  if (!settings || Object.keys(settings).length === 0)
93
150
  return;
151
+ log(PREFIX, "Atmosphere settings changed:", settings);
94
152
  const atmosphere = viewer.scene.atmosphere;
95
153
  if (settings.brightnessShift !== void 0) {
96
154
  atmosphere.brightnessShift = settings.brightnessShift;
@@ -129,11 +187,14 @@ function setupViewerListeners(viewer, model, container, Cesium) {
129
187
  }
130
188
  });
131
189
  model.on("change:sky_atmosphere_settings", () => {
190
+ if (isDestroyed)
191
+ return;
132
192
  if (!viewer || !viewer.scene || !viewer.scene.skyAtmosphere)
133
193
  return;
134
194
  const settings = model.get("sky_atmosphere_settings");
135
195
  if (!settings || Object.keys(settings).length === 0)
136
196
  return;
197
+ log(PREFIX, "Sky atmosphere settings changed:", settings);
137
198
  const skyAtmosphere = viewer.scene.skyAtmosphere;
138
199
  if (settings.show !== void 0) {
139
200
  skyAtmosphere.show = settings.show;
@@ -178,11 +239,14 @@ function setupViewerListeners(viewer, model, container, Cesium) {
178
239
  }
179
240
  });
180
241
  model.on("change:skybox_settings", () => {
242
+ if (isDestroyed)
243
+ return;
181
244
  if (!viewer || !viewer.scene || !viewer.scene.skyBox)
182
245
  return;
183
246
  const settings = model.get("skybox_settings");
184
247
  if (!settings || Object.keys(settings).length === 0)
185
248
  return;
249
+ log(PREFIX, "SkyBox settings changed:", settings);
186
250
  const skyBox = viewer.scene.skyBox;
187
251
  if (settings.show !== void 0) {
188
252
  skyBox.show = settings.show;
@@ -207,104 +271,155 @@ function setupViewerListeners(viewer, model, container, Cesium) {
207
271
  }
208
272
  });
209
273
  function getCameraState() {
210
- const cartographic = viewer.camera.positionCartographic;
211
- return {
212
- latitude: Cesium.Math.toDegrees(cartographic.latitude),
213
- longitude: Cesium.Math.toDegrees(cartographic.longitude),
214
- altitude: cartographic.height,
215
- heading: Cesium.Math.toDegrees(viewer.camera.heading),
216
- pitch: Cesium.Math.toDegrees(viewer.camera.pitch),
217
- roll: Cesium.Math.toDegrees(viewer.camera.roll)
218
- };
274
+ if (!viewer || !viewer.camera || !viewer.camera.positionCartographic) {
275
+ warn(PREFIX, "Cannot get camera state - viewer or camera not available");
276
+ return null;
277
+ }
278
+ try {
279
+ const cartographic = viewer.camera.positionCartographic;
280
+ return {
281
+ latitude: Cesium.Math.toDegrees(cartographic.latitude),
282
+ longitude: Cesium.Math.toDegrees(cartographic.longitude),
283
+ altitude: cartographic.height,
284
+ heading: Cesium.Math.toDegrees(viewer.camera.heading),
285
+ pitch: Cesium.Math.toDegrees(viewer.camera.pitch),
286
+ roll: Cesium.Math.toDegrees(viewer.camera.roll)
287
+ };
288
+ } catch (error2) {
289
+ warn(PREFIX, "Error getting camera state:", error2);
290
+ return null;
291
+ }
219
292
  }
220
293
  function getClockState() {
221
- if (!viewer.clock)
294
+ if (!viewer || !viewer.clock)
222
295
  return null;
223
- return {
224
- current_time: Cesium.JulianDate.toIso8601(viewer.clock.currentTime),
225
- multiplier: viewer.clock.multiplier,
226
- is_animating: viewer.clock.shouldAnimate
227
- };
296
+ try {
297
+ return {
298
+ current_time: Cesium.JulianDate.toIso8601(viewer.clock.currentTime),
299
+ multiplier: viewer.clock.multiplier,
300
+ is_animating: viewer.clock.shouldAnimate
301
+ };
302
+ } catch (error2) {
303
+ warn(PREFIX, "Error getting clock state:", error2);
304
+ return null;
305
+ }
228
306
  }
229
307
  function sendInteractionEvent(type, additionalData = {}) {
308
+ if (isDestroyed) {
309
+ log(PREFIX, "Skipping interaction event - destroyed:", type);
310
+ return;
311
+ }
312
+ if (!viewer) {
313
+ warn(PREFIX, "Cannot send interaction event - viewer not available");
314
+ return;
315
+ }
316
+ const cameraState = getCameraState();
317
+ if (!cameraState) {
318
+ warn(PREFIX, "Skipping interaction event - camera state not available");
319
+ return;
320
+ }
230
321
  const event = {
231
322
  type,
232
323
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
233
- camera: getCameraState(),
324
+ camera: cameraState,
234
325
  clock: getClockState(),
235
326
  ...additionalData
236
327
  };
237
- console.log("[CesiumWidget] Interaction event:", type, event);
328
+ log(PREFIX, "Interaction event:", type, event);
238
329
  model.set("interaction_event", event);
239
330
  model.save_changes();
240
331
  }
241
332
  const camera = viewer.camera;
242
333
  camera.moveEnd.addEventListener(() => {
334
+ if (isDestroyed || !viewer)
335
+ return;
243
336
  sendInteractionEvent("camera_move");
244
337
  });
245
338
  const scene = viewer.scene;
246
339
  const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
247
340
  handler.setInputAction((click) => {
341
+ if (isDestroyed || !viewer || !viewer.scene || !viewer.camera)
342
+ return;
248
343
  const pickedData = {};
249
- const ray = viewer.camera.getPickRay(click.position);
250
- const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
251
- if (cartesian) {
252
- const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
253
- pickedData.picked_position = {
254
- latitude: Cesium.Math.toDegrees(cartographic.latitude),
255
- longitude: Cesium.Math.toDegrees(cartographic.longitude),
256
- altitude: cartographic.height
257
- };
344
+ try {
345
+ const ray = viewer.camera.getPickRay(click.position);
346
+ if (ray && viewer.scene.globe) {
347
+ const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
348
+ if (cartesian) {
349
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
350
+ pickedData.picked_position = {
351
+ latitude: Cesium.Math.toDegrees(cartographic.latitude),
352
+ longitude: Cesium.Math.toDegrees(cartographic.longitude),
353
+ altitude: cartographic.height
354
+ };
355
+ }
356
+ }
357
+ } catch (error2) {
358
+ warn(PREFIX, "Error picking position:", error2);
258
359
  }
259
- const pickedObject = viewer.scene.pick(click.position);
260
- if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
261
- const entity = pickedObject.id;
262
- pickedData.picked_entity = {
263
- id: entity.id,
264
- name: entity.name || null
265
- };
266
- if (entity.properties) {
267
- const props = {};
268
- const propertyNames = entity.properties.propertyNames;
269
- if (propertyNames && propertyNames.length > 0) {
270
- propertyNames.forEach((name) => {
271
- try {
272
- props[name] = entity.properties[name].getValue(viewer.clock.currentTime);
273
- } catch (e) {
360
+ try {
361
+ const pickedObject = viewer.scene.pick(click.position);
362
+ if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
363
+ const entity = pickedObject.id;
364
+ pickedData.picked_entity = {
365
+ id: entity.id,
366
+ name: entity.name || null
367
+ };
368
+ if (entity.properties) {
369
+ const props = {};
370
+ const propertyNames = entity.properties.propertyNames;
371
+ if (propertyNames && propertyNames.length > 0) {
372
+ propertyNames.forEach((name) => {
373
+ try {
374
+ props[name] = entity.properties[name].getValue(viewer.clock.currentTime);
375
+ } catch (e) {
376
+ }
377
+ });
378
+ if (Object.keys(props).length > 0) {
379
+ pickedData.picked_entity.properties = props;
274
380
  }
275
- });
276
- if (Object.keys(props).length > 0) {
277
- pickedData.picked_entity.properties = props;
278
381
  }
279
382
  }
280
383
  }
384
+ } catch (error2) {
385
+ warn(PREFIX, "Error picking entity:", error2);
281
386
  }
282
387
  sendInteractionEvent("left_click", pickedData);
283
388
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
284
389
  handler.setInputAction((click) => {
390
+ if (isDestroyed || !viewer || !viewer.scene || !viewer.camera)
391
+ return;
285
392
  const pickedData = {};
286
- const ray = viewer.camera.getPickRay(click.position);
287
- const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
288
- if (cartesian) {
289
- const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
290
- pickedData.picked_position = {
291
- latitude: Cesium.Math.toDegrees(cartographic.latitude),
292
- longitude: Cesium.Math.toDegrees(cartographic.longitude),
293
- altitude: cartographic.height
294
- };
393
+ try {
394
+ const ray = viewer.camera.getPickRay(click.position);
395
+ if (ray && viewer.scene.globe) {
396
+ const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
397
+ if (cartesian) {
398
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
399
+ pickedData.picked_position = {
400
+ latitude: Cesium.Math.toDegrees(cartographic.latitude),
401
+ longitude: Cesium.Math.toDegrees(cartographic.longitude),
402
+ altitude: cartographic.height
403
+ };
404
+ }
405
+ }
406
+ } catch (error2) {
407
+ warn(PREFIX, "Error picking position:", error2);
295
408
  }
296
409
  sendInteractionEvent("right_click", pickedData);
297
410
  }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
298
411
  if (viewer.timeline) {
299
412
  let timelineScrubbing = false;
300
- let scrubTimeout = null;
301
413
  viewer.clock.onTick.addEventListener(() => {
414
+ if (isDestroyed)
415
+ return;
302
416
  if (viewer.timeline) {
303
417
  if (scrubTimeout) {
304
418
  clearTimeout(scrubTimeout);
419
+ scrubTimeout = null;
305
420
  }
306
421
  scrubTimeout = setTimeout(() => {
307
- if (timelineScrubbing) {
422
+ if (!isDestroyed && timelineScrubbing) {
308
423
  timelineScrubbing = false;
309
424
  sendInteractionEvent("timeline_scrub");
310
425
  }
@@ -313,13 +428,23 @@ function setupViewerListeners(viewer, model, container, Cesium) {
313
428
  }
314
429
  });
315
430
  }
431
+ log(PREFIX, "Viewer listeners setup complete");
316
432
  }
317
433
  function setupGeoJSONLoader(viewer, model, Cesium) {
434
+ log(PREFIX, "Setting up GeoJSON loader");
318
435
  let geojsonDataSources = [];
319
- model.on("change:geojson_data", async () => {
320
- if (!viewer || !viewer.dataSources)
436
+ let isDestroyed = false;
437
+ async function loadGeoJSONData(flyToData = true) {
438
+ if (isDestroyed) {
439
+ log(PREFIX, "Skipping geojson_data load - destroyed");
440
+ return;
441
+ }
442
+ if (!viewer || !viewer.dataSources) {
443
+ warn(PREFIX, "Cannot load GeoJSON - viewer or dataSources not available");
321
444
  return;
445
+ }
322
446
  const geojsonDataArray = model.get("geojson_data");
447
+ log(PREFIX, "Loading GeoJSON data, count:", geojsonDataArray?.length || 0);
323
448
  geojsonDataSources.forEach((dataSource) => {
324
449
  if (viewer && viewer.dataSources) {
325
450
  viewer.dataSources.remove(dataSource);
@@ -329,6 +454,7 @@ function setupGeoJSONLoader(viewer, model, Cesium) {
329
454
  if (geojsonDataArray && Array.isArray(geojsonDataArray)) {
330
455
  for (const geojsonData of geojsonDataArray) {
331
456
  try {
457
+ log(PREFIX, "Loading GeoJSON dataset...");
332
458
  const dataSource = await Cesium.GeoJsonDataSource.load(geojsonData, {
333
459
  stroke: Cesium.Color.HOTPINK,
334
460
  fill: Cesium.Color.PINK.withAlpha(0.5),
@@ -337,18 +463,28 @@ function setupGeoJSONLoader(viewer, model, Cesium) {
337
463
  if (viewer && viewer.dataSources) {
338
464
  viewer.dataSources.add(dataSource);
339
465
  geojsonDataSources.push(dataSource);
466
+ log(PREFIX, "GeoJSON dataset loaded successfully");
340
467
  }
341
- } catch (error) {
342
- console.error("Error loading GeoJSON:", error);
468
+ } catch (error2) {
469
+ error2(PREFIX, "Error loading GeoJSON:", error2);
343
470
  }
344
471
  }
345
- if (geojsonDataSources.length > 0 && viewer && viewer.flyTo) {
472
+ if (flyToData && geojsonDataSources.length > 0 && viewer && viewer.flyTo) {
473
+ log(PREFIX, "Flying to GeoJSON data");
346
474
  viewer.flyTo(geojsonDataSources[0]);
347
475
  }
348
476
  }
349
- });
477
+ }
478
+ model.on("change:geojson_data", () => loadGeoJSONData(true));
479
+ const initialData = model.get("geojson_data");
480
+ if (initialData && Array.isArray(initialData) && initialData.length > 0) {
481
+ log(PREFIX, "Loading initial GeoJSON data...");
482
+ loadGeoJSONData(true);
483
+ }
350
484
  return {
351
485
  destroy: () => {
486
+ log(PREFIX, "Destroying GeoJSON loader");
487
+ isDestroyed = true;
352
488
  geojsonDataSources.forEach((dataSource) => {
353
489
  if (viewer) {
354
490
  viewer.dataSources.remove(dataSource);
@@ -359,11 +495,20 @@ function setupGeoJSONLoader(viewer, model, Cesium) {
359
495
  };
360
496
  }
361
497
  function setupCZMLLoader(viewer, model, Cesium) {
498
+ log(PREFIX, "Setting up CZML loader");
362
499
  let czmlDataSources = [];
363
- model.on("change:czml_data", async () => {
364
- if (!viewer || !viewer.dataSources)
500
+ let isDestroyed = false;
501
+ async function loadCZMLData(flyToData = true) {
502
+ if (isDestroyed) {
503
+ log(PREFIX, "Skipping czml_data load - destroyed");
365
504
  return;
505
+ }
506
+ if (!viewer || !viewer.dataSources) {
507
+ warn(PREFIX, "Cannot load CZML - viewer or dataSources not available");
508
+ return;
509
+ }
366
510
  const czmlDataArray = model.get("czml_data");
511
+ log(PREFIX, "Loading CZML data, count:", czmlDataArray?.length || 0);
367
512
  czmlDataSources.forEach((dataSource) => {
368
513
  if (viewer && viewer.dataSources) {
369
514
  viewer.dataSources.remove(dataSource);
@@ -374,23 +519,36 @@ function setupCZMLLoader(viewer, model, Cesium) {
374
519
  for (const czmlData of czmlDataArray) {
375
520
  if (Array.isArray(czmlData) && czmlData.length > 0) {
376
521
  try {
522
+ log(PREFIX, "Loading CZML document with", czmlData.length, "packets...");
377
523
  const dataSource = await Cesium.CzmlDataSource.load(czmlData);
378
524
  if (viewer && viewer.dataSources) {
379
525
  viewer.dataSources.add(dataSource);
380
526
  czmlDataSources.push(dataSource);
527
+ log(PREFIX, "CZML document loaded successfully, entities:", dataSource.entities.values.length);
381
528
  }
382
- } catch (error) {
383
- console.error("Error loading CZML:", error);
529
+ } catch (error2) {
530
+ error2(PREFIX, "Error loading CZML:", error2);
384
531
  }
532
+ } else {
533
+ warn(PREFIX, "Skipping invalid CZML data (not an array or empty):", czmlData);
385
534
  }
386
535
  }
387
- if (czmlDataSources.length > 0 && viewer && viewer.flyTo) {
536
+ if (flyToData && czmlDataSources.length > 0 && viewer && viewer.flyTo) {
537
+ log(PREFIX, "Flying to CZML data");
388
538
  viewer.flyTo(czmlDataSources[0]);
389
539
  }
390
540
  }
391
- });
541
+ }
542
+ model.on("change:czml_data", () => loadCZMLData(true));
543
+ const initialData = model.get("czml_data");
544
+ if (initialData && Array.isArray(initialData) && initialData.length > 0) {
545
+ log(PREFIX, "Loading initial CZML data...");
546
+ loadCZMLData(true);
547
+ }
392
548
  return {
393
549
  destroy: () => {
550
+ log(PREFIX, "Destroying CZML loader");
551
+ isDestroyed = true;
394
552
  czmlDataSources.forEach((dataSource) => {
395
553
  if (viewer) {
396
554
  viewer.dataSources.remove(dataSource);
@@ -402,30 +560,60 @@ function setupCZMLLoader(viewer, model, Cesium) {
402
560
  }
403
561
 
404
562
  // src/cesiumjs_anywidget/js/camera-sync.js
563
+ var PREFIX2 = "CameraSync";
405
564
  function initializeCameraSync(viewer, model) {
406
565
  const Cesium = window.Cesium;
407
- let cameraUpdateTimeout;
566
+ let cameraUpdateTimeout = null;
567
+ let isDestroyed = false;
568
+ let syncEnabled = model.get("camera_sync_enabled") || false;
569
+ log(PREFIX2, "Initializing camera synchronization, sync enabled:", syncEnabled);
570
+ model.on("change:camera_sync_enabled", () => {
571
+ syncEnabled = model.get("camera_sync_enabled");
572
+ log(PREFIX2, "Camera sync enabled changed:", syncEnabled);
573
+ });
408
574
  function updateCameraFromModel() {
409
- if (!viewer)
575
+ if (isDestroyed) {
576
+ log(PREFIX2, "Skipping updateCameraFromModel - module destroyed");
577
+ return;
578
+ }
579
+ if (!viewer) {
580
+ warn(PREFIX2, "updateCameraFromModel called but viewer is null");
410
581
  return;
582
+ }
411
583
  const lat = model.get("latitude");
412
584
  const lon = model.get("longitude");
413
585
  const alt = model.get("altitude");
414
586
  const heading = Cesium.Math.toRadians(model.get("heading"));
415
587
  const pitch = Cesium.Math.toRadians(model.get("pitch"));
416
588
  const roll = Cesium.Math.toRadians(model.get("roll"));
589
+ log(PREFIX2, "Updating camera from model:", { lat, lon, alt });
417
590
  viewer.camera.setView({
418
591
  destination: Cesium.Cartesian3.fromDegrees(lon, lat, alt),
419
592
  orientation: { heading, pitch, roll }
420
593
  });
421
594
  }
422
595
  function updateModelFromCamera() {
423
- if (!viewer)
596
+ if (isDestroyed) {
597
+ log(PREFIX2, "Skipping updateModelFromCamera - module destroyed");
424
598
  return;
599
+ }
600
+ if (!syncEnabled) {
601
+ log(PREFIX2, "Skipping updateModelFromCamera - sync disabled");
602
+ return;
603
+ }
604
+ if (!viewer) {
605
+ warn(PREFIX2, "updateModelFromCamera called but viewer is null");
606
+ return;
607
+ }
425
608
  const position = viewer.camera.positionCartographic;
426
609
  const heading = viewer.camera.heading;
427
610
  const pitch = viewer.camera.pitch;
428
611
  const roll = viewer.camera.roll;
612
+ log(PREFIX2, "Updating model from camera:", {
613
+ lat: Cesium.Math.toDegrees(position.latitude),
614
+ lon: Cesium.Math.toDegrees(position.longitude),
615
+ alt: position.height
616
+ });
429
617
  model.set("latitude", Cesium.Math.toDegrees(position.latitude));
430
618
  model.set("longitude", Cesium.Math.toDegrees(position.longitude));
431
619
  model.set("altitude", position.height);
@@ -435,9 +623,20 @@ function initializeCameraSync(viewer, model) {
435
623
  model.save_changes();
436
624
  }
437
625
  function handleCameraChanged() {
438
- clearTimeout(cameraUpdateTimeout);
626
+ if (isDestroyed) {
627
+ log(PREFIX2, "Skipping handleCameraChanged - module destroyed");
628
+ return;
629
+ }
630
+ if (!syncEnabled) {
631
+ return;
632
+ }
633
+ if (cameraUpdateTimeout) {
634
+ clearTimeout(cameraUpdateTimeout);
635
+ }
439
636
  cameraUpdateTimeout = setTimeout(() => {
440
- updateModelFromCamera();
637
+ if (!isDestroyed && syncEnabled) {
638
+ updateModelFromCamera();
639
+ }
441
640
  }, 500);
442
641
  }
443
642
  updateCameraFromModel();
@@ -449,10 +648,15 @@ function initializeCameraSync(viewer, model) {
449
648
  model.on("change:pitch", updateCameraFromModel);
450
649
  model.on("change:roll", updateCameraFromModel);
451
650
  model.on("change:camera_command", () => {
651
+ if (isDestroyed) {
652
+ log(PREFIX2, "Skipping camera_command - module destroyed");
653
+ return;
654
+ }
452
655
  const command = model.get("camera_command");
453
656
  if (!command || !command.command || !command.timestamp)
454
657
  return;
455
658
  const cmd = command.command;
659
+ log(PREFIX2, "Executing camera command:", cmd, command);
456
660
  try {
457
661
  switch (cmd) {
458
662
  case "flyTo":
@@ -535,27 +739,36 @@ function initializeCameraSync(viewer, model) {
535
739
  viewer.camera.zoomOut(command.distance || 100);
536
740
  break;
537
741
  default:
538
- console.warn(`Unknown camera command: ${cmd}`);
742
+ warn(PREFIX2, `Unknown camera command: ${cmd}`);
539
743
  }
540
- } catch (error) {
541
- console.error(`Error executing camera command ${cmd}:`, error);
744
+ } catch (err) {
745
+ error(PREFIX2, `Error executing camera command ${cmd}:`, err);
542
746
  }
543
747
  });
544
748
  return {
545
749
  updateCameraFromModel,
546
750
  updateModelFromCamera,
547
751
  destroy: () => {
548
- clearTimeout(cameraUpdateTimeout);
752
+ log(PREFIX2, "Destroying camera sync module");
753
+ isDestroyed = true;
754
+ if (cameraUpdateTimeout) {
755
+ clearTimeout(cameraUpdateTimeout);
756
+ cameraUpdateTimeout = null;
757
+ }
549
758
  viewer.camera.changed.removeEventListener(handleCameraChanged);
759
+ log(PREFIX2, "Camera sync module destroyed");
550
760
  }
551
761
  };
552
762
  }
553
763
 
554
764
  // src/cesiumjs_anywidget/js/measurement-tools.js
765
+ var PREFIX3 = "Measurements";
555
766
  function initializeMeasurementTools(viewer, model, container) {
767
+ log(PREFIX3, "Initializing measurement tools");
556
768
  const Cesium = window.Cesium;
557
769
  let measurementHandler = null;
558
770
  let editHandler = null;
771
+ let isDestroyed = false;
559
772
  let measurementState = {
560
773
  mode: null,
561
774
  points: [],
@@ -777,6 +990,7 @@ function initializeMeasurementTools(viewer, model, container) {
777
990
  };
778
991
  }
779
992
  function clearAllMeasurements() {
993
+ log(PREFIX3, "Clearing all measurements");
780
994
  measurementState.entities.forEach((e) => viewer.entities.remove(e));
781
995
  measurementState.labels.forEach((l) => viewer.entities.remove(l));
782
996
  measurementState.polylines.forEach((p) => viewer.entities.remove(p));
@@ -941,12 +1155,12 @@ function initializeMeasurementTools(viewer, model, container) {
941
1155
  const editLatInput = document.getElementById("edit-lat");
942
1156
  const editAltInput = document.getElementById("edit-alt");
943
1157
  if (!applyBtn || !closeBtn || !editLonInput || !editLatInput || !editAltInput) {
944
- console.warn("[CesiumWidget] Editor panel input elements not found in DOM");
1158
+ warn(PREFIX3, "Editor panel input elements not found in DOM");
945
1159
  }
946
1160
  if (applyBtn) {
947
1161
  applyBtn.onclick = () => {
948
1162
  if (!editLonInput || !editLatInput || !editAltInput) {
949
- console.warn("[CesiumWidget] Editor input fields not available");
1163
+ warn(PREFIX3, "Editor input fields not available");
950
1164
  return;
951
1165
  }
952
1166
  const lon = parseFloat(editLonInput.value);
@@ -1123,9 +1337,10 @@ function initializeMeasurementTools(viewer, model, container) {
1123
1337
  }
1124
1338
  function updateMeasurementsList() {
1125
1339
  const results = model.get("measurement_results") || [];
1340
+ log(PREFIX3, "Updating measurements list, count:", results.length);
1126
1341
  const listContent = document.getElementById("measurements-list-content");
1127
1342
  if (!listContent) {
1128
- console.warn("[CesiumWidget] Measurements list content element not found in DOM");
1343
+ warn(PREFIX3, "Measurements list content element not found in DOM");
1129
1344
  return;
1130
1345
  }
1131
1346
  if (results.length === 0) {
@@ -1185,7 +1400,7 @@ function initializeMeasurementTools(viewer, model, container) {
1185
1400
  renameMeasurement(index, name);
1186
1401
  };
1187
1402
  } else {
1188
- console.warn(`[CesiumWidget] Rename button not found for measurement ${index}`);
1403
+ warn(PREFIX3, `Rename button not found for measurement ${index}`);
1189
1404
  }
1190
1405
  });
1191
1406
  }
@@ -1488,6 +1703,7 @@ function initializeMeasurementTools(viewer, model, container) {
1488
1703
  }
1489
1704
  }
1490
1705
  function enableMeasurementMode(mode) {
1706
+ log(PREFIX3, "Enabling measurement mode:", mode);
1491
1707
  if (measurementHandler) {
1492
1708
  measurementHandler.destroy();
1493
1709
  measurementHandler = null;
@@ -1676,31 +1892,50 @@ function initializeMeasurementTools(viewer, model, container) {
1676
1892
  });
1677
1893
  }
1678
1894
  model.on("change:measurement_mode", () => {
1895
+ if (isDestroyed) {
1896
+ log(PREFIX3, "Skipping measurement_mode change - destroyed");
1897
+ return;
1898
+ }
1679
1899
  const mode = model.get("measurement_mode");
1900
+ log(PREFIX3, "Measurement mode changed:", mode);
1680
1901
  enableMeasurementMode(mode);
1681
1902
  });
1682
1903
  model.on("change:measurement_results", () => {
1904
+ if (isDestroyed) {
1905
+ log(PREFIX3, "Skipping measurement_results change - destroyed");
1906
+ return;
1907
+ }
1683
1908
  const results = model.get("measurement_results") || [];
1909
+ log(PREFIX3, "Measurement results changed, count:", results.length);
1684
1910
  if (results.length === 0) {
1685
1911
  clearAllMeasurements();
1686
1912
  }
1687
1913
  updateMeasurementsList();
1688
1914
  });
1689
1915
  model.on("change:load_measurements_trigger", () => {
1916
+ if (isDestroyed)
1917
+ return;
1690
1918
  const triggerData = model.get("load_measurements_trigger");
1919
+ log(PREFIX3, "Load measurements trigger:", triggerData);
1691
1920
  if (triggerData && triggerData.measurements) {
1692
1921
  loadAndDisplayMeasurements(triggerData.measurements);
1693
1922
  updateMeasurementsList();
1694
1923
  }
1695
1924
  });
1696
1925
  model.on("change:focus_measurement_trigger", () => {
1926
+ if (isDestroyed)
1927
+ return;
1697
1928
  const triggerData = model.get("focus_measurement_trigger");
1929
+ log(PREFIX3, "Focus measurement trigger:", triggerData);
1698
1930
  if (triggerData && typeof triggerData.index === "number") {
1699
1931
  focusOnMeasurement(triggerData.index);
1700
1932
  }
1701
1933
  });
1702
1934
  model.on("change:show_measurement_tools", () => {
1935
+ if (isDestroyed)
1936
+ return;
1703
1937
  const show = model.get("show_measurement_tools");
1938
+ log(PREFIX3, "Show measurement tools:", show);
1704
1939
  toolbarDiv.style.display = show ? "flex" : "none";
1705
1940
  editorPanel.style.display = show ? editorPanel.style.display : "none";
1706
1941
  if (!show && editState.enabled) {
@@ -1709,7 +1944,10 @@ function initializeMeasurementTools(viewer, model, container) {
1709
1944
  }
1710
1945
  });
1711
1946
  model.on("change:show_measurements_list", () => {
1947
+ if (isDestroyed)
1948
+ return;
1712
1949
  const show = model.get("show_measurements_list");
1950
+ log(PREFIX3, "Show measurements list:", show);
1713
1951
  measurementsListPanel.style.display = show ? "block" : "none";
1714
1952
  });
1715
1953
  toolbarDiv.style.display = model.get("show_measurement_tools") ? "flex" : "none";
@@ -1719,6 +1957,8 @@ function initializeMeasurementTools(viewer, model, container) {
1719
1957
  enableMeasurementMode,
1720
1958
  clearAllMeasurements,
1721
1959
  destroy: () => {
1960
+ log(PREFIX3, "Destroying measurement tools");
1961
+ isDestroyed = true;
1722
1962
  if (measurementHandler) {
1723
1963
  measurementHandler.destroy();
1724
1964
  }
@@ -1726,6 +1966,7 @@ function initializeMeasurementTools(viewer, model, container) {
1726
1966
  if (toolbarDiv.parentNode) {
1727
1967
  toolbarDiv.remove();
1728
1968
  }
1969
+ log(PREFIX3, "Measurement tools destroyed");
1729
1970
  }
1730
1971
  };
1731
1972
  }
@@ -1733,15 +1974,26 @@ function initializeMeasurementTools(viewer, model, container) {
1733
1974
  // src/cesiumjs_anywidget/js/index.js
1734
1975
  window.CESIUM_BASE_URL = "https://cesium.com/downloads/cesiumjs/releases/1.135/Build/Cesium/";
1735
1976
  async function render({ model, el }) {
1977
+ setDebugMode(model.get("debug_mode") || false);
1978
+ model.on("change:debug_mode", () => {
1979
+ setDebugMode(model.get("debug_mode"));
1980
+ });
1981
+ log("Main", "Starting render");
1982
+ log("Main", "Loading CesiumJS...");
1736
1983
  const Cesium = await loadCesiumJS();
1984
+ log("Main", "CesiumJS loaded successfully");
1737
1985
  const container = document.createElement("div");
1738
1986
  container.style.width = "100%";
1739
1987
  container.style.height = model.get("height");
1740
1988
  container.style.position = "relative";
1741
1989
  el.appendChild(container);
1990
+ log("Main", "Container created with height:", model.get("height"));
1742
1991
  const ionToken = model.get("ion_access_token");
1743
1992
  if (ionToken) {
1744
1993
  Cesium.Ion.defaultAccessToken = ionToken;
1994
+ log("Main", "Ion access token set");
1995
+ } else {
1996
+ warn("Main", "No Ion access token provided");
1745
1997
  }
1746
1998
  const loadingDiv = createLoadingIndicator(container, !!ionToken);
1747
1999
  let viewer = null;
@@ -1751,37 +2003,57 @@ async function render({ model, el }) {
1751
2003
  let czmlLoader = null;
1752
2004
  (async () => {
1753
2005
  try {
2006
+ log("Main", "Creating Cesium Viewer...");
1754
2007
  viewer = createViewer(container, model, Cesium);
2008
+ log("Main", "Cesium Viewer created successfully");
1755
2009
  if (loadingDiv.parentNode) {
1756
2010
  loadingDiv.remove();
1757
2011
  }
2012
+ log("Main", "Initializing camera synchronization...");
1758
2013
  cameraSync = initializeCameraSync(viewer, model);
2014
+ log("Main", "Camera synchronization initialized");
2015
+ log("Main", "Initializing measurement tools...");
1759
2016
  measurementTools = initializeMeasurementTools(viewer, model, container);
2017
+ log("Main", "Measurement tools initialized");
2018
+ log("Main", "Setting up viewer listeners...");
1760
2019
  setupViewerListeners(viewer, model, container, Cesium);
2020
+ log("Main", "Viewer listeners set up");
2021
+ log("Main", "Setting up GeoJSON loader...");
1761
2022
  geoJsonLoader = setupGeoJSONLoader(viewer, model, Cesium);
2023
+ log("Main", "GeoJSON loader set up");
2024
+ log("Main", "Setting up CZML loader...");
1762
2025
  czmlLoader = setupCZMLLoader(viewer, model, Cesium);
1763
- } catch (error) {
1764
- console.error("Error initializing CesiumJS viewer:", error);
1765
- loadingDiv.textContent = `Error: ${error.message}`;
2026
+ log("Main", "CZML loader set up");
2027
+ log("Main", "Initialization complete");
2028
+ } catch (err) {
2029
+ error("Main", "Error initializing CesiumJS viewer:", err);
2030
+ loadingDiv.textContent = `Error: ${err.message}`;
1766
2031
  loadingDiv.style.background = "rgba(255,0,0,0.8)";
1767
2032
  }
1768
2033
  })();
1769
2034
  return () => {
2035
+ log("Main", "Starting cleanup...");
1770
2036
  if (cameraSync) {
2037
+ log("Main", "Destroying camera sync...");
1771
2038
  cameraSync.destroy();
1772
2039
  }
1773
2040
  if (measurementTools) {
2041
+ log("Main", "Destroying measurement tools...");
1774
2042
  measurementTools.destroy();
1775
2043
  }
1776
2044
  if (geoJsonLoader) {
2045
+ log("Main", "Destroying GeoJSON loader...");
1777
2046
  geoJsonLoader.destroy();
1778
2047
  }
1779
2048
  if (czmlLoader) {
2049
+ log("Main", "Destroying CZML loader...");
1780
2050
  czmlLoader.destroy();
1781
2051
  }
1782
2052
  if (viewer) {
2053
+ log("Main", "Destroying viewer...");
1783
2054
  viewer.destroy();
1784
2055
  }
2056
+ log("Main", "Cleanup complete");
1785
2057
  };
1786
2058
  }
1787
2059
  var js_default = { render };
@@ -130,6 +130,17 @@ class CesiumWidget(anywidget.AnyWidget):
130
130
  default_value=True, help="Show or hide measurements list panel"
131
131
  ).tag(sync=True)
132
132
 
133
+ # Debug mode for JavaScript logging
134
+ debug_mode = traitlets.Bool(
135
+ default_value=False, help="Enable or disable JavaScript console logging"
136
+ ).tag(sync=True)
137
+
138
+ # Camera synchronization callbacks
139
+ camera_sync_enabled = traitlets.Bool(
140
+ default_value=False,
141
+ help="Enable or disable camera position synchronization callbacks"
142
+ ).tag(sync=True)
143
+
133
144
  def __init__(self, **kwargs):
134
145
  """Initialize the CesiumWidget.
135
146
 
@@ -774,6 +785,59 @@ class CesiumWidget(anywidget.AnyWidget):
774
785
  """Hide the measurements list panel."""
775
786
  self.show_measurements_list = False
776
787
 
788
+ def enable_debug(self):
789
+ """Enable JavaScript console logging for debugging.
790
+
791
+ When enabled, detailed logs will be printed to the browser console
792
+ showing widget initialization, data loading, camera events, etc.
793
+
794
+ Examples
795
+ --------
796
+ >>> widget.enable_debug() # Enable logging
797
+ >>> # ... interact with widget, check browser console for logs
798
+ >>> widget.disable_debug() # Disable logging when done
799
+ """
800
+ self.debug_mode = True
801
+
802
+ def disable_debug(self):
803
+ """Disable JavaScript console logging.
804
+
805
+ Examples
806
+ --------
807
+ >>> widget.disable_debug()
808
+ """
809
+ self.debug_mode = False
810
+
811
+ def enable_camera_sync(self):
812
+ """Enable camera synchronization callbacks.
813
+
814
+ When enabled, camera position changes in the Cesium viewer will be
815
+ synchronized back to the Python model (latitude, longitude, altitude,
816
+ heading, pitch, roll properties).
817
+
818
+ Note: This is disabled by default to avoid unnecessary updates when
819
+ you don't need to track camera position in Python.
820
+
821
+ Examples
822
+ --------
823
+ >>> widget.enable_camera_sync()
824
+ >>> # Move camera in the viewer...
825
+ >>> print(widget.latitude, widget.longitude) # Updated values
826
+ """
827
+ self.camera_sync_enabled = True
828
+
829
+ def disable_camera_sync(self):
830
+ """Disable camera synchronization callbacks.
831
+
832
+ When disabled, camera movements in the viewer will not update the
833
+ Python model properties. This is the default state.
834
+
835
+ Examples
836
+ --------
837
+ >>> widget.disable_camera_sync()
838
+ """
839
+ self.camera_sync_enabled = False
840
+
777
841
  def set_atmosphere(self,
778
842
  brightness_shift=None,
779
843
  hue_shift=None,
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cesiumjs-anywidget
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: A Jupyter widget for CesiumJS 3D globe visualization using anywidget
5
5
  Project-URL: Homepage, https://github.com/Alex-PLACET/cesiumjs_anywidget
6
6
  Project-URL: Repository, https://github.com/Alex-PLACET/cesiumjs_anywidget
7
- Author: Alex PLACET
7
+ Author: Alexis Placet
8
8
  License: Apache License
9
9
  Version 2.0, January 2004
10
10
  http://www.apache.org/licenses/
@@ -0,0 +1,8 @@
1
+ cesiumjs_anywidget/__init__.py,sha256=9WVcAtreHgk6C5clPG6sZy4m7s5AIbGU1DJ4oDpx3Is,165
2
+ cesiumjs_anywidget/styles.css,sha256=kt2i9fJuM6gaR7WkoQ2VGGoHzhqy6RpJVK2xwMKPV70,689
3
+ cesiumjs_anywidget/widget.py,sha256=Yh_pluGI23vu-RvB1oHiZtq7ySAxPlYzIHViDFvS85k,41258
4
+ cesiumjs_anywidget/index.js,sha256=22bGV7SzM8b69-kFEWoXhh94TQaUAM9cJmEV5Ykmmbc,78269
5
+ cesiumjs_anywidget-0.6.0.dist-info/METADATA,sha256=D2p5Jx2HOWr-B9krknzNtGWhV53BKMjojhTCYti6SMg,24780
6
+ cesiumjs_anywidget-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
7
+ cesiumjs_anywidget-0.6.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
8
+ cesiumjs_anywidget-0.6.0.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- cesiumjs_anywidget/__init__.py,sha256=9WVcAtreHgk6C5clPG6sZy4m7s5AIbGU1DJ4oDpx3Is,165
2
- cesiumjs_anywidget/styles.css,sha256=kt2i9fJuM6gaR7WkoQ2VGGoHzhqy6RpJVK2xwMKPV70,689
3
- cesiumjs_anywidget/widget.py,sha256=_7uXX7B3KsOg_3QrMDfV0pZ8BGA-6coFnZ7_gTI3nck,39104
4
- cesiumjs_anywidget/index.js,sha256=SrJ9F9xHdfvbz5CkP_ydspiDaPU6vp74vxtUv-RVimE,68405
5
- cesiumjs_anywidget-0.4.0.dist-info/METADATA,sha256=NwADYoiPPp4RLblmHCM5_mhak_cCB7ptC4iN0NXVZTY,24778
6
- cesiumjs_anywidget-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
7
- cesiumjs_anywidget-0.4.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
8
- cesiumjs_anywidget-0.4.0.dist-info/RECORD,,