SpatiaLite for WebAssembly
v5.1.0WebAssemblySpatiaLite 5.1.0 for browsers, Node.js and edge runtimes, precompiled for wasm32, single-threaded and multi-threaded as @crossbind/port-spatialite-wasm.
npm install @crossbind/port-spatialite-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 SpatiaLite's own headers from @crossbind/port-spatialite directly. All 5 work that way.
Imported straight from JavaScript, the headers need this configuration today; its comments say why.
Store points and measure the distance between them
The calls every SpatiaLite program starts with: spatialite_alloc_connection and spatialite_init_ex register the spatial SQL functions on a SQLite connection, InitSpatialMetaData creates the metadata tables and AddGeometryColumn adds a geometry column. GeomFromText reads WKT, and ST_Distance(a, b, 1) measures on the WGS 84 ellipsoid.
5.1.0 3 Istanbul 0 km, Izmir 327 km, Ankara 350 km
The same calls spatial_database.h makes, on sqlite3.h and spatialite.h as the ports ship them. The sqlite3 ** and sqlite3_stmt ** out-parameters are a slot from allocPointer read back with readPointerAt, sqlite3_column_text returns a pointer that readCString reads, and SQLITE_OK and SQLITE_ROW are macros, which do not bind, so their values are written out. The build also needs this module's crossbind.overrides.js: the two headers declare 20 functions the published libraries do not define, such as sqlite3_snapshot_get and load_XL, and the build stops until export.ignoredDeclarations leaves them out.
5.1.0 3 Istanbul 0 km, Izmir 327 km, Ankara 350 km
Find places in a map view and the nearest ones with a spatial index
CreateSpatialIndex keeps an R*Tree of the bounding boxes of a geometry column, and queries reach it through virtual tables: SpatialIndex returns the rows inside a box such as the map view, and KNN2 the rows nearest to a point with their distance in metres.
Bursa, Edirne, Istanbul, Izmir Bursa 133 km, Istanbul 189 km, Ankara 201 km
Numbers bind as they are; text needs SQLITE_TRANSIENT, ((sqlite3_destructor_type)-1), a macro. The Number -1 fails with Cannot pass "-1" as a NativePointer, so the pointer is read back from a slot holding -1, and SQLite copies each name as it does for the C++. null (SQLITE_STATIC) is no stand-in: the binding frees its copy of a JavaScript string when the call returns, and three strings bound that way read back as two empty strings and garbage.
Bursa, Edirne, Istanbul, Izmir Bursa 133 km, Istanbul 189 km, Ankara 201 km
Measure areas and distances in metres
SpatiaLite measures in the units of the coordinates, so ST_Area of a longitude/latitude polygon comes out in square degrees. ST_Transform to a projected CRS, here UTM zone 35N (EPSG:32635), gives square metres and metres, stretched the further a shape lies from the zone; ST_Distance(a, b, 1) measures on the ellipsoid with no projection at all.
0.000200 square degrees, 1868349 m² 350462 m in UTM zone 35N, 350082 m on the ellipsoid
Each method of measure.h becomes a JavaScript function over the same calls, and the WKT stays a plain string, bound with SQLITE_TRANSIENT read from a slot holding -1. SQLITE_NULL is a macro as well, so the check for a shape SpatiaLite cannot measure spells out its value, 5.
0.000200 square degrees, 1868349 m² 350462 m in UTM zone 35N, 350082 m on the ellipsoid
Reproject coordinates between EPSG codes
ST_Transform moves a geometry to another reference system through PROJ, which reads the proj.db the build preloads. InitSpatialMetaData(1) registers the 6,559 EPSG codes SpatiaLite knows in spatial_ref_sys, where their names are too.
WGS 84 / Pseudo-Mercator: POINT(3225860.732004 5013551.237223) WGS 84 / UTM zone 35N: POINT(666370.505017 4541552.487191) back to WGS 84: POINT(28.9784 41.0082)
ST_Transform reads PROJ's proj.db from the data preloaded with the module, the same file the C++ build ships, with no configuration of its own. The WKT is bound with SQLITE_TRANSIENT, read from a slot holding -1, and every answer comes back from sqlite3_column_text through readCString.
WGS 84 / Pseudo-Mercator: POINT(3225860.732004 5013551.237223) WGS 84 / UTM zone 35N: POINT(666370.505017 4541552.487191) back to WGS 84: POINT(28.9784 41.0082)
Read GeoJSON in and write a FeatureCollection out
GeomFromGeoJSON reads a GeoJSON geometry into a SpatiaLite geometry, and AsGeoJSON writes one back rounded to the decimals you ask for. SQLite's JSON functions wrap the rows into a FeatureCollection that Leaflet, MapLibre or OpenLayers load as it is.
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"name":"Galata Tower"},"geometry":{"type":"Point","coordinates":[28.97413,41.02564]}},{"type":"Feature","properties":{"name":"Galata Bridge"},"geometry":{"type":"LineString","coordinates":[[28.97336,41.01963],[28.97139,41.02402]]}}]}The same statements as geojson_layer.h, with sqlite3_changes telling a geometry GeomFromGeoJSON rejected from a stored one. The name and the GeoJSON are bound with SQLITE_TRANSIENT, read from a slot holding -1, and the FeatureCollection comes back from sqlite3_column_text through readCString.
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"name":"Galata Tower"},"geometry":{"type":"Point","coordinates":[28.97413,41.02564]}},{"type":"Feature","properties":{"name":"Galata Bridge"},"geometry":{"type":"LineString","coordinates":[[28.97336,41.01963],[28.97139,41.02402]]}}]}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
- SpatiaLite overview: the apps, every platform's setup and the packages.
- SpatiaLite for Android: React Native apps on Android.
- SpatiaLite for iOS: React Native apps on iOS.
- SpatiaLite 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.