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,359 @@
1
+ // https://github.com/tidwall/tg
2
+ //
3
+ // Copyright 2023 Joshua J Baker. All rights reserved.
4
+ // Use of this source code is governed by a license
5
+ // that can be found in the LICENSE file.
6
+
7
+ #ifndef TG_H
8
+ #define TG_H
9
+
10
+ #include <stdbool.h>
11
+ #include <stdint.h>
12
+ #include <stddef.h>
13
+
14
+ /// The base point type used for all geometries.
15
+ /// @see PointFuncs
16
+ struct tg_point {
17
+ double x;
18
+ double y;
19
+ };
20
+
21
+ /// The base segment type used in tg_line and tg_ring for joining two vertices.
22
+ /// @see SegmentFuncs
23
+ struct tg_segment {
24
+ struct tg_point a;
25
+ struct tg_point b;
26
+ };
27
+
28
+ /// A rectangle defined by a minimum and maximum coordinates.
29
+ /// Returned by the tg_geom_rect(), tg_ring_rect(), and other \*_rect()
30
+ /// functions for getting a geometry's minimum bounding rectangle.
31
+ /// Also used internally for geometry indexing.
32
+ /// @see RectFuncs
33
+ struct tg_rect {
34
+ struct tg_point min;
35
+ struct tg_point max;
36
+ };
37
+
38
+ struct tg_line; ///< Find the description in the tg.c file.
39
+ struct tg_ring; ///< Find the description in the tg.c file.
40
+ struct tg_poly; ///< Find the description in the tg.c file.
41
+ struct tg_geom; ///< Find the description in the tg.c file.
42
+
43
+ /// Geometry types.
44
+ ///
45
+ /// All tg_geom are one of the following underlying types.
46
+ ///
47
+ /// @see tg_geom_typeof()
48
+ /// @see tg_geom_type_string()
49
+ /// @see GeometryAccessors
50
+ enum tg_geom_type {
51
+ TG_POINT = 1, ///< Point
52
+ TG_LINESTRING = 2, ///< LineString
53
+ TG_POLYGON = 3, ///< Polygon
54
+ TG_MULTIPOINT = 4, ///< MultiPoint, collection of points
55
+ TG_MULTILINESTRING = 5, ///< MultiLineString, collection of linestrings
56
+ TG_MULTIPOLYGON = 6, ///< MultiPolygon, collection of polygons
57
+ TG_GEOMETRYCOLLECTION = 7, ///< GeometryCollection, collection of geometries
58
+ };
59
+
60
+ /// Geometry indexing options.
61
+ ///
62
+ /// Used for polygons, rings, and lines to make the point-in-polygon and
63
+ /// geometry intersection operations fast.
64
+ ///
65
+ /// An index can also be used for efficiently traversing, searching, and
66
+ /// performing nearest-neighbor (kNN) queries on the segment using
67
+ /// tg_ring_index_*() and tg_ring_nearest() functions.
68
+ enum tg_index {
69
+ TG_DEFAULT, ///< default is TG_NATURAL or tg_env_set_default_index().
70
+ TG_NONE, ///< no indexing available, or disabled.
71
+ TG_NATURAL, ///< indexing with natural ring order, for rings/lines
72
+ TG_YSTRIPES, ///< indexing using segment striping, rings only
73
+ };
74
+
75
+ /// @defgroup GeometryConstructors Geometry constructors
76
+ /// Functions for creating and freeing geometries.
77
+ /// @{
78
+ struct tg_geom *tg_geom_new_point(struct tg_point point);
79
+ struct tg_geom *tg_geom_new_linestring(const struct tg_line *line);
80
+ struct tg_geom *tg_geom_new_polygon(const struct tg_poly *poly);
81
+ struct tg_geom *tg_geom_new_multipoint(const struct tg_point *points,int npoints);
82
+ struct tg_geom *tg_geom_new_multilinestring(const struct tg_line *const lines[], int nlines);
83
+ struct tg_geom *tg_geom_new_multipolygon(const struct tg_poly *const polys[], int npolys);
84
+ struct tg_geom *tg_geom_new_geometrycollection(const struct tg_geom *const geoms[], int ngeoms);
85
+ struct tg_geom *tg_geom_new_error(const char *errmsg);
86
+ struct tg_geom *tg_geom_clone(const struct tg_geom *geom);
87
+ struct tg_geom *tg_geom_copy(const struct tg_geom *geom);
88
+ void tg_geom_free(struct tg_geom *geom);
89
+ /// @}
90
+
91
+ /// @defgroup GeometryAccessors Geometry accessors
92
+ /// Functions for accessing various information about geometries, such as
93
+ /// getting the geometry type or extracting underlying components or
94
+ /// coordinates.
95
+ /// @{
96
+ enum tg_geom_type tg_geom_typeof(const struct tg_geom *geom);
97
+ const char *tg_geom_type_string(enum tg_geom_type type);
98
+ struct tg_rect tg_geom_rect(const struct tg_geom *geom);
99
+ bool tg_geom_is_feature(const struct tg_geom *geom);
100
+ bool tg_geom_is_featurecollection(const struct tg_geom *geom);
101
+ struct tg_point tg_geom_point(const struct tg_geom *geom);
102
+ const struct tg_line *tg_geom_line(const struct tg_geom *geom);
103
+ const struct tg_poly *tg_geom_poly(const struct tg_geom *geom);
104
+ int tg_geom_num_points(const struct tg_geom *geom);
105
+ struct tg_point tg_geom_point_at(const struct tg_geom *geom, int index);
106
+ int tg_geom_num_lines(const struct tg_geom *geom);
107
+ const struct tg_line *tg_geom_line_at(const struct tg_geom *geom, int index);
108
+ int tg_geom_num_polys(const struct tg_geom *geom);
109
+ const struct tg_poly *tg_geom_poly_at(const struct tg_geom *geom, int index);
110
+ int tg_geom_num_geometries(const struct tg_geom *geom);
111
+ const struct tg_geom *tg_geom_geometry_at(const struct tg_geom *geom, int index);
112
+ const char *tg_geom_extra_json(const struct tg_geom *geom);
113
+ bool tg_geom_is_empty(const struct tg_geom *geom);
114
+ int tg_geom_dims(const struct tg_geom *geom);
115
+ bool tg_geom_has_z(const struct tg_geom *geom);
116
+ bool tg_geom_has_m(const struct tg_geom *geom);
117
+ double tg_geom_z(const struct tg_geom *geom);
118
+ double tg_geom_m(const struct tg_geom *geom);
119
+ const double *tg_geom_extra_coords(const struct tg_geom *geom);
120
+ int tg_geom_num_extra_coords(const struct tg_geom *geom);
121
+ size_t tg_geom_memsize(const struct tg_geom *geom);
122
+ void tg_geom_search(const struct tg_geom *geom, struct tg_rect rect,
123
+ bool (*iter)(const struct tg_geom *geom, int index, void *udata),
124
+ void *udata);
125
+ int tg_geom_fullrect(const struct tg_geom *geom, double min[4], double max[4]);
126
+ /// @}
127
+
128
+ /// @defgroup GeometryPredicates Geometry predicates
129
+ /// Functions for testing the spatial relations of two geometries.
130
+ /// @{
131
+ bool tg_geom_equals(const struct tg_geom *a, const struct tg_geom *b);
132
+ bool tg_geom_intersects(const struct tg_geom *a, const struct tg_geom *b);
133
+ bool tg_geom_disjoint(const struct tg_geom *a, const struct tg_geom *b);
134
+ bool tg_geom_contains(const struct tg_geom *a, const struct tg_geom *b);
135
+ bool tg_geom_within(const struct tg_geom *a, const struct tg_geom *b);
136
+ bool tg_geom_covers(const struct tg_geom *a, const struct tg_geom *b);
137
+ bool tg_geom_coveredby(const struct tg_geom *a, const struct tg_geom *b);
138
+ bool tg_geom_touches(const struct tg_geom *a, const struct tg_geom *b);
139
+ bool tg_geom_intersects_rect(const struct tg_geom *a, struct tg_rect b);
140
+ bool tg_geom_intersects_xy(const struct tg_geom *a, double x, double y);
141
+ /// @}
142
+
143
+ /// @defgroup GeometryParsing Geometry parsing
144
+ /// Functions for parsing geometries from external data representations.
145
+ /// It's recommended to use tg_geom_error() after parsing to check for errors.
146
+ /// @{
147
+ struct tg_geom *tg_parse_geojson(const char *geojson);
148
+ struct tg_geom *tg_parse_geojsonn(const char *geojson, size_t len);
149
+ struct tg_geom *tg_parse_geojson_ix(const char *geojson, enum tg_index ix);
150
+ struct tg_geom *tg_parse_geojsonn_ix(const char *geojson, size_t len, enum tg_index ix);
151
+ struct tg_geom *tg_parse_wkt(const char *wkt);
152
+ struct tg_geom *tg_parse_wktn(const char *wkt, size_t len);
153
+ struct tg_geom *tg_parse_wkt_ix(const char *wkt, enum tg_index ix);
154
+ struct tg_geom *tg_parse_wktn_ix(const char *wkt, size_t len, enum tg_index ix);
155
+ struct tg_geom *tg_parse_wkb(const uint8_t *wkb, size_t len);
156
+ struct tg_geom *tg_parse_wkb_ix(const uint8_t *wkb, size_t len, enum tg_index ix);
157
+ struct tg_geom *tg_parse_hex(const char *hex);
158
+ struct tg_geom *tg_parse_hexn(const char *hex, size_t len);
159
+ struct tg_geom *tg_parse_hex_ix(const char *hex, enum tg_index ix);
160
+ struct tg_geom *tg_parse_hexn_ix(const char *hex, size_t len, enum tg_index ix);
161
+ struct tg_geom *tg_parse_geobin(const uint8_t *geobin, size_t len);
162
+ struct tg_geom *tg_parse_geobin_ix(const uint8_t *geobin, size_t len,enum tg_index ix);
163
+ struct tg_geom *tg_parse(const void *data, size_t len);
164
+ struct tg_geom *tg_parse_ix(const void *data, size_t len, enum tg_index ix);
165
+ const char *tg_geom_error(const struct tg_geom *geom);
166
+ int tg_geobin_fullrect(const uint8_t *geobin, size_t len, double min[4], double max[4]);
167
+ struct tg_rect tg_geobin_rect(const uint8_t *geobin, size_t len);
168
+ struct tg_point tg_geobin_point(const uint8_t *geobin, size_t len);
169
+ /// @}
170
+
171
+ /// @defgroup GeometryWriting Geometry writing
172
+ /// Functions for writing geometries as external data representations.
173
+ /// @{
174
+ size_t tg_geom_geojson(const struct tg_geom *geom, char *dst, size_t n);
175
+ size_t tg_geom_wkt(const struct tg_geom *geom, char *dst, size_t n);
176
+ size_t tg_geom_wkb(const struct tg_geom *geom, uint8_t *dst, size_t n);
177
+ size_t tg_geom_hex(const struct tg_geom *geom, char *dst, size_t n);
178
+ size_t tg_geom_geobin(const struct tg_geom *geom, uint8_t *dst, size_t n);
179
+ /// @}
180
+
181
+ /// @defgroup GeometryConstructorsEx Geometry with alternative dimensions
182
+ /// Functions for working with geometries that have more than two dimensions or
183
+ /// are empty. The extra dimensional coordinates contained within these
184
+ /// geometries are only carried along and serve no other purpose than to be
185
+ /// available for when it's desired to export to an output representation such
186
+ /// as GeoJSON, WKT, or WKB.
187
+ /// @{
188
+ struct tg_geom *tg_geom_new_point_z(struct tg_point point, double z);
189
+ struct tg_geom *tg_geom_new_point_m(struct tg_point point, double m);
190
+ struct tg_geom *tg_geom_new_point_zm(struct tg_point point, double z, double m);
191
+ struct tg_geom *tg_geom_new_point_empty(void);
192
+ struct tg_geom *tg_geom_new_linestring_z(const struct tg_line *line, const double *extra_coords, int ncoords);
193
+ struct tg_geom *tg_geom_new_linestring_m(const struct tg_line *line, const double *extra_coords, int ncoords);
194
+ struct tg_geom *tg_geom_new_linestring_zm(const struct tg_line *line, const double *extra_coords, int ncoords);
195
+ struct tg_geom *tg_geom_new_linestring_empty(void);
196
+ struct tg_geom *tg_geom_new_polygon_z(const struct tg_poly *poly, const double *extra_coords, int ncoords);
197
+ struct tg_geom *tg_geom_new_polygon_m(const struct tg_poly *poly, const double *extra_coords, int ncoords);
198
+ struct tg_geom *tg_geom_new_polygon_zm(const struct tg_poly *poly, const double *extra_coords, int ncoords);
199
+ struct tg_geom *tg_geom_new_polygon_empty(void);
200
+ struct tg_geom *tg_geom_new_multipoint_z(const struct tg_point *points, int npoints, const double *extra_coords, int ncoords);
201
+ struct tg_geom *tg_geom_new_multipoint_m(const struct tg_point *points, int npoints, const double *extra_coords, int ncoords);
202
+ struct tg_geom *tg_geom_new_multipoint_zm(const struct tg_point *points, int npoints, const double *extra_coords, int ncoords);
203
+ struct tg_geom *tg_geom_new_multipoint_empty(void);
204
+ struct tg_geom *tg_geom_new_multilinestring_z(const struct tg_line *const lines[], int nlines, const double *extra_coords, int ncoords);
205
+ struct tg_geom *tg_geom_new_multilinestring_m(const struct tg_line *const lines[], int nlines, const double *extra_coords, int ncoords);
206
+ struct tg_geom *tg_geom_new_multilinestring_zm(const struct tg_line *const lines[], int nlines, const double *extra_coords, int ncoords);
207
+ struct tg_geom *tg_geom_new_multilinestring_empty(void);
208
+ struct tg_geom *tg_geom_new_multipolygon_z(const struct tg_poly *const polys[], int npolys, const double *extra_coords, int ncoords);
209
+ struct tg_geom *tg_geom_new_multipolygon_m(const struct tg_poly *const polys[], int npolys, const double *extra_coords, int ncoords);
210
+ struct tg_geom *tg_geom_new_multipolygon_zm(const struct tg_poly *const polys[], int npolys, const double *extra_coords, int ncoords);
211
+ struct tg_geom *tg_geom_new_multipolygon_empty(void);
212
+ struct tg_geom *tg_geom_new_geometrycollection_empty(void);
213
+ /// @}
214
+
215
+ /// @defgroup PointFuncs Point functions
216
+ /// Functions for working directly with the tg_point type.
217
+ /// @{
218
+ struct tg_rect tg_point_rect(struct tg_point point);
219
+ bool tg_point_intersects_rect(struct tg_point a, struct tg_rect b);
220
+ /// @}
221
+
222
+ /// @defgroup SegmentFuncs Segment functions
223
+ /// Functions for working directly with the tg_segment type.
224
+ /// @{
225
+ struct tg_rect tg_segment_rect(struct tg_segment s);
226
+ bool tg_segment_intersects_segment(struct tg_segment a, struct tg_segment b);
227
+ /// @}
228
+
229
+ /// @defgroup RectFuncs Rectangle functions
230
+ /// Functions for working directly with the tg_rect type.
231
+ /// @{
232
+ struct tg_rect tg_rect_expand(struct tg_rect rect, struct tg_rect other);
233
+ struct tg_rect tg_rect_expand_point(struct tg_rect rect, struct tg_point point);
234
+ struct tg_point tg_rect_center(struct tg_rect rect);
235
+ bool tg_rect_intersects_rect(struct tg_rect a, struct tg_rect b);
236
+ bool tg_rect_intersects_point(struct tg_rect a, struct tg_point b);
237
+ /// @}
238
+
239
+ /// @defgroup RingFuncs Ring functions
240
+ /// Functions for working directly with the tg_ring type.
241
+ ///
242
+ /// There are no direct spatial predicates for tg_ring.
243
+ /// If you want to perform operations like "intersects" or "covers" then you
244
+ /// must upcast the ring to a tg_geom, like such:
245
+ ///
246
+ /// ```
247
+ /// tg_geom_intersects((struct tg_geom*)ring, geom);
248
+ /// ```
249
+ /// @{
250
+ struct tg_ring *tg_ring_new(const struct tg_point *points, int npoints);
251
+ struct tg_ring *tg_ring_new_ix(const struct tg_point *points, int npoints, enum tg_index ix);
252
+ void tg_ring_free(struct tg_ring *ring);
253
+ struct tg_ring *tg_ring_clone(const struct tg_ring *ring);
254
+ struct tg_ring *tg_ring_copy(const struct tg_ring *ring);
255
+ size_t tg_ring_memsize(const struct tg_ring *ring);
256
+ struct tg_rect tg_ring_rect(const struct tg_ring *ring);
257
+ int tg_ring_num_points(const struct tg_ring *ring);
258
+ struct tg_point tg_ring_point_at(const struct tg_ring *ring, int index);
259
+ const struct tg_point *tg_ring_points(const struct tg_ring *ring);
260
+ int tg_ring_num_segments(const struct tg_ring *ring);
261
+ struct tg_segment tg_ring_segment_at(const struct tg_ring *ring, int index);
262
+ bool tg_ring_convex(const struct tg_ring *ring);
263
+ bool tg_ring_clockwise(const struct tg_ring *ring);
264
+ int tg_ring_index_spread(const struct tg_ring *ring);
265
+ int tg_ring_index_num_levels(const struct tg_ring *ring);
266
+ int tg_ring_index_level_num_rects(const struct tg_ring *ring, int levelidx);
267
+ struct tg_rect tg_ring_index_level_rect(const struct tg_ring *ring, int levelidx, int rectidx);
268
+ bool tg_ring_nearest_segment(const struct tg_ring *ring,
269
+ double (*rect_dist)(struct tg_rect rect, int *more, void *udata),
270
+ double (*seg_dist)(struct tg_segment seg, int *more, void *udata),
271
+ bool (*iter)(struct tg_segment seg, double dist, int index, void *udata),
272
+ void *udata);
273
+ void tg_ring_line_search(const struct tg_ring *a, const struct tg_line *b,
274
+ bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg,
275
+ int bidx, void *udata),
276
+ void *udata);
277
+ void tg_ring_ring_search(const struct tg_ring *a, const struct tg_ring *b,
278
+ bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg,
279
+ int bidx, void *udata),
280
+ void *udata);
281
+ double tg_ring_area(const struct tg_ring *ring);
282
+ double tg_ring_perimeter(const struct tg_ring *ring);
283
+ /// @}
284
+
285
+ /// @defgroup LineFuncs Line functions
286
+ /// Functions for working directly with the tg_line type.
287
+ ///
288
+ /// There are no direct spatial predicates for tg_line.
289
+ /// If you want to perform operations like "intersects" or "covers" then you
290
+ /// must upcast the line to a tg_geom, like such:
291
+ ///
292
+ /// ```
293
+ /// tg_geom_intersects((struct tg_geom*)line, geom);
294
+ /// ```
295
+ /// @{
296
+ struct tg_line *tg_line_new(const struct tg_point *points, int npoints);
297
+ struct tg_line *tg_line_new_ix(const struct tg_point *points, int npoints, enum tg_index ix);
298
+ void tg_line_free(struct tg_line *line);
299
+ struct tg_line *tg_line_clone(const struct tg_line *line);
300
+ struct tg_line *tg_line_copy(const struct tg_line *line);
301
+ size_t tg_line_memsize(const struct tg_line *line);
302
+ struct tg_rect tg_line_rect(const struct tg_line *line);
303
+ int tg_line_num_points(const struct tg_line *line);
304
+ const struct tg_point *tg_line_points(const struct tg_line *line);
305
+ struct tg_point tg_line_point_at(const struct tg_line *line, int index);
306
+ int tg_line_num_segments(const struct tg_line *line);
307
+ struct tg_segment tg_line_segment_at(const struct tg_line *line, int index);
308
+ bool tg_line_clockwise(const struct tg_line *line);
309
+ int tg_line_index_spread(const struct tg_line *line);
310
+ int tg_line_index_num_levels(const struct tg_line *line);
311
+ int tg_line_index_level_num_rects(const struct tg_line *line, int levelidx);
312
+ struct tg_rect tg_line_index_level_rect(const struct tg_line *line, int levelidx, int rectidx);
313
+ bool tg_line_nearest_segment(const struct tg_line *line,
314
+ double (*rect_dist)(struct tg_rect rect, int *more, void *udata),
315
+ double (*seg_dist)(struct tg_segment seg, int *more, void *udata),
316
+ bool (*iter)(struct tg_segment seg, double dist, int index, void *udata),
317
+ void *udata);
318
+ void tg_line_line_search(const struct tg_line *a, const struct tg_line *b,
319
+ bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg,
320
+ int bidx, void *udata),
321
+ void *udata);
322
+ double tg_line_length(const struct tg_line *line);
323
+ /// @}
324
+
325
+ /// @defgroup PolyFuncs Polygon functions
326
+ /// Functions for working directly with the tg_poly type.
327
+ ///
328
+ /// There are no direct spatial predicates for tg_poly.
329
+ /// If you want to perform operations like "intersects" or "covers" then you
330
+ /// must upcast the poly to a tg_geom, like such:
331
+ ///
332
+ /// ```
333
+ /// tg_geom_intersects((struct tg_geom*)poly, geom);
334
+ /// ```
335
+ /// @{
336
+ struct tg_poly *tg_poly_new(const struct tg_ring *exterior, const struct tg_ring *const holes[], int nholes);
337
+ void tg_poly_free(struct tg_poly *poly);
338
+ struct tg_poly *tg_poly_clone(const struct tg_poly *poly);
339
+ struct tg_poly *tg_poly_copy(const struct tg_poly *poly);
340
+ size_t tg_poly_memsize(const struct tg_poly *poly);
341
+ const struct tg_ring *tg_poly_exterior(const struct tg_poly *poly);
342
+ int tg_poly_num_holes(const struct tg_poly *poly);
343
+ const struct tg_ring *tg_poly_hole_at(const struct tg_poly *poly, int index);
344
+ struct tg_rect tg_poly_rect(const struct tg_poly *poly);
345
+ bool tg_poly_clockwise(const struct tg_poly *poly);
346
+ /// @}
347
+
348
+ /// @defgroup GlobalFuncs Global environment
349
+ /// Functions for optionally setting the behavior of the TG environment.
350
+ /// These, if desired, should be called only once at program start up and prior
351
+ /// to calling any other tg_*() functions.
352
+ /// @{
353
+ void tg_env_set_allocator(void *(*malloc)(size_t), void *(*realloc)(void*, size_t), void (*free)(void*));
354
+ void tg_env_set_index(enum tg_index ix);
355
+ void tg_env_set_index_spread(int spread);
356
+ void tg_env_set_print_fixed_floats(bool print);
357
+ /// @}
358
+
359
+ #endif // TG_H
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TG
4
+ module Geometry
5
+ module ActiveRecordSource
6
+ module_function
7
+
8
+ def call(scope, id:, geometry:, batch_size: 1_000)
9
+ entries = []
10
+
11
+ each_record(scope, batch_size: batch_size) do |record|
12
+ entries << [read_field(record, id), read_field(record, geometry)]
13
+ end
14
+
15
+ entries
16
+ end
17
+
18
+ def registry_source(scope, id:, geometry:, batch_size: 1_000)
19
+ proc do
20
+ TG::Geometry::ActiveRecordSource.call(
21
+ scope,
22
+ id: id,
23
+ geometry: geometry,
24
+ batch_size: batch_size
25
+ )
26
+ end
27
+ end
28
+
29
+ def each_record(scope, batch_size:)
30
+ if scope.respond_to?(:find_each)
31
+ scope.find_each(batch_size: batch_size) { |record| yield record }
32
+ else
33
+ scope.each { |record| yield record }
34
+ end
35
+ end
36
+ private_class_method :each_record
37
+
38
+ def read_field(record, reader)
39
+ case reader
40
+ when Proc
41
+ reader.call(record)
42
+ when Symbol, String
43
+ if record.respond_to?(reader)
44
+ record.public_send(reader)
45
+ elsif record.respond_to?(:[])
46
+ record[reader]
47
+ else
48
+ raise TG::Geometry::ArgumentError, "record does not expose #{reader.inspect}"
49
+ end
50
+ else
51
+ raise TG::Geometry::ArgumentError, "field reader must be Symbol, String, or Proc"
52
+ end
53
+ end
54
+ private_class_method :read_field
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TG
4
+ module Geometry
5
+ class Registry
6
+ DEFAULT_INDEX_OPTIONS = {
7
+ via: :geojson,
8
+ strategy: :rtree,
9
+ predicate: :covers,
10
+ geometry_index: :ystripes
11
+ }.freeze
12
+
13
+ class << self
14
+ def source(&block)
15
+ if block
16
+ @source = block
17
+ elsif instance_variable_defined?(:@source)
18
+ @source
19
+ elsif superclass.respond_to?(:source)
20
+ superclass.source
21
+ end
22
+ end
23
+
24
+ def index_options(**options)
25
+ if options.empty?
26
+ inherited = superclass.respond_to?(:index_options) ? superclass.index_options : DEFAULT_INDEX_OPTIONS
27
+ inherited.merge(@index_options || {}).freeze
28
+ else
29
+ @index_options = index_options.merge(options).freeze
30
+ end
31
+ end
32
+
33
+ def active_record_source(scope, id:, geometry:, batch_size: 1_000)
34
+ require_relative "active_record_source"
35
+
36
+ source do
37
+ TG::Geometry::ActiveRecordSource.call(
38
+ scope,
39
+ id: id,
40
+ geometry: geometry,
41
+ batch_size: batch_size
42
+ )
43
+ end
44
+ end
45
+ end
46
+
47
+ attr_reader :index_options
48
+
49
+ def initialize(entries: nil, source: nil, **index_options)
50
+ @entries = entries
51
+ @source = source
52
+ @index_options = self.class.index_options.merge(index_options).freeze
53
+ @reload_mutex = Mutex.new
54
+ @index = nil
55
+ end
56
+
57
+ def reload!
58
+ new_index = TG::Geometry::Index.build(resolve_entries, **@index_options)
59
+
60
+ @reload_mutex.synchronize do
61
+ @index = new_index
62
+ end
63
+
64
+ new_index
65
+ end
66
+
67
+ def index
68
+ @index || raise(TG::Geometry::Error, "registry index is not loaded; call reload! first")
69
+ end
70
+
71
+ def loaded?
72
+ !@index.nil?
73
+ end
74
+
75
+ def size
76
+ current_index.size
77
+ end
78
+
79
+ def bbox
80
+ current_index.bbox
81
+ end
82
+
83
+ def find_covering(lon, lat)
84
+ current_index.find_covering(lon, lat)
85
+ end
86
+
87
+ def covering_ids(lon, lat)
88
+ current_index.covering_ids(lon, lat)
89
+ end
90
+
91
+ def intersecting_rect(min_x, min_y, max_x, max_y)
92
+ current_index.intersecting_rect(min_x, min_y, max_x, max_y)
93
+ end
94
+
95
+ def covering_ids_batch_packed(binary_string)
96
+ current_index.covering_ids_batch_packed(binary_string)
97
+ end
98
+
99
+ private
100
+
101
+ def current_index
102
+ @index || raise(TG::Geometry::Error, "registry index is not loaded; call reload! first")
103
+ end
104
+
105
+ def resolve_entries
106
+ return @entries unless @entries.nil?
107
+
108
+ callable = @source || self.class.source
109
+ raise TG::Geometry::Error, "registry source is not configured" unless callable
110
+
111
+ if callable.respond_to?(:call)
112
+ instance_exec(&callable)
113
+ else
114
+ raise TG::Geometry::Error, "registry source must be callable"
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TG
4
+ module Geometry
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "geometry/version"
4
+ require "tg_geometry_ext_geometry_ext"
5
+ require_relative "geometry/registry"
6
+ require_relative "geometry/active_record_source"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tg/geometry"