rgeo 2.3.0 → 3.0.0.pre.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -0
  3. data/README.md +1 -0
  4. data/ext/geos_c_impl/analysis.c +8 -6
  5. data/ext/geos_c_impl/analysis.h +1 -3
  6. data/ext/geos_c_impl/errors.c +10 -8
  7. data/ext/geos_c_impl/errors.h +7 -3
  8. data/ext/geos_c_impl/extconf.rb +3 -0
  9. data/ext/geos_c_impl/factory.c +251 -182
  10. data/ext/geos_c_impl/factory.h +43 -62
  11. data/ext/geos_c_impl/geometry.c +56 -24
  12. data/ext/geos_c_impl/geometry.h +8 -3
  13. data/ext/geos_c_impl/geometry_collection.c +41 -148
  14. data/ext/geos_c_impl/geometry_collection.h +1 -14
  15. data/ext/geos_c_impl/globals.c +91 -0
  16. data/ext/geos_c_impl/globals.h +45 -0
  17. data/ext/geos_c_impl/line_string.c +28 -29
  18. data/ext/geos_c_impl/line_string.h +1 -3
  19. data/ext/geos_c_impl/main.c +10 -9
  20. data/ext/geos_c_impl/point.c +9 -8
  21. data/ext/geos_c_impl/point.h +1 -3
  22. data/ext/geos_c_impl/polygon.c +15 -51
  23. data/ext/geos_c_impl/polygon.h +1 -3
  24. data/ext/geos_c_impl/preface.h +8 -0
  25. data/lib/rgeo/cartesian/analysis.rb +2 -2
  26. data/lib/rgeo/cartesian/calculations.rb +54 -17
  27. data/lib/rgeo/cartesian/factory.rb +0 -7
  28. data/lib/rgeo/cartesian/feature_classes.rb +66 -46
  29. data/lib/rgeo/cartesian/feature_methods.rb +56 -20
  30. data/lib/rgeo/cartesian/interface.rb +0 -6
  31. data/lib/rgeo/cartesian/planar_graph.rb +379 -0
  32. data/lib/rgeo/cartesian/sweepline_intersector.rb +149 -0
  33. data/lib/rgeo/cartesian/valid_op.rb +71 -0
  34. data/lib/rgeo/cartesian.rb +3 -0
  35. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +6 -6
  36. data/lib/rgeo/error.rb +15 -0
  37. data/lib/rgeo/feature/curve.rb +12 -2
  38. data/lib/rgeo/feature/geometry.rb +38 -28
  39. data/lib/rgeo/feature/geometry_collection.rb +13 -5
  40. data/lib/rgeo/feature/line_string.rb +3 -3
  41. data/lib/rgeo/feature/multi_curve.rb +6 -1
  42. data/lib/rgeo/feature/multi_surface.rb +3 -3
  43. data/lib/rgeo/feature/point.rb +4 -4
  44. data/lib/rgeo/feature/surface.rb +3 -3
  45. data/lib/rgeo/geographic/factory.rb +0 -7
  46. data/lib/rgeo/geographic/interface.rb +4 -18
  47. data/lib/rgeo/geographic/proj4_projector.rb +0 -2
  48. data/lib/rgeo/geographic/projected_feature_classes.rb +21 -9
  49. data/lib/rgeo/geographic/projected_feature_methods.rb +63 -30
  50. data/lib/rgeo/geographic/simple_mercator_projector.rb +0 -2
  51. data/lib/rgeo/geographic/spherical_feature_classes.rb +29 -9
  52. data/lib/rgeo/geographic/spherical_feature_methods.rb +68 -2
  53. data/lib/rgeo/geos/capi_factory.rb +21 -31
  54. data/lib/rgeo/geos/capi_feature_classes.rb +64 -11
  55. data/lib/rgeo/geos/ffi_factory.rb +0 -28
  56. data/lib/rgeo/geos/ffi_feature_classes.rb +34 -10
  57. data/lib/rgeo/geos/ffi_feature_methods.rb +53 -10
  58. data/lib/rgeo/geos/interface.rb +18 -10
  59. data/lib/rgeo/geos/zm_factory.rb +0 -12
  60. data/lib/rgeo/geos/zm_feature_methods.rb +30 -5
  61. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +18 -8
  62. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +1 -1
  63. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +37 -26
  64. data/lib/rgeo/impl_helper/basic_point_methods.rb +13 -3
  65. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +8 -3
  66. data/lib/rgeo/impl_helper/valid_op.rb +354 -0
  67. data/lib/rgeo/impl_helper/validity_check.rb +138 -0
  68. data/lib/rgeo/impl_helper.rb +1 -0
  69. data/lib/rgeo/version.rb +1 -1
  70. data/lib/rgeo/wkrep/wkb_generator.rb +1 -1
  71. data/lib/rgeo/wkrep/wkt_generator.rb +6 -6
  72. metadata +30 -7
@@ -9,6 +9,11 @@
9
9
 
10
10
  #include <ruby.h>
11
11
  #include <geos_c.h>
12
+ #include <ctype.h>
13
+ #include <stdarg.h>
14
+ #include <stdio.h>
15
+
16
+ #include "globals.h"
12
17
 
13
18
  #include "factory.h"
14
19
  #include "geometry.h"
@@ -24,94 +29,138 @@ RGEO_BEGIN_C
24
29
  /**** RUBY AND GEOS CALLBACKS ****/
25
30
 
26
31
 
27
- // NOP message handler. GEOS requires that a message handler be set
28
- // for every context handle.
29
-
30
- static void message_handler(const char* fmt, ...)
32
+ // The notice handler is very rarely used by GEOS, only in
33
+ // GEOSIsValid_r (check for NOTICE_MESSAGE in GEOS codebase).
34
+ // We still set it to make sure we do not miss any implementation
35
+ // change. Use `DEBUG=1 rake` to show notice.
36
+ #ifdef RGEO_GEOS_DEBUG
37
+ static void notice_handler(const char* fmt, ...)
31
38
  {
39
+ va_list args;
40
+ va_start(args, fmt);
41
+ fprintf(stderr, "GEOS Notice -- ");
42
+ vfprintf(stderr, fmt, args);
43
+ fprintf(stderr, "\n");
44
+ va_end(args);
32
45
  }
46
+ #endif
33
47
 
48
+ static void error_handler(const char* fmt, ...)
49
+ {
50
+ // See https://en.cppreference.com/w/c/io/vfprintf
51
+ va_list args1;
52
+ va_start(args1, fmt);
53
+ va_list args2;
54
+ va_copy(args2, args1);
55
+ int size = 1+vsnprintf(NULL, 0, fmt, args1);
56
+ va_end(args1);
57
+ char geos_full_error[size];
58
+ vsnprintf(geos_full_error, sizeof geos_full_error, fmt, args2);
59
+ va_end(args2);
60
+
61
+ // NOTE: strtok is destructive, geos_full_error is not to be used afterwards.
62
+ char *geos_error = strtok(geos_full_error, ":");
63
+ char *geos_message = strtok(NULL, ":");
64
+ while(isspace(*geos_message)) geos_message++;
65
+
66
+ if (streq(geos_error, "UnsupportedOperationException")) {
67
+ rb_raise(rb_eRGeoUnsupportedOperation, "%s", geos_message);
68
+ } else if (streq(geos_error, "IllegalArgumentException")) {
69
+ rb_raise(rb_eRGeoInvalidGeometry, "%s", geos_message);
70
+ } else if (geos_message) {
71
+ rb_raise(rb_eGeosError, "%s: %s", geos_error, geos_message);
72
+ } else {
73
+ rb_raise(rb_eGeosError, "%s", geos_error);
74
+ }
75
+ }
34
76
 
35
77
  // Destroy function for factory data. We destroy any serialization
36
78
  // objects that have been created for the factory, and then destroy
37
79
  // the GEOS context, before freeing the factory data itself.
38
80
 
39
- static void destroy_factory_func(RGeo_FactoryData* data)
81
+ static void destroy_factory_func(void* data)
40
82
  {
83
+ RGeo_FactoryData* factory_data;
41
84
  GEOSContextHandle_t context;
42
85
 
43
- context = data->geos_context;
44
- if (data->wkt_reader) {
45
- GEOSWKTReader_destroy_r(context, data->wkt_reader);
86
+ factory_data = (RGeo_FactoryData*)data;
87
+ context = factory_data->geos_context;
88
+ if (factory_data->wkt_reader) {
89
+ GEOSWKTReader_destroy_r(context, factory_data->wkt_reader);
46
90
  }
47
- if (data->wkb_reader) {
48
- GEOSWKBReader_destroy_r(context, data->wkb_reader);
91
+ if (factory_data->wkb_reader) {
92
+ GEOSWKBReader_destroy_r(context, factory_data->wkb_reader);
49
93
  }
50
- if (data->wkt_writer) {
51
- GEOSWKTWriter_destroy_r(context, data->wkt_writer);
94
+ if (factory_data->wkt_writer) {
95
+ GEOSWKTWriter_destroy_r(context, factory_data->wkt_writer);
52
96
  }
53
- if (data->wkb_writer) {
54
- GEOSWKBWriter_destroy_r(context, data->wkb_writer);
97
+ if (factory_data->wkb_writer) {
98
+ GEOSWKBWriter_destroy_r(context, factory_data->wkb_writer);
55
99
  }
56
- if (data->psych_wkt_reader) {
57
- GEOSWKTReader_destroy_r(context, data->psych_wkt_reader);
100
+ if (factory_data->psych_wkt_reader) {
101
+ GEOSWKTReader_destroy_r(context, factory_data->psych_wkt_reader);
58
102
  }
59
- if (data->marshal_wkb_reader) {
60
- GEOSWKBReader_destroy_r(context, data->marshal_wkb_reader);
103
+ if (factory_data->marshal_wkb_reader) {
104
+ GEOSWKBReader_destroy_r(context, factory_data->marshal_wkb_reader);
61
105
  }
62
- if (data->psych_wkt_writer) {
63
- GEOSWKTWriter_destroy_r(context, data->psych_wkt_writer);
106
+ if (factory_data->psych_wkt_writer) {
107
+ GEOSWKTWriter_destroy_r(context, factory_data->psych_wkt_writer);
64
108
  }
65
- if (data->marshal_wkb_writer) {
66
- GEOSWKBWriter_destroy_r(context, data->marshal_wkb_writer);
109
+ if (factory_data->marshal_wkb_writer) {
110
+ GEOSWKBWriter_destroy_r(context, factory_data->marshal_wkb_writer);
67
111
  }
68
112
  finishGEOS_r(context);
69
- free(data);
113
+ free(factory_data);
70
114
  }
71
115
 
72
116
 
73
117
  // Destroy function for geometry data. We destroy the internal
74
118
  // GEOS geometry (if present) before freeing the data itself.
75
119
 
76
- static void destroy_geometry_func(RGeo_GeometryData* data)
120
+ static void destroy_geometry_func(void* data)
77
121
  {
122
+ RGeo_GeometryData* geometry_data;
78
123
  const GEOSPreparedGeometry* prep;
79
124
 
80
- if (data->geom) {
81
- GEOSGeom_destroy_r(data->geos_context, data->geom);
125
+ geometry_data = (RGeo_GeometryData*)data;
126
+ if (geometry_data->geom) {
127
+ GEOSGeom_destroy_r(geometry_data->geos_context, geometry_data->geom);
82
128
  }
83
- prep = data->prep;
129
+ prep = geometry_data->prep;
84
130
  if (prep && prep != (const GEOSPreparedGeometry*)1 && prep != (const GEOSPreparedGeometry*)2 &&
85
131
  prep != (const GEOSPreparedGeometry*)3)
86
132
  {
87
- GEOSPreparedGeom_destroy_r(data->geos_context, prep);
133
+ GEOSPreparedGeom_destroy_r(geometry_data->geos_context, prep);
88
134
  }
89
- free(data);
135
+ free(geometry_data);
90
136
  }
91
137
 
92
138
 
93
139
  // Mark function for factory data. This marks the wkt and wkb generator
94
140
  // handles so they don't get collected.
95
141
 
96
- static void mark_factory_func(RGeo_FactoryData* data)
142
+ static void mark_factory_func(void* data)
97
143
  {
98
- if (!NIL_P(data->wkrep_wkt_generator)) {
99
- rb_gc_mark(data->wkrep_wkt_generator);
144
+ RGeo_FactoryData* factory_data;
145
+
146
+ factory_data = (RGeo_FactoryData*)data;
147
+ if (!NIL_P(factory_data->wkrep_wkt_generator)) {
148
+ mark(factory_data->wkrep_wkt_generator);
100
149
  }
101
- if (!NIL_P(data->wkrep_wkb_generator)) {
102
- rb_gc_mark(data->wkrep_wkb_generator);
150
+ if (!NIL_P(factory_data->wkrep_wkb_generator)) {
151
+ mark(factory_data->wkrep_wkb_generator);
103
152
  }
104
- if (!NIL_P(data->wkrep_wkt_parser)) {
105
- rb_gc_mark(data->wkrep_wkt_parser);
153
+ if (!NIL_P(factory_data->wkrep_wkt_parser)) {
154
+ mark(factory_data->wkrep_wkt_parser);
106
155
  }
107
- if (!NIL_P(data->wkrep_wkb_parser)) {
108
- rb_gc_mark(data->wkrep_wkb_parser);
156
+ if (!NIL_P(factory_data->wkrep_wkb_parser)) {
157
+ mark(factory_data->wkrep_wkb_parser);
109
158
  }
110
- if (!NIL_P(data->proj4_obj)) {
111
- rb_gc_mark(data->proj4_obj);
159
+ if (!NIL_P(factory_data->proj4_obj)) {
160
+ mark(factory_data->proj4_obj);
112
161
  }
113
- if (!NIL_P(data->coord_sys_obj)) {
114
- rb_gc_mark(data->coord_sys_obj);
162
+ if (!NIL_P(factory_data->coord_sys_obj)) {
163
+ mark(factory_data->coord_sys_obj);
115
164
  }
116
165
  }
117
166
 
@@ -119,32 +168,83 @@ static void mark_factory_func(RGeo_FactoryData* data)
119
168
  // Mark function for geometry data. This marks the factory and klasses
120
169
  // held by the geometry so those don't get collected.
121
170
 
122
- static void mark_geometry_func(RGeo_GeometryData* data)
171
+ static void mark_geometry_func(void* data)
123
172
  {
124
- if (!NIL_P(data->factory)) {
125
- rb_gc_mark(data->factory);
173
+ RGeo_GeometryData* geometry_data;
174
+
175
+ geometry_data = (RGeo_GeometryData*)data;
176
+ if (!NIL_P(geometry_data->factory)) {
177
+ mark(geometry_data->factory);
126
178
  }
127
- if (!NIL_P(data->klasses)) {
128
- rb_gc_mark(data->klasses);
179
+ if (!NIL_P(geometry_data->klasses)) {
180
+ mark(geometry_data->klasses);
129
181
  }
130
182
  }
131
183
 
132
184
 
133
- // Destroy function for globals data. We don't need to destroy any
134
- // auxiliary data for now...
135
-
136
- static void destroy_globals_func(RGeo_Globals* data)
185
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
186
+ static void compact_factory_func(void* data)
137
187
  {
138
- free(data);
139
- }
188
+ RGeo_FactoryData* factory_data;
140
189
 
190
+ factory_data = (RGeo_FactoryData*)data;
191
+ if (!NIL_P(factory_data->wkrep_wkt_generator)) {
192
+ factory_data->wkrep_wkt_generator = rb_gc_location(factory_data->wkrep_wkt_generator);
193
+ }
194
+ if (!NIL_P(factory_data->wkrep_wkb_generator)) {
195
+ factory_data->wkrep_wkb_generator = rb_gc_location(factory_data->wkrep_wkb_generator);
196
+ }
197
+ if (!NIL_P(factory_data->wkrep_wkt_parser)) {
198
+ factory_data->wkrep_wkt_parser = rb_gc_location(factory_data->wkrep_wkt_parser);
199
+ }
200
+ if (!NIL_P(factory_data->wkrep_wkb_parser)) {
201
+ factory_data->wkrep_wkb_parser = rb_gc_location(factory_data->wkrep_wkb_parser);
202
+ }
203
+ if (!NIL_P(factory_data->proj4_obj)) {
204
+ factory_data->proj4_obj = rb_gc_location(factory_data->proj4_obj);
205
+ }
206
+ if (!NIL_P(factory_data->coord_sys_obj)) {
207
+ factory_data->coord_sys_obj = rb_gc_location(factory_data->coord_sys_obj);
208
+ }
209
+ }
141
210
 
142
- // Mark function for globals data. This should mark any globals that
143
- // need to be held through garbage collection (none at the moment.)
144
211
 
145
- static void mark_globals_func(RGeo_Globals* data)
212
+ static void compact_geometry_func(void* data)
146
213
  {
214
+ RGeo_GeometryData* geometry_data;
215
+
216
+ geometry_data = (RGeo_GeometryData*)data;
217
+ if (!NIL_P(geometry_data->factory)) {
218
+ geometry_data->factory = rb_gc_location(geometry_data->factory);
219
+ }
220
+ if (!NIL_P(geometry_data->klasses)) {
221
+ geometry_data->klasses = rb_gc_location(geometry_data->klasses);
222
+ }
147
223
  }
224
+ #endif
225
+
226
+
227
+ const rb_data_type_t rgeo_factory_type = {
228
+ .wrap_struct_name = "RGeo/Factory",
229
+ .function = {
230
+ .dmark = mark_factory_func,
231
+ .dfree = destroy_factory_func,
232
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
233
+ .dcompact = compact_factory_func,
234
+ #endif
235
+ }
236
+ };
237
+
238
+ const rb_data_type_t rgeo_geometry_type = {
239
+ .wrap_struct_name = "RGeo/Geometry",
240
+ .function = {
241
+ .dmark = mark_geometry_func,
242
+ .dfree = destroy_geometry_func,
243
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
244
+ .dcompact = compact_geometry_func,
245
+ #endif
246
+ }
247
+ };
148
248
 
149
249
 
150
250
  /**** RUBY METHOD DEFINITIONS ****/
@@ -167,6 +267,25 @@ static VALUE method_factory_flags(VALUE self)
167
267
  return INT2NUM(RGEO_FACTORY_DATA_PTR(self)->flags);
168
268
  }
169
269
 
270
+ VALUE method_factory_supports_z_p(VALUE self)
271
+ {
272
+ return RGEO_FACTORY_DATA_PTR(self)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z ? Qtrue : Qfalse;
273
+ }
274
+
275
+ VALUE method_factory_supports_m_p(VALUE self)
276
+ {
277
+ return RGEO_FACTORY_DATA_PTR(self)->flags & RGEO_FACTORYFLAGS_SUPPORTS_M ? Qtrue : Qfalse;
278
+ }
279
+
280
+ VALUE method_factory_supports_z_or_m_p(VALUE self)
281
+ {
282
+ return RGEO_FACTORY_DATA_PTR(self)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M ? Qtrue : Qfalse;
283
+ }
284
+
285
+ VALUE method_factory_prepare_heuristic_p(VALUE self)
286
+ {
287
+ return RGEO_FACTORY_DATA_PTR(self)->flags & RGEO_FACTORYFLAGS_PREPARE_HEURISTIC ? Qtrue : Qfalse;
288
+ }
170
289
 
171
290
  static VALUE method_factory_parse_wkt(VALUE self, VALUE str)
172
291
  {
@@ -248,7 +367,6 @@ static VALUE method_factory_read_for_marshal(VALUE self, VALUE str)
248
367
  return result;
249
368
  }
250
369
 
251
-
252
370
  static VALUE method_factory_read_for_psych(VALUE self, VALUE str)
253
371
  {
254
372
  RGeo_FactoryData* self_data;
@@ -275,6 +393,9 @@ static VALUE method_factory_read_for_psych(VALUE self, VALUE str)
275
393
  return result;
276
394
  }
277
395
 
396
+ #ifndef RGEO_GEOS_SUPPORTS_SETOUTPUTDIMENSION
397
+ static VALUE marshal_wkb_generator;
398
+ #endif
278
399
 
279
400
  static VALUE method_factory_write_for_marshal(VALUE self, VALUE obj)
280
401
  {
@@ -286,25 +407,18 @@ static VALUE method_factory_write_for_marshal(VALUE self, VALUE obj)
286
407
  char* str;
287
408
  size_t size;
288
409
  char has_3d;
289
- #ifndef RGEO_GEOS_SUPPORTS_SETOUTPUTDIMENSION
290
- RGeo_Globals* globals;
291
- VALUE wkb_generator;
292
- #endif
293
410
 
294
411
  self_data = RGEO_FACTORY_DATA_PTR(self);
295
412
  self_context = self_data->geos_context;
296
413
  has_3d = self_data->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M;
297
414
  #ifndef RGEO_GEOS_SUPPORTS_SETOUTPUTDIMENSION
298
415
  if (has_3d) {
299
- globals = self_data->globals;
300
- wkb_generator = globals->marshal_wkb_generator;
301
- if (NIL_P(wkb_generator)) {
302
- wkb_generator = rb_funcall(
303
- rb_const_get_at(globals->geos_module, rb_intern("Utils")),
416
+ if (NIL_P(marshal_wkb_generator)) {
417
+ marshal_wkb_generator = rb_funcall(
418
+ rb_const_get_at(rgeo_geos_module, rb_intern("Utils")),
304
419
  rb_intern("marshal_wkb_generator"), 0);
305
- globals->marshal_wkb_generator = wkb_generator;
306
420
  }
307
- return rb_funcall(wkb_generator, globals->id_generate, 1, obj);
421
+ return rb_funcall(marshal_wkb_generator, rb_intern("generate"), 1, obj);
308
422
  }
309
423
  #endif
310
424
  wkb_writer = self_data->marshal_wkb_writer;
@@ -329,6 +443,9 @@ static VALUE method_factory_write_for_marshal(VALUE self, VALUE obj)
329
443
  return result;
330
444
  }
331
445
 
446
+ #ifndef RGEO_GEOS_SUPPORTS_SETOUTPUTDIMENSION
447
+ static VALUE psych_wkt_generator;
448
+ #endif
332
449
 
333
450
  static VALUE method_factory_write_for_psych(VALUE self, VALUE obj)
334
451
  {
@@ -339,25 +456,18 @@ static VALUE method_factory_write_for_psych(VALUE self, VALUE obj)
339
456
  VALUE result;
340
457
  char* str;
341
458
  char has_3d;
342
- #ifndef RGEO_GEOS_SUPPORTS_SETOUTPUTDIMENSION
343
- RGeo_Globals* globals;
344
- VALUE wkt_generator;
345
- #endif
346
459
 
347
460
  self_data = RGEO_FACTORY_DATA_PTR(self);
348
461
  self_context = self_data->geos_context;
349
462
  has_3d = self_data->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M;
350
463
  #ifndef RGEO_GEOS_SUPPORTS_SETOUTPUTDIMENSION
351
464
  if (has_3d) {
352
- globals = self_data->globals;
353
- wkt_generator = globals->psych_wkt_generator;
354
- if (NIL_P(wkt_generator)) {
355
- wkt_generator = rb_funcall(
356
- rb_const_get_at(globals->geos_module, rb_intern("Utils")),
465
+ if (NIL_P(psych_wkt_generator)) {
466
+ psych_wkt_generator = rb_funcall(
467
+ rb_const_get_at(rgeo_geos_module, rb_intern("Utils")),
357
468
  rb_intern("psych_wkt_generator"), 0);
358
- globals->psych_wkt_generator = wkt_generator;
359
469
  }
360
- return rb_funcall(wkt_generator, globals->id_generate, 1, obj);
470
+ return rb_funcall(psych_wkt_generator, rb_intern("generate"), 1, obj);
361
471
  }
362
472
  #endif
363
473
  wkt_writer = self_data->psych_wkt_writer;
@@ -404,15 +514,17 @@ static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE
404
514
  VALUE result;
405
515
  RGeo_FactoryData* data;
406
516
  GEOSContextHandle_t context;
407
- VALUE wrapped_globals;
408
517
 
409
518
  result = Qnil;
410
519
  data = ALLOC(RGeo_FactoryData);
411
520
  if (data) {
412
- context = initGEOS_r(message_handler, message_handler);
521
+ context = GEOS_init_r();
522
+ #ifdef RGEO_GEOS_DEBUG
523
+ GEOSContext_setNoticeHandler_r(context, notice_handler);
524
+ #endif
525
+ GEOSContext_setErrorHandler_r(context, error_handler);
526
+
413
527
  if (context) {
414
- wrapped_globals = rb_const_get_at(klass, rb_intern("INTERNAL_CGLOBALS"));
415
- data->globals = (RGeo_Globals*)DATA_PTR(wrapped_globals);
416
528
  data->geos_context = context;
417
529
  data->flags = NUM2INT(flags);
418
530
  data->srid = NUM2INT(srid);
@@ -431,7 +543,7 @@ static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE
431
543
  data->wkrep_wkb_parser = Qnil;
432
544
  data->proj4_obj = proj4_obj;
433
545
  data->coord_sys_obj = coord_sys_obj;
434
- result = Data_Wrap_Struct(klass, mark_factory_func, destroy_factory_func, data);
546
+ result = TypedData_Wrap_Struct(klass, &rgeo_factory_type, data);
435
547
  }
436
548
  else {
437
549
  free(data);
@@ -496,7 +608,7 @@ static VALUE method_factory_initialize_copy(VALUE self, VALUE orig)
496
608
  self_data->coord_sys_obj = Qnil;
497
609
 
498
610
  // Copy new data from original object
499
- if (TYPE(orig) == T_DATA && RDATA(orig)->dfree == (RUBY_DATA_FUNC)destroy_factory_func) {
611
+ if (RGEO_FACTORY_TYPEDDATA_P(orig)) {
500
612
  orig_data = RGEO_FACTORY_DATA_PTR(orig);
501
613
  self_data->flags = orig_data->flags;
502
614
  self_data->srid = orig_data->srid;
@@ -569,55 +681,36 @@ static VALUE alloc_geometry(VALUE klass)
569
681
  /**** INITIALIZATION FUNCTION ****/
570
682
 
571
683
 
572
- RGeo_Globals* rgeo_init_geos_factory()
684
+ void rgeo_init_geos_factory()
573
685
  {
574
- RGeo_Globals* globals;
575
- VALUE rgeo_module;
576
686
  VALUE geos_factory_class;
577
- VALUE wrapped_globals;
578
- VALUE feature_module;
579
-
580
- rgeo_module = rb_define_module("RGeo");
581
-
582
- globals = ALLOC(RGeo_Globals);
583
-
584
- // Cache some modules so we don't have to look them up by name every time
585
- feature_module = rb_define_module_under(rgeo_module, "Feature");
586
- globals->feature_module = feature_module;
587
- globals->geos_module = rb_define_module_under(rgeo_module, "Geos");
588
- globals->feature_geometry = rb_const_get_at(feature_module, rb_intern("Geometry"));
589
- globals->feature_point = rb_const_get_at(feature_module, rb_intern("Point"));
590
- globals->feature_line_string = rb_const_get_at(feature_module, rb_intern("LineString"));
591
- globals->feature_linear_ring = rb_const_get_at(feature_module, rb_intern("LinearRing"));
592
- globals->feature_line = rb_const_get_at(feature_module, rb_intern("Line"));
593
- globals->feature_polygon = rb_const_get_at(feature_module, rb_intern("Polygon"));
594
- globals->feature_geometry_collection = rb_const_get_at(feature_module, rb_intern("GeometryCollection"));
595
- globals->feature_multi_point = rb_const_get_at(feature_module, rb_intern("MultiPoint"));
596
- globals->feature_multi_line_string = rb_const_get_at(feature_module, rb_intern("MultiLineString"));
597
- globals->feature_multi_polygon = rb_const_get_at(feature_module, rb_intern("MultiPolygon"));
598
-
599
- // Cache some commonly used names
600
- globals->id_cast = rb_intern("cast");
601
- globals->id_eql = rb_intern("eql?");
602
- globals->id_generate = rb_intern("generate");
603
- globals->id_enum_for = rb_intern("enum_for");
604
- globals->id_hash = rb_intern("hash");
605
- globals->sym_force_new = ID2SYM(rb_intern("force_new"));
606
- globals->sym_keep_subtype = ID2SYM(rb_intern("keep_subtype"));
687
+
607
688
  #ifndef RGEO_GEOS_SUPPORTS_SETOUTPUTDIMENSION
608
- globals->psych_wkt_generator = Qnil;
609
- globals->marshal_wkb_generator = Qnil;
689
+ /* We favor rb_gc_register_address over rb_gc_register_mark_object because the value changes at runtime */
690
+ psych_wkt_generator = Qnil;
691
+ rb_gc_register_address(&psych_wkt_generator);
692
+ marshal_wkb_generator = Qnil;
693
+ rb_gc_register_address(&marshal_wkb_generator);
610
694
  #endif
611
695
 
612
- // Add C methods to the factory.
613
- geos_factory_class = rb_define_class_under(globals->geos_module, "CAPIFactory", rb_cObject);
696
+ geos_factory_class = rb_define_class_under(rgeo_geos_module, "CAPIFactory", rb_cObject);
614
697
  rb_define_alloc_func(geos_factory_class, alloc_factory);
698
+ // Add C constants to the factory.
699
+ rb_define_const(geos_factory_class, "FLAG_SUPPORTS_Z", INT2FIX(RGEO_FACTORYFLAGS_SUPPORTS_Z));
700
+ rb_define_const(geos_factory_class, "FLAG_SUPPORTS_M", INT2FIX(RGEO_FACTORYFLAGS_SUPPORTS_M));
701
+ rb_define_const(geos_factory_class, "FLAG_SUPPORTS_Z_OR_M", INT2FIX(RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M));
702
+ rb_define_const(geos_factory_class, "FLAG_PREPARE_HEURISTIC", INT2FIX(RGEO_FACTORYFLAGS_PREPARE_HEURISTIC));
703
+ // Add C methods to the factory.
615
704
  rb_define_method(geos_factory_class, "initialize_copy", method_factory_initialize_copy, 1);
616
705
  rb_define_method(geos_factory_class, "_parse_wkt_impl", method_factory_parse_wkt, 1);
617
706
  rb_define_method(geos_factory_class, "_parse_wkb_impl", method_factory_parse_wkb, 1);
618
707
  rb_define_method(geos_factory_class, "_srid", method_factory_srid, 0);
619
708
  rb_define_method(geos_factory_class, "_buffer_resolution", method_factory_buffer_resolution, 0);
620
709
  rb_define_method(geos_factory_class, "_flags", method_factory_flags, 0);
710
+ rb_define_method(geos_factory_class, "supports_z?", method_factory_supports_z_p, 0);
711
+ rb_define_method(geos_factory_class, "supports_m?", method_factory_supports_m_p, 0);
712
+ rb_define_method(geos_factory_class, "supports_z_or_m?", method_factory_supports_z_or_m_p, 0);
713
+ rb_define_method(geos_factory_class, "prepare_heuristic?", method_factory_prepare_heuristic_p, 0);
621
714
  rb_define_method(geos_factory_class, "_set_wkrep_parsers", method_set_wkrep_parsers, 2);
622
715
  rb_define_method(geos_factory_class, "_proj4", method_get_proj4, 0);
623
716
  rb_define_method(geos_factory_class, "_coord_sys", method_get_coord_sys, 0);
@@ -633,34 +726,17 @@ RGeo_Globals* rgeo_init_geos_factory()
633
726
  rb_define_module_function(geos_factory_class, "_geos_version", cmethod_factory_geos_version, 0);
634
727
  rb_define_module_function(geos_factory_class, "_supports_unary_union?", cmethod_factory_supports_unary_union, 0);
635
728
 
636
- // Pre-define implementation classes and set up allocation methods
637
- globals->geos_geometry = rb_define_class_under(globals->geos_module, "CAPIGeometryImpl", rb_cObject);
638
- rb_define_alloc_func(globals->geos_geometry, alloc_geometry);
639
- globals->geos_point = rb_define_class_under(globals->geos_module, "CAPIPointImpl", rb_cObject);
640
- rb_define_alloc_func(globals->geos_point, alloc_geometry);
641
- globals->geos_line_string = rb_define_class_under(globals->geos_module, "CAPILineStringImpl", rb_cObject);
642
- rb_define_alloc_func(globals->geos_line_string, alloc_geometry);
643
- globals->geos_linear_ring = rb_define_class_under(globals->geos_module, "CAPILinearRingImpl", rb_cObject);
644
- rb_define_alloc_func(globals->geos_linear_ring, alloc_geometry);
645
- globals->geos_line = rb_define_class_under(globals->geos_module, "CAPILineImpl", rb_cObject);
646
- rb_define_alloc_func(globals->geos_line, alloc_geometry);
647
- globals->geos_polygon = rb_define_class_under(globals->geos_module, "CAPIPolygonImpl", rb_cObject);
648
- rb_define_alloc_func(globals->geos_polygon, alloc_geometry);
649
- globals->geos_geometry_collection = rb_define_class_under(globals->geos_module, "CAPIGeometryCollectionImpl", rb_cObject);
650
- rb_define_alloc_func(globals->geos_geometry_collection, alloc_geometry);
651
- globals->geos_multi_point = rb_define_class_under(globals->geos_module, "CAPIMultiPointImpl", rb_cObject);
652
- rb_define_alloc_func(globals->geos_multi_point, alloc_geometry);
653
- globals->geos_multi_line_string = rb_define_class_under(globals->geos_module, "CAPIMultiLineStringImpl", rb_cObject);
654
- rb_define_alloc_func(globals->geos_multi_line_string, alloc_geometry);
655
- globals->geos_multi_polygon = rb_define_class_under(globals->geos_module, "CAPIMultiPolygonImpl", rb_cObject);
656
- rb_define_alloc_func(globals->geos_multi_polygon, alloc_geometry);
657
-
658
- // Wrap the globals in a Ruby object and store it off so we have access
659
- // to it later. Each factory instance will reference it internally.
660
- wrapped_globals = Data_Wrap_Struct(rb_cObject, mark_globals_func, destroy_globals_func, globals);
661
- rb_define_const(geos_factory_class, "INTERNAL_CGLOBALS", wrapped_globals);
662
-
663
- return globals;
729
+ // Define allocation methods for global class types
730
+ rb_define_alloc_func(rgeo_geos_geometry_class, alloc_geometry);
731
+ rb_define_alloc_func(rgeo_geos_point_class, alloc_geometry);
732
+ rb_define_alloc_func(rgeo_geos_line_string_class, alloc_geometry);
733
+ rb_define_alloc_func(rgeo_geos_linear_ring_class, alloc_geometry);
734
+ rb_define_alloc_func(rgeo_geos_line_class, alloc_geometry);
735
+ rb_define_alloc_func(rgeo_geos_polygon_class, alloc_geometry);
736
+ rb_define_alloc_func(rgeo_geos_geometry_collection_class, alloc_geometry);
737
+ rb_define_alloc_func(rgeo_geos_multi_point_class, alloc_geometry);
738
+ rb_define_alloc_func(rgeo_geos_multi_line_string_class, alloc_geometry);
739
+ rb_define_alloc_func(rgeo_geos_multi_polygon_class, alloc_geometry);
664
740
  }
665
741
 
666
742
 
@@ -673,7 +749,6 @@ VALUE rgeo_wrap_geos_geometry(VALUE factory, GEOSGeometry* geom, VALUE klass)
673
749
  RGeo_FactoryData* factory_data;
674
750
  GEOSContextHandle_t factory_context;
675
751
  VALUE klasses;
676
- RGeo_Globals* globals;
677
752
  VALUE inferred_klass;
678
753
  char is_collection;
679
754
  RGeo_GeometryData* data;
@@ -682,7 +757,6 @@ VALUE rgeo_wrap_geos_geometry(VALUE factory, GEOSGeometry* geom, VALUE klass)
682
757
  if (geom || !NIL_P(klass)) {
683
758
  factory_data = NIL_P(factory) ? NULL : RGEO_FACTORY_DATA_PTR(factory);
684
759
  factory_context = factory_data ? factory_data->geos_context : NULL;
685
- globals = factory_data ? factory_data->globals : NULL;
686
760
 
687
761
  // We don't allow "empty" points, so replace such objects with
688
762
  // an empty collection.
@@ -690,7 +764,7 @@ VALUE rgeo_wrap_geos_geometry(VALUE factory, GEOSGeometry* geom, VALUE klass)
690
764
  if (GEOSGeomTypeId_r(factory_context, geom) == GEOS_POINT && GEOSGetNumCoordinates_r(factory_context, geom) == 0) {
691
765
  GEOSGeom_destroy_r(factory_context, geom);
692
766
  geom = GEOSGeom_createCollection_r(factory_context, GEOS_GEOMETRYCOLLECTION, NULL, 0);
693
- klass = globals->geos_geometry_collection;
767
+ klass = rgeo_geos_geometry_collection_class;
694
768
  }
695
769
  }
696
770
 
@@ -700,35 +774,35 @@ VALUE rgeo_wrap_geos_geometry(VALUE factory, GEOSGeometry* geom, VALUE klass)
700
774
  is_collection = 0;
701
775
  switch (GEOSGeomTypeId_r(factory_context, geom)) {
702
776
  case GEOS_POINT:
703
- inferred_klass = globals->geos_point;
777
+ inferred_klass = rgeo_geos_point_class;
704
778
  break;
705
779
  case GEOS_LINESTRING:
706
- inferred_klass = globals->geos_line_string;
780
+ inferred_klass = rgeo_geos_line_string_class;
707
781
  break;
708
782
  case GEOS_LINEARRING:
709
- inferred_klass = globals->geos_linear_ring;
783
+ inferred_klass = rgeo_geos_linear_ring_class;
710
784
  break;
711
785
  case GEOS_POLYGON:
712
- inferred_klass = globals->geos_polygon;
786
+ inferred_klass = rgeo_geos_polygon_class;
713
787
  break;
714
788
  case GEOS_MULTIPOINT:
715
- inferred_klass = globals->geos_multi_point;
789
+ inferred_klass = rgeo_geos_multi_point_class;
716
790
  is_collection = 1;
717
791
  break;
718
792
  case GEOS_MULTILINESTRING:
719
- inferred_klass = globals->geos_multi_line_string;
793
+ inferred_klass = rgeo_geos_multi_line_string_class;
720
794
  is_collection = 1;
721
795
  break;
722
796
  case GEOS_MULTIPOLYGON:
723
- inferred_klass = globals->geos_multi_polygon;
797
+ inferred_klass = rgeo_geos_multi_polygon_class;
724
798
  is_collection = 1;
725
799
  break;
726
800
  case GEOS_GEOMETRYCOLLECTION:
727
- inferred_klass = globals->geos_geometry_collection;
801
+ inferred_klass = rgeo_geos_geometry_collection_class;
728
802
  is_collection = 1;
729
803
  break;
730
804
  default:
731
- inferred_klass = globals->geos_geometry;
805
+ inferred_klass = rgeo_geos_geometry_class;
732
806
  break;
733
807
  }
734
808
  if (TYPE(klass) == T_ARRAY && is_collection) {
@@ -747,13 +821,12 @@ VALUE rgeo_wrap_geos_geometry(VALUE factory, GEOSGeometry* geom, VALUE klass)
747
821
  (GEOSPreparedGeometry*)1 : NULL;
748
822
  data->factory = factory;
749
823
  data->klasses = klasses;
750
- result = Data_Wrap_Struct(klass, mark_geometry_func, destroy_geometry_func, data);
824
+ result = TypedData_Wrap_Struct(klass, &rgeo_geometry_type, data);
751
825
  }
752
826
  }
753
827
  return result;
754
828
  }
755
829
 
756
-
757
830
  VALUE rgeo_wrap_geos_geometry_clone(VALUE factory, const GEOSGeometry* geom, VALUE klass)
758
831
  {
759
832
  VALUE result;
@@ -773,21 +846,18 @@ VALUE rgeo_wrap_geos_geometry_clone(VALUE factory, const GEOSGeometry* geom, VAL
773
846
  const GEOSGeometry* rgeo_convert_to_geos_geometry(VALUE factory, VALUE obj, VALUE type)
774
847
  {
775
848
  VALUE object;
776
- const GEOSGeometry* geom;
777
- RGeo_Globals* globals;
778
849
 
779
- if (NIL_P(type) && TYPE(obj) == T_DATA && RDATA(obj)->dfree == (RUBY_DATA_FUNC)destroy_geometry_func && RGEO_GEOMETRY_DATA_PTR(obj)->factory == factory) {
850
+ if (NIL_P(type) && RGEO_GEOMETRY_TYPEDDATA_P(obj) && RGEO_GEOMETRY_DATA_PTR(obj)->factory == factory) {
780
851
  object = obj;
781
852
  }
782
853
  else {
783
- globals = RGEO_FACTORY_DATA_PTR(factory)->globals;
784
- object = rb_funcall(globals->feature_module, globals->id_cast, 3, obj, factory, type);
854
+ object = rb_funcall(rgeo_feature_module, rb_intern("cast"), 3, obj, factory, type);
785
855
  }
786
- geom = NULL;
787
- if (!NIL_P(object)) {
788
- geom = RGEO_GEOMETRY_DATA_PTR(object)->geom;
789
- }
790
- return geom;
856
+ if (NIL_P(object))
857
+ return NULL;
858
+
859
+ Check_TypedStruct(object, &rgeo_geometry_type);
860
+ return RGEO_GEOMETRY_DATA_PTR(object)->geom;
791
861
  }
792
862
 
793
863
 
@@ -797,13 +867,11 @@ GEOSGeometry* rgeo_convert_to_detached_geos_geometry(VALUE obj, VALUE factory, V
797
867
  GEOSGeometry* geom;
798
868
  RGeo_GeometryData* object_data;
799
869
  const GEOSPreparedGeometry* prep;
800
- RGeo_Globals* globals;
801
870
 
802
871
  if (klasses) {
803
872
  *klasses = Qnil;
804
873
  }
805
- globals = RGEO_FACTORY_DATA_PTR(factory)->globals;
806
- object = rb_funcall(globals->feature_module, globals->id_cast, 5, obj, factory, type, globals->sym_force_new, globals->sym_keep_subtype);
874
+ object = rb_funcall(rgeo_feature_module, rb_intern("cast"), 5, obj, factory, type, ID2SYM(rb_intern("force_new")), ID2SYM(rb_intern("keep_subtype")));
807
875
  geom = NULL;
808
876
  if (!NIL_P(object)) {
809
877
  object_data = RGEO_GEOMETRY_DATA_PTR(object);
@@ -830,20 +898,20 @@ GEOSGeometry* rgeo_convert_to_detached_geos_geometry(VALUE obj, VALUE factory, V
830
898
 
831
899
  char rgeo_is_geos_object(VALUE obj)
832
900
  {
833
- return (TYPE(obj) == T_DATA && RDATA(obj)->dfree == (RUBY_DATA_FUNC)destroy_geometry_func) ? 1 : 0;
901
+ return RGEO_GEOMETRY_TYPEDDATA_P(obj) ? 1 : 0;
834
902
  }
835
903
 
836
904
  void rgeo_check_geos_object(VALUE obj)
837
905
  {
838
906
  if (!rgeo_is_geos_object(obj)) {
839
- rb_raise(rgeo_error, "Not a GEOS Geometry object.");
907
+ rb_raise(rb_eRGeoError, "Not a GEOS Geometry object.");
840
908
  }
841
909
  }
842
910
 
843
911
 
844
912
  const GEOSGeometry* rgeo_get_geos_geometry_safe(VALUE obj)
845
913
  {
846
- return (TYPE(obj) == T_DATA && RDATA(obj)->dfree == (RUBY_DATA_FUNC)destroy_geometry_func) ? (const GEOSGeometry*)(RGEO_GEOMETRY_DATA_PTR(obj)->geom) : NULL;
914
+ return RGEO_GEOMETRY_TYPEDDATA_P(obj) ? (const GEOSGeometry*)(RGEO_GEOMETRY_DATA_PTR(obj)->geom) : NULL;
847
915
  }
848
916
 
849
917
 
@@ -931,7 +999,8 @@ VALUE rgeo_geos_klasses_and_factories_eql(VALUE obj1, VALUE obj2)
931
999
  }
932
1000
  else {
933
1001
  factory = RGEO_GEOMETRY_DATA_PTR(obj1)->factory;
934
- result = rb_funcall(factory, RGEO_FACTORY_DATA_PTR(factory)->globals->id_eql, 1, RGEO_GEOMETRY_DATA_PTR(obj2)->factory);
1002
+ /* No need to cache the internal here (https://ips.fastruby.io/4x) */
1003
+ result = rb_funcall(factory, rb_intern("eql?"), 1, RGEO_GEOMETRY_DATA_PTR(obj2)->factory);
935
1004
  }
936
1005
  return result;
937
1006
  }
@@ -984,7 +1053,7 @@ st_index_t rgeo_geos_objbase_hash(VALUE factory, VALUE type_module, st_index_t h
984
1053
  ID hash_method;
985
1054
  RGeo_Objbase_Hash_Struct hash_struct;
986
1055
 
987
- hash_method = RGEO_FACTORY_DATA_PTR(factory)->globals->id_hash;
1056
+ hash_method = rb_intern("hash");
988
1057
  hash_struct.seed_hash = hash;
989
1058
  hash_struct.h1 = FIX2LONG(rb_funcall(factory, hash_method, 0));
990
1059
  hash_struct.h2 = FIX2LONG(rb_funcall(type_module, hash_method, 0));