@crossbind/port-geos 2.0.0-beta.60 → 2.0.0-beta.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +143 -11
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -5,14 +5,23 @@
5
5
  <img alt="NPM version" src="https://img.shields.io/npm/v/@crossbind/port-geos?style=for-the-badge" />
6
6
  </a>
7
7
  <a href="https://github.com/libgeos/geos">
8
- <img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Funpkg.com%2F%40crossbind%2Fpackage-geos%2Fpackage.json&query=%24.nativeVersion&style=for-the-badge&label=Geos" />
8
+ <img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Funpkg.com%2F%40crossbind%2Fport-geos%2Fpackage.json&query=%24.nativeVersion&style=for-the-badge&label=Geos" />
9
9
  </a>
10
10
  <a href="https://github.com/libgeos/geos/blob/main/COPYING">
11
- <img alt="License" src="https://img.shields.io/npm/l/%40crossbind%2Fpackage-geos?style=for-the-badge" />
11
+ <img alt="License" src="https://img.shields.io/npm/l/%40crossbind%2Fport-geos?style=for-the-badge" />
12
12
  </a>
13
13
 
14
14
  > Use it together with **[crossbind](https://crossbind.dev)** — the toolchain for using C++ libraries from JavaScript, TypeScript, WebAssembly, Node.js and React Native. Learn more at **[crossbind.dev](https://crossbind.dev)**.
15
15
 
16
+ ## See it run
17
+ Three apps on **[crossbind.dev/ports/geos](https://crossbind.dev/ports/geos/#apps)** run this package in your browser:
18
+
19
+ - **Geometry lab.** Fifteen GEOS operations on shapes you type as WKT, drawn over their inputs next to the DE-9IM matrix of how the shapes relate. A river crossing a park with a pond keeps 51.77 units of its length inside the park, and the two relate as `1F20F1102`.
20
+ - **Validity doctor.** Seven broken polygons, what GEOS finds wrong and where, and both of its repair methods side by side. A hole that leaks out of its shell repairs to 150 square units with the linework method and to 75 with the structure method.
21
+ - **Coverage simplifier.** A generated map of 36 regions simplified at tolerance 2. One region at a time, 356.917 square units of gaps and 186.433 of overlaps open between neighbours; simplified as one coverage, none do.
22
+
23
+ Their C++ wrappers, and the self-check the site build runs against values computed independently of this package, are in [`landing/demos/lib-geos`](https://github.com/crossbind/crossbind/tree/main/landing/demos/lib-geos).
24
+
16
25
  ## Integration
17
26
  Install the main package together with the platform builds:
18
27
 
@@ -39,18 +48,134 @@ export default {
39
48
  };
40
49
  ```
41
50
 
51
+ A native Node.js addon links the build of its platform: `crossbind build -p darwin`, `-p linux` or `-p win32` takes `@crossbind/port-geos-darwin`, `-linux` or `-win32`. Install it and import its `crossbind.config.js` the same way.
52
+
42
53
  ## Usage
43
- Below are the steps to use GEOS in your C++ or JavaScript code.
54
+ crossbind binds your C++ headers to JavaScript, so the usual pattern is a small wrapper around the library. This one runs GEOS overlays on shapes written as WKT, through GEOS's reentrant C API. Put it in your project's native folder (`src/native/` by default):
44
55
 
45
- ### Usage in C++ Code
46
- ```diff
47
- +#include <geos_c.h>
56
+ ```cpp
57
+ // src/native/overlay.h
58
+ #pragma once
59
+
60
+ #include <geos_c.h>
61
+
62
+ #include <memory>
63
+ #include <stdexcept>
64
+ #include <string>
65
+
66
+ // Overlay of two geometries written as WKT. Every result is normalised, so the same shape always
67
+ // prints the same WKT whatever order its vertices came in.
68
+ class Overlay {
69
+ public:
70
+ static std::string version() { return GEOSversion(); }
71
+
72
+ static std::string intersection(const std::string& a, const std::string& b) { return apply(GEOSIntersection_r, a, b); }
73
+ static std::string unite(const std::string& a, const std::string& b) { return apply(GEOSUnion_r, a, b); }
74
+ static std::string difference(const std::string& a, const std::string& b) { return apply(GEOSDifference_r, a, b); }
75
+
76
+ static double area(const std::string& wkt) {
77
+ Session geos;
78
+ double value = 0;
79
+ if (!GEOSArea_r(geos.context, geos.read(wkt).get(), &value)) geos.fail();
80
+ return value;
81
+ }
82
+
83
+ private:
84
+ using Operation = GEOSGeometry* (*)(GEOSContextHandle_t, const GEOSGeometry*, const GEOSGeometry*);
85
+
86
+ static std::string apply(Operation operation, const std::string& a, const std::string& b) {
87
+ Session geos;
88
+ const auto left = geos.read(a);
89
+ const auto right = geos.read(b);
90
+ return geos.result(operation(geos.context, left.get(), right.get()));
91
+ }
92
+
93
+ // GEOS's reentrant C API: each call gets its own context, which also holds the last error message.
94
+ struct Session {
95
+ struct Destroy {
96
+ GEOSContextHandle_t context;
97
+ void operator()(GEOSGeometry* geometry) const { GEOSGeom_destroy_r(context, geometry); }
98
+ };
99
+ using Geometry = std::unique_ptr<GEOSGeometry, Destroy>;
100
+
101
+ GEOSContextHandle_t context = GEOS_init_r();
102
+ std::string error;
103
+
104
+ Session() { GEOSContext_setErrorMessageHandler_r(context, remember, &error); }
105
+ ~Session() { GEOS_finish_r(context); }
106
+ Session(const Session&) = delete;
107
+ Session& operator=(const Session&) = delete;
48
108
 
49
- std::string Native::sample() {
50
- + return std::string(GEOSversion());
51
- }
109
+ Geometry own(GEOSGeometry* geometry) {
110
+ if (!geometry) fail();
111
+ return Geometry(geometry, Destroy{context});
112
+ }
113
+
114
+ Geometry read(const std::string& wkt) {
115
+ GEOSWKTReader* reader = GEOSWKTReader_create_r(context);
116
+ GEOSGeometry* geometry = GEOSWKTReader_read_r(context, reader, wkt.c_str());
117
+ GEOSWKTReader_destroy_r(context, reader);
118
+ return own(geometry);
119
+ }
120
+
121
+ std::string write(const GEOSGeometry* geometry) {
122
+ GEOSWKTWriter* writer = GEOSWKTWriter_create_r(context);
123
+ char* text = GEOSWKTWriter_write_r(context, writer, geometry);
124
+ GEOSWKTWriter_destroy_r(context, writer);
125
+ if (!text) fail();
126
+ const std::string wkt = text;
127
+ GEOSFree_r(context, text);
128
+ return wkt;
129
+ }
130
+
131
+ // Takes ownership of a result and writes it normalised.
132
+ std::string result(GEOSGeometry* geometry) {
133
+ const Geometry owned = own(geometry);
134
+ if (GEOSNormalize_r(context, owned.get()) != 0) fail();
135
+ return write(owned.get());
136
+ }
137
+
138
+ [[noreturn]] void fail() const { throw std::runtime_error(error.empty() ? "GEOS operation failed" : error); }
139
+
140
+ static void remember(const char* message, void* error) { *static_cast<std::string*>(error) = message; }
141
+ };
142
+ };
143
+ ```
144
+
145
+ Then call it from JavaScript:
146
+
147
+ ```js
148
+ import { initNative, Overlay } from './native/overlay.h';
149
+
150
+ await initNative();
151
+ const parcel = 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))';
152
+ const floodZone = 'POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))';
153
+ const flooded = await Overlay.intersection(parcel, floodZone);
154
+ console.log(await Overlay.version()); // 3.15.0-CAPI-1.21.0
155
+ console.log(flooded, await Overlay.area(flooded)); // POLYGON ((5 5, 5 10, 10 10, 10 5, 5 5)) 25
156
+ console.log(await Overlay.area(await Overlay.unite(parcel, floodZone)), await Overlay.area(await Overlay.difference(parcel, floodZone))); // 175 75
52
157
  ```
53
158
 
159
+ - Shapes cross the binding as WKT strings, and results come back normalised, so the same shape always prints the same WKT.
160
+ - GEOS works in the plane: areas and distances are in the units of the coordinates. Project longitude and latitude before measuring them.
161
+ - Each call opens its own GEOS context with `GEOS_init_r`, which keeps the reentrant C API safe to call from several threads. When GEOS fails, the wrapper throws GEOS's message; for broken WKT, JavaScript catches an error reading `std::runtime_error: ParseException: Expected word but encountered end of stream`.
162
+
163
+ ### More examples
164
+ Each one runs in your browser on [crossbind.dev/ports/geos](https://crossbind.dev/ports/geos/#usage), next to the code shown there:
165
+
166
+ - [Test points against a prepared polygon](https://crossbind.dev/ports/geos/#02-zone): `GEOSPrepare_r` once, then `GEOSPreparedContainsXY_r` and `GEOSPreparedIntersectsXY_r` for each point, and `GEOSPreparedRelate_r` for the DE-9IM matrix.
167
+ - [Measure area, length and distance](https://crossbind.dev/ports/geos/#03-measure): `GEOSArea_r`, `GEOSLength_r`, `GEOSDistance_r`, `GEOSNearestPoints_r` and `GEOSGetCentroid_r`.
168
+ - [Buffer points, lines and polygons](https://crossbind.dev/ports/geos/#04-buffer): `GEOSBuffer_r`, `GEOSBufferWithStyle_r` with cap and join styles, and `GEOSOffsetCurve_r`.
169
+ - [Find why a polygon is invalid and repair it](https://crossbind.dev/ports/geos/#05-validity): `GEOSisValidReason_r`, and `GEOSMakeValidWithParams_r` with both of its methods.
170
+
171
+ Setup and differences per platform: [WebAssembly](https://crossbind.dev/ports/geos/wasm/) · [Android](https://crossbind.dev/ports/geos/android/) · [iOS](https://crossbind.dev/ports/geos/ios/) · [macOS](https://crossbind.dev/ports/geos/darwin/) · [Linux](https://crossbind.dev/ports/geos/linux/) · [Windows](https://crossbind.dev/ports/geos/win32/) · [WASI](https://crossbind.dev/ports/geos/wasi/), which also has a command-line program built with `crossbind build -p wasi`.
172
+
173
+ ## What this build includes
174
+ - GEOS 3.15.0 (C API 1.21.0) as two static libraries: `libgeos_c`, the stable C API the examples use, and `libgeos`, the C++ library behind it.
175
+ - The C API the apps above run: overlays, prepared predicates and DE-9IM, buffers and offset curves, validity checks and both repair methods, coverage simplification and validation, Delaunay and Voronoi, hulls, and WKT and GeoJSON output.
176
+ - No data files: a GEOS module is its `.wasm` and loader alone. The module behind the five examples and three apps is 1,612,592 bytes of WebAssembly and 138,052 bytes of JavaScript.
177
+ - GEOS's own `geosop` tool is not in the library packages; `@crossbind/port-geos-bin-wasi` ships it as a WASI command.
178
+
54
179
  ## Supported platforms
55
180
  This is the main package; the precompiled binaries are shipped per platform:
56
181
 
@@ -59,8 +184,15 @@ This is the main package; the precompiled binaries are shipped per platform:
59
184
  | WebAssembly | [`@crossbind/port-geos-wasm`](https://www.npmjs.com/package/@crossbind/port-geos-wasm) | `wasm32` — single-threaded & multi-threaded |
60
185
  | Android | [`@crossbind/port-geos-android`](https://www.npmjs.com/package/@crossbind/port-geos-android) | `arm64-v8a` (64-bit ARM), `x86_64` (emulator) |
61
186
  | iOS | [`@crossbind/port-geos-ios`](https://www.npmjs.com/package/@crossbind/port-geos-ios) | device (`arm64`), simulator (`arm64`) |
187
+ | macOS | [`@crossbind/port-geos-darwin`](https://www.npmjs.com/package/@crossbind/port-geos-darwin) | `arm64` (Apple silicon), `x64` (Intel) — native Node.js addons |
188
+ | Linux | [`@crossbind/port-geos-linux`](https://www.npmjs.com/package/@crossbind/port-geos-linux) | `x64`, `arm64` — glibc 2.28 or later, native Node.js addons |
189
+ | Windows | [`@crossbind/port-geos-win32`](https://www.npmjs.com/package/@crossbind/port-geos-win32) | `x64`, `arm64` — Windows 10 or later, native Node.js addons |
190
+ | WASI library | [`@crossbind/port-geos-wasi`](https://www.npmjs.com/package/@crossbind/port-geos-wasi) | `wasm32-wasip3` — single-threaded |
191
+ | WASI command | [`@crossbind/port-geos-bin-wasi`](https://www.npmjs.com/package/@crossbind/port-geos-bin-wasi) | the upstream `geosop` CLI as a `geosop-wasi` command (wasmtime 47+) |
62
192
 
63
193
  ## License
64
- This project includes the precompiled GEOS library, which is distributed under the [LGPL License](https://github.com/libgeos/geos/blob/main/COPYING).
194
+ This project includes the precompiled GEOS library, which is distributed under the [GNU LGPL 2.1](https://github.com/libgeos/geos/blob/main/COPYING).
195
+
196
+ An app that ships GEOS conveys it, a web app included: carry GEOS's licence, which is this package's `LICENSE` file, and point to its source. The [LGPL playbook](https://github.com/crossbind/crossbind/blob/main/docs/playbooks/licensing-lgpl.md) covers the rest, such as keeping the WebAssembly a separate, replaceable file.
65
197
 
66
- GEOS Homepage: [https://github.com/libgeos/geos](https://github.com/libgeos/geos)
198
+ GEOS Homepage: [https://libgeos.org/](https://libgeos.org/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossbind/port-geos",
3
- "version": "2.0.0-beta.60",
3
+ "version": "2.0.0-beta.62",
4
4
  "nativeVersion": "3.15.0",
5
5
  "description": "This package provides the precompiled GEOS library, built using crossbind, for seamless integration into JavaScript, WebAssembly, and React Native projects. It supports advanced geometry operations, including spatial analysis and topological calculations, delivering high performance and cross-platform compatibility. Perfect for web, server-side, and mobile geospatial applications.",
6
6
  "homepage": "https://github.com/crossbind/crossbind/tree/main/ports/geos#readme",