Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ddohler committed Sep 29, 2021
2 parents a67bb22 + 781e0cb commit da2373b
Show file tree
Hide file tree
Showing 15 changed files with 2,837 additions and 1,763 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
node-version: [10.x, 12.x]
node-version: [12.x, 14.x]

steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
## Upcoming release
## Upcoming

## 1.1.0 (2021-09-29)
- Improve documentation for integrating with build tools
- Add a reset() function that allows tearing down the Loam worker
- Allow initializing Loam from a CDN
- Add a demo page for local development

## 1.0.0 (2021-03-17)
- No changes from RC.2 except dependency updates
Expand Down
49 changes: 45 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,37 @@ npm install loam

Assuming you are using a build system, the main `loam` library should integrate into your build the same as any other library might. However, in order to correctly initialize the Emscripten environment for running GDAL, there are other assets that need to be accessible via HTTP request at runtime, but which should _not_ be included in the main application bundle. Specifically, these are:

- `loam-worker.min.js`: This is the "backend" of the library; it initializes the Web Worker and translates between the Loam "frontend" and GDAL.
- `loam-worker.js`: This is the "backend" of the library; it initializes the Web Worker and translates between the Loam "frontend" and GDAL.
- [`gdal.js`](https://www.npmjs.com/package/gdal-js): This initializes the Emscripten runtime and loads the GDAL WebAssembly.
- [`gdal.wasm`](https://www.npmjs.com/package/gdal-js): The GDAL binary, compiled to WebAssembly.
- [`gdal.data`](https://www.npmjs.com/package/gdal-js): Contains configuration files that GDAL expects to find on the host filesystem.

All of these files will be included in the `node_modules` folder after running `npm install loam`, but it is up to you to integrate them into your development environment and deployment processes.
All of these files will be included in the `node_modules` folder after running `npm install loam`, but it is up to you to integrate them into your development environment and deployment processes. Unfortunately, support for WebAssembly and Web Workers is still relatively young, so many build tools do not yet have a straightforward out-of-the-box solution that will work. However, in general, treating the four files above similarly to static assets (e.g. images, videos, or PDFs) tends to work fairly well. An example for Create React App is given below.

## Create React App
When integrating Loam with a React app that was initialized using Create React App, the simplest thing to do is probably to copy the assets above into [the `/public` folder](https://create-react-app.dev/docs/using-the-public-folder#adding-assets-outside-of-the-module-system), like so:

```
cp node_modules/gdal-js/gdal.* node_modules/loam/lib/loam-worker.js public/
```

This will cause the CRA build system to copy these files into the build folder untouched, where they can then be accessed by URL (e.g. `http://localhost:3000/gdal.wasm`).
However, this has the disadvantage that you will need to commit the copied files to source control, and they won't be updated if you update Loam. A way to work around this is to put symlinks in `/public` instead:

```
ln -s ../node_modules/loam/lib/loam-worker.js public/loam-worker.js
ln -s ../node_modules/gdal-js/gdal.wasm public/gdal.wasm
ln -s ../node_modules/gdal-js/gdal.data public/gdal.data
ln -s ../node_modules/gdal-js/gdal.js public/gdal.js
```

# API Documentation
## Basic usage

```javascript
import loam from "loam";

// Load WebAssembly and data files asynchronously. Will be called automatically by loam.open()
// but it is often helpful for responsiveness to pre-initialize because these files are fairly large. Returns a promise.
loam.initialize();
Expand All @@ -31,14 +51,15 @@ loam.open(blob).then((dataset) => {
```
## Functions
### `loam.initialize(pathPrefix)`
### `loam.initialize(pathPrefix, gdalPrefix)`
Manually set up web worker and initialize Emscripten runtime. This function is called automatically by other functions on `loam`. Returns a promise that is resolved when Loam is fully initialized.
Although this function is called automatically by other functions, such as `loam.open()`, it is often beneficial for user experience to manually call `loam.initialize()`, because it allows pre-fetching Loam's WebAssembly assets (which are several megabytes uncompressed) at a time when the latency required to download them will be least perceptible by the user. For example, `loam.initialize()` could be called when the user clicks a button to open a file-selection dialog, allowing the WebAssembly to load in the background while the user selects a file.
This function is safe to call multiple times.
#### Parameters
- `pathPrefix`: The path prefix that Loam should use when downloading its WebAssembly assets. If left undefined, Loam will make a best guess based on the source path of its own `<script>` element. If Loam fails to work properly and you see requests resulting in 404s for the `gdal.*` assets listed above, then you will need to set this parameter so that Loam requests the correct paths for its WebAssembly assets.
- `pathPrefix` (optional): The path or URL that Loam should use as a prefix when fetching its Web Worker. If left undefined, Loam will make a best guess based on the source path of its own `<script>` element. URLs with domains may be used to enable Loam to be loaded from CDNs like unpkg, but the file name should be left off.
- `gdalPrefix` (optional): The path or URL that Loam should use as a prefix when fetching WebAssembly assets for GDAL. If left undefined, Loam will use the same value as `pathPrefix`. URLs with domains may be used to enable loading from CDNs like unpkg, but the file name should be left off. If Loam fails to work properly and you see requests resulting in 404s or other errors for the `gdal.*` assets listed above, you will need to set `pathPrefix`, or this parameter, or both, to the correct locations where Loam can find those assets.
#### Return value
A promise that resolves when Loam is initialized. All of the functions described in this document wait for this promise's resolution when executing, so paying attention to whether this promise has resolved or not is not required. However, it may be helpful to do so in some circumstances, for example, if you want to display a visual indicator that your app is ready.
Expand Down Expand Up @@ -76,6 +97,17 @@ A promise that resolves with an array of transformed coordinate pairs.
<br />
### `loam.reset()`
Tear down Loam's internal Web Worker. This will cause initialize() to create a new Web Worker the next time it is called.
**Note**: This exists primarily to enable certain types of unit testing. It should not be necessary to call this function during normal usage of Loam. If you find that you are encountering a problem that loam.reset() solves, please [open an issue](https://github.com/azavea/loam/issues)
#### Parameters
- None
#### Return value
A promise that resolves when the Web Worker has been terminated. This function waits for initialize() to complete or fail before tearing down the worker.
<br />
### `GDALDataset.close()`
This used to be required in order to avoid memory leaks in earlier versions of Loam, but is currently a no-op. It has been maintained to preserve backwards compatibility, but has no effect other than to display a console warning.
#### Return value
Expand Down Expand Up @@ -159,6 +191,15 @@ After cloning,
Built assets are placed in `lib`.
## Demo page
There is a (very!) simple demo page available that utilizes Loam to print info about a GeoTIFF. To view it in a browser, run
`yarn demo`, and then navigate to http://localhost:8080/ . You can use this site for things like:
- Playing around with Loam by editing the source code in `demo/index.js`
- Validating changes that are difficult to test fully in CI
Editing Loam or the source in `demo/` should auto-reload.
# 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).
19 changes: 19 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Loam Test</title>
<script type="text/javascript" src="/loam.js"></script>
</head>
<body>
<form>
<input type="file" id="geotiff-file" />
</form>
<p>
Select a GeoTIFF using the Browse... button. Information about the file will be
displayed below.
</p>

<div id="gdalinfo"></div>
<script src="index.js"></script>
</body>
</html>
60 changes: 60 additions & 0 deletions demo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* global loam */

// Use the locally built version of loam, with a CDN copy of GDAL from unpkg.
loam.initialize('/', 'https://unpkg.com/[email protected]/');

const EPSG4326 =
'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]';

function displayInfo() {
const file = document.querySelector('#geotiff-file').files[0];
const displayElem = document.getElementById('gdalinfo');

// Clear display text
displayElem.innerText = '';
// Use Loam to get GeoTIFF metadata
loam.open(file).then((ds) => {
return Promise.all([ds.width(), ds.height(), ds.count(), ds.wkt(), ds.transform()]).then(
([width, height, count, wkt, geoTransform]) => {
displayElem.innerText +=
'Size: ' + width.toString() + ', ' + height.toString() + '\n';
displayElem.innerText += 'Band count: ' + count.toString() + '\n';
displayElem.innerText += 'Coordinate system:\n' + wkt + '\n';

const cornersPx = [
[0, 0],
[width, 0],
[width, height],
[0, height],
];
const cornersGeo = cornersPx.map(([x, y]) => {
return [
// http://www.gdal.org/gdal_datamodel.html
geoTransform[0] + geoTransform[1] * x + geoTransform[2] * y,
geoTransform[3] + geoTransform[4] * x + geoTransform[5] * y,
];
});

loam.reproject(wkt, EPSG4326, cornersGeo).then((cornersLngLat) => {
displayElem.innerText += 'Corner Coordinates:\n';
cornersLngLat.forEach(([lng, lat], i) => {
displayElem.innerText +=
'(' +
cornersGeo[i][0].toString() +
', ' +
cornersGeo[i][1].toString() +
') (' +
lng.toString() +
', ' +
lat.toString() +
')\n';
});
});
}
);
});
}

document.getElementById('geotiff-file').onchange = function () {
displayInfo();
};
116 changes: 60 additions & 56 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,58 +1,62 @@
{
"name": "loam",
"version": "1.0.0",
"description": "Javascript wrapper for GDAL in the browser",
"main": "lib/loam.js",
"scripts": {
"build": "webpack --env dev && webpack --env build",
"dev": "webpack --progress --colors --watch --env dev",
"test": "karma start --single-run --browser ChromeHeadless karma.conf.js",
"test:watch": "karma start --auto-watch --browser ChromeHeadless karma.conf.js",
"test:ci": "webpack --env dev && webpack --env build && karma start --single-run --browser ChromeHeadless karma.conf.js"
},
"repository": {
"type": "git",
"url": "https://github.com/azavea/loam.git"
},
"keywords": [
"gdal",
"emscripten",
"geospatial",
"raster",
"geotiff"
],
"author": "Derek Dohler",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/azavea/loam/issues"
},
"files": [
"lib/"
],
"homepage": "https://github.com/azavea/loam",
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-eslint": "^10.0.0",
"babel-loader": "^8.0.0",
"babel-plugin-add-module-exports": "^1.0.2",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"eslint": "^4.13.1",
"eslint-loader": "^1.9.0",
"karma": "^4.0.0",
"karma-babel-preprocessor": "^8.0.0",
"karma-chai": "^0.1.0",
"karma-chai-as-promised": "^0.1.2",
"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"
},
"dependencies": {
"gdal-js": "2.0.0"
}
"name": "loam",
"version": "1.1.0",
"description": "Javascript wrapper for GDAL in the browser",
"main": "lib/loam.js",
"scripts": {
"build": "webpack --config=webpack.prod.js",
"dev": "webpack --progress --color --watch --config=webpack.dev.js",
"demo": "webpack serve --config=webpack.dev.js",
"test": "karma start --single-run --browser ChromeHeadless karma.conf.js",
"test:watch": "karma start --auto-watch --browser ChromeHeadless karma.conf.js",
"test:ci": "webpack --config=webpack.dev.js && webpack --config=webpack.prod.js && karma start --single-run --browser ChromeHeadless karma.conf.js"
},
"repository": {
"type": "git",
"url": "https://github.com/azavea/loam.git"
},
"keywords": [
"gdal",
"emscripten",
"geospatial",
"raster",
"geotiff"
],
"author": "Derek Dohler",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/azavea/loam/issues"
},
"files": [
"lib/"
],
"homepage": "https://github.com/azavea/loam",
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-eslint": "^10.0.0",
"babel-loader": "^8.0.0",
"babel-plugin-add-module-exports": "^1.0.2",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"eslint": "^7.31.0",
"eslint-webpack-plugin": "^3.0.1",
"karma": "^4.0.0",
"karma-babel-preprocessor": "^8.0.0",
"karma-chai": "^0.1.0",
"karma-chai-as-promised": "^0.1.2",
"karma-chrome-launcher": "^3.1.0",
"karma-mocha": "^1.3.0",
"mocha": "^6.0.0",
"prettier": "^2.1.2",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^5.35.1",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^4.0.0-rc.0",
"yargs": "^14.0.0"
},
"dependencies": {
"gdal-js": "2.0.0"
}
}
12 changes: 8 additions & 4 deletions src/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initWorker, runOnWorker } from './workerCommunication.js';
import { initWorker, clearWorker, runOnWorker } from './workerCommunication.js';
import { GDALDataset } from './gdalDataset.js';

function open(file) {
Expand Down Expand Up @@ -33,8 +33,12 @@ function reproject(fromCRS, toCRS, coords) {
return runOnWorker('LoamReproject', [fromCRS, toCRS, xCoords, yCoords]);
}

function initialize(pathPrefix) {
return initWorker(pathPrefix);
function initialize(loamPrefix, gdalPrefix) {
return initWorker(loamPrefix, gdalPrefix);
}

export { open, rasterize, initialize, reproject };
function reset() {
return clearWorker();
}

export { open, rasterize, initialize, reset, reproject };
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { open, rasterize, initialize, reproject } from './api.js';
import { open, rasterize, initialize, reset, reproject } from './api.js';
import { GDALDataset } from './gdalDataset.js';

export default { open, rasterize, GDALDataset, initialize, reproject };
export default { open, rasterize, GDALDataset, initialize, reset, reproject };
Loading

0 comments on commit da2373b

Please sign in to comment.