geo.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """Geometry factories based on the geo interface."""
  2. import numpy as np
  3. from shapely.errors import GeometryTypeError
  4. from shapely.geometry.collection import GeometryCollection
  5. from shapely.geometry.linestring import LineString
  6. from shapely.geometry.multilinestring import MultiLineString
  7. from shapely.geometry.multipoint import MultiPoint
  8. from shapely.geometry.multipolygon import MultiPolygon
  9. from shapely.geometry.point import Point
  10. from shapely.geometry.polygon import LinearRing, Polygon
  11. def _is_coordinates_empty(coordinates):
  12. """Identify if coordinates or subset of coordinates are empty."""
  13. if coordinates is None:
  14. return True
  15. if isinstance(coordinates, (list, tuple, np.ndarray)):
  16. if len(coordinates) == 0:
  17. return True
  18. return all(map(_is_coordinates_empty, coordinates))
  19. else:
  20. return False
  21. def _empty_shape_for_no_coordinates(geom_type):
  22. """Return empty counterpart for geom_type."""
  23. if geom_type == "point":
  24. return Point()
  25. elif geom_type == "multipoint":
  26. return MultiPoint()
  27. elif geom_type == "linestring":
  28. return LineString()
  29. elif geom_type == "multilinestring":
  30. return MultiLineString()
  31. elif geom_type == "polygon":
  32. return Polygon()
  33. elif geom_type == "multipolygon":
  34. return MultiPolygon()
  35. else:
  36. raise GeometryTypeError(f"Unknown geometry type: {geom_type!r}")
  37. def box(minx, miny, maxx, maxy, ccw=True):
  38. """Return a rectangular polygon with configurable normal vector."""
  39. coords = [(maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny)]
  40. if not ccw:
  41. coords = coords[::-1]
  42. return Polygon(coords)
  43. def shape(context):
  44. """Return a new, independent geometry with coordinates copied from the context.
  45. Changes to the original context will not be reflected in the geometry
  46. object.
  47. Parameters
  48. ----------
  49. context :
  50. a GeoJSON-like dict, which provides a "type" member describing the type
  51. of the geometry and "coordinates" member providing a list of coordinates,
  52. or an object which implements __geo_interface__.
  53. Returns
  54. -------
  55. Geometry object
  56. Examples
  57. --------
  58. Create a Point from GeoJSON, and then create a copy using __geo_interface__.
  59. >>> from shapely.geometry import shape
  60. >>> context = {'type': 'Point', 'coordinates': [0, 1]}
  61. >>> geom = shape(context)
  62. >>> geom.geom_type == 'Point'
  63. True
  64. >>> geom.wkt
  65. 'POINT (0 1)'
  66. >>> geom2 = shape(geom)
  67. >>> geom == geom2
  68. True
  69. """
  70. if hasattr(context, "__geo_interface__"):
  71. ob = context.__geo_interface__
  72. else:
  73. ob = context
  74. geom_type = ob.get("type").lower()
  75. if geom_type == "feature":
  76. # GeoJSON features must have a 'geometry' field.
  77. ob = ob["geometry"]
  78. geom_type = ob.get("type").lower()
  79. if "coordinates" in ob and _is_coordinates_empty(ob["coordinates"]):
  80. return _empty_shape_for_no_coordinates(geom_type)
  81. elif geom_type == "point":
  82. return Point(ob["coordinates"])
  83. elif geom_type == "linestring":
  84. return LineString(ob["coordinates"])
  85. elif geom_type == "linearring":
  86. return LinearRing(ob["coordinates"])
  87. elif geom_type == "polygon":
  88. return Polygon(ob["coordinates"][0], ob["coordinates"][1:])
  89. elif geom_type == "multipoint":
  90. return MultiPoint(ob["coordinates"])
  91. elif geom_type == "multilinestring":
  92. return MultiLineString(ob["coordinates"])
  93. elif geom_type == "multipolygon":
  94. return MultiPolygon([[c[0], c[1:]] for c in ob["coordinates"]])
  95. elif geom_type == "geometrycollection":
  96. geoms = [shape(g) for g in ob.get("geometries", [])]
  97. return GeometryCollection(geoms)
  98. else:
  99. raise GeometryTypeError(f"Unknown geometry type: {geom_type!r}")
  100. def mapping(ob):
  101. """Return a GeoJSON-like mapping.
  102. Input should be a Geometry or an object which implements __geo_interface__.
  103. Parameters
  104. ----------
  105. ob : geometry or object
  106. An object which implements __geo_interface__.
  107. Returns
  108. -------
  109. dict
  110. Examples
  111. --------
  112. >>> from shapely.geometry import mapping, Point
  113. >>> pt = Point(0, 0)
  114. >>> mapping(pt)
  115. {'type': 'Point', 'coordinates': (0.0, 0.0)}
  116. """
  117. return ob.__geo_interface__