Skip to content

Commit 61a46cc

Browse files
authored
feat: Stabler renderPipeline inference with props placeholder (#157)
Keeping a placeholder for how to handle the data to be later fetched by `getTileData` is a lot more elegant than just inserting a new module at the beginning. Questions: - Should we make a new type "UnresolvedRasterModule" that explicitly allows a function as the value as a prop? - Can we be smart with these generics? Ensure that the type returned from `getTileData` must be the same as the type passed in to the raster module prop function? - Can we improve the type hint of `RasterModule` to say that only numbers and textures are valid values of the properties object? Closes #141
1 parent 8fddeec commit 61a46cc

File tree

3 files changed

+60
-14
lines changed

3 files changed

+60
-14
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ Fully client-side with direct image loading, no server required.
1616
## Features
1717

1818
- Client-side visualization with no server required.
19-
- Fast, GPU-enabled image processing:
19+
- GPU-based image processing:
2020
- Converting color spaces like CMYK, YCbCr, CIELAB to RGB.
2121
- Removing nodata values
2222
- Applying colormaps
23+
- _Soon_: color correction, nodata masks, spectral band math, pixel filtering, etc.
24+
- Automatically-inferred render pipelines based on GeoTIFF metadata
25+
- Or, customizable render pipelines with _no GPU knowledge required_.
2326
- GPU-based raster reprojection supports image sources from most projections [^1]
2427
- Intelligent COG rendering, only fetching the portions of the image required for the current view.
2528

@@ -30,7 +33,7 @@ Fully client-side with direct image loading, no server required.
3033
This monorepo contains the following packages, each of which are published to NPM:
3134

3235
- [`@developmentseed/deck.gl-geotiff`](#developmentseeddeckgl-geotiff)
33-
- [`@developmentseed/deck.gl-zarr`](#developmentseeddeckgl-zarr)
36+
- [`@developmentseed/deck.gl-zarr`](#developmentseeddeckgl-zarr) (_soon_)
3437
- [`@developmentseed/deck.gl-raster`](#developmentseeddeckgl-raster)
3538
- [`@developmentseed/raster-reproject`](#developmentseedraster-reproject)
3639

packages/deck.gl-geotiff/src/geotiff/render-pipeline.ts

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,25 @@ export type TextureDataT = {
2121
texture: Texture;
2222
};
2323

24+
/**
25+
* A raster module that can be "unresolved", meaning that its props may come
26+
* from the result of `getTileData`.
27+
*
28+
* In this case, one or more of the props may be a function that takes the
29+
* `getTileData` result and returns the actual prop value.
30+
*/
31+
// TODO: it would be nice to improve the generics here, to connect the type of
32+
// the props allowed by the module to the return type of this function
33+
type UnresolvedRasterModule<DataT> =
34+
| RasterModule
35+
| {
36+
module: RasterModule["module"];
37+
props?: Record<
38+
string,
39+
number | Texture | ((data: DataT) => number | Texture)
40+
>;
41+
};
42+
2443
export function inferRenderPipeline(
2544
// TODO: narrow type to only used fields
2645
ifd: ImageFileDirectory,
@@ -61,9 +80,14 @@ function createUnormPipeline(
6180
SamplesPerPixel,
6281
} = ifd;
6382

64-
// Texture initialization will be injected inside of renderTile, once the
65-
// tile's data has loaded.
66-
const renderPipeline: RasterModule[] = [];
83+
const renderPipeline: UnresolvedRasterModule<TextureDataT>[] = [
84+
{
85+
module: CreateTexture,
86+
props: {
87+
textureName: (data: TextureDataT) => data.texture,
88+
},
89+
},
90+
];
6791

6892
// Add NoData filtering if GDAL_NODATA is defined
6993
const noDataVal = parseGDALNoData(GDAL_NODATA);
@@ -142,14 +166,7 @@ function createUnormPipeline(
142166
const renderTile: COGLayerProps<TextureDataT>["renderTile"] = (
143167
tileData: TextureDataT,
144168
): RasterModule[] => {
145-
const { texture } = tileData;
146-
return [
147-
{
148-
module: CreateTexture,
149-
props: { textureName: texture },
150-
},
151-
...renderPipeline,
152-
];
169+
return renderPipeline.map((m, _i) => resolveModule(m, tileData));
153170
};
154171

155172
return { getTileData, renderTile };
@@ -208,3 +225,25 @@ function photometricInterpretationToRGB(
208225
);
209226
}
210227
}
228+
229+
/**
230+
* If any prop of any module is a function, replace that prop value with the
231+
* result of that function
232+
*/
233+
function resolveModule<T>(m: UnresolvedRasterModule<T>, data: T): RasterModule {
234+
const { module, props } = m;
235+
236+
if (!props) {
237+
return { module };
238+
}
239+
240+
const resolvedProps: Record<string, number | Texture> = {};
241+
for (const [key, value] of Object.entries(props)) {
242+
const newValue = typeof value === "function" ? value(data) : value;
243+
if (newValue !== undefined) {
244+
resolvedProps[key] = newValue;
245+
}
246+
}
247+
248+
return { module, props: resolvedProps };
249+
}

packages/deck.gl-raster/src/gpu-modules/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import type { Texture } from "@luma.gl/core";
12
import type { ShaderModule } from "@luma.gl/shadertools";
23

34
export type RasterModule<
4-
PropsT extends Record<string, any> = Record<string, any>,
5+
PropsT extends Record<string, number | Texture> = Record<
6+
string,
7+
number | Texture
8+
>,
59
> = {
610
module: ShaderModule<PropsT>;
711
props?: Partial<PropsT>;

0 commit comments

Comments
 (0)