@jupytergis/base 0.2.1 → 0.4.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 (95) hide show
  1. package/lib/annotations/components/Annotation.js +2 -2
  2. package/lib/annotations/model.d.ts +6 -7
  3. package/lib/annotations/model.js +15 -15
  4. package/lib/commands.d.ts +2 -3
  5. package/lib/commands.js +146 -62
  6. package/lib/constants.d.ts +3 -0
  7. package/lib/constants.js +5 -1
  8. package/lib/dialogs/formdialog.d.ts +5 -0
  9. package/lib/dialogs/formdialog.js +2 -2
  10. package/lib/dialogs/layerBrowserDialog.d.ts +4 -5
  11. package/lib/dialogs/layerBrowserDialog.js +9 -9
  12. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +2 -1
  13. package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +26 -0
  14. package/lib/dialogs/symbology/hooks/useGetBandInfo.js +64 -0
  15. package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +1 -1
  16. package/lib/dialogs/symbology/hooks/useGetProperties.js +12 -9
  17. package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -3
  18. package/lib/dialogs/symbology/symbologyDialog.js +10 -9
  19. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
  20. package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +16 -3
  21. package/lib/dialogs/symbology/tiff_layer/components/BandRow.d.ts +16 -3
  22. package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +21 -7
  23. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +4 -0
  24. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +85 -0
  25. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +1 -20
  26. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +25 -65
  27. package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +1 -1
  28. package/lib/dialogs/symbology/vector_layer/VectorRendering.js +18 -13
  29. package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +1 -1
  30. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +30 -19
  31. package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +1 -1
  32. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -13
  33. package/lib/dialogs/symbology/vector_layer/types/Heatmap.d.ts +4 -0
  34. package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +77 -0
  35. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +1 -1
  36. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +4 -3
  37. package/lib/formbuilder/creationform.d.ts +6 -2
  38. package/lib/formbuilder/creationform.js +6 -6
  39. package/lib/formbuilder/editform.d.ts +2 -2
  40. package/lib/formbuilder/editform.js +14 -9
  41. package/lib/formbuilder/formselectors.js +11 -1
  42. package/lib/formbuilder/objectform/baseform.d.ts +12 -3
  43. package/lib/formbuilder/objectform/baseform.js +39 -0
  44. package/lib/formbuilder/objectform/fileselectorwidget.d.ts +2 -0
  45. package/lib/formbuilder/objectform/fileselectorwidget.js +88 -0
  46. package/lib/formbuilder/objectform/geojsonsource.d.ts +5 -7
  47. package/lib/formbuilder/objectform/geojsonsource.js +8 -24
  48. package/lib/formbuilder/objectform/geotiffsource.d.ts +5 -1
  49. package/lib/formbuilder/objectform/geotiffsource.js +64 -18
  50. package/lib/formbuilder/objectform/heatmapLayerForm.d.ts +11 -0
  51. package/lib/formbuilder/objectform/heatmapLayerForm.js +60 -0
  52. package/lib/formbuilder/objectform/layerform.d.ts +2 -0
  53. package/lib/formbuilder/objectform/layerform.js +6 -0
  54. package/lib/formbuilder/objectform/pathbasedsource.d.ts +19 -0
  55. package/lib/formbuilder/objectform/pathbasedsource.js +98 -0
  56. package/lib/formbuilder/objectform/vectorlayerform.d.ts +0 -2
  57. package/lib/formbuilder/objectform/vectorlayerform.js +0 -59
  58. package/lib/icons.d.ts +1 -0
  59. package/lib/icons.js +5 -0
  60. package/lib/keybindings.json +62 -0
  61. package/lib/mainview/TemporalSlider.d.ts +8 -0
  62. package/lib/mainview/TemporalSlider.js +303 -0
  63. package/lib/mainview/mainView.d.ts +46 -8
  64. package/lib/mainview/mainView.js +431 -144
  65. package/lib/mainview/mainviewmodel.d.ts +4 -0
  66. package/lib/mainview/mainviewmodel.js +4 -0
  67. package/lib/mainview/mainviewwidget.d.ts +0 -2
  68. package/lib/mainview/mainviewwidget.js +0 -2
  69. package/lib/panelview/annotationPanel.js +5 -5
  70. package/lib/panelview/components/filter-panel/Filter.js +8 -24
  71. package/lib/panelview/components/identify-panel/IdentifyPanel.js +1 -1
  72. package/lib/panelview/components/layers.js +2 -2
  73. package/lib/panelview/components/sources.js +1 -1
  74. package/lib/panelview/leftpanel.d.ts +3 -0
  75. package/lib/panelview/leftpanel.js +5 -1
  76. package/lib/panelview/model.js +8 -8
  77. package/lib/panelview/objectproperties.js +10 -10
  78. package/lib/panelview/rightpanel.d.ts +1 -1
  79. package/lib/panelview/rightpanel.js +10 -10
  80. package/lib/statusbar/StatusBar.d.ts +13 -0
  81. package/lib/statusbar/StatusBar.js +52 -0
  82. package/lib/toolbar/widget.d.ts +1 -1
  83. package/lib/toolbar/widget.js +44 -37
  84. package/lib/tools.d.ts +50 -7
  85. package/lib/tools.js +394 -12
  86. package/lib/types.d.ts +2 -0
  87. package/lib/widget.d.ts +29 -5
  88. package/lib/widget.js +41 -7
  89. package/package.json +17 -5
  90. package/style/base.css +11 -0
  91. package/style/icons/logo_mini_qgz.svg +31 -0
  92. package/style/leftPanel.css +8 -0
  93. package/style/statusBar.css +16 -0
  94. package/style/symbologyDialog.css +7 -1
  95. package/style/temporalSlider.css +47 -0
package/lib/tools.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { VectorTile } from '@mapbox/vector-tile';
2
- import { IDict, IJGISLayerBrowserRegistry, IJGISOptions } from '@jupytergis/schema';
2
+ import { Contents } from '@jupyterlab/services';
3
+ import { IDict, IJGISLayerBrowserRegistry, IJGISOptions, IJGISSource, IJupyterGISModel } from '@jupytergis/schema';
3
4
  export declare const debounce: (func: CallableFunction, timeout?: number) => CallableFunction;
4
5
  export declare function throttle<T extends (...args: any[]) => void>(callback: T, delay?: number): T;
5
6
  export declare function getElementFromProperty(filePath?: string | null, prop?: string | null): HTMLElement | undefined | null;
@@ -22,7 +23,6 @@ export declare function deepCopy<T = IDict<any>>(value: T): T;
22
23
  */
23
24
  export declare function createDefaultLayerRegistry(layerBrowserRegistry: IJGISLayerBrowserRegistry): void;
24
25
  export declare function getLayerTileInfo(tileUrl: string, mapOptions: Pick<IJGISOptions, 'latitude' | 'longitude' | 'extent' | 'zoom'>, urlParameters?: IDict<string>): Promise<VectorTile>;
25
- export declare function getSourceLayerNames(tileUrl: string, urlParameters?: IDict<string>): Promise<string[]>;
26
26
  export interface IParsedStyle {
27
27
  fillColor: string;
28
28
  strokeColor: string;
@@ -46,24 +46,67 @@ export declare const openDatabase: () => Promise<IDBDatabase>;
46
46
  * @param metadata metadata of file.
47
47
  * @returns A promise that resolves once the data is successfully saved.
48
48
  */
49
- export declare const saveToIndexedDB: (key: string, file: Blob, metadata: any) => Promise<void>;
49
+ export declare const saveToIndexedDB: (key: string, file: any, metadata?: any | undefined) => Promise<void>;
50
50
  /**
51
51
  * Retrieve a file and its metadata from the IndexedDB database.
52
52
  *
53
53
  * @param key fileID (sourceUrl).
54
54
  * @returns A promise that resolves to the stored data object or undefined.
55
55
  */
56
- export declare const getFromIndexedDB: (key: string) => Promise<any>;
56
+ export declare const getFromIndexedDB: (key: string) => Promise<{
57
+ file: any;
58
+ metadata?: any | undefined;
59
+ } | undefined>;
57
60
  /**
58
61
  * Load a GeoTIFF file from IndexedDB database cache or fetch it .
59
62
  *
60
63
  * @param sourceInfo object containing the URL of the GeoTIFF file.
61
64
  * @returns A promise that resolves to the file as a Blob, or undefined .
62
65
  */
63
- export declare const loadGeoTIFFWithCache: (sourceInfo: {
66
+ export declare const loadGeoTiff: (sourceInfo: {
64
67
  url?: string | undefined;
65
- }) => Promise<{
66
- file: Blob;
68
+ }, file?: Contents.IModel | null) => Promise<{
69
+ file: any;
67
70
  metadata: any;
68
71
  sourceUrl: string;
69
72
  } | null>;
73
+ /**
74
+ * Generalized file reader for different source types.
75
+ *
76
+ * @param fileInfo - Object containing the file path and source type.
77
+ * @returns A promise that resolves to the file content.
78
+ */
79
+ export declare const loadFile: (fileInfo: {
80
+ filepath: string;
81
+ type: IJGISSource["type"];
82
+ model: IJupyterGISModel;
83
+ }) => Promise<any>;
84
+ /**
85
+ * Converts a base64-encoded string to a Blob.
86
+ *
87
+ * @param base64 - The base64-encoded string representing the file data.
88
+ * @param mimeType - The MIME type of the data.
89
+ * @returns A promise that resolves to a Blob representing the decoded data.
90
+ */
91
+ export declare const base64ToBlob: (base64: string, mimeType: string) => Promise<Blob>;
92
+ /**
93
+ * A mapping of file extensions to their corresponding MIME types.
94
+ */
95
+ export declare const MIME_TYPES: {
96
+ [ext: string]: string;
97
+ };
98
+ /**
99
+ * Determine the MIME type based on the file extension.
100
+ *
101
+ * @param filename - The name of the file.
102
+ * @returns A string representing the MIME type.
103
+ */
104
+ export declare const getMimeType: (filename: string) => string;
105
+ /**
106
+ * Helper to convert a string (base64) to ArrayBuffer.
107
+ *
108
+ * @param content - File content as a base64 string.
109
+ * @returns An ArrayBuffer.
110
+ */
111
+ export declare const stringToArrayBuffer: (content: string) => Promise<ArrayBuffer>;
112
+ export declare const getNumericFeatureAttributes: (featureProperties: Record<string, Set<any>>) => Record<string, Set<number>>;
package/lib/tools.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import Protobuf from 'pbf';
2
2
  import { VectorTile } from '@mapbox/vector-tile';
3
- import { URLExt } from '@jupyterlab/coreutils';
3
+ import { PathExt, URLExt } from '@jupyterlab/coreutils';
4
4
  import { ServerConnection } from '@jupyterlab/services';
5
+ import { showErrorMessage } from '@jupyterlab/apputils';
5
6
  import * as d3Color from 'd3-color';
7
+ import shp from 'shpjs';
6
8
  import RASTER_LAYER_GALLERY from '../rasterlayer_gallery/raster_layer_gallery.json';
7
9
  import { getGdal } from './gdal';
8
10
  export const debounce = (func, timeout = 100) => {
@@ -213,11 +215,6 @@ export async function getLayerTileInfo(tileUrl, mapOptions, urlParameters) {
213
215
  const tile = new VectorTile(new Protobuf(arrayBuffer));
214
216
  return tile;
215
217
  }
216
- export async function getSourceLayerNames(tileUrl, urlParameters) {
217
- const tile = await getLayerTileInfo(tileUrl, { latitude: 0, longitude: 0, zoom: 0, extent: [] }, urlParameters);
218
- const layerNames = Object.keys(tile.layers);
219
- return layerNames;
220
- }
221
218
  export function parseColor(type, style) {
222
219
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
223
220
  if (!type || !style) {
@@ -310,23 +307,36 @@ export const getFromIndexedDB = async (key) => {
310
307
  * @param sourceInfo object containing the URL of the GeoTIFF file.
311
308
  * @returns A promise that resolves to the file as a Blob, or undefined .
312
309
  */
313
- export const loadGeoTIFFWithCache = async (sourceInfo) => {
310
+ export const loadGeoTiff = async (sourceInfo, file) => {
314
311
  if (!(sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url)) {
315
312
  return null;
316
313
  }
314
+ const mimeType = getMimeType(sourceInfo.url);
315
+ if (!mimeType || !mimeType.startsWith('image/tiff')) {
316
+ throw new Error('Invalid file type. Expected GeoTIFF (image/tiff).');
317
+ }
317
318
  const cachedData = await getFromIndexedDB(sourceInfo.url);
318
319
  if (cachedData) {
319
320
  return {
320
- file: new Blob([cachedData.file]),
321
+ file: cachedData.file,
321
322
  metadata: cachedData.metadata,
322
323
  sourceUrl: sourceInfo.url
323
324
  };
324
325
  }
325
- const response = await fetch(sourceInfo.url);
326
- const fileBlob = await response.blob();
327
- const file = new File([fileBlob], 'loaded.tif');
326
+ let fileBlob;
327
+ if (!file) {
328
+ const response = await fetch(`/jupytergis_core/proxy?url=${sourceInfo.url}`);
329
+ if (!response.ok) {
330
+ throw new Error(`Failed to fetch file. Status: ${response.status}`);
331
+ }
332
+ fileBlob = await response.blob();
333
+ }
334
+ else {
335
+ fileBlob = await base64ToBlob(file.content, mimeType);
336
+ }
337
+ const geotiff = new File([fileBlob], 'loaded.tif');
328
338
  const Gdal = await getGdal();
329
- const result = await Gdal.open(file);
339
+ const result = await Gdal.open(geotiff);
330
340
  const tifDataset = result.datasets[0];
331
341
  const metadata = await Gdal.gdalinfo(tifDataset, ['-stats']);
332
342
  Gdal.close(tifDataset);
@@ -337,3 +347,375 @@ export const loadGeoTIFFWithCache = async (sourceInfo) => {
337
347
  sourceUrl: sourceInfo.url
338
348
  };
339
349
  };
350
+ /**
351
+ * Generalized file reader for different source types.
352
+ *
353
+ * @param fileInfo - Object containing the file path and source type.
354
+ * @returns A promise that resolves to the file content.
355
+ */
356
+ export const loadFile = async (fileInfo) => {
357
+ const { filepath, type, model } = fileInfo;
358
+ if (filepath.startsWith('http://') || filepath.startsWith('https://')) {
359
+ switch (type) {
360
+ case 'ImageSource': {
361
+ try {
362
+ const response = await fetch(filepath);
363
+ if (!response.ok) {
364
+ throw new Error(`Failed to fetch image from URL: ${filepath}`);
365
+ }
366
+ const contentType = response.headers.get('Content-Type');
367
+ if (!contentType || !contentType.startsWith('image/')) {
368
+ throw new Error(`Invalid image URL. Content-Type: ${contentType}`);
369
+ }
370
+ // load the image to verify it's not corrupted
371
+ await validateImage(await response.blob());
372
+ return filepath;
373
+ }
374
+ catch (error) {
375
+ console.error('Error validating remote image:', error);
376
+ throw error;
377
+ }
378
+ }
379
+ case 'ShapefileSource': {
380
+ const cached = await getFromIndexedDB(filepath);
381
+ if (cached) {
382
+ return cached.file;
383
+ }
384
+ // First trying a direct fetch
385
+ try {
386
+ const response = await fetch(filepath);
387
+ const arrayBuffer = await response.arrayBuffer();
388
+ const geojson = await shp(arrayBuffer);
389
+ await saveToIndexedDB(filepath, geojson);
390
+ return geojson;
391
+ }
392
+ catch (error) {
393
+ console.warn('Cannot load shapefile from ${filepath}: ${error}');
394
+ }
395
+ // Trying through our proxy server
396
+ try {
397
+ const response = await fetch(`/jupytergis_core/proxy?url=${filepath}`);
398
+ const arrayBuffer = await response.arrayBuffer();
399
+ const geojson = await shp(arrayBuffer);
400
+ await saveToIndexedDB(filepath, geojson);
401
+ return geojson;
402
+ }
403
+ catch (error) {
404
+ console.warn('Cannot communicate with the JupyterGIS proxy server:', error);
405
+ }
406
+ // Trying through an external proxy server
407
+ try {
408
+ const response = await fetch(`https://corsproxy.io/?url=${filepath}`);
409
+ const arrayBuffer = await response.arrayBuffer();
410
+ const geojson = await shp(arrayBuffer);
411
+ await saveToIndexedDB(filepath, geojson);
412
+ return geojson;
413
+ }
414
+ catch (error) {
415
+ console.warn('Cannot communicate with external proxy server', error);
416
+ }
417
+ showErrorMessage('Network error', 'Failed to fetch ${filepath}');
418
+ throw new Error('Failed to fetch ${filepath}');
419
+ }
420
+ case 'GeoJSONSource': {
421
+ const cached = await getFromIndexedDB(filepath);
422
+ if (cached) {
423
+ return cached.file;
424
+ }
425
+ try {
426
+ const response = await fetch(`/jupytergis_core/proxy?url=${filepath}`);
427
+ if (!response.ok) {
428
+ throw new Error(`Failed to fetch GeoJSON from URL: ${filepath}`);
429
+ }
430
+ const geojson = await response.json();
431
+ await saveToIndexedDB(filepath, geojson);
432
+ return geojson;
433
+ }
434
+ catch (error) {
435
+ console.error('Error loading remote GeoJSON:', error);
436
+ throw error;
437
+ }
438
+ }
439
+ case 'GeoTiffSource': {
440
+ try {
441
+ const tiff = loadGeoTiff({ url: filepath });
442
+ return tiff;
443
+ }
444
+ catch (error) {
445
+ console.error('Error loading remote GeoTIFF:', error);
446
+ throw error;
447
+ }
448
+ }
449
+ default: {
450
+ throw new Error(`Unsupported URL handling for source type: ${type}`);
451
+ }
452
+ }
453
+ }
454
+ if (!model.contentsManager || !model.filePath) {
455
+ throw new Error('ContentsManager or filePath is not initialized.');
456
+ }
457
+ const absolutePath = PathExt.resolve(PathExt.dirname(model.filePath), filepath);
458
+ try {
459
+ const file = await model.contentsManager.get(absolutePath, {
460
+ content: true
461
+ });
462
+ if (!file.content) {
463
+ throw new Error(`File at ${absolutePath} is empty or inaccessible.`);
464
+ }
465
+ switch (type) {
466
+ case 'GeoJSONSource': {
467
+ return typeof file.content === 'string'
468
+ ? JSON.parse(file.content)
469
+ : file.content;
470
+ }
471
+ case 'ShapefileSource': {
472
+ const arrayBuffer = await stringToArrayBuffer(file.content);
473
+ const geojson = await shp(arrayBuffer);
474
+ return geojson;
475
+ }
476
+ case 'ImageSource': {
477
+ if (typeof file.content === 'string') {
478
+ const mimeType = getMimeType(filepath);
479
+ if (!mimeType.startsWith('image/')) {
480
+ throw new Error(`Invalid image file. MIME type: ${mimeType}`);
481
+ }
482
+ // Attempt to decode the base64 data
483
+ try {
484
+ await validateImage(await base64ToBlob(file.content, mimeType));
485
+ return `data:${mimeType};base64,${file.content}`;
486
+ }
487
+ catch (error) {
488
+ console.error('Error image content failed to decode.:', error);
489
+ throw error;
490
+ }
491
+ }
492
+ else {
493
+ throw new Error('Invalid file format for image content.');
494
+ }
495
+ }
496
+ case 'GeoTiffSource': {
497
+ if (typeof file.content === 'string') {
498
+ const tiff = loadGeoTiff({ url: filepath }, file);
499
+ return tiff;
500
+ }
501
+ else {
502
+ throw new Error('Invalid file format for tiff content.');
503
+ }
504
+ }
505
+ default: {
506
+ throw new Error(`Unsupported source type: ${type}`);
507
+ }
508
+ }
509
+ }
510
+ catch (error) {
511
+ console.error(`Error reading file '${filepath}':`, error);
512
+ throw error;
513
+ }
514
+ };
515
+ /**
516
+ * Validates whether a given Blob represents a valid image.
517
+ *
518
+ * @param blob - The Blob to validate.
519
+ * @returns A promise that resolves if the Blob is a valid image, or rejects with an error otherwise.
520
+ */
521
+ const validateImage = async (blob) => {
522
+ return new Promise((resolve, reject) => {
523
+ const img = new Image();
524
+ img.onload = () => resolve(); // Valid image
525
+ img.onerror = () => reject(new Error('Invalid image content.'));
526
+ img.src = URL.createObjectURL(blob);
527
+ });
528
+ };
529
+ /**
530
+ * Converts a base64-encoded string to a Blob.
531
+ *
532
+ * @param base64 - The base64-encoded string representing the file data.
533
+ * @param mimeType - The MIME type of the data.
534
+ * @returns A promise that resolves to a Blob representing the decoded data.
535
+ */
536
+ export const base64ToBlob = async (base64, mimeType) => {
537
+ const response = await fetch(`data:${mimeType};base64,${base64}`);
538
+ return await response.blob();
539
+ };
540
+ /**
541
+ * A mapping of file extensions to their corresponding MIME types.
542
+ */
543
+ export const MIME_TYPES = {
544
+ // from https://github.com/python/cpython/blob/3.9/Lib/mimetypes.py
545
+ '.a': 'application/octet-stream',
546
+ '.ai': 'application/postscript',
547
+ '.aif': 'audio/x-aiff',
548
+ '.aifc': 'audio/x-aiff',
549
+ '.aiff': 'audio/x-aiff',
550
+ '.au': 'audio/basic',
551
+ '.avi': 'video/x-msvideo',
552
+ '.bat': 'text/plain',
553
+ '.bcpio': 'application/x-bcpio',
554
+ '.bin': 'application/octet-stream',
555
+ '.bmp': 'image/bmp',
556
+ '.c': 'text/plain',
557
+ '.cdf': 'application/x-netcdf',
558
+ '.cpio': 'application/x-cpio',
559
+ '.csh': 'application/x-csh',
560
+ '.css': 'text/css',
561
+ '.csv': 'text/csv',
562
+ '.dll': 'application/octet-stream',
563
+ '.doc': 'application/msword',
564
+ '.dot': 'application/msword',
565
+ '.dvi': 'application/x-dvi',
566
+ '.eml': 'message/rfc822',
567
+ '.eps': 'application/postscript',
568
+ '.etx': 'text/x-setext',
569
+ '.exe': 'application/octet-stream',
570
+ '.gif': 'image/gif',
571
+ '.gtar': 'application/x-gtar',
572
+ '.h': 'text/plain',
573
+ '.hdf': 'application/x-hdf',
574
+ '.htm': 'text/html',
575
+ '.html': 'text/html',
576
+ '.ico': 'image/vnd.microsoft.icon',
577
+ '.ief': 'image/ief',
578
+ '.jpe': 'image/jpeg',
579
+ '.jpeg': 'image/jpeg',
580
+ '.jpg': 'image/jpg',
581
+ '.js': 'application/javascript',
582
+ '.json': 'application/json',
583
+ '.ksh': 'text/plain',
584
+ '.latex': 'application/x-latex',
585
+ '.m1v': 'video/mpeg',
586
+ '.m3u': 'application/vnd.apple.mpegurl',
587
+ '.m3u8': 'application/vnd.apple.mpegurl',
588
+ '.man': 'application/x-troff-man',
589
+ '.me': 'application/x-troff-me',
590
+ '.mht': 'message/rfc822',
591
+ '.mhtml': 'message/rfc822',
592
+ '.mid': 'audio/midi',
593
+ '.midi': 'audio/midi',
594
+ '.mif': 'application/x-mif',
595
+ '.mjs': 'application/javascript',
596
+ '.mov': 'video/quicktime',
597
+ '.movie': 'video/x-sgi-movie',
598
+ '.mp2': 'audio/mpeg',
599
+ '.mp3': 'audio/mpeg',
600
+ '.mp4': 'video/mp4',
601
+ '.mpa': 'video/mpeg',
602
+ '.mpe': 'video/mpeg',
603
+ '.mpeg': 'video/mpeg',
604
+ '.mpg': 'video/mpeg',
605
+ '.ms': 'application/x-troff-ms',
606
+ '.nc': 'application/x-netcdf',
607
+ '.nws': 'message/rfc822',
608
+ '.o': 'application/octet-stream',
609
+ '.obj': 'application/octet-stream',
610
+ '.oda': 'application/oda',
611
+ '.p12': 'application/x-pkcs12',
612
+ '.p7c': 'application/pkcs7-mime',
613
+ '.pbm': 'image/x-portable-bitmap',
614
+ '.pct': 'image/pict',
615
+ '.pdf': 'application/pdf',
616
+ '.pfx': 'application/x-pkcs12',
617
+ '.pgm': 'image/x-portable-graymap',
618
+ '.pic': 'image/pict',
619
+ '.pict': 'image/pict',
620
+ '.pl': 'text/plain',
621
+ '.png': 'image/png',
622
+ '.pnm': 'image/x-portable-anymap',
623
+ '.pot': 'application/vnd.ms-powerpoint',
624
+ '.ppa': 'application/vnd.ms-powerpoint',
625
+ '.ppm': 'image/x-portable-pixmap',
626
+ '.pps': 'application/vnd.ms-powerpoint',
627
+ '.ppt': 'application/vnd.ms-powerpoint',
628
+ '.ps': 'application/postscript',
629
+ '.pwz': 'application/vnd.ms-powerpoint',
630
+ '.py': 'text/x-python',
631
+ '.pyc': 'application/x-python-code',
632
+ '.pyo': 'application/x-python-code',
633
+ '.qt': 'video/quicktime',
634
+ '.ra': 'audio/x-pn-realaudio',
635
+ '.ram': 'application/x-pn-realaudio',
636
+ '.ras': 'image/x-cmu-raster',
637
+ '.rdf': 'application/xml',
638
+ '.rgb': 'image/x-rgb',
639
+ '.roff': 'application/x-troff',
640
+ '.rtf': 'application/rtf',
641
+ '.rtx': 'text/richtext',
642
+ '.sgm': 'text/x-sgml',
643
+ '.sgml': 'text/x-sgml',
644
+ '.sh': 'application/x-sh',
645
+ '.shar': 'application/x-shar',
646
+ '.snd': 'audio/basic',
647
+ '.so': 'application/octet-stream',
648
+ '.src': 'application/x-wais-source',
649
+ '.sv4cpio': 'application/x-sv4cpio',
650
+ '.sv4crc': 'application/x-sv4crc',
651
+ '.svg': 'image/svg+xml',
652
+ '.swf': 'application/x-shockwave-flash',
653
+ '.t': 'application/x-troff',
654
+ '.tar': 'application/x-tar',
655
+ '.tcl': 'application/x-tcl',
656
+ '.tex': 'application/x-tex',
657
+ '.texi': 'application/x-texinfo',
658
+ '.texinfo': 'application/x-texinfo',
659
+ '.tif': 'image/tiff',
660
+ '.tiff': 'image/tiff',
661
+ '.tr': 'application/x-troff',
662
+ '.tsv': 'text/tab-separated-values',
663
+ '.txt': 'text/plain',
664
+ '.ustar': 'application/x-ustar',
665
+ '.vcf': 'text/x-vcard',
666
+ '.wasm': 'application/wasm',
667
+ '.wav': 'audio/x-wav',
668
+ '.webm': 'video/webm',
669
+ '.webmanifest': 'application/manifest+json',
670
+ '.wiz': 'application/msword',
671
+ '.wsdl': 'application/xml',
672
+ '.xbm': 'image/x-xbitmap',
673
+ '.xlb': 'application/vnd.ms-excel',
674
+ '.xls': 'application/vnd.ms-excel',
675
+ '.xml': 'text/xml',
676
+ '.xpdl': 'application/xml',
677
+ '.xpm': 'image/x-xpixmap',
678
+ '.xsl': 'application/xml',
679
+ '.xul': 'text/xul',
680
+ '.xwd': 'image/x-xwindowdump',
681
+ '.zip': 'application/zip',
682
+ '.ipynb': 'application/json'
683
+ };
684
+ /**
685
+ * Determine the MIME type based on the file extension.
686
+ *
687
+ * @param filename - The name of the file.
688
+ * @returns A string representing the MIME type.
689
+ */
690
+ export const getMimeType = (filename) => {
691
+ var _a;
692
+ const extension = `.${((_a = filename.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || ''}`;
693
+ if (MIME_TYPES[extension]) {
694
+ return MIME_TYPES[extension];
695
+ }
696
+ console.warn(`Unknown file extension: ${extension}, defaulting to 'application/octet-stream'.`);
697
+ return 'application/octet-stream';
698
+ };
699
+ /**
700
+ * Helper to convert a string (base64) to ArrayBuffer.
701
+ *
702
+ * @param content - File content as a base64 string.
703
+ * @returns An ArrayBuffer.
704
+ */
705
+ export const stringToArrayBuffer = async (content) => {
706
+ const base64Response = await fetch(`data:application/octet-stream;base64,${content}`);
707
+ return await base64Response.arrayBuffer();
708
+ };
709
+ export const getNumericFeatureAttributes = (featureProperties) => {
710
+ // We only want number values here
711
+ const filteredRecord = {};
712
+ for (const [key, set] of Object.entries(featureProperties)) {
713
+ const firstValue = set.values().next().value;
714
+ // Check if the first value is a string that cannot be parsed as a number
715
+ const isInvalidString = typeof firstValue === 'string' && isNaN(Number(firstValue));
716
+ if (!isInvalidString) {
717
+ filteredRecord[key] = set;
718
+ }
719
+ }
720
+ return filteredRecord;
721
+ };
package/lib/types.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { IDict, IJupyterGISDoc, IJupyterGISModel, IJupyterGISTracker, IJupyterGISWidget } from '@jupytergis/schema';
2
+ import { WidgetTracker } from '@jupyterlab/apputils';
2
3
  import { ISignal } from '@lumino/signaling';
3
4
  import { Map } from 'ol';
4
5
  export { IDict };
5
6
  export type ValueOf<T> = T[keyof T];
7
+ export type JupyterGISTracker = WidgetTracker<IJupyterGISWidget>;
6
8
  export interface IControlPanelModel {
7
9
  disconnect(f: any): void;
8
10
  documentChanged: ISignal<IJupyterGISTracker, IJupyterGISWidget | null>;
package/lib/widget.d.ts CHANGED
@@ -1,25 +1,48 @@
1
- import { JSONValue } from '@lumino/coreutils';
2
- import { ISignal } from '@lumino/signaling';
3
- import { SplitPanel } from '@lumino/widgets';
1
+ import { MainAreaWidget } from '@jupyterlab/apputils';
4
2
  import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console';
5
3
  import { DocumentWidget } from '@jupyterlab/docregistry';
6
4
  import { IObservableMap, ObservableMap } from '@jupyterlab/observables';
7
- import { IJupyterGISModel, IJupyterGISWidget } from '@jupytergis/schema';
5
+ import { JSONValue } from '@lumino/coreutils';
6
+ import { ISignal } from '@lumino/signaling';
7
+ import { SplitPanel } from '@lumino/widgets';
8
+ import { IJupyterGISModel, IJupyterGISOutputWidget, IJupyterGISDocumentWidget } from '@jupytergis/schema';
8
9
  import { JupyterGISMainViewPanel } from './mainview';
9
10
  import { MainViewModel } from './mainview/mainviewmodel';
10
11
  import { ConsoleView } from './console';
11
- export declare class JupyterGISWidget extends DocumentWidget<JupyterGISPanel, IJupyterGISModel> implements IJupyterGISWidget {
12
+ import { CommandRegistry } from '@lumino/commands';
13
+ export type JupyterGISWidget = JupyterGISDocumentWidget | JupyterGISOutputWidget;
14
+ export declare class JupyterGISDocumentWidget extends DocumentWidget<JupyterGISPanel, IJupyterGISModel> implements IJupyterGISDocumentWidget {
12
15
  constructor(options: DocumentWidget.IOptions<JupyterGISPanel, IJupyterGISModel>);
16
+ get model(): IJupyterGISModel;
13
17
  /**
14
18
  * Dispose of the resources held by the widget.
15
19
  */
16
20
  dispose(): void;
17
21
  onResize: (msg: any) => void;
18
22
  }
23
+ /**
24
+ * A main area widget designed to be used as Notebook cell output widget, to ease the
25
+ * integration of toolbar and tracking.
26
+ */
27
+ export declare class JupyterGISOutputWidget extends MainAreaWidget<JupyterGISPanel> implements IJupyterGISOutputWidget {
28
+ constructor(options: JupyterGISOutputWidget.IOptions);
29
+ /**
30
+ * Dispose of the resources held by the widget.
31
+ */
32
+ dispose(): void;
33
+ readonly model: IJupyterGISModel;
34
+ readonly resizeObserver: ResizeObserver;
35
+ }
36
+ export declare namespace JupyterGISOutputWidget {
37
+ interface IOptions extends MainAreaWidget.IOptions<JupyterGISPanel> {
38
+ model: IJupyterGISModel;
39
+ }
40
+ }
19
41
  export declare class JupyterGISPanel extends SplitPanel {
20
42
  constructor(options: JupyterGISPanel.IOptions);
21
43
  _initModel(options: {
22
44
  model: IJupyterGISModel;
45
+ commandRegistry: CommandRegistry;
23
46
  }): void;
24
47
  _initView(): void;
25
48
  get jupyterGISMainViewPanel(): JupyterGISMainViewPanel;
@@ -44,6 +67,7 @@ export declare class JupyterGISPanel extends SplitPanel {
44
67
  export declare namespace JupyterGISPanel {
45
68
  interface IOptions extends Partial<ConsoleView.IOptions> {
46
69
  model: IJupyterGISModel;
70
+ commandRegistry: CommandRegistry;
47
71
  consoleTracker?: IConsoleTracker;
48
72
  }
49
73
  }