rgeo 2.4.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 (59) 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 +4 -4
  5. data/ext/geos_c_impl/errors.c +8 -6
  6. data/ext/geos_c_impl/errors.h +7 -3
  7. data/ext/geos_c_impl/extconf.rb +2 -0
  8. data/ext/geos_c_impl/factory.c +80 -7
  9. data/ext/geos_c_impl/factory.h +28 -14
  10. data/ext/geos_c_impl/geometry.c +46 -14
  11. data/ext/geos_c_impl/geometry.h +7 -0
  12. data/ext/geos_c_impl/geometry_collection.c +2 -104
  13. data/ext/geos_c_impl/geometry_collection.h +0 -11
  14. data/ext/geos_c_impl/line_string.c +1 -1
  15. data/ext/geos_c_impl/point.c +1 -1
  16. data/ext/geos_c_impl/polygon.c +1 -37
  17. data/ext/geos_c_impl/preface.h +3 -0
  18. data/lib/rgeo/cartesian/calculations.rb +54 -17
  19. data/lib/rgeo/cartesian/factory.rb +0 -7
  20. data/lib/rgeo/cartesian/feature_classes.rb +66 -46
  21. data/lib/rgeo/cartesian/feature_methods.rb +51 -20
  22. data/lib/rgeo/cartesian/interface.rb +0 -6
  23. data/lib/rgeo/cartesian/planar_graph.rb +379 -0
  24. data/lib/rgeo/cartesian/sweepline_intersector.rb +149 -0
  25. data/lib/rgeo/cartesian/valid_op.rb +71 -0
  26. data/lib/rgeo/cartesian.rb +3 -0
  27. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +6 -6
  28. data/lib/rgeo/error.rb +15 -0
  29. data/lib/rgeo/feature/geometry.rb +28 -28
  30. data/lib/rgeo/feature/geometry_collection.rb +13 -5
  31. data/lib/rgeo/feature/line_string.rb +3 -3
  32. data/lib/rgeo/feature/multi_surface.rb +3 -3
  33. data/lib/rgeo/feature/point.rb +4 -4
  34. data/lib/rgeo/feature/surface.rb +3 -3
  35. data/lib/rgeo/geographic/factory.rb +0 -7
  36. data/lib/rgeo/geographic/interface.rb +5 -20
  37. data/lib/rgeo/geographic/proj4_projector.rb +0 -2
  38. data/lib/rgeo/geographic/projected_feature_classes.rb +21 -9
  39. data/lib/rgeo/geographic/projected_feature_methods.rb +51 -28
  40. data/lib/rgeo/geographic/simple_mercator_projector.rb +0 -2
  41. data/lib/rgeo/geographic/spherical_feature_classes.rb +29 -9
  42. data/lib/rgeo/geographic/spherical_feature_methods.rb +62 -1
  43. data/lib/rgeo/geos/capi_factory.rb +21 -31
  44. data/lib/rgeo/geos/capi_feature_classes.rb +35 -11
  45. data/lib/rgeo/geos/ffi_factory.rb +0 -28
  46. data/lib/rgeo/geos/ffi_feature_classes.rb +34 -10
  47. data/lib/rgeo/geos/ffi_feature_methods.rb +23 -5
  48. data/lib/rgeo/geos/interface.rb +0 -7
  49. data/lib/rgeo/geos/zm_factory.rb +0 -12
  50. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +4 -4
  51. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +1 -1
  52. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +15 -19
  53. data/lib/rgeo/impl_helper/basic_point_methods.rb +1 -1
  54. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +1 -1
  55. data/lib/rgeo/impl_helper/valid_op.rb +354 -0
  56. data/lib/rgeo/impl_helper/validity_check.rb +138 -0
  57. data/lib/rgeo/impl_helper.rb +1 -0
  58. data/lib/rgeo/version.rb +1 -1
  59. metadata +31 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abb42ec2fb37f915e49d13c7e802279560588d75f376d06d37f3a4e4f0e1ab61
4
- data.tar.gz: 6606d1fe12f30d645fb66edf95e694708ffb24ba81afa78ce03a961456f67686
3
+ metadata.gz: e26b01fb2fff6915756d98e206ee4bdabefeaceadae3cd0b85cb7be9ee6d8285
4
+ data.tar.gz: 9b94dd4e6c57e9fa5ab07c7b5fb9c622a52d843b67d146b52454933cb817a4a1
5
5
  SHA512:
6
- metadata.gz: 807c3f239731755e4afe348d70b2ebf0c81f29754a5677a4123dce474c0a18e7a6e63df5ee36fb244a05b04a9ef70356a3e966fab336a0925365afc05e331d5a
7
- data.tar.gz: c940601cc0c30944b5bc4e5f8d9ed632704295b28cb8115973a1856c823f01a7a0b655bc2e277ebe09268a08cf4fe9e1b0abbb4c192a598d2f06c95baef58901
6
+ metadata.gz: 9adb8cc283c8663fb0a178eeb7a1629fe466265f7fa15c54d4e8dac4188646a2682f591a3e76d12271df5dc6c875961833600b88952d035f824933bd454211de
7
+ data.tar.gz: c1f7e26d5a5edcc060cac2efaa782a586c9ee95e1dda08f7af771648ecc1ea2373a1e4ba9053aae00288332e401370c7f61e254367231ceabb4fb36ad5406209
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --markup rdoc
2
+ --output-dir ./yardoc
3
+ lib/**/*.rb
4
+ ext/**/*.{c,h}
5
+ -
6
+ doc/*.md
data/README.md CHANGED
@@ -129,6 +129,7 @@ Here's the current list of available topics:
129
129
  - [Installing GEOS](https://github.com/rgeo/rgeo/blob/master/doc/Installing-GEOS.md)
130
130
  - [Factory Compatibility](https://github.com/rgeo/rgeo/blob/master/doc/Factory-Compatibility.md)
131
131
  - [Which factory should I use?](https://github.com/rgeo/rgeo/blob/master/doc/Which-factory-should-I-use.md)
132
+ - [Geometry validity handling](https://github.com/rgeo/rgeo/blob/master/doc/Geometry-Validity.md)
132
133
  - [Examples](https://github.com/rgeo/rgeo/blob/master/doc/Examples.md)
133
134
  - [Who uses `rgeo`?](https://github.com/rgeo/rgeo/blob/master/doc/Gallery.md)
134
135
 
@@ -37,9 +37,9 @@ VALUE rgeo_geos_analysis_ccw_p(VALUE self, VALUE ring)
37
37
  ring_data = RGEO_GEOMETRY_DATA_PTR(ring);
38
38
 
39
39
  coord_seq = GEOSGeom_getCoordSeq_r(ring_data->geos_context, ring_data->geom);
40
- if (!coord_seq) { rb_raise(geos_error, "Could not retrieve CoordSeq from given ring."); }
40
+ if (!coord_seq) { rb_raise(rb_eGeosError, "Could not retrieve CoordSeq from given ring."); }
41
41
  if (!GEOSCoordSeq_isCCW_r(ring_data->geos_context, coord_seq, &is_ccw)) {
42
- rb_raise(geos_error, "Could not determine if the CoordSeq is CCW.");
42
+ rb_raise(rb_eGeosError, "Could not determine if the CoordSeq is CCW.");
43
43
  }
44
44
 
45
45
  return is_ccw ? Qtrue : Qfalse;
@@ -49,8 +49,8 @@ VALUE rgeo_geos_analysis_ccw_p(VALUE self, VALUE ring)
49
49
 
50
50
  /**
51
51
  * call-seq:
52
- * RGeo::Geos::Analysis.ccw_supported? -> true or false
53
- *
52
+ * RGeo::Geos::Analysis.ccw_supported? -> true or false
53
+ *
54
54
  * Checks if the RGEO_GEOS_SUPPORTS_ISCCW macro is defined, returns +true+
55
55
  * if it is, +false+ otherwise
56
56
  */
@@ -14,18 +14,20 @@
14
14
 
15
15
  RGEO_BEGIN_C
16
16
 
17
- // Any error relative to RGeo.
18
- VALUE rgeo_error;
19
- // RGeo error specific to the GEOS implementation.
20
- VALUE geos_error;
17
+ VALUE rb_eRGeoError;
18
+ VALUE rb_eRGeoInvalidGeometry;
19
+ VALUE rb_eRGeoUnsupportedOperation;
20
+ VALUE rb_eGeosError;
21
21
 
22
22
 
23
23
  void rgeo_init_geos_errors() {
24
24
  VALUE error_module;
25
25
 
26
26
  error_module = rb_define_module_under(rgeo_module, "Error");
27
- rgeo_error = rb_define_class_under(error_module, "RGeoError", rb_eRuntimeError);
28
- geos_error = rb_define_class_under(error_module, "GeosError", rgeo_error);
27
+ rb_eRGeoError = rb_define_class_under(error_module, "RGeoError", rb_eRuntimeError);
28
+ rb_eRGeoInvalidGeometry = rb_define_class_under(error_module, "InvalidGeometry", rb_eRGeoError);
29
+ rb_eRGeoUnsupportedOperation = rb_define_class_under(error_module, "UnsupportedOperation", rb_eRGeoError);
30
+ rb_eGeosError = rb_define_class_under(error_module, "GeosError", rb_eRGeoError);
29
31
  }
30
32
 
31
33
  RGEO_END_C
@@ -8,10 +8,14 @@
8
8
 
9
9
  RGEO_BEGIN_C
10
10
 
11
- // Any error relative to RGeo.
12
- extern VALUE rgeo_error;
11
+ // Main rgeo error type
12
+ extern VALUE rb_eRGeoError;
13
+ // RGeo::Error::InvalidGeometry
14
+ extern VALUE rb_eRGeoInvalidGeometry;
15
+ // RGeo::Error::UnsupportedOperation
16
+ extern VALUE rb_eRGeoUnsupportedOperation;
13
17
  // RGeo error specific to the GEOS implementation.
14
- extern VALUE geos_error;
18
+ extern VALUE rb_eGeosError;
15
19
 
16
20
  void rgeo_init_geos_errors();
17
21
 
@@ -14,6 +14,8 @@ if RUBY_DESCRIPTION =~ /^jruby\s/
14
14
  else
15
15
  require "mkmf"
16
16
 
17
+ $CFLAGS << " -DRGEO_GEOS_DEBUG" if ENV.key?("DEBUG") || ENV.key?("RGEO_GEOS_DEBUG")
18
+
17
19
  geosconfig = with_config("geos-config") || find_executable("geos-config")
18
20
 
19
21
  if geosconfig
@@ -9,6 +9,9 @@
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>
12
15
 
13
16
  #include "globals.h"
14
17
 
@@ -26,13 +29,50 @@ RGEO_BEGIN_C
26
29
  /**** RUBY AND GEOS CALLBACKS ****/
27
30
 
28
31
 
29
- // NOP message handler. GEOS requires that a message handler be set
30
- // for every context handle.
31
-
32
- 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, ...)
33
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);
34
45
  }
46
+ #endif
35
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
+ }
36
76
 
37
77
  // Destroy function for factory data. We destroy any serialization
38
78
  // objects that have been created for the factory, and then destroy
@@ -227,6 +267,25 @@ static VALUE method_factory_flags(VALUE self)
227
267
  return INT2NUM(RGEO_FACTORY_DATA_PTR(self)->flags);
228
268
  }
229
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
+ }
230
289
 
231
290
  static VALUE method_factory_parse_wkt(VALUE self, VALUE str)
232
291
  {
@@ -459,7 +518,12 @@ static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE
459
518
  result = Qnil;
460
519
  data = ALLOC(RGeo_FactoryData);
461
520
  if (data) {
462
- 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
+
463
527
  if (context) {
464
528
  data->geos_context = context;
465
529
  data->flags = NUM2INT(flags);
@@ -629,15 +693,24 @@ void rgeo_init_geos_factory()
629
693
  rb_gc_register_address(&marshal_wkb_generator);
630
694
  #endif
631
695
 
632
- // Add C methods to the factory.
633
696
  geos_factory_class = rb_define_class_under(rgeo_geos_module, "CAPIFactory", rb_cObject);
634
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.
635
704
  rb_define_method(geos_factory_class, "initialize_copy", method_factory_initialize_copy, 1);
636
705
  rb_define_method(geos_factory_class, "_parse_wkt_impl", method_factory_parse_wkt, 1);
637
706
  rb_define_method(geos_factory_class, "_parse_wkb_impl", method_factory_parse_wkb, 1);
638
707
  rb_define_method(geos_factory_class, "_srid", method_factory_srid, 0);
639
708
  rb_define_method(geos_factory_class, "_buffer_resolution", method_factory_buffer_resolution, 0);
640
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);
641
714
  rb_define_method(geos_factory_class, "_set_wkrep_parsers", method_set_wkrep_parsers, 2);
642
715
  rb_define_method(geos_factory_class, "_proj4", method_get_proj4, 0);
643
716
  rb_define_method(geos_factory_class, "_coord_sys", method_get_coord_sys, 0);
@@ -831,7 +904,7 @@ char rgeo_is_geos_object(VALUE obj)
831
904
  void rgeo_check_geos_object(VALUE obj)
832
905
  {
833
906
  if (!rgeo_is_geos_object(obj)) {
834
- rb_raise(rgeo_error, "Not a GEOS Geometry object.");
907
+ rb_raise(rb_eRGeoError, "Not a GEOS Geometry object.");
835
908
  }
836
909
  }
837
910
 
@@ -37,12 +37,35 @@ typedef struct {
37
37
  int buffer_resolution;
38
38
  } RGeo_FactoryData;
39
39
 
40
- #define RGEO_FACTORYFLAGS_LENIENT_MULTIPOLYGON 1
41
- #define RGEO_FACTORYFLAGS_SUPPORTS_Z 2
42
- #define RGEO_FACTORYFLAGS_SUPPORTS_M 4
43
- #define RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M 6
44
- #define RGEO_FACTORYFLAGS_PREPARE_HEURISTIC 8
40
+ /*
41
+ Flags that are used to pass options when creating a factory.
42
+ They are available in ruby under RGeo::Geos::CAPIFactory::FLAG_name
43
+ where name is the name below without the RGEO_FACTORYFLAGS_ prefix.
44
+ */
45
+ #define RGEO_FACTORYFLAGS_SUPPORTS_Z 0b0010
46
+ #define RGEO_FACTORYFLAGS_SUPPORTS_M 0b0100
47
+ #define RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M (RGEO_FACTORYFLAGS_SUPPORTS_Z | RGEO_FACTORYFLAGS_SUPPORTS_M)
48
+ #define RGEO_FACTORYFLAGS_PREPARE_HEURISTIC 0b1000
49
+
50
+ /* call-seq:
51
+ * RGeo::Geos::CAPIFactory.supports_z? -> true or false
52
+ */
53
+ VALUE method_factory_supports_z_p(VALUE self);
54
+
55
+ /* call-seq:
56
+ * RGeo::Geos::CAPIFactory.supports_m? -> true or false
57
+ */
58
+ VALUE method_factory_supports_m_p(VALUE self);
59
+
60
+ /* call-seq:
61
+ * RGeo::Geos::CAPIFactory.supports_z_or_m? -> true or false
62
+ */
63
+ VALUE method_factory_supports_z_or_m_p(VALUE self);
45
64
 
65
+ /* call-seq:
66
+ * RGeo::Geos::CAPIFactory.prepare_heuristic? -> true or false
67
+ */
68
+ VALUE method_factory_prepare_heuristic_p(VALUE self);
46
69
 
47
70
  /*
48
71
  Wrapped structure for Geometry objects.
@@ -164,15 +187,6 @@ void rgeo_check_geos_object(VALUE obj);
164
187
  */
165
188
  const GEOSGeometry* rgeo_get_geos_geometry_safe(VALUE obj);
166
189
 
167
- /*
168
- Compares the coordinate sequences for two given GEOS geometries.
169
- The two given geometries MUST be of types backed directly by
170
- coordinate sequences-- i.e. points or line strings.
171
- Returns Qtrue if the two coordinate sequences are equal, Qfalse
172
- if they are inequal, or Qnil if an error occurs.
173
- */
174
- VALUE rgeo_geos_coordseqs_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2, char check_z);
175
-
176
190
  /*
177
191
  Compares the ruby classes and geometry factories of the two given ruby
178
192
  objects. Returns Qtrue if everything is equal (that is, the two objects
@@ -13,6 +13,7 @@
13
13
 
14
14
  #include "globals.h"
15
15
 
16
+ #include "errors.h"
16
17
  #include "factory.h"
17
18
  #include "geometry.h"
18
19
 
@@ -863,24 +864,21 @@ static VALUE method_geometry_union(VALUE self, VALUE rhs)
863
864
 
864
865
  static VALUE method_geometry_unary_union(VALUE self)
865
866
  {
866
- VALUE result;
867
+ #ifdef RGEO_GEOS_SUPPORTS_UNARYUNION
867
868
  RGeo_GeometryData* self_data;
868
869
  const GEOSGeometry* self_geom;
869
870
 
870
- result = Qnil;
871
-
872
- #ifdef RGEO_GEOS_SUPPORTS_UNARYUNION
873
871
  self_data = RGEO_GEOMETRY_DATA_PTR(self);
874
872
  self_geom = self_data->geom;
875
873
  if (self_geom) {
876
874
  GEOSContextHandle_t self_context = self_data->geos_context;
877
- result = rgeo_wrap_geos_geometry(self_data->factory,
875
+ return rgeo_wrap_geos_geometry(self_data->factory,
878
876
  GEOSUnaryUnion_r(self_context, self_geom),
879
877
  Qnil);
880
878
  }
881
879
  #endif
882
880
 
883
- return result;
881
+ return Qnil;
884
882
  }
885
883
 
886
884
 
@@ -1044,18 +1042,39 @@ static VALUE method_geometry_invalid_reason(VALUE self)
1044
1042
  self_data = RGEO_GEOMETRY_DATA_PTR(self);
1045
1043
  self_geom = self_data->geom;
1046
1044
  if (self_geom) {
1047
- str = GEOSisValidReason_r(self_data->geos_context, self_geom);
1048
- // Per documentation, a valid geometry should give an empty string.
1049
- // However it seems not to be the case. Hence the comparison against
1050
- // the string that is really given: `"Valid Geometry"`.
1051
- // See https://github.com/libgeos/geos/issues/431.
1052
- if (str) result = (str[0] == '\0' || !strcmp(str, "Valid Geometry")) ? Qnil : rb_str_new2(str);
1053
- else result = rb_str_new2("Exception");
1054
- GEOSFree_r(self_data->geos_context, str);
1045
+ // We use NULL there to tell GEOS that we don't care about the position.
1046
+ switch(GEOSisValidDetail_r(self_data->geos_context, self_geom, 0, &str, NULL)) {
1047
+ case 0: // invalid
1048
+ result = rb_utf8_str_new_cstr(str);
1049
+ case 1: // valid
1050
+ break;
1051
+ case 2: // exception
1052
+ default:
1053
+ result = rb_utf8_str_new_cstr("Exception");
1054
+ break;
1055
+ };
1056
+ if (str) GEOSFree_r(self_data->geos_context, str);
1055
1057
  }
1056
1058
  return result;
1057
1059
  }
1058
1060
 
1061
+ static VALUE method_geometry_make_valid(VALUE self)
1062
+ {
1063
+ RGeo_GeometryData* self_data;
1064
+ const GEOSGeometry* self_geom;
1065
+ GEOSGeometry* valid_geom;
1066
+ self_data = RGEO_GEOMETRY_DATA_PTR(self);
1067
+ self_geom = self_data->geom;
1068
+ if (!self_geom) return Qnil;
1069
+
1070
+ // According to GEOS implementation, MakeValid always returns.
1071
+ valid_geom = GEOSMakeValid_r(self_data->geos_context, self_geom);
1072
+ if (!valid_geom) {
1073
+ rb_raise(rb_eRGeoInvalidGeometry, "%"PRIsVALUE, method_geometry_invalid_reason(self));
1074
+ }
1075
+ return rgeo_wrap_geos_geometry(self_data->factory, valid_geom, Qnil);
1076
+ }
1077
+
1059
1078
  static VALUE method_geometry_point_on_surface(VALUE self)
1060
1079
  {
1061
1080
  VALUE result;
@@ -1071,6 +1090,18 @@ static VALUE method_geometry_point_on_surface(VALUE self)
1071
1090
  return result;
1072
1091
  }
1073
1092
 
1093
+ VALUE rgeo_geos_geometries_strict_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2)
1094
+ {
1095
+ switch (GEOSEqualsExact_r(context, geom1, geom2, 0.0)) {
1096
+ case 0:
1097
+ return Qfalse;
1098
+ case 1:
1099
+ return Qtrue;
1100
+ case 2:
1101
+ default:
1102
+ rb_raise(rb_eGeosError, "Cannot test equality.");
1103
+ }
1104
+ }
1074
1105
 
1075
1106
  /**** INITIALIZATION FUNCTION ****/
1076
1107
 
@@ -1126,6 +1157,7 @@ void rgeo_init_geos_geometry()
1126
1157
  rb_define_method(geos_geometry_methods, "valid?", method_geometry_is_valid, 0);
1127
1158
  rb_define_method(geos_geometry_methods, "invalid_reason", method_geometry_invalid_reason, 0);
1128
1159
  rb_define_method(geos_geometry_methods, "point_on_surface", method_geometry_point_on_surface, 0);
1160
+ rb_define_method(geos_geometry_methods, "make_valid", method_geometry_make_valid, 0);
1129
1161
  }
1130
1162
 
1131
1163
 
@@ -16,6 +16,13 @@ RGEO_BEGIN_C
16
16
  void rgeo_init_geos_geometry();
17
17
 
18
18
 
19
+ /*
20
+ Compares two geometries using strict GEOS comparison. return Qtrue
21
+ if they are equal, Qfalse otherwise.
22
+ May raise a `RGeo::Error::GeosError`.
23
+ */
24
+ VALUE rgeo_geos_geometries_strict_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2);
25
+
19
26
  RGEO_END_C
20
27
 
21
28
  #endif
@@ -16,6 +16,7 @@
16
16
  #include "geometry.h"
17
17
  #include "line_string.h"
18
18
  #include "polygon.h"
19
+ #include "geometry.h"
19
20
  #include "geometry_collection.h"
20
21
 
21
22
  #include "coordinates.h"
@@ -43,9 +44,6 @@ static VALUE create_geometry_collection(VALUE module, int type, VALUE factory, V
43
44
  VALUE cast_type;
44
45
  GEOSGeometry* geom;
45
46
  GEOSGeometry* collection;
46
- char problem;
47
- GEOSGeometry* igeom;
48
- GEOSGeometry* jgeom;
49
47
 
50
48
  result = Qnil;
51
49
  Check_Type(array, T_ARRAY);
@@ -90,32 +88,6 @@ static VALUE create_geometry_collection(VALUE module, int type, VALUE factory, V
90
88
  }
91
89
  else {
92
90
  collection = GEOSGeom_createCollection_r(geos_context, type, geoms, len);
93
- // Due to a limitation of GEOS, the MultiPolygon assertions are not checked.
94
- // We do that manually here.
95
- if (collection && type == GEOS_MULTIPOLYGON && (factory_data->flags & 1) == 0) {
96
- problem = 0;
97
- for (i=1; i<len; ++i) {
98
- for (j=0; j<i; ++j) {
99
- igeom = geoms[i];
100
- jgeom = geoms[j];
101
- problem = GEOSRelatePattern_r(geos_context, igeom, jgeom, "2********");
102
- if (problem) {
103
- break;
104
- }
105
- problem = GEOSRelatePattern_r(geos_context, igeom, jgeom, "****1****");
106
- if (problem) {
107
- break;
108
- }
109
- }
110
- if (problem) {
111
- break;
112
- }
113
- }
114
- if (problem) {
115
- GEOSGeom_destroy_r(geos_context, collection);
116
- collection = NULL;
117
- }
118
- }
119
91
  if (collection) {
120
92
  result = rgeo_wrap_geos_geometry(factory, collection, module);
121
93
  RGEO_GEOMETRY_DATA_PTR(result)->klasses = klasses;
@@ -143,7 +115,7 @@ static VALUE method_geometry_collection_eql(VALUE self, VALUE rhs)
143
115
  result = rgeo_geos_klasses_and_factories_eql(self, rhs);
144
116
  if (RTEST(result)) {
145
117
  self_data = RGEO_GEOMETRY_DATA_PTR(self);
146
- result = rgeo_geos_geometry_collections_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom, RGEO_FACTORY_DATA_PTR(self_data->factory)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M);
118
+ result = rgeo_geos_geometries_strict_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom);
147
119
  }
148
120
  return result;
149
121
  }
@@ -635,80 +607,6 @@ void rgeo_init_geos_geometry_collection()
635
607
  /**** OTHER PUBLIC FUNCTIONS ****/
636
608
 
637
609
 
638
- VALUE rgeo_geos_geometry_collections_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2, char check_z)
639
- {
640
- VALUE result;
641
- int len1;
642
- int len2;
643
- int i;
644
- const GEOSGeometry* sub_geom1;
645
- const GEOSGeometry* sub_geom2;
646
- int type1;
647
- int type2;
648
-
649
- result = Qnil;
650
- if (geom1 && geom2) {
651
- len1 = GEOSGetNumGeometries_r(context, geom1);
652
- len2 = GEOSGetNumGeometries_r(context, geom2);
653
- if (len1 >= 0 && len2 >= 0) {
654
- if (len1 == len2) {
655
- result = Qtrue;
656
- for (i=0; i<len1; ++i) {
657
- sub_geom1 = GEOSGetGeometryN_r(context, geom1, i);
658
- sub_geom2 = GEOSGetGeometryN_r(context, geom2, i);
659
- if (sub_geom1 && sub_geom2) {
660
- type1 = GEOSGeomTypeId_r(context, sub_geom1);
661
- type2 = GEOSGeomTypeId_r(context, sub_geom2);
662
- if (type1 >= 0 && type2 >= 0) {
663
- if (type1 == type2) {
664
- switch (type1) {
665
- case GEOS_POINT:
666
- case GEOS_LINESTRING:
667
- case GEOS_LINEARRING:
668
- result = rgeo_geos_coordseqs_eql(context, sub_geom1, sub_geom2, check_z);
669
- break;
670
- case GEOS_POLYGON:
671
- result = rgeo_geos_polygons_eql(context, sub_geom1, sub_geom2, check_z);
672
- break;
673
- case GEOS_GEOMETRYCOLLECTION:
674
- case GEOS_MULTIPOINT:
675
- case GEOS_MULTILINESTRING:
676
- case GEOS_MULTIPOLYGON:
677
- result = rgeo_geos_geometry_collections_eql(context, sub_geom1, sub_geom2, check_z);
678
- break;
679
- default:
680
- result = Qnil;
681
- break;
682
- }
683
- if (!RTEST(result)) {
684
- break;
685
- }
686
- }
687
- else {
688
- result = Qfalse;
689
- break;
690
- }
691
- }
692
- else {
693
- result = Qnil;
694
- break;
695
- }
696
- }
697
- else {
698
- result = Qnil;
699
- break;
700
- }
701
- }
702
- }
703
- else {
704
- result = Qfalse;
705
- }
706
- }
707
- }
708
- return result;
709
- }
710
-
711
-
712
610
  st_index_t rgeo_geos_geometry_collection_hash(GEOSContextHandle_t context, const GEOSGeometry* geom, st_index_t hash)
713
611
  {
714
612
  const GEOSGeometry* sub_geom;
@@ -18,17 +18,6 @@ RGEO_BEGIN_C
18
18
  */
19
19
  void rgeo_init_geos_geometry_collection();
20
20
 
21
- /*
22
- Comopares the contents of two geometry collections. Does not test the
23
- types of the collections themselves, but tests the types, values, and
24
- contents of all the contents. The two given geometries MUST be
25
- collection types-- i.e. GeometryCollection, MultiPoint, MultiLineString,
26
- or MultiPolygon.
27
- Returns Qtrue if the contents of the two geometry collections are equal,
28
- Qfalse if they are inequal, or Qnil if an error occurs.
29
- */
30
- VALUE rgeo_geos_geometry_collections_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2, char check_z);
31
-
32
21
  /*
33
22
  A tool for building up hash values.
34
23
  You must pass in the context, a geos geometry, and a seed hash.
@@ -345,7 +345,7 @@ static VALUE method_line_string_eql(VALUE self, VALUE rhs)
345
345
  result = rgeo_geos_klasses_and_factories_eql(self, rhs);
346
346
  if (RTEST(result)) {
347
347
  self_data = RGEO_GEOMETRY_DATA_PTR(self);
348
- result = rgeo_geos_coordseqs_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom, RGEO_FACTORY_DATA_PTR(self_data->factory)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M);
348
+ result = rgeo_geos_geometries_strict_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom);
349
349
  }
350
350
  return result;
351
351
  }
@@ -155,7 +155,7 @@ static VALUE method_point_eql(VALUE self, VALUE rhs)
155
155
  result = rgeo_geos_klasses_and_factories_eql(self, rhs);
156
156
  if (RTEST(result)) {
157
157
  self_data = RGEO_GEOMETRY_DATA_PTR(self);
158
- result = rgeo_geos_coordseqs_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom, RGEO_FACTORY_DATA_PTR(self_data->factory)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M);
158
+ result = rgeo_geos_geometries_strict_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom);
159
159
  }
160
160
  return result;
161
161
  }
@@ -30,7 +30,7 @@ static VALUE method_polygon_eql(VALUE self, VALUE rhs)
30
30
  result = rgeo_geos_klasses_and_factories_eql(self, rhs);
31
31
  if (RTEST(result)) {
32
32
  self_data = RGEO_GEOMETRY_DATA_PTR(self);
33
- result = rgeo_geos_polygons_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom, RGEO_FACTORY_DATA_PTR(self_data->factory)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M);
33
+ result = rgeo_geos_geometries_strict_eql(self_data->geos_context, self_data->geom, RGEO_GEOMETRY_DATA_PTR(rhs)->geom);
34
34
  }
35
35
  return result;
36
36
  }
@@ -299,42 +299,6 @@ void rgeo_init_geos_polygon()
299
299
  rb_define_method(geos_polygon_methods, "coordinates", method_polygon_coordinates, 0);
300
300
  }
301
301
 
302
-
303
- VALUE rgeo_geos_polygons_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2, char check_z)
304
- {
305
- VALUE result;
306
- int len1;
307
- int len2;
308
- int i;
309
-
310
- result = Qnil;
311
- if (geom1 && geom2) {
312
- result = rgeo_geos_coordseqs_eql(context, GEOSGetExteriorRing_r(context, geom1), GEOSGetExteriorRing_r(context, geom2), check_z);
313
- if (RTEST(result)) {
314
- len1 = GEOSGetNumInteriorRings_r(context, geom1);
315
- len2 = GEOSGetNumInteriorRings_r(context, geom2);
316
- if (len1 >= 0 && len2 >= 0) {
317
- if (len1 == len2) {
318
- for (i=0; i<len1; ++i) {
319
- result = rgeo_geos_coordseqs_eql(context, GEOSGetInteriorRingN_r(context, geom1, i), GEOSGetInteriorRingN_r(context, geom2, i), check_z);
320
- if (!RTEST(result)) {
321
- break;
322
- }
323
- }
324
- }
325
- else {
326
- result = Qfalse;
327
- }
328
- }
329
- else {
330
- result = Qnil;
331
- }
332
- }
333
- }
334
- return result;
335
- }
336
-
337
-
338
302
  st_index_t rgeo_geos_polygon_hash(GEOSContextHandle_t context, const GEOSGeometry* geom, st_index_t hash)
339
303
  {
340
304
  unsigned int len;
@@ -47,3 +47,6 @@
47
47
  #define RGEO_BEGIN_C
48
48
  #define RGEO_END_C
49
49
  #endif
50
+
51
+ // https://ozlabs.org/~rusty/index.cgi/tech/2008-04-01.html
52
+ #define streq(a, b) (!strcmp((a),(b)))