This file documents notable changes to this project. This change log uses the conventions of keepachangelog.com.
handler.cljc: per-worker init and destroy for the clj-native workload-pool :proj handler, plus proj/transform-batch, which reuses a cached transformer against the per-worker PROJ Contextinit! options: :pool adopts a caller-supplied worker-router pool, and :debug-level and :debug-categories select worker logging. :max-live-ctxs (default 128) and :min-age-ms (default 100) bound the live-context set, and getPoolDetail() reports why the pool can or cannot evict each entryresources/clj-kondo.exports, so consumers resolve the generated PROJ surface. bb kondo:fndefs regenerates the hook copy of fndefs, and bb lint fails when it is stalemacros.clj and macros.cljs collapse into one macros.cljcnpm:@wcohen/worker-router@^0.0.2). clj-native's generator makes proj-handler.mjs from the hand-written proj-handler-overrides.mjs, and worker-router owns the call protocol. This replaces the hand-rolled proj-worker.mjs, fetch-worker.mjs, and postMessage ID tablenet.willcohen/native 0.0.1, npm ffi-wasm 0.0.1). native.clj and wasm.cljc keep only the PROJ-specific parts, and the Nix flake takes clj-native as an inputcoi-serviceworker.jstruffle-runtime added, dtype-next to 11.025, and CI runs GraalVM CE 25.2.4 to match the artifact pins./proj-handler exportdeps.edn: dropped the unused ClojureScript dependency, redundant classpath roots, the inert top-level :jvm-opts, and the diverged :nrepl alias (use bb nrepl). Scoped :test to test/cljc and pinned deps-deploydef version in build.clj. The bb tasks read it, and bb version-bump rewrites the other files from itbb lint on a fresh clone. All actions move to their current major versions, and Node.js moves from 20 to 26test/js/proj.test.mjs to cljs.test files under test/cljc. One proj_test.cljc now runs on the JVM and under squint plus NodeContainerfile. Cross builds run in the container that clj-native supplies, and bb test:linux pulls a public imagespec.cljc. The clojure.spec definitions had no callerstest-downstream-user/. Nothing invoked it, and bb test:jar runs the downstream checks inlinesrc/c/proj_network_stubs.c. The GraalVM network callbacks now install through Module.addFunction, so the WASM build carries no C stubsp atom, which the worker pool never sets: get-value, pointer->string, and the CLJS branches of string-array-pointer->strs, wasm/malloc, wasm/heapf64, wasm/alloc-coord-array, and wasm/set-coord-array (the wasm functions are now JVM-only)dispatch-to-platform-with-args, a pure passthrough to call-native, and the impl.native aliases get-os, get-arch, init-ffi!, and reset-projpj_vlogextract-args silently dropped skip-first?. This caused off-by-one ccall arg counts on opt-in call sitesproj_get_units_from_database, proj_get_celestial_body_list_from_database, proj_get_crs_info_list_from_database) silently returned null because the special-argtype detector mishandled keyword first-elementsinit-proj again on every native call: wasm/ensure-proj-initialized! tested the p atom, which is never set when each worker owns its own Emscripten module. It now tests the poolreset! that raced concurrent context creation. It now registers through swap!get_header callback kept its returned C string in one shared slot, which a concurrent grid fetch could drop before PROJ read it. The slot is now per threadattach-context-to-result returned nil for a result that is not a JS object, which dropped the value. It now passes such a result through, like the JVM branch:struct-fields metadata in fndefsout_*) automatically allocate, call, read, and free heap memory. Callers pass only input args and receive typed maps. 11 functions: proj_get_area_of_use, proj_get_area_of_use_ex, proj_cs_get_axis_info, proj_ellipsoid_get_parameters, proj_prime_meridian_get_parameters, proj_coordoperation_get_method_info, proj_coordoperation_get_param, proj_coordoperation_get_grid_used, proj_uom_get_info_from_database, proj_grid_get_info_from_database, proj_coordoperation_get_towgs84_valuesgetEllipsoid, getPrimeMeridian, crsGetCoordinateSystem, crsGetCoordoperation) and all out-param functionsproj_get_units_from_databaseproj_get_celestial_body_list_from_databaseproj_create: raw binding for PROJ strings, WKT, and pipeline definitions (for example, +proj=pipeline +step +proj=robin)create-from-wkt, set-coord!, set-col!, out-param functions across all platforms (CLJ FFI, GraalVM, Node.js, Playwright, Java)bb deploy and bb deploy:dry-run tasks for release workflowdownload-grids task (was WIP, never completed):west-lon-degree). Java: camelCase strings ("westLonDegree"). JS camelCase aliases: camelCase keys (westLonDegree). JS snake_case aliases: snake_case keys (west_lon_degree). For example, the out-param key :west_lon_degree is now :west-lon-degree. The Java keys "auth-name" and "semi_major_metre" are now "authName" and "semiMajorMetre".get-crs-info-list-from-database renamed to proj-get-crs-info-list-from-database (Clojure) / projGetCrsInfoListFromDatabase (JS)projCreateCrsToCrs) instead of snake_case (proj_create_crs_to_crs). The two forms stay functionalstring-array-to-polyglot-array renamed to string-list-to-native-array, now cross-platformproj_as_proj_string on concatenated operations) now return nil. Before, FFI threw an exception and GraalVM returned ""coord->coord-array: missing :browser case in CLJS dispatchcoord->coord-array: missing auto-initialization before dispatchset-coord!: wrapped JVM-only (was incorrectly cross-platform)toggle-graal!: now resets implementation to nil, the same as force-graal!/force-ffi!null (was "")[] and frees allocated memoryextract-args: FFI gets "" (dtype-next rejects nil), WASM gets 0 (NULL, necessary for "all/any" semantics)extract-argsproj_create_from_wkt: pointer args changed to :pointer? (nullable)proj_create_crs_to_crs/proj_get_source_crs roundtrip. The result is ISO-19111 compatible objects. A console.warn message recommends explicit contexts for better performance.force-worker-idx parameter on proj-emscripten-helper and def-wasm-fn-runtime to route calls to a specific workerprojCreateCrsToCrs({source_crs: "EPSG:4326", target_crs: "EPSG:3857"}))extract-args to dispatch-proj-fn for JVM and CLJSprojAsWkt) when you call them without an explicit context on a PJ object from a different workergetCrsInfoListFromDatabase now auto-creates a context when you call it without oneprojTransArray, projCreateCrsToCrs), plus manual aliases for helper functions (setCoords, coordArray, contextCreate, and so on)proj_network_stubs.c with EM_JS)Atomics.wait()proj-worker.mjs, fetch-worker.mjs) with context-to-worker affinitylogging.clj)network.clj) with ProxyExecutable callbacks dispatched through C stubsgetWorkerMode() / getWorkerCount() to examine the worker pool at runtimegraal.clj. GraalVM dispatch now uses the shared wasm.cljc code pathcontext-create accepts {:network false} option across all platformsproj_trans_array transfers data to the correct worker on demand and allocates it there. coord: now takes the coord array object directly instead of coords.malloc. Results are read with getCoords(coords, idx) instead of coords.array[i].proj.db, proj.ini, and proj-emscripten.wasm relative to module URL instead of HTML page (corrects CDN loading)proj_create_crs_to_crs_from_pj function
Java API: PROJ.java wrapper, PROJTest.java tests, bb test:java-ffi, bb test:java-graal, bb test:clj-ffi
Container-Based Build System:
Containerfile with builds for native, WASM, and development targetslinux/amd64, linux/aarch64, and windows/amd64--build-arg USE_LOCAL_PROJ=1Local PROJ Development Workflow:
bb proj:clone task to clone OSGeo/PROJ repository to vendor/PROJ--local-proj flag for all build tasks to use local PROJ instead of release versionextract-args now uses :argsemantics defaults (corrects proj_create_from_database NPE)proj_create_from_database options parameter changed to :pointer? for correct null handlingBabashka Build System: Complete replacement of shell scripts with bb.edn tasks
bb build command with --native, --wasm, and --cross optionsbb test:ffi, bb test:graal, bb test:cljs, bb test:playwright for testsbb jar, bb pom, bb cherry, bb nrepl and other development tasksbb test:all and bb build:all meta-tasks for eventual CI/CD workflowsbb test-run for the complete build and test pipeline (without deployment)Macro-Based Code Generation: Complete architectural refactor
fndefs.cljc containing all PROJ function definitions as datamacros.clj and macros.cljs for compile-time and runtime code generationRuntime Dispatch System: New unified architecture in proj.cljc
dispatch-proj-fn central router for all function callsextract-args for flexible parameter handling (supports the two styles :source-crs and :source_crs)WebAssembly Module: New wasm.cljc namespace
proj-loader.mjs for ES6 module loadingTesting Infrastructure
examples/browser/index.htmlJavaScript/NPM Support
init function alias for the JavaScript APIDeveloper Experience
Build System: Complete migration from shell scripts to Babashka
Project Structure
src/js/proj-emscripten/ to consolidated WASM support in coreEnums.java), not necessary with the macro systemsrc/cljc/net/willcohen/proj/Implementation Files
graal.clj: Refactored to use macro-generated functionsnative.clj: Simpler with the macro systemproj.cljc: Major refactor for runtime dispatchDocumentation
init functionDependencies
proj-emscripten JavaScript packageCan you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |