Skip to content

Commit

Permalink
Tif layer notebook API (#139)
Browse files Browse the repository at this point in the history
* Some tif fixes

* Add notebook api for symbology colors

* Add test and remove max item from schema

* Apply suggestions from code review

Co-authored-by: Nicolas Brichet <[email protected]>

---------

Co-authored-by: martinRenou <[email protected]>
Co-authored-by: Nicolas Brichet <[email protected]>
  • Loading branch information
3 people authored Sep 17, 2024
1 parent c023a5c commit 2d2366e
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 44 deletions.
13 changes: 2 additions & 11 deletions packages/base/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@jupytergis/schema';
import { JupyterFrontEnd } from '@jupyterlab/application';
import { WidgetTracker, showErrorMessage } from '@jupyterlab/apputils';
import { ICompletionProviderManager } from '@jupyterlab/completer';
import { IStateDB } from '@jupyterlab/statedb';
import { ITranslator } from '@jupyterlab/translation';
import { CommandIDs, icons } from './constants';
Expand All @@ -19,7 +20,6 @@ import { LayerBrowserWidget } from './dialogs/layerBrowserDialog';
import { SymbologyWidget } from './dialogs/symbologyDialog';
import { TerrainDialogWidget } from './dialogs/terrainDialog';
import { JupyterGISWidget } from './widget';
import { ICompletionProviderManager } from '@jupyterlab/completer';

interface ICreateEntry {
tracker: WidgetTracker<JupyterGISWidget>;
Expand Down Expand Up @@ -300,16 +300,7 @@ export function addCommands(
createSource: true,
sourceData: {
name: 'Custom GeoTiff Source',
urls: [
{
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B04.tif',
max: 10000
},
{
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B08.tif',
max: 10000
}
]
urls: ['']
},
layerData: { name: 'Custom GeoTiff Layer' },
sourceType: 'GeoTiffSource',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const SingleBandPseudoColor = ({

const setInitialFunction = () => {
if (!layer.parameters?.color) {
setSelectedFunction('linear');
return;
}

Expand Down Expand Up @@ -131,6 +132,8 @@ const SingleBandPseudoColor = ({
const tifDataset = result.datasets[0];
tifData = await Gdal.gdalinfo(tifDataset, ['-stats']);
Gdal.close(tifDataset);

state.save(layerId, JSON.stringify(tifData));
}

tifData['bands'].forEach((bandData: TifBandData) => {
Expand All @@ -147,9 +150,6 @@ const SingleBandPseudoColor = ({
});
});
setBandRows(bandsArr);

console.log('tifData', tifData);
console.log('bandsArr', bandsArr);
};

const buildColorInfo = () => {
Expand Down Expand Up @@ -269,7 +269,7 @@ const SingleBandPseudoColor = ({
});

// fallback value
colorExpr.push([0, 0, 0]);
colorExpr.push([0, 0, 0, 1.0]);
break;
}
case 'exact': {
Expand All @@ -289,7 +289,7 @@ const SingleBandPseudoColor = ({
});

// fallback value
colorExpr.push([0, 0, 0]);
colorExpr.push([0, 0, 0, 1.0]);
break;
}
}
Expand Down
30 changes: 18 additions & 12 deletions packages/base/src/mainview/mainView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import {
IRasterDemSource,
IRasterLayer,
IRasterSource,
IShapefileSource,
IVectorLayer,
IVectorTileLayer,
IVectorTileSource,
IShapefileSource,
IWebGlLayer,
JupyterGISModel
} from '@jupytergis/schema';
Expand All @@ -28,6 +28,7 @@ import { User } from '@jupyterlab/services';
import { JSONValue, UUID } from '@lumino/coreutils';
import { Map as OlMap, View } from 'ol';
import { FeatureLike } from 'ol/Feature';
import { ScaleLine } from 'ol/control';
import { GeoJSON, MVT } from 'ol/format';
import DragAndDrop from 'ol/interaction/DragAndDrop';
import {
Expand All @@ -50,14 +51,13 @@ import {
} from 'ol/source';
import Static from 'ol/source/ImageStatic';
import { Circle, Fill, Stroke, Style } from 'ol/style';
import { ScaleLine } from 'ol/control';
//@ts-expect-error no types for ol-pmtiles
import { PMTilesRasterSource, PMTilesVectorSource } from 'ol-pmtiles';
import * as React from 'react';
import shp from 'shpjs';
import { isLightTheme } from '../tools';
import { MainViewModel } from './mainviewmodel';
import { Spinner } from './spinner';
import shp from 'shpjs';

interface IProps {
viewModel: MainViewModel;
Expand Down Expand Up @@ -598,13 +598,17 @@ export class MainView extends React.Component<IProps, IStates> {
case 'WebGlLayer': {
layerParameters = layer.parameters as IWebGlLayer;

newLayer = new WebGlTileLayer({
// This is to handle python sending a None for the color
const layerOptions: any = {
opacity: layerParameters.opacity,
source: this._sources[layerParameters.source],
style: {
color: layerParameters.color
}
});
source: this._sources[layerParameters.source]
};

if (layerParameters.color) {
layerOptions['style'] = { color: layerParameters.color };
}

newLayer = new WebGlTileLayer(layerOptions);

break;
}
Expand Down Expand Up @@ -849,9 +853,11 @@ export class MainView extends React.Component<IProps, IStates> {
case 'WebGlLayer': {
mapLayer.setOpacity(layer.parameters?.opacity);

(mapLayer as WebGlTileLayer).setStyle({
color: layer?.parameters?.color
});
if (layer?.parameters?.color) {
(mapLayer as WebGlTileLayer).setStyle({
color: layer.parameters.color
});
}
break;
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/schema/src/schema/geoTiffSource.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
}
},
"minItems": 1,
"default": [],
"description": "URLs"
},
"normalize": {
Expand Down
17 changes: 14 additions & 3 deletions packages/schema/src/schema/webGlLayer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"color": {
"oneOf": [
{ "type": "string" },
{ "type": "number" },
{
"type": "array",
"items": {
Expand All @@ -28,14 +29,24 @@
{ "type": "number" },
{
"type": "array",
"items": { "type": "string" }
"items": {
"anyOf": [
{ "type": "number" },
{ "type": "string" },
{
"type": "array",
"items": {
"anyOf": [{ "type": "number" }, { "type": "string" }]
}
}
]
}
}
]
}
}
],
"description": "The color of the the object",
"default": "#FF0000"
"description": "The color of the the object"
}
}
}
118 changes: 107 additions & 11 deletions python/jupytergis_lab/jupytergis_lab/notebook/gis_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,29 @@
import logging
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional, Union
from uuid import uuid4

from pycrdt import Array, Doc, Map
from pydantic import BaseModel
from ypywidgets.comm import CommWidget

from uuid import uuid4

from .utils import normalize_path, get_source_layer_names

from .objects import (
LayerType,
SourceType,
IGeoJSONSource,
IHillshadeLayer,
IImageLayer,
IImageSource,
IRasterLayer,
IRasterSource,
IVectorTileSource,
IVectorLayer,
IVectorTileLayer,
IGeoJSONSource,
IImageSource,
IVectorTileSource,
IVideoSource,
IWebGlLayer
IGeoTiffSource,
IWebGlLayer,
LayerType,
SourceType,
)
from .utils import get_source_layer_names, normalize_path

logger = logging.getLogger(__file__)

Expand Down Expand Up @@ -360,7 +359,102 @@ def add_video_layer(
}

return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))

def add_tiff_layer(
self,
url: str,
min: int = None,
max: int = None,
name: str = "Tiff Layer",
normalize: bool = True,
wrapX: bool = False,
attribution: str = "",
opacity: float = 1.0,
color_expr = None
):
"""
Add a tiff layer
:param str url: URL of the tif
:param int min: Minimum pixel value to be displayed, defaults to letting the map display set the value
:param int max: Maximum pixel value to be displayed, defaults to letting the map display set the value
:param str name: The name that will be used for the object in the document, defaults to "Tiff Layer"
:param bool normalize: Select whether to normalize values between 0..1, if false than min/max have no effect, defaults to True
:param bool wrapX: Render tiles beyond the tile grid extent, defaults to False
:param float opacity: The opacity, between 0 and 1, defaults to 1.0
:param _type_ color_expr: The style expression used to style the layer, defaults to None
"""

source = {
"type": SourceType.GeoTiffSource,
"name": f"{name} Source",
"parameters": {
"urls": [{
"url": url,
"min": min,
"max": max
}],
"normalize": normalize,
"wrapX": wrapX,
},
}
source_id = self._add_source(OBJECT_FACTORY.create_source(source, self))

layer = {
"type": LayerType.WebGlLayer,
"name": name,
"visible": True,
"parameters": {"source": source_id, "opacity": opacity, "color": color_expr},
}

return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))

def create_color_expr(self, color_stops: Dict, band: float = 1.0, interpolation_type: str = 'linear', ):
"""
Create a color expression used to style the layer
:param Dict color_stops: Dictionary of stop values to [r, g, b, a] colors
:param float band: The band to be colored, defaults to 1.0
:param str interpolation_type: The interpolation function. Can be linear, discrete, or exact, defaults to 'linear'
"""

if interpolation_type not in ["linear", "discrete", "exact"]:
raise ValueError("Interpolation type must be one of linear, discrete, or exact")

color = []
if interpolation_type == 'linear':
color = ['interpolate',['linear']]
color.append(['band', band])
# Transparency for nodata
color.append(0.0)
color.append([0.0, 0.0, 0.0, 0.0])

for value, colorVal in color_stops.items():
color.append(value)
color.append(colorVal)

return color

if interpolation_type == 'discrete':
operator = '<='

if interpolation_type == 'exact':
operator = '=='

color = ['case']
# Transparency for nodata
color.append(["==", ["band", band], 0.0])
color.append([0.0, 0.0, 0.0, 0.0])

for value, colorVal in color_stops.items():
color.append([operator, ["band", band], value])
color.append(colorVal)

# Fallback color
color.append([0.0, 0.0, 0.0, 1.0])

return color

def add_filter(self, layer_id: str, logical_op:str, feature:str, operator:str, value:Union[str, number, float]):
"""
Add a filter to a layer
Expand Down Expand Up @@ -529,7 +623,8 @@ class Config:
IVectorTileSource,
IGeoJSONSource,
IImageSource,
IVideoSource
IVideoSource,
IGeoTiffSource
]
_parent = Optional[GISDocument]

Expand Down Expand Up @@ -614,3 +709,4 @@ def create_source(
OBJECT_FACTORY.register_factory(SourceType.GeoJSONSource, IGeoJSONSource)
OBJECT_FACTORY.register_factory(SourceType.ImageSource, IImageSource)
OBJECT_FACTORY.register_factory(SourceType.VideoSource, IVideoSource)
OBJECT_FACTORY.register_factory(SourceType.GeoTiffSource, IGeoTiffSource)
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
from ._schema.geojsonsource import IGeoJSONSource # noqa
from ._schema.videoSource import IVideoSource # noqa
from ._schema.imageSource import IImageSource # noqa
from ._schema.geoTiffSource import IGeoTiffSource # noqa
Loading

0 comments on commit 2d2366e

Please sign in to comment.