-
-
Save CHANG-CHING-CHUNG/7f0df0c872918784e4b2b46672fa13a9 to your computer and use it in GitHub Desktop.
Django GEOS patch for macOS arm64
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def patch_geos_signatures(): | |
""" | |
Patch GEOS to function on macOS arm64 and presumably | |
other odd architectures by ensuring that call signatures | |
are explicit, and that Django 4 bugfixes are backported. | |
Should work on Django 2.2+, minimally tested, caveat emptor. | |
""" | |
import logging | |
from ctypes import POINTER, c_uint, c_int | |
from django.contrib.gis.geos import GeometryCollection, Polygon | |
from django.contrib.gis.geos import prototypes as capi | |
from django.contrib.gis.geos.prototypes import GEOM_PTR | |
from django.contrib.gis.geos.prototypes.geom import GeomOutput | |
from django.contrib.gis.geos.libgeos import geos_version, lgeos | |
from django.contrib.gis.geos.linestring import LineString | |
logger = logging.getLogger("geos_patch") | |
_geos_version = geos_version() | |
logger.debug("GEOS: %s %s", _geos_version, repr(lgeos)) | |
# Backport https://code.djangoproject.com/ticket/30274 | |
def new_linestring_iter(self): | |
for i in range(len(self)): | |
yield self[i] | |
LineString.__iter__ = new_linestring_iter | |
# macOS arm64 requires that we have explicit argtypes for cffi calls. | |
# Patch in argtypes for `create_polygon` and `create_collection`, | |
# and then ensure their prep functions do NOT use byref so that the | |
# arrays (`(GEOM_PTR * length)(...)`) auto-convert into `Geometry**`. | |
# create_empty_polygon doesn't need to be patched as it takes no args. | |
# Geometry* | |
# GEOSGeom_createPolygon_r(GEOSContextHandle_t extHandle, | |
# Geometry* shell, Geometry** holes, unsigned int nholes) | |
capi.create_polygon = GeomOutput( | |
"GEOSGeom_createPolygon", argtypes=[GEOM_PTR, POINTER(GEOM_PTR), c_uint] | |
) | |
# Geometry* | |
# GEOSGeom_createCollection_r(GEOSContextHandle_t extHandle, | |
# int type, Geometry** geoms, unsigned int ngeoms) | |
capi.create_collection = GeomOutput( | |
"GEOSGeom_createCollection", argtypes=[c_int, POINTER(GEOM_PTR), c_uint] | |
) | |
# The below implementations are taken directly from Django 2.2.25 source; | |
# the only changes are unwrapping calls to byref(). | |
def new_create_polygon(self, length, items): | |
# Instantiate LinearRing objects if necessary, but don't clone them yet | |
# _construct_ring will throw a TypeError if a parameter isn't a valid ring | |
# If we cloned the pointers here, we wouldn't be able to clean up | |
# in case of error. | |
if not length: | |
return capi.create_empty_polygon() | |
rings = [] | |
for r in items: | |
if isinstance(r, GEOM_PTR): | |
rings.append(r) | |
else: | |
rings.append(self._construct_ring(r)) | |
shell = self._clone(rings.pop(0)) | |
n_holes = length - 1 | |
if n_holes: | |
holes = (GEOM_PTR * n_holes)(*[self._clone(r) for r in rings]) | |
holes_param = holes | |
else: | |
holes_param = None | |
return capi.create_polygon(shell, holes_param, c_uint(n_holes)) | |
Polygon._create_polygon = new_create_polygon | |
# Need to patch to not call byref so that we can cast to a pointer | |
def new_create_collection(self, length, items): | |
# Creating the geometry pointer array. | |
geoms = (GEOM_PTR * length)( | |
*[ | |
# this is a little sloppy, but makes life easier | |
# allow GEOSGeometry types (python wrappers) or pointer types | |
capi.geom_clone(getattr(g, "ptr", g)) | |
for g in items | |
] | |
) | |
return capi.create_collection(c_int(self._typeid), geoms, c_uint(length)) | |
GeometryCollection._create_collection = new_create_collection |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment