Skip to content

Commit

Permalink
Add a source panel (#60)
Browse files Browse the repository at this point in the history
* restore the geoJSON icon for geoJSON layer button

* Add a sources panel

* order the source by name

* lint

* Add context menu to create source

* Add context menu to rename sources and remove unused sources

* lint

* Minimal height for the source and layer panels

* Update properties panel when a source is removed

* lint

* Add tests on sources

* lint

* Register the icons in a single Map object
  • Loading branch information
brichet authored Jul 23, 2024
1 parent ab33f5b commit 7332f6c
Show file tree
Hide file tree
Showing 17 changed files with 756 additions and 95 deletions.
151 changes: 83 additions & 68 deletions packages/base/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,13 @@ import {
SelectionType
} from '@jupytergis/schema';
import { JupyterFrontEnd } from '@jupyterlab/application';
import { WidgetTracker } from '@jupyterlab/apputils';
import { showErrorMessage, WidgetTracker } from '@jupyterlab/apputils';
import { ITranslator } from '@jupyterlab/translation';
import { redoIcon, undoIcon } from '@jupyterlab/ui-components';

import { CommandIDs, icons } from './constants';
import { LayerBrowserWidget } from './dialogs/layerBrowserDialog';
import { CreationFormDialog } from './dialogs/formdialog';
import { JupyterGISWidget } from './widget';
import { geoJSONIcon } from './icons';

/**
* The command IDs.
*/
export namespace CommandIDs {
export const createNew = 'jupytergis:create-new-jGIS-file';
export const redo = 'jupytergis:redo';
export const undo = 'jupytergis:undo';

export const openLayerBrowser = 'jupytergis:openLayerBrowser';

export const newGeoJSONLayer = 'jupytergis:newGeoJSONLayer';
export const newGeoJSONSource = 'jupytergis:newGeoJSONSource';

export const newVectorTileLayer = 'jupytergis:newVectorTileLayer';

export const newVectorLayer = 'jupytergis:newVectorLayer';

export const renameLayer = 'jupytergis:renameLayer';
export const removeLayer = 'jupytergis:removeLayer';
export const renameGroup = 'jupytergis:renameGroup';
export const removeGroup = 'jupytergis:removeGroup';

export const moveLayersToGroup = 'jupytergis:moveLayersToGroup';
export const moveLayerToNewGroup = 'jupytergis:moveLayerToNewGroup';
}

/**
* Add the commands to the application's command registry.
Expand Down Expand Up @@ -69,7 +42,7 @@ export function addCommands(
return current.context.model.sharedModel.redo();
}
},
icon: redoIcon
...icons.get(CommandIDs.redo)?.icon
});

commands.addCommand(CommandIDs.undo, {
Expand All @@ -86,24 +59,99 @@ export function addCommands(
return current.context.model.sharedModel.undo();
}
},
icon: undoIcon
...icons.get(CommandIDs.undo)
});

/**
* SOURCES and LAYERS creation commands.
*/
commands.addCommand(CommandIDs.openLayerBrowser, {
label: trans.__('Open Layer Browser'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
iconClass: 'fa fa-book-open',
execute: Private.createLayerBrowser(
tracker,
layerBrowserRegistry,
formSchemaRegistry
)
),
...icons.get(CommandIDs.openLayerBrowser)
});

commands.addCommand(CommandIDs.newGeoJSONLayer, {
label: trans.__('New geoJSON layer'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
execute: Private.createGeoJSONLayer(tracker, formSchemaRegistry),
...icons.get(CommandIDs.newGeoJSONLayer)
});

commands.addCommand(CommandIDs.newVectorTileLayer, {
label: trans.__('New vector tile layer'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
execute: Private.createVectorTileLayer(tracker, formSchemaRegistry),
...icons.get(CommandIDs.newVectorTileLayer)
});

/**
* SOURCES only commands.
*/
commands.addCommand(CommandIDs.newGeoJSONSource, {
label: args =>
args.from === 'contextMenu'
? trans.__('GeoJSON')
: trans.__('Add GeoJSON data from file'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
execute: Private.createGeoJSONSource(tracker, formSchemaRegistry),
...icons.get(CommandIDs.newGeoJSONSource)?.icon
});

commands.addCommand(CommandIDs.removeSource, {
label: trans.__('Remove Source'),
execute: () => {
const model = tracker.currentWidget?.context.model;
Private.removeSelectedItems(model, 'source', selection => {
if (!(model?.getLayersBySource(selection).length ?? true)) {
model?.sharedModel.removeSource(selection);
} else {
showErrorMessage(
'Remove source error',
'The source is used by a layer.'
);
}
});
}
});

commands.addCommand(CommandIDs.renameSource, {
label: trans.__('Rename Source'),
execute: async () => {
const model = tracker.currentWidget?.context.model;
await Private.renameSelectedItem(model, 'source', (sourceId, newName) => {
const source = model?.getSource(sourceId);
if (source) {
source.name = newName;
model?.sharedModel.updateSource(sourceId, source);
}
});
}
});
/**
* LAYERS and LAYER GROUPS only commands.
*/
commands.addCommand(CommandIDs.removeLayer, {
label: trans.__('Remove Layer'),
execute: () => {
Expand Down Expand Up @@ -228,48 +276,15 @@ export function addCommands(
}
});

commands.addCommand(CommandIDs.newGeoJSONLayer, {
label: trans.__('New vector layer'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
iconClass: 'fa fa-vector-square',
execute: Private.createGeoJSONLayer(tracker, formSchemaRegistry)
});

commands.addCommand(CommandIDs.newVectorLayer, {
label: trans.__('New vector layer'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
iconClass: 'fa fa-vector-square',
execute: Private.createVectorLayer(tracker, formSchemaRegistry)
});

commands.addCommand(CommandIDs.newVectorTileLayer, {
label: trans.__('New vector tile layer'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
iconClass: 'fa fa-vector-square',
execute: Private.createVectorTileLayer(tracker, formSchemaRegistry)
});

commands.addCommand(CommandIDs.newGeoJSONSource, {
label: trans.__('Add GeoJSON data from file'),
isEnabled: () => {
return tracker.currentWidget
? tracker.currentWidget.context.model.sharedModel.editable
: false;
},
icon: geoJSONIcon,
execute: Private.createGeoJSONSource(tracker, formSchemaRegistry)
execute: Private.createVectorLayer(tracker, formSchemaRegistry),
...icons.get(CommandIDs.newVectorLayer)
});
}

Expand Down
58 changes: 58 additions & 0 deletions packages/base/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { LabIcon, redoIcon, undoIcon } from '@jupyterlab/ui-components';
import { geoJSONIcon, rasterIcon } from './icons';

/**
* The command IDs.
*/
export namespace CommandIDs {
export const createNew = 'jupytergis:create-new-jGIS-file';
export const redo = 'jupytergis:redo';
export const undo = 'jupytergis:undo';

// Layers and sources commands
export const openLayerBrowser = 'jupytergis:openLayerBrowser';
export const newGeoJSONLayer = 'jupytergis:newGeoJSONLayer';
export const newVectorTileLayer = 'jupytergis:newVectorTileLayer';

// Sources only commands
export const newGeoJSONSource = 'jupytergis:newGeoJSONSource';
export const removeSource = 'jupytergis:removeSource';
export const renameSource = 'jupytergis:renameSource';

// Layers only commands
export const newVectorLayer = 'jupytergis:newVectorLayer';

export const renameLayer = 'jupytergis:renameLayer';
export const removeLayer = 'jupytergis:removeLayer';
export const renameGroup = 'jupytergis:renameGroup';
export const removeGroup = 'jupytergis:removeGroup';

export const moveLayersToGroup = 'jupytergis:moveLayersToGroup';
export const moveLayerToNewGroup = 'jupytergis:moveLayerToNewGroup';
}

interface IRegisteredIcon {
icon?: LabIcon;
iconClass?: string;
}

const iconObject = {
RasterSource: { icon: rasterIcon },
GeoJSONSource: { icon: geoJSONIcon },
VectorTileSource: { iconClass: 'fa fa-vector-square' },
RasterLayer: { icon: rasterIcon },
[CommandIDs.redo]: { icon: redoIcon },
[CommandIDs.undo]: { icon: undoIcon },
[CommandIDs.openLayerBrowser]: { iconClass: 'fa fa-book-open' },
[CommandIDs.newGeoJSONLayer]: { icon: geoJSONIcon },
[CommandIDs.newVectorTileLayer]: { iconClass: 'fa fa-vector-square' },
[CommandIDs.newGeoJSONSource]: { icon: geoJSONIcon },
[CommandIDs.newVectorLayer]: { iconClass: 'fa fa-vector-square' }
};

/**
* The registered icons
*/
export const icons = new Map<string, IRegisteredIcon>(
Object.entries(iconObject)
);
1 change: 1 addition & 0 deletions packages/base/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './commands';
export * from './constants';
export * from './dialogs/formdialog';
export * from './mainview';
export * from './tools';
Expand Down
7 changes: 4 additions & 3 deletions packages/base/src/panelview/components/layers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
} from '@jupyterlab/ui-components';
import { Panel } from '@lumino/widgets';
import React, { MouseEvent, useEffect, useState } from 'react';
import { nonVisibilityIcon, rasterIcon, visibilityIcon } from '../../icons';
import { icons } from '../../constants';
import { nonVisibilityIcon, visibilityIcon } from '../../icons';
import { IControlPanelModel } from '../../types';

const LAYERS_PANEL_CLASS = 'jp-gis-layerPanel';
Expand Down Expand Up @@ -368,9 +369,9 @@ function LayerComponent(props: ILayerProps): JSX.Element {
onClick={setSelection}
onContextMenu={setSelection}
>
{layer.type === 'RasterLayer' && (
{icons.has(layer.type) && (
<LabIcon.resolveReact
icon={rasterIcon}
{...icons.get(layer.type)}
className={LAYER_ICON_CLASS}
/>
)}
Expand Down
Loading

0 comments on commit 7332f6c

Please sign in to comment.