ogr2ogr
Version:
ogr2ogr wrapper w/ multiple format support
156 lines (155 loc) • 4.31 kB
JavaScript
// index.ts
import archiver from "archiver";
import { execFile } from "child_process";
import { createReadStream } from "fs";
import { tmpdir } from "os";
import { extname, join } from "path";
import { Readable } from "stream";
var stdoutRe = /csv|geojson|georss|gml|gmt|gpx|jml|kml|mapml|pdf|vdv/i;
var vsiStdIn = "/vsistdin/";
var vsiStdOut = "/vsistdout/";
var uniq = Date.now();
var Ogr2ogr = class {
inputStream;
inputPath;
outputPath;
outputFormat;
outputExt;
customCommand;
customOptions;
customDestination;
customEnv;
timeout;
maxBuffer;
skipFailures;
constructor(input, opts = {}) {
this.inputPath = opts.inputFormat ? opts.inputFormat + ":" + vsiStdIn : vsiStdIn;
this.outputFormat = opts.format ?? "GeoJSON";
this.customCommand = opts.command;
this.customOptions = opts.options;
this.customDestination = opts.destination;
this.customEnv = opts.env;
this.timeout = opts.timeout ?? 0;
this.maxBuffer = opts.maxBuffer ?? 1024 * 1024 * 50;
this.skipFailures = opts.skipFailures ?? true;
let { path, ext } = this.newOutputPath(this.outputFormat);
this.outputPath = path;
this.outputExt = ext;
if (input instanceof Readable) {
this.inputStream = input;
} else if (typeof input === "string") {
this.inputPath = this.newInputPath(input);
} else {
this.inputStream = Readable.from([JSON.stringify(input)]);
}
}
exec(cb) {
this.run().then((res) => cb(null, res)).catch((err) => cb(err));
}
then(onfulfilled, onrejected) {
return this.run().then(onfulfilled, onrejected);
}
newInputPath(p) {
let path = "";
let ext = extname(p);
switch (ext) {
case ".zip":
path = "/vsizip/";
break;
case ".gz":
path = "/vsigzip/";
break;
case ".tar":
path = "/vsitar/";
break;
}
if (/^(http|ftp)/.test(p)) {
path += "/vsicurl/" + p;
return path;
}
path += p;
return path;
}
newOutputPath(f) {
let ext = "." + f.toLowerCase();
if (stdoutRe.test(this.outputFormat)) {
return { path: vsiStdOut, ext };
}
let path = join(tmpdir(), "/ogr_" + uniq++);
switch (f.toLowerCase()) {
case "esri shapefile":
path += ".shz";
ext = ".shz";
break;
case "mapinfo file":
case "flatgeobuf":
ext = ".zip";
break;
default:
path += ext;
}
return { path, ext };
}
createZipStream(p) {
let archive = archiver("zip");
archive.directory(p, false);
archive.on("error", console.error);
archive.finalize();
return archive;
}
async run() {
let command = this.customCommand ?? "ogr2ogr";
let args = ["-f", this.outputFormat];
if (this.skipFailures) args.push("-skipfailures");
args.push(this.customDestination || this.outputPath, this.inputPath);
if (this.customOptions) args.push(...this.customOptions);
let env = this.customEnv ? { ...process.env, ...this.customEnv } : void 0;
let { stdout, stderr } = await new Promise((res2, rej) => {
let proc = execFile(
command,
args,
{ env, timeout: this.timeout, maxBuffer: this.maxBuffer },
(err, stdout2, stderr2) => {
if (err) rej(err);
res2({ stdout: stdout2, stderr: stderr2 });
}
);
if (this.inputStream && proc.stdin) this.inputStream.pipe(proc.stdin);
});
let res = {
cmd: [command, ...args].join(" "),
text: stdout,
details: stderr,
extname: this.outputExt
};
if (/^geojson$/i.test(this.outputFormat)) {
try {
res.data = JSON.parse(stdout);
} catch (_err) {
}
}
if (!this.customDestination && this.outputPath !== vsiStdOut) {
if (this.outputExt === ".zip") {
res.stream = this.createZipStream(this.outputPath);
} else {
res.stream = createReadStream(this.outputPath);
}
}
return res;
}
};
function ogr2ogr(input, opts) {
return new Ogr2ogr(input, opts);
}
ogr2ogr.version = async () => {
let vers = await new Promise((res, rej) => {
execFile("ogr2ogr", ["--version"], {}, (err, stdout) => {
if (err) rej(err);
res(stdout);
});
});
return vers.trim();
};
export {
ogr2ogr
};