Skip to content

Commit

Permalink
Add example to display tiles of a GeoTiff
Browse files Browse the repository at this point in the history
  • Loading branch information
ddohler committed Jan 23, 2018
1 parent a2977fa commit 08c980c
Show file tree
Hide file tree
Showing 9 changed files with 635 additions and 2 deletions.
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ EMMAKE ?= emmake
EMCC ?= emcc
EMCONFIGURE ?= emconfigure
EMCONFIGURE_JS ?= 0
GDAL_EMCC_CFLAGS := -msse -Oz
PROJ_EMCC_CFLAGS := -msse -Oz
GDAL_EMCC_CFLAGS := -msse -O3
PROJ_EMCC_CFLAGS := -msse -O3
EXPORTED_FUNCTIONS = "[\
'_CSLCount',\
'_GDALSetCacheMax',\
Expand All @@ -20,21 +20,32 @@ EXPORTED_FUNCTIONS = "[\
'_GDALGetRasterCount',\
'_GDALGetRasterDataType',\
'_GDALGetRasterBand',\
'_GDALGetRasterStatistics',\
'_GDALGetRasterMinimum',\
'_GDALGetRasterMaximum',\
'_GDALGetRasterNoDataValue',\
'_GDALGetProjectionRef',\
'_GDALSetProjection',\
'_GDALGetGeoTransform',\
'_GDALSetGeoTransform',\
'_OSRNewSpatialReference',\
'_OSRDestroySpatialReference',\
'_OSRImportFromEPSG',\
'_OCTNewCoordinateTransformation',\
'_OCTDestroyCoordinateTransformation',\
'_OCTTransform',\
'_GDALCreateGenImgProjTransformer',\
'_GDALDestroyGenImgProjTransformer',\
'_GDALGenImgProjTransform',\
'_GDALDestroyGenImgProjTransformer',\
'_GDALSuggestedWarpOutput',\
'_GDALTranslate',\
'_GDALTranslateOptionsNew',\
'_GDALTranslateOptionsFree',\
'_GDALWarpAppOptionsNew',\
'_GDALWarpAppOptionsSetProgress',\
'_GDALWarpAppOptionsFree',\
'_GDALWarp',\
'_GDALReprojectImage'\
]"

Expand All @@ -53,6 +64,8 @@ gdal.js: $(GDAL)/libgdal.a
EMCC_CFLAGS="$(GDAL_EMCC_CFLAGS)" $(EMCC) $(GDAL)/libgdal.a $(PROJ4)/src/.libs/libproj.a -o gdal.js \
-s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) \
-s TOTAL_MEMORY=256MB \
-s WASM=1 \
-s NO_EXIT_RUNTIME=1 \
-s RESERVED_FUNCTION_POINTERS=1 \
--preload-file $(GDAL)/data/pcs.csv@/usr/local/share/gdal/pcs.csv \
--preload-file $(GDAL)/data/gcs.csv@/usr/local/share/gdal/gcs.csv \
Expand Down
13 changes: 13 additions & 0 deletions examples/tile_tiff/README
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
generate a thumbnail from a GeoTIFF using GDALTranslate and then display it on
a Leaflet map.

To use, first make sure that `gdal.js`, `gdal.js.mem`, and `gdal.data` 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.
1 change: 1 addition & 0 deletions examples/tile_tiff/gdal.data
21 changes: 21 additions & 0 deletions examples/tile_tiff/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
crossorigin=""/>
<title>Test GDAL.js</title>
</head>
<body>
<input type="file" id="geotiff-select">
<button onclick="openFile()">Display</button>
<p>Select a GeoTIFF using the Browse... button.
Click on the "Thumbnail" button and a thumbnail of the image will be
displayed on the map below.</p>

<div id="leaflet" style="height: 600px"></div>
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"
integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
crossorigin=""></script>
<script src="index.js"></script>
</body>
</html>
94 changes: 94 additions & 0 deletions examples/tile_tiff/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// TODO: This is abusing closures; refactor and make variable scoping nicer.

var tiler = new Worker('worker.js');

var map = L.map('leaflet').setView([0,0],3);

var tileCallbacks = {};

// Calculated min/max/nodata for the file, used for each tile request
var fileStats;

L.GridLayer.WorkerTiles = L.GridLayer.extend({
createTile: function(coords, done) {
var uLPix = {
x: coords.x * 256, // In real life, "this.getTileSize()"
y: coords.y * 256
};
var lRPix = {
x: uLPix.x + 256,
y: uLPix.y + 256
}; // Ditto

var map = this._map; // TODO: Don't rely on Leaflet internals
var uLGeo = map.unproject(uLPix, coords.z);
var lRGeo = map.unproject(lRPix, coords.z);

var tile = document.createElement('img');
tiler.postMessage({ tile: {
upperLeft: uLGeo,
lowerRight: lRGeo,
coords: coords,
stats: fileStats
}});
var callback = function(bytes) {
// This doesn't really seem to make a difference, but it's quicker.
// TODO: Make empty tiles not show up as broken images
if (bytes.length === 0) {
done(null, null);
} else {
var outputBlob = new Blob([bytes], { type: 'image/png' });
var imageURL = window.URL.createObjectURL(outputBlob);
tile.src = imageURL;
done(null, tile); // done(error, tile);
}
}

var callbackKey = coords.x.toString() + ',' + coords.y.toString() + ',' + coords.z.toString();
tileCallbacks[callbackKey] = callback;
return tile;
}
});

L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
maxZoom: 18,
}).addTo(map);

var tiffTiles;

function openFile() {
var files = document.querySelector('#geotiff-select').files;
tiler.postMessage({files: files});
}

tiler.onmessage = function(evt) {
if (evt.data.tile) {
var tileReq = evt.data.tile.request;
var callbackKey = (
tileReq.coords.x.toString() + ',' +
tileReq.coords.y.toString() + ',' +
tileReq.coords.z.toString()
);
tileCallbacks[callbackKey](evt.data.tile.bytes);
delete tileCallbacks[callbackKey];
} else if (evt.data.success) {
if (tiffTiles) {
tiffTiles.remove();
}
tiffTiles = new L.GridLayer.WorkerTiles();
var lats = Array.from(evt.data.bounds[1]);
var lngs = Array.from(evt.data.bounds[0]);

// TODO: Remove globals
fileStats = evt.data.stats;
// Zip
var latLngs = lats.map(function(lat, i, arr) {
return new Array(lat, lngs[i]);
});
map.fitBounds(latLngs);
tiffTiles.addTo(map);
} else {
console.log(evt);
}
};
Loading

0 comments on commit 08c980c

Please sign in to comment.