@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.
- package/lib/annotations/components/Annotation.js +2 -2
- package/lib/annotations/model.d.ts +6 -7
- package/lib/annotations/model.js +15 -15
- package/lib/commands.d.ts +2 -3
- package/lib/commands.js +146 -62
- package/lib/constants.d.ts +3 -0
- package/lib/constants.js +5 -1
- package/lib/dialogs/formdialog.d.ts +5 -0
- package/lib/dialogs/formdialog.js +2 -2
- package/lib/dialogs/layerBrowserDialog.d.ts +4 -5
- package/lib/dialogs/layerBrowserDialog.js +9 -9
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +2 -1
- package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +26 -0
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +64 -0
- package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +1 -1
- package/lib/dialogs/symbology/hooks/useGetProperties.js +12 -9
- package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -3
- package/lib/dialogs/symbology/symbologyDialog.js +10 -9
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +16 -3
- package/lib/dialogs/symbology/tiff_layer/components/BandRow.d.ts +16 -3
- package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +21 -7
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +4 -0
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +85 -0
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +1 -20
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +25 -65
- package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +18 -13
- package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +30 -19
- package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -13
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.d.ts +4 -0
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +77 -0
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +1 -1
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +4 -3
- package/lib/formbuilder/creationform.d.ts +6 -2
- package/lib/formbuilder/creationform.js +6 -6
- package/lib/formbuilder/editform.d.ts +2 -2
- package/lib/formbuilder/editform.js +14 -9
- package/lib/formbuilder/formselectors.js +11 -1
- package/lib/formbuilder/objectform/baseform.d.ts +12 -3
- package/lib/formbuilder/objectform/baseform.js +39 -0
- package/lib/formbuilder/objectform/fileselectorwidget.d.ts +2 -0
- package/lib/formbuilder/objectform/fileselectorwidget.js +88 -0
- package/lib/formbuilder/objectform/geojsonsource.d.ts +5 -7
- package/lib/formbuilder/objectform/geojsonsource.js +8 -24
- package/lib/formbuilder/objectform/geotiffsource.d.ts +5 -1
- package/lib/formbuilder/objectform/geotiffsource.js +64 -18
- package/lib/formbuilder/objectform/heatmapLayerForm.d.ts +11 -0
- package/lib/formbuilder/objectform/heatmapLayerForm.js +60 -0
- package/lib/formbuilder/objectform/layerform.d.ts +2 -0
- package/lib/formbuilder/objectform/layerform.js +6 -0
- package/lib/formbuilder/objectform/pathbasedsource.d.ts +19 -0
- package/lib/formbuilder/objectform/pathbasedsource.js +98 -0
- package/lib/formbuilder/objectform/vectorlayerform.d.ts +0 -2
- package/lib/formbuilder/objectform/vectorlayerform.js +0 -59
- package/lib/icons.d.ts +1 -0
- package/lib/icons.js +5 -0
- package/lib/keybindings.json +62 -0
- package/lib/mainview/TemporalSlider.d.ts +8 -0
- package/lib/mainview/TemporalSlider.js +303 -0
- package/lib/mainview/mainView.d.ts +46 -8
- package/lib/mainview/mainView.js +431 -144
- package/lib/mainview/mainviewmodel.d.ts +4 -0
- package/lib/mainview/mainviewmodel.js +4 -0
- package/lib/mainview/mainviewwidget.d.ts +0 -2
- package/lib/mainview/mainviewwidget.js +0 -2
- package/lib/panelview/annotationPanel.js +5 -5
- package/lib/panelview/components/filter-panel/Filter.js +8 -24
- package/lib/panelview/components/identify-panel/IdentifyPanel.js +1 -1
- package/lib/panelview/components/layers.js +2 -2
- package/lib/panelview/components/sources.js +1 -1
- package/lib/panelview/leftpanel.d.ts +3 -0
- package/lib/panelview/leftpanel.js +5 -1
- package/lib/panelview/model.js +8 -8
- package/lib/panelview/objectproperties.js +10 -10
- package/lib/panelview/rightpanel.d.ts +1 -1
- package/lib/panelview/rightpanel.js +10 -10
- package/lib/statusbar/StatusBar.d.ts +13 -0
- package/lib/statusbar/StatusBar.js +52 -0
- package/lib/toolbar/widget.d.ts +1 -1
- package/lib/toolbar/widget.js +44 -37
- package/lib/tools.d.ts +50 -7
- package/lib/tools.js +394 -12
- package/lib/types.d.ts +2 -0
- package/lib/widget.d.ts +29 -5
- package/lib/widget.js +41 -7
- package/package.json +17 -5
- package/style/base.css +11 -0
- package/style/icons/logo_mini_qgz.svg +31 -0
- package/style/leftPanel.css +8 -0
- package/style/statusBar.css +16 -0
- package/style/symbologyDialog.css +7 -1
- 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 {
|
|
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:
|
|
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<
|
|
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
|
|
66
|
+
export declare const loadGeoTiff: (sourceInfo: {
|
|
64
67
|
url?: string | undefined;
|
|
65
|
-
}) => Promise<{
|
|
66
|
-
file:
|
|
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
|
|
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:
|
|
321
|
+
file: cachedData.file,
|
|
321
322
|
metadata: cachedData.metadata,
|
|
322
323
|
sourceUrl: sourceInfo.url
|
|
323
324
|
};
|
|
324
325
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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(
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
}
|