rgeo 2.3.0 → 3.0.0.pre.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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));