|
1 | 1 | import * as png from "@vivaxy/png";
|
2 | 2 |
|
| 3 | +import { JSONLoader, load } from "@loaders.gl/core"; |
| 4 | + |
| 5 | +import type { TConstructor, TypedArray } from "./typedArray"; |
| 6 | +import { toTypedArray } from "./typedArray"; |
| 7 | + |
| 8 | +async function safeFetch(url: string): Promise<Response> { |
| 9 | + try { |
| 10 | + return await fetch(url); |
| 11 | + } catch (error) { |
| 12 | + console.error(`Failed to fetch ${url}:`, error); |
| 13 | + return new Response(null, { status: 500 }); |
| 14 | + } |
| 15 | +} |
| 16 | + |
3 | 17 | /**
|
4 |
| - * Loads data from a URL as a Float32Array. Supports both PNG images (with absolute float values) |
5 |
| - * and binary float32 files. If the content type is 'image/png', the PNG is decoded and its pixel |
6 |
| - * data is returned as a Float32Array. Otherwise, the file is loaded as a binary array of floats. |
| 18 | + * Loads PNG image data (with absolute float values) from a Response object |
| 19 | + * and decodes it into a typed array of the specified type. |
| 20 | + * |
| 21 | + * This function reads the response as a Blob, decodes the PNG image data, and converts the pixel data |
| 22 | + * into a typed array (e.g., Float32Array, Uint8Array) using the provided constructor. |
7 | 23 | *
|
8 |
| - * @param url - The URL to load data from |
9 |
| - * @returns A Promise resolving to a Float32Array with the data, or null if loading fails |
| 24 | + * @template T - The type of TypedArray to return (e.g., Float32Array, Uint8Array). |
| 25 | + * @param response - The Response object containing the PNG image data. |
| 26 | + * @param type - The constructor for the desired TypedArray type. |
| 27 | + * @returns A promise that resolves to a typed array containing the decoded image data, or null if decoding fails. |
10 | 28 | */
|
11 |
| -export async function loadURLData(url: string): Promise<Float32Array | null> { |
12 |
| - let res: Float32Array | null = null; |
13 |
| - const response = await fetch(url); |
14 |
| - if (!response.ok) { |
15 |
| - console.error("Could not load ", url); |
16 |
| - } |
| 29 | +async function loadPngData<T extends TypedArray>( |
| 30 | + response: Response, |
| 31 | + type: TConstructor<T> |
| 32 | +): Promise<T | null> { |
| 33 | + // Load as PNG with absolute float values. |
17 | 34 | const blob = await response.blob();
|
18 |
| - const contentType = response.headers.get("content-type"); |
19 |
| - const isPng = contentType === "image/png"; |
20 |
| - if (isPng) { |
21 |
| - // Load as PNG with absolute float values. |
22 |
| - res = await new Promise((resolve) => { |
23 |
| - const fileReader = new FileReader(); |
24 |
| - fileReader.readAsArrayBuffer(blob); |
25 |
| - fileReader.onload = () => { |
26 |
| - const arrayBuffer = fileReader.result; |
27 |
| - const imgData = png.decode(arrayBuffer as ArrayBuffer); |
28 |
| - const data = imgData.data; // array of ints (pixel data) |
| 35 | + const result: T | null = await new Promise((resolve) => { |
| 36 | + const fileReader = new FileReader(); |
| 37 | + fileReader.readAsArrayBuffer(blob); |
| 38 | + fileReader.onload = () => { |
| 39 | + const arrayBuffer = fileReader.result; |
| 40 | + const imgData = png.decode(arrayBuffer as ArrayBuffer); |
| 41 | + const data = imgData.data; // array of ints (pixel data) |
29 | 42 |
|
30 |
| - const n = data.length; |
31 |
| - const buffer = new ArrayBuffer(n); |
32 |
| - const view = new DataView(buffer); |
33 |
| - for (let i = 0; i < n; i++) { |
34 |
| - view.setUint8(i, data[i]); |
35 |
| - } |
| 43 | + const n = data.length; |
| 44 | + const buffer = new ArrayBuffer(n); |
| 45 | + const view = new DataView(buffer); |
| 46 | + for (let i = 0; i < n; i++) { |
| 47 | + view.setUint8(i, data[i]); |
| 48 | + } |
36 | 49 |
|
37 |
| - const floatArray = new Float32Array(buffer); |
38 |
| - resolve(floatArray); |
39 |
| - }; |
40 |
| - }); |
41 |
| - } else { |
42 |
| - // Load as binary array of floats. |
43 |
| - const buffer = await blob.arrayBuffer(); |
44 |
| - res = new Float32Array(buffer); |
45 |
| - } |
46 |
| - return res; |
| 50 | + const floatArray = new type(buffer); |
| 51 | + resolve(floatArray); |
| 52 | + }; |
| 53 | + }); |
| 54 | + return result; |
| 55 | +} |
| 56 | + |
| 57 | +function isPngData(headers: Headers): boolean { |
| 58 | + const contentType = headers.get("content-type"); |
| 59 | + return contentType === "image/png"; |
47 | 60 | }
|
48 | 61 |
|
49 | 62 | /**
|
50 |
| - * Loads data as a Float32Array from a string (URL), number array, or Float32Array. |
51 |
| - * If the input is a URL, it loads the data from the URL. If it's an array, it converts it. |
| 63 | + * Loads data as a typed array from a string (URL), number array, or any typed array. |
| 64 | + * If the input is a URL, it loads the data from the URL. Supports binary float32 files, |
| 65 | + * PNG images (with absolute float values) and json files storing number arrays. |
52 | 66 | *
|
53 |
| - * @param data - The data to load (string URL, number[], or Float32Array) |
54 |
| - * @returns A Promise resolving to a Float32Array or null if input is invalid |
| 67 | + * @param data - The data to load (string URL, base 64 encoded string, number[], or any typed array like Float32Array) |
| 68 | + * @param type - The targeted TypedArray type (e.g., any typed array like Float32Array) |
| 69 | + * @returns A Promise resolving to the targeted TypedArray or null if input is invalid |
55 | 70 | */
|
56 |
| -export async function loadFloat32Data( |
57 |
| - data: string | number[] | Float32Array |
58 |
| -): Promise<Float32Array | null> { |
| 71 | +export async function loadDataArray<T extends TypedArray>( |
| 72 | + data: string | number[] | TypedArray, |
| 73 | + type: TConstructor<T> |
| 74 | +): Promise<T | null> { |
59 | 75 | if (!data) {
|
60 | 76 | return null;
|
61 | 77 | }
|
62 |
| - if (ArrayBuffer.isView(data)) { |
63 |
| - // Input data is typed array. |
64 |
| - return data; |
65 |
| - } else if (Array.isArray(data)) { |
66 |
| - // Input data is native javascript array. |
67 |
| - return new Float32Array(data); |
| 78 | + if (typeof data === "string") { |
| 79 | + const extension = data.split(".").pop()?.toLowerCase(); |
| 80 | + // Data is a file name with .json extension |
| 81 | + if (extension === "json") { |
| 82 | + const stringData = await load(data, JSONLoader); |
| 83 | + return new type(stringData); |
| 84 | + } |
| 85 | + // It is assumed that the data is a file containing raw array of bytes. |
| 86 | + const response = await safeFetch(data); |
| 87 | + if (!response.ok) { |
| 88 | + return null; |
| 89 | + } |
| 90 | + if (isPngData(response.headers)) { |
| 91 | + return await loadPngData(response, type); |
| 92 | + } |
| 93 | + |
| 94 | + const blob = await response.blob(); |
| 95 | + const buffer = await blob.arrayBuffer(); |
| 96 | + return new type(buffer); |
68 | 97 | } else {
|
69 |
| - // Input data is an URL. |
70 |
| - return await loadURLData(data); |
| 98 | + return toTypedArray(data, type); |
71 | 99 | }
|
| 100 | + return Promise.reject("loadDataArray: unsupported type of input data"); |
72 | 101 | }
|
73 | 102 |
|
74 | 103 | /**
|
|
0 commit comments