Examples

Conversion

Some functions that analyze or manipulate geometries are possible in SQL but are easier to model in a procedural language. The following example will use Shapely to find the largest component polygon of a multipolygon.

CREATE OR REPLACE FUNCTION largest_poly(geom geometry)
  RETURNS geometry
AS $$
  from plpygis import Geometry
  polygons = Geometry(geom)
  if polygons.type == 'Polygon':
      return polygons
  elif polygons.type == 'MultiPolygon':
      largest = max(polygons.shapely, key=lambda polygon: polygon.area)
      return Geometry.from_shapely(largest)
  else:
      return None
$$ LANGUAGE plpython3u;

A pure PL/pgSQL function will have significantly better performance:

CREATE OR REPLACE FUNCTION largest_poly_fast(polygons geometry)
  RETURNS geometry
AS $$
  WITH geoms AS (
      SELECT (ST_Dump(polygons)).geom AS geom
  )
  SELECT geom
  FROM geoms
  ORDER BY ST_Area(geom) DESC LIMIT 1;
$$ LANGUAGE sql;

External services

Another application of plpygis is accessing external services or commands directly from PostgreSQL.

CREATE OR REPLACE FUNCTION geocode(geom geometry)
  RETURNS text
AS $$
  from geopy import Nominatim
  from plpygis import Geometry
  shape = Geometry(geom).shapely
  centroid = shape.centroid
  lon = centroid.x
  lat = centroid.y

  nominatim = Nominatim()
  location = nominatim.reverse((lat, lon))
  return location.address
$$ LANGUAGE plpython3u;
db=# SELECT name, geocode(geom) FROM countries LIMIT 5;
          name           |                       geocode
-------------------------+-----------------------------------------------------------
 Angola                  | Ringoma, Bié, Angola
 Anguilla                | Eric Reid Road, The Valley, Anguilla
 Albania                 | Bradashesh, Elbasan, Qarku i Elbasanit, 3001, Shqipëria
 American Samoa          | Aunu u, Sa'Ole County, Eastern District, American Samoa
 Andorra                 | Bordes de Rigoder, les Bons, Encamp, AD200, Andorra
(5 rows)

Rendering output

The gj2ascii project allows geometries to be easily rendered with a PL/Python function.

CREATE FUNCTION show(geom geometry)
  RETURNS text
AS $$
  from gj2ascii import render
  from plpygis import Geometry
  g = Geometry(geom)
  return render(g)
$$ LANGUAGE plpython3u
db=# SELECT show(geom) FROM countries WHERE name = 'Malta';
                                show
-------------------------------------------------------------
     + + + + +                                              +
 + + + + + + + + +                                          +
 + + + + + + + + + + +                                      +
   + + + + + + + + + +                                      +
       + + + + +                                            +
                                                            +
                             +                              +
                         + +                                +
                       + + + + + + +                        +
                         + + + + + + + + +                  +
                         + + + + + + + + + + +              +
                         + + + + + + + + + + + + +          +
                         + + + + + + + + + + + + + +        +
                         + + + + + + + + + + + + + +        +
                         + + + + + + + + + + + + + + + +    +
                           + + + + + + + + + + + + + + + +  +
                             + + + + + + + + + + + + + + + ++
                               + + + + + + + + + + + + + + ++
                                 + + + + + + + + + + + + + ++
                                     + + + + + + + + + +    +
                                             + + + + +
(1 row)

Spatial aggregate function

Normally, the function show as defined above would print the geometries of individual rows, one each per line.

db=# SELECT show(geom) FROM countries WHERE continent = 'Africa';

An aggregate version of show would take all the geometries and print them as a single map.

db=# SELECT showall(geom) FROM countries WHERE continent = 'Africa';
                               showall
-------------------------------------------------------------
                     1 1 1 Q                                +
               = = 1 1 1 1 Q                                +
             = = = 1 1 1 1 1 ; ; ;   ; ; - - -              +
           = = 1 1 1 1 1 1 1 ; ; ; ; ; ; - - -              +
         = @ @ 1 1 1 1 1 1 1 ; ; ; ; ; ; - - -              +
         G @ @ > > 1 1 1 1 1 C ; ; ; ; ; - - - -            +
       @ @ @ @ @ > > 1 1 1 C C C O O ; ; H H H H            +
         @ @ @ @ > > > > C C C C O O O H H H H H H          +
       F F @ @ @ > > > C C C C O O O O H H H H H .          +
       F F > > > $ $ C C D C D ( O O H H H H H H 2 2 .      +
         0 0 0 > $ 4 # D D D D ( O O & H H H I 2 2 2 2   K L+
           J 0 ' ' 4 P D D D D ( O & & & I I I 2 2 2 2 2 2  +
             : ' ' 4     D ( ( ( & & & & & I I I 2 2 2 2 L  +
                             ( ( * ) ) ) ) ) S 8 8 8 L L    +
                         M   3 * * ) ) ) ) ) S 8 8 8 L      +
                             3 * ) ) ) ) ) E R R 8 8        +
                               ) ) ) ) ) ) R R R R          +
                               ! ! ) ) ) ) ) R R R          +
                               ! ! ! ! ) ) U U R R          +
                               ! ! ! ! U U ) U ? ? ?        +
                               ! ! ! U U U U ? A ? ?     9 9+
                             B B B ! ! B U V V ?       9 9  +
                               B B B % % % V V         9 9  +
                                 B B % % % T ? ?       9 9  +
                                 B B % % T T ?         9    +
                                 B B T T T T T              +
                                   T T T T T                +
                                   T T T T                  +
                                                            +
                                                            +
(1 row)

The aggregate function is defined with the following properties:

CREATE AGGREGATE showall(geometry) (
  STYPE=geometry[],
  INITCOND='{}',
  SFUNC=array_append,
  FINALFUNC=_final_geom_show
);

The STYPE of geometry[] indicates that after each individual geometry has been processed, there will be a PostgreSQL list of individual geometry objects as a result. INITCOND is used to ensure that list starts empty and can be added to incrementally by the native PostgreSQL function array_append.

The function _final_geom_show will take the STYPE as the single parameter:

CREATE OR REPLACE FUNCTION _final_geom_show(geoms geometry[])
  RETURNS text
AS $$
  from gj2ascii import render_multiple
  from plpygis import Geometry
  from itertools import cycle
  # assign an ascii character sequentially to each geometry
  chars = [chr(i) for i in range(33,126)]
  geojsons = [Geometry(g) for g in geoms]
  layers = zip(geojsons, chars)
  return render_multiple(layers, width)
$$ LANGUAGE plpython3u

PL/Python automatically maps lists to Python arrays, so plpygis is only responsible for converting each elment of the list (in the example, above this is done using list comprehension: [Geometry(g) for g in geoms]).