Skip to content

Commit

Permalink
Merge pull request #56 from azavea/release/1.0.0-rc.2
Browse files Browse the repository at this point in the history
Release/1.0.0 rc.2
  • Loading branch information
ddohler authored Oct 30, 2020
2 parents 81323a6 + 88b6da5 commit 6d02f10
Show file tree
Hide file tree
Showing 27 changed files with 722 additions and 338 deletions.
6 changes: 3 additions & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"block-scoped-var": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": [2, { "properties": "always" }],
"comma-dangle": [2, "never"],
"comma-dangle": [2, "always-multiline"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,
Expand All @@ -51,7 +51,7 @@
"keyword-spacing": [2, {"before": true, "after": true}],
"linebreak-style": 0,
"max-depth": 0,
"max-len": [2, 120, 4],
"max-len": [2, 100, 4],
"max-nested-callbacks": 0,
"max-params": 0,
"max-statements": 0,
Expand Down Expand Up @@ -155,7 +155,7 @@
"operator-linebreak": [2, "after"],
"padded-blocks": 0,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"quotes": [2, "single", {"allowTemplateLiterals": true, "avoidEscape": true}],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": 0,
Expand Down
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"printWidth": 100,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
}
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## Upcoming release

## 1.0.0-rc.2 (2020-10-30)
- Add information on contributing to the README
- Apply code auto-formatting
- Improve error messages with originating function names
- Add GDALDataset.render() to provide gdaldem functionality

## 1.0.0-rc.1 (2020-07-24)
- Add loam.rasterize() wrapper for GDALRasterize()
- Add pathPrefix parameter to loam.initialize()
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,27 @@ Image reprojection and warping utility. This is the equivalent of the [gdalwarp]
#### Return value
A promise that resolves to a new `GDALDataset`.
<br />
### `GDALDataset.render(mode, args, colors)`
Utility for rendering and computing DEM metrics. This is the equivalent of the [gdaldem](https://gdal.org/programs/gdaldem.html) command.
**Note**: This returns a new `GDALDataset` object but does not perform any immediate calculation. Instead, calls to `.render()` are evaluated lazily (as with `convert()` and `warp()`, above). The render operation is only evaluated when necessary in order to access some property of the dataset, such as its size, bytes, or band count. Successive calls to `.warp()` and `.convert()` can be lazily chained onto datasets produced by `.render()`, and vice-versa.
#### Parameters
- `mode`: One of ['hillshade', 'slope','aspect', 'color-relief', 'TRI', 'TPI', 'roughness']. See the [`gdaldem documentation`](https://gdal.org/programs/gdaldem.html#cmdoption-arg-mode) for an explanation of the function of each mode.
- `args`: An array of strings, each representing a single [command-line argument](https://gdal.org/programs/gdaldem.html#synopsis) accepted by the `gdaldem` command. The `inputdem` and `output_xxx_map` parameters should be omitted; these are handled by `GDALDataset`. Example: `ds.render('hillshade', ['-of', 'PNG'])`
- `colors`: If (and only if) `mode` is equal to 'color-relief', an array of strings representing lines in [the color text file](https://gdal.org/programs/gdaldem.html#color-relief). Example: `ds.render('color-relief', ['-of', 'PNG'], ['993.0 255 0 0'])`. See the [`gdaldem documentation`](https://gdal.org/programs/gdaldem.html#cmdoption-arg-color_text_file) for an explanation of the text file syntax.
#### Return value
A promise that resolves to a new `GDALDataset`.
# Developing
After cloning,
1. `yarn install`
2. `yarn dev` and in another session `yarn test:watch`
Built assets are placed in `lib`.
# Contributing
Contributions are welcomed! Please feel free to work on any of the open issues or open an issue describing the changes you'd like to make. All contributions will be licensed under the Apache License, as per the [GitHub Terms of Service](https://docs.github.com/en/github/site-policy/github-terms-of-service#6-contributions-under-repository-license).
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "loam",
"version": "1.0.0-rc.1",
"version": "1.0.0-rc.2",
"description": "Javascript wrapper for GDAL in the browser",
"main": "lib/loam.js",
"scripts": {
Expand Down Expand Up @@ -48,6 +48,7 @@
"karma-chrome-launcher": "^3.1.0",
"karma-mocha": "^1.3.0",
"mocha": "^6.0.0",
"prettier": "^2.1.2",
"webpack": "^3.10.0",
"yargs": "^14.0.0"
},
Expand Down
21 changes: 16 additions & 5 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,32 @@ import { GDALDataset } from './gdalDataset.js';

function open(file) {
return new Promise((resolve, reject) => {
const ds = new GDALDataset({func: 'GDALOpen', src: file, args: []});
const ds = new GDALDataset({ func: 'GDALOpen', src: file, args: [] });

return ds.open().then(() => resolve(ds), (reason) => reject(reason));
return ds.open().then(
() => resolve(ds),
(reason) => reject(reason)
);
});
}

function rasterize(geojson, args) {
return new Promise((resolve, reject) => {
resolve(new GDALDataset({func: 'GDALRasterize', src: geojson, args: args}));
resolve(new GDALDataset({ func: 'GDALRasterize', src: geojson, args: args }));
});
}

function reproject(fromCRS, toCRS, coords) {
var xCoords = new Float64Array(coords.map(function (pair) { return pair[0]; }));
var yCoords = new Float64Array(coords.map(function (pair) { return pair[1]; }));
var xCoords = new Float64Array(
coords.map(function (pair) {
return pair[0];
})
);
var yCoords = new Float64Array(
coords.map(function (pair) {
return pair[1];
})
);

return runOnWorker('LoamReproject', [fromCRS, toCRS, xCoords, yCoords]);
}
Expand Down
35 changes: 32 additions & 3 deletions src/gdalDataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,48 @@ export class GDALDataset {

convert(args) {
return new Promise((resolve, reject) => {
resolve(new GDALDataset(this.source, this.operations.concat(new DatasetOperation('GDALTranslate', args))));
resolve(
new GDALDataset(
this.source,
this.operations.concat(new DatasetOperation('GDALTranslate', args))
)
);
});
}

warp(args) {
return new Promise((resolve, reject) => {
resolve(new GDALDataset(this.source, this.operations.concat(new DatasetOperation('GDALWarp', args))));
resolve(
new GDALDataset(
this.source,
this.operations.concat(new DatasetOperation('GDALWarp', args))
)
);
});
}

render(mode, args, colors) {
return new Promise((resolve, reject) => {
// DEMProcessing requires an auxiliary color definition file in some cases, so the API
// can't be easily represented as an array of strings. This packs the user-friendly
// interface of render() into an array that the worker communication machinery can
// easily make use of. It'll get unpacked inside the worker. Yet another reason to use
// something like comlink (#49)
const cliOrderArgs = [mode, colors].concat(args);

resolve(
new GDALDataset(
this.source,
this.operations.concat(new DatasetOperation('GDALDEMProcessing', cliOrderArgs))
)
);
});
}

close() {
return new Promise((resolve, reject) => {
const warningMsg = 'It is not necessary to call close() on a Loam dataset. This is a no-op';
const warningMsg =
'It is not necessary to call close() on a Loam dataset. This is a no-op';

console.warn(warningMsg);
resolve([]);
Expand Down
2 changes: 1 addition & 1 deletion src/guessFileExtension.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export default function guessFileExtension(args) {
const supportedFormats = {
PNG: 'png',
JPEG: 'jpg',
GTiff: 'tif'
GTiff: 'tif',
};

// Match GDAL 2.1 behavior: if output format is unspecified, the output format is GeoTiff
Expand Down
61 changes: 61 additions & 0 deletions src/stringParamAllocator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { isArrayAllStrings } from './validation.js';

/* global Module */
export default class ParamParser {
constructor(args) {
let self = this;

if (!isArrayAllStrings(args)) {
throw new Error('All items in the argument list must be strings');
}

self.args = args;
}

allocate() {
const self = this;

// So first, we need to allocate Emscripten heap space sufficient to store each string as a
// null-terminated C string.
// Because the C function signature is char **, this array of pointers is going to need to
// get copied into Emscripten heap space eventually, so we're going to prepare by storing
// the pointers as a typed array so that we can more easily copy it into heap space later.
let argPtrsArray = Uint32Array.from(
self.args
.map((argStr) => {
// +1 for the null terminator byte
return Module._malloc(Module.lengthBytesUTF8(argStr) + 1);
})
.concat([0])
);
// ^ In addition to each individual argument being null-terminated, the GDAL docs specify
// that GDALTranslateOptionsNew takes its options passed in as a null-terminated array of
// pointers, so we have to add on a null (0) byte at the end.

// Next, we need to write each string from the JS string array into the Emscripten heap
// space we've allocated for it.
self.args.forEach(function (argStr, i) {
Module.stringToUTF8(argStr, argPtrsArray[i], Module.lengthBytesUTF8(argStr) + 1);
});

// Now, as mentioned above, we also need to copy the pointer array itself into heap space.
let argPtrsArrayPtr = Module._malloc(argPtrsArray.length * argPtrsArray.BYTES_PER_ELEMENT);

Module.HEAPU32.set(argPtrsArray, argPtrsArrayPtr / argPtrsArray.BYTES_PER_ELEMENT);

self.argPtrsArray = argPtrsArray;
self.argPtrsArrayPtr = argPtrsArrayPtr;
}

deallocate() {
const self = this;

Module._free(self.argPtrsArrayPtr);
// Don't try to free the null terminator byte
self.argPtrsArray
.subarray(0, self.argPtrsArray.length - 1)
.forEach((ptr) => Module._free(ptr));
delete self.argPtrsArray;
delete self.argPtrsArrayPtr;
}
}
2 changes: 1 addition & 1 deletion src/validation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function isArrayAllStrings(args) {
return args.every(arg => typeof arg === 'string');
return args.every((arg) => typeof arg === 'string');
}

export { isArrayAllStrings };
Loading

0 comments on commit 6d02f10

Please sign in to comment.