point.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. """Points and related utilities."""
  2. import numpy as np
  3. import shapely
  4. from shapely.errors import DimensionError
  5. from shapely.geometry.base import BaseGeometry
  6. __all__ = ["Point"]
  7. class Point(BaseGeometry):
  8. """A geometry type that represents a single coordinate.
  9. Each coordinate has x, y and possibly z and/or m values.
  10. A point is a zero-dimensional feature and has zero length and zero area.
  11. Parameters
  12. ----------
  13. args : float, or sequence of floats
  14. The coordinates can either be passed as a single parameter, or as
  15. individual float values using multiple parameters:
  16. 1) 1 parameter: a sequence or array-like of with 2 or 3 values.
  17. 2) 2 or 3 parameters (float): x, y, and possibly z.
  18. Attributes
  19. ----------
  20. x, y, z, m : float
  21. Coordinate values
  22. Examples
  23. --------
  24. Constructing the Point using separate parameters for x and y:
  25. >>> from shapely import Point
  26. >>> p = Point(1.0, -1.0)
  27. Constructing the Point using a list of x, y coordinates:
  28. >>> p = Point([1.0, -1.0])
  29. >>> print(p)
  30. POINT (1 -1)
  31. >>> p.y
  32. -1.0
  33. >>> p.x
  34. 1.0
  35. """
  36. __slots__ = []
  37. def __new__(self, *args):
  38. """Create a new Point geometry."""
  39. if len(args) == 0:
  40. # empty geometry
  41. # TODO better constructor
  42. return shapely.from_wkt("POINT EMPTY")
  43. elif len(args) > 3:
  44. raise TypeError(f"Point() takes at most 3 arguments ({len(args)} given)")
  45. elif len(args) == 1:
  46. coords = args[0]
  47. if isinstance(coords, Point):
  48. return coords
  49. # Accept either (x, y) or [(x, y)]
  50. if not hasattr(coords, "__getitem__"): # generators
  51. coords = list(coords)
  52. coords = np.asarray(coords).squeeze()
  53. else:
  54. # 2 or 3 args
  55. coords = np.array(args).squeeze()
  56. if coords.ndim > 1:
  57. raise ValueError(
  58. f"Point() takes only scalar or 1-size vector arguments, got {args}"
  59. )
  60. if not np.issubdtype(coords.dtype, np.number):
  61. coords = [float(c) for c in coords]
  62. geom = shapely.points(coords)
  63. if not isinstance(geom, Point):
  64. raise ValueError("Invalid values passed to Point constructor")
  65. return geom
  66. # Coordinate getters and setters
  67. @property
  68. def x(self):
  69. """Return x coordinate."""
  70. return float(shapely.get_x(self))
  71. @property
  72. def y(self):
  73. """Return y coordinate."""
  74. return float(shapely.get_y(self))
  75. @property
  76. def z(self):
  77. """Return z coordinate."""
  78. z = shapely.get_z(self)
  79. if np.isnan(z) and not shapely.has_z(self):
  80. raise DimensionError("This point has no z coordinate.")
  81. return float(z)
  82. @property
  83. def m(self):
  84. """Return m coordinate.
  85. .. versionadded:: 2.1.0
  86. Also requires GEOS 3.12.0 or later.
  87. """
  88. if not shapely.has_m(self):
  89. raise DimensionError("This point has no m coordinate.")
  90. return float(shapely.get_m(self))
  91. @property
  92. def __geo_interface__(self):
  93. """Return a GeoJSON-like mapping of the Point geometry."""
  94. coords = self.coords
  95. return {"type": "Point", "coordinates": coords[0] if len(coords) > 0 else ()}
  96. def svg(self, scale_factor=1.0, fill_color=None, opacity=None):
  97. """Return SVG circle element for the Point geometry.
  98. Parameters
  99. ----------
  100. scale_factor : float
  101. Multiplication factor for the SVG circle diameter. Default is 1.
  102. fill_color : str, optional
  103. Hex string for fill color. Default is to use "#66cc99" if
  104. geometry is valid, and "#ff3333" if invalid.
  105. opacity : float
  106. Float number between 0 and 1 for color opacity. Default value is 0.6
  107. """
  108. if self.is_empty:
  109. return "<g />"
  110. if fill_color is None:
  111. fill_color = "#66cc99" if self.is_valid else "#ff3333"
  112. if opacity is None:
  113. opacity = 0.6
  114. return (
  115. f'<circle cx="{self.x}" cy="{self.y}" r="{3.0 * scale_factor}" '
  116. f'stroke="#555555" stroke-width="{1.0 * scale_factor}" fill="{fill_color}" '
  117. f'opacity="{opacity}" />'
  118. )
  119. @property
  120. def xy(self):
  121. """Separate arrays of X and Y coordinate values.
  122. Examples
  123. --------
  124. >>> from shapely import Point
  125. >>> x, y = Point(0, 0).xy
  126. >>> list(x)
  127. [0.0]
  128. >>> list(y)
  129. [0.0]
  130. """
  131. return self.coords.xy
  132. shapely.lib.registry[0] = Point