GeoTIFF for WebAssembly
v1.7.4WebAssemblyGeoTIFF 1.7.4 for browsers, Node.js and edge runtimes, precompiled for wasm32, single-threaded and multi-threaded as @crossbind/port-geotiff-wasm.
npm install @crossbind/port-geotiff-wasm@betaInstall
crossbind itself arrives with your bundler plugin, or with a new project from npm create crossbind@beta; Bundlers has Vite, Webpack, Rspack and Rollup.
Usage
Each example runs here, in this tab, and prints what the site build checked.
Each example also has a JavaScript only tab: the same task with no C++ file, calling GeoTIFF's own headers from @crossbind/port-geotiff directly. 4 of 5 work that way; the other says what stops it.
Find where a GeoTIFF is
The question most GeoTIFF code answers: GTIFNew reads the GeoKeys, GTIFGetDefn turns them into a coordinate system, GTIFImageToPCS places pixels on the map and GTIFProj4ToLatLong turns map coordinates into degrees.
libgeotiff 1.7.4, 10262 B: EPSG:32633 WGS 84 / UTM zone 33N, 100 x 100 pixels upper left 500000, 4650000 = 15.000000 E, 42.002015 N lower right 503000, 4647000 = 15.036210 E, 41.974990 N
JavaScript cannot write the file: TIFFSetField and GTIFKeySet are variadic and have no binding. It gives libgeotiff the same tags and keys as listgeo text instead, which GTIFImport reads through a callback (hence useWorker: false) into in-memory tags; the ST_TIFF that ST_Create returns is refused as the void * of GTIFNewSimpleTags, so a zeroed buffer stands in. geokey_t is bound without its keys and GTIFDefn without its fields, so a key crosses as { value: code } and the EPSG code is read from the key; no file size, image size (TIFFGetField is variadic too) or version (a macro) comes back. On a real file XTIFFOpen and GTIFNew replace the import and the rest runs unchanged, over the worker as well.
EPSG:32633 WGS 84 / UTM zone 33N, 100 x 100 pixels upper left 500000, 4650000 = 15.000000 E, 42.002015 N lower right 503000, 4647000 = 15.036210 E, 41.974990 N
Write a compressed GeoTIFF
Georeferencing is two TIFF tags and a few GeoKeys: TIFFTAG_GEOTIEPOINTS and TIFFTAG_GEOPIXELSCALE place the pixels, GTIFKeySet and GTIFWriteKeys name the coordinate system. GTIFPrint then prints the result the way listgeo does.
256 x 128 pixels: 32768 B, 23246 B as a Deflate GeoTIFF
Geotiff_Information:
Version: 1
Key_Revision: 1.0
Tagged_Information:
ModelTiepointTag (2,3):
0 0 0
-10 60 0
ModelPixelScaleTag (1,3):
0.15625 0.1953125 0
End_Of_Tags.
Keyed_Information:
GTModelTypeGeoKey (Short,1): ModelTypeGeographic
GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
GTCitationGeoKey (Ascii,25): "Europe, synthetic relief"
GeographicTypeGeoKey (Short,1): GCS_WGS_84
GeogAngularUnitsGeoKey (Short,1): Angular_Degree
End_Of_Keys.
End_Of_Geotiff.TIFFSetField for the image tags (size, samples, Deflate) and GTIFKeySet for the GeoKeys, and both are variadic: crossbind skips them and TIFFVSetField, and importing them fails the build with MISSING_EXPORT (""GTIFKeySet" is not exported by .../geotiff.h"). Without the image tags libtiff writes no pixels ("Must set "ImageWidth" before writing data."). The other direction does reach JavaScript: GTIFImport reads keys and georeferencing from the text listgeo prints, and GTIFWriteKeys and GTIFPrint are bound, which is how the other examples here get their keys in.Read the GeoKeys, the tiepoint and the pixel scale
GTIFDirectoryInfo counts the keys, GTIFKeyInfo and GTIFKeyGet read each one with its type, and GTIFValueNameEx names its value. The tiepoint and the pixel scale are TIFF tags, read with TIFFGetField.
GeoTIFF 1, key revision 1.0, 5 keys GTModelTypeGeoKey (Short): 2 = ModelTypeGeographic GTRasterTypeGeoKey (Short): 1 = RasterPixelIsArea GTCitationGeoKey (Ascii): "Europe" GeographicTypeGeoKey (Short): 4326 = GCS_WGS_84 GeogAngularUnitsGeoKey (Short): 9102 = Angular_Degree tiepoint: pixel 0, 0 is at -10, 60; pixel scale: 0.625 x 0.78125
The keys come in as listgeo text, as in the first example, and are read back one at a time the way the C++ reads them. Every key function takes a geokey_t, which is bound without its keys, so each key crosses as { value: code } with the code from GTIFKeyCode, and a tagtype_t the same way. The tiepoint and the pixel scale are TIFF tags that only the variadic TIFFGetField reads, so JavaScript asks GTIFImageToPCS where pixels (0, 0) and (1, 1) land and takes the difference.
GeoTIFF 1, key revision 1.0, 5 keys GTModelTypeGeoKey (Short): 2 = ModelTypeGeographic GTRasterTypeGeoKey (Short): 1 = RasterPixelIsArea GTCitationGeoKey (Ascii): "Europe" GeographicTypeGeoKey (Short): 4326 = GCS_WGS_84 GeogAngularUnitsGeoKey (Short): 9102 = Angular_Degree tiepoint: pixel 0, 0 is at -10, 60; pixel scale: 0.625 x 0.78125
Expand an EPSG code into a full definition
GTIFGetDefn normalises the GeoKeys through PROJ's EPSG database: from ProjectedCSTypeGeoKey alone it recovers the projection method and parameters, the datum, the ellipsoid and the unit. GTIFGetProj4Defn writes it as a PROJ string, with the scale factor rounded to six decimals.
ModelTypeProjected EPSG:27700 OSGB36 / British National Grid projection 19916 British National Grid, CT_TransverseMercator ProjNatOriginLatGeoKey 49 ProjNatOriginLongGeoKey -2 ProjScaleAtNatOriginGeoKey 0.9996012717 ProjFalseEastingGeoKey 400000 ProjFalseNorthingGeoKey -100000 geographic 4277 OSGB36, datum 6277 Ordnance Survey of Great Britain 1936 ellipsoid 7001 Airy 1830: 6377563.396 m, 6356256.909 m prime meridian 8901 Greenwich, unit 9001 metre (1 m) +proj=tmerc +lat_0=49.000000000 +lon_0=-2.000000000 +k=0.999601 +x_0=400000.000 +y_0=-100000.000 +a=6377563.396 +b=6356256.909 +units=m
GTIFGetDefn fills a GTIFDefn that crossbind binds without its fields, so JavaScript walks the same EPSG lookups itself: GTIFGetPCSInfo, GTIFGetProjTRFInfo, GTIFGetGCSInfo, GTIFGetDatumInfo, GTIFGetEllipsoidInfo, GTIFGetPMInfo and GTIFGetUOMLengthInfo, each with its answers in out-parameters. Two things stay inside the definition: which GeoKey each projection parameter belongs to, so the parameters are missing, and the method's CT_TransverseMercator name, so it prints as its EPSG code, 9807. The keys go in as listgeo text through a callback, as in the first example.
ModelTypeProjected EPSG:27700 OSGB36 / British National Grid projection 19916 British National Grid, EPSG method 9807 geographic 4277 OSGB36, datum 6277 Ordnance Survey of Great Britain 1936 ellipsoid 7001 Airy 1830: 6377563.396 m, 6356256.909 m prime meridian 8901 Greenwich, unit 9001 metre (1 m) +proj=tmerc +lat_0=49.000000000 +lon_0=-2.000000000 +k=0.999601 +x_0=400000.000 +y_0=-100000.000 +a=6377563.396 +b=6356256.909 +units=m
Convert between pixels and longitude, latitude
GTIFProj4FromLatLong and GTIFPCSToImage find the pixel under a longitude and latitude; GTIFImageToPCS and GTIFProj4ToLatLong go back from a pixel. The class keeps the file open between questions.
Galata Tower: 665970.23, 4543502.03 m, pixel 159.702, 164.980 centre of pixel 159, 164: 665950, 4543550 m, 28.973939, 41.026269
The same four conversions the C++ makes, on a definition GTIFGetDefn fills in C and JavaScript only passes on. The file itself cannot be written from JavaScript (TIFFSetField and GTIFKeySet are variadic and have no binding), so its tags and keys go in as the text listgeo prints, read by GTIFImport through a JavaScript callback, which is why the module runs on the page's thread.
Galata Tower: 665970.23, 4543502.03 m, pixel 159.702, 164.980 centre of pixel 159, 164: 665950, 4543550 m, 28.973939, 41.026269
What is different on WebAssembly
- In a browser the module runs in a Worker by default (
useWorker), so every call returns a promise:awaitcalls and constructors alike. - The module has its own filesystem:
m.FSwrites files,m.getFileBytesreads them back andm.autoMountFilesmountsFileobjects from an<input type=file>./memfslives in memory;/opfspersists across reloads and needs the Worker. See Filesystem. - In Node.js,
m.FSis the real disk, so use real paths there. - Multi-threaded builds (
runtime: 'mt') need COOP and COEP headers in production. See Threading.
Other platforms
- GeoTIFF overview: the apps, every platform's setup and the packages.
- GeoTIFF for Android: React Native apps on Android.
- GeoTIFF for iOS: React Native apps on iOS.
- GeoTIFF for WASI: command-line programs under wasmtime.
Facts on this page come from the port manifests in the repository and from what npm served on beta when the site was built. See the Libraries guide for the full consumer flow.