Skip to content

Commit

Permalink
Vector tile source (#37)
Browse files Browse the repository at this point in the history
* Vector tile source draft

* Working example to be polished

* Linter

* Python API + tests

* Revert changes on test file

* Remove rogue cell

* Update Playwright Snapshots

* Fix title

* Fix descriptions

* Fix edit form for Vector layer

* Remove comment

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
martinRenou and github-actions[bot] authored Jul 16, 2024
1 parent e52fe9f commit c2058ea
Show file tree
Hide file tree
Showing 17 changed files with 369 additions and 38 deletions.
56 changes: 56 additions & 0 deletions examples/buildings.jGIS
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"layers": {
"148f2fb3-3077-4dcb-8d70-831570d5021f": {
"type": "VectorLayer",
"name": "Vector Tile Source Layer",
"parameters": {
"opacity": 1.0,
"source": "7a7ee6fd-c1e2-4c5d-a4e2-a7974db138a4",
"sourceLayer": "bingmlbuildings",
"color": "green",
"type": "fill"
},
"visible": true
},
"f99eb7b0-5e38-4078-b310-36a0746472aa": {
"parameters": {
"source": "ed8628b0-3e0a-45d5-9cd0-65e2a7dd61f5"
},
"visible": true,
"type": "RasterLayer",
"name": "OpenStreetMap.Mapnik Layer"
}
},
"sources": {
"7a7ee6fd-c1e2-4c5d-a4e2-a7974db138a4": {
"name": "Vector Tile Source",
"parameters": {
"minZoom": 13.0,
"url": "https://planetarycomputer.microsoft.com/api/data/v1/vector/collections/ms-buildings/tilesets/global-footprints/tiles/{z}/{x}/{y}",
"maxZoom": 13.0
},
"type": "VectorTileSource"
},
"ed8628b0-3e0a-45d5-9cd0-65e2a7dd61f5": {
"parameters": {
"provider": "OpenStreetMap",
"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
"maxZoom": 19.0,
"attribution": "(C) OpenStreetMap contributors",
"minZoom": 0.0,
"urlParameters": {}
},
"type": "RasterSource",
"name": "OpenStreetMap.Mapnik"
}
},
"options": {
"longitude": -88.1392955068439,
"zoom": 13.915138763623208,
"latitude": 41.932061631424034
},
"layerTree": [
"f99eb7b0-5e38-4078-b310-36a0746472aa",
"148f2fb3-3077-4dcb-8d70-831570d5021f"
]
}
78 changes: 78 additions & 0 deletions packages/base/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export namespace CommandIDs {

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

export const newVectorTileLayer = 'jupytergis:newVectorTileLayer';

export const newVectorLayer = 'jupytergis:newVectorLayer';
}

Expand Down Expand Up @@ -125,6 +128,17 @@ export function addCommands(
execute: Private.createVectorLayer(tracker)
});

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)
});

commands.addCommand(CommandIDs.newGeoJSONSource, {
label: trans.__('Add GeoJSON data from file'),
isEnabled: () => {
Expand Down Expand Up @@ -200,6 +214,70 @@ namespace Private {
};
}

export function createVectorTileLayer(
tracker: WidgetTracker<JupyterGISWidget>
) {
return async (args: any) => {
const current = tracker.currentWidget;

if (!current) {
return;
}

const form = {
title: 'Vector Tile Layer parameters',
default: (model: IJupyterGISModel) => {
return {
name: 'Vector Tile Source',
maxZoom: 24,
minZoom: 0
};
}
};

const dialog = new FormDialog({
context: current.context,
title: form.title,
sourceData: form.default(current.context.model),
schema: FORM_SCHEMA['VectorTileSource'],
syncData: (props: IDict) => {
const sharedModel = current.context.model.sharedModel;
if (!sharedModel) {
return;
}

const { name, ...parameters } = props;

const sourceId = UUID.uuid4();

const sourceModel: IJGISSource = {
type: 'VectorTileSource',
name,
parameters: {
url: parameters.url,
minZoom: parameters.minZoom,
maxZoom: parameters.maxZoom
}
};

const layerModel: IJGISLayer = {
type: 'VectorLayer',
parameters: {
type: 'line',
source: sourceId
},
visible: true,
name: name + ' Layer'
};

sharedModel.addSource(sourceId, sourceModel);
current.context.model.addLayer(UUID.uuid4(), layerModel);
}
});
await dialog.launch();
};
}

/**
* Command to create a GeoJSON source.
*
Expand Down
1 change: 0 additions & 1 deletion packages/base/src/formdialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export interface IFormDialogOptions {
context: DocumentRegistry.IContext<IJupyterGISModel>;
}

// TODO This is currently not used, shall we remove it or will we need it later?
export class FormDialog extends Dialog<IDict> {
constructor(options: IFormDialogOptions) {
const filePath = options.context.path;
Expand Down
66 changes: 40 additions & 26 deletions packages/base/src/mainview/mainview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
IJupyterGISDoc,
IJupyterGISModel,
IRasterSource,
JupyterGISModel
JupyterGISModel,
IVectorLayer,
IVectorTileSource
} from '@jupytergis/schema';
import { showErrorMessage } from '@jupyterlab/apputils';
import { IObservableMap, ObservableMap } from '@jupyterlab/observables';
Expand Down Expand Up @@ -163,6 +165,20 @@ export class MainView extends React.Component<IProps, IStates> {
}
break;
}
case 'VectorTileSource': {
const mapSource = this._Map.getSource(id) as MapLibre.VectorTileSource;
if (!mapSource) {
const parameters = source.parameters as IVectorTileSource;
this._Map.addSource(id, {
type: 'vector',
minzoom: parameters.minZoom,
maxzoom: parameters.maxZoom,
attribution: parameters.attribution || '',
tiles: [this.computeSourceUrl(source)]
});
}
break;
}
case 'GeoJSONSource': {
const mapSource = this._Map.getSource(id) as MapLibre.GeoJSONSource;
if (!mapSource) {
Expand Down Expand Up @@ -217,6 +233,12 @@ export class MainView extends React.Component<IProps, IStates> {
]);
break;
}
case 'VectorTileSource': {
(mapSource as MapLibre.RasterTileSource).setTiles([
this.computeSourceUrl(source)
]);
break;
}
case 'GeoJSONSource': {
const data =
source.parameters?.data ||
Expand Down Expand Up @@ -351,37 +373,29 @@ export class MainView extends React.Component<IProps, IStates> {
break;
}
case 'VectorLayer': {
const vectorLayerType = layer.parameters?.type;
if (!vectorLayerType) {
showErrorMessage(
'Vector layer error',
'The vector layer type is undefined'
);
}
this._Map.addLayer(
{
id: id,
type: vectorLayerType,
layout: {
visibility: layer.visible ? 'visible' : 'none'
},
source: sourceId,
minzoom: source.parameters?.minZoom || 0,
maxzoom: source.parameters?.maxZoom || 24
const parameters = layer.parameters as IVectorLayer;
const layerSpecification: MapLibre.AddLayerObject = {
id,
type: parameters.type,
layout: {
visibility: layer.visible ? 'visible' : 'none'
},
beforeId
);
source: sourceId
};

parameters.sourceLayer &&
(layerSpecification['source-layer'] = parameters.sourceLayer);

this._Map.addLayer(layerSpecification, beforeId);
this._Map.setPaintProperty(
id,
`${vectorLayerType}-color`,
layer.parameters?.color !== undefined
? layer.parameters.color
: '#FF0000'
`${parameters.type}-color`,
parameters.color !== undefined ? parameters.color : '#FF0000'
);
this._Map.setPaintProperty(
id,
`${vectorLayerType}-opacity`,
layer.parameters?.opacity !== undefined ? layer.parameters.opacity : 1
`${parameters.type}-opacity`,
parameters.opacity !== undefined ? parameters.opacity : 1
);
break;
}
Expand Down
20 changes: 15 additions & 5 deletions packages/base/src/panelview/objectproperties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,21 @@ class ObjectPropertiesReact extends React.Component<IProps, IStates> {
let LayerForm = LayerPropertiesForm;
let SourceForm = ObjectPropertiesForm;

if (selectedObjSource.type === 'RasterSource') {
SourceForm = RasterSourcePropertiesForm;
} else if (selectedObjSource.type === 'GeoJSONSource') {
LayerForm = VectorLayerPropertiesForm;
SourceForm = GeoJSONSourcePropertiesForm;
switch (selectedObj.type) {
case 'VectorLayer':
LayerForm = VectorLayerPropertiesForm;
break;
// ADD MORE FORM TYPES HERE
}

switch (selectedObjSource.type) {
case 'GeoJSONSource':
SourceForm = GeoJSONSourcePropertiesForm;
break;
case 'RasterSource':
SourceForm = RasterSourcePropertiesForm;
break;
// ADD MORE FORM TYPES HERE
}

return (
Expand Down
9 changes: 9 additions & 0 deletions packages/base/src/toolbar/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ export class ToolbarWidget extends Toolbar {

this.addItem('separator1', new Separator());

this.addItem(
'newVectorTileLayer',
new CommandToolbarButton({
id: CommandIDs.newVectorTileLayer,
label: '',
commands: options.commands
})
);

this.addItem(
'openLayerBrowser',
new CommandToolbarButton({
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/src/schema/jgis.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"sourceType": {
"type": "string",
"enum": ["RasterSource", "GeoJSONSource"]
"enum": ["RasterSource", "VectorTileSource", "GeoJSONSource"]
},
"jGISLayer": {
"title": "IJGISLayer",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"type": "object",
"description": "VectorLayer",
"title": "IVectorLayer",
"required": ["source"],
"required": ["source", "type"],
"additionalProperties": false,
"properties": {
"source": {
Expand All @@ -12,8 +12,13 @@
"type": {
"type": "string",
"enum": ["circle", "fill", "line"],
"default": "line",
"description": "The type of vector layer"
},
"sourceLayer": {
"type": "string",
"description": "The source layer to use"
},
"color": {
"type": "string",
"description": "The color of the the object",
Expand Down
40 changes: 40 additions & 0 deletions packages/schema/src/schema/vectortilesource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"type": "object",
"description": "VectorTileSource",
"title": "IVectorTileSource",
"required": ["url", "maxZoom", "minZoom"],
"additionalProperties": false,
"properties": {
"url": {
"type": "string",
"description": "The url to the tile provider"
},
"maxZoom": {
"type": "number",
"minimum": 0,
"maximum": 24,
"description": "The maximum zoom level for the vector source"
},
"minZoom": {
"type": "number",
"minimum": 0,
"maximum": 24,
"description": "The minimum zoom level for the vector source"
},
"attribution": {
"type": "string",
"description": "The attribution for the vector source"
},
"provider": {
"type": "string",
"readOnly": true,
"description": "The map provider"
},
"urlParameters": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
3 changes: 3 additions & 0 deletions packages/schema/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export * from './_interface/jgis';
export * from './_interface/rasterlayer';
export * from './_interface/vectorlayer';
export * from './_interface/rastersource';
export * from './_interface/vectortilesource';
export * from './_interface/geojsonsource';
export * from './interfaces';
export * from './model';
export * from './doc';
Expand Down
Loading

0 comments on commit c2058ea

Please sign in to comment.