gdal3.js
Version:
gdal3.js is a port of Gdal applications (**gdal_translate**, **ogr2ogr**, **gdal_rasterize**, **gdalwarp**, **gdaltransform**) to Webassembly. It allows you to convert raster and vector geospatial data to various formats and coordinate systems.
151 lines (139 loc) • 6.79 kB
JavaScript
/* eslint-disable no-continue */
import { GDALFunctions } from '../../allCFunctions';
import { getGdalError } from '../helper/error';
import { INPUTPATH, OUTPUTPATH } from '../helper/const';
import { mount } from '../helper/filesystem';
import { clearOptions, getOptions } from '../helper/options';
import gdalinfo from '../application/gdalinfo';
import ogrinfo from '../application/ogrinfo';
/**
* Opens files selected with HTML input element.
*
* @module f/open
* @async
* @param {FileList|File|Array<string>|string} files Returned by the files property of the HTML input element.
* @param {Array<string>} openOptions Open options passed to candidate drivers.
* @param {Array<string>} VFSHandlers List of Virtual File System handlers, see https://gdal.org/user/virtual_file_systems.html
* @return {Promise<TypeDefs.DatasetList>} "Promise" returns dataset list and error list.
* @example
* // Opening file from file input.
* // HTML
* <input class="input-file" type="file" name="file" id="file" onChange="onFileChange" />
* // JS
* function onFileChange({ target }) {
* const result = await Gdal.open(target.file);
* }
* @example
* // Opening files from file input. (multiple)
* // HTML
* <input class="input-file" type="file" name="files[]" id="file" onChange="onFileChange" multiple />
* // JS
* function onFileChange({ target }) {
* const result = await Gdal.open(target.files);
* }
* @example
* // Opening a file from the network.
* const fileData = await fetch('test/polygon.geojson');
* const file = new File([await fileData.blob()], "polygon.geojson");
* const result = await Gdal.open(file);
* @example
* // Opening a file using the virtual file system handler, ie. /vsicurl/ or /vsizip/.
* // One common scenario is a .zip shapefile
* const result = await Gdal.open(file, [], ['vsizip']);
* @example
* // Opening a file from filesystem on Node.js.
* const result = await Gdal.open('test/polygon.geojson');
* @example
* // Opening a file from filesystem on Node.js with open options.
* const result = await Gdal.open('test/points.csv', ['X_POSSIBLE_NAMES=lng', 'Y_POSSIBLE_NAMES=lat']);
* @example
* // Opening files from filesystem on Node.js.
* const result = await Gdal.open(['test/polygon.geojson', 'test/line.geojson']);
* @example
* // Opening files from virtual gdal3.js path.
* // * Opened files are saved in the /input/... virtual path.
* // * Converted files are saved in the /output/... virtual path.
* const result = await Gdal.open('/output/polygon.geojson');
* const result2 = await Gdal.open('/input/polygon.geojson');
*
*/
export default function open(fileOrFiles, openOptions = [], VFSHandlers = []) {
let files = fileOrFiles;
const optStr = getOptions(openOptions);
if (!(Array.isArray(files) || (typeof FileList === 'function' && files instanceof FileList))) {
files = [files];
}
return new Promise((resolve, reject) => {
const internalFiles = [];
const externalFiles = [];
[...files].forEach((file) => {
if ((typeof file === 'string' || file instanceof String) && (
file.substring(0, INPUTPATH.length + 1) === `${INPUTPATH}/` || file.substring(0, OUTPUTPATH.length + 1) === `${OUTPUTPATH}/`
)) {
internalFiles.push({ name: file.substring(file.indexOf('/', 1) + 1), internal: true, prefix: file.substring(0, file.indexOf('/', 1)) });
} else {
externalFiles.push(file);
}
});
mount(externalFiles).then((mountedExternalFiles) => {
const mountedFiles = [...mountedExternalFiles, ...internalFiles];
const errors = [];
GDALFunctions.CPLErrorReset();
const inputResults = {};
const promises = [];
for (let i = 0; i < mountedFiles.length; i += 1) {
const path = mountedFiles[i].name;
const name = path.split('.', 1)[0];
if (!inputResults[name]) inputResults[name] = {};
if (inputResults[name].pointer) continue;
inputResults[name].path = path;
const vfsHandlerStr = VFSHandlers && VFSHandlers.length ? `/${VFSHandlers.join('/')}/` : '';
let fileFullPath = `${vfsHandlerStr}${INPUTPATH}/${path}`;
if (mountedFiles[i].internal) fileFullPath = `${vfsHandlerStr}${mountedFiles[i].prefix}/${path}`;
const datasetPtr = GDALFunctions.GDALOpenEx(fileFullPath, null, null, optStr.ptr, null);
if (datasetPtr === 0) {
const error = getGdalError();
errors.push(error);
delete inputResults[name];
continue;
}
inputResults[name].pointer = datasetPtr;
const setLegacyType = () => {
const bandCount = GDALFunctions.GDALGetRasterCount(datasetPtr);
const layerCount = GDALFunctions.GDALDatasetGetLayerCount(datasetPtr);
if (bandCount > 0 && layerCount === 0) {
inputResults[name].type = 'raster';
} else {
inputResults[name].type = 'vector';
}
};
const infoPromise = gdalinfo(inputResults[name]).then((info) => {
if (info && info.bands) {
const hasSize = info.size && info.size.length >= 2 && (info.size[0] > 0 || info.size[1] > 0);
inputResults[name].type = info.bands.length > 0 && hasSize ? 'raster' : 'vector';
if (inputResults[name].type === 'vector') {
return ogrinfo(inputResults[name]).then((vectorInfo) => {
inputResults[name].info = vectorInfo;
});
}
inputResults[name].info = info;
} else {
setLegacyType();
}
return true;
}).catch(() => setLegacyType());
promises.push(infoPromise);
}
clearOptions(optStr);
Promise.allSettled(promises).then(() => {
const datasets = Object.values(inputResults);
// unmount();
if (datasets.length > 0 || errors.length === 0) {
resolve({ datasets, errors });
} else {
reject(errors);
}
});
});
});
}