anymap-ts 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anymap-ts",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "TypeScript frontend for anymap-ts interactive maps",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -10,7 +10,7 @@
10
10
  "build:leaflet": "esbuild src/leaflet/index.ts --bundle --format=esm --outfile=anymap_ts/static/leaflet.js --loader:.css=css --loader:.png=dataurl --minify",
11
11
  "build:deckgl": "esbuild src/deckgl/index.ts --bundle --format=esm --outfile=anymap_ts/static/deckgl.js --loader:.css=css --external:path --external:fs --minify",
12
12
  "build:openlayers": "esbuild src/openlayers/index.ts --bundle --format=esm --outfile=anymap_ts/static/openlayers.js --loader:.css=css --minify",
13
- "build:cesium": "esbuild src/cesium/index.ts --bundle --format=esm --outfile=anymap_ts/static/cesium.js --loader:.css=css --minify",
13
+ "build:cesium": "esbuild src/cesium/index.ts --bundle --format=esm --outfile=anymap_ts/static/cesium.js --loader:.css=css --alias:cesium=./node_modules/cesium/Build/Cesium/index.js --alias:cesium-widgets-css=./node_modules/cesium/Build/Cesium/Widgets/widgets.css --define:CESIUM_BASE_URL='\"https://cesium.com/downloads/cesiumjs/releases/1.138/Build/Cesium/\"' --minify",
14
14
  "build:keplergl": "esbuild src/keplergl/index.ts --bundle --format=esm --outfile=anymap_ts/static/keplergl.js --loader:.css=css --minify",
15
15
  "build:potree": "esbuild src/potree/index.ts --bundle --format=esm --outfile=anymap_ts/static/potree.js --loader:.css=css --minify",
16
16
  "build:custom-css": "esbuild src/styles/maplibre.css --bundle --outfile=anymap_ts/static/custom.css --loader:.css=css --minify",
@@ -52,6 +52,7 @@
52
52
  "geotiff-geokeys-to-proj4": "^2024.4.13",
53
53
  "jspdf": "^4.1.0",
54
54
  "leaflet": "^1.9.4",
55
+ "leaflet.heat": "^0.2.0",
55
56
  "mapbox-gl": "^3.18.1",
56
57
  "maplibre-gl": ">=5.14.0",
57
58
  "maplibre-gl-components": "^0.15.0",
@@ -1,25 +1,24 @@
1
1
  /**
2
2
  * Cesium 3D globe widget entry point.
3
3
  *
4
- * Cesium is loaded dynamically from CDN since it's too large to bundle
5
- * and the browser can't resolve bare module specifiers.
4
+ * Cesium is bundled from the npm package to avoid CORS issues
5
+ * in VS Code/Cursor notebook webviews.
6
6
  */
7
7
 
8
8
  import type { AnyModel } from '@anywidget/types';
9
-
10
- // Cesium CDN URLs
11
- const CESIUM_VERSION = '1.120';
12
- const CESIUM_BASE_URL = `https://cesium.com/downloads/cesiumjs/releases/${CESIUM_VERSION}/Build/Cesium`;
13
- const CESIUM_JS_URL = `${CESIUM_BASE_URL}/Cesium.js`;
14
- const CESIUM_CSS_URL = `${CESIUM_BASE_URL}/Widgets/widgets.css`;
15
-
16
- // Declare Cesium on window
17
- declare global {
18
- interface Window {
19
- Cesium: any;
20
- CESIUM_BASE_URL: string;
21
- }
22
- }
9
+ import {
10
+ Viewer,
11
+ Cartesian3,
12
+ Ion,
13
+ UrlTemplateImageryProvider,
14
+ Terrain,
15
+ GeoJsonDataSource,
16
+ Color,
17
+ Math as CesiumMath,
18
+ } from 'cesium';
19
+
20
+ // Import Cesium widget CSS from the pre-built bundle
21
+ import 'cesium-widgets-css';
23
22
 
24
23
  interface CesiumModel extends AnyModel {
25
24
  get(key: 'center'): [number, number];
@@ -30,50 +29,6 @@ interface CesiumModel extends AnyModel {
30
29
  get(key: '_js_calls'): Array<{ id: number; method: string; args: unknown[]; kwargs: Record<string, unknown> }>;
31
30
  }
32
31
 
33
- /**
34
- * Load Cesium CSS dynamically.
35
- */
36
- function loadCesiumCSS(): void {
37
- if (!document.querySelector(`link[href="${CESIUM_CSS_URL}"]`)) {
38
- const link = document.createElement('link');
39
- link.rel = 'stylesheet';
40
- link.href = CESIUM_CSS_URL;
41
- document.head.appendChild(link);
42
- }
43
- }
44
-
45
- /**
46
- * Load Cesium JS dynamically.
47
- */
48
- function loadCesiumJS(): Promise<void> {
49
- return new Promise((resolve, reject) => {
50
- // Check if already loaded
51
- if (window.Cesium) {
52
- resolve();
53
- return;
54
- }
55
-
56
- // Check if script is already loading
57
- const existingScript = document.querySelector(`script[src="${CESIUM_JS_URL}"]`);
58
- if (existingScript) {
59
- existingScript.addEventListener('load', () => resolve());
60
- existingScript.addEventListener('error', () => reject(new Error('Failed to load Cesium')));
61
- return;
62
- }
63
-
64
- // Set Cesium base URL for asset loading
65
- window.CESIUM_BASE_URL = CESIUM_BASE_URL;
66
-
67
- // Load script
68
- const script = document.createElement('script');
69
- script.src = CESIUM_JS_URL;
70
- script.async = true;
71
- script.onload = () => resolve();
72
- script.onerror = () => reject(new Error('Failed to load Cesium'));
73
- document.head.appendChild(script);
74
- });
75
- }
76
-
77
32
  /**
78
33
  * Convert zoom level to camera height.
79
34
  */
@@ -100,18 +55,13 @@ class CesiumWidget {
100
55
  }
101
56
 
102
57
  async initialize(): Promise<void> {
103
- const Cesium = window.Cesium;
104
- if (!Cesium) {
105
- throw new Error('Cesium not loaded');
106
- }
107
-
108
58
  const accessToken = this.model.get('access_token') || '';
109
59
  const center = this.model.get('center') || [0, 0];
110
60
  const zoom = this.model.get('zoom') || 2;
111
61
 
112
62
  // Set access token
113
63
  if (accessToken) {
114
- Cesium.Ion.defaultAccessToken = accessToken;
64
+ Ion.defaultAccessToken = accessToken;
115
65
  }
116
66
 
117
67
  // Set up parent element
@@ -129,8 +79,9 @@ class CesiumWidget {
129
79
  // Calculate camera height from zoom
130
80
  const height = zoomToHeight(zoom);
131
81
 
132
- // Create viewer
133
- this.viewer = new Cesium.Viewer(this.container, {
82
+ // Create viewer without default Ion imagery (which fails in VS Code webviews due to CORS).
83
+ // Use OpenStreetMap as the default base layer instead.
84
+ this.viewer = new Viewer(this.container, {
134
85
  baseLayerPicker: false,
135
86
  geocoder: false,
136
87
  homeButton: false,
@@ -142,11 +93,19 @@ class CesiumWidget {
142
93
  vrButton: false,
143
94
  selectionIndicator: false,
144
95
  infoBox: false,
96
+ baseLayer: false,
97
+ });
98
+
99
+ // Add OpenStreetMap as default basemap
100
+ const osmProvider = new UrlTemplateImageryProvider({
101
+ url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
102
+ maximumLevel: 19,
145
103
  });
104
+ this.viewer.imageryLayers.addImageryProvider(osmProvider);
146
105
 
147
106
  // Set initial camera position
148
107
  this.viewer.camera.setView({
149
- destination: Cesium.Cartesian3.fromDegrees(center[0], center[1], height),
108
+ destination: Cartesian3.fromDegrees(center[0], center[1], height),
150
109
  });
151
110
 
152
111
  // Process pending JS calls
@@ -182,37 +141,34 @@ class CesiumWidget {
182
141
  }
183
142
 
184
143
  private onCenterChange(): void {
185
- const Cesium = window.Cesium;
186
144
  const newCenter = this.model.get('center');
187
- if (this.viewer && Cesium) {
145
+ if (this.viewer) {
188
146
  const currentHeight = this.viewer.camera.positionCartographic.height;
189
147
  this.viewer.camera.flyTo({
190
- destination: Cesium.Cartesian3.fromDegrees(newCenter[0], newCenter[1], currentHeight),
148
+ destination: Cartesian3.fromDegrees(newCenter[0], newCenter[1], currentHeight),
191
149
  });
192
150
  }
193
151
  }
194
152
 
195
153
  // Method handlers
196
154
  handle_addBasemap(args: unknown[], kwargs: Record<string, unknown>): void {
197
- const Cesium = window.Cesium;
198
- if (!this.viewer || !Cesium) return;
155
+ if (!this.viewer) return;
199
156
 
200
157
  const url = args[0] as string;
201
158
  const name = kwargs.name as string || 'basemap';
202
159
 
203
- const imageryProvider = new Cesium.UrlTemplateImageryProvider({ url });
160
+ const imageryProvider = new UrlTemplateImageryProvider({ url });
204
161
  const layer = this.viewer.imageryLayers.addImageryProvider(imageryProvider);
205
162
  this.imageryLayers.set(name, layer);
206
163
  }
207
164
 
208
165
  handle_setTerrain(args: unknown[], kwargs: Record<string, unknown>): void {
209
- const Cesium = window.Cesium;
210
- if (!this.viewer || !Cesium) return;
166
+ if (!this.viewer) return;
211
167
 
212
168
  const url = kwargs.url as string;
213
169
  if (url === 'cesium-world-terrain' || !url) {
214
170
  this.viewer.scene.setTerrain(
215
- Cesium.Terrain.fromWorldTerrain({
171
+ Terrain.fromWorldTerrain({
216
172
  requestVertexNormals: true,
217
173
  requestWaterMask: true,
218
174
  })
@@ -221,8 +177,7 @@ class CesiumWidget {
221
177
  }
222
178
 
223
179
  handle_flyTo(args: unknown[], kwargs: Record<string, unknown>): void {
224
- const Cesium = window.Cesium;
225
- if (!this.viewer || !Cesium) return;
180
+ if (!this.viewer) return;
226
181
 
227
182
  const lng = args[0] as number;
228
183
  const lat = args[1] as number;
@@ -232,10 +187,10 @@ class CesiumWidget {
232
187
  const duration = kwargs.duration as number ?? 2;
233
188
 
234
189
  this.viewer.camera.flyTo({
235
- destination: Cesium.Cartesian3.fromDegrees(lng, lat, height),
190
+ destination: Cartesian3.fromDegrees(lng, lat, height),
236
191
  orientation: {
237
- heading: Cesium.Math.toRadians(heading),
238
- pitch: Cesium.Math.toRadians(pitch),
192
+ heading: CesiumMath.toRadians(heading),
193
+ pitch: CesiumMath.toRadians(pitch),
239
194
  roll: 0,
240
195
  },
241
196
  duration: duration,
@@ -248,8 +203,7 @@ class CesiumWidget {
248
203
  }
249
204
 
250
205
  async handle_addGeoJSON(args: unknown[], kwargs: Record<string, unknown>): Promise<void> {
251
- const Cesium = window.Cesium;
252
- if (!this.viewer || !Cesium) return;
206
+ if (!this.viewer) return;
253
207
 
254
208
  const data = kwargs.data as object;
255
209
  const name = kwargs.name as string || `geojson-${this.dataSources.size}`;
@@ -257,9 +211,9 @@ class CesiumWidget {
257
211
  const fill = kwargs.fill as string || 'rgba(51, 136, 255, 0.5)';
258
212
 
259
213
  try {
260
- const dataSource = await Cesium.GeoJsonDataSource.load(data, {
261
- stroke: Cesium.Color.fromCssColorString(stroke),
262
- fill: Cesium.Color.fromCssColorString(fill),
214
+ const dataSource = await GeoJsonDataSource.load(data, {
215
+ stroke: Color.fromCssColorString(stroke),
216
+ fill: Color.fromCssColorString(fill),
263
217
  clampToGround: true,
264
218
  });
265
219
 
@@ -294,17 +248,6 @@ let widget: CesiumWidget | null = null;
294
248
  * anywidget render function.
295
249
  */
296
250
  async function render({ model, el }: { model: CesiumModel; el: HTMLElement }): Promise<() => void> {
297
- // Load Cesium CSS and JS
298
- loadCesiumCSS();
299
-
300
- try {
301
- await loadCesiumJS();
302
- } catch (error) {
303
- console.error('Failed to load Cesium:', error);
304
- el.innerHTML = '<div style="padding: 20px; color: red;">Failed to load Cesium library</div>';
305
- return () => {};
306
- }
307
-
308
251
  // Create widget
309
252
  widget = new CesiumWidget(model as CesiumModel, el);
310
253
 
@@ -313,6 +256,7 @@ async function render({ model, el }: { model: CesiumModel; el: HTMLElement }): P
313
256
  await widget.initialize();
314
257
  } catch (error) {
315
258
  console.error('Failed to initialize Cesium viewer:', error);
259
+ el.innerHTML = '<div style="padding: 20px; color: red;">Failed to initialize Cesium viewer</div>';
316
260
  }
317
261
 
318
262
  // Return cleanup function