This project supplies a native (or transpiled) version of PROJ (GitHub) for the JVM and JS ecosystems.
The goal is a fast version of PROJ for these platforms that stays synchronized with upstream development.
For the JVM, a package published to
Clojars gives Clojure bindings. For
JavaScript, an ES6 module called
proj-wasm published to NPM gives an
interface to an internal transpiled WASM module.
This project is new. Some functions are not complete. The tests are not complete. Feedback from testers is welcome.
The APIs and the structure of this library are an early work-in-progress. They can change while development continues.
This library is limited to PROJ's C API for ISO 19111
functionality.
The PROJ C API has some sections. Objects from the ISO 19111 section do not
mix with functions from the other sections. The exception is an ISO 19111
CoordinateOperation object with a valid PROJ pipeline export. These objects
operate with transformation functions, for example proj_trans_array().
The library has one API (net.willcohen.proj.proj). At runtime, it automatically selects the first available backend in this sequence:
During initialization, the library identifies the environment and the available backends:
;; The implementation atom tracks which backend is active
(defonce implementation (atom nil))
;; JVM: clj-native's try-init! attempts native FFI, then falls back to
;; the GraalVM WASM backend, and records the winner in the atom.
;; CLJS: starts the worker pool and returns a Promise.
(defn init! [opts]
#?(:clj
(nps/try-init! implementation force-graal log?
native/init-proj
wasm/init-proj)
:cljs
(-> (wasm/init-proj opts)
(.then (fn [_] (reset! implementation runtime))))))
All PROJ functions flow through a central dispatcher:
;; Macros generate thin wrapper functions
(defn proj-create-crs-to-crs [opts]
(dispatch-proj-fn :proj_create_crs_to_crs
(get fndefs :proj_create_crs_to_crs)
opts))
;; The dispatcher orchestrates the entire call flow
(defn dispatch-proj-fn [fn-key fn-def opts]
(ensure-initialized!)
(let [args (extract-args fn-def opts)
result (call-native fn-key fn-def args)]
(process-return-value-with-tracking result fn-def)))
The real dispatch-proj-fn adds an auto-context step, cross-worker argument
reconciliation, a per-return-type branch for :struct-list and
:out-params, and a result check that reads proj_context_errno.
The dispatch system does these tasks:
PROJ returns pointers that the caller must free. The library tracks these resources and releases them automatically:
tech.v3.resource during garbage collectionresource-tracker library;; When a function returns a pointer, the library tracks it automatically
;; No manual cleanup needed - this happens behind the scenes:
(resource/track result-pointer
{:dispose-fn (fn [] (proj-destroy pointer-address))
:track-type :auto}) ; Cleaned up on GC
Manual calls to proj-destroy or other cleanup functions are not necessary. The library releases all resources when they go out of scope or during garbage collection.
PROJ uses contexts for thread safety and operation tracking:
;; Use an explicit context (stored in an atom)
(def ctx (context-create))
(proj-get-authorities-from-database {:context ctx})
;; Or let the library create a temporary context
(proj-get-authorities-from-database {}) ; Creates context internally
In JavaScript, the worker pool pins each context to one worker. If PJ objects from different workers go to the same function (for example, after round-robin context creation), the library recreates the mismatched objects on the target worker through a PROJJSON roundtrip. The library also sends a console.warn message. For the best performance, use an explicit shared context.
When atomic context access is necessary, the library uses the cs (context-swap) wrapper:
swap! for thread-safe accessEach context atom holds this state:
The library transforms coordinates in arrays. A single coordinate uses an array of one.
;; Batch transformation with coordinate arrays
(def coords (coord-array 2)) ; 2 coordinates
;; EPSG:4326 uses lat/lon order. Each coordinate is (x y z t).
(set-coords! coords [[42.3603222 -71.0579667 0 0]
[40.7127 -74.0059 0 0]])
(proj-trans-array {:p transformation :direction 1 :n 2 :coord coords})
proj-trans-array mutates the coordinate array in place. The :direction
key is necessary: 1 is forward, -1 is inverse. set-coords! needs one
coordinate for each row of the array, and each coordinate needs all four
values.
Each platform implements coordinate arrays differently:
dtype-next tensors give zero-copy native memory access;; Force a specific implementation for testing
(force-graal!) ; Use GraalVM even if native is available
(force-ffi!) ; Use native FFI
;; Check current implementation
(ffi?) ; => true if using native
(graal?) ; => true if using GraalVM
The test framework makes sure that behavior is the same on all implementations:
(defmacro with-each-implementation [& body]
;; Runs the same test against FFI, GraalVM, and ClojureScript
...)
The API gives these conveniences:
Parameter naming flexibility: Use underscores or hyphens
;; The two forms work identically:
(proj-create-crs-to-crs {:source_crs "EPSG:4326" :target_crs "EPSG:2249"})
(proj-create-crs-to-crs {:source-crs "EPSG:4326" :target-crs "EPSG:2249"})
Optional parameters with defaults: Functions apply defaults
;; Context is optional - library creates one if needed
(proj-get-authorities-from-database {})
;; Or provide your own
(proj-get-authorities-from-database {:context my-ctx})
Idiomatic return keys per platform: Out-param and struct-list functions return maps with platform-native key casing:
:west-lon-degree, :semi-major-metre)"westLonDegree", "semiMajorMetre")westLonDegree, semiMajorMetre)west_lon_degree, semi_major_metre)Consistent error handling: Error behavior is the same on all platforms
For some PROJ transformations, grid shift files are necessary. These files are TIFF-format datum corrections from cdn.proj.org. Without these grids, some transformations (for example, NAD27->NAD83) give only "ballpark" accuracy. When network access is on, clj-proj fetches grids automatically.
network.clj installs in the WASM function table. No configuration is necessary.Atomics.wait) is permitted. No special headers are necessary.worker_threads with an XMLHttpRequest polyfill that sends sync requests to a fetch-worker through SharedArrayBuffer + Atomics. No configuration is necessary.;; Network enabled by default
(def ctx (proj/context-create))
;; Explicitly disable network
(def ctx-offline (proj/context-create {:network false}))
// JavaScript - context is optional; create one explicitly to control network
const ctx = await proj.contextCreate(); // network enabled (default)
const ctxOffline = await proj.contextCreate({network: false}); // network disabled
There is one WASM build, and it is single-threaded. Each worker runs that
binary with no internal threads. The WASM memory is a plain ArrayBuffer,
not a SharedArrayBuffer.
Thus the Cross-Origin-Opener-Policy header and the
Cross-Origin-Embedder-Policy header are not necessary. The library operates
the same when crossOriginIsolated is true or false. The browser test suite
runs against a server of each type.
Parallelism comes from the worker pool. To change the number of workers, use
the workers option of init.
The JVM implementation has two backends:
Supported platforms (native):
Not yet built:
On platforms with a native precompiled PROJ, this library calls it through
dtype-next on the JDK Panama FFM backend (java.lang.foreign). This is the
preferred option.
The native implementation:
dtype-next library singleton to the absolute path of the extracted librarydtype-next for native interop and memory managementThe library contains pre-compiled PROJ libraries for each platform in resources/{platform}/. At runtime, it identifies the OS and architecture. Then it loads the applicable libraries.
On a computer where the native library was built:
(require '[net.willcohen.proj.proj :as proj])
;; Initialization happens automatically on first use in Clojure/JVM
;; For explicit initialization, call (proj/init!)
;; Create a coordinate transformation
(def ctx (proj/context-create))
(def transformer (proj/proj-create-crs-to-crs {:context ctx
:source-crs "EPSG:4326"
:target-crs "EPSG:2249"}))
;; Transform a single coordinate
(def coords (proj/coord-array 1))
;; EPSG:4326 uses lat/lon order, not lon/lat!
(proj/set-coords! coords [[42.3603222 -71.0579667 0 0]]) ; Boston City Hall (lat, lon)
(proj/proj-trans-array {:p transformer :direction 1 :n 1 :coord coords})
;; coords now contains transformed coordinates in EPSG:2249 (MA State Plane)
;; Query available authorities
(proj/proj-get-authorities-from-database)
;; => ["EPSG" "ESRI" "IAU_2015" "IGNF" "NKG" "NRCAN" ...]
;; No manual cleanup needed! Resources are automatically tracked and
;; cleaned up when they go out of scope or during garbage collection
A Java wrapper class (net.willcohen.proj.PROJ) gives idiomatic Java access to the library:
import net.willcohen.proj.PROJ;
// Initialize (auto-selects best backend: native FFI or GraalVM WASM)
PROJ.init();
// Create a context and transformation
Object ctx = PROJ.contextCreate();
Object transform = PROJ.createCrsToCrs(ctx, "EPSG:4326", "EPSG:2249");
// Transform coordinates (EPSG:4326 uses lat/lon order)
Object coords = PROJ.coordArray(1);
PROJ.setCoords(coords, new double[][]{{42.3603222, -71.0579667}}); // Boston City Hall
PROJ.transArray(transform, coords, 1);
// coords now contains transformed coordinates in EPSG:2249 (MA State Plane)
// Query available authorities
List<String> authorities = PROJ.getAuthoritiesFromDatabase();
// => ["EPSG", "ESRI", "IAU_2015", "IGNF", "NKG", "NRCAN", ...]
// Create transformation from CRS objects (for advanced use)
Object sourceCrs = PROJ.createFromDatabase(ctx, "EPSG", "4326");
Object targetCrs = PROJ.createFromDatabase(ctx, "EPSG", "2249");
Object transformFromPj = PROJ.createCrsToCrsFromPj(ctx, sourceCrs, targetCrs);
// No manual cleanup needed - resources are automatically tracked!
The Java API has the same functions as the Clojure API. It includes:
init(), forceGraal(), forceFfi())contextCreate(), isContext())createCrsToCrs(), createCrsToCrsFromPj(), createFromDatabase())coordArray(), setCoords(), transArray())getAuthoritiesFromDatabase(), getCodesFromDatabase())getAreaOfUse(), ellipsoidGetParameters(), csGetAxisInfo(), primeMeridianGetParameters(), coordoperationGetMethodInfo(), and so on). The library reads C output parameters automatically and returns MapsPJ_FWD, PJ_INV, PJ_IDENT)On platforms with no native library, this library runs the transpiled WebAssembly version of PROJ through GraalVM's WebAssembly support.
For this transpiled PROJ, JDK 25 or later is necessary because of GraalVM. Enable JVMCI for better performance.
When native libraries are not available, the GraalVM implementation:
network.clj) in the WASM function table and registers them with PROJ. This lets Java's HttpClient fetch gridsNote: GraalVM can show "WARNING: The polyglot context is using an implementation that does not support runtime compilation" during initialization. This message shows interpreted (non-JIT) WASM execution. It has no effect on correctness.
Initialization is slow. The load of the WASM binary (3.6MB) and the PROJ database (10.2MB) takes some seconds.
To force GraalVM implementation on a system where native libraries are available:
(require '[net.willcohen.proj.proj :as proj])
;; Force GraalVM WASM implementation
;; If on a fallback-only platform, this step is unneeded
(proj/force-graal!)
;; => true
;; Usage is identical to native implementation
(def ctx (proj/context-create))
(def transformer (proj/proj-create-crs-to-crs {:context ctx
:source-crs "EPSG:4326"
:target-crs "EPSG:2249"}))
;; Transform coordinates (EPSG:4326 uses lat/lon order)
(def coords (proj/coord-array 1))
(proj/set-coords! coords [[42.3603222 -71.0579667 0 0]]) ; Boston City Hall
(proj/proj-trans-array {:p transformer :direction 1 :n 1 :coord coords})
;; coords now contains transformed coordinates
;; No manual cleanup needed - resources are automatically managed!
Note: GraalVM initialization takes 5-7 seconds because it loads the WASM module. You can see Truffle/GraalVM diagnostic output during initialization.
The JavaScript implementation uses emscripten-compiled PROJ in workers:
worker_threads (Node.js). This keeps the main thread responsive. It also lets the workers do synchronous network operations for grid fetchingThe library identifies the JavaScript environment automatically and adapts to it:
worker_threads with an XMLHttpRequest polyfill for grid fetchingFor Node.js, create index.mjs:
import * as proj from "proj-wasm";
// Initialize PROJ (required before any operations in JavaScript)
await proj.init(); // Convenience alias for init! (also available as init_BANG_)
// Create a transformation (all operations are async, context is auto-created)
const transformer = await proj.projCreateCrsToCrs({
source_crs: "EPSG:4326",
target_crs: "EPSG:2249"
});
// Transform coordinates (EPSG:4326 uses lat/lon order)
const coords = await proj.coordArray(1);
await proj.setCoords(coords, [[42.3603222, -71.0579667, 0, 0]]); // Boston City Hall (lat, lon)
await proj.projTransArray({
p: transformer,
direction: proj.PJ_FWD,
n: 1,
coord: coords
});
// Read transformed coordinates
const transformed = await proj.getCoords(coords, 0);
console.log("Transformed:", transformed[0], transformed[1]);
// Shutdown workers when done (allows Node.js process to exit cleanly)
await proj.shutdown();
// Resources are automatically cleaned up - no manual cleanup needed!
// The resource-tracker library releases them when objects go out of scope
// Optional: create an explicit context to disable network or pin to a worker
// const ctx = await proj.contextCreate({ network: false });
// const t = await proj.projCreateCrsToCrs({ context: ctx, ... });
For browsers, the API is the same, and no special headers are necessary.
$ node index.mjs
# Transformed coordinates will be displayed
Builds run through Babashka and Nix. The host build needs no container.
Two tasks do need podman or docker:
bb build --cross and bb build --cross-platform build each target in a
container, from the Containerfile that clj-native supplies. clj-proj has no
Containerfile of its own. The task vendors clj-native into
clj-native-vendor/ and points the flake at it. On the published clj-native
jar, the flake files come out of the jar. With the :dev checkout override,
the task copies the checkout.bb test:linux pulls the public clojure:tools-deps-trixie image.Babashka + Nix users:
direnv allow:graal
PROJ WASM guest JIT-compile. Without it, the guest runs interpreted, which
is much slower. direnv allow points JAVA_HOME at the GraalVM JDK
automatically for each shell that enters the repo. For a shell without
direnv, run tasks with direnv exec . bb <task>. The bb test:graal /
test:clojure-graal tasks give a warning if they identify a non-GraalVM
JDK.direnv exec <dir> <cmd>, which uses the cached dev shell.
nix develop <dir> --command <cmd> gives the same environment. But it
evaluates the flake again on each call, and this takes seconds each time.bb tasks # List all available commands
bb build --help # Show build options
bb clean --help # Show clean options
bb build --native # Native libraries, current platform
bb build --wasm # WebAssembly
bb build --cross-platform linux/amd64 # One cross target
bb build --cross-platform linux/aarch64
bb build --cross-platform windows/amd64
bb build --cross # All default platforms
bb test-run # Build everything, run all tests
direnv allow # One-time setup
bb dev # Rich REPL with Portal
bb demo # Browser demo at localhost:8080
bb jar # JVM (JAR file)
bb squint # JavaScript (ES6 module)
Native builds compile PROJ + dependencies (SQLite, LibTIFF, zlib) for the host platform
resources/{platform}/ (for example, resources/darwin-aarch64/)WASM builds use emscripten to compile PROJ into WebAssembly
resources/wasm/ and src/cljc/net/willcohen/proj/Cross-platform builds use Nix for reproducible builds
bb test:all # Everything
bb test:ffi # Native FFI
bb test:graal # GraalVM WebAssembly
bb test:node # JavaScript / Node.js
bb test:playwright # Browser integration (requires a display)
bb test:jar # JAR as a downstream dependency
bb test:npm # npm package as a downstream dependency
bb test:linux # Linux platforms, in a public container image
The test framework runs identical tests against all implementations. This makes sure that behavior is the same on all platforms.
clj-proj/
├── src/
│ ├── clj/net/willcohen/proj/impl/ # JVM-specific implementations
│ │ ├── native.clj # Panama FFM bindings
│ │ ├── logging.clj # PROJ log callback
│ │ ├── network.clj # GraalVM WASM grid fetching
│ │ └── struct.clj # Native struct definitions
│ ├── cljc/net/willcohen/proj/ # Cross-platform core
│ │ ├── proj.cljc # Public API + dispatch
│ │ ├── wasm.cljc # WASM interface (GraalVM + CLJS workers)
│ │ ├── handler.cljc # Workload-pool handler spec
│ │ ├── fndefs.cljc # PROJ function definitions
│ │ ├── macros.cljc # Macros for JVM and squint
│ │ ├── proj-loader.mjs # Main-thread WASM orchestrator
│ │ ├── proj-handler.mjs # Generated worker-router handler
│ │ ├── proj-handler-overrides.mjs # Hand-written half of the handler
│ │ ├── esbuild.config.mjs # Bundler config
│ │ ├── *.mjs # squint-generated JS modules
│ │ └── dist/ # esbuild bundle output (npm package)
│ └── java/net/willcohen/proj/
│ └── PROJ.java # Java API wrapper
├── resources/
│ ├── {platform}/ # Native libraries per platform
│ ├── wasm/ # WASM artifacts read by GraalVM
│ │ ├── proj-emscripten.js # WASM JS glue
│ │ ├── proj-emscripten.wasm # WASM binary
│ │ └── proj-loader.mjs # proj-loader.mjs for classpath
│ ├── proj.db # PROJ database
│ └── proj.ini # PROJ configuration
├── deps.edn # Clojure dependencies
├── bb.edn # Babashka build tasks
├── build.clj # Clojure build configuration
└── flake.nix # Nix development environment
Core API & Dispatch:
src/cljc/net/willcohen/proj/proj.cljc - Main public API and dispatch logicsrc/cljc/net/willcohen/proj/fndefs.cljc - PROJ function definitions and constantssrc/cljc/net/willcohen/proj/macros.cljc - Code generation macros for multi-platform supportsrc/cljc/net/willcohen/proj/wasm.cljc - GraalVM context management (CLJ) and worker pool (CLJS)src/cljc/net/willcohen/proj/handler.cljc - Per-worker init and destroy for the workload-pool :proj handlerJVM Implementations:
src/clj/net/willcohen/proj/impl/native.clj - Panama FFM implementation for native librariessrc/clj/net/willcohen/proj/impl/struct.clj - Native struct definitions for FFIsrc/clj/net/willcohen/proj/impl/logging.clj - Native upcall for PROJ log routingsrc/clj/net/willcohen/proj/impl/network.clj - Grid fetch callbacks for native FFI and GraalVM WASMJavaScript Workers:
src/cljc/net/willcohen/proj/proj-loader.mjs - Main-thread orchestrator (init, worker pool)src/cljc/net/willcohen/proj/proj-handler.mjs - Generated worker-router handler. It runs every ccallsrc/cljc/net/willcohen/proj/proj-handler-overrides.mjs - Hand-written half of that handlerfetch_worker.mjs (from clj-native) - Node.js sync HTTP bridge through SharedArrayBuffer + AtomicsJava API:
src/java/net/willcohen/proj/PROJ.java - Java wrapper classproj.cljc is the public API for all platforms. On the JVM, init! tries
native FFI first. If that fails, it uses GraalVM WASM. On ClojureScript,
init! initializes the worker pool. After init, all calls dispatch through
the active backend.
JVM — Native FFI (preferred):
proj.cljc Public API. Blocking calls. Dispatches based on
│ @implementation (:ffi or :graal).
▼
native.clj Panama FFM through dtype-next. Extracts the
│ platform-specific shared libraries from
│ resources/{platform}/ to a temp dir, then binds them.
│ Direct C calls, no WASM.
│
├─ logging.clj Native upcall that routes PROJ's log output to
│ clojure.tools.logging.
│
└─ struct.clj Native struct definitions (PJ_COORD and so on) for
zero-copy memory access through dtype-next tensors.
Native upcalls to Java HttpClient fetch the grids.
JVM — GraalVM WASM (fallback, or forced with force-graal!):
proj.cljc Same public API, same dispatch.
│
▼
wasm.cljc Creates a GraalVM polyglot context with JS + WASM support.
│ Loads proj-emscripten.wasm, proj.db, and proj.ini from
│ the classpath (resources/wasm/).
▼
proj-loader.mjs load() runs the Emscripten module directly in the
│ polyglot context. No workers, no postMessage. JVM to
│ JS interop goes through ProxyArray and ProxyObject.
│
├─ network.clj Grid fetch callbacks. ProxyExecutable callbacks in the
│ WASM function table call into Java, where network.clj
│ does the HTTP with Java's HttpClient.
│
└─ logging.clj Same log routing as FFI, adapted for GraalVM callbacks.
Browser / Node.js (ClojureScript):
proj.cljc Public API. All operations return Promises.
│
▼
wasm.cljc Main thread. Spawns the worker-router pool and routes
│ each call to the worker that owns the relevant PJ
│ context. worker-router owns the call protocol, so
│ there is no hand-rolled postMessage ID table here.
▼
proj-loader.mjs Reads proj.db and proj.ini once on the main thread.
│ init-workers! forwards them into each worker through
│ the handler init args.
▼
proj-handler.mjs Runs in a Web Worker (browser) or worker_thread (Node.js).
│ All PROJ ccall/malloc/free operations happen here.
│ Loads the Emscripten module and writes proj.db/proj.ini
│ to Emscripten's virtual filesystem. clj-native's
│ generator makes it from proj-handler-overrides.mjs. Its
│ runtime queue serializes every call.
▼
fetch_worker.mjs Node.js only, supplied by clj-native. A second
worker_thread that connects Emscripten's synchronous
XMLHttpRequest (used for grid fetch) to Node.js async
http/https through SharedArrayBuffer + Atomics.wait and
notify. Browsers do not need this, because Web Workers
can use Emscripten's built-in FETCH support.
bb build --wasm makes one single-threaded WASM build from the PROJ
source. --wasm-browser and --wasm-graal are aliases of the same task.
The task writes proj-emscripten.js and proj-emscripten.wasm to two
directories:
src/cljc/net/willcohen/proj/, where the squint and esbuild pipeline
bundles them into dist/ for ClojureScript.resources/wasm/, where GraalVM reads them from the classpath.One build is sufficient for all three lanes. GraalVM's polyglot engine does
not support pthreads. Browsers get no benefit from a SharedArrayBuffer
memory here. Thus a second variant has no purpose.
A nix flake pins the build dependencies. This prevents errors from changed
dependency versions. The flake operates with direnv
(see .envrc's use flake).
Development REPLs with different configurations:
# Rich development REPL with Portal and other tools
bb dev
# Basic nREPL with Portal (port 7888)
bb nrepl
# Standard Clojure REPL
clj
An editor with Clojure support can help: Emacs with CIDER, VSCode with Calva and Portal extensions, IDEA with Cursive.
The bb nrepl task starts an nREPL server on port 7888. Portal is included for data visualization and debugging.
Run the browser demo locally:
bb demo # Serves at http://localhost:8080/docs/
Generate API documentation (work in progress):
bb quickdoc # Generates docs from source
Run bb tasks for the complete list. Key commands:
Build & Package:
bb build --help - Show build options (native/wasm/cross)bb build:all - Build native + WASM + cross-platform artifactsbb jar - Build JAR file for JVMbb squint - Build JavaScript ES6 modulebb pom - Generate/update pom.xmlTesting:
bb test:all - Run all testsbb test:ffi / bb test:node / bb test:graal - Test specific implementationsbb test-run - Complete build + test cyclebb pre-deploy - Full build, test, and package verification before deployDevelopment:
bb dev - Rich REPL with Portalbb nrepl - nREPL server (port 7888)bb demo - Browser demo (localhost:8080)Deployment:
bb deploy:dry-run - Check auth, show JAR/npm contents, npm publish dry-runbb deploy - Tag, push, deploy to Clojars + npmbb version-bump <version> - Bump version across all filesCI:
bb download-ci-artifacts - Download all build artifacts (source archives, native libs, WASM) from the most recent CI run (the gh CLI is necessary)Utilities:
bb clean --help - Show clean optionsbb jar-contents - List files in JARbb npm-contents - List files in npm packagebb proj:clone --help - Local PROJ developmentThe JavaScript side uses clj-native through its published npm package.
src/cljc/net/willcohen/proj/package.json pins an exact ffi-wasm
version, and a plain npm install gets it from the registry.
To develop against a clj-native checkout, point the dependency at a built
tarball. This is the npm twin of the :dev alias in deps.edn and of the
flake's --override-input:
cd ../clj-native && npm pack
The prepack script runs bb build:js first, which compiles clj-native's
.cljc sources to the .mjs modules that go into the package. Then set
"ffi-wasm" to "file:../../../../../../clj-native/ffi-wasm-<version>.tgz"
in package.json and run npm install --prefix src/cljc/net/willcohen/proj.
Do not commit that change. After each clj-native edit, run the pack and the
install again.
worker-router is a published package (npm:@wcohen/worker-router), so a plain
npm install gets it. For work on worker-router itself, link
node_modules/worker-router to a local checkout. Run npm run build there.
Then run bb squint here to rebuild the bundle.
clj-proj has a workflow for developers of the PROJ C library. The workflow lets you examine local PROJ changes against the bindings before you send the changes upstream.
Setup:
# Clone PROJ repository locally
bb proj:clone # Clone to vendor/PROJ (master branch)
bb proj:clone --branch=feature # Clone specific branch
bb proj:clone --update # Update existing clone
Development Workflow:
# Make changes to PROJ C code
cd vendor/PROJ
# ... edit C files, add features, fix bugs ...
git commit -m "experimental change"
cd ../..
# Test changes against clj-proj
bb build --native --local-proj --debug # Use local PROJ instead of release
bb test:ffi # Verify bindings still work
# Test WASM compatibility
bb build --wasm --local-proj
bb test:node
# Cross-platform verification
bb build --cross --local-proj # Test musl builds with local PROJ
Local PROJ Tasks:
proj:clone - Clone OSGeo/PROJ repository with options (--help for details)Local PROJ Build Flags:
--local-proj - Use vendor/PROJ instead of released PROJ version (applicable to all build tasks)Directory Structure:
clj-proj/
├── vendor/ # gitignored - your local development area
│ └── PROJ/ # cloned OSGeo/PROJ repository
├── bb.edn
└── ...
Copyright (c) 2024, 2025, 2026 Will Cohen
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--
This project uses code from PROJ, which is distributed under these terms:
All source, data files and other contents of the PROJ package are
available under the following terms. Note that the PROJ 4.3 and earlier
was "public domain" as is common with US government work, but apparently
this is not a well defined legal term in many countries. Frank Warmerdam placed
everything under the following MIT style license because he believed it is
effectively the same as public domain, allowing anyone to use the code as
they wish, including making proprietary derivatives.
Initial PROJ 4.3 public domain code was put as Frank Warmerdam as copyright
holder, but he didn't mean to imply he did the work. Essentially all work was
done by Gerald Evenden.
Copyright information can be found in source files.
--------------
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
--
This project (in particular, the web interface) uses code from wasm-proj, which is distributed under these terms:
MIT License
Copyright (c) 2025 Javier Jimenez Shaw
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--
This project uses code from libtiff, which is distributed under these terms:
Copyright © 1988-1997 Sam Leffler
Copyright © 1991-1997 Silicon Graphics, Inc.
Permission to use, copy, modify, distribute, and sell this software and
its documentation for any purpose is hereby granted without fee, provided
that (i) the above copyright notices and this permission notice appear in
all copies of the software and related documentation, and (ii) the names of
Sam Leffler and Silicon Graphics may not be used in any advertising or
publicity relating to the software without the specific, prior written
permission of Sam Leffler and Silicon Graphics.
THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
--
This project bundles SQLite, which is in the public domain. See SQLite Copyright for details.
--
This project uses zlib, which is distributed under these terms:
Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--
This project statically links musl libc for Linux builds, which is distributed under these terms:
Copyright © 2005-2020 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--
This project statically links MinGW-w64 runtime libraries for Windows builds. The MinGW-w64 runtime is distributed under different permissive licenses:
MinGW-w64 runtime licensing
***************************
This program or library was built using MinGW-w64 and statically
linked against the MinGW-w64 runtime. Some parts of the runtime
are under licenses which require that the copyright and license
notices are included when distributing the code in binary form.
These notices are listed below.
========================
Overall copyright notice
========================
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
This license has been certified as open source. It has also been designated
as GPL compatible by the Free Software Foundation (FSF).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions in source code must retain the accompanying copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the accompanying
copyright notice, this list of conditions, and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
3. Names of the copyright holders must not be used to endorse or promote
products derived from this software without prior written permission
from the copyright holders.
4. The right to distribute this software or to use it for any purpose does
not give you the right to use Servicemarks (sm) or Trademarks (tm) of
the copyright holders. Use of them is covered by separate agreement
with the copyright holders.
5. If any files are modified, you must cause the modified files to carry
prominent notices stating that you changed the files and the date of
any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
See MinGW-w64 runtime licensing
--
This project includes PROJ data files (proj.db, proj.ini) that contain coordinate system definitions from different sources (for example, EPSG). These files are distributed under the same terms as PROJ (MIT/X11 style license).
Can 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 |