This article was translated, original article here.
Appetizer
Preface
Developers familiar with GIS work know that GDAL is the most powerful tool for GIS graphics processing. For native client and server-side developers, it's very friendly—providing executable and library files compatible with various CPUs and operating systems. As long as developers aren't using niche platforms, they can easily obtain the required resources. However, for GDAL, the web is a niche platform, and it offers no built-in support. Typically, WebGIS software deploys GDAL-related services on the server-side, with clients accessing them via HTTP requests. This approach has several drawbacks:
- Network requests increase application response time, making real-time applications impossible.
- Client computing power is underutilized, while server costs rise.
Consequently, when WebGIS utilizes GDAL-related features, users often experience sluggishness and lag.
Using Emscripten + WebAssembly makes integrating GDAL into web applications possible: GDAL is a C/C++ project, and Emscripten is a C/C++ compilation toolchain for WebAssembly. It may be feasible to compile GDAL with Emscripten into a form callable by JavaScript, thereby porting this tool into WebGIS applications.
Using Emscripten
For frontend developers accustomed to JavaScript, getting started with Emscripten itself can be challenging. Like many open-source projects, Emscripten starts with compiling from source. We won't extensively cover the installation process here; please refer to the Emscripten SDK (emsdk) documentation for details. If installation proves difficult or you prefer a quicker setup, you can also use the emsdk Docker image.
Once installed and verified, you can start experimenting with Emscripten compilation. Here's a simple example:
// hello_world.c
include <stdio.h>
int main() {
printf("hello, world!\n");
return 0;
}
Compile this code using the emcc
command:
./emcc hello_world.c
After a moment, two files will be generated:
a.out.wasm
is the WebAssembly file containing the compiled code, and a.out.js
is the glue code file that loads and interfaces with the WebAssembly code from JavaScript. Execute it using Node.js
:
node a.out.js
"hello, world!" will be printed to the console.
Observing this, you'll notice that using emsdk is similar to using GCC. When compiling C files, use emcc
instead of gcc
; for C++, use em++
. Similarly, emsdk provides commands like emmake
and emconfigure
, akin to CMake. Apart from specific configuration parameters, most command-line flags are universal. This design significantly lowers the barrier to entry.
Compiling GDAL
The current version of GDAL (3.9) uses CMake for building, with minimal dependencies:
- CMake >= 3.16
- C99 compiler
- C++17 compiler
- PROJ >= 6.3.1
Following the compilation docs, if we replace the command cmake ..
with emcmake cmake ..
, we quickly encounter compilation errors. It's not as simple as swapping the compiler command as described in the previous section. Why? The issue lies in GDAL's dependencies. Compiling a project to WebAssembly requires that all its dependency libraries (like PROJ, GEOS for GDAL) are also compiled with Emscripten. You cannot use locally installed versions or those compiled natively with CMake. Libraries compiled with Emscripten are not placed in the system's default locations. In CMake commands, if dependencies aren't in standard paths, specify them using -D<lib>_INCLUDE_DIR=<value>
and -D<lib>_LIBRARY=<value>
. The correct approach is:
./emcmake cmake ..
-DSQLite3_INCLUDE_DIR=$(EM_OUT_DIR)/include -DSQLite3_LIBRARY=$(EM_OUT_DIR)/lib/libsqlite3.a \
-DPROJ_INCLUDE_DIR=$(EM_OUT_DIR)/include -DPROJ_LIBRARY_RELEASE=$(EM_OUT_DIR)/lib/libproj.a \
-DTIFF_INCLUDE_DIR=$(EM_OUT_DIR)/include -DTIFF_LIBRARY_RELEASE=$(EM_OUT_DIR)/lib/libtiff.a \
-DGEOTIFF_INCLUDE_DIR=$(EM_OUT_DIR)/include -DGEOTIFF_LIBRARY_RELEASE=$(EM_OUT_DIR)/lib/libgeotiff.a \
-DZLIB_INCLUDE_DIR=$(EM_OUT_DIR)/include -DZLIB_LIBRARY_RELEASE=$(EM_OUT_DIR)/lib/libz.a \
-DSPATIALITE_INCLUDE_DIR=$(EM_OUT_DIR)/include -DSPATIALITE_LIBRARY=$(EM_OUT_DIR)/lib/libspatialite.a \
-DGEOS_INCLUDE_DIR=$(EM_OUT_DIR)/include -DGEOS_LIBRARY=$(EM_OUT_DIR)/lib/libgeos.a \
-DWEBP_INCLUDE_DIR=$(EM_OUT_DIR)/include -DWEBP_LIBRARY=$(EM_OUT_DIR)/lib/libwebp.a \
-DEXPAT_INCLUDE_DIR=$(EM_OUT_DIR)/include -DEXPAT_LIBRARY=$(EM_OUT_DIR)/lib/libexpat.a \
-DIconv_INCLUDE_DIR=$(EM_OUT_DIR)/include -DIconv_LIBRARY=$(EM_OUT_DIR)/lib/libiconv.a;
However, this requires pre-compiling all dependency libraries (generating their include
files and .a
static libraries) with Emscripten. It's more complex than initially thought, right?
Fortunately, the gdal3.js
project on GitHub provides a complete compilation script. It uses a stack-like sequential approach to download and compile all source dependencies. Observe the compilation commands for GDAL (line 81) and PROJ (line 201) – both involve numerous dependencies specified via -D<arg>=<value>
parameters.
Execute make
in this Makefile's directory. After a considerable amount of time (likely successful unless errors occur), it generates three files:
Conclusion
This article introduced the basics of using Emscripten and compiling GDAL, resulting in initial WebAssembly and glue code files. However, this is just the starting point. Core challenges remain unsolved:
- How to use the WebAssembly files effectively in a web app.
- The compilation output is large (~38MB for the three files). How to optimize the build size?
- How to integrate this process into a production-ready workflow/engineering pipeline.
These issues will be addressed in upcoming articles.
Top comments (0)