before send to remote
This commit is contained in:
27
env/lib/python3.8/site-packages/django/contrib/gis/geos/LICENSE
vendored
Normal file
27
env/lib/python3.8/site-packages/django/contrib/gis/geos/LICENSE
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2007-2009 Justin Bronn
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of GEOSGeometry nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
18
env/lib/python3.8/site-packages/django/contrib/gis/geos/__init__.py
vendored
Normal file
18
env/lib/python3.8/site-packages/django/contrib/gis/geos/__init__.py
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
The GeoDjango GEOS module. Please consult the GeoDjango documentation
|
||||
for more details: https://docs.djangoproject.com/en/dev/ref/contrib/gis/geos/
|
||||
"""
|
||||
from .collections import ( # NOQA
|
||||
GeometryCollection,
|
||||
MultiLineString,
|
||||
MultiPoint,
|
||||
MultiPolygon,
|
||||
)
|
||||
from .error import GEOSException # NOQA
|
||||
from .factory import fromfile, fromstr # NOQA
|
||||
from .geometry import GEOSGeometry, hex_regex, wkt_regex # NOQA
|
||||
from .io import WKBReader, WKBWriter, WKTReader, WKTWriter # NOQA
|
||||
from .libgeos import geos_version # NOQA
|
||||
from .linestring import LinearRing, LineString # NOQA
|
||||
from .point import Point # NOQA
|
||||
from .polygon import Polygon # NOQA
|
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/base.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/base.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/collections.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/collections.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/coordseq.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/coordseq.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/error.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/error.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/factory.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/factory.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/geometry.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/geometry.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/io.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/io.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/libgeos.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/libgeos.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/linestring.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/linestring.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/mutable_list.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/mutable_list.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/point.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/point.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/polygon.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/polygon.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/prepared.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/__pycache__/prepared.cpython-38.pyc
vendored
Normal file
Binary file not shown.
6
env/lib/python3.8/site-packages/django/contrib/gis/geos/base.py
vendored
Normal file
6
env/lib/python3.8/site-packages/django/contrib/gis/geos/base.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.ptr import CPointerBase
|
||||
|
||||
|
||||
class GEOSBase(CPointerBase):
|
||||
null_ptr_exception_class = GEOSException
|
120
env/lib/python3.8/site-packages/django/contrib/gis/geos/collections.py
vendored
Normal file
120
env/lib/python3.8/site-packages/django/contrib/gis/geos/collections.py
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
This module houses the Geometry Collection objects:
|
||||
GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
|
||||
"""
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry, LinearGeometryMixin
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR
|
||||
from django.contrib.gis.geos.linestring import LinearRing, LineString
|
||||
from django.contrib.gis.geos.point import Point
|
||||
from django.contrib.gis.geos.polygon import Polygon
|
||||
|
||||
|
||||
class GeometryCollection(GEOSGeometry):
|
||||
_typeid = 7
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"Initialize a Geometry Collection from a sequence of Geometry objects."
|
||||
# Checking the arguments
|
||||
if len(args) == 1:
|
||||
# If only one geometry provided or a list of geometries is provided
|
||||
# in the first argument.
|
||||
if isinstance(args[0], (tuple, list)):
|
||||
init_geoms = args[0]
|
||||
else:
|
||||
init_geoms = args
|
||||
else:
|
||||
init_geoms = args
|
||||
|
||||
# Ensuring that only the permitted geometries are allowed in this collection
|
||||
# this is moved to list mixin super class
|
||||
self._check_allowed(init_geoms)
|
||||
|
||||
# Creating the geometry pointer array.
|
||||
collection = self._create_collection(len(init_geoms), init_geoms)
|
||||
super().__init__(collection, **kwargs)
|
||||
|
||||
def __iter__(self):
|
||||
"Iterate over each Geometry in the Collection."
|
||||
for i in range(len(self)):
|
||||
yield self[i]
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of geometries in this Collection."
|
||||
return self.num_geom
|
||||
|
||||
# ### Methods for compatibility with ListMixin ###
|
||||
def _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(self._typeid, geoms, length)
|
||||
|
||||
def _get_single_internal(self, index):
|
||||
return capi.get_geomn(self.ptr, index)
|
||||
|
||||
def _get_single_external(self, index):
|
||||
"Return the Geometry from this Collection at the given index (0-based)."
|
||||
# Checking the index and returning the corresponding GEOS geometry.
|
||||
return GEOSGeometry(
|
||||
capi.geom_clone(self._get_single_internal(index)), srid=self.srid
|
||||
)
|
||||
|
||||
def _set_list(self, length, items):
|
||||
"Create a new collection, and destroy the contents of the previous pointer."
|
||||
prev_ptr = self.ptr
|
||||
srid = self.srid
|
||||
self.ptr = self._create_collection(length, items)
|
||||
if srid:
|
||||
self.srid = srid
|
||||
capi.destroy_geom(prev_ptr)
|
||||
|
||||
_set_single = GEOSGeometry._set_single_rebuild
|
||||
_assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Return the KML for this Geometry Collection."
|
||||
return "<MultiGeometry>%s</MultiGeometry>" % "".join(g.kml for g in self)
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple of all the coordinates in this Geometry Collection"
|
||||
return tuple(g.tuple for g in self)
|
||||
|
||||
coords = tuple
|
||||
|
||||
|
||||
# MultiPoint, MultiLineString, and MultiPolygon class definitions.
|
||||
class MultiPoint(GeometryCollection):
|
||||
_allowed = Point
|
||||
_typeid = 4
|
||||
|
||||
|
||||
class MultiLineString(LinearGeometryMixin, GeometryCollection):
|
||||
_allowed = (LineString, LinearRing)
|
||||
_typeid = 5
|
||||
|
||||
|
||||
class MultiPolygon(GeometryCollection):
|
||||
_allowed = Polygon
|
||||
_typeid = 6
|
||||
|
||||
|
||||
# Setting the allowed types here since GeometryCollection is defined before
|
||||
# its subclasses.
|
||||
GeometryCollection._allowed = (
|
||||
Point,
|
||||
LineString,
|
||||
LinearRing,
|
||||
Polygon,
|
||||
MultiPoint,
|
||||
MultiLineString,
|
||||
MultiPolygon,
|
||||
)
|
220
env/lib/python3.8/site-packages/django/contrib/gis/geos/coordseq.py
vendored
Normal file
220
env/lib/python3.8/site-packages/django/contrib/gis/geos/coordseq.py
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
"""
|
||||
This module houses the GEOSCoordSeq object, which is used internally
|
||||
by GEOSGeometry to house the actual coordinates of the Point,
|
||||
LineString, and LinearRing geometries.
|
||||
"""
|
||||
from ctypes import byref, c_byte, c_double, c_uint
|
||||
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.base import GEOSBase
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.geos.libgeos import CS_PTR, geos_version_tuple
|
||||
from django.contrib.gis.shortcuts import numpy
|
||||
|
||||
|
||||
class GEOSCoordSeq(GEOSBase):
|
||||
"The internal representation of a list of coordinates inside a Geometry."
|
||||
|
||||
ptr_type = CS_PTR
|
||||
|
||||
def __init__(self, ptr, z=False):
|
||||
"Initialize from a GEOS pointer."
|
||||
if not isinstance(ptr, CS_PTR):
|
||||
raise TypeError("Coordinate sequence should initialize with a CS_PTR.")
|
||||
self._ptr = ptr
|
||||
self._z = z
|
||||
|
||||
def __iter__(self):
|
||||
"Iterate over each point in the coordinate sequence."
|
||||
for i in range(self.size):
|
||||
yield self[i]
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of points in the coordinate sequence."
|
||||
return self.size
|
||||
|
||||
def __str__(self):
|
||||
"Return the string representation of the coordinate sequence."
|
||||
return str(self.tuple)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Return the coordinate sequence value at the given index."
|
||||
self._checkindex(index)
|
||||
return self._point_getter(index)
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
"Set the coordinate sequence value at the given index."
|
||||
# Checking the input value
|
||||
if isinstance(value, (list, tuple)):
|
||||
pass
|
||||
elif numpy and isinstance(value, numpy.ndarray):
|
||||
pass
|
||||
else:
|
||||
raise TypeError(
|
||||
"Must set coordinate with a sequence (list, tuple, or numpy array)."
|
||||
)
|
||||
# Checking the dims of the input
|
||||
if self.dims == 3 and self._z:
|
||||
n_args = 3
|
||||
point_setter = self._set_point_3d
|
||||
else:
|
||||
n_args = 2
|
||||
point_setter = self._set_point_2d
|
||||
if len(value) != n_args:
|
||||
raise TypeError("Dimension of value does not match.")
|
||||
self._checkindex(index)
|
||||
point_setter(index, value)
|
||||
|
||||
# #### Internal Routines ####
|
||||
def _checkindex(self, index):
|
||||
"Check the given index."
|
||||
if not (0 <= index < self.size):
|
||||
raise IndexError("invalid GEOS Geometry index: %s" % index)
|
||||
|
||||
def _checkdim(self, dim):
|
||||
"Check the given dimension."
|
||||
if dim < 0 or dim > 2:
|
||||
raise GEOSException('invalid ordinate dimension "%d"' % dim)
|
||||
|
||||
def _get_x(self, index):
|
||||
return capi.cs_getx(self.ptr, index, byref(c_double()))
|
||||
|
||||
def _get_y(self, index):
|
||||
return capi.cs_gety(self.ptr, index, byref(c_double()))
|
||||
|
||||
def _get_z(self, index):
|
||||
return capi.cs_getz(self.ptr, index, byref(c_double()))
|
||||
|
||||
def _set_x(self, index, value):
|
||||
capi.cs_setx(self.ptr, index, value)
|
||||
|
||||
def _set_y(self, index, value):
|
||||
capi.cs_sety(self.ptr, index, value)
|
||||
|
||||
def _set_z(self, index, value):
|
||||
capi.cs_setz(self.ptr, index, value)
|
||||
|
||||
@property
|
||||
def _point_getter(self):
|
||||
return self._get_point_3d if self.dims == 3 and self._z else self._get_point_2d
|
||||
|
||||
def _get_point_2d(self, index):
|
||||
return (self._get_x(index), self._get_y(index))
|
||||
|
||||
def _get_point_3d(self, index):
|
||||
return (self._get_x(index), self._get_y(index), self._get_z(index))
|
||||
|
||||
def _set_point_2d(self, index, value):
|
||||
x, y = value
|
||||
self._set_x(index, x)
|
||||
self._set_y(index, y)
|
||||
|
||||
def _set_point_3d(self, index, value):
|
||||
x, y, z = value
|
||||
self._set_x(index, x)
|
||||
self._set_y(index, y)
|
||||
self._set_z(index, z)
|
||||
|
||||
# #### Ordinate getting and setting routines ####
|
||||
def getOrdinate(self, dimension, index):
|
||||
"Return the value for the given dimension and index."
|
||||
self._checkindex(index)
|
||||
self._checkdim(dimension)
|
||||
return capi.cs_getordinate(self.ptr, index, dimension, byref(c_double()))
|
||||
|
||||
def setOrdinate(self, dimension, index, value):
|
||||
"Set the value for the given dimension and index."
|
||||
self._checkindex(index)
|
||||
self._checkdim(dimension)
|
||||
capi.cs_setordinate(self.ptr, index, dimension, value)
|
||||
|
||||
def getX(self, index):
|
||||
"Get the X value at the index."
|
||||
return self.getOrdinate(0, index)
|
||||
|
||||
def setX(self, index, value):
|
||||
"Set X with the value at the given index."
|
||||
self.setOrdinate(0, index, value)
|
||||
|
||||
def getY(self, index):
|
||||
"Get the Y value at the given index."
|
||||
return self.getOrdinate(1, index)
|
||||
|
||||
def setY(self, index, value):
|
||||
"Set Y with the value at the given index."
|
||||
self.setOrdinate(1, index, value)
|
||||
|
||||
def getZ(self, index):
|
||||
"Get Z with the value at the given index."
|
||||
return self.getOrdinate(2, index)
|
||||
|
||||
def setZ(self, index, value):
|
||||
"Set Z with the value at the given index."
|
||||
self.setOrdinate(2, index, value)
|
||||
|
||||
# ### Dimensions ###
|
||||
@property
|
||||
def size(self):
|
||||
"Return the size of this coordinate sequence."
|
||||
return capi.cs_getsize(self.ptr, byref(c_uint()))
|
||||
|
||||
@property
|
||||
def dims(self):
|
||||
"Return the dimensions of this coordinate sequence."
|
||||
return capi.cs_getdims(self.ptr, byref(c_uint()))
|
||||
|
||||
@property
|
||||
def hasz(self):
|
||||
"""
|
||||
Return whether this coordinate sequence is 3D. This property value is
|
||||
inherited from the parent Geometry.
|
||||
"""
|
||||
return self._z
|
||||
|
||||
# ### Other Methods ###
|
||||
def clone(self):
|
||||
"Clone this coordinate sequence."
|
||||
return GEOSCoordSeq(capi.cs_clone(self.ptr), self.hasz)
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Return the KML representation for the coordinates."
|
||||
# Getting the substitution string depending on whether the coordinates have
|
||||
# a Z dimension.
|
||||
if self.hasz:
|
||||
substr = "%s,%s,%s "
|
||||
else:
|
||||
substr = "%s,%s,0 "
|
||||
return (
|
||||
"<coordinates>%s</coordinates>"
|
||||
% "".join(substr % self[i] for i in range(len(self))).strip()
|
||||
)
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple version of this coordinate sequence."
|
||||
n = self.size
|
||||
get_point = self._point_getter
|
||||
if n == 1:
|
||||
return get_point(0)
|
||||
return tuple(get_point(i) for i in range(n))
|
||||
|
||||
@property
|
||||
def is_counterclockwise(self):
|
||||
"""Return whether this coordinate sequence is counterclockwise."""
|
||||
if geos_version_tuple() < (3, 7):
|
||||
# A modified shoelace algorithm to determine polygon orientation.
|
||||
# See https://en.wikipedia.org/wiki/Shoelace_formula.
|
||||
area = 0.0
|
||||
n = len(self)
|
||||
for i in range(n):
|
||||
j = (i + 1) % n
|
||||
area += self[i][0] * self[j][1]
|
||||
area -= self[j][0] * self[i][1]
|
||||
return area > 0.0
|
||||
ret = c_byte()
|
||||
if not capi.cs_is_ccw(self.ptr, byref(ret)):
|
||||
raise GEOSException(
|
||||
'Error encountered in GEOS C function "%s".' % capi.cs_is_ccw.func_name
|
||||
)
|
||||
return ret.value == 1
|
3
env/lib/python3.8/site-packages/django/contrib/gis/geos/error.py
vendored
Normal file
3
env/lib/python3.8/site-packages/django/contrib/gis/geos/error.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
class GEOSException(Exception):
|
||||
"The base GEOS exception, indicates a GEOS-related error."
|
||||
pass
|
33
env/lib/python3.8/site-packages/django/contrib/gis/geos/factory.py
vendored
Normal file
33
env/lib/python3.8/site-packages/django/contrib/gis/geos/factory.py
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry, hex_regex, wkt_regex
|
||||
|
||||
|
||||
def fromfile(file_h):
|
||||
"""
|
||||
Given a string file name, returns a GEOSGeometry. The file may contain WKB,
|
||||
WKT, or HEX.
|
||||
"""
|
||||
# If given a file name, get a real handle.
|
||||
if isinstance(file_h, str):
|
||||
with open(file_h, "rb") as file_h:
|
||||
buf = file_h.read()
|
||||
else:
|
||||
buf = file_h.read()
|
||||
|
||||
# If we get WKB need to wrap in memoryview(), so run through regexes.
|
||||
if isinstance(buf, bytes):
|
||||
try:
|
||||
decoded = buf.decode()
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
else:
|
||||
if wkt_regex.match(decoded) or hex_regex.match(decoded):
|
||||
return GEOSGeometry(decoded)
|
||||
else:
|
||||
return GEOSGeometry(buf)
|
||||
|
||||
return GEOSGeometry(memoryview(buf))
|
||||
|
||||
|
||||
def fromstr(string, **kwargs):
|
||||
"Given a string value, return a GEOSGeometry object."
|
||||
return GEOSGeometry(string, **kwargs)
|
772
env/lib/python3.8/site-packages/django/contrib/gis/geos/geometry.py
vendored
Normal file
772
env/lib/python3.8/site-packages/django/contrib/gis/geos/geometry.py
vendored
Normal file
@@ -0,0 +1,772 @@
|
||||
"""
|
||||
This module contains the 'base' GEOSGeometry object -- all GEOS Geometries
|
||||
inherit from this object.
|
||||
"""
|
||||
import re
|
||||
from ctypes import addressof, byref, c_double
|
||||
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.geometry import hex_regex, json_regex, wkt_regex
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.base import GEOSBase
|
||||
from django.contrib.gis.geos.coordseq import GEOSCoordSeq
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR, geos_version_tuple
|
||||
from django.contrib.gis.geos.mutable_list import ListMixin
|
||||
from django.contrib.gis.geos.prepared import PreparedGeometry
|
||||
from django.contrib.gis.geos.prototypes.io import ewkb_w, wkb_r, wkb_w, wkt_r, wkt_w
|
||||
from django.utils.deconstruct import deconstructible
|
||||
from django.utils.encoding import force_bytes, force_str
|
||||
|
||||
|
||||
class GEOSGeometryBase(GEOSBase):
|
||||
|
||||
_GEOS_CLASSES = None
|
||||
|
||||
ptr_type = GEOM_PTR
|
||||
destructor = capi.destroy_geom
|
||||
has_cs = False # Only Point, LineString, LinearRing have coordinate sequences
|
||||
|
||||
def __init__(self, ptr, cls):
|
||||
self._ptr = ptr
|
||||
|
||||
# Setting the class type (e.g., Point, Polygon, etc.)
|
||||
if type(self) in (GEOSGeometryBase, GEOSGeometry):
|
||||
if cls is None:
|
||||
if GEOSGeometryBase._GEOS_CLASSES is None:
|
||||
# Inner imports avoid import conflicts with GEOSGeometry.
|
||||
from .collections import (
|
||||
GeometryCollection,
|
||||
MultiLineString,
|
||||
MultiPoint,
|
||||
MultiPolygon,
|
||||
)
|
||||
from .linestring import LinearRing, LineString
|
||||
from .point import Point
|
||||
from .polygon import Polygon
|
||||
|
||||
GEOSGeometryBase._GEOS_CLASSES = {
|
||||
0: Point,
|
||||
1: LineString,
|
||||
2: LinearRing,
|
||||
3: Polygon,
|
||||
4: MultiPoint,
|
||||
5: MultiLineString,
|
||||
6: MultiPolygon,
|
||||
7: GeometryCollection,
|
||||
}
|
||||
cls = GEOSGeometryBase._GEOS_CLASSES[self.geom_typeid]
|
||||
self.__class__ = cls
|
||||
self._post_init()
|
||||
|
||||
def _post_init(self):
|
||||
"Perform post-initialization setup."
|
||||
# Setting the coordinate sequence for the geometry (will be None on
|
||||
# geometries that do not have coordinate sequences)
|
||||
self._cs = (
|
||||
GEOSCoordSeq(capi.get_cs(self.ptr), self.hasz) if self.has_cs else None
|
||||
)
|
||||
|
||||
def __copy__(self):
|
||||
"""
|
||||
Return a clone because the copy of a GEOSGeometry may contain an
|
||||
invalid pointer location if the original is garbage collected.
|
||||
"""
|
||||
return self.clone()
|
||||
|
||||
def __deepcopy__(self, memodict):
|
||||
"""
|
||||
The `deepcopy` routine is used by the `Node` class of django.utils.tree;
|
||||
thus, the protocol routine needs to be implemented to return correct
|
||||
copies (clones) of these GEOS objects, which use C pointers.
|
||||
"""
|
||||
return self.clone()
|
||||
|
||||
def __str__(self):
|
||||
"EWKT is used for the string representation."
|
||||
return self.ewkt
|
||||
|
||||
def __repr__(self):
|
||||
"Short-hand representation because WKT may be very large."
|
||||
return "<%s object at %s>" % (self.geom_type, hex(addressof(self.ptr)))
|
||||
|
||||
# Pickling support
|
||||
def _to_pickle_wkb(self):
|
||||
return bytes(self.wkb)
|
||||
|
||||
def _from_pickle_wkb(self, wkb):
|
||||
return wkb_r().read(memoryview(wkb))
|
||||
|
||||
def __getstate__(self):
|
||||
# The pickled state is simply a tuple of the WKB (in string form)
|
||||
# and the SRID.
|
||||
return self._to_pickle_wkb(), self.srid
|
||||
|
||||
def __setstate__(self, state):
|
||||
# Instantiating from the tuple state that was pickled.
|
||||
wkb, srid = state
|
||||
ptr = self._from_pickle_wkb(wkb)
|
||||
if not ptr:
|
||||
raise GEOSException("Invalid Geometry loaded from pickled state.")
|
||||
self.ptr = ptr
|
||||
self._post_init()
|
||||
self.srid = srid
|
||||
|
||||
@classmethod
|
||||
def _from_wkb(cls, wkb):
|
||||
return wkb_r().read(wkb)
|
||||
|
||||
@staticmethod
|
||||
def from_ewkt(ewkt):
|
||||
ewkt = force_bytes(ewkt)
|
||||
srid = None
|
||||
parts = ewkt.split(b";", 1)
|
||||
if len(parts) == 2:
|
||||
srid_part, wkt = parts
|
||||
match = re.match(rb"SRID=(?P<srid>\-?\d+)", srid_part)
|
||||
if not match:
|
||||
raise ValueError("EWKT has invalid SRID part.")
|
||||
srid = int(match["srid"])
|
||||
else:
|
||||
wkt = ewkt
|
||||
if not wkt:
|
||||
raise ValueError("Expected WKT but got an empty string.")
|
||||
return GEOSGeometry(GEOSGeometry._from_wkt(wkt), srid=srid)
|
||||
|
||||
@staticmethod
|
||||
def _from_wkt(wkt):
|
||||
return wkt_r().read(wkt)
|
||||
|
||||
@classmethod
|
||||
def from_gml(cls, gml_string):
|
||||
return gdal.OGRGeometry.from_gml(gml_string).geos
|
||||
|
||||
# Comparison operators
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Equivalence testing, a Geometry may be compared with another Geometry
|
||||
or an EWKT representation.
|
||||
"""
|
||||
if isinstance(other, str):
|
||||
try:
|
||||
other = GEOSGeometry.from_ewkt(other)
|
||||
except (ValueError, GEOSException):
|
||||
return False
|
||||
return (
|
||||
isinstance(other, GEOSGeometry)
|
||||
and self.srid == other.srid
|
||||
and self.equals_exact(other)
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.srid, self.wkt))
|
||||
|
||||
# ### Geometry set-like operations ###
|
||||
# Thanks to Sean Gillies for inspiration:
|
||||
# http://lists.gispython.org/pipermail/community/2007-July/001034.html
|
||||
# g = g1 | g2
|
||||
def __or__(self, other):
|
||||
"Return the union of this Geometry and the other."
|
||||
return self.union(other)
|
||||
|
||||
# g = g1 & g2
|
||||
def __and__(self, other):
|
||||
"Return the intersection of this Geometry and the other."
|
||||
return self.intersection(other)
|
||||
|
||||
# g = g1 - g2
|
||||
def __sub__(self, other):
|
||||
"Return the difference this Geometry and the other."
|
||||
return self.difference(other)
|
||||
|
||||
# g = g1 ^ g2
|
||||
def __xor__(self, other):
|
||||
"Return the symmetric difference of this Geometry and the other."
|
||||
return self.sym_difference(other)
|
||||
|
||||
# #### Coordinate Sequence Routines ####
|
||||
@property
|
||||
def coord_seq(self):
|
||||
"Return a clone of the coordinate sequence for this Geometry."
|
||||
if self.has_cs:
|
||||
return self._cs.clone()
|
||||
|
||||
# #### Geometry Info ####
|
||||
@property
|
||||
def geom_type(self):
|
||||
"Return a string representing the Geometry type, e.g. 'Polygon'"
|
||||
return capi.geos_type(self.ptr).decode()
|
||||
|
||||
@property
|
||||
def geom_typeid(self):
|
||||
"Return an integer representing the Geometry type."
|
||||
return capi.geos_typeid(self.ptr)
|
||||
|
||||
@property
|
||||
def num_geom(self):
|
||||
"Return the number of geometries in the Geometry."
|
||||
return capi.get_num_geoms(self.ptr)
|
||||
|
||||
@property
|
||||
def num_coords(self):
|
||||
"Return the number of coordinates in the Geometry."
|
||||
return capi.get_num_coords(self.ptr)
|
||||
|
||||
@property
|
||||
def num_points(self):
|
||||
"Return the number points, or coordinates, in the Geometry."
|
||||
return self.num_coords
|
||||
|
||||
@property
|
||||
def dims(self):
|
||||
"Return the dimension of this Geometry (0=point, 1=line, 2=surface)."
|
||||
return capi.get_dims(self.ptr)
|
||||
|
||||
def normalize(self, clone=False):
|
||||
"""
|
||||
Convert this Geometry to normal form (or canonical form).
|
||||
If the `clone` keyword is set, then the geometry is not modified and a
|
||||
normalized clone of the geometry is returned instead.
|
||||
"""
|
||||
if clone:
|
||||
clone = self.clone()
|
||||
capi.geos_normalize(clone.ptr)
|
||||
return clone
|
||||
capi.geos_normalize(self.ptr)
|
||||
|
||||
def make_valid(self):
|
||||
"""
|
||||
Attempt to create a valid representation of a given invalid geometry
|
||||
without losing any of the input vertices.
|
||||
"""
|
||||
if geos_version_tuple() < (3, 8):
|
||||
raise GEOSException("GEOSGeometry.make_valid() requires GEOS >= 3.8.0.")
|
||||
return GEOSGeometry(capi.geos_makevalid(self.ptr), srid=self.srid)
|
||||
|
||||
# #### Unary predicates ####
|
||||
@property
|
||||
def empty(self):
|
||||
"""
|
||||
Return a boolean indicating whether the set of points in this Geometry
|
||||
are empty.
|
||||
"""
|
||||
return capi.geos_isempty(self.ptr)
|
||||
|
||||
@property
|
||||
def hasz(self):
|
||||
"Return whether the geometry has a 3D dimension."
|
||||
return capi.geos_hasz(self.ptr)
|
||||
|
||||
@property
|
||||
def ring(self):
|
||||
"Return whether or not the geometry is a ring."
|
||||
return capi.geos_isring(self.ptr)
|
||||
|
||||
@property
|
||||
def simple(self):
|
||||
"Return false if the Geometry isn't simple."
|
||||
return capi.geos_issimple(self.ptr)
|
||||
|
||||
@property
|
||||
def valid(self):
|
||||
"Test the validity of this Geometry."
|
||||
return capi.geos_isvalid(self.ptr)
|
||||
|
||||
@property
|
||||
def valid_reason(self):
|
||||
"""
|
||||
Return a string containing the reason for any invalidity.
|
||||
"""
|
||||
return capi.geos_isvalidreason(self.ptr).decode()
|
||||
|
||||
# #### Binary predicates. ####
|
||||
def contains(self, other):
|
||||
"Return true if other.within(this) returns true."
|
||||
return capi.geos_contains(self.ptr, other.ptr)
|
||||
|
||||
def covers(self, other):
|
||||
"""
|
||||
Return True if the DE-9IM Intersection Matrix for the two geometries is
|
||||
T*****FF*, *T****FF*, ***T**FF*, or ****T*FF*. If either geometry is
|
||||
empty, return False.
|
||||
"""
|
||||
return capi.geos_covers(self.ptr, other.ptr)
|
||||
|
||||
def crosses(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*T****** (for a point and a curve,a point and an area or a line and
|
||||
an area) 0******** (for two curves).
|
||||
"""
|
||||
return capi.geos_crosses(self.ptr, other.ptr)
|
||||
|
||||
def disjoint(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is FF*FF****.
|
||||
"""
|
||||
return capi.geos_disjoint(self.ptr, other.ptr)
|
||||
|
||||
def equals(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*F**FFF*.
|
||||
"""
|
||||
return capi.geos_equals(self.ptr, other.ptr)
|
||||
|
||||
def equals_exact(self, other, tolerance=0):
|
||||
"""
|
||||
Return true if the two Geometries are exactly equal, up to a
|
||||
specified tolerance.
|
||||
"""
|
||||
return capi.geos_equalsexact(self.ptr, other.ptr, float(tolerance))
|
||||
|
||||
def intersects(self, other):
|
||||
"Return true if disjoint return false."
|
||||
return capi.geos_intersects(self.ptr, other.ptr)
|
||||
|
||||
def overlaps(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves).
|
||||
"""
|
||||
return capi.geos_overlaps(self.ptr, other.ptr)
|
||||
|
||||
def relate_pattern(self, other, pattern):
|
||||
"""
|
||||
Return true if the elements in the DE-9IM intersection matrix for the
|
||||
two Geometries match the elements in pattern.
|
||||
"""
|
||||
if not isinstance(pattern, str) or len(pattern) > 9:
|
||||
raise GEOSException("invalid intersection matrix pattern")
|
||||
return capi.geos_relatepattern(self.ptr, other.ptr, force_bytes(pattern))
|
||||
|
||||
def touches(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is FT*******, F**T***** or F***T****.
|
||||
"""
|
||||
return capi.geos_touches(self.ptr, other.ptr)
|
||||
|
||||
def within(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*F**F***.
|
||||
"""
|
||||
return capi.geos_within(self.ptr, other.ptr)
|
||||
|
||||
# #### SRID Routines ####
|
||||
@property
|
||||
def srid(self):
|
||||
"Get the SRID for the geometry. Return None if no SRID is set."
|
||||
s = capi.geos_get_srid(self.ptr)
|
||||
if s == 0:
|
||||
return None
|
||||
else:
|
||||
return s
|
||||
|
||||
@srid.setter
|
||||
def srid(self, srid):
|
||||
"Set the SRID for the geometry."
|
||||
capi.geos_set_srid(self.ptr, 0 if srid is None else srid)
|
||||
|
||||
# #### Output Routines ####
|
||||
@property
|
||||
def ewkt(self):
|
||||
"""
|
||||
Return the EWKT (SRID + WKT) of the Geometry.
|
||||
"""
|
||||
srid = self.srid
|
||||
return "SRID=%s;%s" % (srid, self.wkt) if srid else self.wkt
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
"Return the WKT (Well-Known Text) representation of this Geometry."
|
||||
return wkt_w(dim=3 if self.hasz else 2, trim=True).write(self).decode()
|
||||
|
||||
@property
|
||||
def hex(self):
|
||||
"""
|
||||
Return the WKB of this Geometry in hexadecimal form. Please note
|
||||
that the SRID is not included in this representation because it is not
|
||||
a part of the OGC specification (use the `hexewkb` property instead).
|
||||
"""
|
||||
# A possible faster, all-python, implementation:
|
||||
# str(self.wkb).encode('hex')
|
||||
return wkb_w(dim=3 if self.hasz else 2).write_hex(self)
|
||||
|
||||
@property
|
||||
def hexewkb(self):
|
||||
"""
|
||||
Return the EWKB of this Geometry in hexadecimal form. This is an
|
||||
extension of the WKB specification that includes SRID value that are
|
||||
a part of this geometry.
|
||||
"""
|
||||
return ewkb_w(dim=3 if self.hasz else 2).write_hex(self)
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""
|
||||
Return GeoJSON representation of this Geometry.
|
||||
"""
|
||||
return self.ogr.json
|
||||
|
||||
geojson = json
|
||||
|
||||
@property
|
||||
def wkb(self):
|
||||
"""
|
||||
Return the WKB (Well-Known Binary) representation of this Geometry
|
||||
as a Python buffer. SRID and Z values are not included, use the
|
||||
`ewkb` property instead.
|
||||
"""
|
||||
return wkb_w(3 if self.hasz else 2).write(self)
|
||||
|
||||
@property
|
||||
def ewkb(self):
|
||||
"""
|
||||
Return the EWKB representation of this Geometry as a Python buffer.
|
||||
This is an extension of the WKB specification that includes any SRID
|
||||
value that are a part of this geometry.
|
||||
"""
|
||||
return ewkb_w(3 if self.hasz else 2).write(self)
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Return the KML representation of this Geometry."
|
||||
gtype = self.geom_type
|
||||
return "<%s>%s</%s>" % (gtype, self.coord_seq.kml, gtype)
|
||||
|
||||
@property
|
||||
def prepared(self):
|
||||
"""
|
||||
Return a PreparedGeometry corresponding to this geometry -- it is
|
||||
optimized for the contains, intersects, and covers operations.
|
||||
"""
|
||||
return PreparedGeometry(self)
|
||||
|
||||
# #### GDAL-specific output routines ####
|
||||
def _ogr_ptr(self):
|
||||
return gdal.OGRGeometry._from_wkb(self.wkb)
|
||||
|
||||
@property
|
||||
def ogr(self):
|
||||
"Return the OGR Geometry for this Geometry."
|
||||
return gdal.OGRGeometry(self._ogr_ptr(), self.srs)
|
||||
|
||||
@property
|
||||
def srs(self):
|
||||
"Return the OSR SpatialReference for SRID of this Geometry."
|
||||
if self.srid:
|
||||
try:
|
||||
return gdal.SpatialReference(self.srid)
|
||||
except (gdal.GDALException, gdal.SRSException):
|
||||
pass
|
||||
return None
|
||||
|
||||
@property
|
||||
def crs(self):
|
||||
"Alias for `srs` property."
|
||||
return self.srs
|
||||
|
||||
def transform(self, ct, clone=False):
|
||||
"""
|
||||
Requires GDAL. Transform the geometry according to the given
|
||||
transformation object, which may be an integer SRID, and WKT or
|
||||
PROJ string. By default, transform the geometry in-place and return
|
||||
nothing. However if the `clone` keyword is set, don't modify the
|
||||
geometry and return a transformed clone instead.
|
||||
"""
|
||||
srid = self.srid
|
||||
|
||||
if ct == srid:
|
||||
# short-circuit where source & dest SRIDs match
|
||||
if clone:
|
||||
return self.clone()
|
||||
else:
|
||||
return
|
||||
|
||||
if isinstance(ct, gdal.CoordTransform):
|
||||
# We don't care about SRID because CoordTransform presupposes
|
||||
# source SRS.
|
||||
srid = None
|
||||
elif srid is None or srid < 0:
|
||||
raise GEOSException("Calling transform() with no SRID set is not supported")
|
||||
|
||||
# Creating an OGR Geometry, which is then transformed.
|
||||
g = gdal.OGRGeometry(self._ogr_ptr(), srid)
|
||||
g.transform(ct)
|
||||
# Getting a new GEOS pointer
|
||||
ptr = g._geos_ptr()
|
||||
if clone:
|
||||
# User wants a cloned transformed geometry returned.
|
||||
return GEOSGeometry(ptr, srid=g.srid)
|
||||
if ptr:
|
||||
# Reassigning pointer, and performing post-initialization setup
|
||||
# again due to the reassignment.
|
||||
capi.destroy_geom(self.ptr)
|
||||
self.ptr = ptr
|
||||
self._post_init()
|
||||
self.srid = g.srid
|
||||
else:
|
||||
raise GEOSException("Transformed WKB was invalid.")
|
||||
|
||||
# #### Topology Routines ####
|
||||
def _topology(self, gptr):
|
||||
"Return Geometry from the given pointer."
|
||||
return GEOSGeometry(gptr, srid=self.srid)
|
||||
|
||||
@property
|
||||
def boundary(self):
|
||||
"Return the boundary as a newly allocated Geometry object."
|
||||
return self._topology(capi.geos_boundary(self.ptr))
|
||||
|
||||
def buffer(self, width, quadsegs=8):
|
||||
"""
|
||||
Return a geometry that represents all points whose distance from this
|
||||
Geometry is less than or equal to distance. Calculations are in the
|
||||
Spatial Reference System of this Geometry. The optional third parameter sets
|
||||
the number of segment used to approximate a quarter circle (defaults to 8).
|
||||
(Text from PostGIS documentation at ch. 6.1.3)
|
||||
"""
|
||||
return self._topology(capi.geos_buffer(self.ptr, width, quadsegs))
|
||||
|
||||
def buffer_with_style(
|
||||
self, width, quadsegs=8, end_cap_style=1, join_style=1, mitre_limit=5.0
|
||||
):
|
||||
"""
|
||||
Same as buffer() but allows customizing the style of the buffer.
|
||||
|
||||
End cap style can be round (1), flat (2), or square (3).
|
||||
Join style can be round (1), mitre (2), or bevel (3).
|
||||
Mitre ratio limit only affects mitered join style.
|
||||
"""
|
||||
return self._topology(
|
||||
capi.geos_bufferwithstyle(
|
||||
self.ptr, width, quadsegs, end_cap_style, join_style, mitre_limit
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def centroid(self):
|
||||
"""
|
||||
The centroid is equal to the centroid of the set of component Geometries
|
||||
of highest dimension (since the lower-dimension geometries contribute zero
|
||||
"weight" to the centroid).
|
||||
"""
|
||||
return self._topology(capi.geos_centroid(self.ptr))
|
||||
|
||||
@property
|
||||
def convex_hull(self):
|
||||
"""
|
||||
Return the smallest convex Polygon that contains all the points
|
||||
in the Geometry.
|
||||
"""
|
||||
return self._topology(capi.geos_convexhull(self.ptr))
|
||||
|
||||
def difference(self, other):
|
||||
"""
|
||||
Return a Geometry representing the points making up this Geometry
|
||||
that do not make up other.
|
||||
"""
|
||||
return self._topology(capi.geos_difference(self.ptr, other.ptr))
|
||||
|
||||
@property
|
||||
def envelope(self):
|
||||
"Return the envelope for this geometry (a polygon)."
|
||||
return self._topology(capi.geos_envelope(self.ptr))
|
||||
|
||||
def intersection(self, other):
|
||||
"Return a Geometry representing the points shared by this Geometry and other."
|
||||
return self._topology(capi.geos_intersection(self.ptr, other.ptr))
|
||||
|
||||
@property
|
||||
def point_on_surface(self):
|
||||
"Compute an interior point of this Geometry."
|
||||
return self._topology(capi.geos_pointonsurface(self.ptr))
|
||||
|
||||
def relate(self, other):
|
||||
"Return the DE-9IM intersection matrix for this Geometry and the other."
|
||||
return capi.geos_relate(self.ptr, other.ptr).decode()
|
||||
|
||||
def simplify(self, tolerance=0.0, preserve_topology=False):
|
||||
"""
|
||||
Return the Geometry, simplified using the Douglas-Peucker algorithm
|
||||
to the specified tolerance (higher tolerance => less points). If no
|
||||
tolerance provided, defaults to 0.
|
||||
|
||||
By default, don't preserve topology - e.g. polygons can be split,
|
||||
collapse to lines or disappear holes can be created or disappear, and
|
||||
lines can cross. By specifying preserve_topology=True, the result will
|
||||
have the same dimension and number of components as the input. This is
|
||||
significantly slower.
|
||||
"""
|
||||
if preserve_topology:
|
||||
return self._topology(capi.geos_preservesimplify(self.ptr, tolerance))
|
||||
else:
|
||||
return self._topology(capi.geos_simplify(self.ptr, tolerance))
|
||||
|
||||
def sym_difference(self, other):
|
||||
"""
|
||||
Return a set combining the points in this Geometry not in other,
|
||||
and the points in other not in this Geometry.
|
||||
"""
|
||||
return self._topology(capi.geos_symdifference(self.ptr, other.ptr))
|
||||
|
||||
@property
|
||||
def unary_union(self):
|
||||
"Return the union of all the elements of this geometry."
|
||||
return self._topology(capi.geos_unary_union(self.ptr))
|
||||
|
||||
def union(self, other):
|
||||
"Return a Geometry representing all the points in this Geometry and other."
|
||||
return self._topology(capi.geos_union(self.ptr, other.ptr))
|
||||
|
||||
# #### Other Routines ####
|
||||
@property
|
||||
def area(self):
|
||||
"Return the area of the Geometry."
|
||||
return capi.geos_area(self.ptr, byref(c_double()))
|
||||
|
||||
def distance(self, other):
|
||||
"""
|
||||
Return the distance between the closest points on this Geometry
|
||||
and the other. Units will be in those of the coordinate system of
|
||||
the Geometry.
|
||||
"""
|
||||
if not isinstance(other, GEOSGeometry):
|
||||
raise TypeError("distance() works only on other GEOS Geometries.")
|
||||
return capi.geos_distance(self.ptr, other.ptr, byref(c_double()))
|
||||
|
||||
@property
|
||||
def extent(self):
|
||||
"""
|
||||
Return the extent of this geometry as a 4-tuple, consisting of
|
||||
(xmin, ymin, xmax, ymax).
|
||||
"""
|
||||
from .point import Point
|
||||
|
||||
env = self.envelope
|
||||
if isinstance(env, Point):
|
||||
xmin, ymin = env.tuple
|
||||
xmax, ymax = xmin, ymin
|
||||
else:
|
||||
xmin, ymin = env[0][0]
|
||||
xmax, ymax = env[0][2]
|
||||
return (xmin, ymin, xmax, ymax)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
"""
|
||||
Return the length of this Geometry (e.g., 0 for point, or the
|
||||
circumference of a Polygon).
|
||||
"""
|
||||
return capi.geos_length(self.ptr, byref(c_double()))
|
||||
|
||||
def clone(self):
|
||||
"Clone this Geometry."
|
||||
return GEOSGeometry(capi.geom_clone(self.ptr))
|
||||
|
||||
|
||||
class LinearGeometryMixin:
|
||||
"""
|
||||
Used for LineString and MultiLineString.
|
||||
"""
|
||||
|
||||
def interpolate(self, distance):
|
||||
return self._topology(capi.geos_interpolate(self.ptr, distance))
|
||||
|
||||
def interpolate_normalized(self, distance):
|
||||
return self._topology(capi.geos_interpolate_normalized(self.ptr, distance))
|
||||
|
||||
def project(self, point):
|
||||
from .point import Point
|
||||
|
||||
if not isinstance(point, Point):
|
||||
raise TypeError("locate_point argument must be a Point")
|
||||
return capi.geos_project(self.ptr, point.ptr)
|
||||
|
||||
def project_normalized(self, point):
|
||||
from .point import Point
|
||||
|
||||
if not isinstance(point, Point):
|
||||
raise TypeError("locate_point argument must be a Point")
|
||||
return capi.geos_project_normalized(self.ptr, point.ptr)
|
||||
|
||||
@property
|
||||
def merged(self):
|
||||
"""
|
||||
Return the line merge of this Geometry.
|
||||
"""
|
||||
return self._topology(capi.geos_linemerge(self.ptr))
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
"""
|
||||
Return whether or not this Geometry is closed.
|
||||
"""
|
||||
return capi.geos_isclosed(self.ptr)
|
||||
|
||||
|
||||
@deconstructible
|
||||
class GEOSGeometry(GEOSGeometryBase, ListMixin):
|
||||
"A class that, generally, encapsulates a GEOS geometry."
|
||||
|
||||
def __init__(self, geo_input, srid=None):
|
||||
"""
|
||||
The base constructor for GEOS geometry objects. It may take the
|
||||
following inputs:
|
||||
|
||||
* strings:
|
||||
- WKT
|
||||
- HEXEWKB (a PostGIS-specific canonical form)
|
||||
- GeoJSON (requires GDAL)
|
||||
* buffer:
|
||||
- WKB
|
||||
|
||||
The `srid` keyword specifies the Source Reference Identifier (SRID)
|
||||
number for this Geometry. If not provided, it defaults to None.
|
||||
"""
|
||||
input_srid = None
|
||||
if isinstance(geo_input, bytes):
|
||||
geo_input = force_str(geo_input)
|
||||
if isinstance(geo_input, str):
|
||||
wkt_m = wkt_regex.match(geo_input)
|
||||
if wkt_m:
|
||||
# Handle WKT input.
|
||||
if wkt_m["srid"]:
|
||||
input_srid = int(wkt_m["srid"])
|
||||
g = self._from_wkt(force_bytes(wkt_m["wkt"]))
|
||||
elif hex_regex.match(geo_input):
|
||||
# Handle HEXEWKB input.
|
||||
g = wkb_r().read(force_bytes(geo_input))
|
||||
elif json_regex.match(geo_input):
|
||||
# Handle GeoJSON input.
|
||||
ogr = gdal.OGRGeometry.from_json(geo_input)
|
||||
g = ogr._geos_ptr()
|
||||
input_srid = ogr.srid
|
||||
else:
|
||||
raise ValueError("String input unrecognized as WKT EWKT, and HEXEWKB.")
|
||||
elif isinstance(geo_input, GEOM_PTR):
|
||||
# When the input is a pointer to a geometry (GEOM_PTR).
|
||||
g = geo_input
|
||||
elif isinstance(geo_input, memoryview):
|
||||
# When the input is a buffer (WKB).
|
||||
g = wkb_r().read(geo_input)
|
||||
elif isinstance(geo_input, GEOSGeometry):
|
||||
g = capi.geom_clone(geo_input.ptr)
|
||||
else:
|
||||
raise TypeError("Improper geometry input type: %s" % type(geo_input))
|
||||
|
||||
if not g:
|
||||
raise GEOSException("Could not initialize GEOS Geometry with given input.")
|
||||
|
||||
input_srid = input_srid or capi.geos_get_srid(g) or None
|
||||
if input_srid and srid and input_srid != srid:
|
||||
raise ValueError("Input geometry already has SRID: %d." % input_srid)
|
||||
|
||||
super().__init__(g, None)
|
||||
# Set the SRID, if given.
|
||||
srid = input_srid or srid
|
||||
if srid and isinstance(srid, int):
|
||||
self.srid = srid
|
27
env/lib/python3.8/site-packages/django/contrib/gis/geos/io.py
vendored
Normal file
27
env/lib/python3.8/site-packages/django/contrib/gis/geos/io.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
Module that holds classes for performing I/O operations on GEOS geometry
|
||||
objects. Specifically, this has Python implementations of WKB/WKT
|
||||
reader and writer classes.
|
||||
"""
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry
|
||||
from django.contrib.gis.geos.prototypes.io import (
|
||||
WKBWriter,
|
||||
WKTWriter,
|
||||
_WKBReader,
|
||||
_WKTReader,
|
||||
)
|
||||
|
||||
__all__ = ["WKBWriter", "WKTWriter", "WKBReader", "WKTReader"]
|
||||
|
||||
|
||||
# Public classes for (WKB|WKT)Reader, which return GEOSGeometry
|
||||
class WKBReader(_WKBReader):
|
||||
def read(self, wkb):
|
||||
"Return a GEOSGeometry for the given WKB buffer."
|
||||
return GEOSGeometry(super().read(wkb))
|
||||
|
||||
|
||||
class WKTReader(_WKTReader):
|
||||
def read(self, wkt):
|
||||
"Return a GEOSGeometry for the given WKT string."
|
||||
return GEOSGeometry(super().read(wkt))
|
174
env/lib/python3.8/site-packages/django/contrib/gis/geos/libgeos.py
vendored
Normal file
174
env/lib/python3.8/site-packages/django/contrib/gis/geos/libgeos.py
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
"""
|
||||
This module houses the ctypes initialization procedures, as well
|
||||
as the notice and error handler function callbacks (get called
|
||||
when an error occurs in GEOS).
|
||||
|
||||
This module also houses GEOS Pointer utilities, including
|
||||
get_pointer_arr(), and GEOM_PTR.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
from ctypes import CDLL, CFUNCTYPE, POINTER, Structure, c_char_p
|
||||
from ctypes.util import find_library
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.functional import SimpleLazyObject, cached_property
|
||||
from django.utils.version import get_version_tuple
|
||||
|
||||
logger = logging.getLogger("django.contrib.gis")
|
||||
|
||||
|
||||
def load_geos():
|
||||
# Custom library path set?
|
||||
try:
|
||||
from django.conf import settings
|
||||
|
||||
lib_path = settings.GEOS_LIBRARY_PATH
|
||||
except (AttributeError, ImportError, ImproperlyConfigured, OSError):
|
||||
lib_path = None
|
||||
|
||||
# Setting the appropriate names for the GEOS-C library.
|
||||
if lib_path:
|
||||
lib_names = None
|
||||
elif os.name == "nt":
|
||||
# Windows NT libraries
|
||||
lib_names = ["geos_c", "libgeos_c-1"]
|
||||
elif os.name == "posix":
|
||||
# *NIX libraries
|
||||
lib_names = ["geos_c", "GEOS"]
|
||||
else:
|
||||
raise ImportError('Unsupported OS "%s"' % os.name)
|
||||
|
||||
# Using the ctypes `find_library` utility to find the path to the GEOS
|
||||
# shared library. This is better than manually specifying each library name
|
||||
# and extension (e.g., libgeos_c.[so|so.1|dylib].).
|
||||
if lib_names:
|
||||
for lib_name in lib_names:
|
||||
lib_path = find_library(lib_name)
|
||||
if lib_path is not None:
|
||||
break
|
||||
|
||||
# No GEOS library could be found.
|
||||
if lib_path is None:
|
||||
raise ImportError(
|
||||
'Could not find the GEOS library (tried "%s"). '
|
||||
"Try setting GEOS_LIBRARY_PATH in your settings." % '", "'.join(lib_names)
|
||||
)
|
||||
# Getting the GEOS C library. The C interface (CDLL) is used for
|
||||
# both *NIX and Windows.
|
||||
# See the GEOS C API source code for more details on the library function calls:
|
||||
# https://libgeos.org/doxygen/geos__c_8h_source.html
|
||||
_lgeos = CDLL(lib_path)
|
||||
# Here we set up the prototypes for the initGEOS_r and finishGEOS_r
|
||||
# routines. These functions aren't actually called until they are
|
||||
# attached to a GEOS context handle -- this actually occurs in
|
||||
# geos/prototypes/threadsafe.py.
|
||||
_lgeos.initGEOS_r.restype = CONTEXT_PTR
|
||||
_lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR]
|
||||
# Set restype for compatibility across 32 and 64-bit platforms.
|
||||
_lgeos.GEOSversion.restype = c_char_p
|
||||
return _lgeos
|
||||
|
||||
|
||||
# The notice and error handler C function callback definitions.
|
||||
# Supposed to mimic the GEOS message handler (C below):
|
||||
# typedef void (*GEOSMessageHandler)(const char *fmt, ...);
|
||||
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||
|
||||
|
||||
def notice_h(fmt, lst):
|
||||
fmt, lst = fmt.decode(), lst.decode()
|
||||
try:
|
||||
warn_msg = fmt % lst
|
||||
except TypeError:
|
||||
warn_msg = fmt
|
||||
logger.warning("GEOS_NOTICE: %s\n", warn_msg)
|
||||
|
||||
|
||||
notice_h = NOTICEFUNC(notice_h)
|
||||
|
||||
ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||
|
||||
|
||||
def error_h(fmt, lst):
|
||||
fmt, lst = fmt.decode(), lst.decode()
|
||||
try:
|
||||
err_msg = fmt % lst
|
||||
except TypeError:
|
||||
err_msg = fmt
|
||||
logger.error("GEOS_ERROR: %s\n", err_msg)
|
||||
|
||||
|
||||
error_h = ERRORFUNC(error_h)
|
||||
|
||||
# #### GEOS Geometry C data structures, and utility functions. ####
|
||||
|
||||
|
||||
# Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR
|
||||
class GEOSGeom_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class GEOSPrepGeom_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class GEOSCoordSeq_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class GEOSContextHandle_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
# Pointers to opaque GEOS geometry structures.
|
||||
GEOM_PTR = POINTER(GEOSGeom_t)
|
||||
PREPGEOM_PTR = POINTER(GEOSPrepGeom_t)
|
||||
CS_PTR = POINTER(GEOSCoordSeq_t)
|
||||
CONTEXT_PTR = POINTER(GEOSContextHandle_t)
|
||||
|
||||
|
||||
lgeos = SimpleLazyObject(load_geos)
|
||||
|
||||
|
||||
class GEOSFuncFactory:
|
||||
"""
|
||||
Lazy loading of GEOS functions.
|
||||
"""
|
||||
|
||||
argtypes = None
|
||||
restype = None
|
||||
errcheck = None
|
||||
|
||||
def __init__(self, func_name, *, restype=None, errcheck=None, argtypes=None):
|
||||
self.func_name = func_name
|
||||
if restype is not None:
|
||||
self.restype = restype
|
||||
if errcheck is not None:
|
||||
self.errcheck = errcheck
|
||||
if argtypes is not None:
|
||||
self.argtypes = argtypes
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.func(*args)
|
||||
|
||||
@cached_property
|
||||
def func(self):
|
||||
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
|
||||
|
||||
func = GEOSFunc(self.func_name)
|
||||
func.argtypes = self.argtypes or []
|
||||
func.restype = self.restype
|
||||
if self.errcheck:
|
||||
func.errcheck = self.errcheck
|
||||
return func
|
||||
|
||||
|
||||
def geos_version():
|
||||
"""Return the string version of the GEOS library."""
|
||||
return lgeos.GEOSversion()
|
||||
|
||||
|
||||
def geos_version_tuple():
|
||||
"""Return the GEOS version as a tuple (major, minor, subminor)."""
|
||||
return get_version_tuple(geos_version().decode())
|
193
env/lib/python3.8/site-packages/django/contrib/gis/geos/linestring.py
vendored
Normal file
193
env/lib/python3.8/site-packages/django/contrib/gis/geos/linestring.py
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.coordseq import GEOSCoordSeq
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry, LinearGeometryMixin
|
||||
from django.contrib.gis.geos.point import Point
|
||||
from django.contrib.gis.shortcuts import numpy
|
||||
|
||||
|
||||
class LineString(LinearGeometryMixin, GEOSGeometry):
|
||||
_init_func = capi.create_linestring
|
||||
_minlength = 2
|
||||
has_cs = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Initialize on the given sequence -- may take lists, tuples, NumPy arrays
|
||||
of X,Y pairs, or Point objects. If Point objects are used, ownership is
|
||||
_not_ transferred to the LineString object.
|
||||
|
||||
Examples:
|
||||
ls = LineString((1, 1), (2, 2))
|
||||
ls = LineString([(1, 1), (2, 2)])
|
||||
ls = LineString(array([(1, 1), (2, 2)]))
|
||||
ls = LineString(Point(1, 1), Point(2, 2))
|
||||
"""
|
||||
# If only one argument provided, set the coords array appropriately
|
||||
if len(args) == 1:
|
||||
coords = args[0]
|
||||
else:
|
||||
coords = args
|
||||
|
||||
if not (
|
||||
isinstance(coords, (tuple, list))
|
||||
or numpy
|
||||
and isinstance(coords, numpy.ndarray)
|
||||
):
|
||||
raise TypeError("Invalid initialization input for LineStrings.")
|
||||
|
||||
# If SRID was passed in with the keyword arguments
|
||||
srid = kwargs.get("srid")
|
||||
|
||||
ncoords = len(coords)
|
||||
if not ncoords:
|
||||
super().__init__(self._init_func(None), srid=srid)
|
||||
return
|
||||
|
||||
if ncoords < self._minlength:
|
||||
raise ValueError(
|
||||
"%s requires at least %d points, got %s."
|
||||
% (
|
||||
self.__class__.__name__,
|
||||
self._minlength,
|
||||
ncoords,
|
||||
)
|
||||
)
|
||||
|
||||
numpy_coords = not isinstance(coords, (tuple, list))
|
||||
if numpy_coords:
|
||||
shape = coords.shape # Using numpy's shape.
|
||||
if len(shape) != 2:
|
||||
raise TypeError("Too many dimensions.")
|
||||
self._checkdim(shape[1])
|
||||
ndim = shape[1]
|
||||
else:
|
||||
# Getting the number of coords and the number of dimensions -- which
|
||||
# must stay the same, e.g., no LineString((1, 2), (1, 2, 3)).
|
||||
ndim = None
|
||||
# Incrementing through each of the coordinates and verifying
|
||||
for coord in coords:
|
||||
if not isinstance(coord, (tuple, list, Point)):
|
||||
raise TypeError(
|
||||
"Each coordinate should be a sequence (list or tuple)"
|
||||
)
|
||||
|
||||
if ndim is None:
|
||||
ndim = len(coord)
|
||||
self._checkdim(ndim)
|
||||
elif len(coord) != ndim:
|
||||
raise TypeError("Dimension mismatch.")
|
||||
|
||||
# Creating a coordinate sequence object because it is easier to
|
||||
# set the points using its methods.
|
||||
cs = GEOSCoordSeq(capi.create_cs(ncoords, ndim), z=bool(ndim == 3))
|
||||
point_setter = cs._set_point_3d if ndim == 3 else cs._set_point_2d
|
||||
|
||||
for i in range(ncoords):
|
||||
if numpy_coords:
|
||||
point_coords = coords[i, :]
|
||||
elif isinstance(coords[i], Point):
|
||||
point_coords = coords[i].tuple
|
||||
else:
|
||||
point_coords = coords[i]
|
||||
point_setter(i, point_coords)
|
||||
|
||||
# Calling the base geometry initialization with the returned pointer
|
||||
# from the function.
|
||||
super().__init__(self._init_func(cs.ptr), srid=srid)
|
||||
|
||||
def __iter__(self):
|
||||
"Allow iteration over this LineString."
|
||||
for i in range(len(self)):
|
||||
yield self[i]
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of points in this LineString."
|
||||
return len(self._cs)
|
||||
|
||||
def _get_single_external(self, index):
|
||||
return self._cs[index]
|
||||
|
||||
_get_single_internal = _get_single_external
|
||||
|
||||
def _set_list(self, length, items):
|
||||
ndim = self._cs.dims
|
||||
hasz = self._cs.hasz # I don't understand why these are different
|
||||
srid = self.srid
|
||||
|
||||
# create a new coordinate sequence and populate accordingly
|
||||
cs = GEOSCoordSeq(capi.create_cs(length, ndim), z=hasz)
|
||||
for i, c in enumerate(items):
|
||||
cs[i] = c
|
||||
|
||||
ptr = self._init_func(cs.ptr)
|
||||
if ptr:
|
||||
capi.destroy_geom(self.ptr)
|
||||
self.ptr = ptr
|
||||
if srid is not None:
|
||||
self.srid = srid
|
||||
self._post_init()
|
||||
else:
|
||||
# can this happen?
|
||||
raise GEOSException("Geometry resulting from slice deletion was invalid.")
|
||||
|
||||
def _set_single(self, index, value):
|
||||
self._cs[index] = value
|
||||
|
||||
def _checkdim(self, dim):
|
||||
if dim not in (2, 3):
|
||||
raise TypeError("Dimension mismatch.")
|
||||
|
||||
# #### Sequence Properties ####
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple version of the geometry from the coordinate sequence."
|
||||
return self._cs.tuple
|
||||
|
||||
coords = tuple
|
||||
|
||||
def _listarr(self, func):
|
||||
"""
|
||||
Return a sequence (list) corresponding with the given function.
|
||||
Return a numpy array if possible.
|
||||
"""
|
||||
lst = [func(i) for i in range(len(self))]
|
||||
if numpy:
|
||||
return numpy.array(lst) # ARRRR!
|
||||
else:
|
||||
return lst
|
||||
|
||||
@property
|
||||
def array(self):
|
||||
"Return a numpy array for the LineString."
|
||||
return self._listarr(self._cs.__getitem__)
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"Return a list or numpy array of the X variable."
|
||||
return self._listarr(self._cs.getX)
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"Return a list or numpy array of the Y variable."
|
||||
return self._listarr(self._cs.getY)
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
"Return a list or numpy array of the Z variable."
|
||||
if not self.hasz:
|
||||
return None
|
||||
else:
|
||||
return self._listarr(self._cs.getZ)
|
||||
|
||||
|
||||
# LinearRings are LineStrings used within Polygons.
|
||||
class LinearRing(LineString):
|
||||
_minlength = 4
|
||||
_init_func = capi.create_linearring
|
||||
|
||||
@property
|
||||
def is_counterclockwise(self):
|
||||
if self.empty:
|
||||
raise ValueError("Orientation of an empty LinearRing cannot be determined.")
|
||||
return self._cs.is_counterclockwise
|
314
env/lib/python3.8/site-packages/django/contrib/gis/geos/mutable_list.py
vendored
Normal file
314
env/lib/python3.8/site-packages/django/contrib/gis/geos/mutable_list.py
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
# Copyright (c) 2008-2009 Aryeh Leib Taurog, all rights reserved.
|
||||
# Released under the New BSD license.
|
||||
"""
|
||||
This module contains a base type which provides list-style mutations
|
||||
without specific data storage methods.
|
||||
|
||||
See also http://static.aryehleib.com/oldsite/MutableLists.html
|
||||
|
||||
Author: Aryeh Leib Taurog.
|
||||
"""
|
||||
from functools import total_ordering
|
||||
|
||||
|
||||
@total_ordering
|
||||
class ListMixin:
|
||||
"""
|
||||
A base class which provides complete list interface.
|
||||
Derived classes must call ListMixin's __init__() function
|
||||
and implement the following:
|
||||
|
||||
function _get_single_external(self, i):
|
||||
Return single item with index i for general use.
|
||||
The index i will always satisfy 0 <= i < len(self).
|
||||
|
||||
function _get_single_internal(self, i):
|
||||
Same as above, but for use within the class [Optional]
|
||||
Note that if _get_single_internal and _get_single_internal return
|
||||
different types of objects, _set_list must distinguish
|
||||
between the two and handle each appropriately.
|
||||
|
||||
function _set_list(self, length, items):
|
||||
Recreate the entire object.
|
||||
|
||||
NOTE: items may be a generator which calls _get_single_internal.
|
||||
Therefore, it is necessary to cache the values in a temporary:
|
||||
temp = list(items)
|
||||
before clobbering the original storage.
|
||||
|
||||
function _set_single(self, i, value):
|
||||
Set the single item at index i to value [Optional]
|
||||
If left undefined, all mutations will result in rebuilding
|
||||
the object using _set_list.
|
||||
|
||||
function __len__(self):
|
||||
Return the length
|
||||
|
||||
int _minlength:
|
||||
The minimum legal length [Optional]
|
||||
|
||||
int _maxlength:
|
||||
The maximum legal length [Optional]
|
||||
|
||||
type or tuple _allowed:
|
||||
A type or tuple of allowed item types [Optional]
|
||||
"""
|
||||
|
||||
_minlength = 0
|
||||
_maxlength = None
|
||||
|
||||
# ### Python initialization and special list interface methods ###
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if not hasattr(self, "_get_single_internal"):
|
||||
self._get_single_internal = self._get_single_external
|
||||
|
||||
if not hasattr(self, "_set_single"):
|
||||
self._set_single = self._set_single_rebuild
|
||||
self._assign_extended_slice = self._assign_extended_slice_rebuild
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Get the item(s) at the specified index/slice."
|
||||
if isinstance(index, slice):
|
||||
return [
|
||||
self._get_single_external(i) for i in range(*index.indices(len(self)))
|
||||
]
|
||||
else:
|
||||
index = self._checkindex(index)
|
||||
return self._get_single_external(index)
|
||||
|
||||
def __delitem__(self, index):
|
||||
"Delete the item(s) at the specified index/slice."
|
||||
if not isinstance(index, (int, slice)):
|
||||
raise TypeError("%s is not a legal index" % index)
|
||||
|
||||
# calculate new length and dimensions
|
||||
origLen = len(self)
|
||||
if isinstance(index, int):
|
||||
index = self._checkindex(index)
|
||||
indexRange = [index]
|
||||
else:
|
||||
indexRange = range(*index.indices(origLen))
|
||||
|
||||
newLen = origLen - len(indexRange)
|
||||
newItems = (
|
||||
self._get_single_internal(i) for i in range(origLen) if i not in indexRange
|
||||
)
|
||||
|
||||
self._rebuild(newLen, newItems)
|
||||
|
||||
def __setitem__(self, index, val):
|
||||
"Set the item(s) at the specified index/slice."
|
||||
if isinstance(index, slice):
|
||||
self._set_slice(index, val)
|
||||
else:
|
||||
index = self._checkindex(index)
|
||||
self._check_allowed((val,))
|
||||
self._set_single(index, val)
|
||||
|
||||
# ### Special methods for arithmetic operations ###
|
||||
def __add__(self, other):
|
||||
"add another list-like object"
|
||||
return self.__class__([*self, *other])
|
||||
|
||||
def __radd__(self, other):
|
||||
"add to another list-like object"
|
||||
return other.__class__([*other, *self])
|
||||
|
||||
def __iadd__(self, other):
|
||||
"add another list-like object to self"
|
||||
self.extend(other)
|
||||
return self
|
||||
|
||||
def __mul__(self, n):
|
||||
"multiply"
|
||||
return self.__class__(list(self) * n)
|
||||
|
||||
def __rmul__(self, n):
|
||||
"multiply"
|
||||
return self.__class__(list(self) * n)
|
||||
|
||||
def __imul__(self, n):
|
||||
"multiply"
|
||||
if n <= 0:
|
||||
del self[:]
|
||||
else:
|
||||
cache = list(self)
|
||||
for i in range(n - 1):
|
||||
self.extend(cache)
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
olen = len(other)
|
||||
for i in range(olen):
|
||||
try:
|
||||
c = self[i] == other[i]
|
||||
except IndexError:
|
||||
# self must be shorter
|
||||
return False
|
||||
if not c:
|
||||
return False
|
||||
return len(self) == olen
|
||||
|
||||
def __lt__(self, other):
|
||||
olen = len(other)
|
||||
for i in range(olen):
|
||||
try:
|
||||
c = self[i] < other[i]
|
||||
except IndexError:
|
||||
# self must be shorter
|
||||
return True
|
||||
if c:
|
||||
return c
|
||||
elif other[i] < self[i]:
|
||||
return False
|
||||
return len(self) < olen
|
||||
|
||||
# ### Public list interface Methods ###
|
||||
# ## Non-mutating ##
|
||||
def count(self, val):
|
||||
"Standard list count method"
|
||||
count = 0
|
||||
for i in self:
|
||||
if val == i:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def index(self, val):
|
||||
"Standard list index method"
|
||||
for i in range(0, len(self)):
|
||||
if self[i] == val:
|
||||
return i
|
||||
raise ValueError("%s not found in object" % val)
|
||||
|
||||
# ## Mutating ##
|
||||
def append(self, val):
|
||||
"Standard list append method"
|
||||
self[len(self) :] = [val]
|
||||
|
||||
def extend(self, vals):
|
||||
"Standard list extend method"
|
||||
self[len(self) :] = vals
|
||||
|
||||
def insert(self, index, val):
|
||||
"Standard list insert method"
|
||||
if not isinstance(index, int):
|
||||
raise TypeError("%s is not a legal index" % index)
|
||||
self[index:index] = [val]
|
||||
|
||||
def pop(self, index=-1):
|
||||
"Standard list pop method"
|
||||
result = self[index]
|
||||
del self[index]
|
||||
return result
|
||||
|
||||
def remove(self, val):
|
||||
"Standard list remove method"
|
||||
del self[self.index(val)]
|
||||
|
||||
def reverse(self):
|
||||
"Standard list reverse method"
|
||||
self[:] = self[-1::-1]
|
||||
|
||||
def sort(self, key=None, reverse=False):
|
||||
"Standard list sort method"
|
||||
self[:] = sorted(self, key=key, reverse=reverse)
|
||||
|
||||
# ### Private routines ###
|
||||
def _rebuild(self, newLen, newItems):
|
||||
if newLen and newLen < self._minlength:
|
||||
raise ValueError("Must have at least %d items" % self._minlength)
|
||||
if self._maxlength is not None and newLen > self._maxlength:
|
||||
raise ValueError("Cannot have more than %d items" % self._maxlength)
|
||||
|
||||
self._set_list(newLen, newItems)
|
||||
|
||||
def _set_single_rebuild(self, index, value):
|
||||
self._set_slice(slice(index, index + 1, 1), [value])
|
||||
|
||||
def _checkindex(self, index):
|
||||
length = len(self)
|
||||
if 0 <= index < length:
|
||||
return index
|
||||
if -length <= index < 0:
|
||||
return index + length
|
||||
raise IndexError("invalid index: %s" % index)
|
||||
|
||||
def _check_allowed(self, items):
|
||||
if hasattr(self, "_allowed"):
|
||||
if False in [isinstance(val, self._allowed) for val in items]:
|
||||
raise TypeError("Invalid type encountered in the arguments.")
|
||||
|
||||
def _set_slice(self, index, values):
|
||||
"Assign values to a slice of the object"
|
||||
try:
|
||||
valueList = list(values)
|
||||
except TypeError:
|
||||
raise TypeError("can only assign an iterable to a slice")
|
||||
|
||||
self._check_allowed(valueList)
|
||||
|
||||
origLen = len(self)
|
||||
start, stop, step = index.indices(origLen)
|
||||
|
||||
# CAREFUL: index.step and step are not the same!
|
||||
# step will never be None
|
||||
if index.step is None:
|
||||
self._assign_simple_slice(start, stop, valueList)
|
||||
else:
|
||||
self._assign_extended_slice(start, stop, step, valueList)
|
||||
|
||||
def _assign_extended_slice_rebuild(self, start, stop, step, valueList):
|
||||
"Assign an extended slice by rebuilding entire list"
|
||||
indexList = range(start, stop, step)
|
||||
# extended slice, only allow assigning slice of same size
|
||||
if len(valueList) != len(indexList):
|
||||
raise ValueError(
|
||||
"attempt to assign sequence of size %d "
|
||||
"to extended slice of size %d" % (len(valueList), len(indexList))
|
||||
)
|
||||
|
||||
# we're not changing the length of the sequence
|
||||
newLen = len(self)
|
||||
newVals = dict(zip(indexList, valueList))
|
||||
|
||||
def newItems():
|
||||
for i in range(newLen):
|
||||
if i in newVals:
|
||||
yield newVals[i]
|
||||
else:
|
||||
yield self._get_single_internal(i)
|
||||
|
||||
self._rebuild(newLen, newItems())
|
||||
|
||||
def _assign_extended_slice(self, start, stop, step, valueList):
|
||||
"Assign an extended slice by re-assigning individual items"
|
||||
indexList = range(start, stop, step)
|
||||
# extended slice, only allow assigning slice of same size
|
||||
if len(valueList) != len(indexList):
|
||||
raise ValueError(
|
||||
"attempt to assign sequence of size %d "
|
||||
"to extended slice of size %d" % (len(valueList), len(indexList))
|
||||
)
|
||||
|
||||
for i, val in zip(indexList, valueList):
|
||||
self._set_single(i, val)
|
||||
|
||||
def _assign_simple_slice(self, start, stop, valueList):
|
||||
"Assign a simple slice; Can assign slice of any length"
|
||||
origLen = len(self)
|
||||
stop = max(start, stop)
|
||||
newLen = origLen - stop + start + len(valueList)
|
||||
|
||||
def newItems():
|
||||
for i in range(origLen + 1):
|
||||
if i == start:
|
||||
yield from valueList
|
||||
|
||||
if i < origLen:
|
||||
if i < start or i >= stop:
|
||||
yield self._get_single_internal(i)
|
||||
|
||||
self._rebuild(newLen, newItems())
|
162
env/lib/python3.8/site-packages/django/contrib/gis/geos/point.py
vendored
Normal file
162
env/lib/python3.8/site-packages/django/contrib/gis/geos/point.py
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
from ctypes import c_uint
|
||||
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry
|
||||
|
||||
|
||||
class Point(GEOSGeometry):
|
||||
_minlength = 2
|
||||
_maxlength = 3
|
||||
has_cs = True
|
||||
|
||||
def __init__(self, x=None, y=None, z=None, srid=None):
|
||||
"""
|
||||
The Point object may be initialized with either a tuple, or individual
|
||||
parameters.
|
||||
|
||||
For example:
|
||||
>>> p = Point((5, 23)) # 2D point, passed in as a tuple
|
||||
>>> p = Point(5, 23, 8) # 3D point, passed in with individual parameters
|
||||
"""
|
||||
if x is None:
|
||||
coords = []
|
||||
elif isinstance(x, (tuple, list)):
|
||||
# Here a tuple or list was passed in under the `x` parameter.
|
||||
coords = x
|
||||
elif isinstance(x, (float, int)) and isinstance(y, (float, int)):
|
||||
# Here X, Y, and (optionally) Z were passed in individually, as parameters.
|
||||
if isinstance(z, (float, int)):
|
||||
coords = [x, y, z]
|
||||
else:
|
||||
coords = [x, y]
|
||||
else:
|
||||
raise TypeError("Invalid parameters given for Point initialization.")
|
||||
|
||||
point = self._create_point(len(coords), coords)
|
||||
|
||||
# Initializing using the address returned from the GEOS
|
||||
# createPoint factory.
|
||||
super().__init__(point, srid=srid)
|
||||
|
||||
def _to_pickle_wkb(self):
|
||||
return None if self.empty else super()._to_pickle_wkb()
|
||||
|
||||
def _from_pickle_wkb(self, wkb):
|
||||
return self._create_empty() if wkb is None else super()._from_pickle_wkb(wkb)
|
||||
|
||||
def _ogr_ptr(self):
|
||||
return (
|
||||
gdal.geometries.Point._create_empty() if self.empty else super()._ogr_ptr()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _create_empty(cls):
|
||||
return cls._create_point(None, None)
|
||||
|
||||
@classmethod
|
||||
def _create_point(cls, ndim, coords):
|
||||
"""
|
||||
Create a coordinate sequence, set X, Y, [Z], and create point
|
||||
"""
|
||||
if not ndim:
|
||||
return capi.create_point(None)
|
||||
|
||||
if ndim < 2 or ndim > 3:
|
||||
raise TypeError("Invalid point dimension: %s" % ndim)
|
||||
|
||||
cs = capi.create_cs(c_uint(1), c_uint(ndim))
|
||||
i = iter(coords)
|
||||
capi.cs_setx(cs, 0, next(i))
|
||||
capi.cs_sety(cs, 0, next(i))
|
||||
if ndim == 3:
|
||||
capi.cs_setz(cs, 0, next(i))
|
||||
|
||||
return capi.create_point(cs)
|
||||
|
||||
def _set_list(self, length, items):
|
||||
ptr = self._create_point(length, items)
|
||||
if ptr:
|
||||
srid = self.srid
|
||||
capi.destroy_geom(self.ptr)
|
||||
self._ptr = ptr
|
||||
if srid is not None:
|
||||
self.srid = srid
|
||||
self._post_init()
|
||||
else:
|
||||
# can this happen?
|
||||
raise GEOSException("Geometry resulting from slice deletion was invalid.")
|
||||
|
||||
def _set_single(self, index, value):
|
||||
self._cs.setOrdinate(index, 0, value)
|
||||
|
||||
def __iter__(self):
|
||||
"Iterate over coordinates of this Point."
|
||||
for i in range(len(self)):
|
||||
yield self[i]
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of dimensions for this Point (either 0, 2 or 3)."
|
||||
if self.empty:
|
||||
return 0
|
||||
if self.hasz:
|
||||
return 3
|
||||
else:
|
||||
return 2
|
||||
|
||||
def _get_single_external(self, index):
|
||||
if index == 0:
|
||||
return self.x
|
||||
elif index == 1:
|
||||
return self.y
|
||||
elif index == 2:
|
||||
return self.z
|
||||
|
||||
_get_single_internal = _get_single_external
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"Return the X component of the Point."
|
||||
return self._cs.getOrdinate(0, 0)
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
"Set the X component of the Point."
|
||||
self._cs.setOrdinate(0, 0, value)
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"Return the Y component of the Point."
|
||||
return self._cs.getOrdinate(1, 0)
|
||||
|
||||
@y.setter
|
||||
def y(self, value):
|
||||
"Set the Y component of the Point."
|
||||
self._cs.setOrdinate(1, 0, value)
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
"Return the Z component of the Point."
|
||||
return self._cs.getOrdinate(2, 0) if self.hasz else None
|
||||
|
||||
@z.setter
|
||||
def z(self, value):
|
||||
"Set the Z component of the Point."
|
||||
if not self.hasz:
|
||||
raise GEOSException("Cannot set Z on 2D Point.")
|
||||
self._cs.setOrdinate(2, 0, value)
|
||||
|
||||
# ### Tuple setting and retrieval routines. ###
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple of the point."
|
||||
return self._cs.tuple
|
||||
|
||||
@tuple.setter
|
||||
def tuple(self, tup):
|
||||
"Set the coordinates of the point with the given tuple."
|
||||
self._cs[0] = tup
|
||||
|
||||
# The tuple and coords properties
|
||||
coords = tuple
|
190
env/lib/python3.8/site-packages/django/contrib/gis/geos/polygon.py
vendored
Normal file
190
env/lib/python3.8/site-packages/django/contrib/gis/geos/polygon.py
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR
|
||||
from django.contrib.gis.geos.linestring import LinearRing
|
||||
|
||||
|
||||
class Polygon(GEOSGeometry):
|
||||
_minlength = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Initialize on an exterior ring and a sequence of holes (both
|
||||
instances may be either LinearRing instances, or a tuple/list
|
||||
that may be constructed into a LinearRing).
|
||||
|
||||
Examples of initialization, where shell, hole1, and hole2 are
|
||||
valid LinearRing geometries:
|
||||
>>> from django.contrib.gis.geos import LinearRing, Polygon
|
||||
>>> shell = hole1 = hole2 = LinearRing()
|
||||
>>> poly = Polygon(shell, hole1, hole2)
|
||||
>>> poly = Polygon(shell, (hole1, hole2))
|
||||
|
||||
>>> # Example where a tuple parameters are used:
|
||||
>>> poly = Polygon(((0, 0), (0, 10), (10, 10), (10, 0), (0, 0)),
|
||||
... ((4, 4), (4, 6), (6, 6), (6, 4), (4, 4)))
|
||||
"""
|
||||
if not args:
|
||||
super().__init__(self._create_polygon(0, None), **kwargs)
|
||||
return
|
||||
|
||||
# Getting the ext_ring and init_holes parameters from the argument list
|
||||
ext_ring, *init_holes = args
|
||||
n_holes = len(init_holes)
|
||||
|
||||
# If initialized as Polygon(shell, (LinearRing, LinearRing))
|
||||
# [for backward-compatibility]
|
||||
if n_holes == 1 and isinstance(init_holes[0], (tuple, list)):
|
||||
if not init_holes[0]:
|
||||
init_holes = ()
|
||||
n_holes = 0
|
||||
elif isinstance(init_holes[0][0], LinearRing):
|
||||
init_holes = init_holes[0]
|
||||
n_holes = len(init_holes)
|
||||
|
||||
polygon = self._create_polygon(n_holes + 1, [ext_ring, *init_holes])
|
||||
super().__init__(polygon, **kwargs)
|
||||
|
||||
def __iter__(self):
|
||||
"Iterate over each ring in the polygon."
|
||||
for i in range(len(self)):
|
||||
yield self[i]
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of rings in this Polygon."
|
||||
return self.num_interior_rings + 1
|
||||
|
||||
@classmethod
|
||||
def from_bbox(cls, bbox):
|
||||
"Construct a Polygon from a bounding box (4-tuple)."
|
||||
x0, y0, x1, y1 = bbox
|
||||
for z in bbox:
|
||||
if not isinstance(z, (float, int)):
|
||||
return GEOSGeometry(
|
||||
"POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))"
|
||||
% (x0, y0, x0, y1, x1, y1, x1, y0, x0, y0)
|
||||
)
|
||||
return Polygon(((x0, y0), (x0, y1), (x1, y1), (x1, y0), (x0, y0)))
|
||||
|
||||
# ### These routines are needed for list-like operation w/ListMixin ###
|
||||
def _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_param = (GEOM_PTR * n_holes)(*[self._clone(r) for r in rings])
|
||||
else:
|
||||
holes_param = None
|
||||
|
||||
return capi.create_polygon(shell, holes_param, n_holes)
|
||||
|
||||
def _clone(self, g):
|
||||
if isinstance(g, GEOM_PTR):
|
||||
return capi.geom_clone(g)
|
||||
else:
|
||||
return capi.geom_clone(g.ptr)
|
||||
|
||||
def _construct_ring(
|
||||
self,
|
||||
param,
|
||||
msg=(
|
||||
"Parameter must be a sequence of LinearRings or objects that can "
|
||||
"initialize to LinearRings"
|
||||
),
|
||||
):
|
||||
"Try to construct a ring from the given parameter."
|
||||
if isinstance(param, LinearRing):
|
||||
return param
|
||||
try:
|
||||
ring = LinearRing(param)
|
||||
return ring
|
||||
except TypeError:
|
||||
raise TypeError(msg)
|
||||
|
||||
def _set_list(self, length, items):
|
||||
# Getting the current pointer, replacing with the newly constructed
|
||||
# geometry, and destroying the old geometry.
|
||||
prev_ptr = self.ptr
|
||||
srid = self.srid
|
||||
self.ptr = self._create_polygon(length, items)
|
||||
if srid:
|
||||
self.srid = srid
|
||||
capi.destroy_geom(prev_ptr)
|
||||
|
||||
def _get_single_internal(self, index):
|
||||
"""
|
||||
Return the ring at the specified index. The first index, 0, will
|
||||
always return the exterior ring. Indices > 0 will return the
|
||||
interior ring at the given index (e.g., poly[1] and poly[2] would
|
||||
return the first and second interior ring, respectively).
|
||||
|
||||
CAREFUL: Internal/External are not the same as Interior/Exterior!
|
||||
Return a pointer from the existing geometries for use internally by the
|
||||
object's methods. _get_single_external() returns a clone of the same
|
||||
geometry for use by external code.
|
||||
"""
|
||||
if index == 0:
|
||||
return capi.get_extring(self.ptr)
|
||||
else:
|
||||
# Getting the interior ring, have to subtract 1 from the index.
|
||||
return capi.get_intring(self.ptr, index - 1)
|
||||
|
||||
def _get_single_external(self, index):
|
||||
return GEOSGeometry(
|
||||
capi.geom_clone(self._get_single_internal(index)), srid=self.srid
|
||||
)
|
||||
|
||||
_set_single = GEOSGeometry._set_single_rebuild
|
||||
_assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
|
||||
|
||||
# #### Polygon Properties ####
|
||||
@property
|
||||
def num_interior_rings(self):
|
||||
"Return the number of interior rings."
|
||||
# Getting the number of rings
|
||||
return capi.get_nrings(self.ptr)
|
||||
|
||||
def _get_ext_ring(self):
|
||||
"Get the exterior ring of the Polygon."
|
||||
return self[0]
|
||||
|
||||
def _set_ext_ring(self, ring):
|
||||
"Set the exterior ring of the Polygon."
|
||||
self[0] = ring
|
||||
|
||||
# Properties for the exterior ring/shell.
|
||||
exterior_ring = property(_get_ext_ring, _set_ext_ring)
|
||||
shell = exterior_ring
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Get the tuple for each ring in this Polygon."
|
||||
return tuple(self[i].tuple for i in range(len(self)))
|
||||
|
||||
coords = tuple
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Return the KML representation of this Polygon."
|
||||
inner_kml = "".join(
|
||||
"<innerBoundaryIs>%s</innerBoundaryIs>" % self[i + 1].kml
|
||||
for i in range(self.num_interior_rings)
|
||||
)
|
||||
return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % (
|
||||
self[0].kml,
|
||||
inner_kml,
|
||||
)
|
51
env/lib/python3.8/site-packages/django/contrib/gis/geos/prepared.py
vendored
Normal file
51
env/lib/python3.8/site-packages/django/contrib/gis/geos/prepared.py
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
from .base import GEOSBase
|
||||
from .prototypes import prepared as capi
|
||||
|
||||
|
||||
class PreparedGeometry(GEOSBase):
|
||||
"""
|
||||
A geometry that is prepared for performing certain operations.
|
||||
At the moment this includes the contains covers, and intersects
|
||||
operations.
|
||||
"""
|
||||
|
||||
ptr_type = capi.PREPGEOM_PTR
|
||||
destructor = capi.prepared_destroy
|
||||
|
||||
def __init__(self, geom):
|
||||
# Keeping a reference to the original geometry object to prevent it
|
||||
# from being garbage collected which could then crash the prepared one
|
||||
# See #21662
|
||||
self._base_geom = geom
|
||||
from .geometry import GEOSGeometry
|
||||
|
||||
if not isinstance(geom, GEOSGeometry):
|
||||
raise TypeError
|
||||
self.ptr = capi.geos_prepare(geom.ptr)
|
||||
|
||||
def contains(self, other):
|
||||
return capi.prepared_contains(self.ptr, other.ptr)
|
||||
|
||||
def contains_properly(self, other):
|
||||
return capi.prepared_contains_properly(self.ptr, other.ptr)
|
||||
|
||||
def covers(self, other):
|
||||
return capi.prepared_covers(self.ptr, other.ptr)
|
||||
|
||||
def intersects(self, other):
|
||||
return capi.prepared_intersects(self.ptr, other.ptr)
|
||||
|
||||
def crosses(self, other):
|
||||
return capi.prepared_crosses(self.ptr, other.ptr)
|
||||
|
||||
def disjoint(self, other):
|
||||
return capi.prepared_disjoint(self.ptr, other.ptr)
|
||||
|
||||
def overlaps(self, other):
|
||||
return capi.prepared_overlaps(self.ptr, other.ptr)
|
||||
|
||||
def touches(self, other):
|
||||
return capi.prepared_touches(self.ptr, other.ptr)
|
||||
|
||||
def within(self, other):
|
||||
return capi.prepared_within(self.ptr, other.ptr)
|
66
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__init__.py
vendored
Normal file
66
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__init__.py
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
This module contains all of the GEOS ctypes function prototypes. Each
|
||||
prototype handles the interaction between the GEOS library and Python
|
||||
via ctypes.
|
||||
"""
|
||||
|
||||
from django.contrib.gis.geos.prototypes.coordseq import ( # NOQA
|
||||
create_cs,
|
||||
cs_clone,
|
||||
cs_getdims,
|
||||
cs_getordinate,
|
||||
cs_getsize,
|
||||
cs_getx,
|
||||
cs_gety,
|
||||
cs_getz,
|
||||
cs_is_ccw,
|
||||
cs_setordinate,
|
||||
cs_setx,
|
||||
cs_sety,
|
||||
cs_setz,
|
||||
get_cs,
|
||||
)
|
||||
from django.contrib.gis.geos.prototypes.geom import ( # NOQA
|
||||
create_collection,
|
||||
create_empty_polygon,
|
||||
create_linearring,
|
||||
create_linestring,
|
||||
create_point,
|
||||
create_polygon,
|
||||
destroy_geom,
|
||||
geom_clone,
|
||||
geos_get_srid,
|
||||
geos_makevalid,
|
||||
geos_normalize,
|
||||
geos_set_srid,
|
||||
geos_type,
|
||||
geos_typeid,
|
||||
get_dims,
|
||||
get_extring,
|
||||
get_geomn,
|
||||
get_intring,
|
||||
get_nrings,
|
||||
get_num_coords,
|
||||
get_num_geoms,
|
||||
)
|
||||
from django.contrib.gis.geos.prototypes.misc import * # NOQA
|
||||
from django.contrib.gis.geos.prototypes.predicates import ( # NOQA
|
||||
geos_contains,
|
||||
geos_covers,
|
||||
geos_crosses,
|
||||
geos_disjoint,
|
||||
geos_equals,
|
||||
geos_equalsexact,
|
||||
geos_hasz,
|
||||
geos_intersects,
|
||||
geos_isclosed,
|
||||
geos_isempty,
|
||||
geos_isring,
|
||||
geos_issimple,
|
||||
geos_isvalid,
|
||||
geos_overlaps,
|
||||
geos_relatepattern,
|
||||
geos_touches,
|
||||
geos_within,
|
||||
)
|
||||
from django.contrib.gis.geos.prototypes.topology import * # NOQA
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__pycache__/geom.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__pycache__/geom.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__pycache__/io.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__pycache__/io.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__pycache__/misc.cpython-38.pyc
vendored
Normal file
BIN
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/__pycache__/misc.cpython-38.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
95
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/coordseq.py
vendored
Normal file
95
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/coordseq.py
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
from ctypes import POINTER, c_byte, c_double, c_int, c_uint
|
||||
|
||||
from django.contrib.gis.geos.libgeos import CS_PTR, GEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import GEOSException, last_arg_byref
|
||||
|
||||
|
||||
# ## Error-checking routines specific to coordinate sequences. ##
|
||||
def check_cs_op(result, func, cargs):
|
||||
"Check the status code of a coordinate sequence operation."
|
||||
if result == 0:
|
||||
raise GEOSException("Could not set value on coordinate sequence")
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
def check_cs_get(result, func, cargs):
|
||||
"Check the coordinate sequence retrieval."
|
||||
check_cs_op(result, func, cargs)
|
||||
# Object in by reference, return its value.
|
||||
return last_arg_byref(cargs)
|
||||
|
||||
|
||||
# ## Coordinate sequence prototype factory classes. ##
|
||||
class CsInt(GEOSFuncFactory):
|
||||
"For coordinate sequence routines that return an integer."
|
||||
argtypes = [CS_PTR, POINTER(c_uint)]
|
||||
restype = c_int
|
||||
errcheck = staticmethod(check_cs_get)
|
||||
|
||||
|
||||
class CsOperation(GEOSFuncFactory):
|
||||
"For coordinate sequence operations."
|
||||
restype = c_int
|
||||
|
||||
def __init__(self, *args, ordinate=False, get=False, **kwargs):
|
||||
if get:
|
||||
# Get routines have double parameter passed-in by reference.
|
||||
errcheck = check_cs_get
|
||||
dbl_param = POINTER(c_double)
|
||||
else:
|
||||
errcheck = check_cs_op
|
||||
dbl_param = c_double
|
||||
|
||||
if ordinate:
|
||||
# Get/Set ordinate routines have an extra uint parameter.
|
||||
argtypes = [CS_PTR, c_uint, c_uint, dbl_param]
|
||||
else:
|
||||
argtypes = [CS_PTR, c_uint, dbl_param]
|
||||
|
||||
super().__init__(
|
||||
*args, **{**kwargs, "errcheck": errcheck, "argtypes": argtypes}
|
||||
)
|
||||
|
||||
|
||||
class CsOutput(GEOSFuncFactory):
|
||||
restype = CS_PTR
|
||||
|
||||
@staticmethod
|
||||
def errcheck(result, func, cargs):
|
||||
if not result:
|
||||
raise GEOSException(
|
||||
"Error encountered checking Coordinate Sequence returned from GEOS "
|
||||
'C function "%s".' % func.__name__
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
# ## Coordinate Sequence ctypes prototypes ##
|
||||
|
||||
# Coordinate Sequence constructors & cloning.
|
||||
cs_clone = CsOutput("GEOSCoordSeq_clone", argtypes=[CS_PTR])
|
||||
create_cs = CsOutput("GEOSCoordSeq_create", argtypes=[c_uint, c_uint])
|
||||
get_cs = CsOutput("GEOSGeom_getCoordSeq", argtypes=[GEOM_PTR])
|
||||
|
||||
# Getting, setting ordinate
|
||||
cs_getordinate = CsOperation("GEOSCoordSeq_getOrdinate", ordinate=True, get=True)
|
||||
cs_setordinate = CsOperation("GEOSCoordSeq_setOrdinate", ordinate=True)
|
||||
|
||||
# For getting, x, y, z
|
||||
cs_getx = CsOperation("GEOSCoordSeq_getX", get=True)
|
||||
cs_gety = CsOperation("GEOSCoordSeq_getY", get=True)
|
||||
cs_getz = CsOperation("GEOSCoordSeq_getZ", get=True)
|
||||
|
||||
# For setting, x, y, z
|
||||
cs_setx = CsOperation("GEOSCoordSeq_setX")
|
||||
cs_sety = CsOperation("GEOSCoordSeq_setY")
|
||||
cs_setz = CsOperation("GEOSCoordSeq_setZ")
|
||||
|
||||
# These routines return size & dimensions.
|
||||
cs_getsize = CsInt("GEOSCoordSeq_getSize")
|
||||
cs_getdims = CsInt("GEOSCoordSeq_getDimensions")
|
||||
|
||||
cs_is_ccw = GEOSFuncFactory(
|
||||
"GEOSCoordSeq_isCCW", restype=c_int, argtypes=[CS_PTR, POINTER(c_byte)]
|
||||
)
|
95
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/errcheck.py
vendored
Normal file
95
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/errcheck.py
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
Error checking functions for GEOS ctypes prototype functions.
|
||||
"""
|
||||
from ctypes import c_void_p, string_at
|
||||
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.geos.libgeos import GEOSFuncFactory
|
||||
|
||||
# Getting the `free` routine used to free the memory allocated for
|
||||
# string pointers returned by GEOS.
|
||||
free = GEOSFuncFactory("GEOSFree")
|
||||
free.argtypes = [c_void_p]
|
||||
|
||||
|
||||
def last_arg_byref(args):
|
||||
"Return the last C argument's value by reference."
|
||||
return args[-1]._obj.value
|
||||
|
||||
|
||||
def check_dbl(result, func, cargs):
|
||||
"Check the status code and returns the double value passed in by reference."
|
||||
# Checking the status code
|
||||
if result != 1:
|
||||
return None
|
||||
# Double passed in by reference, return its value.
|
||||
return last_arg_byref(cargs)
|
||||
|
||||
|
||||
def check_geom(result, func, cargs):
|
||||
"Error checking on routines that return Geometries."
|
||||
if not result:
|
||||
raise GEOSException(
|
||||
'Error encountered checking Geometry returned from GEOS C function "%s".'
|
||||
% func.__name__
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def check_minus_one(result, func, cargs):
|
||||
"Error checking on routines that should not return -1."
|
||||
if result == -1:
|
||||
raise GEOSException(
|
||||
'Error encountered in GEOS C function "%s".' % func.__name__
|
||||
)
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
def check_predicate(result, func, cargs):
|
||||
"Error checking for unary/binary predicate functions."
|
||||
if result == 1:
|
||||
return True
|
||||
elif result == 0:
|
||||
return False
|
||||
else:
|
||||
raise GEOSException(
|
||||
'Error encountered on GEOS C predicate function "%s".' % func.__name__
|
||||
)
|
||||
|
||||
|
||||
def check_sized_string(result, func, cargs):
|
||||
"""
|
||||
Error checking for routines that return explicitly sized strings.
|
||||
|
||||
This frees the memory allocated by GEOS at the result pointer.
|
||||
"""
|
||||
if not result:
|
||||
raise GEOSException(
|
||||
'Invalid string pointer returned by GEOS C function "%s"' % func.__name__
|
||||
)
|
||||
# A c_size_t object is passed in by reference for the second
|
||||
# argument on these routines, and its needed to determine the
|
||||
# correct size.
|
||||
s = string_at(result, last_arg_byref(cargs))
|
||||
# Freeing the memory allocated within GEOS
|
||||
free(result)
|
||||
return s
|
||||
|
||||
|
||||
def check_string(result, func, cargs):
|
||||
"""
|
||||
Error checking for routines that return strings.
|
||||
|
||||
This frees the memory allocated by GEOS at the result pointer.
|
||||
"""
|
||||
if not result:
|
||||
raise GEOSException(
|
||||
'Error encountered checking string return value in GEOS C function "%s".'
|
||||
% func.__name__
|
||||
)
|
||||
# Getting the string value at the pointer address.
|
||||
s = string_at(result)
|
||||
# Freeing the memory allocated within GEOS
|
||||
free(result)
|
||||
return s
|
91
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/geom.py
vendored
Normal file
91
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/geom.py
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
from ctypes import POINTER, c_char_p, c_int, c_ubyte, c_uint
|
||||
|
||||
from django.contrib.gis.geos.libgeos import CS_PTR, GEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import (
|
||||
check_geom,
|
||||
check_minus_one,
|
||||
check_string,
|
||||
)
|
||||
|
||||
# This is the return type used by binary output (WKB, HEX) routines.
|
||||
c_uchar_p = POINTER(c_ubyte)
|
||||
|
||||
|
||||
# We create a simple subclass of c_char_p here because when the response
|
||||
# type is set to c_char_p, you get a _Python_ string and there's no way
|
||||
# to access the string's address inside the error checking function.
|
||||
# In other words, you can't free the memory allocated inside GEOS. Previously,
|
||||
# the return type would just be omitted and the integer address would be
|
||||
# used -- but this allows us to be specific in the function definition and
|
||||
# keeps the reference so it may be free'd.
|
||||
class geos_char_p(c_char_p):
|
||||
pass
|
||||
|
||||
|
||||
# ### ctypes factory classes ###
|
||||
class GeomOutput(GEOSFuncFactory):
|
||||
"For GEOS routines that return a geometry."
|
||||
restype = GEOM_PTR
|
||||
errcheck = staticmethod(check_geom)
|
||||
|
||||
|
||||
class IntFromGeom(GEOSFuncFactory):
|
||||
"Argument is a geometry, return type is an integer."
|
||||
argtypes = [GEOM_PTR]
|
||||
restype = c_int
|
||||
errcheck = staticmethod(check_minus_one)
|
||||
|
||||
|
||||
class StringFromGeom(GEOSFuncFactory):
|
||||
"Argument is a Geometry, return type is a string."
|
||||
argtypes = [GEOM_PTR]
|
||||
restype = geos_char_p
|
||||
errcheck = staticmethod(check_string)
|
||||
|
||||
|
||||
# ### ctypes prototypes ###
|
||||
|
||||
# The GEOS geometry type, typeid, num_coordinates and number of geometries
|
||||
geos_makevalid = GeomOutput("GEOSMakeValid", argtypes=[GEOM_PTR])
|
||||
geos_normalize = IntFromGeom("GEOSNormalize")
|
||||
geos_type = StringFromGeom("GEOSGeomType")
|
||||
geos_typeid = IntFromGeom("GEOSGeomTypeId")
|
||||
get_dims = GEOSFuncFactory("GEOSGeom_getDimensions", argtypes=[GEOM_PTR], restype=c_int)
|
||||
get_num_coords = IntFromGeom("GEOSGetNumCoordinates")
|
||||
get_num_geoms = IntFromGeom("GEOSGetNumGeometries")
|
||||
|
||||
# Geometry creation factories
|
||||
create_point = GeomOutput("GEOSGeom_createPoint", argtypes=[CS_PTR])
|
||||
create_linestring = GeomOutput("GEOSGeom_createLineString", argtypes=[CS_PTR])
|
||||
create_linearring = GeomOutput("GEOSGeom_createLinearRing", argtypes=[CS_PTR])
|
||||
|
||||
# Polygon and collection creation routines need argument types defined
|
||||
# for compatibility with some platforms, e.g. macOS ARM64. With argtypes
|
||||
# defined, arrays are automatically cast and byref() calls are not needed.
|
||||
create_polygon = GeomOutput(
|
||||
"GEOSGeom_createPolygon",
|
||||
argtypes=[GEOM_PTR, POINTER(GEOM_PTR), c_uint],
|
||||
)
|
||||
create_empty_polygon = GeomOutput("GEOSGeom_createEmptyPolygon", argtypes=[])
|
||||
create_collection = GeomOutput(
|
||||
"GEOSGeom_createCollection",
|
||||
argtypes=[c_int, POINTER(GEOM_PTR), c_uint],
|
||||
)
|
||||
|
||||
# Ring routines
|
||||
get_extring = GeomOutput("GEOSGetExteriorRing", argtypes=[GEOM_PTR])
|
||||
get_intring = GeomOutput("GEOSGetInteriorRingN", argtypes=[GEOM_PTR, c_int])
|
||||
get_nrings = IntFromGeom("GEOSGetNumInteriorRings")
|
||||
|
||||
# Collection Routines
|
||||
get_geomn = GeomOutput("GEOSGetGeometryN", argtypes=[GEOM_PTR, c_int])
|
||||
|
||||
# Cloning
|
||||
geom_clone = GEOSFuncFactory("GEOSGeom_clone", argtypes=[GEOM_PTR], restype=GEOM_PTR)
|
||||
|
||||
# Destruction routine.
|
||||
destroy_geom = GEOSFuncFactory("GEOSGeom_destroy", argtypes=[GEOM_PTR])
|
||||
|
||||
# SRID routines
|
||||
geos_get_srid = GEOSFuncFactory("GEOSGetSRID", argtypes=[GEOM_PTR], restype=c_int)
|
||||
geos_set_srid = GEOSFuncFactory("GEOSSetSRID", argtypes=[GEOM_PTR, c_int])
|
366
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/io.py
vendored
Normal file
366
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/io.py
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
import threading
|
||||
from ctypes import POINTER, Structure, byref, c_byte, c_char_p, c_int, c_size_t
|
||||
|
||||
from django.contrib.gis.geos.base import GEOSBase
|
||||
from django.contrib.gis.geos.libgeos import (
|
||||
GEOM_PTR,
|
||||
GEOSFuncFactory,
|
||||
geos_version_tuple,
|
||||
)
|
||||
from django.contrib.gis.geos.prototypes.errcheck import (
|
||||
check_geom,
|
||||
check_sized_string,
|
||||
check_string,
|
||||
)
|
||||
from django.contrib.gis.geos.prototypes.geom import c_uchar_p, geos_char_p
|
||||
from django.utils.encoding import force_bytes
|
||||
|
||||
|
||||
# ### The WKB/WKT Reader/Writer structures and pointers ###
|
||||
class WKTReader_st(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class WKTWriter_st(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class WKBReader_st(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class WKBWriter_st(Structure):
|
||||
pass
|
||||
|
||||
|
||||
WKT_READ_PTR = POINTER(WKTReader_st)
|
||||
WKT_WRITE_PTR = POINTER(WKTWriter_st)
|
||||
WKB_READ_PTR = POINTER(WKBReader_st)
|
||||
WKB_WRITE_PTR = POINTER(WKBReader_st)
|
||||
|
||||
# WKTReader routines
|
||||
wkt_reader_create = GEOSFuncFactory("GEOSWKTReader_create", restype=WKT_READ_PTR)
|
||||
wkt_reader_destroy = GEOSFuncFactory("GEOSWKTReader_destroy", argtypes=[WKT_READ_PTR])
|
||||
|
||||
wkt_reader_read = GEOSFuncFactory(
|
||||
"GEOSWKTReader_read",
|
||||
argtypes=[WKT_READ_PTR, c_char_p],
|
||||
restype=GEOM_PTR,
|
||||
errcheck=check_geom,
|
||||
)
|
||||
# WKTWriter routines
|
||||
wkt_writer_create = GEOSFuncFactory("GEOSWKTWriter_create", restype=WKT_WRITE_PTR)
|
||||
wkt_writer_destroy = GEOSFuncFactory("GEOSWKTWriter_destroy", argtypes=[WKT_WRITE_PTR])
|
||||
|
||||
wkt_writer_write = GEOSFuncFactory(
|
||||
"GEOSWKTWriter_write",
|
||||
argtypes=[WKT_WRITE_PTR, GEOM_PTR],
|
||||
restype=geos_char_p,
|
||||
errcheck=check_string,
|
||||
)
|
||||
|
||||
wkt_writer_get_outdim = GEOSFuncFactory(
|
||||
"GEOSWKTWriter_getOutputDimension", argtypes=[WKT_WRITE_PTR], restype=c_int
|
||||
)
|
||||
wkt_writer_set_outdim = GEOSFuncFactory(
|
||||
"GEOSWKTWriter_setOutputDimension", argtypes=[WKT_WRITE_PTR, c_int]
|
||||
)
|
||||
|
||||
wkt_writer_set_trim = GEOSFuncFactory(
|
||||
"GEOSWKTWriter_setTrim", argtypes=[WKT_WRITE_PTR, c_byte]
|
||||
)
|
||||
wkt_writer_set_precision = GEOSFuncFactory(
|
||||
"GEOSWKTWriter_setRoundingPrecision", argtypes=[WKT_WRITE_PTR, c_int]
|
||||
)
|
||||
|
||||
# WKBReader routines
|
||||
wkb_reader_create = GEOSFuncFactory("GEOSWKBReader_create", restype=WKB_READ_PTR)
|
||||
wkb_reader_destroy = GEOSFuncFactory("GEOSWKBReader_destroy", argtypes=[WKB_READ_PTR])
|
||||
|
||||
|
||||
class WKBReadFunc(GEOSFuncFactory):
|
||||
# Although the function definitions take `const unsigned char *`
|
||||
# as their parameter, we use c_char_p here so the function may
|
||||
# take Python strings directly as parameters. Inside Python there
|
||||
# is not a difference between signed and unsigned characters, so
|
||||
# it is not a problem.
|
||||
argtypes = [WKB_READ_PTR, c_char_p, c_size_t]
|
||||
restype = GEOM_PTR
|
||||
errcheck = staticmethod(check_geom)
|
||||
|
||||
|
||||
wkb_reader_read = WKBReadFunc("GEOSWKBReader_read")
|
||||
wkb_reader_read_hex = WKBReadFunc("GEOSWKBReader_readHEX")
|
||||
|
||||
# WKBWriter routines
|
||||
wkb_writer_create = GEOSFuncFactory("GEOSWKBWriter_create", restype=WKB_WRITE_PTR)
|
||||
wkb_writer_destroy = GEOSFuncFactory("GEOSWKBWriter_destroy", argtypes=[WKB_WRITE_PTR])
|
||||
|
||||
|
||||
# WKB Writing prototypes.
|
||||
class WKBWriteFunc(GEOSFuncFactory):
|
||||
argtypes = [WKB_WRITE_PTR, GEOM_PTR, POINTER(c_size_t)]
|
||||
restype = c_uchar_p
|
||||
errcheck = staticmethod(check_sized_string)
|
||||
|
||||
|
||||
wkb_writer_write = WKBWriteFunc("GEOSWKBWriter_write")
|
||||
wkb_writer_write_hex = WKBWriteFunc("GEOSWKBWriter_writeHEX")
|
||||
|
||||
|
||||
# WKBWriter property getter/setter prototypes.
|
||||
class WKBWriterGet(GEOSFuncFactory):
|
||||
argtypes = [WKB_WRITE_PTR]
|
||||
restype = c_int
|
||||
|
||||
|
||||
class WKBWriterSet(GEOSFuncFactory):
|
||||
argtypes = [WKB_WRITE_PTR, c_int]
|
||||
|
||||
|
||||
wkb_writer_get_byteorder = WKBWriterGet("GEOSWKBWriter_getByteOrder")
|
||||
wkb_writer_set_byteorder = WKBWriterSet("GEOSWKBWriter_setByteOrder")
|
||||
wkb_writer_get_outdim = WKBWriterGet("GEOSWKBWriter_getOutputDimension")
|
||||
wkb_writer_set_outdim = WKBWriterSet("GEOSWKBWriter_setOutputDimension")
|
||||
wkb_writer_get_include_srid = WKBWriterGet(
|
||||
"GEOSWKBWriter_getIncludeSRID", restype=c_byte
|
||||
)
|
||||
wkb_writer_set_include_srid = WKBWriterSet(
|
||||
"GEOSWKBWriter_setIncludeSRID", argtypes=[WKB_WRITE_PTR, c_byte]
|
||||
)
|
||||
|
||||
|
||||
# ### Base I/O Class ###
|
||||
class IOBase(GEOSBase):
|
||||
"Base class for GEOS I/O objects."
|
||||
|
||||
def __init__(self):
|
||||
# Getting the pointer with the constructor.
|
||||
self.ptr = self._constructor()
|
||||
# Loading the real destructor function at this point as doing it in
|
||||
# __del__ is too late (import error).
|
||||
self.destructor.func
|
||||
|
||||
|
||||
# ### Base WKB/WKT Reading and Writing objects ###
|
||||
|
||||
|
||||
# Non-public WKB/WKT reader classes for internal use because
|
||||
# their `read` methods return _pointers_ instead of GEOSGeometry
|
||||
# objects.
|
||||
class _WKTReader(IOBase):
|
||||
_constructor = wkt_reader_create
|
||||
ptr_type = WKT_READ_PTR
|
||||
destructor = wkt_reader_destroy
|
||||
|
||||
def read(self, wkt):
|
||||
if not isinstance(wkt, (bytes, str)):
|
||||
raise TypeError
|
||||
return wkt_reader_read(self.ptr, force_bytes(wkt))
|
||||
|
||||
|
||||
class _WKBReader(IOBase):
|
||||
_constructor = wkb_reader_create
|
||||
ptr_type = WKB_READ_PTR
|
||||
destructor = wkb_reader_destroy
|
||||
|
||||
def read(self, wkb):
|
||||
"Return a _pointer_ to C GEOS Geometry object from the given WKB."
|
||||
if isinstance(wkb, memoryview):
|
||||
wkb_s = bytes(wkb)
|
||||
return wkb_reader_read(self.ptr, wkb_s, len(wkb_s))
|
||||
elif isinstance(wkb, (bytes, str)):
|
||||
return wkb_reader_read_hex(self.ptr, wkb, len(wkb))
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
|
||||
# ### WKB/WKT Writer Classes ###
|
||||
class WKTWriter(IOBase):
|
||||
_constructor = wkt_writer_create
|
||||
ptr_type = WKT_WRITE_PTR
|
||||
destructor = wkt_writer_destroy
|
||||
|
||||
_trim = False
|
||||
_precision = None
|
||||
|
||||
def __init__(self, dim=2, trim=False, precision=None):
|
||||
super().__init__()
|
||||
if bool(trim) != self._trim:
|
||||
self.trim = trim
|
||||
if precision is not None:
|
||||
self.precision = precision
|
||||
self.outdim = dim
|
||||
|
||||
def write(self, geom):
|
||||
"Return the WKT representation of the given geometry."
|
||||
return wkt_writer_write(self.ptr, geom.ptr)
|
||||
|
||||
@property
|
||||
def outdim(self):
|
||||
return wkt_writer_get_outdim(self.ptr)
|
||||
|
||||
@outdim.setter
|
||||
def outdim(self, new_dim):
|
||||
if new_dim not in (2, 3):
|
||||
raise ValueError("WKT output dimension must be 2 or 3")
|
||||
wkt_writer_set_outdim(self.ptr, new_dim)
|
||||
|
||||
@property
|
||||
def trim(self):
|
||||
return self._trim
|
||||
|
||||
@trim.setter
|
||||
def trim(self, flag):
|
||||
if bool(flag) != self._trim:
|
||||
self._trim = bool(flag)
|
||||
wkt_writer_set_trim(self.ptr, self._trim)
|
||||
|
||||
@property
|
||||
def precision(self):
|
||||
return self._precision
|
||||
|
||||
@precision.setter
|
||||
def precision(self, precision):
|
||||
if (not isinstance(precision, int) or precision < 0) and precision is not None:
|
||||
raise AttributeError(
|
||||
"WKT output rounding precision must be non-negative integer or None."
|
||||
)
|
||||
if precision != self._precision:
|
||||
self._precision = precision
|
||||
wkt_writer_set_precision(self.ptr, -1 if precision is None else precision)
|
||||
|
||||
|
||||
class WKBWriter(IOBase):
|
||||
_constructor = wkb_writer_create
|
||||
ptr_type = WKB_WRITE_PTR
|
||||
destructor = wkb_writer_destroy
|
||||
geos_version = geos_version_tuple()
|
||||
|
||||
def __init__(self, dim=2):
|
||||
super().__init__()
|
||||
self.outdim = dim
|
||||
|
||||
def _handle_empty_point(self, geom):
|
||||
from django.contrib.gis.geos import Point
|
||||
|
||||
if isinstance(geom, Point) and geom.empty:
|
||||
if self.srid:
|
||||
# PostGIS uses POINT(NaN NaN) for WKB representation of empty
|
||||
# points. Use it for EWKB as it's a PostGIS specific format.
|
||||
# https://trac.osgeo.org/postgis/ticket/3181
|
||||
geom = Point(float("NaN"), float("NaN"), srid=geom.srid)
|
||||
else:
|
||||
raise ValueError("Empty point is not representable in WKB.")
|
||||
return geom
|
||||
|
||||
def write(self, geom):
|
||||
"Return the WKB representation of the given geometry."
|
||||
from django.contrib.gis.geos import Polygon
|
||||
|
||||
geom = self._handle_empty_point(geom)
|
||||
wkb = wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t()))
|
||||
if self.geos_version < (3, 6, 1) and isinstance(geom, Polygon) and geom.empty:
|
||||
# Fix GEOS output for empty polygon.
|
||||
# See https://trac.osgeo.org/geos/ticket/680.
|
||||
wkb = wkb[:-8] + b"\0" * 4
|
||||
return memoryview(wkb)
|
||||
|
||||
def write_hex(self, geom):
|
||||
"Return the HEXEWKB representation of the given geometry."
|
||||
from django.contrib.gis.geos.polygon import Polygon
|
||||
|
||||
geom = self._handle_empty_point(geom)
|
||||
wkb = wkb_writer_write_hex(self.ptr, geom.ptr, byref(c_size_t()))
|
||||
if self.geos_version < (3, 6, 1) and isinstance(geom, Polygon) and geom.empty:
|
||||
wkb = wkb[:-16] + b"0" * 8
|
||||
return wkb
|
||||
|
||||
# ### WKBWriter Properties ###
|
||||
|
||||
# Property for getting/setting the byteorder.
|
||||
def _get_byteorder(self):
|
||||
return wkb_writer_get_byteorder(self.ptr)
|
||||
|
||||
def _set_byteorder(self, order):
|
||||
if order not in (0, 1):
|
||||
raise ValueError(
|
||||
"Byte order parameter must be 0 (Big Endian) or 1 (Little Endian)."
|
||||
)
|
||||
wkb_writer_set_byteorder(self.ptr, order)
|
||||
|
||||
byteorder = property(_get_byteorder, _set_byteorder)
|
||||
|
||||
# Property for getting/setting the output dimension.
|
||||
@property
|
||||
def outdim(self):
|
||||
return wkb_writer_get_outdim(self.ptr)
|
||||
|
||||
@outdim.setter
|
||||
def outdim(self, new_dim):
|
||||
if new_dim not in (2, 3):
|
||||
raise ValueError("WKB output dimension must be 2 or 3")
|
||||
wkb_writer_set_outdim(self.ptr, new_dim)
|
||||
|
||||
# Property for getting/setting the include srid flag.
|
||||
@property
|
||||
def srid(self):
|
||||
return bool(wkb_writer_get_include_srid(self.ptr))
|
||||
|
||||
@srid.setter
|
||||
def srid(self, include):
|
||||
wkb_writer_set_include_srid(self.ptr, bool(include))
|
||||
|
||||
|
||||
# `ThreadLocalIO` object holds instances of the WKT and WKB reader/writer
|
||||
# objects that are local to the thread. The `GEOSGeometry` internals
|
||||
# access these instances by calling the module-level functions, defined
|
||||
# below.
|
||||
class ThreadLocalIO(threading.local):
|
||||
wkt_r = None
|
||||
wkt_w = None
|
||||
wkb_r = None
|
||||
wkb_w = None
|
||||
ewkb_w = None
|
||||
|
||||
|
||||
thread_context = ThreadLocalIO()
|
||||
|
||||
|
||||
# These module-level routines return the I/O object that is local to the
|
||||
# thread. If the I/O object does not exist yet it will be initialized.
|
||||
def wkt_r():
|
||||
thread_context.wkt_r = thread_context.wkt_r or _WKTReader()
|
||||
return thread_context.wkt_r
|
||||
|
||||
|
||||
def wkt_w(dim=2, trim=False, precision=None):
|
||||
if not thread_context.wkt_w:
|
||||
thread_context.wkt_w = WKTWriter(dim=dim, trim=trim, precision=precision)
|
||||
else:
|
||||
thread_context.wkt_w.outdim = dim
|
||||
thread_context.wkt_w.trim = trim
|
||||
thread_context.wkt_w.precision = precision
|
||||
return thread_context.wkt_w
|
||||
|
||||
|
||||
def wkb_r():
|
||||
thread_context.wkb_r = thread_context.wkb_r or _WKBReader()
|
||||
return thread_context.wkb_r
|
||||
|
||||
|
||||
def wkb_w(dim=2):
|
||||
if not thread_context.wkb_w:
|
||||
thread_context.wkb_w = WKBWriter(dim=dim)
|
||||
else:
|
||||
thread_context.wkb_w.outdim = dim
|
||||
return thread_context.wkb_w
|
||||
|
||||
|
||||
def ewkb_w(dim=2):
|
||||
if not thread_context.ewkb_w:
|
||||
thread_context.ewkb_w = WKBWriter(dim=dim)
|
||||
thread_context.ewkb_w.srid = True
|
||||
else:
|
||||
thread_context.ewkb_w.outdim = dim
|
||||
return thread_context.ewkb_w
|
34
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/misc.py
vendored
Normal file
34
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/misc.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
This module is for the miscellaneous GEOS routines, particularly the
|
||||
ones that return the area, distance, and length.
|
||||
"""
|
||||
from ctypes import POINTER, c_double, c_int
|
||||
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import check_dbl, check_string
|
||||
from django.contrib.gis.geos.prototypes.geom import geos_char_p
|
||||
|
||||
__all__ = ["geos_area", "geos_distance", "geos_length", "geos_isvalidreason"]
|
||||
|
||||
|
||||
class DblFromGeom(GEOSFuncFactory):
|
||||
"""
|
||||
Argument is a Geometry, return type is double that is passed
|
||||
in by reference as the last argument.
|
||||
"""
|
||||
|
||||
restype = c_int # Status code returned
|
||||
errcheck = staticmethod(check_dbl)
|
||||
|
||||
|
||||
# ### ctypes prototypes ###
|
||||
|
||||
# Area, distance, and length prototypes.
|
||||
geos_area = DblFromGeom("GEOSArea", argtypes=[GEOM_PTR, POINTER(c_double)])
|
||||
geos_distance = DblFromGeom(
|
||||
"GEOSDistance", argtypes=[GEOM_PTR, GEOM_PTR, POINTER(c_double)]
|
||||
)
|
||||
geos_length = DblFromGeom("GEOSLength", argtypes=[GEOM_PTR, POINTER(c_double)])
|
||||
geos_isvalidreason = GEOSFuncFactory(
|
||||
"GEOSisValidReason", restype=geos_char_p, errcheck=check_string, argtypes=[GEOM_PTR]
|
||||
)
|
47
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/predicates.py
vendored
Normal file
47
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/predicates.py
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
This module houses the GEOS ctypes prototype functions for the
|
||||
unary and binary predicate operations on geometries.
|
||||
"""
|
||||
from ctypes import c_byte, c_char_p, c_double
|
||||
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import check_predicate
|
||||
|
||||
|
||||
# ## Binary & unary predicate factories ##
|
||||
class UnaryPredicate(GEOSFuncFactory):
|
||||
"For GEOS unary predicate functions."
|
||||
argtypes = [GEOM_PTR]
|
||||
restype = c_byte
|
||||
errcheck = staticmethod(check_predicate)
|
||||
|
||||
|
||||
class BinaryPredicate(UnaryPredicate):
|
||||
"For GEOS binary predicate functions."
|
||||
argtypes = [GEOM_PTR, GEOM_PTR]
|
||||
|
||||
|
||||
# ## Unary Predicates ##
|
||||
geos_hasz = UnaryPredicate("GEOSHasZ")
|
||||
geos_isclosed = UnaryPredicate("GEOSisClosed")
|
||||
geos_isempty = UnaryPredicate("GEOSisEmpty")
|
||||
geos_isring = UnaryPredicate("GEOSisRing")
|
||||
geos_issimple = UnaryPredicate("GEOSisSimple")
|
||||
geos_isvalid = UnaryPredicate("GEOSisValid")
|
||||
|
||||
# ## Binary Predicates ##
|
||||
geos_contains = BinaryPredicate("GEOSContains")
|
||||
geos_covers = BinaryPredicate("GEOSCovers")
|
||||
geos_crosses = BinaryPredicate("GEOSCrosses")
|
||||
geos_disjoint = BinaryPredicate("GEOSDisjoint")
|
||||
geos_equals = BinaryPredicate("GEOSEquals")
|
||||
geos_equalsexact = BinaryPredicate(
|
||||
"GEOSEqualsExact", argtypes=[GEOM_PTR, GEOM_PTR, c_double]
|
||||
)
|
||||
geos_intersects = BinaryPredicate("GEOSIntersects")
|
||||
geos_overlaps = BinaryPredicate("GEOSOverlaps")
|
||||
geos_relatepattern = BinaryPredicate(
|
||||
"GEOSRelatePattern", argtypes=[GEOM_PTR, GEOM_PTR, c_char_p]
|
||||
)
|
||||
geos_touches = BinaryPredicate("GEOSTouches")
|
||||
geos_within = BinaryPredicate("GEOSWithin")
|
26
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/prepared.py
vendored
Normal file
26
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/prepared.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
from ctypes import c_byte
|
||||
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR, PREPGEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import check_predicate
|
||||
|
||||
# Prepared geometry constructor and destructors.
|
||||
geos_prepare = GEOSFuncFactory("GEOSPrepare", argtypes=[GEOM_PTR], restype=PREPGEOM_PTR)
|
||||
prepared_destroy = GEOSFuncFactory("GEOSPreparedGeom_destroy", argtypes=[PREPGEOM_PTR])
|
||||
|
||||
|
||||
# Prepared geometry binary predicate support.
|
||||
class PreparedPredicate(GEOSFuncFactory):
|
||||
argtypes = [PREPGEOM_PTR, GEOM_PTR]
|
||||
restype = c_byte
|
||||
errcheck = staticmethod(check_predicate)
|
||||
|
||||
|
||||
prepared_contains = PreparedPredicate("GEOSPreparedContains")
|
||||
prepared_contains_properly = PreparedPredicate("GEOSPreparedContainsProperly")
|
||||
prepared_covers = PreparedPredicate("GEOSPreparedCovers")
|
||||
prepared_crosses = PreparedPredicate("GEOSPreparedCrosses")
|
||||
prepared_disjoint = PreparedPredicate("GEOSPreparedDisjoint")
|
||||
prepared_intersects = PreparedPredicate("GEOSPreparedIntersects")
|
||||
prepared_overlaps = PreparedPredicate("GEOSPreparedOverlaps")
|
||||
prepared_touches = PreparedPredicate("GEOSPreparedTouches")
|
||||
prepared_within = PreparedPredicate("GEOSPreparedWithin")
|
77
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/threadsafe.py
vendored
Normal file
77
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/threadsafe.py
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
import threading
|
||||
|
||||
from django.contrib.gis.geos.base import GEOSBase
|
||||
from django.contrib.gis.geos.libgeos import CONTEXT_PTR, error_h, lgeos, notice_h
|
||||
|
||||
|
||||
class GEOSContextHandle(GEOSBase):
|
||||
"""Represent a GEOS context handle."""
|
||||
|
||||
ptr_type = CONTEXT_PTR
|
||||
destructor = lgeos.finishGEOS_r
|
||||
|
||||
def __init__(self):
|
||||
# Initializing the context handler for this thread with
|
||||
# the notice and error handler.
|
||||
self.ptr = lgeos.initGEOS_r(notice_h, error_h)
|
||||
|
||||
|
||||
# Defining a thread-local object and creating an instance
|
||||
# to hold a reference to GEOSContextHandle for this thread.
|
||||
class GEOSContext(threading.local):
|
||||
handle = None
|
||||
|
||||
|
||||
thread_context = GEOSContext()
|
||||
|
||||
|
||||
class GEOSFunc:
|
||||
"""
|
||||
Serve as a wrapper for GEOS C Functions. Use thread-safe function
|
||||
variants when available.
|
||||
"""
|
||||
|
||||
def __init__(self, func_name):
|
||||
# GEOS thread-safe function signatures end with '_r' and take an
|
||||
# additional context handle parameter.
|
||||
self.cfunc = getattr(lgeos, func_name + "_r")
|
||||
# Create a reference to thread_context so it's not garbage-collected
|
||||
# before an attempt to call this object.
|
||||
self.thread_context = thread_context
|
||||
|
||||
def __call__(self, *args):
|
||||
# Create a context handle if one doesn't exist for this thread.
|
||||
self.thread_context.handle = self.thread_context.handle or GEOSContextHandle()
|
||||
# Call the threaded GEOS routine with the pointer of the context handle
|
||||
# as the first argument.
|
||||
return self.cfunc(self.thread_context.handle.ptr, *args)
|
||||
|
||||
def __str__(self):
|
||||
return self.cfunc.__name__
|
||||
|
||||
# argtypes property
|
||||
def _get_argtypes(self):
|
||||
return self.cfunc.argtypes
|
||||
|
||||
def _set_argtypes(self, argtypes):
|
||||
self.cfunc.argtypes = [CONTEXT_PTR, *argtypes]
|
||||
|
||||
argtypes = property(_get_argtypes, _set_argtypes)
|
||||
|
||||
# restype property
|
||||
def _get_restype(self):
|
||||
return self.cfunc.restype
|
||||
|
||||
def _set_restype(self, restype):
|
||||
self.cfunc.restype = restype
|
||||
|
||||
restype = property(_get_restype, _set_restype)
|
||||
|
||||
# errcheck property
|
||||
def _get_errcheck(self):
|
||||
return self.cfunc.errcheck
|
||||
|
||||
def _set_errcheck(self, errcheck):
|
||||
self.cfunc.errcheck = errcheck
|
||||
|
||||
errcheck = property(_get_errcheck, _set_errcheck)
|
72
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/topology.py
vendored
Normal file
72
env/lib/python3.8/site-packages/django/contrib/gis/geos/prototypes/topology.py
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
This module houses the GEOS ctypes prototype functions for the
|
||||
topological operations on geometries.
|
||||
"""
|
||||
from ctypes import c_double, c_int
|
||||
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import (
|
||||
check_geom,
|
||||
check_minus_one,
|
||||
check_string,
|
||||
)
|
||||
from django.contrib.gis.geos.prototypes.geom import geos_char_p
|
||||
|
||||
|
||||
class Topology(GEOSFuncFactory):
|
||||
"For GEOS unary topology functions."
|
||||
argtypes = [GEOM_PTR]
|
||||
restype = GEOM_PTR
|
||||
errcheck = staticmethod(check_geom)
|
||||
|
||||
|
||||
# Topology Routines
|
||||
geos_boundary = Topology("GEOSBoundary")
|
||||
geos_buffer = Topology("GEOSBuffer", argtypes=[GEOM_PTR, c_double, c_int])
|
||||
geos_bufferwithstyle = Topology(
|
||||
"GEOSBufferWithStyle", argtypes=[GEOM_PTR, c_double, c_int, c_int, c_int, c_double]
|
||||
)
|
||||
geos_centroid = Topology("GEOSGetCentroid")
|
||||
geos_convexhull = Topology("GEOSConvexHull")
|
||||
geos_difference = Topology("GEOSDifference", argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
geos_envelope = Topology("GEOSEnvelope")
|
||||
geos_intersection = Topology("GEOSIntersection", argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
geos_linemerge = Topology("GEOSLineMerge")
|
||||
geos_pointonsurface = Topology("GEOSPointOnSurface")
|
||||
geos_preservesimplify = Topology(
|
||||
"GEOSTopologyPreserveSimplify", argtypes=[GEOM_PTR, c_double]
|
||||
)
|
||||
geos_simplify = Topology("GEOSSimplify", argtypes=[GEOM_PTR, c_double])
|
||||
geos_symdifference = Topology("GEOSSymDifference", argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
geos_union = Topology("GEOSUnion", argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
|
||||
geos_unary_union = GEOSFuncFactory(
|
||||
"GEOSUnaryUnion", argtypes=[GEOM_PTR], restype=GEOM_PTR
|
||||
)
|
||||
|
||||
# GEOSRelate returns a string, not a geometry.
|
||||
geos_relate = GEOSFuncFactory(
|
||||
"GEOSRelate",
|
||||
argtypes=[GEOM_PTR, GEOM_PTR],
|
||||
restype=geos_char_p,
|
||||
errcheck=check_string,
|
||||
)
|
||||
|
||||
# Linear referencing routines
|
||||
geos_project = GEOSFuncFactory(
|
||||
"GEOSProject",
|
||||
argtypes=[GEOM_PTR, GEOM_PTR],
|
||||
restype=c_double,
|
||||
errcheck=check_minus_one,
|
||||
)
|
||||
geos_interpolate = Topology("GEOSInterpolate", argtypes=[GEOM_PTR, c_double])
|
||||
|
||||
geos_project_normalized = GEOSFuncFactory(
|
||||
"GEOSProjectNormalized",
|
||||
argtypes=[GEOM_PTR, GEOM_PTR],
|
||||
restype=c_double,
|
||||
errcheck=check_minus_one,
|
||||
)
|
||||
geos_interpolate_normalized = Topology(
|
||||
"GEOSInterpolateNormalized", argtypes=[GEOM_PTR, c_double]
|
||||
)
|
Reference in New Issue
Block a user