Skip to content

Dynamic units in buffer dialog #554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions examples/world.jGIS
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"layerTree": [
"6e55cdae-35b0-4bff-9dd1-c8aa9563b2a6"
"6e55cdae-35b0-4bff-9dd1-c8aa9563b2a6",
"f80d0fa2-3e2b-4922-b7d5-fefd4b085259"
],
"layers": {
"6e55cdae-35b0-4bff-9dd1-c8aa9563b2a6": {
"name": "Custom GeoJSON Layer",
"name": "World",
"parameters": {
"color": {
"fill-color": [
Expand Down Expand Up @@ -95,26 +96,53 @@
},
"type": "VectorLayer",
"visible": true
},
"f80d0fa2-3e2b-4922-b7d5-fefd4b085259": {
"name": "France",
"parameters": {
"color": {
"fill-color": "#ff0000",
"stroke-color": "#3399CC",
"stroke-line-cap": "round",
"stroke-line-join": "round",
"stroke-width": 1.25
},
"opacity": 1.0,
"source": "5970d6c9-26be-4cc6-84c9-16593dd3edfe",
"symbologyState": {
"renderType": "Single Symbol"
},
"type": "line"
},
"type": "VectorLayer",
"visible": true
}
},
"metadata": {},
"options": {
"bearing": 0.0,
"extent": [
-35938860.79774074,
-17466155.24107265,
4136155.887837734,
18813049.299423713
-19065470.770224877,
-18193969.787702393,
21009545.91535361,
20037508.342789244
],
"latitude": 6.038467945870664,
"longitude": -142.8442794845441,
"latitude": 8.251719751227498,
"longitude": 8.731962081730105,
"pitch": 0.0,
"projection": "EPSG:3857",
"zoom": 2.100662339005199
"zoom": 1.834471049984222
},
"sources": {
"5970d6c9-26be-4cc6-84c9-16593dd3edfe": {
"name": "France",
"parameters": {
"path": "https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_FRA_1.json"
},
"type": "GeoJSONSource"
},
"b4287bea-e217-443c-b527-58f7559c824c": {
"name": "Custom GeoJSON Layer Source",
"name": "World",
"parameters": {
"path": "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson"
},
Expand Down
175 changes: 170 additions & 5 deletions packages/base/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { ITranslator } from '@jupyterlab/translation';
import { CommandRegistry } from '@lumino/commands';
import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
import { CommandIDs, icons } from './constants';
import { CreationFormDialog } from './dialogs/formdialog';
// @ts-ignore
import { LayerCreationFormDialog } from './dialogs/layerCreationFormDialog';
import { LayerBrowserWidget } from './dialogs/layerBrowserDialog';
import { SymbologyWidget } from './dialogs/symbology/symbologyDialog';
import keybindings from './keybindings.json';
Expand All @@ -27,7 +28,7 @@ import { getGdal } from './gdal';
import { getGeoJSONDataFromLayerSource, downloadFile } from './tools';
import { IJGISLayer, IJGISSource } from '@jupytergis/schema';
import { UUID } from '@lumino/coreutils';
import { FormDialog } from './formbuilder/formdialog';
import { ProcessingFormDialog } from './dialogs/ProcessingFormDialog';

interface ICreateEntry {
tracker: JupyterGISTracker;
Expand Down Expand Up @@ -350,7 +351,7 @@ export function addCommands(

// Open form and get user input
const formValues = await new Promise<IDict>(resolve => {
const dialog = new FormDialog({
const dialog = new ProcessingFormDialog({
title: 'Buffer',
schema: schema,
model: model,
Expand All @@ -359,7 +360,9 @@ export function addCommands(
bufferDistance: 10,
projection: 'EPSG:4326'
},
formContext: 'create',
cancelButton: false,
processingType: 'buffer',
syncData: (props: IDict) => {
resolve(props);
dialog.dispose();
Expand Down Expand Up @@ -454,6 +457,166 @@ export function addCommands(
}
});

commands.addCommand(CommandIDs.dissolve, {
label: trans.__('Dissolve'),
isEnabled: () => {
const selectedLayer = getSingleSelectedLayer(tracker);
if (!selectedLayer) {
return false;
}
return ['VectorLayer', 'ShapefileLayer'].includes(selectedLayer.type);
},
execute: async () => {
const selected = getSingleSelectedLayer(tracker);
if (!selected) {
console.error('No valid selected layer.');
return;
}

const sources = tracker.currentWidget?.model.sharedModel.sources ?? {};
const model = tracker.currentWidget?.model;
const localState = model?.sharedModel.awareness.getLocalState();

if (
!model ||
!localState ||
!localState['selected']?.value ||
!selected.parameters
) {
return;
}

const sourceId = selected.parameters.source;
const source = sources[sourceId];

if (!source || !source.parameters) {
console.error(`Source with ID ${sourceId} not found or missing path.`);
return;
}

// Load GeoJSON data
const geojsonString = await getGeoJSONDataFromLayerSource(source, model);
if (!geojsonString) {
return;
}

const geojson = JSON.parse(geojsonString);
if (!geojson.features || geojson.features.length === 0) {
console.error('Invalid GeoJSON: No features found.');
return;
}

// Extract field names from the first feature's properties
const properties = geojson.features[0].properties;
const fieldNames = Object.keys(properties);

if (fieldNames.length === 0) {
console.error('No attribute fields found in GeoJSON.');
return;
}

// Retrieve dissolve schema and update fields dynamically
const schema = {
...(formSchemaRegistry.getSchemas().get('Dissolve') as IDict),
properties: {
...formSchemaRegistry.getSchemas().get('Dissolve')?.properties,
dissolveField: {
type: 'string',
enum: fieldNames, // Populate dropdown with field names
description: 'Select the field for dissolving features.'
}
}
};

const selectedLayer = localState['selected'].value;
const selectedLayerId = Object.keys(selectedLayer)[0];

// Open form and get user input
const formValues = await new Promise<IDict>(resolve => {
const dialog = new ProcessingFormDialog({
title: 'Dissolve',
schema: schema,
model: model,
sourceData: {
inputLayer: selectedLayerId,
dissolveField: fieldNames[0] // Default to the first field
},
formContext: 'create',
cancelButton: false,
processingType: 'dissolve',
syncData: (props: IDict) => {
resolve(props);
dialog.dispose();
}
});

dialog.launch();
});

if (!formValues) {
return;
}

const dissolveField = formValues.dissolveField;
const fileBlob = new Blob([geojsonString], {
type: 'application/geo+json'
});
const geoFile = new File([fileBlob], 'data.geojson', {
type: 'application/geo+json'
});

const Gdal = await getGdal();
const result = await Gdal.open(geoFile);

if (result.datasets.length > 0) {
const dataset = result.datasets[0] as any;
const layerName = dataset.info.layers[0].name;

const sqlQuery = `
SELECT ST_Union(geometry) AS geometry, ${dissolveField}
FROM ${layerName}
GROUP BY ${dissolveField}
`;

const options = [
'-f',
'GeoJSON',
'-nlt',
'PROMOTE_TO_MULTI',
'-dialect',
'sqlite',
'-sql',
sqlQuery,
'output.geojson'
];

const outputFilePath = await Gdal.ogr2ogr(dataset, options);
const dissolvedBytes = await Gdal.getFileBytes(outputFilePath);
const dissolvedGeoJSONString = new TextDecoder().decode(dissolvedBytes);
Gdal.close(dataset);

const dissolvedGeoJSON = JSON.parse(dissolvedGeoJSONString);

const newSourceId = UUID.uuid4();
const sourceModel: IJGISSource = {
type: 'GeoJSONSource',
name: selected.name + ' Dissolved',
parameters: { data: dissolvedGeoJSON }
};

const layerModel: IJGISLayer = {
type: 'VectorLayer',
parameters: { source: newSourceId },
visible: true,
name: selected.name + ' Dissolved'
};

model.sharedModel.addSource(newSourceId, sourceModel);
model.addLayer(UUID.uuid4(), layerModel);
}
}
});

commands.addCommand(CommandIDs.newGeoJSONEntry, {
label: trans.__('New GeoJSON layer'),
isEnabled: () => {
Expand Down Expand Up @@ -1180,12 +1343,14 @@ export function addCommands(
};

const formValues = await new Promise<IDict>(resolve => {
const dialog = new FormDialog({
const dialog = new ProcessingFormDialog({
title: 'Download GeoJSON',
schema: exportSchema,
model,
sourceData: { exportFormat: 'GeoJSON' },
formContext: 'create',
cancelButton: false,
processingType: 'export',
syncData: (props: IDict) => {
resolve(props);
dialog.dispose();
Expand Down Expand Up @@ -1278,7 +1443,7 @@ namespace Private {
return;
}

const dialog = new CreationFormDialog({
const dialog = new LayerCreationFormDialog({
model: current.model,
title,
createLayer,
Expand Down
1 change: 1 addition & 0 deletions packages/base/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export namespace CommandIDs {

// Processing commands
export const buffer = 'jupytergis:buffer';
export const dissolve = 'jupytergis:dissolve';

// Sources only commands
export const newRasterSource = 'jupytergis:newRasterSource';
Expand Down
Loading