Skip to content

Commit 5b39fa3

Browse files
committed
Major Refactor
1 parent c13857d commit 5b39fa3

File tree

7 files changed

+171
-157
lines changed

7 files changed

+171
-157
lines changed

packages/base/src/commands.ts

+105-97
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,45 @@ function downloadFile(content: BlobPart, fileName: string, mimeType: string) {
6262
document.body.removeChild(downloadLink);
6363
}
6464

65+
function getSelectedLayer(tracker: JupyterGISTracker): { model: IJupyterGISModel; selectedLayer: any; sources: any } | null {
66+
const model = tracker.currentWidget?.model as IJupyterGISModel;
67+
if (!model) {
68+
return null;
69+
}
70+
71+
const localState = model.sharedModel.awareness.getLocalState();
72+
if (!localState || !localState['selected']?.value) {
73+
return null;
74+
}
75+
76+
const selectedLayerId = Object.keys(localState['selected'].value)[0];
77+
const layers = model.sharedModel.layers ?? {};
78+
const sources = model.sharedModel.sources ?? {};
79+
80+
const selectedLayer = layers[selectedLayerId];
81+
if (!selectedLayer || !selectedLayer.parameters) {
82+
return null;
83+
}
84+
85+
return { model, selectedLayer, sources };
86+
}
87+
88+
async function getGeoJSONString(source: any, model: IJupyterGISModel): Promise<string | null> {
89+
if (source.parameters.path) {
90+
const fileContent = await loadFile({
91+
filepath: source.parameters.path,
92+
type: source.type,
93+
model
94+
});
95+
return typeof fileContent === 'object' ? JSON.stringify(fileContent) : fileContent;
96+
} else if (source.parameters.data) {
97+
return JSON.stringify(source.parameters.data);
98+
}
99+
console.error("Source is missing both 'path' and 'data' parameters.");
100+
return null;
101+
}
102+
103+
65104
/**
66105
* Add the commands to the application's command registry.
67106
*/
@@ -1178,64 +1217,78 @@ export function addCommands(
11781217
}
11791218
});
11801219

1181-
commands.addCommand(CommandIDs.exportLayer, {
1182-
label: trans.__('Export Layer'),
1220+
commands.addCommand(CommandIDs.downloadGeoJSON, {
1221+
label: trans.__('Download as GeoJSON'),
11831222
isEnabled: () => {
1184-
const model = tracker.currentWidget?.model;
1185-
const localState = model?.sharedModel.awareness.getLocalState();
1186-
1187-
if (!model || !localState || !localState['selected']?.value) {
1188-
return false;
1223+
const selectedLayer = getSelectedLayer(tracker);
1224+
return selectedLayer ? ['VectorLayer', 'ShapefileLayer'].includes(selectedLayer.selectedLayer.type) : false;
1225+
},
1226+
execute: async () => {
1227+
const selectedData = getSelectedLayer(tracker);
1228+
if (!selectedData) {
1229+
return;
11891230
}
11901231

1191-
const selectedLayers = localState['selected'].value;
1232+
const { model, selectedLayer, sources } = selectedData;
1233+
const exportSchema = { ...(formSchemaRegistry.getSchemas().get('ExportGeoJSONSchema') as IDict) };
11921234

1193-
if (Object.keys(selectedLayers).length > 1) {
1194-
return false;
1195-
}
1235+
const formValues = await new Promise<IDict>(resolve => {
1236+
const dialog = new ProcessingFormDialog({
1237+
title: 'Download GeoJSON',
1238+
schema: exportSchema,
1239+
model,
1240+
sourceData: { exportFormat: 'GeoJSON' },
1241+
cancelButton: false,
1242+
syncData: (props: IDict) => {
1243+
resolve(props);
1244+
dialog.dispose();
1245+
}
1246+
});
11961247

1197-
const layerId = Object.keys(selectedLayers)[0];
1198-
const layer = model.getLayer(layerId);
1248+
dialog.launch();
1249+
});
11991250

1200-
if (!layer) {
1201-
return false;
1251+
if (!formValues) {
1252+
return;
12021253
}
12031254

1204-
return ['VectorLayer', 'ShapefileLayer'].includes(layer.type);
1205-
},
1206-
execute: async () => {
1207-
const layers = tracker.currentWidget?.model.sharedModel.layers ?? {};
1208-
const sources = tracker.currentWidget?.model.sharedModel.sources ?? {};
1255+
const exportFileName = formValues.exportFileName;
1256+
const sourceId = selectedLayer.parameters.source;
1257+
const source = sources[sourceId];
12091258

1210-
const model = tracker.currentWidget?.model;
1211-
const localState = model?.sharedModel.awareness.getLocalState();
1212-
if (!model || !localState || !localState['selected']?.value) {
1259+
const geojsonString = await getGeoJSONString(source, model);
1260+
if (!geojsonString) {
12131261
return;
12141262
}
12151263

1216-
const selectedLayerId = Object.keys(localState['selected'].value)[0];
1217-
const selectedLayer = layers[selectedLayerId];
1264+
downloadFile(geojsonString, `${exportFileName}.geojson`, 'application/geo+json');
1265+
}
1266+
});
12181267

1219-
if (!selectedLayer || !selectedLayer.parameters) {
1220-
console.error('Selected layer not found.');
1268+
commands.addCommand(CommandIDs.downloadGeoTIFF, {
1269+
label: trans.__('Download as GeoTIFF'),
1270+
isEnabled: () => {
1271+
const selectedLayer = getSelectedLayer(tracker);
1272+
return selectedLayer ? ['VectorLayer', 'ShapefileLayer'].includes(selectedLayer.selectedLayer.type) : false;
1273+
},
1274+
execute: async () => {
1275+
const selectedData = getSelectedLayer(tracker);
1276+
if (!selectedData) {
12211277
return;
12221278
}
12231279

1224-
const exportSchema = {
1225-
...(formSchemaRegistry.getSchemas().get('ExportSchema') as IDict)
1226-
};
1280+
const { model, selectedLayer, sources } = selectedData;
1281+
const exportSchema = { ...(formSchemaRegistry.getSchemas().get('ExportGeoTIFFSchema') as IDict) };
1282+
console.log(exportSchema);
1283+
12271284

12281285
const formValues = await new Promise<IDict>(resolve => {
12291286
const dialog = new ProcessingFormDialog({
1230-
title: 'Export Layer',
1287+
title: 'Download GeoTIFF',
12311288
schema: exportSchema,
1232-
model: tracker.currentWidget?.model as IJupyterGISModel,
1233-
sourceData: {
1234-
exportFormat: 'GeoJSON',
1235-
resolutionX: 1200,
1236-
resolutionY: 1200
1237-
},
1238-
cancelButton: true,
1289+
model,
1290+
sourceData: { resolutionX: 1200, resolutionY: 1200 },
1291+
cancelButton: false,
12391292
syncData: (props: IDict) => {
12401293
resolve(props);
12411294
dialog.dispose();
@@ -1250,88 +1303,43 @@ export function addCommands(
12501303
}
12511304

12521305
const exportFileName = formValues.exportFileName;
1253-
const exportFormat = formValues.exportFormat;
12541306
const resolutionX = formValues.resolutionX ?? 1200;
12551307
const resolutionY = formValues.resolutionY ?? 1200;
1256-
12571308
const sourceId = selectedLayer.parameters.source;
12581309
const source = sources[sourceId];
12591310

1260-
if (!source.parameters) {
1261-
console.error(`Source with ID ${sourceId} not found or missing path.`);
1262-
return;
1263-
}
1264-
1265-
let geojsonString: string;
1266-
if (source.parameters.path) {
1267-
const fileContent = await loadFile({
1268-
filepath: source.parameters.path,
1269-
type: source.type,
1270-
model: tracker.currentWidget?.model as IJupyterGISModel
1271-
});
1272-
1273-
geojsonString =
1274-
typeof fileContent === 'object'
1275-
? JSON.stringify(fileContent)
1276-
: fileContent;
1277-
} else if (source.parameters.data) {
1278-
geojsonString = JSON.stringify(source.parameters.data);
1279-
} else {
1280-
throw new Error(
1281-
`Source ${sourceId} is missing both 'path' and 'data' parameters.`
1282-
);
1283-
}
1284-
1285-
if (exportFormat === 'GeoJSON') {
1286-
downloadFile(geojsonString, `${exportFileName}.geojson`, 'application/geo+json');
1311+
const geojsonString = await getGeoJSONString(source, model);
1312+
if (!geojsonString) {
12871313
return;
12881314
}
12891315

12901316
const Gdal = await getGdal();
1291-
const datasetList = await Gdal.open(
1292-
new File([geojsonString], 'data.geojson', {
1293-
type: 'application/geo+json'
1294-
})
1295-
);
1296-
1297-
const dataset = datasetList.datasets[0]; // Extract the first dataset
1317+
const datasetList = await Gdal.open(new File([geojsonString], 'data.geojson', { type: 'application/geo+json' }));
1318+
const dataset = datasetList.datasets[0];
12981319

12991320
if (!dataset) {
13001321
console.error('Dataset could not be opened.');
13011322
return;
13021323
}
13031324

1304-
let options: string[] = [];
1305-
1306-
if (exportFormat === 'GeoTIFF') {
1307-
options = [
1308-
'-of',
1309-
'GTiff',
1310-
'-ot',
1311-
'Float32',
1312-
'-a_nodata',
1313-
'-1.0',
1314-
'-burn',
1315-
'0.0',
1316-
'-ts',
1317-
resolutionX.toString(),
1318-
resolutionY.toString(),
1319-
'-l',
1320-
'data',
1321-
'data.geojson',
1322-
'output.tif'
1323-
];
1324-
}
1325+
const options = [
1326+
'-of', 'GTiff',
1327+
'-ot', 'Float32',
1328+
'-a_nodata', '-1.0',
1329+
'-burn', '0.0',
1330+
'-ts', resolutionX.toString(), resolutionY.toString(),
1331+
'-l', 'data', 'data.geojson', 'output.tif'
1332+
];
13251333

13261334
const outputFilePath = await Gdal.gdal_rasterize(dataset, options);
1327-
13281335
const exportedBytes = await Gdal.getFileBytes(outputFilePath);
1329-
downloadFile(exportedBytes, `${exportFileName}.${exportFormat.toLowerCase()}`, 'application/octet-stream');
1336+
downloadFile(exportedBytes, `${exportFileName}.tif`, 'application/octet-stream');
13301337

13311338
Gdal.close(dataset);
13321339
}
13331340
});
13341341

1342+
13351343
loadKeybindings(commands, keybindings);
13361344
}
13371345

packages/base/src/constants.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ export namespace CommandIDs {
6969
// Map Commands
7070
export const addAnnotation = 'jupytergis:addAnnotation';
7171
export const zoomToLayer = 'jupytergis:zoomToLayer';
72-
export const exportLayer = 'jupytergis:exportLayer';
72+
export const downloadGeoJSON = 'jupytergis:downloadGeoJSON';
73+
export const downloadGeoTIFF = 'jupytergis:downloadGeoTIFF';
7374
}
7475

7576
interface IRegisteredIcon {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"type": "object",
3+
"description": "ExportGeoJSONSchema",
4+
"title": "IExportGeoJSON",
5+
"required": ["exportFileName"],
6+
"additionalProperties": false,
7+
"properties": {
8+
"exportFileName": {
9+
"type": "string",
10+
"title": "GeoJSON File Name",
11+
"default": "exported_layer",
12+
"description": "The name of the exported GeoJSON file."
13+
}
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"type": "object",
3+
"description": "ExportGeoTIFFSchema",
4+
"title": "IExportGeoTIFF",
5+
"required": ["exportFileName", "resolutionX", "resolutionY"],
6+
"additionalProperties": false,
7+
"properties": {
8+
"exportFileName": {
9+
"type": "string",
10+
"title": "GeoTiFF File Name",
11+
"default": "exported_layer",
12+
"description": "The name of the exported GeoTIFF file."
13+
},
14+
"resolutionX": {
15+
"type": "number",
16+
"title": "Resolution (Width)",
17+
"default": 1200,
18+
"minimum": 1,
19+
"maximum": 10000,
20+
"description": "The width resolution for the raster export."
21+
},
22+
"resolutionY": {
23+
"type": "number",
24+
"title": "Resolution (Height)",
25+
"default": 1200,
26+
"minimum": 1,
27+
"maximum": 10000,
28+
"description": "The height resolution for the raster export."
29+
}
30+
}
31+
}

packages/schema/src/schema/exportSchema.json

-56
This file was deleted.

packages/schema/src/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export * from './_interface/heatmapLayer';
2121
export * from './_interface/buffer';
2222

2323
// exportLayer
24-
export * from './_interface/exportSchema';
24+
export * from './_interface/exportGeojson';
25+
export * from './_interface/exportGeotiff';
2526

2627
// Other
2728
export * from './doc';

0 commit comments

Comments
 (0)