-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix STRtree insert/query to avoid void nullptr #261
Fix STRtree insert/query to avoid void nullptr #261
Conversation
|
Right, you don't want to keep a pointer to a local variable that has gone out-of-scope. You can see how it's handled in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this.
What is the advantage to using resizable vectors here? Per #129, I've been meaning to replace those with allocating an array of known size in advance.
Both the geometry array and the index array should be the same size as the incoming array; this could be managed in the tree via malloc and free instead of delegating that to the resizable vector. That should also make passing the pointer to the values into GEOS a little cleaner. I'll add a few suggestions inline that may help show this (untested); probably also needs an update on usage in query callback but I didn't touch that.
(updating the geometry vector should probably be in a different PR; will also need to add a _size member to track the size of that array in that case, since right now that is managed by the vector).
| @@ -172,7 +179,7 @@ static PyObject* STRtree_new(PyTypeObject* type, PyObject* args, PyObject* kwds) | |||
| Py_INCREF(obj); | |||
| kv_push(GeometryObject*, _geoms, obj); | |||
| count++; | |||
| GEOSSTRtree_insert_r(ctx, tree, geom, (void*)i); | |||
| GEOSSTRtree_insert_r(ctx, tree, geom, &kv_A(_tree_indexes, i)); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with array managed in tree
| GEOSSTRtree_insert_r(ctx, tree, geom, &kv_A(_tree_indexes, i)); | |
| GEOSSTRtree_insert_r(ctx, tree, geom, &(_tree_indexes[i]); |
| @@ -115,6 +116,7 @@ static PyObject* STRtree_new(PyTypeObject* type, PyObject* args, PyObject* kwds) | |||
| npy_intp n, i, count = 0; | |||
| GEOSGeometry* geom; | |||
| pg_geom_obj_vec _geoms; | |||
| npy_intp_vec _tree_indexes; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| npy_intp_vec _tree_indexes; | |
| npy_intp* _tree_indexes; |
| @@ -145,7 +147,11 @@ static PyObject* STRtree_new(PyTypeObject* type, PyObject* args, PyObject* kwds) | |||
| n = PyArray_SIZE((PyArrayObject*)arr); | |||
|
|
|||
| kv_init(_geoms); | |||
| kv_init(_tree_indexes); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| kv_init(_tree_indexes); | |
| _tree_indexes = (npy_intp*)malloc(n * sizeof(npy_intp)); |
(and drop following line)
| for (i = 0; i < n; i++) { | ||
| kv_push(npy_intp, _tree_indexes, i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| kv_push(npy_intp, _tree_indexes, i); | |
| _tree_indexes[i] = i; |
| @@ -159,6 +165,7 @@ static PyObject* STRtree_new(PyTypeObject* type, PyObject* args, PyObject* kwds) | |||
| Py_XDECREF(kv_A(_geoms, i)); | |||
| } | |||
| kv_destroy(_geoms); | |||
| kv_destroy(_tree_indexes); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| kv_destroy(_tree_indexes); | |
| free(_tree_indexes); |
| @@ -22,6 +22,7 @@ typedef struct { | |||
| PyObject_HEAD void* ptr; | |||
| npy_intp count; | |||
| pg_geom_obj_vec _geoms; | |||
| npy_intp_vec _tree_indexes; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| npy_intp_vec _tree_indexes; | |
| npy_intp* _tree_indexes; |
| @@ -104,6 +104,7 @@ static void STRtree_dealloc(STRtreeObject* self) { | |||
| Py_XDECREF(kv_A(self->_geoms, i)); | |||
| } | |||
| kv_destroy(self->_geoms); | |||
| kv_destroy(self->_tree_indexes); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| kv_destroy(self->_tree_indexes); | |
| free(self->_tree_indexes); |
|
If the geometries you're putting in the tree are stored in fixed-size array, and you need to get the index when you query the tree (instead of getting the geometry directly), you can still store the geometry pointer in the tree and recover the index via pointer arithmetic from the head of the geometry array. |
I think this might not work, since the incoming array we need to use for indexing may have geometries that we do not add to the tree (they may include Python But, if there is something I'm missing, this would be a clever way of avoiding a redundant array of indexes. |
|
Not missing anything. This would only work if all the geometries in the contiguous array are added to the tree. |
Yeah, I did it mostly out of consistency (to not use a mixture of kvec and plain arrays). And, not having to store the size separately is nice (in general, for the tree_indexes we don't actually need it, that's true). But I know here it has a fixed size, that's also the reason I resized it up front.
We indeed don't add each geometry to the tree, but looking more closely, we are actually adding something to I am only not sure how to do this in the |
You'd have to pass both the start of the Not sure if it's worth the complexity. |
|
New idea (testing now):
|
|
I'm getting some results from the pointer math that I don't completely understand (never used pointer math directly before). Given
When I calculate the offset of the second geometry like When I calculate what the address would be by incrementing the pointer Perhaps I'm missing a type cast somewhere? |
|
I think |
|
I think I figured it out, just needed to be more explicit about calculating the offsets ourselves and not relying on the compiler to infer the correct item size when using subtraction in this case: This now gives me the correct indices back out. I think I might have fallen afoul of the C standard behavior around subtraction only being well defined when the elements are of the same array, and by the time I was running the subtraction (after first adding a geometry to a resizeable vector in |
|
Sorry for the slow follow up here, and thaks @brendan-ward for picking it up! I actually had a version locally with the suggestion of @dbaston, but only the minimal change, your PR is much more comprehensive change, so closing this PR as superseded by #265 |
xref #233 (comment) and the discussion the geos-devel mailing list (https://lists.osgeo.org/pipermail/geos-devel/2020-November/009870.html).
This change is what I understand from that discussion should be the solution, but this is not actually working (it's giving garbage / crashing).
One thing I am wondering: I am now passing the address of an integer, but this int is a loop variable, so that is only a local variable that I suppose is cleaned up at the end of
STRtree_new.So if we want to be able to access those integers in
STRtree_querywe might need to store them on the STRtree object? (the python wrapper object, not the GEOS STRtree).