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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +103 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +385 -0
- data/Rakefile +129 -0
- data/benchmark/_support.rb +115 -0
- data/benchmark/batch_packed_vs_loop.rb +27 -0
- data/benchmark/falcon_concurrency.rb +25 -0
- data/benchmark/flat_vs_rtree.rb +27 -0
- data/benchmark/gvl_threshold.rb +41 -0
- data/benchmark/objectspace_memsize.rb +17 -0
- data/benchmark/parse_throughput.rb +38 -0
- data/benchmark/rss_stability.rb +70 -0
- data/docs/ACTIVE_RECORD.md +26 -0
- data/docs/ARCHITECTURE.md +130 -0
- data/docs/AUTO_STRATEGY.md +15 -0
- data/docs/BENCHMARKING.md +75 -0
- data/docs/CASUAL_EXAMPLE.md +618 -0
- data/docs/CONCURRENCY.md +65 -0
- data/docs/ERROR_HANDLING.md +55 -0
- data/docs/EXPANSION_E_TO_H_STATUS.md +51 -0
- data/docs/FORMAT_COVERAGE.md +23 -0
- data/docs/FULL_TG_API_COVERAGE.md +109 -0
- data/docs/LIMITATIONS.md +61 -0
- data/docs/LOW_LEVEL_GEOMETRY.md +121 -0
- data/docs/MEMORY_OWNERSHIP.md +94 -0
- data/docs/RACTOR.md +40 -0
- data/docs/REGISTRY.md +37 -0
- data/docs/RELEASE_CHECKLIST.md +39 -0
- data/ext/tg_geometry/extconf.rb +91 -0
- data/ext/tg_geometry/tg_geometry_ext.c +3054 -0
- data/ext/tg_geometry/tg_geometry_vendor_rtree.c +1 -0
- data/ext/tg_geometry/tg_geometry_vendor_tg.c +24 -0
- data/ext/tg_geometry/vendor/.vendored +16 -0
- data/ext/tg_geometry/vendor/rtree/LICENSE +20 -0
- data/ext/tg_geometry/vendor/rtree/README.md +202 -0
- data/ext/tg_geometry/vendor/rtree/VERSION +3 -0
- data/ext/tg_geometry/vendor/rtree/rtree.c +840 -0
- data/ext/tg_geometry/vendor/rtree/rtree.h +105 -0
- data/ext/tg_geometry/vendor/tg/LICENSE +19 -0
- data/ext/tg_geometry/vendor/tg/README.md +197 -0
- data/ext/tg_geometry/vendor/tg/VERSION +3 -0
- data/ext/tg_geometry/vendor/tg/tg.c +16010 -0
- data/ext/tg_geometry/vendor/tg/tg.h +359 -0
- data/lib/tg/geometry/active_record_source.rb +57 -0
- data/lib/tg/geometry/registry.rb +119 -0
- data/lib/tg/geometry/version.rb +7 -0
- data/lib/tg/geometry.rb +6 -0
- data/lib/tg_geometry.rb +3 -0
- data/script/vendor_libs.rb +264 -0
- data/spec/block_10_rtree_strategy_spec.rb +82 -0
- data/spec/block_11_rtree_order_spec.rb +53 -0
- data/spec/block_12_batch_packed_spec.rb +55 -0
- data/spec/block_13_error_hardening_spec.rb +65 -0
- data/spec/block_14_memory_gc_hardening_spec.rb +116 -0
- data/spec/block_1_skeleton_spec.rb +45 -0
- data/spec/block_20_concurrency_spec.rb +157 -0
- data/spec/block_20_fuzz_spec.rb +145 -0
- data/spec/block_2_vendor_spec.rb +79 -0
- data/spec/block_3_geom_parse_spec.rb +89 -0
- data/spec/block_4_geom_api_spec.rb +90 -0
- data/spec/block_5_rect_api_spec.rb +96 -0
- data/spec/block_6_index_build_spec.rb +111 -0
- data/spec/block_7_index_owned_geometry_spec.rb +143 -0
- data/spec/block_8_index_borrowed_geometry_spec.rb +106 -0
- data/spec/block_9_flat_query_spec.rb +65 -0
- data/spec/expansion_a_auto_strategy_spec.rb +14 -0
- data/spec/expansion_b_registry_spec.rb +47 -0
- data/spec/expansion_c_active_record_source_spec.rb +42 -0
- data/spec/expansion_d_format_coverage_spec.rb +30 -0
- data/spec/expansion_e_low_level_geometry_spec.rb +82 -0
- data/spec/expansion_i_ractor_spec.rb +25 -0
- data/spec/expansion_j_full_tg_api_coverage_spec.rb +114 -0
- data/spec/spec_helper.rb +15 -0
- metadata +157 -0
|
@@ -0,0 +1,3054 @@
|
|
|
1
|
+
#if defined(__clang__)
|
|
2
|
+
#pragma clang diagnostic push
|
|
3
|
+
#pragma clang diagnostic ignored "-Wc23-extensions"
|
|
4
|
+
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
|
5
|
+
#pragma clang diagnostic ignored "-Wunused-parameter"
|
|
6
|
+
#elif defined(__GNUC__)
|
|
7
|
+
#pragma GCC diagnostic push
|
|
8
|
+
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
9
|
+
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
|
10
|
+
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
11
|
+
#endif
|
|
12
|
+
#include "ruby.h"
|
|
13
|
+
#include "ruby/encoding.h"
|
|
14
|
+
#if defined(__clang__)
|
|
15
|
+
#pragma clang diagnostic pop
|
|
16
|
+
#elif defined(__GNUC__)
|
|
17
|
+
#pragma GCC diagnostic pop
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
#include "tg.h"
|
|
21
|
+
#include "rtree.h"
|
|
22
|
+
|
|
23
|
+
#include <limits.h>
|
|
24
|
+
#include <math.h>
|
|
25
|
+
#include <stdbool.h>
|
|
26
|
+
#include <stdint.h>
|
|
27
|
+
#include <stdlib.h>
|
|
28
|
+
#include <string.h>
|
|
29
|
+
|
|
30
|
+
static VALUE mTG;
|
|
31
|
+
static VALUE mTGGeometry;
|
|
32
|
+
static VALUE cTGGeometryGeom;
|
|
33
|
+
static VALUE cTGGeometryRect;
|
|
34
|
+
static VALUE cTGGeometryIndex;
|
|
35
|
+
static VALUE cTGGeometryLine;
|
|
36
|
+
static VALUE cTGGeometryRing;
|
|
37
|
+
static VALUE cTGGeometryPolygon;
|
|
38
|
+
static VALUE cTGGeometrySegment;
|
|
39
|
+
static VALUE eTGGeometryError;
|
|
40
|
+
static VALUE eTGGeometryParseError;
|
|
41
|
+
static VALUE eTGGeometryArgumentError;
|
|
42
|
+
static VALUE eTGGeometryFrozenIndexError;
|
|
43
|
+
|
|
44
|
+
typedef struct {
|
|
45
|
+
VALUE geom_owner;
|
|
46
|
+
struct tg_geom *geom;
|
|
47
|
+
size_t geom_bytes;
|
|
48
|
+
bool owned;
|
|
49
|
+
} tg_geom_wrapper_t;
|
|
50
|
+
|
|
51
|
+
typedef struct {
|
|
52
|
+
double min_x;
|
|
53
|
+
double min_y;
|
|
54
|
+
double max_x;
|
|
55
|
+
double max_y;
|
|
56
|
+
bool initialized;
|
|
57
|
+
} tg_rect_wrapper_t;
|
|
58
|
+
|
|
59
|
+
typedef struct {
|
|
60
|
+
VALUE geom_owner;
|
|
61
|
+
const struct tg_line *line;
|
|
62
|
+
} tg_line_wrapper_t;
|
|
63
|
+
|
|
64
|
+
typedef struct {
|
|
65
|
+
VALUE geom_owner;
|
|
66
|
+
const struct tg_ring *ring;
|
|
67
|
+
} tg_ring_wrapper_t;
|
|
68
|
+
|
|
69
|
+
typedef struct {
|
|
70
|
+
VALUE geom_owner;
|
|
71
|
+
const struct tg_poly *poly;
|
|
72
|
+
} tg_polygon_wrapper_t;
|
|
73
|
+
|
|
74
|
+
typedef struct {
|
|
75
|
+
struct tg_segment segment;
|
|
76
|
+
bool initialized;
|
|
77
|
+
} tg_segment_wrapper_t;
|
|
78
|
+
|
|
79
|
+
enum tg_geometry_index_via {
|
|
80
|
+
TG_GEOMETRY_INDEX_VIA_GEOM,
|
|
81
|
+
TG_GEOMETRY_INDEX_VIA_GEOJSON,
|
|
82
|
+
TG_GEOMETRY_INDEX_VIA_WKB,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
enum tg_geometry_index_strategy {
|
|
86
|
+
TG_GEOMETRY_INDEX_STRATEGY_FLAT,
|
|
87
|
+
TG_GEOMETRY_INDEX_STRATEGY_RTREE,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
enum tg_geometry_index_predicate {
|
|
91
|
+
TG_GEOMETRY_INDEX_PREDICATE_COVERS,
|
|
92
|
+
TG_GEOMETRY_INDEX_PREDICATE_CONTAINS,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
typedef struct {
|
|
96
|
+
VALUE id;
|
|
97
|
+
VALUE geom_owner;
|
|
98
|
+
struct tg_geom *geom;
|
|
99
|
+
struct tg_rect bbox;
|
|
100
|
+
size_t geom_bytes;
|
|
101
|
+
long ordinal;
|
|
102
|
+
bool owned;
|
|
103
|
+
} tg_index_entry_t;
|
|
104
|
+
|
|
105
|
+
typedef struct {
|
|
106
|
+
tg_index_entry_t *entries;
|
|
107
|
+
long len;
|
|
108
|
+
long initialized;
|
|
109
|
+
long capacity;
|
|
110
|
+
|
|
111
|
+
enum tg_geometry_index_strategy strategy;
|
|
112
|
+
enum tg_geometry_index_predicate predicate;
|
|
113
|
+
struct rtree *rtree;
|
|
114
|
+
|
|
115
|
+
bool frozen;
|
|
116
|
+
|
|
117
|
+
size_t owned_geom_bytes_total;
|
|
118
|
+
size_t rtree_bytes;
|
|
119
|
+
size_t entries_bytes;
|
|
120
|
+
struct tg_rect bbox;
|
|
121
|
+
bool has_bbox;
|
|
122
|
+
} tg_index_t;
|
|
123
|
+
|
|
124
|
+
typedef struct {
|
|
125
|
+
tg_index_t *owner;
|
|
126
|
+
size_t size;
|
|
127
|
+
} tg_rtree_alloc_header_t;
|
|
128
|
+
|
|
129
|
+
static _Thread_local tg_index_t *tg_current_rtree_owner = NULL;
|
|
130
|
+
|
|
131
|
+
static ID id_format;
|
|
132
|
+
static ID id_index;
|
|
133
|
+
static ID id_auto;
|
|
134
|
+
static ID id_geojson;
|
|
135
|
+
static ID id_wkt;
|
|
136
|
+
static ID id_wkb;
|
|
137
|
+
static ID id_hex;
|
|
138
|
+
static ID id_geobin;
|
|
139
|
+
static ID id_default;
|
|
140
|
+
static ID id_none;
|
|
141
|
+
static ID id_natural;
|
|
142
|
+
static ID id_ystripes;
|
|
143
|
+
static ID id_via;
|
|
144
|
+
static ID id_strategy;
|
|
145
|
+
static ID id_predicate;
|
|
146
|
+
static ID id_geometry_index;
|
|
147
|
+
static ID id_geom;
|
|
148
|
+
static ID id_flat;
|
|
149
|
+
static ID id_rtree;
|
|
150
|
+
static ID id_covers;
|
|
151
|
+
static ID id_contains;
|
|
152
|
+
|
|
153
|
+
#ifdef TG_DEBUG_TEST
|
|
154
|
+
static bool tg_debug_fail_next_entries_alloc = false;
|
|
155
|
+
static long tg_debug_fail_rtree_alloc_countdown = -1;
|
|
156
|
+
static bool tg_debug_fail_next_match_buffer_alloc = false;
|
|
157
|
+
#endif
|
|
158
|
+
|
|
159
|
+
static void tg_geometry_vendor_header_sanity(void) {
|
|
160
|
+
struct tg_point p = {0.0, 0.0};
|
|
161
|
+
struct tg_rect rect = {p, p};
|
|
162
|
+
|
|
163
|
+
(void)rect;
|
|
164
|
+
(void)TG_DEFAULT;
|
|
165
|
+
(void)rtree_new_with_allocator;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static void geom_mark(void *ptr) {
|
|
169
|
+
tg_geom_wrapper_t *w = (tg_geom_wrapper_t *)ptr;
|
|
170
|
+
|
|
171
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
172
|
+
rb_gc_mark_movable(w->geom_owner);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static void geom_compact(void *ptr) {
|
|
177
|
+
tg_geom_wrapper_t *w = (tg_geom_wrapper_t *)ptr;
|
|
178
|
+
|
|
179
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
180
|
+
w->geom_owner = rb_gc_location(w->geom_owner);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
static void geom_free(void *ptr) {
|
|
185
|
+
tg_geom_wrapper_t *w = (tg_geom_wrapper_t *)ptr;
|
|
186
|
+
if (!w)
|
|
187
|
+
return;
|
|
188
|
+
|
|
189
|
+
if (w->owned && w->geom) {
|
|
190
|
+
if (w->geom_bytes > 0) {
|
|
191
|
+
rb_gc_adjust_memory_usage(-(ssize_t)w->geom_bytes);
|
|
192
|
+
}
|
|
193
|
+
tg_geom_free(w->geom);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
w->geom = NULL;
|
|
197
|
+
w->geom_owner = Qnil;
|
|
198
|
+
w->geom_bytes = 0;
|
|
199
|
+
w->owned = false;
|
|
200
|
+
|
|
201
|
+
ruby_xfree(w);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static size_t geom_memsize(const void *ptr) {
|
|
205
|
+
const tg_geom_wrapper_t *w = (const tg_geom_wrapper_t *)ptr;
|
|
206
|
+
if (!w)
|
|
207
|
+
return 0;
|
|
208
|
+
|
|
209
|
+
return sizeof(*w) + ((w->owned && w->geom) ? w->geom_bytes : 0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static const rb_data_type_t tg_geom_type = {
|
|
213
|
+
"TG::Geometry::Geom",
|
|
214
|
+
{
|
|
215
|
+
geom_mark,
|
|
216
|
+
geom_free,
|
|
217
|
+
geom_memsize,
|
|
218
|
+
geom_compact,
|
|
219
|
+
{0},
|
|
220
|
+
},
|
|
221
|
+
0,
|
|
222
|
+
0,
|
|
223
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
static void rect_free(void *ptr) {
|
|
227
|
+
ruby_xfree(ptr);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static size_t rect_memsize(const void *ptr) {
|
|
231
|
+
(void)ptr;
|
|
232
|
+
return sizeof(tg_rect_wrapper_t);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static const rb_data_type_t tg_rect_type = {
|
|
236
|
+
"TG::Geometry::Rect",
|
|
237
|
+
{
|
|
238
|
+
NULL,
|
|
239
|
+
rect_free,
|
|
240
|
+
rect_memsize,
|
|
241
|
+
NULL,
|
|
242
|
+
{0},
|
|
243
|
+
},
|
|
244
|
+
0,
|
|
245
|
+
0,
|
|
246
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
static void borrowed_line_mark(void *ptr) {
|
|
250
|
+
tg_line_wrapper_t *w = (tg_line_wrapper_t *)ptr;
|
|
251
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
252
|
+
rb_gc_mark_movable(w->geom_owner);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
static void borrowed_line_compact(void *ptr) {
|
|
257
|
+
tg_line_wrapper_t *w = (tg_line_wrapper_t *)ptr;
|
|
258
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
259
|
+
w->geom_owner = rb_gc_location(w->geom_owner);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static void borrowed_line_free(void *ptr) {
|
|
264
|
+
ruby_xfree(ptr);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
static size_t borrowed_line_memsize(const void *ptr) {
|
|
268
|
+
(void)ptr;
|
|
269
|
+
return sizeof(tg_line_wrapper_t);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
static const rb_data_type_t tg_line_type = {
|
|
273
|
+
"TG::Geometry::Line",
|
|
274
|
+
{
|
|
275
|
+
borrowed_line_mark,
|
|
276
|
+
borrowed_line_free,
|
|
277
|
+
borrowed_line_memsize,
|
|
278
|
+
borrowed_line_compact,
|
|
279
|
+
{0},
|
|
280
|
+
},
|
|
281
|
+
0,
|
|
282
|
+
0,
|
|
283
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
static void borrowed_ring_mark(void *ptr) {
|
|
287
|
+
tg_ring_wrapper_t *w = (tg_ring_wrapper_t *)ptr;
|
|
288
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
289
|
+
rb_gc_mark_movable(w->geom_owner);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
static void borrowed_ring_compact(void *ptr) {
|
|
294
|
+
tg_ring_wrapper_t *w = (tg_ring_wrapper_t *)ptr;
|
|
295
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
296
|
+
w->geom_owner = rb_gc_location(w->geom_owner);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static void borrowed_ring_free(void *ptr) {
|
|
301
|
+
ruby_xfree(ptr);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
static size_t borrowed_ring_memsize(const void *ptr) {
|
|
305
|
+
(void)ptr;
|
|
306
|
+
return sizeof(tg_ring_wrapper_t);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
static const rb_data_type_t tg_ring_type = {
|
|
310
|
+
"TG::Geometry::Ring",
|
|
311
|
+
{
|
|
312
|
+
borrowed_ring_mark,
|
|
313
|
+
borrowed_ring_free,
|
|
314
|
+
borrowed_ring_memsize,
|
|
315
|
+
borrowed_ring_compact,
|
|
316
|
+
{0},
|
|
317
|
+
},
|
|
318
|
+
0,
|
|
319
|
+
0,
|
|
320
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
static void borrowed_polygon_mark(void *ptr) {
|
|
324
|
+
tg_polygon_wrapper_t *w = (tg_polygon_wrapper_t *)ptr;
|
|
325
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
326
|
+
rb_gc_mark_movable(w->geom_owner);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
static void borrowed_polygon_compact(void *ptr) {
|
|
331
|
+
tg_polygon_wrapper_t *w = (tg_polygon_wrapper_t *)ptr;
|
|
332
|
+
if (w && !NIL_P(w->geom_owner)) {
|
|
333
|
+
w->geom_owner = rb_gc_location(w->geom_owner);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
static void borrowed_polygon_free(void *ptr) {
|
|
338
|
+
ruby_xfree(ptr);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
static size_t borrowed_polygon_memsize(const void *ptr) {
|
|
342
|
+
(void)ptr;
|
|
343
|
+
return sizeof(tg_polygon_wrapper_t);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
static const rb_data_type_t tg_polygon_type = {
|
|
347
|
+
"TG::Geometry::Polygon",
|
|
348
|
+
{
|
|
349
|
+
borrowed_polygon_mark,
|
|
350
|
+
borrowed_polygon_free,
|
|
351
|
+
borrowed_polygon_memsize,
|
|
352
|
+
borrowed_polygon_compact,
|
|
353
|
+
{0},
|
|
354
|
+
},
|
|
355
|
+
0,
|
|
356
|
+
0,
|
|
357
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
static void segment_free(void *ptr) {
|
|
361
|
+
ruby_xfree(ptr);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
static size_t segment_memsize(const void *ptr) {
|
|
365
|
+
(void)ptr;
|
|
366
|
+
return sizeof(tg_segment_wrapper_t);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
static const rb_data_type_t tg_segment_type = {
|
|
370
|
+
"TG::Geometry::Segment",
|
|
371
|
+
{
|
|
372
|
+
NULL,
|
|
373
|
+
segment_free,
|
|
374
|
+
segment_memsize,
|
|
375
|
+
NULL,
|
|
376
|
+
{0},
|
|
377
|
+
},
|
|
378
|
+
0,
|
|
379
|
+
0,
|
|
380
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
static void index_dispose(tg_index_t *idx) {
|
|
384
|
+
if (!idx)
|
|
385
|
+
return;
|
|
386
|
+
|
|
387
|
+
if (idx->rtree) {
|
|
388
|
+
rtree_free(idx->rtree);
|
|
389
|
+
idx->rtree = NULL;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (idx->entries) {
|
|
393
|
+
for (long i = 0; i < idx->initialized; i++) {
|
|
394
|
+
tg_index_entry_t *entry = &idx->entries[i];
|
|
395
|
+
|
|
396
|
+
if (entry->owned && entry->geom) {
|
|
397
|
+
if (entry->geom_bytes > 0) {
|
|
398
|
+
rb_gc_adjust_memory_usage(-(ssize_t)entry->geom_bytes);
|
|
399
|
+
if (idx->owned_geom_bytes_total >= entry->geom_bytes) {
|
|
400
|
+
idx->owned_geom_bytes_total -= entry->geom_bytes;
|
|
401
|
+
} else {
|
|
402
|
+
idx->owned_geom_bytes_total = 0;
|
|
403
|
+
}
|
|
404
|
+
entry->geom_bytes = 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
tg_geom_free(entry->geom);
|
|
408
|
+
entry->geom = NULL;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
entry->id = Qnil;
|
|
412
|
+
entry->geom_owner = Qnil;
|
|
413
|
+
entry->owned = false;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
free(idx->entries);
|
|
417
|
+
idx->entries = NULL;
|
|
418
|
+
|
|
419
|
+
if (idx->entries_bytes > 0) {
|
|
420
|
+
rb_gc_adjust_memory_usage(-(ssize_t)idx->entries_bytes);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
idx->len = 0;
|
|
425
|
+
idx->initialized = 0;
|
|
426
|
+
idx->capacity = 0;
|
|
427
|
+
idx->frozen = false;
|
|
428
|
+
idx->owned_geom_bytes_total = 0;
|
|
429
|
+
idx->rtree_bytes = 0;
|
|
430
|
+
idx->entries_bytes = 0;
|
|
431
|
+
idx->has_bbox = false;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static void index_mark(void *ptr) {
|
|
435
|
+
tg_index_t *idx = (tg_index_t *)ptr;
|
|
436
|
+
|
|
437
|
+
if (!idx || !idx->entries)
|
|
438
|
+
return;
|
|
439
|
+
|
|
440
|
+
for (long i = 0; i < idx->initialized; i++) {
|
|
441
|
+
rb_gc_mark_movable(idx->entries[i].id);
|
|
442
|
+
|
|
443
|
+
if (!NIL_P(idx->entries[i].geom_owner)) {
|
|
444
|
+
rb_gc_mark_movable(idx->entries[i].geom_owner);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
static void index_compact(void *ptr) {
|
|
450
|
+
tg_index_t *idx = (tg_index_t *)ptr;
|
|
451
|
+
|
|
452
|
+
if (!idx || !idx->entries)
|
|
453
|
+
return;
|
|
454
|
+
|
|
455
|
+
for (long i = 0; i < idx->initialized; i++) {
|
|
456
|
+
idx->entries[i].id = rb_gc_location(idx->entries[i].id);
|
|
457
|
+
|
|
458
|
+
if (!NIL_P(idx->entries[i].geom_owner)) {
|
|
459
|
+
idx->entries[i].geom_owner = rb_gc_location(idx->entries[i].geom_owner);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
static void index_free(void *ptr) {
|
|
465
|
+
tg_index_t *idx = (tg_index_t *)ptr;
|
|
466
|
+
|
|
467
|
+
index_dispose(idx);
|
|
468
|
+
ruby_xfree(idx);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
static size_t index_memsize(const void *ptr) {
|
|
472
|
+
const tg_index_t *idx = (const tg_index_t *)ptr;
|
|
473
|
+
|
|
474
|
+
if (!idx)
|
|
475
|
+
return 0;
|
|
476
|
+
|
|
477
|
+
return sizeof(*idx) + idx->entries_bytes + idx->owned_geom_bytes_total + idx->rtree_bytes;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
static const rb_data_type_t tg_index_type = {
|
|
481
|
+
"TG::Geometry::Index",
|
|
482
|
+
{
|
|
483
|
+
index_mark,
|
|
484
|
+
index_free,
|
|
485
|
+
index_memsize,
|
|
486
|
+
index_compact,
|
|
487
|
+
{0},
|
|
488
|
+
},
|
|
489
|
+
0,
|
|
490
|
+
0,
|
|
491
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
static VALUE kwargs_value(VALUE kwargs, ID key, VALUE fallback) {
|
|
495
|
+
VALUE value;
|
|
496
|
+
|
|
497
|
+
if (NIL_P(kwargs))
|
|
498
|
+
return fallback;
|
|
499
|
+
|
|
500
|
+
value = rb_hash_aref(kwargs, ID2SYM(key));
|
|
501
|
+
return NIL_P(value) ? fallback : value;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
static enum tg_index parse_index_symbol(VALUE value) {
|
|
505
|
+
ID id;
|
|
506
|
+
|
|
507
|
+
if (!SYMBOL_P(value)) {
|
|
508
|
+
rb_raise(eTGGeometryArgumentError,
|
|
509
|
+
"index: must be one of :default, :none, :natural, :ystripes");
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
id = SYM2ID(value);
|
|
513
|
+
if (id == id_default)
|
|
514
|
+
return TG_DEFAULT;
|
|
515
|
+
if (id == id_none)
|
|
516
|
+
return TG_NONE;
|
|
517
|
+
if (id == id_natural)
|
|
518
|
+
return TG_NATURAL;
|
|
519
|
+
if (id == id_ystripes)
|
|
520
|
+
return TG_YSTRIPES;
|
|
521
|
+
|
|
522
|
+
rb_raise(eTGGeometryArgumentError,
|
|
523
|
+
"index: must be one of :default, :none, :natural, :ystripes");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
enum tg_geometry_parse_format {
|
|
527
|
+
TG_GEOMETRY_FORMAT_AUTO,
|
|
528
|
+
TG_GEOMETRY_FORMAT_GEOJSON,
|
|
529
|
+
TG_GEOMETRY_FORMAT_WKT,
|
|
530
|
+
TG_GEOMETRY_FORMAT_WKB,
|
|
531
|
+
TG_GEOMETRY_FORMAT_HEX,
|
|
532
|
+
TG_GEOMETRY_FORMAT_GEOBIN,
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
static enum tg_geometry_parse_format parse_format_symbol(VALUE value) {
|
|
536
|
+
ID id;
|
|
537
|
+
|
|
538
|
+
if (!SYMBOL_P(value)) {
|
|
539
|
+
rb_raise(eTGGeometryArgumentError,
|
|
540
|
+
"format: must be one of :auto, :geojson, :wkt, :wkb, :hex, :geobin");
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
id = SYM2ID(value);
|
|
544
|
+
if (id == id_auto)
|
|
545
|
+
return TG_GEOMETRY_FORMAT_AUTO;
|
|
546
|
+
if (id == id_geojson)
|
|
547
|
+
return TG_GEOMETRY_FORMAT_GEOJSON;
|
|
548
|
+
if (id == id_wkt)
|
|
549
|
+
return TG_GEOMETRY_FORMAT_WKT;
|
|
550
|
+
if (id == id_wkb)
|
|
551
|
+
return TG_GEOMETRY_FORMAT_WKB;
|
|
552
|
+
if (id == id_hex)
|
|
553
|
+
return TG_GEOMETRY_FORMAT_HEX;
|
|
554
|
+
if (id == id_geobin)
|
|
555
|
+
return TG_GEOMETRY_FORMAT_GEOBIN;
|
|
556
|
+
|
|
557
|
+
rb_raise(eTGGeometryArgumentError,
|
|
558
|
+
"format: must be one of :auto, :geojson, :wkt, :wkb, :hex, :geobin");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
static VALUE required_kwargs_value(VALUE kwargs, ID key, const char *name) {
|
|
562
|
+
VALUE value;
|
|
563
|
+
|
|
564
|
+
if (NIL_P(kwargs)) {
|
|
565
|
+
rb_raise(eTGGeometryArgumentError, "%s is required", name);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
value = rb_hash_aref(kwargs, ID2SYM(key));
|
|
569
|
+
if (NIL_P(value)) {
|
|
570
|
+
rb_raise(eTGGeometryArgumentError, "%s is required", name);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return value;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
static enum tg_geometry_index_via parse_index_via_symbol(VALUE value) {
|
|
577
|
+
ID id;
|
|
578
|
+
|
|
579
|
+
if (!SYMBOL_P(value)) {
|
|
580
|
+
rb_raise(eTGGeometryArgumentError, "via: must be one of :geom, :geojson, :wkb");
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
id = SYM2ID(value);
|
|
584
|
+
if (id == id_geom)
|
|
585
|
+
return TG_GEOMETRY_INDEX_VIA_GEOM;
|
|
586
|
+
if (id == id_geojson)
|
|
587
|
+
return TG_GEOMETRY_INDEX_VIA_GEOJSON;
|
|
588
|
+
if (id == id_wkb)
|
|
589
|
+
return TG_GEOMETRY_INDEX_VIA_WKB;
|
|
590
|
+
|
|
591
|
+
rb_raise(eTGGeometryArgumentError, "via: must be one of :geom, :geojson, :wkb");
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
static enum tg_geometry_index_strategy parse_index_strategy_symbol(VALUE value) {
|
|
595
|
+
ID id;
|
|
596
|
+
|
|
597
|
+
if (!SYMBOL_P(value)) {
|
|
598
|
+
rb_raise(eTGGeometryArgumentError, "strategy: must be one of :flat, :rtree");
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
id = SYM2ID(value);
|
|
602
|
+
if (id == id_flat)
|
|
603
|
+
return TG_GEOMETRY_INDEX_STRATEGY_FLAT;
|
|
604
|
+
if (id == id_rtree)
|
|
605
|
+
return TG_GEOMETRY_INDEX_STRATEGY_RTREE;
|
|
606
|
+
rb_raise(eTGGeometryArgumentError, "strategy: must be one of :flat, :rtree");
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
static enum tg_geometry_index_predicate parse_index_predicate_symbol(VALUE value) {
|
|
610
|
+
ID id;
|
|
611
|
+
|
|
612
|
+
if (!SYMBOL_P(value)) {
|
|
613
|
+
rb_raise(eTGGeometryArgumentError, "predicate: must be one of :covers, :contains");
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
id = SYM2ID(value);
|
|
617
|
+
if (id == id_covers)
|
|
618
|
+
return TG_GEOMETRY_INDEX_PREDICATE_COVERS;
|
|
619
|
+
if (id == id_contains)
|
|
620
|
+
return TG_GEOMETRY_INDEX_PREDICATE_CONTAINS;
|
|
621
|
+
|
|
622
|
+
rb_raise(eTGGeometryArgumentError, "predicate: must be one of :covers, :contains");
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
static VALUE geom_wrap_owned(struct tg_geom *geom) {
|
|
626
|
+
tg_geom_wrapper_t *w;
|
|
627
|
+
VALUE wrapper;
|
|
628
|
+
|
|
629
|
+
if (!geom) {
|
|
630
|
+
rb_raise(rb_eNoMemError, "TG geometry allocation failed");
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
wrapper = TypedData_Make_Struct(cTGGeometryGeom, tg_geom_wrapper_t, &tg_geom_type, w);
|
|
634
|
+
|
|
635
|
+
w->geom_owner = Qnil;
|
|
636
|
+
w->geom = geom;
|
|
637
|
+
w->geom_bytes = tg_geom_memsize(geom);
|
|
638
|
+
w->owned = true;
|
|
639
|
+
|
|
640
|
+
if (w->geom_bytes > 0) {
|
|
641
|
+
rb_gc_adjust_memory_usage((ssize_t)w->geom_bytes);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
rb_obj_freeze(wrapper);
|
|
645
|
+
RB_GC_GUARD(wrapper);
|
|
646
|
+
return wrapper;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
static VALUE geom_wrap_borrowed(VALUE geom_owner, const struct tg_geom *geom) {
|
|
650
|
+
tg_geom_wrapper_t *w;
|
|
651
|
+
VALUE wrapper;
|
|
652
|
+
|
|
653
|
+
if (!geom) {
|
|
654
|
+
return Qnil;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
wrapper = TypedData_Make_Struct(cTGGeometryGeom, tg_geom_wrapper_t, &tg_geom_type, w);
|
|
658
|
+
w->geom_owner = geom_owner;
|
|
659
|
+
w->geom = (struct tg_geom *)geom;
|
|
660
|
+
w->geom_bytes = 0;
|
|
661
|
+
w->owned = false;
|
|
662
|
+
|
|
663
|
+
rb_obj_freeze(wrapper);
|
|
664
|
+
RB_GC_GUARD(geom_owner);
|
|
665
|
+
RB_GC_GUARD(wrapper);
|
|
666
|
+
return wrapper;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
typedef struct {
|
|
670
|
+
const char *ptr;
|
|
671
|
+
long len;
|
|
672
|
+
} tg_string_copy_args_t;
|
|
673
|
+
|
|
674
|
+
static VALUE rb_str_new_from_c_copy(VALUE arg) {
|
|
675
|
+
tg_string_copy_args_t *args = (tg_string_copy_args_t *)arg;
|
|
676
|
+
return rb_str_new(args->ptr, args->len);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
static void raise_geom_error_and_free_as(struct tg_geom *geom, VALUE exception_class,
|
|
680
|
+
const char *nil_message, const char *too_large_message,
|
|
681
|
+
const char *allocation_message) {
|
|
682
|
+
const char *err;
|
|
683
|
+
char *message_copy;
|
|
684
|
+
size_t message_len;
|
|
685
|
+
tg_string_copy_args_t string_args;
|
|
686
|
+
VALUE message;
|
|
687
|
+
int state = 0;
|
|
688
|
+
|
|
689
|
+
if (!geom) {
|
|
690
|
+
rb_raise(rb_eNoMemError, "%s", nil_message);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
err = tg_geom_error(geom);
|
|
694
|
+
if (!err) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
message_len = strlen(err);
|
|
699
|
+
if (message_len > (size_t)LONG_MAX) {
|
|
700
|
+
tg_geom_free(geom);
|
|
701
|
+
rb_raise(rb_eNoMemError, "%s", too_large_message);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
message_copy = (char *)malloc(message_len + 1);
|
|
705
|
+
if (!message_copy) {
|
|
706
|
+
tg_geom_free(geom);
|
|
707
|
+
rb_raise(rb_eNoMemError, "%s", allocation_message);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
memcpy(message_copy, err, message_len + 1);
|
|
711
|
+
tg_geom_free(geom);
|
|
712
|
+
|
|
713
|
+
string_args.ptr = message_copy;
|
|
714
|
+
string_args.len = (long)message_len;
|
|
715
|
+
message = rb_protect(rb_str_new_from_c_copy, (VALUE)&string_args, &state);
|
|
716
|
+
free(message_copy);
|
|
717
|
+
|
|
718
|
+
if (state) {
|
|
719
|
+
rb_jump_tag(state);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
rb_exc_raise(rb_exc_new_str(exception_class, message));
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
static VALUE raise_parse_error_from_geom(struct tg_geom *geom) {
|
|
726
|
+
raise_geom_error_and_free_as(geom, eTGGeometryParseError, "TG geometry allocation failed",
|
|
727
|
+
"parse error message is too large",
|
|
728
|
+
"parse error message allocation failed");
|
|
729
|
+
return Qnil;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
static VALUE parse_string_with_format(VALUE input, enum tg_geometry_parse_format format,
|
|
733
|
+
enum tg_index index) {
|
|
734
|
+
struct tg_geom *geom = NULL;
|
|
735
|
+
const char *data;
|
|
736
|
+
size_t len;
|
|
737
|
+
|
|
738
|
+
StringValue(input);
|
|
739
|
+
data = RSTRING_PTR(input);
|
|
740
|
+
len = (size_t)RSTRING_LEN(input);
|
|
741
|
+
|
|
742
|
+
switch (format) {
|
|
743
|
+
case TG_GEOMETRY_FORMAT_AUTO:
|
|
744
|
+
geom = tg_parse_ix(data, len, index);
|
|
745
|
+
break;
|
|
746
|
+
case TG_GEOMETRY_FORMAT_GEOJSON:
|
|
747
|
+
geom = tg_parse_geojsonn_ix(data, len, index);
|
|
748
|
+
break;
|
|
749
|
+
case TG_GEOMETRY_FORMAT_WKT:
|
|
750
|
+
geom = tg_parse_wktn_ix(data, len, index);
|
|
751
|
+
break;
|
|
752
|
+
case TG_GEOMETRY_FORMAT_WKB:
|
|
753
|
+
geom = tg_parse_wkb_ix((const uint8_t *)data, len, index);
|
|
754
|
+
break;
|
|
755
|
+
case TG_GEOMETRY_FORMAT_HEX:
|
|
756
|
+
geom = tg_parse_hexn_ix(data, len, index);
|
|
757
|
+
break;
|
|
758
|
+
case TG_GEOMETRY_FORMAT_GEOBIN:
|
|
759
|
+
geom = tg_parse_geobin_ix((const uint8_t *)data, len, index);
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
raise_parse_error_from_geom(geom);
|
|
764
|
+
return geom_wrap_owned(geom);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
static VALUE rb_tg_geometry_parse(int argc, VALUE *argv, VALUE self) {
|
|
768
|
+
VALUE input;
|
|
769
|
+
VALUE kwargs;
|
|
770
|
+
VALUE format_value;
|
|
771
|
+
VALUE index_value;
|
|
772
|
+
enum tg_geometry_parse_format format;
|
|
773
|
+
enum tg_index index;
|
|
774
|
+
|
|
775
|
+
(void)self;
|
|
776
|
+
rb_scan_args(argc, argv, "1:", &input, &kwargs);
|
|
777
|
+
|
|
778
|
+
format_value = kwargs_value(kwargs, id_format, ID2SYM(id_auto));
|
|
779
|
+
index_value = kwargs_value(kwargs, id_index, ID2SYM(id_ystripes));
|
|
780
|
+
format = parse_format_symbol(format_value);
|
|
781
|
+
index = parse_index_symbol(index_value);
|
|
782
|
+
|
|
783
|
+
return parse_string_with_format(input, format, index);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
static VALUE rb_tg_geometry_parse_geojson(int argc, VALUE *argv, VALUE self) {
|
|
787
|
+
VALUE input;
|
|
788
|
+
VALUE kwargs;
|
|
789
|
+
VALUE index_value;
|
|
790
|
+
enum tg_index index;
|
|
791
|
+
|
|
792
|
+
(void)self;
|
|
793
|
+
rb_scan_args(argc, argv, "1:", &input, &kwargs);
|
|
794
|
+
|
|
795
|
+
index_value = kwargs_value(kwargs, id_index, ID2SYM(id_ystripes));
|
|
796
|
+
index = parse_index_symbol(index_value);
|
|
797
|
+
|
|
798
|
+
return parse_string_with_format(input, TG_GEOMETRY_FORMAT_GEOJSON, index);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
static VALUE rb_tg_geometry_parse_wkt(int argc, VALUE *argv, VALUE self) {
|
|
802
|
+
VALUE input;
|
|
803
|
+
VALUE kwargs;
|
|
804
|
+
VALUE index_value;
|
|
805
|
+
enum tg_index index;
|
|
806
|
+
|
|
807
|
+
(void)self;
|
|
808
|
+
rb_scan_args(argc, argv, "1:", &input, &kwargs);
|
|
809
|
+
|
|
810
|
+
index_value = kwargs_value(kwargs, id_index, ID2SYM(id_ystripes));
|
|
811
|
+
index = parse_index_symbol(index_value);
|
|
812
|
+
|
|
813
|
+
return parse_string_with_format(input, TG_GEOMETRY_FORMAT_WKT, index);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
static VALUE rb_tg_geometry_parse_wkb(int argc, VALUE *argv, VALUE self) {
|
|
817
|
+
VALUE input;
|
|
818
|
+
VALUE kwargs;
|
|
819
|
+
VALUE index_value;
|
|
820
|
+
enum tg_index index;
|
|
821
|
+
|
|
822
|
+
(void)self;
|
|
823
|
+
rb_scan_args(argc, argv, "1:", &input, &kwargs);
|
|
824
|
+
|
|
825
|
+
index_value = kwargs_value(kwargs, id_index, ID2SYM(id_ystripes));
|
|
826
|
+
index = parse_index_symbol(index_value);
|
|
827
|
+
|
|
828
|
+
return parse_string_with_format(input, TG_GEOMETRY_FORMAT_WKB, index);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
static VALUE rb_tg_geometry_parse_hex(int argc, VALUE *argv, VALUE self) {
|
|
832
|
+
VALUE input;
|
|
833
|
+
VALUE kwargs;
|
|
834
|
+
VALUE index_value;
|
|
835
|
+
enum tg_index index;
|
|
836
|
+
|
|
837
|
+
(void)self;
|
|
838
|
+
rb_scan_args(argc, argv, "1:", &input, &kwargs);
|
|
839
|
+
|
|
840
|
+
index_value = kwargs_value(kwargs, id_index, ID2SYM(id_ystripes));
|
|
841
|
+
index = parse_index_symbol(index_value);
|
|
842
|
+
|
|
843
|
+
return parse_string_with_format(input, TG_GEOMETRY_FORMAT_HEX, index);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
static VALUE rb_tg_geometry_parse_geobin(int argc, VALUE *argv, VALUE self) {
|
|
847
|
+
VALUE input;
|
|
848
|
+
VALUE kwargs;
|
|
849
|
+
VALUE index_value;
|
|
850
|
+
enum tg_index index;
|
|
851
|
+
|
|
852
|
+
(void)self;
|
|
853
|
+
rb_scan_args(argc, argv, "1:", &input, &kwargs);
|
|
854
|
+
|
|
855
|
+
index_value = kwargs_value(kwargs, id_index, ID2SYM(id_ystripes));
|
|
856
|
+
index = parse_index_symbol(index_value);
|
|
857
|
+
|
|
858
|
+
return parse_string_with_format(input, TG_GEOMETRY_FORMAT_GEOBIN, index);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
static tg_geom_wrapper_t *get_geom_wrapper(VALUE value) {
|
|
862
|
+
tg_geom_wrapper_t *w;
|
|
863
|
+
TypedData_Get_Struct(value, tg_geom_wrapper_t, &tg_geom_type, w);
|
|
864
|
+
|
|
865
|
+
if (!w || !w->geom) {
|
|
866
|
+
rb_raise(rb_eArgError, "invalid TG::Geometry::Geom");
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
return w;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
static void check_finite_double(double value, const char *name) {
|
|
873
|
+
if (!isfinite(value)) {
|
|
874
|
+
rb_raise(eTGGeometryArgumentError, "%s must be finite", name);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
static void validate_rect_coordinates(double min_x, double min_y, double max_x, double max_y) {
|
|
879
|
+
check_finite_double(min_x, "min_x");
|
|
880
|
+
check_finite_double(min_y, "min_y");
|
|
881
|
+
check_finite_double(max_x, "max_x");
|
|
882
|
+
check_finite_double(max_y, "max_y");
|
|
883
|
+
|
|
884
|
+
if (min_x > max_x || min_y > max_y) {
|
|
885
|
+
rb_raise(eTGGeometryArgumentError, "rectangle min coordinates must be <= max coordinates");
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
static VALUE wrap_constructed_geom(struct tg_geom *geom) {
|
|
890
|
+
raise_geom_error_and_free_as(geom, eTGGeometryError, "TG geometry allocation failed",
|
|
891
|
+
"TG geometry error message is too large",
|
|
892
|
+
"TG geometry error message allocation failed");
|
|
893
|
+
return geom_wrap_owned(geom);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
static VALUE rb_tg_geometry_point(VALUE self, VALUE x_value, VALUE y_value) {
|
|
897
|
+
double x = NUM2DBL(x_value);
|
|
898
|
+
double y = NUM2DBL(y_value);
|
|
899
|
+
struct tg_point point;
|
|
900
|
+
|
|
901
|
+
(void)self;
|
|
902
|
+
check_finite_double(x, "x");
|
|
903
|
+
check_finite_double(y, "y");
|
|
904
|
+
|
|
905
|
+
point.x = x;
|
|
906
|
+
point.y = y;
|
|
907
|
+
return wrap_constructed_geom(tg_geom_new_point(point));
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
static VALUE rb_tg_geometry_point_z(VALUE self, VALUE x_value, VALUE y_value, VALUE z_value) {
|
|
911
|
+
double x = NUM2DBL(x_value);
|
|
912
|
+
double y = NUM2DBL(y_value);
|
|
913
|
+
double z = NUM2DBL(z_value);
|
|
914
|
+
struct tg_point point;
|
|
915
|
+
|
|
916
|
+
(void)self;
|
|
917
|
+
check_finite_double(x, "x");
|
|
918
|
+
check_finite_double(y, "y");
|
|
919
|
+
check_finite_double(z, "z");
|
|
920
|
+
|
|
921
|
+
point.x = x;
|
|
922
|
+
point.y = y;
|
|
923
|
+
return wrap_constructed_geom(tg_geom_new_point_z(point, z));
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
static VALUE rb_tg_geometry_point_m(VALUE self, VALUE x_value, VALUE y_value, VALUE m_value) {
|
|
927
|
+
double x = NUM2DBL(x_value);
|
|
928
|
+
double y = NUM2DBL(y_value);
|
|
929
|
+
double m = NUM2DBL(m_value);
|
|
930
|
+
struct tg_point point;
|
|
931
|
+
|
|
932
|
+
(void)self;
|
|
933
|
+
check_finite_double(x, "x");
|
|
934
|
+
check_finite_double(y, "y");
|
|
935
|
+
check_finite_double(m, "m");
|
|
936
|
+
|
|
937
|
+
point.x = x;
|
|
938
|
+
point.y = y;
|
|
939
|
+
return wrap_constructed_geom(tg_geom_new_point_m(point, m));
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
static VALUE rb_tg_geometry_point_zm(VALUE self, VALUE x_value, VALUE y_value, VALUE z_value,
|
|
943
|
+
VALUE m_value) {
|
|
944
|
+
double x = NUM2DBL(x_value);
|
|
945
|
+
double y = NUM2DBL(y_value);
|
|
946
|
+
double z = NUM2DBL(z_value);
|
|
947
|
+
double m = NUM2DBL(m_value);
|
|
948
|
+
struct tg_point point;
|
|
949
|
+
|
|
950
|
+
(void)self;
|
|
951
|
+
check_finite_double(x, "x");
|
|
952
|
+
check_finite_double(y, "y");
|
|
953
|
+
check_finite_double(z, "z");
|
|
954
|
+
check_finite_double(m, "m");
|
|
955
|
+
|
|
956
|
+
point.x = x;
|
|
957
|
+
point.y = y;
|
|
958
|
+
return wrap_constructed_geom(tg_geom_new_point_zm(point, z, m));
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
static VALUE rb_tg_geometry_empty_point(VALUE self) {
|
|
962
|
+
(void)self;
|
|
963
|
+
return wrap_constructed_geom(tg_geom_new_point_empty());
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
static VALUE rb_tg_geometry_empty_linestring(VALUE self) {
|
|
967
|
+
(void)self;
|
|
968
|
+
return wrap_constructed_geom(tg_geom_new_linestring_empty());
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
static VALUE rb_tg_geometry_empty_polygon(VALUE self) {
|
|
972
|
+
(void)self;
|
|
973
|
+
return wrap_constructed_geom(tg_geom_new_polygon_empty());
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
static VALUE rb_tg_geometry_empty_multipoint(VALUE self) {
|
|
977
|
+
(void)self;
|
|
978
|
+
return wrap_constructed_geom(tg_geom_new_multipoint_empty());
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
static VALUE rb_tg_geometry_empty_multilinestring(VALUE self) {
|
|
982
|
+
(void)self;
|
|
983
|
+
return wrap_constructed_geom(tg_geom_new_multilinestring_empty());
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
static VALUE rb_tg_geometry_empty_multipolygon(VALUE self) {
|
|
987
|
+
(void)self;
|
|
988
|
+
return wrap_constructed_geom(tg_geom_new_multipolygon_empty());
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
static VALUE rb_tg_geometry_empty_geometrycollection(VALUE self) {
|
|
992
|
+
(void)self;
|
|
993
|
+
return wrap_constructed_geom(tg_geom_new_geometrycollection_empty());
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
static VALUE rect_build(double min_x, double min_y, double max_x, double max_y) {
|
|
997
|
+
tg_rect_wrapper_t *rect_data;
|
|
998
|
+
VALUE rect;
|
|
999
|
+
|
|
1000
|
+
validate_rect_coordinates(min_x, min_y, max_x, max_y);
|
|
1001
|
+
|
|
1002
|
+
rect = TypedData_Make_Struct(cTGGeometryRect, tg_rect_wrapper_t, &tg_rect_type, rect_data);
|
|
1003
|
+
rect_data->min_x = min_x;
|
|
1004
|
+
rect_data->min_y = min_y;
|
|
1005
|
+
rect_data->max_x = max_x;
|
|
1006
|
+
rect_data->max_y = max_y;
|
|
1007
|
+
rect_data->initialized = true;
|
|
1008
|
+
|
|
1009
|
+
rb_obj_freeze(rect);
|
|
1010
|
+
RB_GC_GUARD(rect);
|
|
1011
|
+
return rect;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
static VALUE rect_from_tg_rect(struct tg_rect rect) {
|
|
1015
|
+
return rect_build(rect.min.x, rect.min.y, rect.max.x, rect.max.y);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
static VALUE point_array_from_tg_point(struct tg_point point) {
|
|
1019
|
+
return rb_ary_new_from_args(2, rb_float_new(point.x), rb_float_new(point.y));
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
static VALUE segment_wrap_value(struct tg_segment segment) {
|
|
1023
|
+
tg_segment_wrapper_t *w;
|
|
1024
|
+
VALUE wrapper =
|
|
1025
|
+
TypedData_Make_Struct(cTGGeometrySegment, tg_segment_wrapper_t, &tg_segment_type, w);
|
|
1026
|
+
|
|
1027
|
+
w->segment = segment;
|
|
1028
|
+
w->initialized = true;
|
|
1029
|
+
|
|
1030
|
+
rb_obj_freeze(wrapper);
|
|
1031
|
+
RB_GC_GUARD(wrapper);
|
|
1032
|
+
return wrapper;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
static tg_segment_wrapper_t *get_segment_wrapper(VALUE value) {
|
|
1036
|
+
tg_segment_wrapper_t *w;
|
|
1037
|
+
|
|
1038
|
+
TypedData_Get_Struct(value, tg_segment_wrapper_t, &tg_segment_type, w);
|
|
1039
|
+
if (!w || !w->initialized) {
|
|
1040
|
+
rb_raise(eTGGeometryArgumentError, "invalid TG::Geometry::Segment");
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
return w;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
static int checked_child_index(VALUE index_value, int count, const char *name) {
|
|
1047
|
+
long index = NUM2LONG(index_value);
|
|
1048
|
+
|
|
1049
|
+
if (index < 0 || index >= count) {
|
|
1050
|
+
rb_raise(eTGGeometryArgumentError, "%s index out of range", name);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
if (index > INT_MAX) {
|
|
1054
|
+
rb_raise(eTGGeometryArgumentError, "%s index out of range", name);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
return (int)index;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
static VALUE line_wrap_borrowed(VALUE geom_owner, const struct tg_line *line) {
|
|
1061
|
+
tg_line_wrapper_t *w;
|
|
1062
|
+
VALUE wrapper;
|
|
1063
|
+
|
|
1064
|
+
if (!line) {
|
|
1065
|
+
return Qnil;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
wrapper = TypedData_Make_Struct(cTGGeometryLine, tg_line_wrapper_t, &tg_line_type, w);
|
|
1069
|
+
w->geom_owner = geom_owner;
|
|
1070
|
+
w->line = line;
|
|
1071
|
+
|
|
1072
|
+
rb_obj_freeze(wrapper);
|
|
1073
|
+
RB_GC_GUARD(geom_owner);
|
|
1074
|
+
RB_GC_GUARD(wrapper);
|
|
1075
|
+
return wrapper;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
static VALUE ring_wrap_borrowed(VALUE geom_owner, const struct tg_ring *ring) {
|
|
1079
|
+
tg_ring_wrapper_t *w;
|
|
1080
|
+
VALUE wrapper;
|
|
1081
|
+
|
|
1082
|
+
if (!ring) {
|
|
1083
|
+
return Qnil;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
wrapper = TypedData_Make_Struct(cTGGeometryRing, tg_ring_wrapper_t, &tg_ring_type, w);
|
|
1087
|
+
w->geom_owner = geom_owner;
|
|
1088
|
+
w->ring = ring;
|
|
1089
|
+
|
|
1090
|
+
rb_obj_freeze(wrapper);
|
|
1091
|
+
RB_GC_GUARD(geom_owner);
|
|
1092
|
+
RB_GC_GUARD(wrapper);
|
|
1093
|
+
return wrapper;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
static VALUE polygon_wrap_borrowed(VALUE geom_owner, const struct tg_poly *poly) {
|
|
1097
|
+
tg_polygon_wrapper_t *w;
|
|
1098
|
+
VALUE wrapper;
|
|
1099
|
+
|
|
1100
|
+
if (!poly) {
|
|
1101
|
+
return Qnil;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
wrapper = TypedData_Make_Struct(cTGGeometryPolygon, tg_polygon_wrapper_t, &tg_polygon_type, w);
|
|
1105
|
+
w->geom_owner = geom_owner;
|
|
1106
|
+
w->poly = poly;
|
|
1107
|
+
|
|
1108
|
+
rb_obj_freeze(wrapper);
|
|
1109
|
+
RB_GC_GUARD(geom_owner);
|
|
1110
|
+
RB_GC_GUARD(wrapper);
|
|
1111
|
+
return wrapper;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
static tg_line_wrapper_t *get_line_wrapper(VALUE value) {
|
|
1115
|
+
tg_line_wrapper_t *w;
|
|
1116
|
+
|
|
1117
|
+
TypedData_Get_Struct(value, tg_line_wrapper_t, &tg_line_type, w);
|
|
1118
|
+
if (!w || !w->line) {
|
|
1119
|
+
rb_raise(eTGGeometryArgumentError, "invalid TG::Geometry::Line");
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
return w;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
static tg_ring_wrapper_t *get_ring_wrapper(VALUE value) {
|
|
1126
|
+
tg_ring_wrapper_t *w;
|
|
1127
|
+
|
|
1128
|
+
TypedData_Get_Struct(value, tg_ring_wrapper_t, &tg_ring_type, w);
|
|
1129
|
+
if (!w || !w->ring) {
|
|
1130
|
+
rb_raise(eTGGeometryArgumentError, "invalid TG::Geometry::Ring");
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
return w;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
static tg_polygon_wrapper_t *get_polygon_wrapper(VALUE value) {
|
|
1137
|
+
tg_polygon_wrapper_t *w;
|
|
1138
|
+
|
|
1139
|
+
TypedData_Get_Struct(value, tg_polygon_wrapper_t, &tg_polygon_type, w);
|
|
1140
|
+
if (!w || !w->poly) {
|
|
1141
|
+
rb_raise(eTGGeometryArgumentError, "invalid TG::Geometry::Polygon");
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
return w;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
static VALUE rb_tg_geometry_rect_alloc(VALUE klass) {
|
|
1148
|
+
tg_rect_wrapper_t *rect_data;
|
|
1149
|
+
VALUE rect = TypedData_Make_Struct(klass, tg_rect_wrapper_t, &tg_rect_type, rect_data);
|
|
1150
|
+
|
|
1151
|
+
rect_data->initialized = false;
|
|
1152
|
+
return rect;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
static tg_rect_wrapper_t *get_rect_wrapper(VALUE value) {
|
|
1156
|
+
tg_rect_wrapper_t *rect_data;
|
|
1157
|
+
|
|
1158
|
+
TypedData_Get_Struct(value, tg_rect_wrapper_t, &tg_rect_type, rect_data);
|
|
1159
|
+
|
|
1160
|
+
if (!rect_data || !rect_data->initialized) {
|
|
1161
|
+
rb_raise(eTGGeometryArgumentError, "uninitialized TG::Geometry::Rect");
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return rect_data;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
static bool rect_intersects_rect_data(const tg_rect_wrapper_t *a, const tg_rect_wrapper_t *b) {
|
|
1168
|
+
return a->min_x <= b->max_x && a->max_x >= b->min_x && a->min_y <= b->max_y &&
|
|
1169
|
+
a->max_y >= b->min_y;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
static bool rect_contains_point_data(const tg_rect_wrapper_t *rect_data, double x, double y) {
|
|
1173
|
+
return x >= rect_data->min_x && x <= rect_data->max_x && y >= rect_data->min_y &&
|
|
1174
|
+
y <= rect_data->max_y;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
static VALUE rb_tg_geometry_rect_initialize(int argc, VALUE *argv, VALUE self) {
|
|
1178
|
+
VALUE min_x_value;
|
|
1179
|
+
VALUE min_y_value;
|
|
1180
|
+
VALUE max_x_value;
|
|
1181
|
+
VALUE max_y_value;
|
|
1182
|
+
tg_rect_wrapper_t *rect_data;
|
|
1183
|
+
double min_x;
|
|
1184
|
+
double min_y;
|
|
1185
|
+
double max_x;
|
|
1186
|
+
double max_y;
|
|
1187
|
+
|
|
1188
|
+
rb_check_arity(argc, 4, 4);
|
|
1189
|
+
rb_scan_args(argc, argv, "40", &min_x_value, &min_y_value, &max_x_value, &max_y_value);
|
|
1190
|
+
|
|
1191
|
+
min_x = NUM2DBL(min_x_value);
|
|
1192
|
+
min_y = NUM2DBL(min_y_value);
|
|
1193
|
+
max_x = NUM2DBL(max_x_value);
|
|
1194
|
+
max_y = NUM2DBL(max_y_value);
|
|
1195
|
+
|
|
1196
|
+
validate_rect_coordinates(min_x, min_y, max_x, max_y);
|
|
1197
|
+
|
|
1198
|
+
TypedData_Get_Struct(self, tg_rect_wrapper_t, &tg_rect_type, rect_data);
|
|
1199
|
+
rect_data->min_x = min_x;
|
|
1200
|
+
rect_data->min_y = min_y;
|
|
1201
|
+
rect_data->max_x = max_x;
|
|
1202
|
+
rect_data->max_y = max_y;
|
|
1203
|
+
rect_data->initialized = true;
|
|
1204
|
+
|
|
1205
|
+
rb_obj_freeze(self);
|
|
1206
|
+
return self;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
static VALUE rb_tg_geometry_rect_min_x(VALUE self) {
|
|
1210
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1211
|
+
return rb_float_new(rect_data->min_x);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
static VALUE rb_tg_geometry_rect_min_y(VALUE self) {
|
|
1215
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1216
|
+
return rb_float_new(rect_data->min_y);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
static VALUE rb_tg_geometry_rect_max_x(VALUE self) {
|
|
1220
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1221
|
+
return rb_float_new(rect_data->max_x);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
static VALUE rb_tg_geometry_rect_max_y(VALUE self) {
|
|
1225
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1226
|
+
return rb_float_new(rect_data->max_y);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
static VALUE rb_tg_geometry_rect_center(VALUE self) {
|
|
1230
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1231
|
+
double center_x = rect_data->min_x + ((rect_data->max_x - rect_data->min_x) / 2.0);
|
|
1232
|
+
double center_y = rect_data->min_y + ((rect_data->max_y - rect_data->min_y) / 2.0);
|
|
1233
|
+
|
|
1234
|
+
return rb_ary_new_from_args(2, rb_float_new(center_x), rb_float_new(center_y));
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
static VALUE rb_tg_geometry_rect_intersects_p(VALUE self, VALUE other) {
|
|
1238
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1239
|
+
tg_rect_wrapper_t *other_data = get_rect_wrapper(other);
|
|
1240
|
+
|
|
1241
|
+
return rect_intersects_rect_data(rect_data, other_data) ? Qtrue : Qfalse;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
static VALUE rb_tg_geometry_rect_contains_point_p(VALUE self, VALUE x_value, VALUE y_value) {
|
|
1245
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1246
|
+
double x = NUM2DBL(x_value);
|
|
1247
|
+
double y = NUM2DBL(y_value);
|
|
1248
|
+
|
|
1249
|
+
check_finite_double(x, "x");
|
|
1250
|
+
check_finite_double(y, "y");
|
|
1251
|
+
|
|
1252
|
+
return rect_contains_point_data(rect_data, x, y) ? Qtrue : Qfalse;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
static VALUE rb_tg_geometry_rect_expand_to_include(VALUE self, VALUE other) {
|
|
1256
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1257
|
+
tg_rect_wrapper_t *other_data = get_rect_wrapper(other);
|
|
1258
|
+
double min_x = rect_data->min_x < other_data->min_x ? rect_data->min_x : other_data->min_x;
|
|
1259
|
+
double min_y = rect_data->min_y < other_data->min_y ? rect_data->min_y : other_data->min_y;
|
|
1260
|
+
double max_x = rect_data->max_x > other_data->max_x ? rect_data->max_x : other_data->max_x;
|
|
1261
|
+
double max_y = rect_data->max_y > other_data->max_y ? rect_data->max_y : other_data->max_y;
|
|
1262
|
+
|
|
1263
|
+
return rect_build(min_x, min_y, max_x, max_y);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
static VALUE rb_tg_geometry_rect_expand_to_include_point(VALUE self, VALUE x_value, VALUE y_value) {
|
|
1267
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(self);
|
|
1268
|
+
double x = NUM2DBL(x_value);
|
|
1269
|
+
double y = NUM2DBL(y_value);
|
|
1270
|
+
double min_x;
|
|
1271
|
+
double min_y;
|
|
1272
|
+
double max_x;
|
|
1273
|
+
double max_y;
|
|
1274
|
+
|
|
1275
|
+
check_finite_double(x, "x");
|
|
1276
|
+
check_finite_double(y, "y");
|
|
1277
|
+
|
|
1278
|
+
min_x = rect_data->min_x < x ? rect_data->min_x : x;
|
|
1279
|
+
min_y = rect_data->min_y < y ? rect_data->min_y : y;
|
|
1280
|
+
max_x = rect_data->max_x > x ? rect_data->max_x : x;
|
|
1281
|
+
max_y = rect_data->max_y > y ? rect_data->max_y : y;
|
|
1282
|
+
|
|
1283
|
+
return rect_build(min_x, min_y, max_x, max_y);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
static VALUE rb_tg_geometry_geom_type(VALUE self) {
|
|
1287
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1288
|
+
|
|
1289
|
+
switch (tg_geom_typeof(w->geom)) {
|
|
1290
|
+
case TG_POINT:
|
|
1291
|
+
return ID2SYM(rb_intern("point"));
|
|
1292
|
+
case TG_LINESTRING:
|
|
1293
|
+
return ID2SYM(rb_intern("linestring"));
|
|
1294
|
+
case TG_POLYGON:
|
|
1295
|
+
return ID2SYM(rb_intern("polygon"));
|
|
1296
|
+
case TG_MULTIPOINT:
|
|
1297
|
+
return ID2SYM(rb_intern("multipoint"));
|
|
1298
|
+
case TG_MULTILINESTRING:
|
|
1299
|
+
return ID2SYM(rb_intern("multilinestring"));
|
|
1300
|
+
case TG_MULTIPOLYGON:
|
|
1301
|
+
return ID2SYM(rb_intern("multipolygon"));
|
|
1302
|
+
case TG_GEOMETRYCOLLECTION:
|
|
1303
|
+
return ID2SYM(rb_intern("geometrycollection"));
|
|
1304
|
+
default:
|
|
1305
|
+
return ID2SYM(rb_intern("unknown"));
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
static VALUE rb_tg_geometry_geom_bbox(VALUE self) {
|
|
1310
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1311
|
+
return rect_from_tg_rect(tg_geom_rect(w->geom));
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
static VALUE rb_tg_geometry_geom_covers_xy_p(VALUE self, VALUE x_value, VALUE y_value) {
|
|
1315
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1316
|
+
double x = NUM2DBL(x_value);
|
|
1317
|
+
double y = NUM2DBL(y_value);
|
|
1318
|
+
struct tg_geom *point;
|
|
1319
|
+
bool result;
|
|
1320
|
+
|
|
1321
|
+
check_finite_double(x, "x");
|
|
1322
|
+
check_finite_double(y, "y");
|
|
1323
|
+
|
|
1324
|
+
{
|
|
1325
|
+
struct tg_point tg_point = {x, y};
|
|
1326
|
+
point = tg_geom_new_point(tg_point);
|
|
1327
|
+
}
|
|
1328
|
+
if (!point) {
|
|
1329
|
+
rb_raise(rb_eNoMemError, "TG point geometry allocation failed");
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
if (tg_geom_error(point)) {
|
|
1333
|
+
raise_geom_error_and_free_as(point, eTGGeometryArgumentError,
|
|
1334
|
+
"TG point geometry allocation failed",
|
|
1335
|
+
"TG point geometry error message is too large",
|
|
1336
|
+
"TG point geometry error message allocation failed");
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
result = tg_geom_covers(w->geom, point);
|
|
1340
|
+
tg_geom_free(point);
|
|
1341
|
+
return result ? Qtrue : Qfalse;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
static VALUE geom_binary_predicate(VALUE self, VALUE other,
|
|
1345
|
+
bool (*predicate)(const struct tg_geom *,
|
|
1346
|
+
const struct tg_geom *)) {
|
|
1347
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1348
|
+
tg_geom_wrapper_t *other_w = get_geom_wrapper(other);
|
|
1349
|
+
|
|
1350
|
+
return predicate(w->geom, other_w->geom) ? Qtrue : Qfalse;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
static VALUE rb_tg_geometry_geom_equals_p(VALUE self, VALUE other) {
|
|
1354
|
+
return geom_binary_predicate(self, other, tg_geom_equals);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
static VALUE rb_tg_geometry_geom_contains_p(VALUE self, VALUE other) {
|
|
1358
|
+
return geom_binary_predicate(self, other, tg_geom_contains);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
static VALUE rb_tg_geometry_geom_intersects_p(VALUE self, VALUE other) {
|
|
1362
|
+
return geom_binary_predicate(self, other, tg_geom_intersects);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
static VALUE rb_tg_geometry_geom_disjoint_p(VALUE self, VALUE other) {
|
|
1366
|
+
return geom_binary_predicate(self, other, tg_geom_disjoint);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
static VALUE rb_tg_geometry_geom_within_p(VALUE self, VALUE other) {
|
|
1370
|
+
return geom_binary_predicate(self, other, tg_geom_within);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
static VALUE rb_tg_geometry_geom_covers_p(VALUE self, VALUE other) {
|
|
1374
|
+
return geom_binary_predicate(self, other, tg_geom_covers);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
static VALUE rb_tg_geometry_geom_covered_by_p(VALUE self, VALUE other) {
|
|
1378
|
+
return geom_binary_predicate(self, other, tg_geom_coveredby);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
static VALUE rb_tg_geometry_geom_touches_p(VALUE self, VALUE other) {
|
|
1382
|
+
return geom_binary_predicate(self, other, tg_geom_touches);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
static VALUE rb_tg_geometry_geom_intersects_xy_p(VALUE self, VALUE x_value, VALUE y_value) {
|
|
1386
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1387
|
+
double x = NUM2DBL(x_value);
|
|
1388
|
+
double y = NUM2DBL(y_value);
|
|
1389
|
+
|
|
1390
|
+
check_finite_double(x, "x");
|
|
1391
|
+
check_finite_double(y, "y");
|
|
1392
|
+
|
|
1393
|
+
return tg_geom_intersects_xy(w->geom, x, y) ? Qtrue : Qfalse;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
static VALUE rb_tg_geometry_geom_intersects_rect_p(int argc, VALUE *argv, VALUE self) {
|
|
1397
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1398
|
+
struct tg_rect rect;
|
|
1399
|
+
|
|
1400
|
+
if (argc == 1) {
|
|
1401
|
+
tg_rect_wrapper_t *rect_data = get_rect_wrapper(argv[0]);
|
|
1402
|
+
rect.min.x = rect_data->min_x;
|
|
1403
|
+
rect.min.y = rect_data->min_y;
|
|
1404
|
+
rect.max.x = rect_data->max_x;
|
|
1405
|
+
rect.max.y = rect_data->max_y;
|
|
1406
|
+
} else if (argc == 4) {
|
|
1407
|
+
double min_x = NUM2DBL(argv[0]);
|
|
1408
|
+
double min_y = NUM2DBL(argv[1]);
|
|
1409
|
+
double max_x = NUM2DBL(argv[2]);
|
|
1410
|
+
double max_y = NUM2DBL(argv[3]);
|
|
1411
|
+
|
|
1412
|
+
validate_rect_coordinates(min_x, min_y, max_x, max_y);
|
|
1413
|
+
rect.min.x = min_x;
|
|
1414
|
+
rect.min.y = min_y;
|
|
1415
|
+
rect.max.x = max_x;
|
|
1416
|
+
rect.max.y = max_y;
|
|
1417
|
+
} else {
|
|
1418
|
+
rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1 or 4)", argc);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
return tg_geom_intersects_rect(w->geom, rect) ? Qtrue : Qfalse;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
static VALUE text_writer_result(size_t (*writer)(const struct tg_geom *, char *, size_t),
|
|
1425
|
+
const struct tg_geom *geom) {
|
|
1426
|
+
size_t required = writer(geom, NULL, 0);
|
|
1427
|
+
VALUE str;
|
|
1428
|
+
size_t written;
|
|
1429
|
+
|
|
1430
|
+
if (required > (size_t)LONG_MAX - 1) {
|
|
1431
|
+
rb_raise(rb_eNoMemError, "serialized text output is too large");
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
str = rb_str_new(NULL, (long)(required + 1));
|
|
1435
|
+
written = writer(geom, RSTRING_PTR(str), required + 1);
|
|
1436
|
+
|
|
1437
|
+
if (written != required) {
|
|
1438
|
+
rb_raise(eTGGeometryError, "TG text writer size changed during serialization");
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
rb_str_set_len(str, (long)required);
|
|
1442
|
+
rb_enc_associate(str, rb_utf8_encoding());
|
|
1443
|
+
RB_GC_GUARD(str);
|
|
1444
|
+
return str;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
static VALUE rb_tg_geometry_geom_to_geojson(VALUE self) {
|
|
1448
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1449
|
+
return text_writer_result(tg_geom_geojson, w->geom);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
static VALUE rb_tg_geometry_geom_to_wkt(VALUE self) {
|
|
1453
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1454
|
+
return text_writer_result(tg_geom_wkt, w->geom);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
static VALUE binary_writer_result(size_t (*writer)(const struct tg_geom *, uint8_t *, size_t),
|
|
1458
|
+
const struct tg_geom *geom, const char *name) {
|
|
1459
|
+
size_t required = writer(geom, NULL, 0);
|
|
1460
|
+
VALUE str;
|
|
1461
|
+
size_t written;
|
|
1462
|
+
|
|
1463
|
+
if (required > (size_t)LONG_MAX) {
|
|
1464
|
+
rb_raise(rb_eNoMemError, "serialized %s output is too large", name);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
str = rb_str_new(NULL, (long)required);
|
|
1468
|
+
written = writer(geom, (uint8_t *)RSTRING_PTR(str), required);
|
|
1469
|
+
|
|
1470
|
+
if (written != required) {
|
|
1471
|
+
rb_raise(eTGGeometryError, "TG %s writer size changed during serialization", name);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
rb_enc_associate(str, rb_ascii8bit_encoding());
|
|
1475
|
+
RB_GC_GUARD(str);
|
|
1476
|
+
return str;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
static VALUE rb_tg_geometry_geom_to_wkb(VALUE self) {
|
|
1480
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1481
|
+
return binary_writer_result(tg_geom_wkb, w->geom, "WKB");
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
static VALUE rb_tg_geometry_geom_to_hex(VALUE self) {
|
|
1485
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1486
|
+
return text_writer_result(tg_geom_hex, w->geom);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
static VALUE rb_tg_geometry_geom_to_geobin(VALUE self) {
|
|
1490
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1491
|
+
return binary_writer_result(tg_geom_geobin, w->geom, "GeoBIN");
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
static VALUE rb_tg_geometry_geom_extra_json(VALUE self) {
|
|
1495
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1496
|
+
const char *extra_json = tg_geom_extra_json(w->geom);
|
|
1497
|
+
VALUE str;
|
|
1498
|
+
|
|
1499
|
+
if (!extra_json) {
|
|
1500
|
+
return Qnil;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
str = rb_str_new_cstr(extra_json);
|
|
1504
|
+
rb_enc_associate(str, rb_utf8_encoding());
|
|
1505
|
+
RB_GC_GUARD(str);
|
|
1506
|
+
return str;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
static VALUE rb_tg_geometry_geom_point(VALUE self) {
|
|
1510
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1511
|
+
|
|
1512
|
+
if (tg_geom_typeof(w->geom) != TG_POINT) {
|
|
1513
|
+
return Qnil;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
return point_array_from_tg_point(tg_geom_point(w->geom));
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
static VALUE rb_tg_geometry_geom_line(VALUE self) {
|
|
1520
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1521
|
+
|
|
1522
|
+
if (tg_geom_typeof(w->geom) != TG_LINESTRING) {
|
|
1523
|
+
return Qnil;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
return line_wrap_borrowed(self, tg_geom_line(w->geom));
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
static VALUE rb_tg_geometry_geom_polygon(VALUE self) {
|
|
1530
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1531
|
+
|
|
1532
|
+
if (tg_geom_typeof(w->geom) != TG_POLYGON) {
|
|
1533
|
+
return Qnil;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
return polygon_wrap_borrowed(self, tg_geom_poly(w->geom));
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
static VALUE rb_tg_geometry_geom_feature_p(VALUE self) {
|
|
1540
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1541
|
+
return tg_geom_is_feature(w->geom) ? Qtrue : Qfalse;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
static VALUE rb_tg_geometry_geom_feature_collection_p(VALUE self) {
|
|
1545
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1546
|
+
return tg_geom_is_featurecollection(w->geom) ? Qtrue : Qfalse;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
static VALUE rb_tg_geometry_geom_empty_p(VALUE self) {
|
|
1550
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1551
|
+
return tg_geom_is_empty(w->geom) ? Qtrue : Qfalse;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
static VALUE rb_tg_geometry_geom_dims(VALUE self) {
|
|
1555
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1556
|
+
return INT2NUM(tg_geom_dims(w->geom));
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
static VALUE rb_tg_geometry_geom_has_z_p(VALUE self) {
|
|
1560
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1561
|
+
return tg_geom_has_z(w->geom) ? Qtrue : Qfalse;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
static VALUE rb_tg_geometry_geom_has_m_p(VALUE self) {
|
|
1565
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1566
|
+
return tg_geom_has_m(w->geom) ? Qtrue : Qfalse;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
static VALUE rb_tg_geometry_geom_z(VALUE self) {
|
|
1570
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1571
|
+
if (!tg_geom_has_z(w->geom)) {
|
|
1572
|
+
return Qnil;
|
|
1573
|
+
}
|
|
1574
|
+
return rb_float_new(tg_geom_z(w->geom));
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
static VALUE rb_tg_geometry_geom_m(VALUE self) {
|
|
1578
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1579
|
+
if (!tg_geom_has_m(w->geom)) {
|
|
1580
|
+
return Qnil;
|
|
1581
|
+
}
|
|
1582
|
+
return rb_float_new(tg_geom_m(w->geom));
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
static VALUE rb_tg_geometry_geom_extra_coords(VALUE self) {
|
|
1586
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1587
|
+
int count = tg_geom_num_extra_coords(w->geom);
|
|
1588
|
+
const double *coords = tg_geom_extra_coords(w->geom);
|
|
1589
|
+
VALUE result = rb_ary_new_capa(count);
|
|
1590
|
+
|
|
1591
|
+
for (int i = 0; i < count; i++) {
|
|
1592
|
+
rb_ary_push(result, rb_float_new(coords[i]));
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
RB_GC_GUARD(self);
|
|
1596
|
+
RB_GC_GUARD(result);
|
|
1597
|
+
return result;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
static VALUE rb_tg_geometry_geom_num_points(VALUE self) {
|
|
1601
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1602
|
+
return INT2NUM(tg_geom_num_points(w->geom));
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
static VALUE rb_tg_geometry_geom_point_at(VALUE self, VALUE index_value) {
|
|
1606
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1607
|
+
int index = checked_child_index(index_value, tg_geom_num_points(w->geom), "geometry point");
|
|
1608
|
+
return point_array_from_tg_point(tg_geom_point_at(w->geom, index));
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
static VALUE rb_tg_geometry_geom_points(VALUE self) {
|
|
1612
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1613
|
+
int count = tg_geom_num_points(w->geom);
|
|
1614
|
+
VALUE points = rb_ary_new_capa(count);
|
|
1615
|
+
|
|
1616
|
+
for (int i = 0; i < count; i++) {
|
|
1617
|
+
rb_ary_push(points, point_array_from_tg_point(tg_geom_point_at(w->geom, i)));
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
RB_GC_GUARD(self);
|
|
1621
|
+
RB_GC_GUARD(points);
|
|
1622
|
+
return points;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
static VALUE rb_tg_geometry_geom_num_lines(VALUE self) {
|
|
1626
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1627
|
+
return INT2NUM(tg_geom_num_lines(w->geom));
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
static VALUE rb_tg_geometry_geom_line_at(VALUE self, VALUE index_value) {
|
|
1631
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1632
|
+
int index = checked_child_index(index_value, tg_geom_num_lines(w->geom), "geometry line");
|
|
1633
|
+
return line_wrap_borrowed(self, tg_geom_line_at(w->geom, index));
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
static VALUE rb_tg_geometry_geom_lines(VALUE self) {
|
|
1637
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1638
|
+
int count = tg_geom_num_lines(w->geom);
|
|
1639
|
+
VALUE lines = rb_ary_new_capa(count);
|
|
1640
|
+
|
|
1641
|
+
for (int i = 0; i < count; i++) {
|
|
1642
|
+
rb_ary_push(lines, line_wrap_borrowed(self, tg_geom_line_at(w->geom, i)));
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
RB_GC_GUARD(self);
|
|
1646
|
+
RB_GC_GUARD(lines);
|
|
1647
|
+
return lines;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
static VALUE rb_tg_geometry_geom_num_polygons(VALUE self) {
|
|
1651
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1652
|
+
return INT2NUM(tg_geom_num_polys(w->geom));
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
static VALUE rb_tg_geometry_geom_polygon_at(VALUE self, VALUE index_value) {
|
|
1656
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1657
|
+
int index = checked_child_index(index_value, tg_geom_num_polys(w->geom), "geometry polygon");
|
|
1658
|
+
return polygon_wrap_borrowed(self, tg_geom_poly_at(w->geom, index));
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
static VALUE rb_tg_geometry_geom_polygons(VALUE self) {
|
|
1662
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1663
|
+
int count = tg_geom_num_polys(w->geom);
|
|
1664
|
+
VALUE polygons = rb_ary_new_capa(count);
|
|
1665
|
+
|
|
1666
|
+
for (int i = 0; i < count; i++) {
|
|
1667
|
+
rb_ary_push(polygons, polygon_wrap_borrowed(self, tg_geom_poly_at(w->geom, i)));
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
RB_GC_GUARD(self);
|
|
1671
|
+
RB_GC_GUARD(polygons);
|
|
1672
|
+
return polygons;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
static VALUE rb_tg_geometry_geom_num_geometries(VALUE self) {
|
|
1676
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1677
|
+
return INT2NUM(tg_geom_num_geometries(w->geom));
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
static VALUE rb_tg_geometry_geom_geometry_at(VALUE self, VALUE index_value) {
|
|
1681
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1682
|
+
int index = checked_child_index(index_value, tg_geom_num_geometries(w->geom),
|
|
1683
|
+
"geometry collection child");
|
|
1684
|
+
return geom_wrap_borrowed(self, tg_geom_geometry_at(w->geom, index));
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
static VALUE rb_tg_geometry_geom_geometries(VALUE self) {
|
|
1688
|
+
tg_geom_wrapper_t *w = get_geom_wrapper(self);
|
|
1689
|
+
int count = tg_geom_num_geometries(w->geom);
|
|
1690
|
+
VALUE geometries = rb_ary_new_capa(count);
|
|
1691
|
+
|
|
1692
|
+
for (int i = 0; i < count; i++) {
|
|
1693
|
+
rb_ary_push(geometries, geom_wrap_borrowed(self, tg_geom_geometry_at(w->geom, i)));
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
RB_GC_GUARD(self);
|
|
1697
|
+
RB_GC_GUARD(geometries);
|
|
1698
|
+
return geometries;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
static VALUE rb_tg_geometry_line_bbox(VALUE self) {
|
|
1702
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1703
|
+
VALUE rect = rect_from_tg_rect(tg_line_rect(w->line));
|
|
1704
|
+
|
|
1705
|
+
RB_GC_GUARD(self);
|
|
1706
|
+
return rect;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
static VALUE rb_tg_geometry_line_num_points(VALUE self) {
|
|
1710
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1711
|
+
return INT2NUM(tg_line_num_points(w->line));
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
static VALUE rb_tg_geometry_line_point_at(VALUE self, VALUE index_value) {
|
|
1715
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1716
|
+
int index = checked_child_index(index_value, tg_line_num_points(w->line), "line point");
|
|
1717
|
+
|
|
1718
|
+
return point_array_from_tg_point(tg_line_point_at(w->line, index));
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
static VALUE rb_tg_geometry_line_points(VALUE self) {
|
|
1722
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1723
|
+
int count = tg_line_num_points(w->line);
|
|
1724
|
+
VALUE points = rb_ary_new_capa(count);
|
|
1725
|
+
|
|
1726
|
+
for (int i = 0; i < count; i++) {
|
|
1727
|
+
rb_ary_push(points, point_array_from_tg_point(tg_line_point_at(w->line, i)));
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
RB_GC_GUARD(self);
|
|
1731
|
+
RB_GC_GUARD(points);
|
|
1732
|
+
return points;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
static VALUE rb_tg_geometry_line_num_segments(VALUE self) {
|
|
1736
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1737
|
+
return INT2NUM(tg_line_num_segments(w->line));
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
static VALUE rb_tg_geometry_line_segment_at(VALUE self, VALUE index_value) {
|
|
1741
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1742
|
+
int index = checked_child_index(index_value, tg_line_num_segments(w->line), "line segment");
|
|
1743
|
+
|
|
1744
|
+
return segment_wrap_value(tg_line_segment_at(w->line, index));
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
static VALUE rb_tg_geometry_line_segments(VALUE self) {
|
|
1748
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1749
|
+
int count = tg_line_num_segments(w->line);
|
|
1750
|
+
VALUE segments = rb_ary_new_capa(count);
|
|
1751
|
+
|
|
1752
|
+
for (int i = 0; i < count; i++) {
|
|
1753
|
+
rb_ary_push(segments, segment_wrap_value(tg_line_segment_at(w->line, i)));
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
RB_GC_GUARD(self);
|
|
1757
|
+
RB_GC_GUARD(segments);
|
|
1758
|
+
return segments;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
static VALUE rb_tg_geometry_line_length(VALUE self) {
|
|
1762
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1763
|
+
return rb_float_new(tg_line_length(w->line));
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
static VALUE rb_tg_geometry_line_clockwise_p(VALUE self) {
|
|
1767
|
+
tg_line_wrapper_t *w = get_line_wrapper(self);
|
|
1768
|
+
return tg_line_clockwise(w->line) ? Qtrue : Qfalse;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
static VALUE rb_tg_geometry_ring_bbox(VALUE self) {
|
|
1772
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1773
|
+
VALUE rect = rect_from_tg_rect(tg_ring_rect(w->ring));
|
|
1774
|
+
|
|
1775
|
+
RB_GC_GUARD(self);
|
|
1776
|
+
return rect;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
static VALUE rb_tg_geometry_ring_num_points(VALUE self) {
|
|
1780
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1781
|
+
return INT2NUM(tg_ring_num_points(w->ring));
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
static VALUE rb_tg_geometry_ring_point_at(VALUE self, VALUE index_value) {
|
|
1785
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1786
|
+
int index = checked_child_index(index_value, tg_ring_num_points(w->ring), "ring point");
|
|
1787
|
+
|
|
1788
|
+
return point_array_from_tg_point(tg_ring_point_at(w->ring, index));
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
static VALUE rb_tg_geometry_ring_points(VALUE self) {
|
|
1792
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1793
|
+
int count = tg_ring_num_points(w->ring);
|
|
1794
|
+
VALUE points = rb_ary_new_capa(count);
|
|
1795
|
+
|
|
1796
|
+
for (int i = 0; i < count; i++) {
|
|
1797
|
+
rb_ary_push(points, point_array_from_tg_point(tg_ring_point_at(w->ring, i)));
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
RB_GC_GUARD(self);
|
|
1801
|
+
RB_GC_GUARD(points);
|
|
1802
|
+
return points;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
static VALUE rb_tg_geometry_ring_num_segments(VALUE self) {
|
|
1806
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1807
|
+
return INT2NUM(tg_ring_num_segments(w->ring));
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
static VALUE rb_tg_geometry_ring_segment_at(VALUE self, VALUE index_value) {
|
|
1811
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1812
|
+
int index = checked_child_index(index_value, tg_ring_num_segments(w->ring), "ring segment");
|
|
1813
|
+
|
|
1814
|
+
return segment_wrap_value(tg_ring_segment_at(w->ring, index));
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
static VALUE rb_tg_geometry_ring_segments(VALUE self) {
|
|
1818
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1819
|
+
int count = tg_ring_num_segments(w->ring);
|
|
1820
|
+
VALUE segments = rb_ary_new_capa(count);
|
|
1821
|
+
|
|
1822
|
+
for (int i = 0; i < count; i++) {
|
|
1823
|
+
rb_ary_push(segments, segment_wrap_value(tg_ring_segment_at(w->ring, i)));
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
RB_GC_GUARD(self);
|
|
1827
|
+
RB_GC_GUARD(segments);
|
|
1828
|
+
return segments;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
static VALUE rb_tg_geometry_ring_area(VALUE self) {
|
|
1832
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1833
|
+
return rb_float_new(tg_ring_area(w->ring));
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
static VALUE rb_tg_geometry_ring_perimeter(VALUE self) {
|
|
1837
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1838
|
+
return rb_float_new(tg_ring_perimeter(w->ring));
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
static VALUE rb_tg_geometry_ring_clockwise_p(VALUE self) {
|
|
1842
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1843
|
+
return tg_ring_clockwise(w->ring) ? Qtrue : Qfalse;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
static VALUE rb_tg_geometry_ring_convex_p(VALUE self) {
|
|
1847
|
+
tg_ring_wrapper_t *w = get_ring_wrapper(self);
|
|
1848
|
+
return tg_ring_convex(w->ring) ? Qtrue : Qfalse;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
static VALUE rb_tg_geometry_polygon_bbox(VALUE self) {
|
|
1852
|
+
tg_polygon_wrapper_t *w = get_polygon_wrapper(self);
|
|
1853
|
+
VALUE rect = rect_from_tg_rect(tg_poly_rect(w->poly));
|
|
1854
|
+
|
|
1855
|
+
RB_GC_GUARD(self);
|
|
1856
|
+
return rect;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
static VALUE rb_tg_geometry_polygon_exterior_ring(VALUE self) {
|
|
1860
|
+
tg_polygon_wrapper_t *w = get_polygon_wrapper(self);
|
|
1861
|
+
VALUE ring = ring_wrap_borrowed(w->geom_owner, tg_poly_exterior(w->poly));
|
|
1862
|
+
|
|
1863
|
+
RB_GC_GUARD(self);
|
|
1864
|
+
return ring;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
static VALUE rb_tg_geometry_polygon_num_holes(VALUE self) {
|
|
1868
|
+
tg_polygon_wrapper_t *w = get_polygon_wrapper(self);
|
|
1869
|
+
return INT2NUM(tg_poly_num_holes(w->poly));
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
static VALUE rb_tg_geometry_polygon_hole_at(VALUE self, VALUE index_value) {
|
|
1873
|
+
tg_polygon_wrapper_t *w = get_polygon_wrapper(self);
|
|
1874
|
+
int index = checked_child_index(index_value, tg_poly_num_holes(w->poly), "polygon hole");
|
|
1875
|
+
VALUE ring = ring_wrap_borrowed(w->geom_owner, tg_poly_hole_at(w->poly, index));
|
|
1876
|
+
|
|
1877
|
+
RB_GC_GUARD(self);
|
|
1878
|
+
return ring;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
static VALUE rb_tg_geometry_polygon_holes(VALUE self) {
|
|
1882
|
+
tg_polygon_wrapper_t *w = get_polygon_wrapper(self);
|
|
1883
|
+
int count = tg_poly_num_holes(w->poly);
|
|
1884
|
+
VALUE holes = rb_ary_new_capa(count);
|
|
1885
|
+
|
|
1886
|
+
for (int i = 0; i < count; i++) {
|
|
1887
|
+
rb_ary_push(holes, ring_wrap_borrowed(w->geom_owner, tg_poly_hole_at(w->poly, i)));
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
RB_GC_GUARD(self);
|
|
1891
|
+
RB_GC_GUARD(holes);
|
|
1892
|
+
return holes;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
static VALUE rb_tg_geometry_polygon_clockwise_p(VALUE self) {
|
|
1896
|
+
tg_polygon_wrapper_t *w = get_polygon_wrapper(self);
|
|
1897
|
+
return tg_poly_clockwise(w->poly) ? Qtrue : Qfalse;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
static VALUE rb_tg_geometry_segment_a(VALUE self) {
|
|
1901
|
+
tg_segment_wrapper_t *w = get_segment_wrapper(self);
|
|
1902
|
+
return point_array_from_tg_point(w->segment.a);
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
static VALUE rb_tg_geometry_segment_b(VALUE self) {
|
|
1906
|
+
tg_segment_wrapper_t *w = get_segment_wrapper(self);
|
|
1907
|
+
return point_array_from_tg_point(w->segment.b);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
static VALUE rb_tg_geometry_segment_points(VALUE self) {
|
|
1911
|
+
tg_segment_wrapper_t *w = get_segment_wrapper(self);
|
|
1912
|
+
return rb_ary_new_from_args(2, point_array_from_tg_point(w->segment.a),
|
|
1913
|
+
point_array_from_tg_point(w->segment.b));
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
static VALUE rb_tg_geometry_segment_bbox(VALUE self) {
|
|
1917
|
+
tg_segment_wrapper_t *w = get_segment_wrapper(self);
|
|
1918
|
+
return rect_from_tg_rect(tg_segment_rect(w->segment));
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
static VALUE rb_tg_geometry_segment_intersects_p(VALUE self, VALUE other) {
|
|
1922
|
+
tg_segment_wrapper_t *w = get_segment_wrapper(self);
|
|
1923
|
+
tg_segment_wrapper_t *other_w = get_segment_wrapper(other);
|
|
1924
|
+
|
|
1925
|
+
return tg_segment_intersects_segment(w->segment, other_w->segment) ? Qtrue : Qfalse;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
static tg_index_t *get_index_wrapper(VALUE value) {
|
|
1929
|
+
tg_index_t *idx;
|
|
1930
|
+
|
|
1931
|
+
TypedData_Get_Struct(value, tg_index_t, &tg_index_type, idx);
|
|
1932
|
+
|
|
1933
|
+
if (!idx) {
|
|
1934
|
+
rb_raise(eTGGeometryArgumentError, "invalid TG::Geometry::Index");
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
return idx;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
static void *tg_rtree_malloc(size_t size) {
|
|
1941
|
+
tg_index_t *owner = tg_current_rtree_owner;
|
|
1942
|
+
tg_rtree_alloc_header_t *header;
|
|
1943
|
+
|
|
1944
|
+
if (!owner) {
|
|
1945
|
+
return NULL;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
#ifdef TG_DEBUG_TEST
|
|
1949
|
+
if (tg_debug_fail_rtree_alloc_countdown >= 0) {
|
|
1950
|
+
if (tg_debug_fail_rtree_alloc_countdown == 0) {
|
|
1951
|
+
tg_debug_fail_rtree_alloc_countdown = -1;
|
|
1952
|
+
return NULL;
|
|
1953
|
+
}
|
|
1954
|
+
tg_debug_fail_rtree_alloc_countdown--;
|
|
1955
|
+
}
|
|
1956
|
+
#endif
|
|
1957
|
+
|
|
1958
|
+
if (size > SIZE_MAX - sizeof(tg_rtree_alloc_header_t)) {
|
|
1959
|
+
return NULL;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
header = (tg_rtree_alloc_header_t *)malloc(sizeof(tg_rtree_alloc_header_t) + size);
|
|
1963
|
+
if (!header) {
|
|
1964
|
+
return NULL;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
header->owner = owner;
|
|
1968
|
+
header->size = size;
|
|
1969
|
+
|
|
1970
|
+
owner->rtree_bytes += size;
|
|
1971
|
+
rb_gc_adjust_memory_usage((ssize_t)size);
|
|
1972
|
+
|
|
1973
|
+
return (void *)(header + 1);
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
static void tg_rtree_free(void *ptr) {
|
|
1977
|
+
tg_rtree_alloc_header_t *header;
|
|
1978
|
+
tg_index_t *owner;
|
|
1979
|
+
|
|
1980
|
+
if (!ptr) {
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
header = ((tg_rtree_alloc_header_t *)ptr) - 1;
|
|
1985
|
+
owner = header->owner;
|
|
1986
|
+
|
|
1987
|
+
if (owner) {
|
|
1988
|
+
if (owner->rtree_bytes >= header->size) {
|
|
1989
|
+
owner->rtree_bytes -= header->size;
|
|
1990
|
+
} else {
|
|
1991
|
+
owner->rtree_bytes = 0;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
rb_gc_adjust_memory_usage(-(ssize_t)header->size);
|
|
1996
|
+
free(header);
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
typedef struct {
|
|
2000
|
+
tg_index_t *idx;
|
|
2001
|
+
tg_index_t *saved_owner;
|
|
2002
|
+
} tg_rtree_build_args_t;
|
|
2003
|
+
|
|
2004
|
+
static VALUE rtree_build_body(VALUE arg) {
|
|
2005
|
+
tg_rtree_build_args_t *args = (tg_rtree_build_args_t *)arg;
|
|
2006
|
+
tg_index_t *idx = args->idx;
|
|
2007
|
+
|
|
2008
|
+
if (idx->len == 0) {
|
|
2009
|
+
return Qnil;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
tg_current_rtree_owner = idx;
|
|
2013
|
+
idx->rtree = rtree_new_with_allocator(tg_rtree_malloc, tg_rtree_free);
|
|
2014
|
+
if (!idx->rtree) {
|
|
2015
|
+
rb_raise(rb_eNoMemError, "rtree allocation failed");
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2019
|
+
double min[2] = {idx->entries[i].bbox.min.x, idx->entries[i].bbox.min.y};
|
|
2020
|
+
double max[2] = {idx->entries[i].bbox.max.x, idx->entries[i].bbox.max.y};
|
|
2021
|
+
|
|
2022
|
+
if (!rtree_insert(idx->rtree, min, max, &idx->entries[i])) {
|
|
2023
|
+
rb_raise(rb_eNoMemError, "rtree insert failed");
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
return Qnil;
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
static VALUE rtree_build_ensure(VALUE arg) {
|
|
2031
|
+
tg_rtree_build_args_t *args = (tg_rtree_build_args_t *)arg;
|
|
2032
|
+
tg_current_rtree_owner = args->saved_owner;
|
|
2033
|
+
return Qnil;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
static void index_build_rtree(tg_index_t *idx) {
|
|
2037
|
+
tg_rtree_build_args_t args;
|
|
2038
|
+
|
|
2039
|
+
if (idx->len == 0) {
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
args.idx = idx;
|
|
2044
|
+
args.saved_owner = tg_current_rtree_owner;
|
|
2045
|
+
|
|
2046
|
+
rb_ensure(rtree_build_body, (VALUE)&args, rtree_build_ensure, (VALUE)&args);
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
static struct tg_rect tg_rect_from_xyxy(double min_x, double min_y, double max_x, double max_y) {
|
|
2050
|
+
struct tg_rect rect;
|
|
2051
|
+
|
|
2052
|
+
rect.min.x = min_x;
|
|
2053
|
+
rect.min.y = min_y;
|
|
2054
|
+
rect.max.x = max_x;
|
|
2055
|
+
rect.max.y = max_y;
|
|
2056
|
+
return rect;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
static void tg_rect_to_arrays(struct tg_rect rect, double min[2], double max[2]) {
|
|
2060
|
+
min[0] = rect.min.x;
|
|
2061
|
+
min[1] = rect.min.y;
|
|
2062
|
+
max[0] = rect.max.x;
|
|
2063
|
+
max[1] = rect.max.y;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
static struct tg_geom *tg_query_point_new(double x, double y) {
|
|
2067
|
+
struct tg_point point = {x, y};
|
|
2068
|
+
return tg_geom_new_point(point);
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
static void tg_query_point_raise_if_invalid(struct tg_geom *point) {
|
|
2072
|
+
if (!point) {
|
|
2073
|
+
rb_raise(rb_eNoMemError, "TG point geometry allocation failed");
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
if (tg_geom_error(point)) {
|
|
2077
|
+
tg_geom_free(point);
|
|
2078
|
+
rb_raise(eTGGeometryError, "TG point geometry error");
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
static bool index_entry_matches_point(const tg_index_t *idx, const tg_index_entry_t *entry,
|
|
2083
|
+
const struct tg_geom *point) {
|
|
2084
|
+
switch (idx->predicate) {
|
|
2085
|
+
case TG_GEOMETRY_INDEX_PREDICATE_COVERS:
|
|
2086
|
+
return tg_geom_covers(entry->geom, point);
|
|
2087
|
+
case TG_GEOMETRY_INDEX_PREDICATE_CONTAINS:
|
|
2088
|
+
return tg_geom_contains(entry->geom, point);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
static bool index_entry_bbox_intersects_point(const tg_index_entry_t *entry, double x, double y) {
|
|
2095
|
+
struct tg_point point = {x, y};
|
|
2096
|
+
return tg_rect_intersects_point(entry->bbox, point);
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
static unsigned char *alloc_match_marks_raw(long len) {
|
|
2100
|
+
unsigned char *marks;
|
|
2101
|
+
|
|
2102
|
+
if (len <= 0) {
|
|
2103
|
+
return NULL;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
#ifdef TG_DEBUG_TEST
|
|
2107
|
+
if (tg_debug_fail_next_match_buffer_alloc) {
|
|
2108
|
+
tg_debug_fail_next_match_buffer_alloc = false;
|
|
2109
|
+
return NULL;
|
|
2110
|
+
}
|
|
2111
|
+
#endif
|
|
2112
|
+
|
|
2113
|
+
marks = (unsigned char *)calloc((size_t)len, sizeof(unsigned char));
|
|
2114
|
+
return marks;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
static unsigned char *alloc_match_marks(long len) {
|
|
2118
|
+
unsigned char *marks = alloc_match_marks_raw(len);
|
|
2119
|
+
|
|
2120
|
+
if (len <= 0) {
|
|
2121
|
+
return NULL;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
if (!marks) {
|
|
2125
|
+
rb_raise(rb_eNoMemError, "match buffer allocation failed");
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
return marks;
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
typedef struct {
|
|
2132
|
+
unsigned char *marks;
|
|
2133
|
+
long len;
|
|
2134
|
+
} tg_rtree_mark_args_t;
|
|
2135
|
+
|
|
2136
|
+
static bool rtree_mark_candidate_iter(const double *min, const double *max, const void *data,
|
|
2137
|
+
void *udata) {
|
|
2138
|
+
const tg_index_entry_t *entry = (const tg_index_entry_t *)data;
|
|
2139
|
+
tg_rtree_mark_args_t *args = (tg_rtree_mark_args_t *)udata;
|
|
2140
|
+
|
|
2141
|
+
(void)min;
|
|
2142
|
+
(void)max;
|
|
2143
|
+
|
|
2144
|
+
if (entry && entry->ordinal >= 0 && entry->ordinal < args->len) {
|
|
2145
|
+
args->marks[entry->ordinal] = 1;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
return true;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
static unsigned char *rtree_candidate_marks(tg_index_t *idx, struct tg_rect query_rect) {
|
|
2152
|
+
unsigned char *marks;
|
|
2153
|
+
tg_rtree_mark_args_t args;
|
|
2154
|
+
double min[2];
|
|
2155
|
+
double max[2];
|
|
2156
|
+
|
|
2157
|
+
marks = alloc_match_marks(idx->len);
|
|
2158
|
+
if (idx->len == 0 || !idx->rtree) {
|
|
2159
|
+
return marks;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
args.marks = marks;
|
|
2163
|
+
args.len = idx->len;
|
|
2164
|
+
tg_rect_to_arrays(query_rect, min, max);
|
|
2165
|
+
rtree_search(idx->rtree, min, max, rtree_mark_candidate_iter, &args);
|
|
2166
|
+
|
|
2167
|
+
return marks;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
static VALUE build_ids_from_marks(tg_index_t *idx, const unsigned char *marks) {
|
|
2171
|
+
VALUE result = rb_ary_new();
|
|
2172
|
+
|
|
2173
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2174
|
+
if (marks[i]) {
|
|
2175
|
+
rb_ary_push(result, idx->entries[i].id);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
return result;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
typedef struct {
|
|
2183
|
+
tg_index_t *idx;
|
|
2184
|
+
unsigned char *marks;
|
|
2185
|
+
} tg_build_ids_args_t;
|
|
2186
|
+
|
|
2187
|
+
static VALUE build_ids_from_marks_body(VALUE arg) {
|
|
2188
|
+
tg_build_ids_args_t *args = (tg_build_ids_args_t *)arg;
|
|
2189
|
+
return build_ids_from_marks(args->idx, args->marks);
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
static VALUE build_ids_from_marks_protected(tg_index_t *idx, unsigned char *marks) {
|
|
2193
|
+
tg_build_ids_args_t args;
|
|
2194
|
+
VALUE result;
|
|
2195
|
+
int state = 0;
|
|
2196
|
+
|
|
2197
|
+
args.idx = idx;
|
|
2198
|
+
args.marks = marks;
|
|
2199
|
+
result = rb_protect(build_ids_from_marks_body, (VALUE)&args, &state);
|
|
2200
|
+
free(marks);
|
|
2201
|
+
|
|
2202
|
+
if (state) {
|
|
2203
|
+
rb_jump_tag(state);
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
return result;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
static VALUE index_find_covering_value(tg_index_t *idx, double lon, double lat) {
|
|
2210
|
+
struct tg_geom *point;
|
|
2211
|
+
VALUE result = Qnil;
|
|
2212
|
+
|
|
2213
|
+
if (idx->len == 0) {
|
|
2214
|
+
return Qnil;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
if (idx->strategy == TG_GEOMETRY_INDEX_STRATEGY_RTREE) {
|
|
2218
|
+
struct tg_rect point_rect = tg_rect_from_xyxy(lon, lat, lon, lat);
|
|
2219
|
+
unsigned char *candidates = rtree_candidate_marks(idx, point_rect);
|
|
2220
|
+
|
|
2221
|
+
point = tg_query_point_new(lon, lat);
|
|
2222
|
+
if (!point) {
|
|
2223
|
+
free(candidates);
|
|
2224
|
+
rb_raise(rb_eNoMemError, "TG point geometry allocation failed");
|
|
2225
|
+
}
|
|
2226
|
+
if (tg_geom_error(point)) {
|
|
2227
|
+
tg_geom_free(point);
|
|
2228
|
+
free(candidates);
|
|
2229
|
+
rb_raise(eTGGeometryError, "TG point geometry error");
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2233
|
+
tg_index_entry_t *entry = &idx->entries[i];
|
|
2234
|
+
|
|
2235
|
+
if (!candidates[i]) {
|
|
2236
|
+
continue;
|
|
2237
|
+
}
|
|
2238
|
+
if (index_entry_matches_point(idx, entry, point)) {
|
|
2239
|
+
result = entry->id;
|
|
2240
|
+
break;
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
tg_geom_free(point);
|
|
2245
|
+
free(candidates);
|
|
2246
|
+
return result;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
point = tg_query_point_new(lon, lat);
|
|
2250
|
+
tg_query_point_raise_if_invalid(point);
|
|
2251
|
+
|
|
2252
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2253
|
+
tg_index_entry_t *entry = &idx->entries[i];
|
|
2254
|
+
|
|
2255
|
+
if (!index_entry_bbox_intersects_point(entry, lon, lat)) {
|
|
2256
|
+
continue;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
if (index_entry_matches_point(idx, entry, point)) {
|
|
2260
|
+
result = entry->id;
|
|
2261
|
+
break;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
tg_geom_free(point);
|
|
2266
|
+
return result;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
static unsigned char *index_covering_marks(tg_index_t *idx, double lon, double lat) {
|
|
2270
|
+
unsigned char *marks;
|
|
2271
|
+
struct tg_geom *point;
|
|
2272
|
+
|
|
2273
|
+
if (idx->len == 0) {
|
|
2274
|
+
return NULL;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
if (idx->strategy == TG_GEOMETRY_INDEX_STRATEGY_RTREE) {
|
|
2278
|
+
struct tg_rect point_rect = tg_rect_from_xyxy(lon, lat, lon, lat);
|
|
2279
|
+
unsigned char *candidates = rtree_candidate_marks(idx, point_rect);
|
|
2280
|
+
|
|
2281
|
+
marks = alloc_match_marks_raw(idx->len);
|
|
2282
|
+
if (!marks) {
|
|
2283
|
+
free(candidates);
|
|
2284
|
+
rb_raise(rb_eNoMemError, "match buffer allocation failed");
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
point = tg_query_point_new(lon, lat);
|
|
2288
|
+
if (!point) {
|
|
2289
|
+
free(candidates);
|
|
2290
|
+
free(marks);
|
|
2291
|
+
rb_raise(rb_eNoMemError, "TG point geometry allocation failed");
|
|
2292
|
+
}
|
|
2293
|
+
if (tg_geom_error(point)) {
|
|
2294
|
+
tg_geom_free(point);
|
|
2295
|
+
free(candidates);
|
|
2296
|
+
free(marks);
|
|
2297
|
+
rb_raise(eTGGeometryError, "TG point geometry error");
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2301
|
+
tg_index_entry_t *entry = &idx->entries[i];
|
|
2302
|
+
|
|
2303
|
+
if (candidates[i] && index_entry_matches_point(idx, entry, point)) {
|
|
2304
|
+
marks[i] = 1;
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
tg_geom_free(point);
|
|
2309
|
+
free(candidates);
|
|
2310
|
+
return marks;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
marks = alloc_match_marks(idx->len);
|
|
2314
|
+
point = tg_query_point_new(lon, lat);
|
|
2315
|
+
if (!point) {
|
|
2316
|
+
free(marks);
|
|
2317
|
+
rb_raise(rb_eNoMemError, "TG point geometry allocation failed");
|
|
2318
|
+
}
|
|
2319
|
+
if (tg_geom_error(point)) {
|
|
2320
|
+
tg_geom_free(point);
|
|
2321
|
+
free(marks);
|
|
2322
|
+
rb_raise(eTGGeometryError, "TG point geometry error");
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2326
|
+
tg_index_entry_t *entry = &idx->entries[i];
|
|
2327
|
+
|
|
2328
|
+
if (index_entry_bbox_intersects_point(entry, lon, lat) &&
|
|
2329
|
+
index_entry_matches_point(idx, entry, point)) {
|
|
2330
|
+
marks[i] = 1;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
tg_geom_free(point);
|
|
2335
|
+
return marks;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
static unsigned char *index_intersecting_rect_marks(tg_index_t *idx, struct tg_rect query_rect) {
|
|
2339
|
+
unsigned char *marks;
|
|
2340
|
+
|
|
2341
|
+
if (idx->len == 0) {
|
|
2342
|
+
return NULL;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
if (idx->strategy == TG_GEOMETRY_INDEX_STRATEGY_RTREE) {
|
|
2346
|
+
unsigned char *candidates = rtree_candidate_marks(idx, query_rect);
|
|
2347
|
+
|
|
2348
|
+
marks = alloc_match_marks_raw(idx->len);
|
|
2349
|
+
if (!marks) {
|
|
2350
|
+
free(candidates);
|
|
2351
|
+
rb_raise(rb_eNoMemError, "match buffer allocation failed");
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2355
|
+
tg_index_entry_t *entry = &idx->entries[i];
|
|
2356
|
+
|
|
2357
|
+
if (candidates[i] && tg_geom_intersects_rect(entry->geom, query_rect)) {
|
|
2358
|
+
marks[i] = 1;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
free(candidates);
|
|
2363
|
+
return marks;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
marks = alloc_match_marks(idx->len);
|
|
2367
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2368
|
+
tg_index_entry_t *entry = &idx->entries[i];
|
|
2369
|
+
|
|
2370
|
+
if (tg_rect_intersects_rect(entry->bbox, query_rect) &&
|
|
2371
|
+
tg_geom_intersects_rect(entry->geom, query_rect)) {
|
|
2372
|
+
marks[i] = 1;
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
return marks;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
typedef struct {
|
|
2380
|
+
tg_index_t *idx;
|
|
2381
|
+
VALUE entries;
|
|
2382
|
+
enum tg_geometry_index_via via;
|
|
2383
|
+
enum tg_index geometry_index;
|
|
2384
|
+
} tg_index_build_args_t;
|
|
2385
|
+
|
|
2386
|
+
static void index_expand_bbox(tg_index_t *idx, struct tg_rect bbox) {
|
|
2387
|
+
if (!idx->has_bbox) {
|
|
2388
|
+
idx->bbox = bbox;
|
|
2389
|
+
idx->has_bbox = true;
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
if (bbox.min.x < idx->bbox.min.x)
|
|
2394
|
+
idx->bbox.min.x = bbox.min.x;
|
|
2395
|
+
if (bbox.min.y < idx->bbox.min.y)
|
|
2396
|
+
idx->bbox.min.y = bbox.min.y;
|
|
2397
|
+
if (bbox.max.x > idx->bbox.max.x)
|
|
2398
|
+
idx->bbox.max.x = bbox.max.x;
|
|
2399
|
+
if (bbox.max.y > idx->bbox.max.y)
|
|
2400
|
+
idx->bbox.max.y = bbox.max.y;
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
static void raise_parse_error_and_free_owned_geom(struct tg_geom *geom) {
|
|
2404
|
+
const char *err;
|
|
2405
|
+
char *message_copy;
|
|
2406
|
+
size_t message_len;
|
|
2407
|
+
tg_string_copy_args_t string_args;
|
|
2408
|
+
VALUE message;
|
|
2409
|
+
int state = 0;
|
|
2410
|
+
|
|
2411
|
+
if (!geom) {
|
|
2412
|
+
rb_raise(rb_eNoMemError, "TG geometry allocation failed");
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
err = tg_geom_error(geom);
|
|
2416
|
+
if (!err)
|
|
2417
|
+
return;
|
|
2418
|
+
|
|
2419
|
+
message_len = strlen(err);
|
|
2420
|
+
if (message_len > LONG_MAX) {
|
|
2421
|
+
tg_geom_free(geom);
|
|
2422
|
+
rb_raise(rb_eNoMemError, "parse error message is too large");
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
message_copy = malloc(message_len + 1);
|
|
2426
|
+
if (!message_copy) {
|
|
2427
|
+
tg_geom_free(geom);
|
|
2428
|
+
rb_raise(rb_eNoMemError, "parse error message allocation failed");
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
memcpy(message_copy, err, message_len + 1);
|
|
2432
|
+
tg_geom_free(geom);
|
|
2433
|
+
|
|
2434
|
+
string_args.ptr = message_copy;
|
|
2435
|
+
string_args.len = (long)message_len;
|
|
2436
|
+
message = rb_protect(rb_str_new_from_c_copy, (VALUE)&string_args, &state);
|
|
2437
|
+
free(message_copy);
|
|
2438
|
+
|
|
2439
|
+
if (state) {
|
|
2440
|
+
rb_jump_tag(state);
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
rb_exc_raise(rb_exc_new_str(eTGGeometryParseError, message));
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
static void fill_owned_index_entry(tg_index_t *idx, long i, VALUE id, VALUE value,
|
|
2447
|
+
enum tg_geometry_index_via via, enum tg_index geometry_index) {
|
|
2448
|
+
tg_index_entry_t entry;
|
|
2449
|
+
struct tg_geom *geom;
|
|
2450
|
+
VALUE string_value = value;
|
|
2451
|
+
|
|
2452
|
+
if (!RB_TYPE_P(value, T_STRING)) {
|
|
2453
|
+
rb_raise(rb_eTypeError, "entry object must be a String for via: :geojson or via: :wkb");
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
StringValue(string_value);
|
|
2457
|
+
|
|
2458
|
+
switch (via) {
|
|
2459
|
+
case TG_GEOMETRY_INDEX_VIA_GEOJSON:
|
|
2460
|
+
geom = tg_parse_geojsonn_ix(RSTRING_PTR(string_value), (size_t)RSTRING_LEN(string_value),
|
|
2461
|
+
geometry_index);
|
|
2462
|
+
break;
|
|
2463
|
+
case TG_GEOMETRY_INDEX_VIA_WKB:
|
|
2464
|
+
geom = tg_parse_wkb_ix((const uint8_t *)RSTRING_PTR(string_value),
|
|
2465
|
+
(size_t)RSTRING_LEN(string_value), geometry_index);
|
|
2466
|
+
break;
|
|
2467
|
+
case TG_GEOMETRY_INDEX_VIA_GEOM:
|
|
2468
|
+
rb_raise(eTGGeometryError, "internal owned entry build mode mismatch");
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
raise_parse_error_and_free_owned_geom(geom);
|
|
2472
|
+
|
|
2473
|
+
memset(&entry, 0, sizeof(entry));
|
|
2474
|
+
entry.id = id;
|
|
2475
|
+
entry.geom_owner = Qnil;
|
|
2476
|
+
entry.geom = geom;
|
|
2477
|
+
entry.bbox = tg_geom_rect(geom);
|
|
2478
|
+
entry.geom_bytes = tg_geom_memsize(geom);
|
|
2479
|
+
entry.ordinal = i;
|
|
2480
|
+
entry.owned = true;
|
|
2481
|
+
|
|
2482
|
+
idx->entries[i] = entry;
|
|
2483
|
+
idx->initialized++;
|
|
2484
|
+
idx->owned_geom_bytes_total += entry.geom_bytes;
|
|
2485
|
+
if (entry.geom_bytes > 0) {
|
|
2486
|
+
rb_gc_adjust_memory_usage((ssize_t)entry.geom_bytes);
|
|
2487
|
+
}
|
|
2488
|
+
index_expand_bbox(idx, entry.bbox);
|
|
2489
|
+
|
|
2490
|
+
RB_GC_GUARD(string_value);
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
static void fill_borrowed_index_entry(tg_index_t *idx, long i, VALUE id, VALUE value) {
|
|
2494
|
+
tg_index_entry_t entry;
|
|
2495
|
+
tg_geom_wrapper_t *geom_wrapper = get_geom_wrapper(value);
|
|
2496
|
+
struct tg_geom *borrowed_geom = geom_wrapper->geom;
|
|
2497
|
+
|
|
2498
|
+
if (!borrowed_geom) {
|
|
2499
|
+
rb_raise(eTGGeometryArgumentError, "invalid TG::Geometry::Geom");
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
memset(&entry, 0, sizeof(entry));
|
|
2503
|
+
entry.id = id;
|
|
2504
|
+
entry.geom_owner = value;
|
|
2505
|
+
entry.geom = borrowed_geom;
|
|
2506
|
+
entry.bbox = tg_geom_rect(borrowed_geom);
|
|
2507
|
+
entry.geom_bytes = 0;
|
|
2508
|
+
entry.ordinal = i;
|
|
2509
|
+
entry.owned = false;
|
|
2510
|
+
|
|
2511
|
+
idx->entries[i] = entry;
|
|
2512
|
+
idx->initialized++;
|
|
2513
|
+
index_expand_bbox(idx, entry.bbox);
|
|
2514
|
+
|
|
2515
|
+
RB_GC_GUARD(value);
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
static VALUE index_build_body(VALUE arg) {
|
|
2519
|
+
tg_index_build_args_t *args = (tg_index_build_args_t *)arg;
|
|
2520
|
+
tg_index_t *idx = args->idx;
|
|
2521
|
+
|
|
2522
|
+
for (long i = 0; i < idx->len; i++) {
|
|
2523
|
+
VALUE pair = rb_ary_entry(args->entries, i);
|
|
2524
|
+
VALUE id;
|
|
2525
|
+
VALUE value;
|
|
2526
|
+
|
|
2527
|
+
if (!RB_TYPE_P(pair, T_ARRAY)) {
|
|
2528
|
+
rb_raise(rb_eTypeError, "each entry must be a two-element Array");
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
if (RARRAY_LEN(pair) != 2) {
|
|
2532
|
+
rb_raise(eTGGeometryArgumentError, "each entry must contain exactly [id, object]");
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
id = rb_ary_entry(pair, 0);
|
|
2536
|
+
value = rb_ary_entry(pair, 1);
|
|
2537
|
+
|
|
2538
|
+
if (NIL_P(id)) {
|
|
2539
|
+
rb_raise(eTGGeometryArgumentError, "id cannot be nil");
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
switch (args->via) {
|
|
2543
|
+
case TG_GEOMETRY_INDEX_VIA_GEOM:
|
|
2544
|
+
fill_borrowed_index_entry(idx, i, id, value);
|
|
2545
|
+
break;
|
|
2546
|
+
case TG_GEOMETRY_INDEX_VIA_GEOJSON:
|
|
2547
|
+
case TG_GEOMETRY_INDEX_VIA_WKB:
|
|
2548
|
+
fill_owned_index_entry(idx, i, id, value, args->via, args->geometry_index);
|
|
2549
|
+
break;
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
if (idx->strategy == TG_GEOMETRY_INDEX_STRATEGY_RTREE) {
|
|
2554
|
+
index_build_rtree(idx);
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
return Qnil;
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
static VALUE rb_tg_geometry_index_build(int argc, VALUE *argv, VALUE klass) {
|
|
2561
|
+
VALUE entries_value;
|
|
2562
|
+
VALUE kwargs;
|
|
2563
|
+
VALUE via_value;
|
|
2564
|
+
VALUE strategy_value;
|
|
2565
|
+
VALUE predicate_value;
|
|
2566
|
+
VALUE geometry_index_value;
|
|
2567
|
+
enum tg_geometry_index_via via;
|
|
2568
|
+
enum tg_geometry_index_strategy strategy;
|
|
2569
|
+
enum tg_geometry_index_predicate predicate;
|
|
2570
|
+
enum tg_index geometry_index;
|
|
2571
|
+
long len;
|
|
2572
|
+
tg_index_t *idx;
|
|
2573
|
+
VALUE wrapper;
|
|
2574
|
+
tg_index_build_args_t args;
|
|
2575
|
+
int state = 0;
|
|
2576
|
+
|
|
2577
|
+
rb_scan_args(argc, argv, "1:", &entries_value, &kwargs);
|
|
2578
|
+
|
|
2579
|
+
if (!RB_TYPE_P(entries_value, T_ARRAY)) {
|
|
2580
|
+
rb_raise(rb_eTypeError, "entries must be Array");
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
via_value = required_kwargs_value(kwargs, id_via, "via:");
|
|
2584
|
+
strategy_value = required_kwargs_value(kwargs, id_strategy, "strategy:");
|
|
2585
|
+
predicate_value = kwargs_value(kwargs, id_predicate, ID2SYM(id_covers));
|
|
2586
|
+
geometry_index_value = kwargs_value(kwargs, id_geometry_index, ID2SYM(id_ystripes));
|
|
2587
|
+
|
|
2588
|
+
via = parse_index_via_symbol(via_value);
|
|
2589
|
+
strategy = parse_index_strategy_symbol(strategy_value);
|
|
2590
|
+
predicate = parse_index_predicate_symbol(predicate_value);
|
|
2591
|
+
geometry_index = parse_index_symbol(geometry_index_value);
|
|
2592
|
+
|
|
2593
|
+
len = RARRAY_LEN(entries_value);
|
|
2594
|
+
wrapper = TypedData_Make_Struct(klass, tg_index_t, &tg_index_type, idx);
|
|
2595
|
+
idx->len = len;
|
|
2596
|
+
idx->capacity = len;
|
|
2597
|
+
idx->initialized = 0;
|
|
2598
|
+
idx->strategy = strategy;
|
|
2599
|
+
idx->predicate = predicate;
|
|
2600
|
+
idx->rtree = NULL;
|
|
2601
|
+
idx->frozen = false;
|
|
2602
|
+
idx->has_bbox = false;
|
|
2603
|
+
|
|
2604
|
+
if (len > 0) {
|
|
2605
|
+
if ((size_t)len > SIZE_MAX / sizeof(tg_index_entry_t)) {
|
|
2606
|
+
rb_raise(rb_eNoMemError, "entries allocation size overflow");
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
#ifdef TG_DEBUG_TEST
|
|
2610
|
+
if (tg_debug_fail_next_entries_alloc) {
|
|
2611
|
+
tg_debug_fail_next_entries_alloc = false;
|
|
2612
|
+
rb_raise(rb_eNoMemError, "entries allocation failed");
|
|
2613
|
+
}
|
|
2614
|
+
#endif
|
|
2615
|
+
|
|
2616
|
+
idx->entries = calloc((size_t)len, sizeof(tg_index_entry_t));
|
|
2617
|
+
if (!idx->entries) {
|
|
2618
|
+
rb_raise(rb_eNoMemError, "entries allocation failed");
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
idx->entries_bytes = (size_t)len * sizeof(tg_index_entry_t);
|
|
2622
|
+
rb_gc_adjust_memory_usage((ssize_t)idx->entries_bytes);
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
args.idx = idx;
|
|
2626
|
+
args.entries = entries_value;
|
|
2627
|
+
args.via = via;
|
|
2628
|
+
args.geometry_index = geometry_index;
|
|
2629
|
+
|
|
2630
|
+
rb_protect(index_build_body, (VALUE)&args, &state);
|
|
2631
|
+
|
|
2632
|
+
if (state) {
|
|
2633
|
+
index_dispose(idx);
|
|
2634
|
+
RB_GC_GUARD(entries_value);
|
|
2635
|
+
RB_GC_GUARD(wrapper);
|
|
2636
|
+
rb_jump_tag(state);
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
if (idx->initialized != idx->len) {
|
|
2640
|
+
index_dispose(idx);
|
|
2641
|
+
rb_raise(eTGGeometryError, "internal index build initialization mismatch");
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
idx->frozen = true;
|
|
2645
|
+
rb_obj_freeze(wrapper);
|
|
2646
|
+
|
|
2647
|
+
RB_GC_GUARD(entries_value);
|
|
2648
|
+
RB_GC_GUARD(wrapper);
|
|
2649
|
+
return wrapper;
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
static VALUE rb_tg_geometry_index_size(VALUE self) {
|
|
2653
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2654
|
+
return LONG2NUM(idx->len);
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
static VALUE rb_tg_geometry_index_strategy(VALUE self) {
|
|
2658
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2659
|
+
|
|
2660
|
+
switch (idx->strategy) {
|
|
2661
|
+
case TG_GEOMETRY_INDEX_STRATEGY_FLAT:
|
|
2662
|
+
return ID2SYM(id_flat);
|
|
2663
|
+
case TG_GEOMETRY_INDEX_STRATEGY_RTREE:
|
|
2664
|
+
return ID2SYM(id_rtree);
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
return Qnil;
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
static VALUE rb_tg_geometry_index_predicate(VALUE self) {
|
|
2671
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2672
|
+
|
|
2673
|
+
switch (idx->predicate) {
|
|
2674
|
+
case TG_GEOMETRY_INDEX_PREDICATE_COVERS:
|
|
2675
|
+
return ID2SYM(id_covers);
|
|
2676
|
+
case TG_GEOMETRY_INDEX_PREDICATE_CONTAINS:
|
|
2677
|
+
return ID2SYM(id_contains);
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
return Qnil;
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
static VALUE rb_tg_geometry_index_bbox(VALUE self) {
|
|
2684
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2685
|
+
|
|
2686
|
+
if (!idx->has_bbox)
|
|
2687
|
+
return Qnil;
|
|
2688
|
+
|
|
2689
|
+
return rect_from_tg_rect(idx->bbox);
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
static void parse_public_point_args(VALUE lon_value, VALUE lat_value, double *lon, double *lat) {
|
|
2693
|
+
*lon = NUM2DBL(lon_value);
|
|
2694
|
+
*lat = NUM2DBL(lat_value);
|
|
2695
|
+
check_finite_double(*lon, "lon");
|
|
2696
|
+
check_finite_double(*lat, "lat");
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
static VALUE rb_tg_geometry_index_find_covering(VALUE self, VALUE lon_value, VALUE lat_value) {
|
|
2700
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2701
|
+
double lon;
|
|
2702
|
+
double lat;
|
|
2703
|
+
VALUE result;
|
|
2704
|
+
|
|
2705
|
+
parse_public_point_args(lon_value, lat_value, &lon, &lat);
|
|
2706
|
+
result = index_find_covering_value(idx, lon, lat);
|
|
2707
|
+
|
|
2708
|
+
RB_GC_GUARD(self);
|
|
2709
|
+
return result;
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
static VALUE rb_tg_geometry_index_covering_ids(VALUE self, VALUE lon_value, VALUE lat_value) {
|
|
2713
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2714
|
+
double lon;
|
|
2715
|
+
double lat;
|
|
2716
|
+
unsigned char *marks;
|
|
2717
|
+
VALUE result;
|
|
2718
|
+
|
|
2719
|
+
parse_public_point_args(lon_value, lat_value, &lon, &lat);
|
|
2720
|
+
marks = index_covering_marks(idx, lon, lat);
|
|
2721
|
+
result = build_ids_from_marks_protected(idx, marks);
|
|
2722
|
+
|
|
2723
|
+
RB_GC_GUARD(self);
|
|
2724
|
+
return result;
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
static VALUE rb_tg_geometry_index_intersecting_rect(VALUE self, VALUE min_x_value,
|
|
2728
|
+
VALUE min_y_value, VALUE max_x_value,
|
|
2729
|
+
VALUE max_y_value) {
|
|
2730
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2731
|
+
double min_x = NUM2DBL(min_x_value);
|
|
2732
|
+
double min_y = NUM2DBL(min_y_value);
|
|
2733
|
+
double max_x = NUM2DBL(max_x_value);
|
|
2734
|
+
double max_y = NUM2DBL(max_y_value);
|
|
2735
|
+
struct tg_rect query_rect;
|
|
2736
|
+
unsigned char *marks;
|
|
2737
|
+
VALUE result;
|
|
2738
|
+
|
|
2739
|
+
validate_rect_coordinates(min_x, min_y, max_x, max_y);
|
|
2740
|
+
query_rect = tg_rect_from_xyxy(min_x, min_y, max_x, max_y);
|
|
2741
|
+
|
|
2742
|
+
marks = index_intersecting_rect_marks(idx, query_rect);
|
|
2743
|
+
result = build_ids_from_marks_protected(idx, marks);
|
|
2744
|
+
|
|
2745
|
+
RB_GC_GUARD(self);
|
|
2746
|
+
return result;
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
static VALUE rb_tg_geometry_index_covering_ids_batch_packed(VALUE self, VALUE input) {
|
|
2750
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2751
|
+
const char *data;
|
|
2752
|
+
long byte_len;
|
|
2753
|
+
long count;
|
|
2754
|
+
VALUE result;
|
|
2755
|
+
|
|
2756
|
+
if (!RB_TYPE_P(input, T_STRING)) {
|
|
2757
|
+
rb_raise(rb_eTypeError, "packed input must be String");
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
byte_len = RSTRING_LEN(input);
|
|
2761
|
+
if (byte_len % (long)(2 * sizeof(double)) != 0) {
|
|
2762
|
+
rb_raise(eTGGeometryArgumentError, "packed input length must be multiple of 16 bytes");
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
count = byte_len / (long)(2 * sizeof(double));
|
|
2766
|
+
result = rb_ary_new_capa(count);
|
|
2767
|
+
data = RSTRING_PTR(input);
|
|
2768
|
+
|
|
2769
|
+
for (long i = 0; i < count; i++) {
|
|
2770
|
+
double lon;
|
|
2771
|
+
double lat;
|
|
2772
|
+
VALUE id;
|
|
2773
|
+
|
|
2774
|
+
memcpy(&lon, data + (i * (long)(2 * sizeof(double))), sizeof(double));
|
|
2775
|
+
memcpy(&lat, data + (i * (long)(2 * sizeof(double))) + (long)sizeof(double),
|
|
2776
|
+
sizeof(double));
|
|
2777
|
+
|
|
2778
|
+
check_finite_double(lon, "lon");
|
|
2779
|
+
check_finite_double(lat, "lat");
|
|
2780
|
+
|
|
2781
|
+
id = index_find_covering_value(idx, lon, lat);
|
|
2782
|
+
rb_ary_push(result, id);
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
RB_GC_GUARD(input);
|
|
2786
|
+
RB_GC_GUARD(self);
|
|
2787
|
+
RB_GC_GUARD(result);
|
|
2788
|
+
return result;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
#ifdef TG_DEBUG_TEST
|
|
2792
|
+
static VALUE rb_tg_geometry_debug_reset_test_hooks(VALUE self) {
|
|
2793
|
+
(void)self;
|
|
2794
|
+
tg_debug_fail_next_entries_alloc = false;
|
|
2795
|
+
tg_debug_fail_rtree_alloc_countdown = -1;
|
|
2796
|
+
tg_debug_fail_next_match_buffer_alloc = false;
|
|
2797
|
+
return Qnil;
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
static VALUE rb_tg_geometry_debug_fail_next_entries_alloc(VALUE self) {
|
|
2801
|
+
(void)self;
|
|
2802
|
+
tg_debug_fail_next_entries_alloc = true;
|
|
2803
|
+
return Qnil;
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
static VALUE rb_tg_geometry_debug_fail_next_rtree_alloc(VALUE self) {
|
|
2807
|
+
(void)self;
|
|
2808
|
+
tg_debug_fail_rtree_alloc_countdown = 0;
|
|
2809
|
+
return Qnil;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
static VALUE rb_tg_geometry_debug_fail_rtree_alloc_after(VALUE self, VALUE count_value) {
|
|
2813
|
+
long count;
|
|
2814
|
+
|
|
2815
|
+
(void)self;
|
|
2816
|
+
count = NUM2LONG(count_value);
|
|
2817
|
+
if (count < 0) {
|
|
2818
|
+
rb_raise(eTGGeometryArgumentError, "count must be >= 0");
|
|
2819
|
+
}
|
|
2820
|
+
tg_debug_fail_rtree_alloc_countdown = count;
|
|
2821
|
+
return Qnil;
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
static VALUE rb_tg_geometry_debug_fail_next_match_buffer_alloc(VALUE self) {
|
|
2825
|
+
(void)self;
|
|
2826
|
+
tg_debug_fail_next_match_buffer_alloc = true;
|
|
2827
|
+
return Qnil;
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
static VALUE rb_tg_geometry_index_rtree_bytes_for_test(VALUE self) {
|
|
2831
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2832
|
+
return ULL2NUM((unsigned long long)idx->rtree_bytes);
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
static VALUE rb_tg_geometry_index_entries_bytes_for_test(VALUE self) {
|
|
2836
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2837
|
+
return ULL2NUM((unsigned long long)idx->entries_bytes);
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
static VALUE rb_tg_geometry_index_owned_geom_bytes_for_test(VALUE self) {
|
|
2841
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2842
|
+
return ULL2NUM((unsigned long long)idx->owned_geom_bytes_total);
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
static VALUE rb_tg_geometry_index_initialized_entries_for_test(VALUE self) {
|
|
2846
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2847
|
+
return LONG2NUM(idx->initialized);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
static VALUE rb_tg_geometry_index_force_dispose_for_test(VALUE self) {
|
|
2851
|
+
tg_index_t *idx = get_index_wrapper(self);
|
|
2852
|
+
index_dispose(idx);
|
|
2853
|
+
return Qnil;
|
|
2854
|
+
}
|
|
2855
|
+
#endif
|
|
2856
|
+
|
|
2857
|
+
RUBY_FUNC_EXPORTED void Init_tg_geometry_ext_geometry_ext(void) {
|
|
2858
|
+
tg_geometry_vendor_header_sanity();
|
|
2859
|
+
|
|
2860
|
+
id_format = rb_intern("format");
|
|
2861
|
+
id_index = rb_intern("index");
|
|
2862
|
+
id_auto = rb_intern("auto");
|
|
2863
|
+
id_geojson = rb_intern("geojson");
|
|
2864
|
+
id_wkt = rb_intern("wkt");
|
|
2865
|
+
id_wkb = rb_intern("wkb");
|
|
2866
|
+
id_hex = rb_intern("hex");
|
|
2867
|
+
id_geobin = rb_intern("geobin");
|
|
2868
|
+
id_default = rb_intern("default");
|
|
2869
|
+
id_none = rb_intern("none");
|
|
2870
|
+
id_natural = rb_intern("natural");
|
|
2871
|
+
id_ystripes = rb_intern("ystripes");
|
|
2872
|
+
id_via = rb_intern("via");
|
|
2873
|
+
id_strategy = rb_intern("strategy");
|
|
2874
|
+
id_predicate = rb_intern("predicate");
|
|
2875
|
+
id_geometry_index = rb_intern("geometry_index");
|
|
2876
|
+
id_geom = rb_intern("geom");
|
|
2877
|
+
id_flat = rb_intern("flat");
|
|
2878
|
+
id_rtree = rb_intern("rtree");
|
|
2879
|
+
id_covers = rb_intern("covers");
|
|
2880
|
+
id_contains = rb_intern("contains");
|
|
2881
|
+
|
|
2882
|
+
mTG = rb_define_module("TG");
|
|
2883
|
+
mTGGeometry = rb_define_module_under(mTG, "Geometry");
|
|
2884
|
+
|
|
2885
|
+
eTGGeometryError = rb_define_class_under(mTGGeometry, "Error", rb_eStandardError);
|
|
2886
|
+
eTGGeometryParseError = rb_define_class_under(mTGGeometry, "ParseError", eTGGeometryError);
|
|
2887
|
+
eTGGeometryArgumentError = rb_define_class_under(mTGGeometry, "ArgumentError", rb_eArgError);
|
|
2888
|
+
eTGGeometryFrozenIndexError =
|
|
2889
|
+
rb_define_class_under(mTGGeometry, "FrozenIndexError", eTGGeometryError);
|
|
2890
|
+
|
|
2891
|
+
rb_define_singleton_method(mTGGeometry, "parse", rb_tg_geometry_parse, -1);
|
|
2892
|
+
rb_define_singleton_method(mTGGeometry, "parse_geojson", rb_tg_geometry_parse_geojson, -1);
|
|
2893
|
+
rb_define_singleton_method(mTGGeometry, "parse_wkt", rb_tg_geometry_parse_wkt, -1);
|
|
2894
|
+
rb_define_singleton_method(mTGGeometry, "parse_wkb", rb_tg_geometry_parse_wkb, -1);
|
|
2895
|
+
rb_define_singleton_method(mTGGeometry, "parse_hex", rb_tg_geometry_parse_hex, -1);
|
|
2896
|
+
rb_define_singleton_method(mTGGeometry, "parse_geobin", rb_tg_geometry_parse_geobin, -1);
|
|
2897
|
+
rb_define_singleton_method(mTGGeometry, "point", rb_tg_geometry_point, 2);
|
|
2898
|
+
rb_define_singleton_method(mTGGeometry, "point_z", rb_tg_geometry_point_z, 3);
|
|
2899
|
+
rb_define_singleton_method(mTGGeometry, "point_m", rb_tg_geometry_point_m, 3);
|
|
2900
|
+
rb_define_singleton_method(mTGGeometry, "point_zm", rb_tg_geometry_point_zm, 4);
|
|
2901
|
+
rb_define_singleton_method(mTGGeometry, "empty_point", rb_tg_geometry_empty_point, 0);
|
|
2902
|
+
rb_define_singleton_method(mTGGeometry, "empty_linestring", rb_tg_geometry_empty_linestring, 0);
|
|
2903
|
+
rb_define_singleton_method(mTGGeometry, "empty_polygon", rb_tg_geometry_empty_polygon, 0);
|
|
2904
|
+
rb_define_singleton_method(mTGGeometry, "empty_multipoint", rb_tg_geometry_empty_multipoint, 0);
|
|
2905
|
+
rb_define_singleton_method(mTGGeometry, "empty_multilinestring",
|
|
2906
|
+
rb_tg_geometry_empty_multilinestring, 0);
|
|
2907
|
+
rb_define_singleton_method(mTGGeometry, "empty_multipolygon", rb_tg_geometry_empty_multipolygon,
|
|
2908
|
+
0);
|
|
2909
|
+
rb_define_singleton_method(mTGGeometry, "empty_geometrycollection",
|
|
2910
|
+
rb_tg_geometry_empty_geometrycollection, 0);
|
|
2911
|
+
|
|
2912
|
+
cTGGeometryGeom = rb_define_class_under(mTGGeometry, "Geom", rb_cObject);
|
|
2913
|
+
rb_undef_alloc_func(cTGGeometryGeom);
|
|
2914
|
+
rb_define_method(cTGGeometryGeom, "type", rb_tg_geometry_geom_type, 0);
|
|
2915
|
+
rb_define_method(cTGGeometryGeom, "bbox", rb_tg_geometry_geom_bbox, 0);
|
|
2916
|
+
rb_define_method(cTGGeometryGeom, "covers_xy?", rb_tg_geometry_geom_covers_xy_p, 2);
|
|
2917
|
+
rb_define_method(cTGGeometryGeom, "intersects_xy?", rb_tg_geometry_geom_intersects_xy_p, 2);
|
|
2918
|
+
rb_define_method(cTGGeometryGeom, "equals?", rb_tg_geometry_geom_equals_p, 1);
|
|
2919
|
+
rb_define_method(cTGGeometryGeom, "contains?", rb_tg_geometry_geom_contains_p, 1);
|
|
2920
|
+
rb_define_method(cTGGeometryGeom, "intersects?", rb_tg_geometry_geom_intersects_p, 1);
|
|
2921
|
+
rb_define_method(cTGGeometryGeom, "disjoint?", rb_tg_geometry_geom_disjoint_p, 1);
|
|
2922
|
+
rb_define_method(cTGGeometryGeom, "within?", rb_tg_geometry_geom_within_p, 1);
|
|
2923
|
+
rb_define_method(cTGGeometryGeom, "covers?", rb_tg_geometry_geom_covers_p, 1);
|
|
2924
|
+
rb_define_method(cTGGeometryGeom, "covered_by?", rb_tg_geometry_geom_covered_by_p, 1);
|
|
2925
|
+
rb_define_method(cTGGeometryGeom, "touches?", rb_tg_geometry_geom_touches_p, 1);
|
|
2926
|
+
rb_define_method(cTGGeometryGeom, "intersects_rect?", rb_tg_geometry_geom_intersects_rect_p,
|
|
2927
|
+
-1);
|
|
2928
|
+
rb_define_method(cTGGeometryGeom, "to_geojson", rb_tg_geometry_geom_to_geojson, 0);
|
|
2929
|
+
rb_define_method(cTGGeometryGeom, "to_wkt", rb_tg_geometry_geom_to_wkt, 0);
|
|
2930
|
+
rb_define_method(cTGGeometryGeom, "to_wkb", rb_tg_geometry_geom_to_wkb, 0);
|
|
2931
|
+
rb_define_method(cTGGeometryGeom, "to_hex", rb_tg_geometry_geom_to_hex, 0);
|
|
2932
|
+
rb_define_method(cTGGeometryGeom, "to_geobin", rb_tg_geometry_geom_to_geobin, 0);
|
|
2933
|
+
rb_define_method(cTGGeometryGeom, "extra_json", rb_tg_geometry_geom_extra_json, 0);
|
|
2934
|
+
rb_define_method(cTGGeometryGeom, "feature?", rb_tg_geometry_geom_feature_p, 0);
|
|
2935
|
+
rb_define_method(cTGGeometryGeom, "feature_collection?",
|
|
2936
|
+
rb_tg_geometry_geom_feature_collection_p, 0);
|
|
2937
|
+
rb_define_method(cTGGeometryGeom, "empty?", rb_tg_geometry_geom_empty_p, 0);
|
|
2938
|
+
rb_define_method(cTGGeometryGeom, "dims", rb_tg_geometry_geom_dims, 0);
|
|
2939
|
+
rb_define_method(cTGGeometryGeom, "has_z?", rb_tg_geometry_geom_has_z_p, 0);
|
|
2940
|
+
rb_define_method(cTGGeometryGeom, "has_m?", rb_tg_geometry_geom_has_m_p, 0);
|
|
2941
|
+
rb_define_method(cTGGeometryGeom, "z", rb_tg_geometry_geom_z, 0);
|
|
2942
|
+
rb_define_method(cTGGeometryGeom, "m", rb_tg_geometry_geom_m, 0);
|
|
2943
|
+
rb_define_method(cTGGeometryGeom, "extra_coords", rb_tg_geometry_geom_extra_coords, 0);
|
|
2944
|
+
rb_define_method(cTGGeometryGeom, "point", rb_tg_geometry_geom_point, 0);
|
|
2945
|
+
rb_define_method(cTGGeometryGeom, "num_points", rb_tg_geometry_geom_num_points, 0);
|
|
2946
|
+
rb_define_method(cTGGeometryGeom, "point_at", rb_tg_geometry_geom_point_at, 1);
|
|
2947
|
+
rb_define_method(cTGGeometryGeom, "points", rb_tg_geometry_geom_points, 0);
|
|
2948
|
+
rb_define_method(cTGGeometryGeom, "line", rb_tg_geometry_geom_line, 0);
|
|
2949
|
+
rb_define_method(cTGGeometryGeom, "num_lines", rb_tg_geometry_geom_num_lines, 0);
|
|
2950
|
+
rb_define_method(cTGGeometryGeom, "line_at", rb_tg_geometry_geom_line_at, 1);
|
|
2951
|
+
rb_define_method(cTGGeometryGeom, "lines", rb_tg_geometry_geom_lines, 0);
|
|
2952
|
+
rb_define_method(cTGGeometryGeom, "polygon", rb_tg_geometry_geom_polygon, 0);
|
|
2953
|
+
rb_define_method(cTGGeometryGeom, "num_polygons", rb_tg_geometry_geom_num_polygons, 0);
|
|
2954
|
+
rb_define_method(cTGGeometryGeom, "polygon_at", rb_tg_geometry_geom_polygon_at, 1);
|
|
2955
|
+
rb_define_method(cTGGeometryGeom, "polygons", rb_tg_geometry_geom_polygons, 0);
|
|
2956
|
+
rb_define_method(cTGGeometryGeom, "num_geometries", rb_tg_geometry_geom_num_geometries, 0);
|
|
2957
|
+
rb_define_method(cTGGeometryGeom, "geometry_at", rb_tg_geometry_geom_geometry_at, 1);
|
|
2958
|
+
rb_define_method(cTGGeometryGeom, "geometries", rb_tg_geometry_geom_geometries, 0);
|
|
2959
|
+
|
|
2960
|
+
cTGGeometryLine = rb_define_class_under(mTGGeometry, "Line", rb_cObject);
|
|
2961
|
+
rb_undef_alloc_func(cTGGeometryLine);
|
|
2962
|
+
rb_define_method(cTGGeometryLine, "bbox", rb_tg_geometry_line_bbox, 0);
|
|
2963
|
+
rb_define_method(cTGGeometryLine, "num_points", rb_tg_geometry_line_num_points, 0);
|
|
2964
|
+
rb_define_method(cTGGeometryLine, "point_at", rb_tg_geometry_line_point_at, 1);
|
|
2965
|
+
rb_define_method(cTGGeometryLine, "points", rb_tg_geometry_line_points, 0);
|
|
2966
|
+
rb_define_method(cTGGeometryLine, "num_segments", rb_tg_geometry_line_num_segments, 0);
|
|
2967
|
+
rb_define_method(cTGGeometryLine, "segment_at", rb_tg_geometry_line_segment_at, 1);
|
|
2968
|
+
rb_define_method(cTGGeometryLine, "segments", rb_tg_geometry_line_segments, 0);
|
|
2969
|
+
rb_define_method(cTGGeometryLine, "length", rb_tg_geometry_line_length, 0);
|
|
2970
|
+
rb_define_method(cTGGeometryLine, "clockwise?", rb_tg_geometry_line_clockwise_p, 0);
|
|
2971
|
+
|
|
2972
|
+
cTGGeometryRing = rb_define_class_under(mTGGeometry, "Ring", rb_cObject);
|
|
2973
|
+
rb_undef_alloc_func(cTGGeometryRing);
|
|
2974
|
+
rb_define_method(cTGGeometryRing, "bbox", rb_tg_geometry_ring_bbox, 0);
|
|
2975
|
+
rb_define_method(cTGGeometryRing, "num_points", rb_tg_geometry_ring_num_points, 0);
|
|
2976
|
+
rb_define_method(cTGGeometryRing, "point_at", rb_tg_geometry_ring_point_at, 1);
|
|
2977
|
+
rb_define_method(cTGGeometryRing, "points", rb_tg_geometry_ring_points, 0);
|
|
2978
|
+
rb_define_method(cTGGeometryRing, "num_segments", rb_tg_geometry_ring_num_segments, 0);
|
|
2979
|
+
rb_define_method(cTGGeometryRing, "segment_at", rb_tg_geometry_ring_segment_at, 1);
|
|
2980
|
+
rb_define_method(cTGGeometryRing, "segments", rb_tg_geometry_ring_segments, 0);
|
|
2981
|
+
rb_define_method(cTGGeometryRing, "area", rb_tg_geometry_ring_area, 0);
|
|
2982
|
+
rb_define_method(cTGGeometryRing, "perimeter", rb_tg_geometry_ring_perimeter, 0);
|
|
2983
|
+
rb_define_method(cTGGeometryRing, "clockwise?", rb_tg_geometry_ring_clockwise_p, 0);
|
|
2984
|
+
rb_define_method(cTGGeometryRing, "convex?", rb_tg_geometry_ring_convex_p, 0);
|
|
2985
|
+
|
|
2986
|
+
cTGGeometryPolygon = rb_define_class_under(mTGGeometry, "Polygon", rb_cObject);
|
|
2987
|
+
rb_undef_alloc_func(cTGGeometryPolygon);
|
|
2988
|
+
rb_define_method(cTGGeometryPolygon, "bbox", rb_tg_geometry_polygon_bbox, 0);
|
|
2989
|
+
rb_define_method(cTGGeometryPolygon, "exterior_ring", rb_tg_geometry_polygon_exterior_ring, 0);
|
|
2990
|
+
rb_define_method(cTGGeometryPolygon, "num_holes", rb_tg_geometry_polygon_num_holes, 0);
|
|
2991
|
+
rb_define_method(cTGGeometryPolygon, "hole_at", rb_tg_geometry_polygon_hole_at, 1);
|
|
2992
|
+
rb_define_method(cTGGeometryPolygon, "holes", rb_tg_geometry_polygon_holes, 0);
|
|
2993
|
+
rb_define_method(cTGGeometryPolygon, "clockwise?", rb_tg_geometry_polygon_clockwise_p, 0);
|
|
2994
|
+
|
|
2995
|
+
cTGGeometrySegment = rb_define_class_under(mTGGeometry, "Segment", rb_cObject);
|
|
2996
|
+
rb_undef_alloc_func(cTGGeometrySegment);
|
|
2997
|
+
rb_define_method(cTGGeometrySegment, "a", rb_tg_geometry_segment_a, 0);
|
|
2998
|
+
rb_define_method(cTGGeometrySegment, "b", rb_tg_geometry_segment_b, 0);
|
|
2999
|
+
rb_define_method(cTGGeometrySegment, "points", rb_tg_geometry_segment_points, 0);
|
|
3000
|
+
rb_define_method(cTGGeometrySegment, "bbox", rb_tg_geometry_segment_bbox, 0);
|
|
3001
|
+
rb_define_method(cTGGeometrySegment, "intersects?", rb_tg_geometry_segment_intersects_p, 1);
|
|
3002
|
+
|
|
3003
|
+
cTGGeometryRect = rb_define_class_under(mTGGeometry, "Rect", rb_cObject);
|
|
3004
|
+
rb_define_alloc_func(cTGGeometryRect, rb_tg_geometry_rect_alloc);
|
|
3005
|
+
rb_define_method(cTGGeometryRect, "initialize", rb_tg_geometry_rect_initialize, -1);
|
|
3006
|
+
rb_define_method(cTGGeometryRect, "min_x", rb_tg_geometry_rect_min_x, 0);
|
|
3007
|
+
rb_define_method(cTGGeometryRect, "min_y", rb_tg_geometry_rect_min_y, 0);
|
|
3008
|
+
rb_define_method(cTGGeometryRect, "max_x", rb_tg_geometry_rect_max_x, 0);
|
|
3009
|
+
rb_define_method(cTGGeometryRect, "max_y", rb_tg_geometry_rect_max_y, 0);
|
|
3010
|
+
rb_define_method(cTGGeometryRect, "center", rb_tg_geometry_rect_center, 0);
|
|
3011
|
+
rb_define_method(cTGGeometryRect, "intersects?", rb_tg_geometry_rect_intersects_p, 1);
|
|
3012
|
+
rb_define_method(cTGGeometryRect, "contains_point?", rb_tg_geometry_rect_contains_point_p, 2);
|
|
3013
|
+
rb_define_method(cTGGeometryRect, "expand_to_include", rb_tg_geometry_rect_expand_to_include,
|
|
3014
|
+
1);
|
|
3015
|
+
rb_define_method(cTGGeometryRect, "expand_to_include_point",
|
|
3016
|
+
rb_tg_geometry_rect_expand_to_include_point, 2);
|
|
3017
|
+
|
|
3018
|
+
cTGGeometryIndex = rb_define_class_under(mTGGeometry, "Index", rb_cObject);
|
|
3019
|
+
rb_undef_alloc_func(cTGGeometryIndex);
|
|
3020
|
+
rb_define_singleton_method(cTGGeometryIndex, "build", rb_tg_geometry_index_build, -1);
|
|
3021
|
+
rb_define_method(cTGGeometryIndex, "find_covering", rb_tg_geometry_index_find_covering, 2);
|
|
3022
|
+
rb_define_method(cTGGeometryIndex, "covering_ids", rb_tg_geometry_index_covering_ids, 2);
|
|
3023
|
+
rb_define_method(cTGGeometryIndex, "intersecting_rect", rb_tg_geometry_index_intersecting_rect,
|
|
3024
|
+
4);
|
|
3025
|
+
rb_define_method(cTGGeometryIndex, "covering_ids_batch_packed",
|
|
3026
|
+
rb_tg_geometry_index_covering_ids_batch_packed, 1);
|
|
3027
|
+
rb_define_method(cTGGeometryIndex, "size", rb_tg_geometry_index_size, 0);
|
|
3028
|
+
rb_define_method(cTGGeometryIndex, "strategy", rb_tg_geometry_index_strategy, 0);
|
|
3029
|
+
rb_define_method(cTGGeometryIndex, "predicate", rb_tg_geometry_index_predicate, 0);
|
|
3030
|
+
rb_define_method(cTGGeometryIndex, "bbox", rb_tg_geometry_index_bbox, 0);
|
|
3031
|
+
|
|
3032
|
+
#ifdef TG_DEBUG_TEST
|
|
3033
|
+
rb_define_singleton_method(mTGGeometry, "_debug_reset_test_hooks!",
|
|
3034
|
+
rb_tg_geometry_debug_reset_test_hooks, 0);
|
|
3035
|
+
rb_define_singleton_method(mTGGeometry, "_debug_fail_next_entries_alloc!",
|
|
3036
|
+
rb_tg_geometry_debug_fail_next_entries_alloc, 0);
|
|
3037
|
+
rb_define_singleton_method(mTGGeometry, "_debug_fail_next_rtree_alloc!",
|
|
3038
|
+
rb_tg_geometry_debug_fail_next_rtree_alloc, 0);
|
|
3039
|
+
rb_define_singleton_method(mTGGeometry, "_debug_fail_rtree_alloc_after!",
|
|
3040
|
+
rb_tg_geometry_debug_fail_rtree_alloc_after, 1);
|
|
3041
|
+
rb_define_singleton_method(mTGGeometry, "_debug_fail_next_match_buffer_alloc!",
|
|
3042
|
+
rb_tg_geometry_debug_fail_next_match_buffer_alloc, 0);
|
|
3043
|
+
rb_define_method(cTGGeometryIndex, "_rtree_bytes_for_test",
|
|
3044
|
+
rb_tg_geometry_index_rtree_bytes_for_test, 0);
|
|
3045
|
+
rb_define_method(cTGGeometryIndex, "_entries_bytes_for_test",
|
|
3046
|
+
rb_tg_geometry_index_entries_bytes_for_test, 0);
|
|
3047
|
+
rb_define_method(cTGGeometryIndex, "_owned_geom_bytes_for_test",
|
|
3048
|
+
rb_tg_geometry_index_owned_geom_bytes_for_test, 0);
|
|
3049
|
+
rb_define_method(cTGGeometryIndex, "_initialized_entries_for_test",
|
|
3050
|
+
rb_tg_geometry_index_initialized_entries_for_test, 0);
|
|
3051
|
+
rb_define_method(cTGGeometryIndex, "_force_dispose_for_test!",
|
|
3052
|
+
rb_tg_geometry_index_force_dispose_for_test, 0);
|
|
3053
|
+
#endif
|
|
3054
|
+
}
|