``Geometry`` subclasses ======================= Creating a new ``plpygis`` geometry from a WKB, GeoJSON or Shapely instance, will produce a new subclass of the base :class:`Geometry ` class: * :class:`Point ` * :class:`LineString ` * :class:`Polygon ` * :class:`MultiPoint ` * :class:`LineString ` * :class:`MultiPolygon ` * :class:`GeometryCollection ` Creation -------- New instances of the three base shapes (points, lines and polygons) may be created by passing in coordinates: .. code-block:: python >>> point = Point((0, 0)) >>> line = LineString([(0, 0), (0, 1)]) >>> poly = Polygon([[(0, 0), (0, 10), (10, 10), (10, 0), (0, 0)], [(4, 4), (4, 6), (6, 6), (6, 4), (4, 4)]]) Editing ------- Coordinates may be accessed and modified after creation. .. code-block:: python >>> point = Point((0, 0)) >>> print(point.x) 0 >>> point.x = 10 >>> print(point.x) 10 Composition ----------- Individual :class:`LineString ` instances are composed of a list of :class:`Point ` instances that each represent a vertex in the line. Similarly, :class:`Polygon ` instances are composed of a list of :class:`LineString ` instances that each represent linear rings. The lists of vertices or linear rings can be modified, for example by adding a new :class:`Point ` to the end of a :class:`LineString `. .. note:: The first linear ring in a polygon should represent the exterior ring, while subsequent linear rings are internal boundaries. ``plpygis`` will not validate geometries when they are created. The four collection types, :class:`MultiPoint `, :class:`LineString `, :class:`MultiPolygon ` and :class:`GeometryCollection `, are each composed of a list of other geometries of the appropriate type. At creation time, the collection types are created by passing in a list of existing instances: .. code-block:: python >>> p1 = Point((0, 0)) >>> p2 = Point((1, 1)) >>> mp = MultiPoint([p1, p2]) `plpygis` will not create copies of any `Geometry >> p = Point((0, 0)) >>> mp = MultiPoint([p1]) >>> mp.points[0].x 0 >>> p.x = 100 >>> mp.points[0].x 100 SRIDs ----- An SRID may be added at creation time with an optional ``SRID`` parameter: .. code-block:: python >>> point = Point((0, 0), srid=4326) When creating a multigeometry with an SRID, each geometry must have the same SRID or no SRID. .. code-block:: python >>> p1 = Point((0, 0), srid=4326) >>> p2 = Point((1, 1), srid=4326) >>> mp = MultiPoint([p1, p2], srid=4326) >>> p3 = Point((0, 0)) >>> p4 = Point((1, 1)) >>> mp = MultiPoint([p3, p4], srid=4326) ``plpygis`` will not allow the creation of a multigeometry with no SRID if any of the geometries have one. .. warning:: Changing the SRID of an instance that is part of another geometry (such as a :class:`Point ` that is a vertex in a :class:`LineString ` or a vertex in the linear ring of a :class:`Polygon `) will *not* be detected. When converted to a WKB or Shapely instance, only the SRID of the "parent" geometry will be used. Dimensionality -------------- The ``dimz`` and ``dimm`` boolean parameters will indicate whether the geometry will have Z and M dimensions. ``plpygis`` will attempt to match provided coordinates with the requested dimensions or will set them to an initial value of ``0`` if they have not been provided: .. code-block:: python >>> p1 = Point((0, 0, 1), dimz=True, dimm=True) >>> print("p1", p1.x, p1.y, p1.z, p1.m) p1 0 0 1 0 >>> p2 = Point((0, 0, 1), dimm=True) >>> print("p2", p2.x, p2.y, p2.z, p2.m) p2 0 0 None 1 >>> p3 = Point((0, 0, 1, 2)) >>> print("p3", p3.x, p3.y, p3.z, p3.m) p3 0 0 1 2 The dimensionality of an existing instance may be altered after creation, by setting ``dimz`` or ``dimm``. Adding a dimension will add a Z or M coordinate with an initial value of ``0`` to the geometry and all geometries encompassed within it (*e.g.*, each vertex in a :class:`LineString ` or each :class:`Point ` in a :class:`MultiPoint ` will gain the new dimension). A new dimension may also be added to a single :class:`Point ` by assigning to the :meth:`z ` or :meth:`m ` properties. Adding a new dimension to a :class:`Point ` that is a vertex in a :class:`LineString ` or a vertex in the linear ring of a :class:`Polygon ` will *not* change the dimensionality of the :class:`LineString ` or the :class:`Polygon `. The dimensionality of "parent" instance *must* also be changed for the new coordinates to be reflected when converting to other representations. .. code-block:: python >>> p1 = Point((0, 0)) >>> p2 = Point((1, 1)) >>> mp = MultiPoint([p1, p2]) >>> print(mp.dimz) False >>> p1.z = 2 >>> print(p1.miz) True >>> print(mp.dimz) False >>> mp.dimz = True >>> print(mp.dimz) True >>> print("p1.z", p1.z, "p2.z", p2.z) p1.z 2 p2.z 0 Performance considerations -------------------------- Lazy evaluation ^^^^^^^^^^^^^^^ ``plpygis`` uses native WKB parsing to extract header information that indicates the geometry type, SRID and the presence of a Z or M dimension. Full parsing of the entire geometry only occurs when needed. It is therefore possible to test the type and dimensionality of a :class:`Geometry ` with only the first few bytes of data having been read. Perform these checks before performing any action that will require reading the remainder of the WKB. Caching ^^^^^^^ ``plpygis`` will cache the initial WKB it was created from. As soon as any coordinates or composite geometries are referenced, the cached WKB is lost and a subsequent request that requires the WKB will necessitate it being generated from scratch. For sets of large geometries, this can have a noticeable affect on performance. Therefore, if doing a conversion to a Shapely geometry - an action which relies on the availability of the WKB - it is recommended that this conversion be done before any other operations on the ``plpygis`` geometry. .. note:: Getting :meth:`type `, :meth:`srid `, :meth:`dimz ` and :meth:`dimm ` are considered "safe" operations. However writing a new SRID or changing the dimensionality will also result in the cached WKB being lost. A geometry's type may never be changed. As a summary, getting the following properties will not affect performance: * :meth:`type ` * :meth:`srid ` * :meth:`dimz ` * :meth:`dimm ` Setting the following properties will cause any cached WKB to be cleared: * :meth:`srid ` * :meth:`dimz ` * :meth:`dimm ` Getting the following property relies on the presence of the WKB (cached or generated): * :meth:`shapely ` If the :class:`Geometry ` was created from a WKB, the follwing actions will trigger a full parse and will clear the cached copy of the WKB: * getting :meth:`geojson ` and :meth:`__geo_interface__ ` * getting :meth:`shapely ` * getting any :class:`Point ` coordinate * getting :meth:`bounds ` * getting :meth:`vertices `, :meth:`rings ` * getting any component geometry from :class:`MultiPoint `, :class:`MultiLineString `, :class:`MultiPolygon ` or :class:`GeometryCollection `