@crossbind/port-geos 2.0.0-beta.58 → 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.
- package/README.md +143 -11
- 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%
|
|
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%
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
|
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://
|
|
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.
|
|
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",
|