forked from OSGeo/gdal
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Export functions for inspecting GeoTIFFs
Also adds an example for using gdal.js with WebWorkers.
- Loading branch information
Showing
9 changed files
with
203 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
*.swp | ||
*.mem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
This shows an example of how to use the GDAL API from within a web browser to | ||
inspect certain aspects of a GeoTIFF. It roughly mirrors the functionality | ||
found in http://www.gdal.org/gdal_tutorial.html. | ||
|
||
To use, first make sure that `gdal.js` and `gdal.js.mem` are available in this | ||
directory. There are some symlinks provided that will do this automatically if | ||
you build the project from source. Alternatively, you can | ||
[download a release](https://github.com/ddohler/gdal-js/releases) | ||
and place the files in this directory manually. | ||
|
||
Next, start up an HTTP server to serve this folder. For example, | ||
`python -m SimpleHTTPServer`. Navigate to whatever port your server is listening | ||
at, and follow the instructions on the page. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../gdal.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../gdal.js.mem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<html> | ||
<head> | ||
<script src="index.js"></script> | ||
<title>Test GDAL.js</title> | ||
</head> | ||
<body> | ||
<input type="file" id="geotiff-select"> | ||
<button onclick="inspectFiles()">Inspect files</button> | ||
<p>Select a GeoTIFF using the Browse... button. Next, open the developer console. | ||
Click the "Inspect Files" button and you will see information about the file's | ||
bands, projection, and footprint written to the console.</p> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
var tiffInspector = new Worker('worker.js'); | ||
|
||
function inspectFiles() { | ||
var files = document.querySelector('#geotiff-select').files; | ||
tiffInspector.postMessage(files); | ||
} | ||
|
||
tiffInspector.onmessage = function(evt) { | ||
console.log(evt.data); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* Setup | ||
*/ | ||
var TIFFPATH = '/tiffs'; | ||
|
||
var initialized = false; | ||
|
||
var GDALOpen, | ||
GDALGetRasterCount, | ||
GDALGetRasterXSize, | ||
GDALGetRasterYSize, | ||
GDALGetProjectionRef, | ||
GDALGetGeoTransform; | ||
|
||
// Set up Module object for gdal.js to populate. Emscripten sets up its compiled | ||
// code to look for a Module object in the global scope. If found, it reads runtime | ||
// configuration from the existing object, and then further populates that object | ||
// with other helpful functionality (e.g. ccall() and cwrap(), which are used in | ||
// the onRuntimeInitialized callback, below). | ||
var Module = { | ||
'print': function(text) { console.log('stdout: ' + text); }, | ||
'printErr': function(text) { console.log('stderr: ' + text); }, | ||
// Optimized builds contain a .js.mem file which is loaded asynchronously; | ||
// this waits until that has finished before performing further setup. | ||
'onRuntimeInitialized': function() { | ||
// Initialize GDAL | ||
Module.ccall('GDALAllRegister', null, [], []); | ||
|
||
// Set up JS proxy functions | ||
// Note that JS Number types are used to represent pointers, which means that | ||
// any time we want to pass a pointer to an object, such as in GDALOpen, which in | ||
// C returns a pointer to a GDALDataset, we need to use 'number'. | ||
GDALOpen = Module.cwrap('GDALOpen', 'number', ['string']); | ||
GDALGetRasterCount = Module.cwrap('GDALGetRasterCount', 'number', ['number']); | ||
GDALGetRasterXSize = Module.cwrap('GDALGetRasterXSize', 'number', ['number']); | ||
GDALGetRasterYSize = Module.cwrap('GDALGetRasterYSize', 'number', ['number']); | ||
GDALGetProjectionRef = Module.cwrap('GDALGetProjectionRef', 'string', ['number']); | ||
// Returns an affine transform from geographic coordinate space to geographic coordinate space. | ||
// Applying this transform to (0,0), (0, maxY), (maxX, maxY), and (maxX, 0) gives us the raster's | ||
// georeferenced footprint. See http://www.gdal.org/gdal_datamodel.html | ||
GDALGetGeoTransform = Module.cwrap('GDALGetGeoTransform', 'number', ['number', 'number']); | ||
|
||
// Create a "directory" where user-selected files will be placed | ||
FS.mkdir(TIFFPATH); | ||
initialized = true; | ||
} | ||
}; | ||
|
||
// Load gdal.js. This will populate the Module object, and then call | ||
// Module.onRuntimeInitialized() when it is ready for user code to interact with it. | ||
importScripts('gdal.js'); | ||
|
||
/* | ||
* Logic | ||
*/ | ||
// Use GDAL functions to provide information about a list of files. | ||
// @param files a FileList object as returned by a file input's .files field | ||
function inspectTiff(files) { | ||
// Make GeoTiffs available to GDAL in the virtual filesystem that it lives inside | ||
FS.mount(WORKERFS, { | ||
files: files | ||
}, TIFFPATH); | ||
|
||
var results = []; | ||
for(var i = 0; i < files.length; i++) { | ||
// Create a GDAL Dataset | ||
var dataset = GDALOpen(TIFFPATH + '/' + files[i].name); | ||
var bandCount = GDALGetRasterCount(dataset); | ||
var maxX = GDALGetRasterXSize(dataset); | ||
var maxY = GDALGetRasterYSize(dataset); | ||
wktStr = GDALGetProjectionRef(dataset); | ||
// This is where things get a bit hairy; the C function follows a common C pattern where an array to | ||
// store the results is allocated and passed into the function, which populates the array with the | ||
// results. Emscripten supports passing arrays to functions, but it always creates a *copy* of the | ||
// array, which means that the original JS array remains unchanged, which isn't what we want in this | ||
// case. So first, we have to malloc an array inside the Emscripten heap with the correct size. In this | ||
// case that is 6 because the GDAL affine transform array has six elements. | ||
var byteOffset = Module._malloc(6 * Float64Array.BYTES_PER_ELEMENT); | ||
// byteOffset is now a pointer to the start of the double array in Emscripten heap space | ||
// GDALGetGeoTransform dumps 6 values into the passed double array. | ||
GDALGetGeoTransform(dataset, byteOffset); | ||
// Module.HEAPF64 provides a view into the Emscripten heap, as an array of doubles. Therefore, our byte offset | ||
// from _malloc needs to be converted into a double offset, so we divide it by the number of bytes per double, | ||
// and then get a subarray of those six elements off the Emscripten heap. | ||
var geoTransform = Module.HEAPF64.subarray( | ||
byteOffset/Float64Array.BYTES_PER_ELEMENT, | ||
byteOffset/Float64Array.BYTES_PER_ELEMENT + 6 | ||
); | ||
// Finally, we can apply the affine transform to convert from pixel coordinates into geographic coordinates | ||
// If you wanted to display these on a map, you'd further need to transform to lat/lon, since these | ||
// are in the raster's CRS. | ||
var corners = [ | ||
[0, 0], | ||
[maxX, 0], | ||
[maxX, maxY], | ||
[0, maxY] | ||
]; | ||
var geoCorners = corners.map(function(coords) { | ||
var x = coords[0]; | ||
var y = coords[1]; | ||
return [ | ||
// http://www.gdal.org/gdal_datamodel.html | ||
geoTransform[0] + geoTransform[1]*x + geoTransform[2]*y, | ||
geoTransform[3] + geoTransform[4]*x + geoTransform[5]*y | ||
]; | ||
}); | ||
results.push({ | ||
bandCount: bandCount, | ||
xSize: maxX, | ||
ySize: maxY, | ||
projectionWkt: wktStr, | ||
coordinateTransform: geoTransform, | ||
corners: geoCorners | ||
}); | ||
} | ||
|
||
// Now pass this back to the calling thread, which is presumably where we'd want to handle it: | ||
postMessage(results); | ||
|
||
// And cleanup | ||
FS.unmount(TIFFPATH); | ||
} | ||
|
||
// Assume that all incoming messages are FileLists of GeoTiffs and inspect them. | ||
onmessage = function(msg) { | ||
if (!initialized) { | ||
console.log('Runtime not initialized yet, try again'); | ||
return; | ||
} | ||
inspectTiff(msg.data); | ||
} |