You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

collections.py 4.9 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. """
  2. This module houses the Geometry Collection objects:
  3. GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
  4. """
  5. import json
  6. from ctypes import byref, c_int, c_uint
  7. from django.contrib.gis.geos import prototypes as capi
  8. from django.contrib.gis.geos.geometry import (
  9. GEOSGeometry, ProjectInterpolateMixin,
  10. )
  11. from django.contrib.gis.geos.libgeos import get_pointer_arr
  12. from django.contrib.gis.geos.linestring import LinearRing, LineString
  13. from django.contrib.gis.geos.point import Point
  14. from django.contrib.gis.geos.polygon import Polygon
  15. from django.utils.six.moves import range
  16. class GeometryCollection(GEOSGeometry):
  17. _typeid = 7
  18. def __init__(self, *args, **kwargs):
  19. "Initializes a Geometry Collection from a sequence of Geometry objects."
  20. # Checking the arguments
  21. if not args:
  22. raise TypeError('Must provide at least one Geometry to initialize %s.' % self.__class__.__name__)
  23. if len(args) == 1:
  24. # If only one geometry provided or a list of geometries is provided
  25. # in the first argument.
  26. if isinstance(args[0], (tuple, list)):
  27. init_geoms = args[0]
  28. else:
  29. init_geoms = args
  30. else:
  31. init_geoms = args
  32. # Ensuring that only the permitted geometries are allowed in this collection
  33. # this is moved to list mixin super class
  34. self._check_allowed(init_geoms)
  35. # Creating the geometry pointer array.
  36. collection = self._create_collection(len(init_geoms), iter(init_geoms))
  37. super(GeometryCollection, self).__init__(collection, **kwargs)
  38. def __iter__(self):
  39. "Iterates over each Geometry in the Collection."
  40. for i in range(len(self)):
  41. yield self[i]
  42. def __len__(self):
  43. "Returns the number of geometries in this Collection."
  44. return self.num_geom
  45. # ### Methods for compatibility with ListMixin ###
  46. def _create_collection(self, length, items):
  47. # Creating the geometry pointer array.
  48. geoms = get_pointer_arr(length)
  49. for i, g in enumerate(items):
  50. # this is a little sloppy, but makes life easier
  51. # allow GEOSGeometry types (python wrappers) or pointer types
  52. geoms[i] = capi.geom_clone(getattr(g, 'ptr', g))
  53. return capi.create_collection(c_int(self._typeid), byref(geoms), c_uint(length))
  54. def _get_single_internal(self, index):
  55. return capi.get_geomn(self.ptr, index)
  56. def _get_single_external(self, index):
  57. "Returns the Geometry from this Collection at the given index (0-based)."
  58. # Checking the index and returning the corresponding GEOS geometry.
  59. return GEOSGeometry(capi.geom_clone(self._get_single_internal(index)), srid=self.srid)
  60. def _set_list(self, length, items):
  61. "Create a new collection, and destroy the contents of the previous pointer."
  62. prev_ptr = self.ptr
  63. srid = self.srid
  64. self.ptr = self._create_collection(length, items)
  65. if srid:
  66. self.srid = srid
  67. capi.destroy_geom(prev_ptr)
  68. _set_single = GEOSGeometry._set_single_rebuild
  69. _assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
  70. @property
  71. def json(self):
  72. if self.__class__.__name__ == 'GeometryCollection':
  73. return json.dumps({
  74. 'type': self.__class__.__name__,
  75. 'geometries': [
  76. {'type': geom.__class__.__name__, 'coordinates': geom.coords}
  77. for geom in self
  78. ],
  79. })
  80. return super(GeometryCollection, self).json
  81. geojson = json
  82. @property
  83. def kml(self):
  84. "Returns the KML for this Geometry Collection."
  85. return '<MultiGeometry>%s</MultiGeometry>' % ''.join(g.kml for g in self)
  86. @property
  87. def tuple(self):
  88. "Returns a tuple of all the coordinates in this Geometry Collection"
  89. return tuple(g.tuple for g in self)
  90. coords = tuple
  91. # MultiPoint, MultiLineString, and MultiPolygon class definitions.
  92. class MultiPoint(GeometryCollection):
  93. _allowed = Point
  94. _typeid = 4
  95. class MultiLineString(ProjectInterpolateMixin, GeometryCollection):
  96. _allowed = (LineString, LinearRing)
  97. _typeid = 5
  98. @property
  99. def merged(self):
  100. """
  101. Returns a LineString representing the line merge of this
  102. MultiLineString.
  103. """
  104. return self._topology(capi.geos_linemerge(self.ptr))
  105. class MultiPolygon(GeometryCollection):
  106. _allowed = Polygon
  107. _typeid = 6
  108. @property
  109. def cascaded_union(self):
  110. "Returns a cascaded union of this MultiPolygon."
  111. return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid)
  112. # Setting the allowed types here since GeometryCollection is defined before
  113. # its subclasses.
  114. GeometryCollection._allowed = (Point, LineString, LinearRing, Polygon, MultiPoint, MultiLineString, MultiPolygon)