Phản hồi của lớp dữ liệu xuất hiện trong Tệp GeoTIFF. Bạn có thể sử dụng để lấy dữ liệu mà bạn quan tâm. Ví dụ: giả sử bạn có một Hình ảnh GeoTIFF hiển thị các giá trị nhiệt độ trên một khu vực. Bằng cách sử dụng TypeScript, bạn có thể ánh xạ nhiệt độ thấp với màu xanh dương và nhiệt độ cao với màu đỏ để tạo ra hình ảnh đầy màu sắc dễ hiểu để trực quan hoá nhiệt độ mẫu.
Mã TypeScript này được thiết kế để lấy các tệp hình ảnh đặc biệt được gọi là GeoTIFF và hiển thị chúng trên một trang web sử dụng canvas HTML (như khung ảnh kỹ thuật số). Mã này sử dụng các thành phần sau:
- Hình ảnh GeoTIFF: GeoTIFF có thể lưu trữ nhiều lớp dữ liệu hình ảnh, giúp chúng rất hữu ích cho việc lập bản đồ hoặc phân tích khoa học.
- Hình ảnh RGB: Đây là những loại hình ảnh mà chúng tôi quen thuộc nhất (như ảnh). Mỗi pixel có các giá trị màu đỏ, xanh lục và xanh lam xác định giá trị .
- Bảng khung hiển thị: Đây giống như tập hợp màu vẽ. Chúng chứa danh sách các thuộc tính được xác định trước các màu có thể dùng để tô màu hình ảnh.
Trang này trình bày cách lấy giá trị dữ liệu pixel (thông tin được lưu trữ trong các pixel riêng lẻ của một hình ảnh kỹ thuật số, bao gồm các giá trị màu và các giá trị khác và tính toán vĩ độ và kinh độ dựa trên GeoTIFF và lưu trữ tệp đó trong đối tượng TypeScript.
Đoạn mã sau đây hiển thị định nghĩa loại nơi chúng tôi lưu trữ dữ liệu của quan tâm đến ví dụ này. Trường và loại dữ liệu là "loại" trong TypeScript. Đối với ví dụ cụ thể này, chúng tôi đã chọn cho phép kiểm tra loại, giảm lỗi về kiểu và tăng thêm độ tin cậy cho mã, giúp bạn duy trì. Xác định một loại dữ liệu để lưu trữ dữ liệu đó nhằm trả về nhiều giá trị như giá trị pixel và hộp giới hạn vĩ độ/kinh độ.
export interface GeoTiff { width: number; height: number; rasters: Array<number>[]; bounds: Bounds; }
Chức năng cốt lõi
Mã này có một số hàm hoạt động cùng nhau:
renderRGB
: Chụp hình ảnh GeoTIFF RGB và tạo mặt nạ (cho trong suốt), tạo thành phần canvas cho trang web, lặp lại qua từng pixel GeoTIFF và tô màu pixel tương ứng trên canvas.renderPalette
: Lấy GeoTIFF bằng một lớp dữ liệu và màu sắc bảng màu, ánh xạ các giá trị dữ liệu GeoTIFF với các màu trong bảng, tạo ra một hình ảnh RGB mới sử dụng các màu của bảng màu và gọirenderRGB
để hiển thị hình ảnh đó trên canvas.
/** * Renders an RGB GeoTiff image into an HTML canvas. * * The GeoTiff image must include 3 rasters (bands) which * correspond to [Red, Green, Blue] in that order. * * @param {GeoTiff} rgb GeoTiff with RGB values of the image. * @param {GeoTiff} mask Optional mask for transparency, defaults to opaque. * @return {HTMLCanvasElement} Canvas element with the rendered image. */ export function renderRGB(rgb: GeoTiff, mask?: GeoTiff): HTMLCanvasElement { // Create an HTML canvas to draw the image. // https://www.w3schools.com/tags/canvas_createimagedata.asp const canvas = document.createElement('canvas'); // Set the canvas size to the mask size if it's available, // otherwise set it to the RGB data layer size. canvas.width = mask ? mask.width : rgb.width; canvas.height = mask ? mask.height : rgb.height; // Since the mask size can be different than the RGB data layer size, // we calculate the "delta" between the RGB layer size and the canvas/mask // size. For example, if the RGB layer size is the same as the canvas size, // the delta is 1. If the RGB layer size is smaller than the canvas size, // the delta would be greater than 1. // This is used to translate the index from the canvas to the RGB layer. const dw = rgb.width / canvas.width; const dh = rgb.height / canvas.height; // Get the canvas image data buffer. const ctx = canvas.getContext('2d')!; const img = ctx.getImageData(0, 0, canvas.width, canvas.height); // Fill in every pixel in the canvas with the corresponding RGB layer value. // Since Javascript doesn't support multidimensional arrays or tensors, // everything is stored in flat arrays and we have to keep track of the // indices for each row and column ourselves. for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { // RGB index keeps track of the RGB layer position. // This is multiplied by the deltas since it might be a different // size than the image size. const rgbIdx = Math.floor(y * dh) * rgb.width + Math.floor(x * dw); // Mask index keeps track of the mask layer position. const maskIdx = y * canvas.width + x; // Image index keeps track of the canvas image position. // HTML canvas expects a flat array with consecutive RGBA values. // Each value in the image buffer must be between 0 and 255. // The Alpha value is the transparency of that pixel, // if a mask was not provided, we default to 255 which is opaque. const imgIdx = y * canvas.width * 4 + x * 4; img.data[imgIdx + 0] = rgb.rasters[0][rgbIdx]; // Red img.data[imgIdx + 1] = rgb.rasters[1][rgbIdx]; // Green img.data[imgIdx + 2] = rgb.rasters[2][rgbIdx]; // Blue img.data[imgIdx + 3] = mask // Alpha ? mask.rasters[0][maskIdx] * 255 : 255; } } // Draw the image data buffer into the canvas context. ctx.putImageData(img, 0, 0); return canvas; }
Hàm trợ giúp
Mã này cũng bao gồm một số chức năng trợ giúp cho phép chức năng:
createPalette
: Tạo danh sách màu dùng để tô màu hình ảnh dựa trên danh sách mã màu hệ thập lục phân.colorToRGB
: Chuyển đổi mã màu như "#FF00FF" thành màu đỏ, xanh lục và thành phần màu xanh dương.normalize
,lerp
,clamp
: Hàm trợ giúp toán học dành cho hình ảnh đang xử lý.
/** * Renders a single value GeoTiff image into an HTML canvas. * * The GeoTiff image must include 1 raster (band) which contains * the values we want to display. * * @param {GeoTiff} data GeoTiff with the values of interest. * @param {GeoTiff} mask Optional mask for transparency, defaults to opaque. * @param {string[]} colors Hex color palette, defaults to ['000000', 'ffffff']. * @param {number} min Minimum value of the data range, defaults to 0. * @param {number} max Maximum value of the data range, defaults to 1. * @param {number} index Raster index for the data, defaults to 0. * @return {HTMLCanvasElement} Canvas element with the rendered image. */ export function renderPalette({ data, mask, colors, min, max, index, }: { data: GeoTiff; mask?: GeoTiff; colors?: string[]; min?: number; max?: number; index?: number; }): HTMLCanvasElement { // First create a palette from a list of hex colors. const palette = createPalette(colors ?? ['000000', 'ffffff']); // Normalize each value of our raster/band of interest into indices, // such that they always map into a value within the palette. const indices = data.rasters[index ?? 0] .map((x) => normalize(x, max ?? 1, min ?? 0)) .map((x) => Math.round(x * (palette.length - 1))); return renderRGB( { ...data, // Map each index into the corresponding RGB values. rasters: [ indices.map((i: number) => palette[i].r), indices.map((i: number) => palette[i].g), indices.map((i: number) => palette[i].b), ], }, mask, ); } /** * Creates an {r, g, b} color palette from a hex list of colors. * * Each {r, g, b} value is a number between 0 and 255. * The created palette is always of size 256, regardless of the number of * hex colors passed in. Inbetween values are interpolated. * * @param {string[]} hexColors List of hex colors for the palette. * @return {{r, g, b}[]} RGB values for the color palette. */ export function createPalette(hexColors: string[]): { r: number; g: number; b: number }[] { // Map each hex color into an RGB value. const rgb = hexColors.map(colorToRGB); // Create a palette with 256 colors derived from our rgb colors. const size = 256; const step = (rgb.length - 1) / (size - 1); return Array(size) .fill(0) .map((_, i) => { // Get the lower and upper indices for each color. const index = i * step; const lower = Math.floor(index); const upper = Math.ceil(index); // Interpolate between the colors to get the shades. return { r: lerp(rgb[lower].r, rgb[upper].r, index - lower), g: lerp(rgb[lower].g, rgb[upper].g, index - lower), b: lerp(rgb[lower].b, rgb[upper].b, index - lower), }; }); } /** * Convert a hex color into an {r, g, b} color. * * @param {string} color Hex color like 0099FF or #0099FF. * @return {{r, g, b}} RGB values for that color. */ export function colorToRGB(color: string): { r: number; g: number; b: number } { const hex = color.startsWith('#') ? color.slice(1) : color; return { r: parseInt(hex.substring(0, 2), 16), g: parseInt(hex.substring(2, 4), 16), b: parseInt(hex.substring(4, 6), 16), }; } /** * Normalizes a number to a given data range. * * @param {number} x Value of interest. * @param {number} max Maximum value in data range, defaults to 1. * @param {number} min Minimum value in data range, defaults to 0. * @return {number} Normalized value. */ export function normalize(x: number, max: number = 1, min: number = 0): number { const y = (x - min) / (max - min); return clamp(y, 0, 1); } /** * Calculates the linear interpolation for a value within a range. * * @param {number} x Lower value in the range, when `t` is 0. * @param {number} y Upper value in the range, when `t` is 1. * @param {number} t "Time" between 0 and 1. * @return {number} Inbetween value for that "time". */ export function lerp(x: number, y: number, t: number): number { return x + t * (y - x); } /** * Clamps a value to always be within a range. * * @param {number} x Value to clamp. * @param {number} min Minimum value in the range. * @param {number} max Maximum value in the range. * @return {number} Clamped value. */ export function clamp(x: number, min: number, max: number): number { return Math.min(Math.max(x, min), max); }