You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移检测线串与预准备几何体相交的Cython代码至Shapely2.0遇段错误

问题:Shapely 2.0迁移中Cython代码段错误(GEOSPreparedIntersects_r调用失败)

我编写了自定义Cython代码用于检测线串与预准备几何体的相交情况,从Shapely 1.8迁移至2.0后,以下代码行触发段错误:

result[i] = <np.uint8_t> GEOSPreparedIntersects_r(geos_handle, geom1, geom2)

完整Cython代码

#!python
#cython: language_level=3

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>

cimport cython
from libc.stdint cimport uintptr_t
import shapely.prepared
import numpy as np
cimport numpy as np

__all__ = ['two_points_intersect_geom', "DTYPE"]

np.import_array()
DTYPE = np.float32

ctypedef np.float32_t DTYPE_t

cdef extern from "geos_c.h":
    ctypedef void *GEOSContextHandle_t
    ctypedef struct GEOSGeometry
    ctypedef struct GEOSCoordSequence
    ctypedef struct GEOSPreparedGeometry

    GEOSCoordSequence *GEOSCoordSeq_create_r(GEOSContextHandle_t, unsigned int, unsigned int) nogil
    int GEOSCoordSeq_getSize_r(GEOSContextHandle_t, GEOSCoordSequence *, unsigned int *) nogil
    int GEOSCoordSeq_setX_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
    int GEOSCoordSeq_setY_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
    int GEOSCoordSeq_setZ_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
    GEOSGeometry *GEOSGeom_createLineString_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil

    char GEOSPreparedIntersects_r(GEOSContextHandle_t, const GEOSPreparedGeometry *, const GEOSGeometry *) nogil
    char GEOSIntersects_r(GEOSContextHandle_t, const GEOSGeometry *, const GEOSGeometry *) nogil

cdef GEOSContextHandle_t get_geos_context_handle():
    # Note: This requires that lgeos is defined, so needs to be imported as:
    from shapely.geos import lgeos
    cdef uintptr_t handle = lgeos.geos_handle
    return <GEOSContextHandle_t> handle

cdef GEOSPreparedGeometry *geos_from_prepared(shapely_geom) except *:
    """Get the Prepared GEOS geometry pointer from the given shapely geometry."""
    cdef uintptr_t geos_geom = shapely_geom._geom
    return <GEOSPreparedGeometry *> geos_geom

@cython.boundscheck(False)
@cython.wraparound(False)
def two_points_intersect_geom(np.ndarray[DTYPE_t, ndim=3] latlon, geometry):
    """
    Example:
        import numpy as np
        import cartopy.feature as cfeature
        from shapely.ops import unary_union
        from shapely.prepared import prep
        land = prep(unary_union(list(cfeature.NaturalEarthFeature('physical', 'land', '50m').geometries())))
        latlon = np.array([
            [[0, 0], [0, 10]],
            [[0, 0], [0, -10]],
        ], dtype=float)
        two_points_intersect_geom(latlon, land)
    """
    cdef GEOSCoordSequence *coord_sequence
    cdef GEOSPreparedGeometry *geom1
    cdef GEOSGeometry *geom2
    cdef double lat, lon
    cdef int n_point_pairs = len(latlon)
    cdef int seqSize = 2
    cdef int seqDim = 2
    cdef int i, j
    cdef np.ndarray[np.uint8_t, ndim=1, cast=True] result = np.empty(n_point_pairs, dtype=np.uint8)

    if not isinstance(geometry, shapely.prepared.PreparedGeometry):
        geometry = shapely.prepared.prep(geometry)

    geos_handle = get_geos_context_handle()
    geom1 = geos_from_prepared(geometry)
    for i in range(n_point_pairs):
        coord_sequence = GEOSCoordSeq_create_r(geos_handle, seqSize, seqDim)
        for j in range(2):
            lat = latlon[i][j][0]
            lon = latlon[i][j][1]
            d = GEOSCoordSeq_setX_r(geos_handle, coord_sequence, j, lat)
            d = GEOSCoordSeq_setY_r(geos_handle, coord_sequence, j, lon)
        geom2 = GEOSGeom_createLineString_r(geos_handle, coord_sequence)
        result[i] = <np.uint8_t> GEOSPreparedIntersects_r(geos_handle, geom1, geom2)
    return result.view(dtype=np.bool_)

测试代码

geom = Polygon([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
land = prep(geom)

latlon = np.array(
    [
        [[-0.5, 0.5], [0.5, 0.5]],
        [[10, 10], [20, 20]],
    ],
    dtype=DTYPE_CYTHON,
)
ret = two_points_intersect_geom(latlon, land)
self.assertListEqual([True, False], list(ret))

排查情况

已确认段错误并非来自geos_handle、geom2或结果赋值操作,问题根源在geom1,怀疑获取预准备几何体GEOS指针的函数存在问题。查阅Shapely与GEOS的变更记录后未找到失效原因,且当前使用的是未弃用的_geom属性,而非已弃用的__geom__。


解决方案

Shapely 2.0对PreparedGeometry的内部结构做了核心变更:_geom属性不再指向GEOSPreparedGeometry指针,而是指向原始的GEOSGeometry对象。要获取预准备几何体的指针,需要访问新增的_prepared_geom内部属性。

修改geos_from_prepared函数

将原函数中的_geom替换为_prepared_geom:

cdef GEOSPreparedGeometry *geos_from_prepared(shapely_geom) except *:
    """Get the Prepared GEOS geometry pointer from the given shapely geometry."""
    cdef uintptr_t geos_geom = shapely_geom._prepared_geom
    return <GEOSPreparedGeometry *> geos_geom

额外注意事项

  • 必须确保输入的shapely_geom确实是PreparedGeometry对象,普通Geometry没有_prepared_geom属性,误访问会引发错误
  • _prepared_geom是Shapely 2.0的内部属性,虽目前未被弃用,但后续版本可能发生变化,建议优先使用Shapely提供的公共API,尽量避免直接操作内部指针

内容的提问来源于stack exchange,提问作者Tom McLean

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:57:38