|
| 1 | +# Architecture overview |
| 2 | + |
| 3 | +JupyterGIS is a JupyterLab extension (based on the structure defined by |
| 4 | +[jupyterlab/extensions-cookiecutter-ts](https://github.com/jupyterlab/extension-cookiecutter-ts)). |
| 5 | + |
| 6 | +Its architecture is based on QuantStack's |
| 7 | +[JupyterCAD](https://github.com/jupytercad/JupyterCAD) architecture. |
| 8 | + |
| 9 | +## JupyterLab |
| 10 | + |
| 11 | +### About Lumino and JupyterLab |
| 12 | + |
| 13 | +JupyterGIS is a JupyterLab extension. It may be useful to read more about the |
| 14 | +[extensions developer documentation](https://jupyterlab.readthedocs.io/en/latest/extension/extension_dev.html). |
| 15 | + |
| 16 | +The [Lumino](https://lumino.readthedocs.io/en/latest/api/index.html) library is a |
| 17 | +framework used to control the UI - i.e., tracks what changes in the UI and how it should |
| 18 | +react to that change. |
| 19 | + |
| 20 | +## JupyterGIS components and structure |
| 21 | + |
| 22 | +JupyterGIS is a monorepo containing TypeScript and Python packages. |
| 23 | + |
| 24 | +### TypeScript packages |
| 25 | + |
| 26 | +TypeScript packages live in the `packages/` directory. |
| 27 | + |
| 28 | +If you change anything about TypeScript packages, you'll need to rebuild with `jlpm run |
| 29 | +build`. |
| 30 | + |
| 31 | +### `@jupytergis/base` |
| 32 | + |
| 33 | +This package contains everything that controls the map using |
| 34 | +[OpenLayers](https://openlayers.org/doc/), panels, buttons, dialogs; all as |
| 35 | +[React](https://react.dev/) components. |
| 36 | +It is a UI library, collection of tools - but it does not do anything by itself. |
| 37 | +We use this package to make the JupyterLab extension. |
| 38 | + |
| 39 | +- Defines the map view. See `packages/base/src/mainview`. |
| 40 | +- Generates the layer gallery. See |
| 41 | + `packages/base/rasterlayer_gallery_generator.py`. |
| 42 | +- Defines "commands" that appear in various GUI menus and the command pallette |
| 43 | + (`CTRL+SHIFT+C`). |
| 44 | + See `packages/base/src/commands/`. |
| 45 | + - Defines the toolbar and associated commands. |
| 46 | + See `packages/base/src/toolbar/widget.tsx`. |
| 47 | +- Generates forms from the schema package. |
| 48 | + See `packages/base/src/formbuilder/`. |
| 49 | +- Contains all logic related to adding layers and reading data. |
| 50 | + |
| 51 | +### `@jupytergis/schema` |
| 52 | + |
| 53 | +Defines our `.jgis` file format - as JSON schemas. |
| 54 | +The source of truth for data structures in JupyterGIS. |
| 55 | +If you wish to add a new layer _type_, you would need to add it to the schema. |
| 56 | + |
| 57 | +Python classes and Typescript types are automatically generated from the schema at |
| 58 | +build-time (i.e. not commited to the repository) using |
| 59 | +[`json2ts`](https://github.com/GregorBiswanger/json2ts) for TypeScript, |
| 60 | +and |
| 61 | +[`datamodel-code-generator`](https://docs.pydantic.dev/latest/integrations/datamodel_code_generator/) |
| 62 | +for Python. |
| 63 | + |
| 64 | +- Forms: Generated from e.g. `schema/src/schema/project/layers/vectorlayer.json` |
| 65 | +- Project file / shared model: `schema/src/schema/project/jgis.json` |
| 66 | + |
| 67 | +### Python packages |
| 68 | + |
| 69 | +Python packages live in the `python/` directory. |
| 70 | +These Python packages may include some TypeScript as well. |
| 71 | + |
| 72 | +- `jupytergis`: A metapackage including `jupytergis_core`, `jupytergis_lab`, |
| 73 | + `jupytergis_qgis`, `jupyter-collaboration`, and `jupyterlab`. |
| 74 | +- `jupytergis_lite`: A metapackage including `jupytergis_core` and `jupytergis_lab`. |
| 75 | + For deployment and testing of JupyterGIS in JupyterLite. |
| 76 | +- `jupytergis_core`: Gets the UI to do things - e.g., load / create JupyterGIS files, |
| 77 | + and work with them. |
| 78 | + Also includes a server endpoint for saving the created `.jgis` files to disk (not used |
| 79 | + in JupyterLite). |
| 80 | +- `jupytergis_lab`: Contains everything needed for JupyterGIS to work within a notebook, |
| 81 | + **the Python API**, the notebook renderer (the part that displays the JupyterGIS |
| 82 | + session in the notebook). |
| 83 | + **Might be worth considering renaming this folder? Current name doesn't reflect what |
| 84 | + it does**. |
| 85 | +- `jupytergis_qgis`: Enables importing and exporting QGIS project files. |
| 86 | + Requires a server component, and currently is not used in JupyterLite. |
| 87 | + |
| 88 | +### "Model" |
| 89 | + |
| 90 | +Structure is defined in schema `packages/schema/src/schema/project/jgis.json`. |
| 91 | + |
| 92 | +#### Shared model |
| 93 | + |
| 94 | +All collaborators share this and listen for changes to this. |
| 95 | +It mediates changes with Conflict-free Replicated Data Types (CRDTs), which is handled |
| 96 | +by `yjs`. |
| 97 | +It is the "magic sauce" that enables collaboration! |
| 98 | + |
| 99 | +:::tip |
| 100 | +You can view the shared model in many contexts by writing |
| 101 | +`console.log(model.sharedModel)` in a TypeScript file! |
| 102 | +::: |
| 103 | + |
| 104 | +### Commands |
| 105 | + |
| 106 | +Many new features are a matter of defining a new command. |
| 107 | + |
| 108 | +### Forms |
| 109 | + |
| 110 | +JupyterGIS uses automatically generated forms for creating/editing layers and |
| 111 | +more. |
| 112 | + |
| 113 | +Those forms are generated from schema definitions, meaning that adding a new |
| 114 | +entry in the schema will automatically create user-facing UI components when |
| 115 | +editing layers. |
| 116 | + |
| 117 | +An example of this was [adding a new "interpolate" parameter for raster |
| 118 | +sources](https://github.com/geojupyter/jupytergis/pull/522/files), the only |
| 119 | +required changes were to add the new schema entry, and react on the |
| 120 | +"interpolate" value in the OpenLayers viewer. |
| 121 | + |
| 122 | +Many forms are generated from `BaseForm` (the default form implementation), but |
| 123 | +some forms use other classes which extend `BaseForm` in order to provide more |
| 124 | +advanced controls. |
| 125 | +Each of these classes accepts the relevant schema as a property in order to |
| 126 | +generate the form on-the-fly. The correct form class is selected in |
| 127 | +`formselector.ts`. |
| 128 | + |
| 129 | +### Map view |
| 130 | + |
| 131 | +JupyterGIS uses [OpenLayers](https://openlayers.org/doc/) as a rendering engine. |
| 132 | + |
| 133 | +The action happens in the `@jupytergis/base` package, at |
| 134 | +`packages/base/src/mainview/mainView.tsx`. |
| 135 | + |
| 136 | +#### Swappable rendering engine? |
| 137 | + |
| 138 | +The Venn Diagram of the JavaScript map rendering engine ecosystem unfortunately looks |
| 139 | +like a bunch of disparate circles with few overlaps. |
| 140 | +The burden of understanding this is very high, and we hope to avoid shifting |
| 141 | +this burden on to our users. |
| 142 | + |
| 143 | +For example, OpenLayers has excellent support for alternative map projections and |
| 144 | +low-level API, but lacks support for visualizing huge vector datasets with the |
| 145 | +GPU. |
| 146 | +DeckGL can quickly render huge datasets, but lacks projection support. |
0 commit comments