Skip to content

Commit

Permalink
Add error handler for geos (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
BuonOmo committed Mar 17, 2022
1 parent b340802 commit 6ffb246
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 59 deletions.
2 changes: 1 addition & 1 deletion doc/Factory-Compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ _Note: This list is not exhaustive of all the methods defined by each factory. T
| `Collection#as_binary` ||||||||
| `Collection#empty?` ||||||||
| `Collection#simple?` ||||||||
| `Collection#boundary` | || ||| ||
| `Collection#boundary` | || ||| ||
| `Collection#convex_hull` ||||||||
| `Collection#buffer` ||||||||
| `Collection#equals?(Point)` ||||||||
Expand Down
8 changes: 4 additions & 4 deletions ext/geos_c_impl/analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ VALUE rgeo_geos_analysis_ccw_p(VALUE self, VALUE ring)
ring_data = RGEO_GEOMETRY_DATA_PTR(ring);

coord_seq = GEOSGeom_getCoordSeq_r(ring_data->geos_context, ring_data->geom);
if (!coord_seq) { rb_raise(geos_error, "Could not retrieve CoordSeq from given ring."); }
if (!coord_seq) { rb_raise(rb_eGeosError, "Could not retrieve CoordSeq from given ring."); }
if (!GEOSCoordSeq_isCCW_r(ring_data->geos_context, coord_seq, &is_ccw)) {
rb_raise(geos_error, "Could not determine if the CoordSeq is CCW.");
rb_raise(rb_eGeosError, "Could not determine if the CoordSeq is CCW.");
}

return is_ccw ? Qtrue : Qfalse;
Expand All @@ -49,8 +49,8 @@ VALUE rgeo_geos_analysis_ccw_p(VALUE self, VALUE ring)

/**
* call-seq:
* RGeo::Geos::Analysis.ccw_supported? -> true or false
*
* RGeo::Geos::Analysis.ccw_supported? -> true or false
*
* Checks if the RGEO_GEOS_SUPPORTS_ISCCW macro is defined, returns +true+
* if it is, +false+ otherwise
*/
Expand Down
14 changes: 8 additions & 6 deletions ext/geos_c_impl/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@

RGEO_BEGIN_C

VALUE rgeo_error;
VALUE rgeo_invalid_geometry_error;
VALUE geos_error;
VALUE rb_eRGeoError;
VALUE rb_eRGeoInvalidGeometry;
VALUE rb_eRGeoUnsupportedOperation;
VALUE rb_eGeosError;


void rgeo_init_geos_errors() {
VALUE error_module;

error_module = rb_define_module_under(rgeo_module, "Error");
rgeo_error = rb_define_class_under(error_module, "RGeoError", rb_eRuntimeError);
rgeo_invalid_geometry_error = rb_define_class_under(error_module, "InvalidGeometry", rgeo_error);
geos_error = rb_define_class_under(error_module, "GeosError", rgeo_error);
rb_eRGeoError = rb_define_class_under(error_module, "RGeoError", rb_eRuntimeError);
rb_eRGeoInvalidGeometry = rb_define_class_under(error_module, "InvalidGeometry", rb_eRGeoError);
rb_eRGeoUnsupportedOperation = rb_define_class_under(error_module, "UnsupportedOperation", rb_eRGeoError);
rb_eGeosError = rb_define_class_under(error_module, "GeosError", rb_eRGeoError);
}

RGEO_END_C
Expand Down
8 changes: 5 additions & 3 deletions ext/geos_c_impl/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
RGEO_BEGIN_C

// Main rgeo error type
extern VALUE rgeo_error;
extern VALUE rb_eRGeoError;
// RGeo::Error::InvalidGeometry
extern VALUE rgeo_invalid_geometry_error;
extern VALUE rb_eRGeoInvalidGeometry;
// RGeo::Error::UnsupportedOperation
extern VALUE rb_eRGeoUnsupportedOperation;
// RGeo error specific to the GEOS implementation.
extern VALUE geos_error;
extern VALUE rb_eGeosError;

void rgeo_init_geos_errors();

Expand Down
2 changes: 2 additions & 0 deletions ext/geos_c_impl/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def create_dummy_makefile
else
require "mkmf"

$CFLAGS << " -DRGEO_GEOS_DEBUG" if ENV.key?("DEBUG") || ENV.key?("RGEO_GEOS_DEBUG")

geosconfig = with_config("geos-config") || find_executable("geos-config")

if geosconfig
Expand Down
59 changes: 53 additions & 6 deletions ext/geos_c_impl/factory.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

#include <ruby.h>
#include <geos_c.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>

#include "globals.h"

Expand All @@ -26,13 +29,52 @@ RGEO_BEGIN_C
/**** RUBY AND GEOS CALLBACKS ****/


// NOP message handler. GEOS requires that a message handler be set
// for every context handle.

static void message_handler(const char* fmt, ...)
// The notice handler is very rarely used by GEOS, only in
// GEOSIsValid_r (check for NOTICE_MESSAGE in GEOS codebase).
// We still set it to make sure we do not miss any implementation
// change. Use `DEBUG=1 rake` to show notice.
#ifdef RGEO_GEOS_DEBUG
static void notice_handler(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "GEOS Notice -- ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
}
#endif

static void error_handler(const char* fmt, ...)
{
// See https://en.cppreference.com/w/c/io/vfprintf
va_list args1;
va_start(args1, fmt);
va_list args2;
va_copy(args2, args1);
int size = 1+vsnprintf(NULL, 0, fmt, args1);
va_end(args1);
char geos_full_error[size];
vsnprintf(geos_full_error, sizeof geos_full_error, fmt, args2);
va_end(args2);

// NOTE: strok is destructive, geos_full_error is not to be used afterwards.
char *geos_error = strtok(geos_full_error, ":");
char *geos_message = strtok(NULL, ":");
while(isspace(*geos_message)) geos_message++;

if (strcmp(geos_error, "UnsupportedOperationException") == 0) {
rb_raise(rb_eRGeoUnsupportedOperation, "%s", geos_message);
} else if (strcmp(geos_error, "IllegalArgumentException") == 0) {
rb_raise(rb_eRGeoInvalidGeometry, "%s", geos_message);
} else {
if (geos_message) {
rb_raise(rb_eGeosError, "%s: %s", geos_error, geos_message);
} else {
rb_raise(rb_eGeosError, "%s", geos_error);
}
}
}

// Destroy function for factory data. We destroy any serialization
// objects that have been created for the factory, and then destroy
Expand Down Expand Up @@ -478,7 +520,12 @@ static VALUE cmethod_factory_create(VALUE klass, VALUE flags, VALUE srid, VALUE
result = Qnil;
data = ALLOC(RGeo_FactoryData);
if (data) {
context = initGEOS_r(message_handler, message_handler);
context = GEOS_init_r();
#ifdef RGEO_GEOS_DEBUG
GEOSContext_setNoticeHandler_r(context, notice_handler);
#endif
GEOSContext_setErrorHandler_r(context, error_handler);

if (context) {
data->geos_context = context;
data->flags = NUM2INT(flags);
Expand Down Expand Up @@ -859,7 +906,7 @@ char rgeo_is_geos_object(VALUE obj)
void rgeo_check_geos_object(VALUE obj)
{
if (!rgeo_is_geos_object(obj)) {
rb_raise(rgeo_error, "Not a GEOS Geometry object.");
rb_raise(rb_eRGeoError, "Not a GEOS Geometry object.");
}
}

Expand Down
4 changes: 2 additions & 2 deletions ext/geos_c_impl/geometry.c
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,7 @@ static VALUE method_geometry_make_valid(VALUE self)
// According to GEOS implementation, MakeValid always returns.
valid_geom = GEOSMakeValid_r(self_data->geos_context, self_geom);
if (!valid_geom) {
rb_raise(rgeo_invalid_geometry_error, "%"PRIsVALUE, method_geometry_invalid_reason(self));
rb_raise(rb_eRGeoInvalidGeometry, "%"PRIsVALUE, method_geometry_invalid_reason(self));
}
return rgeo_wrap_geos_geometry(self_data->factory, valid_geom, Qnil);
}
Expand Down Expand Up @@ -1099,7 +1099,7 @@ VALUE rgeo_geos_geometries_strict_eql(GEOSContextHandle_t context, const GEOSGeo
return Qtrue;
case 2:
default:
rb_raise(geos_error, "Cannot test equality.");
rb_raise(rb_eGeosError, "Cannot test equality.");
}
}

Expand Down

0 comments on commit 6ffb246

Please sign in to comment.