tg_geometry 0.1.0

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +103 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +385 -0
  6. data/Rakefile +129 -0
  7. data/benchmark/_support.rb +115 -0
  8. data/benchmark/batch_packed_vs_loop.rb +27 -0
  9. data/benchmark/falcon_concurrency.rb +25 -0
  10. data/benchmark/flat_vs_rtree.rb +27 -0
  11. data/benchmark/gvl_threshold.rb +41 -0
  12. data/benchmark/objectspace_memsize.rb +17 -0
  13. data/benchmark/parse_throughput.rb +38 -0
  14. data/benchmark/rss_stability.rb +70 -0
  15. data/docs/ACTIVE_RECORD.md +26 -0
  16. data/docs/ARCHITECTURE.md +130 -0
  17. data/docs/AUTO_STRATEGY.md +15 -0
  18. data/docs/BENCHMARKING.md +75 -0
  19. data/docs/CASUAL_EXAMPLE.md +618 -0
  20. data/docs/CONCURRENCY.md +65 -0
  21. data/docs/ERROR_HANDLING.md +55 -0
  22. data/docs/EXPANSION_E_TO_H_STATUS.md +51 -0
  23. data/docs/FORMAT_COVERAGE.md +23 -0
  24. data/docs/FULL_TG_API_COVERAGE.md +109 -0
  25. data/docs/LIMITATIONS.md +61 -0
  26. data/docs/LOW_LEVEL_GEOMETRY.md +121 -0
  27. data/docs/MEMORY_OWNERSHIP.md +94 -0
  28. data/docs/RACTOR.md +40 -0
  29. data/docs/REGISTRY.md +37 -0
  30. data/docs/RELEASE_CHECKLIST.md +39 -0
  31. data/ext/tg_geometry/extconf.rb +91 -0
  32. data/ext/tg_geometry/tg_geometry_ext.c +3054 -0
  33. data/ext/tg_geometry/tg_geometry_vendor_rtree.c +1 -0
  34. data/ext/tg_geometry/tg_geometry_vendor_tg.c +24 -0
  35. data/ext/tg_geometry/vendor/.vendored +16 -0
  36. data/ext/tg_geometry/vendor/rtree/LICENSE +20 -0
  37. data/ext/tg_geometry/vendor/rtree/README.md +202 -0
  38. data/ext/tg_geometry/vendor/rtree/VERSION +3 -0
  39. data/ext/tg_geometry/vendor/rtree/rtree.c +840 -0
  40. data/ext/tg_geometry/vendor/rtree/rtree.h +105 -0
  41. data/ext/tg_geometry/vendor/tg/LICENSE +19 -0
  42. data/ext/tg_geometry/vendor/tg/README.md +197 -0
  43. data/ext/tg_geometry/vendor/tg/VERSION +3 -0
  44. data/ext/tg_geometry/vendor/tg/tg.c +16010 -0
  45. data/ext/tg_geometry/vendor/tg/tg.h +359 -0
  46. data/lib/tg/geometry/active_record_source.rb +57 -0
  47. data/lib/tg/geometry/registry.rb +119 -0
  48. data/lib/tg/geometry/version.rb +7 -0
  49. data/lib/tg/geometry.rb +6 -0
  50. data/lib/tg_geometry.rb +3 -0
  51. data/script/vendor_libs.rb +264 -0
  52. data/spec/block_10_rtree_strategy_spec.rb +82 -0
  53. data/spec/block_11_rtree_order_spec.rb +53 -0
  54. data/spec/block_12_batch_packed_spec.rb +55 -0
  55. data/spec/block_13_error_hardening_spec.rb +65 -0
  56. data/spec/block_14_memory_gc_hardening_spec.rb +116 -0
  57. data/spec/block_1_skeleton_spec.rb +45 -0
  58. data/spec/block_20_concurrency_spec.rb +157 -0
  59. data/spec/block_20_fuzz_spec.rb +145 -0
  60. data/spec/block_2_vendor_spec.rb +79 -0
  61. data/spec/block_3_geom_parse_spec.rb +89 -0
  62. data/spec/block_4_geom_api_spec.rb +90 -0
  63. data/spec/block_5_rect_api_spec.rb +96 -0
  64. data/spec/block_6_index_build_spec.rb +111 -0
  65. data/spec/block_7_index_owned_geometry_spec.rb +143 -0
  66. data/spec/block_8_index_borrowed_geometry_spec.rb +106 -0
  67. data/spec/block_9_flat_query_spec.rb +65 -0
  68. data/spec/expansion_a_auto_strategy_spec.rb +14 -0
  69. data/spec/expansion_b_registry_spec.rb +47 -0
  70. data/spec/expansion_c_active_record_source_spec.rb +42 -0
  71. data/spec/expansion_d_format_coverage_spec.rb +30 -0
  72. data/spec/expansion_e_low_level_geometry_spec.rb +82 -0
  73. data/spec/expansion_i_ractor_spec.rb +25 -0
  74. data/spec/expansion_j_full_tg_api_coverage_spec.rb +114 -0
  75. data/spec/spec_helper.rb +15 -0
  76. metadata +157 -0
@@ -0,0 +1,105 @@
1
+ // Copyright 2023 Joshua J Baker. All rights reserved.
2
+ // Use of this source code is governed by an MIT-style
3
+ // license that can be found in the LICENSE file.
4
+
5
+ #ifndef RTREE_H
6
+ #define RTREE_H
7
+
8
+ #include <stdlib.h>
9
+ #include <stdbool.h>
10
+
11
+ // rtree_new returns a new rtree
12
+ //
13
+ // Returns NULL if the system is out of memory.
14
+ struct rtree *rtree_new(void);
15
+
16
+
17
+ // rtree_new returns a new rtree using a custom allocator
18
+ //
19
+ // Returns NULL if the system is out of memory.
20
+ struct rtree *rtree_new_with_allocator(void *(*malloc)(size_t), void (*free)(void*));
21
+
22
+ // rtree_free frees an rtree
23
+ void rtree_free(struct rtree *tr);
24
+
25
+ // rtree_clone makes an instant copy of the btree.
26
+ //
27
+ // This operation uses shadowing / copy-on-write.
28
+ struct rtree *rtree_clone(struct rtree *tr);
29
+
30
+ // rtree_set_item_callbacks sets the item clone and free callbacks that will be
31
+ // called internally by the rtree when items are inserted and removed.
32
+ //
33
+ // These callbacks are optional but may be needed by programs that require
34
+ // copy-on-write support by using the rtree_clone function.
35
+ //
36
+ // The clone function should return true if the clone succeeded or false if the
37
+ // system is out of memory.
38
+ void rtree_set_item_callbacks(struct rtree *tr,
39
+ bool (*clone)(const void *item, void **into, void *udata),
40
+ void (*free)(const void *item, void *udata));
41
+
42
+ // rtree_set_udata sets the user-defined data.
43
+ //
44
+ // This should be called once after rtree_new() and is only used for
45
+ // the item callbacks as defined in rtree_set_item_callbacks().
46
+ void rtree_set_udata(struct rtree *tr, void *udata);
47
+
48
+ // rtree_insert inserts an item into the rtree.
49
+ //
50
+ // This operation performs a copy of the data that is pointed to in the second
51
+ // and third arguments. The R-tree expects a rectangle, which is two arrays of
52
+ // doubles. The first N values as the minimum corner of the rect, and the next
53
+ // N values as the maximum corner of the rect, where N is the number of
54
+ // dimensions.
55
+ //
56
+ // When inserting points, the max coordinates is optional (set to NULL).
57
+ //
58
+ // Returns false if the system is out of memory.
59
+ bool rtree_insert(struct rtree *tr, const double *min, const double *max, const void *data);
60
+
61
+
62
+ // rtree_search searches the rtree and iterates over each item that intersect
63
+ // the provided rectangle.
64
+ //
65
+ // Returning false from the iter will stop the search.
66
+ void rtree_search(const struct rtree *tr, const double *min, const double *max,
67
+ bool (*iter)(const double *min, const double *max, const void *data, void *udata),
68
+ void *udata);
69
+
70
+ // rtree_scan iterates over every item in the rtree.
71
+ //
72
+ // Returning false from the iter will stop the scan.
73
+ void rtree_scan(const struct rtree *tr,
74
+ bool (*iter)(const double *min, const double *max, const void *data, void *udata),
75
+ void *udata);
76
+
77
+ // rtree_count returns the number of items in the rtree.
78
+ size_t rtree_count(const struct rtree *tr);
79
+
80
+ // rtree_delete deletes an item from the rtree.
81
+ //
82
+ // This searches the tree for an item that is contained within the provided
83
+ // rectangle, and perform a binary comparison of its data to the provided
84
+ // data. The first item that is found is deleted.
85
+ //
86
+ // Returns false if the system is out of memory.
87
+ bool rtree_delete(struct rtree *tr, const double *min, const double *max, const void *data);
88
+
89
+ // rtree_delete_with_comparator deletes an item from the rtree.
90
+ // This searches the tree for an item that is contained within the provided
91
+ // rectangle, and perform a comparison of its data to the provided data using
92
+ // a compare function. The first item that is found is deleted.
93
+ //
94
+ // Returns false if the system is out of memory.
95
+ bool rtree_delete_with_comparator(struct rtree *tr, const double *min,
96
+ const double *max, const void *data,
97
+ int (*compare)(const void *a, const void *b, void *udata),
98
+ void *udata);
99
+
100
+ // rtree_opt_relaxed_atomics activates memory_order_relaxed for all atomic
101
+ // loads. This may increase performance for single-threaded programs.
102
+ // Optionally, define RTREE_NOATOMICS to disbale all atomics.
103
+ void rtree_opt_relaxed_atomics(struct rtree *tr);
104
+
105
+ #endif // RTREE_H
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2023 Joshua J Baker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,197 @@
1
+ <p align="center">
2
+ <img src="docs/assets/logo.png" width="240" alt="TG">
3
+ </p>
4
+ <p align="center">
5
+ <a href="docs/API.md"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="API Reference"></a>
6
+ </p>
7
+
8
+ TG is a geometry library for C that is small, fast, and easy to use.
9
+ It's designed for programs that need real-time geospatial, such as geofencing, monitoring, and streaming analysis.
10
+
11
+ ## Features
12
+
13
+ - Implements OGC [Simple Features](https://en.wikipedia.org/wiki/Simple_Features) including Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection.
14
+ - Optimized [polygon indexing](docs/POLYGON_INDEXING.md) that introduces two new structures.
15
+ - Reads and writes [GeoJSON](https://en.wikipedia.org/wiki/GeoJSON), [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry), [WKB](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry), and [GeoBIN](docs/GEOBIN.md).
16
+ - Provides a purely functional [API](docs/API.md) that is reentrant and thread-safe.
17
+ - Spatial predicates including "intersects", "covers", "touches", "equals", etc.
18
+ - Compiles to Webassembly using Emscripten
19
+ - [Test suite](tests/README.md) with 100% coverage using sanitizers and [Valgrind](https://valgrind.org).
20
+ - Self-contained library that is encapsulated in the single [tg.c](tg.c) source file.
21
+ - Pretty darn good performance. 🚀 <sup>[[benchmarks]](docs/BENCHMARKS.md)</sup>
22
+
23
+ ## Goals
24
+
25
+ The main goal of TG is to provide the fastest, most memory efficient geometry library for the purpose of monitoring spatial relationships, specifically operations like point-in-polygon and geometry intersect.
26
+
27
+ It's a non-goal for TG to be a full GIS library. Consider [GEOS](https://libgeos.org) if you need GIS algorithms like generating a convex hull or voronoi diagram. The [tgx](https://github.com/tidwall/tgx) library is available for bridging GEOS and TG geometries, which allows for performing GEOS operations on a TG geometry.
28
+
29
+ ## Performance
30
+
31
+ TG uses [entirely new](docs/POLYGON_INDEXING.md) indexing structures that speed up [geometry predicates](docs/API.md#geometry-predicates). It can index more than 10GB per second of point data on modern hardware, while using less than 7% of additional memory, and can perform over 10 million point-in-polygon operations per second, even when using large polygons with over 10K points.
32
+
33
+ The following benchmark provides an example of the parsing, indexing, and point-in-polygon speed of TG vs GEOS when using a large polygon. In this case Brazil, which has 39K points.
34
+
35
+ <pre>
36
+ <b>Brazil ops/sec ns/op points hits built bytes</b>
37
+ tg/none 96,944 10315 39914 3257 46.73 µs 638,720
38
+ tg/natural 10,143,419 99 39914 3257 53.17 µs 681,360
39
+ tg/ystripes 15,174,761 66 39914 3257 884.06 µs 1,059,548
40
+ geos/none 29,708 33661 39914 3257 135.18 µs 958,104
41
+ geos/prepared 7,885,512 127 39914 3257 2059.94 µs 3,055,496
42
+ </pre>
43
+
44
+ - "built": Column showing how much time it took to construct the polygon and index.
45
+ - "bytes": Column showing the final in-memory size of the polygon and index.
46
+ - "none": No indexing was used.
47
+ - "natural": Using TG [Natural](docs/POLYGON_INDEXING.md#natural) indexing
48
+ - "ystripes": Using TG [YStripes](docs/POLYGON_INDEXING.md#ystripes) indexing
49
+ - "prepared": Using a [GEOS](https://libgeos.org) PreparedGeometry
50
+
51
+ See all [benchmarks](docs/BENCHMARKS.md) for more information.
52
+
53
+ ## Using
54
+
55
+ Just drop the "tg.c" and "tg.h" files into your project.
56
+ Uses standard C11 so most modern C compilers should work.
57
+
58
+ ```sh
59
+ $ cc -c tg.c
60
+ ```
61
+
62
+ ## Programmer notes
63
+
64
+ *Check out the complete [API](docs/API.md) for detailed information.*
65
+
66
+ #### Pure functions
67
+
68
+ TG library functions are thread-safe, reentrant, and (mostly) without side
69
+ effects.
70
+ The exception being with the use of malloc by some functions like
71
+ [geometry constructors](docs/API.md#geometry-constructors).
72
+ In those cases, it's the programmer's responsibiilty to check the return value
73
+ before continuing.
74
+
75
+ ```c
76
+ struct tg_geom *geom = tg_geom_new_point(-112, 33);
77
+ if (!geom) {
78
+ // System is out of memory.
79
+ }
80
+ ```
81
+
82
+ #### Fast cloning
83
+
84
+ The cloning of geometries, as with [tg_geom_clone()](docs/API.md#tg_geom_clone), are
85
+ O(1) operations that use implicit sharing through an atomic reference counter.
86
+ Geometry constructors like [tg_geom_new_polygon()](docs/API.md#tg_geom_new_polygon) will
87
+ use this method under the hood to maintain references of its inputs.
88
+
89
+ While this may only be an implementation detail, it's important for the
90
+ programmer to understand how TG uses memory and object references.
91
+
92
+ For example:
93
+
94
+ ```c
95
+ struct tg_geom *geom = tg_geom_new_polygon(exterior, holes, nholes);
96
+ ```
97
+
98
+ Above, a new geometry "geom" was created and includes a cloned reference to the
99
+ [tg_ring](docs/API.md#tg_ring) "exterior" and all of the holes.
100
+
101
+ Providing `TG_NOATOMICS` to the compiler will disable the use of atomics and
102
+ instead use non-atomic reference counters.
103
+
104
+ ```
105
+ cc -DTG_NOATOMICS tg.c ...
106
+ ```
107
+
108
+ Alternatively, the [tg_geom_copy()](docs/API.md#tg_geom_copy) method is available to perform a deep copy of the
109
+ geometry.
110
+
111
+ #### Avoid memory leaks
112
+
113
+ To avoid memory leaks, call [tg_geom_free()](docs/API.md#tg_geom_free) on geometries
114
+ created from [geometry constructors](docs/API.md#geometry-constructors),
115
+ [geometry parsers](docs/API.md#geometry-parsing), [tg_geom_clone()](docs/API.md#tg_geom_clone), and [tg_geom_copy()](docs/API.md#tg_geom_copy)
116
+
117
+ In other words, for every `tg_geom_new_*()`, `tg_geom_parse_*()`,
118
+ `tg_geom_clone()`, and `tg_geom_copy()` there should be (eventually and exactly)
119
+ one `tg_geom_free()`.
120
+
121
+ #### Upcasting
122
+
123
+ The TG object types [tg_line](docs/API.md#tg_line), [tg_ring](docs/API.md#tg_ring), and
124
+ [tg_poly](docs/API.md#tg_poly) can be safely upcasted to a [tg_geom](docs/API.md#tg_geom) with no
125
+ cost at runtime.
126
+
127
+ ```c
128
+ struct tg_geom *geom1 = (struct tg_geom*)line; // Cast tg_line to tg_geom
129
+ struct tg_geom *geom2 = (struct tg_geom*)ring; // Cast tg_ring to tg_geom
130
+ struct tg_geom *geom3 = (struct tg_geom*)poly; // Cast tg_poly to tg_geom
131
+ ```
132
+
133
+ This allows for exposing all [tg_geom](docs/API.md#tg_geom) functions to the other
134
+ object types.
135
+
136
+ In addition, the [tg_ring](docs/API.md#tg_ring) type can also cast to a
137
+ [tg_poly](docs/API.md#tg_poly).
138
+
139
+ ```c
140
+ struct tg_poly *poly = (struct tg_poly*)ring; // Cast tg_ring to tg_poly
141
+ ```
142
+
143
+ *Do not downcast. It's not generally safe to cast from a [tg_geom](docs/API.md#tg_geom) to other
144
+ types.*
145
+
146
+
147
+ ## Example
148
+
149
+ Create a program that tests if two geometries intersect using [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) as inputs.
150
+
151
+ ```c
152
+ #include <stdio.h>
153
+ #include <tg.h>
154
+
155
+ int main(int argc, char **argv) {
156
+ if (argc != 3) {
157
+ fprintf(stderr, "Usage: %s <geom-a> <geom-b>\n", argv[0]);
158
+ return 1;
159
+ }
160
+
161
+ // Parse the input geometries and check for errors.
162
+ struct tg_geom *a = tg_parse_wkt(argv[1]);
163
+ if (tg_geom_error(a)) {
164
+ fprintf(stderr, "%s\n", tg_geom_error(a));
165
+ return 1;
166
+ }
167
+ struct tg_geom *b = tg_parse_wkt(argv[2]);
168
+ if (tg_geom_error(b)) {
169
+ fprintf(stderr, "%s\n", tg_geom_error(b));
170
+ return 1;
171
+ }
172
+
173
+ // Execute the "intersects" predicate to test if both geometries intersect.
174
+ if (tg_geom_intersects(a, b)) {
175
+ printf("yes\n");
176
+ } else {
177
+ printf("no\n");
178
+ }
179
+
180
+ // Free geometries when done.
181
+ tg_geom_free(a);
182
+ tg_geom_free(b);
183
+ return 0;
184
+ }
185
+ ```
186
+
187
+ Build and run the example:
188
+
189
+ ```sh
190
+ $ cc -I. examples/intersects.c tg.c
191
+ $ ./a.out 'POINT(15 15)' 'POLYGON((10 10,20 10,20 20,10 20,10 10))'
192
+ yes
193
+ ```
194
+
195
+ ## License
196
+
197
+ TG source code is available under the MIT [License](/LICENSE).
@@ -0,0 +1,3 @@
1
+ repo=https://github.com/tidwall/tg.git
2
+ ref=main
3
+ commit=caf840504eaab4563280cf4ab16d618f69a23720