Skip to content
Merged
55 changes: 54 additions & 1 deletion examples/cog-basic/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { DeckProps } from "@deck.gl/core";
import { MapboxOverlay } from "@deck.gl/mapbox";
import { COGLayer, proj } from "@developmentseed/deck.gl-geotiff";
import { COGLayer, loadRgbImage, proj } from "@developmentseed/deck.gl-geotiff";
import { CreateTexture } from "@developmentseed/deck.gl-raster";
import { Device, Texture } from "@luma.gl/core";
import type { GeoTIFFImage } from "geotiff";
import { Pool } from "geotiff";
import { toProj4 } from "geotiff-geokeys-to-proj4";
import "maplibre-gl/dist/maplibre-gl.css";
Expand Down Expand Up @@ -35,6 +38,54 @@ async function geoKeysParser(
const COG_URL =
"https://ds-wheels.s3.us-east-1.amazonaws.com/m_4007307_sw_18_060_20220803.tif";

type DataT = {
texture: Texture;
height: number;
width: number;
};

async function getTileData(
image: GeoTIFFImage,
options: {
device: Device;
window: [number, number, number, number];
signal?: AbortSignal;
pool: Pool;
},
): Promise<DataT> {
const { device } = options;
const { texture: data, height, width } = await loadRgbImage(image, options);

// Note: if we set this format to r8unorm it'll only fill the red channel of
// the texture, making it red.
const texture = device.createTexture({
format: "rgba8unorm",
dimension: "2d",
width,
height,
data,
});

return {
texture,
height,
width,
};
}

function renderTile(data: DataT) {
const { texture } = data;

return [
{
module: CreateTexture,
props: {
textureName: texture,
},
},
];
}

export default function App() {
const mapRef = useRef<MapRef>(null);
const [debug, setDebug] = useState(false);
Expand All @@ -51,6 +102,8 @@ export default function App() {
debugOpacity,
geoKeysParser,
pool,
getTileData,
renderTile,
onGeoTIFFLoad: (_tiff, options) => {
const { west, south, east, north } = options.geographicBounds;
mapRef.current?.fitBounds(
Expand Down
62 changes: 24 additions & 38 deletions examples/land-cover/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { DeckProps } from "@deck.gl/core";
import { MapboxOverlay } from "@deck.gl/mapbox";
import type { Device, Texture } from "@luma.gl/core";
import { ShaderModule } from "@luma.gl/shadertools";
import {
COGLayer,
parseColormap,
Expand All @@ -12,7 +11,12 @@ import type {
GeoTIFFImage,
TypedArrayArrayWithDimensions,
} from "geotiff";
import { RasterLayerProps } from "@developmentseed/deck.gl-raster";
import {
Colormap,
CreateTexture,
FilterNoDataVal,
RasterModule,
} from "@developmentseed/deck.gl-raster";
import { fromUrl, Pool } from "geotiff";
import { toProj4 } from "geotiff-geokeys-to-proj4";
import "maplibre-gl/dist/maplibre-gl.css";
Expand Down Expand Up @@ -88,14 +92,6 @@ async function getCogBounds(
const COG_URL =
"https://ds-wheels.s3.us-east-1.amazonaws.com/Annual_NLCD_LndCov_2023_CU_C1V0.tif";

export type LandCoverProps = {
colormap_texture: Texture;
};

const landCoverModule = {
name: "landCover",
} as const satisfies ShaderModule<LandCoverProps>;

async function getTileData(
image: GeoTIFFImage,
options: {
Expand Down Expand Up @@ -142,44 +138,34 @@ type TileDataT = {
function renderTile(
tileData: TileDataT,
colormapTexture: Texture,
): {
texture: RasterLayerProps["texture"];
shaders?: RasterLayerProps["shaders"];
} {
): RasterModule[] {
const { texture } = tileData;

// Hard coded NoData value but this ideally would be fetched from COG metadata
const nodataVal = 250;
// Since values are 0-1 for unorm textures,
const noDataScaled = nodataVal / 255.0;

return {
texture,
// For colormap rendering:
shaders: {
inject: {
"fs:#decl": `
uniform sampler2D colormap_texture;
`,
"fs:DECKGL_FILTER_COLOR": `
float value = color.r;
vec3 pickingval = vec3(value, 0., 0.);
if (value == ${noDataScaled}) {
discard;
} else {
vec4 color_val = texture(colormap_texture, vec2(value, 0.));
color = color_val;
}
`,
return [
{
module: CreateTexture,
props: {
textureName: texture,
},
modules: [landCoverModule],
shaderProps: {
landCover: {
colormap_texture: colormapTexture,
},
},
{
module: FilterNoDataVal,
props: {
value: noDataScaled,
},
},
};
{
module: Colormap,
props: {
colormapTexture: colormapTexture,
},
},
];
}

export default function App() {
Expand Down
17 changes: 6 additions & 11 deletions packages/deck.gl-geotiff/src/cog-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import {
} from "@deck.gl/geo-layers";
import { PathLayer } from "@deck.gl/layers";
import type {
RasterLayerProps,
RasterModule,
TileMatrix,
TileMatrixSet,
} from "@developmentseed/deck.gl-raster";
import { RasterLayer, RasterTileset2D } from "@developmentseed/deck.gl-raster";
import type { ReprojectionFns } from "@developmentseed/raster-reproject";
import type { Device, Texture } from "@luma.gl/core";
import type { Device } from "@luma.gl/core";
import type { BaseClient, GeoTIFF, GeoTIFFImage, Pool } from "geotiff";
import proj4 from "proj4";
import { parseCOGTileMatrixSet } from "./cog-tile-matrix-set.js";
Expand Down Expand Up @@ -48,7 +48,7 @@ export type MinimalDataT = {
};

export type DefaultDataT = MinimalDataT & {
texture: ImageData | Texture;
texture: ImageData;
};

export type GetTileTextureOptions = {
Expand Down Expand Up @@ -126,10 +126,7 @@ export interface COGLayerProps<
* The default implementation returns an object with a `texture` property,
* assuming that this texture is already renderable.
*/
renderTile: (data: DataT) => {
texture: RasterLayerProps["texture"];
shaders?: RasterLayerProps["shaders"];
};
renderTile: (data: DataT) => ImageData | RasterModule[];

/**
* Enable debug visualization showing the triangulation mesh
Expand Down Expand Up @@ -172,7 +169,7 @@ const defaultProps: Partial<COGLayerProps> = {
geoKeysParser: epsgIoGeoKeyParser,
getTileData: loadRgbImage,
renderTile: (data) => {
return { texture: data.texture };
return data.texture;
},
};

Expand Down Expand Up @@ -314,15 +311,13 @@ export class COGLayer<

if (data) {
const { height, width } = data;
const { texture, shaders } = this.props.renderTile(data);

layers.push(
new RasterLayer({
id: `${props.id}-raster`,
width,
height,
texture,
shaders,
renderPipeline: this.props.renderTile(data),
maxError,
reprojectionFns: {
forwardTransform,
Expand Down
2 changes: 2 additions & 0 deletions packages/deck.gl-raster/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export type {
TileMatrixSet,
TileMatrixSetBoundingBox,
} from "./raster-tileset/types.js";
export { Colormap, CreateTexture, FilterNoDataVal } from "./webgl/index.js";
export type { RasterModule } from "./webgl/types.js";

import { __TEST_EXPORTS as traversalTestExports } from "./raster-tileset/raster-tile-traversal.js";

Expand Down
41 changes: 41 additions & 0 deletions packages/deck.gl-raster/src/mesh-layer/mesh-layer-fragment.glsl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* This is a vendored copy of the SimpleMeshLayer's fragment shader:
* https://github.com/visgl/deck.gl/blob/a15c8cea047993c8a861bf542835c1988f30165c/modules/mesh-layers/src/simple-mesh-layer/simple-mesh-layer-fragment.glsl.ts
* under the MIT license.
*
* We edited this to remove the hard-coded texture uniform because we want to
* support integer and signed integer textures, not only normalized unsigned
* textures.
*/
export default /* glsl */ `#version 300 es
#define SHADER_NAME simple-mesh-layer-fs

precision highp float;

in vec2 vTexCoord;
in vec3 cameraPosition;
in vec3 normals_commonspace;
in vec4 position_commonspace;
in vec4 vColor;

out vec4 fragColor;

void main(void) {
geometry.uv = vTexCoord;

vec3 normal;
if (simpleMesh.flatShading) {

normal = normalize(cross(dFdx(position_commonspace.xyz), dFdy(position_commonspace.xyz)));
} else {
normal = normals_commonspace;
}

// We initialize color here before passing into DECKGL_FILTER_COLOR
vec4 color;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still assumes that color will be a unorm vec4. Maybe we'll want to also allow the parameter into DECKGL_FILTER_COLOR to be a uvec4 or an ivec4?

DECKGL_FILTER_COLOR(color, geometry);

vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, normal);
fragColor = vec4(lightColor, color.a * layer.opacity);
}
`;
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import type { SimpleMeshLayerProps } from "@deck.gl/mesh-layers";
import { SimpleMeshLayer } from "@deck.gl/mesh-layers";
import fs from "./mesh-layer-fragment.glsl.js";

import type { ShaderModule } from "@luma.gl/shadertools";
import type { RasterModule } from "../webgl/types.js";

export interface MeshTextureLayerProps extends SimpleMeshLayerProps {
shaders?: {
inject?: {
"fs:#decl"?: string;
"fs:DECKGL_FILTER_COLOR"?: string;
};
modules?: ShaderModule[];
shaderProps?: { [x: string]: Partial<Record<string, unknown> | undefined> };
};
renderPipeline: RasterModule[];
}

/**
Expand All @@ -31,24 +26,30 @@ export class MeshTextureLayer extends SimpleMeshLayer<
override getShaders() {
const upstreamShaders = super.getShaders();

const modules: ShaderModule[] = upstreamShaders.modules;
for (const m of this.props.renderPipeline) {
modules.push(m.module);
}

return {
...upstreamShaders,
inject: {
...upstreamShaders.inject,
...this.props.shaders?.inject,
},
modules: [
...upstreamShaders.modules,
...(this.props.shaders?.modules || []),
],
// Override upstream's fragment shader with our copy with modified
// injection points
fs,
modules,
};
}

override draw(opts: any): void {
if (this.props.shaders?.shaderProps)
for (const m of super.getModels()) {
m.shaderInputs.setProps(this.props.shaders.shaderProps);
}
const shaderProps: { [x: string]: Partial<Record<string, unknown>> } = {};
for (const m of this.props.renderPipeline) {
// Props should be keyed by module name
shaderProps[m.module.name] = m.props;
}

for (const m of super.getModels()) {
m.shaderInputs.setProps(shaderProps);
}

super.draw(opts);
}
Expand Down
Loading